msm: camera: CPP driver Framework

Initial commit for Camera Post Processor Driver.
The driver contains frame queuing and process logic
using v4l2 framework.

Change-Id: I283c11c3de3bd121b9c3ea5af1446c13e188d329
Signed-off-by: Kevin Chan <ktchan@codeaurora.org>
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index 5ffc133..9c791e4 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -271,6 +271,15 @@
 	cores and composite them into a single
 	interrupt to the MSM.
 
+config MSM_CPP
+        bool "Qualcomm MSM Camera Post Processing Engine support"
+        depends on MSM_CAMERA && MSM_CAMERA_V4L2
+        ---help---
+          Enable support for Camera Post-processing Engine
+          The Post processing engine is capable of scaling
+          and cropping image. The driver support V4L2 subdev
+          APIs.
+
 config QUP_EXCLUSIVE_TO_CAMERA
 	bool "QUP exclusive to camera"
 	depends on MSM_CAMERA
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index 431da2e..63120da 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -13,6 +13,7 @@
   EXTRA_CFLAGS += -Idrivers/media/video/msm/server
   obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o msm_mctl_buf.o msm_mctl_pp.o
   obj-$(CONFIG_MSM_CAMERA) += server/ eeprom/ sensors/ actuators/ csi/
+  obj-$(CONFIG_MSM_CPP) += cpp/
   obj-$(CONFIG_MSM_CAMERA) += msm_gesture.o
   obj-$(CONFIG_MSM_CAM_IRQ_ROUTER) += msm_camirq_router.o
 else
