diff mbox

[v2,01/01] media: gscaler: Add new driver for generic scaler

Message ID 1341484061-10914-2-git-send-email-shaik.ameer@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Shaik Ameer Basha July 5, 2012, 10:27 a.m. UTC
This patch add support for gscaler (generic scaler) device which is a
new device for scaling and color space conversion on EXYNOS5 SoCs.

This device supports the followings as key feature.
 1) Input image format
   - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, TILE
 2) Output image format
   - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, YUV444
 3) Input rotation
   - 0/90/180/270 degree, X/Y Flip
 4) Scale ratio
   - 1/16 scale down to 8 scale up
 5) CSC
   - RGB to YUV / YUV to RGB
 6) Size
   - 2048 x 2048 for tile or rotation
   - 4800 x 3344 other case

Signed-off-by: Hynwoong Kim <khw0178.kim@samsung.com>
Signed-off-by: Sungchun Kang <sungchun.kang@samsung.com>
Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
---
 drivers/media/video/Kconfig               |   10 +
 drivers/media/video/Makefile              |    1 +
 drivers/media/video/exynos/Kconfig        |   11 +
 drivers/media/video/exynos/Makefile       |    1 +
 drivers/media/video/exynos/gsc/Kconfig    |    7 +
 drivers/media/video/exynos/gsc/Makefile   |    3 +
 drivers/media/video/exynos/gsc/gsc-core.c | 1304 +++++++++++++++++++++++++++++
 drivers/media/video/exynos/gsc/gsc-core.h |  652 ++++++++++++++
 drivers/media/video/exynos/gsc/gsc-m2m.c  |  751 +++++++++++++++++
 drivers/media/video/exynos/gsc/gsc-regs.c |  579 +++++++++++++
 drivers/media/video/exynos/gsc/gsc-regs.h |  211 +++++
 include/linux/videodev2.h                 |    2 +
 12 files changed, 3532 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/exynos/Kconfig
 create mode 100644 drivers/media/video/exynos/Makefile
 create mode 100644 drivers/media/video/exynos/gsc/Kconfig
 create mode 100644 drivers/media/video/exynos/gsc/Makefile
 create mode 100644 drivers/media/video/exynos/gsc/gsc-core.c
 create mode 100644 drivers/media/video/exynos/gsc/gsc-core.h
 create mode 100644 drivers/media/video/exynos/gsc/gsc-m2m.c
 create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.c
 create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.h

Comments

Pawel Osciak July 11, 2012, 5:52 p.m. UTC | #1
Hi Shaik,
Thanks for the patches. Please find my comments inline.
Please also make sure you run checkpatch.

On Thu, Jul 5, 2012 at 3:27 AM, Shaik Ameer Basha
<shaik.ameer@samsung.com> wrote:
> This patch add support for gscaler (generic scaler) device which is a
> new device for scaling and color space conversion on EXYNOS5 SoCs.
>
> This device supports the followings as key feature.
>  1) Input image format
>    - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, TILE
>  2) Output image format
>    - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, YUV444
>  3) Input rotation
>    - 0/90/180/270 degree, X/Y Flip
>  4) Scale ratio
>    - 1/16 scale down to 8 scale up
>  5) CSC
>    - RGB to YUV / YUV to RGB
>  6) Size
>    - 2048 x 2048 for tile or rotation
>    - 4800 x 3344 other case
>
> Signed-off-by: Hynwoong Kim <khw0178.kim@samsung.com>
> Signed-off-by: Sungchun Kang <sungchun.kang@samsung.com>
> Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
> ---
>  drivers/media/video/Kconfig               |   10 +
>  drivers/media/video/Makefile              |    1 +
>  drivers/media/video/exynos/Kconfig        |   11 +
>  drivers/media/video/exynos/Makefile       |    1 +
>  drivers/media/video/exynos/gsc/Kconfig    |    7 +
>  drivers/media/video/exynos/gsc/Makefile   |    3 +
>  drivers/media/video/exynos/gsc/gsc-core.c | 1304 +++++++++++++++++++++++++++++
>  drivers/media/video/exynos/gsc/gsc-core.h |  652 ++++++++++++++
>  drivers/media/video/exynos/gsc/gsc-m2m.c  |  751 +++++++++++++++++
>  drivers/media/video/exynos/gsc/gsc-regs.c |  579 +++++++++++++
>  drivers/media/video/exynos/gsc/gsc-regs.h |  211 +++++
>  include/linux/videodev2.h                 |    2 +
>  12 files changed, 3532 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/exynos/Kconfig
>  create mode 100644 drivers/media/video/exynos/Makefile
>  create mode 100644 drivers/media/video/exynos/gsc/Kconfig
>  create mode 100644 drivers/media/video/exynos/gsc/Makefile
>  create mode 100644 drivers/media/video/exynos/gsc/gsc-core.c
>  create mode 100644 drivers/media/video/exynos/gsc/gsc-core.h
>  create mode 100644 drivers/media/video/exynos/gsc/gsc-m2m.c
>  create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.c
>  create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.h
>
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 99937c9..0ff1abf 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -1153,6 +1153,7 @@ config VIDEO_ATMEL_ISI
>
>  source "drivers/media/video/s5p-fimc/Kconfig"
>  source "drivers/media/video/s5p-tv/Kconfig"
> +source "drivers/media/video/exynos/Kconfig"

I believe we'd prefer to have it under exynos-gscaler directory and
not create general platform directories.

>
>  endif # V4L_PLATFORM_DRIVERS
>  endif # VIDEO_CAPTURE_DRIVERS
> @@ -1215,4 +1216,13 @@ config VIDEO_MX2_EMMAPRP
>             memory to memory. Operations include resizing and format
>             conversion.
>
> +config VIDEO_SAMSUNG_GSCALER
> +        tristate "Samsung Exynos GSC driver"
> +        depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
> +        select VIDEOBUF2_DMA_CONTIG
> +        select V4L2_MEM2MEM_DEV
> +        select DMA_SHARED_BUFFER
> +        help
> +            This is v4l2 based g-scaler driver for EXYNOS5
> +
>  endif # V4L_MEM2MEM_DRIVERS
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index d209de0..4389f3e 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -192,6 +192,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC)        += s5p-fimc/
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG)   += s5p-jpeg/
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)    += s5p-mfc/
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)     += s5p-tv/
> +obj-$(CONFIG_VIDEO_EXYNOS)              += exynos/
>
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D)    += s5p-g2d/
>
> diff --git a/drivers/media/video/exynos/Kconfig b/drivers/media/video/exynos/Kconfig
> new file mode 100644
> index 0000000..f5836a2
> --- /dev/null
> +++ b/drivers/media/video/exynos/Kconfig
> @@ -0,0 +1,11 @@
> +config VIDEO_EXYNOS
> +       bool "Exynos Multimedia Devices"
> +       depends on ARCH_EXYNOS5
> +       select VIDEO_FIXED_MINOR_RANGES
> +       select VIDEOBUF2_DMA_CONTIG
> +       help
> +         This is a representative exynos multimedia device.
> +
> +if VIDEO_EXYNOS
> +       source "drivers/media/video/exynos/gsc/Kconfig"
> +endif

Looks like you have two different Kconfigs and Makefiles for the same
thing. Please don't create an exynos/gsc directories, just exynos-gsc
and put everything in there. Don't create any Kconfigs, just add the
entry to the drivers/media/video/Kconfig and only have a Makefile in
exynos-gsc. s5p-g2d is a good example.

> diff --git a/drivers/media/video/exynos/Makefile b/drivers/media/video/exynos/Makefile
> new file mode 100644
> index 0000000..1666724
> --- /dev/null
> +++ b/drivers/media/video/exynos/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_VIDEO_EXYNOS_GSCALER)      += gsc/
> diff --git a/drivers/media/video/exynos/gsc/Kconfig b/drivers/media/video/exynos/gsc/Kconfig
> new file mode 100644
> index 0000000..9036078
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/Kconfig
> @@ -0,0 +1,7 @@
> +config VIDEO_EXYNOS_GSCALER
> +       bool "Exynos G-Scaler driver"
> +       depends on VIDEO_EXYNOS
> +       select MEDIA_EXYNOS
> +       select V4L2_MEM2MEM_DEV
> +       help
> +         This is a v4l2 driver for exynos G-Scaler device.
> diff --git a/drivers/media/video/exynos/gsc/Makefile b/drivers/media/video/exynos/gsc/Makefile
> new file mode 100644
> index 0000000..d3f66e4
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/Makefile
> @@ -0,0 +1,3 @@
> +gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o
> +
> +obj-$(CONFIG_VIDEO_SAMSUNG_GSCALER)    += gsc.o
> diff --git a/drivers/media/video/exynos/gsc/gsc-core.c b/drivers/media/video/exynos/gsc/gsc-core.c
> new file mode 100644
> index 0000000..06bd24f
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-core.c
> @@ -0,0 +1,1304 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-core.c

No need for this line.

> + *
> + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
> + *             http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/bug.h>
> +#include <linux/interrupt.h>
> +#include <linux/workqueue.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <media/v4l2-ioctl.h>
> +#include <linux/of.h>
> +#include "gsc-core.h"
> +
> +#define GSC_CLOCK_GATE_NAME    "gscl"
> +
> +int gsc_dbg;
> +module_param(gsc_dbg, int, 0644);
> +
> +
> +static struct gsc_fmt gsc_formats[] = {
> +       {
> +               .name           = "RGB565",
> +               .pixelformat    = V4L2_PIX_FMT_RGB565X,
> +               .depth          = { 16 },
> +               .color          = GSC_RGB,
> +               .num_planes     = 1,
> +               .num_comp       = 1,
> +       }, {
> +               .name           = "XRGB-8-8-8-8, 32 bpp",
> +               .pixelformat    = V4L2_PIX_FMT_RGB32,
> +               .depth          = { 32 },
> +               .color          = GSC_RGB,
> +               .num_planes     = 1,
> +               .num_comp       = 1,
> +       }, {
> +               .name           = "YUV 4:2:2 packed, YCbYCr",
> +               .pixelformat    = V4L2_PIX_FMT_YUYV,
> +               .depth          = { 16 },
> +               .color          = GSC_YUV422,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 1,
> +               .num_comp       = 1,
> +               .mbus_code      = V4L2_MBUS_FMT_YUYV8_2X8,
> +       }, {
> +               .name           = "YUV 4:2:2 packed, CbYCrY",
> +               .pixelformat    = V4L2_PIX_FMT_UYVY,
> +               .depth          = { 16 },
> +               .color          = GSC_YUV422,
> +               .yorder         = GSC_LSB_C,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 1,
> +               .num_comp       = 1,
> +               .mbus_code      = V4L2_MBUS_FMT_UYVY8_2X8,
> +       }, {
> +               .name           = "YUV 4:2:2 packed, CrYCbY",
> +               .pixelformat    = V4L2_PIX_FMT_VYUY,
> +               .depth          = { 16 },
> +               .color          = GSC_YUV422,
> +               .yorder         = GSC_LSB_C,
> +               .corder         = GSC_CRCB,
> +               .num_planes     = 1,
> +               .num_comp       = 1,
> +               .mbus_code      = V4L2_MBUS_FMT_VYUY8_2X8,
> +       }, {
> +               .name           = "YUV 4:2:2 packed, YCrYCb",
> +               .pixelformat    = V4L2_PIX_FMT_YVYU,
> +               .depth          = { 16 },
> +               .color          = GSC_YUV422,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CRCB,
> +               .num_planes     = 1,
> +               .num_comp       = 1,
> +               .mbus_code      = V4L2_MBUS_FMT_YVYU8_2X8,
> +       }, {
> +               .name           = "YUV 4:4:4 planar, YCbYCr",
> +               .pixelformat    = V4L2_PIX_FMT_YUV32,
> +               .depth          = { 32 },
> +               .color          = GSC_YUV444,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 1,
> +               .num_comp       = 1,
> +       }, {
> +               .name           = "YUV 4:2:2 planar, Y/Cb/Cr",
> +               .pixelformat    = V4L2_PIX_FMT_YUV422P,
> +               .depth          = { 16 },
> +               .color          = GSC_YUV422,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 1,
> +               .num_comp       = 3,
> +       }, {
> +               .name           = "YUV 4:2:2 planar, Y/CbCr",
> +               .pixelformat    = V4L2_PIX_FMT_NV16,
> +               .depth          = { 16 },
> +               .color          = GSC_YUV422,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 1,
> +               .num_comp       = 2,
> +       }, {
> +               .name           = "YUV 4:2:2 planar, Y/CrCb",
> +               .pixelformat    = V4L2_PIX_FMT_NV61,
> +               .depth          = { 16 },
> +               .color          = GSC_YUV422,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CRCB,
> +               .num_planes     = 1,
> +               .num_comp       = 2,
> +       }, {
> +               .name           = "YUV 4:2:0 planar, YCbCr",
> +               .pixelformat    = V4L2_PIX_FMT_YUV420,
> +               .depth          = { 12 },
> +               .color          = GSC_YUV420,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 1,
> +               .num_comp       = 3,
> +       }, {
> +               .name           = "YUV 4:2:0 planar, YCbCr",

Same name as above, should be YCrCb?

> +               .pixelformat    = V4L2_PIX_FMT_YVU420,
> +               .depth          = { 12 },
> +               .color          = GSC_YUV420,

Is this a copy-paste bug or should be the same as above?

> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CRCB,
> +               .num_planes     = 1,
> +               .num_comp       = 3,
> +
> +       }, {
> +               .name           = "YUV 4:2:0 planar, Y/CbCr",
> +               .pixelformat    = V4L2_PIX_FMT_NV12,
> +               .depth          = { 12 },
> +               .color          = GSC_YUV420,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 1,
> +               .num_comp       = 2,
> +       }, {
> +               .name           = "YUV 4:2:0 planar, Y/CrCb",
> +               .pixelformat    = V4L2_PIX_FMT_NV21,
> +               .depth          = { 12 },
> +               .color          = GSC_YUV420,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CRCB,
> +               .num_planes     = 1,
> +               .num_comp       = 2,
> +       }, {
> +               .name           = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
> +               .pixelformat    = V4L2_PIX_FMT_NV12M,
> +               .depth          = { 8, 4 },
> +               .color          = GSC_YUV420,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 2,
> +               .num_comp       = 2,
> +       }, {
> +               .name           = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
> +               .pixelformat    = V4L2_PIX_FMT_YUV420M,
> +               .depth          = { 8, 2, 2 },
> +               .color          = GSC_YUV420,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 3,
> +               .num_comp       = 3,
> +       }, {
> +               .name           = "YUV 4:2:0 non-contiguous 3-planar, Y/Cr/Cb",

YUV -> YVU?

> +               .pixelformat    = V4L2_PIX_FMT_YVU420M,
> +               .depth          = { 8, 2, 2 },
> +               .color          = GSC_YUV420,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CRCB,
> +               .num_planes     = 3,
> +               .num_comp       = 3,
> +       }, {
> +               .name           =
> +                       "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",
> +               .pixelformat    = V4L2_PIX_FMT_NV12MT_16X16,
> +               .depth          = { 8, 4 },
> +               .color          = GSC_YUV420,
> +               .yorder         = GSC_LSB_Y,
> +               .corder         = GSC_CBCR,
> +               .num_planes     = 2,
> +               .num_comp       = 2,
> +       },
> +};
> +
> +struct gsc_fmt *get_format(int index)
> +{

if (index >= ARRAY_SIZE(gsc_formats))
         return NULL;

> +       return &gsc_formats[index];
> +}
> +
> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
> +{
> +       struct gsc_fmt *fmt, *def_fmt = NULL;
> +       unsigned int i;
> +
> +       if (index >= ARRAY_SIZE(gsc_formats))
> +               return NULL;
> +
> +       for (i = 0; i < ARRAY_SIZE(gsc_formats); ++i) {
> +               fmt = get_format(i);
> +               if (pixelformat && fmt->pixelformat == *pixelformat)
> +                       return fmt;
> +               if (mbus_code && fmt->mbus_code == *mbus_code)
> +                       return fmt;
> +               if (index == i)
> +                       def_fmt = fmt;
> +       }
> +       return def_fmt;
> +
> +}
> +
> +void gsc_set_frame_size(struct gsc_frame *frame, int width, int height)
> +{
> +       frame->f_width  = width;
> +       frame->f_height = height;
> +       frame->crop.width = width;
> +       frame->crop.height = height;
> +       frame->crop.left = 0;
> +       frame->crop.top = 0;
> +}
> +
> +int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
> +                                                               u32 *ratio)
> +{
> +       if ((dst > src) || (dst >= src / var->poly_sc_down_max)) {
> +               *ratio = 1;
> +               return 0;
> +       }
> +
> +       if ((src / var->poly_sc_down_max / var->pre_sc_down_max) > dst) {
> +               gsc_err("scale ratio exceeded maximun scale down ratio(1/16)");
> +               return -EINVAL;
> +       }
> +
> +       *ratio = (dst > (src / 8)) ? 2 : 4;
> +
> +       return 0;
> +}
> +
> +void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh)
> +{
> +       if (hratio == 4 && vratio == 4)
> +               *sh = 4;
> +       else if ((hratio == 4 && vratio == 2) ||
> +                (hratio == 2 && vratio == 4))
> +               *sh = 3;
> +       else if ((hratio == 4 && vratio == 1) ||
> +                (hratio == 1 && vratio == 4) ||
> +                (hratio == 2 && vratio == 2))
> +               *sh = 2;
> +       else if (hratio == 1 && vratio == 1)
> +               *sh = 0;
> +       else
> +               *sh = 1;
> +}
> +
> +void gsc_check_src_scale_info(struct gsc_variant *var,
> +                               struct gsc_frame *s_frame, u32 *wratio,
> +                                u32 tx, u32 ty, u32 *hratio)
> +{
> +       int remainder = 0, walign, halign;
> +
> +       if (is_yuv420(s_frame->fmt->color)) {
> +               walign = GSC_SC_ALIGN_4;
> +               halign = GSC_SC_ALIGN_4;
> +       } else if (is_yuv422(s_frame->fmt->color)) {
> +               walign = GSC_SC_ALIGN_4;
> +               halign = GSC_SC_ALIGN_2;
> +       } else {
> +               walign = GSC_SC_ALIGN_2;
> +               halign = GSC_SC_ALIGN_2;
> +       }
> +
> +       remainder = s_frame->crop.width % (*wratio * walign);
> +       if (remainder) {
> +               s_frame->crop.width -= remainder;
> +               gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio);
> +               gsc_info("cropped src width size is recalculated from %d to %d",
> +                       s_frame->crop.width + remainder, s_frame->crop.width);
> +       }
> +
> +       remainder = s_frame->crop.height % (*hratio * halign);
> +       if (remainder) {
> +               s_frame->crop.height -= remainder;
> +               gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio);
> +               gsc_info("cropped src height size is recalculated from %d to %d",
> +                       s_frame->crop.height + remainder, s_frame->crop.height);
> +       }
> +}
> +
> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
> +{
> +       struct gsc_fmt *fmt;
> +
> +       fmt = find_fmt(NULL, NULL, f->index);
> +       if (!fmt)
> +               return -EINVAL;
> +
> +       strncpy(f->description, fmt->name, sizeof(f->description) - 1);
> +       f->pixelformat = fmt->pixelformat;
> +
> +       return 0;
> +}
> +
> +u32 get_plane_size(struct gsc_frame *frame, unsigned int plane)
> +{
> +       if (!frame || plane >= frame->fmt->num_planes) {
> +               gsc_err("Invalid argument");
> +               return 0;
> +       }
> +
> +       return frame->payload[plane];
> +}
> +
> +u32 get_plane_info(struct gsc_frame frm, u32 addr, u32 *index)
> +{
> +       if (frm.addr.y == addr) {
> +               *index = 0;
> +               return frm.addr.y;
> +       } else if (frm.addr.cb == addr) {
> +               *index = 1;
> +               return frm.addr.cb;
> +       } else if (frm.addr.cr == addr) {
> +               *index = 2;
> +               return frm.addr.cr;
> +       } else {
> +               gsc_err("Plane address is wrong");
> +               return -EINVAL;
> +       }
> +}
> +
> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm)
> +{
> +       u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
> +       f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
> +
> +       f_chk_addr = frm.addr.y;
> +       f_chk_len = frm.payload[0];
> +       if (frm.fmt->num_planes == 2) {
> +               s_chk_addr = frm.addr.cb;
> +               s_chk_len = frm.payload[1];
> +       } else if (frm.fmt->num_planes == 3) {
> +               u32 low_addr, low_plane, mid_addr, mid_plane;
> +               u32 high_addr, high_plane;
> +               u32 t_min, t_max;
> +
> +               t_min = min3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> +               low_addr = get_plane_info(frm, t_min, &low_plane);
> +               t_max = max3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> +               high_addr = get_plane_info(frm, t_max, &high_plane);
> +
> +               mid_plane = 3 - (low_plane + high_plane);
> +               if (mid_plane == 0)
> +                       mid_addr = frm.addr.y;
> +               else if (mid_plane == 1)
> +                       mid_addr = frm.addr.cb;
> +               else if (mid_plane == 2)
> +                       mid_addr = frm.addr.cr;
> +               else
> +                       return;
> +
> +               f_chk_addr = low_addr;
> +               if (mid_addr + frm.payload[mid_plane] - low_addr >
> +                   high_addr + frm.payload[high_plane] - mid_addr) {
> +                       f_chk_len = frm.payload[low_plane];
> +                       s_chk_addr = mid_addr;
> +                       s_chk_len = high_addr +
> +                                       frm.payload[high_plane] - mid_addr;
> +               } else {
> +                       f_chk_len = mid_addr +
> +                                       frm.payload[mid_plane] - low_addr;
> +                       s_chk_addr = high_addr;
> +                       s_chk_len = frm.payload[high_plane];
> +               }
> +       }
> +       gsc_dbg("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
> +               f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
> +}
> +
> +int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
> +{
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +       struct gsc_variant *variant = gsc->variant;
> +       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
> +       struct gsc_fmt *fmt;
> +       u32 max_w, max_h, mod_x, mod_y;
> +       u32 min_w, min_h, tmp_w, tmp_h;
> +       int i;
> +
> +       gsc_dbg("user put w: %d, h: %d", pix_mp->width, pix_mp->height);
> +
> +       fmt = find_fmt(&pix_mp->pixelformat, NULL, 0);
> +       if (!fmt) {
> +               gsc_err("pixelformat format (0x%X) invalid\n",
> +                                               pix_mp->pixelformat);
> +               return -EINVAL;
> +       }
> +
> +       if (pix_mp->field == V4L2_FIELD_ANY)
> +               pix_mp->field = V4L2_FIELD_NONE;
> +       else if (pix_mp->field != V4L2_FIELD_NONE) {
> +               gsc_err("Not supported field order(%d)\n", pix_mp->field);
> +               return -EINVAL;
> +       }
> +
> +       max_w = variant->pix_max->target_rot_dis_w;
> +       max_h = variant->pix_max->target_rot_dis_h;
> +       if (V4L2_TYPE_IS_OUTPUT(f->type)) {
> +               mod_x = ffs(variant->pix_align->org_w) - 1;
> +               if (is_yuv420(fmt->color))
> +                       mod_y = ffs(variant->pix_align->org_h) - 1;
> +               else
> +                       mod_y = ffs(variant->pix_align->org_h) - 2;
> +               min_w = variant->pix_min->org_w;
> +               min_h = variant->pix_min->org_h;
> +       } else {
> +               mod_x = ffs(variant->pix_align->org_w) - 1;
> +               if (is_yuv420(fmt->color))
> +                       mod_y = ffs(variant->pix_align->org_h) - 1;
> +               else
> +                       mod_y = ffs(variant->pix_align->org_h) - 2;
> +               min_w = variant->pix_min->target_rot_dis_w;
> +               min_h = variant->pix_min->target_rot_dis_h;
> +       }

Looks to me like only the last 2 lines in both branches are different,
so the first 5 could be abstracted out.

> +       gsc_dbg("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
> +            mod_x, mod_y, max_w, max_h);
> +       /* To check if image size is modified to adjust parameter against
> +          hardware abilities */
> +       tmp_w = pix_mp->width;
> +       tmp_h = pix_mp->height;
> +
> +       v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
> +               &pix_mp->height, min_h, max_h, mod_y, 0);
> +       if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
> +               gsc_info("Image size has been modified from %dx%d to %dx%d",
> +                        tmp_w, tmp_h, pix_mp->width, pix_mp->height);
> +
> +       pix_mp->num_planes = fmt->num_planes;
> +
> +       if (ctx->gsc_ctrls.csc_eq_mode->val)
> +               ctx->gsc_ctrls.csc_eq->val =
> +                       (pix_mp->width >= 1280) ? 1 : 0;
> +       if (ctx->gsc_ctrls.csc_eq->val) /* HD */
> +               pix_mp->colorspace = V4L2_COLORSPACE_REC709;
> +       else    /* SD */
> +               pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> +
> +       for (i = 0; i < pix_mp->num_planes; ++i) {
> +               int bpl = (pix_mp->width * fmt->depth[i]) >> 3;
> +               pix_mp->plane_fmt[i].bytesperline = bpl;
> +               pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
> +
> +               gsc_dbg("[%d]: bpl: %d, sizeimage: %d",
> +                   i, bpl, pix_mp->plane_fmt[i].sizeimage);
> +       }
> +
> +       return 0;
> +}
> +
> +int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
> +{
> +       struct gsc_frame *frame;
> +       struct v4l2_pix_format_mplane *pix_mp;
> +       int i;
> +
> +       frame = ctx_get_frame(ctx, f->type);
> +       if (IS_ERR(frame))
> +               return PTR_ERR(frame);
> +
> +       pix_mp = &f->fmt.pix_mp;
> +
> +       pix_mp->width           = frame->f_width;
> +       pix_mp->height          = frame->f_height;
> +       pix_mp->field           = V4L2_FIELD_NONE;
> +       pix_mp->pixelformat     = frame->fmt->pixelformat;
> +       pix_mp->colorspace      = V4L2_COLORSPACE_JPEG;

I don't know the right answer, but why JPEG not SRGB?

> +       pix_mp->num_planes      = frame->fmt->num_planes;
> +
> +       for (i = 0; i < pix_mp->num_planes; ++i) {
> +               pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
> +                       frame->fmt->depth[i]) / 8;
> +               pix_mp->plane_fmt[i].sizeimage =
> +                        pix_mp->plane_fmt[i].bytesperline * frame->f_height;
> +       }
> +
> +       return 0;
> +}
> +
> +void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
> +{
> +       if (tmp_w != *w || tmp_h != *h) {
> +               gsc_info("Cropped size has been modified from %dx%d to %dx%d",
> +                                                       *w, *h, tmp_w, tmp_h);
> +               *w = tmp_w;
> +               *h = tmp_h;
> +       }
> +}
> +
> +int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
> +{
> +       struct gsc_frame *frame;
> +
> +       frame = ctx_get_frame(ctx, cr->type);
> +       if (IS_ERR(frame))
> +               return PTR_ERR(frame);
> +
> +       cr->c = frame->crop;
> +
> +       return 0;
> +}
> +
> +int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
> +{
> +       struct gsc_frame *f;
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +       struct gsc_variant *variant = gsc->variant;
> +       u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h;
> +       u32 min_w, min_h, max_w, max_h;
> +
> +       if (cr->c.top < 0 || cr->c.left < 0) {
> +               gsc_err("doesn't support negative values for top & left\n");
> +               return -EINVAL;
> +       }
> +       gsc_dbg("user put w: %d, h: %d", cr->c.width, cr->c.height);
> +
> +       if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +               f = &ctx->d_frame;
> +       else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +               f = &ctx->s_frame;
> +       else
> +               return -EINVAL;
> +
> +       max_w = f->f_width;
> +       max_h = f->f_height;
> +       tmp_w = cr->c.width;
> +       tmp_h = cr->c.height;
> +
> +       if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
> +               if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 1) ||
> +                   is_rgb(f->fmt->color))
> +                       min_w = 32;
> +               else
> +                       min_w = 64;
> +               if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 3) ||
> +                   is_yuv420(f->fmt->color))
> +                       min_h = 32;
> +               else
> +                       min_h = 16;
> +       } else {
> +               if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
> +                       mod_x = ffs(variant->pix_align->target_w) - 1;
> +               if (is_yuv420(f->fmt->color))
> +                       mod_y = ffs(variant->pix_align->target_h) - 1;
> +               if (ctx->gsc_ctrls.rotate->val == 90 ||
> +                   ctx->gsc_ctrls.rotate->val == 270) {
> +                       max_w = f->f_height;
> +                       max_h = f->f_width;
> +                       min_w = variant->pix_min->target_rot_en_w;
> +                       min_h = variant->pix_min->target_rot_en_h;
> +                       tmp_w = cr->c.height;
> +                       tmp_h = cr->c.width;
> +               } else {
> +                       min_w = variant->pix_min->target_rot_dis_w;
> +                       min_h = variant->pix_min->target_rot_dis_h;
> +               }
> +       }
> +       gsc_dbg("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d",
> +                                       mod_x, mod_y, min_w, min_h);
> +       gsc_dbg("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h);
> +
> +       v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
> +                             &tmp_h, min_h, max_h, mod_y, 0);
> +
> +       if (!V4L2_TYPE_IS_OUTPUT(cr->type) &&
> +           (ctx->gsc_ctrls.rotate->val == 90 ||
> +           ctx->gsc_ctrls.rotate->val == 270)) {
> +               gsc_check_crop_change(tmp_h, tmp_w, &cr->c.width,
> +                                                       &cr->c.height);
> +       } else {
> +               gsc_check_crop_change(tmp_w, tmp_h, &cr->c.width,
> +                                                       &cr->c.height);
> +       }

No need for braces.

> +
> +       /* adjust left/top if cropping rectangle is out of bounds */
> +       /* Need to add code to algin left value with 2's multiple */
> +       if (cr->c.left + tmp_w > max_w)
> +               cr->c.left = max_w - tmp_w;
> +       if (cr->c.top + tmp_h > max_h)
> +               cr->c.top = max_h - tmp_h;
> +
> +       if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
> +               if (cr->c.left % 2)
> +                       cr->c.left -= 1;
> +
> +       gsc_dbg("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
> +           cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
> +
> +       return 0;
> +}
> +
> +int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
> +                          int dh, int rot, int out_path)
> +{
> +       int tmp_w, tmp_h, sc_down_max;
> +       sc_down_max =
> +               (out_path == GSC_DMA) ? var->sc_down_max : var->local_sc_down;
> +
> +       if (rot == 90 || rot == 270) {
> +               tmp_w = dh;
> +               tmp_h = dw;
> +       } else {
> +               tmp_w = dw;
> +               tmp_h = dh;
> +       }
> +
> +       if ((sw / tmp_w) > sc_down_max ||
> +           (sh / tmp_h) > sc_down_max ||
> +           (tmp_w / sw) > var->sc_up_max ||
> +           (tmp_h / sh) > var->sc_up_max)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +int gsc_set_scaler_info(struct gsc_ctx *ctx)
> +{
> +       struct gsc_scaler *sc = &ctx->scaler;
> +       struct gsc_frame *s_frame = &ctx->s_frame;
> +       struct gsc_frame *d_frame = &ctx->d_frame;
> +       struct gsc_variant *variant = ctx->gsc_dev->variant;
> +       int tx, ty;
> +       int ret;
> +
> +       ret = gsc_check_scaler_ratio(variant, s_frame->crop.width,
> +               s_frame->crop.height, d_frame->crop.width, d_frame->crop.height,
> +               ctx->gsc_ctrls.rotate->val, ctx->out_path);
> +       if (ret) {
> +               gsc_err("out of scaler range");
> +               return ret;
> +       }
> +
> +       if (ctx->gsc_ctrls.rotate->val == 90 ||
> +           ctx->gsc_ctrls.rotate->val == 270) {
> +               ty = d_frame->crop.width;
> +               tx = d_frame->crop.height;
> +       } else {
> +               tx = d_frame->crop.width;
> +               ty = d_frame->crop.height;
> +       }
> +
> +       ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width,
> +                                     tx, &sc->pre_hratio);
> +       if (ret) {
> +               gsc_err("Horizontal scale ratio is out of range");
> +               return ret;
> +       }
> +
> +       ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height,
> +                                     ty, &sc->pre_vratio);
> +       if (ret) {
> +               gsc_err("Vertical scale ratio is out of range");
> +               return ret;
> +       }
> +
> +       gsc_check_src_scale_info(variant, s_frame, &sc->pre_hratio,
> +                                tx, ty, &sc->pre_vratio);
> +
> +       gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
> +                                  &sc->pre_shfactor);
> +
> +       sc->main_hratio = (s_frame->crop.width << 16) / tx;
> +       sc->main_vratio = (s_frame->crop.height << 16) / ty;
> +

Would be nice to ensure tx and ty were not zeros, unless it's done somewhere
else.

> +       gsc_dbg("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d",
> +                       s_frame->crop.width, s_frame->crop.height, tx, ty);
> +       gsc_dbg("scaler ratio info : pre_shfactor : %d, pre_h : %d",
> +                       sc->pre_shfactor, sc->pre_hratio);
> +       gsc_dbg("pre_v :%d, main_h : %ld, main_v : %ld",
> +                       sc->pre_vratio, sc->main_hratio, sc->main_vratio);
> +
> +       return 0;
> +}
> +
> +
> +/*
> + * V4L2 controls handling
> + */
> +static int gsc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +       struct gsc_ctx *ctx = ctrl_to_ctx(ctrl);
> +
> +       switch (ctrl->id) {
> +       case V4L2_CID_HFLIP:
> +               user_to_drv(ctx->gsc_ctrls.hflip, ctrl->val);
> +               break;
> +
> +       case V4L2_CID_VFLIP:
> +               user_to_drv(ctx->gsc_ctrls.vflip, ctrl->val);
> +               break;
> +
> +       case V4L2_CID_ROTATE:
> +               user_to_drv(ctx->gsc_ctrls.rotate, ctrl->val);
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       if (gsc_m2m_opened(ctx->gsc_dev))
> +               gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
> +
> +       return 0;
> +}
> +
> +const struct v4l2_ctrl_ops gsc_ctrl_ops = {
> +       .s_ctrl = gsc_s_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config gsc_custom_ctrl[] = {
> +       {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_GLOBAL_ALPHA,
> +               .name = "Set RGB alpha",
> +               .type = V4L2_CTRL_TYPE_INTEGER,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +               .max = 255,
> +               .step = 1,
> +               .def = 0,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_CACHEABLE,
> +               .name = "Set cacheable",
> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +               .max = 1,
> +               .def = true,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_TV_LAYER_BLEND_ENABLE,
> +               .name = "Enable layer alpha blending",
> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_TV_LAYER_BLEND_ALPHA,
> +               .name = "Set alpha for layer blending",
> +               .type = V4L2_CTRL_TYPE_INTEGER,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +               .min = 0,
> +               .max = 255,
> +               .step = 1,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_TV_PIXEL_BLEND_ENABLE,
> +               .name = "Enable pixel alpha blending",
> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_TV_CHROMA_ENABLE,
> +               .name = "Enable chromakey",
> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_TV_CHROMA_VALUE,
> +               .name = "Set chromakey value",
> +               .type = V4L2_CTRL_TYPE_INTEGER,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +               .min = 0,
> +               .max = 255,
> +               .step = 1,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_CSC_EQ_MODE,
> +               .name = "Set CSC equation mode",
> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +               .max = DEFAULT_CSC_EQ,
> +               .def = DEFAULT_CSC_EQ,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_CSC_EQ,
> +               .name = "Set CSC equation",
> +               .type = V4L2_CTRL_TYPE_INTEGER,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +               .step = 1,
> +               .max = 8,
> +               .def = V4L2_COLORSPACE_REC709,
> +       }, {
> +               .ops = &gsc_ctrl_ops,
> +               .id = V4L2_CID_CSC_RANGE,
> +               .name = "Set CSC range",
> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
> +               .flags = V4L2_CTRL_FLAG_SLIDER,
> +               .max = DEFAULT_CSC_RANGE,
> +               .def = DEFAULT_CSC_RANGE,
> +       },
> +};
> +
> +int gsc_ctrls_create(struct gsc_ctx *ctx)
> +{
> +       if (ctx->ctrls_rdy) {
> +               gsc_err("Control handler of this context was created already");
> +               return 0;
> +       }
> +
> +       v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM);
> +
> +       ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +                               &gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
> +       ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +                               &gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> +       ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +                               &gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> +
> +
> +       ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[0], NULL);
> +       ctx->gsc_ctrls.layer_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[2], NULL);
> +       ctx->gsc_ctrls.layer_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[3], NULL);
> +       ctx->gsc_ctrls.pixel_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[4], NULL);
> +       ctx->gsc_ctrls.chroma_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[5], NULL);
> +       ctx->gsc_ctrls.chroma_val = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[6], NULL);
> +
> +/* for CSC equation */
> +       ctx->gsc_ctrls.csc_eq_mode = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[7], NULL);
> +       ctx->gsc_ctrls.csc_eq = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[8], NULL);
> +       ctx->gsc_ctrls.csc_range = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +                                       &gsc_custom_ctrl[9], NULL);
> +
> +       ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
> +
> +
> +       if (ctx->ctrl_handler.error) {
> +               int err = ctx->ctrl_handler.error;
> +               v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +               gsc_err("Failed to gscaler control hander create");
> +               return err;
> +       }
> +
> +       return 0;
> +}
> +
> +void gsc_ctrls_delete(struct gsc_ctx *ctx)
> +{
> +       if (ctx->ctrls_rdy) {
> +               v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +               ctx->ctrls_rdy = false;
> +       }
> +}
> +
> +/* The color format (num_comp, num_planes) must be already configured. */
> +int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
> +                       struct gsc_frame *frame, struct gsc_addr *addr)
> +{
> +       int ret = 0;
> +       u32 pix_size;
> +
> +       if (IS_ERR(vb) || IS_ERR(frame)) {

Can vb pointer really contain ERRors? I think you should use !vb here.

> +               gsc_err("Invalid argument");
> +               return -EINVAL;
> +       }
> +
> +       pix_size = frame->f_width * frame->f_height;
> +
> +       gsc_dbg("num_planes= %d, num_comp= %d, pix_size= %d",
> +               frame->fmt->num_planes, frame->fmt->num_comp, pix_size);
> +
> +       addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
> +
> +       if (frame->fmt->num_planes == 1) {
> +               switch (frame->fmt->num_comp) {
> +               case 1:
> +                       addr->cb = 0;
> +                       addr->cr = 0;
> +                       break;
> +               case 2:
> +                       /* decompose Y into Y/Cb */
> +                       addr->cb = (dma_addr_t)(addr->y + pix_size);
> +                       addr->cr = 0;
> +                       break;
> +               case 3:
> +                       addr->cb = (dma_addr_t)(addr->y + pix_size);
> +                       addr->cr = (dma_addr_t)(addr->cb + (pix_size >> 2));

No need to distinguish between 420 and 422?

> +                       break;
> +               default:
> +                       gsc_err("Invalid the number of color planes");
> +                       return -EINVAL;
> +               }
> +       } else {
> +               if (frame->fmt->num_planes >= 2)
> +                       addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
> +
> +               if (frame->fmt->num_planes == 3)
> +                       addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
> +       }
> +
> +       if (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) {

What about other YVU formats?

> +               u32 t_cb = addr->cb;
> +               addr->cb = addr->cr;
> +               addr->cr = t_cb;
> +       }
> +
> +       gsc_dbg("ADDR: y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
> +               addr->y, addr->cb, addr->cr, ret);
> +
> +       return ret;
> +}
> +
> +static irqreturn_t gsc_irq_handler(int irq, void *priv)
> +{
> +       struct gsc_dev *gsc = priv;
> +       int gsc_irq;
> +
> +       gsc_irq = gsc_hw_get_irq_status(gsc);
> +       gsc_hw_clear_irq(gsc, gsc_irq);
> +
> +       if (gsc_irq == GSC_OR_IRQ) {
> +               gsc_err("Local path input over-run interrupt has occurred!\n");
> +               return IRQ_HANDLED;
> +       }
> +
> +       spin_lock(&gsc->slock);
> +
> +       if (test_and_clear_bit(ST_M2M_RUN, &gsc->state)) {
> +               struct vb2_buffer *src_vb, *dst_vb;
> +               struct gsc_ctx *ctx =
> +                       v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> +
> +               if (!ctx || !ctx->m2m_ctx)
> +                       goto isr_unlock;
> +
> +               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_and_clear_bit(ST_STOP_REQ, &gsc->state))
> +                               wake_up(&gsc->irq_queue);
> +                       else
> +                               v4l2_m2m_job_finish(gsc->m2m.m2m_dev,
> +                                                       ctx->m2m_ctx);

Why not job_finish before wake_up if we are shutting down?

> +                       /* wake_up job_abort, stop_streaming */
> +                       spin_lock(&ctx->slock);
> +                       if (ctx->state & GSC_CTX_STOP_REQ) {
> +                               ctx->state &= ~GSC_CTX_STOP_REQ;
> +                               wake_up(&gsc->irq_queue);
> +                       }
> +                       spin_unlock(&ctx->slock);
> +               }
> +       }
> +
> +isr_unlock:
> +       spin_unlock(&gsc->slock);
> +       return IRQ_HANDLED;
> +}
> +
> +static int gsc_runtime_suspend(struct device *dev)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
> +
> +       if (gsc_m2m_opened(gsc))
> +               gsc->m2m.ctx = NULL;
> +
> +       clk_disable(gsc->clock);
> +       clear_bit(ST_PWR_ON, &gsc->state);
> +
> +       return 0;
> +}
> +
> +static int gsc_runtime_resume(struct device *dev)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
> +       int ret;
> +
> +       ret = clk_enable(gsc->clock);
> +       if (ret)
> +               return ret;
> +
> +       set_bit(ST_PWR_ON, &gsc->state);
> +       return 0;
> +}
> +
> +static inline void *gsc_get_drv_data(struct platform_device *pdev);
> +
> +static int gsc_probe(struct platform_device *pdev)
> +{
> +       struct gsc_dev *gsc;
> +       struct resource *res;
> +       struct gsc_driverdata *drv_data;
> +
> +       int ret = 0;
> +
> +       dev_dbg(&pdev->dev, "%s():\n", __func__);
> +       drv_data = (struct gsc_driverdata *)
> +                               gsc_get_drv_data(pdev);
> +
> +       if (pdev->dev.of_node) {
> +               pdev->id = of_alias_get_id(pdev->dev.of_node, "gsc");
> +               if (pdev->id < 0)
> +                       dev_err(&pdev->dev,
> +                               "failed to get alias id, errno %d\n", ret);
> +       }
> +
> +       if (pdev->id >= drv_data->num_entities) {
> +               dev_err(&pdev->dev, "Invalid platform device id: %d\n",
> +                       pdev->id);
> +               return -EINVAL;
> +       }
> +
> +       gsc = devm_kzalloc(&pdev->dev, sizeof(struct gsc_dev), GFP_KERNEL);
> +       if (!gsc)
> +               return -ENOMEM;
> +
> +       gsc->id = pdev->id;
> +       gsc->variant = drv_data->variant[gsc->id];
> +       gsc->pdev = pdev;
> +       gsc->pdata = pdev->dev.platform_data;
> +
> +       init_waitqueue_head(&gsc->irq_queue);
> +       spin_lock_init(&gsc->slock);
> +       mutex_init(&gsc->lock);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       gsc->regs = devm_request_and_ioremap(&pdev->dev, res);
> +       if (!gsc->regs) {
> +               dev_err(&pdev->dev, "failed to map registers\n");
> +               return -ENOENT;
> +       }
> +
> +       /* Get Gscaler clock */
> +       gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
> +       if (IS_ERR(gsc->clock)) {
> +               gsc_err("failed to get gscaler.%d clock", gsc->id);
> +               return -ENXIO;
> +       }
> +       clk_put(gsc->clock);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +       if (!res) {
> +               dev_err(&pdev->dev, "failed to get IRQ resource\n");
> +               return -ENXIO;
> +       }
> +       gsc->irq = res->start;
> +
> +       ret = devm_request_irq(&pdev->dev, gsc->irq, gsc_irq_handler, 0,
> +                                                       pdev->name, gsc);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
> +               return ret;
> +       }
> +
> +       gsc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> +       if (IS_ERR(gsc->alloc_ctx)) {
> +               ret = PTR_ERR(gsc->alloc_ctx);
> +               goto err_irq;
> +       }
> +
> +       platform_set_drvdata(pdev, gsc);
> +
> +       ret = gsc_register_m2m_device(gsc);
> +       if (ret)
> +               goto err_irq;
> +
> +       gsc_runtime_resume(&pdev->dev);
> +       pm_runtime_enable(&pdev->dev);
> +
> +       gsc_info("gsc-%d registered successfully", gsc->id);
> +       return 0;
> +

I think you are missing additional error path here with
platform_release_resource()s
that you might have acquired with platform_get_resource().

> +err_irq:
> +       free_irq(gsc->irq, gsc);
> +
> +       return ret;
> +}
> +
> +static int __devexit gsc_remove(struct platform_device *pdev)
> +{
> +       struct gsc_dev *gsc =
> +               (struct gsc_dev *)platform_get_drvdata(pdev);
> +
> +       free_irq(gsc->irq, gsc);
> +
> +       gsc_unregister_m2m_device(gsc);
> +
> +       vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
> +       pm_runtime_disable(&pdev->dev);
> +
> +       iounmap(gsc->regs);
> +       release_resource(gsc->regs_res);
> +       kfree(gsc->regs_res);
> +       kfree(gsc);
> +
> +       dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
> +       return 0;
> +}
> +
> +static int gsc_suspend(struct device *dev)
> +{
> +       struct platform_device *pdev;
> +       struct gsc_dev *gsc;
> +       int ret = 0;
> +
> +       pdev = to_platform_device(dev);
> +       gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
> +
> +       if (gsc_m2m_run(gsc)) {
> +               set_bit(ST_STOP_REQ, &gsc->state);
> +               ret = wait_event_timeout(gsc->irq_queue,
> +                               !test_bit(ST_STOP_REQ, &gsc->state),
> +                               GSC_SHUTDOWN_TIMEOUT);
> +               if (ret == 0)
> +                       dev_err(&gsc->pdev->dev, "wait timeout : %s\n",
> +                               __func__);
> +       }
> +
> +       pm_runtime_put_sync(dev);
> +
> +       return ret;
> +}
> +
> +static int gsc_resume(struct device *dev)
> +{
> +       struct gsc_dev *gsc;
> +       struct gsc_ctx *ctx;
> +
> +       gsc = dev_get_drvdata(dev);
> +
> +       pm_runtime_get_sync(dev);
> +       if (gsc_m2m_opened(gsc)) {
> +               ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> +               if (ctx != NULL) {
> +                       gsc->m2m.ctx = NULL;
> +                       v4l2_m2m_job_finish(gsc->m2m.m2m_dev, ctx->m2m_ctx);
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct dev_pm_ops gsc_pm_ops = {
> +       .suspend                = gsc_suspend,
> +       .resume                 = gsc_resume,
> +       .runtime_suspend        = gsc_runtime_suspend,
> +       .runtime_resume         = gsc_runtime_resume,
> +};
> +
> +struct gsc_pix_max gsc_v_100_max = {
> +       .org_scaler_bypass_w    = 8192,
> +       .org_scaler_bypass_h    = 8192,
> +       .org_scaler_input_w     = 4800,
> +       .org_scaler_input_h     = 3344,
> +       .real_rot_dis_w         = 4800,
> +       .real_rot_dis_h         = 3344,
> +       .real_rot_en_w          = 2047,
> +       .real_rot_en_h          = 2047,
> +       .target_rot_dis_w       = 4800,
> +       .target_rot_dis_h       = 3344,
> +       .target_rot_en_w        = 2016,
> +       .target_rot_en_h        = 2016,
> +};
> +
> +struct gsc_pix_min gsc_v_100_min = {
> +       .org_w                  = 64,
> +       .org_h                  = 32,
> +       .real_w                 = 64,
> +       .real_h                 = 32,
> +       .target_rot_dis_w       = 64,
> +       .target_rot_dis_h       = 32,
> +       .target_rot_en_w        = 32,
> +       .target_rot_en_h        = 16,
> +};
> +
> +struct gsc_pix_align gsc_v_100_align = {
> +       .org_h                  = 16,
> +       .org_w                  = 16, /* yuv420 : 16, others : 8 */
> +       .offset_h               = 2,  /* yuv420/422 : 2, others : 1 */
> +       .real_w                 = 16, /* yuv420/422 : 4~16, others : 2~8 */
> +       .real_h                 = 16, /* yuv420 : 4~16, others : 1 */
> +       .target_w               = 2,  /* yuv420/422 : 2, others : 1 */
> +       .target_h               = 2,  /* yuv420 : 2, others : 1 */
> +};
> +
> +struct gsc_variant gsc_v_100_variant = {
> +       .pix_max                = &gsc_v_100_max,
> +       .pix_min                = &gsc_v_100_min,
> +       .pix_align              = &gsc_v_100_align,
> +       .in_buf_cnt             = 8,
> +       .out_buf_cnt            = 16,
> +       .sc_up_max              = 8,
> +       .sc_down_max            = 16,
> +       .poly_sc_down_max       = 4,
> +       .pre_sc_down_max        = 4,
> +       .local_sc_down          = 2,
> +};

static structs?

> +
> +static struct gsc_driverdata gsc_v_100_drvdata = {
> +       .variant = {
> +               [0] = &gsc_v_100_variant,
> +               [1] = &gsc_v_100_variant,
> +               [2] = &gsc_v_100_variant,
> +               [3] = &gsc_v_100_variant,
> +       },
> +       .num_entities = 4,
> +       .lclk_frequency = 266000000UL,
> +};
> +
> +static struct platform_device_id gsc_driver_ids[] = {
> +       {
> +               .name           = "exynos-gsc",
> +               .driver_data    = (unsigned long)&gsc_v_100_drvdata,
> +       },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(platform, gsc_driver_ids);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id exynos_gsc_match[] = {
> +       { .compatible = "samsung,exynos-gsc",
> +       .data = &gsc_v_100_drvdata, },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, exynos_gsc_match);
> +#else
> +#define exynos_gsc_match NULL
> +#endif
> +
> +static inline void *gsc_get_drv_data(struct platform_device *pdev)
> +{
> +#ifdef CONFIG_OF
> +       if (pdev->dev.of_node) {
> +               const struct of_device_id *match;
> +       match = of_match_node(exynos_gsc_match, pdev->dev.of_node);
> +       return (struct gsc_driverdata *) match->data;
> +       }
> +#endif
> +       return (struct gsc_driverdata *)
> +                       platform_get_device_id(pdev)->driver_data;
> +}
> +
> +static struct platform_driver gsc_driver = {
> +       .probe          = gsc_probe,
> +       .remove = __devexit_p(gsc_remove),
> +       .id_table       = gsc_driver_ids,
> +       .driver = {
> +               .name   = GSC_MODULE_NAME,
> +               .owner  = THIS_MODULE,
> +               .pm     = &gsc_pm_ops,
> +               .of_match_table = exynos_gsc_match,
> +       }
> +};
> +
> +static int __init gsc_init(void)
> +{
> +       int ret = platform_driver_register(&gsc_driver);
> +       if (ret)
> +               gsc_err("platform_driver_register failed: %d\n", ret);
> +       return ret;
> +}
> +
> +static void __exit gsc_exit(void)
> +{
> +       platform_driver_unregister(&gsc_driver);
> +}
> +
> +module_init(gsc_init);
> +module_exit(gsc_exit);
> +
> +MODULE_AUTHOR("Hyunwong Kim <khw0178.kim@xxxxxxxxxxx>");
> +MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/video/exynos/gsc/gsc-core.h b/drivers/media/video/exynos/gsc/gsc-core.h
> new file mode 100644
> index 0000000..3a236ec
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-core.h
> @@ -0,0 +1,652 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-core.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *             http://www.samsung.com
> + *
> + * header file for Samsung EXYNOS5 SoC series G-scaler 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 GSC_CORE_H_
> +#define GSC_CORE_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 <media/v4l2-mediabus.h>
> +#define CONFIG_VB2_GSC_DMA_CONTIG      1
> +#include <media/videobuf2-dma-contig.h>
> +#include "gsc-regs.h"
> +extern int gsc_dbg;
> +
> +#define GSC_MODULE_NAME                        "exynos-gsc"
> +
> +#define gsc_info(fmt, args...)                                         \
> +       do {                                                            \
> +               if (gsc_dbg >= 6)                                       \
> +                       pr_info("[INFO]%s:%d: "fmt "\n",                \
> +                               __func__, __LINE__, ##args);            \
> +       } while (0)
> +
> +#define gsc_err(fmt, args...)                                          \
> +       do {                                                            \
> +               if (gsc_dbg >= 3)                                       \
> +                       pr_err("[ERROR]%s:%d: "fmt "\n",                \
> +                               __func__, __LINE__, ##args);            \
> +       } while (0)
> +
> +#define gsc_warn(fmt, args...)                                         \
> +       do {                                                            \
> +               if (gsc_dbg >= 4)                                       \
> +                       pr_warn("[WARN]%s:%d: "fmt "\n",                \
> +                               __func__, __LINE__, ##args);            \
> +       } while (0)
> +
> +#define gsc_dbg(fmt, args...)                                          \
> +       do {                                                            \
> +               if (gsc_dbg >= 7)                                       \
> +                       pr_debug("[DEBUG]%s:%d: "fmt "\n",              \
> +                               __func__, __LINE__, ##args);            \
> +       } while (0)
> +
> +#define GSC_MAX_CLOCKS                 3

never used

> +#define GSC_SHUTDOWN_TIMEOUT           ((100*HZ)/1000)
> +#define GSC_MAX_DEVS                   4
> +#define WORKQUEUE_NAME_SIZE            32

never used

> +#define FIMD_NAME_SIZE                 32

ditto

> +#define GSC_M2M_BUF_NUM                        0
> +#define GSC_OUT_BUF_MAX                        2

ditto

> +#define GSC_MAX_CTRL_NUM               10
> +#define GSC_OUT_MAX_MASK_NUM           7

ditto

> +#define GSC_SC_ALIGN_4                 4
> +#define GSC_SC_ALIGN_2                 2

> +#define GSC_OUT_DEF_SRC                        15
> +#define GSC_OUT_DEF_DST                        7
> +#define DEFAULT_GSC_SINK_WIDTH         800
> +#define DEFAULT_GSC_SINK_HEIGHT                480
> +#define DEFAULT_GSC_SOURCE_WIDTH       800
> +#define DEFAULT_GSC_SOURCE_HEIGHT      480

ditto for all 6 above.

> +#define DEFAULT_CSC_EQ                 1
> +#define DEFAULT_CSC_RANGE              1
> +
> +#define GSC_LAST_DEV_ID                        3
> +#define GSC_PAD_SINK                   0
> +#define GSC_PAD_SOURCE                 1
> +#define GSC_PADS_NUM                   2

ditto for all 4 above.

> +
> +#define        GSC_PARAMS                      (1 << 0)
> +#define        GSC_SRC_FMT                     (1 << 1)
> +#define        GSC_DST_FMT                     (1 << 2)
> +#define        GSC_CTX_M2M                     (1 << 3)
> +#define        GSC_CTX_OUTPUT                  (1 << 4)
> +#define        GSC_CTX_START                   (1 << 5)
> +#define        GSC_CTX_STOP_REQ                (1 << 6)
> +#define        GSC_CTX_CAP                     (1 << 10)

> +#define MAX_MDEV                       2

ditto

> +
> +#define V4L2_CID_CACHEABLE                     (V4L2_CID_LASTP1 + 1)
> +#define V4L2_CID_TV_LAYER_BLEND_ENABLE         (V4L2_CID_LASTP1 + 2)
> +#define V4L2_CID_TV_LAYER_BLEND_ALPHA          (V4L2_CID_LASTP1 + 3)
> +#define V4L2_CID_TV_PIXEL_BLEND_ENABLE         (V4L2_CID_LASTP1 + 4)
> +#define V4L2_CID_TV_CHROMA_ENABLE              (V4L2_CID_LASTP1 + 5)
> +#define V4L2_CID_TV_CHROMA_VALUE               (V4L2_CID_LASTP1 + 6)
> +/* for color space conversion equation selection */
> +#define V4L2_CID_CSC_EQ_MODE                   (V4L2_CID_LASTP1 + 8)
> +#define V4L2_CID_CSC_EQ                                (V4L2_CID_LASTP1 + 9)
> +#define V4L2_CID_CSC_RANGE                     (V4L2_CID_LASTP1 + 10)
> +#define V4L2_CID_GLOBAL_ALPHA                  (V4L2_CID_LASTP1 + 11)
> +
> +enum gsc_dev_flags {
> +       /* for global */
> +       ST_PWR_ON,
> +       ST_STOP_REQ,
> +       /* for m2m node */
> +       ST_M2M_OPEN,
> +       ST_M2M_RUN,
> +};
> +
> +enum gsc_irq {
> +       GSC_OR_IRQ = 17,
> +       GSC_DONE_IRQ = 16,
> +};
> +
> +/**
> + * enum gsc_datapath - the path of data used for gscaler
> + * @GSC_CAMERA: from camera
> + * @GSC_DMA: from/to DMA
> + * @GSC_LOCAL: to local path
> + * @GSC_WRITEBACK: from FIMD
> + */
> +enum gsc_datapath {
> +       GSC_CAMERA = 0x1,
> +       GSC_DMA,
> +       GSC_MIXER,
> +       GSC_FIMD,
> +       GSC_WRITEBACK,
> +};
> +
> +enum gsc_color_fmt {
> +       GSC_RGB = 0x1,
> +       GSC_YUV420 = 0x2,
> +       GSC_YUV422 = 0x4,
> +       GSC_YUV444 = 0x8,
> +};
> +
> +enum gsc_yuv_fmt {
> +       GSC_LSB_Y = 0x10,
> +       GSC_LSB_C,
> +       GSC_CBCR = 0x20,
> +       GSC_CRCB,
> +};
> +
> +#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh)
> +#define is_rgb(x) (!!((x) & 0x1))
> +#define is_yuv420(x) (!!((x) & 0x2))
> +#define is_yuv422(x) (!!((x) & 0x4))
> +#define gsc_m2m_run(dev) test_bit(ST_M2M_RUN, &(dev)->state)
> +#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state)
> +
> +#define ctrl_to_ctx(__ctrl) \
> +       container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler)
> +/**
> + * struct gsc_fmt - the driver's internal color format data
> + * @mbus_code: Media Bus pixel code, -1 if not applicable
> + * @name: format description
> + * @pixelformat: the fourcc code for this format, 0 if not applicable
> + * @yorder: Y/C order
> + * @corder: Chrominance order control
> + * @num_planes: number of physically non-contiguous data planes
> + * @nr_comp: number of physically contiguous data planes
> + * @depth: per plane driver's private 'number of bits per pixel'
> + * @flags: flags indicating which operation mode format applies to
> + */
> +struct gsc_fmt {
> +       enum v4l2_mbus_pixelcode mbus_code;
> +       char    *name;
> +       u32     pixelformat;
> +       u32     color;
> +       u32     yorder;
> +       u32     corder;
> +       u16     num_planes;
> +       u16     num_comp;
> +       u8      depth[VIDEO_MAX_PLANES];
> +       u32     flags;
> +};
> +
> +/**
> + * struct gsc_input_buf - the driver's video buffer
> + * @vb:        videobuf2 buffer
> + * @list : linked list structure for buffer queue
> + * @idx : index of G-Scaler input buffer
> + */
> +struct gsc_input_buf {
> +       struct vb2_buffer       vb;
> +       struct list_head        list;
> +       int                     idx;
> +};
> +
> +/**
> + * struct gsc_addr - the G-Scaler physical address set
> + * @y:  luminance plane address
> + * @cb:         Cb plane address
> + * @cr:         Cr plane address
> + */
> +struct gsc_addr {
> +       dma_addr_t      y;
> +       dma_addr_t      cb;
> +       dma_addr_t      cr;
> +};
> +
> +/* struct gsc_ctrls - the G-Scaler control set
> + * @rotate: rotation degree
> + * @hflip: horizontal flip
> + * @vflip: vertical flip
> + * @global_alpha: the alpha value of current frame
> + * @layer_blend_en: enable mixer layer alpha blending
> + * @layer_alpha: set alpha value for mixer layer
> + * @pixel_blend_en: enable mixer pixel alpha blending
> + * @chroma_en: enable chromakey
> + * @chroma_val:        set value for chromakey
> + * @csc_eq_mode: mode to select csc equation of current frame
> + * @csc_eq: csc equation of current frame
> + * @csc_range: csc range of current frame
> + */
> +struct gsc_ctrls {
> +       struct v4l2_ctrl        *rotate;
> +       struct v4l2_ctrl        *hflip;
> +       struct v4l2_ctrl        *vflip;
> +       struct v4l2_ctrl        *global_alpha;
> +       struct v4l2_ctrl        *layer_blend_en;
> +       struct v4l2_ctrl        *layer_alpha;
> +       struct v4l2_ctrl        *pixel_blend_en;
> +       struct v4l2_ctrl        *chroma_en;
> +       struct v4l2_ctrl        *chroma_val;
> +       struct v4l2_ctrl        *csc_eq_mode;
> +       struct v4l2_ctrl        *csc_eq;
> +       struct v4l2_ctrl        *csc_range;
> +};

Can you do this? Keep the pointer to the v4l2_ctrl struct passed to s_ctrl
after it returns?

> +
> +/**
> + * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler
> + * @pre_shfactor:      pre sclaer shift factor
> + * @pre_hratio:                horizontal ratio of the prescaler
> + * @pre_vratio:                vertical ratio of the prescaler
> + * @main_hratio:       the main scaler's horizontal ratio
> + * @main_vratio:       the main scaler's vertical ratio
> + */
> +struct gsc_scaler {
> +       u32     pre_shfactor;
> +       u32     pre_hratio;
> +       u32     pre_vratio;
> +       unsigned long main_hratio;
> +       unsigned long main_vratio;
> +};
> +
> +struct gsc_dev;
> +
> +struct gsc_ctx;
> +
> +/**
> + * struct gsc_frame - source/target frame properties
> + * @f_width:   SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
> + * @f_height:  SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
> + * @crop:      cropped(source)/scaled(destination) size
> + * @payload:   image size in bytes (w x h x bpp)
> + * @addr:      image frame buffer physical addresses
> + * @fmt:       G-scaler color format pointer
> + * @alph:      frame's alpha value
> + */
> +struct gsc_frame {
> +       u32     f_width;
> +       u32     f_height;
> +       struct v4l2_rect        crop;
> +       unsigned long payload[VIDEO_MAX_PLANES];
> +       struct gsc_addr         addr;
> +       struct gsc_fmt          *fmt;
> +       u8      alpha;
> +};
> +
> +/**
> + * struct gsc_m2m_device - v4l2 memory-to-memory device data
> + * @vfd: the video device node for v4l2 m2m mode
> + * @m2m_dev: v4l2 memory-to-memory device data
> + * @ctx: hardware context data
> + * @refcnt: the reference counter
> + */
> +struct gsc_m2m_device {
> +       struct video_device     *vfd;
> +       struct v4l2_m2m_dev     *m2m_dev;
> +       struct gsc_ctx          *ctx;
> +       int                     refcnt;
> +};
> +
> +/**
> + *  struct gsc_pix_max - image pixel size limits in various IP configurations
> + *
> + *  @org_scaler_bypass_w: max pixel width when the scaler is disabled
> + *  @org_scaler_bypass_h: max pixel height when the scaler is disabled
> + *  @org_scaler_input_w: max pixel width when the scaler is enabled
> + *  @org_scaler_input_h: max pixel height when the scaler is enabled
> + *  @real_rot_dis_w: max pixel src cropped height with the rotator is off
> + *  @real_rot_dis_h: max pixel src croppped width with the rotator is off
> + *  @real_rot_en_w: max pixel src cropped width with the rotator is on
> + *  @real_rot_en_h: max pixel src cropped height with the rotator is on
> + *  @target_rot_dis_w: max pixel dst scaled width with the rotator is off
> + *  @target_rot_dis_h: max pixel dst scaled height with the rotator is off
> + *  @target_rot_en_w: max pixel dst scaled width with the rotator is on
> + *  @target_rot_en_h: max pixel dst scaled height with the rotator is on
> + */
> +struct gsc_pix_max {
> +       u16 org_scaler_bypass_w;
> +       u16 org_scaler_bypass_h;
> +       u16 org_scaler_input_w;
> +       u16 org_scaler_input_h;
> +       u16 real_rot_dis_w;
> +       u16 real_rot_dis_h;
> +       u16 real_rot_en_w;
> +       u16 real_rot_en_h;
> +       u16 target_rot_dis_w;
> +       u16 target_rot_dis_h;
> +       u16 target_rot_en_w;
> +       u16 target_rot_en_h;
> +};
> +
> +/**
> + *  struct gsc_pix_min - image pixel size limits in various IP configurations
> + *
> + *  @org_w: minimum source pixel width
> + *  @org_h: minimum source pixel height
> + *  @real_w: minimum input crop pixel width
> + *  @real_h: minimum input crop pixel height
> + *  @target_rot_dis_w: minimum output scaled pixel height when rotator is off
> + *  @target_rot_dis_h: minimum output scaled pixel height when rotator is off
> + *  @target_rot_en_w: minimum output scaled pixel height when rotator is on
> + *  @target_rot_en_h: minimum output scaled pixel height when rotator is on
> + */
> +struct gsc_pix_min {
> +       u16 org_w;
> +       u16 org_h;
> +       u16 real_w;
> +       u16 real_h;
> +       u16 target_rot_dis_w;
> +       u16 target_rot_dis_h;
> +       u16 target_rot_en_w;
> +       u16 target_rot_en_h;
> +};
> +
> +struct gsc_pix_align {
> +       u16 org_h;
> +       u16 org_w;
> +       u16 offset_h;
> +       u16 real_w;
> +       u16 real_h;
> +       u16 target_w;
> +       u16 target_h;
> +};
> +
> +/**
> + * struct gsc_variant - G-Scaler variant information
> + */
> +struct gsc_variant {
> +       struct gsc_pix_max *pix_max;
> +       struct gsc_pix_min *pix_min;
> +       struct gsc_pix_align *pix_align;
> +       u16             in_buf_cnt;
> +       u16             out_buf_cnt;
> +       u16             sc_up_max;
> +       u16             sc_down_max;
> +       u16             poly_sc_down_max;
> +       u16             pre_sc_down_max;
> +       u16             local_sc_down;
> +};
> +
> +/**
> + * struct gsc_driverdata - per device type driver data for init time.
> + *
> + * @variant: the variant information for this driver.
> + * @lclk_frequency: g-scaler clock frequency
> + * @num_entities: the number of g-scalers
> + */
> +struct gsc_driverdata {
> +       struct gsc_variant *variant[GSC_MAX_DEVS];
> +       unsigned long   lclk_frequency;
> +       int             num_entities;
> +};
> +
> +struct gsc_vb2 {
> +       const struct vb2_mem_ops *ops;
> +       void *(*init)(struct gsc_dev *gsc);
> +       void (*cleanup)(void *alloc_ctx);
> +
> +       void (*resume)(void *alloc_ctx);
> +       void (*suspend)(void *alloc_ctx);
> +
> +       int (*cache_flush)(struct vb2_buffer *vb, u32 num_planes);
> +       void (*set_cacheable)(void *alloc_ctx, bool cacheable);
> +       void (*set_sharable)(void *alloc_ctx, bool sharable);
> +};

I don't see anything from here being used apart from resume/suspend.
Am I missing something?

> +
> +/**
> + * struct gsc_dev - abstraction for G-Scaler entity
> + * @slock:     the spinlock protecting this data structure
> + * @lock:      the mutex protecting this data structure
> + * @pdev:      pointer to the G-Scaler platform device
> + * @variant:   the IP variant information
> + * @id:                g_scaler device index (0..GSC_MAX_DEVS)
> + * @regs:      the mapped hardware registers
> + * @regs_res:  the resource claimed for IO registers
> + * @irq:       G-scaler interrupt number
> + * @irq_queue: interrupt handler waitqueue
> + * @m2m:       memory-to-memory V4L2 device information
> + * @out:       memory-to-local V4L2 output device information
> + * @state:     flags used to synchronize m2m and capture mode operation
> + * @alloc_ctx: videobuf2 memory allocator context
> + * @vb2:       videobuf2 memory allocator call-back functions
> + * @mdev:      pointer to exynos media device
> + * @pipeline:  pointer to subdevs that are connected with gscaler
> + */
> +struct gsc_dev {
> +       spinlock_t                      slock;
> +       struct mutex                    lock;
> +       struct platform_device          *pdev;
> +       struct gsc_variant              *variant;
> +       u16                             id;
> +       struct clk                      *clock;
> +       void __iomem                    *regs;
> +       struct resource                 *regs_res;
> +       int                             irq;
> +       wait_queue_head_t               irq_queue;
> +       struct gsc_m2m_device           m2m;
> +       struct exynos_platform_gscaler  *pdata;
> +       unsigned long                   state;
> +       struct vb2_alloc_ctx            *alloc_ctx;
> +       const struct gsc_vb2            *vb2;
> +};
> +
> +/**
> + * gsc_ctx - the device context data
> + * @slock:             spinlock protecting this data structure
> + * @s_frame:           source frame properties
> + * @d_frame:           destination frame properties
> + * @in_path:           input mode (DMA or camera)
> + * @out_path:          output mode (DMA or FIFO)
> + * @scaler:            image scaler properties
> + * @flags:             additional flags for image conversion
> + * @state:             flags to keep track of user configuration
> + * @gsc_dev:           the g-scaler device this context applies to
> + * @m2m_ctx:           memory-to-memory device context
> + * @fh:                 v4l2 file handle
> + * @ctrl_handler:       v4l2 controls handler
> + * @ctrls_rdy:          true if the control handler is initialized
> + * @gsc_ctrls          G-Scaler control set
> + * @m2m_ctx:           memory-to-memory device context
> + */
> +struct gsc_ctx {
> +       spinlock_t              slock;
> +       struct gsc_frame        s_frame;
> +       struct gsc_frame        d_frame;
> +       enum gsc_datapath       in_path;
> +       enum gsc_datapath       out_path;
> +       struct gsc_scaler       scaler;
> +       u32                     flags;
> +       u32                     state;
> +       struct gsc_dev          *gsc_dev;
> +       struct v4l2_m2m_ctx     *m2m_ctx;
> +       struct v4l2_fh          fh;
> +       struct v4l2_ctrl_handler ctrl_handler;
> +       struct gsc_ctrls        gsc_ctrls;
> +       bool                    ctrls_rdy;
> +};
> +
> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm);
> +void gsc_clk_release(struct gsc_dev *gsc);

no such function

> +int gsc_register_m2m_device(struct gsc_dev *gsc);
> +void gsc_unregister_m2m_device(struct gsc_dev *gsc);
> +
> +u32 get_plane_size(struct gsc_frame *fr, unsigned int plane);
> +char gsc_total_fmts(void);

no such function

> +struct gsc_fmt *get_format(int index);
> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index);
> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f);
> +int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
> +void gsc_set_frame_size(struct gsc_frame *frame, int width, int height);
> +int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
> +void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h);
> +int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
> +int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
> +int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
> +                                                       u32 *ratio);
> +void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh);
> +void gsc_check_src_scale_info(struct gsc_variant *var,
> +                               struct gsc_frame *s_frame,
> +                               u32 *wratio, u32 tx, u32 ty, u32 *hratio);
> +int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
> +                          int dh, int rot, int out_path);
> +int gsc_set_scaler_info(struct gsc_ctx *ctx);
> +int gsc_ctrls_create(struct gsc_ctx *ctx);
> +void gsc_ctrls_delete(struct gsc_ctx *ctx);
> +int gsc_out_hw_set(struct gsc_ctx *ctx);
> +int gsc_out_set_in_addr(struct gsc_dev *gsc, struct gsc_ctx *ctx,
> +                  struct gsc_input_buf *buf, int index);

Above two don't exist.

> +int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
> +                    struct gsc_frame *frame, struct gsc_addr *addr);
> +int gsc_out_link_validate(const struct media_pad *source,
> +                         const struct media_pad *sink);
> +int gsc_pipeline_s_stream(struct gsc_dev *gsc, bool on);

Ditto for above two.

> +
> +static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx)
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&ctx->slock, flags);
> +       ctx->state |= state;
> +       spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
> +static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx)
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&ctx->slock, flags);
> +       ctx->state &= ~state;
> +       spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
> +static inline int get_win_num(struct gsc_dev *dev)
> +{
> +       return (dev->id == 3) ? 2 : dev->id;
> +}

unused

> +static inline int is_tiled(struct gsc_fmt *fmt)
> +{
> +       return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16;
> +}
> +
> +static inline int is_output(enum v4l2_buf_type type)
> +{
> +       return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
> +               type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 1 : 0;
> +}

never used

> +
> +static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on)
> +{
> +       u32 cfg = readl(dev->regs + GSC_ENABLE);
> +
> +       if (on)
> +               cfg |= GSC_ENABLE_ON;
> +       else
> +               cfg &= ~GSC_ENABLE_ON;
> +
> +       writel(cfg, dev->regs + GSC_ENABLE);
> +}
> +
> +static inline int gsc_hw_get_irq_status(struct gsc_dev *dev)
> +{
> +       u32 cfg = readl(dev->regs + GSC_IRQ);
> +       if (cfg & (1 << GSC_OR_IRQ))
> +               return GSC_OR_IRQ;
> +       else
> +               return GSC_DONE_IRQ;
> +
> +}
> +
> +static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq)
> +{
> +       u32 cfg = readl(dev->regs + GSC_IRQ);
> +       if (irq == GSC_OR_IRQ)
> +               cfg |= GSC_IRQ_STATUS_OR_IRQ;
> +       else if (irq == GSC_DONE_IRQ)
> +               cfg |= GSC_IRQ_STATUS_OR_FRM_DONE;
> +       writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +static inline void gsc_lock(struct vb2_queue *vq)
> +{
> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
> +       mutex_lock(&ctx->gsc_dev->lock);
> +}
> +
> +static inline void gsc_unlock(struct vb2_queue *vq)
> +{
> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
> +       mutex_unlock(&ctx->gsc_dev->lock);
> +}
> +
> +static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx)
> +{
> +       unsigned long flags;
> +       bool ret;
> +
> +       spin_lock_irqsave(&ctx->slock, flags);
> +       ret = (ctx->state & mask) == mask;
> +       spin_unlock_irqrestore(&ctx->slock, flags);
> +       return ret;
> +}
> +
> +static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx,
> +                                             enum v4l2_buf_type type)
> +{
> +       struct gsc_frame *frame;
> +
> +       if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
> +               frame = &ctx->s_frame;
> +       } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
> +               frame = &ctx->d_frame;
> +       } else {
> +               gsc_err("Wrong buffer/video queue type (%d)", type);
> +               return ERR_PTR(-EINVAL);
> +       }
> +
> +       return frame;
> +}
> +
> +static inline void user_to_drv(struct v4l2_ctrl *ctrl, s32 value)
> +{
> +       ctrl->cur.val = ctrl->val = value;
> +}

This is fishy. Can you store the pointer to the control after s_ctrl returns?

> +
> +void gsc_hw_set_sw_reset(struct gsc_dev *dev);
> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask);

Both never used.

> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask);

Never used.

> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask);

> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev);
> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev);

both never used

> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
> +                                                       int index);
> +void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr,
> +                                                       int index);
> +void gsc_hw_set_input_path(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_size(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx);
> +void gsc_hw_set_output_path(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_size(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx);
> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx);
> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx);
> +void gsc_hw_set_rotation(struct gsc_ctx *ctx);
> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx);
> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx);
> +
> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev);
> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev);
> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev);
> +int gsc_wait_reset(struct gsc_dev *dev);

Above 4 never used.

> +int gsc_wait_operating(struct gsc_dev *dev);
> +int gsc_wait_stop(struct gsc_dev *dev);

Never used.

> +
> +#endif /* GSC_CORE_H_ */
> diff --git a/drivers/media/video/exynos/gsc/gsc-m2m.c b/drivers/media/video/exynos/gsc/gsc-m2m.c
> new file mode 100644
> index 0000000..ead59a0
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-m2m.c
> @@ -0,0 +1,751 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-m2m.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *             http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/version.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/bug.h>
> +#include <linux/interrupt.h>
> +#include <linux/workqueue.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <media/v4l2-ioctl.h>
> +
> +#include "gsc-core.h"
> +
> +static int gsc_ctx_stop_req(struct gsc_ctx *ctx)
> +{
> +       struct gsc_ctx *curr_ctx;
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +       int ret = 0;
> +
> +       curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> +       if (!gsc_m2m_run(gsc) || (curr_ctx != ctx))
> +               return 0;
> +       ctx->state |= GSC_CTX_STOP_REQ;
> +       ret = wait_event_timeout(gsc->irq_queue,
> +                       !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
> +                       GSC_SHUTDOWN_TIMEOUT);
> +       if (!ret)
> +               ret = -EBUSY;
> +
> +       return ret;
> +}
> +
> +static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> +       struct gsc_ctx *ctx = q->drv_priv;
> +       int ret;
> +
> +       ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev);
> +       return ret > 0 ? 0 : ret;
> +}
> +
> +static int gsc_m2m_stop_streaming(struct vb2_queue *q)
> +{
> +       struct gsc_ctx *ctx = q->drv_priv;
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +       int ret;
> +
> +       ret = gsc_ctx_stop_req(ctx);
> +       /* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */

Can we fix this?

> +       if (ret < 0)
> +               dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
> +
> +       pm_runtime_put(&ctx->gsc_dev->pdev->dev);
> +
> +       return 0;
> +}
> +
> +static void gsc_m2m_job_abort(void *priv)
> +{
> +       struct gsc_ctx *ctx = priv;
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +       int ret;
> +
> +       ret = gsc_ctx_stop_req(ctx);
> +       /* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */

Can we fix this?

> +       if (ret < 0)
> +               dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
> +}
> +
> +int gsc_fill_addr(struct gsc_ctx *ctx)
> +{
> +       struct gsc_frame *s_frame, *d_frame;
> +       struct vb2_buffer *vb = NULL;
> +       int ret = 0;
> +
> +       s_frame = &ctx->s_frame;
> +       d_frame = &ctx->d_frame;
> +
> +       vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
> +       ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr);
> +       if (ret)
> +               return ret;
> +
> +       vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
> +       ret = gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr);
> +
> +       return ret;
> +}
> +
> +static void gsc_m2m_device_run(void *priv)
> +{
> +       struct gsc_ctx *ctx = priv;
> +       struct gsc_dev *gsc;
> +       unsigned long flags;
> +       u32 ret;
> +       bool is_set = false;
> +
> +       if (WARN(!ctx, "null hardware context\n"))
> +               return;
> +
> +       gsc = ctx->gsc_dev;
> +
> +       spin_lock_irqsave(&ctx->slock, flags);
> +       /* Reconfigure hardware if the context has changed. */
> +       if (gsc->m2m.ctx != ctx) {
> +               gsc_dbg("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
> +                         gsc->m2m.ctx, ctx);
> +               ctx->state |= GSC_PARAMS;
> +               gsc->m2m.ctx = ctx;
> +       }
> +
> +       is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0;
> +       ctx->state &= ~GSC_CTX_STOP_REQ;
> +       if (is_set) {
> +               wake_up(&gsc->irq_queue);
> +               goto put_device;
> +       }
> +
> +       ret = gsc_fill_addr(ctx);
> +       if (ret) {
> +               gsc_err("Wrong address");
> +               goto put_device;
> +       }
> +
> +       gsc_set_prefbuf(gsc, ctx->s_frame);
> +       gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
> +       gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
> +
> +       if (ctx->state & GSC_PARAMS) {
> +               gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
> +               gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
> +               gsc_hw_set_frm_done_irq_mask(gsc, false);
> +               gsc_hw_set_gsc_irq_enable(gsc, true);
> +
> +               if (gsc_set_scaler_info(ctx)) {
> +                       gsc_err("Scaler setup error");
> +                       goto put_device;
> +               }
> +
> +               gsc_hw_set_input_path(ctx);
> +               gsc_hw_set_in_size(ctx);
> +               gsc_hw_set_in_image_format(ctx);
> +
> +               gsc_hw_set_output_path(ctx);
> +               gsc_hw_set_out_size(ctx);
> +               gsc_hw_set_out_image_format(ctx);
> +
> +               gsc_hw_set_prescaler(ctx);
> +               gsc_hw_set_mainscaler(ctx);
> +               gsc_hw_set_rotation(ctx);
> +               gsc_hw_set_global_alpha(ctx);
> +       }
> +       /* When you update SFRs in the middle of operating
> +       gsc_hw_set_sfr_update(ctx);
> +       */

Leftover?

> +
> +       ctx->state &= ~GSC_PARAMS;
> +
> +       if (!test_and_set_bit(ST_M2M_RUN, &gsc->state)) {
> +               /* One frame mode sequence
> +                GSCALER_ON on -> GSCALER_OP_STATUS is operating ->
> +                GSCALER_ON off */
> +               gsc_hw_enable_control(gsc, true);
> +               ret = gsc_wait_operating(gsc);
> +               if (ret < 0) {
> +                       gsc_err("gscaler wait operating timeout");
> +                       goto put_device;
> +               }
> +               gsc_hw_enable_control(gsc, false);
> +       }
> +
> +       spin_unlock_irqrestore(&ctx->slock, flags);
> +       return;
> +
> +put_device:
> +       ctx->state &= ~GSC_PARAMS;
> +       spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
> +static int gsc_m2m_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 gsc_ctx *ctx = vb2_get_drv_priv(vq);
> +       struct gsc_frame *frame;
> +       int i;
> +
> +       frame = ctx_get_frame(ctx, vq->type);
> +       if (IS_ERR(frame))
> +               return PTR_ERR(frame);
> +
> +       if (!frame->fmt)
> +               return -EINVAL;
> +
> +       *num_planes = frame->fmt->num_planes;
> +       for (i = 0; i < frame->fmt->num_planes; i++) {
> +               sizes[i] = get_plane_size(frame, i);
> +               allocators[i] = ctx->gsc_dev->alloc_ctx;
> +       }
> +       return 0;
> +}
> +
> +static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
> +{
> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +       struct gsc_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->fmt->num_planes; i++)
> +                       vb2_set_plane_payload(vb, i, frame->payload[i]);
> +       }
> +
> +       return 0;
> +}
> +
> +static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
> +{
> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +
> +       gsc_dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
> +
> +       if (ctx->m2m_ctx)
> +               v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
> +}
> +
> +struct vb2_ops gsc_m2m_qops = {
> +       .queue_setup     = gsc_m2m_queue_setup,
> +       .buf_prepare     = gsc_m2m_buf_prepare,
> +       .buf_queue       = gsc_m2m_buf_queue,
> +       .wait_prepare    = gsc_unlock,
> +       .wait_finish     = gsc_lock,
> +       .stop_streaming  = gsc_m2m_stop_streaming,
> +       .start_streaming = gsc_m2m_start_streaming,
> +};
> +
> +static int gsc_m2m_querycap(struct file *file, void *fh,
> +                          struct v4l2_capability *cap)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +
> +       strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
> +       strncpy(cap->card, gsc->pdev->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 gsc_m2m_enum_fmt_mplane(struct file *file, void *priv,
> +                               struct v4l2_fmtdesc *f)
> +{
> +       return gsc_enum_fmt_mplane(f);
> +}
> +
> +static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
> +                            struct v4l2_format *f)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +       return gsc_g_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
> +                                 struct v4l2_format *f)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +       return gsc_try_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
> +                                struct v4l2_format *f)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       struct vb2_queue *vq;
> +       struct gsc_frame *frame;
> +       struct v4l2_pix_format_mplane *pix;
> +       int i, ret = 0;
> +
> +       ret = gsc_m2m_try_fmt_mplane(file, fh, f);
> +       if (ret)
> +               return ret;
> +
> +       vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
> +
> +       if (vb2_is_streaming(vq)) {
> +               gsc_err("queue (%d) busy", f->type);
> +               return -EBUSY;
> +       }
> +
> +       if (V4L2_TYPE_IS_OUTPUT(f->type))
> +               frame = &ctx->s_frame;
> +       else
> +               frame = &ctx->d_frame;
> +
> +       pix = &f->fmt.pix_mp;
> +       frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
> +       if (!frame->fmt)
> +               return -EINVAL;
> +
> +       for (i = 0; i < frame->fmt->num_planes; i++)
> +               frame->payload[i] = pix->plane_fmt[i].sizeimage;
> +
> +       gsc_set_frame_size(frame, pix->width, pix->height);
> +
> +       if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +               gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
> +       else
> +               gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
> +
> +       gsc_dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
> +
> +       return 0;
> +}
> +
> +static int gsc_m2m_reqbufs(struct file *file, void *fh,
> +                         struct v4l2_requestbuffers *reqbufs)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +       struct gsc_frame *frame;
> +       u32 max_cnt;
> +
> +       max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
> +               gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
> +       if (reqbufs->count > max_cnt)
> +               return -EINVAL;

Needs braces.

> +       else if (reqbufs->count == 0) {
> +               if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +                       gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
> +               else
> +                       gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
> +       }
> +
> +       frame = ctx_get_frame(ctx, reqbufs->type);
> +
> +       return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
> +}
> +
> +static int gsc_m2m_querybuf(struct file *file, void *fh,
> +                                       struct v4l2_buffer *buf)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_qbuf(struct file *file, void *fh,
> +                         struct v4l2_buffer *buf)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_dqbuf(struct file *file, void *fh,
> +                          struct v4l2_buffer *buf)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_streamon(struct file *file, void *fh,
> +                          enum v4l2_buf_type type)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +       /* The source and target color format need to be set */
> +       if (V4L2_TYPE_IS_OUTPUT(type)) {
> +               if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
> +                       return -EINVAL;
> +       } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
> +               return -EINVAL;
> +       }
> +
> +       return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
> +}
> +
> +static int gsc_m2m_streamoff(struct file *file, void *fh,
> +                           enum v4l2_buf_type type)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
> +}
> +
> +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
> +static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
> +{
> +       if (a->left < b->left || a->top < b->top)
> +               return 0;
> +
> +       if (a->left + a->width > b->left + b->width)
> +               return 0;
> +
> +       if (a->top + a->height > b->top + b->height)
> +               return 0;
> +
> +       return 1;
> +}
> +
> +static int gsc_m2m_g_selection(struct file *file, void *fh,
> +                       struct v4l2_selection *s)
> +{
> +       struct gsc_frame *frame;
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +       if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
> +           (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +               return -EINVAL;
> +
> +       frame = ctx_get_frame(ctx, s->type);
> +       if (IS_ERR(frame))
> +               return PTR_ERR(frame);
> +
> +       switch (s->target) {
> +       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> +       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +       case V4L2_SEL_TGT_CROP_BOUNDS:
> +       case V4L2_SEL_TGT_CROP_DEFAULT:
> +               s->r.left = 0;
> +               s->r.top = 0;
> +               s->r.width = frame->f_width;
> +               s->r.height = frame->f_height;
> +               return 0;
> +
> +       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
> +       case V4L2_SEL_TGT_CROP_ACTIVE:
> +               s->r.left = frame->crop.left;
> +               s->r.top = frame->crop.top;
> +               s->r.width = frame->crop.width;
> +               s->r.height = frame->crop.height;
> +               return 0;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static int gsc_m2m_s_selection(struct file *file, void *fh,
> +                       struct v4l2_selection *s)
> +{
> +       struct gsc_frame *frame;
> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
> +       struct v4l2_crop cr;
> +       struct gsc_variant *variant = ctx->gsc_dev->variant;
> +       int ret;
> +
> +       cr.type = s->type;
> +       cr.c = s->r;
> +
> +       if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
> +           (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +               return -EINVAL;
> +
> +       ret = gsc_try_crop(ctx, &cr);
> +       if (ret)
> +               return ret;
> +
> +       if (s->flags & V4L2_SEL_FLAG_LE &&
> +           !is_rectangle_enclosed(&cr.c, &s->r))
> +               return -ERANGE;
> +
> +       if (s->flags & V4L2_SEL_FLAG_GE &&
> +           !is_rectangle_enclosed(&s->r, &cr.c))
> +               return -ERANGE;
> +
> +       s->r = cr.c;
> +
> +       switch (s->target) {
> +       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> +       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
> +               frame = &ctx->s_frame;
> +               break;
> +
> +       case V4L2_SEL_TGT_CROP_BOUNDS:
> +       case V4L2_SEL_TGT_CROP_ACTIVE:
> +       case V4L2_SEL_TGT_CROP_DEFAULT:
> +               frame = &ctx->d_frame;
> +               break;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       /* Check to see if scaling ratio is within supported range */
> +       if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
> +               if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +                       ret = gsc_check_scaler_ratio(variant, cr.c.width,
> +                               cr.c.height, ctx->d_frame.crop.width,
> +                               ctx->d_frame.crop.height,
> +                               ctx->gsc_ctrls.rotate->val, ctx->out_path);
> +               } else {
> +                       ret = gsc_check_scaler_ratio(variant,
> +                               ctx->s_frame.crop.width,
> +                               ctx->s_frame.crop.height, cr.c.width,
> +                               cr.c.height, ctx->gsc_ctrls.rotate->val,
> +                               ctx->out_path);
> +               }
> +
> +               if (ret) {
> +                       gsc_err("Out of scaler range");
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       frame->crop = cr.c;
> +
> +       gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
> +       return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
> +       .vidioc_querycap                = gsc_m2m_querycap,
> +
> +       .vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane,
> +       .vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane,
> +
> +       .vidioc_g_fmt_vid_cap_mplane    = gsc_m2m_g_fmt_mplane,
> +       .vidioc_g_fmt_vid_out_mplane    = gsc_m2m_g_fmt_mplane,
> +
> +       .vidioc_try_fmt_vid_cap_mplane  = gsc_m2m_try_fmt_mplane,
> +       .vidioc_try_fmt_vid_out_mplane  = gsc_m2m_try_fmt_mplane,
> +
> +       .vidioc_s_fmt_vid_cap_mplane    = gsc_m2m_s_fmt_mplane,
> +       .vidioc_s_fmt_vid_out_mplane    = gsc_m2m_s_fmt_mplane,
> +
> +       .vidioc_reqbufs                 = gsc_m2m_reqbufs,
> +       .vidioc_querybuf                = gsc_m2m_querybuf,
> +
> +       .vidioc_qbuf                    = gsc_m2m_qbuf,
> +       .vidioc_dqbuf                   = gsc_m2m_dqbuf,
> +
> +       .vidioc_streamon                = gsc_m2m_streamon,
> +       .vidioc_streamoff               = gsc_m2m_streamoff,
> +
> +       .vidioc_g_selection             = gsc_m2m_g_selection,
> +       .vidioc_s_selection             = gsc_m2m_s_selection
> +
> +};
> +
> +static int queue_init(void *priv, struct vb2_queue *src_vq,
> +                     struct vb2_queue *dst_vq)
> +{
> +       struct gsc_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->drv_priv = ctx;
> +       src_vq->ops = &gsc_m2m_qops;
> +       src_vq->mem_ops = &vb2_dma_contig_memops;
> +       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->drv_priv = ctx;
> +       dst_vq->ops = &gsc_m2m_qops;
> +       dst_vq->mem_ops = &vb2_dma_contig_memops;
> +       dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> +
> +       return vb2_queue_init(dst_vq);
> +}
> +
> +static int gsc_m2m_open(struct file *file)
> +{
> +       struct gsc_dev *gsc = video_drvdata(file);
> +       struct gsc_ctx *ctx = NULL;
> +       int ret;
> +
> +       gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> +       ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +       if (!ctx)
> +               return -ENOMEM;
> +
> +       v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
> +       ret = gsc_ctrls_create(ctx);
> +       if (ret)
> +               goto error_fh;
> +
> +       /* Use separate control handler per file handle */
> +       ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> +       file->private_data = &ctx->fh;
> +       v4l2_fh_add(&ctx->fh);
> +
> +       ctx->gsc_dev = gsc;
> +       /* Default color format */
> +       ctx->s_frame.fmt = get_format(0);
> +       ctx->d_frame.fmt = get_format(0);
> +       /* Setup the device context for mem2mem mode. */
> +       ctx->state |= GSC_CTX_M2M;
> +       ctx->flags = 0;
> +       ctx->in_path = GSC_DMA;
> +       ctx->out_path = GSC_DMA;
> +       spin_lock_init(&ctx->slock);
> +
> +       ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
> +       if (IS_ERR(ctx->m2m_ctx)) {
> +               gsc_err("Failed to initialize m2m context");
> +               ret = PTR_ERR(ctx->m2m_ctx);
> +               goto error_fh;
> +       }
> +
> +       if (gsc->m2m.refcnt++ == 0)
> +               set_bit(ST_M2M_OPEN, &gsc->state);
> +
> +       gsc_dbg("gsc m2m driver is opened, ctx(0x%p)", ctx);
> +       return 0;
> +
> +error_fh:

No need to ctrls_delete somewhere here as well?

> +       v4l2_fh_del(&ctx->fh);
> +       v4l2_fh_exit(&ctx->fh);
> +       kfree(ctx);
> +       return ret;
> +}
> +
> +static int gsc_m2m_release(struct file *file)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +       struct gsc_dev *gsc = ctx->gsc_dev;
> +
> +       gsc_dbg("pid: %d, state: 0x%lx, refcnt= %d",
> +               task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
> +
> +       v4l2_m2m_ctx_release(ctx->m2m_ctx);
> +       gsc_ctrls_delete(ctx);
> +       v4l2_fh_del(&ctx->fh);
> +       v4l2_fh_exit(&ctx->fh);
> +
> +       if (--gsc->m2m.refcnt <= 0)
> +               clear_bit(ST_M2M_OPEN, &gsc->state);
> +       kfree(ctx);
> +       return 0;
> +}
> +
> +static unsigned int gsc_m2m_poll(struct file *file,
> +                                    struct poll_table_struct *wait)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> +       return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
> +}
> +
> +static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +       struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> +       return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
> +}
> +static const struct v4l2_file_operations gsc_m2m_fops = {
> +       .owner          = THIS_MODULE,
> +       .open           = gsc_m2m_open,
> +       .release        = gsc_m2m_release,
> +       .poll           = gsc_m2m_poll,
> +       .unlocked_ioctl = video_ioctl2,
> +       .mmap           = gsc_m2m_mmap,
> +};
> +
> +static struct v4l2_m2m_ops gsc_m2m_ops = {
> +       .device_run     = gsc_m2m_device_run,
> +       .job_abort      = gsc_m2m_job_abort,
> +};
> +
> +int gsc_register_m2m_device(struct gsc_dev *gsc)
> +{
> +       struct video_device *vfd;
> +       struct platform_device *pdev;
> +       int ret = 0;
> +
> +       if (!gsc)
> +               return -ENODEV;
> +
> +       pdev = gsc->pdev;
> +
> +       vfd = video_device_alloc();
> +       if (!vfd) {
> +               dev_err(&pdev->dev, "Failed to allocate video device\n");
> +               return -ENOMEM;
> +       }
> +
> +       vfd->fops       = &gsc_m2m_fops;
> +       vfd->ioctl_ops  = &gsc_m2m_ioctl_ops;
> +       vfd->release    = video_device_release;
> +       vfd->lock       = &gsc->lock;
> +       snprintf(vfd->name, sizeof(vfd->name), "%s.%d:m2m",
> +                                       GSC_MODULE_NAME, gsc->id);
> +
> +       video_set_drvdata(vfd, gsc);
> +
> +       gsc->m2m.vfd = vfd;
> +       gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
> +       if (IS_ERR(gsc->m2m.m2m_dev)) {
> +               dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
> +               ret = PTR_ERR(gsc->m2m.m2m_dev);
> +               goto err_m2m_r1;
> +       }
> +
> +       ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
> +       if (ret) {
> +               dev_err(&pdev->dev,
> +                        "%s(): failed to register video device\n", __func__);
> +               goto err_m2m_r2;
> +       }
> +
> +       gsc_dbg("gsc m2m driver registered as /dev/video%d", vfd->num);
> +
> +       return 0;
> +
> +err_m2m_r2:
> +       v4l2_m2m_release(gsc->m2m.m2m_dev);
> +err_m2m_r1:
> +       video_device_release(gsc->m2m.vfd);
> +
> +       return ret;
> +}
> +
> +void gsc_unregister_m2m_device(struct gsc_dev *gsc)
> +{
> +       if (gsc)
> +               v4l2_m2m_release(gsc->m2m.m2m_dev);
> +}
> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c
> new file mode 100644
> index 0000000..7c5cce2
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-regs.c
> @@ -0,0 +1,579 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-regs.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *             http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <mach/map.h>
> +#include "gsc-core.h"
> +
> +void gsc_hw_set_sw_reset(struct gsc_dev *dev)
> +{
> +       u32 cfg = 0;
> +
> +       cfg |= GSC_SW_RESET_SRESET;
> +       writel(cfg, dev->regs + GSC_SW_RESET);
> +}
> +
> +int gsc_wait_reset(struct gsc_dev *dev)
> +{
> +       unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> +       u32 cfg;
> +
> +       while (time_before(jiffies, timeo)) {
> +               cfg = readl(dev->regs + GSC_SW_RESET);
> +               if (!cfg)
> +                       return 0;
> +               usleep_range(10, 20);
> +       }
> +       gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
> +
> +       return -EBUSY;
> +}
> +
> +int gsc_wait_operating(struct gsc_dev *dev)
> +{
> +       unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> +       u32 cfg;
> +
> +       while (time_before(jiffies, timeo)) {
> +               cfg = readl(dev->regs + GSC_ENABLE);
> +               if ((cfg & GSC_ENABLE_OP_STATUS) == GSC_ENABLE_OP_STATUS)
> +                       return 0;
> +               usleep_range(10, 20);
> +       }
> +       gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
> +
> +       return -EBUSY;
> +}
> +
> +int gsc_wait_stop(struct gsc_dev *dev)
> +{
> +       unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> +       u32 cfg;
> +
> +       while (time_before(jiffies, timeo)) {
> +               cfg = readl(dev->regs + GSC_ENABLE);
> +               if (!(cfg & GSC_ENABLE_OP_STATUS))
> +                       return 0;
> +               usleep_range(10, 20);
> +       }
> +       gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
> +
> +       return -EBUSY;
> +}
> +
> +
> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask)
> +{
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_ENABLE);
> +       if (mask)
> +               cfg |= GSC_ENABLE_ON_CLEAR;
> +       else
> +               cfg &= ~GSC_ENABLE_ON_CLEAR;
> +       writel(cfg, dev->regs + GSC_ENABLE);
> +}
> +
> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev)
> +{
> +       u32 cfg, status, bits = 0;
> +
> +       cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +       status = cfg & GSC_IN_BASE_ADDR_MASK;
> +       while (status) {
> +               status = status & (status - 1);
> +               bits++;
> +       }
> +       return bits;
> +}
> +
> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev)
> +{
> +       u32 cfg, curr_index, i;
> +
> +       cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +       curr_index = GSC_IN_CURR_GET_INDEX(cfg);
> +       for (i = curr_index; i > 1; i--) {
> +               if (cfg ^ (1 << (i - 2)))
> +                       return i - 2;
> +       }
> +
> +       for (i = dev->variant->in_buf_cnt; i > curr_index; i--) {
> +               if (cfg ^ (1 << (i - 1)))
> +                       return i - 1;
> +       }
> +
> +       return curr_index - 1;
> +}
> +
> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask)
> +{
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_IRQ);
> +       if (mask)
> +               cfg |= GSC_IRQ_FRMDONE_MASK;
> +       else
> +               cfg &= ~GSC_IRQ_FRMDONE_MASK;
> +       writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask)
> +{
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_IRQ);
> +       if (mask)
> +               cfg |= GSC_IRQ_OR_MASK;
> +       else
> +               cfg &= ~GSC_IRQ_OR_MASK;
> +       writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask)
> +{
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_IRQ);
> +       if (mask)
> +               cfg |= GSC_IRQ_ENABLE;
> +       else
> +               cfg &= ~GSC_IRQ_ENABLE;
> +       writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev)
> +{
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +       cfg |= GSC_IN_BASE_ADDR_MASK;
> +       cfg |= GSC_IN_BASE_ADDR_PINGPONG(dev->variant->in_buf_cnt);
> +
> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev)
> +{
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +       cfg |= GSC_OUT_BASE_ADDR_MASK;
> +       cfg |= GSC_OUT_BASE_ADDR_PINGPONG(dev->variant->out_buf_cnt);
> +
> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift,
> +                               bool enable)
> +{
> +       u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +       u32 mask = 1 << shift;
> +
> +       cfg &= (~mask);
> +       cfg |= enable << shift;
> +
> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
> +                               bool enable)
> +{
> +       u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +       u32 mask = 1 << shift;
> +
> +       cfg &= (~mask);
> +       cfg |= enable << shift;
> +
> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
> +}
> +
> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev)
> +{
> +       u32 bits = 0;
> +       u32 mask_bits = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +       mask_bits &= GSC_OUT_BASE_ADDR_MASK;
> +
> +       while (mask_bits) {
> +               mask_bits = mask_bits & (mask_bits - 1);

mask_bits &=

> +               bits++;
> +       }
> +       bits = 16 - bits;
> +
> +       return bits;
> +}
> +
> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
> +                               int index)
> +{
> +       gsc_dbg("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
> +               addr->y, addr->cb, addr->cr);
> +       writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
> +       writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
> +       writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
> +
> +}
> +
> +void gsc_hw_set_output_addr(struct gsc_dev *dev,
> +                            struct gsc_addr *addr, int index)
> +{
> +       gsc_dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
> +                       index, addr->y, addr->cb, addr->cr);
> +       writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
> +       writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
> +       writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
> +}
> +
> +void gsc_hw_set_input_path(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +
> +       u32 cfg = readl(dev->regs + GSC_IN_CON);
> +       cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
> +
> +       if (ctx->in_path == GSC_DMA)
> +               cfg |= GSC_IN_PATH_MEMORY;
> +
> +       writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_in_size(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_frame *frame = &ctx->s_frame;
> +       u32 cfg;
> +
> +       /* Set input pixel offset */
> +       cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left);
> +       cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top);
> +       writel(cfg, dev->regs + GSC_SRCIMG_OFFSET);
> +
> +       /* Set input original size */
> +       cfg = GSC_SRCIMG_WIDTH(frame->f_width);
> +       cfg |= GSC_SRCIMG_HEIGHT(frame->f_height);
> +       writel(cfg, dev->regs + GSC_SRCIMG_SIZE);
> +
> +       /* Set input cropped size */
> +       cfg = GSC_CROPPED_WIDTH(frame->crop.width);
> +       cfg |= GSC_CROPPED_HEIGHT(frame->crop.height);
> +       writel(cfg, dev->regs + GSC_CROPPED_SIZE);
> +}
> +
> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_frame *frame = &ctx->s_frame;
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_IN_CON);
> +       if (ctx->gsc_ctrls.csc_eq->val) {
> +               if (ctx->gsc_ctrls.csc_range->val)
> +                       cfg |= GSC_IN_RGB_HD_WIDE;
> +               else
> +                       cfg |= GSC_IN_RGB_HD_NARROW;
> +       } else {
> +               if (ctx->gsc_ctrls.csc_range->val)
> +                       cfg |= GSC_IN_RGB_SD_WIDE;
> +               else
> +                       cfg |= GSC_IN_RGB_SD_NARROW;
> +       }
> +
> +       if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
> +               cfg |= GSC_IN_RGB565;
> +       else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
> +               cfg |= GSC_IN_XRGB8888;
> +
> +       writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_frame *frame = &ctx->s_frame;
> +       u32 i, depth = 0;
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_IN_CON);
> +       cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
> +                GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK |
> +                GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE);
> +       writel(cfg, dev->regs + GSC_IN_CON);
> +
> +       if (is_rgb(frame->fmt->color)) {
> +               gsc_hw_set_in_image_rgb(ctx);
> +               return;
> +       }
> +       for (i = 0; i < frame->fmt->num_planes; i++)
> +               depth += frame->fmt->depth[i];
> +
> +       switch (frame->fmt->num_comp) {
> +       case 1:
> +               cfg |= GSC_IN_YUV422_1P;
> +               if (frame->fmt->yorder == GSC_LSB_Y)
> +                       cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y;
> +               else
> +                       cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C;
> +               if (frame->fmt->corder == GSC_CBCR)
> +                       cfg |= GSC_IN_CHROMA_ORDER_CBCR;
> +               else
> +                       cfg |= GSC_IN_CHROMA_ORDER_CRCB;
> +               break;
> +       case 2:
> +               if (depth == 12)
> +                       cfg |= GSC_IN_YUV420_2P;
> +               else
> +                       cfg |= GSC_IN_YUV422_2P;
> +               if (frame->fmt->corder == GSC_CBCR)
> +                       cfg |= GSC_IN_CHROMA_ORDER_CBCR;
> +               else
> +                       cfg |= GSC_IN_CHROMA_ORDER_CRCB;
> +               break;
> +       case 3:
> +               if (depth == 12)
> +                       cfg |= GSC_IN_YUV420_3P;
> +               else
> +                       cfg |= GSC_IN_YUV422_3P;
> +               break;
> +       };
> +
> +       if (is_tiled(frame->fmt))
> +               cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE;
> +
> +       writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_output_path(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +
> +       u32 cfg = readl(dev->regs + GSC_OUT_CON);
> +       cfg &= ~GSC_OUT_PATH_MASK;
> +
> +       if (ctx->out_path == GSC_DMA)
> +               cfg |= GSC_OUT_PATH_MEMORY;
> +       else
> +               cfg |= GSC_OUT_PATH_LOCAL;
> +
> +       writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_out_size(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_frame *frame = &ctx->d_frame;
> +       u32 cfg;
> +
> +       /* Set output original size */
> +       if (ctx->out_path == GSC_DMA) {
> +               cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left);
> +               cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top);
> +               writel(cfg, dev->regs + GSC_DSTIMG_OFFSET);
> +
> +               cfg = GSC_DSTIMG_WIDTH(frame->f_width);
> +               cfg |= GSC_DSTIMG_HEIGHT(frame->f_height);
> +               writel(cfg, dev->regs + GSC_DSTIMG_SIZE);
> +       }
> +
> +       /* Set output scaled size */
> +       if (ctx->gsc_ctrls.rotate->val == 90 ||
> +           ctx->gsc_ctrls.rotate->val == 270) {
> +               cfg = GSC_SCALED_WIDTH(frame->crop.height);
> +               cfg |= GSC_SCALED_HEIGHT(frame->crop.width);
> +       } else {
> +               cfg = GSC_SCALED_WIDTH(frame->crop.width);
> +               cfg |= GSC_SCALED_HEIGHT(frame->crop.height);
> +       }
> +       writel(cfg, dev->regs + GSC_SCALED_SIZE);
> +}
> +
> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_frame *frame = &ctx->d_frame;
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_OUT_CON);
> +       if (ctx->gsc_ctrls.csc_eq->val) {
> +               if (ctx->gsc_ctrls.csc_range->val)
> +                       cfg |= GSC_OUT_RGB_HD_WIDE;
> +               else
> +                       cfg |= GSC_OUT_RGB_HD_NARROW;
> +       } else {
> +               if (ctx->gsc_ctrls.csc_range->val)
> +                       cfg |= GSC_OUT_RGB_SD_WIDE;
> +               else
> +                       cfg |= GSC_OUT_RGB_SD_NARROW;
> +       }
> +
> +       if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
> +               cfg |= GSC_OUT_RGB565;
> +       else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
> +               cfg |= GSC_OUT_XRGB8888;
> +
> +       writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_frame *frame = &ctx->d_frame;
> +       u32 i, depth = 0;
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_OUT_CON);
> +       cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
> +                GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK |
> +                GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE);
> +       writel(cfg, dev->regs + GSC_OUT_CON);
> +
> +       if (is_rgb(frame->fmt->color)) {
> +               gsc_hw_set_out_image_rgb(ctx);
> +               return;
> +       }
> +
> +       if (ctx->out_path != GSC_DMA) {
> +               cfg |= GSC_OUT_YUV444;
> +               goto end_set;
> +       }
> +
> +       for (i = 0; i < frame->fmt->num_planes; i++)
> +               depth += frame->fmt->depth[i];
> +
> +       switch (frame->fmt->num_comp) {
> +       case 1:
> +               cfg |= GSC_OUT_YUV422_1P;
> +               if (frame->fmt->yorder == GSC_LSB_Y)
> +                       cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y;
> +               else
> +                       cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C;
> +               if (frame->fmt->corder == GSC_CBCR)
> +                       cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
> +               else
> +                       cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
> +               break;
> +       case 2:
> +               if (depth == 12)
> +                       cfg |= GSC_OUT_YUV420_2P;
> +               else
> +                       cfg |= GSC_OUT_YUV422_2P;
> +               if (frame->fmt->corder == GSC_CBCR)
> +                       cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
> +               else
> +                       cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
> +               break;
> +       case 3:
> +               cfg |= GSC_OUT_YUV420_3P;
> +               break;
> +       };
> +
> +       if (is_tiled(frame->fmt))
> +               cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE;
> +
> +end_set:
> +       writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_scaler *sc = &ctx->scaler;
> +       u32 cfg;
> +
> +       cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor);
> +       cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio);
> +       cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio);
> +       writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO);
> +}
> +
> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_scaler *sc = &ctx->scaler;
> +       u32 cfg;
> +
> +       cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
> +       writel(cfg, dev->regs + GSC_MAIN_H_RATIO);
> +
> +       cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio);
> +       writel(cfg, dev->regs + GSC_MAIN_V_RATIO);
> +}
> +
> +void gsc_hw_set_rotation(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_IN_CON);
> +       cfg &= ~GSC_IN_ROT_MASK;
> +
> +       switch (ctx->gsc_ctrls.rotate->val) {
> +       case 270:
> +               cfg |= GSC_IN_ROT_270;
> +               break;
> +       case 180:
> +               cfg |= GSC_IN_ROT_180;
> +               break;
> +       case 90:
> +               if (ctx->gsc_ctrls.hflip->val)
> +                       cfg |= GSC_IN_ROT_90_XFLIP;
> +               else if (ctx->gsc_ctrls.vflip->val)
> +                       cfg |= GSC_IN_ROT_90_YFLIP;
> +               else
> +                       cfg |= GSC_IN_ROT_90;
> +               break;
> +       case 0:
> +               if (ctx->gsc_ctrls.hflip->val)
> +                       cfg |= GSC_IN_ROT_XFLIP;
> +               else if (ctx->gsc_ctrls.vflip->val)
> +                       cfg |= GSC_IN_ROT_YFLIP;
> +       }
> +
> +       writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       struct gsc_frame *frame = &ctx->d_frame;
> +       u32 cfg;
> +
> +       if (!is_rgb(frame->fmt->color)) {
> +               gsc_dbg("Not a RGB format");
> +               return;
> +       }
> +
> +       cfg = readl(dev->regs + GSC_OUT_CON);
> +       cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK;
> +
> +       cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val);
> +       writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx)
> +{
> +       struct gsc_dev *dev = ctx->gsc_dev;
> +       u32 cfg;
> +
> +       cfg = readl(dev->regs + GSC_ENABLE);
> +       cfg |= GSC_ENABLE_SFR_UPDATE;
> +       writel(cfg, dev->regs + GSC_ENABLE);
> +}
> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.h b/drivers/media/video/exynos/gsc/gsc-regs.h
> new file mode 100644
> index 0000000..50ddabe
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-regs.h
> @@ -0,0 +1,211 @@
> +/* linux/drivers/media/video/exynos/gsc/regs-gsc.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *             http://www.samsung.com
> + *
> + * Register definition file for Samsung G-Scaler 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 REGS_GSC_H_
> +#define REGS_GSC_H_
> +
> +/* G-Scaler enable */
> +#define GSC_ENABLE                     0x00
> +#define GSC_ENABLE_ON_CLEAR            (1 << 4)
> +#define GSC_ENABLE_QOS_ENABLE          (1 << 3)
> +#define GSC_ENABLE_OP_STATUS           (1 << 2)
> +#define GSC_ENABLE_SFR_UPDATE          (1 << 1)
> +#define GSC_ENABLE_ON                  (1 << 0)
> +
> +/* G-Scaler S/W reset */
> +#define GSC_SW_RESET                   0x04
> +#define GSC_SW_RESET_SRESET            (1 << 0)
> +
> +/* G-Scaler IRQ */
> +#define GSC_IRQ                                0x08
> +#define GSC_IRQ_STATUS_OR_IRQ          (1 << 17)
> +#define GSC_IRQ_STATUS_OR_FRM_DONE     (1 << 16)
> +#define GSC_IRQ_OR_MASK                        (1 << 2)
> +#define GSC_IRQ_FRMDONE_MASK           (1 << 1)
> +#define GSC_IRQ_ENABLE                 (1 << 0)
> +
> +/* G-Scaler input control */
> +#define GSC_IN_CON                     0x10
> +#define GSC_IN_ROT_MASK                        (7 << 16)
> +#define GSC_IN_ROT_270                 (7 << 16)
> +#define GSC_IN_ROT_90_YFLIP            (6 << 16)
> +#define GSC_IN_ROT_90_XFLIP            (5 << 16)
> +#define GSC_IN_ROT_90                  (4 << 16)
> +#define GSC_IN_ROT_180                 (3 << 16)
> +#define GSC_IN_ROT_YFLIP               (2 << 16)
> +#define GSC_IN_ROT_XFLIP               (1 << 16)
> +#define GSC_IN_RGB_TYPE_MASK           (3 << 14)
> +#define GSC_IN_RGB_HD_WIDE             (3 << 14)
> +#define GSC_IN_RGB_HD_NARROW           (2 << 14)
> +#define GSC_IN_RGB_SD_WIDE             (1 << 14)
> +#define GSC_IN_RGB_SD_NARROW           (0 << 14)
> +#define GSC_IN_YUV422_1P_ORDER_MASK    (1 << 13)
> +#define GSC_IN_YUV422_1P_ORDER_LSB_Y   (0 << 13)
> +#define GSC_IN_YUV422_1P_OEDER_LSB_C   (1 << 13)
> +#define GSC_IN_CHROMA_ORDER_MASK       (1 << 12)
> +#define GSC_IN_CHROMA_ORDER_CBCR       (0 << 12)
> +#define GSC_IN_CHROMA_ORDER_CRCB       (1 << 12)
> +#define GSC_IN_FORMAT_MASK             (7 << 8)
> +#define GSC_IN_XRGB8888                        (0 << 8)
> +#define GSC_IN_RGB565                  (1 << 8)
> +#define GSC_IN_YUV420_2P               (2 << 8)
> +#define GSC_IN_YUV420_3P               (3 << 8)
> +#define GSC_IN_YUV422_1P               (4 << 8)
> +#define GSC_IN_YUV422_2P               (5 << 8)
> +#define GSC_IN_YUV422_3P               (6 << 8)
> +#define GSC_IN_TILE_TYPE_MASK          (1 << 4)
> +#define GSC_IN_TILE_C_16x8             (0 << 4)
> +#define GSC_IN_TILE_C_16x16            (1 << 4)
> +#define GSC_IN_TILE_MODE               (1 << 3)
> +#define GSC_IN_LOCAL_SEL_MASK          (3 << 1)
> +#define GSC_IN_LOCAL_FIMD_WB           (2 << 1)
> +#define GSC_IN_LOCAL_CAM1              (1 << 1)
> +#define GSC_IN_LOCAL_CAM0              (0 << 1)
> +#define GSC_IN_PATH_MASK               (1 << 0)
> +#define GSC_IN_PATH_LOCAL              (1 << 0)
> +#define GSC_IN_PATH_MEMORY             (0 << 0)
> +
> +/* G-Scaler source image size */
> +#define GSC_SRCIMG_SIZE                        0x14
> +#define GSC_SRCIMG_HEIGHT_MASK         (0x1fff << 16)
> +#define GSC_SRCIMG_HEIGHT(x)           ((x) << 16)
> +#define GSC_SRCIMG_WIDTH_MASK          (0x1fff << 0)
> +#define GSC_SRCIMG_WIDTH(x)            ((x) << 0)
> +
> +/* G-Scaler source image offset */
> +#define GSC_SRCIMG_OFFSET              0x18
> +#define GSC_SRCIMG_OFFSET_Y_MASK       (0x1fff << 16)
> +#define GSC_SRCIMG_OFFSET_Y(x)         ((x) << 16)
> +#define GSC_SRCIMG_OFFSET_X_MASK       (0x1fff << 0)
> +#define GSC_SRCIMG_OFFSET_X(x)         ((x) << 0)
> +
> +/* G-Scaler cropped source image size */
> +#define GSC_CROPPED_SIZE               0x1c
> +#define GSC_CROPPED_HEIGHT_MASK                (0x1fff << 16)
> +#define GSC_CROPPED_HEIGHT(x)          ((x) << 16)
> +#define GSC_CROPPED_WIDTH_MASK         (0x1fff << 0)
> +#define GSC_CROPPED_WIDTH(x)           ((x) << 0)
> +
> +/* G-Scaler output control */
> +#define GSC_OUT_CON                    0x20
> +#define GSC_OUT_GLOBAL_ALPHA_MASK      (0xff << 24)
> +#define GSC_OUT_GLOBAL_ALPHA(x)                ((x) << 24)
> +#define GSC_OUT_RGB_TYPE_MASK          (3 << 10)
> +#define GSC_OUT_RGB_HD_NARROW          (3 << 10)
> +#define GSC_OUT_RGB_HD_WIDE            (2 << 10)
> +#define GSC_OUT_RGB_SD_NARROW          (1 << 10)
> +#define GSC_OUT_RGB_SD_WIDE            (0 << 10)
> +#define GSC_OUT_YUV422_1P_ORDER_MASK   (1 << 9)
> +#define GSC_OUT_YUV422_1P_ORDER_LSB_Y  (0 << 9)
> +#define GSC_OUT_YUV422_1P_OEDER_LSB_C  (1 << 9)
> +#define GSC_OUT_CHROMA_ORDER_MASK      (1 << 8)
> +#define GSC_OUT_CHROMA_ORDER_CBCR      (0 << 8)
> +#define GSC_OUT_CHROMA_ORDER_CRCB      (1 << 8)
> +#define GSC_OUT_FORMAT_MASK            (7 << 4)
> +#define GSC_OUT_XRGB8888               (0 << 4)
> +#define GSC_OUT_RGB565                 (1 << 4)
> +#define GSC_OUT_YUV420_2P              (2 << 4)
> +#define GSC_OUT_YUV420_3P              (3 << 4)
> +#define GSC_OUT_YUV422_1P              (4 << 4)
> +#define GSC_OUT_YUV422_2P              (5 << 4)
> +#define GSC_OUT_YUV444                 (7 << 4)
> +#define GSC_OUT_TILE_TYPE_MASK         (1 << 2)
> +#define GSC_OUT_TILE_C_16x8            (0 << 2)
> +#define GSC_OUT_TILE_C_16x16           (1 << 2)
> +#define GSC_OUT_TILE_MODE              (1 << 1)
> +#define GSC_OUT_PATH_MASK              (1 << 0)
> +#define GSC_OUT_PATH_LOCAL             (1 << 0)
> +#define GSC_OUT_PATH_MEMORY            (0 << 0)
> +
> +/* G-Scaler scaled destination image size */
> +#define GSC_SCALED_SIZE                        0x24
> +#define GSC_SCALED_HEIGHT_MASK         (0x1fff << 16)
> +#define GSC_SCALED_HEIGHT(x)           ((x) << 16)
> +#define GSC_SCALED_WIDTH_MASK          (0x1fff << 0)
> +#define GSC_SCALED_WIDTH(x)            ((x) << 0)
> +
> +/* G-Scaler pre scale ratio */
> +#define GSC_PRE_SCALE_RATIO            0x28
> +#define GSC_PRESC_SHFACTOR_MASK                (7 << 28)
> +#define GSC_PRESC_SHFACTOR(x)          ((x) << 28)
> +#define GSC_PRESC_V_RATIO_MASK         (7 << 16)
> +#define GSC_PRESC_V_RATIO(x)           ((x) << 16)
> +#define GSC_PRESC_H_RATIO_MASK         (7 << 0)
> +#define GSC_PRESC_H_RATIO(x)           ((x) << 0)
> +
> +/* G-Scaler main scale horizontal ratio */
> +#define GSC_MAIN_H_RATIO               0x2c
> +#define GSC_MAIN_H_RATIO_MASK          (0xfffff << 0)
> +#define GSC_MAIN_H_RATIO_VALUE(x)      ((x) << 0)
> +
> +/* G-Scaler main scale vertical ratio */
> +#define GSC_MAIN_V_RATIO               0x30
> +#define GSC_MAIN_V_RATIO_MASK          (0xfffff << 0)
> +#define GSC_MAIN_V_RATIO_VALUE(x)      ((x) << 0)
> +
> +/* G-Scaler destination image size */
> +#define GSC_DSTIMG_SIZE                        0x40
> +#define GSC_DSTIMG_HEIGHT_MASK         (0x1fff << 16)
> +#define GSC_DSTIMG_HEIGHT(x)           ((x) << 16)
> +#define GSC_DSTIMG_WIDTH_MASK          (0x1fff << 0)
> +#define GSC_DSTIMG_WIDTH(x)            ((x) << 0)
> +
> +/* G-Scaler destination image offset */
> +#define GSC_DSTIMG_OFFSET              0x44
> +#define GSC_DSTIMG_OFFSET_Y_MASK       (0x1fff << 16)
> +#define GSC_DSTIMG_OFFSET_Y(x)         ((x) << 16)
> +#define GSC_DSTIMG_OFFSET_X_MASK       (0x1fff << 0)
> +#define GSC_DSTIMG_OFFSET_X(x)         ((x) << 0)
> +
> +/* G-Scaler input y address mask */
> +#define GSC_IN_BASE_ADDR_Y_MASK                0x4c
> +/* G-Scaler input y base address */
> +#define GSC_IN_BASE_ADDR_Y(n)          (0x50 + (n) * 0x4)
> +
> +/* G-Scaler input cb address mask */
> +#define GSC_IN_BASE_ADDR_CB_MASK       0x7c
> +/* G-Scaler input cb base address */
> +#define GSC_IN_BASE_ADDR_CB(n)         (0x80 + (n) * 0x4)
> +
> +/* G-Scaler input cr address mask */
> +#define GSC_IN_BASE_ADDR_CR_MASK       0xac
> +/* G-Scaler input cr base address */
> +#define GSC_IN_BASE_ADDR_CR(n)         (0xb0 + (n) * 0x4)
> +
> +/* G-Scaler input address mask */
> +#define GSC_IN_CURR_ADDR_INDEX         (0xf << 12)
> +#define GSC_IN_CURR_GET_INDEX(x)       ((x) >> 12)
> +#define GSC_IN_BASE_ADDR_PINGPONG(x)   ((x) << 8)
> +#define GSC_IN_BASE_ADDR_MASK          (0xff << 0)
> +
> +/* G-Scaler output y address mask */
> +#define GSC_OUT_BASE_ADDR_Y_MASK       0x10c
> +/* G-Scaler output y base address */
> +#define GSC_OUT_BASE_ADDR_Y(n)         (0x110 + (n) * 0x4)
> +
> +/* G-Scaler output cb address mask */
> +#define GSC_OUT_BASE_ADDR_CB_MASK      0x15c
> +/* G-Scaler output cb base address */
> +#define GSC_OUT_BASE_ADDR_CB(n)                (0x160 + (n) * 0x4)
> +
> +/* G-Scaler output cr address mask */
> +#define GSC_OUT_BASE_ADDR_CR_MASK      0x1ac
> +/* G-Scaler output cr base address */
> +#define GSC_OUT_BASE_ADDR_CR(n)                (0x1b0 + (n) * 0x4)
> +
> +/* G-Scaler output address mask */
> +#define GSC_OUT_CURR_ADDR_INDEX                (0xf << 24)
> +#define GSC_OUT_CURR_GET_INDEX(x)      ((x) >> 24)
> +#define GSC_OUT_BASE_ADDR_PINGPONG(x)  ((x) << 16)
> +#define GSC_OUT_BASE_ADDR_MASK         (0xffff << 0)

Some of the defines in this file are not used like those above.

> +
> +#endif /* REGS_GSC_H_ */
> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
> index f79d0cc..5b4a33b 100644
> --- a/include/linux/videodev2.h
> +++ b/include/linux/videodev2.h
> @@ -360,9 +360,11 @@ struct v4l2_pix_format {
>  /* two non contiguous planes - one Y, one Cr + Cb interleaved  */
>  #define V4L2_PIX_FMT_NV12M   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
>  #define V4L2_PIX_FMT_NV12MT  v4l2_fourcc('T', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 64x32 macroblocks */

Already defined in videodev2.h

> +#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 16x16 macroblocks */
>
>  /* three non contiguous planes - Y, Cb, Cr */
>  #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
> +#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'V', 'U', 'M') /* 12  YVU420 planar */
>
>  /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
>  #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */

Some of those formats are defined in videodev2.h. For those that are not,
you should add them there instead.

> --
> 1.7.0.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sylwester Nawrocki July 11, 2012, 10:39 p.m. UTC | #2
Hi Shaik,

On 07/05/2012 12:27 PM, Shaik Ameer Basha wrote:
> This patch add support for gscaler (generic scaler) device which is a

nit: s/add/adds

> new device for scaling and color space conversion on EXYNOS5 SoCs.
>
> This device supports the followings as key feature.
>   1) Input image format
>     - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, TILE
>   2) Output image format
>     - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, YUV444
>   3) Input rotation
>     - 0/90/180/270 degree, X/Y Flip
>   4) Scale ratio
>     - 1/16 scale down to 8 scale up
>   5) CSC
>     - RGB to YUV / YUV to RGB
>   6) Size
>     - 2048 x 2048 for tile or rotation
>     - 4800 x 3344 other case
>
> Signed-off-by: Hynwoong Kim<khw0178.kim@samsung.com>
> Signed-off-by: Sungchun Kang<sungchun.kang@samsung.com>
> Signed-off-by: Shaik Ameer Basha<shaik.ameer@samsung.com>

Overall this looks quite good to me. There seems just to be a few issues
that need more polishing. Please see below...

> ---
>   drivers/media/video/Kconfig               |   10 +
>   drivers/media/video/Makefile              |    1 +
>   drivers/media/video/exynos/Kconfig        |   11 +
>   drivers/media/video/exynos/Makefile       |    1 +
>   drivers/media/video/exynos/gsc/Kconfig    |    7 +
>   drivers/media/video/exynos/gsc/Makefile   |    3 +
>   drivers/media/video/exynos/gsc/gsc-core.c | 1304 +++++++++++++++++++++++++++++
>   drivers/media/video/exynos/gsc/gsc-core.h |  652 ++++++++++++++
>   drivers/media/video/exynos/gsc/gsc-m2m.c  |  751 +++++++++++++++++
>   drivers/media/video/exynos/gsc/gsc-regs.c |  579 +++++++++++++
>   drivers/media/video/exynos/gsc/gsc-regs.h |  211 +++++
>   include/linux/videodev2.h                 |    2 +
>   12 files changed, 3532 insertions(+), 0 deletions(-)
>   create mode 100644 drivers/media/video/exynos/Kconfig
>   create mode 100644 drivers/media/video/exynos/Makefile
>   create mode 100644 drivers/media/video/exynos/gsc/Kconfig
>   create mode 100644 drivers/media/video/exynos/gsc/Makefile
>   create mode 100644 drivers/media/video/exynos/gsc/gsc-core.c
>   create mode 100644 drivers/media/video/exynos/gsc/gsc-core.h
>   create mode 100644 drivers/media/video/exynos/gsc/gsc-m2m.c
>   create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.c
>   create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.h

How about just placing this driver in drivers/media/video/exynos-gsc/
directory ? We could avoid this way unnecessary number of nested 
directories, plus it would better reflect current situation, as some
media/video/s5p-* drivers also cover Exynos SoCs.

>
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 99937c9..0ff1abf 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -1153,6 +1153,7 @@ config VIDEO_ATMEL_ISI
>
>   source "drivers/media/video/s5p-fimc/Kconfig"
>   source "drivers/media/video/s5p-tv/Kconfig"
> +source "drivers/media/video/exynos/Kconfig"
>
>   endif # V4L_PLATFORM_DRIVERS
>   endif # VIDEO_CAPTURE_DRIVERS
> @@ -1215,4 +1216,13 @@ config VIDEO_MX2_EMMAPRP
>   	    memory to memory. Operations include resizing and format
>   	    conversion.
>
> +config VIDEO_SAMSUNG_GSCALER
> +        tristate "Samsung Exynos GSC driver"
> +        depends on VIDEO_DEV&&  VIDEO_V4L2&&  PLAT_S5P
> +        select VIDEOBUF2_DMA_CONTIG
> +        select V4L2_MEM2MEM_DEV
> +        select DMA_SHARED_BUFFER

Is this line really needed ? Support for dma_buf could be added 
in a separate patch, after Tomasz's work on dma_buf support in 
V4L2 hits mainline.

> +        help
> +            This is v4l2 based g-scaler driver for EXYNOS5
> +
>   endif # V4L_MEM2MEM_DRIVERS
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index d209de0..4389f3e 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -192,6 +192,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
>   obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG)	+= s5p-jpeg/
>   obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
>   obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
> +obj-$(CONFIG_VIDEO_EXYNOS)              += exynos/
>
>   obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D)	+= s5p-g2d/
>
> diff --git a/drivers/media/video/exynos/Kconfig b/drivers/media/video/exynos/Kconfig
> new file mode 100644
> index 0000000..f5836a2
> --- /dev/null
> +++ b/drivers/media/video/exynos/Kconfig
> @@ -0,0 +1,11 @@
> +config VIDEO_EXYNOS
> +	bool "Exynos Multimedia Devices"
> +	depends on ARCH_EXYNOS5
> +	select VIDEO_FIXED_MINOR_RANGES
> +	select VIDEOBUF2_DMA_CONTIG
> +	help
> +	  This is a representative exynos multimedia device.
> +
> +if VIDEO_EXYNOS
> +	source "drivers/media/video/exynos/gsc/Kconfig"
> +endif
> diff --git a/drivers/media/video/exynos/Makefile b/drivers/media/video/exynos/Makefile
> new file mode 100644
> index 0000000..1666724
> --- /dev/null
> +++ b/drivers/media/video/exynos/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_VIDEO_EXYNOS_GSCALER)      += gsc/
> diff --git a/drivers/media/video/exynos/gsc/Kconfig b/drivers/media/video/exynos/gsc/Kconfig
> new file mode 100644
> index 0000000..9036078
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/Kconfig
> @@ -0,0 +1,7 @@
> +config VIDEO_EXYNOS_GSCALER
> +	bool "Exynos G-Scaler driver"
> +	depends on VIDEO_EXYNOS
> +	select MEDIA_EXYNOS
> +	select V4L2_MEM2MEM_DEV
> +	help
> +	  This is a v4l2 driver for exynos G-Scaler device.
> diff --git a/drivers/media/video/exynos/gsc/Makefile b/drivers/media/video/exynos/gsc/Makefile
> new file mode 100644
> index 0000000..d3f66e4
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/Makefile
> @@ -0,0 +1,3 @@
> +gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o
> +
> +obj-$(CONFIG_VIDEO_SAMSUNG_GSCALER)	+= gsc.o
> diff --git a/drivers/media/video/exynos/gsc/gsc-core.c b/drivers/media/video/exynos/gsc/gsc-core.c
> new file mode 100644
> index 0000000..06bd24f
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-core.c
> @@ -0,0 +1,1304 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-core.c
> + *
> + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/types.h>
> +#include<linux/errno.h>
> +#include<linux/bug.h>
> +#include<linux/interrupt.h>
> +#include<linux/workqueue.h>
> +#include<linux/device.h>
> +#include<linux/platform_device.h>
> +#include<linux/list.h>
> +#include<linux/io.h>
> +#include<linux/slab.h>
> +#include<linux/clk.h>
> +#include<media/v4l2-ioctl.h>
> +#include<linux/of.h>
> +#include "gsc-core.h"

How about sorting these alphabetically ? This also applies to
other files.

> +#define GSC_CLOCK_GATE_NAME	"gscl"
> +
> +int gsc_dbg;
> +module_param(gsc_dbg, int, 0644);
> +
> +
> +static struct gsc_fmt gsc_formats[] = {
> +	{
> +		.name		= "RGB565",
> +		.pixelformat	= V4L2_PIX_FMT_RGB565X,
> +		.depth		= { 16 },
> +		.color		= GSC_RGB,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +	}, {
> +		.name		= "XRGB-8-8-8-8, 32 bpp",
> +		.pixelformat	= V4L2_PIX_FMT_RGB32,
> +		.depth		= { 32 },
> +		.color		= GSC_RGB,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +	}, {
> +		.name		= "YUV 4:2:2 packed, YCbYCr",
> +		.pixelformat	= V4L2_PIX_FMT_YUYV,
> +		.depth		= { 16 },
> +		.color		= GSC_YUV422,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
> +	}, {
> +		.name		= "YUV 4:2:2 packed, CbYCrY",
> +		.pixelformat	= V4L2_PIX_FMT_UYVY,
> +		.depth		= { 16 },
> +		.color		= GSC_YUV422,
> +		.yorder		= GSC_LSB_C,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +		.mbus_code	= V4L2_MBUS_FMT_UYVY8_2X8,
> +	}, {
> +		.name		= "YUV 4:2:2 packed, CrYCbY",
> +		.pixelformat	= V4L2_PIX_FMT_VYUY,
> +		.depth		= { 16 },
> +		.color		= GSC_YUV422,
> +		.yorder		= GSC_LSB_C,
> +		.corder		= GSC_CRCB,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +		.mbus_code	= V4L2_MBUS_FMT_VYUY8_2X8,
> +	}, {
> +		.name		= "YUV 4:2:2 packed, YCrYCb",
> +		.pixelformat	= V4L2_PIX_FMT_YVYU,
> +		.depth		= { 16 },
> +		.color		= GSC_YUV422,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CRCB,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +		.mbus_code	= V4L2_MBUS_FMT_YVYU8_2X8,
> +	}, {
> +		.name		= "YUV 4:4:4 planar, YCbYCr",
> +		.pixelformat	= V4L2_PIX_FMT_YUV32,
> +		.depth		= { 32 },
> +		.color		= GSC_YUV444,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +	}, {
> +		.name		= "YUV 4:2:2 planar, Y/Cb/Cr",
> +		.pixelformat	= V4L2_PIX_FMT_YUV422P,
> +		.depth		= { 16 },
> +		.color		= GSC_YUV422,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 1,
> +		.num_comp	= 3,
> +	}, {
> +		.name		= "YUV 4:2:2 planar, Y/CbCr",
> +		.pixelformat	= V4L2_PIX_FMT_NV16,
> +		.depth		= { 16 },
> +		.color		= GSC_YUV422,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 1,
> +		.num_comp	= 2,
> +	}, {
> +		.name		= "YUV 4:2:2 planar, Y/CrCb",
> +		.pixelformat	= V4L2_PIX_FMT_NV61,
> +		.depth		= { 16 },
> +		.color		= GSC_YUV422,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CRCB,
> +		.num_planes	= 1,
> +		.num_comp	= 2,
> +	}, {
> +		.name		= "YUV 4:2:0 planar, YCbCr",
> +		.pixelformat	= V4L2_PIX_FMT_YUV420,
> +		.depth		= { 12 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 1,
> +		.num_comp	= 3,
> +	}, {
> +		.name		= "YUV 4:2:0 planar, YCbCr",
> +		.pixelformat	= V4L2_PIX_FMT_YVU420,
> +		.depth		= { 12 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CRCB,
> +		.num_planes	= 1,
> +		.num_comp	= 3,
> +
> +	}, {
> +		.name		= "YUV 4:2:0 planar, Y/CbCr",
> +		.pixelformat	= V4L2_PIX_FMT_NV12,
> +		.depth		= { 12 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 1,
> +		.num_comp	= 2,
> +	}, {
> +		.name		= "YUV 4:2:0 planar, Y/CrCb",
> +		.pixelformat	= V4L2_PIX_FMT_NV21,
> +		.depth		= { 12 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CRCB,
> +		.num_planes	= 1,
> +		.num_comp	= 2,
> +	}, {
> +		.name		= "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
> +		.pixelformat	= V4L2_PIX_FMT_NV12M,
> +		.depth		= { 8, 4 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 2,
> +		.num_comp	= 2,
> +	}, {
> +		.name		= "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
> +		.pixelformat	= V4L2_PIX_FMT_YUV420M,
> +		.depth		= { 8, 2, 2 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 3,
> +		.num_comp	= 3,
> +	}, {
> +		.name		= "YUV 4:2:0 non-contiguous 3-planar, Y/Cr/Cb",
> +		.pixelformat	= V4L2_PIX_FMT_YVU420M,
> +		.depth		= { 8, 2, 2 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CRCB,
> +		.num_planes	= 3,
> +		.num_comp	= 3,
> +	}, {
> +		.name		=
> +			"YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",

The maximum length of these character strings should not exceed 32 
(including null terminator, IIRC). So please make these descriptions
shorter. You may want to have a look at:
http://git.infradead.org/users/kmpark/linux-samsung/commitdiff/0350f3f1a407a2d0e6fd6935f8433408ef9d5681

> +		.pixelformat	= V4L2_PIX_FMT_NV12MT_16X16,
> +		.depth		= { 8, 4 },
> +		.color		= GSC_YUV420,
> +		.yorder		= GSC_LSB_Y,
> +		.corder		= GSC_CBCR,
> +		.num_planes	= 2,
> +		.num_comp	= 2,
> +	},
> +};
> +
> +struct gsc_fmt *get_format(int index)
> +{
> +	return&gsc_formats[index];
> +}
> +
> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
> +{
> +	struct gsc_fmt *fmt, *def_fmt = NULL;
> +	unsigned int i;
> +
> +	if (index>= ARRAY_SIZE(gsc_formats))
> +		return NULL;
> +
> +	for (i = 0; i<  ARRAY_SIZE(gsc_formats); ++i) {
> +		fmt = get_format(i);
> +		if (pixelformat&&  fmt->pixelformat == *pixelformat)
> +			return fmt;
> +		if (mbus_code&&  fmt->mbus_code == *mbus_code)
> +			return fmt;
> +		if (index == i)
> +			def_fmt = fmt;
> +	}
> +	return def_fmt;
> +
> +}
> +
> +void gsc_set_frame_size(struct gsc_frame *frame, int width, int height)
> +{
> +	frame->f_width	= width;
> +	frame->f_height	= height;
> +	frame->crop.width = width;
> +	frame->crop.height = height;
> +	frame->crop.left = 0;
> +	frame->crop.top = 0;
> +}
> +
> +int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
> +								u32 *ratio)
> +{
> +	if ((dst>  src) || (dst>= src / var->poly_sc_down_max)) {
> +		*ratio = 1;
> +		return 0;
> +	}
> +
> +	if ((src / var->poly_sc_down_max / var->pre_sc_down_max)>  dst) {
> +		gsc_err("scale ratio exceeded maximun scale down ratio(1/16)");

typo: maximun -> maximum, I would suggest to make it:

"Exceeded maximum downscaling ratio (1/16)"

> +		return -EINVAL;
> +	}
> +
> +	*ratio = (dst>  (src / 8)) ? 2 : 4;
> +
> +	return 0;
> +}
> +
> +void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh)
> +{
> +	if (hratio == 4&&  vratio == 4)
> +		*sh = 4;
> +	else if ((hratio == 4&&  vratio == 2) ||
> +		 (hratio == 2&&  vratio == 4))
> +		*sh = 3;
> +	else if ((hratio == 4&&  vratio == 1) ||
> +		 (hratio == 1&&  vratio == 4) ||
> +		 (hratio == 2&&  vratio == 2))
> +		*sh = 2;
> +	else if (hratio == 1&&  vratio == 1)
> +		*sh = 0;
> +	else
> +		*sh = 1;
> +}
> +
> +void gsc_check_src_scale_info(struct gsc_variant *var,
> +				struct gsc_frame *s_frame, u32 *wratio,
> +				 u32 tx, u32 ty, u32 *hratio)
> +{
> +	int remainder = 0, walign, halign;
> +
> +	if (is_yuv420(s_frame->fmt->color)) {
> +		walign = GSC_SC_ALIGN_4;
> +		halign = GSC_SC_ALIGN_4;
> +	} else if (is_yuv422(s_frame->fmt->color)) {
> +		walign = GSC_SC_ALIGN_4;
> +		halign = GSC_SC_ALIGN_2;
> +	} else {
> +		walign = GSC_SC_ALIGN_2;
> +		halign = GSC_SC_ALIGN_2;
> +	}
> +
> +	remainder = s_frame->crop.width % (*wratio * walign);
> +	if (remainder) {
> +		s_frame->crop.width -= remainder;
> +		gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio);
> +		gsc_info("cropped src width size is recalculated from %d to %d",
> +			s_frame->crop.width + remainder, s_frame->crop.width);
> +	}
> +
> +	remainder = s_frame->crop.height % (*hratio * halign);
> +	if (remainder) {
> +		s_frame->crop.height -= remainder;
> +		gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio);
> +		gsc_info("cropped src height size is recalculated from %d to %d",
> +			s_frame->crop.height + remainder, s_frame->crop.height);
> +	}
> +}
> +
> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
> +{
> +	struct gsc_fmt *fmt;
> +
> +	fmt = find_fmt(NULL, NULL, f->index);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
> +	f->pixelformat = fmt->pixelformat;
> +
> +	return 0;
> +}
> +
> +u32 get_plane_size(struct gsc_frame *frame, unsigned int plane)
> +{
> +	if (!frame || plane>= frame->fmt->num_planes) {
> +		gsc_err("Invalid argument");
> +		return 0;
> +	}
> +
> +	return frame->payload[plane];
> +}

Hint: this is only used once. Would perhaps be much better to open
code this within the caller function.

> +
> +u32 get_plane_info(struct gsc_frame frm, u32 addr, u32 *index)
> +{
> +	if (frm.addr.y == addr) {
> +		*index = 0;
> +		return frm.addr.y;
> +	} else if (frm.addr.cb == addr) {
> +		*index = 1;
> +		return frm.addr.cb;
> +	} else if (frm.addr.cr == addr) {
> +		*index = 2;
> +		return frm.addr.cr;
> +	} else {
> +		gsc_err("Plane address is wrong");
> +		return -EINVAL;
> +	}
> +}
> +
> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm)
> +{
> +	u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
> +	f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
> +
> +	f_chk_addr = frm.addr.y;
> +	f_chk_len = frm.payload[0];
> +	if (frm.fmt->num_planes == 2) {
> +		s_chk_addr = frm.addr.cb;
> +		s_chk_len = frm.payload[1];
> +	} else if (frm.fmt->num_planes == 3) {
> +		u32 low_addr, low_plane, mid_addr, mid_plane;
> +		u32 high_addr, high_plane;
> +		u32 t_min, t_max;
> +
> +		t_min = min3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> +		low_addr = get_plane_info(frm, t_min,&low_plane);
> +		t_max = max3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> +		high_addr = get_plane_info(frm, t_max,&high_plane);
> +
> +		mid_plane = 3 - (low_plane + high_plane);
> +		if (mid_plane == 0)
> +			mid_addr = frm.addr.y;
> +		else if (mid_plane == 1)
> +			mid_addr = frm.addr.cb;
> +		else if (mid_plane == 2)
> +			mid_addr = frm.addr.cr;
> +		else
> +			return;
> +
> +		f_chk_addr = low_addr;
> +		if (mid_addr + frm.payload[mid_plane] - low_addr>
> +		    high_addr + frm.payload[high_plane] - mid_addr) {
> +			f_chk_len = frm.payload[low_plane];
> +			s_chk_addr = mid_addr;
> +			s_chk_len = high_addr +
> +					frm.payload[high_plane] - mid_addr;
> +		} else {
> +			f_chk_len = mid_addr +
> +					frm.payload[mid_plane] - low_addr;
> +			s_chk_addr = high_addr;
> +			s_chk_len = frm.payload[high_plane];
> +		}
> +	}
> +	gsc_dbg("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
> +		f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
> +}
> +
> +int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
> +{
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +	struct gsc_variant *variant = gsc->variant;
> +	struct v4l2_pix_format_mplane *pix_mp =&f->fmt.pix_mp;
> +	struct gsc_fmt *fmt;
> +	u32 max_w, max_h, mod_x, mod_y;
> +	u32 min_w, min_h, tmp_w, tmp_h;
> +	int i;
> +
> +	gsc_dbg("user put w: %d, h: %d", pix_mp->width, pix_mp->height);

Would be nice to get rid of all gsc_dbg/err/... macros and use dev_dbg/err/...,
v4l2_dbg/err or just plain pr_debug + "#define pr_fmt(fmt) ...".

It's up to yo though. I think it's preferred to use common debugging
utilities across the kernel, rather than having each driver inventing
their own.

> +
> +	fmt = find_fmt(&pix_mp->pixelformat, NULL, 0);
> +	if (!fmt) {
> +		gsc_err("pixelformat format (0x%X) invalid\n",
> +						pix_mp->pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	if (pix_mp->field == V4L2_FIELD_ANY)
> +		pix_mp->field = V4L2_FIELD_NONE;
> +	else if (pix_mp->field != V4L2_FIELD_NONE) {
> +		gsc_err("Not supported field order(%d)\n", pix_mp->field);
> +		return -EINVAL;
> +	}
> +
> +	max_w = variant->pix_max->target_rot_dis_w;
> +	max_h = variant->pix_max->target_rot_dis_h;
> +	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
> +		mod_x = ffs(variant->pix_align->org_w) - 1;
> +		if (is_yuv420(fmt->color))
> +			mod_y = ffs(variant->pix_align->org_h) - 1;
> +		else
> +			mod_y = ffs(variant->pix_align->org_h) - 2;
> +		min_w = variant->pix_min->org_w;
> +		min_h = variant->pix_min->org_h;
> +	} else {
> +		mod_x = ffs(variant->pix_align->org_w) - 1;
> +		if (is_yuv420(fmt->color))
> +			mod_y = ffs(variant->pix_align->org_h) - 1;
> +		else
> +			mod_y = ffs(variant->pix_align->org_h) - 2;
> +		min_w = variant->pix_min->target_rot_dis_w;
> +		min_h = variant->pix_min->target_rot_dis_h;
> +	}
> +	gsc_dbg("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
> +	     mod_x, mod_y, max_w, max_h);
> +	/* To check if image size is modified to adjust parameter against
> +	   hardware abilities */
> +	tmp_w = pix_mp->width;
> +	tmp_h = pix_mp->height;
> +
> +	v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
> +		&pix_mp->height, min_h, max_h, mod_y, 0);
> +	if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
> +		gsc_info("Image size has been modified from %dx%d to %dx%d",
> +			 tmp_w, tmp_h, pix_mp->width, pix_mp->height);
> +
> +	pix_mp->num_planes = fmt->num_planes;
> +
> +	if (ctx->gsc_ctrls.csc_eq_mode->val)
> +		ctx->gsc_ctrls.csc_eq->val =
> +			(pix_mp->width>= 1280) ? 1 : 0;
> +	if (ctx->gsc_ctrls.csc_eq->val) /* HD */
> +		pix_mp->colorspace = V4L2_COLORSPACE_REC709;
> +	else	/* SD */
> +		pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> +
> +	for (i = 0; i<  pix_mp->num_planes; ++i) {
> +		int bpl = (pix_mp->width * fmt->depth[i])>>  3;
> +		pix_mp->plane_fmt[i].bytesperline = bpl;
> +		pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
> +
> +		gsc_dbg("[%d]: bpl: %d, sizeimage: %d",
> +		    i, bpl, pix_mp->plane_fmt[i].sizeimage);
> +	}
> +
> +	return 0;
> +}
> +
> +int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
> +{
> +	struct gsc_frame *frame;
> +	struct v4l2_pix_format_mplane *pix_mp;
> +	int i;
> +
> +	frame = ctx_get_frame(ctx, f->type);
> +	if (IS_ERR(frame))
> +		return PTR_ERR(frame);
> +
> +	pix_mp =&f->fmt.pix_mp;
> +
> +	pix_mp->width		= frame->f_width;
> +	pix_mp->height		= frame->f_height;
> +	pix_mp->field		= V4L2_FIELD_NONE;
> +	pix_mp->pixelformat	= frame->fmt->pixelformat;
> +	pix_mp->colorspace	= V4L2_COLORSPACE_JPEG;
> +	pix_mp->num_planes	= frame->fmt->num_planes;
> +
> +	for (i = 0; i<  pix_mp->num_planes; ++i) {
> +		pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
> +			frame->fmt->depth[i]) / 8;
> +		pix_mp->plane_fmt[i].sizeimage =
> +			 pix_mp->plane_fmt[i].bytesperline * frame->f_height;
> +	}
> +
> +	return 0;
> +}
> +
> +void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
> +{
> +	if (tmp_w != *w || tmp_h != *h) {
> +		gsc_info("Cropped size has been modified from %dx%d to %dx%d",
> +							*w, *h, tmp_w, tmp_h);
> +		*w = tmp_w;
> +		*h = tmp_h;
> +	}
> +}
> +
> +int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
> +{
> +	struct gsc_frame *frame;
> +
> +	frame = ctx_get_frame(ctx, cr->type);
> +	if (IS_ERR(frame))
> +		return PTR_ERR(frame);
> +
> +	cr->c = frame->crop;
> +
> +	return 0;
> +}
> +
> +int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
> +{
> +	struct gsc_frame *f;
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +	struct gsc_variant *variant = gsc->variant;
> +	u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h;
> +	u32 min_w, min_h, max_w, max_h;
> +
> +	if (cr->c.top<  0 || cr->c.left<  0) {
> +		gsc_err("doesn't support negative values for top&  left\n");
> +		return -EINVAL;
> +	}
> +	gsc_dbg("user put w: %d, h: %d", cr->c.width, cr->c.height);
> +
> +	if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +		f =&ctx->d_frame;
> +	else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		f =&ctx->s_frame;
> +	else
> +		return -EINVAL;
> +
> +	max_w = f->f_width;
> +	max_h = f->f_height;
> +	tmp_w = cr->c.width;
> +	tmp_h = cr->c.height;
> +
> +	if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
> +		if ((is_yuv422(f->fmt->color)&&  f->fmt->num_comp == 1) ||
> +		    is_rgb(f->fmt->color))
> +			min_w = 32;
> +		else
> +			min_w = 64;
> +		if ((is_yuv422(f->fmt->color)&&  f->fmt->num_comp == 3) ||
> +		    is_yuv420(f->fmt->color))
> +			min_h = 32;
> +		else
> +			min_h = 16;
> +	} else {
> +		if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
> +			mod_x = ffs(variant->pix_align->target_w) - 1;
> +		if (is_yuv420(f->fmt->color))
> +			mod_y = ffs(variant->pix_align->target_h) - 1;
> +		if (ctx->gsc_ctrls.rotate->val == 90 ||
> +		    ctx->gsc_ctrls.rotate->val == 270) {
> +			max_w = f->f_height;
> +			max_h = f->f_width;
> +			min_w = variant->pix_min->target_rot_en_w;
> +			min_h = variant->pix_min->target_rot_en_h;
> +			tmp_w = cr->c.height;
> +			tmp_h = cr->c.width;
> +		} else {
> +			min_w = variant->pix_min->target_rot_dis_w;
> +			min_h = variant->pix_min->target_rot_dis_h;
> +		}
> +	}
> +	gsc_dbg("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d",
> +					mod_x, mod_y, min_w, min_h);
> +	gsc_dbg("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h);
> +
> +	v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
> +			&tmp_h, min_h, max_h, mod_y, 0);
> +
> +	if (!V4L2_TYPE_IS_OUTPUT(cr->type)&&
> +	    (ctx->gsc_ctrls.rotate->val == 90 ||
> +	    ctx->gsc_ctrls.rotate->val == 270)) {
> +		gsc_check_crop_change(tmp_h, tmp_w,&cr->c.width,
> +							&cr->c.height);
> +	} else {
> +		gsc_check_crop_change(tmp_w, tmp_h,&cr->c.width,
> +							&cr->c.height);
> +	}
> +
> +	/* adjust left/top if cropping rectangle is out of bounds */
> +	/* Need to add code to algin left value with 2's multiple */
> +	if (cr->c.left + tmp_w>  max_w)
> +		cr->c.left = max_w - tmp_w;
> +	if (cr->c.top + tmp_h>  max_h)
> +		cr->c.top = max_h - tmp_h;
> +
> +	if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
> +		if (cr->c.left % 2)
> +			cr->c.left -= 1;
> +
> +	gsc_dbg("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
> +	    cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
> +
> +	return 0;
> +}
> +
> +int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
> +			   int dh, int rot, int out_path)
> +{
> +	int tmp_w, tmp_h, sc_down_max;
> +	sc_down_max =
> +		(out_path == GSC_DMA) ? var->sc_down_max : var->local_sc_down;
> +
> +	if (rot == 90 || rot == 270) {
> +		tmp_w = dh;
> +		tmp_h = dw;
> +	} else {
> +		tmp_w = dw;
> +		tmp_h = dh;
> +	}
> +
> +	if ((sw / tmp_w)>  sc_down_max ||
> +	    (sh / tmp_h)>  sc_down_max ||
> +	    (tmp_w / sw)>  var->sc_up_max ||
> +	    (tmp_h / sh)>  var->sc_up_max)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +int gsc_set_scaler_info(struct gsc_ctx *ctx)
> +{
> +	struct gsc_scaler *sc =&ctx->scaler;
> +	struct gsc_frame *s_frame =&ctx->s_frame;
> +	struct gsc_frame *d_frame =&ctx->d_frame;
> +	struct gsc_variant *variant = ctx->gsc_dev->variant;
> +	int tx, ty;
> +	int ret;
> +
> +	ret = gsc_check_scaler_ratio(variant, s_frame->crop.width,
> +		s_frame->crop.height, d_frame->crop.width, d_frame->crop.height,
> +		ctx->gsc_ctrls.rotate->val, ctx->out_path);
> +	if (ret) {
> +		gsc_err("out of scaler range");
> +		return ret;
> +	}
> +
> +	if (ctx->gsc_ctrls.rotate->val == 90 ||
> +	    ctx->gsc_ctrls.rotate->val == 270) {
> +		ty = d_frame->crop.width;
> +		tx = d_frame->crop.height;
> +	} else {
> +		tx = d_frame->crop.width;
> +		ty = d_frame->crop.height;
> +	}
> +
> +	ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width,
> +				      tx,&sc->pre_hratio);
> +	if (ret) {
> +		gsc_err("Horizontal scale ratio is out of range");
> +		return ret;
> +	}
> +
> +	ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height,
> +				      ty,&sc->pre_vratio);
> +	if (ret) {
> +		gsc_err("Vertical scale ratio is out of range");
> +		return ret;
> +	}
> +
> +	gsc_check_src_scale_info(variant, s_frame,&sc->pre_hratio,
> +				 tx, ty,&sc->pre_vratio);
> +
> +	gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
> +				&sc->pre_shfactor);
> +
> +	sc->main_hratio = (s_frame->crop.width<<  16) / tx;
> +	sc->main_vratio = (s_frame->crop.height<<  16) / ty;
> +
> +	gsc_dbg("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d",
> +			s_frame->crop.width, s_frame->crop.height, tx, ty);
> +	gsc_dbg("scaler ratio info : pre_shfactor : %d, pre_h : %d",
> +			sc->pre_shfactor, sc->pre_hratio);
> +	gsc_dbg("pre_v :%d, main_h : %ld, main_v : %ld",
> +			sc->pre_vratio, sc->main_hratio, sc->main_vratio);
> +
> +	return 0;
> +}
> +
> +
> +/*
> + * V4L2 controls handling
> + */
> +static int gsc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct gsc_ctx *ctx = ctrl_to_ctx(ctrl);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_HFLIP:
> +		user_to_drv(ctx->gsc_ctrls.hflip, ctrl->val);
> +		break;
> +
> +	case V4L2_CID_VFLIP:
> +		user_to_drv(ctx->gsc_ctrls.vflip, ctrl->val);
> +		break;
> +
> +	case V4L2_CID_ROTATE:
> +		user_to_drv(ctx->gsc_ctrls.rotate, ctrl->val);
> +		break;
> +
> +	default:
> +		break;

It's safe to remove these two lines. Also see my comments on
user_to_drv() below.

> +	}
> +
> +	if (gsc_m2m_opened(ctx->gsc_dev))
> +		gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
> +
> +	return 0;
> +}
> +
> +const struct v4l2_ctrl_ops gsc_ctrl_ops = {
> +	.s_ctrl = gsc_s_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config gsc_custom_ctrl[] = {
> +	{
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_GLOBAL_ALPHA,

There is now a standard V4L2 control for this: V4L2_CID_ALPHA_COMPONENT.
http://linuxtv.org/downloads/v4l-dvb-apis/control.html
So please use it instead of a private control. We're generally trying 
to avoid private controls. If some driver uses them, they should be well
documented, e.g. under Documentation/video4linux/.

> +		.name = "Set RGB alpha",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +		.max = 255,
> +		.step = 1,
> +		.def = 0,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_CACHEABLE,
> +		.name = "Set cacheable",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,

Err, you're lying here. Boolean control with a slider flag ?
Are you perhaps using V4L2_CTRL_FLAG_SLIDER in some non-standard way ?
(it's set throughout all controls in this array).

> +		.max = 1,
> +		.def = true,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_TV_LAYER_BLEND_ENABLE,
> +		.name = "Enable layer alpha blending",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_TV_LAYER_BLEND_ALPHA,
> +		.name = "Set alpha for layer blending",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +		.min = 0,
> +		.max = 255,
> +		.step = 1,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_TV_PIXEL_BLEND_ENABLE,
> +		.name = "Enable pixel alpha blending",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_TV_CHROMA_ENABLE,
> +		.name = "Enable chromakey",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_TV_CHROMA_VALUE,
> +		.name = "Set chromakey value",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +		.min = 0,
> +		.max = 255,
> +		.step = 1,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_CSC_EQ_MODE,
> +		.name = "Set CSC equation mode",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +		.max = DEFAULT_CSC_EQ,
> +		.def = DEFAULT_CSC_EQ,
> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_CSC_EQ,
> +		.name = "Set CSC equation",
> +		.type = V4L2_CTRL_TYPE_INTEGER,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +		.step = 1,
> +		.max = 8,
> +		.def = V4L2_COLORSPACE_REC709,

Are you switching color spaces with a control ? The colorspace 
handling is supposed to be done with VIDIOC_S/G_FMT.
Please, can you explain why these controls are need and what
exact device features they expose ?

Can't what is covered by V4L2_CID_CSC_EQ_MODE and V4L2_CID_CSC_RANGE
be derived from the colorspace ?

> +	}, {
> +		.ops =&gsc_ctrl_ops,
> +		.id = V4L2_CID_CSC_RANGE,
> +		.name = "Set CSC range",
> +		.type = V4L2_CTRL_TYPE_BOOLEAN,
> +		.flags = V4L2_CTRL_FLAG_SLIDER,
> +		.max = DEFAULT_CSC_RANGE,
> +		.def = DEFAULT_CSC_RANGE,
> +	},
> +};
> +
> +int gsc_ctrls_create(struct gsc_ctx *ctx)
> +{
> +	if (ctx->ctrls_rdy) {
> +		gsc_err("Control handler of this context was created already");
> +		return 0;
> +	}
> +
> +	v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM);
> +
> +	ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +				&gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
> +	ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +				&gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> +	ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +				&gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> +
> +
> +	ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[0], NULL);
> +	ctx->gsc_ctrls.layer_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[2], NULL);
> +	ctx->gsc_ctrls.layer_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[3], NULL);
> +	ctx->gsc_ctrls.pixel_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[4], NULL);
> +	ctx->gsc_ctrls.chroma_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[5], NULL);
> +	ctx->gsc_ctrls.chroma_val = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[6], NULL);
> +
> +/* for CSC equation */

missing tab ?

> +	ctx->gsc_ctrls.csc_eq_mode = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[7], NULL);
> +	ctx->gsc_ctrls.csc_eq = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[8], NULL);
> +	ctx->gsc_ctrls.csc_range = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
> +					&gsc_custom_ctrl[9], NULL);
> +
> +	ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
> +
> +
> +	if (ctx->ctrl_handler.error) {
> +		int err = ctx->ctrl_handler.error;
> +		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +		gsc_err("Failed to gscaler control hander create");
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +void gsc_ctrls_delete(struct gsc_ctx *ctx)
> +{
> +	if (ctx->ctrls_rdy) {
> +		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +		ctx->ctrls_rdy = false;
> +	}
> +}
> +
> +/* The color format (num_comp, num_planes) must be already configured. */
> +int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
> +			struct gsc_frame *frame, struct gsc_addr *addr)
> +{
> +	int ret = 0;
> +	u32 pix_size;
> +
> +	if (IS_ERR(vb) || IS_ERR(frame)) {
> +		gsc_err("Invalid argument");
> +		return -EINVAL;
> +	}
> +
> +	pix_size = frame->f_width * frame->f_height;
> +
> +	gsc_dbg("num_planes= %d, num_comp= %d, pix_size= %d",
> +		frame->fmt->num_planes, frame->fmt->num_comp, pix_size);
> +
> +	addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
> +
> +	if (frame->fmt->num_planes == 1) {
> +		switch (frame->fmt->num_comp) {
> +		case 1:
> +			addr->cb = 0;
> +			addr->cr = 0;
> +			break;
> +		case 2:
> +			/* decompose Y into Y/Cb */
> +			addr->cb = (dma_addr_t)(addr->y + pix_size);
> +			addr->cr = 0;
> +			break;
> +		case 3:
> +			addr->cb = (dma_addr_t)(addr->y + pix_size);
> +			addr->cr = (dma_addr_t)(addr->cb + (pix_size>>  2));
> +			break;
> +		default:
> +			gsc_err("Invalid the number of color planes");
> +			return -EINVAL;
> +		}
> +	} else {
> +		if (frame->fmt->num_planes>= 2)
> +			addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
> +
> +		if (frame->fmt->num_planes == 3)
> +			addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
> +	}
> +
> +	if (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) {
> +		u32 t_cb = addr->cb;
> +		addr->cb = addr->cr;
> +		addr->cr = t_cb;

swap(addr->cb, addr->cr); would do as well.

> +	}
> +
> +	gsc_dbg("ADDR: y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
> +		addr->y, addr->cb, addr->cr, ret);
> +
> +	return ret;
> +}
> +
> +static irqreturn_t gsc_irq_handler(int irq, void *priv)
> +{
> +	struct gsc_dev *gsc = priv;
> +	int gsc_irq;
> +
> +	gsc_irq = gsc_hw_get_irq_status(gsc);
> +	gsc_hw_clear_irq(gsc, gsc_irq);
> +
> +	if (gsc_irq == GSC_OR_IRQ) {
> +		gsc_err("Local path input over-run interrupt has occurred!\n");
> +		return IRQ_HANDLED;
> +	}
> +
> +	spin_lock(&gsc->slock);
> +
> +	if (test_and_clear_bit(ST_M2M_RUN,&gsc->state)) {
> +		struct vb2_buffer *src_vb, *dst_vb;
> +		struct gsc_ctx *ctx =
> +			v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> +
> +		if (!ctx || !ctx->m2m_ctx)
> +			goto isr_unlock;
> +
> +		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_and_clear_bit(ST_STOP_REQ,&gsc->state))
> +				wake_up(&gsc->irq_queue);
> +			else
> +				v4l2_m2m_job_finish(gsc->m2m.m2m_dev,
> +							ctx->m2m_ctx);
> +
> +			/* wake_up job_abort, stop_streaming */
> +			spin_lock(&ctx->slock);
> +			if (ctx->state&  GSC_CTX_STOP_REQ) {
> +				ctx->state&= ~GSC_CTX_STOP_REQ;
> +				wake_up(&gsc->irq_queue);
> +			}
> +			spin_unlock(&ctx->slock);
> +		}
> +	}
> +
> +isr_unlock:
> +	spin_unlock(&gsc->slock);
> +	return IRQ_HANDLED;
> +}
> +
> +static int gsc_runtime_suspend(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);

No need for casting, these two lines can be replaced with:

	struct gsc_dev *gsc = dev_get_drvdata(dev);

> +
> +	if (gsc_m2m_opened(gsc))
> +		gsc->m2m.ctx = NULL;
> +
> +	clk_disable(gsc->clock);
> +	clear_bit(ST_PWR_ON,&gsc->state);
> +
> +	return 0;
> +}
> +
> +static int gsc_runtime_resume(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);

ditto

> +	int ret;
> +
> +	ret = clk_enable(gsc->clock);
> +	if (ret)
> +		return ret;
> +
> +	set_bit(ST_PWR_ON,&gsc->state);
> +	return 0;
> +}
> +
> +static inline void *gsc_get_drv_data(struct platform_device *pdev);

Huh ? No need for "inline" here.

> +
> +static int gsc_probe(struct platform_device *pdev)
> +{
> +	struct gsc_dev *gsc;
> +	struct resource *res;
> +	struct gsc_driverdata *drv_data;
> +

Nit: No need for an empty line.

> +	int ret = 0;
> +
> +	dev_dbg(&pdev->dev, "%s():\n", __func__);

I would remove this line, but that just may be me. Also, how about 
adding:

	struct device *dev = &pdev->dev;

and using it below to simplify the code a little ?

> +	drv_data = (struct gsc_driverdata *)
> +				gsc_get_drv_data(pdev);
> +
> +	if (pdev->dev.of_node) {
> +		pdev->id = of_alias_get_id(pdev->dev.of_node, "gsc");

It is illegal to change struct platform_device::id in drivers after 
a device has been registered to the driver core, isn't it ?

I suggest to just use gsc->id in whole driver, something like:

	if (pdev->dev.of_node)
		gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc");
	else
		gsc->id = pdev->id;
	
	/* sanity check on gsc->id here, e.g. */

	if (gsc->id < 0 || gsc->id >= drv_data->num_entities)
		...

> +		if (pdev->id<  0)
> +			dev_err(&pdev->dev,
> +				"failed to get alias id, errno %d\n", ret);

OK, so it doesn't really fail here if aliases are not specified in DT.
I would just make probe() fail here if the id is wrong.

> +	}
> +
> +	if (pdev->id>= drv_data->num_entities) {
> +		dev_err(&pdev->dev, "Invalid platform device id: %d\n",
> +			pdev->id);
> +		return -EINVAL;
> +	}
> +
> +	gsc = devm_kzalloc(&pdev->dev, sizeof(struct gsc_dev), GFP_KERNEL);
> +	if (!gsc)
> +		return -ENOMEM;
> +
> +	gsc->id = pdev->id;
> +	gsc->variant = drv_data->variant[gsc->id];
> +	gsc->pdev = pdev;
> +	gsc->pdata = pdev->dev.platform_data;
> +
> +	init_waitqueue_head(&gsc->irq_queue);
> +	spin_lock_init(&gsc->slock);
> +	mutex_init(&gsc->lock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	gsc->regs = devm_request_and_ioremap(&pdev->dev, res);
> +	if (!gsc->regs) {
> +		dev_err(&pdev->dev, "failed to map registers\n");
> +		return -ENOENT;
> +	}
> +
> +	/* Get Gscaler clock */
> +	gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
> +	if (IS_ERR(gsc->clock)) {
> +		gsc_err("failed to get gscaler.%d clock", gsc->id);
> +		return -ENXIO;
> +	}
> +	clk_put(gsc->clock);

You don't do clk_put until the clock is actively used by the driver.
This is just a wrong clock API usage. I would check if devm_clk_get()
could be used, it does clk_put automatically when a driver is detached.

clk_prepare/clk_unprepare are also missing here. Samsung platforms will 
be eventually converted to common clock API and those calls are a 
mandatory pre-requisite. 

Please see: http://patchwork.linuxtv.org/patch/9920/
> +
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "failed to get IRQ resource\n");
> +		return -ENXIO;
> +	}
> +	gsc->irq = res->start;

irq can be dropped from struct gsc_dev, it is not really needed since
you use devm_* API.

> +
> +	ret = devm_request_irq(&pdev->dev, gsc->irq, gsc_irq_handler, 0,
> +							pdev->name, gsc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	gsc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> +	if (IS_ERR(gsc->alloc_ctx)) {
> +		ret = PTR_ERR(gsc->alloc_ctx);
> +		goto err_irq;
> +	}
> +
> +	platform_set_drvdata(pdev, gsc);
> +
> +	ret = gsc_register_m2m_device(gsc);
> +	if (ret)
> +		goto err_irq;
> +
> +	gsc_runtime_resume(&pdev->dev);

Why is this needed ? If you do that, the clock gets enabled in probe
and pm_runtime calls are useless, since the clock is always enabled
(due to an unbalanced clk_enable at the initialization stage). 
Or am I missing something ?

> +	pm_runtime_enable(&pdev->dev);
> +
> +	gsc_info("gsc-%d registered successfully", gsc->id);

You probably want to make it just dev_dbg(...);

> +	return 0;
> +
> +err_irq:
> +	free_irq(gsc->irq, gsc);

This isn't right, the point of using devm_request_irq() is that an 
IRQ is freed when a driver is unloaded and also when probe() exits 
with an error (if dev->bus->probe() in really_probe() fails
devres_relase_all() is invoked). So can you please remove this line ?

> +	return ret;
> +}
> +
> +static int __devexit gsc_remove(struct platform_device *pdev)
> +{
> +	struct gsc_dev *gsc =
> +		(struct gsc_dev *)platform_get_drvdata(pdev);

No need to cast and a line break.

> +
> +	free_irq(gsc->irq, gsc);

See my comment on free_irq() above.

> +
> +	gsc_unregister_m2m_device(gsc);
> +
> +	vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	iounmap(gsc->regs);
> +	release_resource(gsc->regs_res);
> +	kfree(gsc->regs_res);
> +	kfree(gsc);

It is not sane, all these resources are freed/unmapped automatically
by the driver core. So please remove that 4 lines.

It just indicates this driver has never been tested as a loadable module.
Can you please take care of this ? I'm pretty sure there is more bugs,
with eyes and teeth shining white in the darkness...

> +
> +	dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);

How about just making it dev_dbg ?

> +	return 0;
> +}
> +
> +static int gsc_suspend(struct device *dev)
> +{
> +	struct platform_device *pdev;
> +	struct gsc_dev *gsc;
> +	int ret = 0;
> +
> +	pdev = to_platform_device(dev);
> +	gsc = (struct gsc_dev *)platform_get_drvdata(pdev);

Again, 
	struct gsc_dev *gsc = dev_get_drvdata(dev);

and please move it above to the declarations statements.

> +	if (gsc_m2m_run(gsc)) {
> +		set_bit(ST_STOP_REQ,&gsc->state);
> +		ret = wait_event_timeout(gsc->irq_queue,
> +				!test_bit(ST_STOP_REQ,&gsc->state),
> +				GSC_SHUTDOWN_TIMEOUT);
> +		if (ret == 0)
> +			dev_err(&gsc->pdev->dev, "wait timeout : %s\n",
> +				__func__);
> +	}
> +
> +	pm_runtime_put_sync(dev);

Using the PM runtime calls from within system wide suspend/resume
callback is illegal. You don't know at what state the platform 
run time PM is at the time system the sleep callbacks in this driver
are invoked. Instead, I would just call gsc_runtime_resume/suspend
functions directly and would ensure proper locking of driver's 
runtime PM and system sleep callbacks.

> +
> +	return ret;
> +}
> +
> +static int gsc_resume(struct device *dev)
> +{
> +	struct gsc_dev *gsc;

struct gsc_dev *gsc = dev_get_drvdata(dev);

> +	struct gsc_ctx *ctx;
> +
> +	gsc = dev_get_drvdata(dev);
> +
> +	pm_runtime_get_sync(dev);
> +	if (gsc_m2m_opened(gsc)) {
> +		ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> +		if (ctx != NULL) {
> +			gsc->m2m.ctx = NULL;
> +			v4l2_m2m_job_finish(gsc->m2m.m2m_dev, ctx->m2m_ctx);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops gsc_pm_ops = {
> +	.suspend		= gsc_suspend,
> +	.resume			= gsc_resume,
> +	.runtime_suspend	= gsc_runtime_suspend,
> +	.runtime_resume		= gsc_runtime_resume,
> +};
> +
> +struct gsc_pix_max gsc_v_100_max = {
> +	.org_scaler_bypass_w	= 8192,
> +	.org_scaler_bypass_h	= 8192,
> +	.org_scaler_input_w	= 4800,
> +	.org_scaler_input_h	= 3344,
> +	.real_rot_dis_w		= 4800,
> +	.real_rot_dis_h		= 3344,
> +	.real_rot_en_w		= 2047,
> +	.real_rot_en_h		= 2047,
> +	.target_rot_dis_w	= 4800,
> +	.target_rot_dis_h	= 3344,
> +	.target_rot_en_w	= 2016,
> +	.target_rot_en_h	= 2016,
> +};
> +
> +struct gsc_pix_min gsc_v_100_min = {
> +	.org_w			= 64,
> +	.org_h			= 32,
> +	.real_w			= 64,
> +	.real_h			= 32,
> +	.target_rot_dis_w	= 64,
> +	.target_rot_dis_h	= 32,
> +	.target_rot_en_w	= 32,
> +	.target_rot_en_h	= 16,
> +};
> +
> +struct gsc_pix_align gsc_v_100_align = {
> +	.org_h			= 16,
> +	.org_w			= 16, /* yuv420 : 16, others : 8 */
> +	.offset_h		= 2,  /* yuv420/422 : 2, others : 1 */
> +	.real_w			= 16, /* yuv420/422 : 4~16, others : 2~8 */
> +	.real_h			= 16, /* yuv420 : 4~16, others : 1 */
> +	.target_w		= 2,  /* yuv420/422 : 2, others : 1 */
> +	.target_h		= 2,  /* yuv420 : 2, others : 1 */
> +};
> +
> +struct gsc_variant gsc_v_100_variant = {

static const struct ...

> +	.pix_max		=&gsc_v_100_max,
> +	.pix_min		=&gsc_v_100_min,
> +	.pix_align		=&gsc_v_100_align,
> +	.in_buf_cnt		= 8,
> +	.out_buf_cnt		= 16,
> +	.sc_up_max		= 8,
> +	.sc_down_max		= 16,
> +	.poly_sc_down_max	= 4,
> +	.pre_sc_down_max	= 4,
> +	.local_sc_down		= 2,
> +};
> +
> +static struct gsc_driverdata gsc_v_100_drvdata = {
> +	.variant = {
> +		[0] =&gsc_v_100_variant,
> +		[1] =&gsc_v_100_variant,
> +		[2] =&gsc_v_100_variant,
> +		[3] =&gsc_v_100_variant,
> +	},
> +	.num_entities = 4,
> +	.lclk_frequency = 266000000UL,
> +};
> +
> +static struct platform_device_id gsc_driver_ids[] = {
> +	{
> +		.name		= "exynos-gsc",
> +		.driver_data	= (unsigned long)&gsc_v_100_drvdata,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(platform, gsc_driver_ids);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id exynos_gsc_match[] = {
> +	{ .compatible = "samsung,exynos-gsc",
> +	.data =&gsc_v_100_drvdata, },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, exynos_gsc_match);
> +#else
> +#define exynos_gsc_match NULL
> +#endif

You could drop these 2 lines...

> +
> +static inline void *gsc_get_drv_data(struct platform_device *pdev)

[Unnecesary "inline".]

> +{
> +#ifdef CONFIG_OF

..remove this #ifdefs...

> +	if (pdev->dev.of_node) {
> +		const struct of_device_id *match;
> +	match = of_match_node(exynos_gsc_match, pdev->dev.of_node);

and use "of_match_ptr(exynos_gsc_match)" instead of exynos_gsc_match
here

> +	return (struct gsc_driverdata *) match->data;
> +	}
> +#endif
> +	return (struct gsc_driverdata *)
> +			platform_get_device_id(pdev)->driver_data;
> +}

static void *gsc_get_drv_data(struct platform_device *pdev)
{
	struct gsc_driverdata *driver_data = NULL;

	if (pdev->dev.of_node) {
		const struct of_device_id *match;
		match = of_match_node(of_match_ptr(exynos_gsc_match), 
				pdev->dev.of_node);
		if (match)
			driver_data =  match->data;
	} else {
		driver_data = platform_get_device_id(pdev)->driver_data;
	}

	return driver_data;
}

> +
> +static struct platform_driver gsc_driver = {
> +	.probe		= gsc_probe,
> +	.remove	= __devexit_p(gsc_remove),
> +	.id_table	= gsc_driver_ids,
> +	.driver = {
> +		.name	= GSC_MODULE_NAME,
> +		.owner	= THIS_MODULE,
> +		.pm	=&gsc_pm_ops,
> +		.of_match_table = exynos_gsc_match,

... and here.

#ifdefs are *ugly* :)

> +	}
> +};
> +
> +static int __init gsc_init(void)
> +{
> +	int ret = platform_driver_register(&gsc_driver);
> +	if (ret)
> +		gsc_err("platform_driver_register failed: %d\n", ret);
> +	return ret;
> +}
> +
> +static void __exit gsc_exit(void)
> +{
> +	platform_driver_unregister(&gsc_driver);
> +}
> +
> +module_init(gsc_init);
> +module_exit(gsc_exit);

How about using module_platform_driver() macro instead ?

> +
> +MODULE_AUTHOR("Hyunwong Kim<khw0178.kim@xxxxxxxxxxx>");
> +MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/video/exynos/gsc/gsc-core.h b/drivers/media/video/exynos/gsc/gsc-core.h
> new file mode 100644
> index 0000000..3a236ec
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-core.h
> @@ -0,0 +1,652 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-core.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * header file for Samsung EXYNOS5 SoC series G-scaler 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 GSC_CORE_H_
> +#define GSC_CORE_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<media/v4l2-mediabus.h>
> +#define CONFIG_VB2_GSC_DMA_CONTIG	1
> +#include<media/videobuf2-dma-contig.h>
> +#include "gsc-regs.h"
> +extern int gsc_dbg;
> +
> +#define GSC_MODULE_NAME			"exynos-gsc"
> +
> +#define gsc_info(fmt, args...)						\
> +	do {								\
> +		if (gsc_dbg>= 6)					\
> +			pr_info("[INFO]%s:%d: "fmt "\n",		\

Those [INFO], [ERROR] things are just funny :o)

> +				__func__, __LINE__, ##args);		\
> +	} while (0)
> +
> +#define gsc_err(fmt, args...)						\
> +	do {								\
> +		if (gsc_dbg>= 3)					\
> +			pr_err("[ERROR]%s:%d: "fmt "\n",		\
> +				__func__, __LINE__, ##args);		\
> +	} while (0)
> +
> +#define gsc_warn(fmt, args...)						\
> +	do {								\
> +		if (gsc_dbg>= 4)					\
> +			pr_warn("[WARN]%s:%d: "fmt "\n",		\
> +				__func__, __LINE__, ##args);		\
> +	} while (0)
> +
> +#define gsc_dbg(fmt, args...)						\
> +	do {								\
> +		if (gsc_dbg>= 7)					\
> +			pr_debug("[DEBUG]%s:%d: "fmt "\n",		\
> +				__func__, __LINE__, ##args);		\
> +	} while (0)

As I mentioned above, would be nice to get rid of these custom trace
macros.

> +#define GSC_MAX_CLOCKS			3
> +#define GSC_SHUTDOWN_TIMEOUT		((100*HZ)/1000)
> +#define GSC_MAX_DEVS			4
> +#define WORKQUEUE_NAME_SIZE		32
> +#define FIMD_NAME_SIZE			32
> +#define GSC_M2M_BUF_NUM			0
> +#define GSC_OUT_BUF_MAX			2
> +#define GSC_MAX_CTRL_NUM		10
> +#define GSC_OUT_MAX_MASK_NUM		7
> +#define GSC_SC_ALIGN_4			4
> +#define GSC_SC_ALIGN_2			2
> +#define GSC_OUT_DEF_SRC			15
> +#define GSC_OUT_DEF_DST			7
> +#define DEFAULT_GSC_SINK_WIDTH		800
> +#define DEFAULT_GSC_SINK_HEIGHT		480
> +#define DEFAULT_GSC_SOURCE_WIDTH	800
> +#define DEFAULT_GSC_SOURCE_HEIGHT	480
> +#define DEFAULT_CSC_EQ			1
> +#define DEFAULT_CSC_RANGE		1
> +
> +#define GSC_LAST_DEV_ID			3
> +#define GSC_PAD_SINK			0
> +#define GSC_PAD_SOURCE			1
> +#define GSC_PADS_NUM			2
> +
> +#define	GSC_PARAMS			(1<<  0)
> +#define	GSC_SRC_FMT			(1<<  1)
> +#define	GSC_DST_FMT			(1<<  2)
> +#define	GSC_CTX_M2M			(1<<  3)
> +#define	GSC_CTX_OUTPUT			(1<<  4)
> +#define	GSC_CTX_START			(1<<  5)
> +#define	GSC_CTX_STOP_REQ		(1<<  6)
> +#define	GSC_CTX_CAP			(1<<  10)

Inconsistent indentation.

> +#define MAX_MDEV			2
> +
> +#define V4L2_CID_CACHEABLE			(V4L2_CID_LASTP1 + 1)

That's not right. It should have been:

#define V4L2_CID_CACHEABLE			(V4L2_CID_PRIVATE_BASE + 0)

and +1 for subsequent definitions, until V4L2_CID_CSC_RANGE.

> +#define V4L2_CID_TV_LAYER_BLEND_ENABLE		(V4L2_CID_LASTP1 + 2)
> +#define V4L2_CID_TV_LAYER_BLEND_ALPHA		(V4L2_CID_LASTP1 + 3)
> +#define V4L2_CID_TV_PIXEL_BLEND_ENABLE		(V4L2_CID_LASTP1 + 4)
> +#define V4L2_CID_TV_CHROMA_ENABLE		(V4L2_CID_LASTP1 + 5)
> +#define V4L2_CID_TV_CHROMA_VALUE		(V4L2_CID_LASTP1 + 6)
> +/* for color space conversion equation selection */
> +#define V4L2_CID_CSC_EQ_MODE			(V4L2_CID_LASTP1 + 8)
> +#define V4L2_CID_CSC_EQ				(V4L2_CID_LASTP1 + 9)
> +#define V4L2_CID_CSC_RANGE			(V4L2_CID_LASTP1 + 10)
> +#define V4L2_CID_GLOBAL_ALPHA			(V4L2_CID_LASTP1 + 11)

Remove it and use V4L2_CID_ALPHA_COMPONENT.

> +enum gsc_dev_flags {
> +	/* for global */
> +	ST_PWR_ON,
> +	ST_STOP_REQ,
> +	/* for m2m node */
> +	ST_M2M_OPEN,
> +	ST_M2M_RUN,
> +};
> +
> +enum gsc_irq {
> +	GSC_OR_IRQ = 17,
> +	GSC_DONE_IRQ = 16,

Strange, are these some status bit field definitions ?
Why do we need an enum ? Why these numbers ?

> +};
> +
> +/**
> + * enum gsc_datapath - the path of data used for gscaler
> + * @GSC_CAMERA: from camera
> + * @GSC_DMA: from/to DMA
> + * @GSC_LOCAL: to local path
> + * @GSC_WRITEBACK: from FIMD
> + */
> +enum gsc_datapath {
> +	GSC_CAMERA = 0x1,
> +	GSC_DMA,
> +	GSC_MIXER,
> +	GSC_FIMD,
> +	GSC_WRITEBACK,
> +};
> +
> +enum gsc_color_fmt {
> +	GSC_RGB = 0x1,
> +	GSC_YUV420 = 0x2,
> +	GSC_YUV422 = 0x4,
> +	GSC_YUV444 = 0x8,
> +};
> +
> +enum gsc_yuv_fmt {
> +	GSC_LSB_Y = 0x10,
> +	GSC_LSB_C,
> +	GSC_CBCR = 0x20,
> +	GSC_CRCB,
> +};
> +
> +#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh)
> +#define is_rgb(x) (!!((x)&  0x1))
> +#define is_yuv420(x) (!!((x)&  0x2))
> +#define is_yuv422(x) (!!((x)&  0x4))
> +#define gsc_m2m_run(dev) test_bit(ST_M2M_RUN,&(dev)->state)
> +#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN,&(dev)->state)
> +
> +#define ctrl_to_ctx(__ctrl) \
> +	container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler)
> +/**
> + * struct gsc_fmt - the driver's internal color format data
> + * @mbus_code: Media Bus pixel code, -1 if not applicable
> + * @name: format description
> + * @pixelformat: the fourcc code for this format, 0 if not applicable
> + * @yorder: Y/C order
> + * @corder: Chrominance order control
> + * @num_planes: number of physically non-contiguous data planes
> + * @nr_comp: number of physically contiguous data planes
> + * @depth: per plane driver's private 'number of bits per pixel'
> + * @flags: flags indicating which operation mode format applies to
> + */
> +struct gsc_fmt {
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	char	*name;
> +	u32	pixelformat;
> +	u32	color;
> +	u32	yorder;
> +	u32	corder;
> +	u16	num_planes;
> +	u16	num_comp;
> +	u8	depth[VIDEO_MAX_PLANES];
> +	u32	flags;
> +};
> +
> +/**
> + * struct gsc_input_buf - the driver's video buffer
> + * @vb:	videobuf2 buffer
> + * @list : linked list structure for buffer queue
> + * @idx : index of G-Scaler input buffer
> + */
> +struct gsc_input_buf {
> +	struct vb2_buffer	vb;
> +	struct list_head	list;
> +	int			idx;
> +};
> +
> +/**
> + * struct gsc_addr - the G-Scaler physical address set
> + * @y:	 luminance plane address
> + * @cb:	 Cb plane address
> + * @cr:	 Cr plane address
> + */
> +struct gsc_addr {
> +	dma_addr_t	y;
> +	dma_addr_t	cb;
> +	dma_addr_t	cr;
> +};
> +
> +/* struct gsc_ctrls - the G-Scaler control set
> + * @rotate: rotation degree
> + * @hflip: horizontal flip
> + * @vflip: vertical flip
> + * @global_alpha: the alpha value of current frame
> + * @layer_blend_en: enable mixer layer alpha blending
> + * @layer_alpha: set alpha value for mixer layer
> + * @pixel_blend_en: enable mixer pixel alpha blending
> + * @chroma_en: enable chromakey
> + * @chroma_val:	set value for chromakey
> + * @csc_eq_mode: mode to select csc equation of current frame
> + * @csc_eq: csc equation of current frame
> + * @csc_range: csc range of current frame
> + */
> +struct gsc_ctrls {
> +	struct v4l2_ctrl	*rotate;
> +	struct v4l2_ctrl	*hflip;
> +	struct v4l2_ctrl	*vflip;
> +	struct v4l2_ctrl	*global_alpha;
> +	struct v4l2_ctrl	*layer_blend_en;
> +	struct v4l2_ctrl	*layer_alpha;
> +	struct v4l2_ctrl	*pixel_blend_en;
> +	struct v4l2_ctrl	*chroma_en;
> +	struct v4l2_ctrl	*chroma_val;
> +	struct v4l2_ctrl	*csc_eq_mode;
> +	struct v4l2_ctrl	*csc_eq;
> +	struct v4l2_ctrl	*csc_range;
> +};
> +
> +/**
> + * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler
> + * @pre_shfactor:	pre sclaer shift factor
> + * @pre_hratio:		horizontal ratio of the prescaler
> + * @pre_vratio:		vertical ratio of the prescaler
> + * @main_hratio:	the main scaler's horizontal ratio
> + * @main_vratio:	the main scaler's vertical ratio
> + */
> +struct gsc_scaler {
> +	u32	pre_shfactor;
> +	u32	pre_hratio;
> +	u32	pre_vratio;
> +	unsigned long main_hratio;
> +	unsigned long main_vratio;

Isn't unsigned int or u32 enough ?

> +};
> +
> +struct gsc_dev;
> +
> +struct gsc_ctx;
> +
> +/**
> + * struct gsc_frame - source/target frame properties
> + * @f_width:	SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
> + * @f_height:	SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
> + * @crop:	cropped(source)/scaled(destination) size
> + * @payload:	image size in bytes (w x h x bpp)
> + * @addr:	image frame buffer physical addresses
> + * @fmt:	G-scaler color format pointer

g-scaler, G-scaler or G-Scaler ? How about to choose one and stick 
to it ?

> + * @alph:	frame's alpha value
> + */
> +struct gsc_frame {
> +	u32	f_width;
> +	u32	f_height;
> +	struct v4l2_rect	crop;
> +	unsigned long payload[VIDEO_MAX_PLANES];
> +	struct gsc_addr		addr;
> +	struct gsc_fmt		*fmt;
> +	u8	alpha;
> +};
> +
> +/**
> + * struct gsc_m2m_device - v4l2 memory-to-memory device data
> + * @vfd: the video device node for v4l2 m2m mode
> + * @m2m_dev: v4l2 memory-to-memory device data
> + * @ctx: hardware context data
> + * @refcnt: the reference counter
> + */
> +struct gsc_m2m_device {
> +	struct video_device	*vfd;
> +	struct v4l2_m2m_dev	*m2m_dev;
> +	struct gsc_ctx		*ctx;
> +	int			refcnt;
> +};
> +
> +/**
> + *  struct gsc_pix_max - image pixel size limits in various IP configurations
> + *
> + *  @org_scaler_bypass_w: max pixel width when the scaler is disabled
> + *  @org_scaler_bypass_h: max pixel height when the scaler is disabled
> + *  @org_scaler_input_w: max pixel width when the scaler is enabled
> + *  @org_scaler_input_h: max pixel height when the scaler is enabled
> + *  @real_rot_dis_w: max pixel src cropped height with the rotator is off
> + *  @real_rot_dis_h: max pixel src croppped width with the rotator is off
> + *  @real_rot_en_w: max pixel src cropped width with the rotator is on
> + *  @real_rot_en_h: max pixel src cropped height with the rotator is on
> + *  @target_rot_dis_w: max pixel dst scaled width with the rotator is off
> + *  @target_rot_dis_h: max pixel dst scaled height with the rotator is off
> + *  @target_rot_en_w: max pixel dst scaled width with the rotator is on
> + *  @target_rot_en_h: max pixel dst scaled height with the rotator is on
> + */
> +struct gsc_pix_max {
> +	u16 org_scaler_bypass_w;
> +	u16 org_scaler_bypass_h;
> +	u16 org_scaler_input_w;
> +	u16 org_scaler_input_h;
> +	u16 real_rot_dis_w;
> +	u16 real_rot_dis_h;
> +	u16 real_rot_en_w;
> +	u16 real_rot_en_h;
> +	u16 target_rot_dis_w;
> +	u16 target_rot_dis_h;
> +	u16 target_rot_en_w;
> +	u16 target_rot_en_h;
> +};
> +
> +/**
> + *  struct gsc_pix_min - image pixel size limits in various IP configurations
> + *
> + *  @org_w: minimum source pixel width
> + *  @org_h: minimum source pixel height
> + *  @real_w: minimum input crop pixel width
> + *  @real_h: minimum input crop pixel height
> + *  @target_rot_dis_w: minimum output scaled pixel height when rotator is off
> + *  @target_rot_dis_h: minimum output scaled pixel height when rotator is off
> + *  @target_rot_en_w: minimum output scaled pixel height when rotator is on
> + *  @target_rot_en_h: minimum output scaled pixel height when rotator is on
> + */
> +struct gsc_pix_min {
> +	u16 org_w;
> +	u16 org_h;
> +	u16 real_w;
> +	u16 real_h;
> +	u16 target_rot_dis_w;
> +	u16 target_rot_dis_h;
> +	u16 target_rot_en_w;
> +	u16 target_rot_en_h;
> +};
> +
> +struct gsc_pix_align {
> +	u16 org_h;
> +	u16 org_w;
> +	u16 offset_h;
> +	u16 real_w;
> +	u16 real_h;
> +	u16 target_w;
> +	u16 target_h;
> +};
> +
> +/**
> + * struct gsc_variant - G-Scaler variant information
> + */
> +struct gsc_variant {
> +	struct gsc_pix_max *pix_max;
> +	struct gsc_pix_min *pix_min;
> +	struct gsc_pix_align *pix_align;
> +	u16		in_buf_cnt;
> +	u16		out_buf_cnt;
> +	u16		sc_up_max;
> +	u16		sc_down_max;
> +	u16		poly_sc_down_max;
> +	u16		pre_sc_down_max;
> +	u16		local_sc_down;
> +};
> +
> +/**
> + * struct gsc_driverdata - per device type driver data for init time.
> + *
> + * @variant: the variant information for this driver.
> + * @lclk_frequency: g-scaler clock frequency
> + * @num_entities: the number of g-scalers
> + */
> +struct gsc_driverdata {
> +	struct gsc_variant *variant[GSC_MAX_DEVS];
> +	unsigned long	lclk_frequency;
> +	int		num_entities;
> +};
> +
> +struct gsc_vb2 {
> +	const struct vb2_mem_ops *ops;
> +	void *(*init)(struct gsc_dev *gsc);
> +	void (*cleanup)(void *alloc_ctx);
> +
> +	void (*resume)(void *alloc_ctx);
> +	void (*suspend)(void *alloc_ctx);
> +
> +	int (*cache_flush)(struct vb2_buffer *vb, u32 num_planes);
> +	void (*set_cacheable)(void *alloc_ctx, bool cacheable);
> +	void (*set_sharable)(void *alloc_ctx, bool sharable);
> +};

This looks like something from completely different story, but
seems easy to handle since it's not used in this driver at all,
is it ? Please make sure an unused code is not being added.

> +/**
> + * struct gsc_dev - abstraction for G-Scaler entity
> + * @slock:	the spinlock protecting this data structure
> + * @lock:	the mutex protecting this data structure
> + * @pdev:	pointer to the G-Scaler platform device
> + * @variant:	the IP variant information
> + * @id:		g_scaler device index (0..GSC_MAX_DEVS)
> + * @regs:	the mapped hardware registers
> + * @regs_res:	the resource claimed for IO registers
> + * @irq:	G-scaler interrupt number
> + * @irq_queue:	interrupt handler waitqueue
> + * @m2m:	memory-to-memory V4L2 device information

The comments don't match actual structure declaration below this point.

> + * @out:	memory-to-local V4L2 output device information
> + * @state:	flags used to synchronize m2m and capture mode operation
> + * @alloc_ctx:	videobuf2 memory allocator context
> + * @vb2:	videobuf2 memory allocator call-back functions

Why is this needed ? If it's unused, please just remove it.

> + * @mdev:	pointer to exynos media device
> + * @pipeline:	pointer to subdevs that are connected with gscaler
> + */
> +struct gsc_dev {
> +	spinlock_t			slock;
> +	struct mutex			lock;
> +	struct platform_device		*pdev;
> +	struct gsc_variant		*variant;
> +	u16				id;
> +	struct clk			*clock;
> +	void __iomem			*regs;
> +	struct resource			*regs_res;

unused

> +	int				irq;
> +	wait_queue_head_t		irq_queue;
> +	struct gsc_m2m_device		m2m;
> +	struct exynos_platform_gscaler	*pdata;
> +	unsigned long			state;
> +	struct vb2_alloc_ctx		*alloc_ctx;
> +	const struct gsc_vb2		*vb2;
> +};
> +
> +/**
> + * gsc_ctx - the device context data
> + * @slock:		spinlock protecting this data structure
> + * @s_frame:		source frame properties
> + * @d_frame:		destination frame properties
> + * @in_path:		input mode (DMA or camera)
> + * @out_path:		output mode (DMA or FIFO)
> + * @scaler:		image scaler properties
> + * @flags:		additional flags for image conversion
> + * @state:		flags to keep track of user configuration
> + * @gsc_dev:		the g-scaler device this context applies to
> + * @m2m_ctx:		memory-to-memory device context
> + * @fh:                 v4l2 file handle
> + * @ctrl_handler:       v4l2 controls handler
> + * @ctrls_rdy:          true if the control handler is initialized
> + * @gsc_ctrls		G-Scaler control set
> + * @m2m_ctx:		memory-to-memory device context

Please reorder to match the actual structure declaration.

> + */
> +struct gsc_ctx {
> +	spinlock_t		slock;
> +	struct gsc_frame	s_frame;
> +	struct gsc_frame	d_frame;
> +	enum gsc_datapath	in_path;
> +	enum gsc_datapath	out_path;
> +	struct gsc_scaler	scaler;
> +	u32			flags;
> +	u32			state;
> +	struct gsc_dev		*gsc_dev;
> +	struct v4l2_m2m_ctx	*m2m_ctx;
> +	struct v4l2_fh		fh;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct gsc_ctrls	gsc_ctrls;
> +	bool			ctrls_rdy;
> +};
> +
<snip>
> +
> +static inline void user_to_drv(struct v4l2_ctrl *ctrl, s32 value)
> +{
> +	ctrl->cur.val = ctrl->val = value;

No, see what Hans says about assigning values to ctrl->cur in drivers
(Documentation/video4linux/v4l2-controls.txt):

"For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
contains the current value, which you can use (but not change!) as well.

If s_ctrl returns 0 (OK), then the control framework will copy the new final
values to the 'cur' union.
"

So this user_to_drv() function completely don't make sense - it does what
new_to_cur() function at drivers/media/video/v4l2-ctrls.c is intended for.
Please check how it is all done in drivers/media/video/s5p-fimc/fimc-core.c.

> +}
> +
> +void gsc_hw_set_sw_reset(struct gsc_dev *dev);
> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev);
> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev);
> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
> +							int index);
> +void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr,
> +							int index);
> +void gsc_hw_set_input_path(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_size(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx);
> +void gsc_hw_set_output_path(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_size(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx);
> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx);
> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx);
> +void gsc_hw_set_rotation(struct gsc_ctx *ctx);
> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx);
> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx);
> +
> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev);
> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev);
> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev);
> +int gsc_wait_reset(struct gsc_dev *dev);
> +int gsc_wait_operating(struct gsc_dev *dev);
> +int gsc_wait_stop(struct gsc_dev *dev);
> +
> +#endif /* GSC_CORE_H_ */
> diff --git a/drivers/media/video/exynos/gsc/gsc-m2m.c b/drivers/media/video/exynos/gsc/gsc-m2m.c
> new file mode 100644
> index 0000000..ead59a0
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-m2m.c
> @@ -0,0 +1,751 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-m2m.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/types.h>
> +#include<linux/errno.h>
> +#include<linux/bug.h>
> +#include<linux/interrupt.h>
> +#include<linux/workqueue.h>
> +#include<linux/device.h>
> +#include<linux/platform_device.h>
> +#include<linux/list.h>
> +#include<linux/io.h>
> +#include<linux/slab.h>
> +#include<linux/clk.h>
> +#include<media/v4l2-ioctl.h>
> +
> +#include "gsc-core.h"
> +
> +static int gsc_ctx_stop_req(struct gsc_ctx *ctx)
> +{
> +	struct gsc_ctx *curr_ctx;
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +	int ret = 0;
> +
> +	curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> +	if (!gsc_m2m_run(gsc) || (curr_ctx != ctx))
> +		return 0;
> +	ctx->state |= GSC_CTX_STOP_REQ;
> +	ret = wait_event_timeout(gsc->irq_queue,
> +			!gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
> +			GSC_SHUTDOWN_TIMEOUT);
> +	if (!ret)
> +		ret = -EBUSY;
> +
> +	return ret;
> +}
> +
> +static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> +	struct gsc_ctx *ctx = q->drv_priv;
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev);
> +	return ret>  0 ? 0 : ret;
> +}
> +
> +static int gsc_m2m_stop_streaming(struct vb2_queue *q)
> +{
> +	struct gsc_ctx *ctx = q->drv_priv;
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +	int ret;
> +
> +	ret = gsc_ctx_stop_req(ctx);
> +	/* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
> +	if (ret<  0)
> +		dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
> +
> +	pm_runtime_put(&ctx->gsc_dev->pdev->dev);
> +
> +	return 0;
> +}
> +
> +static void gsc_m2m_job_abort(void *priv)
> +{
> +	struct gsc_ctx *ctx = priv;
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +	int ret;
> +
> +	ret = gsc_ctx_stop_req(ctx);
> +	/* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
> +	if (ret<  0)
> +		dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
> +}
> +
> +int gsc_fill_addr(struct gsc_ctx *ctx)
> +{
> +	struct gsc_frame *s_frame, *d_frame;
> +	struct vb2_buffer *vb = NULL;
> +	int ret = 0;
> +
> +	s_frame =&ctx->s_frame;
> +	d_frame =&ctx->d_frame;
> +
> +	vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
> +	ret = gsc_prepare_addr(ctx, vb, s_frame,&s_frame->addr);
> +	if (ret)
> +		return ret;
> +
> +	vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
> +	ret = gsc_prepare_addr(ctx, vb, d_frame,&d_frame->addr);
> +
> +	return ret;
> +}
> +
> +static void gsc_m2m_device_run(void *priv)
> +{
> +	struct gsc_ctx *ctx = priv;
> +	struct gsc_dev *gsc;
> +	unsigned long flags;
> +	u32 ret;
> +	bool is_set = false;
> +
> +	if (WARN(!ctx, "null hardware context\n"))
> +		return;
> +
> +	gsc = ctx->gsc_dev;
> +
> +	spin_lock_irqsave(&ctx->slock, flags);
> +	/* Reconfigure hardware if the context has changed. */
> +	if (gsc->m2m.ctx != ctx) {
> +		gsc_dbg("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
> +			  gsc->m2m.ctx, ctx);
> +		ctx->state |= GSC_PARAMS;
> +		gsc->m2m.ctx = ctx;
> +	}
> +
> +	is_set = (ctx->state&  GSC_CTX_STOP_REQ) ? 1 : 0;
> +	ctx->state&= ~GSC_CTX_STOP_REQ;
> +	if (is_set) {
> +		wake_up(&gsc->irq_queue);
> +		goto put_device;
> +	}
> +
> +	ret = gsc_fill_addr(ctx);
> +	if (ret) {
> +		gsc_err("Wrong address");
> +		goto put_device;
> +	}
> +
> +	gsc_set_prefbuf(gsc, ctx->s_frame);
> +	gsc_hw_set_input_addr(gsc,&ctx->s_frame.addr, GSC_M2M_BUF_NUM);
> +	gsc_hw_set_output_addr(gsc,&ctx->d_frame.addr, GSC_M2M_BUF_NUM);
> +
> +	if (ctx->state&  GSC_PARAMS) {
> +		gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
> +		gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
> +		gsc_hw_set_frm_done_irq_mask(gsc, false);
> +		gsc_hw_set_gsc_irq_enable(gsc, true);
> +
> +		if (gsc_set_scaler_info(ctx)) {
> +			gsc_err("Scaler setup error");
> +			goto put_device;
> +		}
> +
> +		gsc_hw_set_input_path(ctx);
> +		gsc_hw_set_in_size(ctx);
> +		gsc_hw_set_in_image_format(ctx);
> +
> +		gsc_hw_set_output_path(ctx);
> +		gsc_hw_set_out_size(ctx);
> +		gsc_hw_set_out_image_format(ctx);
> +
> +		gsc_hw_set_prescaler(ctx);
> +		gsc_hw_set_mainscaler(ctx);
> +		gsc_hw_set_rotation(ctx);
> +		gsc_hw_set_global_alpha(ctx);
> +	}
> +	/* When you update SFRs in the middle of operating
> +	gsc_hw_set_sfr_update(ctx);
> +	*/
> +
> +	ctx->state&= ~GSC_PARAMS;
> +
> +	if (!test_and_set_bit(ST_M2M_RUN,&gsc->state)) {
> +		/* One frame mode sequence
> +		 GSCALER_ON on ->  GSCALER_OP_STATUS is operating ->
> +		 GSCALER_ON off */
> +		gsc_hw_enable_control(gsc, true);
> +		ret = gsc_wait_operating(gsc);

gsc_m2m_device_run() can be invoked from the interrupt handler (through
v4l2_mm_job_finish()), so we can be in an interrupt context here. 
Additionally, this section is executed with ctx->slock spinlock held 
(spin_lock_irqsave(&ctx->slock, flags) above). Guess what would have
happened if this code would have been executed on single-processor
system ? Don't you get a "scheduling while atomic" BUG, due to
usleep_range() being called in gsc_wait_operating() ?

It takes maximum 10 jiffies for gsc_wait_operating() to complete,
and all this from within an interrupt context and with interrupts
disabled on a local CPU!

There must be something wrong with the design here...

> +		if (ret<  0) {
> +			gsc_err("gscaler wait operating timeout");
> +			goto put_device;
> +		}
> +		gsc_hw_enable_control(gsc, false);
> +	}
> +
> +	spin_unlock_irqrestore(&ctx->slock, flags);
> +	return;
> +
> +put_device:
> +	ctx->state&= ~GSC_PARAMS;
> +	spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
> +static int gsc_m2m_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 gsc_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct gsc_frame *frame;
> +	int i;
> +
> +	frame = ctx_get_frame(ctx, vq->type);
> +	if (IS_ERR(frame))
> +		return PTR_ERR(frame);
> +
> +	if (!frame->fmt)
> +		return -EINVAL;
> +
> +	*num_planes = frame->fmt->num_planes;
> +	for (i = 0; i<  frame->fmt->num_planes; i++) {
> +		sizes[i] = get_plane_size(frame, i);
> +		allocators[i] = ctx->gsc_dev->alloc_ctx;
> +	}
> +	return 0;
> +}
> +
> +static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct gsc_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->fmt->num_planes; i++)
> +			vb2_set_plane_payload(vb, i, frame->payload[i]);
> +	}
> +
> +	return 0;
> +}
> +
> +static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	gsc_dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
> +
> +	if (ctx->m2m_ctx)
> +		v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
> +}
> +
> +struct vb2_ops gsc_m2m_qops = {
> +	.queue_setup	 = gsc_m2m_queue_setup,
> +	.buf_prepare	 = gsc_m2m_buf_prepare,
> +	.buf_queue	 = gsc_m2m_buf_queue,
> +	.wait_prepare	 = gsc_unlock,
> +	.wait_finish	 = gsc_lock,
> +	.stop_streaming	 = gsc_m2m_stop_streaming,
> +	.start_streaming = gsc_m2m_start_streaming,
> +};
> +
> +static int gsc_m2m_querycap(struct file *file, void *fh,
> +			   struct v4l2_capability *cap)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +
> +	strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
> +	strncpy(cap->card, gsc->pdev->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 gsc_m2m_enum_fmt_mplane(struct file *file, void *priv,
> +				struct v4l2_fmtdesc *f)
> +{
> +	return gsc_enum_fmt_mplane(f);
> +}
> +
> +static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
> +			     struct v4l2_format *f)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +	return gsc_g_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
> +				  struct v4l2_format *f)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +	return gsc_try_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
> +				 struct v4l2_format *f)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	struct vb2_queue *vq;
> +	struct gsc_frame *frame;
> +	struct v4l2_pix_format_mplane *pix;
> +	int i, ret = 0;
> +
> +	ret = gsc_m2m_try_fmt_mplane(file, fh, f);
> +	if (ret)
> +		return ret;
> +
> +	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
> +
> +	if (vb2_is_streaming(vq)) {
> +		gsc_err("queue (%d) busy", f->type);
> +		return -EBUSY;
> +	}
> +
> +	if (V4L2_TYPE_IS_OUTPUT(f->type))
> +		frame =&ctx->s_frame;
> +	else
> +		frame =&ctx->d_frame;
> +
> +	pix =&f->fmt.pix_mp;
> +	frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
> +	if (!frame->fmt)
> +		return -EINVAL;
> +
> +	for (i = 0; i<  frame->fmt->num_planes; i++)
> +		frame->payload[i] = pix->plane_fmt[i].sizeimage;
> +
> +	gsc_set_frame_size(frame, pix->width, pix->height);
> +
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
> +	else
> +		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
> +
> +	gsc_dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
> +
> +	return 0;
> +}
> +
> +static int gsc_m2m_reqbufs(struct file *file, void *fh,
> +			  struct v4l2_requestbuffers *reqbufs)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +	struct gsc_frame *frame;
> +	u32 max_cnt;
> +
> +	max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
> +		gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
> +	if (reqbufs->count>  max_cnt)
> +		return -EINVAL;
> +	else if (reqbufs->count == 0) {
> +		if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +			gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
> +		else
> +			gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
> +	}
> +
> +	frame = ctx_get_frame(ctx, reqbufs->type);
> +
> +	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
> +}
> +
> +static int gsc_m2m_querybuf(struct file *file, void *fh,
> +					struct v4l2_buffer *buf)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_qbuf(struct file *file, void *fh,
> +			  struct v4l2_buffer *buf)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_dqbuf(struct file *file, void *fh,
> +			   struct v4l2_buffer *buf)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_streamon(struct file *file, void *fh,
> +			   enum v4l2_buf_type type)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +	/* The source and target color format need to be set */
> +	if (V4L2_TYPE_IS_OUTPUT(type)) {
> +		if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
> +			return -EINVAL;
> +	} else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
> +		return -EINVAL;
> +	}
> +
> +	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
> +}
> +
> +static int gsc_m2m_streamoff(struct file *file, void *fh,
> +			    enum v4l2_buf_type type)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
> +}
> +
> +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
> +static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
> +{
> +	if (a->left<  b->left || a->top<  b->top)
> +		return 0;
> +
> +	if (a->left + a->width>  b->left + b->width)
> +		return 0;
> +
> +	if (a->top + a->height>  b->top + b->height)
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static int gsc_m2m_g_selection(struct file *file, void *fh,
> +			struct v4l2_selection *s)
> +{
> +	struct gsc_frame *frame;
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)&&
> +	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +		return -EINVAL;
> +
> +	frame = ctx_get_frame(ctx, s->type);
> +	if (IS_ERR(frame))
> +		return PTR_ERR(frame);
> +
> +	switch (s->target) {
> +	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> +	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +		s->r.left = 0;
> +		s->r.top = 0;
> +		s->r.width = frame->f_width;
> +		s->r.height = frame->f_height;
> +		return 0;
> +
> +	case V4L2_SEL_TGT_COMPOSE_ACTIVE:
> +	case V4L2_SEL_TGT_CROP_ACTIVE:
> +		s->r.left = frame->crop.left;
> +		s->r.top = frame->crop.top;
> +		s->r.width = frame->crop.width;
> +		s->r.height = frame->crop.height;
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int gsc_m2m_s_selection(struct file *file, void *fh,
> +			struct v4l2_selection *s)
> +{
> +	struct gsc_frame *frame;
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	struct v4l2_crop cr;
> +	struct gsc_variant *variant = ctx->gsc_dev->variant;
> +	int ret;
> +
> +	cr.type = s->type;
> +	cr.c = s->r;
> +
> +	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)&&
> +	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +		return -EINVAL;
> +
> +	ret = gsc_try_crop(ctx,&cr);
> +	if (ret)
> +		return ret;
> +
> +	if (s->flags&  V4L2_SEL_FLAG_LE&&
> +	    !is_rectangle_enclosed(&cr.c,&s->r))
> +		return -ERANGE;
> +
> +	if (s->flags&  V4L2_SEL_FLAG_GE&&
> +	    !is_rectangle_enclosed(&s->r,&cr.c))
> +		return -ERANGE;
> +
> +	s->r = cr.c;
> +
> +	switch (s->target) {
> +	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> +	case V4L2_SEL_TGT_COMPOSE_ACTIVE:
> +		frame =&ctx->s_frame;
> +		break;
> +
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +	case V4L2_SEL_TGT_CROP_ACTIVE:
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +		frame =&ctx->d_frame;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* Check to see if scaling ratio is within supported range */
> +	if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
> +		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +			ret = gsc_check_scaler_ratio(variant, cr.c.width,
> +				cr.c.height, ctx->d_frame.crop.width,
> +				ctx->d_frame.crop.height,
> +				ctx->gsc_ctrls.rotate->val, ctx->out_path);
> +		} else {
> +			ret = gsc_check_scaler_ratio(variant,
> +				ctx->s_frame.crop.width,
> +				ctx->s_frame.crop.height, cr.c.width,
> +				cr.c.height, ctx->gsc_ctrls.rotate->val,
> +				ctx->out_path);
> +		}
> +
> +		if (ret) {
> +			gsc_err("Out of scaler range");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	frame->crop = cr.c;
> +
> +	gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
> +	.vidioc_querycap		= gsc_m2m_querycap,
> +
> +	.vidioc_enum_fmt_vid_cap_mplane	= gsc_m2m_enum_fmt_mplane,
> +	.vidioc_enum_fmt_vid_out_mplane	= gsc_m2m_enum_fmt_mplane,
> +
> +	.vidioc_g_fmt_vid_cap_mplane	= gsc_m2m_g_fmt_mplane,
> +	.vidioc_g_fmt_vid_out_mplane	= gsc_m2m_g_fmt_mplane,
> +
> +	.vidioc_try_fmt_vid_cap_mplane	= gsc_m2m_try_fmt_mplane,
> +	.vidioc_try_fmt_vid_out_mplane	= gsc_m2m_try_fmt_mplane,
> +
> +	.vidioc_s_fmt_vid_cap_mplane	= gsc_m2m_s_fmt_mplane,
> +	.vidioc_s_fmt_vid_out_mplane	= gsc_m2m_s_fmt_mplane,
> +
> +	.vidioc_reqbufs			= gsc_m2m_reqbufs,
> +	.vidioc_querybuf		= gsc_m2m_querybuf,
> +
> +	.vidioc_qbuf			= gsc_m2m_qbuf,
> +	.vidioc_dqbuf			= gsc_m2m_dqbuf,
> +
> +	.vidioc_streamon		= gsc_m2m_streamon,
> +	.vidioc_streamoff		= gsc_m2m_streamoff,
> +
> +	.vidioc_g_selection		= gsc_m2m_g_selection,
> +	.vidioc_s_selection		= gsc_m2m_s_selection
> +
> +};
> +
> +static int queue_init(void *priv, struct vb2_queue *src_vq,
> +		      struct vb2_queue *dst_vq)
> +{
> +	struct gsc_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->drv_priv = ctx;
> +	src_vq->ops =&gsc_m2m_qops;
> +	src_vq->mem_ops =&vb2_dma_contig_memops;
> +	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->drv_priv = ctx;
> +	dst_vq->ops =&gsc_m2m_qops;
> +	dst_vq->mem_ops =&vb2_dma_contig_memops;
> +	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> +
> +	return vb2_queue_init(dst_vq);
> +}
> +
> +static int gsc_m2m_open(struct file *file)
> +{
> +	struct gsc_dev *gsc = video_drvdata(file);
> +	struct gsc_ctx *ctx = NULL;
> +	int ret;
> +
> +	gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> +	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
> +	ret = gsc_ctrls_create(ctx);
> +	if (ret)
> +		goto error_fh;
> +
> +	/* Use separate control handler per file handle */
> +	ctx->fh.ctrl_handler =&ctx->ctrl_handler;
> +	file->private_data =&ctx->fh;
> +	v4l2_fh_add(&ctx->fh);
> +
> +	ctx->gsc_dev = gsc;
> +	/* Default color format */
> +	ctx->s_frame.fmt = get_format(0);
> +	ctx->d_frame.fmt = get_format(0);
> +	/* Setup the device context for mem2mem mode. */
> +	ctx->state |= GSC_CTX_M2M;
> +	ctx->flags = 0;
> +	ctx->in_path = GSC_DMA;
> +	ctx->out_path = GSC_DMA;
> +	spin_lock_init(&ctx->slock);
> +
> +	ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
> +	if (IS_ERR(ctx->m2m_ctx)) {
> +		gsc_err("Failed to initialize m2m context");
> +		ret = PTR_ERR(ctx->m2m_ctx);
> +		goto error_fh;
> +	}
> +
> +	if (gsc->m2m.refcnt++ == 0)
> +		set_bit(ST_M2M_OPEN,&gsc->state);
> +
> +	gsc_dbg("gsc m2m driver is opened, ctx(0x%p)", ctx);
> +	return 0;
> +
> +error_fh:
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +	kfree(ctx);
> +	return ret;
> +}
> +
> +static int gsc_m2m_release(struct file *file)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +
> +	gsc_dbg("pid: %d, state: 0x%lx, refcnt= %d",
> +		task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
> +
> +	v4l2_m2m_ctx_release(ctx->m2m_ctx);
> +	gsc_ctrls_delete(ctx);
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +
> +	if (--gsc->m2m.refcnt<= 0)
> +		clear_bit(ST_M2M_OPEN,&gsc->state);
> +	kfree(ctx);
> +	return 0;
> +}
> +
> +static unsigned int gsc_m2m_poll(struct file *file,
> +				     struct poll_table_struct *wait)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> +	return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
> +}
> +
> +static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> +	return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
> +}
> +static const struct v4l2_file_operations gsc_m2m_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= gsc_m2m_open,
> +	.release	= gsc_m2m_release,
> +	.poll		= gsc_m2m_poll,
> +	.unlocked_ioctl	= video_ioctl2,
> +	.mmap		= gsc_m2m_mmap,
> +};
> +
> +static struct v4l2_m2m_ops gsc_m2m_ops = {
> +	.device_run	= gsc_m2m_device_run,
> +	.job_abort	= gsc_m2m_job_abort,
> +};
> +
> +int gsc_register_m2m_device(struct gsc_dev *gsc)
> +{
> +	struct video_device *vfd;
> +	struct platform_device *pdev;
> +	int ret = 0;
> +
> +	if (!gsc)
> +		return -ENODEV;
> +
> +	pdev = gsc->pdev;
> +
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		dev_err(&pdev->dev, "Failed to allocate video device\n");
> +		return -ENOMEM;
> +	}
> +
> +	vfd->fops	=&gsc_m2m_fops;
> +	vfd->ioctl_ops	=&gsc_m2m_ioctl_ops;
> +	vfd->release	= video_device_release;
> +	vfd->lock	=&gsc->lock;
> +	snprintf(vfd->name, sizeof(vfd->name), "%s.%d:m2m",
> +					GSC_MODULE_NAME, gsc->id);
> +
> +	video_set_drvdata(vfd, gsc);
> +
> +	gsc->m2m.vfd = vfd;
> +	gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
> +	if (IS_ERR(gsc->m2m.m2m_dev)) {
> +		dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
> +		ret = PTR_ERR(gsc->m2m.m2m_dev);
> +		goto err_m2m_r1;
> +	}
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(&pdev->dev,
> +			 "%s(): failed to register video device\n", __func__);
> +		goto err_m2m_r2;
> +	}
> +
> +	gsc_dbg("gsc m2m driver registered as /dev/video%d", vfd->num);
> +
> +	return 0;
> +
> +err_m2m_r2:
> +	v4l2_m2m_release(gsc->m2m.m2m_dev);
> +err_m2m_r1:
> +	video_device_release(gsc->m2m.vfd);
> +
> +	return ret;
> +}
> +
> +void gsc_unregister_m2m_device(struct gsc_dev *gsc)
> +{
> +	if (gsc)
> +		v4l2_m2m_release(gsc->m2m.m2m_dev);
> +}
> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c
> new file mode 100644
> index 0000000..7c5cce2
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-regs.c
> @@ -0,0 +1,579 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-regs.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/io.h>
> +#include<linux/delay.h>
> +#include<mach/map.h>
> +#include "gsc-core.h"
> +
> +void gsc_hw_set_sw_reset(struct gsc_dev *dev)
> +{
> +	u32 cfg = 0;
> +
> +	cfg |= GSC_SW_RESET_SRESET;
> +	writel(cfg, dev->regs + GSC_SW_RESET);
> +}
> +
> +int gsc_wait_reset(struct gsc_dev *dev)
> +{
> +	unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> +	u32 cfg;
> +
> +	while (time_before(jiffies, timeo)) {
> +		cfg = readl(dev->regs + GSC_SW_RESET);
> +		if (!cfg)
> +			return 0;
> +		usleep_range(10, 20);
> +	}
> +	gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
> +
> +	return -EBUSY;
> +}
> +
> +int gsc_wait_operating(struct gsc_dev *dev)
> +{
> +	unsigned long timeo = jiffies + 10; /* timeout of 50ms */

It would have been 50 ms for:

unsigned long timeo = jiffies + msecs_to_jiffies(50); /* timeout of 50ms */

Now it's just 10 jiffies!

Would you mind renaming "timeo" to "end" ?

> +	u32 cfg;
> +
> +	while (time_before(jiffies, timeo)) {
> +		cfg = readl(dev->regs + GSC_ENABLE);
> +		if ((cfg&  GSC_ENABLE_OP_STATUS) == GSC_ENABLE_OP_STATUS)
> +			return 0;
> +		usleep_range(10, 20);
> +	}
> +	gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));

Misleading value. Might make sense to define some contants for
these timeouts.

> +
> +	return -EBUSY;
> +}
> +
> +int gsc_wait_stop(struct gsc_dev *dev)
> +{
> +	unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> +	u32 cfg;
> +
> +	while (time_before(jiffies, timeo)) {
> +		cfg = readl(dev->regs + GSC_ENABLE);
> +		if (!(cfg&  GSC_ENABLE_OP_STATUS))
> +			return 0;
> +		usleep_range(10, 20);
> +	}
> +	gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));

ditto

> +
> +	return -EBUSY;
> +}
> +
> +
> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_ENABLE);
> +	if (mask)
> +		cfg |= GSC_ENABLE_ON_CLEAR;
> +	else
> +		cfg&= ~GSC_ENABLE_ON_CLEAR;
> +	writel(cfg, dev->regs + GSC_ENABLE);
> +}
> +
> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev)
> +{
> +	u32 cfg, status, bits = 0;
> +
> +	cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +	status = cfg&  GSC_IN_BASE_ADDR_MASK;
> +	while (status) {
> +		status = status&  (status - 1);
> +		bits++;
> +	}
> +	return bits;
> +}
> +
> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev)
> +{
> +	u32 cfg, curr_index, i;
> +
> +	cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +	curr_index = GSC_IN_CURR_GET_INDEX(cfg);
> +	for (i = curr_index; i>  1; i--) {
> +		if (cfg ^ (1<<  (i - 2)))
> +			return i - 2;
> +	}
> +
> +	for (i = dev->variant->in_buf_cnt; i>  curr_index; i--) {
> +		if (cfg ^ (1<<  (i - 1)))
> +			return i - 1;
> +	}
> +
> +	return curr_index - 1;
> +}
> +
> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_IRQ);
> +	if (mask)
> +		cfg |= GSC_IRQ_FRMDONE_MASK;
> +	else
> +		cfg&= ~GSC_IRQ_FRMDONE_MASK;
> +	writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_IRQ);
> +	if (mask)
> +		cfg |= GSC_IRQ_OR_MASK;
> +	else
> +		cfg&= ~GSC_IRQ_OR_MASK;
> +	writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_IRQ);
> +	if (mask)
> +		cfg |= GSC_IRQ_ENABLE;
> +	else
> +		cfg&= ~GSC_IRQ_ENABLE;
> +	writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +	cfg |= GSC_IN_BASE_ADDR_MASK;
> +	cfg |= GSC_IN_BASE_ADDR_PINGPONG(dev->variant->in_buf_cnt);
> +
> +	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
> +	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +	cfg |= GSC_OUT_BASE_ADDR_MASK;
> +	cfg |= GSC_OUT_BASE_ADDR_PINGPONG(dev->variant->out_buf_cnt);
> +
> +	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
> +	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift,
> +				bool enable)
> +{
> +	u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +	u32 mask = 1<<  shift;
> +
> +	cfg&= (~mask);

Suprefluous parentheses.

> +	cfg |= enable<<  shift;
> +
> +	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> +	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
> +	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
> +				bool enable)
> +{
> +	u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +	u32 mask = 1<<  shift;
> +
> +	cfg&= (~mask);

ditto

> +	cfg |= enable<<  shift;
> +
> +	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> +	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
> +	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
> +}
> +
> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev)
> +{
> +	u32 bits = 0;
> +	u32 mask_bits = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);

Care to add an empty line? It might also look better to order 
declarations in decending line order. 

> +	mask_bits&= GSC_OUT_BASE_ADDR_MASK;
> +
> +	while (mask_bits) {
> +		mask_bits = mask_bits&  (mask_bits - 1);
> +		bits++;
> +	}
> +	bits = 16 - bits;

return 16 - bits;

> +
> +	return bits;
> +}
> +
> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
> +				int index)
> +{
> +	gsc_dbg("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
> +		addr->y, addr->cb, addr->cr);
> +	writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
> +	writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
> +	writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
> +
> +}
> +
> +void gsc_hw_set_output_addr(struct gsc_dev *dev,
> +			     struct gsc_addr *addr, int index)
> +{
> +	gsc_dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
> +			index, addr->y, addr->cb, addr->cr);
> +	writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
> +	writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
> +	writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
> +}
> +
> +void gsc_hw_set_input_path(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +
> +	u32 cfg = readl(dev->regs + GSC_IN_CON);
> +	cfg&= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
> +
> +	if (ctx->in_path == GSC_DMA)
> +		cfg |= GSC_IN_PATH_MEMORY;
> +
> +	writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_in_size(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_frame *frame =&ctx->s_frame;
> +	u32 cfg;
> +
> +	/* Set input pixel offset */
> +	cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left);
> +	cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top);
> +	writel(cfg, dev->regs + GSC_SRCIMG_OFFSET);
> +
> +	/* Set input original size */
> +	cfg = GSC_SRCIMG_WIDTH(frame->f_width);
> +	cfg |= GSC_SRCIMG_HEIGHT(frame->f_height);
> +	writel(cfg, dev->regs + GSC_SRCIMG_SIZE);
> +
> +	/* Set input cropped size */
> +	cfg = GSC_CROPPED_WIDTH(frame->crop.width);
> +	cfg |= GSC_CROPPED_HEIGHT(frame->crop.height);
> +	writel(cfg, dev->regs + GSC_CROPPED_SIZE);
> +}
> +
> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_frame *frame =&ctx->s_frame;
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_IN_CON);
> +	if (ctx->gsc_ctrls.csc_eq->val) {
> +		if (ctx->gsc_ctrls.csc_range->val)
> +			cfg |= GSC_IN_RGB_HD_WIDE;
> +		else
> +			cfg |= GSC_IN_RGB_HD_NARROW;
> +	} else {
> +		if (ctx->gsc_ctrls.csc_range->val)
> +			cfg |= GSC_IN_RGB_SD_WIDE;
> +		else
> +			cfg |= GSC_IN_RGB_SD_NARROW;
> +	}
> +
> +	if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
> +		cfg |= GSC_IN_RGB565;
> +	else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
> +		cfg |= GSC_IN_XRGB8888;
> +
> +	writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_frame *frame =&ctx->s_frame;
> +	u32 i, depth = 0;
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_IN_CON);
> +	cfg&= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
> +		 GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK |
> +		 GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE);
> +	writel(cfg, dev->regs + GSC_IN_CON);
> +
> +	if (is_rgb(frame->fmt->color)) {
> +		gsc_hw_set_in_image_rgb(ctx);
> +		return;
> +	}
> +	for (i = 0; i<  frame->fmt->num_planes; i++)
> +		depth += frame->fmt->depth[i];
> +
> +	switch (frame->fmt->num_comp) {
> +	case 1:
> +		cfg |= GSC_IN_YUV422_1P;
> +		if (frame->fmt->yorder == GSC_LSB_Y)
> +			cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y;
> +		else
> +			cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C;
> +		if (frame->fmt->corder == GSC_CBCR)
> +			cfg |= GSC_IN_CHROMA_ORDER_CBCR;
> +		else
> +			cfg |= GSC_IN_CHROMA_ORDER_CRCB;
> +		break;
> +	case 2:
> +		if (depth == 12)
> +			cfg |= GSC_IN_YUV420_2P;
> +		else
> +			cfg |= GSC_IN_YUV422_2P;
> +		if (frame->fmt->corder == GSC_CBCR)
> +			cfg |= GSC_IN_CHROMA_ORDER_CBCR;
> +		else
> +			cfg |= GSC_IN_CHROMA_ORDER_CRCB;
> +		break;
> +	case 3:
> +		if (depth == 12)
> +			cfg |= GSC_IN_YUV420_3P;
> +		else
> +			cfg |= GSC_IN_YUV422_3P;
> +		break;
> +	};
> +
> +	if (is_tiled(frame->fmt))
> +		cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE;
> +
> +	writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_output_path(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +
> +	u32 cfg = readl(dev->regs + GSC_OUT_CON);
> +	cfg&= ~GSC_OUT_PATH_MASK;
> +
> +	if (ctx->out_path == GSC_DMA)
> +		cfg |= GSC_OUT_PATH_MEMORY;
> +	else
> +		cfg |= GSC_OUT_PATH_LOCAL;
> +
> +	writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_out_size(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_frame *frame =&ctx->d_frame;
> +	u32 cfg;
> +
> +	/* Set output original size */
> +	if (ctx->out_path == GSC_DMA) {
> +		cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left);
> +		cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top);
> +		writel(cfg, dev->regs + GSC_DSTIMG_OFFSET);
> +
> +		cfg = GSC_DSTIMG_WIDTH(frame->f_width);
> +		cfg |= GSC_DSTIMG_HEIGHT(frame->f_height);
> +		writel(cfg, dev->regs + GSC_DSTIMG_SIZE);
> +	}
> +
> +	/* Set output scaled size */
> +	if (ctx->gsc_ctrls.rotate->val == 90 ||
> +	    ctx->gsc_ctrls.rotate->val == 270) {
> +		cfg = GSC_SCALED_WIDTH(frame->crop.height);
> +		cfg |= GSC_SCALED_HEIGHT(frame->crop.width);
> +	} else {
> +		cfg = GSC_SCALED_WIDTH(frame->crop.width);
> +		cfg |= GSC_SCALED_HEIGHT(frame->crop.height);
> +	}
> +	writel(cfg, dev->regs + GSC_SCALED_SIZE);
> +}
> +
> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_frame *frame =&ctx->d_frame;
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_OUT_CON);
> +	if (ctx->gsc_ctrls.csc_eq->val) {
> +		if (ctx->gsc_ctrls.csc_range->val)
> +			cfg |= GSC_OUT_RGB_HD_WIDE;
> +		else
> +			cfg |= GSC_OUT_RGB_HD_NARROW;
> +	} else {
> +		if (ctx->gsc_ctrls.csc_range->val)
> +			cfg |= GSC_OUT_RGB_SD_WIDE;
> +		else
> +			cfg |= GSC_OUT_RGB_SD_NARROW;
> +	}
> +
> +	if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
> +		cfg |= GSC_OUT_RGB565;
> +	else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
> +		cfg |= GSC_OUT_XRGB8888;
> +
> +	writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_frame *frame =&ctx->d_frame;
> +	u32 i, depth = 0;
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_OUT_CON);
> +	cfg&= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
> +		 GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK |
> +		 GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE);
> +	writel(cfg, dev->regs + GSC_OUT_CON);
> +
> +	if (is_rgb(frame->fmt->color)) {
> +		gsc_hw_set_out_image_rgb(ctx);
> +		return;
> +	}
> +
> +	if (ctx->out_path != GSC_DMA) {
> +		cfg |= GSC_OUT_YUV444;
> +		goto end_set;
> +	}
> +
> +	for (i = 0; i<  frame->fmt->num_planes; i++)
> +		depth += frame->fmt->depth[i];
> +
> +	switch (frame->fmt->num_comp) {
> +	case 1:
> +		cfg |= GSC_OUT_YUV422_1P;
> +		if (frame->fmt->yorder == GSC_LSB_Y)
> +			cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y;
> +		else
> +			cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C;
> +		if (frame->fmt->corder == GSC_CBCR)
> +			cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
> +		else
> +			cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
> +		break;
> +	case 2:
> +		if (depth == 12)
> +			cfg |= GSC_OUT_YUV420_2P;
> +		else
> +			cfg |= GSC_OUT_YUV422_2P;
> +		if (frame->fmt->corder == GSC_CBCR)
> +			cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
> +		else
> +			cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
> +		break;
> +	case 3:
> +		cfg |= GSC_OUT_YUV420_3P;
> +		break;
> +	};
> +
> +	if (is_tiled(frame->fmt))
> +		cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE;
> +
> +end_set:
> +	writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_scaler *sc =&ctx->scaler;
> +	u32 cfg;
> +
> +	cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor);
> +	cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio);
> +	cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio);
> +	writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO);
> +}
> +
> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_scaler *sc =&ctx->scaler;
> +	u32 cfg;
> +
> +	cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
> +	writel(cfg, dev->regs + GSC_MAIN_H_RATIO);
> +
> +	cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio);
> +	writel(cfg, dev->regs + GSC_MAIN_V_RATIO);
> +}
> +
> +void gsc_hw_set_rotation(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_IN_CON);
> +	cfg&= ~GSC_IN_ROT_MASK;
> +
> +	switch (ctx->gsc_ctrls.rotate->val) {
> +	case 270:
> +		cfg |= GSC_IN_ROT_270;
> +		break;
> +	case 180:
> +		cfg |= GSC_IN_ROT_180;
> +		break;
> +	case 90:
> +		if (ctx->gsc_ctrls.hflip->val)
> +			cfg |= GSC_IN_ROT_90_XFLIP;
> +		else if (ctx->gsc_ctrls.vflip->val)
> +			cfg |= GSC_IN_ROT_90_YFLIP;
> +		else
> +			cfg |= GSC_IN_ROT_90;
> +		break;
> +	case 0:
> +		if (ctx->gsc_ctrls.hflip->val)
> +			cfg |= GSC_IN_ROT_XFLIP;
> +		else if (ctx->gsc_ctrls.vflip->val)
> +			cfg |= GSC_IN_ROT_YFLIP;
> +	}
> +
> +	writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	struct gsc_frame *frame =&ctx->d_frame;
> +	u32 cfg;
> +
> +	if (!is_rgb(frame->fmt->color)) {
> +		gsc_dbg("Not a RGB format");
> +		return;
> +	}
> +
> +	cfg = readl(dev->regs + GSC_OUT_CON);
> +	cfg&= ~GSC_OUT_GLOBAL_ALPHA_MASK;
> +
> +	cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val);
> +	writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx)
> +{
> +	struct gsc_dev *dev = ctx->gsc_dev;
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + GSC_ENABLE);
> +	cfg |= GSC_ENABLE_SFR_UPDATE;
> +	writel(cfg, dev->regs + GSC_ENABLE);
> +}
...
> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
> index f79d0cc..5b4a33b 100644
> --- a/include/linux/videodev2.h
> +++ b/include/linux/videodev2.h
> @@ -360,9 +360,11 @@ struct v4l2_pix_format {
>   /* two non contiguous planes - one Y, one Cr + Cb interleaved  */
>   #define V4L2_PIX_FMT_NV12M   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
>   #define V4L2_PIX_FMT_NV12MT  v4l2_fourcc('T', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 64x32 macroblocks */
> +#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 16x16 macroblocks */
>
>   /* three non contiguous planes - Y, Cb, Cr */
>   #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
> +#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'V', 'U', 'M') /* 12  YVU420 planar */
>
>   /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
>   #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */

Please split videodev2.h changes into separate patches, adding new fourcc
and proper documentation to media DocBook.

It would be great if you could split this patch even further, it's 
tiresome to review so many lines of code at once. I had this review
half-finished for a few days before I just got it completed tonight,
and you posted another patch version in the meantime.

--

Thanks,
Sylwester
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sylwester Nawrocki July 11, 2012, 11:44 p.m. UTC | #3
On 07/12/2012 12:39 AM, Sylwester Nawrocki wrote:
>> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev)
>> +{
>> +	u32 cfg, status, bits = 0;
>> +
>> +	cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);

	return hweight32(cfg & GSC_IN_BASE_ADDR_MASK);

>> +	status = cfg&   GSC_IN_BASE_ADDR_MASK;
>> +	while (status) {
>> +		status = status&   (status - 1);
>> +		bits++;
>> +	}
>> +	return bits;
>> +}
[...]
>> +
>> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev)
>> +{
>> +	u32 bits = 0;
>> +	u32 mask_bits = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
>
> Care to add an empty line? It might also look better to order
> declarations in decending line order.
>
>> +	mask_bits&= GSC_OUT_BASE_ADDR_MASK;
>> +
>> +	while (mask_bits) {
>> +		mask_bits = mask_bits&   (mask_bits - 1);
>> +		bits++;
>> +	}

It seems you are computing a Hamming weight here, then it could
be simplified to a built-in function:

	bits = hweight32(mask_bits);

But it's not quite clear to me, why you're subtracting it
from 16 ?

>> +	bits = 16 - bits;
>
> return 16 - bits;
>
>> +
>> +	return bits;
>> +}
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sylwester Nawrocki July 12, 2012, 7:09 p.m. UTC | #4
Hi Shaik,

A few more remarks...

On 07/05/2012 12:27 PM, Shaik Ameer Basha wrote:
> diff --git a/drivers/media/video/exynos/gsc/gsc-core.c b/drivers/media/video/exynos/gsc/gsc-core.c
> new file mode 100644
> index 0000000..06bd24f
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-core.c
> @@ -0,0 +1,1304 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-core.c
> + *
> + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/types.h>
> +#include<linux/errno.h>
> +#include<linux/bug.h>
> +#include<linux/interrupt.h>
> +#include<linux/workqueue.h>
> +#include<linux/device.h>
> +#include<linux/platform_device.h>
> +#include<linux/list.h>
> +#include<linux/io.h>
> +#include<linux/slab.h>
> +#include<linux/clk.h>
> +#include<media/v4l2-ioctl.h>
> +#include<linux/of.h>
> +#include "gsc-core.h"
> +
> +#define GSC_CLOCK_GATE_NAME	"gscl"
> +
> +int gsc_dbg;
> +module_param(gsc_dbg, int, 0644);
> +
> +
> +static struct gsc_fmt gsc_formats[] = {

static const

> +	{
> +		.name		= "RGB565",
> +		.pixelformat	= V4L2_PIX_FMT_RGB565X,
> +		.depth		= { 16 },
> +		.color		= GSC_RGB,
> +		.num_planes	= 1,
> +		.num_comp	= 1,
> +	}, {
...
> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
> +{
> +	struct gsc_fmt *fmt, *def_fmt = NULL;
> +	unsigned int i;
> +
> +	if (index>= ARRAY_SIZE(gsc_formats))

	if (index >= (int)ARRAY_SIZE(gsc_formats))

(otherwise negative indexes won't work)

> +		return NULL;
> +
> +	for (i = 0; i<  ARRAY_SIZE(gsc_formats); ++i) {
> +		fmt = get_format(i);
> +		if (pixelformat&&  fmt->pixelformat == *pixelformat)
> +			return fmt;
> +		if (mbus_code&&  fmt->mbus_code == *mbus_code)
> +			return fmt;
> +		if (index == i)
> +			def_fmt = fmt;
> +	}
> +	return def_fmt;
> +
> +}
...
> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
> +{
> +	struct gsc_fmt *fmt;
> +
> +	fmt = find_fmt(NULL, NULL, f->index);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	strncpy(f->description, fmt->name, sizeof(f->description) - 1);

	strlcpy(f->description, fmt->name, sizeof(f->description));

> +	f->pixelformat = fmt->pixelformat;
> +
> +	return 0;
> +}
> +
> +u32 get_plane_size(struct gsc_frame *frame, unsigned int plane)
> +{
> +	if (!frame || plane>= frame->fmt->num_planes) {
> +		gsc_err("Invalid argument");
> +		return 0;
> +	}
> +
> +	return frame->payload[plane];
> +}
> +
> +u32 get_plane_info(struct gsc_frame frm, u32 addr, u32 *index)

Why don't you just pass a pointer to struct gsc_frame ?
It is so inefficient this way...

> +{
> +	if (frm.addr.y == addr) {
> +		*index = 0;
> +		return frm.addr.y;
> +	} else if (frm.addr.cb == addr) {
> +		*index = 1;
> +		return frm.addr.cb;
> +	} else if (frm.addr.cr == addr) {
> +		*index = 2;
> +		return frm.addr.cr;
> +	} else {
> +		gsc_err("Plane address is wrong");
> +		return -EINVAL;
> +	}
> +}
> +
> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm)

Ditto.

> +{
> +	u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
> +	f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
> +
> +	f_chk_addr = frm.addr.y;
> +	f_chk_len = frm.payload[0];
> +	if (frm.fmt->num_planes == 2) {
> +		s_chk_addr = frm.addr.cb;
> +		s_chk_len = frm.payload[1];
> +	} else if (frm.fmt->num_planes == 3) {
> +		u32 low_addr, low_plane, mid_addr, mid_plane;
> +		u32 high_addr, high_plane;
> +		u32 t_min, t_max;
> +
> +		t_min = min3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> +		low_addr = get_plane_info(frm, t_min,&low_plane);
> +		t_max = max3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> +		high_addr = get_plane_info(frm, t_max,&high_plane);
> +
> +		mid_plane = 3 - (low_plane + high_plane);
> +		if (mid_plane == 0)
> +			mid_addr = frm.addr.y;
> +		else if (mid_plane == 1)
> +			mid_addr = frm.addr.cb;
> +		else if (mid_plane == 2)
> +			mid_addr = frm.addr.cr;
> +		else
> +			return;
> +
> +		f_chk_addr = low_addr;
> +		if (mid_addr + frm.payload[mid_plane] - low_addr>
> +		    high_addr + frm.payload[high_plane] - mid_addr) {
> +			f_chk_len = frm.payload[low_plane];
> +			s_chk_addr = mid_addr;
> +			s_chk_len = high_addr +
> +					frm.payload[high_plane] - mid_addr;
> +		} else {
> +			f_chk_len = mid_addr +
> +					frm.payload[mid_plane] - low_addr;
> +			s_chk_addr = high_addr;
> +			s_chk_len = frm.payload[high_plane];
> +		}
> +	}
> +	gsc_dbg("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
> +		f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
> +}
> +
...
> +static int gsc_m2m_querycap(struct file *file, void *fh,
> +			   struct v4l2_capability *cap)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +
> +	strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);

strlcpy(cap->driver, gsc->pdev->name, sizeof(cap->driver));

> +	strncpy(cap->card, gsc->pdev->name, sizeof(cap->card) - 1);

strlcpy(cap->card, gsc->pdev->name, sizeof(cap->card));

> +	cap->bus_info[0] = 0;

strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));

> +	cap->capabilities = V4L2_CAP_STREAMING |
> +		V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;

Need to set device_caps as well:

 cap->device_caps = V4L2_CAP_STREAMING | 
		    V4L2_CAP_VIDEO_CAPTURE_MPLANE | 
		    V4L2_CAP_VIDEO_OUTPUT_MPLANE;

 cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;


Howewer this will probably need to be 

 cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;

as per http://www.mail-archive.com/linux-media@vger.kernel.org/msg48498.html
by the time this patch is to be merged (v3.7 ?).

> +
> +	return 0;
> +}
> +
[...]
> +static int gsc_m2m_g_selection(struct file *file, void *fh,
> +			struct v4l2_selection *s)
> +{
> +	struct gsc_frame *frame;
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> +	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)&&
> +	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +		return -EINVAL;
> +
> +	frame = ctx_get_frame(ctx, s->type);
> +	if (IS_ERR(frame))
> +		return PTR_ERR(frame);
> +
> +	switch (s->target) {
> +	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> +	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +		s->r.left = 0;
> +		s->r.top = 0;
> +		s->r.width = frame->f_width;
> +		s->r.height = frame->f_height;
> +		return 0;
> +
> +	case V4L2_SEL_TGT_COMPOSE_ACTIVE:
> +	case V4L2_SEL_TGT_CROP_ACTIVE:

Please use V4L2_SEL_TGT_CROP/COMPOSE instead of V4L2_SEL_TGT_CROP/COMPOSE_ACTIVE
which is being phased out.

http://git.linuxtv.org/media_tree.git/commit/c133482300113b3b71fa4a1fd2118531e765b36a

> +		s->r.left = frame->crop.left;
> +		s->r.top = frame->crop.top;
> +		s->r.width = frame->crop.width;
> +		s->r.height = frame->crop.height;
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int gsc_m2m_s_selection(struct file *file, void *fh,
> +			struct v4l2_selection *s)
> +{
> +	struct gsc_frame *frame;
> +	struct gsc_ctx *ctx = fh_to_ctx(fh);
> +	struct v4l2_crop cr;
> +	struct gsc_variant *variant = ctx->gsc_dev->variant;
> +	int ret;
> +
> +	cr.type = s->type;
> +	cr.c = s->r;
> +
> +	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)&&
> +	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +		return -EINVAL;
> +
> +	ret = gsc_try_crop(ctx,&cr);
> +	if (ret)
> +		return ret;
> +
> +	if (s->flags&  V4L2_SEL_FLAG_LE&&
> +	    !is_rectangle_enclosed(&cr.c,&s->r))
> +		return -ERANGE;
> +
> +	if (s->flags&  V4L2_SEL_FLAG_GE&&
> +	    !is_rectangle_enclosed(&s->r,&cr.c))
> +		return -ERANGE;
> +
> +	s->r = cr.c;
> +
> +	switch (s->target) {
> +	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> +	case V4L2_SEL_TGT_COMPOSE_ACTIVE:

V4L2_SEL_TGT_COMPOSE_ACTIVE -> V4L2_SEL_TGT_COMPOSE

> +		frame =&ctx->s_frame;
> +		break;
> +
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +	case V4L2_SEL_TGT_CROP_ACTIVE:

V4L2_SEL_TGT_CROP_ACTIVE -> V4L2_SEL_TGT_CROP

> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +		frame =&ctx->d_frame;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* Check to see if scaling ratio is within supported range */
> +	if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
> +		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +			ret = gsc_check_scaler_ratio(variant, cr.c.width,
> +				cr.c.height, ctx->d_frame.crop.width,
> +				ctx->d_frame.crop.height,
> +				ctx->gsc_ctrls.rotate->val, ctx->out_path);
> +		} else {
> +			ret = gsc_check_scaler_ratio(variant,
> +				ctx->s_frame.crop.width,
> +				ctx->s_frame.crop.height, cr.c.width,
> +				cr.c.height, ctx->gsc_ctrls.rotate->val,
> +				ctx->out_path);
> +		}
> +
> +		if (ret) {
> +			gsc_err("Out of scaler range");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	frame->crop = cr.c;
> +
> +	gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
> +	return 0;
> +}
[...]
> +static int gsc_m2m_open(struct file *file)
> +{
> +	struct gsc_dev *gsc = video_drvdata(file);
> +	struct gsc_ctx *ctx = NULL;
> +	int ret;
> +
> +	gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> +	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
> +	ret = gsc_ctrls_create(ctx);
> +	if (ret)
> +		goto error_fh;
> +
> +	/* Use separate control handler per file handle */
> +	ctx->fh.ctrl_handler =&ctx->ctrl_handler;
> +	file->private_data =&ctx->fh;
> +	v4l2_fh_add(&ctx->fh);
> +
> +	ctx->gsc_dev = gsc;
> +	/* Default color format */
> +	ctx->s_frame.fmt = get_format(0);
> +	ctx->d_frame.fmt = get_format(0);
> +	/* Setup the device context for mem2mem mode. */
> +	ctx->state |= GSC_CTX_M2M;
> +	ctx->flags = 0;
> +	ctx->in_path = GSC_DMA;
> +	ctx->out_path = GSC_DMA;
> +	spin_lock_init(&ctx->slock);
> +
> +	ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
> +	if (IS_ERR(ctx->m2m_ctx)) {
> +		gsc_err("Failed to initialize m2m context");
> +		ret = PTR_ERR(ctx->m2m_ctx);
> +		goto error_fh;
> +	}
> +
> +	if (gsc->m2m.refcnt++ == 0)
> +		set_bit(ST_M2M_OPEN,&gsc->state);
> +
> +	gsc_dbg("gsc m2m driver is opened, ctx(0x%p)", ctx);
> +	return 0;
> +
> +error_fh:
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +	kfree(ctx);
> +	return ret;
> +}
> +
> +static int gsc_m2m_release(struct file *file)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +	struct gsc_dev *gsc = ctx->gsc_dev;
> +
> +	gsc_dbg("pid: %d, state: 0x%lx, refcnt= %d",
> +		task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
> +
> +	v4l2_m2m_ctx_release(ctx->m2m_ctx);
> +	gsc_ctrls_delete(ctx);
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +
> +	if (--gsc->m2m.refcnt<= 0)
> +		clear_bit(ST_M2M_OPEN,&gsc->state);
> +	kfree(ctx);
> +	return 0;
> +}
> +
> +static unsigned int gsc_m2m_poll(struct file *file,
> +				     struct poll_table_struct *wait)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> +	return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
> +}
> +
> +static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> +	return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
> +}
> +static const struct v4l2_file_operations gsc_m2m_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= gsc_m2m_open,
> +	.release	= gsc_m2m_release,
> +	.poll		= gsc_m2m_poll,
> +	.unlocked_ioctl	= video_ioctl2,
> +	.mmap		= gsc_m2m_mmap,

There was a change recently in the v4l2 core modules and only 
.unlocked_ioctl is serialized with the core video device mutex.
All other file operations must be serialized explicitly in drivers.

For more details see:
http://git.infradead.org/users/kmpark/linux-samsung/commit/66ba3641dbc122c9c82f03aed9320f087730dd8f

[...]
> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c
> new file mode 100644
> index 0000000..7c5cce2
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-regs.c
> @@ -0,0 +1,579 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-regs.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/io.h>
> +#include<linux/delay.h>
> +#include<mach/map.h>
> +#include "gsc-core.h"
> +
> +void gsc_hw_set_sw_reset(struct gsc_dev *dev)
> +{
> +	u32 cfg = 0;
> +
> +	cfg |= GSC_SW_RESET_SRESET;
> +	writel(cfg, dev->regs + GSC_SW_RESET);
> +}

Probably worth to make it an inline function and move it to some 
header, e.g.

+static inline void gsc_hw_set_sw_reset(struct gsc_dev *dev)
+{
+	writel(GSC_SW_RESET_SRESET, dev->regs + GSC_SW_RESET);
+}

--

Thanks,
Sylwester
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Shaik Ameer Basha July 23, 2012, 6:18 a.m. UTC | #5
Hi Sylwester,

thanks for your review comments...
I will update the Gscaler driver code with the other two set of review
comments you posted earlier...

however, i have some doubts in this set of review comments.
please find my comments inline...

On Fri, Jul 13, 2012 at 12:39 AM, Sylwester Nawrocki
<sylvester.nawrocki@gmail.com> wrote:
> Hi Shaik,
>
> A few more remarks...
>
> On 07/05/2012 12:27 PM, Shaik Ameer Basha wrote:
>> diff --git a/drivers/media/video/exynos/gsc/gsc-core.c b/drivers/media/video/exynos/gsc/gsc-core.c
>> new file mode 100644
>> index 0000000..06bd24f
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/gsc-core.c
>> @@ -0,0 +1,1304 @@
>> +/* linux/drivers/media/video/exynos/gsc/gsc-core.c
>> + *
>> + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * Samsung EXYNOS5 SoC series G-scaler driver
>> + *
>> + * 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.
>> + */
>> +
>> +#include<linux/module.h>
>> +#include<linux/kernel.h>
>> +#include<linux/version.h>
>> +#include<linux/types.h>
>> +#include<linux/errno.h>
>> +#include<linux/bug.h>
>> +#include<linux/interrupt.h>
>> +#include<linux/workqueue.h>
>> +#include<linux/device.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/list.h>
>> +#include<linux/io.h>
>> +#include<linux/slab.h>
>> +#include<linux/clk.h>
>> +#include<media/v4l2-ioctl.h>
>> +#include<linux/of.h>
>> +#include "gsc-core.h"
>> +
>> +#define GSC_CLOCK_GATE_NAME  "gscl"
>> +
>> +int gsc_dbg;
>> +module_param(gsc_dbg, int, 0644);
>> +
>> +
>> +static struct gsc_fmt gsc_formats[] = {
>
> static const

OK. I will change that.

>
>> +     {
>> +             .name           = "RGB565",
>> +             .pixelformat    = V4L2_PIX_FMT_RGB565X,
>> +             .depth          = { 16 },
>> +             .color          = GSC_RGB,
>> +             .num_planes     = 1,
>> +             .num_comp       = 1,
>> +     }, {
> ...
>> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
>> +{
>> +     struct gsc_fmt *fmt, *def_fmt = NULL;
>> +     unsigned int i;
>> +
>> +     if (index>= ARRAY_SIZE(gsc_formats))
>
>         if (index >= (int)ARRAY_SIZE(gsc_formats))

changed the function prototype to take "u32 index"....

>
> (otherwise negative indexes won't work)
>
>> +             return NULL;
>> +
>> +     for (i = 0; i<  ARRAY_SIZE(gsc_formats); ++i) {
>> +             fmt = get_format(i);
>> +             if (pixelformat&&  fmt->pixelformat == *pixelformat)
>> +                     return fmt;
>> +             if (mbus_code&&  fmt->mbus_code == *mbus_code)
>> +                     return fmt;
>> +             if (index == i)
>> +                     def_fmt = fmt;
>> +     }
>> +     return def_fmt;
>> +
>> +}
> ...
>> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
>> +{
>> +     struct gsc_fmt *fmt;
>> +
>> +     fmt = find_fmt(NULL, NULL, f->index);
>> +     if (!fmt)
>> +             return -EINVAL;
>> +
>> +     strncpy(f->description, fmt->name, sizeof(f->description) - 1);
>
>         strlcpy(f->description, fmt->name, sizeof(f->description));
>

OK. I will change that.

>> +     f->pixelformat = fmt->pixelformat;
>> +
>> +     return 0;
>> +}
>> +
>> +u32 get_plane_size(struct gsc_frame *frame, unsigned int plane)
>> +{
>> +     if (!frame || plane>= frame->fmt->num_planes) {
>> +             gsc_err("Invalid argument");
>> +             return 0;
>> +     }
>> +
>> +     return frame->payload[plane];
>> +}
>> +
>> +u32 get_plane_info(struct gsc_frame frm, u32 addr, u32 *index)
>
> Why don't you just pass a pointer to struct gsc_frame ?
> It is so inefficient this way...
>

OK. I will change that.

>> +{
>> +     if (frm.addr.y == addr) {
>> +             *index = 0;
>> +             return frm.addr.y;
>> +     } else if (frm.addr.cb == addr) {
>> +             *index = 1;
>> +             return frm.addr.cb;
>> +     } else if (frm.addr.cr == addr) {
>> +             *index = 2;
>> +             return frm.addr.cr;
>> +     } else {
>> +             gsc_err("Plane address is wrong");
>> +             return -EINVAL;
>> +     }
>> +}
>> +
>> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm)
>
> Ditto.
>

OK. I will change that.

>> +{
>> +     u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
>> +     f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
>> +
>> +     f_chk_addr = frm.addr.y;
>> +     f_chk_len = frm.payload[0];
>> +     if (frm.fmt->num_planes == 2) {
>> +             s_chk_addr = frm.addr.cb;
>> +             s_chk_len = frm.payload[1];
>> +     } else if (frm.fmt->num_planes == 3) {
>> +             u32 low_addr, low_plane, mid_addr, mid_plane;
>> +             u32 high_addr, high_plane;
>> +             u32 t_min, t_max;
>> +
>> +             t_min = min3(frm.addr.y, frm.addr.cb, frm.addr.cr);
>> +             low_addr = get_plane_info(frm, t_min,&low_plane);
>> +             t_max = max3(frm.addr.y, frm.addr.cb, frm.addr.cr);
>> +             high_addr = get_plane_info(frm, t_max,&high_plane);
>> +
>> +             mid_plane = 3 - (low_plane + high_plane);
>> +             if (mid_plane == 0)
>> +                     mid_addr = frm.addr.y;
>> +             else if (mid_plane == 1)
>> +                     mid_addr = frm.addr.cb;
>> +             else if (mid_plane == 2)
>> +                     mid_addr = frm.addr.cr;
>> +             else
>> +                     return;
>> +
>> +             f_chk_addr = low_addr;
>> +             if (mid_addr + frm.payload[mid_plane] - low_addr>
>> +                 high_addr + frm.payload[high_plane] - mid_addr) {
>> +                     f_chk_len = frm.payload[low_plane];
>> +                     s_chk_addr = mid_addr;
>> +                     s_chk_len = high_addr +
>> +                                     frm.payload[high_plane] - mid_addr;
>> +             } else {
>> +                     f_chk_len = mid_addr +
>> +                                     frm.payload[mid_plane] - low_addr;
>> +                     s_chk_addr = high_addr;
>> +                     s_chk_len = frm.payload[high_plane];
>> +             }
>> +     }
>> +     gsc_dbg("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
>> +             f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
>> +}
>> +
> ...
>> +static int gsc_m2m_querycap(struct file *file, void *fh,
>> +                        struct v4l2_capability *cap)
>> +{
>> +     struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +     struct gsc_dev *gsc = ctx->gsc_dev;
>> +
>> +     strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
>
> strlcpy(cap->driver, gsc->pdev->name, sizeof(cap->driver));
>
>> +     strncpy(cap->card, gsc->pdev->name, sizeof(cap->card) - 1);
>
> strlcpy(cap->card, gsc->pdev->name, sizeof(cap->card));
>
>> +     cap->bus_info[0] = 0;
>
> strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
>
>> +     cap->capabilities = V4L2_CAP_STREAMING |
>> +             V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
>
> Need to set device_caps as well:
>
>  cap->device_caps = V4L2_CAP_STREAMING |
>                     V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>                     V4L2_CAP_VIDEO_OUTPUT_MPLANE;
>
>  cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
>

OK. I will change that.

>
> Howewer this will probably need to be
>
>  cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
>
> as per http://www.mail-archive.com/linux-media@vger.kernel.org/msg48498.html
> by the time this patch is to be merged (v3.7 ?).
>

currently my plan is to merge these changes in v3.6.
Isn't it possible ????


>> +
>> +     return 0;
>> +}
>> +
> [...]
>> +static int gsc_m2m_g_selection(struct file *file, void *fh,
>> +                     struct v4l2_selection *s)
>> +{
>> +     struct gsc_frame *frame;
>> +     struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +
>> +     if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)&&
>> +         (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>> +             return -EINVAL;
>> +
>> +     frame = ctx_get_frame(ctx, s->type);
>> +     if (IS_ERR(frame))
>> +             return PTR_ERR(frame);
>> +
>> +     switch (s->target) {
>> +     case V4L2_SEL_TGT_COMPOSE_DEFAULT:
>> +     case V4L2_SEL_TGT_COMPOSE_BOUNDS:
>> +     case V4L2_SEL_TGT_CROP_BOUNDS:
>> +     case V4L2_SEL_TGT_CROP_DEFAULT:
>> +             s->r.left = 0;
>> +             s->r.top = 0;
>> +             s->r.width = frame->f_width;
>> +             s->r.height = frame->f_height;
>> +             return 0;
>> +
>> +     case V4L2_SEL_TGT_COMPOSE_ACTIVE:
>> +     case V4L2_SEL_TGT_CROP_ACTIVE:
>
> Please use V4L2_SEL_TGT_CROP/COMPOSE instead of V4L2_SEL_TGT_CROP/COMPOSE_ACTIVE
> which is being phased out.
>
> http://git.linuxtv.org/media_tree.git/commit/c133482300113b3b71fa4a1fd2118531e765b36a
>

currently, I am rebasing the code on media-next git...
http://linuxtv.org/git/mchehab/media-next.git

I am not able to find the above changes mentioned in this git...
do you want me to rebase my code on top of the patch you provided ????

Or should i use any other git repository for rebasing my code ???


>> +             s->r.left = frame->crop.left;
>> +             s->r.top = frame->crop.top;
>> +             s->r.width = frame->crop.width;
>> +             s->r.height = frame->crop.height;
>> +             return 0;
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +
>> +static int gsc_m2m_s_selection(struct file *file, void *fh,
>> +                     struct v4l2_selection *s)
>> +{
>> +     struct gsc_frame *frame;
>> +     struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +     struct v4l2_crop cr;
>> +     struct gsc_variant *variant = ctx->gsc_dev->variant;
>> +     int ret;
>> +
>> +     cr.type = s->type;
>> +     cr.c = s->r;
>> +
>> +     if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)&&
>> +         (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>> +             return -EINVAL;
>> +
>> +     ret = gsc_try_crop(ctx,&cr);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (s->flags&  V4L2_SEL_FLAG_LE&&
>> +         !is_rectangle_enclosed(&cr.c,&s->r))
>> +             return -ERANGE;
>> +
>> +     if (s->flags&  V4L2_SEL_FLAG_GE&&
>> +         !is_rectangle_enclosed(&s->r,&cr.c))
>> +             return -ERANGE;
>> +
>> +     s->r = cr.c;
>> +
>> +     switch (s->target) {
>> +     case V4L2_SEL_TGT_COMPOSE_BOUNDS:
>> +     case V4L2_SEL_TGT_COMPOSE_DEFAULT:
>> +     case V4L2_SEL_TGT_COMPOSE_ACTIVE:
>
> V4L2_SEL_TGT_COMPOSE_ACTIVE -> V4L2_SEL_TGT_COMPOSE
>
>> +             frame =&ctx->s_frame;
>> +             break;
>> +
>> +     case V4L2_SEL_TGT_CROP_BOUNDS:
>> +     case V4L2_SEL_TGT_CROP_ACTIVE:
>
> V4L2_SEL_TGT_CROP_ACTIVE -> V4L2_SEL_TGT_CROP
>
>> +     case V4L2_SEL_TGT_CROP_DEFAULT:
>> +             frame =&ctx->d_frame;
>> +             break;
>> +
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Check to see if scaling ratio is within supported range */
>> +     if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
>> +             if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
>> +                     ret = gsc_check_scaler_ratio(variant, cr.c.width,
>> +                             cr.c.height, ctx->d_frame.crop.width,
>> +                             ctx->d_frame.crop.height,
>> +                             ctx->gsc_ctrls.rotate->val, ctx->out_path);
>> +             } else {
>> +                     ret = gsc_check_scaler_ratio(variant,
>> +                             ctx->s_frame.crop.width,
>> +                             ctx->s_frame.crop.height, cr.c.width,
>> +                             cr.c.height, ctx->gsc_ctrls.rotate->val,
>> +                             ctx->out_path);
>> +             }
>> +
>> +             if (ret) {
>> +                     gsc_err("Out of scaler range");
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     frame->crop = cr.c;
>> +
>> +     gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
>> +     return 0;
>> +}
> [...]
>> +static int gsc_m2m_open(struct file *file)
>> +{
>> +     struct gsc_dev *gsc = video_drvdata(file);
>> +     struct gsc_ctx *ctx = NULL;
>> +     int ret;
>> +
>> +     gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
>> +
>> +     ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
>> +     if (!ctx)
>> +             return -ENOMEM;
>> +
>> +     v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
>> +     ret = gsc_ctrls_create(ctx);
>> +     if (ret)
>> +             goto error_fh;
>> +
>> +     /* Use separate control handler per file handle */
>> +     ctx->fh.ctrl_handler =&ctx->ctrl_handler;
>> +     file->private_data =&ctx->fh;
>> +     v4l2_fh_add(&ctx->fh);
>> +
>> +     ctx->gsc_dev = gsc;
>> +     /* Default color format */
>> +     ctx->s_frame.fmt = get_format(0);
>> +     ctx->d_frame.fmt = get_format(0);
>> +     /* Setup the device context for mem2mem mode. */
>> +     ctx->state |= GSC_CTX_M2M;
>> +     ctx->flags = 0;
>> +     ctx->in_path = GSC_DMA;
>> +     ctx->out_path = GSC_DMA;
>> +     spin_lock_init(&ctx->slock);
>> +
>> +     ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
>> +     if (IS_ERR(ctx->m2m_ctx)) {
>> +             gsc_err("Failed to initialize m2m context");
>> +             ret = PTR_ERR(ctx->m2m_ctx);
>> +             goto error_fh;
>> +     }
>> +
>> +     if (gsc->m2m.refcnt++ == 0)
>> +             set_bit(ST_M2M_OPEN,&gsc->state);
>> +
>> +     gsc_dbg("gsc m2m driver is opened, ctx(0x%p)", ctx);
>> +     return 0;
>> +
>> +error_fh:
>> +     v4l2_fh_del(&ctx->fh);
>> +     v4l2_fh_exit(&ctx->fh);
>> +     kfree(ctx);
>> +     return ret;
>> +}
>> +
>> +static int gsc_m2m_release(struct file *file)
>> +{
>> +     struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
>> +     struct gsc_dev *gsc = ctx->gsc_dev;
>> +
>> +     gsc_dbg("pid: %d, state: 0x%lx, refcnt= %d",
>> +             task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
>> +
>> +     v4l2_m2m_ctx_release(ctx->m2m_ctx);
>> +     gsc_ctrls_delete(ctx);
>> +     v4l2_fh_del(&ctx->fh);
>> +     v4l2_fh_exit(&ctx->fh);
>> +
>> +     if (--gsc->m2m.refcnt<= 0)
>> +             clear_bit(ST_M2M_OPEN,&gsc->state);
>> +     kfree(ctx);
>> +     return 0;
>> +}
>> +
>> +static unsigned int gsc_m2m_poll(struct file *file,
>> +                                  struct poll_table_struct *wait)
>> +{
>> +     struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
>> +
>> +     return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
>> +}
>> +
>> +static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
>> +{
>> +     struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
>> +
>> +     return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
>> +}
>> +static const struct v4l2_file_operations gsc_m2m_fops = {
>> +     .owner          = THIS_MODULE,
>> +     .open           = gsc_m2m_open,
>> +     .release        = gsc_m2m_release,
>> +     .poll           = gsc_m2m_poll,
>> +     .unlocked_ioctl = video_ioctl2,
>> +     .mmap           = gsc_m2m_mmap,
>
> There was a change recently in the v4l2 core modules and only
> .unlocked_ioctl is serialized with the core video device mutex.
> All other file operations must be serialized explicitly in drivers.
>
> For more details see:
> http://git.infradead.org/users/kmpark/linux-samsung/commit/66ba3641dbc122c9c82f03aed9320f087730dd8f
>
> [...]
>> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c
>> new file mode 100644
>> index 0000000..7c5cce2
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/gsc-regs.c
>> @@ -0,0 +1,579 @@
>> +/* linux/drivers/media/video/exynos/gsc/gsc-regs.c
>> + *
>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * Samsung EXYNOS5 SoC series G-scaler driver
>> + *
>> + * 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.
>> + */
>> +
>> +#include<linux/io.h>
>> +#include<linux/delay.h>
>> +#include<mach/map.h>
>> +#include "gsc-core.h"
>> +
>> +void gsc_hw_set_sw_reset(struct gsc_dev *dev)
>> +{
>> +     u32 cfg = 0;
>> +
>> +     cfg |= GSC_SW_RESET_SRESET;
>> +     writel(cfg, dev->regs + GSC_SW_RESET);
>> +}
>
> Probably worth to make it an inline function and move it to some
> header, e.g.
>
> +static inline void gsc_hw_set_sw_reset(struct gsc_dev *dev)
> +{
> +       writel(GSC_SW_RESET_SRESET, dev->regs + GSC_SW_RESET);
> +}
>
> --
>
> Thanks,
> Sylwester


Thanks,
Shaik Ameer Basha
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sylwester Nawrocki July 23, 2012, 6:35 p.m. UTC | #6
Hi Shaik,

On 07/23/2012 08:18 AM, Shaik Ameer Basha wrote:
>>> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
>>> +{
>>> +     struct gsc_fmt *fmt, *def_fmt = NULL;
>>> +     unsigned int i;
>>> +
>>> +     if (index>= ARRAY_SIZE(gsc_formats))
>>
>>          if (index>= (int)ARRAY_SIZE(gsc_formats))
> 
> changed the function prototype to take "u32 index"....

Yeah, that's fine too, since you seem not to be passing negative values
as the third argument to this function.

>>
>> (otherwise negative indexes won't work)
>>
>>> +             return NULL;
>>> +
>>> +     for (i = 0; i<   ARRAY_SIZE(gsc_formats); ++i) {
>>> +             fmt = get_format(i);
>>> +             if (pixelformat&&   fmt->pixelformat == *pixelformat)
>>> +                     return fmt;
>>> +             if (mbus_code&&   fmt->mbus_code == *mbus_code)
>>> +                     return fmt;
>>> +             if (index == i)
>>> +                     def_fmt = fmt;
>>> +     }
>>> +     return def_fmt;
>>> +
>>> +}
...
>>> +     cap->capabilities = V4L2_CAP_STREAMING |
>>> +             V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
>>
>> Need to set device_caps as well:
>>
>>   cap->device_caps = V4L2_CAP_STREAMING |
>>                      V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>>                      V4L2_CAP_VIDEO_OUTPUT_MPLANE;
>>
>>   cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
>>
> 
> OK. I will change that.
> 
>>
>> Howewer this will probably need to be
>>
>>   cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
>>
>> as per http://www.mail-archive.com/linux-media@vger.kernel.org/msg48498.html
>> by the time this patch is to be merged (v3.7 ?).
>>
> 
> currently my plan is to merge these changes in v3.6.
> Isn't it possible ????

I presume it's still possible. It depends how quick you resolve all
issues pointed out during reviews. However v3.6 merge window has already
opened. So you should probably send the pull request this week at the 
latest. And don't worry too much about the new M2M capability flag.

However, there was previously a rule that everything for next release
must be pushed to the -next tree before a merge window opens.
If this is still in force then it's already too late.

I would suggest to address the review comments and to send a pull 
request to Mauro and let him decide if he pulls it for 3.6 or not.

Here is some more details regarding patch submitting rules:
http://linuxtv.org/wiki/index.php/Maintaining_Git_trees

>>> +     return 0;
>>> +}
>>> +
>> [...]
>>> +static int gsc_m2m_g_selection(struct file *file, void *fh,
>>> +                     struct v4l2_selection *s)
>>> +{
>>> +     struct gsc_frame *frame;
>>> +     struct gsc_ctx *ctx = fh_to_ctx(fh);
>>> +
>>> +     if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)&&
>>> +         (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>>> +             return -EINVAL;
>>> +
>>> +     frame = ctx_get_frame(ctx, s->type);
>>> +     if (IS_ERR(frame))
>>> +             return PTR_ERR(frame);
>>> +
>>> +     switch (s->target) {
>>> +     case V4L2_SEL_TGT_COMPOSE_DEFAULT:
>>> +     case V4L2_SEL_TGT_COMPOSE_BOUNDS:
>>> +     case V4L2_SEL_TGT_CROP_BOUNDS:
>>> +     case V4L2_SEL_TGT_CROP_DEFAULT:
>>> +             s->r.left = 0;
>>> +             s->r.top = 0;
>>> +             s->r.width = frame->f_width;
>>> +             s->r.height = frame->f_height;
>>> +             return 0;
>>> +
>>> +     case V4L2_SEL_TGT_COMPOSE_ACTIVE:
>>> +     case V4L2_SEL_TGT_CROP_ACTIVE:
>>
>> Please use V4L2_SEL_TGT_CROP/COMPOSE instead of V4L2_SEL_TGT_CROP/COMPOSE_ACTIVE
>> which is being phased out.
>>
>> http://git.linuxtv.org/media_tree.git/commit/c133482300113b3b71fa4a1fd2118531e765b36a
>>
> 
> currently, I am rebasing the code on media-next git...
> http://linuxtv.org/git/mchehab/media-next.git
> 
> I am not able to find the above changes mentioned in this git...
> do you want me to rebase my code on top of the patch you provided ????
> 
> Or should i use any other git repository for rebasing my code ???

The proper tree you should base your patches of off for next kernel
release is:

git://linuxtv.org/media_tree.git
branch: staging/for_v{N + 0.1}, where N is current kernel release.

So in your case staging/for_v3.6. You'll find there the changes 
I talked about previously and other patches queued up for v3.6.

More details can be found here:
http://git.linuxtv.org/media_tree.git

--

Regards,
Sylwester
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Shaik Ameer Basha July 24, 2012, 9:30 a.m. UTC | #7
Hi Pawel,

Thanks for the review comments.,
I will try to release the v3 patch in a day or two with your comments
addressed ...

please find my review comments inline...


On Wed, Jul 11, 2012 at 11:22 PM, Pawel Osciak <pawel@osciak.com> wrote:
> Hi Shaik,
> Thanks for the patches. Please find my comments inline.
> Please also make sure you run checkpatch.

I will make sure this...

>
> On Thu, Jul 5, 2012 at 3:27 AM, Shaik Ameer Basha
> <shaik.ameer@samsung.com> wrote:
>> This patch add support for gscaler (generic scaler) device which is a
>> new device for scaling and color space conversion on EXYNOS5 SoCs.
>>
>> This device supports the followings as key feature.
>>  1) Input image format
>>    - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, TILE
>>  2) Output image format
>>    - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, YUV444
>>  3) Input rotation
>>    - 0/90/180/270 degree, X/Y Flip
>>  4) Scale ratio
>>    - 1/16 scale down to 8 scale up
>>  5) CSC
>>    - RGB to YUV / YUV to RGB
>>  6) Size
>>    - 2048 x 2048 for tile or rotation
>>    - 4800 x 3344 other case
>>
>> Signed-off-by: Hynwoong Kim <khw0178.kim@samsung.com>
>> Signed-off-by: Sungchun Kang <sungchun.kang@samsung.com>
>> Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
>> ---
>>  drivers/media/video/Kconfig               |   10 +
>>  drivers/media/video/Makefile              |    1 +
>>  drivers/media/video/exynos/Kconfig        |   11 +
>>  drivers/media/video/exynos/Makefile       |    1 +
>>  drivers/media/video/exynos/gsc/Kconfig    |    7 +
>>  drivers/media/video/exynos/gsc/Makefile   |    3 +
>>  drivers/media/video/exynos/gsc/gsc-core.c | 1304 +++++++++++++++++++++++++++++
>>  drivers/media/video/exynos/gsc/gsc-core.h |  652 ++++++++++++++
>>  drivers/media/video/exynos/gsc/gsc-m2m.c  |  751 +++++++++++++++++
>>  drivers/media/video/exynos/gsc/gsc-regs.c |  579 +++++++++++++
>>  drivers/media/video/exynos/gsc/gsc-regs.h |  211 +++++
>>  include/linux/videodev2.h                 |    2 +
>>  12 files changed, 3532 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/media/video/exynos/Kconfig
>>  create mode 100644 drivers/media/video/exynos/Makefile
>>  create mode 100644 drivers/media/video/exynos/gsc/Kconfig
>>  create mode 100644 drivers/media/video/exynos/gsc/Makefile
>>  create mode 100644 drivers/media/video/exynos/gsc/gsc-core.c
>>  create mode 100644 drivers/media/video/exynos/gsc/gsc-core.h
>>  create mode 100644 drivers/media/video/exynos/gsc/gsc-m2m.c
>>  create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.c
>>  create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.h
>>
>> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
>> index 99937c9..0ff1abf 100644
>> --- a/drivers/media/video/Kconfig
>> +++ b/drivers/media/video/Kconfig
>> @@ -1153,6 +1153,7 @@ config VIDEO_ATMEL_ISI
>>
>>  source "drivers/media/video/s5p-fimc/Kconfig"
>>  source "drivers/media/video/s5p-tv/Kconfig"
>> +source "drivers/media/video/exynos/Kconfig"
>
> I believe we'd prefer to have it under exynos-gscaler directory and
> not create general platform directories.
>

OK. I will create "exynos-gsc" folder.

>>
>>  endif # V4L_PLATFORM_DRIVERS
>>  endif # VIDEO_CAPTURE_DRIVERS
>> @@ -1215,4 +1216,13 @@ config VIDEO_MX2_EMMAPRP
>>             memory to memory. Operations include resizing and format
>>             conversion.
>>
>> +config VIDEO_SAMSUNG_GSCALER
>> +        tristate "Samsung Exynos GSC driver"
>> +        depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
>> +        select VIDEOBUF2_DMA_CONTIG
>> +        select V4L2_MEM2MEM_DEV
>> +        select DMA_SHARED_BUFFER
>> +        help
>> +            This is v4l2 based g-scaler driver for EXYNOS5
>> +
>>  endif # V4L_MEM2MEM_DRIVERS
>> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
>> index d209de0..4389f3e 100644
>> --- a/drivers/media/video/Makefile
>> +++ b/drivers/media/video/Makefile
>> @@ -192,6 +192,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC)        += s5p-fimc/
>>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG)   += s5p-jpeg/
>>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)    += s5p-mfc/
>>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)     += s5p-tv/
>> +obj-$(CONFIG_VIDEO_EXYNOS)              += exynos/
>>
>>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D)    += s5p-g2d/
>>
>> diff --git a/drivers/media/video/exynos/Kconfig b/drivers/media/video/exynos/Kconfig
>> new file mode 100644
>> index 0000000..f5836a2
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/Kconfig
>> @@ -0,0 +1,11 @@
>> +config VIDEO_EXYNOS
>> +       bool "Exynos Multimedia Devices"
>> +       depends on ARCH_EXYNOS5
>> +       select VIDEO_FIXED_MINOR_RANGES
>> +       select VIDEOBUF2_DMA_CONTIG
>> +       help
>> +         This is a representative exynos multimedia device.
>> +
>> +if VIDEO_EXYNOS
>> +       source "drivers/media/video/exynos/gsc/Kconfig"
>> +endif
>
> Looks like you have two different Kconfigs and Makefiles for the same
> thing. Please don't create an exynos/gsc directories, just exynos-gsc
> and put everything in there. Don't create any Kconfigs, just add the
> entry to the drivers/media/video/Kconfig and only have a Makefile in
> exynos-gsc. s5p-g2d is a good example.
>
>> diff --git a/drivers/media/video/exynos/Makefile b/drivers/media/video/exynos/Makefile
>> new file mode 100644
>> index 0000000..1666724
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/Makefile
>> @@ -0,0 +1 @@
>> +obj-$(CONFIG_VIDEO_EXYNOS_GSCALER)      += gsc/
>> diff --git a/drivers/media/video/exynos/gsc/Kconfig b/drivers/media/video/exynos/gsc/Kconfig
>> new file mode 100644
>> index 0000000..9036078
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/Kconfig
>> @@ -0,0 +1,7 @@
>> +config VIDEO_EXYNOS_GSCALER
>> +       bool "Exynos G-Scaler driver"
>> +       depends on VIDEO_EXYNOS
>> +       select MEDIA_EXYNOS
>> +       select V4L2_MEM2MEM_DEV
>> +       help
>> +         This is a v4l2 driver for exynos G-Scaler device.
>> diff --git a/drivers/media/video/exynos/gsc/Makefile b/drivers/media/video/exynos/gsc/Makefile
>> new file mode 100644
>> index 0000000..d3f66e4
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/Makefile
>> @@ -0,0 +1,3 @@
>> +gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o
>> +
>> +obj-$(CONFIG_VIDEO_SAMSUNG_GSCALER)    += gsc.o
>> diff --git a/drivers/media/video/exynos/gsc/gsc-core.c b/drivers/media/video/exynos/gsc/gsc-core.c
>> new file mode 100644
>> index 0000000..06bd24f
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/gsc-core.c
>> @@ -0,0 +1,1304 @@
>> +/* linux/drivers/media/video/exynos/gsc/gsc-core.c
>
> No need for this line.

OK. I will remove that.

>
>> + *
>> + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
>> + *             http://www.samsung.com
>> + *
>> + * Samsung EXYNOS5 SoC series G-scaler driver
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/version.h>
>> +#include <linux/types.h>
>> +#include <linux/errno.h>
>> +#include <linux/bug.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/list.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/clk.h>
>> +#include <media/v4l2-ioctl.h>
>> +#include <linux/of.h>
>> +#include "gsc-core.h"
>> +
>> +#define GSC_CLOCK_GATE_NAME    "gscl"
>> +
>> +int gsc_dbg;
>> +module_param(gsc_dbg, int, 0644);
>> +
>> +
>> +static struct gsc_fmt gsc_formats[] = {
>> +       {
>> +               .name           = "RGB565",
>> +               .pixelformat    = V4L2_PIX_FMT_RGB565X,
>> +               .depth          = { 16 },
>> +               .color          = GSC_RGB,
>> +               .num_planes     = 1,
>> +               .num_comp       = 1,
>> +       }, {
>> +               .name           = "XRGB-8-8-8-8, 32 bpp",
>> +               .pixelformat    = V4L2_PIX_FMT_RGB32,
>> +               .depth          = { 32 },
>> +               .color          = GSC_RGB,
>> +               .num_planes     = 1,
>> +               .num_comp       = 1,
>> +       }, {
>> +               .name           = "YUV 4:2:2 packed, YCbYCr",
>> +               .pixelformat    = V4L2_PIX_FMT_YUYV,
>> +               .depth          = { 16 },
>> +               .color          = GSC_YUV422,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 1,
>> +               .num_comp       = 1,
>> +               .mbus_code      = V4L2_MBUS_FMT_YUYV8_2X8,
>> +       }, {
>> +               .name           = "YUV 4:2:2 packed, CbYCrY",
>> +               .pixelformat    = V4L2_PIX_FMT_UYVY,
>> +               .depth          = { 16 },
>> +               .color          = GSC_YUV422,
>> +               .yorder         = GSC_LSB_C,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 1,
>> +               .num_comp       = 1,
>> +               .mbus_code      = V4L2_MBUS_FMT_UYVY8_2X8,
>> +       }, {
>> +               .name           = "YUV 4:2:2 packed, CrYCbY",
>> +               .pixelformat    = V4L2_PIX_FMT_VYUY,
>> +               .depth          = { 16 },
>> +               .color          = GSC_YUV422,
>> +               .yorder         = GSC_LSB_C,
>> +               .corder         = GSC_CRCB,
>> +               .num_planes     = 1,
>> +               .num_comp       = 1,
>> +               .mbus_code      = V4L2_MBUS_FMT_VYUY8_2X8,
>> +       }, {
>> +               .name           = "YUV 4:2:2 packed, YCrYCb",
>> +               .pixelformat    = V4L2_PIX_FMT_YVYU,
>> +               .depth          = { 16 },
>> +               .color          = GSC_YUV422,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CRCB,
>> +               .num_planes     = 1,
>> +               .num_comp       = 1,
>> +               .mbus_code      = V4L2_MBUS_FMT_YVYU8_2X8,
>> +       }, {
>> +               .name           = "YUV 4:4:4 planar, YCbYCr",
>> +               .pixelformat    = V4L2_PIX_FMT_YUV32,
>> +               .depth          = { 32 },
>> +               .color          = GSC_YUV444,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 1,
>> +               .num_comp       = 1,
>> +       }, {
>> +               .name           = "YUV 4:2:2 planar, Y/Cb/Cr",
>> +               .pixelformat    = V4L2_PIX_FMT_YUV422P,
>> +               .depth          = { 16 },
>> +               .color          = GSC_YUV422,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 1,
>> +               .num_comp       = 3,
>> +       }, {
>> +               .name           = "YUV 4:2:2 planar, Y/CbCr",
>> +               .pixelformat    = V4L2_PIX_FMT_NV16,
>> +               .depth          = { 16 },
>> +               .color          = GSC_YUV422,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 1,
>> +               .num_comp       = 2,
>> +       }, {
>> +               .name           = "YUV 4:2:2 planar, Y/CrCb",
>> +               .pixelformat    = V4L2_PIX_FMT_NV61,
>> +               .depth          = { 16 },
>> +               .color          = GSC_YUV422,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CRCB,
>> +               .num_planes     = 1,
>> +               .num_comp       = 2,
>> +       }, {
>> +               .name           = "YUV 4:2:0 planar, YCbCr",
>> +               .pixelformat    = V4L2_PIX_FMT_YUV420,
>> +               .depth          = { 12 },
>> +               .color          = GSC_YUV420,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 1,
>> +               .num_comp       = 3,
>> +       }, {
>> +               .name           = "YUV 4:2:0 planar, YCbCr",
>
> Same name as above, should be YCrCb?

Ah... I will change that.

>
>> +               .pixelformat    = V4L2_PIX_FMT_YVU420,
>> +               .depth          = { 12 },
>> +               .color          = GSC_YUV420,
>
> Is this a copy-paste bug or should be the same as above?

".color" remains same, the VU order is taken care under ".corder" field

>
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CRCB,
>> +               .num_planes     = 1,
>> +               .num_comp       = 3,
>> +
>> +       }, {
>> +               .name           = "YUV 4:2:0 planar, Y/CbCr",
>> +               .pixelformat    = V4L2_PIX_FMT_NV12,
>> +               .depth          = { 12 },
>> +               .color          = GSC_YUV420,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 1,
>> +               .num_comp       = 2,
>> +       }, {
>> +               .name           = "YUV 4:2:0 planar, Y/CrCb",
>> +               .pixelformat    = V4L2_PIX_FMT_NV21,
>> +               .depth          = { 12 },
>> +               .color          = GSC_YUV420,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CRCB,
>> +               .num_planes     = 1,
>> +               .num_comp       = 2,
>> +       }, {
>> +               .name           = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
>> +               .pixelformat    = V4L2_PIX_FMT_NV12M,
>> +               .depth          = { 8, 4 },
>> +               .color          = GSC_YUV420,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 2,
>> +               .num_comp       = 2,
>> +       }, {
>> +               .name           = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
>> +               .pixelformat    = V4L2_PIX_FMT_YUV420M,
>> +               .depth          = { 8, 2, 2 },
>> +               .color          = GSC_YUV420,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 3,
>> +               .num_comp       = 3,
>> +       }, {
>> +               .name           = "YUV 4:2:0 non-contiguous 3-planar, Y/Cr/Cb",
>
> YUV -> YVU?

Its same.. YUV. as you can see, the plane order is changed in the name (Y/Cr/Cb)

>
>> +               .pixelformat    = V4L2_PIX_FMT_YVU420M,
>> +               .depth          = { 8, 2, 2 },
>> +               .color          = GSC_YUV420,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CRCB,
>> +               .num_planes     = 3,
>> +               .num_comp       = 3,
>> +       }, {
>> +               .name           =
>> +                       "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",
>> +               .pixelformat    = V4L2_PIX_FMT_NV12MT_16X16,
>> +               .depth          = { 8, 4 },
>> +               .color          = GSC_YUV420,
>> +               .yorder         = GSC_LSB_Y,
>> +               .corder         = GSC_CBCR,
>> +               .num_planes     = 2,
>> +               .num_comp       = 2,
>> +       },
>> +};
>> +
>> +struct gsc_fmt *get_format(int index)
>> +{
>
> if (index >= ARRAY_SIZE(gsc_formats))
>          return NULL;
>

Ok. I will change that.

>> +       return &gsc_formats[index];
>> +}
>> +
>> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
>> +{
>> +       struct gsc_fmt *fmt, *def_fmt = NULL;
>> +       unsigned int i;
>> +
>> +       if (index >= ARRAY_SIZE(gsc_formats))
>> +               return NULL;
>> +
>> +       for (i = 0; i < ARRAY_SIZE(gsc_formats); ++i) {
>> +               fmt = get_format(i);
>> +               if (pixelformat && fmt->pixelformat == *pixelformat)
>> +                       return fmt;
>> +               if (mbus_code && fmt->mbus_code == *mbus_code)
>> +                       return fmt;
>> +               if (index == i)
>> +                       def_fmt = fmt;
>> +       }
>> +       return def_fmt;
>> +
>> +}
>> +
>> +void gsc_set_frame_size(struct gsc_frame *frame, int width, int height)
>> +{
>> +       frame->f_width  = width;
>> +       frame->f_height = height;
>> +       frame->crop.width = width;
>> +       frame->crop.height = height;
>> +       frame->crop.left = 0;
>> +       frame->crop.top = 0;
>> +}
>> +
>> +int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
>> +                                                               u32 *ratio)
>> +{
>> +       if ((dst > src) || (dst >= src / var->poly_sc_down_max)) {
>> +               *ratio = 1;
>> +               return 0;
>> +       }
>> +
>> +       if ((src / var->poly_sc_down_max / var->pre_sc_down_max) > dst) {
>> +               gsc_err("scale ratio exceeded maximun scale down ratio(1/16)");
>> +               return -EINVAL;
>> +       }
>> +
>> +       *ratio = (dst > (src / 8)) ? 2 : 4;
>> +
>> +       return 0;
>> +}
>> +
>> +void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh)
>> +{
>> +       if (hratio == 4 && vratio == 4)
>> +               *sh = 4;
>> +       else if ((hratio == 4 && vratio == 2) ||
>> +                (hratio == 2 && vratio == 4))
>> +               *sh = 3;
>> +       else if ((hratio == 4 && vratio == 1) ||
>> +                (hratio == 1 && vratio == 4) ||
>> +                (hratio == 2 && vratio == 2))
>> +               *sh = 2;
>> +       else if (hratio == 1 && vratio == 1)
>> +               *sh = 0;
>> +       else
>> +               *sh = 1;
>> +}
>> +
>> +void gsc_check_src_scale_info(struct gsc_variant *var,
>> +                               struct gsc_frame *s_frame, u32 *wratio,
>> +                                u32 tx, u32 ty, u32 *hratio)
>> +{
>> +       int remainder = 0, walign, halign;
>> +
>> +       if (is_yuv420(s_frame->fmt->color)) {
>> +               walign = GSC_SC_ALIGN_4;
>> +               halign = GSC_SC_ALIGN_4;
>> +       } else if (is_yuv422(s_frame->fmt->color)) {
>> +               walign = GSC_SC_ALIGN_4;
>> +               halign = GSC_SC_ALIGN_2;
>> +       } else {
>> +               walign = GSC_SC_ALIGN_2;
>> +               halign = GSC_SC_ALIGN_2;
>> +       }
>> +
>> +       remainder = s_frame->crop.width % (*wratio * walign);
>> +       if (remainder) {
>> +               s_frame->crop.width -= remainder;
>> +               gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio);
>> +               gsc_info("cropped src width size is recalculated from %d to %d",
>> +                       s_frame->crop.width + remainder, s_frame->crop.width);
>> +       }
>> +
>> +       remainder = s_frame->crop.height % (*hratio * halign);
>> +       if (remainder) {
>> +               s_frame->crop.height -= remainder;
>> +               gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio);
>> +               gsc_info("cropped src height size is recalculated from %d to %d",
>> +                       s_frame->crop.height + remainder, s_frame->crop.height);
>> +       }
>> +}
>> +
>> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
>> +{
>> +       struct gsc_fmt *fmt;
>> +
>> +       fmt = find_fmt(NULL, NULL, f->index);
>> +       if (!fmt)
>> +               return -EINVAL;
>> +
>> +       strncpy(f->description, fmt->name, sizeof(f->description) - 1);
>> +       f->pixelformat = fmt->pixelformat;
>> +
>> +       return 0;
>> +}
>> +
>> +u32 get_plane_size(struct gsc_frame *frame, unsigned int plane)
>> +{
>> +       if (!frame || plane >= frame->fmt->num_planes) {
>> +               gsc_err("Invalid argument");
>> +               return 0;
>> +       }
>> +
>> +       return frame->payload[plane];
>> +}
>> +
>> +u32 get_plane_info(struct gsc_frame frm, u32 addr, u32 *index)
>> +{
>> +       if (frm.addr.y == addr) {
>> +               *index = 0;
>> +               return frm.addr.y;
>> +       } else if (frm.addr.cb == addr) {
>> +               *index = 1;
>> +               return frm.addr.cb;
>> +       } else if (frm.addr.cr == addr) {
>> +               *index = 2;
>> +               return frm.addr.cr;
>> +       } else {
>> +               gsc_err("Plane address is wrong");
>> +               return -EINVAL;
>> +       }
>> +}
>> +
>> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm)
>> +{
>> +       u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
>> +       f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
>> +
>> +       f_chk_addr = frm.addr.y;
>> +       f_chk_len = frm.payload[0];
>> +       if (frm.fmt->num_planes == 2) {
>> +               s_chk_addr = frm.addr.cb;
>> +               s_chk_len = frm.payload[1];
>> +       } else if (frm.fmt->num_planes == 3) {
>> +               u32 low_addr, low_plane, mid_addr, mid_plane;
>> +               u32 high_addr, high_plane;
>> +               u32 t_min, t_max;
>> +
>> +               t_min = min3(frm.addr.y, frm.addr.cb, frm.addr.cr);
>> +               low_addr = get_plane_info(frm, t_min, &low_plane);
>> +               t_max = max3(frm.addr.y, frm.addr.cb, frm.addr.cr);
>> +               high_addr = get_plane_info(frm, t_max, &high_plane);
>> +
>> +               mid_plane = 3 - (low_plane + high_plane);
>> +               if (mid_plane == 0)
>> +                       mid_addr = frm.addr.y;
>> +               else if (mid_plane == 1)
>> +                       mid_addr = frm.addr.cb;
>> +               else if (mid_plane == 2)
>> +                       mid_addr = frm.addr.cr;
>> +               else
>> +                       return;
>> +
>> +               f_chk_addr = low_addr;
>> +               if (mid_addr + frm.payload[mid_plane] - low_addr >
>> +                   high_addr + frm.payload[high_plane] - mid_addr) {
>> +                       f_chk_len = frm.payload[low_plane];
>> +                       s_chk_addr = mid_addr;
>> +                       s_chk_len = high_addr +
>> +                                       frm.payload[high_plane] - mid_addr;
>> +               } else {
>> +                       f_chk_len = mid_addr +
>> +                                       frm.payload[mid_plane] - low_addr;
>> +                       s_chk_addr = high_addr;
>> +                       s_chk_len = frm.payload[high_plane];
>> +               }
>> +       }
>> +       gsc_dbg("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
>> +               f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
>> +}
>> +
>> +int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
>> +{
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +       struct gsc_variant *variant = gsc->variant;
>> +       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
>> +       struct gsc_fmt *fmt;
>> +       u32 max_w, max_h, mod_x, mod_y;
>> +       u32 min_w, min_h, tmp_w, tmp_h;
>> +       int i;
>> +
>> +       gsc_dbg("user put w: %d, h: %d", pix_mp->width, pix_mp->height);
>> +
>> +       fmt = find_fmt(&pix_mp->pixelformat, NULL, 0);
>> +       if (!fmt) {
>> +               gsc_err("pixelformat format (0x%X) invalid\n",
>> +                                               pix_mp->pixelformat);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (pix_mp->field == V4L2_FIELD_ANY)
>> +               pix_mp->field = V4L2_FIELD_NONE;
>> +       else if (pix_mp->field != V4L2_FIELD_NONE) {
>> +               gsc_err("Not supported field order(%d)\n", pix_mp->field);
>> +               return -EINVAL;
>> +       }
>> +
>> +       max_w = variant->pix_max->target_rot_dis_w;
>> +       max_h = variant->pix_max->target_rot_dis_h;
>> +       if (V4L2_TYPE_IS_OUTPUT(f->type)) {
>> +               mod_x = ffs(variant->pix_align->org_w) - 1;
>> +               if (is_yuv420(fmt->color))
>> +                       mod_y = ffs(variant->pix_align->org_h) - 1;
>> +               else
>> +                       mod_y = ffs(variant->pix_align->org_h) - 2;
>> +               min_w = variant->pix_min->org_w;
>> +               min_h = variant->pix_min->org_h;
>> +       } else {
>> +               mod_x = ffs(variant->pix_align->org_w) - 1;
>> +               if (is_yuv420(fmt->color))
>> +                       mod_y = ffs(variant->pix_align->org_h) - 1;
>> +               else
>> +                       mod_y = ffs(variant->pix_align->org_h) - 2;
>> +               min_w = variant->pix_min->target_rot_dis_w;
>> +               min_h = variant->pix_min->target_rot_dis_h;
>> +       }
>
> Looks to me like only the last 2 lines in both branches are different,
> so the first 5 could be abstracted out.

Ok. I will change that.

>
>> +       gsc_dbg("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
>> +            mod_x, mod_y, max_w, max_h);
>> +       /* To check if image size is modified to adjust parameter against
>> +          hardware abilities */
>> +       tmp_w = pix_mp->width;
>> +       tmp_h = pix_mp->height;
>> +
>> +       v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
>> +               &pix_mp->height, min_h, max_h, mod_y, 0);
>> +       if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
>> +               gsc_info("Image size has been modified from %dx%d to %dx%d",
>> +                        tmp_w, tmp_h, pix_mp->width, pix_mp->height);
>> +
>> +       pix_mp->num_planes = fmt->num_planes;
>> +
>> +       if (ctx->gsc_ctrls.csc_eq_mode->val)
>> +               ctx->gsc_ctrls.csc_eq->val =
>> +                       (pix_mp->width >= 1280) ? 1 : 0;
>> +       if (ctx->gsc_ctrls.csc_eq->val) /* HD */
>> +               pix_mp->colorspace = V4L2_COLORSPACE_REC709;
>> +       else    /* SD */
>> +               pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
>> +
>> +
>> +       for (i = 0; i < pix_mp->num_planes; ++i) {
>> +               int bpl = (pix_mp->width * fmt->depth[i]) >> 3;
>> +               pix_mp->plane_fmt[i].bytesperline = bpl;
>> +               pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
>> +
>> +               gsc_dbg("[%d]: bpl: %d, sizeimage: %d",
>> +                   i, bpl, pix_mp->plane_fmt[i].sizeimage);
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
>> +{
>> +       struct gsc_frame *frame;
>> +       struct v4l2_pix_format_mplane *pix_mp;
>> +       int i;
>> +
>> +       frame = ctx_get_frame(ctx, f->type);
>> +       if (IS_ERR(frame))
>> +               return PTR_ERR(frame);
>> +
>> +       pix_mp = &f->fmt.pix_mp;
>> +
>> +       pix_mp->width           = frame->f_width;
>> +       pix_mp->height          = frame->f_height;
>> +       pix_mp->field           = V4L2_FIELD_NONE;
>> +       pix_mp->pixelformat     = frame->fmt->pixelformat;
>> +       pix_mp->colorspace      = V4L2_COLORSPACE_JPEG;
>
> I don't know the right answer, but why JPEG not SRGB?

Gscaler supports RGB Types
RGB601 (SD, 16 ~ 235)
RGB601 (SD, 0 ~ 255)
RGB709 (HD, 16 ~ 235)
RGB709 (HD, 0 ~ 255)

so i will change this field to either V4L2_COLORSPACE_REC709 or
V4L2_COLORSPACE_SMPTE170M as default.
Is it fine ???

>
>> +       pix_mp->num_planes      = frame->fmt->num_planes;
>> +
>> +       for (i = 0; i < pix_mp->num_planes; ++i) {
>> +               pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
>> +                       frame->fmt->depth[i]) / 8;
>> +               pix_mp->plane_fmt[i].sizeimage =
>> +                        pix_mp->plane_fmt[i].bytesperline * frame->f_height;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
>> +{
>> +       if (tmp_w != *w || tmp_h != *h) {
>> +               gsc_info("Cropped size has been modified from %dx%d to %dx%d",
>> +                                                       *w, *h, tmp_w, tmp_h);
>> +               *w = tmp_w;
>> +               *h = tmp_h;
>> +       }
>> +}
>> +
>> +int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
>> +{
>> +       struct gsc_frame *frame;
>> +
>> +       frame = ctx_get_frame(ctx, cr->type);
>> +       if (IS_ERR(frame))
>> +               return PTR_ERR(frame);
>> +
>> +       cr->c = frame->crop;
>> +
>> +       return 0;
>> +}
>> +
>> +int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
>> +{
>> +       struct gsc_frame *f;
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +       struct gsc_variant *variant = gsc->variant;
>> +       u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h;
>> +       u32 min_w, min_h, max_w, max_h;
>> +
>> +       if (cr->c.top < 0 || cr->c.left < 0) {
>> +               gsc_err("doesn't support negative values for top & left\n");
>> +               return -EINVAL;
>> +       }
>> +       gsc_dbg("user put w: %d, h: %d", cr->c.width, cr->c.height);
>> +
>> +       if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
>> +               f = &ctx->d_frame;
>> +       else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
>> +               f = &ctx->s_frame;
>> +       else
>> +               return -EINVAL;
>> +
>> +       max_w = f->f_width;
>> +       max_h = f->f_height;
>> +       tmp_w = cr->c.width;
>> +       tmp_h = cr->c.height;
>> +
>> +       if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
>> +               if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 1) ||
>> +                   is_rgb(f->fmt->color))
>> +                       min_w = 32;
>> +               else
>> +                       min_w = 64;
>> +               if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 3) ||
>> +                   is_yuv420(f->fmt->color))
>> +                       min_h = 32;
>> +               else
>> +                       min_h = 16;
>> +       } else {
>> +               if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
>> +                       mod_x = ffs(variant->pix_align->target_w) - 1;
>> +               if (is_yuv420(f->fmt->color))
>> +                       mod_y = ffs(variant->pix_align->target_h) - 1;
>> +               if (ctx->gsc_ctrls.rotate->val == 90 ||
>> +                   ctx->gsc_ctrls.rotate->val == 270) {
>> +                       max_w = f->f_height;
>> +                       max_h = f->f_width;
>> +                       min_w = variant->pix_min->target_rot_en_w;
>> +                       min_h = variant->pix_min->target_rot_en_h;
>> +                       tmp_w = cr->c.height;
>> +                       tmp_h = cr->c.width;
>> +               } else {
>> +                       min_w = variant->pix_min->target_rot_dis_w;
>> +                       min_h = variant->pix_min->target_rot_dis_h;
>> +               }
>> +       }
>> +       gsc_dbg("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d",
>> +                                       mod_x, mod_y, min_w, min_h);
>> +       gsc_dbg("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h);
>> +
>> +       v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
>> +                             &tmp_h, min_h, max_h, mod_y, 0);
>> +
>> +       if (!V4L2_TYPE_IS_OUTPUT(cr->type) &&
>> +           (ctx->gsc_ctrls.rotate->val == 90 ||
>> +           ctx->gsc_ctrls.rotate->val == 270)) {
>> +               gsc_check_crop_change(tmp_h, tmp_w, &cr->c.width,
>> +                                                       &cr->c.height);
>> +       } else {
>> +               gsc_check_crop_change(tmp_w, tmp_h, &cr->c.width,
>> +                                                       &cr->c.height);
>> +       }
>
> No need for braces.
>

Ok. I will change this.

>> +
>> +       /* adjust left/top if cropping rectangle is out of bounds */
>> +       /* Need to add code to algin left value with 2's multiple */
>> +       if (cr->c.left + tmp_w > max_w)
>> +               cr->c.left = max_w - tmp_w;
>> +       if (cr->c.top + tmp_h > max_h)
>> +               cr->c.top = max_h - tmp_h;
>> +
>> +       if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
>> +               if (cr->c.left % 2)
>> +                       cr->c.left -= 1;
>> +
>> +       gsc_dbg("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
>> +           cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
>> +
>> +       return 0;
>> +}
>> +
>> +int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
>> +                          int dh, int rot, int out_path)
>> +{
>> +       int tmp_w, tmp_h, sc_down_max;
>> +       sc_down_max =
>> +               (out_path == GSC_DMA) ? var->sc_down_max : var->local_sc_down;
>> +
>> +       if (rot == 90 || rot == 270) {
>> +               tmp_w = dh;
>> +               tmp_h = dw;
>> +       } else {
>> +               tmp_w = dw;
>> +               tmp_h = dh;
>> +       }
>> +
>> +       if ((sw / tmp_w) > sc_down_max ||
>> +           (sh / tmp_h) > sc_down_max ||
>> +           (tmp_w / sw) > var->sc_up_max ||
>> +           (tmp_h / sh) > var->sc_up_max)
>> +               return -EINVAL;
>> +
>> +       return 0;
>> +}
>> +
>> +int gsc_set_scaler_info(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_scaler *sc = &ctx->scaler;
>> +       struct gsc_frame *s_frame = &ctx->s_frame;
>> +       struct gsc_frame *d_frame = &ctx->d_frame;
>> +       struct gsc_variant *variant = ctx->gsc_dev->variant;
>> +       int tx, ty;
>> +       int ret;
>> +
>> +       ret = gsc_check_scaler_ratio(variant, s_frame->crop.width,
>> +               s_frame->crop.height, d_frame->crop.width, d_frame->crop.height,
>> +               ctx->gsc_ctrls.rotate->val, ctx->out_path);
>> +       if (ret) {
>> +               gsc_err("out of scaler range");
>> +               return ret;
>> +       }
>> +
>> +       if (ctx->gsc_ctrls.rotate->val == 90 ||
>> +           ctx->gsc_ctrls.rotate->val == 270) {
>> +               ty = d_frame->crop.width;
>> +               tx = d_frame->crop.height;
>> +       } else {
>> +               tx = d_frame->crop.width;
>> +               ty = d_frame->crop.height;
>> +       }
>> +
>> +       ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width,
>> +                                     tx, &sc->pre_hratio);
>> +       if (ret) {
>> +               gsc_err("Horizontal scale ratio is out of range");
>> +               return ret;
>> +       }
>> +
>> +       ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height,
>> +                                     ty, &sc->pre_vratio);
>> +       if (ret) {
>> +               gsc_err("Vertical scale ratio is out of range");
>> +               return ret;
>> +       }
>> +
>> +       gsc_check_src_scale_info(variant, s_frame, &sc->pre_hratio,
>> +                                tx, ty, &sc->pre_vratio);
>> +
>> +       gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
>> +                                  &sc->pre_shfactor);
>> +
>> +       sc->main_hratio = (s_frame->crop.width << 16) / tx;
>> +       sc->main_vratio = (s_frame->crop.height << 16) / ty;
>> +
>
> Would be nice to ensure tx and ty were not zeros, unless it's done somewhere
> else.
>

Ok. I will check for zero parameters.

>> +       gsc_dbg("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d",
>> +                       s_frame->crop.width, s_frame->crop.height, tx, ty);
>> +       gsc_dbg("scaler ratio info : pre_shfactor : %d, pre_h : %d",
>> +                       sc->pre_shfactor, sc->pre_hratio);
>> +       gsc_dbg("pre_v :%d, main_h : %ld, main_v : %ld",
>> +                       sc->pre_vratio, sc->main_hratio, sc->main_vratio);
>> +
>> +       return 0;
>> +}
>> +
>> +
>> +/*
>> + * V4L2 controls handling
>> + */
>> +static int gsc_s_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +       struct gsc_ctx *ctx = ctrl_to_ctx(ctrl);
>> +
>> +       switch (ctrl->id) {
>> +       case V4L2_CID_HFLIP:
>> +               user_to_drv(ctx->gsc_ctrls.hflip, ctrl->val);
>> +               break;
>> +
>> +       case V4L2_CID_VFLIP:
>> +               user_to_drv(ctx->gsc_ctrls.vflip, ctrl->val);
>> +               break;
>> +
>> +       case V4L2_CID_ROTATE:
>> +               user_to_drv(ctx->gsc_ctrls.rotate, ctrl->val);
>> +               break;
>> +
>> +       default:
>> +               break;
>> +       }
>> +
>> +       if (gsc_m2m_opened(ctx->gsc_dev))
>> +               gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
>> +
>> +       return 0;
>> +}
>> +
>> +const struct v4l2_ctrl_ops gsc_ctrl_ops = {
>> +       .s_ctrl = gsc_s_ctrl,
>> +};
>> +
>> +static const struct v4l2_ctrl_config gsc_custom_ctrl[] = {
>> +       {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_GLOBAL_ALPHA,
>> +               .name = "Set RGB alpha",
>> +               .type = V4L2_CTRL_TYPE_INTEGER,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +               .max = 255,
>> +               .step = 1,
>> +               .def = 0,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_CACHEABLE,
>> +               .name = "Set cacheable",
>> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +               .max = 1,
>> +               .def = true,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_TV_LAYER_BLEND_ENABLE,
>> +               .name = "Enable layer alpha blending",
>> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_TV_LAYER_BLEND_ALPHA,
>> +               .name = "Set alpha for layer blending",
>> +               .type = V4L2_CTRL_TYPE_INTEGER,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +               .min = 0,
>> +               .max = 255,
>> +               .step = 1,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_TV_PIXEL_BLEND_ENABLE,
>> +               .name = "Enable pixel alpha blending",
>> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_TV_CHROMA_ENABLE,
>> +               .name = "Enable chromakey",
>> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_TV_CHROMA_VALUE,
>> +               .name = "Set chromakey value",
>> +               .type = V4L2_CTRL_TYPE_INTEGER,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +               .min = 0,
>> +               .max = 255,
>> +               .step = 1,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_CSC_EQ_MODE,
>> +               .name = "Set CSC equation mode",
>> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +               .max = DEFAULT_CSC_EQ,
>> +               .def = DEFAULT_CSC_EQ,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_CSC_EQ,
>> +               .name = "Set CSC equation",
>> +               .type = V4L2_CTRL_TYPE_INTEGER,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +               .step = 1,
>> +               .max = 8,
>> +               .def = V4L2_COLORSPACE_REC709,
>> +       }, {
>> +               .ops = &gsc_ctrl_ops,
>> +               .id = V4L2_CID_CSC_RANGE,
>> +               .name = "Set CSC range",
>> +               .type = V4L2_CTRL_TYPE_BOOLEAN,
>> +               .flags = V4L2_CTRL_FLAG_SLIDER,
>> +               .max = DEFAULT_CSC_RANGE,
>> +               .def = DEFAULT_CSC_RANGE,
>> +       },
>> +};
>> +
>> +int gsc_ctrls_create(struct gsc_ctx *ctx)
>> +{
>> +       if (ctx->ctrls_rdy) {
>> +               gsc_err("Control handler of this context was created already");
>> +               return 0;
>> +       }
>> +
>> +       v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM);
>> +
>> +       ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
>> +                               &gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
>> +       ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
>> +                               &gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
>> +       ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
>> +                               &gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
>> +
>> +
>> +       ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[0], NULL);
>> +       ctx->gsc_ctrls.layer_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[2], NULL);
>> +       ctx->gsc_ctrls.layer_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[3], NULL);
>> +       ctx->gsc_ctrls.pixel_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[4], NULL);
>> +       ctx->gsc_ctrls.chroma_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[5], NULL);
>> +       ctx->gsc_ctrls.chroma_val = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[6], NULL);
>> +
>> +/* for CSC equation */
>> +       ctx->gsc_ctrls.csc_eq_mode = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[7], NULL);
>> +       ctx->gsc_ctrls.csc_eq = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[8], NULL);
>> +       ctx->gsc_ctrls.csc_range = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
>> +                                       &gsc_custom_ctrl[9], NULL);
>> +
>> +       ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
>> +
>> +
>> +       if (ctx->ctrl_handler.error) {
>> +               int err = ctx->ctrl_handler.error;
>> +               v4l2_ctrl_handler_free(&ctx->ctrl_handler);
>> +               gsc_err("Failed to gscaler control hander create");
>> +               return err;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +void gsc_ctrls_delete(struct gsc_ctx *ctx)
>> +{
>> +       if (ctx->ctrls_rdy) {
>> +               v4l2_ctrl_handler_free(&ctx->ctrl_handler);
>> +               ctx->ctrls_rdy = false;
>> +       }
>> +}
>> +
>> +/* The color format (num_comp, num_planes) must be already configured. */
>> +int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
>> +                       struct gsc_frame *frame, struct gsc_addr *addr)
>> +{
>> +       int ret = 0;
>> +       u32 pix_size;
>> +
>> +       if (IS_ERR(vb) || IS_ERR(frame)) {
>
> Can vb pointer really contain ERRors? I think you should use !vb here.
>

sorry. i will change this.

>> +               gsc_err("Invalid argument");
>> +               return -EINVAL;
>> +       }
>> +
>> +       pix_size = frame->f_width * frame->f_height;
>> +
>> +       gsc_dbg("num_planes= %d, num_comp= %d, pix_size= %d",
>> +               frame->fmt->num_planes, frame->fmt->num_comp, pix_size);
>> +
>> +       addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
>> +
>> +       if (frame->fmt->num_planes == 1) {
>> +               switch (frame->fmt->num_comp) {
>> +               case 1:
>> +                       addr->cb = 0;
>> +                       addr->cr = 0;
>> +                       break;
>> +               case 2:
>> +                       /* decompose Y into Y/Cb */
>> +                       addr->cb = (dma_addr_t)(addr->y + pix_size);
>> +                       addr->cr = 0;
>> +                       break;
>> +               case 3:
>> +                       addr->cb = (dma_addr_t)(addr->y + pix_size);
>> +                       addr->cr = (dma_addr_t)(addr->cb + (pix_size >> 2));
>
> No need to distinguish between 420 and 422?

Yes. I will update the code.

>
>> +                       break;
>> +               default:
>> +                       gsc_err("Invalid the number of color planes");
>> +                       return -EINVAL;
>> +               }
>> +       } else {
>> +               if (frame->fmt->num_planes >= 2)
>> +                       addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
>> +
>> +               if (frame->fmt->num_planes == 3)
>> +                       addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
>> +       }
>> +
>> +       if (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) {
>
> What about other YVU formats?

Ok. I will update the code.

>
>> +               u32 t_cb = addr->cb;
>> +               addr->cb = addr->cr;
>> +               addr->cr = t_cb;
>> +       }
>> +
>> +       gsc_dbg("ADDR: y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
>> +               addr->y, addr->cb, addr->cr, ret);
>> +
>> +       return ret;
>> +}
>> +
>> +static irqreturn_t gsc_irq_handler(int irq, void *priv)
>> +{
>> +       struct gsc_dev *gsc = priv;
>> +       int gsc_irq;
>> +
>> +       gsc_irq = gsc_hw_get_irq_status(gsc);
>> +       gsc_hw_clear_irq(gsc, gsc_irq);
>> +
>> +       if (gsc_irq == GSC_OR_IRQ) {
>> +               gsc_err("Local path input over-run interrupt has occurred!\n");
>> +               return IRQ_HANDLED;
>> +       }
>> +
>> +       spin_lock(&gsc->slock);
>> +
>> +       if (test_and_clear_bit(ST_M2M_RUN, &gsc->state)) {
>> +               struct vb2_buffer *src_vb, *dst_vb;
>> +               struct gsc_ctx *ctx =
>> +                       v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
>> +
>> +               if (!ctx || !ctx->m2m_ctx)
>> +                       goto isr_unlock;
>> +
>> +               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_and_clear_bit(ST_STOP_REQ, &gsc->state))
>> +                               wake_up(&gsc->irq_queue);
>> +                       else
>> +                               v4l2_m2m_job_finish(gsc->m2m.m2m_dev,
>> +                                                       ctx->m2m_ctx);
>
> Why not job_finish before wake_up if we are shutting down?

yes. you are right.. i will update the code.


>
>> +                       /* wake_up job_abort, stop_streaming */
>> +                       spin_lock(&ctx->slock);
>> +                       if (ctx->state & GSC_CTX_STOP_REQ) {
>> +                               ctx->state &= ~GSC_CTX_STOP_REQ;
>> +                               wake_up(&gsc->irq_queue);
>> +                       }
>> +                       spin_unlock(&ctx->slock);
>> +               }
>> +       }
>> +
>> +isr_unlock:
>> +       spin_unlock(&gsc->slock);
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static int gsc_runtime_suspend(struct device *dev)
>> +{
>> +       struct platform_device *pdev = to_platform_device(dev);
>> +       struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
>> +
>> +       if (gsc_m2m_opened(gsc))
>> +               gsc->m2m.ctx = NULL;
>> +
>> +       clk_disable(gsc->clock);
>> +       clear_bit(ST_PWR_ON, &gsc->state);
>> +
>> +       return 0;
>> +}
>> +
>> +static int gsc_runtime_resume(struct device *dev)
>> +{
>> +       struct platform_device *pdev = to_platform_device(dev);
>> +       struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
>> +       int ret;
>> +
>> +       ret = clk_enable(gsc->clock);
>> +       if (ret)
>> +               return ret;
>> +
>> +       set_bit(ST_PWR_ON, &gsc->state);
>> +       return 0;
>> +}
>> +
>> +static inline void *gsc_get_drv_data(struct platform_device *pdev);
>> +
>> +static int gsc_probe(struct platform_device *pdev)
>> +{
>> +       struct gsc_dev *gsc;
>> +       struct resource *res;
>> +       struct gsc_driverdata *drv_data;
>> +
>> +       int ret = 0;
>> +
>> +       dev_dbg(&pdev->dev, "%s():\n", __func__);
>> +       drv_data = (struct gsc_driverdata *)
>> +                               gsc_get_drv_data(pdev);
>> +
>> +       if (pdev->dev.of_node) {
>> +               pdev->id = of_alias_get_id(pdev->dev.of_node, "gsc");
>> +               if (pdev->id < 0)
>> +                       dev_err(&pdev->dev,
>> +                               "failed to get alias id, errno %d\n", ret);
>> +       }
>> +
>> +       if (pdev->id >= drv_data->num_entities) {
>> +               dev_err(&pdev->dev, "Invalid platform device id: %d\n",
>> +                       pdev->id);
>> +               return -EINVAL;
>> +       }
>> +
>> +       gsc = devm_kzalloc(&pdev->dev, sizeof(struct gsc_dev), GFP_KERNEL);
>> +       if (!gsc)
>> +               return -ENOMEM;
>> +
>> +       gsc->id = pdev->id;
>> +       gsc->variant = drv_data->variant[gsc->id];
>> +       gsc->pdev = pdev;
>> +       gsc->pdata = pdev->dev.platform_data;
>> +
>> +       init_waitqueue_head(&gsc->irq_queue);
>> +       spin_lock_init(&gsc->slock);
>> +       mutex_init(&gsc->lock);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       gsc->regs = devm_request_and_ioremap(&pdev->dev, res);
>> +       if (!gsc->regs) {
>> +               dev_err(&pdev->dev, "failed to map registers\n");
>> +               return -ENOENT;
>> +       }
>> +
>> +       /* Get Gscaler clock */
>> +       gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
>> +       if (IS_ERR(gsc->clock)) {
>> +               gsc_err("failed to get gscaler.%d clock", gsc->id);
>> +               return -ENXIO;
>> +       }
>> +       clk_put(gsc->clock);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +       if (!res) {
>> +               dev_err(&pdev->dev, "failed to get IRQ resource\n");
>> +               return -ENXIO;
>> +       }
>> +       gsc->irq = res->start;
>> +
>> +       ret = devm_request_irq(&pdev->dev, gsc->irq, gsc_irq_handler, 0,
>> +                                                       pdev->name, gsc);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       gsc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
>> +       if (IS_ERR(gsc->alloc_ctx)) {
>> +               ret = PTR_ERR(gsc->alloc_ctx);
>> +               goto err_irq;
>> +       }
>> +
>> +       platform_set_drvdata(pdev, gsc);
>> +
>> +       ret = gsc_register_m2m_device(gsc);
>> +       if (ret)
>> +               goto err_irq;
>> +
>> +       gsc_runtime_resume(&pdev->dev);
>> +       pm_runtime_enable(&pdev->dev);
>> +
>> +       gsc_info("gsc-%d registered successfully", gsc->id);
>> +       return 0;
>> +
>
> I think you are missing additional error path here with
> platform_release_resource()s
> that you might have acquired with platform_get_resource().
>

platform_get_resource() is getting some references from
platform_device. I don't think we need to explicitly release these
resources..
Is there anything i am missing?? i mean any API's ???

>> +err_irq:
>> +       free_irq(gsc->irq, gsc);
>> +
>> +       return ret;
>> +}
>> +
>> +static int __devexit gsc_remove(struct platform_device *pdev)
>> +{
>> +       struct gsc_dev *gsc =
>> +               (struct gsc_dev *)platform_get_drvdata(pdev);
>> +
>> +       free_irq(gsc->irq, gsc);
>> +
>> +       gsc_unregister_m2m_device(gsc);
>> +
>> +       vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
>> +       pm_runtime_disable(&pdev->dev);
>> +
>> +       iounmap(gsc->regs);
>> +       release_resource(gsc->regs_res);
>> +       kfree(gsc->regs_res);
>> +       kfree(gsc);
>> +
>> +       dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
>> +       return 0;
>> +}
>> +
>> +static int gsc_suspend(struct device *dev)
>> +{
>> +       struct platform_device *pdev;
>> +       struct gsc_dev *gsc;
>> +       int ret = 0;
>> +
>> +       pdev = to_platform_device(dev);
>> +       gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
>> +
>> +       if (gsc_m2m_run(gsc)) {
>> +               set_bit(ST_STOP_REQ, &gsc->state);
>> +               ret = wait_event_timeout(gsc->irq_queue,
>> +                               !test_bit(ST_STOP_REQ, &gsc->state),
>> +                               GSC_SHUTDOWN_TIMEOUT);
>> +               if (ret == 0)
>> +                       dev_err(&gsc->pdev->dev, "wait timeout : %s\n",
>> +                               __func__);
>> +       }
>> +
>> +       pm_runtime_put_sync(dev);
>> +
>> +       return ret;
>> +}
>> +
>> +static int gsc_resume(struct device *dev)
>> +{
>> +       struct gsc_dev *gsc;
>> +       struct gsc_ctx *ctx;
>> +
>> +       gsc = dev_get_drvdata(dev);
>> +
>> +       pm_runtime_get_sync(dev);
>> +       if (gsc_m2m_opened(gsc)) {
>> +               ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
>> +               if (ctx != NULL) {
>> +                       gsc->m2m.ctx = NULL;
>> +                       v4l2_m2m_job_finish(gsc->m2m.m2m_dev, ctx->m2m_ctx);
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct dev_pm_ops gsc_pm_ops = {
>> +       .suspend                = gsc_suspend,
>> +       .resume                 = gsc_resume,
>> +       .runtime_suspend        = gsc_runtime_suspend,
>> +       .runtime_resume         = gsc_runtime_resume,
>> +};
>> +
>> +struct gsc_pix_max gsc_v_100_max = {
>> +       .org_scaler_bypass_w    = 8192,
>> +       .org_scaler_bypass_h    = 8192,
>> +       .org_scaler_input_w     = 4800,
>> +       .org_scaler_input_h     = 3344,
>> +       .real_rot_dis_w         = 4800,
>> +       .real_rot_dis_h         = 3344,
>> +       .real_rot_en_w          = 2047,
>> +       .real_rot_en_h          = 2047,
>> +       .target_rot_dis_w       = 4800,
>> +       .target_rot_dis_h       = 3344,
>> +       .target_rot_en_w        = 2016,
>> +       .target_rot_en_h        = 2016,
>> +};
>> +
>> +struct gsc_pix_min gsc_v_100_min = {
>> +       .org_w                  = 64,
>> +       .org_h                  = 32,
>> +       .real_w                 = 64,
>> +       .real_h                 = 32,
>> +       .target_rot_dis_w       = 64,
>> +       .target_rot_dis_h       = 32,
>> +       .target_rot_en_w        = 32,
>> +       .target_rot_en_h        = 16,
>> +};
>> +
>> +struct gsc_pix_align gsc_v_100_align = {
>> +       .org_h                  = 16,
>> +       .org_w                  = 16, /* yuv420 : 16, others : 8 */
>> +       .offset_h               = 2,  /* yuv420/422 : 2, others : 1 */
>> +       .real_w                 = 16, /* yuv420/422 : 4~16, others : 2~8 */
>> +       .real_h                 = 16, /* yuv420 : 4~16, others : 1 */
>> +       .target_w               = 2,  /* yuv420/422 : 2, others : 1 */
>> +       .target_h               = 2,  /* yuv420 : 2, others : 1 */
>> +};
>> +
>> +struct gsc_variant gsc_v_100_variant = {
>> +       .pix_max                = &gsc_v_100_max,
>> +       .pix_min                = &gsc_v_100_min,
>> +       .pix_align              = &gsc_v_100_align,
>> +       .in_buf_cnt             = 8,
>> +       .out_buf_cnt            = 16,
>> +       .sc_up_max              = 8,
>> +       .sc_down_max            = 16,
>> +       .poly_sc_down_max       = 4,
>> +       .pre_sc_down_max        = 4,
>> +       .local_sc_down          = 2,
>> +};
>
> static structs?
>

Ok. I will change this.

>> +
>> +static struct gsc_driverdata gsc_v_100_drvdata = {
>> +       .variant = {
>> +               [0] = &gsc_v_100_variant,
>> +               [1] = &gsc_v_100_variant,
>> +               [2] = &gsc_v_100_variant,
>> +               [3] = &gsc_v_100_variant,
>> +       },
>> +       .num_entities = 4,
>> +       .lclk_frequency = 266000000UL,
>> +};
>> +
>> +static struct platform_device_id gsc_driver_ids[] = {
>> +       {
>> +               .name           = "exynos-gsc",
>> +               .driver_data    = (unsigned long)&gsc_v_100_drvdata,
>> +       },
>> +       {},
>> +};
>> +MODULE_DEVICE_TABLE(platform, gsc_driver_ids);
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id exynos_gsc_match[] = {
>> +       { .compatible = "samsung,exynos-gsc",
>> +       .data = &gsc_v_100_drvdata, },
>> +       {},
>> +};
>> +MODULE_DEVICE_TABLE(of, exynos_gsc_match);
>> +#else
>> +#define exynos_gsc_match NULL
>> +#endif
>> +
>> +static inline void *gsc_get_drv_data(struct platform_device *pdev)
>> +{
>> +#ifdef CONFIG_OF
>> +       if (pdev->dev.of_node) {
>> +               const struct of_device_id *match;
>> +       match = of_match_node(exynos_gsc_match, pdev->dev.of_node);
>> +       return (struct gsc_driverdata *) match->data;
>> +       }
>> +#endif
>> +       return (struct gsc_driverdata *)
>> +                       platform_get_device_id(pdev)->driver_data;
>> +}
>> +
>> +static struct platform_driver gsc_driver = {
>> +       .probe          = gsc_probe,
>> +       .remove = __devexit_p(gsc_remove),
>> +       .id_table       = gsc_driver_ids,
>> +       .driver = {
>> +               .name   = GSC_MODULE_NAME,
>> +               .owner  = THIS_MODULE,
>> +               .pm     = &gsc_pm_ops,
>> +               .of_match_table = exynos_gsc_match,
>> +       }
>> +};
>> +
>> +static int __init gsc_init(void)
>> +{
>> +       int ret = platform_driver_register(&gsc_driver);
>> +       if (ret)
>> +               gsc_err("platform_driver_register failed: %d\n", ret);
>> +       return ret;
>> +}
>> +
>> +static void __exit gsc_exit(void)
>> +{
>> +       platform_driver_unregister(&gsc_driver);
>> +}
>> +
>> +module_init(gsc_init);
>> +module_exit(gsc_exit);
>> +
>> +MODULE_AUTHOR("Hyunwong Kim <khw0178.kim@xxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/video/exynos/gsc/gsc-core.h b/drivers/media/video/exynos/gsc/gsc-core.h
>> new file mode 100644
>> index 0000000..3a236ec
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/gsc-core.h
>> @@ -0,0 +1,652 @@
>> +/* linux/drivers/media/video/exynos/gsc/gsc-core.h
>> + *
>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>> + *             http://www.samsung.com
>> + *
>> + * header file for Samsung EXYNOS5 SoC series G-scaler 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 GSC_CORE_H_
>> +#define GSC_CORE_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 <media/v4l2-mediabus.h>
>> +#define CONFIG_VB2_GSC_DMA_CONTIG      1
>> +#include <media/videobuf2-dma-contig.h>
>> +#include "gsc-regs.h"
>> +extern int gsc_dbg;
>> +
>> +#define GSC_MODULE_NAME                        "exynos-gsc"
>> +
>> +#define gsc_info(fmt, args...)                                         \
>> +       do {                                                            \
>> +               if (gsc_dbg >= 6)                                       \
>> +                       pr_info("[INFO]%s:%d: "fmt "\n",                \
>> +                               __func__, __LINE__, ##args);            \
>> +       } while (0)
>> +
>> +#define gsc_err(fmt, args...)                                          \
>> +       do {                                                            \
>> +               if (gsc_dbg >= 3)                                       \
>> +                       pr_err("[ERROR]%s:%d: "fmt "\n",                \
>> +                               __func__, __LINE__, ##args);            \
>> +       } while (0)
>> +
>> +#define gsc_warn(fmt, args...)                                         \
>> +       do {                                                            \
>> +               if (gsc_dbg >= 4)                                       \
>> +                       pr_warn("[WARN]%s:%d: "fmt "\n",                \
>> +                               __func__, __LINE__, ##args);            \
>> +       } while (0)
>> +
>> +#define gsc_dbg(fmt, args...)                                          \
>> +       do {                                                            \
>> +               if (gsc_dbg >= 7)                                       \
>> +                       pr_debug("[DEBUG]%s:%d: "fmt "\n",              \
>> +                               __func__, __LINE__, ##args);            \
>> +       } while (0)
>> +
>> +#define GSC_MAX_CLOCKS                 3
>
> never used
>

Ok. i will remove all the unused code...

>> +#define GSC_SHUTDOWN_TIMEOUT           ((100*HZ)/1000)
>> +#define GSC_MAX_DEVS                   4
>> +#define WORKQUEUE_NAME_SIZE            32
>
> never used
>
>> +#define FIMD_NAME_SIZE                 32
>
> ditto
>
>> +#define GSC_M2M_BUF_NUM                        0
>> +#define GSC_OUT_BUF_MAX                        2
>
> ditto
>
>> +#define GSC_MAX_CTRL_NUM               10
>> +#define GSC_OUT_MAX_MASK_NUM           7
>
> ditto
>
>> +#define GSC_SC_ALIGN_4                 4
>> +#define GSC_SC_ALIGN_2                 2
>
>> +#define GSC_OUT_DEF_SRC                        15
>> +#define GSC_OUT_DEF_DST                        7
>> +#define DEFAULT_GSC_SINK_WIDTH         800
>> +#define DEFAULT_GSC_SINK_HEIGHT                480
>> +#define DEFAULT_GSC_SOURCE_WIDTH       800
>> +#define DEFAULT_GSC_SOURCE_HEIGHT      480
>
> ditto for all 6 above.
>
>> +#define DEFAULT_CSC_EQ                 1
>> +#define DEFAULT_CSC_RANGE              1
>> +
>> +#define GSC_LAST_DEV_ID                        3
>> +#define GSC_PAD_SINK                   0
>> +#define GSC_PAD_SOURCE                 1
>> +#define GSC_PADS_NUM                   2
>
> ditto for all 4 above.
>
>> +
>> +#define        GSC_PARAMS                      (1 << 0)
>> +#define        GSC_SRC_FMT                     (1 << 1)
>> +#define        GSC_DST_FMT                     (1 << 2)
>> +#define        GSC_CTX_M2M                     (1 << 3)
>> +#define        GSC_CTX_OUTPUT                  (1 << 4)
>> +#define        GSC_CTX_START                   (1 << 5)
>> +#define        GSC_CTX_STOP_REQ                (1 << 6)
>> +#define        GSC_CTX_CAP                     (1 << 10)
>
>> +#define MAX_MDEV                       2
>
> ditto
>
>> +
>> +#define V4L2_CID_CACHEABLE                     (V4L2_CID_LASTP1 + 1)
>> +#define V4L2_CID_TV_LAYER_BLEND_ENABLE         (V4L2_CID_LASTP1 + 2)
>> +#define V4L2_CID_TV_LAYER_BLEND_ALPHA          (V4L2_CID_LASTP1 + 3)
>> +#define V4L2_CID_TV_PIXEL_BLEND_ENABLE         (V4L2_CID_LASTP1 + 4)
>> +#define V4L2_CID_TV_CHROMA_ENABLE              (V4L2_CID_LASTP1 + 5)
>> +#define V4L2_CID_TV_CHROMA_VALUE               (V4L2_CID_LASTP1 + 6)
>> +/* for color space conversion equation selection */
>> +#define V4L2_CID_CSC_EQ_MODE                   (V4L2_CID_LASTP1 + 8)
>> +#define V4L2_CID_CSC_EQ                                (V4L2_CID_LASTP1 + 9)
>> +#define V4L2_CID_CSC_RANGE                     (V4L2_CID_LASTP1 + 10)
>> +#define V4L2_CID_GLOBAL_ALPHA                  (V4L2_CID_LASTP1 + 11)
>> +
>> +enum gsc_dev_flags {
>> +       /* for global */
>> +       ST_PWR_ON,
>> +       ST_STOP_REQ,
>> +       /* for m2m node */
>> +       ST_M2M_OPEN,
>> +       ST_M2M_RUN,
>> +};
>> +
>> +enum gsc_irq {
>> +       GSC_OR_IRQ = 17,
>> +       GSC_DONE_IRQ = 16,
>> +};
>> +
>> +/**
>> + * enum gsc_datapath - the path of data used for gscaler
>> + * @GSC_CAMERA: from camera
>> + * @GSC_DMA: from/to DMA
>> + * @GSC_LOCAL: to local path
>> + * @GSC_WRITEBACK: from FIMD
>> + */
>> +enum gsc_datapath {
>> +       GSC_CAMERA = 0x1,
>> +       GSC_DMA,
>> +       GSC_MIXER,
>> +       GSC_FIMD,
>> +       GSC_WRITEBACK,
>> +};
>> +
>> +enum gsc_color_fmt {
>> +       GSC_RGB = 0x1,
>> +       GSC_YUV420 = 0x2,
>> +       GSC_YUV422 = 0x4,
>> +       GSC_YUV444 = 0x8,
>> +};
>> +
>> +enum gsc_yuv_fmt {
>> +       GSC_LSB_Y = 0x10,
>> +       GSC_LSB_C,
>> +       GSC_CBCR = 0x20,
>> +       GSC_CRCB,
>> +};
>> +
>> +#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh)
>> +#define is_rgb(x) (!!((x) & 0x1))
>> +#define is_yuv420(x) (!!((x) & 0x2))
>> +#define is_yuv422(x) (!!((x) & 0x4))
>> +#define gsc_m2m_run(dev) test_bit(ST_M2M_RUN, &(dev)->state)
>> +#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state)
>> +
>> +#define ctrl_to_ctx(__ctrl) \
>> +       container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler)
>> +/**
>> + * struct gsc_fmt - the driver's internal color format data
>> + * @mbus_code: Media Bus pixel code, -1 if not applicable
>> + * @name: format description
>> + * @pixelformat: the fourcc code for this format, 0 if not applicable
>> + * @yorder: Y/C order
>> + * @corder: Chrominance order control
>> + * @num_planes: number of physically non-contiguous data planes
>> + * @nr_comp: number of physically contiguous data planes
>> + * @depth: per plane driver's private 'number of bits per pixel'
>> + * @flags: flags indicating which operation mode format applies to
>> + */
>> +struct gsc_fmt {
>> +       enum v4l2_mbus_pixelcode mbus_code;
>> +       char    *name;
>> +       u32     pixelformat;
>> +       u32     color;
>> +       u32     yorder;
>> +       u32     corder;
>> +       u16     num_planes;
>> +       u16     num_comp;
>> +       u8      depth[VIDEO_MAX_PLANES];
>> +       u32     flags;
>> +};
>> +
>> +/**
>> + * struct gsc_input_buf - the driver's video buffer
>> + * @vb:        videobuf2 buffer
>> + * @list : linked list structure for buffer queue
>> + * @idx : index of G-Scaler input buffer
>> + */
>> +struct gsc_input_buf {
>> +       struct vb2_buffer       vb;
>> +       struct list_head        list;
>> +       int                     idx;
>> +};
>> +
>> +/**
>> + * struct gsc_addr - the G-Scaler physical address set
>> + * @y:  luminance plane address
>> + * @cb:         Cb plane address
>> + * @cr:         Cr plane address
>> + */
>> +struct gsc_addr {
>> +       dma_addr_t      y;
>> +       dma_addr_t      cb;
>> +       dma_addr_t      cr;
>> +};
>> +
>> +/* struct gsc_ctrls - the G-Scaler control set
>> + * @rotate: rotation degree
>> + * @hflip: horizontal flip
>> + * @vflip: vertical flip
>> + * @global_alpha: the alpha value of current frame
>> + * @layer_blend_en: enable mixer layer alpha blending
>> + * @layer_alpha: set alpha value for mixer layer
>> + * @pixel_blend_en: enable mixer pixel alpha blending
>> + * @chroma_en: enable chromakey
>> + * @chroma_val:        set value for chromakey
>> + * @csc_eq_mode: mode to select csc equation of current frame
>> + * @csc_eq: csc equation of current frame
>> + * @csc_range: csc range of current frame
>> + */
>> +struct gsc_ctrls {
>> +       struct v4l2_ctrl        *rotate;
>> +       struct v4l2_ctrl        *hflip;
>> +       struct v4l2_ctrl        *vflip;
>> +       struct v4l2_ctrl        *global_alpha;
>> +       struct v4l2_ctrl        *layer_blend_en;
>> +       struct v4l2_ctrl        *layer_alpha;
>> +       struct v4l2_ctrl        *pixel_blend_en;
>> +       struct v4l2_ctrl        *chroma_en;
>> +       struct v4l2_ctrl        *chroma_val;
>> +       struct v4l2_ctrl        *csc_eq_mode;
>> +       struct v4l2_ctrl        *csc_eq;
>> +       struct v4l2_ctrl        *csc_range;
>> +};
>
> Can you do this? Keep the pointer to the v4l2_ctrl struct passed to s_ctrl
> after it returns?

Yes. I think we can keep the reference.. but updating the values are
not valid... which i was doing in the user_to_drv() funciton.
I will change that..

>
>> +
>> +/**
>> + * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler
>> + * @pre_shfactor:      pre sclaer shift factor
>> + * @pre_hratio:                horizontal ratio of the prescaler
>> + * @pre_vratio:                vertical ratio of the prescaler
>> + * @main_hratio:       the main scaler's horizontal ratio
>> + * @main_vratio:       the main scaler's vertical ratio
>> + */
>> +struct gsc_scaler {
>> +       u32     pre_shfactor;
>> +       u32     pre_hratio;
>> +       u32     pre_vratio;
>> +       unsigned long main_hratio;
>> +       unsigned long main_vratio;
>> +};
>> +
>> +struct gsc_dev;
>> +
>> +struct gsc_ctx;
>> +
>> +/**
>> + * struct gsc_frame - source/target frame properties
>> + * @f_width:   SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
>> + * @f_height:  SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
>> + * @crop:      cropped(source)/scaled(destination) size
>> + * @payload:   image size in bytes (w x h x bpp)
>> + * @addr:      image frame buffer physical addresses
>> + * @fmt:       G-scaler color format pointer
>> + * @alph:      frame's alpha value
>> + */
>> +struct gsc_frame {
>> +       u32     f_width;
>> +       u32     f_height;
>> +       struct v4l2_rect        crop;
>> +       unsigned long payload[VIDEO_MAX_PLANES];
>> +       struct gsc_addr         addr;
>> +       struct gsc_fmt          *fmt;
>> +       u8      alpha;
>> +};
>> +
>> +/**
>> + * struct gsc_m2m_device - v4l2 memory-to-memory device data
>> + * @vfd: the video device node for v4l2 m2m mode
>> + * @m2m_dev: v4l2 memory-to-memory device data
>> + * @ctx: hardware context data
>> + * @refcnt: the reference counter
>> + */
>> +struct gsc_m2m_device {
>> +       struct video_device     *vfd;
>> +       struct v4l2_m2m_dev     *m2m_dev;
>> +       struct gsc_ctx          *ctx;
>> +       int                     refcnt;
>> +};
>> +
>> +/**
>> + *  struct gsc_pix_max - image pixel size limits in various IP configurations
>> + *
>> + *  @org_scaler_bypass_w: max pixel width when the scaler is disabled
>> + *  @org_scaler_bypass_h: max pixel height when the scaler is disabled
>> + *  @org_scaler_input_w: max pixel width when the scaler is enabled
>> + *  @org_scaler_input_h: max pixel height when the scaler is enabled
>> + *  @real_rot_dis_w: max pixel src cropped height with the rotator is off
>> + *  @real_rot_dis_h: max pixel src croppped width with the rotator is off
>> + *  @real_rot_en_w: max pixel src cropped width with the rotator is on
>> + *  @real_rot_en_h: max pixel src cropped height with the rotator is on
>> + *  @target_rot_dis_w: max pixel dst scaled width with the rotator is off
>> + *  @target_rot_dis_h: max pixel dst scaled height with the rotator is off
>> + *  @target_rot_en_w: max pixel dst scaled width with the rotator is on
>> + *  @target_rot_en_h: max pixel dst scaled height with the rotator is on
>> + */
>> +struct gsc_pix_max {
>> +       u16 org_scaler_bypass_w;
>> +       u16 org_scaler_bypass_h;
>> +       u16 org_scaler_input_w;
>> +       u16 org_scaler_input_h;
>> +       u16 real_rot_dis_w;
>> +       u16 real_rot_dis_h;
>> +       u16 real_rot_en_w;
>> +       u16 real_rot_en_h;
>> +       u16 target_rot_dis_w;
>> +       u16 target_rot_dis_h;
>> +       u16 target_rot_en_w;
>> +       u16 target_rot_en_h;
>> +};
>> +
>> +/**
>> + *  struct gsc_pix_min - image pixel size limits in various IP configurations
>> + *
>> + *  @org_w: minimum source pixel width
>> + *  @org_h: minimum source pixel height
>> + *  @real_w: minimum input crop pixel width
>> + *  @real_h: minimum input crop pixel height
>> + *  @target_rot_dis_w: minimum output scaled pixel height when rotator is off
>> + *  @target_rot_dis_h: minimum output scaled pixel height when rotator is off
>> + *  @target_rot_en_w: minimum output scaled pixel height when rotator is on
>> + *  @target_rot_en_h: minimum output scaled pixel height when rotator is on
>> + */
>> +struct gsc_pix_min {
>> +       u16 org_w;
>> +       u16 org_h;
>> +       u16 real_w;
>> +       u16 real_h;
>> +       u16 target_rot_dis_w;
>> +       u16 target_rot_dis_h;
>> +       u16 target_rot_en_w;
>> +       u16 target_rot_en_h;
>> +};
>> +
>> +struct gsc_pix_align {
>> +       u16 org_h;
>> +       u16 org_w;
>> +       u16 offset_h;
>> +       u16 real_w;
>> +       u16 real_h;
>> +       u16 target_w;
>> +       u16 target_h;
>> +};
>> +
>> +/**
>> + * struct gsc_variant - G-Scaler variant information
>> + */
>> +struct gsc_variant {
>> +       struct gsc_pix_max *pix_max;
>> +       struct gsc_pix_min *pix_min;
>> +       struct gsc_pix_align *pix_align;
>> +       u16             in_buf_cnt;
>> +       u16             out_buf_cnt;
>> +       u16             sc_up_max;
>> +       u16             sc_down_max;
>> +       u16             poly_sc_down_max;
>> +       u16             pre_sc_down_max;
>> +       u16             local_sc_down;
>> +};
>> +
>> +/**
>> + * struct gsc_driverdata - per device type driver data for init time.
>> + *
>> + * @variant: the variant information for this driver.
>> + * @lclk_frequency: g-scaler clock frequency
>> + * @num_entities: the number of g-scalers
>> + */
>> +struct gsc_driverdata {
>> +       struct gsc_variant *variant[GSC_MAX_DEVS];
>> +       unsigned long   lclk_frequency;
>> +       int             num_entities;
>> +};
>> +
>> +struct gsc_vb2 {
>> +       const struct vb2_mem_ops *ops;
>> +       void *(*init)(struct gsc_dev *gsc);
>> +       void (*cleanup)(void *alloc_ctx);
>> +
>> +       void (*resume)(void *alloc_ctx);
>> +       void (*suspend)(void *alloc_ctx);
>> +
>> +       int (*cache_flush)(struct vb2_buffer *vb, u32 num_planes);
>> +       void (*set_cacheable)(void *alloc_ctx, bool cacheable);
>> +       void (*set_sharable)(void *alloc_ctx, bool sharable);
>> +};
>
> I don't see anything from here being used apart from resume/suspend.
> Am I missing something?
>
>> +
>> +/**
>> + * struct gsc_dev - abstraction for G-Scaler entity
>> + * @slock:     the spinlock protecting this data structure
>> + * @lock:      the mutex protecting this data structure
>> + * @pdev:      pointer to the G-Scaler platform device
>> + * @variant:   the IP variant information
>> + * @id:                g_scaler device index (0..GSC_MAX_DEVS)
>> + * @regs:      the mapped hardware registers
>> + * @regs_res:  the resource claimed for IO registers
>> + * @irq:       G-scaler interrupt number
>> + * @irq_queue: interrupt handler waitqueue
>> + * @m2m:       memory-to-memory V4L2 device information
>> + * @out:       memory-to-local V4L2 output device information
>> + * @state:     flags used to synchronize m2m and capture mode operation
>> + * @alloc_ctx: videobuf2 memory allocator context
>> + * @vb2:       videobuf2 memory allocator call-back functions
>> + * @mdev:      pointer to exynos media device
>> + * @pipeline:  pointer to subdevs that are connected with gscaler
>> + */
>> +struct gsc_dev {
>> +       spinlock_t                      slock;
>> +       struct mutex                    lock;
>> +       struct platform_device          *pdev;
>> +       struct gsc_variant              *variant;
>> +       u16                             id;
>> +       struct clk                      *clock;
>> +       void __iomem                    *regs;
>> +       struct resource                 *regs_res;
>> +       int                             irq;
>> +       wait_queue_head_t               irq_queue;
>> +       struct gsc_m2m_device           m2m;
>> +       struct exynos_platform_gscaler  *pdata;
>> +       unsigned long                   state;
>> +       struct vb2_alloc_ctx            *alloc_ctx;
>> +       const struct gsc_vb2            *vb2;
>> +};
>> +
>> +/**
>> + * gsc_ctx - the device context data
>> + * @slock:             spinlock protecting this data structure
>> + * @s_frame:           source frame properties
>> + * @d_frame:           destination frame properties
>> + * @in_path:           input mode (DMA or camera)
>> + * @out_path:          output mode (DMA or FIFO)
>> + * @scaler:            image scaler properties
>> + * @flags:             additional flags for image conversion
>> + * @state:             flags to keep track of user configuration
>> + * @gsc_dev:           the g-scaler device this context applies to
>> + * @m2m_ctx:           memory-to-memory device context
>> + * @fh:                 v4l2 file handle
>> + * @ctrl_handler:       v4l2 controls handler
>> + * @ctrls_rdy:          true if the control handler is initialized
>> + * @gsc_ctrls          G-Scaler control set
>> + * @m2m_ctx:           memory-to-memory device context
>> + */
>> +struct gsc_ctx {
>> +       spinlock_t              slock;
>> +       struct gsc_frame        s_frame;
>> +       struct gsc_frame        d_frame;
>> +       enum gsc_datapath       in_path;
>> +       enum gsc_datapath       out_path;
>> +       struct gsc_scaler       scaler;
>> +       u32                     flags;
>> +       u32                     state;
>> +       struct gsc_dev          *gsc_dev;
>> +       struct v4l2_m2m_ctx     *m2m_ctx;
>> +       struct v4l2_fh          fh;
>> +       struct v4l2_ctrl_handler ctrl_handler;
>> +       struct gsc_ctrls        gsc_ctrls;
>> +       bool                    ctrls_rdy;
>> +};
>> +
>> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm);
>> +void gsc_clk_release(struct gsc_dev *gsc);
>
> no such function
>
>> +int gsc_register_m2m_device(struct gsc_dev *gsc);
>> +void gsc_unregister_m2m_device(struct gsc_dev *gsc);
>> +
>> +u32 get_plane_size(struct gsc_frame *fr, unsigned int plane);
>> +char gsc_total_fmts(void);
>
> no such function
>
>> +struct gsc_fmt *get_format(int index);
>> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index);
>> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f);
>> +int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
>> +void gsc_set_frame_size(struct gsc_frame *frame, int width, int height);
>> +int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
>> +void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h);
>> +int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
>> +int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
>> +int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
>> +                                                       u32 *ratio);
>> +void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh);
>> +void gsc_check_src_scale_info(struct gsc_variant *var,
>> +                               struct gsc_frame *s_frame,
>> +                               u32 *wratio, u32 tx, u32 ty, u32 *hratio);
>> +int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
>> +                          int dh, int rot, int out_path);
>> +int gsc_set_scaler_info(struct gsc_ctx *ctx);
>> +int gsc_ctrls_create(struct gsc_ctx *ctx);
>> +void gsc_ctrls_delete(struct gsc_ctx *ctx);
>> +int gsc_out_hw_set(struct gsc_ctx *ctx);
>> +int gsc_out_set_in_addr(struct gsc_dev *gsc, struct gsc_ctx *ctx,
>> +                  struct gsc_input_buf *buf, int index);
>
> Above two don't exist.
>
>> +int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
>> +                    struct gsc_frame *frame, struct gsc_addr *addr);
>> +int gsc_out_link_validate(const struct media_pad *source,
>> +                         const struct media_pad *sink);
>> +int gsc_pipeline_s_stream(struct gsc_dev *gsc, bool on);
>
> Ditto for above two.
>
>> +
>> +static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx)
>> +{
>> +       unsigned long flags;
>> +
>> +       spin_lock_irqsave(&ctx->slock, flags);
>> +       ctx->state |= state;
>> +       spin_unlock_irqrestore(&ctx->slock, flags);
>> +}
>> +
>> +static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx)
>> +{
>> +       unsigned long flags;
>> +
>> +       spin_lock_irqsave(&ctx->slock, flags);
>> +       ctx->state &= ~state;
>> +       spin_unlock_irqrestore(&ctx->slock, flags);
>> +}
>> +
>> +static inline int get_win_num(struct gsc_dev *dev)
>> +{
>> +       return (dev->id == 3) ? 2 : dev->id;
>> +}
>
> unused
>
>> +static inline int is_tiled(struct gsc_fmt *fmt)
>> +{
>> +       return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16;
>> +}
>> +
>> +static inline int is_output(enum v4l2_buf_type type)
>> +{
>> +       return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
>> +               type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 1 : 0;
>> +}
>
> never used
>
>> +
>> +static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on)
>> +{
>> +       u32 cfg = readl(dev->regs + GSC_ENABLE);
>> +
>> +       if (on)
>> +               cfg |= GSC_ENABLE_ON;
>> +       else
>> +               cfg &= ~GSC_ENABLE_ON;
>> +
>> +       writel(cfg, dev->regs + GSC_ENABLE);
>> +}
>> +
>> +static inline int gsc_hw_get_irq_status(struct gsc_dev *dev)
>> +{
>> +       u32 cfg = readl(dev->regs + GSC_IRQ);
>> +       if (cfg & (1 << GSC_OR_IRQ))
>> +               return GSC_OR_IRQ;
>> +       else
>> +               return GSC_DONE_IRQ;
>> +
>> +}
>> +
>> +static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq)
>> +{
>> +       u32 cfg = readl(dev->regs + GSC_IRQ);
>> +       if (irq == GSC_OR_IRQ)
>> +               cfg |= GSC_IRQ_STATUS_OR_IRQ;
>> +       else if (irq == GSC_DONE_IRQ)
>> +               cfg |= GSC_IRQ_STATUS_OR_FRM_DONE;
>> +       writel(cfg, dev->regs + GSC_IRQ);
>> +}
>> +
>> +static inline void gsc_lock(struct vb2_queue *vq)
>> +{
>> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
>> +       mutex_lock(&ctx->gsc_dev->lock);
>> +}
>> +
>> +static inline void gsc_unlock(struct vb2_queue *vq)
>> +{
>> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
>> +       mutex_unlock(&ctx->gsc_dev->lock);
>> +}
>> +
>> +static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx)
>> +{
>> +       unsigned long flags;
>> +       bool ret;
>> +
>> +       spin_lock_irqsave(&ctx->slock, flags);
>> +       ret = (ctx->state & mask) == mask;
>> +       spin_unlock_irqrestore(&ctx->slock, flags);
>> +       return ret;
>> +}
>> +
>> +static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx,
>> +                                             enum v4l2_buf_type type)
>> +{
>> +       struct gsc_frame *frame;
>> +
>> +       if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
>> +               frame = &ctx->s_frame;
>> +       } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
>> +               frame = &ctx->d_frame;
>> +       } else {
>> +               gsc_err("Wrong buffer/video queue type (%d)", type);
>> +               return ERR_PTR(-EINVAL);
>> +       }
>> +
>> +       return frame;
>> +}
>> +
>> +static inline void user_to_drv(struct v4l2_ctrl *ctrl, s32 value)
>> +{
>> +       ctrl->cur.val = ctrl->val = value;
>> +}
>
> This is fishy. Can you store the pointer to the control after s_ctrl returns?
>

Sorry.. i read the documentation... we can store the pointer but we
should not update values explicitly...
I will change this implementation.

>> +
>> +void gsc_hw_set_sw_reset(struct gsc_dev *dev);
>> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask);
>
> Both never used.
>
>> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask);
>> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask);
>
> Never used.
>
>> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask);
>
>> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev);
>> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev);
>
> both never used
>
>> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
>> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
>> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
>> +                                                       int index);
>> +void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr,
>> +                                                       int index);
>> +void gsc_hw_set_input_path(struct gsc_ctx *ctx);
>> +void gsc_hw_set_in_size(struct gsc_ctx *ctx);
>> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx);
>> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx);
>> +void gsc_hw_set_output_path(struct gsc_ctx *ctx);
>> +void gsc_hw_set_out_size(struct gsc_ctx *ctx);
>> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx);
>> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx);
>> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx);
>> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx);
>> +void gsc_hw_set_rotation(struct gsc_ctx *ctx);
>> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx);
>> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx);
>> +
>> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev);
>> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev);
>> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev);
>> +int gsc_wait_reset(struct gsc_dev *dev);
>
> Above 4 never used.
>
>> +int gsc_wait_operating(struct gsc_dev *dev);
>> +int gsc_wait_stop(struct gsc_dev *dev);
>
> Never used.
>
>> +
>> +#endif /* GSC_CORE_H_ */
>> diff --git a/drivers/media/video/exynos/gsc/gsc-m2m.c b/drivers/media/video/exynos/gsc/gsc-m2m.c
>> new file mode 100644
>> index 0000000..ead59a0
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/gsc-m2m.c
>> @@ -0,0 +1,751 @@
>> +/* linux/drivers/media/video/exynos/gsc/gsc-m2m.c
>> + *
>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>> + *             http://www.samsung.com
>> + *
>> + * Samsung EXYNOS5 SoC series G-scaler driver
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/version.h>
>> +#include <linux/types.h>
>> +#include <linux/errno.h>
>> +#include <linux/bug.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/list.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/clk.h>
>> +#include <media/v4l2-ioctl.h>
>> +
>> +#include "gsc-core.h"
>> +
>> +static int gsc_ctx_stop_req(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_ctx *curr_ctx;
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +       int ret = 0;
>> +
>> +       curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
>> +       if (!gsc_m2m_run(gsc) || (curr_ctx != ctx))
>> +               return 0;
>> +       ctx->state |= GSC_CTX_STOP_REQ;
>> +       ret = wait_event_timeout(gsc->irq_queue,
>> +                       !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
>> +                       GSC_SHUTDOWN_TIMEOUT);
>> +       if (!ret)
>> +               ret = -EBUSY;
>> +
>> +       return ret;
>> +}
>> +
>> +static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
>> +{
>> +       struct gsc_ctx *ctx = q->drv_priv;
>> +       int ret;
>> +
>> +       ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev);
>> +       return ret > 0 ? 0 : ret;
>> +}
>> +
>> +static int gsc_m2m_stop_streaming(struct vb2_queue *q)
>> +{
>> +       struct gsc_ctx *ctx = q->drv_priv;
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +       int ret;
>> +
>> +       ret = gsc_ctx_stop_req(ctx);
>> +       /* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
>
> Can we fix this?

Ok. I will fix this and all others...

>
>> +       if (ret < 0)
>> +               dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
>> +
>> +       pm_runtime_put(&ctx->gsc_dev->pdev->dev);
>> +
>> +       return 0;
>> +}
>> +
>> +static void gsc_m2m_job_abort(void *priv)
>> +{
>> +       struct gsc_ctx *ctx = priv;
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +       int ret;
>> +
>> +       ret = gsc_ctx_stop_req(ctx);
>> +       /* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
>
> Can we fix this?
>
>> +       if (ret < 0)
>> +               dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
>> +}
>> +
>> +int gsc_fill_addr(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_frame *s_frame, *d_frame;
>> +       struct vb2_buffer *vb = NULL;
>> +       int ret = 0;
>> +
>> +       s_frame = &ctx->s_frame;
>> +       d_frame = &ctx->d_frame;
>> +
>> +       vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
>> +       ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr);
>> +       if (ret)
>> +               return ret;
>> +
>> +       vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
>> +       ret = gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr);
>> +
>> +       return ret;
>> +}
>> +
>> +static void gsc_m2m_device_run(void *priv)
>> +{
>> +       struct gsc_ctx *ctx = priv;
>> +       struct gsc_dev *gsc;
>> +       unsigned long flags;
>> +       u32 ret;
>> +       bool is_set = false;
>> +
>> +       if (WARN(!ctx, "null hardware context\n"))
>> +               return;
>> +
>> +       gsc = ctx->gsc_dev;
>> +
>> +       spin_lock_irqsave(&ctx->slock, flags);
>> +       /* Reconfigure hardware if the context has changed. */
>> +       if (gsc->m2m.ctx != ctx) {
>> +               gsc_dbg("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
>> +                         gsc->m2m.ctx, ctx);
>> +               ctx->state |= GSC_PARAMS;
>> +               gsc->m2m.ctx = ctx;
>> +       }
>> +
>> +       is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0;
>> +       ctx->state &= ~GSC_CTX_STOP_REQ;
>> +       if (is_set) {
>> +               wake_up(&gsc->irq_queue);
>> +               goto put_device;
>> +       }
>> +
>> +       ret = gsc_fill_addr(ctx);
>> +       if (ret) {
>> +               gsc_err("Wrong address");
>> +               goto put_device;
>> +       }
>> +
>> +       gsc_set_prefbuf(gsc, ctx->s_frame);
>> +       gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
>> +       gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
>> +
>> +       if (ctx->state & GSC_PARAMS) {
>> +               gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
>> +               gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
>> +               gsc_hw_set_frm_done_irq_mask(gsc, false);
>> +               gsc_hw_set_gsc_irq_enable(gsc, true);
>> +
>> +               if (gsc_set_scaler_info(ctx)) {
>> +                       gsc_err("Scaler setup error");
>> +                       goto put_device;
>> +               }
>> +
>> +               gsc_hw_set_input_path(ctx);
>> +               gsc_hw_set_in_size(ctx);
>> +               gsc_hw_set_in_image_format(ctx);
>> +
>> +               gsc_hw_set_output_path(ctx);
>> +               gsc_hw_set_out_size(ctx);
>> +               gsc_hw_set_out_image_format(ctx);
>> +
>> +               gsc_hw_set_prescaler(ctx);
>> +               gsc_hw_set_mainscaler(ctx);
>> +               gsc_hw_set_rotation(ctx);
>> +               gsc_hw_set_global_alpha(ctx);
>> +       }
>> +       /* When you update SFRs in the middle of operating
>> +       gsc_hw_set_sfr_update(ctx);
>> +       */
>
> Leftover?
>
>> +
>> +       ctx->state &= ~GSC_PARAMS;
>> +
>> +       if (!test_and_set_bit(ST_M2M_RUN, &gsc->state)) {
>> +               /* One frame mode sequence
>> +                GSCALER_ON on -> GSCALER_OP_STATUS is operating ->
>> +                GSCALER_ON off */
>> +               gsc_hw_enable_control(gsc, true);
>> +               ret = gsc_wait_operating(gsc);
>> +               if (ret < 0) {
>> +                       gsc_err("gscaler wait operating timeout");
>> +                       goto put_device;
>> +               }
>> +               gsc_hw_enable_control(gsc, false);
>> +       }
>> +
>> +       spin_unlock_irqrestore(&ctx->slock, flags);
>> +       return;
>> +
>> +put_device:
>> +       ctx->state &= ~GSC_PARAMS;
>> +       spin_unlock_irqrestore(&ctx->slock, flags);
>> +}
>> +
>> +static int gsc_m2m_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 gsc_ctx *ctx = vb2_get_drv_priv(vq);
>> +       struct gsc_frame *frame;
>> +       int i;
>> +
>> +       frame = ctx_get_frame(ctx, vq->type);
>> +       if (IS_ERR(frame))
>> +               return PTR_ERR(frame);
>> +
>> +       if (!frame->fmt)
>> +               return -EINVAL;
>> +
>> +       *num_planes = frame->fmt->num_planes;
>> +       for (i = 0; i < frame->fmt->num_planes; i++) {
>> +               sizes[i] = get_plane_size(frame, i);
>> +               allocators[i] = ctx->gsc_dev->alloc_ctx;
>> +       }
>> +       return 0;
>> +}
>> +
>> +static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
>> +{
>> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>> +       struct gsc_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->fmt->num_planes; i++)
>> +                       vb2_set_plane_payload(vb, i, frame->payload[i]);
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
>> +{
>> +       struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
>> +
>> +       gsc_dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
>> +
>> +       if (ctx->m2m_ctx)
>> +               v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
>> +}
>> +
>> +struct vb2_ops gsc_m2m_qops = {
>> +       .queue_setup     = gsc_m2m_queue_setup,
>> +       .buf_prepare     = gsc_m2m_buf_prepare,
>> +       .buf_queue       = gsc_m2m_buf_queue,
>> +       .wait_prepare    = gsc_unlock,
>> +       .wait_finish     = gsc_lock,
>> +       .stop_streaming  = gsc_m2m_stop_streaming,
>> +       .start_streaming = gsc_m2m_start_streaming,
>> +};
>> +
>> +static int gsc_m2m_querycap(struct file *file, void *fh,
>> +                          struct v4l2_capability *cap)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +
>> +       strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
>> +       strncpy(cap->card, gsc->pdev->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 gsc_m2m_enum_fmt_mplane(struct file *file, void *priv,
>> +                               struct v4l2_fmtdesc *f)
>> +{
>> +       return gsc_enum_fmt_mplane(f);
>> +}
>> +
>> +static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
>> +                            struct v4l2_format *f)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +
>> +       return gsc_g_fmt_mplane(ctx, f);
>> +}
>> +
>> +static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
>> +                                 struct v4l2_format *f)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +
>> +       return gsc_try_fmt_mplane(ctx, f);
>> +}
>> +
>> +static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
>> +                                struct v4l2_format *f)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       struct vb2_queue *vq;
>> +       struct gsc_frame *frame;
>> +       struct v4l2_pix_format_mplane *pix;
>> +       int i, ret = 0;
>> +
>> +       ret = gsc_m2m_try_fmt_mplane(file, fh, f);
>> +       if (ret)
>> +               return ret;
>> +
>> +       vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
>> +
>> +       if (vb2_is_streaming(vq)) {
>> +               gsc_err("queue (%d) busy", f->type);
>> +               return -EBUSY;
>> +       }
>> +
>> +       if (V4L2_TYPE_IS_OUTPUT(f->type))
>> +               frame = &ctx->s_frame;
>> +       else
>> +               frame = &ctx->d_frame;
>> +
>> +       pix = &f->fmt.pix_mp;
>> +       frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
>> +       if (!frame->fmt)
>> +               return -EINVAL;
>> +
>> +       for (i = 0; i < frame->fmt->num_planes; i++)
>> +               frame->payload[i] = pix->plane_fmt[i].sizeimage;
>> +
>> +       gsc_set_frame_size(frame, pix->width, pix->height);
>> +
>> +       if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
>> +               gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
>> +       else
>> +               gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
>> +
>> +       gsc_dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
>> +
>> +       return 0;
>> +}
>> +
>> +static int gsc_m2m_reqbufs(struct file *file, void *fh,
>> +                         struct v4l2_requestbuffers *reqbufs)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +       struct gsc_frame *frame;
>> +       u32 max_cnt;
>> +
>> +       max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
>> +               gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
>> +       if (reqbufs->count > max_cnt)
>> +               return -EINVAL;
>
> Needs braces.

Ok. I will fix.

>
>> +       else if (reqbufs->count == 0) {
>> +               if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
>> +                       gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
>> +               else
>> +                       gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
>> +       }
>> +
>> +       frame = ctx_get_frame(ctx, reqbufs->type);
>> +
>> +       return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
>> +}
>> +
>> +static int gsc_m2m_querybuf(struct file *file, void *fh,
>> +                                       struct v4l2_buffer *buf)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
>> +}
>> +
>> +static int gsc_m2m_qbuf(struct file *file, void *fh,
>> +                         struct v4l2_buffer *buf)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
>> +}
>> +
>> +static int gsc_m2m_dqbuf(struct file *file, void *fh,
>> +                          struct v4l2_buffer *buf)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
>> +}
>> +
>> +static int gsc_m2m_streamon(struct file *file, void *fh,
>> +                          enum v4l2_buf_type type)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +
>> +       /* The source and target color format need to be set */
>> +       if (V4L2_TYPE_IS_OUTPUT(type)) {
>> +               if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
>> +                       return -EINVAL;
>> +       } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
>> +               return -EINVAL;
>> +       }
>> +
>> +       return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
>> +}
>> +
>> +static int gsc_m2m_streamoff(struct file *file, void *fh,
>> +                           enum v4l2_buf_type type)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
>> +}
>> +
>> +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
>> +static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
>> +{
>> +       if (a->left < b->left || a->top < b->top)
>> +               return 0;
>> +
>> +       if (a->left + a->width > b->left + b->width)
>> +               return 0;
>> +
>> +       if (a->top + a->height > b->top + b->height)
>> +               return 0;
>> +
>> +       return 1;
>> +}
>> +
>> +static int gsc_m2m_g_selection(struct file *file, void *fh,
>> +                       struct v4l2_selection *s)
>> +{
>> +       struct gsc_frame *frame;
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +
>> +       if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
>> +           (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>> +               return -EINVAL;
>> +
>> +       frame = ctx_get_frame(ctx, s->type);
>> +       if (IS_ERR(frame))
>> +               return PTR_ERR(frame);
>> +
>> +       switch (s->target) {
>> +       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
>> +       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
>> +       case V4L2_SEL_TGT_CROP_BOUNDS:
>> +       case V4L2_SEL_TGT_CROP_DEFAULT:
>> +               s->r.left = 0;
>> +               s->r.top = 0;
>> +               s->r.width = frame->f_width;
>> +               s->r.height = frame->f_height;
>> +               return 0;
>> +
>> +       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
>> +       case V4L2_SEL_TGT_CROP_ACTIVE:
>> +               s->r.left = frame->crop.left;
>> +               s->r.top = frame->crop.top;
>> +               s->r.width = frame->crop.width;
>> +               s->r.height = frame->crop.height;
>> +               return 0;
>> +       }
>> +
>> +       return -EINVAL;
>> +}
>> +
>> +static int gsc_m2m_s_selection(struct file *file, void *fh,
>> +                       struct v4l2_selection *s)
>> +{
>> +       struct gsc_frame *frame;
>> +       struct gsc_ctx *ctx = fh_to_ctx(fh);
>> +       struct v4l2_crop cr;
>> +       struct gsc_variant *variant = ctx->gsc_dev->variant;
>> +       int ret;
>> +
>> +       cr.type = s->type;
>> +       cr.c = s->r;
>> +
>> +       if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
>> +           (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>> +               return -EINVAL;
>> +
>> +       ret = gsc_try_crop(ctx, &cr);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (s->flags & V4L2_SEL_FLAG_LE &&
>> +           !is_rectangle_enclosed(&cr.c, &s->r))
>> +               return -ERANGE;
>> +
>> +       if (s->flags & V4L2_SEL_FLAG_GE &&
>> +           !is_rectangle_enclosed(&s->r, &cr.c))
>> +               return -ERANGE;
>> +
>> +       s->r = cr.c;
>> +
>> +       switch (s->target) {
>> +       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
>> +       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
>> +       case V4L2_SEL_TGT_COMPOSE_ACTIVE:
>> +               frame = &ctx->s_frame;
>> +               break;
>> +
>> +       case V4L2_SEL_TGT_CROP_BOUNDS:
>> +       case V4L2_SEL_TGT_CROP_ACTIVE:
>> +       case V4L2_SEL_TGT_CROP_DEFAULT:
>> +               frame = &ctx->d_frame;
>> +               break;
>> +
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +
>> +       /* Check to see if scaling ratio is within supported range */
>> +       if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
>> +               if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
>> +                       ret = gsc_check_scaler_ratio(variant, cr.c.width,
>> +                               cr.c.height, ctx->d_frame.crop.width,
>> +                               ctx->d_frame.crop.height,
>> +                               ctx->gsc_ctrls.rotate->val, ctx->out_path);
>> +               } else {
>> +                       ret = gsc_check_scaler_ratio(variant,
>> +                               ctx->s_frame.crop.width,
>> +                               ctx->s_frame.crop.height, cr.c.width,
>> +                               cr.c.height, ctx->gsc_ctrls.rotate->val,
>> +                               ctx->out_path);
>> +               }
>> +
>> +               if (ret) {
>> +                       gsc_err("Out of scaler range");
>> +                       return -EINVAL;
>> +               }
>> +       }
>> +
>> +       frame->crop = cr.c;
>> +
>> +       gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
>> +       return 0;
>> +}
>> +
>> +static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
>> +       .vidioc_querycap                = gsc_m2m_querycap,
>> +
>> +       .vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane,
>> +       .vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane,
>> +
>> +       .vidioc_g_fmt_vid_cap_mplane    = gsc_m2m_g_fmt_mplane,
>> +       .vidioc_g_fmt_vid_out_mplane    = gsc_m2m_g_fmt_mplane,
>> +
>> +       .vidioc_try_fmt_vid_cap_mplane  = gsc_m2m_try_fmt_mplane,
>> +       .vidioc_try_fmt_vid_out_mplane  = gsc_m2m_try_fmt_mplane,
>> +
>> +       .vidioc_s_fmt_vid_cap_mplane    = gsc_m2m_s_fmt_mplane,
>> +       .vidioc_s_fmt_vid_out_mplane    = gsc_m2m_s_fmt_mplane,
>> +
>> +       .vidioc_reqbufs                 = gsc_m2m_reqbufs,
>> +       .vidioc_querybuf                = gsc_m2m_querybuf,
>> +
>> +       .vidioc_qbuf                    = gsc_m2m_qbuf,
>> +       .vidioc_dqbuf                   = gsc_m2m_dqbuf,
>> +
>> +       .vidioc_streamon                = gsc_m2m_streamon,
>> +       .vidioc_streamoff               = gsc_m2m_streamoff,
>> +
>> +       .vidioc_g_selection             = gsc_m2m_g_selection,
>> +       .vidioc_s_selection             = gsc_m2m_s_selection
>> +
>> +};
>> +
>> +static int queue_init(void *priv, struct vb2_queue *src_vq,
>> +                     struct vb2_queue *dst_vq)
>> +{
>> +       struct gsc_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->drv_priv = ctx;
>> +       src_vq->ops = &gsc_m2m_qops;
>> +       src_vq->mem_ops = &vb2_dma_contig_memops;
>> +       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->drv_priv = ctx;
>> +       dst_vq->ops = &gsc_m2m_qops;
>> +       dst_vq->mem_ops = &vb2_dma_contig_memops;
>> +       dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
>> +
>> +       return vb2_queue_init(dst_vq);
>> +}
>> +
>> +static int gsc_m2m_open(struct file *file)
>> +{
>> +       struct gsc_dev *gsc = video_drvdata(file);
>> +       struct gsc_ctx *ctx = NULL;
>> +       int ret;
>> +
>> +       gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
>> +
>> +       ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
>> +       if (!ctx)
>> +               return -ENOMEM;
>> +
>> +       v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
>> +       ret = gsc_ctrls_create(ctx);
>> +       if (ret)
>> +               goto error_fh;
>> +
>> +       /* Use separate control handler per file handle */
>> +       ctx->fh.ctrl_handler = &ctx->ctrl_handler;
>> +       file->private_data = &ctx->fh;
>> +       v4l2_fh_add(&ctx->fh);
>> +
>> +       ctx->gsc_dev = gsc;
>> +       /* Default color format */
>> +       ctx->s_frame.fmt = get_format(0);
>> +       ctx->d_frame.fmt = get_format(0);
>> +       /* Setup the device context for mem2mem mode. */
>> +       ctx->state |= GSC_CTX_M2M;
>> +       ctx->flags = 0;
>> +       ctx->in_path = GSC_DMA;
>> +       ctx->out_path = GSC_DMA;
>> +       spin_lock_init(&ctx->slock);
>> +
>> +       ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
>> +       if (IS_ERR(ctx->m2m_ctx)) {
>> +               gsc_err("Failed to initialize m2m context");
>> +               ret = PTR_ERR(ctx->m2m_ctx);
>> +               goto error_fh;
>> +       }
>> +
>> +       if (gsc->m2m.refcnt++ == 0)
>> +               set_bit(ST_M2M_OPEN, &gsc->state);
>> +
>> +       gsc_dbg("gsc m2m driver is opened, ctx(0x%p)", ctx);
>> +       return 0;
>> +
>> +error_fh:
>
> No need to ctrls_delete somewhere here as well?
>

Ok. I will fix.

>> +       v4l2_fh_del(&ctx->fh);
>> +       v4l2_fh_exit(&ctx->fh);
>> +       kfree(ctx);
>> +       return ret;
>> +}
>> +
>> +static int gsc_m2m_release(struct file *file)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
>> +       struct gsc_dev *gsc = ctx->gsc_dev;
>> +
>> +       gsc_dbg("pid: %d, state: 0x%lx, refcnt= %d",
>> +               task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
>> +
>> +       v4l2_m2m_ctx_release(ctx->m2m_ctx);
>> +       gsc_ctrls_delete(ctx);
>> +       v4l2_fh_del(&ctx->fh);
>> +       v4l2_fh_exit(&ctx->fh);
>> +
>> +       if (--gsc->m2m.refcnt <= 0)
>> +               clear_bit(ST_M2M_OPEN, &gsc->state);
>> +       kfree(ctx);
>> +       return 0;
>> +}
>> +
>> +static unsigned int gsc_m2m_poll(struct file *file,
>> +                                    struct poll_table_struct *wait)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
>> +
>> +       return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
>> +}
>> +
>> +static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
>> +{
>> +       struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
>> +
>> +       return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
>> +}
>> +static const struct v4l2_file_operations gsc_m2m_fops = {
>> +       .owner          = THIS_MODULE,
>> +       .open           = gsc_m2m_open,
>> +       .release        = gsc_m2m_release,
>> +       .poll           = gsc_m2m_poll,
>> +       .unlocked_ioctl = video_ioctl2,
>> +       .mmap           = gsc_m2m_mmap,
>> +};
>> +
>> +static struct v4l2_m2m_ops gsc_m2m_ops = {
>> +       .device_run     = gsc_m2m_device_run,
>> +       .job_abort      = gsc_m2m_job_abort,
>> +};
>> +
>> +int gsc_register_m2m_device(struct gsc_dev *gsc)
>> +{
>> +       struct video_device *vfd;
>> +       struct platform_device *pdev;
>> +       int ret = 0;
>> +
>> +       if (!gsc)
>> +               return -ENODEV;
>> +
>> +       pdev = gsc->pdev;
>> +
>> +       vfd = video_device_alloc();
>> +       if (!vfd) {
>> +               dev_err(&pdev->dev, "Failed to allocate video device\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       vfd->fops       = &gsc_m2m_fops;
>> +       vfd->ioctl_ops  = &gsc_m2m_ioctl_ops;
>> +       vfd->release    = video_device_release;
>> +       vfd->lock       = &gsc->lock;
>> +       snprintf(vfd->name, sizeof(vfd->name), "%s.%d:m2m",
>> +                                       GSC_MODULE_NAME, gsc->id);
>> +
>> +       video_set_drvdata(vfd, gsc);
>> +
>> +       gsc->m2m.vfd = vfd;
>> +       gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
>> +       if (IS_ERR(gsc->m2m.m2m_dev)) {
>> +               dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
>> +               ret = PTR_ERR(gsc->m2m.m2m_dev);
>> +               goto err_m2m_r1;
>> +       }
>> +
>> +       ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
>> +       if (ret) {
>> +               dev_err(&pdev->dev,
>> +                        "%s(): failed to register video device\n", __func__);
>> +               goto err_m2m_r2;
>> +       }
>> +
>> +       gsc_dbg("gsc m2m driver registered as /dev/video%d", vfd->num);
>> +
>> +       return 0;
>> +
>> +err_m2m_r2:
>> +       v4l2_m2m_release(gsc->m2m.m2m_dev);
>> +err_m2m_r1:
>> +       video_device_release(gsc->m2m.vfd);
>> +
>> +       return ret;
>> +}
>> +
>> +void gsc_unregister_m2m_device(struct gsc_dev *gsc)
>> +{
>> +       if (gsc)
>> +               v4l2_m2m_release(gsc->m2m.m2m_dev);
>> +}
>> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c
>> new file mode 100644
>> index 0000000..7c5cce2
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/gsc-regs.c
>> @@ -0,0 +1,579 @@
>> +/* linux/drivers/media/video/exynos/gsc/gsc-regs.c
>> + *
>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>> + *             http://www.samsung.com
>> + *
>> + * Samsung EXYNOS5 SoC series G-scaler driver
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/delay.h>
>> +#include <mach/map.h>
>> +#include "gsc-core.h"
>> +
>> +void gsc_hw_set_sw_reset(struct gsc_dev *dev)
>> +{
>> +       u32 cfg = 0;
>> +
>> +       cfg |= GSC_SW_RESET_SRESET;
>> +       writel(cfg, dev->regs + GSC_SW_RESET);
>> +}
>> +
>> +int gsc_wait_reset(struct gsc_dev *dev)
>> +{
>> +       unsigned long timeo = jiffies + 10; /* timeout of 50ms */
>> +       u32 cfg;
>> +
>> +       while (time_before(jiffies, timeo)) {
>> +               cfg = readl(dev->regs + GSC_SW_RESET);
>> +               if (!cfg)
>> +                       return 0;
>> +               usleep_range(10, 20);
>> +       }
>> +       gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
>> +
>> +       return -EBUSY;
>> +}
>> +
>> +int gsc_wait_operating(struct gsc_dev *dev)
>> +{
>> +       unsigned long timeo = jiffies + 10; /* timeout of 50ms */
>> +       u32 cfg;
>> +
>> +       while (time_before(jiffies, timeo)) {
>> +               cfg = readl(dev->regs + GSC_ENABLE);
>> +               if ((cfg & GSC_ENABLE_OP_STATUS) == GSC_ENABLE_OP_STATUS)
>> +                       return 0;
>> +               usleep_range(10, 20);
>> +       }
>> +       gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
>> +
>> +       return -EBUSY;
>> +}
>> +
>> +int gsc_wait_stop(struct gsc_dev *dev)
>> +{
>> +       unsigned long timeo = jiffies + 10; /* timeout of 50ms */
>> +       u32 cfg;
>> +
>> +       while (time_before(jiffies, timeo)) {
>> +               cfg = readl(dev->regs + GSC_ENABLE);
>> +               if (!(cfg & GSC_ENABLE_OP_STATUS))
>> +                       return 0;
>> +               usleep_range(10, 20);
>> +       }
>> +       gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
>> +
>> +       return -EBUSY;
>> +}
>> +
>> +
>> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask)
>> +{
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_ENABLE);
>> +       if (mask)
>> +               cfg |= GSC_ENABLE_ON_CLEAR;
>> +       else
>> +               cfg &= ~GSC_ENABLE_ON_CLEAR;
>> +       writel(cfg, dev->regs + GSC_ENABLE);
>> +}
>> +
>> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev)
>> +{
>> +       u32 cfg, status, bits = 0;
>> +
>> +       cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
>> +       status = cfg & GSC_IN_BASE_ADDR_MASK;
>> +       while (status) {
>> +               status = status & (status - 1);
>> +               bits++;
>> +       }
>> +       return bits;
>> +}
>> +
>> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev)
>> +{
>> +       u32 cfg, curr_index, i;
>> +
>> +       cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
>> +       curr_index = GSC_IN_CURR_GET_INDEX(cfg);
>> +       for (i = curr_index; i > 1; i--) {
>> +               if (cfg ^ (1 << (i - 2)))
>> +                       return i - 2;
>> +       }
>> +
>> +       for (i = dev->variant->in_buf_cnt; i > curr_index; i--) {
>> +               if (cfg ^ (1 << (i - 1)))
>> +                       return i - 1;
>> +       }
>> +
>> +       return curr_index - 1;
>> +}
>> +
>> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask)
>> +{
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_IRQ);
>> +       if (mask)
>> +               cfg |= GSC_IRQ_FRMDONE_MASK;
>> +       else
>> +               cfg &= ~GSC_IRQ_FRMDONE_MASK;
>> +       writel(cfg, dev->regs + GSC_IRQ);
>> +}
>> +
>> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask)
>> +{
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_IRQ);
>> +       if (mask)
>> +               cfg |= GSC_IRQ_OR_MASK;
>> +       else
>> +               cfg &= ~GSC_IRQ_OR_MASK;
>> +       writel(cfg, dev->regs + GSC_IRQ);
>> +}
>> +
>> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask)
>> +{
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_IRQ);
>> +       if (mask)
>> +               cfg |= GSC_IRQ_ENABLE;
>> +       else
>> +               cfg &= ~GSC_IRQ_ENABLE;
>> +       writel(cfg, dev->regs + GSC_IRQ);
>> +}
>> +
>> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev)
>> +{
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
>> +       cfg |= GSC_IN_BASE_ADDR_MASK;
>> +       cfg |= GSC_IN_BASE_ADDR_PINGPONG(dev->variant->in_buf_cnt);
>> +
>> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
>> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
>> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
>> +}
>> +
>> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev)
>> +{
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
>> +       cfg |= GSC_OUT_BASE_ADDR_MASK;
>> +       cfg |= GSC_OUT_BASE_ADDR_PINGPONG(dev->variant->out_buf_cnt);
>> +
>> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
>> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
>> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
>> +}
>> +
>> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift,
>> +                               bool enable)
>> +{
>> +       u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
>> +       u32 mask = 1 << shift;
>> +
>> +       cfg &= (~mask);
>> +       cfg |= enable << shift;
>> +
>> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
>> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
>> +       writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
>> +}
>> +
>> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
>> +                               bool enable)
>> +{
>> +       u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
>> +       u32 mask = 1 << shift;
>> +
>> +       cfg &= (~mask);
>> +       cfg |= enable << shift;
>> +
>> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
>> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
>> +       writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
>> +}
>> +
>> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev)
>> +{
>> +       u32 bits = 0;
>> +       u32 mask_bits = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
>> +       mask_bits &= GSC_OUT_BASE_ADDR_MASK;
>> +
>> +       while (mask_bits) {
>> +               mask_bits = mask_bits & (mask_bits - 1);
>
> mask_bits &=
>
>> +               bits++;
>> +       }
>> +       bits = 16 - bits;
>> +
>> +       return bits;
>> +}
>> +
>> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
>> +                               int index)
>> +{
>> +       gsc_dbg("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
>> +               addr->y, addr->cb, addr->cr);
>> +       writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
>> +       writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
>> +       writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
>> +
>> +}
>> +
>> +void gsc_hw_set_output_addr(struct gsc_dev *dev,
>> +                            struct gsc_addr *addr, int index)
>> +{
>> +       gsc_dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
>> +                       index, addr->y, addr->cb, addr->cr);
>> +       writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
>> +       writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
>> +       writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
>> +}
>> +
>> +void gsc_hw_set_input_path(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +
>> +       u32 cfg = readl(dev->regs + GSC_IN_CON);
>> +       cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
>> +
>> +       if (ctx->in_path == GSC_DMA)
>> +               cfg |= GSC_IN_PATH_MEMORY;
>> +
>> +       writel(cfg, dev->regs + GSC_IN_CON);
>> +}
>> +
>> +void gsc_hw_set_in_size(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_frame *frame = &ctx->s_frame;
>> +       u32 cfg;
>> +
>> +       /* Set input pixel offset */
>> +       cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left);
>> +       cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top);
>> +       writel(cfg, dev->regs + GSC_SRCIMG_OFFSET);
>> +
>> +       /* Set input original size */
>> +       cfg = GSC_SRCIMG_WIDTH(frame->f_width);
>> +       cfg |= GSC_SRCIMG_HEIGHT(frame->f_height);
>> +       writel(cfg, dev->regs + GSC_SRCIMG_SIZE);
>> +
>> +       /* Set input cropped size */
>> +       cfg = GSC_CROPPED_WIDTH(frame->crop.width);
>> +       cfg |= GSC_CROPPED_HEIGHT(frame->crop.height);
>> +       writel(cfg, dev->regs + GSC_CROPPED_SIZE);
>> +}
>> +
>> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_frame *frame = &ctx->s_frame;
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_IN_CON);
>> +       if (ctx->gsc_ctrls.csc_eq->val) {
>> +               if (ctx->gsc_ctrls.csc_range->val)
>> +                       cfg |= GSC_IN_RGB_HD_WIDE;
>> +               else
>> +                       cfg |= GSC_IN_RGB_HD_NARROW;
>> +       } else {
>> +               if (ctx->gsc_ctrls.csc_range->val)
>> +                       cfg |= GSC_IN_RGB_SD_WIDE;
>> +               else
>> +                       cfg |= GSC_IN_RGB_SD_NARROW;
>> +       }
>> +
>> +       if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
>> +               cfg |= GSC_IN_RGB565;
>> +       else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
>> +               cfg |= GSC_IN_XRGB8888;
>> +
>> +       writel(cfg, dev->regs + GSC_IN_CON);
>> +}
>> +
>> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_frame *frame = &ctx->s_frame;
>> +       u32 i, depth = 0;
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_IN_CON);
>> +       cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
>> +                GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK |
>> +                GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE);
>> +       writel(cfg, dev->regs + GSC_IN_CON);
>> +
>> +       if (is_rgb(frame->fmt->color)) {
>> +               gsc_hw_set_in_image_rgb(ctx);
>> +               return;
>> +       }
>> +       for (i = 0; i < frame->fmt->num_planes; i++)
>> +               depth += frame->fmt->depth[i];
>> +
>> +       switch (frame->fmt->num_comp) {
>> +       case 1:
>> +               cfg |= GSC_IN_YUV422_1P;
>> +               if (frame->fmt->yorder == GSC_LSB_Y)
>> +                       cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y;
>> +               else
>> +                       cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C;
>> +               if (frame->fmt->corder == GSC_CBCR)
>> +                       cfg |= GSC_IN_CHROMA_ORDER_CBCR;
>> +               else
>> +                       cfg |= GSC_IN_CHROMA_ORDER_CRCB;
>> +               break;
>> +       case 2:
>> +               if (depth == 12)
>> +                       cfg |= GSC_IN_YUV420_2P;
>> +               else
>> +                       cfg |= GSC_IN_YUV422_2P;
>> +               if (frame->fmt->corder == GSC_CBCR)
>> +                       cfg |= GSC_IN_CHROMA_ORDER_CBCR;
>> +               else
>> +                       cfg |= GSC_IN_CHROMA_ORDER_CRCB;
>> +               break;
>> +       case 3:
>> +               if (depth == 12)
>> +                       cfg |= GSC_IN_YUV420_3P;
>> +               else
>> +                       cfg |= GSC_IN_YUV422_3P;
>> +               break;
>> +       };
>> +
>> +       if (is_tiled(frame->fmt))
>> +               cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE;
>> +
>> +       writel(cfg, dev->regs + GSC_IN_CON);
>> +}
>> +
>> +void gsc_hw_set_output_path(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +
>> +       u32 cfg = readl(dev->regs + GSC_OUT_CON);
>> +       cfg &= ~GSC_OUT_PATH_MASK;
>> +
>> +       if (ctx->out_path == GSC_DMA)
>> +               cfg |= GSC_OUT_PATH_MEMORY;
>> +       else
>> +               cfg |= GSC_OUT_PATH_LOCAL;
>> +
>> +       writel(cfg, dev->regs + GSC_OUT_CON);
>> +}
>> +
>> +void gsc_hw_set_out_size(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_frame *frame = &ctx->d_frame;
>> +       u32 cfg;
>> +
>> +       /* Set output original size */
>> +       if (ctx->out_path == GSC_DMA) {
>> +               cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left);
>> +               cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top);
>> +               writel(cfg, dev->regs + GSC_DSTIMG_OFFSET);
>> +
>> +               cfg = GSC_DSTIMG_WIDTH(frame->f_width);
>> +               cfg |= GSC_DSTIMG_HEIGHT(frame->f_height);
>> +               writel(cfg, dev->regs + GSC_DSTIMG_SIZE);
>> +       }
>> +
>> +       /* Set output scaled size */
>> +       if (ctx->gsc_ctrls.rotate->val == 90 ||
>> +           ctx->gsc_ctrls.rotate->val == 270) {
>> +               cfg = GSC_SCALED_WIDTH(frame->crop.height);
>> +               cfg |= GSC_SCALED_HEIGHT(frame->crop.width);
>> +       } else {
>> +               cfg = GSC_SCALED_WIDTH(frame->crop.width);
>> +               cfg |= GSC_SCALED_HEIGHT(frame->crop.height);
>> +       }
>> +       writel(cfg, dev->regs + GSC_SCALED_SIZE);
>> +}
>> +
>> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_frame *frame = &ctx->d_frame;
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_OUT_CON);
>> +       if (ctx->gsc_ctrls.csc_eq->val) {
>> +               if (ctx->gsc_ctrls.csc_range->val)
>> +                       cfg |= GSC_OUT_RGB_HD_WIDE;
>> +               else
>> +                       cfg |= GSC_OUT_RGB_HD_NARROW;
>> +       } else {
>> +               if (ctx->gsc_ctrls.csc_range->val)
>> +                       cfg |= GSC_OUT_RGB_SD_WIDE;
>> +               else
>> +                       cfg |= GSC_OUT_RGB_SD_NARROW;
>> +       }
>> +
>> +       if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
>> +               cfg |= GSC_OUT_RGB565;
>> +       else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
>> +               cfg |= GSC_OUT_XRGB8888;
>> +
>> +       writel(cfg, dev->regs + GSC_OUT_CON);
>> +}
>> +
>> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_frame *frame = &ctx->d_frame;
>> +       u32 i, depth = 0;
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_OUT_CON);
>> +       cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
>> +                GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK |
>> +                GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE);
>> +       writel(cfg, dev->regs + GSC_OUT_CON);
>> +
>> +       if (is_rgb(frame->fmt->color)) {
>> +               gsc_hw_set_out_image_rgb(ctx);
>> +               return;
>> +       }
>> +
>> +       if (ctx->out_path != GSC_DMA) {
>> +               cfg |= GSC_OUT_YUV444;
>> +               goto end_set;
>> +       }
>> +
>> +       for (i = 0; i < frame->fmt->num_planes; i++)
>> +               depth += frame->fmt->depth[i];
>> +
>> +       switch (frame->fmt->num_comp) {
>> +       case 1:
>> +               cfg |= GSC_OUT_YUV422_1P;
>> +               if (frame->fmt->yorder == GSC_LSB_Y)
>> +                       cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y;
>> +               else
>> +                       cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C;
>> +               if (frame->fmt->corder == GSC_CBCR)
>> +                       cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
>> +               else
>> +                       cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
>> +               break;
>> +       case 2:
>> +               if (depth == 12)
>> +                       cfg |= GSC_OUT_YUV420_2P;
>> +               else
>> +                       cfg |= GSC_OUT_YUV422_2P;
>> +               if (frame->fmt->corder == GSC_CBCR)
>> +                       cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
>> +               else
>> +                       cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
>> +               break;
>> +       case 3:
>> +               cfg |= GSC_OUT_YUV420_3P;
>> +               break;
>> +       };
>> +
>> +       if (is_tiled(frame->fmt))
>> +               cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE;
>> +
>> +end_set:
>> +       writel(cfg, dev->regs + GSC_OUT_CON);
>> +}
>> +
>> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_scaler *sc = &ctx->scaler;
>> +       u32 cfg;
>> +
>> +       cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor);
>> +       cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio);
>> +       cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio);
>> +       writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO);
>> +}
>> +
>> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_scaler *sc = &ctx->scaler;
>> +       u32 cfg;
>> +
>> +       cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
>> +       writel(cfg, dev->regs + GSC_MAIN_H_RATIO);
>> +
>> +       cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio);
>> +       writel(cfg, dev->regs + GSC_MAIN_V_RATIO);
>> +}
>> +
>> +void gsc_hw_set_rotation(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_IN_CON);
>> +       cfg &= ~GSC_IN_ROT_MASK;
>> +
>> +       switch (ctx->gsc_ctrls.rotate->val) {
>> +       case 270:
>> +               cfg |= GSC_IN_ROT_270;
>> +               break;
>> +       case 180:
>> +               cfg |= GSC_IN_ROT_180;
>> +               break;
>> +       case 90:
>> +               if (ctx->gsc_ctrls.hflip->val)
>> +                       cfg |= GSC_IN_ROT_90_XFLIP;
>> +               else if (ctx->gsc_ctrls.vflip->val)
>> +                       cfg |= GSC_IN_ROT_90_YFLIP;
>> +               else
>> +                       cfg |= GSC_IN_ROT_90;
>> +               break;
>> +       case 0:
>> +               if (ctx->gsc_ctrls.hflip->val)
>> +                       cfg |= GSC_IN_ROT_XFLIP;
>> +               else if (ctx->gsc_ctrls.vflip->val)
>> +                       cfg |= GSC_IN_ROT_YFLIP;
>> +       }
>> +
>> +       writel(cfg, dev->regs + GSC_IN_CON);
>> +}
>> +
>> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       struct gsc_frame *frame = &ctx->d_frame;
>> +       u32 cfg;
>> +
>> +       if (!is_rgb(frame->fmt->color)) {
>> +               gsc_dbg("Not a RGB format");
>> +               return;
>> +       }
>> +
>> +       cfg = readl(dev->regs + GSC_OUT_CON);
>> +       cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK;
>> +
>> +       cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val);
>> +       writel(cfg, dev->regs + GSC_OUT_CON);
>> +}
>> +
>> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx)
>> +{
>> +       struct gsc_dev *dev = ctx->gsc_dev;
>> +       u32 cfg;
>> +
>> +       cfg = readl(dev->regs + GSC_ENABLE);
>> +       cfg |= GSC_ENABLE_SFR_UPDATE;
>> +       writel(cfg, dev->regs + GSC_ENABLE);
>> +}
>> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.h b/drivers/media/video/exynos/gsc/gsc-regs.h
>> new file mode 100644
>> index 0000000..50ddabe
>> --- /dev/null
>> +++ b/drivers/media/video/exynos/gsc/gsc-regs.h
>> @@ -0,0 +1,211 @@
>> +/* linux/drivers/media/video/exynos/gsc/regs-gsc.h
>> + *
>> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
>> + *             http://www.samsung.com
>> + *
>> + * Register definition file for Samsung G-Scaler 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 REGS_GSC_H_
>> +#define REGS_GSC_H_
>> +
>> +/* G-Scaler enable */
>> +#define GSC_ENABLE                     0x00
>> +#define GSC_ENABLE_ON_CLEAR            (1 << 4)
>> +#define GSC_ENABLE_QOS_ENABLE          (1 << 3)
>> +#define GSC_ENABLE_OP_STATUS           (1 << 2)
>> +#define GSC_ENABLE_SFR_UPDATE          (1 << 1)
>> +#define GSC_ENABLE_ON                  (1 << 0)
>> +
>> +/* G-Scaler S/W reset */
>> +#define GSC_SW_RESET                   0x04
>> +#define GSC_SW_RESET_SRESET            (1 << 0)
>> +
>> +/* G-Scaler IRQ */
>> +#define GSC_IRQ                                0x08
>> +#define GSC_IRQ_STATUS_OR_IRQ          (1 << 17)
>> +#define GSC_IRQ_STATUS_OR_FRM_DONE     (1 << 16)
>> +#define GSC_IRQ_OR_MASK                        (1 << 2)
>> +#define GSC_IRQ_FRMDONE_MASK           (1 << 1)
>> +#define GSC_IRQ_ENABLE                 (1 << 0)
>> +
>> +/* G-Scaler input control */
>> +#define GSC_IN_CON                     0x10
>> +#define GSC_IN_ROT_MASK                        (7 << 16)
>> +#define GSC_IN_ROT_270                 (7 << 16)
>> +#define GSC_IN_ROT_90_YFLIP            (6 << 16)
>> +#define GSC_IN_ROT_90_XFLIP            (5 << 16)
>> +#define GSC_IN_ROT_90                  (4 << 16)
>> +#define GSC_IN_ROT_180                 (3 << 16)
>> +#define GSC_IN_ROT_YFLIP               (2 << 16)
>> +#define GSC_IN_ROT_XFLIP               (1 << 16)
>> +#define GSC_IN_RGB_TYPE_MASK           (3 << 14)
>> +#define GSC_IN_RGB_HD_WIDE             (3 << 14)
>> +#define GSC_IN_RGB_HD_NARROW           (2 << 14)
>> +#define GSC_IN_RGB_SD_WIDE             (1 << 14)
>> +#define GSC_IN_RGB_SD_NARROW           (0 << 14)
>> +#define GSC_IN_YUV422_1P_ORDER_MASK    (1 << 13)
>> +#define GSC_IN_YUV422_1P_ORDER_LSB_Y   (0 << 13)
>> +#define GSC_IN_YUV422_1P_OEDER_LSB_C   (1 << 13)
>> +#define GSC_IN_CHROMA_ORDER_MASK       (1 << 12)
>> +#define GSC_IN_CHROMA_ORDER_CBCR       (0 << 12)
>> +#define GSC_IN_CHROMA_ORDER_CRCB       (1 << 12)
>> +#define GSC_IN_FORMAT_MASK             (7 << 8)
>> +#define GSC_IN_XRGB8888                        (0 << 8)
>> +#define GSC_IN_RGB565                  (1 << 8)
>> +#define GSC_IN_YUV420_2P               (2 << 8)
>> +#define GSC_IN_YUV420_3P               (3 << 8)
>> +#define GSC_IN_YUV422_1P               (4 << 8)
>> +#define GSC_IN_YUV422_2P               (5 << 8)
>> +#define GSC_IN_YUV422_3P               (6 << 8)
>> +#define GSC_IN_TILE_TYPE_MASK          (1 << 4)
>> +#define GSC_IN_TILE_C_16x8             (0 << 4)
>> +#define GSC_IN_TILE_C_16x16            (1 << 4)
>> +#define GSC_IN_TILE_MODE               (1 << 3)
>> +#define GSC_IN_LOCAL_SEL_MASK          (3 << 1)
>> +#define GSC_IN_LOCAL_FIMD_WB           (2 << 1)
>> +#define GSC_IN_LOCAL_CAM1              (1 << 1)
>> +#define GSC_IN_LOCAL_CAM0              (0 << 1)
>> +#define GSC_IN_PATH_MASK               (1 << 0)
>> +#define GSC_IN_PATH_LOCAL              (1 << 0)
>> +#define GSC_IN_PATH_MEMORY             (0 << 0)
>> +
>> +/* G-Scaler source image size */
>> +#define GSC_SRCIMG_SIZE                        0x14
>> +#define GSC_SRCIMG_HEIGHT_MASK         (0x1fff << 16)
>> +#define GSC_SRCIMG_HEIGHT(x)           ((x) << 16)
>> +#define GSC_SRCIMG_WIDTH_MASK          (0x1fff << 0)
>> +#define GSC_SRCIMG_WIDTH(x)            ((x) << 0)
>> +
>> +/* G-Scaler source image offset */
>> +#define GSC_SRCIMG_OFFSET              0x18
>> +#define GSC_SRCIMG_OFFSET_Y_MASK       (0x1fff << 16)
>> +#define GSC_SRCIMG_OFFSET_Y(x)         ((x) << 16)
>> +#define GSC_SRCIMG_OFFSET_X_MASK       (0x1fff << 0)
>> +#define GSC_SRCIMG_OFFSET_X(x)         ((x) << 0)
>> +
>> +/* G-Scaler cropped source image size */
>> +#define GSC_CROPPED_SIZE               0x1c
>> +#define GSC_CROPPED_HEIGHT_MASK                (0x1fff << 16)
>> +#define GSC_CROPPED_HEIGHT(x)          ((x) << 16)
>> +#define GSC_CROPPED_WIDTH_MASK         (0x1fff << 0)
>> +#define GSC_CROPPED_WIDTH(x)           ((x) << 0)
>> +
>> +/* G-Scaler output control */
>> +#define GSC_OUT_CON                    0x20
>> +#define GSC_OUT_GLOBAL_ALPHA_MASK      (0xff << 24)
>> +#define GSC_OUT_GLOBAL_ALPHA(x)                ((x) << 24)
>> +#define GSC_OUT_RGB_TYPE_MASK          (3 << 10)
>> +#define GSC_OUT_RGB_HD_NARROW          (3 << 10)
>> +#define GSC_OUT_RGB_HD_WIDE            (2 << 10)
>> +#define GSC_OUT_RGB_SD_NARROW          (1 << 10)
>> +#define GSC_OUT_RGB_SD_WIDE            (0 << 10)
>> +#define GSC_OUT_YUV422_1P_ORDER_MASK   (1 << 9)
>> +#define GSC_OUT_YUV422_1P_ORDER_LSB_Y  (0 << 9)
>> +#define GSC_OUT_YUV422_1P_OEDER_LSB_C  (1 << 9)
>> +#define GSC_OUT_CHROMA_ORDER_MASK      (1 << 8)
>> +#define GSC_OUT_CHROMA_ORDER_CBCR      (0 << 8)
>> +#define GSC_OUT_CHROMA_ORDER_CRCB      (1 << 8)
>> +#define GSC_OUT_FORMAT_MASK            (7 << 4)
>> +#define GSC_OUT_XRGB8888               (0 << 4)
>> +#define GSC_OUT_RGB565                 (1 << 4)
>> +#define GSC_OUT_YUV420_2P              (2 << 4)
>> +#define GSC_OUT_YUV420_3P              (3 << 4)
>> +#define GSC_OUT_YUV422_1P              (4 << 4)
>> +#define GSC_OUT_YUV422_2P              (5 << 4)
>> +#define GSC_OUT_YUV444                 (7 << 4)
>> +#define GSC_OUT_TILE_TYPE_MASK         (1 << 2)
>> +#define GSC_OUT_TILE_C_16x8            (0 << 2)
>> +#define GSC_OUT_TILE_C_16x16           (1 << 2)
>> +#define GSC_OUT_TILE_MODE              (1 << 1)
>> +#define GSC_OUT_PATH_MASK              (1 << 0)
>> +#define GSC_OUT_PATH_LOCAL             (1 << 0)
>> +#define GSC_OUT_PATH_MEMORY            (0 << 0)
>> +
>> +/* G-Scaler scaled destination image size */
>> +#define GSC_SCALED_SIZE                        0x24
>> +#define GSC_SCALED_HEIGHT_MASK         (0x1fff << 16)
>> +#define GSC_SCALED_HEIGHT(x)           ((x) << 16)
>> +#define GSC_SCALED_WIDTH_MASK          (0x1fff << 0)
>> +#define GSC_SCALED_WIDTH(x)            ((x) << 0)
>> +
>> +/* G-Scaler pre scale ratio */
>> +#define GSC_PRE_SCALE_RATIO            0x28
>> +#define GSC_PRESC_SHFACTOR_MASK                (7 << 28)
>> +#define GSC_PRESC_SHFACTOR(x)          ((x) << 28)
>> +#define GSC_PRESC_V_RATIO_MASK         (7 << 16)
>> +#define GSC_PRESC_V_RATIO(x)           ((x) << 16)
>> +#define GSC_PRESC_H_RATIO_MASK         (7 << 0)
>> +#define GSC_PRESC_H_RATIO(x)           ((x) << 0)
>> +
>> +/* G-Scaler main scale horizontal ratio */
>> +#define GSC_MAIN_H_RATIO               0x2c
>> +#define GSC_MAIN_H_RATIO_MASK          (0xfffff << 0)
>> +#define GSC_MAIN_H_RATIO_VALUE(x)      ((x) << 0)
>> +
>> +/* G-Scaler main scale vertical ratio */
>> +#define GSC_MAIN_V_RATIO               0x30
>> +#define GSC_MAIN_V_RATIO_MASK          (0xfffff << 0)
>> +#define GSC_MAIN_V_RATIO_VALUE(x)      ((x) << 0)
>> +
>> +/* G-Scaler destination image size */
>> +#define GSC_DSTIMG_SIZE                        0x40
>> +#define GSC_DSTIMG_HEIGHT_MASK         (0x1fff << 16)
>> +#define GSC_DSTIMG_HEIGHT(x)           ((x) << 16)
>> +#define GSC_DSTIMG_WIDTH_MASK          (0x1fff << 0)
>> +#define GSC_DSTIMG_WIDTH(x)            ((x) << 0)
>> +
>> +/* G-Scaler destination image offset */
>> +#define GSC_DSTIMG_OFFSET              0x44
>> +#define GSC_DSTIMG_OFFSET_Y_MASK       (0x1fff << 16)
>> +#define GSC_DSTIMG_OFFSET_Y(x)         ((x) << 16)
>> +#define GSC_DSTIMG_OFFSET_X_MASK       (0x1fff << 0)
>> +#define GSC_DSTIMG_OFFSET_X(x)         ((x) << 0)
>> +
>> +/* G-Scaler input y address mask */
>> +#define GSC_IN_BASE_ADDR_Y_MASK                0x4c
>> +/* G-Scaler input y base address */
>> +#define GSC_IN_BASE_ADDR_Y(n)          (0x50 + (n) * 0x4)
>> +
>> +/* G-Scaler input cb address mask */
>> +#define GSC_IN_BASE_ADDR_CB_MASK       0x7c
>> +/* G-Scaler input cb base address */
>> +#define GSC_IN_BASE_ADDR_CB(n)         (0x80 + (n) * 0x4)
>> +
>> +/* G-Scaler input cr address mask */
>> +#define GSC_IN_BASE_ADDR_CR_MASK       0xac
>> +/* G-Scaler input cr base address */
>> +#define GSC_IN_BASE_ADDR_CR(n)         (0xb0 + (n) * 0x4)
>> +
>> +/* G-Scaler input address mask */
>> +#define GSC_IN_CURR_ADDR_INDEX         (0xf << 12)
>> +#define GSC_IN_CURR_GET_INDEX(x)       ((x) >> 12)
>> +#define GSC_IN_BASE_ADDR_PINGPONG(x)   ((x) << 8)
>> +#define GSC_IN_BASE_ADDR_MASK          (0xff << 0)
>> +
>> +/* G-Scaler output y address mask */
>> +#define GSC_OUT_BASE_ADDR_Y_MASK       0x10c
>> +/* G-Scaler output y base address */
>> +#define GSC_OUT_BASE_ADDR_Y(n)         (0x110 + (n) * 0x4)
>> +
>> +/* G-Scaler output cb address mask */
>> +#define GSC_OUT_BASE_ADDR_CB_MASK      0x15c
>> +/* G-Scaler output cb base address */
>> +#define GSC_OUT_BASE_ADDR_CB(n)                (0x160 + (n) * 0x4)
>> +
>> +/* G-Scaler output cr address mask */
>> +#define GSC_OUT_BASE_ADDR_CR_MASK      0x1ac
>> +/* G-Scaler output cr base address */
>> +#define GSC_OUT_BASE_ADDR_CR(n)                (0x1b0 + (n) * 0x4)
>> +
>> +/* G-Scaler output address mask */
>> +#define GSC_OUT_CURR_ADDR_INDEX                (0xf << 24)
>> +#define GSC_OUT_CURR_GET_INDEX(x)      ((x) >> 24)
>> +#define GSC_OUT_BASE_ADDR_PINGPONG(x)  ((x) << 16)
>> +#define GSC_OUT_BASE_ADDR_MASK         (0xffff << 0)
>
> Some of the defines in this file are not used like those above.
>

I will remove the unused code...

>> +
>> +#endif /* REGS_GSC_H_ */
>> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
>> index f79d0cc..5b4a33b 100644
>> --- a/include/linux/videodev2.h
>> +++ b/include/linux/videodev2.h
>> @@ -360,9 +360,11 @@ struct v4l2_pix_format {
>>  /* two non contiguous planes - one Y, one Cr + Cb interleaved  */
>>  #define V4L2_PIX_FMT_NV12M   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
>>  #define V4L2_PIX_FMT_NV12MT  v4l2_fourcc('T', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 64x32 macroblocks */
>
> Already defined in videodev2.h
>
>> +#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 16x16 macroblocks */
>>
>>  /* three non contiguous planes - Y, Cb, Cr */
>>  #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
>> +#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'V', 'U', 'M') /* 12  YVU420 planar */
>>
>>  /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
>>  #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */
>
> Some of those formats are defined in videodev2.h. For those that are not,
> you should add them there instead.
>
>> --
>> 1.7.0.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>
> --
> Best regards,
> Pawel Osciak


Regards,
Shaik Ameer Basha
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 99937c9..0ff1abf 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1153,6 +1153,7 @@  config VIDEO_ATMEL_ISI
 
 source "drivers/media/video/s5p-fimc/Kconfig"
 source "drivers/media/video/s5p-tv/Kconfig"
+source "drivers/media/video/exynos/Kconfig"
 
 endif # V4L_PLATFORM_DRIVERS
 endif # VIDEO_CAPTURE_DRIVERS
@@ -1215,4 +1216,13 @@  config VIDEO_MX2_EMMAPRP
 	    memory to memory. Operations include resizing and format
 	    conversion.
 
+config VIDEO_SAMSUNG_GSCALER
+        tristate "Samsung Exynos GSC driver"
+        depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+        select VIDEOBUF2_DMA_CONTIG
+        select V4L2_MEM2MEM_DEV
+        select DMA_SHARED_BUFFER
+        help
+            This is v4l2 based g-scaler driver for EXYNOS5
+
 endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index d209de0..4389f3e 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -192,6 +192,7 @@  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG)	+= s5p-jpeg/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
+obj-$(CONFIG_VIDEO_EXYNOS)              += exynos/
 
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D)	+= s5p-g2d/
 
diff --git a/drivers/media/video/exynos/Kconfig b/drivers/media/video/exynos/Kconfig
new file mode 100644
index 0000000..f5836a2
--- /dev/null
+++ b/drivers/media/video/exynos/Kconfig
@@ -0,0 +1,11 @@ 
+config VIDEO_EXYNOS
+	bool "Exynos Multimedia Devices"
+	depends on ARCH_EXYNOS5
+	select VIDEO_FIXED_MINOR_RANGES
+	select VIDEOBUF2_DMA_CONTIG
+	help
+	  This is a representative exynos multimedia device.
+
+if VIDEO_EXYNOS
+	source "drivers/media/video/exynos/gsc/Kconfig"
+endif
diff --git a/drivers/media/video/exynos/Makefile b/drivers/media/video/exynos/Makefile
new file mode 100644
index 0000000..1666724
--- /dev/null
+++ b/drivers/media/video/exynos/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_VIDEO_EXYNOS_GSCALER)      += gsc/
diff --git a/drivers/media/video/exynos/gsc/Kconfig b/drivers/media/video/exynos/gsc/Kconfig
new file mode 100644
index 0000000..9036078
--- /dev/null
+++ b/drivers/media/video/exynos/gsc/Kconfig
@@ -0,0 +1,7 @@ 
+config VIDEO_EXYNOS_GSCALER
+	bool "Exynos G-Scaler driver"
+	depends on VIDEO_EXYNOS
+	select MEDIA_EXYNOS
+	select V4L2_MEM2MEM_DEV
+	help
+	  This is a v4l2 driver for exynos G-Scaler device.
diff --git a/drivers/media/video/exynos/gsc/Makefile b/drivers/media/video/exynos/gsc/Makefile
new file mode 100644
index 0000000..d3f66e4
--- /dev/null
+++ b/drivers/media/video/exynos/gsc/Makefile
@@ -0,0 +1,3 @@ 
+gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_GSCALER)	+= gsc.o
diff --git a/drivers/media/video/exynos/gsc/gsc-core.c b/drivers/media/video/exynos/gsc/gsc-core.c
new file mode 100644
index 0000000..06bd24f
--- /dev/null
+++ b/drivers/media/video/exynos/gsc/gsc-core.c
@@ -0,0 +1,1304 @@ 
+/* linux/drivers/media/video/exynos/gsc/gsc-core.c
+ *
+ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Samsung EXYNOS5 SoC series G-scaler driver
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/of.h>
+#include "gsc-core.h"
+
+#define GSC_CLOCK_GATE_NAME	"gscl"
+
+int gsc_dbg;
+module_param(gsc_dbg, int, 0644);
+
+
+static struct gsc_fmt gsc_formats[] = {
+	{
+		.name		= "RGB565",
+		.pixelformat	= V4L2_PIX_FMT_RGB565X,
+		.depth		= { 16 },
+		.color		= GSC_RGB,
+		.num_planes	= 1,
+		.num_comp	= 1,
+	}, {
+		.name		= "XRGB-8-8-8-8, 32 bpp",
+		.pixelformat	= V4L2_PIX_FMT_RGB32,
+		.depth		= { 32 },
+		.color		= GSC_RGB,
+		.num_planes	= 1,
+		.num_comp	= 1,
+	}, {
+		.name		= "YUV 4:2:2 packed, YCbYCr",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.depth		= { 16 },
+		.color		= GSC_YUV422,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 1,
+		.num_comp	= 1,
+		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
+	}, {
+		.name		= "YUV 4:2:2 packed, CbYCrY",
+		.pixelformat	= V4L2_PIX_FMT_UYVY,
+		.depth		= { 16 },
+		.color		= GSC_YUV422,
+		.yorder		= GSC_LSB_C,
+		.corder		= GSC_CBCR,
+		.num_planes	= 1,
+		.num_comp	= 1,
+		.mbus_code	= V4L2_MBUS_FMT_UYVY8_2X8,
+	}, {
+		.name		= "YUV 4:2:2 packed, CrYCbY",
+		.pixelformat	= V4L2_PIX_FMT_VYUY,
+		.depth		= { 16 },
+		.color		= GSC_YUV422,
+		.yorder		= GSC_LSB_C,
+		.corder		= GSC_CRCB,
+		.num_planes	= 1,
+		.num_comp	= 1,
+		.mbus_code	= V4L2_MBUS_FMT_VYUY8_2X8,
+	}, {
+		.name		= "YUV 4:2:2 packed, YCrYCb",
+		.pixelformat	= V4L2_PIX_FMT_YVYU,
+		.depth		= { 16 },
+		.color		= GSC_YUV422,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CRCB,
+		.num_planes	= 1,
+		.num_comp	= 1,
+		.mbus_code	= V4L2_MBUS_FMT_YVYU8_2X8,
+	}, {
+		.name		= "YUV 4:4:4 planar, YCbYCr",
+		.pixelformat	= V4L2_PIX_FMT_YUV32,
+		.depth		= { 32 },
+		.color		= GSC_YUV444,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 1,
+		.num_comp	= 1,
+	}, {
+		.name		= "YUV 4:2:2 planar, Y/Cb/Cr",
+		.pixelformat	= V4L2_PIX_FMT_YUV422P,
+		.depth		= { 16 },
+		.color		= GSC_YUV422,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 1,
+		.num_comp	= 3,
+	}, {
+		.name		= "YUV 4:2:2 planar, Y/CbCr",
+		.pixelformat	= V4L2_PIX_FMT_NV16,
+		.depth		= { 16 },
+		.color		= GSC_YUV422,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 1,
+		.num_comp	= 2,
+	}, {
+		.name		= "YUV 4:2:2 planar, Y/CrCb",
+		.pixelformat	= V4L2_PIX_FMT_NV61,
+		.depth		= { 16 },
+		.color		= GSC_YUV422,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CRCB,
+		.num_planes	= 1,
+		.num_comp	= 2,
+	}, {
+		.name		= "YUV 4:2:0 planar, YCbCr",
+		.pixelformat	= V4L2_PIX_FMT_YUV420,
+		.depth		= { 12 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 1,
+		.num_comp	= 3,
+	}, {
+		.name		= "YUV 4:2:0 planar, YCbCr",
+		.pixelformat	= V4L2_PIX_FMT_YVU420,
+		.depth		= { 12 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CRCB,
+		.num_planes	= 1,
+		.num_comp	= 3,
+
+	}, {
+		.name		= "YUV 4:2:0 planar, Y/CbCr",
+		.pixelformat	= V4L2_PIX_FMT_NV12,
+		.depth		= { 12 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 1,
+		.num_comp	= 2,
+	}, {
+		.name		= "YUV 4:2:0 planar, Y/CrCb",
+		.pixelformat	= V4L2_PIX_FMT_NV21,
+		.depth		= { 12 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CRCB,
+		.num_planes	= 1,
+		.num_comp	= 2,
+	}, {
+		.name		= "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
+		.pixelformat	= V4L2_PIX_FMT_NV12M,
+		.depth		= { 8, 4 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 2,
+		.num_comp	= 2,
+	}, {
+		.name		= "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
+		.pixelformat	= V4L2_PIX_FMT_YUV420M,
+		.depth		= { 8, 2, 2 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 3,
+		.num_comp	= 3,
+	}, {
+		.name		= "YUV 4:2:0 non-contiguous 3-planar, Y/Cr/Cb",
+		.pixelformat	= V4L2_PIX_FMT_YVU420M,
+		.depth		= { 8, 2, 2 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CRCB,
+		.num_planes	= 3,
+		.num_comp	= 3,
+	}, {
+		.name		=
+			"YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",
+		.pixelformat	= V4L2_PIX_FMT_NV12MT_16X16,
+		.depth		= { 8, 4 },
+		.color		= GSC_YUV420,
+		.yorder		= GSC_LSB_Y,
+		.corder		= GSC_CBCR,
+		.num_planes	= 2,
+		.num_comp	= 2,
+	},
+};
+
+struct gsc_fmt *get_format(int index)
+{
+	return &gsc_formats[index];
+}
+
+struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
+{
+	struct gsc_fmt *fmt, *def_fmt = NULL;
+	unsigned int i;
+
+	if (index >= ARRAY_SIZE(gsc_formats))
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(gsc_formats); ++i) {
+		fmt = get_format(i);
+		if (pixelformat && fmt->pixelformat == *pixelformat)
+			return fmt;
+		if (mbus_code && fmt->mbus_code == *mbus_code)
+			return fmt;
+		if (index == i)
+			def_fmt = fmt;
+	}
+	return def_fmt;
+
+}
+
+void gsc_set_frame_size(struct gsc_frame *frame, int width, int height)
+{
+	frame->f_width	= width;
+	frame->f_height	= height;
+	frame->crop.width = width;
+	frame->crop.height = height;
+	frame->crop.left = 0;
+	frame->crop.top = 0;
+}
+
+int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
+								u32 *ratio)
+{
+	if ((dst > src) || (dst >= src / var->poly_sc_down_max)) {
+		*ratio = 1;
+		return 0;
+	}
+
+	if ((src / var->poly_sc_down_max / var->pre_sc_down_max) > dst) {
+		gsc_err("scale ratio exceeded maximun scale down ratio(1/16)");
+		return -EINVAL;
+	}
+
+	*ratio = (dst > (src / 8)) ? 2 : 4;
+
+	return 0;
+}
+
+void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh)
+{
+	if (hratio == 4 && vratio == 4)
+		*sh = 4;
+	else if ((hratio == 4 && vratio == 2) ||
+		 (hratio == 2 && vratio == 4))
+		*sh = 3;
+	else if ((hratio == 4 && vratio == 1) ||
+		 (hratio == 1 && vratio == 4) ||
+		 (hratio == 2 && vratio == 2))
+		*sh = 2;
+	else if (hratio == 1 && vratio == 1)
+		*sh = 0;
+	else
+		*sh = 1;
+}
+
+void gsc_check_src_scale_info(struct gsc_variant *var,
+				struct gsc_frame *s_frame, u32 *wratio,
+				 u32 tx, u32 ty, u32 *hratio)
+{
+	int remainder = 0, walign, halign;
+
+	if (is_yuv420(s_frame->fmt->color)) {
+		walign = GSC_SC_ALIGN_4;
+		halign = GSC_SC_ALIGN_4;
+	} else if (is_yuv422(s_frame->fmt->color)) {
+		walign = GSC_SC_ALIGN_4;
+		halign = GSC_SC_ALIGN_2;
+	} else {
+		walign = GSC_SC_ALIGN_2;
+		halign = GSC_SC_ALIGN_2;
+	}
+
+	remainder = s_frame->crop.width % (*wratio * walign);
+	if (remainder) {
+		s_frame->crop.width -= remainder;
+		gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio);
+		gsc_info("cropped src width size is recalculated from %d to %d",
+			s_frame->crop.width + remainder, s_frame->crop.width);
+	}
+
+	remainder = s_frame->crop.height % (*hratio * halign);
+	if (remainder) {
+		s_frame->crop.height -= remainder;
+		gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio);
+		gsc_info("cropped src height size is recalculated from %d to %d",
+			s_frame->crop.height + remainder, s_frame->crop.height);
+	}
+}
+
+int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
+{
+	struct gsc_fmt *fmt;
+
+	fmt = find_fmt(NULL, NULL, f->index);
+	if (!fmt)
+		return -EINVAL;
+
+	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+	f->pixelformat = fmt->pixelformat;
+
+	return 0;
+}
+
+u32 get_plane_size(struct gsc_frame *frame, unsigned int plane)
+{
+	if (!frame || plane >= frame->fmt->num_planes) {
+		gsc_err("Invalid argument");
+		return 0;
+	}
+
+	return frame->payload[plane];
+}
+
+u32 get_plane_info(struct gsc_frame frm, u32 addr, u32 *index)
+{
+	if (frm.addr.y == addr) {
+		*index = 0;
+		return frm.addr.y;
+	} else if (frm.addr.cb == addr) {
+		*index = 1;
+		return frm.addr.cb;
+	} else if (frm.addr.cr == addr) {
+		*index = 2;
+		return frm.addr.cr;
+	} else {
+		gsc_err("Plane address is wrong");
+		return -EINVAL;
+	}
+}
+
+void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm)
+{
+	u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
+	f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
+
+	f_chk_addr = frm.addr.y;
+	f_chk_len = frm.payload[0];
+	if (frm.fmt->num_planes == 2) {
+		s_chk_addr = frm.addr.cb;
+		s_chk_len = frm.payload[1];
+	} else if (frm.fmt->num_planes == 3) {
+		u32 low_addr, low_plane, mid_addr, mid_plane;
+		u32 high_addr, high_plane;
+		u32 t_min, t_max;
+
+		t_min = min3(frm.addr.y, frm.addr.cb, frm.addr.cr);
+		low_addr = get_plane_info(frm, t_min, &low_plane);
+		t_max = max3(frm.addr.y, frm.addr.cb, frm.addr.cr);
+		high_addr = get_plane_info(frm, t_max, &high_plane);
+
+		mid_plane = 3 - (low_plane + high_plane);
+		if (mid_plane == 0)
+			mid_addr = frm.addr.y;
+		else if (mid_plane == 1)
+			mid_addr = frm.addr.cb;
+		else if (mid_plane == 2)
+			mid_addr = frm.addr.cr;
+		else
+			return;
+
+		f_chk_addr = low_addr;
+		if (mid_addr + frm.payload[mid_plane] - low_addr >
+		    high_addr + frm.payload[high_plane] - mid_addr) {
+			f_chk_len = frm.payload[low_plane];
+			s_chk_addr = mid_addr;
+			s_chk_len = high_addr +
+					frm.payload[high_plane] - mid_addr;
+		} else {
+			f_chk_len = mid_addr +
+					frm.payload[mid_plane] - low_addr;
+			s_chk_addr = high_addr;
+			s_chk_len = frm.payload[high_plane];
+		}
+	}
+	gsc_dbg("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
+		f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
+}
+
+int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
+{
+	struct gsc_dev *gsc = ctx->gsc_dev;
+	struct gsc_variant *variant = gsc->variant;
+	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+	struct gsc_fmt *fmt;
+	u32 max_w, max_h, mod_x, mod_y;
+	u32 min_w, min_h, tmp_w, tmp_h;
+	int i;
+
+	gsc_dbg("user put w: %d, h: %d", pix_mp->width, pix_mp->height);
+
+	fmt = find_fmt(&pix_mp->pixelformat, NULL, 0);
+	if (!fmt) {
+		gsc_err("pixelformat format (0x%X) invalid\n",
+						pix_mp->pixelformat);
+		return -EINVAL;
+	}
+
+	if (pix_mp->field == V4L2_FIELD_ANY)
+		pix_mp->field = V4L2_FIELD_NONE;
+	else if (pix_mp->field != V4L2_FIELD_NONE) {
+		gsc_err("Not supported field order(%d)\n", pix_mp->field);
+		return -EINVAL;
+	}
+
+	max_w = variant->pix_max->target_rot_dis_w;
+	max_h = variant->pix_max->target_rot_dis_h;
+	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+		mod_x = ffs(variant->pix_align->org_w) - 1;
+		if (is_yuv420(fmt->color))
+			mod_y = ffs(variant->pix_align->org_h) - 1;
+		else
+			mod_y = ffs(variant->pix_align->org_h) - 2;
+		min_w = variant->pix_min->org_w;
+		min_h = variant->pix_min->org_h;
+	} else {
+		mod_x = ffs(variant->pix_align->org_w) - 1;
+		if (is_yuv420(fmt->color))
+			mod_y = ffs(variant->pix_align->org_h) - 1;
+		else
+			mod_y = ffs(variant->pix_align->org_h) - 2;
+		min_w = variant->pix_min->target_rot_dis_w;
+		min_h = variant->pix_min->target_rot_dis_h;
+	}
+	gsc_dbg("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
+	     mod_x, mod_y, max_w, max_h);
+	/* To check if image size is modified to adjust parameter against
+	   hardware abilities */
+	tmp_w = pix_mp->width;
+	tmp_h = pix_mp->height;
+
+	v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
+		&pix_mp->height, min_h, max_h, mod_y, 0);
+	if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
+		gsc_info("Image size has been modified from %dx%d to %dx%d",
+			 tmp_w, tmp_h, pix_mp->width, pix_mp->height);
+
+	pix_mp->num_planes = fmt->num_planes;
+
+	if (ctx->gsc_ctrls.csc_eq_mode->val)
+		ctx->gsc_ctrls.csc_eq->val =
+			(pix_mp->width >= 1280) ? 1 : 0;
+	if (ctx->gsc_ctrls.csc_eq->val) /* HD */
+		pix_mp->colorspace = V4L2_COLORSPACE_REC709;
+	else	/* SD */
+		pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+
+	for (i = 0; i < pix_mp->num_planes; ++i) {
+		int bpl = (pix_mp->width * fmt->depth[i]) >> 3;
+		pix_mp->plane_fmt[i].bytesperline = bpl;
+		pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
+
+		gsc_dbg("[%d]: bpl: %d, sizeimage: %d",
+		    i, bpl, pix_mp->plane_fmt[i].sizeimage);
+	}
+
+	return 0;
+}
+
+int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
+{
+	struct gsc_frame *frame;
+	struct v4l2_pix_format_mplane *pix_mp;
+	int i;
+
+	frame = ctx_get_frame(ctx, f->type);
+	if (IS_ERR(frame))
+		return PTR_ERR(frame);
+
+	pix_mp = &f->fmt.pix_mp;
+
+	pix_mp->width		= frame->f_width;
+	pix_mp->height		= frame->f_height;
+	pix_mp->field		= V4L2_FIELD_NONE;
+	pix_mp->pixelformat	= frame->fmt->pixelformat;
+	pix_mp->colorspace	= V4L2_COLORSPACE_JPEG;
+	pix_mp->num_planes	= frame->fmt->num_planes;
+
+	for (i = 0; i < pix_mp->num_planes; ++i) {
+		pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
+			frame->fmt->depth[i]) / 8;
+		pix_mp->plane_fmt[i].sizeimage =
+			 pix_mp->plane_fmt[i].bytesperline * frame->f_height;
+	}
+
+	return 0;
+}
+
+void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
+{
+	if (tmp_w != *w || tmp_h != *h) {
+		gsc_info("Cropped size has been modified from %dx%d to %dx%d",
+							*w, *h, tmp_w, tmp_h);
+		*w = tmp_w;
+		*h = tmp_h;
+	}
+}
+
+int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
+{
+	struct gsc_frame *frame;
+
+	frame = ctx_get_frame(ctx, cr->type);
+	if (IS_ERR(frame))
+		return PTR_ERR(frame);
+
+	cr->c = frame->crop;
+
+	return 0;
+}
+
+int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
+{
+	struct gsc_frame *f;
+	struct gsc_dev *gsc = ctx->gsc_dev;
+	struct gsc_variant *variant = gsc->variant;
+	u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h;
+	u32 min_w, min_h, max_w, max_h;
+
+	if (cr->c.top < 0 || cr->c.left < 0) {
+		gsc_err("doesn't support negative values for top & left\n");
+		return -EINVAL;
+	}
+	gsc_dbg("user put w: %d, h: %d", cr->c.width, cr->c.height);
+
+	if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		f = &ctx->d_frame;
+	else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		f = &ctx->s_frame;
+	else
+		return -EINVAL;
+
+	max_w = f->f_width;
+	max_h = f->f_height;
+	tmp_w = cr->c.width;
+	tmp_h = cr->c.height;
+
+	if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
+		if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 1) ||
+		    is_rgb(f->fmt->color))
+			min_w = 32;
+		else
+			min_w = 64;
+		if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 3) ||
+		    is_yuv420(f->fmt->color))
+			min_h = 32;
+		else
+			min_h = 16;
+	} else {
+		if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
+			mod_x = ffs(variant->pix_align->target_w) - 1;
+		if (is_yuv420(f->fmt->color))
+			mod_y = ffs(variant->pix_align->target_h) - 1;
+		if (ctx->gsc_ctrls.rotate->val == 90 ||
+		    ctx->gsc_ctrls.rotate->val == 270) {
+			max_w = f->f_height;
+			max_h = f->f_width;
+			min_w = variant->pix_min->target_rot_en_w;
+			min_h = variant->pix_min->target_rot_en_h;
+			tmp_w = cr->c.height;
+			tmp_h = cr->c.width;
+		} else {
+			min_w = variant->pix_min->target_rot_dis_w;
+			min_h = variant->pix_min->target_rot_dis_h;
+		}
+	}
+	gsc_dbg("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d",
+					mod_x, mod_y, min_w, min_h);
+	gsc_dbg("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h);
+
+	v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
+			      &tmp_h, min_h, max_h, mod_y, 0);
+
+	if (!V4L2_TYPE_IS_OUTPUT(cr->type) &&
+	    (ctx->gsc_ctrls.rotate->val == 90 ||
+	    ctx->gsc_ctrls.rotate->val == 270)) {
+		gsc_check_crop_change(tmp_h, tmp_w, &cr->c.width,
+							&cr->c.height);
+	} else {
+		gsc_check_crop_change(tmp_w, tmp_h, &cr->c.width,
+							&cr->c.height);
+	}
+
+	/* adjust left/top if cropping rectangle is out of bounds */
+	/* Need to add code to algin left value with 2's multiple */
+	if (cr->c.left + tmp_w > max_w)
+		cr->c.left = max_w - tmp_w;
+	if (cr->c.top + tmp_h > max_h)
+		cr->c.top = max_h - tmp_h;
+
+	if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
+		if (cr->c.left % 2)
+			cr->c.left -= 1;
+
+	gsc_dbg("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
+	    cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
+
+	return 0;
+}
+
+int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
+			   int dh, int rot, int out_path)
+{
+	int tmp_w, tmp_h, sc_down_max;
+	sc_down_max =
+		(out_path == GSC_DMA) ? var->sc_down_max : var->local_sc_down;
+
+	if (rot == 90 || rot == 270) {
+		tmp_w = dh;
+		tmp_h = dw;
+	} else {
+		tmp_w = dw;
+		tmp_h = dh;
+	}
+
+	if ((sw / tmp_w) > sc_down_max ||
+	    (sh / tmp_h) > sc_down_max ||
+	    (tmp_w / sw) > var->sc_up_max ||
+	    (tmp_h / sh) > var->sc_up_max)
+		return -EINVAL;
+
+	return 0;
+}
+
+int gsc_set_scaler_info(struct gsc_ctx *ctx)
+{
+	struct gsc_scaler *sc = &ctx->scaler;
+	struct gsc_frame *s_frame = &ctx->s_frame;
+	struct gsc_frame *d_frame = &ctx->d_frame;
+	struct gsc_variant *variant = ctx->gsc_dev->variant;
+	int tx, ty;
+	int ret;
+
+	ret = gsc_check_scaler_ratio(variant, s_frame->crop.width,
+		s_frame->crop.height, d_frame->crop.width, d_frame->crop.height,
+		ctx->gsc_ctrls.rotate->val, ctx->out_path);
+	if (ret) {
+		gsc_err("out of scaler range");
+		return ret;
+	}
+
+	if (ctx->gsc_ctrls.rotate->val == 90 ||
+	    ctx->gsc_ctrls.rotate->val == 270) {
+		ty = d_frame->crop.width;
+		tx = d_frame->crop.height;
+	} else {
+		tx = d_frame->crop.width;
+		ty = d_frame->crop.height;
+	}
+
+	ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width,
+				      tx, &sc->pre_hratio);
+	if (ret) {
+		gsc_err("Horizontal scale ratio is out of range");
+		return ret;
+	}
+
+	ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height,
+				      ty, &sc->pre_vratio);
+	if (ret) {
+		gsc_err("Vertical scale ratio is out of range");
+		return ret;
+	}
+
+	gsc_check_src_scale_info(variant, s_frame, &sc->pre_hratio,
+				 tx, ty, &sc->pre_vratio);
+
+	gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
+				   &sc->pre_shfactor);
+
+	sc->main_hratio = (s_frame->crop.width << 16) / tx;
+	sc->main_vratio = (s_frame->crop.height << 16) / ty;
+
+	gsc_dbg("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d",
+			s_frame->crop.width, s_frame->crop.height, tx, ty);
+	gsc_dbg("scaler ratio info : pre_shfactor : %d, pre_h : %d",
+			sc->pre_shfactor, sc->pre_hratio);
+	gsc_dbg("pre_v :%d, main_h : %ld, main_v : %ld",
+			sc->pre_vratio, sc->main_hratio, sc->main_vratio);
+
+	return 0;
+}
+
+
+/*
+ * V4L2 controls handling
+ */
+static int gsc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct gsc_ctx *ctx = ctrl_to_ctx(ctrl);
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		user_to_drv(ctx->gsc_ctrls.hflip, ctrl->val);
+		break;
+
+	case V4L2_CID_VFLIP:
+		user_to_drv(ctx->gsc_ctrls.vflip, ctrl->val);
+		break;
+
+	case V4L2_CID_ROTATE:
+		user_to_drv(ctx->gsc_ctrls.rotate, ctrl->val);
+		break;
+
+	default:
+		break;
+	}
+
+	if (gsc_m2m_opened(ctx->gsc_dev))
+		gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
+
+	return 0;
+}
+
+const struct v4l2_ctrl_ops gsc_ctrl_ops = {
+	.s_ctrl = gsc_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config gsc_custom_ctrl[] = {
+	{
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_GLOBAL_ALPHA,
+		.name = "Set RGB alpha",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+		.max = 255,
+		.step = 1,
+		.def = 0,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_CACHEABLE,
+		.name = "Set cacheable",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+		.max = 1,
+		.def = true,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_TV_LAYER_BLEND_ENABLE,
+		.name = "Enable layer alpha blending",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_TV_LAYER_BLEND_ALPHA,
+		.name = "Set alpha for layer blending",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+		.min = 0,
+		.max = 255,
+		.step = 1,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_TV_PIXEL_BLEND_ENABLE,
+		.name = "Enable pixel alpha blending",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_TV_CHROMA_ENABLE,
+		.name = "Enable chromakey",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_TV_CHROMA_VALUE,
+		.name = "Set chromakey value",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+		.min = 0,
+		.max = 255,
+		.step = 1,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_CSC_EQ_MODE,
+		.name = "Set CSC equation mode",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+		.max = DEFAULT_CSC_EQ,
+		.def = DEFAULT_CSC_EQ,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_CSC_EQ,
+		.name = "Set CSC equation",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+		.step = 1,
+		.max = 8,
+		.def = V4L2_COLORSPACE_REC709,
+	}, {
+		.ops = &gsc_ctrl_ops,
+		.id = V4L2_CID_CSC_RANGE,
+		.name = "Set CSC range",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.flags = V4L2_CTRL_FLAG_SLIDER,
+		.max = DEFAULT_CSC_RANGE,
+		.def = DEFAULT_CSC_RANGE,
+	},
+};
+
+int gsc_ctrls_create(struct gsc_ctx *ctx)
+{
+	if (ctx->ctrls_rdy) {
+		gsc_err("Control handler of this context was created already");
+		return 0;
+	}
+
+	v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM);
+
+	ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+				&gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
+	ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+				&gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+	ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+				&gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+
+	ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[0], NULL);
+	ctx->gsc_ctrls.layer_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[2], NULL);
+	ctx->gsc_ctrls.layer_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[3], NULL);
+	ctx->gsc_ctrls.pixel_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[4], NULL);
+	ctx->gsc_ctrls.chroma_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[5], NULL);
+	ctx->gsc_ctrls.chroma_val = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[6], NULL);
+
+/* for CSC equation */
+	ctx->gsc_ctrls.csc_eq_mode = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[7], NULL);
+	ctx->gsc_ctrls.csc_eq = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[8], NULL);
+	ctx->gsc_ctrls.csc_range = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+					&gsc_custom_ctrl[9], NULL);
+
+	ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
+
+
+	if (ctx->ctrl_handler.error) {
+		int err = ctx->ctrl_handler.error;
+		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+		gsc_err("Failed to gscaler control hander create");
+		return err;
+	}
+
+	return 0;
+}
+
+void gsc_ctrls_delete(struct gsc_ctx *ctx)
+{
+	if (ctx->ctrls_rdy) {
+		v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+		ctx->ctrls_rdy = false;
+	}
+}
+
+/* The color format (num_comp, num_planes) must be already configured. */
+int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
+			struct gsc_frame *frame, struct gsc_addr *addr)
+{
+	int ret = 0;
+	u32 pix_size;
+
+	if (IS_ERR(vb) || IS_ERR(frame)) {
+		gsc_err("Invalid argument");
+		return -EINVAL;
+	}
+
+	pix_size = frame->f_width * frame->f_height;
+
+	gsc_dbg("num_planes= %d, num_comp= %d, pix_size= %d",
+		frame->fmt->num_planes, frame->fmt->num_comp, pix_size);
+
+	addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+	if (frame->fmt->num_planes == 1) {
+		switch (frame->fmt->num_comp) {
+		case 1:
+			addr->cb = 0;
+			addr->cr = 0;
+			break;
+		case 2:
+			/* decompose Y into Y/Cb */
+			addr->cb = (dma_addr_t)(addr->y + pix_size);
+			addr->cr = 0;
+			break;
+		case 3:
+			addr->cb = (dma_addr_t)(addr->y + pix_size);
+			addr->cr = (dma_addr_t)(addr->cb + (pix_size >> 2));
+			break;
+		default:
+			gsc_err("Invalid the number of color planes");
+			return -EINVAL;
+		}
+	} else {
+		if (frame->fmt->num_planes >= 2)
+			addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+
+		if (frame->fmt->num_planes == 3)
+			addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
+	}
+
+	if (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) {
+		u32 t_cb = addr->cb;
+		addr->cb = addr->cr;
+		addr->cr = t_cb;
+	}
+
+	gsc_dbg("ADDR: y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
+		addr->y, addr->cb, addr->cr, ret);
+
+	return ret;
+}
+
+static irqreturn_t gsc_irq_handler(int irq, void *priv)
+{
+	struct gsc_dev *gsc = priv;
+	int gsc_irq;
+
+	gsc_irq = gsc_hw_get_irq_status(gsc);
+	gsc_hw_clear_irq(gsc, gsc_irq);
+
+	if (gsc_irq == GSC_OR_IRQ) {
+		gsc_err("Local path input over-run interrupt has occurred!\n");
+		return IRQ_HANDLED;
+	}
+
+	spin_lock(&gsc->slock);
+
+	if (test_and_clear_bit(ST_M2M_RUN, &gsc->state)) {
+		struct vb2_buffer *src_vb, *dst_vb;
+		struct gsc_ctx *ctx =
+			v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
+
+		if (!ctx || !ctx->m2m_ctx)
+			goto isr_unlock;
+
+		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_and_clear_bit(ST_STOP_REQ, &gsc->state))
+				wake_up(&gsc->irq_queue);
+			else
+				v4l2_m2m_job_finish(gsc->m2m.m2m_dev,
+							ctx->m2m_ctx);
+
+			/* wake_up job_abort, stop_streaming */
+			spin_lock(&ctx->slock);
+			if (ctx->state & GSC_CTX_STOP_REQ) {
+				ctx->state &= ~GSC_CTX_STOP_REQ;
+				wake_up(&gsc->irq_queue);
+			}
+			spin_unlock(&ctx->slock);
+		}
+	}
+
+isr_unlock:
+	spin_unlock(&gsc->slock);
+	return IRQ_HANDLED;
+}
+
+static int gsc_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
+
+	if (gsc_m2m_opened(gsc))
+		gsc->m2m.ctx = NULL;
+
+	clk_disable(gsc->clock);
+	clear_bit(ST_PWR_ON, &gsc->state);
+
+	return 0;
+}
+
+static int gsc_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
+	int ret;
+
+	ret = clk_enable(gsc->clock);
+	if (ret)
+		return ret;
+
+	set_bit(ST_PWR_ON, &gsc->state);
+	return 0;
+}
+
+static inline void *gsc_get_drv_data(struct platform_device *pdev);
+
+static int gsc_probe(struct platform_device *pdev)
+{
+	struct gsc_dev *gsc;
+	struct resource *res;
+	struct gsc_driverdata *drv_data;
+
+	int ret = 0;
+
+	dev_dbg(&pdev->dev, "%s():\n", __func__);
+	drv_data = (struct gsc_driverdata *)
+				gsc_get_drv_data(pdev);
+
+	if (pdev->dev.of_node) {
+		pdev->id = of_alias_get_id(pdev->dev.of_node, "gsc");
+		if (pdev->id < 0)
+			dev_err(&pdev->dev,
+				"failed to get alias id, errno %d\n", ret);
+	}
+
+	if (pdev->id >= drv_data->num_entities) {
+		dev_err(&pdev->dev, "Invalid platform device id: %d\n",
+			pdev->id);
+		return -EINVAL;
+	}
+
+	gsc = devm_kzalloc(&pdev->dev, sizeof(struct gsc_dev), GFP_KERNEL);
+	if (!gsc)
+		return -ENOMEM;
+
+	gsc->id = pdev->id;
+	gsc->variant = drv_data->variant[gsc->id];
+	gsc->pdev = pdev;
+	gsc->pdata = pdev->dev.platform_data;
+
+	init_waitqueue_head(&gsc->irq_queue);
+	spin_lock_init(&gsc->slock);
+	mutex_init(&gsc->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	gsc->regs = devm_request_and_ioremap(&pdev->dev, res);
+	if (!gsc->regs) {
+		dev_err(&pdev->dev, "failed to map registers\n");
+		return -ENOENT;
+	}
+
+	/* Get Gscaler clock */
+	gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
+	if (IS_ERR(gsc->clock)) {
+		gsc_err("failed to get gscaler.%d clock", gsc->id);
+		return -ENXIO;
+	}
+	clk_put(gsc->clock);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get IRQ resource\n");
+		return -ENXIO;
+	}
+	gsc->irq = res->start;
+
+	ret = devm_request_irq(&pdev->dev, gsc->irq, gsc_irq_handler, 0,
+							pdev->name, gsc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
+		return ret;
+	}
+
+	gsc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+	if (IS_ERR(gsc->alloc_ctx)) {
+		ret = PTR_ERR(gsc->alloc_ctx);
+		goto err_irq;
+	}
+
+	platform_set_drvdata(pdev, gsc);
+
+	ret = gsc_register_m2m_device(gsc);
+	if (ret)
+		goto err_irq;
+
+	gsc_runtime_resume(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	gsc_info("gsc-%d registered successfully", gsc->id);
+	return 0;
+
+err_irq:
+	free_irq(gsc->irq, gsc);
+
+	return ret;
+}
+
+static int __devexit gsc_remove(struct platform_device *pdev)
+{
+	struct gsc_dev *gsc =
+		(struct gsc_dev *)platform_get_drvdata(pdev);
+
+	free_irq(gsc->irq, gsc);
+
+	gsc_unregister_m2m_device(gsc);
+
+	vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
+	pm_runtime_disable(&pdev->dev);
+
+	iounmap(gsc->regs);
+	release_resource(gsc->regs_res);
+	kfree(gsc->regs_res);
+	kfree(gsc);
+
+	dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
+	return 0;
+}
+
+static int gsc_suspend(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct gsc_dev *gsc;
+	int ret = 0;
+
+	pdev = to_platform_device(dev);
+	gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
+
+	if (gsc_m2m_run(gsc)) {
+		set_bit(ST_STOP_REQ, &gsc->state);
+		ret = wait_event_timeout(gsc->irq_queue,
+				!test_bit(ST_STOP_REQ, &gsc->state),
+				GSC_SHUTDOWN_TIMEOUT);
+		if (ret == 0)
+			dev_err(&gsc->pdev->dev, "wait timeout : %s\n",
+				__func__);
+	}
+
+	pm_runtime_put_sync(dev);
+
+	return ret;
+}
+
+static int gsc_resume(struct device *dev)
+{
+	struct gsc_dev *gsc;
+	struct gsc_ctx *ctx;
+
+	gsc = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(dev);
+	if (gsc_m2m_opened(gsc)) {
+		ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
+		if (ctx != NULL) {
+			gsc->m2m.ctx = NULL;
+			v4l2_m2m_job_finish(gsc->m2m.m2m_dev, ctx->m2m_ctx);
+		}
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops gsc_pm_ops = {
+	.suspend		= gsc_suspend,
+	.resume			= gsc_resume,
+	.runtime_suspend	= gsc_runtime_suspend,
+	.runtime_resume		= gsc_runtime_resume,
+};
+
+struct gsc_pix_max gsc_v_100_max = {
+	.org_scaler_bypass_w	= 8192,
+	.org_scaler_bypass_h	= 8192,
+	.org_scaler_input_w	= 4800,
+	.org_scaler_input_h	= 3344,
+	.real_rot_dis_w		= 4800,
+	.real_rot_dis_h		= 3344,
+	.real_rot_en_w		= 2047,
+	.real_rot_en_h		= 2047,
+	.target_rot_dis_w	= 4800,
+	.target_rot_dis_h	= 3344,
+	.target_rot_en_w	= 2016,
+	.target_rot_en_h	= 2016,
+};
+
+struct gsc_pix_min gsc_v_100_min = {
+	.org_w			= 64,
+	.org_h			= 32,
+	.real_w			= 64,
+	.real_h			= 32,
+	.target_rot_dis_w	= 64,
+	.target_rot_dis_h	= 32,
+	.target_rot_en_w	= 32,
+	.target_rot_en_h	= 16,
+};
+
+struct gsc_pix_align gsc_v_100_align = {
+	.org_h			= 16,
+	.org_w			= 16, /* yuv420 : 16, others : 8 */
+	.offset_h		= 2,  /* yuv420/422 : 2, others : 1 */
+	.real_w			= 16, /* yuv420/422 : 4~16, others : 2~8 */
+	.real_h			= 16, /* yuv420 : 4~16, others : 1 */
+	.target_w		= 2,  /* yuv420/422 : 2, others : 1 */
+	.target_h		= 2,  /* yuv420 : 2, others : 1 */
+};
+
+struct gsc_variant gsc_v_100_variant = {
+	.pix_max		= &gsc_v_100_max,
+	.pix_min		= &gsc_v_100_min,
+	.pix_align		= &gsc_v_100_align,
+	.in_buf_cnt		= 8,
+	.out_buf_cnt		= 16,
+	.sc_up_max		= 8,
+	.sc_down_max		= 16,
+	.poly_sc_down_max	= 4,
+	.pre_sc_down_max	= 4,
+	.local_sc_down		= 2,
+};
+
+static struct gsc_driverdata gsc_v_100_drvdata = {
+	.variant = {
+		[0] = &gsc_v_100_variant,
+		[1] = &gsc_v_100_variant,
+		[2] = &gsc_v_100_variant,
+		[3] = &gsc_v_100_variant,
+	},
+	.num_entities = 4,
+	.lclk_frequency = 266000000UL,
+};
+
+static struct platform_device_id gsc_driver_ids[] = {
+	{
+		.name		= "exynos-gsc",
+		.driver_data	= (unsigned long)&gsc_v_100_drvdata,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(platform, gsc_driver_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_gsc_match[] = {
+	{ .compatible = "samsung,exynos-gsc",
+	.data = &gsc_v_100_drvdata, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos_gsc_match);
+#else
+#define exynos_gsc_match NULL
+#endif
+
+static inline void *gsc_get_drv_data(struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+	match = of_match_node(exynos_gsc_match, pdev->dev.of_node);
+	return (struct gsc_driverdata *) match->data;
+	}
+#endif
+	return (struct gsc_driverdata *)
+			platform_get_device_id(pdev)->driver_data;
+}
+
+static struct platform_driver gsc_driver = {
+	.probe		= gsc_probe,
+	.remove	= __devexit_p(gsc_remove),
+	.id_table	= gsc_driver_ids,
+	.driver = {
+		.name	= GSC_MODULE_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &gsc_pm_ops,
+		.of_match_table = exynos_gsc_match,
+	}
+};
+
+static int __init gsc_init(void)
+{
+	int ret = platform_driver_register(&gsc_driver);
+	if (ret)
+		gsc_err("platform_driver_register failed: %d\n", ret);
+	return ret;
+}
+
+static void __exit gsc_exit(void)
+{
+	platform_driver_unregister(&gsc_driver);
+}
+
+module_init(gsc_init);
+module_exit(gsc_exit);
+
+MODULE_AUTHOR("Hyunwong Kim <khw0178.kim@xxxxxxxxxxx>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/exynos/gsc/gsc-core.h b/drivers/media/video/exynos/gsc/gsc-core.h
new file mode 100644
index 0000000..3a236ec
--- /dev/null
+++ b/drivers/media/video/exynos/gsc/gsc-core.h
@@ -0,0 +1,652 @@ 
+/* linux/drivers/media/video/exynos/gsc/gsc-core.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * header file for Samsung EXYNOS5 SoC series G-scaler 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 GSC_CORE_H_
+#define GSC_CORE_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 <media/v4l2-mediabus.h>
+#define CONFIG_VB2_GSC_DMA_CONTIG	1
+#include <media/videobuf2-dma-contig.h>
+#include "gsc-regs.h"
+extern int gsc_dbg;
+
+#define GSC_MODULE_NAME			"exynos-gsc"
+
+#define gsc_info(fmt, args...)						\
+	do {								\
+		if (gsc_dbg >= 6)					\
+			pr_info("[INFO]%s:%d: "fmt "\n",		\
+				__func__, __LINE__, ##args);		\
+	} while (0)
+
+#define gsc_err(fmt, args...)						\
+	do {								\
+		if (gsc_dbg >= 3)					\
+			pr_err("[ERROR]%s:%d: "fmt "\n",		\
+				__func__, __LINE__, ##args);		\
+	} while (0)
+
+#define gsc_warn(fmt, args...)						\
+	do {								\
+		if (gsc_dbg >= 4)					\
+			pr_warn("[WARN]%s:%d: "fmt "\n",		\
+				__func__, __LINE__, ##args);		\
+	} while (0)
+
+#define gsc_dbg(fmt, args...)						\
+	do {								\
+		if (gsc_dbg >= 7)					\
+			pr_debug("[DEBUG]%s:%d: "fmt "\n",		\
+				__func__, __LINE__, ##args);		\
+	} while (0)
+
+#define GSC_MAX_CLOCKS			3
+#define GSC_SHUTDOWN_TIMEOUT		((100*HZ)/1000)
+#define GSC_MAX_DEVS			4
+#define WORKQUEUE_NAME_SIZE		32
+#define FIMD_NAME_SIZE			32
+#define GSC_M2M_BUF_NUM			0
+#define GSC_OUT_BUF_MAX			2
+#define GSC_MAX_CTRL_NUM		10
+#define GSC_OUT_MAX_MASK_NUM		7
+#define GSC_SC_ALIGN_4			4
+#define GSC_SC_ALIGN_2			2
+#define GSC_OUT_DEF_SRC			15
+#define GSC_OUT_DEF_DST			7
+#define DEFAULT_GSC_SINK_WIDTH		800
+#define DEFAULT_GSC_SINK_HEIGHT		480
+#define DEFAULT_GSC_SOURCE_WIDTH	800
+#define DEFAULT_GSC_SOURCE_HEIGHT	480
+#define DEFAULT_CSC_EQ			1
+#define DEFAULT_CSC_RANGE		1
+
+#define GSC_LAST_DEV_ID			3
+#define GSC_PAD_SINK			0
+#define GSC_PAD_SOURCE			1
+#define GSC_PADS_NUM			2
+
+#define	GSC_PARAMS			(1 << 0)
+#define	GSC_SRC_FMT			(1 << 1)
+#define	GSC_DST_FMT			(1 << 2)
+#define	GSC_CTX_M2M			(1 << 3)
+#define	GSC_CTX_OUTPUT			(1 << 4)
+#define	GSC_CTX_START			(1 << 5)
+#define	GSC_CTX_STOP_REQ		(1 << 6)
+#define	GSC_CTX_CAP			(1 << 10)
+#define MAX_MDEV			2
+
+#define V4L2_CID_CACHEABLE			(V4L2_CID_LASTP1 + 1)
+#define V4L2_CID_TV_LAYER_BLEND_ENABLE		(V4L2_CID_LASTP1 + 2)
+#define V4L2_CID_TV_LAYER_BLEND_ALPHA		(V4L2_CID_LASTP1 + 3)
+#define V4L2_CID_TV_PIXEL_BLEND_ENABLE		(V4L2_CID_LASTP1 + 4)
+#define V4L2_CID_TV_CHROMA_ENABLE		(V4L2_CID_LASTP1 + 5)
+#define V4L2_CID_TV_CHROMA_VALUE		(V4L2_CID_LASTP1 + 6)
+/* for color space conversion equation selection */
+#define V4L2_CID_CSC_EQ_MODE			(V4L2_CID_LASTP1 + 8)
+#define V4L2_CID_CSC_EQ				(V4L2_CID_LASTP1 + 9)
+#define V4L2_CID_CSC_RANGE			(V4L2_CID_LASTP1 + 10)
+#define V4L2_CID_GLOBAL_ALPHA			(V4L2_CID_LASTP1 + 11)
+
+enum gsc_dev_flags {
+	/* for global */
+	ST_PWR_ON,
+	ST_STOP_REQ,
+	/* for m2m node */
+	ST_M2M_OPEN,
+	ST_M2M_RUN,
+};
+
+enum gsc_irq {
+	GSC_OR_IRQ = 17,
+	GSC_DONE_IRQ = 16,
+};
+
+/**
+ * enum gsc_datapath - the path of data used for gscaler
+ * @GSC_CAMERA: from camera
+ * @GSC_DMA: from/to DMA
+ * @GSC_LOCAL: to local path
+ * @GSC_WRITEBACK: from FIMD
+ */
+enum gsc_datapath {
+	GSC_CAMERA = 0x1,
+	GSC_DMA,
+	GSC_MIXER,
+	GSC_FIMD,
+	GSC_WRITEBACK,
+};
+
+enum gsc_color_fmt {
+	GSC_RGB = 0x1,
+	GSC_YUV420 = 0x2,
+	GSC_YUV422 = 0x4,
+	GSC_YUV444 = 0x8,
+};
+
+enum gsc_yuv_fmt {
+	GSC_LSB_Y = 0x10,
+	GSC_LSB_C,
+	GSC_CBCR = 0x20,
+	GSC_CRCB,
+};
+
+#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh)
+#define is_rgb(x) (!!((x) & 0x1))
+#define is_yuv420(x) (!!((x) & 0x2))
+#define is_yuv422(x) (!!((x) & 0x4))
+#define gsc_m2m_run(dev) test_bit(ST_M2M_RUN, &(dev)->state)
+#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state)
+
+#define ctrl_to_ctx(__ctrl) \
+	container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler)
+/**
+ * struct gsc_fmt - the driver's internal color format data
+ * @mbus_code: Media Bus pixel code, -1 if not applicable
+ * @name: format description
+ * @pixelformat: the fourcc code for this format, 0 if not applicable
+ * @yorder: Y/C order
+ * @corder: Chrominance order control
+ * @num_planes: number of physically non-contiguous data planes
+ * @nr_comp: number of physically contiguous data planes
+ * @depth: per plane driver's private 'number of bits per pixel'
+ * @flags: flags indicating which operation mode format applies to
+ */
+struct gsc_fmt {
+	enum v4l2_mbus_pixelcode mbus_code;
+	char	*name;
+	u32	pixelformat;
+	u32	color;
+	u32	yorder;
+	u32	corder;
+	u16	num_planes;
+	u16	num_comp;
+	u8	depth[VIDEO_MAX_PLANES];
+	u32	flags;
+};
+
+/**
+ * struct gsc_input_buf - the driver's video buffer
+ * @vb:	videobuf2 buffer
+ * @list : linked list structure for buffer queue
+ * @idx : index of G-Scaler input buffer
+ */
+struct gsc_input_buf {
+	struct vb2_buffer	vb;
+	struct list_head	list;
+	int			idx;
+};
+
+/**
+ * struct gsc_addr - the G-Scaler physical address set
+ * @y:	 luminance plane address
+ * @cb:	 Cb plane address
+ * @cr:	 Cr plane address
+ */
+struct gsc_addr {
+	dma_addr_t	y;
+	dma_addr_t	cb;
+	dma_addr_t	cr;
+};
+
+/* struct gsc_ctrls - the G-Scaler control set
+ * @rotate: rotation degree
+ * @hflip: horizontal flip
+ * @vflip: vertical flip
+ * @global_alpha: the alpha value of current frame
+ * @layer_blend_en: enable mixer layer alpha blending
+ * @layer_alpha: set alpha value for mixer layer
+ * @pixel_blend_en: enable mixer pixel alpha blending
+ * @chroma_en: enable chromakey
+ * @chroma_val:	set value for chromakey
+ * @csc_eq_mode: mode to select csc equation of current frame
+ * @csc_eq: csc equation of current frame
+ * @csc_range: csc range of current frame
+ */
+struct gsc_ctrls {
+	struct v4l2_ctrl	*rotate;
+	struct v4l2_ctrl	*hflip;
+	struct v4l2_ctrl	*vflip;
+	struct v4l2_ctrl	*global_alpha;
+	struct v4l2_ctrl	*layer_blend_en;
+	struct v4l2_ctrl	*layer_alpha;
+	struct v4l2_ctrl	*pixel_blend_en;
+	struct v4l2_ctrl	*chroma_en;
+	struct v4l2_ctrl	*chroma_val;
+	struct v4l2_ctrl	*csc_eq_mode;
+	struct v4l2_ctrl	*csc_eq;
+	struct v4l2_ctrl	*csc_range;
+};
+
+/**
+ * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler
+ * @pre_shfactor:	pre sclaer shift factor
+ * @pre_hratio:		horizontal ratio of the prescaler
+ * @pre_vratio:		vertical ratio of the prescaler
+ * @main_hratio:	the main scaler's horizontal ratio
+ * @main_vratio:	the main scaler's vertical ratio
+ */
+struct gsc_scaler {
+	u32	pre_shfactor;
+	u32	pre_hratio;
+	u32	pre_vratio;
+	unsigned long main_hratio;
+	unsigned long main_vratio;
+};
+
+struct gsc_dev;
+
+struct gsc_ctx;
+
+/**
+ * struct gsc_frame - source/target frame properties
+ * @f_width:	SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
+ * @f_height:	SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
+ * @crop:	cropped(source)/scaled(destination) size
+ * @payload:	image size in bytes (w x h x bpp)
+ * @addr:	image frame buffer physical addresses
+ * @fmt:	G-scaler color format pointer
+ * @alph:	frame's alpha value
+ */
+struct gsc_frame {
+	u32	f_width;
+	u32	f_height;
+	struct v4l2_rect	crop;
+	unsigned long payload[VIDEO_MAX_PLANES];
+	struct gsc_addr		addr;
+	struct gsc_fmt		*fmt;
+	u8	alpha;
+};
+
+/**
+ * struct gsc_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ * @refcnt: the reference counter
+ */
+struct gsc_m2m_device {
+	struct video_device	*vfd;
+	struct v4l2_m2m_dev	*m2m_dev;
+	struct gsc_ctx		*ctx;
+	int			refcnt;
+};
+
+/**
+ *  struct gsc_pix_max - image pixel size limits in various IP configurations
+ *
+ *  @org_scaler_bypass_w: max pixel width when the scaler is disabled
+ *  @org_scaler_bypass_h: max pixel height when the scaler is disabled
+ *  @org_scaler_input_w: max pixel width when the scaler is enabled
+ *  @org_scaler_input_h: max pixel height when the scaler is enabled
+ *  @real_rot_dis_w: max pixel src cropped height with the rotator is off
+ *  @real_rot_dis_h: max pixel src croppped width with the rotator is off
+ *  @real_rot_en_w: max pixel src cropped width with the rotator is on
+ *  @real_rot_en_h: max pixel src cropped height with the rotator is on
+ *  @target_rot_dis_w: max pixel dst scaled width with the rotator is off
+ *  @target_rot_dis_h: max pixel dst scaled height with the rotator is off
+ *  @target_rot_en_w: max pixel dst scaled width with the rotator is on
+ *  @target_rot_en_h: max pixel dst scaled height with the rotator is on
+ */
+struct gsc_pix_max {
+	u16 org_scaler_bypass_w;
+	u16 org_scaler_bypass_h;
+	u16 org_scaler_input_w;
+	u16 org_scaler_input_h;
+	u16 real_rot_dis_w;
+	u16 real_rot_dis_h;
+	u16 real_rot_en_w;
+	u16 real_rot_en_h;
+	u16 target_rot_dis_w;
+	u16 target_rot_dis_h;
+	u16 target_rot_en_w;
+	u16 target_rot_en_h;
+};
+
+/**
+ *  struct gsc_pix_min - image pixel size limits in various IP configurations
+ *
+ *  @org_w: minimum source pixel width
+ *  @org_h: minimum source pixel height
+ *  @real_w: minimum input crop pixel width
+ *  @real_h: minimum input crop pixel height
+ *  @target_rot_dis_w: minimum output scaled pixel height when rotator is off
+ *  @target_rot_dis_h: minimum output scaled pixel height when rotator is off
+ *  @target_rot_en_w: minimum output scaled pixel height when rotator is on
+ *  @target_rot_en_h: minimum output scaled pixel height when rotator is on
+ */
+struct gsc_pix_min {
+	u16 org_w;
+	u16 org_h;
+	u16 real_w;
+	u16 real_h;
+	u16 target_rot_dis_w;
+	u16 target_rot_dis_h;
+	u16 target_rot_en_w;
+	u16 target_rot_en_h;
+};
+
+struct gsc_pix_align {
+	u16 org_h;
+	u16 org_w;
+	u16 offset_h;
+	u16 real_w;
+	u16 real_h;
+	u16 target_w;
+	u16 target_h;
+};
+
+/**
+ * struct gsc_variant - G-Scaler variant information
+ */
+struct gsc_variant {
+	struct gsc_pix_max *pix_max;
+	struct gsc_pix_min *pix_min;
+	struct gsc_pix_align *pix_align;
+	u16		in_buf_cnt;
+	u16		out_buf_cnt;
+	u16		sc_up_max;
+	u16		sc_down_max;
+	u16		poly_sc_down_max;
+	u16		pre_sc_down_max;
+	u16		local_sc_down;
+};
+
+/**
+ * struct gsc_driverdata - per device type driver data for init time.
+ *
+ * @variant: the variant information for this driver.
+ * @lclk_frequency: g-scaler clock frequency
+ * @num_entities: the number of g-scalers
+ */
+struct gsc_driverdata {
+	struct gsc_variant *variant[GSC_MAX_DEVS];
+	unsigned long	lclk_frequency;
+	int		num_entities;
+};
+
+struct gsc_vb2 {
+	const struct vb2_mem_ops *ops;
+	void *(*init)(struct gsc_dev *gsc);
+	void (*cleanup)(void *alloc_ctx);
+
+	void (*resume)(void *alloc_ctx);
+	void (*suspend)(void *alloc_ctx);
+
+	int (*cache_flush)(struct vb2_buffer *vb, u32 num_planes);
+	void (*set_cacheable)(void *alloc_ctx, bool cacheable);
+	void (*set_sharable)(void *alloc_ctx, bool sharable);
+};
+
+/**
+ * struct gsc_dev - abstraction for G-Scaler entity
+ * @slock:	the spinlock protecting this data structure
+ * @lock:	the mutex protecting this data structure
+ * @pdev:	pointer to the G-Scaler platform device
+ * @variant:	the IP variant information
+ * @id:		g_scaler device index (0..GSC_MAX_DEVS)
+ * @regs:	the mapped hardware registers
+ * @regs_res:	the resource claimed for IO registers
+ * @irq:	G-scaler interrupt number
+ * @irq_queue:	interrupt handler waitqueue
+ * @m2m:	memory-to-memory V4L2 device information
+ * @out:	memory-to-local V4L2 output device information
+ * @state:	flags used to synchronize m2m and capture mode operation
+ * @alloc_ctx:	videobuf2 memory allocator context
+ * @vb2:	videobuf2 memory allocator call-back functions
+ * @mdev:	pointer to exynos media device
+ * @pipeline:	pointer to subdevs that are connected with gscaler
+ */
+struct gsc_dev {
+	spinlock_t			slock;
+	struct mutex			lock;
+	struct platform_device		*pdev;
+	struct gsc_variant		*variant;
+	u16				id;
+	struct clk			*clock;
+	void __iomem			*regs;
+	struct resource			*regs_res;
+	int				irq;
+	wait_queue_head_t		irq_queue;
+	struct gsc_m2m_device		m2m;
+	struct exynos_platform_gscaler	*pdata;
+	unsigned long			state;
+	struct vb2_alloc_ctx		*alloc_ctx;
+	const struct gsc_vb2		*vb2;
+};
+
+/**
+ * gsc_ctx - the device context data
+ * @slock:		spinlock protecting this data structure
+ * @s_frame:		source frame properties
+ * @d_frame:		destination frame properties
+ * @in_path:		input mode (DMA or camera)
+ * @out_path:		output mode (DMA or FIFO)
+ * @scaler:		image scaler properties
+ * @flags:		additional flags for image conversion
+ * @state:		flags to keep track of user configuration
+ * @gsc_dev:		the g-scaler device this context applies to
+ * @m2m_ctx:		memory-to-memory device context
+ * @fh:                 v4l2 file handle
+ * @ctrl_handler:       v4l2 controls handler
+ * @ctrls_rdy:          true if the control handler is initialized
+ * @gsc_ctrls		G-Scaler control set
+ * @m2m_ctx:		memory-to-memory device context
+ */
+struct gsc_ctx {
+	spinlock_t		slock;
+	struct gsc_frame	s_frame;
+	struct gsc_frame	d_frame;
+	enum gsc_datapath	in_path;
+	enum gsc_datapath	out_path;
+	struct gsc_scaler	scaler;
+	u32			flags;
+	u32			state;
+	struct gsc_dev		*gsc_dev;
+	struct v4l2_m2m_ctx	*m2m_ctx;
+	struct v4l2_fh		fh;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct gsc_ctrls	gsc_ctrls;
+	bool			ctrls_rdy;
+};
+
+void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm);
+void gsc_clk_release(struct gsc_dev *gsc);
+int gsc_register_m2m_device(struct gsc_dev *gsc);
+void gsc_unregister_m2m_device(struct gsc_dev *gsc);
+
+u32 get_plane_size(struct gsc_frame *fr, unsigned int plane);
+char gsc_total_fmts(void);
+struct gsc_fmt *get_format(int index);
+struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index);
+int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f);
+int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
+void gsc_set_frame_size(struct gsc_frame *frame, int width, int height);
+int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
+void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h);
+int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
+int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
+int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
+							u32 *ratio);
+void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh);
+void gsc_check_src_scale_info(struct gsc_variant *var,
+				struct gsc_frame *s_frame,
+				u32 *wratio, u32 tx, u32 ty, u32 *hratio);
+int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
+			   int dh, int rot, int out_path);
+int gsc_set_scaler_info(struct gsc_ctx *ctx);
+int gsc_ctrls_create(struct gsc_ctx *ctx);
+void gsc_ctrls_delete(struct gsc_ctx *ctx);
+int gsc_out_hw_set(struct gsc_ctx *ctx);
+int gsc_out_set_in_addr(struct gsc_dev *gsc, struct gsc_ctx *ctx,
+		   struct gsc_input_buf *buf, int index);
+int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
+		     struct gsc_frame *frame, struct gsc_addr *addr);
+int gsc_out_link_validate(const struct media_pad *source,
+			  const struct media_pad *sink);
+int gsc_pipeline_s_stream(struct gsc_dev *gsc, bool on);
+
+static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctx->slock, flags);
+	ctx->state |= state;
+	spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctx->slock, flags);
+	ctx->state &= ~state;
+	spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static inline int get_win_num(struct gsc_dev *dev)
+{
+	return (dev->id == 3) ? 2 : dev->id;
+}
+static inline int is_tiled(struct gsc_fmt *fmt)
+{
+	return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16;
+}
+
+static inline int is_output(enum v4l2_buf_type type)
+{
+	return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
+		type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 1 : 0;
+}
+
+static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on)
+{
+	u32 cfg = readl(dev->regs + GSC_ENABLE);
+
+	if (on)
+		cfg |= GSC_ENABLE_ON;
+	else
+		cfg &= ~GSC_ENABLE_ON;
+
+	writel(cfg, dev->regs + GSC_ENABLE);
+}
+
+static inline int gsc_hw_get_irq_status(struct gsc_dev *dev)
+{
+	u32 cfg = readl(dev->regs + GSC_IRQ);
+	if (cfg & (1 << GSC_OR_IRQ))
+		return GSC_OR_IRQ;
+	else
+		return GSC_DONE_IRQ;
+
+}
+
+static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq)
+{
+	u32 cfg = readl(dev->regs + GSC_IRQ);
+	if (irq == GSC_OR_IRQ)
+		cfg |= GSC_IRQ_STATUS_OR_IRQ;
+	else if (irq == GSC_DONE_IRQ)
+		cfg |= GSC_IRQ_STATUS_OR_FRM_DONE;
+	writel(cfg, dev->regs + GSC_IRQ);
+}
+
+static inline void gsc_lock(struct vb2_queue *vq)
+{
+	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
+	mutex_lock(&ctx->gsc_dev->lock);
+}
+
+static inline void gsc_unlock(struct vb2_queue *vq)
+{
+	struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
+	mutex_unlock(&ctx->gsc_dev->lock);
+}
+
+static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx)
+{
+	unsigned long flags;
+	bool ret;
+
+	spin_lock_irqsave(&ctx->slock, flags);
+	ret = (ctx->state & mask) == mask;
+	spin_unlock_irqrestore(&ctx->slock, flags);
+	return ret;
+}
+
+static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx,
+					      enum v4l2_buf_type type)
+{
+	struct gsc_frame *frame;
+
+	if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
+		frame = &ctx->s_frame;
+	} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
+		frame = &ctx->d_frame;
+	} else {
+		gsc_err("Wrong buffer/video queue type (%d)", type);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return frame;
+}
+
+static inline void user_to_drv(struct v4l2_ctrl *ctrl, s32 value)
+{
+	ctrl->cur.val = ctrl->val = value;
+}
+
+void gsc_hw_set_sw_reset(struct gsc_dev *dev);
+void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask);
+void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask);
+void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask);
+void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask);
+void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev);
+void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev);
+void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
+void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
+void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
+							int index);
+void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr,
+							int index);
+void gsc_hw_set_input_path(struct gsc_ctx *ctx);
+void gsc_hw_set_in_size(struct gsc_ctx *ctx);
+void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx);
+void gsc_hw_set_in_image_format(struct gsc_ctx *ctx);
+void gsc_hw_set_output_path(struct gsc_ctx *ctx);
+void gsc_hw_set_out_size(struct gsc_ctx *ctx);
+void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx);
+void gsc_hw_set_out_image_format(struct gsc_ctx *ctx);
+void gsc_hw_set_prescaler(struct gsc_ctx *ctx);
+void gsc_hw_set_mainscaler(struct gsc_ctx *ctx);
+void gsc_hw_set_rotation(struct gsc_ctx *ctx);
+void gsc_hw_set_global_alpha(struct gsc_ctx *ctx);
+void gsc_hw_set_sfr_update(struct gsc_ctx *ctx);
+
+int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev);
+int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev);
+int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev);
+int gsc_wait_reset(struct gsc_dev *dev);
+int gsc_wait_operating(struct gsc_dev *dev);
+int gsc_wait_stop(struct gsc_dev *dev);
+
+#endif /* GSC_CORE_H_ */
diff --git a/drivers/media/video/exynos/gsc/gsc-m2m.c b/drivers/media/video/exynos/gsc/gsc-m2m.c
new file mode 100644
index 0000000..ead59a0
--- /dev/null
+++ b/drivers/media/video/exynos/gsc/gsc-m2m.c
@@ -0,0 +1,751 @@ 
+/* linux/drivers/media/video/exynos/gsc/gsc-m2m.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Samsung EXYNOS5 SoC series G-scaler driver
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+
+#include "gsc-core.h"
+
+static int gsc_ctx_stop_req(struct gsc_ctx *ctx)
+{
+	struct gsc_ctx *curr_ctx;
+	struct gsc_dev *gsc = ctx->gsc_dev;
+	int ret = 0;
+
+	curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
+	if (!gsc_m2m_run(gsc) || (curr_ctx != ctx))
+		return 0;
+	ctx->state |= GSC_CTX_STOP_REQ;
+	ret = wait_event_timeout(gsc->irq_queue,
+			!gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
+			GSC_SHUTDOWN_TIMEOUT);
+	if (!ret)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct gsc_ctx *ctx = q->drv_priv;
+	int ret;
+
+	ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev);
+	return ret > 0 ? 0 : ret;
+}
+
+static int gsc_m2m_stop_streaming(struct vb2_queue *q)
+{
+	struct gsc_ctx *ctx = q->drv_priv;
+	struct gsc_dev *gsc = ctx->gsc_dev;
+	int ret;
+
+	ret = gsc_ctx_stop_req(ctx);
+	/* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
+	if (ret < 0)
+		dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
+
+	pm_runtime_put(&ctx->gsc_dev->pdev->dev);
+
+	return 0;
+}
+
+static void gsc_m2m_job_abort(void *priv)
+{
+	struct gsc_ctx *ctx = priv;
+	struct gsc_dev *gsc = ctx->gsc_dev;
+	int ret;
+
+	ret = gsc_ctx_stop_req(ctx);
+	/* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
+	if (ret < 0)
+		dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
+}
+
+int gsc_fill_addr(struct gsc_ctx *ctx)
+{
+	struct gsc_frame *s_frame, *d_frame;
+	struct vb2_buffer *vb = NULL;
+	int ret = 0;
+
+	s_frame = &ctx->s_frame;
+	d_frame = &ctx->d_frame;
+
+	vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+	ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr);
+	if (ret)
+		return ret;
+
+	vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+	ret = gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr);
+
+	return ret;
+}
+
+static void gsc_m2m_device_run(void *priv)
+{
+	struct gsc_ctx *ctx = priv;
+	struct gsc_dev *gsc;
+	unsigned long flags;
+	u32 ret;
+	bool is_set = false;
+
+	if (WARN(!ctx, "null hardware context\n"))
+		return;
+
+	gsc = ctx->gsc_dev;
+
+	spin_lock_irqsave(&ctx->slock, flags);
+	/* Reconfigure hardware if the context has changed. */
+	if (gsc->m2m.ctx != ctx) {
+		gsc_dbg("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
+			  gsc->m2m.ctx, ctx);
+		ctx->state |= GSC_PARAMS;
+		gsc->m2m.ctx = ctx;
+	}
+
+	is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0;
+	ctx->state &= ~GSC_CTX_STOP_REQ;
+	if (is_set) {
+		wake_up(&gsc->irq_queue);
+		goto put_device;
+	}
+
+	ret = gsc_fill_addr(ctx);
+	if (ret) {
+		gsc_err("Wrong address");
+		goto put_device;
+	}
+
+	gsc_set_prefbuf(gsc, ctx->s_frame);
+	gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
+	gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
+
+	if (ctx->state & GSC_PARAMS) {
+		gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
+		gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
+		gsc_hw_set_frm_done_irq_mask(gsc, false);
+		gsc_hw_set_gsc_irq_enable(gsc, true);
+
+		if (gsc_set_scaler_info(ctx)) {
+			gsc_err("Scaler setup error");
+			goto put_device;
+		}
+
+		gsc_hw_set_input_path(ctx);
+		gsc_hw_set_in_size(ctx);
+		gsc_hw_set_in_image_format(ctx);
+
+		gsc_hw_set_output_path(ctx);
+		gsc_hw_set_out_size(ctx);
+		gsc_hw_set_out_image_format(ctx);
+
+		gsc_hw_set_prescaler(ctx);
+		gsc_hw_set_mainscaler(ctx);
+		gsc_hw_set_rotation(ctx);
+		gsc_hw_set_global_alpha(ctx);
+	}
+	/* When you update SFRs in the middle of operating
+	gsc_hw_set_sfr_update(ctx);
+	*/
+
+	ctx->state &= ~GSC_PARAMS;
+
+	if (!test_and_set_bit(ST_M2M_RUN, &gsc->state)) {
+		/* One frame mode sequence
+		 GSCALER_ON on -> GSCALER_OP_STATUS is operating ->
+		 GSCALER_ON off */
+		gsc_hw_enable_control(gsc, true);
+		ret = gsc_wait_operating(gsc);
+		if (ret < 0) {
+			gsc_err("gscaler wait operating timeout");
+			goto put_device;
+		}
+		gsc_hw_enable_control(gsc, false);
+	}
+
+	spin_unlock_irqrestore(&ctx->slock, flags);
+	return;
+
+put_device:
+	ctx->state &= ~GSC_PARAMS;
+	spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static int gsc_m2m_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 gsc_ctx *ctx = vb2_get_drv_priv(vq);
+	struct gsc_frame *frame;
+	int i;
+
+	frame = ctx_get_frame(ctx, vq->type);
+	if (IS_ERR(frame))
+		return PTR_ERR(frame);
+
+	if (!frame->fmt)
+		return -EINVAL;
+
+	*num_planes = frame->fmt->num_planes;
+	for (i = 0; i < frame->fmt->num_planes; i++) {
+		sizes[i] = get_plane_size(frame, i);
+		allocators[i] = ctx->gsc_dev->alloc_ctx;
+	}
+	return 0;
+}
+
+static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
+{
+	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct gsc_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->fmt->num_planes; i++)
+			vb2_set_plane_payload(vb, i, frame->payload[i]);
+	}
+
+	return 0;
+}
+
+static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
+{
+	struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	gsc_dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
+
+	if (ctx->m2m_ctx)
+		v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+struct vb2_ops gsc_m2m_qops = {
+	.queue_setup	 = gsc_m2m_queue_setup,
+	.buf_prepare	 = gsc_m2m_buf_prepare,
+	.buf_queue	 = gsc_m2m_buf_queue,
+	.wait_prepare	 = gsc_unlock,
+	.wait_finish	 = gsc_lock,
+	.stop_streaming	 = gsc_m2m_stop_streaming,
+	.start_streaming = gsc_m2m_start_streaming,
+};
+
+static int gsc_m2m_querycap(struct file *file, void *fh,
+			   struct v4l2_capability *cap)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	struct gsc_dev *gsc = ctx->gsc_dev;
+
+	strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
+	strncpy(cap->card, gsc->pdev->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 gsc_m2m_enum_fmt_mplane(struct file *file, void *priv,
+				struct v4l2_fmtdesc *f)
+{
+	return gsc_enum_fmt_mplane(f);
+}
+
+static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
+			     struct v4l2_format *f)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+	return gsc_g_fmt_mplane(ctx, f);
+}
+
+static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+	return gsc_try_fmt_mplane(ctx, f);
+}
+
+static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
+				 struct v4l2_format *f)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	struct vb2_queue *vq;
+	struct gsc_frame *frame;
+	struct v4l2_pix_format_mplane *pix;
+	int i, ret = 0;
+
+	ret = gsc_m2m_try_fmt_mplane(file, fh, f);
+	if (ret)
+		return ret;
+
+	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+
+	if (vb2_is_streaming(vq)) {
+		gsc_err("queue (%d) busy", f->type);
+		return -EBUSY;
+	}
+
+	if (V4L2_TYPE_IS_OUTPUT(f->type))
+		frame = &ctx->s_frame;
+	else
+		frame = &ctx->d_frame;
+
+	pix = &f->fmt.pix_mp;
+	frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
+	if (!frame->fmt)
+		return -EINVAL;
+
+	for (i = 0; i < frame->fmt->num_planes; i++)
+		frame->payload[i] = pix->plane_fmt[i].sizeimage;
+
+	gsc_set_frame_size(frame, pix->width, pix->height);
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
+	else
+		gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
+
+	gsc_dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
+
+	return 0;
+}
+
+static int gsc_m2m_reqbufs(struct file *file, void *fh,
+			  struct v4l2_requestbuffers *reqbufs)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	struct gsc_dev *gsc = ctx->gsc_dev;
+	struct gsc_frame *frame;
+	u32 max_cnt;
+
+	max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
+		gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
+	if (reqbufs->count > max_cnt)
+		return -EINVAL;
+	else if (reqbufs->count == 0) {
+		if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+			gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
+		else
+			gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
+	}
+
+	frame = ctx_get_frame(ctx, reqbufs->type);
+
+	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int gsc_m2m_querybuf(struct file *file, void *fh,
+					struct v4l2_buffer *buf)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int gsc_m2m_qbuf(struct file *file, void *fh,
+			  struct v4l2_buffer *buf)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int gsc_m2m_dqbuf(struct file *file, void *fh,
+			   struct v4l2_buffer *buf)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int gsc_m2m_streamon(struct file *file, void *fh,
+			   enum v4l2_buf_type type)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+	/* The source and target color format need to be set */
+	if (V4L2_TYPE_IS_OUTPUT(type)) {
+		if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
+			return -EINVAL;
+	} else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
+		return -EINVAL;
+	}
+
+	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int gsc_m2m_streamoff(struct file *file, void *fh,
+			    enum v4l2_buf_type type)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+	if (a->left < b->left || a->top < b->top)
+		return 0;
+
+	if (a->left + a->width > b->left + b->width)
+		return 0;
+
+	if (a->top + a->height > b->top + b->height)
+		return 0;
+
+	return 1;
+}
+
+static int gsc_m2m_g_selection(struct file *file, void *fh,
+			struct v4l2_selection *s)
+{
+	struct gsc_frame *frame;
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+		return -EINVAL;
+
+	frame = ctx_get_frame(ctx, s->type);
+	if (IS_ERR(frame))
+		return PTR_ERR(frame);
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = frame->f_width;
+		s->r.height = frame->f_height;
+		return 0;
+
+	case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+	case V4L2_SEL_TGT_CROP_ACTIVE:
+		s->r.left = frame->crop.left;
+		s->r.top = frame->crop.top;
+		s->r.width = frame->crop.width;
+		s->r.height = frame->crop.height;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int gsc_m2m_s_selection(struct file *file, void *fh,
+			struct v4l2_selection *s)
+{
+	struct gsc_frame *frame;
+	struct gsc_ctx *ctx = fh_to_ctx(fh);
+	struct v4l2_crop cr;
+	struct gsc_variant *variant = ctx->gsc_dev->variant;
+	int ret;
+
+	cr.type = s->type;
+	cr.c = s->r;
+
+	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+		return -EINVAL;
+
+	ret = gsc_try_crop(ctx, &cr);
+	if (ret)
+		return ret;
+
+	if (s->flags & V4L2_SEL_FLAG_LE &&
+	    !is_rectangle_enclosed(&cr.c, &s->r))
+		return -ERANGE;
+
+	if (s->flags & V4L2_SEL_FLAG_GE &&
+	    !is_rectangle_enclosed(&s->r, &cr.c))
+		return -ERANGE;
+
+	s->r = cr.c;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+		frame = &ctx->s_frame;
+		break;
+
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_ACTIVE:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		frame = &ctx->d_frame;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* Check to see if scaling ratio is within supported range */
+	if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
+		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+			ret = gsc_check_scaler_ratio(variant, cr.c.width,
+				cr.c.height, ctx->d_frame.crop.width,
+				ctx->d_frame.crop.height,
+				ctx->gsc_ctrls.rotate->val, ctx->out_path);
+		} else {
+			ret = gsc_check_scaler_ratio(variant,
+				ctx->s_frame.crop.width,
+				ctx->s_frame.crop.height, cr.c.width,
+				cr.c.height, ctx->gsc_ctrls.rotate->val,
+				ctx->out_path);
+		}
+
+		if (ret) {
+			gsc_err("Out of scaler range");
+			return -EINVAL;
+		}
+	}
+
+	frame->crop = cr.c;
+
+	gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
+	.vidioc_querycap		= gsc_m2m_querycap,
+
+	.vidioc_enum_fmt_vid_cap_mplane	= gsc_m2m_enum_fmt_mplane,
+	.vidioc_enum_fmt_vid_out_mplane	= gsc_m2m_enum_fmt_mplane,
+
+	.vidioc_g_fmt_vid_cap_mplane	= gsc_m2m_g_fmt_mplane,
+	.vidioc_g_fmt_vid_out_mplane	= gsc_m2m_g_fmt_mplane,
+
+	.vidioc_try_fmt_vid_cap_mplane	= gsc_m2m_try_fmt_mplane,
+	.vidioc_try_fmt_vid_out_mplane	= gsc_m2m_try_fmt_mplane,
+
+	.vidioc_s_fmt_vid_cap_mplane	= gsc_m2m_s_fmt_mplane,
+	.vidioc_s_fmt_vid_out_mplane	= gsc_m2m_s_fmt_mplane,
+
+	.vidioc_reqbufs			= gsc_m2m_reqbufs,
+	.vidioc_querybuf		= gsc_m2m_querybuf,
+
+	.vidioc_qbuf			= gsc_m2m_qbuf,
+	.vidioc_dqbuf			= gsc_m2m_dqbuf,
+
+	.vidioc_streamon		= gsc_m2m_streamon,
+	.vidioc_streamoff		= gsc_m2m_streamoff,
+
+	.vidioc_g_selection		= gsc_m2m_g_selection,
+	.vidioc_s_selection		= gsc_m2m_s_selection
+
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct gsc_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->drv_priv = ctx;
+	src_vq->ops = &gsc_m2m_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	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->drv_priv = ctx;
+	dst_vq->ops = &gsc_m2m_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+	return vb2_queue_init(dst_vq);
+}
+
+static int gsc_m2m_open(struct file *file)
+{
+	struct gsc_dev *gsc = video_drvdata(file);
+	struct gsc_ctx *ctx = NULL;
+	int ret;
+
+	gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
+
+	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
+	ret = gsc_ctrls_create(ctx);
+	if (ret)
+		goto error_fh;
+
+	/* Use separate control handler per file handle */
+	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+	file->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+
+	ctx->gsc_dev = gsc;
+	/* Default color format */
+	ctx->s_frame.fmt = get_format(0);
+	ctx->d_frame.fmt = get_format(0);
+	/* Setup the device context for mem2mem mode. */
+	ctx->state |= GSC_CTX_M2M;
+	ctx->flags = 0;
+	ctx->in_path = GSC_DMA;
+	ctx->out_path = GSC_DMA;
+	spin_lock_init(&ctx->slock);
+
+	ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
+	if (IS_ERR(ctx->m2m_ctx)) {
+		gsc_err("Failed to initialize m2m context");
+		ret = PTR_ERR(ctx->m2m_ctx);
+		goto error_fh;
+	}
+
+	if (gsc->m2m.refcnt++ == 0)
+		set_bit(ST_M2M_OPEN, &gsc->state);
+
+	gsc_dbg("gsc m2m driver is opened, ctx(0x%p)", ctx);
+	return 0;
+
+error_fh:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx);
+	return ret;
+}
+
+static int gsc_m2m_release(struct file *file)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
+	struct gsc_dev *gsc = ctx->gsc_dev;
+
+	gsc_dbg("pid: %d, state: 0x%lx, refcnt= %d",
+		task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
+
+	v4l2_m2m_ctx_release(ctx->m2m_ctx);
+	gsc_ctrls_delete(ctx);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+
+	if (--gsc->m2m.refcnt <= 0)
+		clear_bit(ST_M2M_OPEN, &gsc->state);
+	kfree(ctx);
+	return 0;
+}
+
+static unsigned int gsc_m2m_poll(struct file *file,
+				     struct poll_table_struct *wait)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
+
+	return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
+
+	return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+static const struct v4l2_file_operations gsc_m2m_fops = {
+	.owner		= THIS_MODULE,
+	.open		= gsc_m2m_open,
+	.release	= gsc_m2m_release,
+	.poll		= gsc_m2m_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= gsc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops gsc_m2m_ops = {
+	.device_run	= gsc_m2m_device_run,
+	.job_abort	= gsc_m2m_job_abort,
+};
+
+int gsc_register_m2m_device(struct gsc_dev *gsc)
+{
+	struct video_device *vfd;
+	struct platform_device *pdev;
+	int ret = 0;
+
+	if (!gsc)
+		return -ENODEV;
+
+	pdev = gsc->pdev;
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		dev_err(&pdev->dev, "Failed to allocate video device\n");
+		return -ENOMEM;
+	}
+
+	vfd->fops	= &gsc_m2m_fops;
+	vfd->ioctl_ops	= &gsc_m2m_ioctl_ops;
+	vfd->release	= video_device_release;
+	vfd->lock	= &gsc->lock;
+	snprintf(vfd->name, sizeof(vfd->name), "%s.%d:m2m",
+					GSC_MODULE_NAME, gsc->id);
+
+	video_set_drvdata(vfd, gsc);
+
+	gsc->m2m.vfd = vfd;
+	gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
+	if (IS_ERR(gsc->m2m.m2m_dev)) {
+		dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
+		ret = PTR_ERR(gsc->m2m.m2m_dev);
+		goto err_m2m_r1;
+	}
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(&pdev->dev,
+			 "%s(): failed to register video device\n", __func__);
+		goto err_m2m_r2;
+	}
+
+	gsc_dbg("gsc m2m driver registered as /dev/video%d", vfd->num);
+
+	return 0;
+
+err_m2m_r2:
+	v4l2_m2m_release(gsc->m2m.m2m_dev);
+err_m2m_r1:
+	video_device_release(gsc->m2m.vfd);
+
+	return ret;
+}
+
+void gsc_unregister_m2m_device(struct gsc_dev *gsc)
+{
+	if (gsc)
+		v4l2_m2m_release(gsc->m2m.m2m_dev);
+}
diff --git a/drivers/media/video/exynos/gsc/gsc-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c
new file mode 100644
index 0000000..7c5cce2
--- /dev/null
+++ b/drivers/media/video/exynos/gsc/gsc-regs.c
@@ -0,0 +1,579 @@ 
+/* linux/drivers/media/video/exynos/gsc/gsc-regs.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Samsung EXYNOS5 SoC series G-scaler driver
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/map.h>
+#include "gsc-core.h"
+
+void gsc_hw_set_sw_reset(struct gsc_dev *dev)
+{
+	u32 cfg = 0;
+
+	cfg |= GSC_SW_RESET_SRESET;
+	writel(cfg, dev->regs + GSC_SW_RESET);
+}
+
+int gsc_wait_reset(struct gsc_dev *dev)
+{
+	unsigned long timeo = jiffies + 10; /* timeout of 50ms */
+	u32 cfg;
+
+	while (time_before(jiffies, timeo)) {
+		cfg = readl(dev->regs + GSC_SW_RESET);
+		if (!cfg)
+			return 0;
+		usleep_range(10, 20);
+	}
+	gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
+
+	return -EBUSY;
+}
+
+int gsc_wait_operating(struct gsc_dev *dev)
+{
+	unsigned long timeo = jiffies + 10; /* timeout of 50ms */
+	u32 cfg;
+
+	while (time_before(jiffies, timeo)) {
+		cfg = readl(dev->regs + GSC_ENABLE);
+		if ((cfg & GSC_ENABLE_OP_STATUS) == GSC_ENABLE_OP_STATUS)
+			return 0;
+		usleep_range(10, 20);
+	}
+	gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
+
+	return -EBUSY;
+}
+
+int gsc_wait_stop(struct gsc_dev *dev)
+{
+	unsigned long timeo = jiffies + 10; /* timeout of 50ms */
+	u32 cfg;
+
+	while (time_before(jiffies, timeo)) {
+		cfg = readl(dev->regs + GSC_ENABLE);
+		if (!(cfg & GSC_ENABLE_OP_STATUS))
+			return 0;
+		usleep_range(10, 20);
+	}
+	gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
+
+	return -EBUSY;
+}
+
+
+void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask)
+{
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_ENABLE);
+	if (mask)
+		cfg |= GSC_ENABLE_ON_CLEAR;
+	else
+		cfg &= ~GSC_ENABLE_ON_CLEAR;
+	writel(cfg, dev->regs + GSC_ENABLE);
+}
+
+int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev)
+{
+	u32 cfg, status, bits = 0;
+
+	cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+	status = cfg & GSC_IN_BASE_ADDR_MASK;
+	while (status) {
+		status = status & (status - 1);
+		bits++;
+	}
+	return bits;
+}
+
+int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev)
+{
+	u32 cfg, curr_index, i;
+
+	cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+	curr_index = GSC_IN_CURR_GET_INDEX(cfg);
+	for (i = curr_index; i > 1; i--) {
+		if (cfg ^ (1 << (i - 2)))
+			return i - 2;
+	}
+
+	for (i = dev->variant->in_buf_cnt; i > curr_index; i--) {
+		if (cfg ^ (1 << (i - 1)))
+			return i - 1;
+	}
+
+	return curr_index - 1;
+}
+
+void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask)
+{
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_IRQ);
+	if (mask)
+		cfg |= GSC_IRQ_FRMDONE_MASK;
+	else
+		cfg &= ~GSC_IRQ_FRMDONE_MASK;
+	writel(cfg, dev->regs + GSC_IRQ);
+}
+
+void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask)
+{
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_IRQ);
+	if (mask)
+		cfg |= GSC_IRQ_OR_MASK;
+	else
+		cfg &= ~GSC_IRQ_OR_MASK;
+	writel(cfg, dev->regs + GSC_IRQ);
+}
+
+void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask)
+{
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_IRQ);
+	if (mask)
+		cfg |= GSC_IRQ_ENABLE;
+	else
+		cfg &= ~GSC_IRQ_ENABLE;
+	writel(cfg, dev->regs + GSC_IRQ);
+}
+
+void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev)
+{
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+	cfg |= GSC_IN_BASE_ADDR_MASK;
+	cfg |= GSC_IN_BASE_ADDR_PINGPONG(dev->variant->in_buf_cnt);
+
+	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
+	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
+}
+
+void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev)
+{
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
+	cfg |= GSC_OUT_BASE_ADDR_MASK;
+	cfg |= GSC_OUT_BASE_ADDR_PINGPONG(dev->variant->out_buf_cnt);
+
+	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
+	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
+	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
+}
+
+void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift,
+				bool enable)
+{
+	u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+	u32 mask = 1 << shift;
+
+	cfg &= (~mask);
+	cfg |= enable << shift;
+
+	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
+	writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
+}
+
+void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
+				bool enable)
+{
+	u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
+	u32 mask = 1 << shift;
+
+	cfg &= (~mask);
+	cfg |= enable << shift;
+
+	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
+	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
+	writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
+}
+
+int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev)
+{
+	u32 bits = 0;
+	u32 mask_bits = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
+	mask_bits &= GSC_OUT_BASE_ADDR_MASK;
+
+	while (mask_bits) {
+		mask_bits = mask_bits & (mask_bits - 1);
+		bits++;
+	}
+	bits = 16 - bits;
+
+	return bits;
+}
+
+void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
+				int index)
+{
+	gsc_dbg("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
+		addr->y, addr->cb, addr->cr);
+	writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
+	writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
+	writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
+
+}
+
+void gsc_hw_set_output_addr(struct gsc_dev *dev,
+			     struct gsc_addr *addr, int index)
+{
+	gsc_dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
+			index, addr->y, addr->cb, addr->cr);
+	writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
+	writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
+	writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
+}
+
+void gsc_hw_set_input_path(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+
+	u32 cfg = readl(dev->regs + GSC_IN_CON);
+	cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
+
+	if (ctx->in_path == GSC_DMA)
+		cfg |= GSC_IN_PATH_MEMORY;
+
+	writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_in_size(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_frame *frame = &ctx->s_frame;
+	u32 cfg;
+
+	/* Set input pixel offset */
+	cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left);
+	cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top);
+	writel(cfg, dev->regs + GSC_SRCIMG_OFFSET);
+
+	/* Set input original size */
+	cfg = GSC_SRCIMG_WIDTH(frame->f_width);
+	cfg |= GSC_SRCIMG_HEIGHT(frame->f_height);
+	writel(cfg, dev->regs + GSC_SRCIMG_SIZE);
+
+	/* Set input cropped size */
+	cfg = GSC_CROPPED_WIDTH(frame->crop.width);
+	cfg |= GSC_CROPPED_HEIGHT(frame->crop.height);
+	writel(cfg, dev->regs + GSC_CROPPED_SIZE);
+}
+
+void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_frame *frame = &ctx->s_frame;
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_IN_CON);
+	if (ctx->gsc_ctrls.csc_eq->val) {
+		if (ctx->gsc_ctrls.csc_range->val)
+			cfg |= GSC_IN_RGB_HD_WIDE;
+		else
+			cfg |= GSC_IN_RGB_HD_NARROW;
+	} else {
+		if (ctx->gsc_ctrls.csc_range->val)
+			cfg |= GSC_IN_RGB_SD_WIDE;
+		else
+			cfg |= GSC_IN_RGB_SD_NARROW;
+	}
+
+	if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
+		cfg |= GSC_IN_RGB565;
+	else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
+		cfg |= GSC_IN_XRGB8888;
+
+	writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_frame *frame = &ctx->s_frame;
+	u32 i, depth = 0;
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_IN_CON);
+	cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
+		 GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK |
+		 GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE);
+	writel(cfg, dev->regs + GSC_IN_CON);
+
+	if (is_rgb(frame->fmt->color)) {
+		gsc_hw_set_in_image_rgb(ctx);
+		return;
+	}
+	for (i = 0; i < frame->fmt->num_planes; i++)
+		depth += frame->fmt->depth[i];
+
+	switch (frame->fmt->num_comp) {
+	case 1:
+		cfg |= GSC_IN_YUV422_1P;
+		if (frame->fmt->yorder == GSC_LSB_Y)
+			cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y;
+		else
+			cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C;
+		if (frame->fmt->corder == GSC_CBCR)
+			cfg |= GSC_IN_CHROMA_ORDER_CBCR;
+		else
+			cfg |= GSC_IN_CHROMA_ORDER_CRCB;
+		break;
+	case 2:
+		if (depth == 12)
+			cfg |= GSC_IN_YUV420_2P;
+		else
+			cfg |= GSC_IN_YUV422_2P;
+		if (frame->fmt->corder == GSC_CBCR)
+			cfg |= GSC_IN_CHROMA_ORDER_CBCR;
+		else
+			cfg |= GSC_IN_CHROMA_ORDER_CRCB;
+		break;
+	case 3:
+		if (depth == 12)
+			cfg |= GSC_IN_YUV420_3P;
+		else
+			cfg |= GSC_IN_YUV422_3P;
+		break;
+	};
+
+	if (is_tiled(frame->fmt))
+		cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE;
+
+	writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_output_path(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+
+	u32 cfg = readl(dev->regs + GSC_OUT_CON);
+	cfg &= ~GSC_OUT_PATH_MASK;
+
+	if (ctx->out_path == GSC_DMA)
+		cfg |= GSC_OUT_PATH_MEMORY;
+	else
+		cfg |= GSC_OUT_PATH_LOCAL;
+
+	writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_out_size(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_frame *frame = &ctx->d_frame;
+	u32 cfg;
+
+	/* Set output original size */
+	if (ctx->out_path == GSC_DMA) {
+		cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left);
+		cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top);
+		writel(cfg, dev->regs + GSC_DSTIMG_OFFSET);
+
+		cfg = GSC_DSTIMG_WIDTH(frame->f_width);
+		cfg |= GSC_DSTIMG_HEIGHT(frame->f_height);
+		writel(cfg, dev->regs + GSC_DSTIMG_SIZE);
+	}
+
+	/* Set output scaled size */
+	if (ctx->gsc_ctrls.rotate->val == 90 ||
+	    ctx->gsc_ctrls.rotate->val == 270) {
+		cfg = GSC_SCALED_WIDTH(frame->crop.height);
+		cfg |= GSC_SCALED_HEIGHT(frame->crop.width);
+	} else {
+		cfg = GSC_SCALED_WIDTH(frame->crop.width);
+		cfg |= GSC_SCALED_HEIGHT(frame->crop.height);
+	}
+	writel(cfg, dev->regs + GSC_SCALED_SIZE);
+}
+
+void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_frame *frame = &ctx->d_frame;
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_OUT_CON);
+	if (ctx->gsc_ctrls.csc_eq->val) {
+		if (ctx->gsc_ctrls.csc_range->val)
+			cfg |= GSC_OUT_RGB_HD_WIDE;
+		else
+			cfg |= GSC_OUT_RGB_HD_NARROW;
+	} else {
+		if (ctx->gsc_ctrls.csc_range->val)
+			cfg |= GSC_OUT_RGB_SD_WIDE;
+		else
+			cfg |= GSC_OUT_RGB_SD_NARROW;
+	}
+
+	if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
+		cfg |= GSC_OUT_RGB565;
+	else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
+		cfg |= GSC_OUT_XRGB8888;
+
+	writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_frame *frame = &ctx->d_frame;
+	u32 i, depth = 0;
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_OUT_CON);
+	cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
+		 GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK |
+		 GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE);
+	writel(cfg, dev->regs + GSC_OUT_CON);
+
+	if (is_rgb(frame->fmt->color)) {
+		gsc_hw_set_out_image_rgb(ctx);
+		return;
+	}
+
+	if (ctx->out_path != GSC_DMA) {
+		cfg |= GSC_OUT_YUV444;
+		goto end_set;
+	}
+
+	for (i = 0; i < frame->fmt->num_planes; i++)
+		depth += frame->fmt->depth[i];
+
+	switch (frame->fmt->num_comp) {
+	case 1:
+		cfg |= GSC_OUT_YUV422_1P;
+		if (frame->fmt->yorder == GSC_LSB_Y)
+			cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y;
+		else
+			cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C;
+		if (frame->fmt->corder == GSC_CBCR)
+			cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
+		else
+			cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
+		break;
+	case 2:
+		if (depth == 12)
+			cfg |= GSC_OUT_YUV420_2P;
+		else
+			cfg |= GSC_OUT_YUV422_2P;
+		if (frame->fmt->corder == GSC_CBCR)
+			cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
+		else
+			cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
+		break;
+	case 3:
+		cfg |= GSC_OUT_YUV420_3P;
+		break;
+	};
+
+	if (is_tiled(frame->fmt))
+		cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE;
+
+end_set:
+	writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_prescaler(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_scaler *sc = &ctx->scaler;
+	u32 cfg;
+
+	cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor);
+	cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio);
+	cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio);
+	writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO);
+}
+
+void gsc_hw_set_mainscaler(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_scaler *sc = &ctx->scaler;
+	u32 cfg;
+
+	cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
+	writel(cfg, dev->regs + GSC_MAIN_H_RATIO);
+
+	cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio);
+	writel(cfg, dev->regs + GSC_MAIN_V_RATIO);
+}
+
+void gsc_hw_set_rotation(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_IN_CON);
+	cfg &= ~GSC_IN_ROT_MASK;
+
+	switch (ctx->gsc_ctrls.rotate->val) {
+	case 270:
+		cfg |= GSC_IN_ROT_270;
+		break;
+	case 180:
+		cfg |= GSC_IN_ROT_180;
+		break;
+	case 90:
+		if (ctx->gsc_ctrls.hflip->val)
+			cfg |= GSC_IN_ROT_90_XFLIP;
+		else if (ctx->gsc_ctrls.vflip->val)
+			cfg |= GSC_IN_ROT_90_YFLIP;
+		else
+			cfg |= GSC_IN_ROT_90;
+		break;
+	case 0:
+		if (ctx->gsc_ctrls.hflip->val)
+			cfg |= GSC_IN_ROT_XFLIP;
+		else if (ctx->gsc_ctrls.vflip->val)
+			cfg |= GSC_IN_ROT_YFLIP;
+	}
+
+	writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_global_alpha(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	struct gsc_frame *frame = &ctx->d_frame;
+	u32 cfg;
+
+	if (!is_rgb(frame->fmt->color)) {
+		gsc_dbg("Not a RGB format");
+		return;
+	}
+
+	cfg = readl(dev->regs + GSC_OUT_CON);
+	cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK;
+
+	cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val);
+	writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_sfr_update(struct gsc_ctx *ctx)
+{
+	struct gsc_dev *dev = ctx->gsc_dev;
+	u32 cfg;
+
+	cfg = readl(dev->regs + GSC_ENABLE);
+	cfg |= GSC_ENABLE_SFR_UPDATE;
+	writel(cfg, dev->regs + GSC_ENABLE);
+}
diff --git a/drivers/media/video/exynos/gsc/gsc-regs.h b/drivers/media/video/exynos/gsc/gsc-regs.h
new file mode 100644
index 0000000..50ddabe
--- /dev/null
+++ b/drivers/media/video/exynos/gsc/gsc-regs.h
@@ -0,0 +1,211 @@ 
+/* linux/drivers/media/video/exynos/gsc/regs-gsc.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Register definition file for Samsung G-Scaler 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 REGS_GSC_H_
+#define REGS_GSC_H_
+
+/* G-Scaler enable */
+#define GSC_ENABLE			0x00
+#define GSC_ENABLE_ON_CLEAR		(1 << 4)
+#define GSC_ENABLE_QOS_ENABLE		(1 << 3)
+#define GSC_ENABLE_OP_STATUS		(1 << 2)
+#define GSC_ENABLE_SFR_UPDATE		(1 << 1)
+#define GSC_ENABLE_ON			(1 << 0)
+
+/* G-Scaler S/W reset */
+#define GSC_SW_RESET			0x04
+#define GSC_SW_RESET_SRESET		(1 << 0)
+
+/* G-Scaler IRQ */
+#define GSC_IRQ				0x08
+#define GSC_IRQ_STATUS_OR_IRQ		(1 << 17)
+#define GSC_IRQ_STATUS_OR_FRM_DONE	(1 << 16)
+#define GSC_IRQ_OR_MASK			(1 << 2)
+#define GSC_IRQ_FRMDONE_MASK		(1 << 1)
+#define GSC_IRQ_ENABLE			(1 << 0)
+
+/* G-Scaler input control */
+#define GSC_IN_CON			0x10
+#define GSC_IN_ROT_MASK			(7 << 16)
+#define GSC_IN_ROT_270			(7 << 16)
+#define GSC_IN_ROT_90_YFLIP		(6 << 16)
+#define GSC_IN_ROT_90_XFLIP		(5 << 16)
+#define GSC_IN_ROT_90			(4 << 16)
+#define GSC_IN_ROT_180			(3 << 16)
+#define GSC_IN_ROT_YFLIP		(2 << 16)
+#define GSC_IN_ROT_XFLIP		(1 << 16)
+#define GSC_IN_RGB_TYPE_MASK		(3 << 14)
+#define GSC_IN_RGB_HD_WIDE		(3 << 14)
+#define GSC_IN_RGB_HD_NARROW		(2 << 14)
+#define GSC_IN_RGB_SD_WIDE		(1 << 14)
+#define GSC_IN_RGB_SD_NARROW		(0 << 14)
+#define GSC_IN_YUV422_1P_ORDER_MASK	(1 << 13)
+#define GSC_IN_YUV422_1P_ORDER_LSB_Y	(0 << 13)
+#define GSC_IN_YUV422_1P_OEDER_LSB_C	(1 << 13)
+#define GSC_IN_CHROMA_ORDER_MASK	(1 << 12)
+#define GSC_IN_CHROMA_ORDER_CBCR	(0 << 12)
+#define GSC_IN_CHROMA_ORDER_CRCB	(1 << 12)
+#define GSC_IN_FORMAT_MASK		(7 << 8)
+#define GSC_IN_XRGB8888			(0 << 8)
+#define GSC_IN_RGB565			(1 << 8)
+#define GSC_IN_YUV420_2P		(2 << 8)
+#define GSC_IN_YUV420_3P		(3 << 8)
+#define GSC_IN_YUV422_1P		(4 << 8)
+#define GSC_IN_YUV422_2P		(5 << 8)
+#define GSC_IN_YUV422_3P		(6 << 8)
+#define GSC_IN_TILE_TYPE_MASK		(1 << 4)
+#define GSC_IN_TILE_C_16x8		(0 << 4)
+#define GSC_IN_TILE_C_16x16		(1 << 4)
+#define GSC_IN_TILE_MODE		(1 << 3)
+#define GSC_IN_LOCAL_SEL_MASK		(3 << 1)
+#define GSC_IN_LOCAL_FIMD_WB		(2 << 1)
+#define GSC_IN_LOCAL_CAM1		(1 << 1)
+#define GSC_IN_LOCAL_CAM0		(0 << 1)
+#define GSC_IN_PATH_MASK		(1 << 0)
+#define GSC_IN_PATH_LOCAL		(1 << 0)
+#define GSC_IN_PATH_MEMORY		(0 << 0)
+
+/* G-Scaler source image size */
+#define GSC_SRCIMG_SIZE			0x14
+#define GSC_SRCIMG_HEIGHT_MASK		(0x1fff << 16)
+#define GSC_SRCIMG_HEIGHT(x)		((x) << 16)
+#define GSC_SRCIMG_WIDTH_MASK		(0x1fff << 0)
+#define GSC_SRCIMG_WIDTH(x)		((x) << 0)
+
+/* G-Scaler source image offset */
+#define GSC_SRCIMG_OFFSET		0x18
+#define GSC_SRCIMG_OFFSET_Y_MASK	(0x1fff << 16)
+#define GSC_SRCIMG_OFFSET_Y(x)		((x) << 16)
+#define GSC_SRCIMG_OFFSET_X_MASK	(0x1fff << 0)
+#define GSC_SRCIMG_OFFSET_X(x)		((x) << 0)
+
+/* G-Scaler cropped source image size */
+#define GSC_CROPPED_SIZE		0x1c
+#define GSC_CROPPED_HEIGHT_MASK		(0x1fff << 16)
+#define GSC_CROPPED_HEIGHT(x)		((x) << 16)
+#define GSC_CROPPED_WIDTH_MASK		(0x1fff << 0)
+#define GSC_CROPPED_WIDTH(x)		((x) << 0)
+
+/* G-Scaler output control */
+#define GSC_OUT_CON			0x20
+#define GSC_OUT_GLOBAL_ALPHA_MASK	(0xff << 24)
+#define GSC_OUT_GLOBAL_ALPHA(x)		((x) << 24)
+#define GSC_OUT_RGB_TYPE_MASK		(3 << 10)
+#define GSC_OUT_RGB_HD_NARROW		(3 << 10)
+#define GSC_OUT_RGB_HD_WIDE		(2 << 10)
+#define GSC_OUT_RGB_SD_NARROW		(1 << 10)
+#define GSC_OUT_RGB_SD_WIDE		(0 << 10)
+#define GSC_OUT_YUV422_1P_ORDER_MASK	(1 << 9)
+#define GSC_OUT_YUV422_1P_ORDER_LSB_Y	(0 << 9)
+#define GSC_OUT_YUV422_1P_OEDER_LSB_C	(1 << 9)
+#define GSC_OUT_CHROMA_ORDER_MASK	(1 << 8)
+#define GSC_OUT_CHROMA_ORDER_CBCR	(0 << 8)
+#define GSC_OUT_CHROMA_ORDER_CRCB	(1 << 8)
+#define GSC_OUT_FORMAT_MASK		(7 << 4)
+#define GSC_OUT_XRGB8888		(0 << 4)
+#define GSC_OUT_RGB565			(1 << 4)
+#define GSC_OUT_YUV420_2P		(2 << 4)
+#define GSC_OUT_YUV420_3P		(3 << 4)
+#define GSC_OUT_YUV422_1P		(4 << 4)
+#define GSC_OUT_YUV422_2P		(5 << 4)
+#define GSC_OUT_YUV444			(7 << 4)
+#define GSC_OUT_TILE_TYPE_MASK		(1 << 2)
+#define GSC_OUT_TILE_C_16x8		(0 << 2)
+#define GSC_OUT_TILE_C_16x16		(1 << 2)
+#define GSC_OUT_TILE_MODE		(1 << 1)
+#define GSC_OUT_PATH_MASK		(1 << 0)
+#define GSC_OUT_PATH_LOCAL		(1 << 0)
+#define GSC_OUT_PATH_MEMORY		(0 << 0)
+
+/* G-Scaler scaled destination image size */
+#define GSC_SCALED_SIZE			0x24
+#define GSC_SCALED_HEIGHT_MASK		(0x1fff << 16)
+#define GSC_SCALED_HEIGHT(x)		((x) << 16)
+#define GSC_SCALED_WIDTH_MASK		(0x1fff << 0)
+#define GSC_SCALED_WIDTH(x)		((x) << 0)
+
+/* G-Scaler pre scale ratio */
+#define GSC_PRE_SCALE_RATIO		0x28
+#define GSC_PRESC_SHFACTOR_MASK		(7 << 28)
+#define GSC_PRESC_SHFACTOR(x)		((x) << 28)
+#define GSC_PRESC_V_RATIO_MASK		(7 << 16)
+#define GSC_PRESC_V_RATIO(x)		((x) << 16)
+#define GSC_PRESC_H_RATIO_MASK		(7 << 0)
+#define GSC_PRESC_H_RATIO(x)		((x) << 0)
+
+/* G-Scaler main scale horizontal ratio */
+#define GSC_MAIN_H_RATIO		0x2c
+#define GSC_MAIN_H_RATIO_MASK		(0xfffff << 0)
+#define GSC_MAIN_H_RATIO_VALUE(x)	((x) << 0)
+
+/* G-Scaler main scale vertical ratio */
+#define GSC_MAIN_V_RATIO		0x30
+#define GSC_MAIN_V_RATIO_MASK		(0xfffff << 0)
+#define GSC_MAIN_V_RATIO_VALUE(x)	((x) << 0)
+
+/* G-Scaler destination image size */
+#define GSC_DSTIMG_SIZE			0x40
+#define GSC_DSTIMG_HEIGHT_MASK		(0x1fff << 16)
+#define GSC_DSTIMG_HEIGHT(x)		((x) << 16)
+#define GSC_DSTIMG_WIDTH_MASK		(0x1fff << 0)
+#define GSC_DSTIMG_WIDTH(x)		((x) << 0)
+
+/* G-Scaler destination image offset */
+#define GSC_DSTIMG_OFFSET		0x44
+#define GSC_DSTIMG_OFFSET_Y_MASK	(0x1fff << 16)
+#define GSC_DSTIMG_OFFSET_Y(x)		((x) << 16)
+#define GSC_DSTIMG_OFFSET_X_MASK	(0x1fff << 0)
+#define GSC_DSTIMG_OFFSET_X(x)		((x) << 0)
+
+/* G-Scaler input y address mask */
+#define GSC_IN_BASE_ADDR_Y_MASK		0x4c
+/* G-Scaler input y base address */
+#define GSC_IN_BASE_ADDR_Y(n)		(0x50 + (n) * 0x4)
+
+/* G-Scaler input cb address mask */
+#define GSC_IN_BASE_ADDR_CB_MASK	0x7c
+/* G-Scaler input cb base address */
+#define GSC_IN_BASE_ADDR_CB(n)		(0x80 + (n) * 0x4)
+
+/* G-Scaler input cr address mask */
+#define GSC_IN_BASE_ADDR_CR_MASK	0xac
+/* G-Scaler input cr base address */
+#define GSC_IN_BASE_ADDR_CR(n)		(0xb0 + (n) * 0x4)
+
+/* G-Scaler input address mask */
+#define GSC_IN_CURR_ADDR_INDEX		(0xf << 12)
+#define GSC_IN_CURR_GET_INDEX(x)	((x) >> 12)
+#define GSC_IN_BASE_ADDR_PINGPONG(x)	((x) << 8)
+#define GSC_IN_BASE_ADDR_MASK		(0xff << 0)
+
+/* G-Scaler output y address mask */
+#define GSC_OUT_BASE_ADDR_Y_MASK	0x10c
+/* G-Scaler output y base address */
+#define GSC_OUT_BASE_ADDR_Y(n)		(0x110 + (n) * 0x4)
+
+/* G-Scaler output cb address mask */
+#define GSC_OUT_BASE_ADDR_CB_MASK	0x15c
+/* G-Scaler output cb base address */
+#define GSC_OUT_BASE_ADDR_CB(n)		(0x160 + (n) * 0x4)
+
+/* G-Scaler output cr address mask */
+#define GSC_OUT_BASE_ADDR_CR_MASK	0x1ac
+/* G-Scaler output cr base address */
+#define GSC_OUT_BASE_ADDR_CR(n)		(0x1b0 + (n) * 0x4)
+
+/* G-Scaler output address mask */
+#define GSC_OUT_CURR_ADDR_INDEX		(0xf << 24)
+#define GSC_OUT_CURR_GET_INDEX(x)	((x) >> 24)
+#define GSC_OUT_BASE_ADDR_PINGPONG(x)	((x) << 16)
+#define GSC_OUT_BASE_ADDR_MASK		(0xffff << 0)
+
+#endif /* REGS_GSC_H_ */
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index f79d0cc..5b4a33b 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -360,9 +360,11 @@  struct v4l2_pix_format {
 /* two non contiguous planes - one Y, one Cr + Cb interleaved  */
 #define V4L2_PIX_FMT_NV12M   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
 #define V4L2_PIX_FMT_NV12MT  v4l2_fourcc('T', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 64x32 macroblocks */
+#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 16x16 macroblocks */
 
 /* three non contiguous planes - Y, Cb, Cr */
 #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
+#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'V', 'U', 'M') /* 12  YVU420 planar */
 
 /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
 #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */