msm: camera2: Add driver for cpp module

Add driver for Camera Post Processing hardware. This
module is responsible for supporting scaling, cropping,
rotation, flip, wavelet denoise and Sharpening features.

Change-Id: I4b130a5e53d40f96186992d540b93bbf4f8e628f
Signed-off-by: Sreesudhan Ramakrish Ramkumar <srramku@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 887f7cc..c3e1e3e 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -5590,6 +5590,20 @@
 						"fda20000.qcom,jpeg"),
 	CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
 						"fda24000.qcom,jpeg"),
+	CLK_LOOKUP("micro_iface_clk", camss_micro_ahb_clk.c,
+		"fda04000.qcom,cpp"),
+	CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+		"fda04000.qcom,cpp"),
+	CLK_LOOKUP("cpp_iface_clk", camss_vfe_cpp_ahb_clk.c,
+		"fda04000.qcom,cpp"),
+	CLK_LOOKUP("cpp_core_clk", camss_vfe_cpp_clk.c, "fda04000.qcom,cpp"),
+	CLK_LOOKUP("cpp_bus_clk", camss_vfe_vfe_axi_clk.c, "fda04000.qcom,cpp"),
+	CLK_LOOKUP("vfe_clk_src", vfe0_clk_src.c,	 "fda04000.qcom,cpp"),
+	CLK_LOOKUP("camss_vfe_vfe_clk", camss_vfe_vfe0_clk.c,
+					"fda04000.qcom,cpp"),
+	CLK_LOOKUP("iface_clk", camss_vfe_vfe_ahb_clk.c, "fda04000.qcom,cpp"),
+
+
 	CLK_LOOKUP("iface_clk", camss_micro_ahb_clk.c, ""),
 	CLK_LOOKUP("iface_clk", camss_vfe_vfe_ahb_clk.c, "fda44000.qcom,iommu"),
 	CLK_LOOKUP("core_clk", camss_vfe_vfe_axi_clk.c, "fda44000.qcom,iommu"),
diff --git a/drivers/media/platform/msm/camera_v2/Makefile b/drivers/media/platform/msm/camera_v2/Makefile
index 25dfd37..a1c5ea5 100644
--- a/drivers/media/platform/msm/camera_v2/Makefile
+++ b/drivers/media/platform/msm/camera_v2/Makefile
@@ -2,7 +2,7 @@
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/codecs
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/isps
-ccflags-y += -Idrivers/media/platform/msm/camera_v2/pps
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/pproc
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/camera
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/jpeg_10
@@ -15,3 +15,4 @@
 obj-$(CONFIG_MSMB_CAMERA) += ispif/
 obj-$(CONFIG_MSMB_JPEG) += jpeg_10/
 obj-$(CONFIG_MSMB_CAMERA) += msm_buf_mgr/
