diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index f663490e949d..2632b0ee5d2d 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -3,7 +3,8 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 exynosdrm-y := exynos_drm_drv.o exynos_drm_crtc.o exynos_drm_fb.o \
-		exynos_drm_gem.o exynos_drm_core.o exynos_drm_plane.o
+		exynos_drm_gem.o exynos_drm_core.o exynos_drm_plane.o \
+		exynos_drm_pp.o
 
 exynosdrm-$(CONFIG_DRM_FBDEV_EMULATION) += exynos_drm_fbdev.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 035d02ecffcd..386a9d325c5a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -27,6 +27,7 @@
 #include "exynos_drm_fb.h"
 #include "exynos_drm_gem.h"
 #include "exynos_drm_plane.h"
+#include "exynos_drm_pp.h"
 #include "exynos_drm_vidi.h"
 #include "exynos_drm_g2d.h"
 #include "exynos_drm_ipp.h"
@@ -53,6 +54,7 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
 
 	init_waitqueue_head(&private->wait);
 	spin_lock_init(&private->lock);
+	INIT_LIST_HEAD(&private->pp_list);
 
 	dev_set_drvdata(dev->dev, dev);
 	dev->dev_private = (void *)private;
@@ -241,6 +243,12 @@ static void exynos_drm_lastclose(struct drm_device *dev)
 			DRM_AUTH | DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, exynos_drm_ipp_cmd_ctrl,
 			DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(EXYNOS_PP_GET_RESOURCES, exynos_drm_pp_get_res,
+			DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(EXYNOS_PP_GET, exynos_drm_pp_get,
+			DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(EXYNOS_PP_COMMIT, exynos_drm_pp_commit,
+			DRM_AUTH | DRM_RENDER_ALLOW),
 };
 
 static const struct file_operations exynos_drm_driver_fops = {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index cf6e08cb35a7..bdc049e8c94f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -220,6 +220,10 @@ struct exynos_drm_private {
 	u32			pending;
 	spinlock_t		lock;
 	wait_queue_head_t	wait;
+
+	/* for pp api */
+	int num_pp;
+	struct list_head pp_list;
 };
 
 static inline struct exynos_drm_crtc *
diff --git a/drivers/gpu/drm/exynos/exynos_drm_pp.c b/drivers/gpu/drm/exynos/exynos_drm_pp.c
new file mode 100644
index 000000000000..e94231dbcb9c
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_pp.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2017 Samsung Electronics Co.Ltd
+ * Authors:
+ *	Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * Exynos DRM Picture Processor (PP) related functions
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#include <drm/drmP.h>
+#include <drm/drm_mode.h>
+#include <uapi/drm/exynos_drm.h>
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_pp.h"
+
+struct drm_pending_exynos_pp_event {
+	struct drm_pending_event base;
+	struct drm_exynos_pp_event event;
+};
+
+static const uint32_t exynos_drm_standard_parameters[] = {
+	DRM_EXYNOS_PP_PARAM_SRC_FB,
+	DRM_EXYNOS_PP_PARAM_SRC_X,
+	DRM_EXYNOS_PP_PARAM_SRC_Y,
+	DRM_EXYNOS_PP_PARAM_SRC_WIDTH,
+	DRM_EXYNOS_PP_PARAM_SRC_HEIGHT,
+	DRM_EXYNOS_PP_PARAM_DST_FB,
+	DRM_EXYNOS_PP_PARAM_DST_X,
+	DRM_EXYNOS_PP_PARAM_DST_Y,
+	DRM_EXYNOS_PP_PARAM_DST_WIDTH,
+	DRM_EXYNOS_PP_PARAM_DST_HEIGHT,
+	DRM_EXYNOS_PP_PARAM_ROTATION,
+};
+
+/**
+ * exynos_drm_pp_register - Register a new picture processor hardware module
+ * @dev: DRM device
+ * @pp: pp module to init
+ * @funcs: callbacks for the new pp object
+ * @caps: bitmask of pp capabilities (%DRM_EXYNOS_PP_CAP_*)
+ * @src_fmts: array of supported source fb formats (%DRM_FORMAT_*)
+ * @src_fmt_count: number of elements in @src_fmts
+ * @dst_fmts: array of supported destination fb formats (%DRM_FORMAT_*)
+ * @dst_fmt_count: number of elements in @dst_fmts
+ * @rotation: a set of supported rotation transformations
+ * @name: name (for debugging purposes)
+ *
+ * Initializes a pp module.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int exynos_drm_pp_register(struct drm_device *dev, struct exynos_drm_pp *pp,
+		    const struct exynos_drm_pp_funcs *funcs, unsigned int caps,
+		    const uint32_t *src_fmts, unsigned int src_fmt_count,
+		    const uint32_t *dst_fmts, unsigned int dst_fmt_count,
+		    unsigned int rotation, const char *name)
+{
+	struct exynos_drm_private *priv = dev->dev_private;
+
+	spin_lock_init(&pp->lock);
+	INIT_LIST_HEAD(&pp->todo_list);
+	init_waitqueue_head(&pp->done_wq);
+	pp->dev = dev;
+	pp->funcs = funcs;
+	pp->capabilities = caps;
+	pp->src_format_types = src_fmts;
+	pp->src_format_count = src_fmt_count;
+	pp->dst_format_types = dst_fmts;
+	pp->dst_format_count = dst_fmt_count;
+	pp->rotation = rotation;
+	pp->parameters = exynos_drm_standard_parameters;
+	pp->parameters_count = ARRAY_SIZE(exynos_drm_standard_parameters);
+	pp->name = name;
+
+	list_add_tail(&pp->head, &priv->pp_list);
+	pp->id = priv->num_pp++;
+
+	DRM_DEBUG_DRIVER("Registered pp %d\n", pp->id);
+
+	return 0;
+}
+
+/**
+ * exynos_drm_pp_unregister - Unregister the picture processor module
+ * @dev: DRM device
+ * @pp: pp module
+ */
+void exynos_drm_pp_unregister(struct drm_device *dev, struct exynos_drm_pp *pp)
+{
+	BUG_ON(pp->task);
+	BUG_ON(!list_empty(&pp->todo_list));
+	list_del(&pp->head);
+}
+
+/**
+ * exynos_drm_pp_get_res - enumerate all pp modules
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Construct a list of pp ids to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int exynos_drm_pp_get_res(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	struct exynos_drm_private *priv = dev->dev_private;
+	struct drm_exynos_pp_get_res *resp = data;
+	struct exynos_drm_pp *pp;
+	uint32_t __user *pp_ptr;
+	unsigned int count = priv->num_pp, copied = 0;
+
+	/*
+	 * This ioctl is called twice, once to determine how much space is
+	 * needed, and the 2nd time to fill it.
+	 */
+	if (count && resp->count_pps >= count) {
+		pp_ptr = (uint32_t __user *)
+					(unsigned long)resp->pp_id_ptr;
+
+		list_for_each_entry(pp, &priv->pp_list, head) {
+			if (put_user(pp->id, pp_ptr + copied))
+				return -EFAULT;
+			copied++;
+		}
+	}
+	resp->count_pps = count;
+
+	return 0;
+}
+
+static inline struct exynos_drm_pp *exynos_drm_pp_find(struct drm_device *dev,
+						       uint32_t id)
+{
+	struct exynos_drm_private *priv = dev->dev_private;
+	struct exynos_drm_pp *pp;
+
+	list_for_each_entry(pp, &priv->pp_list, head) {
+		if (pp->id == id)
+			return pp;
+	}
+	return NULL;
+}
+
+/**
+ * exynos_drm_pp_get - get picture processor module parameters
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Construct a pp configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int exynos_drm_pp_get(struct drm_device *dev, void *data,
+		      struct drm_file *file_priv)
+{
+	struct drm_exynos_pp_get *resp = data;
+	struct exynos_drm_pp *pp;
+	uint32_t __user *format_ptr;
+
+	pp = exynos_drm_pp_find(dev, resp->pp_id);
+	if (!pp)
+		return -ENOENT;
+
+	resp->pp_id = pp->id;
+	resp->capabilities = pp->capabilities;
+
+	/*
+	 * This ioctl is called twice, once to determine how much space is
+	 * needed, and the 2nd time to fill it.
+	 */
+	if (pp->src_format_count &&
+	    (resp->src_format_count >= pp->src_format_count)) {
+		format_ptr = (uint32_t __user *)
+				(unsigned long)resp->src_format_type_ptr;
+		if (copy_to_user(format_ptr, pp->src_format_types,
+				 sizeof(uint32_t) * pp->src_format_count))
+			return -EFAULT;
+	}
+	if (pp->dst_format_count &&
+	    (resp->dst_format_count >= pp->dst_format_count)) {
+		format_ptr = (uint32_t __user *)
+				(unsigned long)resp->dst_format_type_ptr;
+		if (copy_to_user(format_ptr, pp->dst_format_types,
+				 sizeof(uint32_t) * pp->dst_format_count))
+			return -EFAULT;
+	}
+	if (pp->parameters_count &&
+	    (resp->params_count >= pp->parameters_count)) {
+		format_ptr = (uint32_t __user *)
+				(unsigned long)resp->params_ptr;
+		if (copy_to_user(format_ptr, pp->parameters,
+				 sizeof(uint32_t) * pp->parameters_count))
+			return -EFAULT;
+	}
+	resp->src_format_count = pp->src_format_count;
+	resp->dst_format_count = pp->dst_format_count;
+	resp->params_count = pp->parameters_count;
+
+	return 0;
+}
+
+static inline struct exynos_drm_pp_task *
+	exynos_drm_pp_task_alloc(struct exynos_drm_pp *pp)
+{
+	struct exynos_drm_pp_task *task;
+
+	task = kzalloc(sizeof(*task), GFP_KERNEL);
+	if (!task)
+		return NULL;
+
+	task->dev = pp->dev;
+	task->pp = pp;
+	task->src_w = task->dst_w = UINT_MAX;
+	task->src_h = task->dst_h = UINT_MAX;
+	task->rotation = DRM_ROTATE_0;
+
+	DRM_DEBUG_DRIVER("Allocated task %pK\n", task);
+
+	return task;
+}
+
+static void exynos_drm_pp_task_free(struct exynos_drm_pp *pp,
+				 struct exynos_drm_pp_task *task)
+{
+	DRM_DEBUG_DRIVER("Freeing task %pK\n", task);
+
+	task->pp = NULL;
+
+	if (task->src_fb) {
+		drm_framebuffer_unreference(task->src_fb);
+		task->src_fb = NULL;
+	}
+	if (task->dst_fb) {
+		drm_framebuffer_unreference(task->dst_fb);
+		task->dst_fb = NULL;
+	}
+	if (task->event) {
+		drm_event_cancel_free(pp->dev, &task->event->base);
+		task->event = NULL;
+	}
+	kfree(task);
+}
+
+static int exynos_drm_pp_task_set_param(struct exynos_drm_pp_task *task,
+		unsigned int param, unsigned int value)
+{
+	struct drm_device *dev = task->dev;
+	struct drm_framebuffer *fb;
+	int ret = 0;
+
+	if (param == DRM_EXYNOS_PP_PARAM_SRC_FB) {
+		fb = drm_framebuffer_lookup(dev, value);
+		if (task->src_fb)
+			drm_framebuffer_unreference(task->src_fb);
+		task->src_fb = fb;
+	} else if (param == DRM_EXYNOS_PP_PARAM_SRC_X) {
+		task->src_x = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_SRC_Y) {
+		task->src_y = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_SRC_WIDTH) {
+		task->src_w = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_SRC_HEIGHT) {
+		task->src_h = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_DST_FB) {
+		fb = drm_framebuffer_lookup(dev, value);
+		if (task->dst_fb)
+			drm_framebuffer_unreference(task->dst_fb);
+		task->dst_fb = fb;
+	} else if (param == DRM_EXYNOS_PP_PARAM_DST_X) {
+		task->dst_x = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_DST_Y) {
+		task->dst_y = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_DST_WIDTH) {
+		task->dst_w = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_DST_HEIGHT) {
+		task->dst_h = value;
+	} else if (param == DRM_EXYNOS_PP_PARAM_ROTATION) {
+		task->rotation = value;
+	} else {
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct drm_pending_exynos_pp_event *exynos_drm_pp_event_create(
+			struct drm_device *dev, struct drm_file *file_priv,
+			uint64_t user_data)
+{
+	struct drm_pending_exynos_pp_event *e = NULL;
+	int ret;
+
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+	if (!e)
+		return NULL;
+
+	e->event.base.type = DRM_EXYNOS_PP_EVENT;
+	e->event.base.length = sizeof(e->event);
+	e->event.user_data = user_data;
+
+	if (file_priv) {
+		ret = drm_event_reserve_init(dev, file_priv, &e->base,
+					     &e->event.base);
+		if (ret) {
+			kfree(e);
+			return NULL;
+		}
+	}
+
+	return e;
+}
+
+static void exynos_drm_pp_event_send(struct drm_device *dev,
+				  struct exynos_drm_pp *pp,
+				  struct drm_pending_exynos_pp_event *e)
+{
+	struct timeval now = ktime_to_timeval(ktime_get());
+
+	e->event.tv_sec = now.tv_sec;
+	e->event.tv_usec = now.tv_usec;
+	e->event.sequence = atomic_inc_return(&pp->sequence);
+
+	drm_send_event(dev, &e->base);
+}
+
+static inline bool drm_fb_check_format(struct drm_framebuffer *fb,
+				const uint32_t *formats, int format_counts)
+{
+	while (format_counts--)
+		if (*formats++ == fb->format->format)
+			return true;
+	return false;
+}
+
+static int exynos_drm_pp_task_check(struct exynos_drm_pp_task *task)
+{
+	struct exynos_drm_pp *pp = task->pp;
+	int ret = 0;
+
+	DRM_DEBUG_DRIVER("checking %pK\n", task);
+
+	if (!task->src_fb || !task->dst_fb)
+		return -EINVAL;
+
+	if (!drm_fb_check_format(task->src_fb, pp->src_format_types,
+				 pp->src_format_count))
+		return -EINVAL;
+
+	if (!drm_fb_check_format(task->dst_fb, pp->dst_format_types,
+				 pp->dst_format_count))
+		return -EINVAL;
+
+	if (task->src_w == UINT_MAX)
+		task->src_w = task->src_fb->width << 16;
+	if (task->src_h == UINT_MAX)
+		task->src_h = task->src_fb->height << 16;
+	if (task->dst_w == UINT_MAX)
+		task->dst_w = task->dst_fb->width << 16;
+	if (task->dst_h == UINT_MAX)
+		task->dst_h = task->dst_fb->height << 16;
+
+	if (task->src_x + task->src_w > (task->src_fb->width << 16) ||
+	    task->src_y + task->src_h > (task->src_fb->height << 16) ||
+	    task->dst_x + task->dst_w > (task->dst_fb->width << 16) ||
+	    task->dst_y + task->dst_h > (task->dst_fb->height << 16))
+		return -EINVAL;
+
+	if (!(pp->capabilities & DRM_EXYNOS_PP_CAP_CROP) &&
+	    (task->src_x || task->src_y || task->dst_x || task->dst_y))
+		return -EINVAL;
+
+	if (!(pp->capabilities & DRM_EXYNOS_PP_CAP_ROTATE) &&
+	    task->rotation != DRM_ROTATE_0)
+		return -EINVAL;
+
+	if (!(pp->capabilities & DRM_EXYNOS_PP_CAP_SCALE) &&
+	    (!drm_rotation_90_or_270(task->rotation) &&
+	     (task->src_w != task->dst_w || task->src_h != task->dst_h)) &&
+	    (drm_rotation_90_or_270(task->rotation) &&
+	     (task->src_w != task->dst_h || task->src_h != task->dst_w)))
+		return -EINVAL;
+
+	if (!(pp->capabilities & DRM_EXYNOS_PP_CAP_CONVERT) &&
+	    task->src_fb->format->format != task->dst_fb->format->format)
+		return -EINVAL;
+
+	if (pp->funcs->check)
+		ret = pp->funcs->check(pp, task);
+
+	return ret;
+}
+
+static int exynos_drm_pp_task_cleanup(struct exynos_drm_pp_task *task)
+{
+	int ret = task->ret;
+
+	if (ret == 0 && task->event) {
+		exynos_drm_pp_event_send(task->dev, task->pp, task->event);
+		/* ensure event won't be canceled on task free */
+		task->event = NULL;
+	}
+
+	exynos_drm_pp_task_free(task->pp, task);
+	return ret;
+}
+
+static void exynos_drm_pp_cleanup_work(struct work_struct *work)
+{
+	struct exynos_drm_pp_task *task = container_of(work,
+				struct exynos_drm_pp_task, cleanup_work);
+
+	exynos_drm_pp_task_cleanup(task);
+}
+
+static void exynos_drm_pp_next_task(struct exynos_drm_pp *pp);
+
+void exynos_drm_pp_task_done(struct exynos_drm_pp_task *task, int ret)
+{
+	struct exynos_drm_pp *pp = task->pp;
+	unsigned long flags;
+
+	DRM_DEBUG_DRIVER("pp: %d, task %pK done\n", pp->id, task);
+
+	spin_lock_irqsave(&pp->lock, flags);
+	if (pp->task == task)
+		pp->task = NULL;
+	task->flags |= DRM_EXYNOS_PP_TASK_DONE;
+	task->ret = ret;
+	spin_unlock_irqrestore(&pp->lock, flags);
+
+	exynos_drm_pp_next_task(pp);
+	wake_up(&pp->done_wq);
+
+	if (task->flags & DRM_EXYNOS_PP_TASK_ASYNC) {
+		INIT_WORK(&task->cleanup_work, exynos_drm_pp_cleanup_work);
+		schedule_work(&task->cleanup_work);
+	}
+}
+
+static void exynos_drm_pp_next_task(struct exynos_drm_pp *pp)
+{
+	struct exynos_drm_pp_task *task;
+	unsigned long flags;
+	int ret;
+
+	DRM_DEBUG_DRIVER("pp: %d, try to run new task\n", pp->id);
+
+	spin_lock_irqsave(&pp->lock, flags);
+
+	if (pp->task || list_empty(&pp->todo_list)) {
+		spin_unlock_irqrestore(&pp->lock, flags);
+		return;
+	}
+
+	task = list_first_entry(&pp->todo_list, struct exynos_drm_pp_task,
+				head);
+	list_del_init(&task->head);
+	pp->task = task;
+
+	spin_unlock_irqrestore(&pp->lock, flags);
+
+	DRM_DEBUG_DRIVER("pp: %d, selected task %pK to run\n", pp->id, task);
+
+	ret = pp->funcs->commit(pp, task);
+	if (ret)
+		exynos_drm_pp_task_done(task, ret);
+}
+
+static void exynos_drm_pp_schedule_task(struct exynos_drm_pp *pp,
+				     struct exynos_drm_pp_task *task)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pp->lock, flags);
+	list_add(&task->head, &pp->todo_list);
+	spin_unlock_irqrestore(&pp->lock, flags);
+
+	exynos_drm_pp_next_task(pp);
+}
+
+static void exynos_drm_pp_task_abort(struct exynos_drm_pp *pp,
+				  struct exynos_drm_pp_task *task)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pp->lock, flags);
+	if (task->flags & DRM_EXYNOS_PP_TASK_DONE) {
+		/* already completed task */
+		exynos_drm_pp_task_cleanup(task);
+	} else if (pp->task != task) {
+		/* task has not been scheduled for execution yet */
+		list_del_init(&task->head);
+		exynos_drm_pp_task_cleanup(task);
+	} else {
+		/*
+		 * currently processed task, call abort() and perform
+		 * cleanup with async worker
+		 */
+		task->flags |= DRM_EXYNOS_PP_TASK_ASYNC;
+		if (pp->funcs->abort)
+			pp->funcs->abort(pp, task);
+	}
+	spin_unlock_irqrestore(&pp->lock, flags);
+}
+
+/**
+ * exynos_drm_pp_ioctl - perform operation on framebuffer processor object
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Construct a pp task from the set of properties provided from the user
+ * and try to schedule it to framebuffer processor hardware.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int exynos_drm_pp_commit(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	struct drm_exynos_pp_commit *arg = data;
+	uint32_t __user *param_ids_ptr =
+		(uint32_t __user *)(unsigned long)(arg->param_ids_ptr);
+	uint64_t __user *param_values_ptr =
+		(uint64_t __user *)(unsigned long)(arg->param_values_ptr);
+	struct exynos_drm_pp *pp;
+	struct exynos_drm_pp_task *task;
+	int ret = 0;
+	unsigned int i;
+
+	if (arg->flags & ~DRM_EXYNOS_PP_FLAGS)
+		return -EINVAL;
+
+	if (arg->reserved)
+		return -EINVAL;
+
+	/* can't test and expect an event at the same time */
+	if ((arg->flags & DRM_EXYNOS_PP_FLAG_TEST_ONLY) &&
+			(arg->flags & DRM_EXYNOS_PP_FLAG_EVENT))
+		return -EINVAL;
+
+	pp = exynos_drm_pp_find(dev, arg->id);
+	if (!pp)
+		return -ENOENT;
+
+	task = exynos_drm_pp_task_alloc(pp);
+	if (!task) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	for (i = 0; i < arg->params_count; i++) {
+		uint32_t param_id;
+		uint64_t param_value;
+
+		if (get_user(param_id, param_ids_ptr + i)) {
+			ret = -EFAULT;
+			goto free;
+		}
+
+		if (copy_from_user(&param_value, param_values_ptr + i,
+				   sizeof(param_value))) {
+			ret = -EFAULT;
+			goto free;
+		}
+
+		ret = exynos_drm_pp_task_set_param(task, param_id, param_value);
+		if (ret)
+			goto free;
+	}
+
+	if (arg->flags & DRM_EXYNOS_PP_FLAG_EVENT) {
+		struct drm_pending_exynos_pp_event *e;
+
+		e = exynos_drm_pp_event_create(dev, file_priv, arg->user_data);
+		if (!e) {
+			ret = -ENOMEM;
+			goto free;
+		}
+		task->event = e;
+	}
+
+	ret = exynos_drm_pp_task_check(task);
+	if (ret || arg->flags & DRM_EXYNOS_PP_FLAG_TEST_ONLY)
+		goto free;
+
+	/*
+	 * Queue task for processing on the hardware. task object will be
+	 * then freed after exynos_drm_pp_task_done()
+	 */
+	if (arg->flags & DRM_EXYNOS_PP_FLAG_NONBLOCK) {
+		DRM_DEBUG_DRIVER("pp: %d, nonblocking processing task %pK\n",
+				 task->pp->id, task);
+
+		task->flags |= DRM_EXYNOS_PP_TASK_ASYNC;
+		exynos_drm_pp_schedule_task(task->pp, task);
+		ret = 0;
+	} else {
+		DRM_DEBUG_DRIVER("pp: %d, processing task %pK\n", pp->id,
+				 task);
+		exynos_drm_pp_schedule_task(pp, task);
+		ret = wait_event_interruptible(pp->done_wq,
+					task->flags & DRM_EXYNOS_PP_TASK_DONE);
+		if (ret)
+			exynos_drm_pp_task_abort(pp, task);
+		else
+			ret = exynos_drm_pp_task_cleanup(task);
+	}
+	return ret;
+free:
+	exynos_drm_pp_task_free(pp, task);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_pp.h b/drivers/gpu/drm/exynos/exynos_drm_pp.h
new file mode 100644
index 000000000000..d0422abb2de4
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_pp.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _EXYNOS_DRM_FBPROC_H_
+#define _EXYNOS_DRM_FBPORC_H_
+
+#include <drm/drmP.h>
+
+struct exynos_drm_pp;
+struct exynos_drm_pp_task;
+
+/**
+ * struct exynos_drm_pp_funcs - exynos_drm_pp control functions
+ */
+struct exynos_drm_pp_funcs {
+	/**
+	 * @check:
+	 *
+	 * This is the optional hook to validate an pp task. This function
+	 * must reject any task which the hardware or driver doesn't support.
+	 * This includes but is of course not limited to:
+	 *
+	 *  - Checking that the framebuffers, scaling and placement
+	 *    requirements and so on are within the limits of the hardware.
+	 *
+	 *  - The driver does not need to repeat basic input validation like
+	 *    done in the exynos_drm_pp_check_only() function. The core does
+	 *    that before calling this hook.
+	 *
+	 * RETURNS:
+	 *
+	 * 0 on success or one of the below negative error codes:
+	 *
+	 *  - -EINVAL, if any of the above constraints are violated.
+	 */
+	int (*check)(struct exynos_drm_pp *pp,
+		     struct exynos_drm_pp_task *task);
+
+	/**
+	 * @commit:
+	 *
+	 * This is the main entry point to start framebuffer processing
+	 * in the hardware. The exynos_drm_pp_task has been already validated.
+	 * This function must not wait until the device finishes processing.
+	 * When the driver finishes processing, it has to call
+	 * exynos_exynos_drm_pp_task_done() function.
+	 *
+	 * RETURNS:
+	 *
+	 * 0 on success or negative error codes in case of failure.
+	 */
+	int (*commit)(struct exynos_drm_pp *pp,
+		      struct exynos_drm_pp_task *task);
+
+	/**
+	 * @abort:
+	 *
+	 * Informs the driver that it has to abort the currently running
+	 * task as soon as possible (i.e. as soon as it can stop the device
+	 * safely), even if the task would not have been finished by then.
+	 * After the driver performs the necessary steps, it has to call
+	 * exynos_drm_pp_task_done() (as if the task ended normally).
+	 * This function does not have to (and will usually not) wait
+	 * until the device enters a state when it can be stopped.
+	 */
+	void (*abort)(struct exynos_drm_pp *pp,
+		      struct exynos_drm_pp_task *task);
+};
+
+/**
+ * struct exynos_drm_pp - central picture processor module structure
+ */
+struct exynos_drm_pp {
+	struct drm_device *dev;
+	struct list_head head;
+	unsigned int id;
+
+	const char *name;
+	const struct exynos_drm_pp_funcs *funcs;
+	unsigned int capabilities;
+	unsigned int *params;
+	unsigned int rotation;
+	atomic_t sequence;
+
+	spinlock_t lock;
+	struct exynos_drm_pp_task *task;
+	struct list_head todo_list;
+	wait_queue_head_t done_wq;
+
+	const uint32_t *src_format_types;
+	unsigned int src_format_count;
+	const uint32_t *dst_format_types;
+	unsigned int dst_format_count;
+	const uint32_t *parameters;
+	unsigned int parameters_count;
+};
+
+/**
+ * struct exynos_drm_pp_task - a structure describing transformation that
+ * has to be performed by the picture processor hardware module
+ */
+struct exynos_drm_pp_task {
+	struct drm_device *dev;
+	struct exynos_drm_pp *pp;
+	struct list_head head;
+
+	struct drm_framebuffer *src_fb;
+
+	/* Source values are 16.16 fixed point */
+	uint32_t src_x, src_y;
+	uint32_t src_h, src_w;
+
+	struct drm_framebuffer *dst_fb;
+
+	/* Destination values are 16.16 fixed point */
+	uint32_t dst_x, dst_y;
+	uint32_t dst_h, dst_w;
+
+	uint32_t rotation;
+
+	struct work_struct cleanup_work;
+	unsigned int flags;
+	int ret;
+
+	struct drm_pending_exynos_pp_event *event;
+};
+
+#define DRM_EXYNOS_PP_TASK_DONE		(1 << 0)
+#define DRM_EXYNOS_PP_TASK_ASYNC	(1 << 1)
+
+int exynos_drm_pp_register(struct drm_device *dev, struct exynos_drm_pp *pp,
+		    const struct exynos_drm_pp_funcs *funcs, unsigned int caps,
+		    const uint32_t *src_fmts, unsigned int src_fmt_count,
+		    const uint32_t *dst_fmts, unsigned int dst_fmt_count,
+		    unsigned int rotation, const char *name);
+void exynos_drm_pp_unregister(struct drm_device *dev, struct exynos_drm_pp *pp);
+
+void exynos_drm_pp_task_done(struct exynos_drm_pp_task *task, int ret);
+
+int exynos_drm_pp_get_res(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv);
+int exynos_drm_pp_get(struct drm_device *dev, void *data,
+		      struct drm_file *file_priv);
+int exynos_drm_pp_commit(struct drm_device *dev,
+			 void *data, struct drm_file *file_priv);
+
+#endif
diff --git a/include/uapi/drm/exynos_drm.h b/include/uapi/drm/exynos_drm.h
index cb3e9f9d029f..a4e980395ddd 100644
--- a/include/uapi/drm/exynos_drm.h
+++ b/include/uapi/drm/exynos_drm.h
@@ -300,6 +300,60 @@ struct drm_exynos_ipp_cmd_ctrl {
 	__u32	ctrl;
 };
 
