This patch adds new rotator driver which is a device for
rotation on EXYNOS SoCs.

This rotator device supports the belows as key feature.
 1) Image format
   : RGB565/888, YUV422 1p, YUV420 2p/3p
 2) rotation
   : 0/90/180/270 and X/Y Flip

Signed-off-by: Sunyoung Kang <sy0816.k...@samsung.com>
Signed-off-by: Ayoung Sim <a....@samsung.com>
---NOTE:
This patch has been created based on following
 - media: media-dev: Add media devices for EXYNOS5 by Sungchun Kang
 - media: fimc-lite: Add new driver for camera interface by Sungchun Kang

Changes since v1:
 - address comments from Sylwester Nawrocki
   
 drivers/media/video/exynos/Kconfig                |    1 +
 drivers/media/video/exynos/Makefile               |    1 +
 drivers/media/video/exynos/rotator/Kconfig        |    7 +
 drivers/media/video/exynos/rotator/Makefile       |    9 +
 drivers/media/video/exynos/rotator/rotator-core.c | 1344 +++++++++++++++++++++
 drivers/media/video/exynos/rotator/rotator-regs.c |  215 ++++
 drivers/media/video/exynos/rotator/rotator-regs.h |   70 ++
 drivers/media/video/exynos/rotator/rotator.h      |  286 +++++
 8 files changed, 1933 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/exynos/rotator/Kconfig
 create mode 100644 drivers/media/video/exynos/rotator/Makefile
 create mode 100644 drivers/media/video/exynos/rotator/rotator-core.c
 create mode 100644 drivers/media/video/exynos/rotator/rotator-regs.c
 create mode 100644 drivers/media/video/exynos/rotator/rotator-regs.h
 create mode 100644 drivers/media/video/exynos/rotator/rotator.h

diff --git a/drivers/media/video/exynos/Kconfig 
b/drivers/media/video/exynos/Kconfig
index a84097d..38a885d 100644
--- a/drivers/media/video/exynos/Kconfig
+++ b/drivers/media/video/exynos/Kconfig
@@ -12,6 +12,7 @@ config VIDEO_EXYNOS
 if VIDEO_EXYNOS
        source "drivers/media/video/exynos/mdev/Kconfig"
        source "drivers/media/video/exynos/fimc-lite/Kconfig"
+       source "drivers/media/video/exynos/rotator/Kconfig"
 endif
 
 config MEDIA_EXYNOS
diff --git a/drivers/media/video/exynos/Makefile 
b/drivers/media/video/exynos/Makefile
index 56cb7b2..24f19c5 100644
--- a/drivers/media/video/exynos/Makefile
+++ b/drivers/media/video/exynos/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_EXYNOS_MEDIA_DEVICE)      += mdev/
 obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE)   += fimc-lite/
+obj-$(CONFIG_VIDEO_EXYNOS_ROTATOR)     += rotator/
 
 EXTRA_CLAGS += -Idrivers/media/video