diff --git a/drivers/media/video/msm/cpp/Makefile b/drivers/media/video/msm/cpp/Makefile
new file mode 100644
index 0000000..b4f1fdf
--- /dev/null
+++ b/drivers/media/video/msm/cpp/Makefile
@@ -0,0 +1,5 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ccflags-y += -Idrivers/media/video/msm
+ccflags-y += -Idrivers/media/video/msm/io
+obj-$(CONFIG_MSM_CPP) += msm_cpp.o
+
diff --git a/drivers/media/video/msm/cpp/msm_cpp.c b/drivers/media/video/msm/cpp/msm_cpp.c
new file mode 100644
index 0000000..e569388
--- /dev/null
+++ b/drivers/media/video/msm/cpp/msm_cpp.c
@@ -0,0 +1,412 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include <mach/vreg.h>
+#include <media/msm_isp.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+
+#include "msm_cpp.h"
+#include "msm.h"
+
+#define CONFIG_MSM_CPP_DBG 0
+
+#if CONFIG_MSM_CPP_DBG
+#define CPP_DBG(fmt, args...) pr_info(fmt, ##args)
+#else
+#define CPP_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+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("%s\n", __func__);
+
+	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++;
+	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--;
+	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;
+
+		event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+		if (!event_qcmd) {
+			pr_err("%s Insufficient memory. return", __func__);
+			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->subdev.devnode, &v4l2_evt);
+	}
+	return 0;
+}
+
+static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev)
+{
+	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("%s: All frames queued\n", __func__);
+				break;
+			}
+			frame_qcmd = msm_dequeue(queue, list_frame);
+			/*TBD Code to actually sending to harware*/
+			process_frame = frame_qcmd->command;
+
+			msm_enqueue(&cpp_dev->processing_q,
+						&frame_qcmd->list_frame);
+		}
+	}
+	return 0;
+}
+
+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;
+
+	CPP_DBG("%s: %d\n", __func__, __LINE__);
+	mutex_lock(&cpp_dev->mutex);
+	CPP_DBG("%s cmd: %d\n", __func__, cmd);
+	switch (cmd) {
+	case VIDIOC_MSM_CPP_CFG: {
+		struct msm_queue_cmd *frame_qcmd;
+		struct msm_cpp_frame_info_t *new_frame =
+			kzalloc(sizeof(struct msm_cpp_frame_info_t),
+					GFP_KERNEL);
+		if (!new_frame) {
+			pr_err("%s Insufficient memory. return", __func__);
+			mutex_unlock(&cpp_dev->mutex);
+			return -ENOMEM;
+		}
+
+		COPY_FROM_USER(rc, new_frame,
+			       (void __user *)ioctl_ptr->ioctl_ptr,
+			       sizeof(struct msm_cpp_frame_info_t));
+		if (rc) {
+			ERR_COPY_FROM_USER();
+			kfree(new_frame);
+			mutex_unlock(&cpp_dev->mutex);
+			return -EINVAL;
+		}
+
+		frame_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
+		if (!frame_qcmd) {
+			pr_err("%s Insufficient memory. return", __func__);
+			kfree(new_frame);
+			mutex_unlock(&cpp_dev->mutex);
+			return -ENOMEM;
+		}
+
+		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("%s: Invalid frame type\n", __func__);
+			kfree(new_frame);
+			kfree(frame_qcmd);
+			mutex_unlock(&cpp_dev->mutex);
+			return -EINVAL;
+		}
+		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);
+		kfree(event_qcmd);
+		break;
+	}
+	}
+	mutex_unlock(&cpp_dev->mutex);
+	CPP_DBG("%s: %d\n", __func__, __LINE__);
+	return 0;
+}
+
+int msm_cpp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+	struct v4l2_event_subscription *sub)
+{
+	CPP_DBG("%s\n", __func__);
+	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("%s\n", __func__);
+	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 __devinit cpp_probe(struct platform_device *pdev)
+{
+	struct cpp_device *cpp_dev;
+	struct msm_cam_subdev_info sd_info;
+	int rc = 0;
+	CDBG("%s: device id = %d\n", __func__, pdev->id);
+	cpp_dev = kzalloc(sizeof(struct cpp_device), GFP_KERNEL);
+	if (!cpp_dev) {
+		pr_err("%s: no enough memory\n", __func__);
+		return -ENOMEM;
+	}
+	v4l2_subdev_init(&cpp_dev->subdev, &msm_cpp_subdev_ops);
+	cpp_dev->subdev.internal_ops = &msm_cpp_internal_ops;
+	snprintf(cpp_dev->subdev.name, ARRAY_SIZE(cpp_dev->subdev.name),
+		 "cpp");
+	cpp_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	cpp_dev->subdev.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+	v4l2_set_subdevdata(&cpp_dev->subdev, cpp_dev);
+	platform_set_drvdata(pdev, &cpp_dev->subdev);
+	mutex_init(&cpp_dev->mutex);
+
+	cpp_dev->pdev = pdev;
+
+	media_entity_init(&cpp_dev->subdev.entity, 0, NULL, 0);
+	cpp_dev->subdev.entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+	cpp_dev->subdev.entity.group_id = CPP_DEV;
+	cpp_dev->subdev.entity.name = pdev->name;
+	sd_info.sdev_type = CPP_DEV;
+	sd_info.sd_index = pdev->id;
+	msm_cam_register_subdev_node(&cpp_dev->subdev, &sd_info);
+	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->subdev.devnode->fops = &msm_cpp_v4l2_subdev_fops;
+	cpp_dev->subdev.entity.revision = cpp_dev->subdev.devnode->num;
+	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;
+}
+
+static struct platform_driver cpp_driver = {
+	.probe = cpp_probe,
+	.driver = {
+		.name = MSM_CPP_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+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;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cpp_debugfs_stream, NULL,
+			msm_cpp_debugfs_stream_s, "%llu\n");
+
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev)
+{
+	struct dentry *debugfs_base;
+	debugfs_base = debugfs_create_dir("msm_camera", NULL);
+	if (!debugfs_base)
+		return -ENOMEM;
+
+	if (!debugfs_create_file("test", S_IRUGO | S_IWUSR, debugfs_base,
+			(void *)cpp_dev, &cpp_debugfs_stream))
+		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/video/msm/cpp/msm_cpp.h b/drivers/media/video/msm/cpp/msm_cpp.h
new file mode 100644
index 0000000..8c10cac
--- /dev/null
+++ b/drivers/media/video/msm/cpp/msm_cpp.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <media/v4l2-subdev.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
+
+struct cpp_subscribe_info {
+	struct v4l2_fh *vfh;
+	uint32_t active;
+};
+
+struct cpp_device {
+	struct platform_device *pdev;
+	struct v4l2_subdev subdev;
+	struct resource *mem;
+	struct resource *irq;
+	struct resource *io;
+	void __iomem *base;
+	struct clk *cpp_clk[2];
+	struct mutex mutex;
+
+	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;
+};
+
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index d0322d1..30f3c35 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -59,6 +59,7 @@
 #define MSM_MERCURY_DRV_NAME "msm_mercury"
 #define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux"
 #define MSM_IRQ_ROUTER_DRV_NAME "msm_cam_irq_router"
+#define MSM_CPP_DRV_NAME "msm_cpp"
 
 #define MAX_NUM_CSIPHY_DEV 3
 #define MAX_NUM_CSID_DEV 4
@@ -68,6 +69,7 @@
 #define MAX_NUM_AXI_DEV 2
 #define MAX_NUM_VPE_DEV 1
 #define MAX_NUM_JPEG_DEV 3
+#define MAX_NUM_CPP_DEV 1
 
 enum msm_cam_subdev_type {
 	CSIPHY_DEV,
@@ -82,6 +84,7 @@
 	EEPROM_DEV,
 	GESTURE_DEV,
 	IRQ_ROUTER_DEV,
+	CPP_DEV,
 };
 
 /* msm queue management APIs*/
@@ -261,6 +264,7 @@
 	struct v4l2_subdev *vpe_sdev; /* vpe sub device */
 	struct v4l2_subdev *axi_sdev; /* axi sub device */
 	struct v4l2_subdev *eeprom_sdev; /* eeprom sub device */
+	struct v4l2_subdev *cpp_sdev;/*cpp sub device*/
 
 	struct msm_isp_ops *isp_sdev;    /* isp sub device : camif/VFE */
 	struct msm_cam_config_dev *config_device;
@@ -540,6 +544,7 @@
 	struct v4l2_subdev *axi_device[MAX_NUM_AXI_DEV];
 	struct v4l2_subdev *vpe_device[MAX_NUM_VPE_DEV];
 	struct v4l2_subdev *gesture_device;
+	struct v4l2_subdev *cpp_device[MAX_NUM_CPP_DEV];
 	struct v4l2_subdev *irqr_device;
 
 	spinlock_t  intr_table_lock;
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
index f2bb65f..51b5429 100644
--- a/drivers/media/video/msm/server/msm_cam_server.c
+++ b/drivers/media/video/msm/server/msm_cam_server.c
@@ -1894,8 +1894,17 @@
 	case GESTURE_DEV:
 		g_server_dev.gesture_device = sd;
 		break;
+
 	case IRQ_ROUTER_DEV:
 		g_server_dev.irqr_device = sd;
+
+	case CPP_DEV:
+		if (index >= MAX_NUM_CPP_DEV) {
+			pr_err("%s Invalid CPP idx %d", __func__, index);
+			err = -EINVAL;
+			break;
+		}
+		g_server_dev.cpp_device[index] = sd;
 		break;
 	default:
 		break;
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 3308243..da450fc 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -1659,4 +1659,86 @@
 #define MSM_IRQROUTER_CFG_COMPIRQ \
 	_IOWR('V', BASE_VIDIOC_PRIVATE, void __user *)
 
+#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;
+};
+
+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;
+};
+
+#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)
+
 #endif /* __LINUX_MSM_CAMERA_H */