+struct drm_exynos_pp_get_res {
+	__u64 pp_id_ptr;
+	__u32 count_pps;
+};
+
+#define DRM_EXYNOS_PP_PARAM_SRC_FB	0x0001
+#define DRM_EXYNOS_PP_PARAM_SRC_X	0x0002
+#define DRM_EXYNOS_PP_PARAM_SRC_Y	0x0004
+#define DRM_EXYNOS_PP_PARAM_SRC_WIDTH	0x0008
+#define DRM_EXYNOS_PP_PARAM_SRC_HEIGHT	0x0010
+#define DRM_EXYNOS_PP_PARAM_DST_FB	0x0020
+#define DRM_EXYNOS_PP_PARAM_DST_X	0x0040
+#define DRM_EXYNOS_PP_PARAM_DST_Y	0x0080
+#define DRM_EXYNOS_PP_PARAM_DST_WIDTH	0x0100
+#define DRM_EXYNOS_PP_PARAM_DST_HEIGHT	0x0200
+#define DRM_EXYNOS_PP_PARAM_ROTATION	0x0400
+
+struct drm_exynos_pp_get {
+	__u32 pp_id;
+	__u32 capabilities;
+
+	__u32 src_format_count;
+	__u32 dst_format_count;
+	__u32 params_count;
+	__u32 reserved1;
+
+	__u64 src_format_type_ptr;
+	__u64 dst_format_type_ptr;
+	__u64 params_ptr;
+	__u64 reserved2;
+};
+
+#define DRM_EXYNOS_PP_CAP_CROP		0x01
+#define DRM_EXYNOS_PP_CAP_ROTATE	0x02
+#define DRM_EXYNOS_PP_CAP_SCALE		0x04
+#define DRM_EXYNOS_PP_CAP_CONVERT	0x08
+
+#define DRM_EXYNOS_PP_FLAG_EVENT	0x01
+#define DRM_EXYNOS_PP_FLAG_TEST_ONLY	0x02
+#define DRM_EXYNOS_PP_FLAG_NONBLOCK	0x04
+
+#define DRM_EXYNOS_PP_FLAGS (DRM_EXYNOS_PP_FLAG_EVENT |\
+		DRM_EXYNOS_PP_FLAG_TEST_ONLY | DRM_EXYNOS_PP_FLAG_NONBLOCK)
+
+struct drm_exynos_pp_commit {
+	__u32 id;
+	__u32 flags;
+	__u32 params_count;
+	__u32 reserved;
+	__u64 param_ids_ptr;
+	__u64 param_values_ptr;
+	__u64 user_data;
+};
+
 #define DRM_EXYNOS_GEM_CREATE		0x00
 #define DRM_EXYNOS_GEM_MAP		0x01
 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
