All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Dooks <ben-linux@fluff.org>
To: Sylwester Nawrocki <s.nawrocki@samsung.com>
Cc: linux-media@vger.kernel.org, linux-samsung-soc@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, p.osciak@samsung.com,
	m.szyprowski@samsung.com, kyungmin.park@samsung.com
Subject: Re: [PATCH v1 3/3] ARM: S5P: Add Camera interface (video postprocessor) driver
Date: Thu, 6 May 2010 07:25:55 +0100	[thread overview]
Message-ID: <20100506062555.GP26401@trinity.fluff.org> (raw)
In-Reply-To: <1271673000-3020-4-git-send-email-s.nawrocki@samsung.com>

On Mon, Apr 19, 2010 at 12:30:00PM +0200, Sylwester Nawrocki wrote:
> The purpose of this driver is to expose, as an initial stage,
>     memory to memory operations, including color conversion,
>     image resizing, flipping and rotation.
> 
>     Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>     Reviewed-by: Pawel Osciak <p.osciak@samsung.com>
>     Reviewed-by: Marek Szyprowski <m.szyprowski@samsung.com>
>     Reviewed-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig                     |    9 +
>  drivers/media/video/Makefile                    |    1 +
>  drivers/media/video/samsung-fimc/Makefile       |    3 +
>  drivers/media/video/samsung-fimc/s5p_fimc.c     | 1491 +++++++++++++++++++++++
>  drivers/media/video/samsung-fimc/s5p_fimc.h     |  331 +++++
>  drivers/media/video/samsung-fimc/s5p_fimc_reg.c |  559 +++++++++
>  include/linux/videodev2.h                       |    1 +
>  7 files changed, 2395 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/samsung-fimc/Makefile
>  create mode 100644 drivers/media/video/samsung-fimc/s5p_fimc.c
>  create mode 100644 drivers/media/video/samsung-fimc/s5p_fimc.h
>  create mode 100644 drivers/media/video/samsung-fimc/s5p_fimc_reg.c

are these also contained in the s3c64xx?

 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index f8fc865..1560de8 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -949,6 +949,15 @@ config VIDEO_OMAP2