+obj-$(CONFIG_MSMB_CAMERA) += pproc/
diff --git a/drivers/media/platform/msm/camera_v2/pproc/Makefile b/drivers/media/platform/msm/camera_v2/pproc/Makefile
new file mode 100644
index 0000000..854e4e7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MSMB_CAMERA) += cpp/
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile
new file mode 100644
index 0000000..2f969d2
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSM_CPP) += msm_cpp.o
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
new file mode 100644
index 0000000..4b1b1c7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -0,0 +1,1025 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "MSM-CPP %s:%d " fmt, __func__, __LINE__
+
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ion.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <mach/iommu_domains.h>
+#include <mach/iommu.h>
+#include <mach/vreg.h>
+#include <media/msm_isp.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msmb_camera.h>
+#include <media/msmb_pproc.h>
+#include "msm_cpp.h"
+#include "msm_camera_io_util.h"
+
+#define MSM_CPP_DRV_NAME "msm_cpp"
+
+#define CONFIG_MSM_CPP_DBG 0
+
+#if CONFIG_MSM_CPP_DBG
+#define CPP_DBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CPP_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+#define ERR_USER_COPY(to) pr_err("copy %s user\n", \
+			((to) ? "to" : "from"))
+#define ERR_COPY_FROM_USER() ERR_USER_COPY(0)
+
+#define msm_dequeue(queue, member) ({	   \
+	unsigned long flags;		  \
+	struct msm_device_queue *__q = (queue);	 \
+	struct msm_queue_cmd *qcmd = 0;	   \
+	spin_lock_irqsave(&__q->lock, flags);	 \
+	if (!list_empty(&__q->list)) {		\
+		__q->len--;		 \
+		qcmd = list_first_entry(&__q->list,   \
+		struct msm_queue_cmd, member);  \
+		list_del_init(&qcmd->member);	 \
+	}			 \
+	spin_unlock_irqrestore(&__q->lock, flags);  \
+	qcmd;			 \
+})
+
+static void msm_queue_init(struct msm_device_queue *queue, const char *name)
+{
+	CPP_DBG("E\n");
+	spin_lock_init(&queue->lock);
+	queue->len = 0;
+	queue->max = 0;
+	queue->name = name;
+	INIT_LIST_HEAD(&queue->list);
+	init_waitqueue_head(&queue->wait);
+}
+
+static void msm_enqueue(struct msm_device_queue *queue,
+			struct list_head *entry)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&queue->lock, flags);
+	queue->len++;
+	if (queue->len > queue->max) {
+		queue->max = queue->len;
+		pr_info("queue %s new max is %d\n", queue->name, queue->max);
+	}
+	list_add_tail(entry, &queue->list);
+	wake_up(&queue->wait);
+	CPP_DBG("woke up %s\n", queue->name);
+	spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static struct msm_cam_clk_info cpp_clk_info[] = {
+	{"camss_top_ahb_clk", -1},
+	{"vfe_clk_src", 266670000},
+	{"camss_vfe_vfe_clk", -1},
+	{"iface_clk", -1},
+	{"cpp_core_clk", 266670000},
+	{"cpp_iface_clk", -1},
+	{"cpp_bus_clk", -1},
+	{"micro_iface_clk", -1},
+};
+static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev);
+
+static void msm_cpp_write(u32 data, void __iomem *cpp_base)
+{
+	writel_relaxed((data), cpp_base + MSM_CPP_MICRO_FIFO_RX_DATA);
+}
+
+static uint32_t msm_cpp_read(void __iomem *cpp_base)
+{
+	uint32_t tmp, retry = 0;
+	do {
+		tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_TX_STAT);
+	} while (((tmp & 0x2) == 0x0) && (retry++ < 10)) ;
+	if (retry < 10) {
+		tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_TX_DATA);
+		CPP_DBG("Read data: 0%x\n", tmp);
+	} else {
+		CPP_DBG("Read failed\n");
+		tmp = 0xDEADBEEF;
+	}
+	return tmp;
+}
+
+static void msm_cpp_poll(void __iomem *cpp_base, u32 val)
+{
+	uint32_t tmp, retry = 0;
+	do {
+		usleep_range(1000, 2000);
+		tmp = msm_cpp_read(cpp_base);
+		if (tmp != 0xDEADBEEF)
+			CPP_DBG("poll: 0%x\n", tmp);
+	} while ((tmp != val) && (retry++ < MSM_CPP_POLL_RETRIES));
+	if (retry < MSM_CPP_POLL_RETRIES)
+		CPP_DBG("Poll finished\n");
+	else
+		pr_err("Poll failed: expect: 0x%x\n", val);
+}
+
+void cpp_release_ion_client(struct kref *ref)
+{
+	struct cpp_device *cpp_dev = container_of(ref,
+		struct cpp_device, refcount);
+	pr_err("Calling ion_client_destroy\n");
+	ion_client_destroy(cpp_dev->client);
+}
+
+static int cpp_init_mem(struct cpp_device *cpp_dev)
+{
+	int rc = 0;
+
+	kref_init(&cpp_dev->refcount);
+	kref_get(&cpp_dev->refcount);
+	cpp_dev->client = msm_ion_client_create(-1, "cpp");
+
+	CPP_DBG("E\n");
+	if (!cpp_dev->domain) {
+		pr_err("domain / iommu context not found\n");
+		return  -ENODEV;
+	}
+
+	CPP_DBG("X\n");
+	return rc;
+}
+
+static void cpp_deinit_mem(struct cpp_device *cpp_dev)
+{
+	CPP_DBG("E\n");
+	kref_put(&cpp_dev->refcount, cpp_release_ion_client);
+	CPP_DBG("X\n");
+}
+
+static irqreturn_t msm_cpp_irq(int irq_num, void *data)
+{
+	uint32_t tx_level;
+	uint32_t irq_status;
+	uint32_t msg_id, cmd_len;
+	uint32_t i;
+	uint32_t tx_fifo[16];
+	struct cpp_device *cpp_dev = data;
+	irq_status = msm_camera_io_r(cpp_dev->base + MSM_CPP_MICRO_IRQGEN_STAT);
+	CPP_DBG("status: 0x%x\n", irq_status);
+	if (irq_status & 0x8) {
+		tx_level = msm_camera_io_r(cpp_dev->base +
+			MSM_CPP_MICRO_FIFO_TX_STAT) >> 2;
+		for (i = 0; i < tx_level; i++) {
+			tx_fifo[i] = msm_camera_io_r(cpp_dev->base +
+				MSM_CPP_MICRO_FIFO_TX_DATA);
+		}
+
+		for (i = 0; i < tx_level; i++) {
+			if (tx_fifo[i] == MSM_CPP_MSG_ID_CMD) {
+				cmd_len = tx_fifo[i+1];
+				msg_id = tx_fifo[i+2];
+				if (msg_id == MSM_CPP_MSG_ID_FRAME_ACK) {
+					CPP_DBG("Frame done!!\n");
+					msm_cpp_notify_frame_done(cpp_dev);
+				}
+				i += cmd_len + 2;
+			}
+		}
+	}
+	msm_camera_io_w(irq_status, cpp_dev->base + MSM_CPP_MICRO_IRQGEN_CLR);
+	return IRQ_HANDLED;
+}
+
+static int cpp_init_hardware(struct cpp_device *cpp_dev)
+{
+	int rc = 0;
+
+	if (cpp_dev->fs_cpp == NULL) {
+		cpp_dev->fs_cpp =
+			regulator_get(&cpp_dev->pdev->dev, "vdd");
+		if (IS_ERR(cpp_dev->fs_cpp)) {
+			pr_err("Regulator cpp vdd get failed %ld\n",
+				PTR_ERR(cpp_dev->fs_cpp));
+			cpp_dev->fs_cpp = NULL;
+			goto fs_failed;
+		} else if (regulator_enable(cpp_dev->fs_cpp)) {
+			pr_err("Regulator cpp vdd enable failed\n");
+			regulator_put(cpp_dev->fs_cpp);
+			cpp_dev->fs_cpp = NULL;
+			goto fs_failed;
+		}
+	}
+
+	rc = msm_cam_clk_enable(&cpp_dev->pdev->dev, cpp_clk_info,
+			cpp_dev->cpp_clk, ARRAY_SIZE(cpp_clk_info), 1);
+	if (rc < 0) {
+		pr_err("clk enable failed\n");
+		goto clk_failed;
+	}
+
+	cpp_dev->base = ioremap(cpp_dev->mem->start,
+		resource_size(cpp_dev->mem));
+	if (!cpp_dev->base) {
+		rc = -ENOMEM;
+		pr_err("ioremap failed\n");
+		goto remap_failed;
+	}
+
+	cpp_dev->vbif_base = ioremap(cpp_dev->vbif_mem->start,
+		resource_size(cpp_dev->vbif_mem));
+	if (!cpp_dev->vbif_base) {
+		rc = -ENOMEM;
+		pr_err("ioremap failed\n");
+		goto vbif_remap_failed;
+	}
+
+	if (cpp_dev->state != CPP_STATE_BOOT) {
+		rc = request_irq(cpp_dev->irq->start, msm_cpp_irq,
+			IRQF_TRIGGER_RISING, "cpp", cpp_dev);
+		if (rc < 0) {
+			pr_err("irq request fail\n");
+			rc = -EBUSY;
+			goto req_irq_fail;
+		}
+	}
+
+	msm_camera_io_w(0x1, cpp_dev->vbif_base + 0x4);
+
+	return rc;
+req_irq_fail:
+	iounmap(cpp_dev->vbif_base);
+vbif_remap_failed:
+	iounmap(cpp_dev->base);
+remap_failed:
+	msm_cam_clk_enable(&cpp_dev->pdev->dev, cpp_clk_info,
+		cpp_dev->cpp_clk, ARRAY_SIZE(cpp_clk_info), 0);
+clk_failed:
+	regulator_disable(cpp_dev->fs_cpp);
+	regulator_put(cpp_dev->fs_cpp);
+fs_failed:
+	return rc;
+}
+
+static void cpp_release_hardware(struct cpp_device *cpp_dev)
+{
+	if (cpp_dev->state != CPP_STATE_BOOT)
+		free_irq(cpp_dev->irq->start, cpp_dev);
+
+	iounmap(cpp_dev->base);
+	msm_cam_clk_enable(&cpp_dev->pdev->dev, cpp_clk_info,
+		cpp_dev->cpp_clk, ARRAY_SIZE(cpp_clk_info), 0);
+	if (0) {
+		regulator_disable(cpp_dev->fs_cpp);
+		regulator_put(cpp_dev->fs_cpp);
+		cpp_dev->fs_cpp = NULL;
+	}
+}
+
+static void cpp_load_fw(struct cpp_device *cpp_dev)
+{
+	uint32_t i;
+	uint32_t *ptr_bin = NULL;
+	int32_t rc = -EFAULT;
+	const struct firmware *fw = NULL;
+	char *fw_name_bin = "cpp_firmware_v1_1_1.fw";
+	struct device *dev = &cpp_dev->pdev->dev;
+
+	rc = request_firmware(&fw, fw_name_bin, dev);
+	if (rc) {
+		dev_err(dev, "Failed to locate blob %s from device %p, Error: %d\n",
+				fw_name_bin, dev, rc);
+	}
+
+	CPP_DBG("HW Ver:0x%x\n",
+		msm_camera_io_r(cpp_dev->base +
+		MSM_CPP_MICRO_HW_VERSION));
+
+	msm_camera_io_w(0x1, cpp_dev->base +
+					   MSM_CPP_MICRO_BOOT_START);
+	/*Enable MC clock*/
+	msm_camera_io_w(0x1, cpp_dev->base +
+					   MSM_CPP_MICRO_CLKEN_CTL);
+
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+
+	/*Start firmware loading*/
+	msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
+	msm_cpp_write(MSM_CPP_END_ADDRESS, cpp_dev->base);
+	msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
+	if (NULL != fw)
+		ptr_bin = (uint32_t *)fw->data;
+
+	for (i = 0; i < fw->size/4; i++) {
+		if (ptr_bin) {
+			msm_cpp_write(*ptr_bin, cpp_dev->base);
+			ptr_bin++;
+		}
+	}
+	if (fw)
+		release_firmware(fw);
+
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+
+	/*Trigger MC to jump to start address*/
+	msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
+	msm_cpp_write(MSM_CPP_JUMP_ADDRESS, cpp_dev->base);
+
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+	msm_cpp_poll(cpp_dev->base, 0x1);
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_JUMP_ACK);
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
+
+	/*Get Bootloader Version*/
+	msm_cpp_write(MSM_CPP_CMD_GET_BOOTLOADER_VER, cpp_dev->base);
+	pr_info("MC Bootloader Version: 0x%x\n",
+		   msm_cpp_read(cpp_dev->base));
+
+	/*Get Firmware Version*/
+	msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
+	msm_cpp_write(MSM_CPP_MSG_ID_CMD, cpp_dev->base);
+	msm_cpp_write(0x1, cpp_dev->base);
+	msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
+	msm_cpp_write(MSM_CPP_MSG_ID_TRAILER, cpp_dev->base);
+
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+	msm_cpp_poll(cpp_dev->base, 0x2);
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_FW_VER);
+	pr_info("CPP FW Version: 0x%x\n", msm_cpp_read(cpp_dev->base));
+	msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
+
+}
+
+static int cpp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	uint32_t i;
+	struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
+	CPP_DBG("E\n");
+
+	mutex_lock(&cpp_dev->mutex);
+	if (cpp_dev->cpp_open_cnt == MAX_ACTIVE_CPP_INSTANCE) {
+		pr_err("No free CPP instance\n");
+		mutex_unlock(&cpp_dev->mutex);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
+		if (cpp_dev->cpp_subscribe_list[i].active == 0) {
+			cpp_dev->cpp_subscribe_list[i].active = 1;
+			cpp_dev->cpp_subscribe_list[i].vfh = &fh->vfh;
+			break;
+		}
+	}
+	if (i == MAX_ACTIVE_CPP_INSTANCE) {
+		pr_err("No free instance\n");
+		mutex_unlock(&cpp_dev->mutex);
+		return -ENODEV;
+	}
+
+	CPP_DBG("open %d %p\n", i, &fh->vfh);
+	cpp_dev->cpp_open_cnt++;
+	if (cpp_dev->cpp_open_cnt == 1) {
+		cpp_init_hardware(cpp_dev);
+		cpp_init_mem(cpp_dev);
+		disable_irq(cpp_dev->irq->start);
+
+		cpp_load_fw(cpp_dev);
+
+		enable_irq(cpp_dev->irq->start);
+
+		msm_camera_io_w_mb(0x8, cpp_dev->base +
+			MSM_CPP_MICRO_IRQGEN_MASK);
+		msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
+			MSM_CPP_MICRO_IRQGEN_CLR);
+		cpp_dev->state = CPP_STATE_IDLE;
+	}
+	mutex_unlock(&cpp_dev->mutex);
+	return 0;
+}
+
+static int cpp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	uint32_t i;
+	struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
+	mutex_lock(&cpp_dev->mutex);
+	for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
+		if (cpp_dev->cpp_subscribe_list[i].vfh == &fh->vfh) {
+			cpp_dev->cpp_subscribe_list[i].active = 0;
+			cpp_dev->cpp_subscribe_list[i].vfh = NULL;
+			break;
+		}
+	}
+	if (i == MAX_ACTIVE_CPP_INSTANCE) {
+		pr_err("Invalid close\n");
+		mutex_unlock(&cpp_dev->mutex);
+		return -ENODEV;
+	}
+
+	CPP_DBG("close %d %p\n", i, &fh->vfh);
+	cpp_dev->cpp_open_cnt--;
+	if (cpp_dev->cpp_open_cnt == 0) {
+		msm_camera_io_w(0x0, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
+		cpp_deinit_mem(cpp_dev);
+		cpp_release_hardware(cpp_dev);
+		cpp_dev->state = CPP_STATE_OFF;
+	}
+	mutex_unlock(&cpp_dev->mutex);
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops msm_cpp_internal_ops = {
+	.open = cpp_open_node,
+	.close = cpp_close_node,
+};
+
+static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev)
+{
+	struct v4l2_event v4l2_evt;
+	struct msm_queue_cmd *frame_qcmd;
+	struct msm_queue_cmd *event_qcmd;
+	struct msm_cpp_frame_info_t *processed_frame;
+	struct msm_device_queue *queue = &cpp_dev->processing_q;
+
+	if (queue->len > 0) {
+		frame_qcmd = msm_dequeue(queue, list_frame);
+		processed_frame = frame_qcmd->command;
+		kfree(frame_qcmd);
+		event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_ATOMIC);
+		if (!event_qcmd) {
+			pr_err("Insufficient memory. return");
+			return -ENOMEM;
+		}
+		atomic_set(&event_qcmd->on_heap, 1);
+		event_qcmd->command = processed_frame;
+		CPP_DBG("fid %d\n", processed_frame->frame_id);
+		msm_enqueue(&cpp_dev->eventData_q, &event_qcmd->list_eventdata);
+
+		v4l2_evt.id = processed_frame->inst_id;
+		v4l2_evt.type = V4L2_EVENT_CPP_FRAME_DONE;
+		v4l2_event_queue(cpp_dev->msm_sd.sd.devnode, &v4l2_evt);
+	}
+	return 0;
+}
+
+static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev)
+{
+	uint32_t i;
+	struct msm_queue_cmd *frame_qcmd;
+	struct msm_cpp_frame_info_t *process_frame;
+	struct msm_device_queue *queue;
+
+	if (cpp_dev->processing_q.len < MAX_CPP_PROCESSING_FRAME) {
+		while (cpp_dev->processing_q.len < MAX_CPP_PROCESSING_FRAME) {
+			if (cpp_dev->realtime_q.len != 0) {
+				queue = &cpp_dev->realtime_q;
+			} else if (cpp_dev->offline_q.len != 0) {
+				queue = &cpp_dev->offline_q;
+			} else {
+				pr_debug("All frames queued\n");
+				break;
+			}
+			frame_qcmd = msm_dequeue(queue, list_frame);
+			process_frame = frame_qcmd->command;
+			msm_enqueue(&cpp_dev->processing_q,
+						&frame_qcmd->list_frame);
+			msm_cpp_write(0x6, cpp_dev->base);
+			for (i = 0; i < process_frame->msg_len; i++)
+				msm_cpp_write(process_frame->cpp_cmd_msg[i],
+					cpp_dev->base);
+		}
+	}
+	return 0;
+}
+
+static int msm_cpp_cfg(struct cpp_device *cpp_dev,
+	struct msm_camera_v4l2_ioctl_t *ioctl_ptr)
+{
+	int rc = 0;
+	struct msm_queue_cmd *frame_qcmd = NULL;
+	struct msm_cpp_frame_info_t *new_frame =
+		kzalloc(sizeof(struct msm_cpp_frame_info_t), GFP_KERNEL);
+	uint32_t *cpp_frame_msg;
+	struct ion_handle *src_ion_handle = NULL;
+	struct ion_handle *dest_ion_handle = NULL;
+	unsigned long len;
+	unsigned long in_phyaddr, out_phyaddr;
+	uint16_t num_stripes = 0;
+
+	int i = 0;
+	if (!new_frame) {
+		pr_err("Insufficient memory. return\n");
+		return -ENOMEM;
+	}
+
+	rc = (copy_from_user(new_frame, (void __user *)ioctl_ptr->ioctl_ptr,
+		sizeof(struct msm_cpp_frame_info_t)) ? -EFAULT : 0);
+	if (rc) {
+		ERR_COPY_FROM_USER();
+		rc = -EINVAL;
+		goto ERROR1;
+	}
+
+	cpp_frame_msg = kzalloc(sizeof(uint32_t)*new_frame->msg_len,
+		GFP_KERNEL);
+	if (!cpp_frame_msg) {
+		pr_err("Insufficient memory. return");
+		rc = -ENOMEM;
+		goto ERROR1;
+	}
+
+	rc = (copy_from_user(cpp_frame_msg,
+		(void __user *)new_frame->cpp_cmd_msg,
+		sizeof(uint32_t)*new_frame->msg_len) ? -EFAULT : 0);
+	if (rc) {
+		ERR_COPY_FROM_USER();
+		rc = -EINVAL;
+		goto ERROR2;
+	}
+
+	new_frame->cpp_cmd_msg = cpp_frame_msg;
+
+	CPP_DBG("CPP in_fd: %d out_fd: %d\n", new_frame->src_fd,
+		new_frame->dst_fd);
+
+	src_ion_handle = ion_import_dma_buf(cpp_dev->client,
+		new_frame->src_fd);
+	if (IS_ERR_OR_NULL(src_ion_handle)) {
+		pr_err("ION import failed\n");
+		rc = PTR_ERR(src_ion_handle);
+		goto ERROR2;
+	}
+	rc = ion_map_iommu(cpp_dev->client, src_ion_handle,
+		cpp_dev->domain_num, 0, SZ_4K, 0,
+		(unsigned long *)&in_phyaddr, &len, 0, 0);
+	if (rc < 0) {
+		pr_err("ION import failed\n");
+		rc = PTR_ERR(src_ion_handle);
+		goto ERROR3;
+	}
+
+	CPP_DBG("in phy addr: 0x%x len: %ld\n", (uint32_t) in_phyaddr, len);
+
+	dest_ion_handle = ion_import_dma_buf(cpp_dev->client,
+		new_frame->dst_fd);
+	if (IS_ERR_OR_NULL(dest_ion_handle)) {
+		pr_err("ION import failed\n");
+		rc = PTR_ERR(dest_ion_handle);
+		goto ERROR4;
+	}
+	rc = ion_map_iommu(cpp_dev->client, dest_ion_handle,
+		cpp_dev->domain_num, 0, SZ_4K, 0,
+		(unsigned long *)&out_phyaddr, &len, 0, 0);
+	if (rc < 0) {
+		rc = PTR_ERR(dest_ion_handle);
+		goto ERROR5;
+	}
+
+	CPP_DBG("out phy addr: 0x%x len: %ld\n", (uint32_t)out_phyaddr, len);
+
+	num_stripes = ((cpp_frame_msg[12] >> 20) & 0x3FF) +
+		((cpp_frame_msg[12] >> 10) & 0x3FF) +
+		(cpp_frame_msg[12] & 0x3FF);
+
+	for (i = 0; i < num_stripes; i++) {
+		cpp_frame_msg[133 + i * 27] += (uint32_t) in_phyaddr;
+		cpp_frame_msg[139 + i * 27] += (uint32_t) out_phyaddr;
+		cpp_frame_msg[140 + i * 27] += (uint32_t) out_phyaddr;
+		cpp_frame_msg[141 + i * 27] += (uint32_t) out_phyaddr;
+		cpp_frame_msg[142 + i * 27] += (uint32_t) out_phyaddr;
+	}
+
+	frame_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+	if (!frame_qcmd) {
+		pr_err("Insufficient memory. return\n");
+		rc = -ENOMEM;
+		goto ERROR6;
+	}
+
+	atomic_set(&frame_qcmd->on_heap, 1);
+	frame_qcmd->command = new_frame;
+	if (new_frame->frame_type == MSM_CPP_REALTIME_FRAME) {
+		msm_enqueue(&cpp_dev->realtime_q,
+					&frame_qcmd->list_frame);
+	} else if (new_frame->frame_type == MSM_CPP_OFFLINE_FRAME) {
+		msm_enqueue(&cpp_dev->offline_q,
+					&frame_qcmd->list_frame);
+	} else {
+		pr_err("Invalid frame type\n");
+		rc = -EINVAL;
+		goto ERROR7;
+	}
+	msm_cpp_send_frame_to_hardware(cpp_dev);
+	return rc;
+ERROR7:
+	kfree(frame_qcmd);
+ERROR6:
+	ion_unmap_iommu(cpp_dev->client, dest_ion_handle,
+		cpp_dev->domain_num, 0);
+ERROR5:
+	ion_free(cpp_dev->client, dest_ion_handle);
+ERROR4:
+	ion_unmap_iommu(cpp_dev->client, src_ion_handle,
+		cpp_dev->domain_num, 0);
+ERROR3:
+	ion_free(cpp_dev->client, src_ion_handle);
+ERROR2:
+	kfree(cpp_frame_msg);
+ERROR1:
+	kfree(new_frame);
+	return rc;
+
+}
+
+long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
+	struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+	int rc = 0;
+
+	mutex_lock(&cpp_dev->mutex);
+	CPP_DBG("E cmd: %d\n", cmd);
+	switch (cmd) {
+	case VIDIOC_MSM_CPP_CFG:
+		rc = msm_cpp_cfg(cpp_dev, ioctl_ptr);
+		break;
+	case VIDIOC_MSM_CPP_GET_EVENTPAYLOAD: {
+		struct msm_device_queue *queue = &cpp_dev->eventData_q;
+		struct msm_queue_cmd *event_qcmd;
+		struct msm_cpp_frame_info_t *process_frame;
+		event_qcmd = msm_dequeue(queue, list_eventdata);
+		process_frame = event_qcmd->command;
+		CPP_DBG("fid %d\n", process_frame->frame_id);
+		if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
+				process_frame,
+				sizeof(struct msm_cpp_frame_info_t))) {
+					mutex_unlock(&cpp_dev->mutex);
+					return -EINVAL;
+		}
+		kfree(process_frame->cpp_cmd_msg);
+		kfree(process_frame);
+		kfree(event_qcmd);
+		break;
+	}
+	}
+	mutex_unlock(&cpp_dev->mutex);
+	CPP_DBG("X\n");
+	return 0;
+}
+
+int msm_cpp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+	struct v4l2_event_subscription *sub)
+{
+	CPP_DBG("Called\n");
+	return v4l2_event_subscribe(fh, sub, MAX_CPP_V4l2_EVENTS);
+}
+
+int msm_cpp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+	struct v4l2_event_subscription *sub)
+{
+	CPP_DBG("Called\n");
+	return v4l2_event_unsubscribe(fh, sub);
+}
+
+static struct v4l2_subdev_core_ops msm_cpp_subdev_core_ops = {
+	.ioctl = msm_cpp_subdev_ioctl,
+	.subscribe_event = msm_cpp_subscribe_event,
+	.unsubscribe_event = msm_cpp_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_ops msm_cpp_subdev_ops = {
+	.core = &msm_cpp_subdev_core_ops,
+};
+
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev);
+
+static struct v4l2_file_operations msm_cpp_v4l2_subdev_fops;
+
+static long msm_cpp_subdev_do_ioctl(
+	struct file *file, unsigned int cmd, void *arg)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+	struct v4l2_fh *vfh = file->private_data;
+
+	switch (cmd) {
+	case VIDIOC_DQEVENT:
+		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+			return -ENOIOCTLCMD;
+
+		return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+	case VIDIOC_SUBSCRIBE_EVENT:
+		return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+	case VIDIOC_UNSUBSCRIBE_EVENT:
+		return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+	case VIDIOC_MSM_CPP_GET_INST_INFO: {
+		uint32_t i;
+		struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
+		struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
+		struct msm_cpp_frame_info_t inst_info;
+		for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
+			if (cpp_dev->cpp_subscribe_list[i].vfh == vfh) {
+				inst_info.inst_id = i;
+				break;
+			}
+		}
+		if (copy_to_user(
+				(void __user *)ioctl_ptr->ioctl_ptr, &inst_info,
+				sizeof(struct msm_cpp_frame_info_t))) {
+			return -EINVAL;
+		}
+	}
+	break;
+	default:
+		return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+	}
+
+	return 0;
+}
+
+static long msm_cpp_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	return video_usercopy(file, cmd, arg, msm_cpp_subdev_do_ioctl);
+}
+
+static int cpp_register_domain(void)
+{
+	struct msm_iova_partition cpp_fw_partition = {
+		.start = SZ_128K,
+		.size = SZ_2G - SZ_128K,
+	};
+	struct msm_iova_layout cpp_fw_layout = {
+		.partitions = &cpp_fw_partition,
+		.npartitions = 1,
+		.client_name = "camera_cpp",
+		.domain_flags = 0,
+	};
+
+	return msm_register_domain(&cpp_fw_layout);
+}
+
+static int __devinit cpp_probe(struct platform_device *pdev)
+{
+	struct cpp_device *cpp_dev;
+	int rc = 0;
+
+	cpp_dev = kzalloc(sizeof(struct cpp_device), GFP_KERNEL);
+	if (!cpp_dev) {
+		pr_err("no enough memory\n");
+		return -ENOMEM;
+	}
+
+	cpp_dev->cpp_clk = kzalloc(sizeof(struct clk *) *
+		ARRAY_SIZE(cpp_clk_info), GFP_KERNEL);
+	if (!cpp_dev->cpp_clk) {
+		pr_err("no enough memory\n");
+		rc = -ENOMEM;
+		goto ERROR1;
+	}
+
+	v4l2_subdev_init(&cpp_dev->msm_sd.sd, &msm_cpp_subdev_ops);
+	cpp_dev->msm_sd.sd.internal_ops = &msm_cpp_internal_ops;
+	snprintf(cpp_dev->msm_sd.sd.name, ARRAY_SIZE(cpp_dev->msm_sd.sd.name),
+		 "cpp");
+	cpp_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	cpp_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+	v4l2_set_subdevdata(&cpp_dev->msm_sd.sd, cpp_dev);
+	platform_set_drvdata(pdev, &cpp_dev->msm_sd.sd);
+	mutex_init(&cpp_dev->mutex);
+
+	if (pdev->dev.of_node)
+		of_property_read_u32((&pdev->dev)->of_node,
+					"cell-index", &pdev->id);
+
+	cpp_dev->pdev = pdev;
+
+	cpp_dev->mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "cpp");
+	if (!cpp_dev->mem) {
+		pr_err("no mem resource?\n");
+		rc = -ENODEV;
+		goto ERROR2;
+	}
+
+	cpp_dev->vbif_mem = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "cpp_vbif");
+	if (!cpp_dev->vbif_mem) {
+		pr_err("no mem resource?\n");
+		rc = -ENODEV;
+		goto ERROR2;
+	}
+
+	cpp_dev->irq = platform_get_resource_byname(pdev,
+					IORESOURCE_IRQ, "cpp");
+	if (!cpp_dev->irq) {
+		pr_err("%s: no irq resource?\n", __func__);
+		rc = -ENODEV;
+		goto ERROR2;
+	}
+
+	cpp_dev->io = request_mem_region(cpp_dev->mem->start,
+		resource_size(cpp_dev->mem), pdev->name);
+	if (!cpp_dev->io) {
+		pr_err("%s: no valid mem region\n", __func__);
+		rc = -EBUSY;
+		goto ERROR2;
+	}
+
+	cpp_dev->domain_num = cpp_register_domain();
+	if (cpp_dev->domain_num < 0) {
+		pr_err("%s: could not register domain\n", __func__);
+		rc = -ENODEV;
+		goto ERROR3;
+	}
+
+	cpp_dev->domain =
+		msm_get_iommu_domain(cpp_dev->domain_num);
+	if (!cpp_dev->domain) {
+		pr_err("%s: cannot find domain\n", __func__);
+		rc = -ENODEV;
+		goto ERROR3;
+	}
+
+	cpp_dev->iommu_ctx = msm_iommu_get_ctx("cpp");
+	if (!cpp_dev->iommu_ctx) {
+		pr_err("%s: cannot get iommu_ctx\n", __func__);
+		rc = -ENODEV;
+		goto ERROR3;
+	}
+
+	media_entity_init(&cpp_dev->msm_sd.sd.entity, 0, NULL, 0);
+	cpp_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+	cpp_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CPP;
+	cpp_dev->msm_sd.sd.entity.name = pdev->name;
+	msm_sd_register(&cpp_dev->msm_sd);
+	msm_cpp_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner;
+	msm_cpp_v4l2_subdev_fops.open = v4l2_subdev_fops.open;
+	msm_cpp_v4l2_subdev_fops.unlocked_ioctl = msm_cpp_subdev_fops_ioctl;
+	msm_cpp_v4l2_subdev_fops.release = v4l2_subdev_fops.release;
+	msm_cpp_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll;
+
+	cpp_dev->msm_sd.sd.devnode->fops = &msm_cpp_v4l2_subdev_fops;
+	cpp_dev->msm_sd.sd.entity.revision = cpp_dev->msm_sd.sd.devnode->num;
+	cpp_dev->state = CPP_STATE_BOOT;
+	cpp_init_hardware(cpp_dev);
+	iommu_attach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
+
+	msm_camera_io_w(0x0, cpp_dev->base +
+					   MSM_CPP_MICRO_IRQGEN_MASK);
+	msm_camera_io_w(0xFFFF, cpp_dev->base +
+					   MSM_CPP_MICRO_IRQGEN_CLR);
+
+	cpp_release_hardware(cpp_dev);
+	cpp_dev->state = CPP_STATE_OFF;
+
+	msm_cpp_enable_debugfs(cpp_dev);
+	msm_queue_init(&cpp_dev->eventData_q, "eventdata");
+	msm_queue_init(&cpp_dev->offline_q, "frame");
+	msm_queue_init(&cpp_dev->realtime_q, "frame");
+	msm_queue_init(&cpp_dev->processing_q, "frame");
+	cpp_dev->cpp_open_cnt = 0;
+
+	return rc;
+
+ERROR3:
+	release_mem_region(cpp_dev->mem->start, resource_size(cpp_dev->mem));
+ERROR2:
+	kfree(cpp_dev->cpp_clk);
+ERROR1:
+	kfree(cpp_dev);
+	return rc;
+}
+
+static const struct of_device_id msm_cpp_dt_match[] = {
+	{.compatible = "qcom,cpp"},
+	{}
+};
+
+static int cpp_device_remove(struct platform_device *dev)
+{
+	struct v4l2_subdev *sd = platform_get_drvdata(dev);
+	struct cpp_device  *cpp_dev;
+	if (!sd) {
+		pr_err("%s: Subdevice is NULL\n", __func__);
+		return 0;
+	}
+
+	cpp_dev = (struct cpp_device *)v4l2_get_subdevdata(sd);
+	if (!cpp_dev) {
+		pr_err("%s: cpp device is NULL\n", __func__);
+		return 0;
+	}
+
+	iommu_detach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
+	msm_sd_unregister(&cpp_dev->msm_sd);
+	release_mem_region(cpp_dev->mem->start, resource_size(cpp_dev->mem));
+	mutex_destroy(&cpp_dev->mutex);
+	kfree(cpp_dev->cpp_clk);
+	kfree(cpp_dev);
+	return 0;
+}
+
+static struct platform_driver cpp_driver = {
+	.probe = cpp_probe,
+	.remove = cpp_device_remove,
+	.driver = {
+		.name = MSM_CPP_DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = msm_cpp_dt_match,
+	},
+};
+
+static int __init msm_cpp_init_module(void)
+{
+	return platform_driver_register(&cpp_driver);
+}
+
+static void __exit msm_cpp_exit_module(void)
+{
+	platform_driver_unregister(&cpp_driver);
+}
+
+static int msm_cpp_debugfs_stream_s(void *data, u64 val)
+{
+	struct cpp_device *cpp_dev = data;
+	CPP_DBG("CPP processing frame E\n");
+	while (1) {
+		mutex_lock(&cpp_dev->mutex);
+		msm_cpp_notify_frame_done(cpp_dev);
+		msm_cpp_send_frame_to_hardware(cpp_dev);
+		mutex_unlock(&cpp_dev->mutex);
+		msleep(20);
+	}
+	CPP_DBG("CPP processing frame X\n");
+	return 0;
+}
+
+static int msm_cpp_debugfs_load_fw(void *data, u64 val)
+{
+	const struct firmware *fw = NULL;
+	struct cpp_device *cpp_dev = data;
+	int rc = 0;
+	CPP_DBG("%s\n", __func__);
+	rc = request_firmware(&fw, "FIRMWARE.bin", &cpp_dev->pdev->dev);
+	if (rc) {
+		pr_err("request_fw failed\n");
+	} else {
+		CPP_DBG("request ok\n");
+		release_firmware(fw);
+	}
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cpp_debugfs_stream, NULL,
+			msm_cpp_debugfs_stream_s, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(cpp_debugfs_fw, NULL,
+			msm_cpp_debugfs_load_fw, "%llu\n");
+
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev)
+{
+	struct dentry *debugfs_base, *debugfs_test;
+	debugfs_base = debugfs_create_dir("msm_camera", NULL);
+	if (!debugfs_base)
+		return -ENOMEM;
+
+	debugfs_test = debugfs_create_file("test", S_IRUGO | S_IWUSR,
+		debugfs_base, (void *)cpp_dev, &cpp_debugfs_stream);
+	if (!debugfs_test) {
+		debugfs_remove(debugfs_base);
+		return -ENOMEM;
+	}
+
+	if (!debugfs_create_file("fw", S_IRUGO | S_IWUSR, debugfs_base,
+			(void *)cpp_dev, &cpp_debugfs_fw)) {
+		debugfs_remove(debugfs_test);
+		debugfs_remove(debugfs_base);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+module_init(msm_cpp_init_module);
+module_exit(msm_cpp_exit_module);
+MODULE_DESCRIPTION("MSM CPP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
new file mode 100644
index 0000000..8deff72
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -0,0 +1,155 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_CPP_H__
+#define __MSM_CPP_H__
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include "msm_sd.h"
+
+#define MAX_ACTIVE_CPP_INSTANCE 8
+#define MAX_CPP_PROCESSING_FRAME 2
+#define MAX_CPP_V4l2_EVENTS 30
+
+#define MSM_CPP_MICRO_BASE          0x4000
+#define MSM_CPP_MICRO_HW_VERSION    0x0000
+#define MSM_CPP_MICRO_IRQGEN_STAT   0x0004
+#define MSM_CPP_MICRO_IRQGEN_CLR    0x0008
+#define MSM_CPP_MICRO_IRQGEN_MASK   0x000C
+#define MSM_CPP_MICRO_FIFO_TX_DATA  0x0010
+#define MSM_CPP_MICRO_FIFO_TX_STAT  0x0014
+#define MSM_CPP_MICRO_FIFO_RX_DATA  0x0018
+#define MSM_CPP_MICRO_FIFO_RX_STAT  0x001C
+#define MSM_CPP_MICRO_BOOT_START    0x0020
+#define MSM_CPP_MICRO_BOOT_LDORG    0x0024
+#define MSM_CPP_MICRO_CLKEN_CTL     0x0030
+
+#define MSM_CPP_CMD_GET_BOOTLOADER_VER	0x1
+#define MSM_CPP_CMD_FW_LOAD				0x2
+#define MSM_CPP_CMD_EXEC_JUMP			0x3
+#define MSM_CPP_CMD_RESET_HW			0x5
+#define MSM_CPP_CMD_PROCESS_FRAME		0x6
+#define MSM_CPP_CMD_FLUSH_STREAM		0x7
+#define MSM_CPP_CMD_CFG_MEM_PARAM		0x8
+#define MSM_CPP_CMD_ERROR_REQUEST		0x9
+#define MSM_CPP_CMD_GET_STATUS			0xA
+#define MSM_CPP_CMD_GET_FW_VER			0xB
+
+#define MSM_CPP_MSG_ID_CMD          0x3E646D63
+#define MSM_CPP_MSG_ID_OK           0x0A0A4B4F
+#define MSM_CPP_MSG_ID_TRAILER      0xABCDEFAA
+
+#define MSM_CPP_MSG_ID_JUMP_ACK     0x00000001
+#define MSM_CPP_MSG_ID_FRAME_ACK    0x00000002
+#define MSM_CPP_MSG_ID_FRAME_NACK   0x00000003
+#define MSM_CPP_MSG_ID_FLUSH_ACK    0x00000004
+#define MSM_CPP_MSG_ID_FLUSH_NACK   0x00000005
+#define MSM_CPP_MSG_ID_CFG_MEM_ACK  0x00000006
+#define MSM_CPP_MSG_ID_CFG_MEM_INV  0x00000007
+#define MSM_CPP_MSG_ID_ERROR_STATUS 0x00000008
+#define MSM_CPP_MSG_ID_INVALID_CMD  0x00000009
+#define MSM_CPP_MSG_ID_GEN_STATUS   0x0000000A
+#define MSM_CPP_MSG_ID_FLUSHED      0x0000000B
+#define MSM_CPP_MSG_ID_FW_VER       0x0000000C
+
+#define MSM_CPP_JUMP_ADDRESS		0x20
+#define MSM_CPP_START_ADDRESS		0x0
+#define MSM_CPP_END_ADDRESS			0x3F00
+
+#define MSM_CPP_POLL_RETRIES		20
+
+struct cpp_subscribe_info {
+	struct v4l2_fh *vfh;
+	uint32_t active;
+};
+
+enum cpp_state {
+	CPP_STATE_BOOT,
+	CPP_STATE_IDLE,
+	CPP_STATE_ACTIVE,
+	CPP_STATE_OFF,
+};
+
+enum msm_queue {
+	MSM_CAM_Q_CTRL,     /* control command or control command status */
+	MSM_CAM_Q_VFE_EVT,  /* adsp event */
+	MSM_CAM_Q_VFE_MSG,  /* adsp message */
+	MSM_CAM_Q_V4L2_REQ, /* v4l2 request */
+	MSM_CAM_Q_VPE_MSG,  /* vpe message */
+	MSM_CAM_Q_PP_MSG,  /* pp message */
+};
+
+struct msm_queue_cmd {
+	struct list_head list_config;
+	struct list_head list_control;
+	struct list_head list_frame;
+	struct list_head list_pict;
+	struct list_head list_vpe_frame;
+	struct list_head list_eventdata;
+	enum msm_queue type;
+	void *command;
+	atomic_t on_heap;
+	struct timespec ts;
+	uint32_t error_code;
+	uint32_t trans_code;
+};
+
+struct msm_device_queue {
+	struct list_head list;
+	spinlock_t lock;
+	wait_queue_head_t wait;
+	int max;
+	int len;
+	const char *name;
+};
+
+struct cpp_device {
+	struct platform_device *pdev;
+	struct msm_sd_subdev msm_sd;
+	struct v4l2_subdev subdev;
+	struct resource *mem;
+	struct resource *irq;
+	struct resource *io;
+	struct resource	*vbif_mem;
+	struct resource *vbif_io;
+	void __iomem *vbif_base;
+	void __iomem *base;
+	struct clk **cpp_clk;
+	struct regulator *fs_cpp;
+	struct mutex mutex;
+	enum cpp_state state;
+
+	int domain_num;
+	struct iommu_domain *domain;
+	struct device *iommu_ctx;
+	struct ion_client *client;
+	struct kref refcount;
+
+	struct cpp_subscribe_info cpp_subscribe_list[MAX_ACTIVE_CPP_INSTANCE];
+	uint32_t cpp_open_cnt;
+
+	struct msm_device_queue eventData_q; /* V4L2 Event Payload Queue */
+
+	/* Offline Frame Queue process when realtime queue is empty */
+	struct msm_device_queue offline_q;
+	/* Realtime Frame Queue process with highest priority */
+	struct msm_device_queue realtime_q;
+	/* Processing Queue
+	 * store frame info for frames sent to microcontroller
+	 */
+	struct msm_device_queue processing_q;
+};
+#endif /* __MSM_CPP_H__ */
diff --git a/include/media/Kbuild b/include/media/Kbuild
index 060047f..16786a9 100644
--- a/include/media/Kbuild
+++ b/include/media/Kbuild
@@ -14,3 +14,4 @@
 header-y += msmb_isp.h
 header-y += msmb_ispif.h
 header-y += msmb_generic_buf_mgr.h
+header-y += msmb_pproc.h
diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h
new file mode 100644
index 0000000..939c4e3
--- /dev/null
+++ b/include/media/msmb_pproc.h
@@ -0,0 +1,114 @@
+#ifndef __MSMB_PPROC_H
+#define __MSMB_PPROC_H
+
+#ifdef MSM_CAMERA_BIONIC
+#include <sys/types.h>
+#endif
+#include <linux/videodev2.h>
+#include <linux/types.h>
+
+/* Should be same as VIDEO_MAX_PLANES in videodev2.h */
+#define MAX_PLANES VIDEO_MAX_PLANES
+
+#define MAX_NUM_CPP_STRIPS 8
+
+enum msm_cpp_frame_type {
+	MSM_CPP_OFFLINE_FRAME,
+	MSM_CPP_REALTIME_FRAME,
+};
+
+struct msm_cpp_frame_strip_info {
+	int scale_v_en;
+	int scale_h_en;
+
+	int upscale_v_en;
+	int upscale_h_en;
+
+	int src_start_x;
+	int src_end_x;
+	int src_start_y;
+	int src_end_y;
+
+	/* Padding is required for upscaler because it does not
+	 * pad internally like other blocks, also needed for rotation
+	 * rotation expects all the blocks in the stripe to be the same size
+	 * Padding is done such that all the extra padded pixels
+	 * are on the right and bottom
+	 */
+	int pad_bottom;
+	int pad_top;
+	int pad_right;
+	int pad_left;
+
+	int v_init_phase;
+	int h_init_phase;
+	int h_phase_step;
+	int v_phase_step;
+
+	int prescale_crop_width_first_pixel;
+	int prescale_crop_width_last_pixel;
+	int prescale_crop_height_first_line;
+	int prescale_crop_height_last_line;
+
+	int postscale_crop_height_first_line;
+	int postscale_crop_height_last_line;
+	int postscale_crop_width_first_pixel;
+	int postscale_crop_width_last_pixel;
+
+	int dst_start_x;
+	int dst_end_x;
+	int dst_start_y;
+	int dst_end_y;
+
+	int bytes_per_pixel;
+	unsigned int source_address;
+	unsigned int destination_address;
+	unsigned int src_stride;
+	unsigned int dst_stride;
+	int rotate_270;
+	int horizontal_flip;
+	int vertical_flip;
+	int scale_output_width;
+	int scale_output_height;
+	int prescale_crop_en;
+	int postscale_crop_en;
+};
+
+struct msm_cpp_frame_info_t {
+	int32_t frame_id;
+	uint32_t inst_id;
+	uint32_t client_id;
+	enum msm_cpp_frame_type frame_type;
+	uint32_t num_strips;
+	struct msm_cpp_frame_strip_info *strip_info;
+	uint32_t msg_len;
+	uint32_t *cpp_cmd_msg;
+	int src_fd;
+	int dst_fd;
+};
+
+struct msm_ver_num_info {
+	uint32_t main;
+	uint32_t minor;
+	uint32_t rev;
+};
+
+#define VIDIOC_MSM_CPP_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_GET_EVENTPAYLOAD \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct msm_camera_v4l2_ioctl_t)
+
+#define VIDIOC_MSM_CPP_GET_INST_INFO \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct msm_camera_v4l2_ioctl_t)
+
+#define V4L2_EVENT_CPP_FRAME_DONE  (V4L2_EVENT_PRIVATE_START + 0)
+
+struct msm_camera_v4l2_ioctl_t {
+	uint32_t id;
+	uint32_t len;
+	uint32_t trans_code;
+	void __user *ioctl_ptr;
+};
+
+#endif /* __MSMB_PPROC_H */