@@ -317,6 +371,10 @@ struct drm_exynos_ipp_cmd_ctrl {
 #define DRM_EXYNOS_IPP_QUEUE_BUF	0x32
 #define DRM_EXYNOS_IPP_CMD_CTRL	0x33
 
+#define DRM_EXYNOS_PP_GET_RESOURCES	0x40
+#define DRM_EXYNOS_PP_GET		0x41
+#define DRM_EXYNOS_PP_COMMIT		0x42
+
 #define DRM_IOCTL_EXYNOS_GEM_CREATE		DRM_IOWR(DRM_COMMAND_BASE + \
 		DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
 #define DRM_IOCTL_EXYNOS_GEM_MAP		DRM_IOWR(DRM_COMMAND_BASE + \
@@ -343,9 +401,17 @@ struct drm_exynos_ipp_cmd_ctrl {
 #define DRM_IOCTL_EXYNOS_IPP_CMD_CTRL		DRM_IOWR(DRM_COMMAND_BASE + \
 		DRM_EXYNOS_IPP_CMD_CTRL, struct drm_exynos_ipp_cmd_ctrl)
 
+#define DRM_IOCTL_EXYNOS_PP_GET_RESOURCES	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_PP_GET_RESOURCES, struct drm_exynos_pp_get_res)
+#define DRM_IOCTL_EXYNOS_PP_GET			DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_PP_GET, struct drm_exynos_pp_get)
+#define DRM_IOCTL_EXYNOS_PP_COMMIT		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_PP_COMMIT, struct drm_exynos_pp_commit)
+
 /* EXYNOS specific events */
 #define DRM_EXYNOS_G2D_EVENT		0x80000000
 #define DRM_EXYNOS_IPP_EVENT		0x80000001
+#define DRM_EXYNOS_PP_EVENT		0x80000002
 
 struct drm_exynos_g2d_event {
 	struct drm_event	base;
@@ -366,6 +432,16 @@ struct drm_exynos_ipp_event {
 	__u32			buf_id[EXYNOS_DRM_OPS_MAX];
 };
 
+struct drm_exynos_pp_event {
+	struct drm_event	base;
+	__u64			user_data;
+	__u32			tv_sec;
+	__u32			tv_usec;
+	__u32			pp_id;
+	__u32			sequence;
+	__u64			reserved;
+};
+
 #if defined(__cplusplus)
 }
 #endif