>  	---help---
>  	  This is a v4l2 driver for the TI OMAP2 camera capture interface
>  
> +config  VIDEO_SAMSUNG_FIMC
> +	tristate "Samsung S5P Camera Interface (video postprocessor) driver"
> +	depends on VIDEO_DEV && VIDEO_V4L2
> +	select VIDEOBUF_DMA_CONTIG
> +	select V4L2_MEM2MEM_DEV
> +	default n
> +	help
> +	  This is a v4l2 driver the Samsung S5P camera capture interface
> +
>  #
>  # USB Multimedia device configuration
>  #
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index b88b617..c176a34 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -159,6 +159,7 @@ obj-$(CONFIG_VIDEO_MX1)			+= mx1_camera.o
>  obj-$(CONFIG_VIDEO_MX3)			+= mx3_camera.o
>  obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
> +obj-$(CONFIG_VIDEO_SAMSUNG_FIMC) 	+= samsung-fimc/
>  
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
>  
> diff --git a/drivers/media/video/samsung-fimc/Makefile b/drivers/media/video/samsung-fimc/Makefile
> new file mode 100644
> index 0000000..564d7c4
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/Makefile
> @@ -0,0 +1,3 @@
> +
> +obj-$(CONFIG_VIDEO_SAMSUNG_FIMC) := s5px_fimc.o
> +s5px_fimc-y := s5p_fimc.o s5p_fimc_reg.o
> diff --git a/drivers/media/video/samsung-fimc/s5p_fimc.c b/drivers/media/video/samsung-fimc/s5p_fimc.c
> new file mode 100644
> index 0000000..47e6765
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/s5p_fimc.c
> @@ -0,0 +1,1491 @@
> +/* linux/drivers/media/video/samsung-fimc/s5p_fimc.c
> + *
> + * S5P camera interface (postprocessor) driver
> + *
> + * Copyright (c) 2010 Samsung Electronics
> + *
> + * Sylwester Nawrocki, s.nawrocki@samsung.com
> + *
> + * 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 Foundiation. 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/clk.h>
> +#include <linux/wait.h>
> +#include <linux/fs.h>
> +#include <linux/irq.h>
> +#include <linux/mm.h>
> +#include <linux/interrupt.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/timer.h>
> +#include <linux/proc_fs.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/bug.h>
> +#include <linux/memory.h>
> +#include <plat/clock.h>

do you really need plat/clock.h
<linux/clk.h> should be enough.

> +#include <plat/regs-fimc.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/videobuf-core.h>
> +#include <media/videobuf-dma-contig.h>
> +#include "s5p_fimc.h"

some spacing between the different include groups would be nice.

> +
> +
> +#define ctx_get_frame(frame, ctx, type) do {			\
> +	if (V4L2_BUF_TYPE_VIDEO_OUTPUT == (type))		\
> +		frame = &(ctx)->s_frame;			\
> +	else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == (type))		\
> +		frame = &(ctx)->d_frame;			\
> +	else {							\
> +		v4l2_err(&(ctx)->fimc_dev->v4l2_dev,		\
> +		 "Wrong buffer/video queue type (%d)\n", type); \
> +		return -EINVAL;					\
> +	}							\
> +} while (0)
> +
> +
> +static int s5p_fimc_prepare_addr(struct fimc_ctx *ctx,
> +	struct fimc_vid_buffer *buf, enum v4l2_buf_type type);
> +static void queue_init(void *priv, struct videobuf_queue *vq,
> +		       enum v4l2_buf_type type);
> +static int s5p_fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
> +
> +
> +static struct videobuf_queue_ops s5p_fimc_qops;
> +
> +static struct s5p_fimc_fmt formats[] = {
> +	{
> +		.name	= "RGB565",
> +		.fourcc	= V4L2_PIX_FMT_RGB565X,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_RGB565,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "RGB666",
> +		.fourcc	= V4L2_PIX_FMT_RGB666,
> +		.depth	= 32,
> +		.color	= S5P_FIMC_RGB666,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name = "XRGB-8-8-8-8, 24 bpp",
> +		.fourcc	= V4L2_PIX_FMT_RGB24,
> +		.depth = 32,
> +		.color	= S5P_FIMC_RGB888,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 packed, YCbYCr",
> +		.fourcc	= V4L2_PIX_FMT_YUYV,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_YCBYCR422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +		}, {
> +		.name	= "YUV 4:2:2 packed, CbYCrY",
> +		.fourcc	= V4L2_PIX_FMT_UYVY,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_CBYCRY422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 packed, CrYCbY",
> +		.fourcc	= V4L2_PIX_FMT_VYUY,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_CRYCBY422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 packed, YCrYCb",
> +		.fourcc	= V4L2_PIX_FMT_YVYU,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_YCRYCB422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 planar, Y/Cb/Cr",
> +		.fourcc	= V4L2_PIX_FMT_YUV422P,
> +		.depth	= 12,
> +		.color	= S5P_FIMC_YCBCR422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 3
> +	}, {
> +		.name	= "YUV 4:2:2 planar, Y/CbCr",
> +		.fourcc	= V4L2_PIX_FMT_NV16,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_YCBCR422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 2
> +	}, {
> +		.name	= "YUV 4:2:2 planar, Y/CrCb",
> +		.fourcc	= V4L2_PIX_FMT_NV61,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_RGB565,
> +		.buff_cnt = 1,
> +		.planes_cnt = 2
> +	}, {
> +		.name	= "YUV 4:2:0 planar, YCbCr",
> +		.fourcc	= V4L2_PIX_FMT_YUV420,
> +		.depth	= 12,
> +		.color	= S5P_FIMC_YCBCR420,
> +		.buff_cnt = 1,
> +		.planes_cnt = 3
> +	}, {
> +		.name	= "YUV 4:2:0 planar, Y/CbCr",
> +		.fourcc	= V4L2_PIX_FMT_NV12,
> +		.depth	= 12,
> +		.color	= S5P_FIMC_YCBCR420,
> +		.buff_cnt = 1,
> +		.planes_cnt = 2
> +	}
> + };
> +
> +#define NUM_FORMATS ARRAY_SIZE(formats)

is this really necessary?

> +static struct v4l2_queryctrl s5p_fimc_ctrls[] = {
> +	{
> +		.id		= V4L2_CID_HFLIP,
> +		.type		= V4L2_CTRL_TYPE_BOOLEAN,
> +		.name		= "Horizontal flip",
> +		.minimum	= 0,
> +		.maximum	= 1,
> +		.default_value	= 0,
> +	},
> +	{
> +		.id		= V4L2_CID_VFLIP,
> +		.type		= V4L2_CTRL_TYPE_BOOLEAN,
> +		.name		= "Vertical flip",
> +		.minimum	= 0,
> +		.maximum	= 1,
> +		.default_value	= 0,
> +	},
> +	{
> +		.id		= V4L2_CID_ROTATE,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Rotation (CCW)",
> +		.minimum	= 0,
> +		.maximum	= 270,
> +		.step		= 90,
> +		.default_value	= 0,
> +	},
> +};
> +
> +
> +static struct v4l2_queryctrl *get_ctrl(int id)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(s5p_fimc_ctrls); ++i)
> +		if (id == s5p_fimc_ctrls[i].id)
> +			return &s5p_fimc_ctrls[i];
> +	return NULL;
> +}
> +
> +static int s5p_fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
> +{
> +	if (src >= tar * 64) {
> +		return -EINVAL;
> +	} else if (src >= tar * 32) {
> +		*ratio = 32;
> +		*shift = 5;
> +	} else if (src >= tar * 16) {
> +		*ratio = 16;
> +		*shift = 4;
> +	} else if (src >= tar * 8) {
> +		*ratio = 8;
> +		*shift = 3;
> +	} else if (src >= tar * 4) {
> +		*ratio = 4;
> +		*shift = 2;
> +	} else if (src >= tar * 2) {
> +		*ratio = 2;
> +		*shift = 1;
> +	} else {
> +		*ratio = 1;
> +		*shift = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +int s5p_fimc_set_scaler_info(struct fimc_ctx *ctx)
> +{
> +	struct fimc_scaler *sc = &ctx->scaler;
> +	struct fimc_dma_offset *d_ofs = &ctx->s_frame.dma_offset;
> +	int tx, ty, sx, sy;
> +	int width, height, h_ofs, v_ofs;
> +	int ret;
> +	struct fimc_frame *src_frame = &ctx->s_frame;
> +	struct fimc_frame *dst_frame = &ctx->d_frame;
> +
> +	if (ctx->in_path == S5P_FIMC_DMA) {
> +		if ((90 == ctx->rotation || 270 == ctx->rotation)

don't like if (const == var) a lot.

> +			&& ctx->out_path == S5P_FIMC_LCDFIFO) {
> +			/* here we are using only the input rotator */
> +			width = src_frame->height;
> +			height = src_frame->width;
> +			h_ofs = d_ofs->y_v;
> +			v_ofs = d_ofs->y_h;
> +		} else {
> +			width = src_frame->width;
> +			height = src_frame->height;
> +			h_ofs = d_ofs->y_h;
> +			v_ofs = d_ofs->y_v;
> +		}
> +	} else {
> +		width = src_frame->width;
> +		height = src_frame->height;
> +		h_ofs = d_ofs->y_h;
> +		v_ofs = d_ofs->y_v;
> +	}
> +
> +	tx = dst_frame->width;
> +	ty = dst_frame->height;
> +
> +	if (tx <= 0 || ty <= 0) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev,
> +			"invalid target size: %d %d", tx, ty);
> +		return -EINVAL;
> +	}
> +
> +	sx = width;
> +	sy = height;
> +
> +	sc->real_width = width;
> +	sc->real_height = height;
> +
> +	if (sx <= 0 || sy <= 0) {
> +		err("invalid source size: s: %d %d, t: %d %d", sx, sy, tx, ty);
> +		return -EINVAL;
> +	}
> +
> +	dbg("sx= %d, sy= %d, tx= %d, ty= %d", sx, sy, tx, ty);
> +
> +	ret = s5p_fimc_get_scaler_factor(sx, tx,
> +			  &sc->pre_hratio, &sc->hfactor);
> +	if (ret)
> +		return ret;
> +
> +	ret = s5p_fimc_get_scaler_factor(sy, ty,
> +			  &sc->pre_vratio, &sc->vfactor);
> +	if (ret)
> +		return ret;
> +
> +	sc->pre_dst_width = sx/sc->pre_hratio;
> +	sc->pre_dst_height = sy/sc->pre_vratio;
> +
> +	sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
> +	sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
> +
> +	dbg("sc->main_hratio= %d, sc->main_vratio= %d",
> +		sc->main_hratio, sc->main_vratio);
> +	dbg("sc->hfactor= %d, sc->vfactor= %d", sc->hfactor, sc->vfactor);
> +
> +	sc->scaleup_h = (tx >= sx) ? 1 : 0;
> +	sc->scaleup_v = (ty >= sy) ? 1 : 0;
> +
> +	/* check to see if input and output size/format differ */
> +	if (src_frame->fmt->color == dst_frame->fmt->color
> +		&& src_frame->width == dst_frame->width
> +		&& src_frame->height == dst_frame->height)
> +		sc->copy_mode = 1;
> +	else
> +		sc->copy_mode = 0;
> +
> +	return 0;
> +}
> +
> +static void s5p_fimc_clear_irq(struct fimc_dev *dev)
> +{
> +	u32 cfg = readl(dev->regs + S5P_CIGCTRL);
> +	cfg |= S5P_CIGCTRL_IRQ_CLR;
> +	writel(cfg, dev->regs + S5P_CIGCTRL);
> +}
> +
> +static irqreturn_t s5p_fimc_isr(int irq, void *priv)
> +{
> +	struct fimc_ctx *ctx;
> +	struct fimc_vid_buffer *src_buf, *dst_buf;
> +	struct fimc_dev *dev = (struct fimc_dev *)priv;
> +
> +	BUG_ON(NULL == dev);
> +
> +	s5p_fimc_clear_irq(dev);
> +	s5p_fimc_check_fifo(dev);
> +
> +	ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
> +	if (NULL == ctx)
> +		return IRQ_HANDLED;
> +
> +	src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
> +	dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
> +	spin_lock(&dev->irqlock);
> +	src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
> +	wake_up(&src_buf->vb.done);
> +	wake_up(&dst_buf->vb.done);
> +	spin_unlock(&dev->irqlock);
> +	v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Rewrite context data to the registers as required and start
> + * the transaction in hardware.
> + */
> +static void s5p_fimc_dma_run(void *priv)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame;
> +	u32	ret;
> +
> +	if (NULL == ctx || S5P_FIMC_DMA != ctx->out_path)
> +		return;

!ctx would be sufficient for the first test.

> +
> +	ctx->updated |= S5P_FIMC_SRC_ADDR | S5P_FIMC_DST_ADDR;
> +	ret = s5p_fimc_prepare_config(ctx, ctx->updated);
> +	if (ret) {
> +		err("Wrong configuration");
> +		return;
> +	}
> +
> +	frame = &ctx->s_frame;
> +	dbg("SRC: width= %d, height= %d, f_width= %d, f_height= %d " \
> +		"offs_h= %d, offs_v= %d",
> +		frame->width, frame->height,
> +		frame->f_width, frame->f_height,
> +		frame->offs_h, frame->offs_v);
> +
> +	frame = &ctx->d_frame;
> +	dbg("DST: width= %d, height= %d, f_width= %d, f_height= %d " \
> +		"offs_h= %d, offs_v= %d",
> +		frame->width, frame->height,
> +		frame->f_width, frame->f_height,
> +		frame->offs_h, frame->offs_v);
> +
> +	s5p_fimc_set_input_addr(ctx);
> +
> +	if (ctx->updated&S5P_FIMC_PARAMS) {
> +		s5p_fimc_set_input_path(ctx);
> +
> +		if (ctx->in_path == S5P_FIMC_DMA)
> +			s5p_fimc_set_in_dma(ctx);
> +
> +		if (s5p_fimc_set_scaler_info(ctx)) {
> +			err("scaler configuration error");
> +			return;
> +		}
> +		s5p_fimc_set_prescaler(ctx);
> +		s5p_fimc_set_scaler(ctx);
> +		s5p_fimc_set_target_format(ctx);
> +		s5p_fimc_set_effect(ctx);
> +	}
> +
> +	s5p_fimc_set_output_path(ctx);
> +	if (ctx->updated & (S5P_FIMC_DST_ADDR | S5P_FIMC_PARAMS))
> +		s5p_fimc_set_output_addr(ctx);
> +
> +	if (ctx->updated & S5P_FIMC_PARAMS)
> +		s5p_fimc_set_out_dma(ctx);
> +
> +	if (ctx->scaler.enabled)
> +		s5p_fimc_start_scaler(ctx);
> +
> +	s5p_fimc_en_capture(ctx);
> +
> +	if (ctx->in_path == S5P_FIMC_DMA)
> +		s5p_fimc_start_in_dma(dev);
> +
> +	ctx->updated = 0;
> +}
> +
> +static void s5p_fimc_job_abort(void *priv)
> +{
> +
> +}
> +
> +/* set order for 1 and 2 plane YCBCR 4:2:2 formats */
> +static void s5p_fimc_set_yuv_order(struct fimc_ctx *ctx)
> +{
> +	/* the one only mode supported in SoC */
> +	ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
> +	ctx->out_order_2p = S5P_FIMC_LSB_CRCB;
> +
> +	/* set order for 1 plane input formats */
> +	switch (ctx->s_frame.fmt->color) {
> +	case S5P_FIMC_YCRYCB422:
> +		ctx->in_order_1p = S5P_FIMC_IN_YCRYCB;
> +		break;
> +	case S5P_FIMC_CBYCRY422:
> +		ctx->in_order_1p = S5P_FIMC_IN_CBYCRY;
> +		break;
> +	case S5P_FIMC_CRYCBY422:
> +		ctx->in_order_1p = S5P_FIMC_IN_CRYCBY;
> +		break;
> +	case S5P_FIMC_YCBYCR422: /* fall through */
> +	default:
> +		ctx->in_order_1p = S5P_FIMC_IN_YCBYCR;
> +		break;
> +	}
> +	dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
> +
> +	switch (ctx->d_frame.fmt->color) {
> +	case S5P_FIMC_YCRYCB422:
> +		ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB;
> +		break;
> +	case S5P_FIMC_CBYCRY422:
> +		ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY;
> +		break;
> +	case S5P_FIMC_CRYCBY422:
> +		ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY;
> +		break;
> +	case S5P_FIMC_YCBYCR422: /* fall through */
> +	default:
> +		ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR;
> +		break;
> +	}
> +	dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
> +}
> +
> +/**
> + * @name	s5p_fimc_prepare_config
> + *		Check dimensions, operation and color mode
> + * @return	0 if dimensions are valid, non zero otherwise
> + */
> +static int s5p_fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
> +{
> +	struct fimc_frame *src_frame, *dst_frame;
> +	struct fimc_vid_buffer *buf = NULL;
> +	int ret = 0;
> +	u32 tmp;
> +
> +	dbg("flags= 0x%X", flags);
> +
> +	src_frame = &ctx->s_frame;
> +	dst_frame = &ctx->d_frame;
> +
> +	if (flags & S5P_FIMC_PARAMS) {
> +		if ((S5P_FIMC_DMA == ctx->out_path) &&
> +			(90 == ctx->rotation || 270 == ctx->rotation)) {
> +			tmp = dst_frame->f_width;
> +			dst_frame->f_width = dst_frame->f_height;
> +			dst_frame->f_height = tmp;
> +			tmp = dst_frame->width;
> +			dst_frame->width = dst_frame->height;
> +			dst_frame->height = tmp;
> +		}
> +
> +		/* prepare the output offset related attributes for scaler */
> +		dst_frame->dma_offset.y_h = dst_frame->offs_h
> +			 * (dst_frame->fmt->depth >> 3);
> +		dst_frame->dma_offset.y_v = dst_frame->offs_v;
> +
> +		dst_frame->dma_offset.cb_h = dst_frame->offs_h;
> +		dst_frame->dma_offset.cb_v = dst_frame->offs_v;
> +
> +		dst_frame->dma_offset.cr_h = dst_frame->offs_h;
> +		dst_frame->dma_offset.cr_v = dst_frame->offs_v;
> +
> +		if (3 == dst_frame->fmt->planes_cnt) {
> +			dst_frame->dma_offset.cb_h /= 2;
> +			dst_frame->dma_offset.cb_v /= 2;
> +			dst_frame->dma_offset.cr_h /= 2;
> +			dst_frame->dma_offset.cr_v /= 2;
> +		}
> +
> +		dbg("OUT OFFSET: color= %d, y_h= %d, y_v= %d",
> +			dst_frame->fmt->color,
> +			dst_frame->dma_offset.y_h, dst_frame->dma_offset.y_v);
> +
> +		/* prepare the input offset related attributes for scaler */
> +		src_frame->dma_offset.y_h = src_frame->offs_h
> +			* (src_frame->fmt->depth >> 3);
> +		src_frame->dma_offset.y_v = src_frame->offs_v;
> +
> +		src_frame->dma_offset.cb_h = src_frame->offs_h;
> +		src_frame->dma_offset.cb_v = src_frame->offs_v;
> +
> +		src_frame->dma_offset.cr_h = src_frame->offs_h;
> +		src_frame->dma_offset.cr_v = src_frame->offs_v;
> +
> +		if (3 == src_frame->fmt->planes_cnt) {
> +			src_frame->dma_offset.cb_h /= 2;
> +			src_frame->dma_offset.cb_v /= 2;
> +			src_frame->dma_offset.cr_h /= 2;
> +			src_frame->dma_offset.cr_v /= 2;
> +		}
> +
> +		dbg("IN OFFSET: color= %d, y_h= %d, y_v= %d",
> +			src_frame->fmt->color, src_frame->dma_offset.y_h,
> +			src_frame->dma_offset.y_v);
> +
> +		s5p_fimc_set_yuv_order(ctx);
> +
> +		/* Check against the scaler ratio */
> +		if (src_frame->height > SCALER_MAX_VRATIO*dst_frame->height ||
> +			src_frame->width > SCALER_MAX_HRATIO*dst_frame->width) {
> +				err("Out of the scaler range");
> +				return -EINVAL;
> +		}
> +	} /* if (flags & S5P_FIMC_PARAMS) */
> +
> +	/* input DMA mode is not allowed when the scaler is disabled */
> +	ctx->scaler.enabled = 1;
> +
> +	if (flags & (S5P_FIMC_SRC_ADDR)) {
> +		buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
> +		ret = s5p_fimc_prepare_addr(ctx, buf,
> +			V4L2_BUF_TYPE_VIDEO_OUTPUT);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (flags & (S5P_FIMC_DST_ADDR)) {
> +		buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
> +		ret = s5p_fimc_prepare_addr(ctx, buf,
> +			V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +	}
> +
> +	return ret;
> +}
> +
> +/* the color format (planes_cnt, buff_cnt) must be already configured */
> +static int s5p_fimc_prepare_addr(struct fimc_ctx *ctx,
> +		struct fimc_vid_buffer *buf, enum v4l2_buf_type type)
> +{
> +	int ret = 0;
> +	struct fimc_frame *frame;
> +	u32 pix_size;
> +
> +	ctx_get_frame(frame, ctx, type);
> +
> +	if (!buf)
> +		return -EINVAL;
> +
> +	pix_size = frame->width * frame->height;
> +
> +	dbg("%d buff_cnt, planes_cnt= %d, frame->size= %d, pix_size= %d",
> +		frame->fmt->buff_cnt, frame->fmt->planes_cnt,
> +		frame->size, pix_size);
> +
> +
> +	if (frame->fmt->buff_cnt == 1) {
> +		frame->addr_y = videobuf_to_dma_contig(&buf->vb);
> +		switch (frame->fmt->planes_cnt) {
> +		case 1:
> +			frame->addr_cb = 0;
> +			frame->addr_cr = 0;
> +			break;
> +		case 2:
> +			/* decompose Y into Y/Cb */
> +			frame->addr_cb = (u32)(frame->addr_y + pix_size);
> +			frame->addr_cr = 0;
> +			break;
> +		case 3:
> +			frame->addr_cb = (u32)(frame->addr_y + pix_size);
> +			/* decompose Y into Y/Cb/Cr */
> +			if (S5P_FIMC_YCBCR420 == frame->fmt->color)
> +				frame->addr_cr = (u32)(frame->addr_cb
> +						+ (pix_size >> 2));
> +			else /* 422 */
> +				frame->addr_cr = (u32)(frame->addr_cb
> +						+ (pix_size >> 1));
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	dbg("PHYS_ADDR: type= %d, y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
> +	type, frame->addr_y, frame->addr_cb, frame->addr_cr, ret);
> +
> +	return ret;
> +}
> +
> +static void s5p_fimc_buf_release(struct videobuf_queue *vq,
> +				    struct videobuf_buffer *vb)
> +{
> +	videobuf_dma_contig_free(vq, vb);
> +	vb->state = VIDEOBUF_NEEDS_INIT;
> +}
> +
> +static int s5p_fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
> +				unsigned int *size)
> +{
> +	struct fimc_ctx *ctx = vq->priv_data;
> +	struct fimc_frame *frame;
> +
> +	ctx_get_frame(frame, ctx, vq->type);
> +
> +	*size = (frame->width * frame->height * frame->fmt->depth) >> 3;
> +	if (0 == *count)
> +		*count = 1;
> +	return 0;
> +}
> +
> +static int s5p_fimc_buf_prepare(struct videobuf_queue *vq,
> +				struct videobuf_buffer *vb,
> +				enum v4l2_field field)
> +{
> +	struct fimc_ctx *ctx = vq->priv_data;
> +	struct fimc_frame *frame;
> +	int ret;
> +
> +	ctx_get_frame(frame, ctx, vq->type);
> +
> +	if (vb->baddr) {
> +		if (vb->bsize < frame->size) {
> +			v4l2_err(&ctx->fimc_dev->v4l2_dev,
> +				"User-provided buffer too small (%d < %d)\n",
> +				 vb->bsize, frame->size);
> +			WARN_ON(1);
> +			return -EINVAL;
> +		}
> +	} else if (vb->state != VIDEOBUF_NEEDS_INIT
> +		   && vb->bsize < frame->size) {
> +		return -EINVAL;
> +	}
> +
> +	vb->width       = frame->width;
> +	vb->height      = frame->height;
> +	vb->bytesperline = (frame->width * frame->fmt->depth) >> 3;
> +	vb->size        = frame->size;
> +	vb->field       = field;
> +
> +	if (VIDEOBUF_NEEDS_INIT == vb->state) {
> +		ret = videobuf_iolock(vq, vb, NULL);
> +		if (ret) {
> +			v4l2_err(&ctx->fimc_dev->v4l2_dev,
> +				"Iolock failed\n");
> +			goto fail;
> +		}
> +	}
> +	vb->state = VIDEOBUF_PREPARED;
> +
> +	return 0;
> +fail:
> +	s5p_fimc_buf_release(vq, vb);
> +	return ret;
> +}
> +
> +static void s5p_fimc_buf_queue(struct videobuf_queue *vq,
> +				  struct videobuf_buffer *vb)
> +{
> +	struct fimc_ctx *ctx = vq->priv_data;
> +	v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
> +}
> +
> +
> +static struct videobuf_queue_ops s5p_fimc_qops = {
> +	.buf_setup	= s5p_fimc_buf_setup,
> +	.buf_prepare	= s5p_fimc_buf_prepare,
> +	.buf_queue	= s5p_fimc_buf_queue,
> +	.buf_release	= s5p_fimc_buf_release,
> +};
> +
> +static int s5p_fimc_querycap(struct file *file, void *priv,
> +			   struct v4l2_capability *cap)
> +{
> +	struct fimc_ctx *ctx = file->private_data;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +
> +	strncpy(cap->driver, dev->pdev->name, sizeof(cap->driver) - 1);
> +	strncpy(cap->card, dev->pdev->name, sizeof(cap->card) - 1);
> +	cap->bus_info[0] = 0;
> +	cap->version = KERNEL_VERSION(1, 0, 0);
> +	cap->capabilities = V4L2_CAP_STREAMING |
> +		V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_enum_fmt(struct file *file, void *priv,
> +			   struct v4l2_fmtdesc *f)
> +{
> +	struct s5p_fimc_fmt *fmt;
> +
> +	if (f->index >= NUM_FORMATS)
> +		return -EINVAL;
> +
> +	fmt = &formats[f->index];
> +	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
> +	f->pixelformat = fmt->fourcc;
> +	return 0;
> +}
> +
> +static int s5p_fimc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_frame *frame;
> +
> +	ctx_get_frame(frame, ctx, f->type);
> +
> +	f->fmt.pix.width	= frame->width;
> +	f->fmt.pix.height	= frame->height;
> +	f->fmt.pix.field	= V4L2_FIELD_NONE;
> +	f->fmt.pix.pixelformat	= frame->fmt->fourcc;
> +
> +	return 0;
> +}
> +
> +static struct s5p_fimc_fmt *find_format(struct v4l2_format *f)
> +{
> +	struct s5p_fimc_fmt *fmt;
> +	unsigned int i;
> +
> +	for (i = 0; i < NUM_FORMATS; ++i) {
> +		fmt = &formats[i];
> +		if (fmt->fourcc == f->fmt.pix.pixelformat)
> +			break;
> +	}
> +	if (i == NUM_FORMATS)
> +		return NULL;
> +
> +	return fmt;
> +}
> +
> +static int s5p_fimc_try_fmt(struct file *file, void *priv,
> +			     struct v4l2_format *f)
> +{
> +	struct s5p_fimc_fmt *fmt;
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	u32 max_width, max_height;
> +	u32 mod_x = 1;	/* defaults for non-DMA mode */
> +	u32 mod_y = 1;
> +
> +
> +	fmt = find_format(f);
> +	if (!fmt) {
> +		v4l2_err(&dev->v4l2_dev, "Fourcc format (0x%08x) invalid.\n",
> +			 pix->pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	if (pix->field == V4L2_FIELD_ANY)
> +		pix->field = V4L2_FIELD_NONE;
> +	else if (V4L2_FIELD_NONE != pix->field)
> +		return -EINVAL;
> +
> +	if (V4L2_BUF_TYPE_VIDEO_OUTPUT == f->type) {
> +		max_width = dev->pldata->scaler_dis_w;
> +		max_height = dev->pldata->scaler_dis_w;
> +	} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == f->type) {
> +		max_width = dev->pldata->out_rot_dis_w;
> +		max_height = dev->pldata->out_rot_dis_w;
> +	} else {
> +		err("Wrong stream type (%d)", f->type);
> +		return -EINVAL;
> +	}
> +
> +	dbg("max_w= %d, max_h= %d", max_width, max_height);
> +
> +	if (pix->height > max_height)
> +		pix->height = max_height;
> +	if (pix->width > max_width)
> +		pix->width = max_width;
> +
> +	if (S5P_FIMC_DMA == ctx->in_path
> +		|| V4L2_BUF_TYPE_VIDEO_CAPTURE == f->type) {
> +		mod_x = (DMA_MIN_SIZE - 1);
> +		mod_y = (DMA_MIN_SIZE - 1);
> +	} else {
> +		mod_x = 1, mod_y = 1;
> +	}
> +	dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y);
> +
> +	pix->width &= ~mod_x;
> +	pix->height &= ~mod_y;
> +	if (0 == pix->width)
> +		pix->width = mod_x + 1;
> +	if (0 == pix->height)
> +		pix->height = mod_y + 1;
> +
> +	if (0 == pix->bytesperline) {
> +		pix->bytesperline = (pix->width * fmt->depth) >> 3;
> +	} else {
> +		u32 f_width = pix->bytesperline*8/fmt->depth;
> +		if (f_width > max_width)
> +			pix->bytesperline = (max_width * fmt->depth) >> 3;
> +	}
> +
> +	if (0 == pix->sizeimage)
> +		pix->sizeimage = pix->height * pix->bytesperline;
> +
> +	dbg("pix->bytesperline= %d, fmt->depth= %d",
> +	    pix->bytesperline, fmt->depth);
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +	struct fimc_frame *frame;
> +	struct v4l2_pix_format *pix;
> +	struct videobuf_queue	*src_vq = NULL,
> +				*dst_vq = NULL;
> +	struct fimc_ctx *ctx = priv;
> +	int ret = 0;
> +
> +	BUG_ON(NULL == ctx);
> +
> +	ret = s5p_fimc_try_fmt(file, priv, f);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&ctx->lock);
> +
> +	src_vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
> +	dst_vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
> +
> +	mutex_lock(&src_vq->vb_lock);
> +	mutex_lock(&dst_vq->vb_lock);
> +
> +	if (videobuf_queue_is_busy(src_vq)) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "%s queue busy\n", __func__);
> +		ret = -EBUSY;
> +		goto s_fmt_out;
> +	}
> +
> +	if (videobuf_queue_is_busy(dst_vq)) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "%s queue busy\n", __func__);
> +		ret = -EBUSY;
> +		goto s_fmt_out;
> +	}
> +
> +	ctx_get_frame(frame, ctx, f->type);
> +
> +	pix = &f->fmt.pix;
> +	frame->fmt = find_format(f);
> +	if (!frame->fmt) {
> +		ret = -EINVAL;
> +		goto s_fmt_out;
> +	}
> +
> +	dbg("depth=%d, bytesperline=%d", frame->fmt->depth, pix->bytesperline);
> +
> +	frame->width = pix->width;
> +	frame->height = pix->height;
> +
> +	frame->f_width = pix->bytesperline*8/frame->fmt->depth;
> +	frame->f_height = pix->sizeimage/pix->bytesperline;
> +
> +	frame->o_width = pix->width;
> +	frame->o_height = pix->height;
> +	frame->offs_h = 0;
> +	frame->offs_v = 0;
> +	frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
> +	ctx->updated |= S5P_FIMC_PARAMS;
> +	src_vq->field = dst_vq->field = pix->field;
> +	dbg("f_width= %d, f_height= %d", frame->f_width, frame->f_height);
> +
> +s_fmt_out:
> +	mutex_unlock(&dst_vq->vb_lock);
> +	mutex_unlock(&src_vq->vb_lock);
> +	mutex_unlock(&ctx->lock);
> +	return ret;
> +}
> +
> +static int s5p_fimc_reqbufs(struct file *file, void *priv,
> +			  struct v4l2_requestbuffers *reqbufs)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
> +}
> +
> +static int s5p_fimc_querybuf(struct file *file, void *priv,
> +			   struct v4l2_buffer *buf)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int s5p_fimc_qbuf(struct file *file, void *priv,
> +			  struct v4l2_buffer *buf)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int s5p_fimc_dqbuf(struct file *file, void *priv,
> +			   struct v4l2_buffer *buf)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int s5p_fimc_streamon(struct file *file, void *priv,
> +			   enum v4l2_buf_type type)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
> +}
> +
> +static int s5p_fimc_streamoff(struct file *file, void *priv,
> +			    enum v4l2_buf_type type)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
> +}
> +
> +static int s5p_fimc_queryctrl(struct file *file, void *priv,
> +			    struct v4l2_queryctrl *qc)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(qc->id);
> +	if (!c)
> +		return -EINVAL;
> +	*qc = *c;
> +	return 0;
> +}
> +
> +static int s5p_fimc_g_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct fimc_ctx *ctx = priv;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_HFLIP:
> +		ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0;
> +		break;
> +	case V4L2_CID_VFLIP:
> +		ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0;
> +		break;
> +	case V4L2_CID_ROTATE:
> +		ctrl->value = ctx->rotation;
> +		break;
> +	default:
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	dbg("ctrl->value= %d", ctrl->value);
> +	return 0;
> +}
> +
> +static int check_ctrl_val(struct fimc_ctx *ctx,
> +			  struct v4l2_control *ctrl)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(ctrl->id);
> +	if (!c)
> +		return -EINVAL;
> +
> +	if (ctrl->value < c->minimum || ctrl->value > c->maximum
> +		|| (c->step != 0 && ctrl->value % c->step != 0)) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "Invalid control value\n");
> +		return -ERANGE;
> +	}
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_s_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct s5p_platform_fimc *pldata = ctx->fimc_dev->pldata;
> +	int ret = 0;
> +
> +	ret = check_ctrl_val(ctx, ctrl);
> +	if (ret != 0)
> +		return ret;
> +
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_HFLIP:
> +		if (ctrl->value)
> +			ctx->flip |= FLIP_X_AXIS;
> +		else
> +			ctx->flip &= ~FLIP_X_AXIS;
> +		break;
> +
> +	case V4L2_CID_VFLIP:
> +		if (ctrl->value)
> +			ctx->flip |= FLIP_Y_AXIS;
> +		else
> +			ctx->flip &= ~FLIP_Y_AXIS;
> +		break;
> +
> +	case V4L2_CID_ROTATE:
> +		if (90 == ctrl->value || 270 == ctrl->value) {
> +			/* use input rotator in LCDFIFO output mode only */
> +			if (S5P_FIMC_LCDFIFO == ctx->out_path) {
> +				if (!(pldata->capability & S5P_FIMC_IN_ROT))
> +					return -EINVAL;
> +			} /* in DMA/ out DMA case */
> +			else if (S5P_FIMC_DMA == ctx->in_path &&
> +				 !(pldata->capability & S5P_FIMC_OUT_ROT))
> +				return -EINVAL;
> +		}
> +
> +		ctx->rotation = ctrl->value;
> +		if (180 == ctrl->value)
> +			ctx->flip = FLIP_XY_AXIS;
> +		break;
> +
> +	default:
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +
> +	ctx->updated |= S5P_FIMC_PARAMS;
> +	return 0;
> +}
> +
> +
> +static int s5p_fimc_cropcap(struct file *file, void *fh,
> +			     struct v4l2_cropcap *cr)
> +{
> +	struct fimc_frame *frame;
> +	struct fimc_ctx *ctx = fh;
> +	if (!ctx) {
> +		WARN_ON(1);
> +		return -ENOENT;
> +	}
> +
> +	ctx_get_frame(frame, ctx, cr->type);
> +
> +	cr->bounds.left = 0;
> +	cr->bounds.top = 0;
> +	cr->bounds.width = frame->f_width;
> +	cr->bounds.height = frame->f_height;
> +	cr->defrect.left = frame->offs_h;
> +	cr->defrect.top = frame->offs_v;
> +	cr->defrect.width = frame->o_width;
> +	cr->defrect.height = frame->o_height;
> +	return 0;
> +}
> +
> +static int s5p_fimc_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
> +{
> +	struct fimc_frame *frame;
> +	struct fimc_ctx *ctx = file->private_data;
> +
> +	if (!ctx) {
> +		WARN_ON(1);
> +		return -ENOENT;
> +	}
> +
> +	ctx_get_frame(frame, ctx, cr->type);
> +
> +	cr->c.left = frame->offs_h;
> +	cr->c.top = frame->offs_v;
> +	cr->c.width = frame->width;
> +	cr->c.height = frame->height;
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
> +{
> +	struct fimc_frame *f;
> +	struct fimc_ctx *ctx = file->private_data;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +
> +	if (cr->c.top < 0 || cr->c.left < 0) {
> +		v4l2_err(&dev->v4l2_dev,
> +			"doesn't support negative values for top & left\n");
> +		return -EINVAL;
> +	}
> +
> +	ctx_get_frame(f, ctx, cr->type);
> +
> +	dbg("%d %d %d %d f_w= %d, f_h= %d",
> +		cr->c.left, cr->c.top, cr->c.width, cr->c.height,
> +		f->f_width, f->f_height);
> +
> +	/* adjust to pixel boundary */
> +	cr->c.width = cr->c.width & ~PIX_ALIGN_MASK;
> +	cr->c.height = cr->c.height & ~PIX_ALIGN_MASK;
> +	if (0 == cr->c.width)
> +		cr->c.width = DMA_MIN_SIZE;
> +	if (0 == cr->c.height)
> +		cr->c.height = DMA_MIN_SIZE;
> +	cr->c.left = ((cr->c.left + PIX_ALIGN_MASK - 1) & ~PIX_ALIGN_MASK);
> +	cr->c.top = ((cr->c.top + PIX_ALIGN_MASK - 1) & ~PIX_ALIGN_MASK);
> +
> +	if ((cr->c.left + cr->c.width > f->o_width)
> +		|| (cr->c.top + cr->c.height > f->o_height)) {
> +		v4l2_err(&dev->v4l2_dev, "Error in S_CROP params\n");
> +		dbg("%d %d %d %d. %d %d",
> +			cr->c.left, cr->c.top, cr->c.width, cr->c.height,
> +			f->o_width, f->o_height);
> +		return -EINVAL;
> +	}
> +
> +	/* check for the pixel scaling ratio when cropping input image */
> +	if (V4L2_BUF_TYPE_VIDEO_OUTPUT == cr->type) {
> +		if (f->width > (SCALER_MAX_HRATIO*ctx->d_frame.width) ||
> +			f->height > (SCALER_MAX_VRATIO*ctx->d_frame.height)) {
> +			v4l2_err(&dev->v4l2_dev, "Out of the scaler range");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	f->offs_h = cr->c.left;
> +	f->offs_v = cr->c.top;
> +	f->width = cr->c.width;
> +	f->height = cr->c.height;
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops s5p_fimc_ioctl_ops = {
> +	.vidioc_querycap		= s5p_fimc_querycap,
> +
> +	.vidioc_enum_fmt_vid_cap	= s5p_fimc_enum_fmt,
> +	.vidioc_enum_fmt_vid_out	= s5p_fimc_enum_fmt,
> +
> +	.vidioc_g_fmt_vid_cap		= s5p_fimc_g_fmt,
> +	.vidioc_g_fmt_vid_out		= s5p_fimc_g_fmt,
> +
> +	.vidioc_try_fmt_vid_cap		= s5p_fimc_try_fmt,
> +	.vidioc_try_fmt_vid_out		= s5p_fimc_try_fmt,
> +
> +	.vidioc_s_fmt_vid_cap		= s5p_fimc_s_fmt,
> +	.vidioc_s_fmt_vid_out		= s5p_fimc_s_fmt,
> +
> +	.vidioc_reqbufs			= s5p_fimc_reqbufs,
> +	.vidioc_querybuf		= s5p_fimc_querybuf,
> +
> +	.vidioc_qbuf			= s5p_fimc_qbuf,
> +	.vidioc_dqbuf			= s5p_fimc_dqbuf,
> +
> +	.vidioc_streamon		= s5p_fimc_streamon,
> +	.vidioc_streamoff		= s5p_fimc_streamoff,
> +
> +	.vidioc_queryctrl		= s5p_fimc_queryctrl,
> +	.vidioc_g_ctrl			= s5p_fimc_g_ctrl,
> +	.vidioc_s_ctrl			= s5p_fimc_s_ctrl,
> +
> +	.vidioc_g_crop			= s5p_fimc_g_crop,
> +	.vidioc_s_crop			= s5p_fimc_s_crop,
> +	.vidioc_cropcap			= s5p_fimc_cropcap
> +
> +};
> +
> +static void queue_init(void *priv, struct videobuf_queue *vq,
> +		       enum v4l2_buf_type type)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +
> +	videobuf_queue_dma_contig_init(vq, &s5p_fimc_qops, dev->v4l2_dev.dev,
> +			&dev->irqlock, type, V4L2_FIELD_NONE,
> +			sizeof(struct fimc_vid_buffer), priv);
> +}
> +
> +static int s5p_fimc_open(struct file *file)
> +{
> +	struct fimc_dev *dev = video_drvdata(file);
> +	struct fimc_ctx *ctx = NULL;
> +	int err;
> +
> +	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	file->private_data = ctx;
> +	ctx->fimc_dev = dev;
> +	/* default format */
> +	ctx->s_frame.fmt = &formats[0];
> +	ctx->d_frame.fmt = &formats[0];
> +	/* per user process device context initialization */
> +	ctx->updated = 0;
> +	ctx->effect.type = S5P_FIMC_EFFECT_ORIGINAL;
> +	ctx->flags = 0;
> +	ctx->in_path = S5P_FIMC_DMA;
> +	ctx->out_path = S5P_FIMC_DMA;
> +	mutex_init(&ctx->lock);
> +
> +	ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, dev->m2m_dev, queue_init);
> +
> +	if (IS_ERR(ctx->m2m_ctx)) {
> +		err = PTR_ERR(ctx->m2m_ctx);
> +		kfree(ctx);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_release(struct file *file)
> +{
> +	struct fimc_ctx *ctx =
> +		(struct fimc_ctx *)file->private_data;
> +
> +	v4l2_m2m_ctx_release(ctx->m2m_ctx);
> +	kfree(ctx);
> +	return 0;
> +}
> +
> +static unsigned int s5p_fimc_poll(struct file *file,
> +				     struct poll_table_struct *wait)
> +{
> +	struct fimc_ctx *ctx = file->private_data;
> +	return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
> +}
> +
> +
> +static int s5p_fimc_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct fimc_ctx *ctx = file->private_data;
> +	return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
> +}
> +
> +static const struct v4l2_file_operations s5p_fimc_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= s5p_fimc_open,
> +	.release	= s5p_fimc_release,
> +	.poll		= s5p_fimc_poll,
> +	.ioctl		= video_ioctl2,
> +	.mmap		= s5p_fimc_mmap,
> +};
> +
> +static struct v4l2_m2m_ops m2m_ops = {
> +	.device_run	= s5p_fimc_dma_run,
> +	.job_abort	= s5p_fimc_job_abort,
> +};
> +
> +
> +static int s5p_fimc_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct video_device *vfd;
> +	struct fimc_dev *dev;
> +	int ret = 0;
> +	struct clk *srclk;
> +
> +	dev_dbg(&pdev->dev, "probe\n");
> +
> +	if (NULL == pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data is not set\n");
> +		return -ENOENT;
> +	}
> +
> +	dev = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->id = pdev->id;
> +	dev->pdev = pdev;
> +	dev->pldata = pdev->dev.platform_data;
> +
> +	spin_lock_init(&dev->irqlock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "failed to find the registers\n");
> +		ret = -ENOENT;
> +		goto err_info;
> +	}
> +
> +	dev->regs_res = request_mem_region(res->start, resource_size(res),
> +			dev_name(&pdev->dev));
> +	if (!dev->regs_res) {
> +		dev_err(&pdev->dev, "failed to obtain register region\n");
> +		ret = -ENOENT;
> +		goto err_info;
> +	}
> +
> +	dev->regs = ioremap(res->start, resource_size(res));
> +	if (!dev->regs) {
> +		dev_err(&pdev->dev, "failed to map registers\n");
> +		ret = -ENXIO;
> +		goto err_req_region;
> +	}
> +
> +	/* FIMC parent clock */
> +	srclk = clk_get(&pdev->dev, dev->pldata->srclk_name);
> +	if (IS_ERR(srclk)) {
> +		dev_err(&pdev->dev, "failed to get source clock of fimc\n");
> +		goto err_regs_unmap;
> +	}

you don't seem to clk_put this if the call exits normally?

> +	/* FIMC clock */
> +	dev->clock = clk_get(&pdev->dev, "fimc");
> +	if (IS_ERR(dev->clock)) {
> +		dev_err(&pdev->dev, "failed to get fimc clock source\n");
> +		goto err_clk2;
> +	}
> +
> +	ret = clk_set_parent(dev->clock, srclk);
> +	if (ret)
> +		goto err_clk;
> +
> +	/* set FIMC local clock rate */
> +	clk_set_rate(dev->clock, dev->pldata->clockrate);
> +	clk_enable(dev->clock);
> +
> +	/* IRQ */
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get IRQ resource\n");
> +		ret = -ENXIO;
> +		goto err_clk;
> +	}
> +	dev->irq = res->start;
> +
> +	s5p_fimc_reset(dev);
> +
> +	ret = request_irq(dev->irq, s5p_fimc_isr, 0, pdev->name, dev);
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
> +		goto err_clk;
> +	}
> +
> +	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> +	if (ret)
> +		goto err_irq;
> +
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
> +		goto err_unreg_dev;
> +	}
> +
> +	if ((u32)dev->id >= MAX_FIMC_DEVICES)
> +		goto err_unreg_dev;
> +
> +	vfd->fops	= &s5p_fimc_fops;
> +	vfd->ioctl_ops	= &s5p_fimc_ioctl_ops;
> +	vfd->minor	= -1;
> +	vfd->release	= video_device_release;
> +
> +	snprintf(vfd->name, sizeof(vfd->name), "%s%d", MODULE_NAME, dev->id);
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> +	if (ret) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
> +		goto err_rel_vdev;
> +	}
> +	video_set_drvdata(vfd, dev);
> +	dev->vfd = vfd;
> +	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
> +		vfd->num);
> +
> +	platform_set_drvdata(pdev, dev);
> +	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
> +	if (IS_ERR(dev->m2m_dev)) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
> +		ret = PTR_ERR(dev->m2m_dev);
> +		goto err_m2m;
> +	}
> +
> +	s5p_fimc_en_lastirq(dev, 1);
> +
> +	dev_dbg(&pdev->dev, "installation successful\n");
> +	return 0;
> +
> +err_m2m:
> +	video_unregister_device(dev->vfd);
> +err_rel_vdev:
> +	video_device_release(vfd);
> +err_unreg_dev:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +err_irq:
> +	free_irq(dev->irq, dev);
> +err_clk:
> +	clk_disable(dev->clock);
> +	clk_put(dev->clock);
> +err_clk2:
> +	clk_put(srclk);
> +err_regs_unmap:
> +	iounmap(dev->regs);
> +err_req_region:
> +	release_resource(dev->regs_res);
> +	kfree(dev->regs_res);
> +err_info:
> +	dev_err(&pdev->dev, "failed to install\n");
> +	return ret;
> +}
> +
> +static int __devexit s5p_fimc_remove(struct platform_device *pdev)
> +{
> +	struct fimc_dev *dev =
> +		(struct fimc_dev *)platform_get_drvdata(pdev);

no need to cast.

> +	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
> +
> +	free_irq(dev->irq, dev);
> +
> +	s5p_fimc_reset(dev);
> +	v4l2_m2m_release(dev->m2m_dev);
> +	video_unregister_device(dev->vfd);
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +
> +	iounmap(dev->regs);
> +	release_resource(dev->regs_res);
> +	kfree(dev->regs_res);
> +	kfree(dev);
> +	return 0;
> +}
> +
> +static int s5p_fimc_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_fimc_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops s5p_fimc_pm_ops = {
> +	.suspend = s5p_fimc_suspend,
> +	.resume = s5p_fimc_resume,
> +};