diff --git a/drivers/media/video/exynos/rotator/Kconfig 
b/drivers/media/video/exynos/rotator/Kconfig
new file mode 100644
index 0000000..977423a
--- /dev/null
+++ b/drivers/media/video/exynos/rotator/Kconfig
@@ -0,0 +1,7 @@
+config VIDEO_EXYNOS_ROTATOR
+       bool "EXYNOS Image Rotator Driver"
+       select V4L2_MEM2MEM_DEV
+       select VIDEOBUF2_DMA_CONTIG
+       default n
+       ---help---
+         Support for Image Rotator Driver with v4l2 on EXYNOS SoCs
diff --git a/drivers/media/video/exynos/rotator/Makefile 
b/drivers/media/video/exynos/rotator/Makefile
new file mode 100644
index 0000000..6f74403
--- /dev/null
+++ b/drivers/media/video/exynos/rotator/Makefile
@@ -0,0 +1,9 @@
+#
+# Copyright (c) 2012 Samsung Electronics Co., Ltd.
+#              http://www.samsung.com
+#
+# Licensed under GPLv2
+#
+
+rotator-objs                           := rotator-core.o rotator-regs.o
+obj-$(CONFIG_VIDEO_EXYNOS_ROTATOR)     += rotator.o
diff --git a/drivers/media/video/exynos/rotator/rotator-core.c 
b/drivers/media/video/exynos/rotator/rotator-core.c
new file mode 100644
index 0000000..75d28f2
--- /dev/null
+++ b/drivers/media/video/exynos/rotator/rotator-core.c
@@ -0,0 +1,1344 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Core file for Samsung EXYNOS Image Rotator driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "rotator.h"
+
+int log_level;
+module_param_named(log_level, log_level, uint, 0644);
+
+static struct rot_fmt rot_formats[] = {
+       {
+               .name           = "RGB565",
+               .pixelformat    = V4L2_PIX_FMT_RGB565,
+               .num_planes     = 1,
+               .num_comp       = 1,
+               .bitperpixel    = { 16 },
+       }, {
+               .name           = "XRGB-8888, 32 bps",
+               .pixelformat    = V4L2_PIX_FMT_RGB32,
+               .num_planes     = 1,
+               .num_comp       = 1,
+               .bitperpixel    = { 32 },
+       }, {
+               .name           = "YUV 4:2:2 packed, YCbYCr",
+               .pixelformat    = V4L2_PIX_FMT_YUYV,
+               .num_planes     = 1,
+               .num_comp       = 1,
+               .bitperpixel    = { 16 },
+       }, {
+               .name           = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
+               .pixelformat    = V4L2_PIX_FMT_NV12M,
+               .num_planes     = 2,
+               .num_comp       = 2,
+               .bitperpixel    = { 8, 4 },
+       }, {
+               .name           = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
+               .pixelformat    = V4L2_PIX_FMT_YUV420M,
+               .num_planes     = 3,
+               .num_comp       = 3,
+               .bitperpixel    = { 8, 2, 2 },
+       },
+};
+
+/* Find the matches format */
+static struct rot_fmt *rot_find_format(struct v4l2_format *f)
+{
+       struct rot_fmt *rot_fmt;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rot_formats); ++i) {
+               rot_fmt = &rot_formats[i];
+               if (rot_fmt->pixelformat == f->fmt.pix_mp.pixelformat)
+                       return &rot_formats[i];
+       }
+
+       return NULL;
+}
+
+static void rot_bound_align_image(struct rot_ctx *ctx, struct rot_fmt *rot_fmt,
+                                 u32 *width, u32 *height)
+{
+       struct exynos_rot_variant *variant = ctx->rot_dev->variant;
+       struct exynos_rot_size_limit *limit = NULL;
+
+       switch (rot_fmt->pixelformat) {
+       case V4L2_PIX_FMT_YUV420M:
+               limit = &variant->limit_yuv420_3p;
+               break;
+       case V4L2_PIX_FMT_NV12M:
+               limit = &variant->limit_yuv420_2p;
+               break;
+       case V4L2_PIX_FMT_YUYV:
+               limit = &variant->limit_yuv422;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               limit = &variant->limit_rgb565;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+               limit = &variant->limit_rgb888;
+               break;
+       default:
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
+                               "not supported format values\n");
+               return;
+       }
+
+       /* Bound an image to have width and height in limit */
+       v4l_bound_align_image(width, limit->min_x, limit->max_x,
+                       limit->align, height, limit->min_y,
+                       limit->max_y, limit->align, 0);
+}
+
+static void rot_adjust_pixminfo(struct rot_ctx *ctx, struct rot_frame *frame,
+                               struct v4l2_pix_format_mplane *pixm)
+{
+       struct rot_frame *rot_frame;
+
+       if (frame == &ctx->s_frame) {
+               if (test_bit(CTX_DST, &ctx->flags)) {
+                       rot_frame = &ctx->d_frame;
+                       pixm->pixelformat = rot_frame->rot_fmt->pixelformat;
+               }
+               set_bit(CTX_SRC, &ctx->flags);
+       } else if (frame == &ctx->d_frame) {
+               if (test_bit(CTX_SRC, &ctx->flags)) {
+                       rot_frame = &ctx->s_frame;
+                       pixm->pixelformat = rot_frame->rot_fmt->pixelformat;
+               }
+               set_bit(CTX_DST, &ctx->flags);
+       }
+}
+
+static void rot_adjust_cropinfo(struct rot_ctx *ctx, struct rot_frame *frame,
+                               struct v4l2_rect *crop)
+{
+       struct rot_frame *rot_frame;
+
+       if (frame == &ctx->s_frame) {
+               if (test_bit(CTX_DST, &ctx->flags)) {
+                       rot_frame = &ctx->d_frame;
+                       crop->width  = rot_frame->crop.height;
+                       crop->height = rot_frame->crop.width;
+               }
+               set_bit(CTX_SRC, &ctx->flags);
+       } else if (frame == &ctx->d_frame) {
+               if (test_bit(CTX_SRC, &ctx->flags)) {
+                       rot_frame = &ctx->s_frame;
+                       crop->width  = rot_frame->crop.height;
+                       crop->height = rot_frame->crop.width;
+               }
+               set_bit(CTX_DST, &ctx->flags);
+       }
+}
+
+static int rot_v4l2_querycap(struct file *file, void *fh,
+                            struct v4l2_capability *cap)
+{
+       strncpy(cap->driver, MODULE_NAME, sizeof(cap->driver) - 1);
+       strncpy(cap->card, MODULE_NAME, sizeof(cap->card) - 1);
+
+       cap->bus_info[0] = 0;
+       cap->capabilities = V4L2_CAP_STREAMING |
+               V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+       return 0;
+}
+
+static int rot_v4l2_enum_fmt_mplane(struct file *file, void *fh,
+                                   struct v4l2_fmtdesc *f)
+{
+       struct rot_fmt *rot_fmt;
+
+       if (f->index >= ARRAY_SIZE(rot_formats))
+               return -EINVAL;
+
+       rot_fmt = &rot_formats[f->index];
+       strncpy(f->description, rot_fmt->name, sizeof(f->description) - 1);
+       f->pixelformat = rot_fmt->pixelformat;
+
+       return 0;
+}
+
+static int rot_v4l2_g_fmt_mplane(struct file *file, void *fh,
+                                struct v4l2_format *f)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       struct rot_fmt *rot_fmt;
+       struct rot_frame *frame;
+       struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+       int i;
+
+       frame = ctx_get_frame(ctx, f->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       rot_fmt = frame->rot_fmt;
+
+       pixm->width             = frame->pix_mp.width;
+       pixm->height            = frame->pix_mp.height;
+       pixm->pixelformat       = frame->pix_mp.pixelformat;
+       pixm->field             = V4L2_FIELD_NONE;
+       pixm->num_planes        = frame->rot_fmt->num_planes;
+       pixm->colorspace        = 0;
+
+       for (i = 0; i < pixm->num_planes; ++i) {
+               pixm->plane_fmt[i].bytesperline = (pixm->width *
+                               rot_fmt->bitperpixel[i]) >> 3;
+               pixm->plane_fmt[i].sizeimage = pixm->plane_fmt[i].bytesperline
+                               * pixm->height;
+
+               v4l2_dbg(1, log_level, &ctx->rot_dev->m2m.v4l2_dev,
+                               "[%d] plane: bytesperline %d, sizeimage %d\n",
+                               i, pixm->plane_fmt[i].bytesperline,
+                               pixm->plane_fmt[i].sizeimage);
+       }
+
+       return 0;
+}
+
+static int rot_v4l2_try_fmt_mplane(struct file *file, void *fh,
+                                   struct v4l2_format *f)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       struct rot_fmt *rot_fmt;
+       struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+       int i;
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
+                               "not supported v4l2 type\n");
+               return -EINVAL;
+       }
+
+       rot_fmt = rot_find_format(f);
+       if (!rot_fmt) {
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
+                               "not supported format type\n");
+               return -EINVAL;
+       }
+
+       rot_bound_align_image(ctx, rot_fmt, &pixm->width, &pixm->height);
+
+       pixm->num_planes = rot_fmt->num_planes;
+       pixm->colorspace = 0;
+
+       for (i = 0; i < pixm->num_planes; ++i) {
+               pixm->plane_fmt[i].bytesperline = (pixm->width *
+                               rot_fmt->bitperpixel[i]) >> 3;
+               pixm->plane_fmt[i].sizeimage = pixm->plane_fmt[i].bytesperline
+                               * pixm->height;
+
+               v4l2_dbg(1, log_level, &ctx->rot_dev->m2m.v4l2_dev,
+                               "[%d] plane: bytesperline %d, sizeimage %d\n",
+                               i, pixm->plane_fmt[i].bytesperline,
+                               pixm->plane_fmt[i].sizeimage);
+       }
+
+       return 0;
+}
+
+static int rot_v4l2_s_fmt_mplane(struct file *file, void *fh,
+                                struct v4l2_format *f)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+       struct rot_frame *frame;
+       struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+       int i, ret = 0;
+
+       if (vb2_is_streaming(vq)) {
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev, "device is busy\n");
+               return -EBUSY;
+       }
+
+       ret = rot_v4l2_try_fmt_mplane(file, fh, f);
+       if (ret < 0)
+               return ret;
+
+       frame = ctx_get_frame(ctx, f->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       set_bit(CTX_PARAMS, &ctx->flags);
+
+       frame->rot_fmt = rot_find_format(f);
+       if (!frame->rot_fmt) {
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
+                               "not supported format values\n");
+               return -EINVAL;
+       }
+
+       rot_adjust_pixminfo(ctx, frame, pixm);
+
+       frame->pix_mp.pixelformat = pixm->pixelformat;
+       frame->pix_mp.width     = pixm->width;
+       frame->pix_mp.height    = pixm->height;
+
+       /*
+        * Shouldn't call s_crop or g_crop before called g_fmt or s_fmt.
+        * Let's assume that we can keep the order.
+        */
+       frame->crop.width       = pixm->width;
+       frame->crop.height      = pixm->height;
+
+       for (i = 0; i < frame->rot_fmt->num_planes; ++i)
+               frame->bytesused[i] = (pixm->width * pixm->height *
+                               frame->rot_fmt->bitperpixel[i]) >> 3;
+
+       return 0;
+}
+
+static int rot_v4l2_reqbufs(struct file *file, void *fh,
+                           struct v4l2_requestbuffers *reqbufs)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       struct rot_frame *frame;
+
+       frame = ctx_get_frame(ctx, reqbufs->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       if (frame == &ctx->s_frame)
+               clear_bit(CTX_SRC, &ctx->flags);
+       else if (frame == &ctx->d_frame)
+               clear_bit(CTX_DST, &ctx->flags);
+
+       return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int rot_v4l2_querybuf(struct file *file, void *fh,
+                            struct v4l2_buffer *buf)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int rot_v4l2_qbuf(struct file *file, void *fh,
+                        struct v4l2_buffer *buf)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int rot_v4l2_dqbuf(struct file *file, void *fh,
+                         struct v4l2_buffer *buf)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int rot_v4l2_streamon(struct file *file, void *fh,
+                            enum v4l2_buf_type type)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int rot_v4l2_streamoff(struct file *file, void *fh,
+                             enum v4l2_buf_type type)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int rot_v4l2_cropcap(struct file *file, void *fh,
+                           struct v4l2_cropcap *cr)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       struct rot_frame *frame;
+
+       frame = ctx_get_frame(ctx, cr->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       cr->bounds.left         = 0;
+       cr->bounds.top          = 0;
+       cr->bounds.width        = frame->pix_mp.width;
+       cr->bounds.height       = frame->pix_mp.height;
+       cr->defrect             = cr->bounds;
+
+       return 0;
+}
+
+static int rot_v4l2_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       struct rot_frame *frame;
+
+       frame = ctx_get_frame(ctx, cr->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       cr->c = frame->crop;
+
+       return 0;
+}
+
+static int rot_v4l2_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(fh);
+       struct rot_frame *frame;
+       struct v4l2_pix_format_mplane *pixm;
+       int i;
+
+       frame = ctx_get_frame(ctx, cr->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       if (!test_bit(CTX_PARAMS, &ctx->flags)) {
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
+                               "color format is not set\n");
+               return -EINVAL;
+       }
+
+       if (cr->c.left < 0 || cr->c.top < 0 ||
+                       cr->c.width < 0 || cr->c.height < 0) {
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
+                               "crop value is negative\n");
+               return -EINVAL;
+       }
+
+       pixm = &frame->pix_mp;
+       rot_adjust_cropinfo(ctx, frame, &cr->c);
+       rot_bound_align_image(ctx, frame->rot_fmt, &cr->c.width, &cr->c.height);
+
+       /* Adjust left/top if cropping rectangle is out of bounds */
+       if (cr->c.left + cr->c.width > pixm->width) {
+               dev_warn(ctx->rot_dev->dev,
+                       "out of bound left cropping size:left %d, width %d\n",
+                               cr->c.left, cr->c.width);
+               cr->c.left = pixm->width - cr->c.width;
+       }
+       if (cr->c.top + cr->c.height > pixm->height) {
+               dev_warn(ctx->rot_dev->dev,
+                       "out of bound top cropping size:top %d, height %d\n",
+                               cr->c.top, cr->c.height);
+               cr->c.top = pixm->height - cr->c.height;
+       }
+
+       frame->crop = cr->c;
+
+       for (i = 0; i < frame->rot_fmt->num_planes; ++i)
+               frame->bytesused[i] = (cr->c.width * cr->c.height *
+                               frame->rot_fmt->bitperpixel[i]) >> 3;
+
+       return 0;
+}
+
+static const struct v4l2_ioctl_ops rot_v4l2_ioctl_ops = {
+       .vidioc_querycap                = rot_v4l2_querycap,
+
+       .vidioc_enum_fmt_vid_cap_mplane = rot_v4l2_enum_fmt_mplane,
+       .vidioc_enum_fmt_vid_out_mplane = rot_v4l2_enum_fmt_mplane,
+
+       .vidioc_g_fmt_vid_cap_mplane    = rot_v4l2_g_fmt_mplane,
+       .vidioc_g_fmt_vid_out_mplane    = rot_v4l2_g_fmt_mplane,
+
+       .vidioc_try_fmt_vid_cap_mplane  = rot_v4l2_try_fmt_mplane,
+       .vidioc_try_fmt_vid_out_mplane  = rot_v4l2_try_fmt_mplane,
+
+       .vidioc_s_fmt_vid_cap_mplane    = rot_v4l2_s_fmt_mplane,
+       .vidioc_s_fmt_vid_out_mplane    = rot_v4l2_s_fmt_mplane,
+
+       .vidioc_reqbufs                 = rot_v4l2_reqbufs,
+       .vidioc_querybuf                = rot_v4l2_querybuf,
+
+       .vidioc_qbuf                    = rot_v4l2_qbuf,
+       .vidioc_dqbuf                   = rot_v4l2_dqbuf,
+
+       .vidioc_streamon                = rot_v4l2_streamon,
+       .vidioc_streamoff               = rot_v4l2_streamoff,
+
+       .vidioc_g_crop                  = rot_v4l2_g_crop,
+       .vidioc_s_crop                  = rot_v4l2_s_crop,
+       .vidioc_cropcap                 = rot_v4l2_cropcap
+};
+
+static int rot_ctx_stop_req(struct rot_ctx *ctx)
+{
+       struct rot_ctx *curr_ctx;
+       struct rot_dev *rot = ctx->rot_dev;
+       int ret = 0;
+
+       curr_ctx = v4l2_m2m_get_curr_priv(rot->m2m.m2m_dev);
+       if (!test_bit(CTX_RUN, &ctx->flags) || (curr_ctx != ctx))
+               return 0;
+
+       set_bit(CTX_ABORT, &ctx->flags);
+
+       ret = wait_event_timeout(rot->irq.wait,
+                       !test_bit(CTX_RUN, &ctx->flags), ROT_TIMEOUT);
+
+       /* TODO: How to handle case of timeout event */
+       if (ret == 0) {
+               dev_err(rot->dev, "device failed to stop request\n");
+               ret = -EBUSY;
+       }
+
+       return ret;
+}
+
+static int rot_vb2_queue_setup(struct vb2_queue *vq,
+               const struct v4l2_format *fmt, unsigned int *num_buffers,
+               unsigned int *num_planes, unsigned int sizes[],
+               void *allocators[])
+{
+       struct rot_ctx *ctx = vb2_get_drv_priv(vq);
+       struct rot_frame *frame;
+       int i;
+
+       frame = ctx_get_frame(ctx, vq->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       /* Get number of planes from format_list in driver */
+       *num_planes = frame->rot_fmt->num_planes;
+       for (i = 0; i < frame->rot_fmt->num_planes; i++) {
+               sizes[i] = (frame->pix_mp.width * frame->pix_mp.height *
+                               frame->rot_fmt->bitperpixel[i]) >> 3;
+               allocators[i] = ctx->rot_dev->alloc_ctx;
+       }
+
+       return 0;
+}
+
+static int rot_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+       struct rot_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       struct rot_frame *frame;
+       int i;
+
+       frame = ctx_get_frame(ctx, vb->vb2_queue->type);
+       if (IS_ERR(frame))
+               return PTR_ERR(frame);
+
+       if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+               for (i = 0; i < frame->rot_fmt->num_planes; i++)
+                       vb2_set_plane_payload(vb, i, frame->bytesused[i]);
+       }
+
+       return 0;
+}
+
+static void rot_vb2_buf_queue(struct vb2_buffer *vb)
+{
+       struct rot_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+       if (ctx->m2m_ctx)
+               v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void rot_vb2_lock(struct vb2_queue *vq)
+{
+       struct rot_ctx *ctx = vb2_get_drv_priv(vq);
+       mutex_lock(&ctx->rot_dev->lock);
+}
+
+static void rot_vb2_unlock(struct vb2_queue *vq)
+{
+       struct rot_ctx *ctx = vb2_get_drv_priv(vq);
+       mutex_unlock(&ctx->rot_dev->lock);
+}
+
+static int rot_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct rot_ctx *ctx = vb2_get_drv_priv(vq);
+       set_bit(CTX_STREAMING, &ctx->flags);
+
+       return 0;
+}
+
+static int rot_vb2_stop_streaming(struct vb2_queue *vq)
+{
+       struct rot_ctx *ctx = vb2_get_drv_priv(vq);
+       int ret;
+
+       ret = rot_ctx_stop_req(ctx);
+       if (ret < 0)
+               dev_err(ctx->rot_dev->dev, "wait timeout\n");
+
+       clear_bit(CTX_STREAMING, &ctx->flags);
+
+       return ret;
+}
+
+static struct vb2_ops rot_vb2_ops = {
+       .queue_setup            = rot_vb2_queue_setup,
+       .buf_prepare            = rot_vb2_buf_prepare,
+       .buf_queue              = rot_vb2_buf_queue,
+       .wait_finish            = rot_vb2_lock,
+       .wait_prepare           = rot_vb2_unlock,
+       .start_streaming        = rot_vb2_start_streaming,
+       .stop_streaming         = rot_vb2_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+                     struct vb2_queue *dst_vq)
+{
+       struct rot_ctx *ctx = priv;
+       int ret;
+
+       memset(src_vq, 0, sizeof(*src_vq));
+       src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+       src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+       src_vq->ops = &rot_vb2_ops;
+       src_vq->mem_ops = &vb2_dma_contig_memops;
+       src_vq->drv_priv = ctx;
+       src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+       ret = vb2_queue_init(src_vq);
+       if (ret)
+               return ret;
+
+       memset(dst_vq, 0, sizeof(*dst_vq));
+       dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+       dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+       dst_vq->ops = &rot_vb2_ops;
+       dst_vq->mem_ops = &vb2_dma_contig_memops;
+       dst_vq->drv_priv = ctx;
+       dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+       return vb2_queue_init(dst_vq);
+}
+
+static int rot_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct rot_ctx *ctx;
+       unsigned long flags;
+       int ret = 0;
+
+       ctx = container_of(ctrl->handler, struct rot_ctx, ctrl_handler);
+       spin_lock_irqsave(&ctx->slock, flags);
+
+       rot_dbg("ctrl ID:%d, value:%d\n", ctrl->id, ctrl->val);
+       switch (ctrl->id) {
+       case V4L2_CID_VFLIP:
+               if (ctrl->val)
+                       ctx->flip |= ROT_VFLIP;
+               else
+                       ctx->flip &= ~ROT_VFLIP;
+               break;
+       case V4L2_CID_HFLIP:
+               if (ctrl->val)
+                       ctx->flip |= ROT_HFLIP;
+               else
+                       ctx->flip &= ~ROT_HFLIP;
+               break;
+       case V4L2_CID_ROTATE:
+               ctx->rotation = ctrl->val;
+               break;
+       default:
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev, "invalid control id\n");
+               ret = -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&ctx->slock, flags);
+
+       return ret;
+}
+
+static const struct v4l2_ctrl_ops rot_ctrl_ops = {
+       .s_ctrl = rot_s_ctrl,
+};
+
+static int rot_add_ctrls(struct rot_ctx *ctx)
+{
+       v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
+       v4l2_ctrl_new_std(&ctx->ctrl_handler, &rot_ctrl_ops,
+                       V4L2_CID_VFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(&ctx->ctrl_handler, &rot_ctrl_ops,
+                       V4L2_CID_HFLIP, 0, 1, 1, 0);
+       v4l2_ctrl_new_std(&ctx->ctrl_handler, &rot_ctrl_ops,
+                       V4L2_CID_ROTATE, 0, 270, 90, 0);
+
+       if (ctx->ctrl_handler.error) {
+               int err = ctx->ctrl_handler.error;
+               v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
+                               "v4l2_ctrl_handler_init failed\n");
+               v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+               return err;
+       }
+
+       return 0;
+}
+
+static int rot_open(struct file *file)
+{
+       struct rot_dev *rot = video_drvdata(file);
+       struct rot_ctx *ctx = NULL;
+       int ret;
+
+       ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+       if (!ctx) {
+               dev_err(rot->dev, "no memory for open context\n");
+               return -ENOMEM;
+       }
+
+       atomic_inc(&rot->m2m.in_use);
+       ctx->rot_dev = rot;
+
+       v4l2_fh_init(&ctx->fh, rot->m2m.vfd);
+       ret = rot_add_ctrls(ctx);
+       if (ret)
+               goto err_fh;
+
+       ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+       file->private_data = &ctx->fh;
+       v4l2_fh_add(&ctx->fh);
+
+       /* Default color format */
+       ctx->s_frame.rot_fmt = &rot_formats[0];
+       ctx->d_frame.rot_fmt = &rot_formats[0];
+       init_waitqueue_head(&rot->irq.wait);
+       spin_lock_init(&ctx->slock);
+
+       /* Setup the device context for mem2mem mode. */
+       ctx->m2m_ctx = v4l2_m2m_ctx_init(rot->m2m.m2m_dev, ctx, queue_init);
+       if (IS_ERR(ctx->m2m_ctx)) {
+               ret = -EINVAL;
+               goto err_ctx;
+       }
+
+       return 0;
+
+err_ctx:
+       v4l2_fh_del(&ctx->fh);
+err_fh:
+       v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+       v4l2_fh_exit(&ctx->fh);
+       atomic_dec(&rot->m2m.in_use);
+       kfree(ctx);
+
+       return ret;
+}
+
+static int rot_release(struct file *file)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(file->private_data);
+       struct rot_dev *rot = ctx->rot_dev;
+
+       rot_dbg("refcnt= %d", atomic_read(&rot->m2m.in_use));
+
+       v4l2_m2m_ctx_release(ctx->m2m_ctx);
+       v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+       atomic_dec(&rot->m2m.in_use);
+       kfree(ctx);
+
+       return 0;
+}
+
+static unsigned int rot_poll(struct file *file,
+                            struct poll_table_struct *wait)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(file->private_data);
+
+       return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int rot_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct rot_ctx *ctx = fh_to_rot_ctx(file->private_data);
+
+       return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations rot_v4l2_fops = {
+       .owner          = THIS_MODULE,
+       .open           = rot_open,
+       .release        = rot_release,
+       .poll           = rot_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = rot_mmap,
+};
+
+static void rot_watchdog(unsigned long arg)
+{
+       struct rot_dev *rot = (struct rot_dev *)arg;
+       struct rot_ctx *ctx;
+       unsigned long flags;
+       struct vb2_buffer *src_vb, *dst_vb;
+
+       rot_dbg("timeout watchdog\n");
+       if (atomic_read(&rot->wdt.cnt) >= ROT_WDT_CNT) {
+               pm_runtime_put(rot->dev);
+
+               rot_dbg("wakeup blocked process\n");
+               atomic_set(&rot->wdt.cnt, 0);
+               clear_bit(DEV_RUN, &rot->state);
+
+               ctx = v4l2_m2m_get_curr_priv(rot->m2m.m2m_dev);
+               if (!ctx || !ctx->m2m_ctx) {
+                       dev_err(rot->dev, "current ctx is NULL\n");
+                       return;
+               }
+               spin_lock_irqsave(&rot->slock, flags);
+               clear_bit(CTX_RUN, &ctx->flags);
+               src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+               dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+               if (src_vb && dst_vb) {
+                       v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
+                       v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
+
+                       v4l2_m2m_job_finish(rot->m2m.m2m_dev, ctx->m2m_ctx);
+               }
+               spin_unlock_irqrestore(&rot->slock, flags);
+               return;
+       }
+
+       if (test_bit(DEV_RUN, &rot->state)) {
+               atomic_inc(&rot->wdt.cnt);
+               dev_err(rot->dev, "rotator is still running\n");
+               rot->wdt.timer.expires = jiffies + ROT_TIMEOUT;
+               add_timer(&rot->wdt.timer);
+       } else {
+               rot_dbg("rotator finished job\n");
+       }
+}
+
+static irqreturn_t rot_irq_handler(int irq, void *priv)
+{
+       struct rot_dev *rot = priv;
+       struct rot_ctx *ctx;
+       struct vb2_buffer *src_vb, *dst_vb;
+       unsigned int irq_src;
+
+       spin_lock(&rot->slock);
+
+       clear_bit(DEV_RUN, &rot->state);
+       if (timer_pending(&rot->wdt.timer))
+               del_timer(&rot->wdt.timer);
+
+       rot_hwget_irq_src(rot, &irq_src);
+       rot_hwset_irq_clear(rot, &irq_src);
+
+       if (irq_src != ISR_PEND_DONE) {
+               dev_err(rot->dev, "####################\n");
+               dev_err(rot->dev, "set SFR illegally\n");
+               dev_err(rot->dev, "maybe the result is wrong\n");
+               dev_err(rot->dev, "####################\n");
+               rot_dump_register(rot);
+       }
+
+       ctx = v4l2_m2m_get_curr_priv(rot->m2m.m2m_dev);
+       if (!ctx || !ctx->m2m_ctx) {
+               dev_err(rot->dev, "current ctx is NULL\n");
+               goto isr_unlock;
+       }
+
+       clear_bit(CTX_RUN, &ctx->flags);
+
+       src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+       dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+       if (src_vb && dst_vb) {
+               v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+               v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+
+               if (test_bit(DEV_SUSPEND, &rot->state)) {
+                       rot_dbg("wake up blocked process by suspend\n");
+                       wake_up(&rot->irq.wait);
+               } else {
+                       v4l2_m2m_job_finish(rot->m2m.m2m_dev, ctx->m2m_ctx);
+               }
+
+               /* Wake up from CTX_ABORT state */
+               if (test_and_clear_bit(CTX_ABORT, &ctx->flags))
+                       wake_up(&rot->irq.wait);
+
+               pm_runtime_put(rot->dev);
+       } else {
+               dev_err(rot->dev, "failed to get the buffer done\n");
+       }
+
+isr_unlock:
+       spin_unlock(&rot->slock);
+
+       return IRQ_HANDLED;
+}
+
+static void rot_get_bufaddr(struct rot_dev *rot, struct vb2_buffer *vb,
+                           struct rot_frame *frame, struct rot_addr *addr)
+{
+       unsigned int pix_size;
+
+       pix_size = frame->pix_mp.width * frame->pix_mp.height;
+
+       addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
+       addr->cb = 0;
+       addr->cr = 0;
+
+       switch (frame->rot_fmt->num_comp) {
+       case 2:
+               if (frame->rot_fmt->num_planes == 1)
+                       addr->cb = addr->y + pix_size;
+               else if (frame->rot_fmt->num_planes == 2)
+                       addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+               break;
+       case 3:
+               if (frame->rot_fmt->num_planes == 3) {
+                       addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+                       addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void rot_set_frame_addr(struct rot_ctx *ctx)
+{
+       struct vb2_buffer *vb;
+       struct rot_frame *s_frame, *d_frame;
+       struct rot_dev *rot = ctx->rot_dev;
+
+       s_frame = &ctx->s_frame;
+       d_frame = &ctx->d_frame;
+
+       /* set source buffer address */
+       vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+       rot_get_bufaddr(rot, vb, s_frame, &s_frame->addr);
+
+       rot_hwset_src_addr(rot, s_frame->addr.y, ROT_ADDR_Y);
+       rot_hwset_src_addr(rot, s_frame->addr.cb, ROT_ADDR_CB);
+       rot_hwset_src_addr(rot, s_frame->addr.cr, ROT_ADDR_CR);
+
+       /* set destination buffer address */
+       vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+       rot_get_bufaddr(rot, vb, d_frame, &d_frame->addr);
+
+       rot_hwset_dst_addr(rot, d_frame->addr.y, ROT_ADDR_Y);
+       rot_hwset_dst_addr(rot, d_frame->addr.cb, ROT_ADDR_CB);
+       rot_hwset_dst_addr(rot, d_frame->addr.cr, ROT_ADDR_CR);
+}
+
+static void rot_mapping_flip(struct rot_ctx *ctx, u32 *degree, u32 *flip)
+{
+       *degree = ctx->rotation;
+       *flip = ctx->flip;
+
+       if (ctx->flip == (ROT_VFLIP | ROT_HFLIP)) {
+               *flip = ROT_NOFLIP;
+               switch (ctx->rotation) {
+               case 0:
+                       *degree = 180;
+                       break;
+               case 90:
+                       *degree = 270;
+                       break;
+               case 180:
+                       *degree = 0;
+                       break;
+               case 270:
+                       *degree = 90;
+                       break;
+               }
+       }
+}
+
+static void rot_m2m_device_run(void *priv)
+{
+       struct rot_ctx *ctx = priv;
+       struct rot_frame *s_frame, *d_frame;
+       struct rot_dev *rot;
+       unsigned long flags, tmp;
+       u32 degree = 0, flip = 0;
+
+       spin_lock_irqsave(&ctx->slock, flags);
+
+       rot = ctx->rot_dev;
+
+       if (test_bit(DEV_RUN, &rot->state)) {
+               dev_err(rot->dev, "Rotator is already in progress\n");
+               goto run_unlock;
+       }
+
+       if (test_bit(DEV_SUSPEND, &rot->state)) {
+               dev_err(rot->dev, "Rotator is in suspend state\n");
+               goto run_unlock;
+       }
+
+       if (test_bit(CTX_ABORT, &ctx->flags)) {
+               rot_dbg("aborted rot device run\n");
+               goto run_unlock;
+       }
+
+       pm_runtime_get_sync(ctx->rot_dev->dev);
+
+       s_frame = &ctx->s_frame;
+       d_frame = &ctx->d_frame;
+
+       /* Configuration rotator registers */
+       rot_hwset_image_format(rot, s_frame->rot_fmt->pixelformat);
+       rot_mapping_flip(ctx, &degree, &flip);
+       rot_hwset_flip(rot, flip);
+       rot_hwset_rotation(rot, degree);
+
+       if (ctx->rotation == 90 || ctx->rotation == 270) {
+               tmp                     = d_frame->pix_mp.height;
+               d_frame->pix_mp.height  = d_frame->pix_mp.width;
+               d_frame->pix_mp.width   = tmp;
+       }
+
+       rot_hwset_src_imgsize(rot, s_frame);
+       rot_hwset_dst_imgsize(rot, d_frame);
+
+       rot_hwset_src_crop(rot, &s_frame->crop);
+       rot_hwset_dst_crop(rot, &d_frame->crop);
+
+       rot_set_frame_addr(ctx);
+
+       /* Enable rotator interrupt */
+       rot_hwset_irq_frame_done(rot, 1);
+       rot_hwset_irq_illegal_config(rot, 1);
+
+       set_bit(DEV_RUN, &rot->state);
+       set_bit(CTX_RUN, &ctx->flags);
+
+       /* Start rotate operation */
+       rot_hwset_start(rot);
+
+       /* Start watchdog timer */
+       rot->wdt.timer.expires = jiffies + ROT_TIMEOUT;
+       if (timer_pending(&rot->wdt.timer) == 0)
+               add_timer(&rot->wdt.timer);
+       else
+               mod_timer(&rot->wdt.timer, rot->wdt.timer.expires);
+
+run_unlock:
+       spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static void rot_m2m_job_abort(void *priv)
+{
+       struct rot_ctx *ctx = priv;
+       int ret;
+
+       ret = rot_ctx_stop_req(ctx);
+       if (ret < 0)
+               dev_err(ctx->rot_dev->dev, "wait timeout\n");
+}
+
+static struct v4l2_m2m_ops rot_m2m_ops = {
+       .device_run     = rot_m2m_device_run,
+       .job_abort      = rot_m2m_job_abort,
+};
+
+static int rot_register_m2m_device(struct rot_dev *rot)
+{
+       struct v4l2_device *v4l2_dev;
+       struct device *dev;
+       struct video_device *vfd;
+       int ret = 0;
+
+       if (!rot)
+               return -ENODEV;
+
+       dev = rot->dev;
+       v4l2_dev = &rot->m2m.v4l2_dev;
+
+       snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s.m2m",
+                       MODULE_NAME);
+
+       ret = v4l2_device_register(dev, v4l2_dev);
+       if (ret) {
+               dev_err(rot->dev, "failed to register v4l2 device\n");
+               return ret;
+       }
+
+       vfd = video_device_alloc();
+       if (!vfd) {
+               dev_err(rot->dev, "failed to allocate video device\n");
+               goto err_v4l2_dev;
+       }
+
+       vfd->fops       = &rot_v4l2_fops;
+       vfd->ioctl_ops  = &rot_v4l2_ioctl_ops;
+       vfd->release    = video_device_release;
+       snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", MODULE_NAME);
+
+       video_set_drvdata(vfd, rot);
+
+       rot->m2m.vfd = vfd;
+       rot->m2m.m2m_dev = v4l2_m2m_init(&rot_m2m_ops);
+       if (IS_ERR(rot->m2m.m2m_dev)) {
+               dev_err(rot->dev, "failed to initialize v4l2-m2m device\n");
+               ret = PTR_ERR(rot->m2m.m2m_dev);
+               goto err_dev_alloc;
+       }
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+       if (ret) {
+               dev_err(rot->dev, "failed to register video device\n");
+               goto err_m2m_dev;
+       }
+
+       return 0;
+
+err_m2m_dev:
+       v4l2_m2m_release(rot->m2m.m2m_dev);
+err_dev_alloc:
+       video_device_release(rot->m2m.vfd);
+err_v4l2_dev:
+       v4l2_device_unregister(v4l2_dev);
+
+       return ret;
+}
+
+static int rot_suspend(struct device *dev)
+{
+       struct rot_dev *rot;
+       int ret = 0;
+
+       rot = dev_get_drvdata(dev);
+       set_bit(DEV_SUSPEND, &rot->state);
+
+       ret = wait_event_timeout(rot->irq.wait,
+                       !test_bit(DEV_RUN, &rot->state), ROT_TIMEOUT);
+       if (ret == 0)
+               dev_err(rot->dev, "wait timeout\n");
+
+       return 0;
+}
+
+static int rot_resume(struct device *dev)
+{
+       struct rot_dev *rot;
+
+       rot = dev_get_drvdata(dev);
+       clear_bit(DEV_SUSPEND, &rot->state);
+
+       return 0;
+}
+
+static int rot_runtime_suspend(struct device *dev)
+{
+       struct rot_dev *rot;
+       struct platform_device *pdev;
+
+       pdev = to_platform_device(dev);
+       rot = (struct rot_dev *)platform_get_drvdata(pdev);
+
+       clk_disable(rot->clock);
+
+       return 0;
+}
+
+static int rot_runtime_resume(struct device *dev)
+{
+       struct rot_dev *rot;
+       struct platform_device *pdev;
+
+       pdev = to_platform_device(dev);
+       rot = (struct rot_dev *)platform_get_drvdata(pdev);
+
+       clk_enable(rot->clock);
+
+       return 0;
+}
+
+static const struct dev_pm_ops rot_pm_ops = {
+       .suspend                = rot_suspend,
+       .resume                 = rot_resume,
+       .runtime_suspend        = rot_runtime_suspend,
+       .runtime_resume         = rot_runtime_resume,
+};
+
+static int rot_probe(struct platform_device *pdev)
+{
+       struct exynos_rot_driverdata *drv_data;
+       struct rot_dev *rot;
+       struct resource *res;
+       int variant_num, ret = 0;
+
+       dev_info(&pdev->dev, "++%s\n", __func__);
+       drv_data = (struct exynos_rot_driverdata *)
+                       platform_get_device_id(pdev)->driver_data;
+
+       if (pdev->id >= drv_data->nr_dev) {
+               dev_err(&pdev->dev, "Invalid platform device id\n");
+               return -EINVAL;
+       }
+
+       rot = devm_kzalloc(&pdev->dev, sizeof(struct rot_dev), GFP_KERNEL);
+       if (!rot) {
+               dev_err(&pdev->dev, "no memory for rotator device\n");
+               return -ENOMEM;
+       }
+
+       rot->dev = &pdev->dev;
+       rot->id = pdev->id;
+       variant_num = (rot->id < 0) ? 0 : rot->id;
+       rot->variant = drv_data->variant[variant_num];
+
+       spin_lock_init(&rot->slock);
+
+       /* Get memory resource and map SFR region. */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rot->regs = devm_request_and_ioremap(&pdev->dev, res);
+       if (rot->regs == NULL) {
+               dev_err(&pdev->dev, "failed to claim register region\n");
+               return -ENOENT;
+       }
+
+       /* Get IRQ resource and register IRQ handler. */
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to get IRQ resource\n");
+               return -ENXIO;
+       }
+       rot->irq.num = res->start;
+
+       ret = devm_request_irq(&pdev->dev, rot->irq.num, rot_irq_handler, 0,
+                       pdev->name, rot);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to install irq\n");
+               return ret;
+       }
+
+       atomic_set(&rot->wdt.cnt, 0);
+       setup_timer(&rot->wdt.timer, rot_watchdog, (unsigned long)rot);
+
+       rot->clock = clk_get(rot->dev, "rotator");
+       if (IS_ERR(rot->clock)) {
+               dev_err(&pdev->dev, "failed to get clock for rotator\n");
+               return -ENXIO;
+       }
+
+       rot->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(rot->alloc_ctx)) {
+               dev_err(&pdev->dev, "failed to get alloc_ctx\n");
+               ret = -EPERM;
+               goto err;
+       }
+
+       ret = rot_register_m2m_device(rot);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register m2m device\n");
+               ret = -EPERM;
+               goto err;
+       }
+
+#ifdef CONFIG_PM_RUNTIME
+       pm_runtime_enable(&pdev->dev);
+#else
+       rot_runtime_resume(&pdev->dev);
+#endif
+
+       dev_info(&pdev->dev, "rotator registered successfully\n");
+
+       return 0;
+
+err:
+       clk_put(rot->clock);
+       return ret;
+}
+
+static int rot_remove(struct platform_device *pdev)
+{
+       struct rot_dev *rot = (struct rot_dev *)platform_get_drvdata(pdev);
+
+       clk_put(rot->clock);
+#ifdef CONFIG_PM_RUNTIME
+       pm_runtime_disable(&pdev->dev);
+#else
+       rot_runtime_suspend(&pdev->dev);
+#endif
+
+       if (timer_pending(&rot->wdt.timer))
+               del_timer(&rot->wdt.timer);
+
+       return 0;
+}
+
+static struct exynos_rot_variant rot_variant_exynos = {
+       .limit_rgb565 = {
+               .min_x = 16,
+               .min_y = 16,
+               .max_x = SZ_16K,
+               .max_y = SZ_16K,
+               .align = 2,
+       },
+       .limit_rgb888 = {
+               .min_x = 8,
+               .min_y = 8,
+               .max_x = SZ_8K,
+               .max_y = SZ_8K,
+               .align = 2,
+       },
+       .limit_yuv422 = {
+               .min_x = 16,
+               .min_y = 16,
+               .max_x = SZ_16K,
+               .max_y = SZ_16K,
+               .align = 2,
+       },
+       .limit_yuv420_2p = {
+               .min_x = 32,
+               .min_y = 32,
+               .max_x = SZ_32K,
+               .max_y = SZ_32K,
+               .align = 3,
+       },
+       .limit_yuv420_3p = {
+               .min_x = 64,
+               .min_y = 32,
+               .max_x = SZ_32K,
+               .max_y = SZ_32K,
+               .align = 4,
+       },
+};
+
+static struct exynos_rot_driverdata rot_drvdata_exynos = {
+       .variant = {
+               [0] = &rot_variant_exynos,
+       },
+       .nr_dev = 1,
+};
+
+static struct platform_device_id rot_driver_ids[] = {
+       {
+               .name           = MODULE_NAME,
+               .driver_data    = (unsigned long)&rot_drvdata_exynos,
+       },
+       {},
+};
+
+static struct platform_driver rot_driver = {
+       .probe          = rot_probe,
+       .remove         = rot_remove,
+       .id_table       = rot_driver_ids,
+       .driver = {
+               .name   = MODULE_NAME,
+               .owner  = THIS_MODULE,
+               .pm     = &rot_pm_ops,
+       }
+};
+
+module_platform_driver(rot_driver);
+
+MODULE_AUTHOR("Sunyoung, Kang <sy0816.k...@samsung.com>");
+MODULE_AUTHOR("Ayoung, Sim <a....@samsung.com>");
+MODULE_DESCRIPTION("Exynos Image Rotator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/exynos/rotator/rotator-regs.c 
b/drivers/media/video/exynos/rotator/rotator-regs.c
new file mode 100644
index 0000000..dcb71cf
--- /dev/null
+++ b/drivers/media/video/exynos/rotator/rotator-regs.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Register interface file for Exynos Rotator driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include "rotator.h"
+
+void rot_hwset_irq_frame_done(struct rot_dev *rot, u32 enable)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_CONFIG);
+
+       if (enable)
+               cfg |= ROTATOR_CONFIG_IRQ_DONE;
+       else
+               cfg &= ~ROTATOR_CONFIG_IRQ_DONE;
+
+       writel(cfg, rot->regs + ROTATOR_CONFIG);
+}
+
+void rot_hwset_irq_illegal_config(struct rot_dev *rot, u32 enable)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_CONFIG);
+
+       if (enable)
+               cfg |= ROTATOR_CONFIG_IRQ_ILLEGAL;
+       else
+               cfg &= ~ROTATOR_CONFIG_IRQ_ILLEGAL;
+
+       writel(cfg, rot->regs + ROTATOR_CONFIG);
+}
+
+int rot_hwset_image_format(struct rot_dev *rot, u32 pixelformat)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_CONTROL);
+       cfg &= ~ROTATOR_CONTROL_FMT_MASK;
+
+       switch (pixelformat) {
+       case V4L2_PIX_FMT_YUV420M:
+               cfg |= ROTATOR_CONTROL_FMT_YCBCR420_3P;
+               break;
+       case V4L2_PIX_FMT_NV12M:
+               cfg |= ROTATOR_CONTROL_FMT_YCBCR420_2P;
+               break;
+       case V4L2_PIX_FMT_YUYV:
+               cfg |= ROTATOR_CONTROL_FMT_YCBCR422;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               cfg |= ROTATOR_CONTROL_FMT_RGB565;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+               cfg |= ROTATOR_CONTROL_FMT_RGB888;
+               break;
+       default:
+               dev_err(rot->dev, "invalid pixelformat type\n");
+               return -EINVAL;
+       }
+       writel(cfg, rot->regs + ROTATOR_CONTROL);
+       return 0;
+}
+
+void rot_hwset_flip(struct rot_dev *rot, u32 direction)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_CONTROL);
+       cfg &= ~ROTATOR_CONTROL_FLIP_MASK;
+
+       if (direction == ROT_VFLIP)
+               cfg |= ROTATOR_CONTROL_FLIP_V;
+       else if (direction == ROT_HFLIP)
+               cfg |= ROTATOR_CONTROL_FLIP_H;
+
+       writel(cfg, rot->regs + ROTATOR_CONTROL);
+}
+
+void rot_hwset_rotation(struct rot_dev *rot, int degree)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_CONTROL);
+       cfg &= ~ROTATOR_CONTROL_ROT_MASK;
+
+       if (degree == 90)
+               cfg |= ROTATOR_CONTROL_ROT_90;
+       else if (degree == 180)
+               cfg |= ROTATOR_CONTROL_ROT_180;
+       else if (degree == 270)
+               cfg |= ROTATOR_CONTROL_ROT_270;
+
+       writel(cfg, rot->regs + ROTATOR_CONTROL);
+}
+
+void rot_hwset_start(struct rot_dev *rot)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_CONTROL);
+
+       cfg |= ROTATOR_CONTROL_START;
+
+       writel(cfg, rot->regs + ROTATOR_CONTROL);
+}
+
+void rot_hwset_src_addr(struct rot_dev *rot, dma_addr_t addr, u32 comp)
+{
+       writel(addr, rot->regs + ROTATOR_SRC_IMG_ADDR(comp));
+}
+
+void rot_hwset_dst_addr(struct rot_dev *rot, dma_addr_t addr, u32 comp)
+{
+       writel(addr, rot->regs + ROTATOR_DST_IMG_ADDR(comp));
+}
+
+void rot_hwset_src_imgsize(struct rot_dev *rot, struct rot_frame *frame)
+{
+       unsigned long cfg;
+
+       cfg = ROTATOR_SRCIMG_YSIZE(frame->pix_mp.height) |
+               ROTATOR_SRCIMG_XSIZE(frame->pix_mp.width);
+
+       writel(cfg, rot->regs + ROTATOR_SRCIMG);
+
+       cfg = ROTATOR_SRCROT_YSIZE(frame->pix_mp.height) |
+               ROTATOR_SRCROT_XSIZE(frame->pix_mp.width);
+
+       writel(cfg, rot->regs + ROTATOR_SRCROT);
+}
+
+void rot_hwset_src_crop(struct rot_dev *rot, struct v4l2_rect *rect)
+{
+       unsigned long cfg;
+
+       cfg = ROTATOR_SRC_Y(rect->top) |
+               ROTATOR_SRC_X(rect->left);
+
+       writel(cfg, rot->regs + ROTATOR_SRC);
+
+       cfg = ROTATOR_SRCROT_YSIZE(rect->height) |
+               ROTATOR_SRCROT_XSIZE(rect->width);
+
+       writel(cfg, rot->regs + ROTATOR_SRCROT);
+}
+
+void rot_hwset_dst_imgsize(struct rot_dev *rot, struct rot_frame *frame)
+{
+       unsigned long cfg;
+
+       cfg = ROTATOR_DSTIMG_YSIZE(frame->pix_mp.height) |
+               ROTATOR_DSTIMG_XSIZE(frame->pix_mp.width);
+
+       writel(cfg, rot->regs + ROTATOR_DSTIMG);
+}
+
+void rot_hwset_dst_crop(struct rot_dev *rot, struct v4l2_rect *rect)
+{
+       unsigned long cfg;
+
+       cfg =  ROTATOR_DST_Y(rect->top) |
+               ROTATOR_DST_X(rect->left);
+
+       writel(cfg, rot->regs + ROTATOR_DST);
+}
+
+void rot_hwget_irq_src(struct rot_dev *rot, enum rot_irq_src *irq)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_STATUS);
+       cfg = ROTATOR_STATUS_IRQ(cfg);
+
+       if (cfg == 1)
+               *irq = ISR_PEND_DONE;
+       else if (cfg == 2)
+               *irq = ISR_PEND_ILLEGAL;
+}
+
+void rot_hwset_irq_clear(struct rot_dev *rot, enum rot_irq_src *irq)
+{
+       unsigned long cfg = readl(rot->regs + ROTATOR_STATUS);
+       cfg |= ROTATOR_STATUS_IRQ_PENDING((u32)irq);
+
+       writel(cfg, rot->regs + ROTATOR_STATUS);
+}
+
+void rot_hwget_status(struct rot_dev *rot, enum rot_status *state)
+{
+       unsigned long cfg;
+
+       cfg = readl(rot->regs + ROTATOR_STATUS);
+       cfg &= ROTATOR_STATUS_MASK;
+
+       switch (cfg) {
+       case 0:
+               *state = ROT_IDLE;
+               break;
+       case 1:
+               *state = ROT_RESERVED;
+               break;
+       case 2:
+               *state = ROT_RUNNING;
+               break;
+       case 3:
+               *state = ROT_RUNNING_REMAIN;
+               break;
+       };
+}
+
+void rot_dump_register(struct rot_dev *rot)
+{
+       unsigned int tmp, i;
+
+       rot_dbg("dump rotator registers\n");
+       for (i = 0; i <= ROTATOR_DST; i += 0x4) {
+               tmp = readl(rot->regs + i);
+               rot_dbg("0x%08x: 0x%08x", i, tmp);
+       }
+}
diff --git a/drivers/media/video/exynos/rotator/rotator-regs.h 
b/drivers/media/video/exynos/rotator/rotator-regs.h
new file mode 100644
index 0000000..a603417
--- /dev/null
+++ b/drivers/media/video/exynos/rotator/rotator-regs.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Register header file for Exynos Rotator driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* Configuration */
+#define ROTATOR_CONFIG                         0x00
+#define ROTATOR_CONFIG_IRQ_ILLEGAL             (1 << 9)
+#define ROTATOR_CONFIG_IRQ_DONE                        (1 << 8)
+
+/* Image0 Control */
+#define ROTATOR_CONTROL                                0x10
+#define ROTATOR_CONTROL_PATTERN_WRITE          (1 << 16)
+#define ROTATOR_CONTROL_FMT_YCBCR420_3P                (0 << 8)
+#define ROTATOR_CONTROL_FMT_YCBCR420_2P                (1 << 8)
+#define ROTATOR_CONTROL_FMT_YCBCR422           (3 << 8)
+#define ROTATOR_CONTROL_FMT_RGB565             (4 << 8)
+#define ROTATOR_CONTROL_FMT_RGB888             (6 << 8)
+#define ROTATOR_CONTROL_FMT_MASK               (7 << 8)
+#define ROTATOR_CONTROL_FLIP_V                 (2 << 6)
+#define ROTATOR_CONTROL_FLIP_H                 (3 << 6)
+#define ROTATOR_CONTROL_FLIP_MASK              (3 << 6)
+#define ROTATOR_CONTROL_ROT_90                 (1 << 4)
+#define ROTATOR_CONTROL_ROT_180                        (2 << 4)
+#define ROTATOR_CONTROL_ROT_270                        (3 << 4)
+#define ROTATOR_CONTROL_ROT_MASK               (3 << 4)
+#define ROTATOR_CONTROL_START                  (1 << 0)
+
+/* Status */
+#define ROTATOR_STATUS                         0x20
+#define ROTATOR_STATUS_IRQ_PENDING(x)          (1 << (x))
+#define ROTATOR_STATUS_IRQ(x)                  (((x) >> 8) & 0x3)
+#define ROTATOR_STATUS_MASK                    (3 << 0)
+
+/* Sourc Image Base Address */
+#define ROTATOR_SRC_IMG_ADDR(n)                        (0x30 + ((n) << 2))
+
+/* Source Image X,Y Size */
+#define ROTATOR_SRCIMG                         0x3c
+#define ROTATOR_SRCIMG_YSIZE(x)                        ((x) << 16)
+#define ROTATOR_SRCIMG_XSIZE(x)                        ((x) << 0)
+
+/* Source Image X,Y Coordinates */
+#define ROTATOR_SRC                            0x40
+#define ROTATOR_SRC_Y(x)                       ((x) << 16)
+#define ROTATOR_SRC_X(x)                       ((x) << 0)
+
+/* Source Image Rotation Size */
+#define ROTATOR_SRCROT                         0x44
+#define ROTATOR_SRCROT_YSIZE(x)                        ((x) << 16)
+#define ROTATOR_SRCROT_XSIZE(x)                        ((x) << 0)
+
+/* Destination Image Base Address */
+#define ROTATOR_DST_IMG_ADDR(n)                        (0x50 + ((n) << 2))
+
+/* Destination Image X,Y Size */
+#define ROTATOR_DSTIMG                         0x5c
+#define ROTATOR_DSTIMG_YSIZE(x)                        ((x) << 16)
+#define ROTATOR_DSTIMG_XSIZE(x)                        ((x) << 0)
+
+/* Destination Image X,Y Coordinates */
+#define ROTATOR_DST                            0x60
+#define ROTATOR_DST_Y(x)                       ((x) << 16)
+#define ROTATOR_DST_X(x)                       ((x) << 0)
diff --git a/drivers/media/video/exynos/rotator/rotator.h 
b/drivers/media/video/exynos/rotator/rotator.h
new file mode 100644
index 0000000..4034383
--- /dev/null
+++ b/drivers/media/video/exynos/rotator/rotator.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Header file for Exynos Rotator driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef ROTATOR__H_
+#define ROTATOR__H_
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rotator-regs.h"
+
+extern int log_level;
+
+#define rot_dbg(fmt, args...)                                          \
+       do {                                                            \
+               if (log_level)                                          \
+                       printk(KERN_DEBUG "[%s:%d] "                    \
+                       fmt, __func__, __LINE__, ##args);               \
+       } while (0)
+
+/* Time to wait for frame done interrupt */
+#define ROT_TIMEOUT                    (2 * HZ)
+#define ROT_WDT_CNT                    5
+#define MODULE_NAME            "exynos-rot"
+#define ROT_MAX_DEVS                   1
+
+/* Address index */
+#define ROT_ADDR_RGB                   0
+#define ROT_ADDR_Y                     0
+#define ROT_ADDR_CB                    1
+#define ROT_ADDR_CBCR                  1
+#define ROT_ADDR_CR                    2
+
+/* Rotator flip direction */
+#define ROT_NOFLIP             (1 << 0)
+#define ROT_VFLIP              (1 << 1)
+#define ROT_HFLIP              (1 << 2)
+
+/* Rotator hardware device state */
+#define DEV_RUN                        (1 << 0)
+#define DEV_SUSPEND            (1 << 1)
+
+/* Rotator m2m context state */
+#define CTX_PARAMS             (1 << 0)
+#define CTX_STREAMING          (1 << 1)
+#define CTX_RUN                        (1 << 2)
+#define CTX_ABORT              (1 << 3)
+#define CTX_SRC                        (1 << 4)
+#define CTX_DST                        (1 << 5)
+
+enum rot_irq_src {
+       ISR_PEND_DONE = 8,
+       ISR_PEND_ILLEGAL = 9,
+};
+
+enum rot_status {
+       ROT_IDLE,
+       ROT_RESERVED,
+       ROT_RUNNING,
+       ROT_RUNNING_REMAIN,
+};
+
+/*
+ * struct exynos_rot_size_limit - Rotator variant size  information
+ *
+ * @min_x: minimum pixel x size
+ * @min_y: minimum pixel y size
+ * @max_x: maximum pixel x size
+ * @max_y: maximum pixel y size
+ */
+struct exynos_rot_size_limit {
+       u32 min_x;
+       u32 min_y;
+       u32 max_x;
+       u32 max_y;
+       u32 align;
+};
+
+struct exynos_rot_variant {
+       struct exynos_rot_size_limit limit_rgb565;
+       struct exynos_rot_size_limit limit_rgb888;
+       struct exynos_rot_size_limit limit_yuv422;
+       struct exynos_rot_size_limit limit_yuv420_2p;
+       struct exynos_rot_size_limit limit_yuv420_3p;
+};
+
+/*
+ * struct exynos_rot_driverdata - per device type driver data for init time.
+ *
+ * @variant: the variant information for this driver.
+ * @nr_dev: number of devices available in SoC
+ */
+struct exynos_rot_driverdata {
+       struct exynos_rot_variant       *variant[ROT_MAX_DEVS];
+       int                             nr_dev;
+};
+
+/**
+ * struct rot_fmt - the driver's internal color format data
+ * @name: format description
+ * @pixelformat: the fourcc code for this format, 0 if not applicable
+ * @num_planes: number of physically non-contiguous data planes
+ * @num_comp: number of color components(ex. RGB, Y, Cb, Cr)
+ * @bitperpixel: bits per pixel
+ */
+struct rot_fmt {
+       char    *name;
+       u32     pixelformat;
+       u16     num_planes;
+       u16     num_comp;
+       u32     bitperpixel[VIDEO_MAX_PLANES];
+};
+
+struct rot_addr {
+       dma_addr_t      y;
+       dma_addr_t      cb;
+       dma_addr_t      cr;
+};
+
+/*
+ * struct rot_frame - source/target frame properties
+ * @fmt:       buffer format(like virtual screen)
+ * @crop:      image size / position
+ * @addr:      buffer start address(access using ROT_ADDR_XXX)
+ * @bytesused: image size in bytes (w x h x bpp)
+ * @cacheable: cache property for image address
+ */
+struct rot_frame {
+       struct rot_fmt                  *rot_fmt;
+       struct v4l2_pix_format_mplane   pix_mp;
+       struct v4l2_rect                crop;
+       struct rot_addr                 addr;
+       unsigned long                   bytesused[VIDEO_MAX_PLANES];
+       bool                            cacheable;
+};
+
+/*
+ * struct rot_m2m_device - v4l2 memory-to-memory device data
+ * @v4l2_dev: v4l2 device
+ * @vfd: the video device node
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @in_use: the open count
+ */
+struct rot_m2m_device {
+       struct v4l2_device      v4l2_dev;
+       struct video_device     *vfd;
+       struct v4l2_m2m_dev     *m2m_dev;
+       atomic_t                in_use;
+};
+
+/*
+ * struct rot_irq - Rotator irq information
+ * @num:       Rotator interrupt number
+ * @wait:      interrupt handler waitqueue
+ */
+struct rot_irq {
+       int                     num;
+       wait_queue_head_t       wait;
+};
+
+struct rot_wdt {
+       struct timer_list       timer;
+       atomic_t                cnt;
+};
+
+struct rot_ctx;
+struct rot_vb2;
+
+/*
+ * struct rot_dev - the abstraction for Rotator device
+ * @dev:       pointer to the Rotator device
+ * @pdata:     pointer to the device platform data
+ * @variant:   the IP variant information
+ * @m2m:       memory-to-memory V4L2 device information
+ * @id:                Rotator device index (0..ROT_MAX_DEVS)
+ * @clock:     clock required for Rotator operation
+ * @regs:      the mapped hardware registers
+ * @regs_res:  the resource claimed for IO registers
+ * @irq:       irq information
+ * @ws:                work struct
+ * @state:     device state flags
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @rot_vb2:   videobuf2 memory allocator callbacks
+ * @slock:     the spinlock protecting this data structure
+ * @lock:      the mutex protecting this data structure
+ * @wdt:       watchdog timer information
+ */
+struct rot_dev {
+       struct device                   *dev;
+       struct exynos_platform_rot      *pdata;
+       struct exynos_rot_variant       *variant;
+       struct rot_m2m_device           m2m;
+       int                             id;
+       struct clk                      *clock;
+       void __iomem                    *regs;
+       struct rot_irq                  irq;
+       struct work_struct              ws;
+       unsigned long                   state;
+       struct vb2_alloc_ctx            *alloc_ctx;
+       const struct rot_vb2            *vb2;
+       spinlock_t                      slock;
+       struct mutex                    lock;
+       struct rot_wdt                  wdt;
+};
+
+/*
+ * rot_ctx - the abstration for Rotator open context
+ * @rot_dev:           the Rotator device this context applies to
+ * @m2m_ctx:           memory-to-memory device context
+ * @frame:             source frame properties
+ * @ctrl_handler:      v4l2 controls handler
+ * @fh:                        v4l2 file handle
+ * @rotation:          image clockwise rotation in degrees
+ * @flip:              image flip mode
+ * @state:             context state flags
+ * @slock:             spinlock protecting this data structure
+ */
+struct rot_ctx {
+       struct rot_dev          *rot_dev;
+       struct v4l2_m2m_ctx     *m2m_ctx;
+       struct rot_frame        s_frame;
+       struct rot_frame        d_frame;
+       struct v4l2_ctrl_handler        ctrl_handler;
+       struct v4l2_fh                  fh;
+       int                     rotation;
+       u32                     flip;
+       unsigned long           flags;
+       spinlock_t              slock;
+};
+
+static inline struct rot_frame *ctx_get_frame(struct rot_ctx *ctx,
+                                               enum v4l2_buf_type type)
+{
+       struct rot_frame *frame;
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+               if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+                       frame = &ctx->s_frame;
+               else
+                       frame = &ctx->d_frame;
+       } else {
+               dev_err(ctx->rot_dev->dev,
+                               "Wrong V4L2 buffer type %d\n", type);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return frame;
+}
+
+#define fh_to_rot_ctx(__fh)    container_of(__fh, struct rot_ctx, fh)
+
+void rot_hwset_irq_frame_done(struct rot_dev *rot, u32 enable);
+void rot_hwset_irq_illegal_config(struct rot_dev *rot, u32 enable);
+int rot_hwset_image_format(struct rot_dev *rot, u32 pixelformat);
+void rot_hwset_flip(struct rot_dev *rot, u32 direction);
+void rot_hwset_rotation(struct rot_dev *rot, int degree);
+void rot_hwset_start(struct rot_dev *rot);
+void rot_hwset_src_addr(struct rot_dev *rot, dma_addr_t addr, u32 comp);
+void rot_hwset_dst_addr(struct rot_dev *rot, dma_addr_t addr, u32 comp);
+void rot_hwset_src_imgsize(struct rot_dev *rot, struct rot_frame *frame);
+void rot_hwset_src_crop(struct rot_dev *rot, struct v4l2_rect *rect);
+void rot_hwset_dst_imgsize(struct rot_dev *rot, struct rot_frame *frame);
+void rot_hwset_dst_crop(struct rot_dev *rot, struct v4l2_rect *rect);
+void rot_hwget_irq_src(struct rot_dev *rot, enum rot_irq_src *irq);
+void rot_hwset_irq_clear(struct rot_dev *rot, enum rot_irq_src *irq);
+void rot_hwget_status(struct rot_dev *rot, enum rot_status *state);
+void rot_dump_register(struct rot_dev *rot);
+
+#endif /* ROTATOR__H_ */
-- 
1.7.1


--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to