given these are null ops, is this really a good idea including
the pm ops?

> +static struct platform_driver s5p_fimc_driver = {
> +	.probe = s5p_fimc_probe,
> +	.remove = __devexit_p(s5p_fimc_remove),
> +	.driver = {
> +		.name = "s5p-fimc",
> +		.owner = THIS_MODULE,
> +		.pm = &s5p_fimc_pm_ops
> +	}
> +};
> +
> +static char banner[] __initdata = KERN_INFO \
> +	"S5PC Camera Interface V4L2 Driver, (c) 2010 Samsung Electronics\n";
> +
> +static int __init s5p_fimc_init(void)
> +{
> +	u32 ret;
> +	printk(banner);
> +
> +	ret = platform_driver_register(&s5p_fimc_driver);
> +	if (ret != 0) {
> +		printk(KERN_ERR "FIMC platform driver register failed\n");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void __exit s5p_fimc_exit(void)
> +{
> +	platform_driver_unregister(&s5p_fimc_driver);
> +}
> +
> +module_init(s5p_fimc_init);
> +module_exit(s5p_fimc_exit);
> +
> +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
> +MODULE_DESCRIPTION("S5PV210 FIMC (video postprocessor) driver");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/drivers/media/video/samsung-fimc/s5p_fimc.h b/drivers/media/video/samsung-fimc/s5p_fimc.h
> new file mode 100644
> index 0000000..f4485a8
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/s5p_fimc.h
> @@ -0,0 +1,331 @@
> +/*
> + * drivers/media/video/samsung-fimc/s5p_fimc.h
> + *
> + * Copyright (c) 2010 Samsung Electronics
> + *
> + * Sylwester Nawrocki, s.nawrocki@samsung.com
> + *
> + * 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 S5P_FIMC_H_
> +#define S5P_FIMC_H_
> +
> +#include <linux/types.h>
> +#include <plat/fimc.h>
> +#include <media/videobuf-core.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <linux/videodev2.h>
> +
> +
> +#define err(fmt, args...) \
> +	printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
> +
> +#ifdef DEBUG
> +#define dbg(fmt, args...) \
> +	printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
> +#else
> +#define dbg(fmt, args...)
> +#endif
> +
> +#define MODULE_NAME		"s5p-fimc"
> +#define MAX_FIMC_DEVICES	3
> +#define S5P_FIMC_MAX_FRAMES	4
> +#define DMA_MIN_SIZE		16
> +#define SCALER_MAX_HRATIO	64
> +#define SCALER_MAX_VRATIO	64
> +#define PIX_ALIGN_MASK		(DMA_MIN_SIZE - 1)
> +
> +#define	S5P_FIMC_CHANGED_NONE	0
> +#define	S5P_FIMC_PARAMS		(1 << 0)
> +#define	S5P_FIMC_SRC_ADDR	(1 << 1)
> +#define	S5P_FIMC_DST_ADDR	(1 << 2)
> +
> +
> +enum fimc_datapath {
> +	S5P_FIMC_ITU_CAM_A,
> +	S5P_FIMC_ITU_CAM_B,
> +	S5P_FIMC_MIPI_CAM,
> +	S5P_FIMC_DMA,
> +	S5P_FIMC_LCDFIFO,
> +	S5P_FIMC_WRITEBACK
> +};
> +
> +enum fimc_color_fmt {
> +	S5P_FIMC_RGB565,
> +	S5P_FIMC_RGB666,
> +	S5P_FIMC_RGB888,
> +	S5P_FIMC_YCBCR420,
> +	S5P_FIMC_YCBCR422,
> +	S5P_FIMC_YCBYCR422,
> +	S5P_FIMC_YCRYCB422,
> +	S5P_FIMC_CBYCRY422,
> +	S5P_FIMC_CRYCBY422,
> +	S5P_FIMC_RGB30_LOCAL,
> +	S5P_FIMC_YCBCR444_LOCAL,
> +	S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL,
> +	S5P_FIMC_COLOR_MASK = 0x0F,
> +};
> +
> +/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats */
> +#define	S5P_FIMC_OUT_CRYCBY	S5P_CIOCTRL_ORDER422_CRYCBY
> +#define	S5P_FIMC_OUT_CBYCRY	S5P_CIOCTRL_ORDER422_YCRYCB
> +#define	S5P_FIMC_OUT_YCRYCB	S5P_CIOCTRL_ORDER422_CBYCRY
> +#define	S5P_FIMC_OUT_YCBYCR	S5P_CIOCTRL_ORDER422_YCBYCR
> +
> +/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats */
> +#define	S5P_FIMC_IN_CRYCBY	S5P_MSCTRL_ORDER422_CRYCBY
> +#define	S5P_FIMC_IN_CBYCRY	S5P_MSCTRL_ORDER422_YCRYCB
> +#define	S5P_FIMC_IN_YCRYCB	S5P_MSCTRL_ORDER422_CBYCRY
> +#define	S5P_FIMC_IN_YCBYCR	S5P_MSCTRL_ORDER422_YCBYCR
> +
> +/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats */
> +#define	S5P_FIMC_LSB_CRCB	S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
> +
> +/* The embedded image effect selection */
> +#define	S5P_FIMC_EFFECT_ORIGINAL	S5P_CIIMGEFF_FIN_BYPASS
> +#define	S5P_FIMC_EFFECT_ARBITRARY	S5P_CIIMGEFF_FIN_ARBITRARY
> +#define	S5P_FIMC_EFFECT_NEGATIVE	S5P_CIIMGEFF_FIN_NEGATIVE
> +#define	S5P_FIMC_EFFECT_ARTFREEZE	S5P_CIIMGEFF_FIN_ARTFREEZE
> +#define	S5P_FIMC_EFFECT_EMBOSSING	S5P_CIIMGEFF_FIN_EMBOSSING
> +#define	S5P_FIMC_EFFECT_SIKHOUETTE	S5P_CIIMGEFF_FIN_SILHOUETTE
> +
> +
> +/* Definitions for flags field in struct fimc_ctx */
> +#define	IN_DMA_ACCESS_LINEAR		0	/* default */
> +#define	IN_DMA_ACCESS_TILED		(1 << 0)
> +#define	OUT_DMA_ACCESS_LINEAR		(0 << 1)
> +#define	OUT_DMA_ACCESS_TILED		(1 << 1)
> +#define	SCAN_MODE_PROGRESSIVE		(0 << 2)
> +#define	SCAN_MODE_INTERLACED		(1 << 2)
> +/* YCbCr data dynamic range selection for the RGB <-> YCBCR color space
> + * conversion. Y/Cb/Cr (0 ~ 255) : Wide default */
> +#define	S5P_FIMC_COLOR_RANGE_WIDE	(0 << 3)
> +/* Y (16 ~ 235), Cb/Cr (16 ~ 240) : Narrow */
> +#define	S5P_FIMC_COLOR_RANGE_NARROW	(1 << 3)
> +
> +#define	FLIP_ORIGINAL	0
> +#define	FLIP_X_AXIS	0x01
> +#define	FLIP_Y_AXIS	0x02
> +#define	FLIP_XY_AXIS	(FLIP_X_AXIS | FLIP_Y_AXIS)
> +
> +/**
> + * struct fimc_dma_offset
> + * @y_h:	y value horizontal offset
> + * @y_v:	y value vertical offset
> + * @cb_h:	cb value horizontal offset
> + * @cb_v:	cb value vertical offset
> + * @cr_h:	cr value horizontal offset
> + * @cr_v:	cr value vertical offset
> + */
> +struct fimc_dma_offset {
> +	int	y_h;
> +	int	y_v;
> +	int	cb_h;
> +	int	cb_v;
> +	int	cr_h;
> +	int	cr_v;
> +};
> +
> +/**
> + * struct s5p_fimc_effect
> + * @type:	effect type
> + * @pat_cb:	cr value when type is "arbitrary"
> + * @pat_cr:	cr value when type is "arbitrary"
> + */
> +struct fimc_effect {
> +	u32	type;
> +	u8	pat_cb;
> +	u8	pat_cr;
> +};
> +
> +/**
> + * struct fimc_scaler
> + * @enabled:		flag indicating whether the scaler is used or not
> + * @hfactor:		horizontal shift factor
> + * @vfactor:		vertical shift factor
> + * @pre_hratio:		prescaler horizontal ratio
> + * @pre_vratio:		prescaler vertical ratio
> + * @pre_dst_width:	prescaler destination width
> + * @pre_dst_height:	pre-scaler destination height
> + * @scaleup_h:		flag indicating scaling up horizontally
> + * @scaleup_v:		flag indicating scaling up vertically
> + * @main_hratio:	main scaler horizontal ratio
> + * @main_vratio:	main scaler vertical ratio
> + * @real_width:		src_width - offset
> + * @real_height:	src_height - offset
> + * @copy_mode:		flag indicating one to one mode, i.e. no scaling
> + *			and color format conversion
> + */
> +struct fimc_scaler {
> +	u32	enabled;
> +	u32	hfactor;
> +	u32	vfactor;
> +	u32	pre_hratio;
> +	u32	pre_vratio;
> +	u32	pre_dst_width;
> +	u32	pre_dst_height;
> +	u32	scaleup_h;
> +	u32	scaleup_v;
> +	u32	main_hratio;
> +	u32	main_vratio;
> +	u32	real_width;
> +	u32	real_height;
> +	u32	copy_mode;
> +};
> +
> +/**
> + * struct fimc_frame - input/output frame format properties
> + * @f_width:	image full width (virtual screen size)
> + * @f_height:	image full height (virtual screen size)
> + * @o_width:	original image width as set by S_FMT
> + * @o_height:	original image height as set by S_FMT
> + * @offs_h:	image horizontal pixel offset
> + * @offs_v:	image vertical pixel offset
> + * @width:	image pixel width
> + * @height:	image pixel weight
> + * @addr_y:	RGB/Y physical address
> + * @addr_cb:	CB physical address
> + * @addr_cr:	CR physical addres
> + * @buf_cnt:	number of buffers depending on a color format
> + * @size:	image size in bytes
> + * @color:	color format
> + * @dma_offset:	DMA offset in bytes
> + */
> +struct fimc_frame {
> +	u32	f_width;
> +	u32	f_height;
> +	u32	o_width;
> +	u32	o_height;
> +	u32	offs_h;
> +	u32	offs_v;
> +	u32	width;
> +	u32	height;
> +	u32	addr_y;
> +	u32	addr_cb;
> +	u32	addr_cr;
> +	u32	size;
> +	struct fimc_dma_offset	dma_offset;
> +	struct s5p_fimc_fmt	*fmt;
> +};
> +
> +
> +struct fimc_vid_buffer {
> +	struct videobuf_buffer	vb;
> +	int	index;
> +};
> +
> +struct s5p_fimc_fmt {
> +	char	*name;
> +	u32	fourcc;
> +	u32	color;
> +	u32	depth;
> +	/* Number of data planes used */
> +	u16	buff_cnt;
> +	u16	planes_cnt;
> +};
> +
> +struct fimc_ctx;
> +
> +/**
> + * struct fimc_subdev: abstraction for a FIMC entity
> + * @pdev:	a pointer to the FIMC platform device
> + * @pldata:	a pointer to the device platform data
> + * @id:		the FIMC device index (0..2)
> + * @clock:
> + * @regs:
> + * @regs_res:
> + * @mem_res:
> + * @irq:	interrupt number of the FIMC subdevice
> + * @irqlock:	spinlock protecting videbuffer queue
> + * @timer:
> + * @vfd:
> + * @v4l2_dev:
> + * @m2m_dev:
> + */
> +struct fimc_dev {
> +	struct platform_device		*pdev;
> +	struct s5p_platform_fimc	*pldata;
> +	int				id;
> +	struct clk			*clock;
> +	void __iomem			*regs;
> +	struct resource		*regs_res;
> +	struct resource		*mem_res;
> +	int				irq;
> +	spinlock_t			irqlock;
> +	struct video_device		*vfd;
> +	struct v4l2_device		v4l2_dev;
> +	struct v4l2_m2m_dev		*m2m_dev;
> +};
> +
> +
> +/**
> + * fimc_ctx - the device context data structure
> + * @lock:		mutex protecting the context data
> + * @s_frame:		source frame properties
> + * @d_frame:		destination frame properties
> + * @osd:		osd (output) window parameters
> + * @out_order_1p:	output 1-plane YCBCR order
> + * @out_order_2p:	output 2-plane YCBCR order
> + * @in_order_1p		input 1-plane YCBCR order
> + * @in_order_2p:	input 2-plane YCBCR order
> + * @in_path:		input mode
> + * @out_path:		output mode
> + * @scaler:		image scaler properties
> + * @effect:		image effect
> + * @rotation:		image clockwise rotation in degrees
> + * @flip:		image flip mode
> + * @flags:		device state flags
> + * @updated:		flags variable to keep track of user configurable
> + *			parameters (ioctl)
> + * @fimc_dev:		fimc device related to the context
> + * @m2m_ctx:		memory-to-memory device context
> + */
> +struct fimc_ctx {
> +	spinlock_t		slock;
> +	struct mutex		lock;
> +	struct fimc_frame	s_frame;
> +	struct fimc_frame	d_frame;
> +	u32			out_order_1p;
> +	u32			out_order_2p;
> +	u32			in_order_1p;
> +	u32			in_order_2p;
> +	enum fimc_datapath	in_path;
> +	enum fimc_datapath	out_path;
> +	struct fimc_scaler	scaler;
> +	struct fimc_effect	effect;
> +	int			rotation;
> +	u32			flip;
> +	u32			flags;
> +	u32			updated;
> +	struct fimc_dev		*fimc_dev;
> +	struct v4l2_m2m_ctx	*m2m_ctx;
> +};
> +
> +int s5p_fimc_set_scaler_info(struct fimc_ctx *ctx);
> +int s5p_fimc_check_fifo(struct fimc_dev *dev);
> +void s5p_fimc_reset(struct fimc_dev *dev);
> +void s5p_fimc_set_target_format(struct fimc_ctx *ctx);
> +void s5p_fimc_set_out_dma(struct fimc_ctx *ctx);
> +void s5p_fimc_en_lastirq(struct fimc_dev *dev, int enable);
> +void s5p_fimc_en_irq(struct fimc_dev *dev, int enable);
> +void s5p_fimc_en_autoload(struct fimc_ctx *ctx, int enable);
> +void s5p_fimc_set_prescaler(struct fimc_ctx *ctx);
> +void s5p_fimc_set_scaler(struct fimc_ctx *ctx);
> +void s5p_fimc_start_scaler(struct fimc_ctx *ctx);
> +void s5p_fimc_stop_scaler(struct fimc_ctx *ctx);
> +void s5p_fimc_en_capture(struct fimc_ctx *ctx);
> +void s5p_fimc_dis_capture(struct fimc_ctx *ctx);
> +void s5p_fimc_set_effect(struct fimc_ctx *ctx);
> +void s5p_fimc_set_in_dma(struct fimc_ctx *ctx);
> +void s5p_fimc_start_in_dma(struct fimc_dev *dev);
> +void s5p_fimc_stop_in_dma(struct fimc_dev *dev);
> +void s5p_fimc_set_input_path(struct fimc_ctx *ctx);
> +void s5p_fimc_set_output_path(struct fimc_ctx *ctx);
> +void s5p_fimc_set_input_addr(struct fimc_ctx *ctx);
> +void s5p_fimc_set_output_addr(struct fimc_ctx *ctx);
> +
> +#endif /* S5P_FIMC_H_ */
> diff --git a/drivers/media/video/samsung-fimc/s5p_fimc_reg.c b/drivers/media/video/samsung-fimc/s5p_fimc_reg.c
> new file mode 100644
> index 0000000..a6f719e
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/s5p_fimc_reg.c
> @@ -0,0 +1,559 @@
> +/*
> + * Register interface file for Samsung Camera Interface (FIMC) driver
> + *
> + * Copyright (c) 2010 Samsung Electronics
> + *
> + * Sylwester Nawrocki, s.nawrocki@samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <mach/map.h>
> +#include <plat/regs-fimc.h>
> +#include <plat/fimc.h>
> +#include "s5p_fimc.h"
> +
> +
> +int s5p_fimc_check_fifo(struct fimc_dev *ctrl)
> +{
> +	u32 cfg, status;
> +
> +	status = readl(ctrl->regs + S5P_CISTATUS);
> +
> +	if ((S5P_CISTATUS_OVFIY | S5P_CISTATUS_OVFICB | S5P_CISTATUS_OVFICR)
> +		& status) {
> +		cfg = readl(ctrl->regs + S5P_CIWDOFST);
> +		cfg |= (S5P_CIWDOFST_CLROVFIY | S5P_CIWDOFST_CLROVFICB
> +			| S5P_CIWDOFST_CLROVFICR);
> +		writel(cfg, ctrl->regs + S5P_CIWDOFST);
> +
> +		cfg = readl(ctrl->regs + S5P_CIWDOFST);
> +		cfg &= ~(S5P_CIWDOFST_CLROVFIY | S5P_CIWDOFST_CLROVFICB
> +			| S5P_CIWDOFST_CLROVFICR);
> +		writel(cfg, ctrl->regs + S5P_CIWDOFST);
> +	}
> +
> +	return 0;
> +}
> +
> +void s5p_fimc_reset(struct fimc_dev *dev)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + S5P_CISRCFMT);
> +	cfg |= S5P_CISRCFMT_ITU601_8BIT;
> +	writel(cfg, dev->regs + S5P_CISRCFMT);
> +
> +	/* sw reset */
> +	cfg = readl(dev->regs + S5P_CIGCTRL);
> +	cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
> +	writel(cfg, dev->regs + S5P_CIGCTRL);
> +	mdelay(1);
> +
> +	cfg = readl(dev->regs + S5P_CIGCTRL);
> +	cfg &= ~S5P_CIGCTRL_SWRST;
> +	writel(cfg, dev->regs + S5P_CIGCTRL);
> +
> +}
> +
> +static void s5p_fimc_set_rot90(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CITRGFMT);
> +
> +	cfg &= ~(S5P_CITRGFMT_INROT90_CLOCKWISE |
> +		S5P_CITRGFMT_OUTROT90_CLOCKWISE);
> +
> +	/* The input and output rotator cannot work simultaneously,
> +	in input DMA mode only the output rotator shall be used. */
> +	if (ctx->out_path == S5P_FIMC_LCDFIFO)
> +		cfg |= S5P_CITRGFMT_INROT90_CLOCKWISE;
> +	else
> +		cfg |= S5P_CITRGFMT_OUTROT90_CLOCKWISE;
> +
> +	writel(cfg, dev->regs + S5P_CITRGFMT);
> +}
> +
> +static u32 s5p_fimc_get_in_flip(struct fimc_ctx *ctx)
> +{
> +	u32 flip = S5P_MSCTRL_FLIP_NORMAL;
> +
> +	switch(ctx->flip) {
> +	case FLIP_X_AXIS:
> +		flip = S5P_MSCTRL_FLIP_X_MIRROR;
> +		break;
> +	case FLIP_Y_AXIS:
> +		flip = S5P_MSCTRL_FLIP_Y_MIRROR;
> +		break;
> +	case FLIP_XY_AXIS:
> +		flip = S5P_MSCTRL_FLIP_180;
> +		break;
> +	}
> +
> +	return flip;
> +}
> +
> +static u32 s5p_fimc_get_target_flip(struct fimc_ctx *ctx)
> +{
> +	u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
> +
> +	switch(ctx->flip) {
> +	case FLIP_X_AXIS:
> +		flip = S5P_CITRGFMT_FLIP_X_MIRROR;
> +		break;
> +	case FLIP_Y_AXIS:
> +		flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
> +		break;
> +	case FLIP_XY_AXIS:
> +		flip = S5P_CITRGFMT_FLIP_180;
> +		break;
> +	}
> +	return flip;
> +}
> +
> +void s5p_fimc_set_target_format(struct fimc_ctx *ctx)
> +{
> +	u32 cfg = 0;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->d_frame;
> +
> +	dbg("w= %d, h= %d color: %d", frame->width,
> +		frame->height, frame->fmt->color);
> +
> +	switch (frame->fmt->color) {
> +	case S5P_FIMC_RGB565:
> +	case S5P_FIMC_RGB666:
> +	case S5P_FIMC_RGB888:
> +		cfg |= S5P_CITRGFMT_OUTFORMAT_RGB;
> +		break;
> +	case S5P_FIMC_YCBCR420:
> +		cfg |= S5P_CITRGFMT_OUTFORMAT_YCBCR420;
> +		break;
> +	case S5P_FIMC_YCBYCR422:
> +	case S5P_FIMC_YCRYCB422:
> +	case S5P_FIMC_CBYCRY422:
> +	case S5P_FIMC_CRYCBY422:
> +		if (frame->fmt->planes_cnt == 1)
> +			cfg |= S5P_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE;
> +		else
> +			cfg |= S5P_CITRGFMT_OUTFORMAT_YCBCR422;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	cfg |= S5P_CITRGFMT_TARGETHSIZE(frame->width);
> +	cfg |= S5P_CITRGFMT_TARGETVSIZE(frame->height);
> +	cfg |= s5p_fimc_get_target_flip(ctx);
> +	writel(cfg, dev->regs + S5P_CITRGFMT);
> +
> +	if (90 == ctx->rotation || 270 == ctx->rotation)
> +		s5p_fimc_set_rot90(ctx);
> +
> +	cfg = S5P_CITAREA_TARGET_AREA(frame->width * frame->height);
> +	writel(cfg, dev->regs + S5P_CITAREA);
> +}
> +
> +static void s5p_fimc_set_out_dma_size(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->d_frame;
> +	u32 cfg = 0;
> +
> +	dbg("h= 0x%X  w= 0x%X", frame->f_width, frame->f_height);
> +
> +	if (90 == ctx->rotation || 270 == ctx->rotation) {
> +		cfg |= S5P_ORGOSIZE_HORIZONTAL(frame->f_height);
> +		cfg |= S5P_ORGOSIZE_VERTICAL(frame->f_width);
> +	} else {
> +		cfg |= S5P_ORGOSIZE_HORIZONTAL(frame->f_width);
> +		cfg |= S5P_ORGOSIZE_VERTICAL(frame->f_height);
> +	}
> +	dbg("ORGOSIZE: 0x%X", cfg);
> +	writel(cfg, dev->regs + S5P_ORGOSIZE);
> +}
> +
> +void s5p_fimc_set_out_dma(struct fimc_ctx *ctx)
> +{
> +	u32 cfg;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->d_frame;
> +	struct fimc_dma_offset *offset = &frame->dma_offset;
> +
> +	/* input dma offsets */
> +	cfg = 0;
> +	cfg |= S5P_CIOYOFF_HORIZONTAL(offset->y_h);
> +	cfg |= S5P_CIOYOFF_VERTICAL(offset->y_v);
> +	writel(cfg, dev->regs + S5P_CIOYOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIOCBOFF_HORIZONTAL(offset->cb_h);
> +	cfg |= S5P_CIOCBOFF_VERTICAL(offset->cb_v);
> +	writel(cfg, dev->regs + S5P_CIOCBOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIOCROFF_HORIZONTAL(offset->cr_h);
> +	cfg |= S5P_CIOCROFF_VERTICAL(offset->cr_v);
> +	writel(cfg, dev->regs + S5P_CIOCROFF);
> +
> +	s5p_fimc_set_out_dma_size(ctx);
> +
> +	/* configure chroma components order */
> +	cfg = readl(dev->regs + S5P_CIOCTRL);
> +
> +	cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
> +		 S5P_CIOCTRL_YCBCR_PLANE_MASK);
> +
> +	if (frame->fmt->planes_cnt == 1)
> +		cfg |= ctx->out_order_1p;
> +	else if (frame->fmt->planes_cnt == 2)
> +		cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
> +	else if (frame->fmt->planes_cnt == 3)
> +		cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
> +
> +	writel(cfg, dev->regs + S5P_CIOCTRL);
> +}
> +
> +void s5p_fimc_en_autoload(struct fimc_ctx *ctx, int enable)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_ORGISIZE);
> +	if (enable)
> +		cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_ENABLE;
> +	else
> +		cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_ENABLE;
> +	writel(cfg, dev->regs + S5P_ORGISIZE);
> +}
> +
> +void s5p_fimc_en_lastirq(struct fimc_dev *dev, int enable)
> +{
> +	unsigned long flags;
> +	u32 cfg;
> +	local_irq_save(flags);
> +
> +	cfg = readl(dev->regs + S5P_CIOCTRL);
> +	if (enable)
> +		cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
> +	else
> +		cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
> +	writel(cfg, dev->regs + S5P_CIOCTRL);
> +
> +	local_irq_restore(flags);
> +}
> +
> +void s5p_fimc_set_prescaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev =  ctx->fimc_dev;
> +	struct fimc_scaler *sc = &ctx->scaler;
> +	u32 cfg = 0, shfactor;
> +
> +	shfactor = 10 - (sc->hfactor + sc->vfactor);
> +
> +	cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
> +	cfg |= S5P_CISCPRERATIO_PREHORRATIO(sc->pre_hratio);
> +	cfg |= S5P_CISCPRERATIO_PREVERRATIO(sc->pre_vratio);
> +
> +	writel(cfg, dev->regs + S5P_CISCPRERATIO);
> +
> +	cfg = 0;
> +	cfg |= S5P_CISCPREDST_PREDSTWIDTH(sc->pre_dst_width);
> +	cfg |= S5P_CISCPREDST_PREDSTHEIGHT(sc->pre_dst_height);
> +
> +	writel(cfg, dev->regs + S5P_CISCPREDST);
> +}
> +
> +void s5p_fimc_set_scaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_scaler *sc = &ctx->scaler;
> +	struct fimc_frame *src_frame = &ctx->s_frame;
> +	struct fimc_frame *dst_frame = &ctx->d_frame;
> +	u32 cfg;
> +
> +	if (ctx->flags & S5P_FIMC_COLOR_RANGE_NARROW)
> +		cfg = (S5P_CISCCTRL_CSCR2Y_NARROW|S5P_CISCCTRL_CSCY2R_NARROW);
> +	else
> +		cfg = (S5P_CISCCTRL_CSCR2Y_WIDE|S5P_CISCCTRL_CSCY2R_WIDE);
> +
> +	if (!sc->enabled)
> +		cfg |= S5P_CISCCTRL_SCALERBYPASS;
> +
> +	if (sc->scaleup_h)
> +		cfg |= S5P_CISCCTRL_SCALEUP_H;
> +
> +	if (sc->scaleup_v)
> +		cfg |= S5P_CISCCTRL_SCALEUP_V;
> +
> +	if (sc->copy_mode)
> +		cfg |= S5P_CISCCTRL_ONE2ONE;
> +
> +
> +	if (ctx->in_path == S5P_FIMC_DMA) {
> +		if (src_frame->fmt->color == S5P_FIMC_RGB565)
> +			cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
> +		else if (src_frame->fmt->color == S5P_FIMC_RGB666)
> +			cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
> +		else if (src_frame->fmt->color == S5P_FIMC_RGB888)
> +			cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
> +	}
> +
> +	if (ctx->out_path == S5P_FIMC_DMA) {
> +		if (dst_frame->fmt->color == S5P_FIMC_RGB565)
> +			cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
> +		else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
> +			cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
> +		else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
> +			cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
> +	} else {
> +		cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
> +
> +		if (ctx->flags&SCAN_MODE_INTERLACED)
> +			cfg |= S5P_CISCCTRL_INTERLACE;
> +		else
> +			cfg |= S5P_CISCCTRL_PROGRESSIVE;
> +	}
> +
> +	dbg("main_hratio= 0x%X  main_vratio= 0x%X",
> +		sc->main_hratio, sc->main_vratio);
> +
> +	cfg |= S5P_CISCCTRL_MAINHORRATIO(sc->main_hratio);
> +	cfg |= S5P_CISCCTRL_MAINVERRATIO(sc->main_vratio);
> +
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_start_scaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
> +
> +	cfg |= S5P_CISCCTRL_SCALERSTART;
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_stop_scaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
> +
> +	cfg &= ~S5P_CISCCTRL_SCALERSTART;
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_en_capture(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
> +
> +	if (S5P_FIMC_DMA == ctx->out_path) {
> +		/* one shot mode */
> +		cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN
> +			| S5P_CIIMGCPT_CPT_FRMOD_EN;
> +	} else {
> +		/* freerun */
> +		cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE |
> +			 S5P_CIIMGCPT_CPT_FRMOD_EN);
> +		cfg |= S5P_CIIMGCPT_IMGCPTEN;
> +	}
> +
> +	if (ctx->scaler.enabled)
> +		cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
> +
> +	writel(cfg, dev->regs + S5P_CIIMGCPT);
> +}
> +
> +void s5p_fimc_dis_capture(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
> +	cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
> +	writel(cfg, dev->regs + S5P_CIIMGCPT);
> +}
> +
> +void s5p_fimc_set_effect(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_effect *effect = &ctx->effect;
> +	u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
> +
> +	cfg |= effect->type;
> +
> +	if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
> +		cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
> +		cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
> +	}
> +
> +	writel(cfg, dev->regs + S5P_CIIMGEFF);
> +}
> +
> +static void s5p_fimc_set_in_dma_size(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->s_frame;
> +	u32 cfg_o = 0;
> +	u32 cfg_r = 0;
> +
> +	if (S5P_FIMC_LCDFIFO == ctx->out_path)
> +		cfg_r |=  S5P_CIREAL_ISIZE_AUTOLOAD_ENABLE;
> +
> +	cfg_o |= S5P_ORGISIZE_HORIZONTAL(frame->f_width);
> +	cfg_o |= S5P_ORGISIZE_VERTICAL(frame->f_height);
> +	cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
> +	cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
> +
> +	writel(cfg_o, dev->regs + S5P_ORGISIZE);
> +	writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
> +}
> +
> +void s5p_fimc_set_in_dma(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->s_frame;
> +	struct fimc_dma_offset *offset = &frame->dma_offset;
> +	u32 cfg = 0;
> +
> +	/* offsets */
> +	cfg |= S5P_CIIYOFF_HORIZONTAL(offset->y_h);
> +	cfg |= S5P_CIIYOFF_VERTICAL(offset->y_v);
> +	writel(cfg, dev->regs + S5P_CIIYOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIICBOFF_HORIZONTAL(offset->cb_h);
> +	cfg |= S5P_CIICBOFF_VERTICAL(offset->cb_v);
> +	writel(cfg, dev->regs + S5P_CIICBOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIICROFF_HORIZONTAL(offset->cr_h);
> +	cfg |= S5P_CIICROFF_VERTICAL(offset->cr_v);
> +	writel(cfg, dev->regs + S5P_CIICROFF);
> +
> +	/* original & real size */
> +	s5p_fimc_set_in_dma_size(ctx);
> +
> +	/* autoload is used currently only in FIFO mode */
> +	s5p_fimc_en_autoload(ctx, S5P_FIMC_LCDFIFO == ctx->out_path);
> +
> +	/* input dma set to process single frame only */
> +	cfg = (S5P_MSCTRL_SUCCESSIVE_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
> +
> +	switch (frame->fmt->color) {
> +	case S5P_FIMC_RGB565:
> +	case S5P_FIMC_RGB666:
> +	case S5P_FIMC_RGB888:
> +		cfg |= S5P_MSCTRL_INFORMAT_RGB;
> +		break;
> +	case S5P_FIMC_YCBCR420:
> +		cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
> +
> +		if (frame->fmt->planes_cnt == 2)
> +			cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
> +		else
> +			cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
> +
> +		break;
> +	case S5P_FIMC_YCBYCR422:
> +	case S5P_FIMC_YCRYCB422:
> +	case S5P_FIMC_CBYCRY422:
> +	case S5P_FIMC_CRYCBY422:
> +		if (frame->fmt->planes_cnt == 1) {
> +			cfg |= ctx->in_order_1p
> +				| S5P_MSCTRL_INFORMAT_YCBCR422_1PLANE;
> +		} else {
> +			cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
> +
> +			if (frame->fmt->planes_cnt == 2)
> +				cfg |= ctx->in_order_2p
> +					| S5P_MSCTRL_C_INT_IN_2PLANE;
> +			else
> +				cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* input DMA flip mode */
> +	if (ctx->out_path == S5P_FIMC_LCDFIFO)
> +		cfg |= s5p_fimc_get_in_flip(ctx);;
> +
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +
> +	/* in/out DMA linear/tiled mode */
> +	cfg = readl(dev->regs + S5P_CIDMAPARAM);
> +	cfg &= ~(S5P_CIDMAPARAM_R_MODE_64X32 | S5P_CIDMAPARAM_W_MODE_64X32);
> +
> +	writel(cfg, dev->regs + S5P_CIDMAPARAM);
> +}
> +
> +void s5p_fimc_start_in_dma(struct fimc_dev *dev)
> +{
> +	u32 cfg = readl(dev->regs + S5P_MSCTRL);
> +	cfg |= S5P_MSCTRL_ENVID;
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +}
> +
> +void s5p_fimc_stop_in_dma(struct fimc_dev *dev)
> +{
> +	u32 cfg = readl(dev->regs + S5P_MSCTRL);
> +	cfg &= ~S5P_MSCTRL_ENVID;
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +}
> +
> +void s5p_fimc_set_input_path(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_MSCTRL);
> +	cfg &= ~S5P_MSCTRL_INPUT_MASK;
> +
> +	if (ctx->in_path == S5P_FIMC_DMA)
> +		cfg |= S5P_MSCTRL_INPUT_MEMORY;
> +	else
> +		cfg |= S5P_MSCTRL_INPUT_EXTCAM;
> +
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +}
> +
> +void s5p_fimc_set_output_path(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
> +	cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
> +
> +	if (ctx->out_path == S5P_FIMC_LCDFIFO)
> +		cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
> +
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_set_input_addr(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = 0;
> +
> +	cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
> +	cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DISABLE;
> +	writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
> +
> +	writel(ctx->s_frame.addr_y, dev->regs + S5P_CIIYSA0);
> +	writel(ctx->s_frame.addr_cb, dev->regs + S5P_CIICBSA0);
> +	writel(ctx->s_frame.addr_cr, dev->regs + S5P_CIICRSA0);
> +
> +	cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DISABLE;
> +	writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
> +}
> +
> +void s5p_fimc_set_output_addr(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	int i = 0;
> +	/* each of the four output register sets points to the same buffer */
> +	for (i = 0; i < S5P_FIMC_MAX_FRAMES; i++) {
> +		writel(ctx->d_frame.addr_y, dev->regs + S5P_CIOYSA(i));
> +		writel(ctx->d_frame.addr_cb, dev->regs + S5P_CIOCBSA(i));
> +		writel(ctx->d_frame.addr_cr, dev->regs + S5P_CIOCRSA(i));
> +	}
> +}
> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
> index 3793d16..ada05c5 100644
> --- a/include/linux/videodev2.h
> +++ b/include/linux/videodev2.h
> @@ -287,6 +287,7 @@ struct v4l2_pix_format {
>  #define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
>  #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
>  #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
> +#define V4L2_PIX_FMT_RGB666  v4l2_fourcc('R', 'G', 'B', 'H') /* 18  RGB-6-6-6	  */
>  #define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
>  #define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
>  #define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
> -- 
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
-- 
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.

WARNING: multiple messages have this Message-ID (diff)
From: ben-linux@fluff.org (Ben Dooks)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v1 3/3] ARM: S5P: Add Camera interface (video postprocessor) driver
Date: Thu, 6 May 2010 07:25:55 +0100	[thread overview]
Message-ID: <20100506062555.GP26401@trinity.fluff.org> (raw)
In-Reply-To: <1271673000-3020-4-git-send-email-s.nawrocki@samsung.com>

On Mon, Apr 19, 2010 at 12:30:00PM +0200, Sylwester Nawrocki wrote:
> The purpose of this driver is to expose, as an initial stage,
>     memory to memory operations, including color conversion,
>     image resizing, flipping and rotation.
> 
>     Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>     Reviewed-by: Pawel Osciak <p.osciak@samsung.com>
>     Reviewed-by: Marek Szyprowski <m.szyprowski@samsung.com>
>     Reviewed-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig                     |    9 +
>  drivers/media/video/Makefile                    |    1 +
>  drivers/media/video/samsung-fimc/Makefile       |    3 +
>  drivers/media/video/samsung-fimc/s5p_fimc.c     | 1491 +++++++++++++++++++++++
>  drivers/media/video/samsung-fimc/s5p_fimc.h     |  331 +++++
>  drivers/media/video/samsung-fimc/s5p_fimc_reg.c |  559 +++++++++
>  include/linux/videodev2.h                       |    1 +
>  7 files changed, 2395 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/samsung-fimc/Makefile
>  create mode 100644 drivers/media/video/samsung-fimc/s5p_fimc.c
>  create mode 100644 drivers/media/video/samsung-fimc/s5p_fimc.h
>  create mode 100644 drivers/media/video/samsung-fimc/s5p_fimc_reg.c

are these also contained in the s3c64xx?

 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index f8fc865..1560de8 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -949,6 +949,15 @@ config VIDEO_OMAP2
>  	---help---
>  	  This is a v4l2 driver for the TI OMAP2 camera capture interface
>  
> +config  VIDEO_SAMSUNG_FIMC
> +	tristate "Samsung S5P Camera Interface (video postprocessor) driver"
> +	depends on VIDEO_DEV && VIDEO_V4L2
> +	select VIDEOBUF_DMA_CONTIG
> +	select V4L2_MEM2MEM_DEV
> +	default n
> +	help
> +	  This is a v4l2 driver the Samsung S5P camera capture interface
> +
>  #
>  # USB Multimedia device configuration
>  #
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index b88b617..c176a34 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -159,6 +159,7 @@ obj-$(CONFIG_VIDEO_MX1)			+= mx1_camera.o
>  obj-$(CONFIG_VIDEO_MX3)			+= mx3_camera.o
>  obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
> +obj-$(CONFIG_VIDEO_SAMSUNG_FIMC) 	+= samsung-fimc/
>  
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
>  
> diff --git a/drivers/media/video/samsung-fimc/Makefile b/drivers/media/video/samsung-fimc/Makefile
> new file mode 100644
> index 0000000..564d7c4
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/Makefile
> @@ -0,0 +1,3 @@
> +
> +obj-$(CONFIG_VIDEO_SAMSUNG_FIMC) := s5px_fimc.o
> +s5px_fimc-y := s5p_fimc.o s5p_fimc_reg.o
> diff --git a/drivers/media/video/samsung-fimc/s5p_fimc.c b/drivers/media/video/samsung-fimc/s5p_fimc.c
> new file mode 100644
> index 0000000..47e6765
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/s5p_fimc.c
> @@ -0,0 +1,1491 @@
> +/* linux/drivers/media/video/samsung-fimc/s5p_fimc.c
> + *
> + * S5P camera interface (postprocessor) driver
> + *
> + * Copyright (c) 2010 Samsung Electronics
> + *
> + * Sylwester Nawrocki, s.nawrocki at samsung.com
> + *
> + * 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 Foundiation. 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/clk.h>
> +#include <linux/wait.h>
> +#include <linux/fs.h>
> +#include <linux/irq.h>
> +#include <linux/mm.h>
> +#include <linux/interrupt.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/timer.h>
> +#include <linux/proc_fs.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/bug.h>
> +#include <linux/memory.h>
> +#include <plat/clock.h>

do you really need plat/clock.h
<linux/clk.h> should be enough.

> +#include <plat/regs-fimc.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/videobuf-core.h>
> +#include <media/videobuf-dma-contig.h>
> +#include "s5p_fimc.h"

some spacing between the different include groups would be nice.

> +
> +
> +#define ctx_get_frame(frame, ctx, type) do {			\
> +	if (V4L2_BUF_TYPE_VIDEO_OUTPUT == (type))		\
> +		frame = &(ctx)->s_frame;			\
> +	else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == (type))		\
> +		frame = &(ctx)->d_frame;			\
> +	else {							\
> +		v4l2_err(&(ctx)->fimc_dev->v4l2_dev,		\
> +		 "Wrong buffer/video queue type (%d)\n", type); \
> +		return -EINVAL;					\
> +	}							\
> +} while (0)
> +
> +
> +static int s5p_fimc_prepare_addr(struct fimc_ctx *ctx,
> +	struct fimc_vid_buffer *buf, enum v4l2_buf_type type);
> +static void queue_init(void *priv, struct videobuf_queue *vq,
> +		       enum v4l2_buf_type type);
> +static int s5p_fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
> +
> +
> +static struct videobuf_queue_ops s5p_fimc_qops;
> +
> +static struct s5p_fimc_fmt formats[] = {
> +	{
> +		.name	= "RGB565",
> +		.fourcc	= V4L2_PIX_FMT_RGB565X,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_RGB565,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "RGB666",
> +		.fourcc	= V4L2_PIX_FMT_RGB666,
> +		.depth	= 32,
> +		.color	= S5P_FIMC_RGB666,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name = "XRGB-8-8-8-8, 24 bpp",
> +		.fourcc	= V4L2_PIX_FMT_RGB24,
> +		.depth = 32,
> +		.color	= S5P_FIMC_RGB888,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 packed, YCbYCr",
> +		.fourcc	= V4L2_PIX_FMT_YUYV,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_YCBYCR422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +		}, {
> +		.name	= "YUV 4:2:2 packed, CbYCrY",
> +		.fourcc	= V4L2_PIX_FMT_UYVY,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_CBYCRY422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 packed, CrYCbY",
> +		.fourcc	= V4L2_PIX_FMT_VYUY,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_CRYCBY422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 packed, YCrYCb",
> +		.fourcc	= V4L2_PIX_FMT_YVYU,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_YCRYCB422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 1
> +	}, {
> +		.name	= "YUV 4:2:2 planar, Y/Cb/Cr",
> +		.fourcc	= V4L2_PIX_FMT_YUV422P,
> +		.depth	= 12,
> +		.color	= S5P_FIMC_YCBCR422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 3
> +	}, {
> +		.name	= "YUV 4:2:2 planar, Y/CbCr",
> +		.fourcc	= V4L2_PIX_FMT_NV16,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_YCBCR422,
> +		.buff_cnt = 1,
> +		.planes_cnt = 2
> +	}, {
> +		.name	= "YUV 4:2:2 planar, Y/CrCb",
> +		.fourcc	= V4L2_PIX_FMT_NV61,
> +		.depth	= 16,
> +		.color	= S5P_FIMC_RGB565,
> +		.buff_cnt = 1,
> +		.planes_cnt = 2
> +	}, {
> +		.name	= "YUV 4:2:0 planar, YCbCr",
> +		.fourcc	= V4L2_PIX_FMT_YUV420,
> +		.depth	= 12,
> +		.color	= S5P_FIMC_YCBCR420,
> +		.buff_cnt = 1,
> +		.planes_cnt = 3
> +	}, {
> +		.name	= "YUV 4:2:0 planar, Y/CbCr",
> +		.fourcc	= V4L2_PIX_FMT_NV12,
> +		.depth	= 12,
> +		.color	= S5P_FIMC_YCBCR420,
> +		.buff_cnt = 1,
> +		.planes_cnt = 2
> +	}
> + };
> +
> +#define NUM_FORMATS ARRAY_SIZE(formats)

is this really necessary?

> +static struct v4l2_queryctrl s5p_fimc_ctrls[] = {
> +	{
> +		.id		= V4L2_CID_HFLIP,
> +		.type		= V4L2_CTRL_TYPE_BOOLEAN,
> +		.name		= "Horizontal flip",
> +		.minimum	= 0,
> +		.maximum	= 1,
> +		.default_value	= 0,
> +	},
> +	{
> +		.id		= V4L2_CID_VFLIP,
> +		.type		= V4L2_CTRL_TYPE_BOOLEAN,
> +		.name		= "Vertical flip",
> +		.minimum	= 0,
> +		.maximum	= 1,
> +		.default_value	= 0,
> +	},
> +	{
> +		.id		= V4L2_CID_ROTATE,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Rotation (CCW)",
> +		.minimum	= 0,
> +		.maximum	= 270,
> +		.step		= 90,
> +		.default_value	= 0,
> +	},
> +};
> +
> +
> +static struct v4l2_queryctrl *get_ctrl(int id)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(s5p_fimc_ctrls); ++i)
> +		if (id == s5p_fimc_ctrls[i].id)
> +			return &s5p_fimc_ctrls[i];
> +	return NULL;
> +}
> +
> +static int s5p_fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
> +{
> +	if (src >= tar * 64) {
> +		return -EINVAL;
> +	} else if (src >= tar * 32) {
> +		*ratio = 32;
> +		*shift = 5;
> +	} else if (src >= tar * 16) {
> +		*ratio = 16;
> +		*shift = 4;
> +	} else if (src >= tar * 8) {
> +		*ratio = 8;
> +		*shift = 3;
> +	} else if (src >= tar * 4) {
> +		*ratio = 4;
> +		*shift = 2;
> +	} else if (src >= tar * 2) {
> +		*ratio = 2;
> +		*shift = 1;
> +	} else {
> +		*ratio = 1;
> +		*shift = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +int s5p_fimc_set_scaler_info(struct fimc_ctx *ctx)
> +{
> +	struct fimc_scaler *sc = &ctx->scaler;
> +	struct fimc_dma_offset *d_ofs = &ctx->s_frame.dma_offset;
> +	int tx, ty, sx, sy;
> +	int width, height, h_ofs, v_ofs;
> +	int ret;
> +	struct fimc_frame *src_frame = &ctx->s_frame;
> +	struct fimc_frame *dst_frame = &ctx->d_frame;
> +
> +	if (ctx->in_path == S5P_FIMC_DMA) {
> +		if ((90 == ctx->rotation || 270 == ctx->rotation)

don't like if (const == var) a lot.

> +			&& ctx->out_path == S5P_FIMC_LCDFIFO) {
> +			/* here we are using only the input rotator */
> +			width = src_frame->height;
> +			height = src_frame->width;
> +			h_ofs = d_ofs->y_v;
> +			v_ofs = d_ofs->y_h;
> +		} else {
> +			width = src_frame->width;
> +			height = src_frame->height;
> +			h_ofs = d_ofs->y_h;
> +			v_ofs = d_ofs->y_v;
> +		}
> +	} else {
> +		width = src_frame->width;
> +		height = src_frame->height;
> +		h_ofs = d_ofs->y_h;
> +		v_ofs = d_ofs->y_v;
> +	}
> +
> +	tx = dst_frame->width;
> +	ty = dst_frame->height;
> +
> +	if (tx <= 0 || ty <= 0) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev,
> +			"invalid target size: %d %d", tx, ty);
> +		return -EINVAL;
> +	}
> +
> +	sx = width;
> +	sy = height;
> +
> +	sc->real_width = width;
> +	sc->real_height = height;
> +
> +	if (sx <= 0 || sy <= 0) {
> +		err("invalid source size: s: %d %d, t: %d %d", sx, sy, tx, ty);
> +		return -EINVAL;
> +	}
> +
> +	dbg("sx= %d, sy= %d, tx= %d, ty= %d", sx, sy, tx, ty);
> +
> +	ret = s5p_fimc_get_scaler_factor(sx, tx,
> +			  &sc->pre_hratio, &sc->hfactor);
> +	if (ret)
> +		return ret;
> +
> +	ret = s5p_fimc_get_scaler_factor(sy, ty,
> +			  &sc->pre_vratio, &sc->vfactor);
> +	if (ret)
> +		return ret;
> +
> +	sc->pre_dst_width = sx/sc->pre_hratio;
> +	sc->pre_dst_height = sy/sc->pre_vratio;
> +
> +	sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
> +	sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
> +
> +	dbg("sc->main_hratio= %d, sc->main_vratio= %d",
> +		sc->main_hratio, sc->main_vratio);
> +	dbg("sc->hfactor= %d, sc->vfactor= %d", sc->hfactor, sc->vfactor);
> +
> +	sc->scaleup_h = (tx >= sx) ? 1 : 0;
> +	sc->scaleup_v = (ty >= sy) ? 1 : 0;
> +
> +	/* check to see if input and output size/format differ */
> +	if (src_frame->fmt->color == dst_frame->fmt->color
> +		&& src_frame->width == dst_frame->width
> +		&& src_frame->height == dst_frame->height)
> +		sc->copy_mode = 1;
> +	else
> +		sc->copy_mode = 0;
> +
> +	return 0;
> +}
> +
> +static void s5p_fimc_clear_irq(struct fimc_dev *dev)
> +{
> +	u32 cfg = readl(dev->regs + S5P_CIGCTRL);
> +	cfg |= S5P_CIGCTRL_IRQ_CLR;
> +	writel(cfg, dev->regs + S5P_CIGCTRL);
> +}
> +
> +static irqreturn_t s5p_fimc_isr(int irq, void *priv)
> +{
> +	struct fimc_ctx *ctx;
> +	struct fimc_vid_buffer *src_buf, *dst_buf;
> +	struct fimc_dev *dev = (struct fimc_dev *)priv;
> +
> +	BUG_ON(NULL == dev);
> +
> +	s5p_fimc_clear_irq(dev);
> +	s5p_fimc_check_fifo(dev);
> +
> +	ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
> +	if (NULL == ctx)
> +		return IRQ_HANDLED;
> +
> +	src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
> +	dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
> +	spin_lock(&dev->irqlock);
> +	src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
> +	wake_up(&src_buf->vb.done);
> +	wake_up(&dst_buf->vb.done);
> +	spin_unlock(&dev->irqlock);
> +	v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Rewrite context data to the registers as required and start
> + * the transaction in hardware.
> + */
> +static void s5p_fimc_dma_run(void *priv)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame;
> +	u32	ret;
> +
> +	if (NULL == ctx || S5P_FIMC_DMA != ctx->out_path)
> +		return;

!ctx would be sufficient for the first test.

> +
> +	ctx->updated |= S5P_FIMC_SRC_ADDR | S5P_FIMC_DST_ADDR;
> +	ret = s5p_fimc_prepare_config(ctx, ctx->updated);
> +	if (ret) {
> +		err("Wrong configuration");
> +		return;
> +	}
> +
> +	frame = &ctx->s_frame;
> +	dbg("SRC: width= %d, height= %d, f_width= %d, f_height= %d " \
> +		"offs_h= %d, offs_v= %d",
> +		frame->width, frame->height,
> +		frame->f_width, frame->f_height,
> +		frame->offs_h, frame->offs_v);
> +
> +	frame = &ctx->d_frame;
> +	dbg("DST: width= %d, height= %d, f_width= %d, f_height= %d " \
> +		"offs_h= %d, offs_v= %d",
> +		frame->width, frame->height,
> +		frame->f_width, frame->f_height,
> +		frame->offs_h, frame->offs_v);
> +
> +	s5p_fimc_set_input_addr(ctx);
> +
> +	if (ctx->updated&S5P_FIMC_PARAMS) {
> +		s5p_fimc_set_input_path(ctx);
> +
> +		if (ctx->in_path == S5P_FIMC_DMA)
> +			s5p_fimc_set_in_dma(ctx);
> +
> +		if (s5p_fimc_set_scaler_info(ctx)) {
> +			err("scaler configuration error");
> +			return;
> +		}
> +		s5p_fimc_set_prescaler(ctx);
> +		s5p_fimc_set_scaler(ctx);
> +		s5p_fimc_set_target_format(ctx);
> +		s5p_fimc_set_effect(ctx);
> +	}
> +
> +	s5p_fimc_set_output_path(ctx);
> +	if (ctx->updated & (S5P_FIMC_DST_ADDR | S5P_FIMC_PARAMS))
> +		s5p_fimc_set_output_addr(ctx);
> +
> +	if (ctx->updated & S5P_FIMC_PARAMS)
> +		s5p_fimc_set_out_dma(ctx);
> +
> +	if (ctx->scaler.enabled)
> +		s5p_fimc_start_scaler(ctx);
> +
> +	s5p_fimc_en_capture(ctx);
> +
> +	if (ctx->in_path == S5P_FIMC_DMA)
> +		s5p_fimc_start_in_dma(dev);
> +
> +	ctx->updated = 0;
> +}
> +
> +static void s5p_fimc_job_abort(void *priv)
> +{
> +
> +}
> +
> +/* set order for 1 and 2 plane YCBCR 4:2:2 formats */
> +static void s5p_fimc_set_yuv_order(struct fimc_ctx *ctx)
> +{
> +	/* the one only mode supported in SoC */
> +	ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
> +	ctx->out_order_2p = S5P_FIMC_LSB_CRCB;
> +
> +	/* set order for 1 plane input formats */
> +	switch (ctx->s_frame.fmt->color) {
> +	case S5P_FIMC_YCRYCB422:
> +		ctx->in_order_1p = S5P_FIMC_IN_YCRYCB;
> +		break;
> +	case S5P_FIMC_CBYCRY422:
> +		ctx->in_order_1p = S5P_FIMC_IN_CBYCRY;
> +		break;
> +	case S5P_FIMC_CRYCBY422:
> +		ctx->in_order_1p = S5P_FIMC_IN_CRYCBY;
> +		break;
> +	case S5P_FIMC_YCBYCR422: /* fall through */
> +	default:
> +		ctx->in_order_1p = S5P_FIMC_IN_YCBYCR;
> +		break;
> +	}
> +	dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
> +
> +	switch (ctx->d_frame.fmt->color) {
> +	case S5P_FIMC_YCRYCB422:
> +		ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB;
> +		break;
> +	case S5P_FIMC_CBYCRY422:
> +		ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY;
> +		break;
> +	case S5P_FIMC_CRYCBY422:
> +		ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY;
> +		break;
> +	case S5P_FIMC_YCBYCR422: /* fall through */
> +	default:
> +		ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR;
> +		break;
> +	}
> +	dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
> +}
> +
> +/**
> + * @name	s5p_fimc_prepare_config
> + *		Check dimensions, operation and color mode
> + * @return	0 if dimensions are valid, non zero otherwise
> + */
> +static int s5p_fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
> +{
> +	struct fimc_frame *src_frame, *dst_frame;
> +	struct fimc_vid_buffer *buf = NULL;
> +	int ret = 0;
> +	u32 tmp;
> +
> +	dbg("flags= 0x%X", flags);
> +
> +	src_frame = &ctx->s_frame;
> +	dst_frame = &ctx->d_frame;
> +
> +	if (flags & S5P_FIMC_PARAMS) {
> +		if ((S5P_FIMC_DMA == ctx->out_path) &&
> +			(90 == ctx->rotation || 270 == ctx->rotation)) {
> +			tmp = dst_frame->f_width;
> +			dst_frame->f_width = dst_frame->f_height;
> +			dst_frame->f_height = tmp;
> +			tmp = dst_frame->width;
> +			dst_frame->width = dst_frame->height;
> +			dst_frame->height = tmp;
> +		}
> +
> +		/* prepare the output offset related attributes for scaler */
> +		dst_frame->dma_offset.y_h = dst_frame->offs_h
> +			 * (dst_frame->fmt->depth >> 3);
> +		dst_frame->dma_offset.y_v = dst_frame->offs_v;
> +
> +		dst_frame->dma_offset.cb_h = dst_frame->offs_h;
> +		dst_frame->dma_offset.cb_v = dst_frame->offs_v;
> +
> +		dst_frame->dma_offset.cr_h = dst_frame->offs_h;
> +		dst_frame->dma_offset.cr_v = dst_frame->offs_v;
> +
> +		if (3 == dst_frame->fmt->planes_cnt) {
> +			dst_frame->dma_offset.cb_h /= 2;
> +			dst_frame->dma_offset.cb_v /= 2;
> +			dst_frame->dma_offset.cr_h /= 2;
> +			dst_frame->dma_offset.cr_v /= 2;
> +		}
> +
> +		dbg("OUT OFFSET: color= %d, y_h= %d, y_v= %d",
> +			dst_frame->fmt->color,
> +			dst_frame->dma_offset.y_h, dst_frame->dma_offset.y_v);
> +
> +		/* prepare the input offset related attributes for scaler */
> +		src_frame->dma_offset.y_h = src_frame->offs_h
> +			* (src_frame->fmt->depth >> 3);
> +		src_frame->dma_offset.y_v = src_frame->offs_v;
> +
> +		src_frame->dma_offset.cb_h = src_frame->offs_h;
> +		src_frame->dma_offset.cb_v = src_frame->offs_v;
> +
> +		src_frame->dma_offset.cr_h = src_frame->offs_h;
> +		src_frame->dma_offset.cr_v = src_frame->offs_v;
> +
> +		if (3 == src_frame->fmt->planes_cnt) {
> +			src_frame->dma_offset.cb_h /= 2;
> +			src_frame->dma_offset.cb_v /= 2;
> +			src_frame->dma_offset.cr_h /= 2;
> +			src_frame->dma_offset.cr_v /= 2;
> +		}
> +
> +		dbg("IN OFFSET: color= %d, y_h= %d, y_v= %d",
> +			src_frame->fmt->color, src_frame->dma_offset.y_h,
> +			src_frame->dma_offset.y_v);
> +
> +		s5p_fimc_set_yuv_order(ctx);
> +
> +		/* Check against the scaler ratio */
> +		if (src_frame->height > SCALER_MAX_VRATIO*dst_frame->height ||
> +			src_frame->width > SCALER_MAX_HRATIO*dst_frame->width) {
> +				err("Out of the scaler range");
> +				return -EINVAL;
> +		}
> +	} /* if (flags & S5P_FIMC_PARAMS) */
> +
> +	/* input DMA mode is not allowed when the scaler is disabled */
> +	ctx->scaler.enabled = 1;
> +
> +	if (flags & (S5P_FIMC_SRC_ADDR)) {
> +		buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
> +		ret = s5p_fimc_prepare_addr(ctx, buf,
> +			V4L2_BUF_TYPE_VIDEO_OUTPUT);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (flags & (S5P_FIMC_DST_ADDR)) {
> +		buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
> +		ret = s5p_fimc_prepare_addr(ctx, buf,
> +			V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +	}
> +
> +	return ret;
> +}
> +
> +/* the color format (planes_cnt, buff_cnt) must be already configured */
> +static int s5p_fimc_prepare_addr(struct fimc_ctx *ctx,
> +		struct fimc_vid_buffer *buf, enum v4l2_buf_type type)
> +{
> +	int ret = 0;
> +	struct fimc_frame *frame;
> +	u32 pix_size;
> +
> +	ctx_get_frame(frame, ctx, type);
> +
> +	if (!buf)
> +		return -EINVAL;
> +
> +	pix_size = frame->width * frame->height;
> +
> +	dbg("%d buff_cnt, planes_cnt= %d, frame->size= %d, pix_size= %d",
> +		frame->fmt->buff_cnt, frame->fmt->planes_cnt,
> +		frame->size, pix_size);
> +
> +
> +	if (frame->fmt->buff_cnt == 1) {
> +		frame->addr_y = videobuf_to_dma_contig(&buf->vb);
> +		switch (frame->fmt->planes_cnt) {
> +		case 1:
> +			frame->addr_cb = 0;
> +			frame->addr_cr = 0;
> +			break;
> +		case 2:
> +			/* decompose Y into Y/Cb */
> +			frame->addr_cb = (u32)(frame->addr_y + pix_size);
> +			frame->addr_cr = 0;
> +			break;
> +		case 3:
> +			frame->addr_cb = (u32)(frame->addr_y + pix_size);
> +			/* decompose Y into Y/Cb/Cr */
> +			if (S5P_FIMC_YCBCR420 == frame->fmt->color)
> +				frame->addr_cr = (u32)(frame->addr_cb
> +						+ (pix_size >> 2));
> +			else /* 422 */
> +				frame->addr_cr = (u32)(frame->addr_cb
> +						+ (pix_size >> 1));
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	dbg("PHYS_ADDR: type= %d, y= 0x%X  cb= 0x%X cr= 0x%X ret= %d",
> +	type, frame->addr_y, frame->addr_cb, frame->addr_cr, ret);
> +
> +	return ret;
> +}
> +
> +static void s5p_fimc_buf_release(struct videobuf_queue *vq,
> +				    struct videobuf_buffer *vb)
> +{
> +	videobuf_dma_contig_free(vq, vb);
> +	vb->state = VIDEOBUF_NEEDS_INIT;
> +}
> +
> +static int s5p_fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
> +				unsigned int *size)
> +{
> +	struct fimc_ctx *ctx = vq->priv_data;
> +	struct fimc_frame *frame;
> +
> +	ctx_get_frame(frame, ctx, vq->type);
> +
> +	*size = (frame->width * frame->height * frame->fmt->depth) >> 3;
> +	if (0 == *count)
> +		*count = 1;
> +	return 0;
> +}
> +
> +static int s5p_fimc_buf_prepare(struct videobuf_queue *vq,
> +				struct videobuf_buffer *vb,
> +				enum v4l2_field field)
> +{
> +	struct fimc_ctx *ctx = vq->priv_data;
> +	struct fimc_frame *frame;
> +	int ret;
> +
> +	ctx_get_frame(frame, ctx, vq->type);
> +
> +	if (vb->baddr) {
> +		if (vb->bsize < frame->size) {
> +			v4l2_err(&ctx->fimc_dev->v4l2_dev,
> +				"User-provided buffer too small (%d < %d)\n",
> +				 vb->bsize, frame->size);
> +			WARN_ON(1);
> +			return -EINVAL;
> +		}
> +	} else if (vb->state != VIDEOBUF_NEEDS_INIT
> +		   && vb->bsize < frame->size) {
> +		return -EINVAL;
> +	}
> +
> +	vb->width       = frame->width;
> +	vb->height      = frame->height;
> +	vb->bytesperline = (frame->width * frame->fmt->depth) >> 3;
> +	vb->size        = frame->size;
> +	vb->field       = field;
> +
> +	if (VIDEOBUF_NEEDS_INIT == vb->state) {
> +		ret = videobuf_iolock(vq, vb, NULL);
> +		if (ret) {
> +			v4l2_err(&ctx->fimc_dev->v4l2_dev,
> +				"Iolock failed\n");
> +			goto fail;
> +		}
> +	}
> +	vb->state = VIDEOBUF_PREPARED;
> +
> +	return 0;
> +fail:
> +	s5p_fimc_buf_release(vq, vb);
> +	return ret;
> +}
> +
> +static void s5p_fimc_buf_queue(struct videobuf_queue *vq,
> +				  struct videobuf_buffer *vb)
> +{
> +	struct fimc_ctx *ctx = vq->priv_data;
> +	v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
> +}
> +
> +
> +static struct videobuf_queue_ops s5p_fimc_qops = {
> +	.buf_setup	= s5p_fimc_buf_setup,
> +	.buf_prepare	= s5p_fimc_buf_prepare,
> +	.buf_queue	= s5p_fimc_buf_queue,
> +	.buf_release	= s5p_fimc_buf_release,
> +};
> +
> +static int s5p_fimc_querycap(struct file *file, void *priv,
> +			   struct v4l2_capability *cap)
> +{
> +	struct fimc_ctx *ctx = file->private_data;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +
> +	strncpy(cap->driver, dev->pdev->name, sizeof(cap->driver) - 1);
> +	strncpy(cap->card, dev->pdev->name, sizeof(cap->card) - 1);
> +	cap->bus_info[0] = 0;
> +	cap->version = KERNEL_VERSION(1, 0, 0);
> +	cap->capabilities = V4L2_CAP_STREAMING |
> +		V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_enum_fmt(struct file *file, void *priv,
> +			   struct v4l2_fmtdesc *f)
> +{
> +	struct s5p_fimc_fmt *fmt;
> +
> +	if (f->index >= NUM_FORMATS)
> +		return -EINVAL;
> +
> +	fmt = &formats[f->index];
> +	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
> +	f->pixelformat = fmt->fourcc;
> +	return 0;
> +}
> +
> +static int s5p_fimc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_frame *frame;
> +
> +	ctx_get_frame(frame, ctx, f->type);
> +
> +	f->fmt.pix.width	= frame->width;
> +	f->fmt.pix.height	= frame->height;
> +	f->fmt.pix.field	= V4L2_FIELD_NONE;
> +	f->fmt.pix.pixelformat	= frame->fmt->fourcc;
> +
> +	return 0;
> +}
> +
> +static struct s5p_fimc_fmt *find_format(struct v4l2_format *f)
> +{
> +	struct s5p_fimc_fmt *fmt;
> +	unsigned int i;
> +
> +	for (i = 0; i < NUM_FORMATS; ++i) {
> +		fmt = &formats[i];
> +		if (fmt->fourcc == f->fmt.pix.pixelformat)
> +			break;
> +	}
> +	if (i == NUM_FORMATS)
> +		return NULL;
> +
> +	return fmt;
> +}
> +
> +static int s5p_fimc_try_fmt(struct file *file, void *priv,
> +			     struct v4l2_format *f)
> +{
> +	struct s5p_fimc_fmt *fmt;
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	u32 max_width, max_height;
> +	u32 mod_x = 1;	/* defaults for non-DMA mode */
> +	u32 mod_y = 1;
> +
> +
> +	fmt = find_format(f);
> +	if (!fmt) {
> +		v4l2_err(&dev->v4l2_dev, "Fourcc format (0x%08x) invalid.\n",
> +			 pix->pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	if (pix->field == V4L2_FIELD_ANY)
> +		pix->field = V4L2_FIELD_NONE;
> +	else if (V4L2_FIELD_NONE != pix->field)
> +		return -EINVAL;
> +
> +	if (V4L2_BUF_TYPE_VIDEO_OUTPUT == f->type) {
> +		max_width = dev->pldata->scaler_dis_w;
> +		max_height = dev->pldata->scaler_dis_w;
> +	} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == f->type) {
> +		max_width = dev->pldata->out_rot_dis_w;
> +		max_height = dev->pldata->out_rot_dis_w;
> +	} else {
> +		err("Wrong stream type (%d)", f->type);
> +		return -EINVAL;
> +	}
> +
> +	dbg("max_w= %d, max_h= %d", max_width, max_height);
> +
> +	if (pix->height > max_height)
> +		pix->height = max_height;
> +	if (pix->width > max_width)
> +		pix->width = max_width;
> +
> +	if (S5P_FIMC_DMA == ctx->in_path
> +		|| V4L2_BUF_TYPE_VIDEO_CAPTURE == f->type) {
> +		mod_x = (DMA_MIN_SIZE - 1);
> +		mod_y = (DMA_MIN_SIZE - 1);
> +	} else {
> +		mod_x = 1, mod_y = 1;
> +	}
> +	dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y);
> +
> +	pix->width &= ~mod_x;
> +	pix->height &= ~mod_y;
> +	if (0 == pix->width)
> +		pix->width = mod_x + 1;
> +	if (0 == pix->height)
> +		pix->height = mod_y + 1;
> +
> +	if (0 == pix->bytesperline) {
> +		pix->bytesperline = (pix->width * fmt->depth) >> 3;
> +	} else {
> +		u32 f_width = pix->bytesperline*8/fmt->depth;
> +		if (f_width > max_width)
> +			pix->bytesperline = (max_width * fmt->depth) >> 3;
> +	}
> +
> +	if (0 == pix->sizeimage)
> +		pix->sizeimage = pix->height * pix->bytesperline;
> +
> +	dbg("pix->bytesperline= %d, fmt->depth= %d",
> +	    pix->bytesperline, fmt->depth);
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +	struct fimc_frame *frame;
> +	struct v4l2_pix_format *pix;
> +	struct videobuf_queue	*src_vq = NULL,
> +				*dst_vq = NULL;
> +	struct fimc_ctx *ctx = priv;
> +	int ret = 0;
> +
> +	BUG_ON(NULL == ctx);
> +
> +	ret = s5p_fimc_try_fmt(file, priv, f);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&ctx->lock);
> +
> +	src_vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
> +	dst_vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
> +
> +	mutex_lock(&src_vq->vb_lock);
> +	mutex_lock(&dst_vq->vb_lock);
> +
> +	if (videobuf_queue_is_busy(src_vq)) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "%s queue busy\n", __func__);
> +		ret = -EBUSY;
> +		goto s_fmt_out;
> +	}
> +
> +	if (videobuf_queue_is_busy(dst_vq)) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "%s queue busy\n", __func__);
> +		ret = -EBUSY;
> +		goto s_fmt_out;
> +	}
> +
> +	ctx_get_frame(frame, ctx, f->type);
> +
> +	pix = &f->fmt.pix;
> +	frame->fmt = find_format(f);
> +	if (!frame->fmt) {
> +		ret = -EINVAL;
> +		goto s_fmt_out;
> +	}
> +
> +	dbg("depth=%d, bytesperline=%d", frame->fmt->depth, pix->bytesperline);
> +
> +	frame->width = pix->width;
> +	frame->height = pix->height;
> +
> +	frame->f_width = pix->bytesperline*8/frame->fmt->depth;
> +	frame->f_height = pix->sizeimage/pix->bytesperline;
> +
> +	frame->o_width = pix->width;
> +	frame->o_height = pix->height;
> +	frame->offs_h = 0;
> +	frame->offs_v = 0;
> +	frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
> +	ctx->updated |= S5P_FIMC_PARAMS;
> +	src_vq->field = dst_vq->field = pix->field;
> +	dbg("f_width= %d, f_height= %d", frame->f_width, frame->f_height);
> +
> +s_fmt_out:
> +	mutex_unlock(&dst_vq->vb_lock);
> +	mutex_unlock(&src_vq->vb_lock);
> +	mutex_unlock(&ctx->lock);
> +	return ret;
> +}
> +
> +static int s5p_fimc_reqbufs(struct file *file, void *priv,
> +			  struct v4l2_requestbuffers *reqbufs)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
> +}
> +
> +static int s5p_fimc_querybuf(struct file *file, void *priv,
> +			   struct v4l2_buffer *buf)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int s5p_fimc_qbuf(struct file *file, void *priv,
> +			  struct v4l2_buffer *buf)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int s5p_fimc_dqbuf(struct file *file, void *priv,
> +			   struct v4l2_buffer *buf)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int s5p_fimc_streamon(struct file *file, void *priv,
> +			   enum v4l2_buf_type type)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
> +}
> +
> +static int s5p_fimc_streamoff(struct file *file, void *priv,
> +			    enum v4l2_buf_type type)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
> +}
> +
> +static int s5p_fimc_queryctrl(struct file *file, void *priv,
> +			    struct v4l2_queryctrl *qc)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(qc->id);
> +	if (!c)
> +		return -EINVAL;
> +	*qc = *c;
> +	return 0;
> +}
> +
> +static int s5p_fimc_g_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct fimc_ctx *ctx = priv;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_HFLIP:
> +		ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0;
> +		break;
> +	case V4L2_CID_VFLIP:
> +		ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0;
> +		break;
> +	case V4L2_CID_ROTATE:
> +		ctrl->value = ctx->rotation;
> +		break;
> +	default:
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	dbg("ctrl->value= %d", ctrl->value);
> +	return 0;
> +}
> +
> +static int check_ctrl_val(struct fimc_ctx *ctx,
> +			  struct v4l2_control *ctrl)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(ctrl->id);
> +	if (!c)
> +		return -EINVAL;
> +
> +	if (ctrl->value < c->minimum || ctrl->value > c->maximum
> +		|| (c->step != 0 && ctrl->value % c->step != 0)) {
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "Invalid control value\n");
> +		return -ERANGE;
> +	}
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_s_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct s5p_platform_fimc *pldata = ctx->fimc_dev->pldata;
> +	int ret = 0;
> +
> +	ret = check_ctrl_val(ctx, ctrl);
> +	if (ret != 0)
> +		return ret;
> +
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_HFLIP:
> +		if (ctrl->value)
> +			ctx->flip |= FLIP_X_AXIS;
> +		else
> +			ctx->flip &= ~FLIP_X_AXIS;
> +		break;
> +
> +	case V4L2_CID_VFLIP:
> +		if (ctrl->value)
> +			ctx->flip |= FLIP_Y_AXIS;
> +		else
> +			ctx->flip &= ~FLIP_Y_AXIS;
> +		break;
> +
> +	case V4L2_CID_ROTATE:
> +		if (90 == ctrl->value || 270 == ctrl->value) {
> +			/* use input rotator in LCDFIFO output mode only */
> +			if (S5P_FIMC_LCDFIFO == ctx->out_path) {
> +				if (!(pldata->capability & S5P_FIMC_IN_ROT))
> +					return -EINVAL;
> +			} /* in DMA/ out DMA case */
> +			else if (S5P_FIMC_DMA == ctx->in_path &&
> +				 !(pldata->capability & S5P_FIMC_OUT_ROT))
> +				return -EINVAL;
> +		}
> +
> +		ctx->rotation = ctrl->value;
> +		if (180 == ctrl->value)
> +			ctx->flip = FLIP_XY_AXIS;
> +		break;
> +
> +	default:
> +		v4l2_err(&ctx->fimc_dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +
> +	ctx->updated |= S5P_FIMC_PARAMS;
> +	return 0;
> +}
> +
> +
> +static int s5p_fimc_cropcap(struct file *file, void *fh,
> +			     struct v4l2_cropcap *cr)
> +{
> +	struct fimc_frame *frame;
> +	struct fimc_ctx *ctx = fh;
> +	if (!ctx) {
> +		WARN_ON(1);
> +		return -ENOENT;
> +	}
> +
> +	ctx_get_frame(frame, ctx, cr->type);
> +
> +	cr->bounds.left = 0;
> +	cr->bounds.top = 0;
> +	cr->bounds.width = frame->f_width;
> +	cr->bounds.height = frame->f_height;
> +	cr->defrect.left = frame->offs_h;
> +	cr->defrect.top = frame->offs_v;
> +	cr->defrect.width = frame->o_width;
> +	cr->defrect.height = frame->o_height;
> +	return 0;
> +}
> +
> +static int s5p_fimc_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
> +{
> +	struct fimc_frame *frame;
> +	struct fimc_ctx *ctx = file->private_data;
> +
> +	if (!ctx) {
> +		WARN_ON(1);
> +		return -ENOENT;
> +	}
> +
> +	ctx_get_frame(frame, ctx, cr->type);
> +
> +	cr->c.left = frame->offs_h;
> +	cr->c.top = frame->offs_v;
> +	cr->c.width = frame->width;
> +	cr->c.height = frame->height;
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
> +{
> +	struct fimc_frame *f;
> +	struct fimc_ctx *ctx = file->private_data;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +
> +	if (cr->c.top < 0 || cr->c.left < 0) {
> +		v4l2_err(&dev->v4l2_dev,
> +			"doesn't support negative values for top & left\n");
> +		return -EINVAL;
> +	}
> +
> +	ctx_get_frame(f, ctx, cr->type);
> +
> +	dbg("%d %d %d %d f_w= %d, f_h= %d",
> +		cr->c.left, cr->c.top, cr->c.width, cr->c.height,
> +		f->f_width, f->f_height);
> +
> +	/* adjust to pixel boundary */
> +	cr->c.width = cr->c.width & ~PIX_ALIGN_MASK;
> +	cr->c.height = cr->c.height & ~PIX_ALIGN_MASK;
> +	if (0 == cr->c.width)
> +		cr->c.width = DMA_MIN_SIZE;
> +	if (0 == cr->c.height)
> +		cr->c.height = DMA_MIN_SIZE;
> +	cr->c.left = ((cr->c.left + PIX_ALIGN_MASK - 1) & ~PIX_ALIGN_MASK);
> +	cr->c.top = ((cr->c.top + PIX_ALIGN_MASK - 1) & ~PIX_ALIGN_MASK);
> +
> +	if ((cr->c.left + cr->c.width > f->o_width)
> +		|| (cr->c.top + cr->c.height > f->o_height)) {
> +		v4l2_err(&dev->v4l2_dev, "Error in S_CROP params\n");
> +		dbg("%d %d %d %d. %d %d",
> +			cr->c.left, cr->c.top, cr->c.width, cr->c.height,
> +			f->o_width, f->o_height);
> +		return -EINVAL;
> +	}
> +
> +	/* check for the pixel scaling ratio when cropping input image */
> +	if (V4L2_BUF_TYPE_VIDEO_OUTPUT == cr->type) {
> +		if (f->width > (SCALER_MAX_HRATIO*ctx->d_frame.width) ||
> +			f->height > (SCALER_MAX_VRATIO*ctx->d_frame.height)) {
> +			v4l2_err(&dev->v4l2_dev, "Out of the scaler range");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	f->offs_h = cr->c.left;
> +	f->offs_v = cr->c.top;
> +	f->width = cr->c.width;
> +	f->height = cr->c.height;
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops s5p_fimc_ioctl_ops = {
> +	.vidioc_querycap		= s5p_fimc_querycap,
> +
> +	.vidioc_enum_fmt_vid_cap	= s5p_fimc_enum_fmt,
> +	.vidioc_enum_fmt_vid_out	= s5p_fimc_enum_fmt,
> +
> +	.vidioc_g_fmt_vid_cap		= s5p_fimc_g_fmt,
> +	.vidioc_g_fmt_vid_out		= s5p_fimc_g_fmt,
> +
> +	.vidioc_try_fmt_vid_cap		= s5p_fimc_try_fmt,
> +	.vidioc_try_fmt_vid_out		= s5p_fimc_try_fmt,
> +
> +	.vidioc_s_fmt_vid_cap		= s5p_fimc_s_fmt,
> +	.vidioc_s_fmt_vid_out		= s5p_fimc_s_fmt,
> +
> +	.vidioc_reqbufs			= s5p_fimc_reqbufs,
> +	.vidioc_querybuf		= s5p_fimc_querybuf,
> +
> +	.vidioc_qbuf			= s5p_fimc_qbuf,
> +	.vidioc_dqbuf			= s5p_fimc_dqbuf,
> +
> +	.vidioc_streamon		= s5p_fimc_streamon,
> +	.vidioc_streamoff		= s5p_fimc_streamoff,
> +
> +	.vidioc_queryctrl		= s5p_fimc_queryctrl,
> +	.vidioc_g_ctrl			= s5p_fimc_g_ctrl,
> +	.vidioc_s_ctrl			= s5p_fimc_s_ctrl,
> +
> +	.vidioc_g_crop			= s5p_fimc_g_crop,
> +	.vidioc_s_crop			= s5p_fimc_s_crop,
> +	.vidioc_cropcap			= s5p_fimc_cropcap
> +
> +};
> +
> +static void queue_init(void *priv, struct videobuf_queue *vq,
> +		       enum v4l2_buf_type type)
> +{
> +	struct fimc_ctx *ctx = priv;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +
> +	videobuf_queue_dma_contig_init(vq, &s5p_fimc_qops, dev->v4l2_dev.dev,
> +			&dev->irqlock, type, V4L2_FIELD_NONE,
> +			sizeof(struct fimc_vid_buffer), priv);
> +}
> +
> +static int s5p_fimc_open(struct file *file)
> +{
> +	struct fimc_dev *dev = video_drvdata(file);
> +	struct fimc_ctx *ctx = NULL;
> +	int err;
> +
> +	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	file->private_data = ctx;
> +	ctx->fimc_dev = dev;
> +	/* default format */
> +	ctx->s_frame.fmt = &formats[0];
> +	ctx->d_frame.fmt = &formats[0];
> +	/* per user process device context initialization */
> +	ctx->updated = 0;
> +	ctx->effect.type = S5P_FIMC_EFFECT_ORIGINAL;
> +	ctx->flags = 0;
> +	ctx->in_path = S5P_FIMC_DMA;
> +	ctx->out_path = S5P_FIMC_DMA;
> +	mutex_init(&ctx->lock);
> +
> +	ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, dev->m2m_dev, queue_init);
> +
> +	if (IS_ERR(ctx->m2m_ctx)) {
> +		err = PTR_ERR(ctx->m2m_ctx);
> +		kfree(ctx);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int s5p_fimc_release(struct file *file)
> +{
> +	struct fimc_ctx *ctx =
> +		(struct fimc_ctx *)file->private_data;
> +
> +	v4l2_m2m_ctx_release(ctx->m2m_ctx);
> +	kfree(ctx);
> +	return 0;
> +}
> +
> +static unsigned int s5p_fimc_poll(struct file *file,
> +				     struct poll_table_struct *wait)
> +{
> +	struct fimc_ctx *ctx = file->private_data;
> +	return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
> +}
> +
> +
> +static int s5p_fimc_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct fimc_ctx *ctx = file->private_data;
> +	return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
> +}
> +
> +static const struct v4l2_file_operations s5p_fimc_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= s5p_fimc_open,
> +	.release	= s5p_fimc_release,
> +	.poll		= s5p_fimc_poll,
> +	.ioctl		= video_ioctl2,
> +	.mmap		= s5p_fimc_mmap,
> +};
> +
> +static struct v4l2_m2m_ops m2m_ops = {
> +	.device_run	= s5p_fimc_dma_run,
> +	.job_abort	= s5p_fimc_job_abort,
> +};
> +
> +
> +static int s5p_fimc_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct video_device *vfd;
> +	struct fimc_dev *dev;
> +	int ret = 0;
> +	struct clk *srclk;
> +
> +	dev_dbg(&pdev->dev, "probe\n");
> +
> +	if (NULL == pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data is not set\n");
> +		return -ENOENT;
> +	}
> +
> +	dev = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->id = pdev->id;
> +	dev->pdev = pdev;
> +	dev->pldata = pdev->dev.platform_data;
> +
> +	spin_lock_init(&dev->irqlock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "failed to find the registers\n");
> +		ret = -ENOENT;
> +		goto err_info;
> +	}
> +
> +	dev->regs_res = request_mem_region(res->start, resource_size(res),
> +			dev_name(&pdev->dev));
> +	if (!dev->regs_res) {
> +		dev_err(&pdev->dev, "failed to obtain register region\n");
> +		ret = -ENOENT;
> +		goto err_info;
> +	}
> +
> +	dev->regs = ioremap(res->start, resource_size(res));
> +	if (!dev->regs) {
> +		dev_err(&pdev->dev, "failed to map registers\n");
> +		ret = -ENXIO;
> +		goto err_req_region;
> +	}
> +
> +	/* FIMC parent clock */
> +	srclk = clk_get(&pdev->dev, dev->pldata->srclk_name);
> +	if (IS_ERR(srclk)) {
> +		dev_err(&pdev->dev, "failed to get source clock of fimc\n");
> +		goto err_regs_unmap;
> +	}

you don't seem to clk_put this if the call exits normally?

> +	/* FIMC clock */
> +	dev->clock = clk_get(&pdev->dev, "fimc");
> +	if (IS_ERR(dev->clock)) {
> +		dev_err(&pdev->dev, "failed to get fimc clock source\n");
> +		goto err_clk2;
> +	}
> +
> +	ret = clk_set_parent(dev->clock, srclk);
> +	if (ret)
> +		goto err_clk;
> +
> +	/* set FIMC local clock rate */
> +	clk_set_rate(dev->clock, dev->pldata->clockrate);
> +	clk_enable(dev->clock);
> +
> +	/* IRQ */
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get IRQ resource\n");
> +		ret = -ENXIO;
> +		goto err_clk;
> +	}
> +	dev->irq = res->start;
> +
> +	s5p_fimc_reset(dev);
> +
> +	ret = request_irq(dev->irq, s5p_fimc_isr, 0, pdev->name, dev);
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
> +		goto err_clk;
> +	}
> +
> +	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> +	if (ret)
> +		goto err_irq;
> +
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
> +		goto err_unreg_dev;
> +	}
> +
> +	if ((u32)dev->id >= MAX_FIMC_DEVICES)
> +		goto err_unreg_dev;
> +
> +	vfd->fops	= &s5p_fimc_fops;
> +	vfd->ioctl_ops	= &s5p_fimc_ioctl_ops;
> +	vfd->minor	= -1;
> +	vfd->release	= video_device_release;
> +
> +	snprintf(vfd->name, sizeof(vfd->name), "%s%d", MODULE_NAME, dev->id);
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> +	if (ret) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
> +		goto err_rel_vdev;
> +	}
> +	video_set_drvdata(vfd, dev);
> +	dev->vfd = vfd;
> +	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
> +		vfd->num);
> +
> +	platform_set_drvdata(pdev, dev);
> +	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
> +	if (IS_ERR(dev->m2m_dev)) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
> +		ret = PTR_ERR(dev->m2m_dev);
> +		goto err_m2m;
> +	}
> +
> +	s5p_fimc_en_lastirq(dev, 1);
> +
> +	dev_dbg(&pdev->dev, "installation successful\n");
> +	return 0;
> +
> +err_m2m:
> +	video_unregister_device(dev->vfd);
> +err_rel_vdev:
> +	video_device_release(vfd);
> +err_unreg_dev:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +err_irq:
> +	free_irq(dev->irq, dev);
> +err_clk:
> +	clk_disable(dev->clock);
> +	clk_put(dev->clock);
> +err_clk2:
> +	clk_put(srclk);
> +err_regs_unmap:
> +	iounmap(dev->regs);
> +err_req_region:
> +	release_resource(dev->regs_res);
> +	kfree(dev->regs_res);
> +err_info:
> +	dev_err(&pdev->dev, "failed to install\n");
> +	return ret;
> +}
> +
> +static int __devexit s5p_fimc_remove(struct platform_device *pdev)
> +{
> +	struct fimc_dev *dev =
> +		(struct fimc_dev *)platform_get_drvdata(pdev);

no need to cast.

> +	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
> +
> +	free_irq(dev->irq, dev);
> +
> +	s5p_fimc_reset(dev);
> +	v4l2_m2m_release(dev->m2m_dev);
> +	video_unregister_device(dev->vfd);
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +
> +	iounmap(dev->regs);
> +	release_resource(dev->regs_res);
> +	kfree(dev->regs_res);
> +	kfree(dev);
> +	return 0;
> +}
> +
> +static int s5p_fimc_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_fimc_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops s5p_fimc_pm_ops = {
> +	.suspend = s5p_fimc_suspend,
> +	.resume = s5p_fimc_resume,
> +};


given these are null ops, is this really a good idea including
the pm ops?

> +static struct platform_driver s5p_fimc_driver = {
> +	.probe = s5p_fimc_probe,
> +	.remove = __devexit_p(s5p_fimc_remove),
> +	.driver = {
> +		.name = "s5p-fimc",
> +		.owner = THIS_MODULE,
> +		.pm = &s5p_fimc_pm_ops
> +	}
> +};
> +
> +static char banner[] __initdata = KERN_INFO \
> +	"S5PC Camera Interface V4L2 Driver, (c) 2010 Samsung Electronics\n";
> +
> +static int __init s5p_fimc_init(void)
> +{
> +	u32 ret;
> +	printk(banner);
> +
> +	ret = platform_driver_register(&s5p_fimc_driver);
> +	if (ret != 0) {
> +		printk(KERN_ERR "FIMC platform driver register failed\n");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void __exit s5p_fimc_exit(void)
> +{
> +	platform_driver_unregister(&s5p_fimc_driver);
> +}
> +
> +module_init(s5p_fimc_init);
> +module_exit(s5p_fimc_exit);
> +
> +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
> +MODULE_DESCRIPTION("S5PV210 FIMC (video postprocessor) driver");
> +MODULE_LICENSE("GPL");
> +
> diff --git a/drivers/media/video/samsung-fimc/s5p_fimc.h b/drivers/media/video/samsung-fimc/s5p_fimc.h
> new file mode 100644
> index 0000000..f4485a8
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/s5p_fimc.h
> @@ -0,0 +1,331 @@
> +/*
> + * drivers/media/video/samsung-fimc/s5p_fimc.h
> + *
> + * Copyright (c) 2010 Samsung Electronics
> + *
> + * Sylwester Nawrocki, s.nawrocki at samsung.com
> + *
> + * 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 S5P_FIMC_H_
> +#define S5P_FIMC_H_
> +
> +#include <linux/types.h>
> +#include <plat/fimc.h>
> +#include <media/videobuf-core.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <linux/videodev2.h>
> +
> +
> +#define err(fmt, args...) \
> +	printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
> +
> +#ifdef DEBUG
> +#define dbg(fmt, args...) \
> +	printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
> +#else
> +#define dbg(fmt, args...)
> +#endif
> +
> +#define MODULE_NAME		"s5p-fimc"
> +#define MAX_FIMC_DEVICES	3
> +#define S5P_FIMC_MAX_FRAMES	4
> +#define DMA_MIN_SIZE		16
> +#define SCALER_MAX_HRATIO	64
> +#define SCALER_MAX_VRATIO	64
> +#define PIX_ALIGN_MASK		(DMA_MIN_SIZE - 1)
> +
> +#define	S5P_FIMC_CHANGED_NONE	0
> +#define	S5P_FIMC_PARAMS		(1 << 0)
> +#define	S5P_FIMC_SRC_ADDR	(1 << 1)
> +#define	S5P_FIMC_DST_ADDR	(1 << 2)
> +
> +
> +enum fimc_datapath {
> +	S5P_FIMC_ITU_CAM_A,
> +	S5P_FIMC_ITU_CAM_B,
> +	S5P_FIMC_MIPI_CAM,
> +	S5P_FIMC_DMA,
> +	S5P_FIMC_LCDFIFO,
> +	S5P_FIMC_WRITEBACK
> +};
> +
> +enum fimc_color_fmt {
> +	S5P_FIMC_RGB565,
> +	S5P_FIMC_RGB666,
> +	S5P_FIMC_RGB888,
> +	S5P_FIMC_YCBCR420,
> +	S5P_FIMC_YCBCR422,
> +	S5P_FIMC_YCBYCR422,
> +	S5P_FIMC_YCRYCB422,
> +	S5P_FIMC_CBYCRY422,
> +	S5P_FIMC_CRYCBY422,
> +	S5P_FIMC_RGB30_LOCAL,
> +	S5P_FIMC_YCBCR444_LOCAL,
> +	S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL,
> +	S5P_FIMC_COLOR_MASK = 0x0F,
> +};
> +
> +/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats */
> +#define	S5P_FIMC_OUT_CRYCBY	S5P_CIOCTRL_ORDER422_CRYCBY
> +#define	S5P_FIMC_OUT_CBYCRY	S5P_CIOCTRL_ORDER422_YCRYCB
> +#define	S5P_FIMC_OUT_YCRYCB	S5P_CIOCTRL_ORDER422_CBYCRY
> +#define	S5P_FIMC_OUT_YCBYCR	S5P_CIOCTRL_ORDER422_YCBYCR
> +
> +/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats */
> +#define	S5P_FIMC_IN_CRYCBY	S5P_MSCTRL_ORDER422_CRYCBY
> +#define	S5P_FIMC_IN_CBYCRY	S5P_MSCTRL_ORDER422_YCRYCB
> +#define	S5P_FIMC_IN_YCRYCB	S5P_MSCTRL_ORDER422_CBYCRY
> +#define	S5P_FIMC_IN_YCBYCR	S5P_MSCTRL_ORDER422_YCBYCR
> +
> +/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats */
> +#define	S5P_FIMC_LSB_CRCB	S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
> +
> +/* The embedded image effect selection */
> +#define	S5P_FIMC_EFFECT_ORIGINAL	S5P_CIIMGEFF_FIN_BYPASS
> +#define	S5P_FIMC_EFFECT_ARBITRARY	S5P_CIIMGEFF_FIN_ARBITRARY
> +#define	S5P_FIMC_EFFECT_NEGATIVE	S5P_CIIMGEFF_FIN_NEGATIVE
> +#define	S5P_FIMC_EFFECT_ARTFREEZE	S5P_CIIMGEFF_FIN_ARTFREEZE
> +#define	S5P_FIMC_EFFECT_EMBOSSING	S5P_CIIMGEFF_FIN_EMBOSSING
> +#define	S5P_FIMC_EFFECT_SIKHOUETTE	S5P_CIIMGEFF_FIN_SILHOUETTE
> +
> +
> +/* Definitions for flags field in struct fimc_ctx */
> +#define	IN_DMA_ACCESS_LINEAR		0	/* default */
> +#define	IN_DMA_ACCESS_TILED		(1 << 0)
> +#define	OUT_DMA_ACCESS_LINEAR		(0 << 1)
> +#define	OUT_DMA_ACCESS_TILED		(1 << 1)
> +#define	SCAN_MODE_PROGRESSIVE		(0 << 2)
> +#define	SCAN_MODE_INTERLACED		(1 << 2)
> +/* YCbCr data dynamic range selection for the RGB <-> YCBCR color space
> + * conversion. Y/Cb/Cr (0 ~ 255) : Wide default */
> +#define	S5P_FIMC_COLOR_RANGE_WIDE	(0 << 3)
> +/* Y (16 ~ 235), Cb/Cr (16 ~ 240) : Narrow */
> +#define	S5P_FIMC_COLOR_RANGE_NARROW	(1 << 3)
> +
> +#define	FLIP_ORIGINAL	0
> +#define	FLIP_X_AXIS	0x01
> +#define	FLIP_Y_AXIS	0x02
> +#define	FLIP_XY_AXIS	(FLIP_X_AXIS | FLIP_Y_AXIS)
> +
> +/**
> + * struct fimc_dma_offset
> + * @y_h:	y value horizontal offset
> + * @y_v:	y value vertical offset
> + * @cb_h:	cb value horizontal offset
> + * @cb_v:	cb value vertical offset
> + * @cr_h:	cr value horizontal offset
> + * @cr_v:	cr value vertical offset
> + */
> +struct fimc_dma_offset {
> +	int	y_h;
> +	int	y_v;
> +	int	cb_h;
> +	int	cb_v;
> +	int	cr_h;
> +	int	cr_v;
> +};
> +
> +/**
> + * struct s5p_fimc_effect
> + * @type:	effect type
> + * @pat_cb:	cr value when type is "arbitrary"
> + * @pat_cr:	cr value when type is "arbitrary"
> + */
> +struct fimc_effect {
> +	u32	type;
> +	u8	pat_cb;
> +	u8	pat_cr;
> +};
> +
> +/**
> + * struct fimc_scaler
> + * @enabled:		flag indicating whether the scaler is used or not
> + * @hfactor:		horizontal shift factor
> + * @vfactor:		vertical shift factor
> + * @pre_hratio:		prescaler horizontal ratio
> + * @pre_vratio:		prescaler vertical ratio
> + * @pre_dst_width:	prescaler destination width
> + * @pre_dst_height:	pre-scaler destination height
> + * @scaleup_h:		flag indicating scaling up horizontally
> + * @scaleup_v:		flag indicating scaling up vertically
> + * @main_hratio:	main scaler horizontal ratio
> + * @main_vratio:	main scaler vertical ratio
> + * @real_width:		src_width - offset
> + * @real_height:	src_height - offset
> + * @copy_mode:		flag indicating one to one mode, i.e. no scaling
> + *			and color format conversion
> + */
> +struct fimc_scaler {
> +	u32	enabled;
> +	u32	hfactor;
> +	u32	vfactor;
> +	u32	pre_hratio;
> +	u32	pre_vratio;
> +	u32	pre_dst_width;
> +	u32	pre_dst_height;
> +	u32	scaleup_h;
> +	u32	scaleup_v;
> +	u32	main_hratio;
> +	u32	main_vratio;
> +	u32	real_width;
> +	u32	real_height;
> +	u32	copy_mode;
> +};
> +
> +/**
> + * struct fimc_frame - input/output frame format properties
> + * @f_width:	image full width (virtual screen size)
> + * @f_height:	image full height (virtual screen size)
> + * @o_width:	original image width as set by S_FMT
> + * @o_height:	original image height as set by S_FMT
> + * @offs_h:	image horizontal pixel offset
> + * @offs_v:	image vertical pixel offset
> + * @width:	image pixel width
> + * @height:	image pixel weight
> + * @addr_y:	RGB/Y physical address
> + * @addr_cb:	CB physical address
> + * @addr_cr:	CR physical addres
> + * @buf_cnt:	number of buffers depending on a color format
> + * @size:	image size in bytes
> + * @color:	color format
> + * @dma_offset:	DMA offset in bytes
> + */
> +struct fimc_frame {
> +	u32	f_width;
> +	u32	f_height;
> +	u32	o_width;
> +	u32	o_height;
> +	u32	offs_h;
> +	u32	offs_v;
> +	u32	width;
> +	u32	height;
> +	u32	addr_y;
> +	u32	addr_cb;
> +	u32	addr_cr;
> +	u32	size;
> +	struct fimc_dma_offset	dma_offset;
> +	struct s5p_fimc_fmt	*fmt;
> +};
> +
> +
> +struct fimc_vid_buffer {
> +	struct videobuf_buffer	vb;
> +	int	index;
> +};
> +
> +struct s5p_fimc_fmt {
> +	char	*name;
> +	u32	fourcc;
> +	u32	color;
> +	u32	depth;
> +	/* Number of data planes used */
> +	u16	buff_cnt;
> +	u16	planes_cnt;
> +};
> +
> +struct fimc_ctx;
> +
> +/**
> + * struct fimc_subdev: abstraction for a FIMC entity
> + * @pdev:	a pointer to the FIMC platform device
> + * @pldata:	a pointer to the device platform data
> + * @id:		the FIMC device index (0..2)
> + * @clock:
> + * @regs:
> + * @regs_res:
> + * @mem_res:
> + * @irq:	interrupt number of the FIMC subdevice
> + * @irqlock:	spinlock protecting videbuffer queue
> + * @timer:
> + * @vfd:
> + * @v4l2_dev:
> + * @m2m_dev:
> + */
> +struct fimc_dev {
> +	struct platform_device		*pdev;
> +	struct s5p_platform_fimc	*pldata;
> +	int				id;
> +	struct clk			*clock;
> +	void __iomem			*regs;
> +	struct resource		*regs_res;
> +	struct resource		*mem_res;
> +	int				irq;
> +	spinlock_t			irqlock;
> +	struct video_device		*vfd;
> +	struct v4l2_device		v4l2_dev;
> +	struct v4l2_m2m_dev		*m2m_dev;
> +};
> +
> +
> +/**
> + * fimc_ctx - the device context data structure
> + * @lock:		mutex protecting the context data
> + * @s_frame:		source frame properties
> + * @d_frame:		destination frame properties
> + * @osd:		osd (output) window parameters
> + * @out_order_1p:	output 1-plane YCBCR order
> + * @out_order_2p:	output 2-plane YCBCR order
> + * @in_order_1p		input 1-plane YCBCR order
> + * @in_order_2p:	input 2-plane YCBCR order
> + * @in_path:		input mode
> + * @out_path:		output mode
> + * @scaler:		image scaler properties
> + * @effect:		image effect
> + * @rotation:		image clockwise rotation in degrees
> + * @flip:		image flip mode
> + * @flags:		device state flags
> + * @updated:		flags variable to keep track of user configurable
> + *			parameters (ioctl)
> + * @fimc_dev:		fimc device related to the context
> + * @m2m_ctx:		memory-to-memory device context
> + */
> +struct fimc_ctx {
> +	spinlock_t		slock;
> +	struct mutex		lock;
> +	struct fimc_frame	s_frame;
> +	struct fimc_frame	d_frame;
> +	u32			out_order_1p;
> +	u32			out_order_2p;
> +	u32			in_order_1p;
> +	u32			in_order_2p;
> +	enum fimc_datapath	in_path;
> +	enum fimc_datapath	out_path;
> +	struct fimc_scaler	scaler;
> +	struct fimc_effect	effect;
> +	int			rotation;
> +	u32			flip;
> +	u32			flags;
> +	u32			updated;
> +	struct fimc_dev		*fimc_dev;
> +	struct v4l2_m2m_ctx	*m2m_ctx;
> +};
> +
> +int s5p_fimc_set_scaler_info(struct fimc_ctx *ctx);
> +int s5p_fimc_check_fifo(struct fimc_dev *dev);
> +void s5p_fimc_reset(struct fimc_dev *dev);
> +void s5p_fimc_set_target_format(struct fimc_ctx *ctx);
> +void s5p_fimc_set_out_dma(struct fimc_ctx *ctx);
> +void s5p_fimc_en_lastirq(struct fimc_dev *dev, int enable);
> +void s5p_fimc_en_irq(struct fimc_dev *dev, int enable);
> +void s5p_fimc_en_autoload(struct fimc_ctx *ctx, int enable);
> +void s5p_fimc_set_prescaler(struct fimc_ctx *ctx);
> +void s5p_fimc_set_scaler(struct fimc_ctx *ctx);
> +void s5p_fimc_start_scaler(struct fimc_ctx *ctx);
> +void s5p_fimc_stop_scaler(struct fimc_ctx *ctx);
> +void s5p_fimc_en_capture(struct fimc_ctx *ctx);
> +void s5p_fimc_dis_capture(struct fimc_ctx *ctx);
> +void s5p_fimc_set_effect(struct fimc_ctx *ctx);
> +void s5p_fimc_set_in_dma(struct fimc_ctx *ctx);
> +void s5p_fimc_start_in_dma(struct fimc_dev *dev);
> +void s5p_fimc_stop_in_dma(struct fimc_dev *dev);
> +void s5p_fimc_set_input_path(struct fimc_ctx *ctx);
> +void s5p_fimc_set_output_path(struct fimc_ctx *ctx);
> +void s5p_fimc_set_input_addr(struct fimc_ctx *ctx);
> +void s5p_fimc_set_output_addr(struct fimc_ctx *ctx);
> +
> +#endif /* S5P_FIMC_H_ */
> diff --git a/drivers/media/video/samsung-fimc/s5p_fimc_reg.c b/drivers/media/video/samsung-fimc/s5p_fimc_reg.c
> new file mode 100644
> index 0000000..a6f719e
> --- /dev/null
> +++ b/drivers/media/video/samsung-fimc/s5p_fimc_reg.c
> @@ -0,0 +1,559 @@
> +/*
> + * Register interface file for Samsung Camera Interface (FIMC) driver
> + *
> + * Copyright (c) 2010 Samsung Electronics
> + *
> + * Sylwester Nawrocki, s.nawrocki at samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <mach/map.h>
> +#include <plat/regs-fimc.h>
> +#include <plat/fimc.h>
> +#include "s5p_fimc.h"
> +
> +
> +int s5p_fimc_check_fifo(struct fimc_dev *ctrl)
> +{
> +	u32 cfg, status;
> +
> +	status = readl(ctrl->regs + S5P_CISTATUS);
> +
> +	if ((S5P_CISTATUS_OVFIY | S5P_CISTATUS_OVFICB | S5P_CISTATUS_OVFICR)
> +		& status) {
> +		cfg = readl(ctrl->regs + S5P_CIWDOFST);
> +		cfg |= (S5P_CIWDOFST_CLROVFIY | S5P_CIWDOFST_CLROVFICB
> +			| S5P_CIWDOFST_CLROVFICR);
> +		writel(cfg, ctrl->regs + S5P_CIWDOFST);
> +
> +		cfg = readl(ctrl->regs + S5P_CIWDOFST);
> +		cfg &= ~(S5P_CIWDOFST_CLROVFIY | S5P_CIWDOFST_CLROVFICB
> +			| S5P_CIWDOFST_CLROVFICR);
> +		writel(cfg, ctrl->regs + S5P_CIWDOFST);
> +	}
> +
> +	return 0;
> +}
> +
> +void s5p_fimc_reset(struct fimc_dev *dev)
> +{
> +	u32 cfg;
> +
> +	cfg = readl(dev->regs + S5P_CISRCFMT);
> +	cfg |= S5P_CISRCFMT_ITU601_8BIT;
> +	writel(cfg, dev->regs + S5P_CISRCFMT);
> +
> +	/* sw reset */
> +	cfg = readl(dev->regs + S5P_CIGCTRL);
> +	cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
> +	writel(cfg, dev->regs + S5P_CIGCTRL);
> +	mdelay(1);
> +
> +	cfg = readl(dev->regs + S5P_CIGCTRL);
> +	cfg &= ~S5P_CIGCTRL_SWRST;
> +	writel(cfg, dev->regs + S5P_CIGCTRL);
> +
> +}
> +
> +static void s5p_fimc_set_rot90(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CITRGFMT);
> +
> +	cfg &= ~(S5P_CITRGFMT_INROT90_CLOCKWISE |
> +		S5P_CITRGFMT_OUTROT90_CLOCKWISE);
> +
> +	/* The input and output rotator cannot work simultaneously,
> +	in input DMA mode only the output rotator shall be used. */
> +	if (ctx->out_path == S5P_FIMC_LCDFIFO)
> +		cfg |= S5P_CITRGFMT_INROT90_CLOCKWISE;
> +	else
> +		cfg |= S5P_CITRGFMT_OUTROT90_CLOCKWISE;
> +
> +	writel(cfg, dev->regs + S5P_CITRGFMT);
> +}
> +
> +static u32 s5p_fimc_get_in_flip(struct fimc_ctx *ctx)
> +{
> +	u32 flip = S5P_MSCTRL_FLIP_NORMAL;
> +
> +	switch(ctx->flip) {
> +	case FLIP_X_AXIS:
> +		flip = S5P_MSCTRL_FLIP_X_MIRROR;
> +		break;
> +	case FLIP_Y_AXIS:
> +		flip = S5P_MSCTRL_FLIP_Y_MIRROR;
> +		break;
> +	case FLIP_XY_AXIS:
> +		flip = S5P_MSCTRL_FLIP_180;
> +		break;
> +	}
> +
> +	return flip;
> +}
> +
> +static u32 s5p_fimc_get_target_flip(struct fimc_ctx *ctx)
> +{
> +	u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
> +
> +	switch(ctx->flip) {
> +	case FLIP_X_AXIS:
> +		flip = S5P_CITRGFMT_FLIP_X_MIRROR;
> +		break;
> +	case FLIP_Y_AXIS:
> +		flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
> +		break;
> +	case FLIP_XY_AXIS:
> +		flip = S5P_CITRGFMT_FLIP_180;
> +		break;
> +	}
> +	return flip;
> +}
> +
> +void s5p_fimc_set_target_format(struct fimc_ctx *ctx)
> +{
> +	u32 cfg = 0;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->d_frame;
> +
> +	dbg("w= %d, h= %d color: %d", frame->width,
> +		frame->height, frame->fmt->color);
> +
> +	switch (frame->fmt->color) {
> +	case S5P_FIMC_RGB565:
> +	case S5P_FIMC_RGB666:
> +	case S5P_FIMC_RGB888:
> +		cfg |= S5P_CITRGFMT_OUTFORMAT_RGB;
> +		break;
> +	case S5P_FIMC_YCBCR420:
> +		cfg |= S5P_CITRGFMT_OUTFORMAT_YCBCR420;
> +		break;
> +	case S5P_FIMC_YCBYCR422:
> +	case S5P_FIMC_YCRYCB422:
> +	case S5P_FIMC_CBYCRY422:
> +	case S5P_FIMC_CRYCBY422:
> +		if (frame->fmt->planes_cnt == 1)
> +			cfg |= S5P_CITRGFMT_OUTFORMAT_YCBCR422_1PLANE;
> +		else
> +			cfg |= S5P_CITRGFMT_OUTFORMAT_YCBCR422;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	cfg |= S5P_CITRGFMT_TARGETHSIZE(frame->width);
> +	cfg |= S5P_CITRGFMT_TARGETVSIZE(frame->height);
> +	cfg |= s5p_fimc_get_target_flip(ctx);
> +	writel(cfg, dev->regs + S5P_CITRGFMT);
> +
> +	if (90 == ctx->rotation || 270 == ctx->rotation)
> +		s5p_fimc_set_rot90(ctx);
> +
> +	cfg = S5P_CITAREA_TARGET_AREA(frame->width * frame->height);
> +	writel(cfg, dev->regs + S5P_CITAREA);
> +}
> +
> +static void s5p_fimc_set_out_dma_size(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->d_frame;
> +	u32 cfg = 0;
> +
> +	dbg("h= 0x%X  w= 0x%X", frame->f_width, frame->f_height);
> +
> +	if (90 == ctx->rotation || 270 == ctx->rotation) {
> +		cfg |= S5P_ORGOSIZE_HORIZONTAL(frame->f_height);
> +		cfg |= S5P_ORGOSIZE_VERTICAL(frame->f_width);
> +	} else {
> +		cfg |= S5P_ORGOSIZE_HORIZONTAL(frame->f_width);
> +		cfg |= S5P_ORGOSIZE_VERTICAL(frame->f_height);
> +	}
> +	dbg("ORGOSIZE: 0x%X", cfg);
> +	writel(cfg, dev->regs + S5P_ORGOSIZE);
> +}
> +
> +void s5p_fimc_set_out_dma(struct fimc_ctx *ctx)
> +{
> +	u32 cfg;
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->d_frame;
> +	struct fimc_dma_offset *offset = &frame->dma_offset;
> +
> +	/* input dma offsets */
> +	cfg = 0;
> +	cfg |= S5P_CIOYOFF_HORIZONTAL(offset->y_h);
> +	cfg |= S5P_CIOYOFF_VERTICAL(offset->y_v);
> +	writel(cfg, dev->regs + S5P_CIOYOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIOCBOFF_HORIZONTAL(offset->cb_h);
> +	cfg |= S5P_CIOCBOFF_VERTICAL(offset->cb_v);
> +	writel(cfg, dev->regs + S5P_CIOCBOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIOCROFF_HORIZONTAL(offset->cr_h);
> +	cfg |= S5P_CIOCROFF_VERTICAL(offset->cr_v);
> +	writel(cfg, dev->regs + S5P_CIOCROFF);
> +
> +	s5p_fimc_set_out_dma_size(ctx);
> +
> +	/* configure chroma components order */
> +	cfg = readl(dev->regs + S5P_CIOCTRL);
> +
> +	cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
> +		 S5P_CIOCTRL_YCBCR_PLANE_MASK);
> +
> +	if (frame->fmt->planes_cnt == 1)
> +		cfg |= ctx->out_order_1p;
> +	else if (frame->fmt->planes_cnt == 2)
> +		cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
> +	else if (frame->fmt->planes_cnt == 3)
> +		cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
> +
> +	writel(cfg, dev->regs + S5P_CIOCTRL);
> +}
> +
> +void s5p_fimc_en_autoload(struct fimc_ctx *ctx, int enable)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_ORGISIZE);
> +	if (enable)
> +		cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_ENABLE;
> +	else
> +		cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_ENABLE;
> +	writel(cfg, dev->regs + S5P_ORGISIZE);
> +}
> +
> +void s5p_fimc_en_lastirq(struct fimc_dev *dev, int enable)
> +{
> +	unsigned long flags;
> +	u32 cfg;
> +	local_irq_save(flags);
> +
> +	cfg = readl(dev->regs + S5P_CIOCTRL);
> +	if (enable)
> +		cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
> +	else
> +		cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
> +	writel(cfg, dev->regs + S5P_CIOCTRL);
> +
> +	local_irq_restore(flags);
> +}
> +
> +void s5p_fimc_set_prescaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev =  ctx->fimc_dev;
> +	struct fimc_scaler *sc = &ctx->scaler;
> +	u32 cfg = 0, shfactor;
> +
> +	shfactor = 10 - (sc->hfactor + sc->vfactor);
> +
> +	cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
> +	cfg |= S5P_CISCPRERATIO_PREHORRATIO(sc->pre_hratio);
> +	cfg |= S5P_CISCPRERATIO_PREVERRATIO(sc->pre_vratio);
> +
> +	writel(cfg, dev->regs + S5P_CISCPRERATIO);
> +
> +	cfg = 0;
> +	cfg |= S5P_CISCPREDST_PREDSTWIDTH(sc->pre_dst_width);
> +	cfg |= S5P_CISCPREDST_PREDSTHEIGHT(sc->pre_dst_height);
> +
> +	writel(cfg, dev->regs + S5P_CISCPREDST);
> +}
> +
> +void s5p_fimc_set_scaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_scaler *sc = &ctx->scaler;
> +	struct fimc_frame *src_frame = &ctx->s_frame;
> +	struct fimc_frame *dst_frame = &ctx->d_frame;
> +	u32 cfg;
> +
> +	if (ctx->flags & S5P_FIMC_COLOR_RANGE_NARROW)
> +		cfg = (S5P_CISCCTRL_CSCR2Y_NARROW|S5P_CISCCTRL_CSCY2R_NARROW);
> +	else
> +		cfg = (S5P_CISCCTRL_CSCR2Y_WIDE|S5P_CISCCTRL_CSCY2R_WIDE);
> +
> +	if (!sc->enabled)
> +		cfg |= S5P_CISCCTRL_SCALERBYPASS;
> +
> +	if (sc->scaleup_h)
> +		cfg |= S5P_CISCCTRL_SCALEUP_H;
> +
> +	if (sc->scaleup_v)
> +		cfg |= S5P_CISCCTRL_SCALEUP_V;
> +
> +	if (sc->copy_mode)
> +		cfg |= S5P_CISCCTRL_ONE2ONE;
> +
> +
> +	if (ctx->in_path == S5P_FIMC_DMA) {
> +		if (src_frame->fmt->color == S5P_FIMC_RGB565)
> +			cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
> +		else if (src_frame->fmt->color == S5P_FIMC_RGB666)
> +			cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
> +		else if (src_frame->fmt->color == S5P_FIMC_RGB888)
> +			cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
> +	}
> +
> +	if (ctx->out_path == S5P_FIMC_DMA) {
> +		if (dst_frame->fmt->color == S5P_FIMC_RGB565)
> +			cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
> +		else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
> +			cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
> +		else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
> +			cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
> +	} else {
> +		cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
> +
> +		if (ctx->flags&SCAN_MODE_INTERLACED)
> +			cfg |= S5P_CISCCTRL_INTERLACE;
> +		else
> +			cfg |= S5P_CISCCTRL_PROGRESSIVE;
> +	}
> +
> +	dbg("main_hratio= 0x%X  main_vratio= 0x%X",
> +		sc->main_hratio, sc->main_vratio);
> +
> +	cfg |= S5P_CISCCTRL_MAINHORRATIO(sc->main_hratio);
> +	cfg |= S5P_CISCCTRL_MAINVERRATIO(sc->main_vratio);
> +
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_start_scaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
> +
> +	cfg |= S5P_CISCCTRL_SCALERSTART;
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_stop_scaler(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
> +
> +	cfg &= ~S5P_CISCCTRL_SCALERSTART;
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_en_capture(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
> +
> +	if (S5P_FIMC_DMA == ctx->out_path) {
> +		/* one shot mode */
> +		cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN
> +			| S5P_CIIMGCPT_CPT_FRMOD_EN;
> +	} else {
> +		/* freerun */
> +		cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE |
> +			 S5P_CIIMGCPT_CPT_FRMOD_EN);
> +		cfg |= S5P_CIIMGCPT_IMGCPTEN;
> +	}
> +
> +	if (ctx->scaler.enabled)
> +		cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
> +
> +	writel(cfg, dev->regs + S5P_CIIMGCPT);
> +}
> +
> +void s5p_fimc_dis_capture(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
> +	cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
> +	writel(cfg, dev->regs + S5P_CIIMGCPT);
> +}
> +
> +void s5p_fimc_set_effect(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_effect *effect = &ctx->effect;
> +	u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
> +
> +	cfg |= effect->type;
> +
> +	if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
> +		cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
> +		cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
> +	}
> +
> +	writel(cfg, dev->regs + S5P_CIIMGEFF);
> +}
> +
> +static void s5p_fimc_set_in_dma_size(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->s_frame;
> +	u32 cfg_o = 0;
> +	u32 cfg_r = 0;
> +
> +	if (S5P_FIMC_LCDFIFO == ctx->out_path)
> +		cfg_r |=  S5P_CIREAL_ISIZE_AUTOLOAD_ENABLE;
> +
> +	cfg_o |= S5P_ORGISIZE_HORIZONTAL(frame->f_width);
> +	cfg_o |= S5P_ORGISIZE_VERTICAL(frame->f_height);
> +	cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
> +	cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
> +
> +	writel(cfg_o, dev->regs + S5P_ORGISIZE);
> +	writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
> +}
> +
> +void s5p_fimc_set_in_dma(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	struct fimc_frame *frame = &ctx->s_frame;
> +	struct fimc_dma_offset *offset = &frame->dma_offset;
> +	u32 cfg = 0;
> +
> +	/* offsets */
> +	cfg |= S5P_CIIYOFF_HORIZONTAL(offset->y_h);
> +	cfg |= S5P_CIIYOFF_VERTICAL(offset->y_v);
> +	writel(cfg, dev->regs + S5P_CIIYOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIICBOFF_HORIZONTAL(offset->cb_h);
> +	cfg |= S5P_CIICBOFF_VERTICAL(offset->cb_v);
> +	writel(cfg, dev->regs + S5P_CIICBOFF);
> +
> +	cfg = 0;
> +	cfg |= S5P_CIICROFF_HORIZONTAL(offset->cr_h);
> +	cfg |= S5P_CIICROFF_VERTICAL(offset->cr_v);
> +	writel(cfg, dev->regs + S5P_CIICROFF);
> +
> +	/* original & real size */
> +	s5p_fimc_set_in_dma_size(ctx);
> +
> +	/* autoload is used currently only in FIFO mode */
> +	s5p_fimc_en_autoload(ctx, S5P_FIMC_LCDFIFO == ctx->out_path);
> +
> +	/* input dma set to process single frame only */
> +	cfg = (S5P_MSCTRL_SUCCESSIVE_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
> +
> +	switch (frame->fmt->color) {
> +	case S5P_FIMC_RGB565:
> +	case S5P_FIMC_RGB666:
> +	case S5P_FIMC_RGB888:
> +		cfg |= S5P_MSCTRL_INFORMAT_RGB;
> +		break;
> +	case S5P_FIMC_YCBCR420:
> +		cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
> +
> +		if (frame->fmt->planes_cnt == 2)
> +			cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
> +		else
> +			cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
> +
> +		break;
> +	case S5P_FIMC_YCBYCR422:
> +	case S5P_FIMC_YCRYCB422:
> +	case S5P_FIMC_CBYCRY422:
> +	case S5P_FIMC_CRYCBY422:
> +		if (frame->fmt->planes_cnt == 1) {
> +			cfg |= ctx->in_order_1p
> +				| S5P_MSCTRL_INFORMAT_YCBCR422_1PLANE;
> +		} else {
> +			cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
> +
> +			if (frame->fmt->planes_cnt == 2)
> +				cfg |= ctx->in_order_2p
> +					| S5P_MSCTRL_C_INT_IN_2PLANE;
> +			else
> +				cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* input DMA flip mode */
> +	if (ctx->out_path == S5P_FIMC_LCDFIFO)
> +		cfg |= s5p_fimc_get_in_flip(ctx);;
> +
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +
> +	/* in/out DMA linear/tiled mode */
> +	cfg = readl(dev->regs + S5P_CIDMAPARAM);
> +	cfg &= ~(S5P_CIDMAPARAM_R_MODE_64X32 | S5P_CIDMAPARAM_W_MODE_64X32);
> +
> +	writel(cfg, dev->regs + S5P_CIDMAPARAM);
> +}
> +
> +void s5p_fimc_start_in_dma(struct fimc_dev *dev)
> +{
> +	u32 cfg = readl(dev->regs + S5P_MSCTRL);
> +	cfg |= S5P_MSCTRL_ENVID;
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +}
> +
> +void s5p_fimc_stop_in_dma(struct fimc_dev *dev)
> +{
> +	u32 cfg = readl(dev->regs + S5P_MSCTRL);
> +	cfg &= ~S5P_MSCTRL_ENVID;
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +}
> +
> +void s5p_fimc_set_input_path(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_MSCTRL);
> +	cfg &= ~S5P_MSCTRL_INPUT_MASK;
> +
> +	if (ctx->in_path == S5P_FIMC_DMA)
> +		cfg |= S5P_MSCTRL_INPUT_MEMORY;
> +	else
> +		cfg |= S5P_MSCTRL_INPUT_EXTCAM;
> +
> +	writel(cfg, dev->regs + S5P_MSCTRL);
> +}
> +
> +void s5p_fimc_set_output_path(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = readl(dev->regs + S5P_CISCCTRL);
> +	cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
> +
> +	if (ctx->out_path == S5P_FIMC_LCDFIFO)
> +		cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
> +
> +	writel(cfg, dev->regs + S5P_CISCCTRL);
> +}
> +
> +void s5p_fimc_set_input_addr(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	u32 cfg = 0;
> +
> +	cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
> +	cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DISABLE;
> +	writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
> +
> +	writel(ctx->s_frame.addr_y, dev->regs + S5P_CIIYSA0);
> +	writel(ctx->s_frame.addr_cb, dev->regs + S5P_CIICBSA0);
> +	writel(ctx->s_frame.addr_cr, dev->regs + S5P_CIICRSA0);
> +
> +	cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DISABLE;
> +	writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
> +}
> +
> +void s5p_fimc_set_output_addr(struct fimc_ctx *ctx)
> +{
> +	struct fimc_dev *dev = ctx->fimc_dev;
> +	int i = 0;
> +	/* each of the four output register sets points to the same buffer */
> +	for (i = 0; i < S5P_FIMC_MAX_FRAMES; i++) {
> +		writel(ctx->d_frame.addr_y, dev->regs + S5P_CIOYSA(i));
> +		writel(ctx->d_frame.addr_cb, dev->regs + S5P_CIOCBSA(i));
> +		writel(ctx->d_frame.addr_cr, dev->regs + S5P_CIOCRSA(i));
> +	}
> +}
> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
> index 3793d16..ada05c5 100644
> --- a/include/linux/videodev2.h
> +++ b/include/linux/videodev2.h
> @@ -287,6 +287,7 @@ struct v4l2_pix_format {
>  #define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
>  #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
>  #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
> +#define V4L2_PIX_FMT_RGB666  v4l2_fourcc('R', 'G', 'B', 'H') /* 18  RGB-6-6-6	  */
>  #define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
>  #define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
>  #define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
> -- 
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
-- 
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.

  reply	other threads:[~2010-05-06  6:25 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-04-19 10:29 [PATCH/RFC v1 0/3] [ARM] Add Samsung S5P camera interface driver Sylwester Nawrocki
2010-04-19 10:29 ` Sylwester Nawrocki
2010-04-19 10:29 ` Sylwester Nawrocki
2010-04-19 10:29 ` [PATCH v1 1/3] ARM: S5P: Add FIMC driver platform helpers Sylwester Nawrocki
2010-04-19 10:29   ` Sylwester Nawrocki
2010-04-19 10:29 ` [PATCH v1 2/3] ARM: S5PC100: " Sylwester Nawrocki
2010-04-19 10:29   ` Sylwester Nawrocki
2010-04-19 10:29   ` Sylwester Nawrocki
2010-04-19 10:30 ` [PATCH v1 3/3] ARM: S5P: Add Camera interface (video postprocessor) driver Sylwester Nawrocki
2010-04-19 10:30   ` Sylwester Nawrocki
2010-05-06  6:25   ` Ben Dooks [this message]
2010-05-06  6:25     ` Ben Dooks
2010-05-07  9:36     ` Sylwester Nawrocki
2010-05-07  9:36       ` Sylwester Nawrocki
2010-05-07  9:36       ` Sylwester Nawrocki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20100506062555.GP26401@trinity.fluff.org \
    --to=ben-linux@fluff.org \
    --cc=kyungmin.park@samsung.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=m.szyprowski@samsung.com \
    --cc=p.osciak@samsung.com \
    --cc=s.nawrocki@samsung.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.