diff mbox

[RFC,v3,4/5] drm/exynos: add rotator ipp driver

Message ID 1355297682-7990-5-git-send-email-chulspro.kim@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Eunchul Kim Dec. 12, 2012, 7:34 a.m. UTC
Rotator supports rotation/crop/flip and input/output DMA operations
Rotator ipp driver supports 90,180,270 degree rotaion and vertical, horizontal flip.
and has some limitations(source and destination format have to be same, no scaler)

Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
---
 drivers/gpu/drm/exynos/Kconfig              |    7 +
 drivers/gpu/drm/exynos/Makefile             |    1 +
 drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
 drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
 drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829 +++++++++++++++++++++++++++
 drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
 drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
 7 files changed, 939 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
 create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
 create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h

Comments

Inki Dae Dec. 12, 2012, 8:29 a.m. UTC | #1
> -----Original Message-----
> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> Sent: Wednesday, December 12, 2012 4:35 PM
> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com; jmock.shin@samsung.com;
> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
> chulspro.kim@samsung.com
> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> 
> Rotator supports rotation/crop/flip and input/output DMA operations
> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
> horizontal flip.
> and has some limitations(source and destination format have to be same, no
> scaler)
> 
> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
> ---
>  drivers/gpu/drm/exynos/Kconfig              |    7 +
>  drivers/gpu/drm/exynos/Makefile             |    1 +
>  drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
>  drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
>  drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
> +++++++++++++++++++++++++++
>  drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
>  drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
>  7 files changed, 939 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
>  create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
>  create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
> 
> diff --git a/drivers/gpu/drm/exynos/Kconfig
> b/drivers/gpu/drm/exynos/Kconfig
> index 4915ab6..4860835 100644
> --- a/drivers/gpu/drm/exynos/Kconfig
> +++ b/drivers/gpu/drm/exynos/Kconfig
> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
>  	depends on DRM_EXYNOS_IPP
>  	help
>  	  Choose this option if you want to use Exynos FIMC for DRM.
> +
> +config DRM_EXYNOS_ROTATOR
> +	bool "Exynos DRM Rotator"
> +	depends on DRM_EXYNOS_IPP
> +	help
> +	  Choose this option if you want to use Exynos Rotator for DRM.
> +
> diff --git a/drivers/gpu/drm/exynos/Makefile
> b/drivers/gpu/drm/exynos/Makefile
> index 9710024..3b70668 100644
> --- a/drivers/gpu/drm/exynos/Makefile
> +++ b/drivers/gpu/drm/exynos/Makefile
> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
> exynos_drm_vidi.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+= exynos_drm_rotator.o
> 
>  obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> index 73f02ac..09d884b 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
>  		goto out_fimc;
>  #endif
> 
> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> +	ret = platform_driver_register(&rotator_driver);
> +	if (ret < 0)
> +		goto out_rotator;
> +#endif
> +
>  #ifdef CONFIG_DRM_EXYNOS_IPP
>  	ret = platform_driver_register(&ipp_driver);
>  	if (ret < 0)
> @@ -406,6 +412,11 @@ out_drm:
>  out_ipp:
>  #endif
> 
> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> +	platform_driver_unregister(&rotator_driver);
> +out_rotator:
> +#endif
> +
>  #ifdef CONFIG_DRM_EXYNOS_FIMC
>  	platform_driver_unregister(&fimc_driver);
>  out_fimc:
> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
>  	platform_driver_unregister(&ipp_driver);
>  #endif
> 
> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> +	platform_driver_unregister(&rotator_driver);
> +#endif
> +
>  #ifdef CONFIG_DRM_EXYNOS_FIMC
>  	platform_driver_unregister(&fimc_driver);
>  #endif
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> index 14f9490..a74e37c 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> @@ -353,5 +353,6 @@ extern struct platform_driver
> exynos_drm_common_hdmi_driver;
>  extern struct platform_driver vidi_driver;
>  extern struct platform_driver g2d_driver;
>  extern struct platform_driver fimc_driver;
> +extern struct platform_driver rotator_driver;
>  extern struct platform_driver ipp_driver;
>  #endif
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> new file mode 100644
> index 0000000..121569c
> --- /dev/null
> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> @@ -0,0 +1,829 @@
> +/*
> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> + * Authors:
> + *	YoungJun Cho <yj44.cho@samsung.com>
> + *	Eunchul Kim <chulspro.kim@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 Foundationr
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/exynos_drm.h>
> +#include "regs-rotator.h"
> +#include "exynos_drm.h"
> +#include "exynos_drm_ipp.h"
> +
> +/*
> + * Rotator supports image crop/rotator and input/output DMA operations.
> + * input DMA reads image data from the memory.
> + * output DMA writes image data to memory.
> + */
> +
> +#define get_rot_context(dev)
> 	platform_get_drvdata(to_platform_device(dev))
> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
> +					struct rot_context, ippdrv);
> +#define rot_read(offset)		readl(rot->regs + (offset))
> +#define rot_write(cfg, offset)	writel(cfg, rot->regs + (offset))
> +
> +enum rot_irq_status {
> +	ROT_IRQ_STATUS_COMPLETE	= 8,
> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
> +};
> +
> +/*
> + * A structure of limitation.
> + *
> + * @min_w: minimum width.
> + * @min_h: minimum height.
> + * @max_w: maximum width.
> + * @max_h: maximum height.
> + * @align: align size.
> + */
> +struct rot_limit {
> +	u32	min_w;
> +	u32	min_h;
> +	u32	max_w;
> +	u32	max_h;
> +	u32	align;
> +};
> +
> +/*
> + * A structure of limitation table.
> + *
> + * @ycbcr420_2p: case of YUV.
> + * @rgb888: case of RGB.
> + */
> +struct rot_limit_table {
> +	struct rot_limit	ycbcr420_2p;
> +	struct rot_limit	rgb888;
> +};
> +
> +/*
> + * A structure of rotator context.
> + * @ippdrv: prepare initialization using ippdrv.
> + * @regs_res: register resources.
> + * @regs: memory mapped io registers.
> + * @clock: rotator gate clock.
> + * @limit_tbl: limitation of rotator.
> + * @irq: irq number.
> + * @cur_buf_id: current operation buffer id.
> + * @suspended: suspended state.
> + */
> +struct rot_context {
> +	struct exynos_drm_ippdrv	ippdrv;
> +	struct resource	*regs_res;
> +	void __iomem	*regs;
> +	struct clk	*clock;
> +	struct rot_limit_table	*limit_tbl;
> +	int	irq;
> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
> +	bool	suspended;
> +};
> +
> +static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
> +{
> +	u32 val = rot_read(ROT_CONFIG);
> +
> +	if (enable == true)
> +		val |= ROT_CONFIG_IRQ;
> +	else
> +		val &= ~ROT_CONFIG_IRQ;
> +
> +	rot_write(val, ROT_CONFIG);
> +}
> +
> +static u32 rotator_reg_get_format(struct rot_context *rot)
> +{
> +	u32 val = rot_read(ROT_CONTROL);
> +
> +	val &= ROT_CONTROL_FMT_MASK;
> +
> +	return val;
> +}
> +
> +static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context
> *rot)
> +{
> +	u32 val = rot_read(ROT_STATUS);
> +
> +	val = ROT_STATUS_IRQ(val);
> +
> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
> +		return ROT_IRQ_STATUS_COMPLETE;
> +	else

Remove else.

> +		return ROT_IRQ_STATUS_ILLEGAL;
> +}
> +
> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
> +{
> +	struct rot_context *rot = arg;
> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
> +	struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
> +	enum rot_irq_status irq_status;
> +	u32 val;
> +
> +	/* Get execution result */
> +	irq_status = rotator_reg_get_irq_status(rot);
> +
> +	/* clear status */
> +	val = rot_read(ROT_STATUS);
> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
> +	rot_write(val, ROT_STATUS);
> +
> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
> +		event_work->ippdrv = ippdrv;
> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
> +		queue_work(ippdrv->event_workq,
> +			(struct work_struct *)event_work);
> +	} else
> +		DRM_ERROR("the SFR is set illegally\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32
> *hsize,
> +		u32 *vsize)
> +{
> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
> +	struct rot_limit *limit;
> +	u32 mask, val;
> +
> +	/* Get size limit */
> +	if (fmt == ROT_CONTROL_FMT_RGB888)
> +		limit = &limit_tbl->rgb888;
> +	else
> +		limit = &limit_tbl->ycbcr420_2p;
> +
> +	/* Get mask for rounding to nearest aligned val */
> +	mask = ~((1 << limit->align) - 1);
> +
> +	/* Set aligned width */
> +	val = ROT_ALIGN(*hsize, limit->align, mask);
> +	if (val < limit->min_w)
> +		*hsize = ROT_MIN(limit->min_w, mask);
> +	else if (val > limit->max_w)
> +		*hsize = ROT_MAX(limit->max_w, mask);
> +	else
> +		*hsize = val;
> +
> +	/* Set aligned height */
> +	val = ROT_ALIGN(*vsize, limit->align, mask);
> +	if (val < limit->min_h)
> +		*vsize = ROT_MIN(limit->min_h, mask);
> +	else if (val > limit->max_h)
> +		*vsize = ROT_MAX(limit->max_h, mask);
> +	else
> +		*vsize = val;
> +}
> +
> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	u32 val;
> +
> +	val = rot_read(ROT_CONTROL);
> +	val &= ~ROT_CONTROL_FMT_MASK;
> +
> +	switch (fmt) {
> +	case DRM_FORMAT_NV12:
> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
> +		break;
> +	case DRM_FORMAT_XRGB8888:
> +		val |= ROT_CONTROL_FMT_RGB888;
> +		break;
> +	default:
> +		DRM_ERROR("invalid image format\n");
> +		return -EINVAL;
> +	}
> +
> +	rot_write(val, ROT_CONTROL);
> +
> +	return 0;
> +}
> +
> +static int rotator_src_set_size(struct device *dev, int swap,
> +		struct drm_exynos_pos *pos,
> +		struct drm_exynos_sz *sz)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	u32 fmt, hsize, vsize;
> +	u32 val;
> +
> +	/* Get format */
> +	fmt = rotator_reg_get_format(rot);
> +
> +	/* Align buffer size */
> +	hsize = sz->hsize;
> +	vsize = sz->vsize;
> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> +
> +	/* Set buffer size configuration */
> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> +	rot_write(val, ROT_SRC_BUF_SIZE);
> +
> +	/* Set crop image position configuration */
> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> +	rot_write(val, ROT_SRC_CROP_POS);
> +	val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
> +	rot_write(val, ROT_SRC_CROP_SIZE);
> +
> +	return 0;
> +}
> +
> +static int rotator_src_set_addr(struct device *dev,
> +		struct drm_exynos_ipp_buf_info *buf_info,
> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> +	u32 val, fmt, hsize, vsize;
> +	int i;
> +
> +	/* Set current buf_id */
> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
> +
> +	switch (buf_type) {
> +	case IPP_BUF_ENQUEUE:
> +		/* Set address configuration */
> +		for_each_ipp_planar(i)
> +			addr[i] = buf_info->base[i];

Check NULL.

> +
> +		/* Get format */
> +		fmt = rotator_reg_get_format(rot);
> +
> +		/* Re-set cb planar for NV12 format */
> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
0x00)) {

What is 0x00?

> +
> +			val = rot_read(ROT_SRC_BUF_SIZE);
> +			hsize = ROT_GET_BUF_SIZE_W(val);
> +			vsize = ROT_GET_BUF_SIZE_H(val);
> +
> +			/* Set cb planar */
> +			addr[EXYNOS_DRM_PLANAR_CB] =
> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
> +		}
> +
> +		for_each_ipp_planar(i)
> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
> +		break;
> +	case IPP_BUF_DEQUEUE:
> +		for_each_ipp_planar(i)
> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
> +		break;
> +	default:
> +		/* Nothing to do */
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rotator_dst_set_transf(struct device *dev,
> +		enum drm_exynos_degree degree,
> +		enum drm_exynos_flip flip)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	u32 val;
> +
> +	/* Set transform configuration */
> +	val = rot_read(ROT_CONTROL);
> +	val &= ~ROT_CONTROL_FLIP_MASK;
> +
> +	switch (flip) {
> +	case EXYNOS_DRM_FLIP_VERTICAL:
> +		val |= ROT_CONTROL_FLIP_VERTICAL;
> +		break;
> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
> +		break;
> +	default:
> +		/* Flip None */
> +		break;
> +	}
> +
> +	val &= ~ROT_CONTROL_ROT_MASK;
> +
> +	switch (degree) {
> +	case EXYNOS_DRM_DEGREE_90:
> +		val |= ROT_CONTROL_ROT_90;
> +		break;
> +	case EXYNOS_DRM_DEGREE_180:
> +		val |= ROT_CONTROL_ROT_180;
> +		break;
> +	case EXYNOS_DRM_DEGREE_270:
> +		val |= ROT_CONTROL_ROT_270;
> +		break;
> +	default:
> +		/* Rotation 0 Degree */
> +		break;
> +	}
> +
> +	rot_write(val, ROT_CONTROL);
> +
> +	/* Check degree for setting buffer size swap */
> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
> +		(degree == EXYNOS_DRM_DEGREE_270))
> +		return 1;

Correct return type. This function should return 0 or negative.

> +	else
> +		return 0;

Ditto.

> +}
> +
> +static int rotator_dst_set_size(struct device *dev, int swap,
> +		struct drm_exynos_pos *pos,
> +		struct drm_exynos_sz *sz)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	u32 val, fmt, hsize, vsize;
> +
> +	/* Get format */
> +	fmt = rotator_reg_get_format(rot);

Check if fmt is valid or not.

> +
> +	/* Align buffer size */
> +	hsize = sz->hsize;
> +	vsize = sz->vsize;
> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> +
> +	/* Set buffer size configuration */
> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> +	rot_write(val, ROT_DST_BUF_SIZE);
> +
> +	/* Set crop image position configuration */
> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> +	rot_write(val, ROT_DST_CROP_POS);
> +
> +	return 0;
> +}
> +
> +static int rotator_dst_set_addr(struct device *dev,
> +		struct drm_exynos_ipp_buf_info *buf_info,
> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> +	u32 val, fmt, hsize, vsize;
> +	int i;
> +
> +	/* Set current buf_id */
> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
> +
> +	switch (buf_type) {
> +	case IPP_BUF_ENQUEUE:
> +		/* Set address configuration */
> +		for_each_ipp_planar(i)
> +			addr[i] = buf_info->base[i];
> +
> +		/* Get format */
> +		fmt = rotator_reg_get_format(rot);

Check if fmt is valid or not.

> +
> +		/* Re-set cb planar for NV12 format */
> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
0x00)) {
> +			/* Get buf size */
> +			val = rot_read(ROT_DST_BUF_SIZE);
> +
> +			hsize = ROT_GET_BUF_SIZE_W(val);
> +			vsize = ROT_GET_BUF_SIZE_H(val);
> +
> +			/* Set cb planar */
> +			addr[EXYNOS_DRM_PLANAR_CB] =
> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
> +		}
> +
> +		for_each_ipp_planar(i)
> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
> +		break;
> +	case IPP_BUF_DEQUEUE:
> +		for_each_ipp_planar(i)
> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
> +		break;
> +	default:
> +		/* Nothing to do */
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct exynos_drm_ipp_ops rot_src_ops = {
> +	.set_fmt	=	rotator_src_set_fmt,
> +	.set_size	=	rotator_src_set_size,
> +	.set_addr	=	rotator_src_set_addr,
> +};
> +
> +static struct exynos_drm_ipp_ops rot_dst_ops = {
> +	.set_transf	=	rotator_dst_set_transf,
> +	.set_size	=	rotator_dst_set_size,
> +	.set_addr	=	rotator_dst_set_addr,
> +};
> +
> +static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
> +{
> +	struct drm_exynos_ipp_prop_list *prop_list;
> +
> +	DRM_DEBUG_KMS("%s\n", __func__);
> +
> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
> +	if (!prop_list) {
> +		DRM_ERROR("failed to alloc property list.\n");
> +		return -ENOMEM;
> +	}
> +
> +	/*ToDo fix support function list*/
> +
> +	prop_list->version = 1;
> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
> +				(1 << EXYNOS_DRM_DEGREE_90) |
> +				(1 << EXYNOS_DRM_DEGREE_180) |
> +				(1 << EXYNOS_DRM_DEGREE_270);
> +	prop_list->csc = 0;
> +	prop_list->crop = 0;
> +	prop_list->scale = 0;
> +
> +	ippdrv->prop_list = prop_list;
> +
> +	return 0;
> +}
> +
> +static int rotator_ippdrv_check_property(struct device *dev,
> +		struct drm_exynos_ipp_property *property)
> +{
> +	struct drm_exynos_ipp_config *src_config =
> +
&property->config[EXYNOS_DRM_OPS_SRC];
> +	struct drm_exynos_ipp_config *dst_config =
> +
&property->config[EXYNOS_DRM_OPS_DST];
> +	struct drm_exynos_pos *src_pos = &src_config->pos;
> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
> +	struct drm_exynos_sz *src_sz = &src_config->sz;
> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
> +	bool swap = false;
> +
> +	/* Check format configuration */
> +	if (src_config->fmt != dst_config->fmt) {
> +		DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	switch (src_config->fmt) {
> +	case DRM_FORMAT_XRGB8888:
> +	case DRM_FORMAT_NV12:
> +		/* No problem */
> +		break;
> +	default:
> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
> +		return -EINVAL;
> +	}

Use macro instead of switch-case. this just checks only format type.

> +
> +	/* Check transform configuration */
> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
> +		DRM_DEBUG_KMS("%s:not support source-side rotation\n",
> +								__func__);
> +		return -EINVAL;
> +	}
> +
> +	switch (dst_config->degree) {
> +	case EXYNOS_DRM_DEGREE_90:
> +	case EXYNOS_DRM_DEGREE_270:
> +		swap = true;
> +	case EXYNOS_DRM_DEGREE_0:
> +	case EXYNOS_DRM_DEGREE_180:
> +		/* No problem */
> +		break;
> +	default:
> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
__func__);
> +		return -EINVAL;
> +	}
> +
> +	switch (dst_config->flip) {
> +	case EXYNOS_DRM_FLIP_NONE:
> +	case EXYNOS_DRM_FLIP_VERTICAL:
> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> +		/* No problem */
> +		break;
> +	default:
> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
> +		return -EINVAL;
> +	}

Use macro instead of switch-case. this just checks only flip type.

> +
> +	/* Check size configuration */
> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (swap) {
> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
> +			DRM_DEBUG_KMS("%s:out of destination buffer
bound\n",
> +								__func__);
> +			return -EINVAL;
> +		}
> +
> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
dst_pos->w))
> {
> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
> +								__func__);
> +			return -EINVAL;
> +		}
> +	} else {
> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
> +			DRM_DEBUG_KMS("%s:out of destination buffer
bound\n",
> +								__func__);
> +			return -EINVAL;
> +		}
> +
> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
dst_pos->h))
> {
> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
> +								__func__);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int rotator_ippdrv_start(struct device *dev, enum
> drm_exynos_ipp_cmd cmd)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	u32 val;
> +
> +	if (rot->suspended) {
> +		DRM_ERROR("suspended state\n");
> +		return -EPERM;
> +	}
> +
> +	if (cmd != IPP_CMD_M2M) {
> +		DRM_ERROR("not support cmd: %d\n", cmd);
> +		return -EINVAL;
> +	}
> +
> +	/* Set interrupt enable */
> +	rotator_reg_set_irq(rot, true);
> +
> +	val = rot_read(ROT_CONTROL);
> +	val |= ROT_CONTROL_START;
> +
> +	rot_write(val, ROT_CONTROL);
> +
> +	return 0;
> +}
> +
> +static int __devinit rotator_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct rot_context *rot;
> +	struct resource *res;
> +	struct exynos_drm_ippdrv *ippdrv;
> +	int ret;
> +
> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
> +	if (!rot) {
> +		dev_err(dev, "failed to allocate rot\n");
> +		return -ENOMEM;
> +	}
> +
> +	rot->limit_tbl = (struct rot_limit_table *)
> +				platform_get_device_id(pdev)->driver_data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "failed to find registers\n");
> +		ret = -ENOENT;
> +		goto err_get_resource;
> +	}
> +
> +	rot->regs_res = request_mem_region(res->start, resource_size(res),
> +
dev_name(dev));
> +	if (!rot->regs_res) {
> +		dev_err(dev, "failed to claim register region\n");
> +		ret = -ENOENT;
> +		goto err_get_resource;
> +	}
> +
> +	rot->regs = ioremap(res->start, resource_size(res));
> +	if (!rot->regs) {
> +		dev_err(dev, "failed to map register\n");
> +		ret = -ENXIO;
> +		goto err_ioremap;
> +	}
> +
> +	rot->irq = platform_get_irq(pdev, 0);
> +	if (rot->irq < 0) {
> +		dev_err(dev, "failed to get irq\n");
> +		ret = rot->irq;
> +		goto err_get_irq;
> +	}
> +
> +	ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler,
> +					IRQF_ONESHOT, "drm_rotator", rot);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to request irq\n");
> +		goto err_get_irq;
> +	}
> +
> +	rot->clock = clk_get(dev, "rotator");
> +	if (IS_ERR_OR_NULL(rot->clock)) {
> +		dev_err(dev, "failed to get clock\n");
> +		ret = PTR_ERR(rot->clock);
> +		goto err_clk_get;
> +	}
> +
> +	pm_runtime_enable(dev);
> +
> +	ippdrv = &rot->ippdrv;
> +	ippdrv->dev = dev;
> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
> +	ippdrv->check_property = rotator_ippdrv_check_property;
> +	ippdrv->start = rotator_ippdrv_start;
> +	ret = rotator_init_prop_list(ippdrv);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init property list.\n");
> +		goto err_ippdrv_register;
> +	}
> +
> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
> +
> +	platform_set_drvdata(pdev, rot);
> +
> +	ret = exynos_drm_ippdrv_register(ippdrv);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to register drm rotator device\n");
> +		kfree(ippdrv->prop_list);
> +		goto err_ippdrv_register;
> +	}
> +
> +	dev_info(dev, "The exynos rotator is probed successfully\n");
> +
> +	return 0;
> +
> +err_ippdrv_register:
> +	pm_runtime_disable(dev);
> +	clk_put(rot->clock);
> +err_clk_get:
> +	free_irq(rot->irq, rot);
> +err_get_irq:
> +	iounmap(rot->regs);
> +err_ioremap:
> +	release_resource(rot->regs_res);
> +	kfree(rot->regs_res);
> +err_get_resource:
> +	kfree(rot);
> +	return ret;
> +}
> +
> +static int __devexit rotator_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> +
> +	kfree(ippdrv->prop_list);
> +	exynos_drm_ippdrv_unregister(ippdrv);
> +
> +	pm_runtime_disable(dev);
> +	clk_put(rot->clock);
> +
> +	free_irq(rot->irq, rot);
> +
> +	iounmap(rot->regs);
> +
> +	release_resource(rot->regs_res);
> +	kfree(rot->regs_res);
> +
> +	kfree(rot);
> +
> +	return 0;
> +}
> +
> +struct rot_limit_table rot_limit_tbl = {
> +	.ycbcr420_2p = {
> +		.min_w = 32,
> +		.min_h = 32,
> +		.max_w = SZ_32K,
> +		.max_h = SZ_32K,
> +		.align = 3,
> +	},
> +	.rgb888 = {
> +		.min_w = 8,
> +		.min_h = 8,
> +		.max_w = SZ_8K,
> +		.max_h = SZ_8K,
> +		.align = 2,
> +	},
> +};
> +
> +struct platform_device_id rotator_driver_ids[] = {
> +	{
> +		.name		= "exynos-rot",
> +		.driver_data	= (unsigned long)&rot_limit_tbl,
> +	},
> +	{},
> +};
> +
> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
> +{
> +	DRM_DEBUG_KMS("%s\n", __func__);
> +
> +	if (enable) {
> +		clk_enable(rot->clock);
> +		rot->suspended = false;
> +	} else {
> +		clk_disable(rot->clock);
> +		rot->suspended = true;
> +	}
> +
> +	return 0;
> +}
> +
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int rotator_suspend(struct device *dev)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +
> +	/* ToDo */
> +
> +	DRM_DEBUG_KMS("%s\n", __func__);
> +
> +	if (pm_runtime_suspended(dev))
> +		return 0;
> +
> +	return rotator_clk_crtl(rot, false);
> +}
> +
> +static int rotator_resume(struct device *dev)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +
> +	/* ToDo */
> +
> +	DRM_DEBUG_KMS("%s\n", __func__);
> +
> +	if (!pm_runtime_suspended(dev))
> +		return rotator_clk_crtl(rot, true);
> +
> +	return 0;
> +}
> +#endif
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static int rotator_runtime_suspend(struct device *dev)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +
> +	/* ToDo */
> +
> +	DRM_DEBUG_KMS("%s\n", __func__);
> +
> +	return  rotator_clk_crtl(rot, false);
> +}
> +
> +static int rotator_runtime_resume(struct device *dev)
> +{
> +	struct rot_context *rot = dev_get_drvdata(dev);
> +
> +	/* ToDo */
> +
> +	DRM_DEBUG_KMS("%s\n", __func__);
> +
> +	return  rotator_clk_crtl(rot, true);
> +}
> +#endif
> +
> +static const struct dev_pm_ops rotator_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
> +
NULL)
> +};
> +
> +struct platform_driver rotator_driver = {
> +	.probe		= rotator_probe,
> +	.remove		= __devexit_p(rotator_remove),
> +	.id_table	= rotator_driver_ids,
> +	.driver		= {
> +		.name	= "exynos-rot",
> +		.owner	= THIS_MODULE,
> +		.pm	= &rotator_pm_ops,
> +	},
> +};
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> new file mode 100644
> index 0000000..fe929c9
> --- /dev/null
> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> @@ -0,0 +1,13 @@
> +/*
> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
> + */
> +
> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
> +#define	_EXYNOS_DRM_ROTATOR_H_
> +
> +#endif
> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
> b/drivers/gpu/drm/exynos/regs-rotator.h
> new file mode 100644
> index 0000000..a09ac6e
> --- /dev/null
> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
> @@ -0,0 +1,73 @@
> +/* drivers/gpu/drm/exynos/regs-rotator.h
> + *
> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com/
> + *
> + * Register definition file for Samsung Rotator Interface (Rotator)
> driver
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#ifndef EXYNOS_REGS_ROTATOR_H
> +#define EXYNOS_REGS_ROTATOR_H
> +
> +/* Configuration */
> +#define ROT_CONFIG			0x00
> +#define ROT_CONFIG_IRQ			(3 << 8)
> +
> +/* Image Control */
> +#define ROT_CONTROL			0x10
> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
> +#define ROT_CONTROL_ROT_90		(1 << 4)
> +#define ROT_CONTROL_ROT_180		(2 << 4)
> +#define ROT_CONTROL_ROT_270		(3 << 4)
> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
> +#define ROT_CONTROL_START		(1 << 0)
> +
> +/* Status */
> +#define ROT_STATUS			0x20
> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
> +
> +/* Buffer Address */
> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
> +
> +/* Buffer Size */
> +#define ROT_SRC_BUF_SIZE		0x3c
> +#define ROT_DST_BUF_SIZE		0x5c
> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
> +
> +/* Crop Position */
> +#define ROT_SRC_CROP_POS		0x40
> +#define ROT_DST_CROP_POS		0x60
> +#define ROT_CROP_POS_Y(x)		((x) << 16)
> +#define ROT_CROP_POS_X(x)		((x) << 0)
> +
> +/* Source Crop Size */
> +#define ROT_SRC_CROP_SIZE		0x44
> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
> +
> +/* Round to nearest aligned value */
> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) - 1))) &
> (mask))
> +/* Minimum limit value */
> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
> +/* Maximum limit value */
> +#define ROT_MAX(max, mask)		((max) & (mask))
> +
> +#endif /* EXYNOS_REGS_ROTATOR_H */
> +
> --
> 1.7.0.4
Eunchul Kim Dec. 12, 2012, 9:26 a.m. UTC | #2
Thank's your comment.

I answer your comment. please check that.

Thank's

BR
Eunchul Kim

On 12/12/2012 05:29 PM, Inki Dae wrote:
>
>
>> -----Original Message-----
>> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
>> Sent: Wednesday, December 12, 2012 4:35 PM
>> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
>> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com; jmock.shin@samsung.com;
>> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
>> chulspro.kim@samsung.com
>> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
>>
>> Rotator supports rotation/crop/flip and input/output DMA operations
>> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
>> horizontal flip.
>> and has some limitations(source and destination format have to be same, no
>> scaler)
>>
>> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
>> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
>> ---
>>   drivers/gpu/drm/exynos/Kconfig              |    7 +
>>   drivers/gpu/drm/exynos/Makefile             |    1 +
>>   drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
>>   drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
>>   drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
>> +++++++++++++++++++++++++++
>>   drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
>>   drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
>>   7 files changed, 939 insertions(+), 0 deletions(-)
>>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>   create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
>>
>> diff --git a/drivers/gpu/drm/exynos/Kconfig
>> b/drivers/gpu/drm/exynos/Kconfig
>> index 4915ab6..4860835 100644
>> --- a/drivers/gpu/drm/exynos/Kconfig
>> +++ b/drivers/gpu/drm/exynos/Kconfig
>> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
>>   	depends on DRM_EXYNOS_IPP
>>   	help
>>   	  Choose this option if you want to use Exynos FIMC for DRM.
>> +
>> +config DRM_EXYNOS_ROTATOR
>> +	bool "Exynos DRM Rotator"
>> +	depends on DRM_EXYNOS_IPP
>> +	help
>> +	  Choose this option if you want to use Exynos Rotator for DRM.
>> +
>> diff --git a/drivers/gpu/drm/exynos/Makefile
>> b/drivers/gpu/drm/exynos/Makefile
>> index 9710024..3b70668 100644
>> --- a/drivers/gpu/drm/exynos/Makefile
>> +++ b/drivers/gpu/drm/exynos/Makefile
>> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
>> exynos_drm_vidi.o
>>   exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
>>   exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
>>   exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
>> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+= exynos_drm_rotator.o
>>
>>   obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> index 73f02ac..09d884b 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
>>   		goto out_fimc;
>>   #endif
>>
>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>> +	ret = platform_driver_register(&rotator_driver);
>> +	if (ret < 0)
>> +		goto out_rotator;
>> +#endif
>> +
>>   #ifdef CONFIG_DRM_EXYNOS_IPP
>>   	ret = platform_driver_register(&ipp_driver);
>>   	if (ret < 0)
>> @@ -406,6 +412,11 @@ out_drm:
>>   out_ipp:
>>   #endif
>>
>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>> +	platform_driver_unregister(&rotator_driver);
>> +out_rotator:
>> +#endif
>> +
>>   #ifdef CONFIG_DRM_EXYNOS_FIMC
>>   	platform_driver_unregister(&fimc_driver);
>>   out_fimc:
>> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
>>   	platform_driver_unregister(&ipp_driver);
>>   #endif
>>
>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>> +	platform_driver_unregister(&rotator_driver);
>> +#endif
>> +
>>   #ifdef CONFIG_DRM_EXYNOS_FIMC
>>   	platform_driver_unregister(&fimc_driver);
>>   #endif
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>> index 14f9490..a74e37c 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>> @@ -353,5 +353,6 @@ extern struct platform_driver
>> exynos_drm_common_hdmi_driver;
>>   extern struct platform_driver vidi_driver;
>>   extern struct platform_driver g2d_driver;
>>   extern struct platform_driver fimc_driver;
>> +extern struct platform_driver rotator_driver;
>>   extern struct platform_driver ipp_driver;
>>   #endif
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>> new file mode 100644
>> index 0000000..121569c
>> --- /dev/null
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>> @@ -0,0 +1,829 @@
>> +/*
>> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
>> + * Authors:
>> + *	YoungJun Cho <yj44.cho@samsung.com>
>> + *	Eunchul Kim <chulspro.kim@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 Foundationr
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/err.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/exynos_drm.h>
>> +#include "regs-rotator.h"
>> +#include "exynos_drm.h"
>> +#include "exynos_drm_ipp.h"
>> +
>> +/*
>> + * Rotator supports image crop/rotator and input/output DMA operations.
>> + * input DMA reads image data from the memory.
>> + * output DMA writes image data to memory.
>> + */
>> +
>> +#define get_rot_context(dev)
>> 	platform_get_drvdata(to_platform_device(dev))
>> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
>> +					struct rot_context, ippdrv);
>> +#define rot_read(offset)		readl(rot->regs + (offset))
>> +#define rot_write(cfg, offset)	writel(cfg, rot->regs + (offset))
>> +
>> +enum rot_irq_status {
>> +	ROT_IRQ_STATUS_COMPLETE	= 8,
>> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
>> +};
>> +
>> +/*
>> + * A structure of limitation.
>> + *
>> + * @min_w: minimum width.
>> + * @min_h: minimum height.
>> + * @max_w: maximum width.
>> + * @max_h: maximum height.
>> + * @align: align size.
>> + */
>> +struct rot_limit {
>> +	u32	min_w;
>> +	u32	min_h;
>> +	u32	max_w;
>> +	u32	max_h;
>> +	u32	align;
>> +};
>> +
>> +/*
>> + * A structure of limitation table.
>> + *
>> + * @ycbcr420_2p: case of YUV.
>> + * @rgb888: case of RGB.
>> + */
>> +struct rot_limit_table {
>> +	struct rot_limit	ycbcr420_2p;
>> +	struct rot_limit	rgb888;
>> +};
>> +
>> +/*
>> + * A structure of rotator context.
>> + * @ippdrv: prepare initialization using ippdrv.
>> + * @regs_res: register resources.
>> + * @regs: memory mapped io registers.
>> + * @clock: rotator gate clock.
>> + * @limit_tbl: limitation of rotator.
>> + * @irq: irq number.
>> + * @cur_buf_id: current operation buffer id.
>> + * @suspended: suspended state.
>> + */
>> +struct rot_context {
>> +	struct exynos_drm_ippdrv	ippdrv;
>> +	struct resource	*regs_res;
>> +	void __iomem	*regs;
>> +	struct clk	*clock;
>> +	struct rot_limit_table	*limit_tbl;
>> +	int	irq;
>> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
>> +	bool	suspended;
>> +};
>> +
>> +static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
>> +{
>> +	u32 val = rot_read(ROT_CONFIG);
>> +
>> +	if (enable == true)
>> +		val |= ROT_CONFIG_IRQ;
>> +	else
>> +		val &= ~ROT_CONFIG_IRQ;
>> +
>> +	rot_write(val, ROT_CONFIG);
>> +}
>> +
>> +static u32 rotator_reg_get_format(struct rot_context *rot)
>> +{
>> +	u32 val = rot_read(ROT_CONTROL);
>> +
>> +	val &= ROT_CONTROL_FMT_MASK;
>> +
>> +	return val;
>> +}
>> +
>> +static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context
>> *rot)
>> +{
>> +	u32 val = rot_read(ROT_STATUS);
>> +
>> +	val = ROT_STATUS_IRQ(val);
>> +
>> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
>> +		return ROT_IRQ_STATUS_COMPLETE;
>> +	else
>
> Remove else.

- done.

>
>> +		return ROT_IRQ_STATUS_ILLEGAL;
>> +}
>> +
>> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
>> +{
>> +	struct rot_context *rot = arg;
>> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
>> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
>> +	struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
>> +	enum rot_irq_status irq_status;
>> +	u32 val;
>> +
>> +	/* Get execution result */
>> +	irq_status = rotator_reg_get_irq_status(rot);
>> +
>> +	/* clear status */
>> +	val = rot_read(ROT_STATUS);
>> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
>> +	rot_write(val, ROT_STATUS);
>> +
>> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
>> +		event_work->ippdrv = ippdrv;
>> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
>> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
>> +		queue_work(ippdrv->event_workq,
>> +			(struct work_struct *)event_work);
>> +	} else
>> +		DRM_ERROR("the SFR is set illegally\n");
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32
>> *hsize,
>> +		u32 *vsize)
>> +{
>> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
>> +	struct rot_limit *limit;
>> +	u32 mask, val;
>> +
>> +	/* Get size limit */
>> +	if (fmt == ROT_CONTROL_FMT_RGB888)
>> +		limit = &limit_tbl->rgb888;
>> +	else
>> +		limit = &limit_tbl->ycbcr420_2p;
>> +
>> +	/* Get mask for rounding to nearest aligned val */
>> +	mask = ~((1 << limit->align) - 1);
>> +
>> +	/* Set aligned width */
>> +	val = ROT_ALIGN(*hsize, limit->align, mask);
>> +	if (val < limit->min_w)
>> +		*hsize = ROT_MIN(limit->min_w, mask);
>> +	else if (val > limit->max_w)
>> +		*hsize = ROT_MAX(limit->max_w, mask);
>> +	else
>> +		*hsize = val;
>> +
>> +	/* Set aligned height */
>> +	val = ROT_ALIGN(*vsize, limit->align, mask);
>> +	if (val < limit->min_h)
>> +		*vsize = ROT_MIN(limit->min_h, mask);
>> +	else if (val > limit->max_h)
>> +		*vsize = ROT_MAX(limit->max_h, mask);
>> +	else
>> +		*vsize = val;
>> +}
>> +
>> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	u32 val;
>> +
>> +	val = rot_read(ROT_CONTROL);
>> +	val &= ~ROT_CONTROL_FMT_MASK;
>> +
>> +	switch (fmt) {
>> +	case DRM_FORMAT_NV12:
>> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
>> +		break;
>> +	case DRM_FORMAT_XRGB8888:
>> +		val |= ROT_CONTROL_FMT_RGB888;
>> +		break;
>> +	default:
>> +		DRM_ERROR("invalid image format\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	rot_write(val, ROT_CONTROL);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rotator_src_set_size(struct device *dev, int swap,
>> +		struct drm_exynos_pos *pos,
>> +		struct drm_exynos_sz *sz)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	u32 fmt, hsize, vsize;
>> +	u32 val;
>> +
>> +	/* Get format */
>> +	fmt = rotator_reg_get_format(rot);
>> +
>> +	/* Align buffer size */
>> +	hsize = sz->hsize;
>> +	vsize = sz->vsize;
>> +	rotator_align_size(rot, fmt, &hsize, &vsize);
>> +
>> +	/* Set buffer size configuration */
>> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
>> +	rot_write(val, ROT_SRC_BUF_SIZE);
>> +
>> +	/* Set crop image position configuration */
>> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
>> +	rot_write(val, ROT_SRC_CROP_POS);
>> +	val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
>> +	rot_write(val, ROT_SRC_CROP_SIZE);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rotator_src_set_addr(struct device *dev,
>> +		struct drm_exynos_ipp_buf_info *buf_info,
>> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
>> +	u32 val, fmt, hsize, vsize;
>> +	int i;
>> +
>> +	/* Set current buf_id */
>> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
>> +
>> +	switch (buf_type) {
>> +	case IPP_BUF_ENQUEUE:
>> +		/* Set address configuration */
>> +		for_each_ipp_planar(i)
>> +			addr[i] = buf_info->base[i];
>
> Check NULL.

- If not copy this state, we need to memset about addr.
   no need check null.

>
>> +
>> +		/* Get format */
>> +		fmt = rotator_reg_get_format(rot);
>> +
>> +		/* Re-set cb planar for NV12 format */
>> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
>> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
> 0x00)) {
>
> What is 0x00?

- It is NULL, I changed !addr instead of addr == 0x00

>
>> +
>> +			val = rot_read(ROT_SRC_BUF_SIZE);
>> +			hsize = ROT_GET_BUF_SIZE_W(val);
>> +			vsize = ROT_GET_BUF_SIZE_H(val);
>> +
>> +			/* Set cb planar */
>> +			addr[EXYNOS_DRM_PLANAR_CB] =
>> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
>> +		}
>> +
>> +		for_each_ipp_planar(i)
>> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
>> +		break;
>> +	case IPP_BUF_DEQUEUE:
>> +		for_each_ipp_planar(i)
>> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
>> +		break;
>> +	default:
>> +		/* Nothing to do */
>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rotator_dst_set_transf(struct device *dev,
>> +		enum drm_exynos_degree degree,
>> +		enum drm_exynos_flip flip)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	u32 val;
>> +
>> +	/* Set transform configuration */
>> +	val = rot_read(ROT_CONTROL);
>> +	val &= ~ROT_CONTROL_FLIP_MASK;
>> +
>> +	switch (flip) {
>> +	case EXYNOS_DRM_FLIP_VERTICAL:
>> +		val |= ROT_CONTROL_FLIP_VERTICAL;
>> +		break;
>> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
>> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
>> +		break;
>> +	default:
>> +		/* Flip None */
>> +		break;
>> +	}
>> +
>> +	val &= ~ROT_CONTROL_ROT_MASK;
>> +
>> +	switch (degree) {
>> +	case EXYNOS_DRM_DEGREE_90:
>> +		val |= ROT_CONTROL_ROT_90;
>> +		break;
>> +	case EXYNOS_DRM_DEGREE_180:
>> +		val |= ROT_CONTROL_ROT_180;
>> +		break;
>> +	case EXYNOS_DRM_DEGREE_270:
>> +		val |= ROT_CONTROL_ROT_270;
>> +		break;
>> +	default:
>> +		/* Rotation 0 Degree */
>> +		break;
>> +	}
>> +
>> +	rot_write(val, ROT_CONTROL);
>> +
>> +	/* Check degree for setting buffer size swap */
>> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
>> +		(degree == EXYNOS_DRM_DEGREE_270))
>> +		return 1;
>
> Correct return type. This function should return 0 or negative.

- no ~ this return type is boolean true or false.
   but we need to error handling so, we use integer as you know
   we reviewed this routine at our local git from our team(you and me).

>
>> +	else
>> +		return 0;
>
> Ditto.

- ditto.

>
>> +}
>> +
>> +static int rotator_dst_set_size(struct device *dev, int swap,
>> +		struct drm_exynos_pos *pos,
>> +		struct drm_exynos_sz *sz)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	u32 val, fmt, hsize, vsize;
>> +
>> +	/* Get format */
>> +	fmt = rotator_reg_get_format(rot);
>
> Check if fmt is valid or not.

- added rotator_check_fmt()

>
>> +
>> +	/* Align buffer size */
>> +	hsize = sz->hsize;
>> +	vsize = sz->vsize;
>> +	rotator_align_size(rot, fmt, &hsize, &vsize);
>> +
>> +	/* Set buffer size configuration */
>> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
>> +	rot_write(val, ROT_DST_BUF_SIZE);
>> +
>> +	/* Set crop image position configuration */
>> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
>> +	rot_write(val, ROT_DST_CROP_POS);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rotator_dst_set_addr(struct device *dev,
>> +		struct drm_exynos_ipp_buf_info *buf_info,
>> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
>> +	u32 val, fmt, hsize, vsize;
>> +	int i;
>> +
>> +	/* Set current buf_id */
>> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
>> +
>> +	switch (buf_type) {
>> +	case IPP_BUF_ENQUEUE:
>> +		/* Set address configuration */
>> +		for_each_ipp_planar(i)
>> +			addr[i] = buf_info->base[i];
>> +
>> +		/* Get format */
>> +		fmt = rotator_reg_get_format(rot);
>
> Check if fmt is valid or not.

- done.

>
>> +
>> +		/* Re-set cb planar for NV12 format */
>> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
>> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
> 0x00)) {
>> +			/* Get buf size */
>> +			val = rot_read(ROT_DST_BUF_SIZE);
>> +
>> +			hsize = ROT_GET_BUF_SIZE_W(val);
>> +			vsize = ROT_GET_BUF_SIZE_H(val);
>> +
>> +			/* Set cb planar */
>> +			addr[EXYNOS_DRM_PLANAR_CB] =
>> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
>> +		}
>> +
>> +		for_each_ipp_planar(i)
>> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
>> +		break;
>> +	case IPP_BUF_DEQUEUE:
>> +		for_each_ipp_planar(i)
>> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
>> +		break;
>> +	default:
>> +		/* Nothing to do */
>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct exynos_drm_ipp_ops rot_src_ops = {
>> +	.set_fmt	=	rotator_src_set_fmt,
>> +	.set_size	=	rotator_src_set_size,
>> +	.set_addr	=	rotator_src_set_addr,
>> +};
>> +
>> +static struct exynos_drm_ipp_ops rot_dst_ops = {
>> +	.set_transf	=	rotator_dst_set_transf,
>> +	.set_size	=	rotator_dst_set_size,
>> +	.set_addr	=	rotator_dst_set_addr,
>> +};
>> +
>> +static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
>> +{
>> +	struct drm_exynos_ipp_prop_list *prop_list;
>> +
>> +	DRM_DEBUG_KMS("%s\n", __func__);
>> +
>> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
>> +	if (!prop_list) {
>> +		DRM_ERROR("failed to alloc property list.\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	/*ToDo fix support function list*/
>> +
>> +	prop_list->version = 1;
>> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
>> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
>> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
>> +				(1 << EXYNOS_DRM_DEGREE_90) |
>> +				(1 << EXYNOS_DRM_DEGREE_180) |
>> +				(1 << EXYNOS_DRM_DEGREE_270);
>> +	prop_list->csc = 0;
>> +	prop_list->crop = 0;
>> +	prop_list->scale = 0;
>> +
>> +	ippdrv->prop_list = prop_list;
>> +
>> +	return 0;
>> +}
>> +
>> +static int rotator_ippdrv_check_property(struct device *dev,
>> +		struct drm_exynos_ipp_property *property)
>> +{
>> +	struct drm_exynos_ipp_config *src_config =
>> +
> &property->config[EXYNOS_DRM_OPS_SRC];
>> +	struct drm_exynos_ipp_config *dst_config =
>> +
> &property->config[EXYNOS_DRM_OPS_DST];
>> +	struct drm_exynos_pos *src_pos = &src_config->pos;
>> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
>> +	struct drm_exynos_sz *src_sz = &src_config->sz;
>> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
>> +	bool swap = false;
>> +
>> +	/* Check format configuration */
>> +	if (src_config->fmt != dst_config->fmt) {
>> +		DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (src_config->fmt) {
>> +	case DRM_FORMAT_XRGB8888:
>> +	case DRM_FORMAT_NV12:
>> +		/* No problem */
>> +		break;
>> +	default:
>> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
>> +		return -EINVAL;
>> +	}
>
> Use macro instead of switch-case. this just checks only format type.

- I don't thing so, If we make macro about this.
   then we got some confusion about this macro at next change.
   this case switch-case is better.
   Do you prefer this ? please one more comment.

#define drm_check_fmt(fmt) if (fmt == DRM_FORMAT_XRGB8888 || fmt == 
DRM_FORMAT_NV12) \
					return true;

>
>> +
>> +	/* Check transform configuration */
>> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
>> +		DRM_DEBUG_KMS("%s:not support source-side rotation\n",
>> +								__func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (dst_config->degree) {
>> +	case EXYNOS_DRM_DEGREE_90:
>> +	case EXYNOS_DRM_DEGREE_270:
>> +		swap = true;
>> +	case EXYNOS_DRM_DEGREE_0:
>> +	case EXYNOS_DRM_DEGREE_180:
>> +		/* No problem */
>> +		break;
>> +	default:
>> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
>> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
> __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (dst_config->flip) {
>> +	case EXYNOS_DRM_FLIP_NONE:
>> +	case EXYNOS_DRM_FLIP_VERTICAL:
>> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
>> +		/* No problem */
>> +		break;
>> +	default:
>> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
>> +		return -EINVAL;
>> +	}
>
> Use macro instead of switch-case. this just checks only flip type.

- ditto. please one more comment.
  in my opinion: I prefer enumeration use switch-case.

>
>> +
>> +	/* Check size configuration */
>> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
>> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
>> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (swap) {
>> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
>> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
>> +			DRM_DEBUG_KMS("%s:out of destination buffer
> bound\n",
>> +								__func__);
>> +			return -EINVAL;
>> +		}
>> +
>> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
> dst_pos->w))
>> {
>> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
>> +								__func__);
>> +			return -EINVAL;
>> +		}
>> +	} else {
>> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
>> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
>> +			DRM_DEBUG_KMS("%s:out of destination buffer
> bound\n",
>> +								__func__);
>> +			return -EINVAL;
>> +		}
>> +
>> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
> dst_pos->h))
>> {
>> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
>> +								__func__);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rotator_ippdrv_start(struct device *dev, enum
>> drm_exynos_ipp_cmd cmd)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	u32 val;
>> +
>> +	if (rot->suspended) {
>> +		DRM_ERROR("suspended state\n");
>> +		return -EPERM;
>> +	}
>> +
>> +	if (cmd != IPP_CMD_M2M) {
>> +		DRM_ERROR("not support cmd: %d\n", cmd);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Set interrupt enable */
>> +	rotator_reg_set_irq(rot, true);
>> +
>> +	val = rot_read(ROT_CONTROL);
>> +	val |= ROT_CONTROL_START;
>> +
>> +	rot_write(val, ROT_CONTROL);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __devinit rotator_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct rot_context *rot;
>> +	struct resource *res;
>> +	struct exynos_drm_ippdrv *ippdrv;
>> +	int ret;
>> +
>> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
>> +	if (!rot) {
>> +		dev_err(dev, "failed to allocate rot\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	rot->limit_tbl = (struct rot_limit_table *)
>> +				platform_get_device_id(pdev)->driver_data;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(dev, "failed to find registers\n");
>> +		ret = -ENOENT;
>> +		goto err_get_resource;
>> +	}
>> +
>> +	rot->regs_res = request_mem_region(res->start, resource_size(res),
>> +
> dev_name(dev));
>> +	if (!rot->regs_res) {
>> +		dev_err(dev, "failed to claim register region\n");
>> +		ret = -ENOENT;
>> +		goto err_get_resource;
>> +	}
>> +
>> +	rot->regs = ioremap(res->start, resource_size(res));
>> +	if (!rot->regs) {
>> +		dev_err(dev, "failed to map register\n");
>> +		ret = -ENXIO;
>> +		goto err_ioremap;
>> +	}
>> +
>> +	rot->irq = platform_get_irq(pdev, 0);
>> +	if (rot->irq < 0) {
>> +		dev_err(dev, "failed to get irq\n");
>> +		ret = rot->irq;
>> +		goto err_get_irq;
>> +	}
>> +
>> +	ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler,
>> +					IRQF_ONESHOT, "drm_rotator", rot);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to request irq\n");
>> +		goto err_get_irq;
>> +	}
>> +
>> +	rot->clock = clk_get(dev, "rotator");
>> +	if (IS_ERR_OR_NULL(rot->clock)) {
>> +		dev_err(dev, "failed to get clock\n");
>> +		ret = PTR_ERR(rot->clock);
>> +		goto err_clk_get;
>> +	}
>> +
>> +	pm_runtime_enable(dev);
>> +
>> +	ippdrv = &rot->ippdrv;
>> +	ippdrv->dev = dev;
>> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
>> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
>> +	ippdrv->check_property = rotator_ippdrv_check_property;
>> +	ippdrv->start = rotator_ippdrv_start;
>> +	ret = rotator_init_prop_list(ippdrv);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to init property list.\n");
>> +		goto err_ippdrv_register;
>> +	}
>> +
>> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
>> +
>> +	platform_set_drvdata(pdev, rot);
>> +
>> +	ret = exynos_drm_ippdrv_register(ippdrv);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to register drm rotator device\n");
>> +		kfree(ippdrv->prop_list);
>> +		goto err_ippdrv_register;
>> +	}
>> +
>> +	dev_info(dev, "The exynos rotator is probed successfully\n");
>> +
>> +	return 0;
>> +
>> +err_ippdrv_register:
>> +	pm_runtime_disable(dev);
>> +	clk_put(rot->clock);
>> +err_clk_get:
>> +	free_irq(rot->irq, rot);
>> +err_get_irq:
>> +	iounmap(rot->regs);
>> +err_ioremap:
>> +	release_resource(rot->regs_res);
>> +	kfree(rot->regs_res);
>> +err_get_resource:
>> +	kfree(rot);
>> +	return ret;
>> +}
>> +
>> +static int __devexit rotator_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
>> +
>> +	kfree(ippdrv->prop_list);
>> +	exynos_drm_ippdrv_unregister(ippdrv);
>> +
>> +	pm_runtime_disable(dev);
>> +	clk_put(rot->clock);
>> +
>> +	free_irq(rot->irq, rot);
>> +
>> +	iounmap(rot->regs);
>> +
>> +	release_resource(rot->regs_res);
>> +	kfree(rot->regs_res);
>> +
>> +	kfree(rot);
>> +
>> +	return 0;
>> +}
>> +
>> +struct rot_limit_table rot_limit_tbl = {
>> +	.ycbcr420_2p = {
>> +		.min_w = 32,
>> +		.min_h = 32,
>> +		.max_w = SZ_32K,
>> +		.max_h = SZ_32K,
>> +		.align = 3,
>> +	},
>> +	.rgb888 = {
>> +		.min_w = 8,
>> +		.min_h = 8,
>> +		.max_w = SZ_8K,
>> +		.max_h = SZ_8K,
>> +		.align = 2,
>> +	},
>> +};
>> +
>> +struct platform_device_id rotator_driver_ids[] = {
>> +	{
>> +		.name		= "exynos-rot",
>> +		.driver_data	= (unsigned long)&rot_limit_tbl,
>> +	},
>> +	{},
>> +};
>> +
>> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
>> +{
>> +	DRM_DEBUG_KMS("%s\n", __func__);
>> +
>> +	if (enable) {
>> +		clk_enable(rot->clock);
>> +		rot->suspended = false;
>> +	} else {
>> +		clk_disable(rot->clock);
>> +		rot->suspended = true;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int rotator_suspend(struct device *dev)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +
>> +	/* ToDo */
>> +
>> +	DRM_DEBUG_KMS("%s\n", __func__);
>> +
>> +	if (pm_runtime_suspended(dev))
>> +		return 0;
>> +
>> +	return rotator_clk_crtl(rot, false);
>> +}
>> +
>> +static int rotator_resume(struct device *dev)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +
>> +	/* ToDo */
>> +
>> +	DRM_DEBUG_KMS("%s\n", __func__);
>> +
>> +	if (!pm_runtime_suspended(dev))
>> +		return rotator_clk_crtl(rot, true);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +#ifdef CONFIG_PM_RUNTIME
>> +static int rotator_runtime_suspend(struct device *dev)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +
>> +	/* ToDo */
>> +
>> +	DRM_DEBUG_KMS("%s\n", __func__);
>> +
>> +	return  rotator_clk_crtl(rot, false);
>> +}
>> +
>> +static int rotator_runtime_resume(struct device *dev)
>> +{
>> +	struct rot_context *rot = dev_get_drvdata(dev);
>> +
>> +	/* ToDo */
>> +
>> +	DRM_DEBUG_KMS("%s\n", __func__);
>> +
>> +	return  rotator_clk_crtl(rot, true);
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops rotator_pm_ops = {
>> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
>> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
>> +
> NULL)
>> +};
>> +
>> +struct platform_driver rotator_driver = {
>> +	.probe		= rotator_probe,
>> +	.remove		= __devexit_p(rotator_remove),
>> +	.id_table	= rotator_driver_ids,
>> +	.driver		= {
>> +		.name	= "exynos-rot",
>> +		.owner	= THIS_MODULE,
>> +		.pm	= &rotator_pm_ops,
>> +	},
>> +};
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>> new file mode 100644
>> index 0000000..fe929c9
>> --- /dev/null
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>> @@ -0,0 +1,13 @@
>> +/*
>> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
>> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
>> + */
>> +
>> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
>> +#define	_EXYNOS_DRM_ROTATOR_H_
>> +
>> +#endif
>> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
>> b/drivers/gpu/drm/exynos/regs-rotator.h
>> new file mode 100644
>> index 0000000..a09ac6e
>> --- /dev/null
>> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
>> @@ -0,0 +1,73 @@
>> +/* drivers/gpu/drm/exynos/regs-rotator.h
>> + *
>> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
>> + *		http://www.samsung.com/
>> + *
>> + * Register definition file for Samsung Rotator Interface (Rotator)
>> driver
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#ifndef EXYNOS_REGS_ROTATOR_H
>> +#define EXYNOS_REGS_ROTATOR_H
>> +
>> +/* Configuration */
>> +#define ROT_CONFIG			0x00
>> +#define ROT_CONFIG_IRQ			(3 << 8)
>> +
>> +/* Image Control */
>> +#define ROT_CONTROL			0x10
>> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
>> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
>> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
>> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
>> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
>> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
>> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
>> +#define ROT_CONTROL_ROT_90		(1 << 4)
>> +#define ROT_CONTROL_ROT_180		(2 << 4)
>> +#define ROT_CONTROL_ROT_270		(3 << 4)
>> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
>> +#define ROT_CONTROL_START		(1 << 0)
>> +
>> +/* Status */
>> +#define ROT_STATUS			0x20
>> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
>> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
>> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
>> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
>> +
>> +/* Buffer Address */
>> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
>> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
>> +
>> +/* Buffer Size */
>> +#define ROT_SRC_BUF_SIZE		0x3c
>> +#define ROT_DST_BUF_SIZE		0x5c
>> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
>> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
>> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
>> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
>> +
>> +/* Crop Position */
>> +#define ROT_SRC_CROP_POS		0x40
>> +#define ROT_DST_CROP_POS		0x60
>> +#define ROT_CROP_POS_Y(x)		((x) << 16)
>> +#define ROT_CROP_POS_X(x)		((x) << 0)
>> +
>> +/* Source Crop Size */
>> +#define ROT_SRC_CROP_SIZE		0x44
>> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
>> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
>> +
>> +/* Round to nearest aligned value */
>> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) - 1))) &
>> (mask))
>> +/* Minimum limit value */
>> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
>> +/* Maximum limit value */
>> +#define ROT_MAX(max, mask)		((max) & (mask))
>> +
>> +#endif /* EXYNOS_REGS_ROTATOR_H */
>> +
>> --
>> 1.7.0.4
>
>
Inki Dae Dec. 12, 2012, 9:41 a.m. UTC | #3
> -----Original Message-----
> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> Sent: Wednesday, December 12, 2012 6:26 PM
> To: Inki Dae
> Cc: dri-devel@lists.freedesktop.org; jy0.jeon@samsung.com;
> yj44.cho@samsung.com; jmock.shin@samsung.com; jaejoon.seo@samsung.com;
> kyungmin.park@samsung.com; sw0312.kim@samsung.com; Joonyoung Shim
> Subject: Re: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> 
> Thank's your comment.
> 
> I answer your comment. please check that.
> 
> Thank's
> 
> BR
> Eunchul Kim
> 
> On 12/12/2012 05:29 PM, Inki Dae wrote:
> >
> >
> >> -----Original Message-----
> >> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> >> Sent: Wednesday, December 12, 2012 4:35 PM
> >> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
> >> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com; jmock.shin@samsung.com;
> >> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
> >> chulspro.kim@samsung.com
> >> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> >>
> >> Rotator supports rotation/crop/flip and input/output DMA operations
> >> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
> >> horizontal flip.
> >> and has some limitations(source and destination format have to be same,
> no
> >> scaler)
> >>
> >> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
> >> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
> >> ---
> >>   drivers/gpu/drm/exynos/Kconfig              |    7 +
> >>   drivers/gpu/drm/exynos/Makefile             |    1 +
> >>   drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
> >>   drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
> >>   drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
> >> +++++++++++++++++++++++++++
> >>   drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
> >>   drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
> >>   7 files changed, 939 insertions(+), 0 deletions(-)
> >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >>   create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
> >>
> >> diff --git a/drivers/gpu/drm/exynos/Kconfig
> >> b/drivers/gpu/drm/exynos/Kconfig
> >> index 4915ab6..4860835 100644
> >> --- a/drivers/gpu/drm/exynos/Kconfig
> >> +++ b/drivers/gpu/drm/exynos/Kconfig
> >> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
> >>   	depends on DRM_EXYNOS_IPP
> >>   	help
> >>   	  Choose this option if you want to use Exynos FIMC for DRM.
> >> +
> >> +config DRM_EXYNOS_ROTATOR
> >> +	bool "Exynos DRM Rotator"
> >> +	depends on DRM_EXYNOS_IPP
> >> +	help
> >> +	  Choose this option if you want to use Exynos Rotator for DRM.
> >> +
> >> diff --git a/drivers/gpu/drm/exynos/Makefile
> >> b/drivers/gpu/drm/exynos/Makefile
> >> index 9710024..3b70668 100644
> >> --- a/drivers/gpu/drm/exynos/Makefile
> >> +++ b/drivers/gpu/drm/exynos/Makefile
> >> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
> >> exynos_drm_vidi.o
> >>   exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
> >>   exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
> >>   exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
> >> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+= exynos_drm_rotator.o
> >>
> >>   obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> index 73f02ac..09d884b 100644
> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
> >>   		goto out_fimc;
> >>   #endif
> >>
> >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> >> +	ret = platform_driver_register(&rotator_driver);
> >> +	if (ret < 0)
> >> +		goto out_rotator;
> >> +#endif
> >> +
> >>   #ifdef CONFIG_DRM_EXYNOS_IPP
> >>   	ret = platform_driver_register(&ipp_driver);
> >>   	if (ret < 0)
> >> @@ -406,6 +412,11 @@ out_drm:
> >>   out_ipp:
> >>   #endif
> >>
> >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> >> +	platform_driver_unregister(&rotator_driver);
> >> +out_rotator:
> >> +#endif
> >> +
> >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> >>   	platform_driver_unregister(&fimc_driver);
> >>   out_fimc:
> >> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
> >>   	platform_driver_unregister(&ipp_driver);
> >>   #endif
> >>
> >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> >> +	platform_driver_unregister(&rotator_driver);
> >> +#endif
> >> +
> >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> >>   	platform_driver_unregister(&fimc_driver);
> >>   #endif
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> index 14f9490..a74e37c 100644
> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> @@ -353,5 +353,6 @@ extern struct platform_driver
> >> exynos_drm_common_hdmi_driver;
> >>   extern struct platform_driver vidi_driver;
> >>   extern struct platform_driver g2d_driver;
> >>   extern struct platform_driver fimc_driver;
> >> +extern struct platform_driver rotator_driver;
> >>   extern struct platform_driver ipp_driver;
> >>   #endif
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >> new file mode 100644
> >> index 0000000..121569c
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >> @@ -0,0 +1,829 @@
> >> +/*
> >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> >> + * Authors:
> >> + *	YoungJun Cho <yj44.cho@samsung.com>
> >> + *	Eunchul Kim <chulspro.kim@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 Foundationr
> >> + */
> >> +
> >> +#include <linux/kernel.h>
> >> +#include <linux/module.h>
> >> +#include <linux/err.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/io.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/pm_runtime.h>
> >> +
> >> +#include <drm/drmP.h>
> >> +#include <drm/exynos_drm.h>
> >> +#include "regs-rotator.h"
> >> +#include "exynos_drm.h"
> >> +#include "exynos_drm_ipp.h"
> >> +
> >> +/*
> >> + * Rotator supports image crop/rotator and input/output DMA
operations.
> >> + * input DMA reads image data from the memory.
> >> + * output DMA writes image data to memory.
> >> + */
> >> +
> >> +#define get_rot_context(dev)
> >> 	platform_get_drvdata(to_platform_device(dev))
> >> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
> >> +					struct rot_context, ippdrv);
> >> +#define rot_read(offset)		readl(rot->regs + (offset))
> >> +#define rot_write(cfg, offset)	writel(cfg, rot->regs + (offset))
> >> +
> >> +enum rot_irq_status {
> >> +	ROT_IRQ_STATUS_COMPLETE	= 8,
> >> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
> >> +};
> >> +
> >> +/*
> >> + * A structure of limitation.
> >> + *
> >> + * @min_w: minimum width.
> >> + * @min_h: minimum height.
> >> + * @max_w: maximum width.
> >> + * @max_h: maximum height.
> >> + * @align: align size.
> >> + */
> >> +struct rot_limit {
> >> +	u32	min_w;
> >> +	u32	min_h;
> >> +	u32	max_w;
> >> +	u32	max_h;
> >> +	u32	align;
> >> +};
> >> +
> >> +/*
> >> + * A structure of limitation table.
> >> + *
> >> + * @ycbcr420_2p: case of YUV.
> >> + * @rgb888: case of RGB.
> >> + */
> >> +struct rot_limit_table {
> >> +	struct rot_limit	ycbcr420_2p;
> >> +	struct rot_limit	rgb888;
> >> +};
> >> +
> >> +/*
> >> + * A structure of rotator context.
> >> + * @ippdrv: prepare initialization using ippdrv.
> >> + * @regs_res: register resources.
> >> + * @regs: memory mapped io registers.
> >> + * @clock: rotator gate clock.
> >> + * @limit_tbl: limitation of rotator.
> >> + * @irq: irq number.
> >> + * @cur_buf_id: current operation buffer id.
> >> + * @suspended: suspended state.
> >> + */
> >> +struct rot_context {
> >> +	struct exynos_drm_ippdrv	ippdrv;
> >> +	struct resource	*regs_res;
> >> +	void __iomem	*regs;
> >> +	struct clk	*clock;
> >> +	struct rot_limit_table	*limit_tbl;
> >> +	int	irq;
> >> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
> >> +	bool	suspended;
> >> +};
> >> +
> >> +static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
> >> +{
> >> +	u32 val = rot_read(ROT_CONFIG);
> >> +
> >> +	if (enable == true)
> >> +		val |= ROT_CONFIG_IRQ;
> >> +	else
> >> +		val &= ~ROT_CONFIG_IRQ;
> >> +
> >> +	rot_write(val, ROT_CONFIG);
> >> +}
> >> +
> >> +static u32 rotator_reg_get_format(struct rot_context *rot)
> >> +{
> >> +	u32 val = rot_read(ROT_CONTROL);
> >> +
> >> +	val &= ROT_CONTROL_FMT_MASK;
> >> +
> >> +	return val;
> >> +}
> >> +
> >> +static enum rot_irq_status rotator_reg_get_irq_status(struct
> rot_context
> >> *rot)
> >> +{
> >> +	u32 val = rot_read(ROT_STATUS);
> >> +
> >> +	val = ROT_STATUS_IRQ(val);
> >> +
> >> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
> >> +		return ROT_IRQ_STATUS_COMPLETE;
> >> +	else
> >
> > Remove else.
> 
> - done.
> 
> >
> >> +		return ROT_IRQ_STATUS_ILLEGAL;
> >> +}
> >> +
> >> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
> >> +{
> >> +	struct rot_context *rot = arg;
> >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> >> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
> >> +	struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
> >> +	enum rot_irq_status irq_status;
> >> +	u32 val;
> >> +
> >> +	/* Get execution result */
> >> +	irq_status = rotator_reg_get_irq_status(rot);
> >> +
> >> +	/* clear status */
> >> +	val = rot_read(ROT_STATUS);
> >> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
> >> +	rot_write(val, ROT_STATUS);
> >> +
> >> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
> >> +		event_work->ippdrv = ippdrv;
> >> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
> >> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
> >> +		queue_work(ippdrv->event_workq,
> >> +			(struct work_struct *)event_work);
> >> +	} else
> >> +		DRM_ERROR("the SFR is set illegally\n");
> >> +
> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >> +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32
> >> *hsize,
> >> +		u32 *vsize)
> >> +{
> >> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
> >> +	struct rot_limit *limit;
> >> +	u32 mask, val;
> >> +
> >> +	/* Get size limit */
> >> +	if (fmt == ROT_CONTROL_FMT_RGB888)
> >> +		limit = &limit_tbl->rgb888;
> >> +	else
> >> +		limit = &limit_tbl->ycbcr420_2p;
> >> +
> >> +	/* Get mask for rounding to nearest aligned val */
> >> +	mask = ~((1 << limit->align) - 1);
> >> +
> >> +	/* Set aligned width */
> >> +	val = ROT_ALIGN(*hsize, limit->align, mask);
> >> +	if (val < limit->min_w)
> >> +		*hsize = ROT_MIN(limit->min_w, mask);
> >> +	else if (val > limit->max_w)
> >> +		*hsize = ROT_MAX(limit->max_w, mask);
> >> +	else
> >> +		*hsize = val;
> >> +
> >> +	/* Set aligned height */
> >> +	val = ROT_ALIGN(*vsize, limit->align, mask);
> >> +	if (val < limit->min_h)
> >> +		*vsize = ROT_MIN(limit->min_h, mask);
> >> +	else if (val > limit->max_h)
> >> +		*vsize = ROT_MAX(limit->max_h, mask);
> >> +	else
> >> +		*vsize = val;
> >> +}
> >> +
> >> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val;
> >> +
> >> +	val = rot_read(ROT_CONTROL);
> >> +	val &= ~ROT_CONTROL_FMT_MASK;
> >> +
> >> +	switch (fmt) {
> >> +	case DRM_FORMAT_NV12:
> >> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
> >> +		break;
> >> +	case DRM_FORMAT_XRGB8888:
> >> +		val |= ROT_CONTROL_FMT_RGB888;
> >> +		break;
> >> +	default:
> >> +		DRM_ERROR("invalid image format\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	rot_write(val, ROT_CONTROL);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_src_set_size(struct device *dev, int swap,
> >> +		struct drm_exynos_pos *pos,
> >> +		struct drm_exynos_sz *sz)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 fmt, hsize, vsize;
> >> +	u32 val;
> >> +
> >> +	/* Get format */
> >> +	fmt = rotator_reg_get_format(rot);
> >> +
> >> +	/* Align buffer size */
> >> +	hsize = sz->hsize;
> >> +	vsize = sz->vsize;
> >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> >> +
> >> +	/* Set buffer size configuration */
> >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> >> +	rot_write(val, ROT_SRC_BUF_SIZE);
> >> +
> >> +	/* Set crop image position configuration */
> >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> >> +	rot_write(val, ROT_SRC_CROP_POS);
> >> +	val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
> >> +	rot_write(val, ROT_SRC_CROP_SIZE);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_src_set_addr(struct device *dev,
> >> +		struct drm_exynos_ipp_buf_info *buf_info,
> >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> >> +	u32 val, fmt, hsize, vsize;
> >> +	int i;
> >> +
> >> +	/* Set current buf_id */
> >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
> >> +
> >> +	switch (buf_type) {
> >> +	case IPP_BUF_ENQUEUE:
> >> +		/* Set address configuration */
> >> +		for_each_ipp_planar(i)
> >> +			addr[i] = buf_info->base[i];
> >
> > Check NULL.
> 
> - If not copy this state, we need to memset about addr.
>    no need check null.
> 
> >
> >> +
> >> +		/* Get format */
> >> +		fmt = rotator_reg_get_format(rot);
> >> +
> >> +		/* Re-set cb planar for NV12 format */
> >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> >> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
> > 0x00)) {
> >
> > What is 0x00?
> 
> - It is NULL, I changed !addr instead of addr == 0x00
> 
> >
> >> +
> >> +			val = rot_read(ROT_SRC_BUF_SIZE);
> >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> >> +
> >> +			/* Set cb planar */
> >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
> >> +		}
> >> +
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
> >> +		break;
> >> +	case IPP_BUF_DEQUEUE:
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
> >> +		break;
> >> +	default:
> >> +		/* Nothing to do */
> >> +		break;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_dst_set_transf(struct device *dev,
> >> +		enum drm_exynos_degree degree,
> >> +		enum drm_exynos_flip flip)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val;
> >> +
> >> +	/* Set transform configuration */
> >> +	val = rot_read(ROT_CONTROL);
> >> +	val &= ~ROT_CONTROL_FLIP_MASK;
> >> +
> >> +	switch (flip) {
> >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> >> +		val |= ROT_CONTROL_FLIP_VERTICAL;
> >> +		break;
> >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> >> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
> >> +		break;
> >> +	default:
> >> +		/* Flip None */
> >> +		break;
> >> +	}
> >> +
> >> +	val &= ~ROT_CONTROL_ROT_MASK;
> >> +
> >> +	switch (degree) {
> >> +	case EXYNOS_DRM_DEGREE_90:
> >> +		val |= ROT_CONTROL_ROT_90;
> >> +		break;
> >> +	case EXYNOS_DRM_DEGREE_180:
> >> +		val |= ROT_CONTROL_ROT_180;
> >> +		break;
> >> +	case EXYNOS_DRM_DEGREE_270:
> >> +		val |= ROT_CONTROL_ROT_270;
> >> +		break;
> >> +	default:
> >> +		/* Rotation 0 Degree */
> >> +		break;
> >> +	}
> >> +
> >> +	rot_write(val, ROT_CONTROL);
> >> +
> >> +	/* Check degree for setting buffer size swap */
> >> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
> >> +		(degree == EXYNOS_DRM_DEGREE_270))
> >> +		return 1;
> >
> > Correct return type. This function should return 0 or negative.
> 
> - no ~ this return type is boolean true or false.
>    but we need to error handling so, we use integer as you know
>    we reviewed this routine at our local git from our team(you and me).
> 
> >
> >> +	else
> >> +		return 0;
> >
> > Ditto.
> 
> - ditto.
> 
> >
> >> +}
> >> +
> >> +static int rotator_dst_set_size(struct device *dev, int swap,
> >> +		struct drm_exynos_pos *pos,
> >> +		struct drm_exynos_sz *sz)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val, fmt, hsize, vsize;
> >> +
> >> +	/* Get format */
> >> +	fmt = rotator_reg_get_format(rot);
> >
> > Check if fmt is valid or not.
> 
> - added rotator_check_fmt()
> 
> >
> >> +
> >> +	/* Align buffer size */
> >> +	hsize = sz->hsize;
> >> +	vsize = sz->vsize;
> >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> >> +
> >> +	/* Set buffer size configuration */
> >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> >> +	rot_write(val, ROT_DST_BUF_SIZE);
> >> +
> >> +	/* Set crop image position configuration */
> >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> >> +	rot_write(val, ROT_DST_CROP_POS);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_dst_set_addr(struct device *dev,
> >> +		struct drm_exynos_ipp_buf_info *buf_info,
> >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> >> +	u32 val, fmt, hsize, vsize;
> >> +	int i;
> >> +
> >> +	/* Set current buf_id */
> >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
> >> +
> >> +	switch (buf_type) {
> >> +	case IPP_BUF_ENQUEUE:
> >> +		/* Set address configuration */
> >> +		for_each_ipp_planar(i)
> >> +			addr[i] = buf_info->base[i];
> >> +
> >> +		/* Get format */
> >> +		fmt = rotator_reg_get_format(rot);
> >
> > Check if fmt is valid or not.
> 
> - done.
> 
> >
> >> +
> >> +		/* Re-set cb planar for NV12 format */
> >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> >> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
> > 0x00)) {
> >> +			/* Get buf size */
> >> +			val = rot_read(ROT_DST_BUF_SIZE);
> >> +
> >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> >> +
> >> +			/* Set cb planar */
> >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
> >> +		}
> >> +
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
> >> +		break;
> >> +	case IPP_BUF_DEQUEUE:
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
> >> +		break;
> >> +	default:
> >> +		/* Nothing to do */
> >> +		break;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static struct exynos_drm_ipp_ops rot_src_ops = {
> >> +	.set_fmt	=	rotator_src_set_fmt,
> >> +	.set_size	=	rotator_src_set_size,
> >> +	.set_addr	=	rotator_src_set_addr,
> >> +};
> >> +
> >> +static struct exynos_drm_ipp_ops rot_dst_ops = {
> >> +	.set_transf	=	rotator_dst_set_transf,
> >> +	.set_size	=	rotator_dst_set_size,
> >> +	.set_addr	=	rotator_dst_set_addr,
> >> +};
> >> +
> >> +static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
> >> +{
> >> +	struct drm_exynos_ipp_prop_list *prop_list;
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
> >> +	if (!prop_list) {
> >> +		DRM_ERROR("failed to alloc property list.\n");
> >> +		return -ENOMEM;
> >> +	}
> >> +
> >> +	/*ToDo fix support function list*/
> >> +
> >> +	prop_list->version = 1;
> >> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
> >> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
> >> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
> >> +				(1 << EXYNOS_DRM_DEGREE_90) |
> >> +				(1 << EXYNOS_DRM_DEGREE_180) |
> >> +				(1 << EXYNOS_DRM_DEGREE_270);
> >> +	prop_list->csc = 0;
> >> +	prop_list->crop = 0;
> >> +	prop_list->scale = 0;
> >> +
> >> +	ippdrv->prop_list = prop_list;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_ippdrv_check_property(struct device *dev,
> >> +		struct drm_exynos_ipp_property *property)
> >> +{
> >> +	struct drm_exynos_ipp_config *src_config =
> >> +
> > &property->config[EXYNOS_DRM_OPS_SRC];
> >> +	struct drm_exynos_ipp_config *dst_config =
> >> +
> > &property->config[EXYNOS_DRM_OPS_DST];
> >> +	struct drm_exynos_pos *src_pos = &src_config->pos;
> >> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
> >> +	struct drm_exynos_sz *src_sz = &src_config->sz;
> >> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
> >> +	bool swap = false;
> >> +
> >> +	/* Check format configuration */
> >> +	if (src_config->fmt != dst_config->fmt) {
> >> +		DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	switch (src_config->fmt) {
> >> +	case DRM_FORMAT_XRGB8888:
> >> +	case DRM_FORMAT_NV12:
> >> +		/* No problem */
> >> +		break;
> >> +	default:
> >> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >
> > Use macro instead of switch-case. this just checks only format type.
> 
> - I don't thing so, If we make macro about this.
>    then we got some confusion about this macro at next change.
>    this case switch-case is better.
>    Do you prefer this ? please one more comment.
> 
> #define drm_check_fmt(fmt) if (fmt == DRM_FORMAT_XRGB8888 || fmt ==
> DRM_FORMAT_NV12) \
> 					return true;

#define drm_check_fmt(fmt)		((fmt & (DRM_FORMAT_XRGB8888 |
DRM_FORMAT_NV12)) ? true : false)

if (drm_check_fmt(src_config->fmt))
	Something;
else
	Something;

> 
> >
> >> +
> >> +	/* Check transform configuration */
> >> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
> >> +		DRM_DEBUG_KMS("%s:not support source-side rotation\n",
> >> +								__func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	switch (dst_config->degree) {
> >> +	case EXYNOS_DRM_DEGREE_90:
> >> +	case EXYNOS_DRM_DEGREE_270:
> >> +		swap = true;
> >> +	case EXYNOS_DRM_DEGREE_0:
> >> +	case EXYNOS_DRM_DEGREE_180:
> >> +		/* No problem */
> >> +		break;
> >> +	default:
> >> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
> >> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
> > __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	switch (dst_config->flip) {
> >> +	case EXYNOS_DRM_FLIP_NONE:
> >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> >> +		/* No problem */
> >> +		break;
> >> +	default:
> >> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >
> > Use macro instead of switch-case. this just checks only flip type.
> 
> - ditto. please one more comment.
>   in my opinion: I prefer enumeration use switch-case.

What does each case do? Just check? If so, use macro as I mentioned. We
can't accept this codes.

> 
> >
> >> +
> >> +	/* Check size configuration */
> >> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
> >> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
> >> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	if (swap) {
> >> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
> >> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
> >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > bound\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
> > dst_pos->w))
> >> {
> >> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +	} else {
> >> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
> >> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
> >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > bound\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
> > dst_pos->h))
> >> {
> >> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_ippdrv_start(struct device *dev, enum
> >> drm_exynos_ipp_cmd cmd)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val;
> >> +
> >> +	if (rot->suspended) {
> >> +		DRM_ERROR("suspended state\n");
> >> +		return -EPERM;
> >> +	}
> >> +
> >> +	if (cmd != IPP_CMD_M2M) {
> >> +		DRM_ERROR("not support cmd: %d\n", cmd);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/* Set interrupt enable */
> >> +	rotator_reg_set_irq(rot, true);
> >> +
> >> +	val = rot_read(ROT_CONTROL);
> >> +	val |= ROT_CONTROL_START;
> >> +
> >> +	rot_write(val, ROT_CONTROL);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int __devinit rotator_probe(struct platform_device *pdev)
> >> +{
> >> +	struct device *dev = &pdev->dev;
> >> +	struct rot_context *rot;
> >> +	struct resource *res;
> >> +	struct exynos_drm_ippdrv *ippdrv;
> >> +	int ret;
> >> +
> >> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
> >> +	if (!rot) {
> >> +		dev_err(dev, "failed to allocate rot\n");
> >> +		return -ENOMEM;
> >> +	}
> >> +
> >> +	rot->limit_tbl = (struct rot_limit_table *)
> >> +				platform_get_device_id(pdev)->driver_data;
> >> +
> >> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> +	if (!res) {
> >> +		dev_err(dev, "failed to find registers\n");
> >> +		ret = -ENOENT;
> >> +		goto err_get_resource;
> >> +	}
> >> +
> >> +	rot->regs_res = request_mem_region(res->start, resource_size(res),
> >> +
> > dev_name(dev));
> >> +	if (!rot->regs_res) {
> >> +		dev_err(dev, "failed to claim register region\n");
> >> +		ret = -ENOENT;
> >> +		goto err_get_resource;
> >> +	}
> >> +
> >> +	rot->regs = ioremap(res->start, resource_size(res));
> >> +	if (!rot->regs) {
> >> +		dev_err(dev, "failed to map register\n");
> >> +		ret = -ENXIO;
> >> +		goto err_ioremap;
> >> +	}
> >> +
> >> +	rot->irq = platform_get_irq(pdev, 0);
> >> +	if (rot->irq < 0) {
> >> +		dev_err(dev, "failed to get irq\n");
> >> +		ret = rot->irq;
> >> +		goto err_get_irq;
> >> +	}
> >> +
> >> +	ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler,
> >> +					IRQF_ONESHOT, "drm_rotator", rot);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to request irq\n");
> >> +		goto err_get_irq;
> >> +	}
> >> +
> >> +	rot->clock = clk_get(dev, "rotator");
> >> +	if (IS_ERR_OR_NULL(rot->clock)) {
> >> +		dev_err(dev, "failed to get clock\n");
> >> +		ret = PTR_ERR(rot->clock);
> >> +		goto err_clk_get;
> >> +	}
> >> +
> >> +	pm_runtime_enable(dev);
> >> +
> >> +	ippdrv = &rot->ippdrv;
> >> +	ippdrv->dev = dev;
> >> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
> >> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
> >> +	ippdrv->check_property = rotator_ippdrv_check_property;
> >> +	ippdrv->start = rotator_ippdrv_start;
> >> +	ret = rotator_init_prop_list(ippdrv);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to init property list.\n");
> >> +		goto err_ippdrv_register;
> >> +	}
> >> +
> >> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
> >> +
> >> +	platform_set_drvdata(pdev, rot);
> >> +
> >> +	ret = exynos_drm_ippdrv_register(ippdrv);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to register drm rotator device\n");
> >> +		kfree(ippdrv->prop_list);
> >> +		goto err_ippdrv_register;
> >> +	}
> >> +
> >> +	dev_info(dev, "The exynos rotator is probed successfully\n");
> >> +
> >> +	return 0;
> >> +
> >> +err_ippdrv_register:
> >> +	pm_runtime_disable(dev);
> >> +	clk_put(rot->clock);
> >> +err_clk_get:
> >> +	free_irq(rot->irq, rot);
> >> +err_get_irq:
> >> +	iounmap(rot->regs);
> >> +err_ioremap:
> >> +	release_resource(rot->regs_res);
> >> +	kfree(rot->regs_res);
> >> +err_get_resource:
> >> +	kfree(rot);
> >> +	return ret;
> >> +}
> >> +
> >> +static int __devexit rotator_remove(struct platform_device *pdev)
> >> +{
> >> +	struct device *dev = &pdev->dev;
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> >> +
> >> +	kfree(ippdrv->prop_list);
> >> +	exynos_drm_ippdrv_unregister(ippdrv);
> >> +
> >> +	pm_runtime_disable(dev);
> >> +	clk_put(rot->clock);
> >> +
> >> +	free_irq(rot->irq, rot);
> >> +
> >> +	iounmap(rot->regs);
> >> +
> >> +	release_resource(rot->regs_res);
> >> +	kfree(rot->regs_res);
> >> +
> >> +	kfree(rot);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +struct rot_limit_table rot_limit_tbl = {
> >> +	.ycbcr420_2p = {
> >> +		.min_w = 32,
> >> +		.min_h = 32,
> >> +		.max_w = SZ_32K,
> >> +		.max_h = SZ_32K,
> >> +		.align = 3,
> >> +	},
> >> +	.rgb888 = {
> >> +		.min_w = 8,
> >> +		.min_h = 8,
> >> +		.max_w = SZ_8K,
> >> +		.max_h = SZ_8K,
> >> +		.align = 2,
> >> +	},
> >> +};
> >> +
> >> +struct platform_device_id rotator_driver_ids[] = {
> >> +	{
> >> +		.name		= "exynos-rot",
> >> +		.driver_data	= (unsigned long)&rot_limit_tbl,
> >> +	},
> >> +	{},
> >> +};
> >> +
> >> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
> >> +{
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	if (enable) {
> >> +		clk_enable(rot->clock);
> >> +		rot->suspended = false;
> >> +	} else {
> >> +		clk_disable(rot->clock);
> >> +		rot->suspended = true;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +
> >> +#ifdef CONFIG_PM_SLEEP
> >> +static int rotator_suspend(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	if (pm_runtime_suspended(dev))
> >> +		return 0;
> >> +
> >> +	return rotator_clk_crtl(rot, false);
> >> +}
> >> +
> >> +static int rotator_resume(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	if (!pm_runtime_suspended(dev))
> >> +		return rotator_clk_crtl(rot, true);
> >> +
> >> +	return 0;
> >> +}
> >> +#endif
> >> +
> >> +#ifdef CONFIG_PM_RUNTIME
> >> +static int rotator_runtime_suspend(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	return  rotator_clk_crtl(rot, false);
> >> +}
> >> +
> >> +static int rotator_runtime_resume(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	return  rotator_clk_crtl(rot, true);
> >> +}
> >> +#endif
> >> +
> >> +static const struct dev_pm_ops rotator_pm_ops = {
> >> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
> >> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
> >> +
> > NULL)
> >> +};
> >> +
> >> +struct platform_driver rotator_driver = {
> >> +	.probe		= rotator_probe,
> >> +	.remove		= __devexit_p(rotator_remove),
> >> +	.id_table	= rotator_driver_ids,
> >> +	.driver		= {
> >> +		.name	= "exynos-rot",
> >> +		.owner	= THIS_MODULE,
> >> +		.pm	= &rotator_pm_ops,
> >> +	},
> >> +};
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >> new file mode 100644
> >> index 0000000..fe929c9
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >> @@ -0,0 +1,13 @@
> >> +/*
> >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> >> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
> >> + */
> >> +
> >> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
> >> +#define	_EXYNOS_DRM_ROTATOR_H_
> >> +
> >> +#endif
> >> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
> >> b/drivers/gpu/drm/exynos/regs-rotator.h
> >> new file mode 100644
> >> index 0000000..a09ac6e
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
> >> @@ -0,0 +1,73 @@
> >> +/* drivers/gpu/drm/exynos/regs-rotator.h
> >> + *
> >> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> >> + *		http://www.samsung.com/
> >> + *
> >> + * Register definition file for Samsung Rotator Interface (Rotator)
> >> driver
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> +*/
> >> +
> >> +#ifndef EXYNOS_REGS_ROTATOR_H
> >> +#define EXYNOS_REGS_ROTATOR_H
> >> +
> >> +/* Configuration */
> >> +#define ROT_CONFIG			0x00
> >> +#define ROT_CONFIG_IRQ			(3 << 8)
> >> +
> >> +/* Image Control */
> >> +#define ROT_CONTROL			0x10
> >> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
> >> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
> >> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
> >> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
> >> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
> >> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
> >> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
> >> +#define ROT_CONTROL_ROT_90		(1 << 4)
> >> +#define ROT_CONTROL_ROT_180		(2 << 4)
> >> +#define ROT_CONTROL_ROT_270		(3 << 4)
> >> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
> >> +#define ROT_CONTROL_START		(1 << 0)
> >> +
> >> +/* Status */
> >> +#define ROT_STATUS			0x20
> >> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
> >> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
> >> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
> >> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
> >> +
> >> +/* Buffer Address */
> >> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
> >> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
> >> +
> >> +/* Buffer Size */
> >> +#define ROT_SRC_BUF_SIZE		0x3c
> >> +#define ROT_DST_BUF_SIZE		0x5c
> >> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
> >> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
> >> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
> >> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
> >> +
> >> +/* Crop Position */
> >> +#define ROT_SRC_CROP_POS		0x40
> >> +#define ROT_DST_CROP_POS		0x60
> >> +#define ROT_CROP_POS_Y(x)		((x) << 16)
> >> +#define ROT_CROP_POS_X(x)		((x) << 0)
> >> +
> >> +/* Source Crop Size */
> >> +#define ROT_SRC_CROP_SIZE		0x44
> >> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
> >> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
> >> +
> >> +/* Round to nearest aligned value */
> >> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) - 1))) &
> >> (mask))
> >> +/* Minimum limit value */
> >> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
> >> +/* Maximum limit value */
> >> +#define ROT_MAX(max, mask)		((max) & (mask))
> >> +
> >> +#endif /* EXYNOS_REGS_ROTATOR_H */
> >> +
> >> --
> >> 1.7.0.4
> >
> >
Inki Dae Dec. 12, 2012, 9:46 a.m. UTC | #4
One more comment.


> -----Original Message-----
> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> Sent: Wednesday, December 12, 2012 6:26 PM
> To: Inki Dae
> Cc: dri-devel@lists.freedesktop.org; jy0.jeon@samsung.com;
> yj44.cho@samsung.com; jmock.shin@samsung.com; jaejoon.seo@samsung.com;
> kyungmin.park@samsung.com; sw0312.kim@samsung.com; Joonyoung Shim
> Subject: Re: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> 
> Thank's your comment.
> 
> I answer your comment. please check that.
> 
> Thank's
> 
> BR
> Eunchul Kim
> 
> On 12/12/2012 05:29 PM, Inki Dae wrote:
> >
> >
> >> -----Original Message-----
> >> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> >> Sent: Wednesday, December 12, 2012 4:35 PM
> >> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
> >> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com; jmock.shin@samsung.com;
> >> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
> >> chulspro.kim@samsung.com
> >> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> >>
> >> Rotator supports rotation/crop/flip and input/output DMA operations
> >> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
> >> horizontal flip.
> >> and has some limitations(source and destination format have to be same,
> no
> >> scaler)
> >>
> >> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
> >> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
> >> ---
> >>   drivers/gpu/drm/exynos/Kconfig              |    7 +
> >>   drivers/gpu/drm/exynos/Makefile             |    1 +
> >>   drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
> >>   drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
> >>   drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
> >> +++++++++++++++++++++++++++
> >>   drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
> >>   drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
> >>   7 files changed, 939 insertions(+), 0 deletions(-)
> >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >>   create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
> >>
> >> diff --git a/drivers/gpu/drm/exynos/Kconfig
> >> b/drivers/gpu/drm/exynos/Kconfig
> >> index 4915ab6..4860835 100644
> >> --- a/drivers/gpu/drm/exynos/Kconfig
> >> +++ b/drivers/gpu/drm/exynos/Kconfig
> >> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
> >>   	depends on DRM_EXYNOS_IPP
> >>   	help
> >>   	  Choose this option if you want to use Exynos FIMC for DRM.
> >> +
> >> +config DRM_EXYNOS_ROTATOR
> >> +	bool "Exynos DRM Rotator"
> >> +	depends on DRM_EXYNOS_IPP
> >> +	help
> >> +	  Choose this option if you want to use Exynos Rotator for DRM.
> >> +
> >> diff --git a/drivers/gpu/drm/exynos/Makefile
> >> b/drivers/gpu/drm/exynos/Makefile
> >> index 9710024..3b70668 100644
> >> --- a/drivers/gpu/drm/exynos/Makefile
> >> +++ b/drivers/gpu/drm/exynos/Makefile
> >> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
> >> exynos_drm_vidi.o
> >>   exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
> >>   exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
> >>   exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
> >> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+= exynos_drm_rotator.o
> >>
> >>   obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> index 73f02ac..09d884b 100644
> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> >> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
> >>   		goto out_fimc;
> >>   #endif
> >>
> >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> >> +	ret = platform_driver_register(&rotator_driver);
> >> +	if (ret < 0)
> >> +		goto out_rotator;
> >> +#endif
> >> +
> >>   #ifdef CONFIG_DRM_EXYNOS_IPP
> >>   	ret = platform_driver_register(&ipp_driver);
> >>   	if (ret < 0)
> >> @@ -406,6 +412,11 @@ out_drm:
> >>   out_ipp:
> >>   #endif
> >>
> >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> >> +	platform_driver_unregister(&rotator_driver);
> >> +out_rotator:
> >> +#endif
> >> +
> >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> >>   	platform_driver_unregister(&fimc_driver);
> >>   out_fimc:
> >> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
> >>   	platform_driver_unregister(&ipp_driver);
> >>   #endif
> >>
> >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> >> +	platform_driver_unregister(&rotator_driver);
> >> +#endif
> >> +
> >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> >>   	platform_driver_unregister(&fimc_driver);
> >>   #endif
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> index 14f9490..a74e37c 100644
> >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> >> @@ -353,5 +353,6 @@ extern struct platform_driver
> >> exynos_drm_common_hdmi_driver;
> >>   extern struct platform_driver vidi_driver;
> >>   extern struct platform_driver g2d_driver;
> >>   extern struct platform_driver fimc_driver;
> >> +extern struct platform_driver rotator_driver;
> >>   extern struct platform_driver ipp_driver;
> >>   #endif
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >> new file mode 100644
> >> index 0000000..121569c
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> >> @@ -0,0 +1,829 @@
> >> +/*
> >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> >> + * Authors:
> >> + *	YoungJun Cho <yj44.cho@samsung.com>
> >> + *	Eunchul Kim <chulspro.kim@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 Foundationr
> >> + */
> >> +
> >> +#include <linux/kernel.h>
> >> +#include <linux/module.h>
> >> +#include <linux/err.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/io.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/pm_runtime.h>
> >> +
> >> +#include <drm/drmP.h>
> >> +#include <drm/exynos_drm.h>
> >> +#include "regs-rotator.h"
> >> +#include "exynos_drm.h"
> >> +#include "exynos_drm_ipp.h"
> >> +
> >> +/*
> >> + * Rotator supports image crop/rotator and input/output DMA
operations.
> >> + * input DMA reads image data from the memory.
> >> + * output DMA writes image data to memory.
> >> + */
> >> +
> >> +#define get_rot_context(dev)
> >> 	platform_get_drvdata(to_platform_device(dev))
> >> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
> >> +					struct rot_context, ippdrv);
> >> +#define rot_read(offset)		readl(rot->regs + (offset))
> >> +#define rot_write(cfg, offset)	writel(cfg, rot->regs + (offset))
> >> +
> >> +enum rot_irq_status {
> >> +	ROT_IRQ_STATUS_COMPLETE	= 8,
> >> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
> >> +};
> >> +
> >> +/*
> >> + * A structure of limitation.
> >> + *
> >> + * @min_w: minimum width.
> >> + * @min_h: minimum height.
> >> + * @max_w: maximum width.
> >> + * @max_h: maximum height.
> >> + * @align: align size.
> >> + */
> >> +struct rot_limit {
> >> +	u32	min_w;
> >> +	u32	min_h;
> >> +	u32	max_w;
> >> +	u32	max_h;
> >> +	u32	align;
> >> +};
> >> +
> >> +/*
> >> + * A structure of limitation table.
> >> + *
> >> + * @ycbcr420_2p: case of YUV.
> >> + * @rgb888: case of RGB.
> >> + */
> >> +struct rot_limit_table {
> >> +	struct rot_limit	ycbcr420_2p;
> >> +	struct rot_limit	rgb888;
> >> +};
> >> +
> >> +/*
> >> + * A structure of rotator context.
> >> + * @ippdrv: prepare initialization using ippdrv.
> >> + * @regs_res: register resources.
> >> + * @regs: memory mapped io registers.
> >> + * @clock: rotator gate clock.
> >> + * @limit_tbl: limitation of rotator.
> >> + * @irq: irq number.
> >> + * @cur_buf_id: current operation buffer id.
> >> + * @suspended: suspended state.
> >> + */
> >> +struct rot_context {
> >> +	struct exynos_drm_ippdrv	ippdrv;
> >> +	struct resource	*regs_res;
> >> +	void __iomem	*regs;
> >> +	struct clk	*clock;
> >> +	struct rot_limit_table	*limit_tbl;
> >> +	int	irq;
> >> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
> >> +	bool	suspended;
> >> +};
> >> +
> >> +static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
> >> +{
> >> +	u32 val = rot_read(ROT_CONFIG);
> >> +
> >> +	if (enable == true)
> >> +		val |= ROT_CONFIG_IRQ;
> >> +	else
> >> +		val &= ~ROT_CONFIG_IRQ;
> >> +
> >> +	rot_write(val, ROT_CONFIG);
> >> +}
> >> +
> >> +static u32 rotator_reg_get_format(struct rot_context *rot)
> >> +{
> >> +	u32 val = rot_read(ROT_CONTROL);
> >> +
> >> +	val &= ROT_CONTROL_FMT_MASK;
> >> +
> >> +	return val;
> >> +}
> >> +
> >> +static enum rot_irq_status rotator_reg_get_irq_status(struct
> rot_context
> >> *rot)
> >> +{
> >> +	u32 val = rot_read(ROT_STATUS);
> >> +
> >> +	val = ROT_STATUS_IRQ(val);
> >> +
> >> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
> >> +		return ROT_IRQ_STATUS_COMPLETE;
> >> +	else
> >
> > Remove else.
> 
> - done.
> 
> >
> >> +		return ROT_IRQ_STATUS_ILLEGAL;
> >> +}
> >> +
> >> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
> >> +{
> >> +	struct rot_context *rot = arg;
> >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> >> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
> >> +	struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
> >> +	enum rot_irq_status irq_status;
> >> +	u32 val;
> >> +
> >> +	/* Get execution result */
> >> +	irq_status = rotator_reg_get_irq_status(rot);
> >> +
> >> +	/* clear status */
> >> +	val = rot_read(ROT_STATUS);
> >> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
> >> +	rot_write(val, ROT_STATUS);
> >> +
> >> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
> >> +		event_work->ippdrv = ippdrv;
> >> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
> >> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
> >> +		queue_work(ippdrv->event_workq,
> >> +			(struct work_struct *)event_work);
> >> +	} else
> >> +		DRM_ERROR("the SFR is set illegally\n");
> >> +
> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >> +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32
> >> *hsize,
> >> +		u32 *vsize)
> >> +{
> >> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
> >> +	struct rot_limit *limit;
> >> +	u32 mask, val;
> >> +
> >> +	/* Get size limit */
> >> +	if (fmt == ROT_CONTROL_FMT_RGB888)
> >> +		limit = &limit_tbl->rgb888;
> >> +	else
> >> +		limit = &limit_tbl->ycbcr420_2p;
> >> +
> >> +	/* Get mask for rounding to nearest aligned val */
> >> +	mask = ~((1 << limit->align) - 1);
> >> +
> >> +	/* Set aligned width */
> >> +	val = ROT_ALIGN(*hsize, limit->align, mask);
> >> +	if (val < limit->min_w)
> >> +		*hsize = ROT_MIN(limit->min_w, mask);
> >> +	else if (val > limit->max_w)
> >> +		*hsize = ROT_MAX(limit->max_w, mask);
> >> +	else
> >> +		*hsize = val;
> >> +
> >> +	/* Set aligned height */
> >> +	val = ROT_ALIGN(*vsize, limit->align, mask);
> >> +	if (val < limit->min_h)
> >> +		*vsize = ROT_MIN(limit->min_h, mask);
> >> +	else if (val > limit->max_h)
> >> +		*vsize = ROT_MAX(limit->max_h, mask);
> >> +	else
> >> +		*vsize = val;
> >> +}
> >> +
> >> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val;
> >> +
> >> +	val = rot_read(ROT_CONTROL);
> >> +	val &= ~ROT_CONTROL_FMT_MASK;
> >> +
> >> +	switch (fmt) {
> >> +	case DRM_FORMAT_NV12:
> >> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
> >> +		break;
> >> +	case DRM_FORMAT_XRGB8888:
> >> +		val |= ROT_CONTROL_FMT_RGB888;
> >> +		break;
> >> +	default:
> >> +		DRM_ERROR("invalid image format\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	rot_write(val, ROT_CONTROL);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_src_set_size(struct device *dev, int swap,
> >> +		struct drm_exynos_pos *pos,
> >> +		struct drm_exynos_sz *sz)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 fmt, hsize, vsize;
> >> +	u32 val;
> >> +
> >> +	/* Get format */
> >> +	fmt = rotator_reg_get_format(rot);
> >> +
> >> +	/* Align buffer size */
> >> +	hsize = sz->hsize;
> >> +	vsize = sz->vsize;
> >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> >> +
> >> +	/* Set buffer size configuration */
> >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> >> +	rot_write(val, ROT_SRC_BUF_SIZE);
> >> +
> >> +	/* Set crop image position configuration */
> >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> >> +	rot_write(val, ROT_SRC_CROP_POS);
> >> +	val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
> >> +	rot_write(val, ROT_SRC_CROP_SIZE);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_src_set_addr(struct device *dev,
> >> +		struct drm_exynos_ipp_buf_info *buf_info,
> >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> >> +	u32 val, fmt, hsize, vsize;
> >> +	int i;
> >> +
> >> +	/* Set current buf_id */
> >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
> >> +
> >> +	switch (buf_type) {
> >> +	case IPP_BUF_ENQUEUE:
> >> +		/* Set address configuration */
> >> +		for_each_ipp_planar(i)
> >> +			addr[i] = buf_info->base[i];
> >
> > Check NULL.
> 
> - If not copy this state, we need to memset about addr.
>    no need check null.
> 
> >
> >> +
> >> +		/* Get format */
> >> +		fmt = rotator_reg_get_format(rot);
> >> +
> >> +		/* Re-set cb planar for NV12 format */
> >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> >> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
> > 0x00)) {
> >
> > What is 0x00?
> 
> - It is NULL, I changed !addr instead of addr == 0x00
> 
> >
> >> +
> >> +			val = rot_read(ROT_SRC_BUF_SIZE);
> >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> >> +
> >> +			/* Set cb planar */
> >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
> >> +		}
> >> +
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
> >> +		break;
> >> +	case IPP_BUF_DEQUEUE:
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
> >> +		break;
> >> +	default:
> >> +		/* Nothing to do */
> >> +		break;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_dst_set_transf(struct device *dev,
> >> +		enum drm_exynos_degree degree,
> >> +		enum drm_exynos_flip flip)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val;
> >> +
> >> +	/* Set transform configuration */
> >> +	val = rot_read(ROT_CONTROL);
> >> +	val &= ~ROT_CONTROL_FLIP_MASK;
> >> +
> >> +	switch (flip) {
> >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> >> +		val |= ROT_CONTROL_FLIP_VERTICAL;
> >> +		break;
> >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> >> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
> >> +		break;
> >> +	default:
> >> +		/* Flip None */
> >> +		break;
> >> +	}
> >> +
> >> +	val &= ~ROT_CONTROL_ROT_MASK;
> >> +
> >> +	switch (degree) {
> >> +	case EXYNOS_DRM_DEGREE_90:
> >> +		val |= ROT_CONTROL_ROT_90;
> >> +		break;
> >> +	case EXYNOS_DRM_DEGREE_180:
> >> +		val |= ROT_CONTROL_ROT_180;
> >> +		break;
> >> +	case EXYNOS_DRM_DEGREE_270:
> >> +		val |= ROT_CONTROL_ROT_270;
> >> +		break;
> >> +	default:
> >> +		/* Rotation 0 Degree */
> >> +		break;
> >> +	}
> >> +
> >> +	rot_write(val, ROT_CONTROL);
> >> +
> >> +	/* Check degree for setting buffer size swap */
> >> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
> >> +		(degree == EXYNOS_DRM_DEGREE_270))
> >> +		return 1;
> >
> > Correct return type. This function should return 0 or negative.
> 
> - no ~ this return type is boolean true or false.
>    but we need to error handling so, we use integer as you know
>    we reviewed this routine at our local git from our team(you and me).
> 

Ok, then what do the below codes mean?

	/* set transform for rotation, flip */
	if (ops->set_transf) {
		swap = ops->set_transf(ippdrv->dev, config->degree,
			config->flip);
		if (swap < 0) {
			DRM_ERROR("not support tranf.\n");
			return -EINVAL;
		}

If return value should be true or false then make sure error checking.

> >
> >> +	else
> >> +		return 0;
> >
> > Ditto.
> 
> - ditto.
> 
> >
> >> +}
> >> +
> >> +static int rotator_dst_set_size(struct device *dev, int swap,
> >> +		struct drm_exynos_pos *pos,
> >> +		struct drm_exynos_sz *sz)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val, fmt, hsize, vsize;
> >> +
> >> +	/* Get format */
> >> +	fmt = rotator_reg_get_format(rot);
> >
> > Check if fmt is valid or not.
> 
> - added rotator_check_fmt()
> 
> >
> >> +
> >> +	/* Align buffer size */
> >> +	hsize = sz->hsize;
> >> +	vsize = sz->vsize;
> >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> >> +
> >> +	/* Set buffer size configuration */
> >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> >> +	rot_write(val, ROT_DST_BUF_SIZE);
> >> +
> >> +	/* Set crop image position configuration */
> >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> >> +	rot_write(val, ROT_DST_CROP_POS);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_dst_set_addr(struct device *dev,
> >> +		struct drm_exynos_ipp_buf_info *buf_info,
> >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> >> +	u32 val, fmt, hsize, vsize;
> >> +	int i;
> >> +
> >> +	/* Set current buf_id */
> >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
> >> +
> >> +	switch (buf_type) {
> >> +	case IPP_BUF_ENQUEUE:
> >> +		/* Set address configuration */
> >> +		for_each_ipp_planar(i)
> >> +			addr[i] = buf_info->base[i];
> >> +
> >> +		/* Get format */
> >> +		fmt = rotator_reg_get_format(rot);
> >
> > Check if fmt is valid or not.
> 
> - done.
> 
> >
> >> +
> >> +		/* Re-set cb planar for NV12 format */
> >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> >> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
> > 0x00)) {
> >> +			/* Get buf size */
> >> +			val = rot_read(ROT_DST_BUF_SIZE);
> >> +
> >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> >> +
> >> +			/* Set cb planar */
> >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
> >> +		}
> >> +
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
> >> +		break;
> >> +	case IPP_BUF_DEQUEUE:
> >> +		for_each_ipp_planar(i)
> >> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
> >> +		break;
> >> +	default:
> >> +		/* Nothing to do */
> >> +		break;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static struct exynos_drm_ipp_ops rot_src_ops = {
> >> +	.set_fmt	=	rotator_src_set_fmt,
> >> +	.set_size	=	rotator_src_set_size,
> >> +	.set_addr	=	rotator_src_set_addr,
> >> +};
> >> +
> >> +static struct exynos_drm_ipp_ops rot_dst_ops = {
> >> +	.set_transf	=	rotator_dst_set_transf,
> >> +	.set_size	=	rotator_dst_set_size,
> >> +	.set_addr	=	rotator_dst_set_addr,
> >> +};
> >> +
> >> +static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
> >> +{
> >> +	struct drm_exynos_ipp_prop_list *prop_list;
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
> >> +	if (!prop_list) {
> >> +		DRM_ERROR("failed to alloc property list.\n");
> >> +		return -ENOMEM;
> >> +	}
> >> +
> >> +	/*ToDo fix support function list*/
> >> +
> >> +	prop_list->version = 1;
> >> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
> >> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
> >> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
> >> +				(1 << EXYNOS_DRM_DEGREE_90) |
> >> +				(1 << EXYNOS_DRM_DEGREE_180) |
> >> +				(1 << EXYNOS_DRM_DEGREE_270);
> >> +	prop_list->csc = 0;
> >> +	prop_list->crop = 0;
> >> +	prop_list->scale = 0;
> >> +
> >> +	ippdrv->prop_list = prop_list;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_ippdrv_check_property(struct device *dev,
> >> +		struct drm_exynos_ipp_property *property)
> >> +{
> >> +	struct drm_exynos_ipp_config *src_config =
> >> +
> > &property->config[EXYNOS_DRM_OPS_SRC];
> >> +	struct drm_exynos_ipp_config *dst_config =
> >> +
> > &property->config[EXYNOS_DRM_OPS_DST];
> >> +	struct drm_exynos_pos *src_pos = &src_config->pos;
> >> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
> >> +	struct drm_exynos_sz *src_sz = &src_config->sz;
> >> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
> >> +	bool swap = false;
> >> +
> >> +	/* Check format configuration */
> >> +	if (src_config->fmt != dst_config->fmt) {
> >> +		DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	switch (src_config->fmt) {
> >> +	case DRM_FORMAT_XRGB8888:
> >> +	case DRM_FORMAT_NV12:
> >> +		/* No problem */
> >> +		break;
> >> +	default:
> >> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >
> > Use macro instead of switch-case. this just checks only format type.
> 
> - I don't thing so, If we make macro about this.
>    then we got some confusion about this macro at next change.
>    this case switch-case is better.
>    Do you prefer this ? please one more comment.
> 
> #define drm_check_fmt(fmt) if (fmt == DRM_FORMAT_XRGB8888 || fmt ==
> DRM_FORMAT_NV12) \
> 					return true;
> 
> >
> >> +
> >> +	/* Check transform configuration */
> >> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
> >> +		DRM_DEBUG_KMS("%s:not support source-side rotation\n",
> >> +								__func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	switch (dst_config->degree) {
> >> +	case EXYNOS_DRM_DEGREE_90:
> >> +	case EXYNOS_DRM_DEGREE_270:
> >> +		swap = true;
> >> +	case EXYNOS_DRM_DEGREE_0:
> >> +	case EXYNOS_DRM_DEGREE_180:
> >> +		/* No problem */
> >> +		break;
> >> +	default:
> >> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
> >> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
> > __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	switch (dst_config->flip) {
> >> +	case EXYNOS_DRM_FLIP_NONE:
> >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> >> +		/* No problem */
> >> +		break;
> >> +	default:
> >> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >
> > Use macro instead of switch-case. this just checks only flip type.
> 
> - ditto. please one more comment.
>   in my opinion: I prefer enumeration use switch-case.
> 
> >
> >> +
> >> +	/* Check size configuration */
> >> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
> >> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
> >> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	if (swap) {
> >> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
> >> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
> >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > bound\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
> > dst_pos->w))
> >> {
> >> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +	} else {
> >> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
> >> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
> >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > bound\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
> > dst_pos->h))
> >> {
> >> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
> >> +								__func__);
> >> +			return -EINVAL;
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rotator_ippdrv_start(struct device *dev, enum
> >> drm_exynos_ipp_cmd cmd)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	u32 val;
> >> +
> >> +	if (rot->suspended) {
> >> +		DRM_ERROR("suspended state\n");
> >> +		return -EPERM;
> >> +	}
> >> +
> >> +	if (cmd != IPP_CMD_M2M) {
> >> +		DRM_ERROR("not support cmd: %d\n", cmd);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/* Set interrupt enable */
> >> +	rotator_reg_set_irq(rot, true);
> >> +
> >> +	val = rot_read(ROT_CONTROL);
> >> +	val |= ROT_CONTROL_START;
> >> +
> >> +	rot_write(val, ROT_CONTROL);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int __devinit rotator_probe(struct platform_device *pdev)
> >> +{
> >> +	struct device *dev = &pdev->dev;
> >> +	struct rot_context *rot;
> >> +	struct resource *res;
> >> +	struct exynos_drm_ippdrv *ippdrv;
> >> +	int ret;
> >> +
> >> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
> >> +	if (!rot) {
> >> +		dev_err(dev, "failed to allocate rot\n");
> >> +		return -ENOMEM;
> >> +	}
> >> +
> >> +	rot->limit_tbl = (struct rot_limit_table *)
> >> +				platform_get_device_id(pdev)->driver_data;
> >> +
> >> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> +	if (!res) {
> >> +		dev_err(dev, "failed to find registers\n");
> >> +		ret = -ENOENT;
> >> +		goto err_get_resource;
> >> +	}
> >> +
> >> +	rot->regs_res = request_mem_region(res->start, resource_size(res),
> >> +
> > dev_name(dev));
> >> +	if (!rot->regs_res) {
> >> +		dev_err(dev, "failed to claim register region\n");
> >> +		ret = -ENOENT;
> >> +		goto err_get_resource;
> >> +	}
> >> +
> >> +	rot->regs = ioremap(res->start, resource_size(res));
> >> +	if (!rot->regs) {
> >> +		dev_err(dev, "failed to map register\n");
> >> +		ret = -ENXIO;
> >> +		goto err_ioremap;
> >> +	}
> >> +
> >> +	rot->irq = platform_get_irq(pdev, 0);
> >> +	if (rot->irq < 0) {
> >> +		dev_err(dev, "failed to get irq\n");
> >> +		ret = rot->irq;
> >> +		goto err_get_irq;
> >> +	}
> >> +
> >> +	ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler,
> >> +					IRQF_ONESHOT, "drm_rotator", rot);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to request irq\n");
> >> +		goto err_get_irq;
> >> +	}
> >> +
> >> +	rot->clock = clk_get(dev, "rotator");
> >> +	if (IS_ERR_OR_NULL(rot->clock)) {
> >> +		dev_err(dev, "failed to get clock\n");
> >> +		ret = PTR_ERR(rot->clock);
> >> +		goto err_clk_get;
> >> +	}
> >> +
> >> +	pm_runtime_enable(dev);
> >> +
> >> +	ippdrv = &rot->ippdrv;
> >> +	ippdrv->dev = dev;
> >> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
> >> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
> >> +	ippdrv->check_property = rotator_ippdrv_check_property;
> >> +	ippdrv->start = rotator_ippdrv_start;
> >> +	ret = rotator_init_prop_list(ippdrv);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to init property list.\n");
> >> +		goto err_ippdrv_register;
> >> +	}
> >> +
> >> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
> >> +
> >> +	platform_set_drvdata(pdev, rot);
> >> +
> >> +	ret = exynos_drm_ippdrv_register(ippdrv);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to register drm rotator device\n");
> >> +		kfree(ippdrv->prop_list);
> >> +		goto err_ippdrv_register;
> >> +	}
> >> +
> >> +	dev_info(dev, "The exynos rotator is probed successfully\n");
> >> +
> >> +	return 0;
> >> +
> >> +err_ippdrv_register:
> >> +	pm_runtime_disable(dev);
> >> +	clk_put(rot->clock);
> >> +err_clk_get:
> >> +	free_irq(rot->irq, rot);
> >> +err_get_irq:
> >> +	iounmap(rot->regs);
> >> +err_ioremap:
> >> +	release_resource(rot->regs_res);
> >> +	kfree(rot->regs_res);
> >> +err_get_resource:
> >> +	kfree(rot);
> >> +	return ret;
> >> +}
> >> +
> >> +static int __devexit rotator_remove(struct platform_device *pdev)
> >> +{
> >> +	struct device *dev = &pdev->dev;
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> >> +
> >> +	kfree(ippdrv->prop_list);
> >> +	exynos_drm_ippdrv_unregister(ippdrv);
> >> +
> >> +	pm_runtime_disable(dev);
> >> +	clk_put(rot->clock);
> >> +
> >> +	free_irq(rot->irq, rot);
> >> +
> >> +	iounmap(rot->regs);
> >> +
> >> +	release_resource(rot->regs_res);
> >> +	kfree(rot->regs_res);
> >> +
> >> +	kfree(rot);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +struct rot_limit_table rot_limit_tbl = {
> >> +	.ycbcr420_2p = {
> >> +		.min_w = 32,
> >> +		.min_h = 32,
> >> +		.max_w = SZ_32K,
> >> +		.max_h = SZ_32K,
> >> +		.align = 3,
> >> +	},
> >> +	.rgb888 = {
> >> +		.min_w = 8,
> >> +		.min_h = 8,
> >> +		.max_w = SZ_8K,
> >> +		.max_h = SZ_8K,
> >> +		.align = 2,
> >> +	},
> >> +};
> >> +
> >> +struct platform_device_id rotator_driver_ids[] = {
> >> +	{
> >> +		.name		= "exynos-rot",
> >> +		.driver_data	= (unsigned long)&rot_limit_tbl,
> >> +	},
> >> +	{},
> >> +};
> >> +
> >> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
> >> +{
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	if (enable) {
> >> +		clk_enable(rot->clock);
> >> +		rot->suspended = false;
> >> +	} else {
> >> +		clk_disable(rot->clock);
> >> +		rot->suspended = true;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +
> >> +#ifdef CONFIG_PM_SLEEP
> >> +static int rotator_suspend(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	if (pm_runtime_suspended(dev))
> >> +		return 0;
> >> +
> >> +	return rotator_clk_crtl(rot, false);
> >> +}
> >> +
> >> +static int rotator_resume(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	if (!pm_runtime_suspended(dev))
> >> +		return rotator_clk_crtl(rot, true);
> >> +
> >> +	return 0;
> >> +}
> >> +#endif
> >> +
> >> +#ifdef CONFIG_PM_RUNTIME
> >> +static int rotator_runtime_suspend(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	return  rotator_clk_crtl(rot, false);
> >> +}
> >> +
> >> +static int rotator_runtime_resume(struct device *dev)
> >> +{
> >> +	struct rot_context *rot = dev_get_drvdata(dev);
> >> +
> >> +	/* ToDo */
> >> +
> >> +	DRM_DEBUG_KMS("%s\n", __func__);
> >> +
> >> +	return  rotator_clk_crtl(rot, true);
> >> +}
> >> +#endif
> >> +
> >> +static const struct dev_pm_ops rotator_pm_ops = {
> >> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
> >> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
> >> +
> > NULL)
> >> +};
> >> +
> >> +struct platform_driver rotator_driver = {
> >> +	.probe		= rotator_probe,
> >> +	.remove		= __devexit_p(rotator_remove),
> >> +	.id_table	= rotator_driver_ids,
> >> +	.driver		= {
> >> +		.name	= "exynos-rot",
> >> +		.owner	= THIS_MODULE,
> >> +		.pm	= &rotator_pm_ops,
> >> +	},
> >> +};
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >> new file mode 100644
> >> index 0000000..fe929c9
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> >> @@ -0,0 +1,13 @@
> >> +/*
> >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> >> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
> >> + */
> >> +
> >> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
> >> +#define	_EXYNOS_DRM_ROTATOR_H_
> >> +
> >> +#endif
> >> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
> >> b/drivers/gpu/drm/exynos/regs-rotator.h
> >> new file mode 100644
> >> index 0000000..a09ac6e
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
> >> @@ -0,0 +1,73 @@
> >> +/* drivers/gpu/drm/exynos/regs-rotator.h
> >> + *
> >> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> >> + *		http://www.samsung.com/
> >> + *
> >> + * Register definition file for Samsung Rotator Interface (Rotator)
> >> driver
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> +*/
> >> +
> >> +#ifndef EXYNOS_REGS_ROTATOR_H
> >> +#define EXYNOS_REGS_ROTATOR_H
> >> +
> >> +/* Configuration */
> >> +#define ROT_CONFIG			0x00
> >> +#define ROT_CONFIG_IRQ			(3 << 8)
> >> +
> >> +/* Image Control */
> >> +#define ROT_CONTROL			0x10
> >> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
> >> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
> >> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
> >> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
> >> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
> >> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
> >> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
> >> +#define ROT_CONTROL_ROT_90		(1 << 4)
> >> +#define ROT_CONTROL_ROT_180		(2 << 4)
> >> +#define ROT_CONTROL_ROT_270		(3 << 4)
> >> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
> >> +#define ROT_CONTROL_START		(1 << 0)
> >> +
> >> +/* Status */
> >> +#define ROT_STATUS			0x20
> >> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
> >> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
> >> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
> >> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
> >> +
> >> +/* Buffer Address */
> >> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
> >> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
> >> +
> >> +/* Buffer Size */
> >> +#define ROT_SRC_BUF_SIZE		0x3c
> >> +#define ROT_DST_BUF_SIZE		0x5c
> >> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
> >> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
> >> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
> >> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
> >> +
> >> +/* Crop Position */
> >> +#define ROT_SRC_CROP_POS		0x40
> >> +#define ROT_DST_CROP_POS		0x60
> >> +#define ROT_CROP_POS_Y(x)		((x) << 16)
> >> +#define ROT_CROP_POS_X(x)		((x) << 0)
> >> +
> >> +/* Source Crop Size */
> >> +#define ROT_SRC_CROP_SIZE		0x44
> >> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
> >> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
> >> +
> >> +/* Round to nearest aligned value */
> >> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) - 1))) &
> >> (mask))
> >> +/* Minimum limit value */
> >> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
> >> +/* Maximum limit value */
> >> +#define ROT_MAX(max, mask)		((max) & (mask))
> >> +
> >> +#endif /* EXYNOS_REGS_ROTATOR_H */
> >> +
> >> --
> >> 1.7.0.4
> >
> >
Inki Dae Dec. 12, 2012, 10 a.m. UTC | #5
> -----Original Message-----
> From: dri-devel-bounces+inki.dae=samsung.com@lists.freedesktop.org
> [mailto:dri-devel-bounces+inki.dae=samsung.com@lists.freedesktop.org] On
> Behalf Of Inki Dae
> Sent: Wednesday, December 12, 2012 6:42 PM
> To: 'Eunchul Kim'
> Cc: jy0.jeon@samsung.com; sw0312.kim@samsung.com; jaejoon.seo@samsung.com;
> kyungmin.park@samsung.com; dri-devel@lists.freedesktop.org;
> jmock.shin@samsung.com
> Subject: RE: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> 
> 
> 
> > -----Original Message-----
> > From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> > Sent: Wednesday, December 12, 2012 6:26 PM
> > To: Inki Dae
> > Cc: dri-devel@lists.freedesktop.org; jy0.jeon@samsung.com;
> > yj44.cho@samsung.com; jmock.shin@samsung.com; jaejoon.seo@samsung.com;
> > kyungmin.park@samsung.com; sw0312.kim@samsung.com; Joonyoung Shim
> > Subject: Re: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> >
> > Thank's your comment.
> >
> > I answer your comment. please check that.
> >
> > Thank's
> >
> > BR
> > Eunchul Kim
> >
> > On 12/12/2012 05:29 PM, Inki Dae wrote:
> > >
> > >
> > >> -----Original Message-----
> > >> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> > >> Sent: Wednesday, December 12, 2012 4:35 PM
> > >> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
> > >> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com;
> jmock.shin@samsung.com;
> > >> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
> > >> chulspro.kim@samsung.com
> > >> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> > >>
> > >> Rotator supports rotation/crop/flip and input/output DMA operations
> > >> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
> > >> horizontal flip.
> > >> and has some limitations(source and destination format have to be
> same,
> > no
> > >> scaler)
> > >>
> > >> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
> > >> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
> > >> ---
> > >>   drivers/gpu/drm/exynos/Kconfig              |    7 +
> > >>   drivers/gpu/drm/exynos/Makefile             |    1 +
> > >>   drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
> > >>   drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
> > >>   drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
> > >> +++++++++++++++++++++++++++
> > >>   drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
> > >>   drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
> > >>   7 files changed, 939 insertions(+), 0 deletions(-)
> > >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > >>   create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
> > >>
> > >> diff --git a/drivers/gpu/drm/exynos/Kconfig
> > >> b/drivers/gpu/drm/exynos/Kconfig
> > >> index 4915ab6..4860835 100644
> > >> --- a/drivers/gpu/drm/exynos/Kconfig
> > >> +++ b/drivers/gpu/drm/exynos/Kconfig
> > >> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
> > >>   	depends on DRM_EXYNOS_IPP
> > >>   	help
> > >>   	  Choose this option if you want to use Exynos FIMC for DRM.
> > >> +
> > >> +config DRM_EXYNOS_ROTATOR
> > >> +	bool "Exynos DRM Rotator"
> > >> +	depends on DRM_EXYNOS_IPP
> > >> +	help
> > >> +	  Choose this option if you want to use Exynos Rotator for
DRM.
> > >> +
> > >> diff --git a/drivers/gpu/drm/exynos/Makefile
> > >> b/drivers/gpu/drm/exynos/Makefile
> > >> index 9710024..3b70668 100644
> > >> --- a/drivers/gpu/drm/exynos/Makefile
> > >> +++ b/drivers/gpu/drm/exynos/Makefile
> > >> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
> > >> exynos_drm_vidi.o
> > >>   exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
> > >>   exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
> > >>   exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
> > >> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+= exynos_drm_rotator.o
> > >>
> > >>   obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
> > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > >> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > >> index 73f02ac..09d884b 100644
> > >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > >> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
> > >>   		goto out_fimc;
> > >>   #endif
> > >>
> > >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> > >> +	ret = platform_driver_register(&rotator_driver);
> > >> +	if (ret < 0)
> > >> +		goto out_rotator;
> > >> +#endif
> > >> +
> > >>   #ifdef CONFIG_DRM_EXYNOS_IPP
> > >>   	ret = platform_driver_register(&ipp_driver);
> > >>   	if (ret < 0)
> > >> @@ -406,6 +412,11 @@ out_drm:
> > >>   out_ipp:
> > >>   #endif
> > >>
> > >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> > >> +	platform_driver_unregister(&rotator_driver);
> > >> +out_rotator:
> > >> +#endif
> > >> +
> > >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> > >>   	platform_driver_unregister(&fimc_driver);
> > >>   out_fimc:
> > >> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
> > >>   	platform_driver_unregister(&ipp_driver);
> > >>   #endif
> > >>
> > >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> > >> +	platform_driver_unregister(&rotator_driver);
> > >> +#endif
> > >> +
> > >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> > >>   	platform_driver_unregister(&fimc_driver);
> > >>   #endif
> > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > >> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > >> index 14f9490..a74e37c 100644
> > >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > >> @@ -353,5 +353,6 @@ extern struct platform_driver
> > >> exynos_drm_common_hdmi_driver;
> > >>   extern struct platform_driver vidi_driver;
> > >>   extern struct platform_driver g2d_driver;
> > >>   extern struct platform_driver fimc_driver;
> > >> +extern struct platform_driver rotator_driver;
> > >>   extern struct platform_driver ipp_driver;
> > >>   #endif
> > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > >> new file mode 100644
> > >> index 0000000..121569c
> > >> --- /dev/null
> > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > >> @@ -0,0 +1,829 @@
> > >> +/*
> > >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> > >> + * Authors:
> > >> + *	YoungJun Cho <yj44.cho@samsung.com>
> > >> + *	Eunchul Kim <chulspro.kim@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 Foundationr
> > >> + */
> > >> +
> > >> +#include <linux/kernel.h>
> > >> +#include <linux/module.h>
> > >> +#include <linux/err.h>
> > >> +#include <linux/interrupt.h>
> > >> +#include <linux/io.h>
> > >> +#include <linux/platform_device.h>
> > >> +#include <linux/clk.h>
> > >> +#include <linux/pm_runtime.h>
> > >> +
> > >> +#include <drm/drmP.h>
> > >> +#include <drm/exynos_drm.h>
> > >> +#include "regs-rotator.h"
> > >> +#include "exynos_drm.h"
> > >> +#include "exynos_drm_ipp.h"
> > >> +
> > >> +/*
> > >> + * Rotator supports image crop/rotator and input/output DMA
> operations.
> > >> + * input DMA reads image data from the memory.
> > >> + * output DMA writes image data to memory.
> > >> + */
> > >> +
> > >> +#define get_rot_context(dev)
> > >> 	platform_get_drvdata(to_platform_device(dev))
> > >> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
> > >> +					struct rot_context, ippdrv);
> > >> +#define rot_read(offset)		readl(rot->regs + (offset))
> > >> +#define rot_write(cfg, offset)	writel(cfg, rot->regs + (offset))
> > >> +
> > >> +enum rot_irq_status {
> > >> +	ROT_IRQ_STATUS_COMPLETE	= 8,
> > >> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
> > >> +};
> > >> +
> > >> +/*
> > >> + * A structure of limitation.
> > >> + *
> > >> + * @min_w: minimum width.
> > >> + * @min_h: minimum height.
> > >> + * @max_w: maximum width.
> > >> + * @max_h: maximum height.
> > >> + * @align: align size.
> > >> + */
> > >> +struct rot_limit {
> > >> +	u32	min_w;
> > >> +	u32	min_h;
> > >> +	u32	max_w;
> > >> +	u32	max_h;
> > >> +	u32	align;
> > >> +};
> > >> +
> > >> +/*
> > >> + * A structure of limitation table.
> > >> + *
> > >> + * @ycbcr420_2p: case of YUV.
> > >> + * @rgb888: case of RGB.
> > >> + */
> > >> +struct rot_limit_table {
> > >> +	struct rot_limit	ycbcr420_2p;
> > >> +	struct rot_limit	rgb888;
> > >> +};
> > >> +
> > >> +/*
> > >> + * A structure of rotator context.
> > >> + * @ippdrv: prepare initialization using ippdrv.
> > >> + * @regs_res: register resources.
> > >> + * @regs: memory mapped io registers.
> > >> + * @clock: rotator gate clock.
> > >> + * @limit_tbl: limitation of rotator.
> > >> + * @irq: irq number.
> > >> + * @cur_buf_id: current operation buffer id.
> > >> + * @suspended: suspended state.
> > >> + */
> > >> +struct rot_context {
> > >> +	struct exynos_drm_ippdrv	ippdrv;
> > >> +	struct resource	*regs_res;
> > >> +	void __iomem	*regs;
> > >> +	struct clk	*clock;
> > >> +	struct rot_limit_table	*limit_tbl;
> > >> +	int	irq;
> > >> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
> > >> +	bool	suspended;
> > >> +};
> > >> +
> > >> +static void rotator_reg_set_irq(struct rot_context *rot, bool
enable)
> > >> +{
> > >> +	u32 val = rot_read(ROT_CONFIG);
> > >> +
> > >> +	if (enable == true)
> > >> +		val |= ROT_CONFIG_IRQ;
> > >> +	else
> > >> +		val &= ~ROT_CONFIG_IRQ;
> > >> +
> > >> +	rot_write(val, ROT_CONFIG);
> > >> +}
> > >> +
> > >> +static u32 rotator_reg_get_format(struct rot_context *rot)
> > >> +{
> > >> +	u32 val = rot_read(ROT_CONTROL);
> > >> +
> > >> +	val &= ROT_CONTROL_FMT_MASK;
> > >> +
> > >> +	return val;
> > >> +}
> > >> +
> > >> +static enum rot_irq_status rotator_reg_get_irq_status(struct
> > rot_context
> > >> *rot)
> > >> +{
> > >> +	u32 val = rot_read(ROT_STATUS);
> > >> +
> > >> +	val = ROT_STATUS_IRQ(val);
> > >> +
> > >> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
> > >> +		return ROT_IRQ_STATUS_COMPLETE;
> > >> +	else
> > >
> > > Remove else.
> >
> > - done.
> >
> > >
> > >> +		return ROT_IRQ_STATUS_ILLEGAL;
> > >> +}
> > >> +
> > >> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
> > >> +{
> > >> +	struct rot_context *rot = arg;
> > >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> > >> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
> > >> +	struct drm_exynos_ipp_event_work *event_work =
c_node->event_work;
> > >> +	enum rot_irq_status irq_status;
> > >> +	u32 val;
> > >> +
> > >> +	/* Get execution result */
> > >> +	irq_status = rotator_reg_get_irq_status(rot);
> > >> +
> > >> +	/* clear status */
> > >> +	val = rot_read(ROT_STATUS);
> > >> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
> > >> +	rot_write(val, ROT_STATUS);
> > >> +
> > >> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
> > >> +		event_work->ippdrv = ippdrv;
> > >> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
> > >> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
> > >> +		queue_work(ippdrv->event_workq,
> > >> +			(struct work_struct *)event_work);
> > >> +	} else
> > >> +		DRM_ERROR("the SFR is set illegally\n");
> > >> +
> > >> +	return IRQ_HANDLED;
> > >> +}
> > >> +
> > >> +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32
> > >> *hsize,
> > >> +		u32 *vsize)
> > >> +{
> > >> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
> > >> +	struct rot_limit *limit;
> > >> +	u32 mask, val;
> > >> +
> > >> +	/* Get size limit */
> > >> +	if (fmt == ROT_CONTROL_FMT_RGB888)
> > >> +		limit = &limit_tbl->rgb888;
> > >> +	else
> > >> +		limit = &limit_tbl->ycbcr420_2p;
> > >> +
> > >> +	/* Get mask for rounding to nearest aligned val */
> > >> +	mask = ~((1 << limit->align) - 1);
> > >> +
> > >> +	/* Set aligned width */
> > >> +	val = ROT_ALIGN(*hsize, limit->align, mask);
> > >> +	if (val < limit->min_w)
> > >> +		*hsize = ROT_MIN(limit->min_w, mask);
> > >> +	else if (val > limit->max_w)
> > >> +		*hsize = ROT_MAX(limit->max_w, mask);
> > >> +	else
> > >> +		*hsize = val;
> > >> +
> > >> +	/* Set aligned height */
> > >> +	val = ROT_ALIGN(*vsize, limit->align, mask);
> > >> +	if (val < limit->min_h)
> > >> +		*vsize = ROT_MIN(limit->min_h, mask);
> > >> +	else if (val > limit->max_h)
> > >> +		*vsize = ROT_MAX(limit->max_h, mask);
> > >> +	else
> > >> +		*vsize = val;
> > >> +}
> > >> +
> > >> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	u32 val;
> > >> +
> > >> +	val = rot_read(ROT_CONTROL);
> > >> +	val &= ~ROT_CONTROL_FMT_MASK;
> > >> +
> > >> +	switch (fmt) {
> > >> +	case DRM_FORMAT_NV12:
> > >> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
> > >> +		break;
> > >> +	case DRM_FORMAT_XRGB8888:
> > >> +		val |= ROT_CONTROL_FMT_RGB888;
> > >> +		break;
> > >> +	default:
> > >> +		DRM_ERROR("invalid image format\n");
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	rot_write(val, ROT_CONTROL);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int rotator_src_set_size(struct device *dev, int swap,
> > >> +		struct drm_exynos_pos *pos,
> > >> +		struct drm_exynos_sz *sz)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	u32 fmt, hsize, vsize;
> > >> +	u32 val;
> > >> +
> > >> +	/* Get format */
> > >> +	fmt = rotator_reg_get_format(rot);
> > >> +
> > >> +	/* Align buffer size */
> > >> +	hsize = sz->hsize;
> > >> +	vsize = sz->vsize;
> > >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> > >> +
> > >> +	/* Set buffer size configuration */
> > >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> > >> +	rot_write(val, ROT_SRC_BUF_SIZE);
> > >> +
> > >> +	/* Set crop image position configuration */
> > >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> > >> +	rot_write(val, ROT_SRC_CROP_POS);
> > >> +	val = ROT_SRC_CROP_SIZE_H(pos->h) |
ROT_SRC_CROP_SIZE_W(pos->w);
> > >> +	rot_write(val, ROT_SRC_CROP_SIZE);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int rotator_src_set_addr(struct device *dev,
> > >> +		struct drm_exynos_ipp_buf_info *buf_info,
> > >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> > >> +	u32 val, fmt, hsize, vsize;
> > >> +	int i;
> > >> +
> > >> +	/* Set current buf_id */
> > >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
> > >> +
> > >> +	switch (buf_type) {
> > >> +	case IPP_BUF_ENQUEUE:
> > >> +		/* Set address configuration */
> > >> +		for_each_ipp_planar(i)
> > >> +			addr[i] = buf_info->base[i];
> > >
> > > Check NULL.
> >
> > - If not copy this state, we need to memset about addr.
> >    no need check null.
> >
> > >
> > >> +
> > >> +		/* Get format */
> > >> +		fmt = rotator_reg_get_format(rot);
> > >> +
> > >> +		/* Re-set cb planar for NV12 format */
> > >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> > >> +					(addr[EXYNOS_DRM_PLANAR_CB]
==
> > > 0x00)) {
> > >
> > > What is 0x00?
> >
> > - It is NULL, I changed !addr instead of addr == 0x00
> >
> > >
> > >> +
> > >> +			val = rot_read(ROT_SRC_BUF_SIZE);
> > >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> > >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> > >> +
> > >> +			/* Set cb planar */
> > >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> > >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize *
vsize;
> > >> +		}
> > >> +
> > >> +		for_each_ipp_planar(i)
> > >> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
> > >> +		break;
> > >> +	case IPP_BUF_DEQUEUE:
> > >> +		for_each_ipp_planar(i)
> > >> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
> > >> +		break;
> > >> +	default:
> > >> +		/* Nothing to do */
> > >> +		break;
> > >> +	}
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int rotator_dst_set_transf(struct device *dev,
> > >> +		enum drm_exynos_degree degree,
> > >> +		enum drm_exynos_flip flip)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	u32 val;
> > >> +
> > >> +	/* Set transform configuration */
> > >> +	val = rot_read(ROT_CONTROL);
> > >> +	val &= ~ROT_CONTROL_FLIP_MASK;
> > >> +
> > >> +	switch (flip) {
> > >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> > >> +		val |= ROT_CONTROL_FLIP_VERTICAL;
> > >> +		break;
> > >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> > >> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
> > >> +		break;
> > >> +	default:
> > >> +		/* Flip None */
> > >> +		break;
> > >> +	}
> > >> +
> > >> +	val &= ~ROT_CONTROL_ROT_MASK;
> > >> +
> > >> +	switch (degree) {
> > >> +	case EXYNOS_DRM_DEGREE_90:
> > >> +		val |= ROT_CONTROL_ROT_90;
> > >> +		break;
> > >> +	case EXYNOS_DRM_DEGREE_180:
> > >> +		val |= ROT_CONTROL_ROT_180;
> > >> +		break;
> > >> +	case EXYNOS_DRM_DEGREE_270:
> > >> +		val |= ROT_CONTROL_ROT_270;
> > >> +		break;
> > >> +	default:
> > >> +		/* Rotation 0 Degree */
> > >> +		break;
> > >> +	}
> > >> +
> > >> +	rot_write(val, ROT_CONTROL);
> > >> +
> > >> +	/* Check degree for setting buffer size swap */
> > >> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
> > >> +		(degree == EXYNOS_DRM_DEGREE_270))
> > >> +		return 1;
> > >
> > > Correct return type. This function should return 0 or negative.
> >
> > - no ~ this return type is boolean true or false.
> >    but we need to error handling so, we use integer as you know
> >    we reviewed this routine at our local git from our team(you and me).
> >
> > >
> > >> +	else
> > >> +		return 0;
> > >
> > > Ditto.
> >
> > - ditto.
> >
> > >
> > >> +}
> > >> +
> > >> +static int rotator_dst_set_size(struct device *dev, int swap,
> > >> +		struct drm_exynos_pos *pos,
> > >> +		struct drm_exynos_sz *sz)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	u32 val, fmt, hsize, vsize;
> > >> +
> > >> +	/* Get format */
> > >> +	fmt = rotator_reg_get_format(rot);
> > >
> > > Check if fmt is valid or not.
> >
> > - added rotator_check_fmt()
> >
> > >
> > >> +
> > >> +	/* Align buffer size */
> > >> +	hsize = sz->hsize;
> > >> +	vsize = sz->vsize;
> > >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> > >> +
> > >> +	/* Set buffer size configuration */
> > >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> > >> +	rot_write(val, ROT_DST_BUF_SIZE);
> > >> +
> > >> +	/* Set crop image position configuration */
> > >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> > >> +	rot_write(val, ROT_DST_CROP_POS);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int rotator_dst_set_addr(struct device *dev,
> > >> +		struct drm_exynos_ipp_buf_info *buf_info,
> > >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> > >> +	u32 val, fmt, hsize, vsize;
> > >> +	int i;
> > >> +
> > >> +	/* Set current buf_id */
> > >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
> > >> +
> > >> +	switch (buf_type) {
> > >> +	case IPP_BUF_ENQUEUE:
> > >> +		/* Set address configuration */
> > >> +		for_each_ipp_planar(i)
> > >> +			addr[i] = buf_info->base[i];
> > >> +
> > >> +		/* Get format */
> > >> +		fmt = rotator_reg_get_format(rot);
> > >
> > > Check if fmt is valid or not.
> >
> > - done.
> >
> > >
> > >> +
> > >> +		/* Re-set cb planar for NV12 format */
> > >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> > >> +					(addr[EXYNOS_DRM_PLANAR_CB]
==
> > > 0x00)) {
> > >> +			/* Get buf size */
> > >> +			val = rot_read(ROT_DST_BUF_SIZE);
> > >> +
> > >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> > >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> > >> +
> > >> +			/* Set cb planar */
> > >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> > >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize *
vsize;
> > >> +		}
> > >> +
> > >> +		for_each_ipp_planar(i)
> > >> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
> > >> +		break;
> > >> +	case IPP_BUF_DEQUEUE:
> > >> +		for_each_ipp_planar(i)
> > >> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
> > >> +		break;
> > >> +	default:
> > >> +		/* Nothing to do */
> > >> +		break;
> > >> +	}
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static struct exynos_drm_ipp_ops rot_src_ops = {
> > >> +	.set_fmt	=	rotator_src_set_fmt,
> > >> +	.set_size	=	rotator_src_set_size,
> > >> +	.set_addr	=	rotator_src_set_addr,
> > >> +};
> > >> +
> > >> +static struct exynos_drm_ipp_ops rot_dst_ops = {
> > >> +	.set_transf	=	rotator_dst_set_transf,
> > >> +	.set_size	=	rotator_dst_set_size,
> > >> +	.set_addr	=	rotator_dst_set_addr,
> > >> +};
> > >> +
> > >> +static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
> > >> +{
> > >> +	struct drm_exynos_ipp_prop_list *prop_list;
> > >> +
> > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > >> +
> > >> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
> > >> +	if (!prop_list) {
> > >> +		DRM_ERROR("failed to alloc property list.\n");
> > >> +		return -ENOMEM;
> > >> +	}
> > >> +
> > >> +	/*ToDo fix support function list*/
> > >> +
> > >> +	prop_list->version = 1;
> > >> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
> > >> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
> > >> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
> > >> +				(1 << EXYNOS_DRM_DEGREE_90) |
> > >> +				(1 << EXYNOS_DRM_DEGREE_180) |
> > >> +				(1 << EXYNOS_DRM_DEGREE_270);
> > >> +	prop_list->csc = 0;
> > >> +	prop_list->crop = 0;
> > >> +	prop_list->scale = 0;
> > >> +
> > >> +	ippdrv->prop_list = prop_list;
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int rotator_ippdrv_check_property(struct device *dev,
> > >> +		struct drm_exynos_ipp_property *property)
> > >> +{
> > >> +	struct drm_exynos_ipp_config *src_config =
> > >> +
> > > &property->config[EXYNOS_DRM_OPS_SRC];
> > >> +	struct drm_exynos_ipp_config *dst_config =
> > >> +
> > > &property->config[EXYNOS_DRM_OPS_DST];
> > >> +	struct drm_exynos_pos *src_pos = &src_config->pos;
> > >> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
> > >> +	struct drm_exynos_sz *src_sz = &src_config->sz;
> > >> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
> > >> +	bool swap = false;
> > >> +
> > >> +	/* Check format configuration */
> > >> +	if (src_config->fmt != dst_config->fmt) {
> > >> +		DRM_DEBUG_KMS("%s:not support csc feature\n",
__func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	switch (src_config->fmt) {
> > >> +	case DRM_FORMAT_XRGB8888:
> > >> +	case DRM_FORMAT_NV12:
> > >> +		/* No problem */
> > >> +		break;
> > >> +	default:
> > >> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >
> > > Use macro instead of switch-case. this just checks only format type.
> >
> > - I don't thing so, If we make macro about this.
> >    then we got some confusion about this macro at next change.
> >    this case switch-case is better.
> >    Do you prefer this ? please one more comment.
> >
> > #define drm_check_fmt(fmt) if (fmt == DRM_FORMAT_XRGB8888 || fmt ==
> > DRM_FORMAT_NV12) \
> > 					return true;
> 
> #define drm_check_fmt(fmt)		((fmt & (DRM_FORMAT_XRGB8888 |
> DRM_FORMAT_NV12)) ? true : false)
> 
> if (drm_check_fmt(src_config->fmt))
> 	Something;
> else
> 	Something;

Again,

#define DRM_FORMAT_MASK			(DRM_FORMAT_XRGB888 |
DRM_FORMAT_NV12)
 #define drm_check_fmt(fmt)		(((fmt & DRM_FORMAT_MASK) &
(DRM_FORMAT_XRGB8888 | \
	
DRM_FORMAT_NV12)) ? true : false)
 
 if (drm_check_fmt(src_config->fmt))
 	Something;
 else
 	Something;


> 
> >
> > >
> > >> +
> > >> +	/* Check transform configuration */
> > >> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
> > >> +		DRM_DEBUG_KMS("%s:not support source-side
rotation\n",
> > >> +
__func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	switch (dst_config->degree) {
> > >> +	case EXYNOS_DRM_DEGREE_90:
> > >> +	case EXYNOS_DRM_DEGREE_270:
> > >> +		swap = true;
> > >> +	case EXYNOS_DRM_DEGREE_0:
> > >> +	case EXYNOS_DRM_DEGREE_180:
> > >> +		/* No problem */
> > >> +		break;
> > >> +	default:
> > >> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
> > >> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
> > > __func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	switch (dst_config->flip) {
> > >> +	case EXYNOS_DRM_FLIP_NONE:
> > >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> > >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> > >> +		/* No problem */
> > >> +		break;
> > >> +	default:
> > >> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >
> > > Use macro instead of switch-case. this just checks only flip type.
> >
> > - ditto. please one more comment.
> >   in my opinion: I prefer enumeration use switch-case.
> 
> What does each case do? Just check? If so, use macro as I mentioned. We
> can't accept this codes.
> 
> >
> > >
> > >> +
> > >> +	/* Check size configuration */
> > >> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
> > >> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
> > >> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n",
__func__);
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	if (swap) {
> > >> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
> > >> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
> > >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > > bound\n",
> > >> +
__func__);
> > >> +			return -EINVAL;
> > >> +		}
> > >> +
> > >> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
> > > dst_pos->w))
> > >> {
> > >> +			DRM_DEBUG_KMS("%s:not support scale
feature\n",
> > >> +
__func__);
> > >> +			return -EINVAL;
> > >> +		}
> > >> +	} else {
> > >> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
> > >> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
> > >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > > bound\n",
> > >> +
__func__);
> > >> +			return -EINVAL;
> > >> +		}
> > >> +
> > >> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
> > > dst_pos->h))
> > >> {
> > >> +			DRM_DEBUG_KMS("%s:not support scale
feature\n",
> > >> +
__func__);
> > >> +			return -EINVAL;
> > >> +		}
> > >> +	}
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int rotator_ippdrv_start(struct device *dev, enum
> > >> drm_exynos_ipp_cmd cmd)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	u32 val;
> > >> +
> > >> +	if (rot->suspended) {
> > >> +		DRM_ERROR("suspended state\n");
> > >> +		return -EPERM;
> > >> +	}
> > >> +
> > >> +	if (cmd != IPP_CMD_M2M) {
> > >> +		DRM_ERROR("not support cmd: %d\n", cmd);
> > >> +		return -EINVAL;
> > >> +	}
> > >> +
> > >> +	/* Set interrupt enable */
> > >> +	rotator_reg_set_irq(rot, true);
> > >> +
> > >> +	val = rot_read(ROT_CONTROL);
> > >> +	val |= ROT_CONTROL_START;
> > >> +
> > >> +	rot_write(val, ROT_CONTROL);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int __devinit rotator_probe(struct platform_device *pdev)
> > >> +{
> > >> +	struct device *dev = &pdev->dev;
> > >> +	struct rot_context *rot;
> > >> +	struct resource *res;
> > >> +	struct exynos_drm_ippdrv *ippdrv;
> > >> +	int ret;
> > >> +
> > >> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
> > >> +	if (!rot) {
> > >> +		dev_err(dev, "failed to allocate rot\n");
> > >> +		return -ENOMEM;
> > >> +	}
> > >> +
> > >> +	rot->limit_tbl = (struct rot_limit_table *)
> > >> +
platform_get_device_id(pdev)->driver_data;
> > >> +
> > >> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > >> +	if (!res) {
> > >> +		dev_err(dev, "failed to find registers\n");
> > >> +		ret = -ENOENT;
> > >> +		goto err_get_resource;
> > >> +	}
> > >> +
> > >> +	rot->regs_res = request_mem_region(res->start,
resource_size(res),
> > >> +
> > > dev_name(dev));
> > >> +	if (!rot->regs_res) {
> > >> +		dev_err(dev, "failed to claim register region\n");
> > >> +		ret = -ENOENT;
> > >> +		goto err_get_resource;
> > >> +	}
> > >> +
> > >> +	rot->regs = ioremap(res->start, resource_size(res));
> > >> +	if (!rot->regs) {
> > >> +		dev_err(dev, "failed to map register\n");
> > >> +		ret = -ENXIO;
> > >> +		goto err_ioremap;
> > >> +	}
> > >> +
> > >> +	rot->irq = platform_get_irq(pdev, 0);
> > >> +	if (rot->irq < 0) {
> > >> +		dev_err(dev, "failed to get irq\n");
> > >> +		ret = rot->irq;
> > >> +		goto err_get_irq;
> > >> +	}
> > >> +
> > >> +	ret = request_threaded_irq(rot->irq, NULL,
rotator_irq_handler,
> > >> +					IRQF_ONESHOT, "drm_rotator",
rot);
> > >> +	if (ret < 0) {
> > >> +		dev_err(dev, "failed to request irq\n");
> > >> +		goto err_get_irq;
> > >> +	}
> > >> +
> > >> +	rot->clock = clk_get(dev, "rotator");
> > >> +	if (IS_ERR_OR_NULL(rot->clock)) {
> > >> +		dev_err(dev, "failed to get clock\n");
> > >> +		ret = PTR_ERR(rot->clock);
> > >> +		goto err_clk_get;
> > >> +	}
> > >> +
> > >> +	pm_runtime_enable(dev);
> > >> +
> > >> +	ippdrv = &rot->ippdrv;
> > >> +	ippdrv->dev = dev;
> > >> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
> > >> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
> > >> +	ippdrv->check_property = rotator_ippdrv_check_property;
> > >> +	ippdrv->start = rotator_ippdrv_start;
> > >> +	ret = rotator_init_prop_list(ippdrv);
> > >> +	if (ret < 0) {
> > >> +		dev_err(dev, "failed to init property list.\n");
> > >> +		goto err_ippdrv_register;
> > >> +	}
> > >> +
> > >> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
> > >> +
> > >> +	platform_set_drvdata(pdev, rot);
> > >> +
> > >> +	ret = exynos_drm_ippdrv_register(ippdrv);
> > >> +	if (ret < 0) {
> > >> +		dev_err(dev, "failed to register drm rotator
device\n");
> > >> +		kfree(ippdrv->prop_list);
> > >> +		goto err_ippdrv_register;
> > >> +	}
> > >> +
> > >> +	dev_info(dev, "The exynos rotator is probed
successfully\n");
> > >> +
> > >> +	return 0;
> > >> +
> > >> +err_ippdrv_register:
> > >> +	pm_runtime_disable(dev);
> > >> +	clk_put(rot->clock);
> > >> +err_clk_get:
> > >> +	free_irq(rot->irq, rot);
> > >> +err_get_irq:
> > >> +	iounmap(rot->regs);
> > >> +err_ioremap:
> > >> +	release_resource(rot->regs_res);
> > >> +	kfree(rot->regs_res);
> > >> +err_get_resource:
> > >> +	kfree(rot);
> > >> +	return ret;
> > >> +}
> > >> +
> > >> +static int __devexit rotator_remove(struct platform_device *pdev)
> > >> +{
> > >> +	struct device *dev = &pdev->dev;
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> > >> +
> > >> +	kfree(ippdrv->prop_list);
> > >> +	exynos_drm_ippdrv_unregister(ippdrv);
> > >> +
> > >> +	pm_runtime_disable(dev);
> > >> +	clk_put(rot->clock);
> > >> +
> > >> +	free_irq(rot->irq, rot);
> > >> +
> > >> +	iounmap(rot->regs);
> > >> +
> > >> +	release_resource(rot->regs_res);
> > >> +	kfree(rot->regs_res);
> > >> +
> > >> +	kfree(rot);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +struct rot_limit_table rot_limit_tbl = {
> > >> +	.ycbcr420_2p = {
> > >> +		.min_w = 32,
> > >> +		.min_h = 32,
> > >> +		.max_w = SZ_32K,
> > >> +		.max_h = SZ_32K,
> > >> +		.align = 3,
> > >> +	},
> > >> +	.rgb888 = {
> > >> +		.min_w = 8,
> > >> +		.min_h = 8,
> > >> +		.max_w = SZ_8K,
> > >> +		.max_h = SZ_8K,
> > >> +		.align = 2,
> > >> +	},
> > >> +};
> > >> +
> > >> +struct platform_device_id rotator_driver_ids[] = {
> > >> +	{
> > >> +		.name		= "exynos-rot",
> > >> +		.driver_data	= (unsigned long)&rot_limit_tbl,
> > >> +	},
> > >> +	{},
> > >> +};
> > >> +
> > >> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
> > >> +{
> > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > >> +
> > >> +	if (enable) {
> > >> +		clk_enable(rot->clock);
> > >> +		rot->suspended = false;
> > >> +	} else {
> > >> +		clk_disable(rot->clock);
> > >> +		rot->suspended = true;
> > >> +	}
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +
> > >> +#ifdef CONFIG_PM_SLEEP
> > >> +static int rotator_suspend(struct device *dev)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +
> > >> +	/* ToDo */
> > >> +
> > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > >> +
> > >> +	if (pm_runtime_suspended(dev))
> > >> +		return 0;
> > >> +
> > >> +	return rotator_clk_crtl(rot, false);
> > >> +}
> > >> +
> > >> +static int rotator_resume(struct device *dev)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +
> > >> +	/* ToDo */
> > >> +
> > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > >> +
> > >> +	if (!pm_runtime_suspended(dev))
> > >> +		return rotator_clk_crtl(rot, true);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +#endif
> > >> +
> > >> +#ifdef CONFIG_PM_RUNTIME
> > >> +static int rotator_runtime_suspend(struct device *dev)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +
> > >> +	/* ToDo */
> > >> +
> > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > >> +
> > >> +	return  rotator_clk_crtl(rot, false);
> > >> +}
> > >> +
> > >> +static int rotator_runtime_resume(struct device *dev)
> > >> +{
> > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > >> +
> > >> +	/* ToDo */
> > >> +
> > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > >> +
> > >> +	return  rotator_clk_crtl(rot, true);
> > >> +}
> > >> +#endif
> > >> +
> > >> +static const struct dev_pm_ops rotator_pm_ops = {
> > >> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
> > >> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend,
rotator_runtime_resume,
> > >> +
> > > NULL)
> > >> +};
> > >> +
> > >> +struct platform_driver rotator_driver = {
> > >> +	.probe		= rotator_probe,
> > >> +	.remove		= __devexit_p(rotator_remove),
> > >> +	.id_table	= rotator_driver_ids,
> > >> +	.driver		= {
> > >> +		.name	= "exynos-rot",
> > >> +		.owner	= THIS_MODULE,
> > >> +		.pm	= &rotator_pm_ops,
> > >> +	},
> > >> +};
> > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > >> new file mode 100644
> > >> index 0000000..fe929c9
> > >> --- /dev/null
> > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > >> @@ -0,0 +1,13 @@
> > >> +/*
> > >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> > >> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
> > >> + */
> > >> +
> > >> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
> > >> +#define	_EXYNOS_DRM_ROTATOR_H_
> > >> +
> > >> +#endif
> > >> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
> > >> b/drivers/gpu/drm/exynos/regs-rotator.h
> > >> new file mode 100644
> > >> index 0000000..a09ac6e
> > >> --- /dev/null
> > >> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
> > >> @@ -0,0 +1,73 @@
> > >> +/* drivers/gpu/drm/exynos/regs-rotator.h
> > >> + *
> > >> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> > >> + *		http://www.samsung.com/
> > >> + *
> > >> + * Register definition file for Samsung Rotator Interface (Rotator)
> > >> driver
> > >> + *
> > >> + * This program is free software; you can redistribute it and/or
> > modify
> > >> + * it under the terms of the GNU General Public License version 2 as
> > >> + * published by the Free Software Foundation.
> > >> +*/
> > >> +
> > >> +#ifndef EXYNOS_REGS_ROTATOR_H
> > >> +#define EXYNOS_REGS_ROTATOR_H
> > >> +
> > >> +/* Configuration */
> > >> +#define ROT_CONFIG			0x00
> > >> +#define ROT_CONFIG_IRQ			(3 << 8)
> > >> +
> > >> +/* Image Control */
> > >> +#define ROT_CONTROL			0x10
> > >> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
> > >> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
> > >> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
> > >> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
> > >> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
> > >> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
> > >> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
> > >> +#define ROT_CONTROL_ROT_90		(1 << 4)
> > >> +#define ROT_CONTROL_ROT_180		(2 << 4)
> > >> +#define ROT_CONTROL_ROT_270		(3 << 4)
> > >> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
> > >> +#define ROT_CONTROL_START		(1 << 0)
> > >> +
> > >> +/* Status */
> > >> +#define ROT_STATUS			0x20
> > >> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
> > >> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
> > >> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
> > >> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
> > >> +
> > >> +/* Buffer Address */
> > >> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
> > >> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
> > >> +
> > >> +/* Buffer Size */
> > >> +#define ROT_SRC_BUF_SIZE		0x3c
> > >> +#define ROT_DST_BUF_SIZE		0x5c
> > >> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
> > >> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
> > >> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
> > >> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
> > >> +
> > >> +/* Crop Position */
> > >> +#define ROT_SRC_CROP_POS		0x40
> > >> +#define ROT_DST_CROP_POS		0x60
> > >> +#define ROT_CROP_POS_Y(x)		((x) << 16)
> > >> +#define ROT_CROP_POS_X(x)		((x) << 0)
> > >> +
> > >> +/* Source Crop Size */
> > >> +#define ROT_SRC_CROP_SIZE		0x44
> > >> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
> > >> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
> > >> +
> > >> +/* Round to nearest aligned value */
> > >> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) -
1))) &
> > >> (mask))
> > >> +/* Minimum limit value */
> > >> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
> > >> +/* Maximum limit value */
> > >> +#define ROT_MAX(max, mask)		((max) & (mask))
> > >> +
> > >> +#endif /* EXYNOS_REGS_ROTATOR_H */
> > >> +
> > >> --
> > >> 1.7.0.4
> > >
> > >
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Inki Dae Dec. 12, 2012, 10:39 a.m. UTC | #6
> -----Original Message-----
> From: Inki Dae [mailto:inki.dae@samsung.com]
> Sent: Wednesday, December 12, 2012 7:01 PM
> To: 'Inki Dae'; 'Eunchul Kim'
> Cc: jy0.jeon@samsung.com; sw0312.kim@samsung.com; jaejoon.seo@samsung.com;
> kyungmin.park@samsung.com; dri-devel@lists.freedesktop.org;
> jmock.shin@samsung.com
> Subject: RE: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> 
> 
> 
> > -----Original Message-----
> > From: dri-devel-bounces+inki.dae=samsung.com@lists.freedesktop.org
> > [mailto:dri-devel-bounces+inki.dae=samsung.com@lists.freedesktop.org] On
> > Behalf Of Inki Dae
> > Sent: Wednesday, December 12, 2012 6:42 PM
> > To: 'Eunchul Kim'
> > Cc: jy0.jeon@samsung.com; sw0312.kim@samsung.com;
> jaejoon.seo@samsung.com;
> > kyungmin.park@samsung.com; dri-devel@lists.freedesktop.org;
> > jmock.shin@samsung.com
> > Subject: RE: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> >
> >
> >
> > > -----Original Message-----
> > > From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> > > Sent: Wednesday, December 12, 2012 6:26 PM
> > > To: Inki Dae
> > > Cc: dri-devel@lists.freedesktop.org; jy0.jeon@samsung.com;
> > > yj44.cho@samsung.com; jmock.shin@samsung.com; jaejoon.seo@samsung.com;
> > > kyungmin.park@samsung.com; sw0312.kim@samsung.com; Joonyoung Shim
> > > Subject: Re: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> > >
> > > Thank's your comment.
> > >
> > > I answer your comment. please check that.
> > >
> > > Thank's
> > >
> > > BR
> > > Eunchul Kim
> > >
> > > On 12/12/2012 05:29 PM, Inki Dae wrote:
> > > >
> > > >
> > > >> -----Original Message-----
> > > >> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
> > > >> Sent: Wednesday, December 12, 2012 4:35 PM
> > > >> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
> > > >> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com;
> > jmock.shin@samsung.com;
> > > >> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
> > > >> chulspro.kim@samsung.com
> > > >> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
> > > >>
> > > >> Rotator supports rotation/crop/flip and input/output DMA operations
> > > >> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
> > > >> horizontal flip.
> > > >> and has some limitations(source and destination format have to be
> > same,
> > > no
> > > >> scaler)
> > > >>
> > > >> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
> > > >> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
> > > >> ---
> > > >>   drivers/gpu/drm/exynos/Kconfig              |    7 +
> > > >>   drivers/gpu/drm/exynos/Makefile             |    1 +
> > > >>   drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
> > > >>   drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
> > > >>   drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
> > > >> +++++++++++++++++++++++++++
> > > >>   drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
> > > >>   drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
> > > >>   7 files changed, 939 insertions(+), 0 deletions(-)
> > > >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > > >>   create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > > >>   create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
> > > >>
> > > >> diff --git a/drivers/gpu/drm/exynos/Kconfig
> > > >> b/drivers/gpu/drm/exynos/Kconfig
> > > >> index 4915ab6..4860835 100644
> > > >> --- a/drivers/gpu/drm/exynos/Kconfig
> > > >> +++ b/drivers/gpu/drm/exynos/Kconfig
> > > >> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
> > > >>   	depends on DRM_EXYNOS_IPP
> > > >>   	help
> > > >>   	  Choose this option if you want to use Exynos FIMC for DRM.
> > > >> +
> > > >> +config DRM_EXYNOS_ROTATOR
> > > >> +	bool "Exynos DRM Rotator"
> > > >> +	depends on DRM_EXYNOS_IPP
> > > >> +	help
> > > >> +	  Choose this option if you want to use Exynos Rotator for
> DRM.
> > > >> +
> > > >> diff --git a/drivers/gpu/drm/exynos/Makefile
> > > >> b/drivers/gpu/drm/exynos/Makefile
> > > >> index 9710024..3b70668 100644
> > > >> --- a/drivers/gpu/drm/exynos/Makefile
> > > >> +++ b/drivers/gpu/drm/exynos/Makefile
> > > >> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
> > > >> exynos_drm_vidi.o
> > > >>   exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
> > > >>   exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
> > > >>   exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
> > > >> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+=
exynos_drm_rotator.o
> > > >>
> > > >>   obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
> > > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > > >> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > > >> index 73f02ac..09d884b 100644
> > > >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> > > >> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
> > > >>   		goto out_fimc;
> > > >>   #endif
> > > >>
> > > >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> > > >> +	ret = platform_driver_register(&rotator_driver);
> > > >> +	if (ret < 0)
> > > >> +		goto out_rotator;
> > > >> +#endif
> > > >> +
> > > >>   #ifdef CONFIG_DRM_EXYNOS_IPP
> > > >>   	ret = platform_driver_register(&ipp_driver);
> > > >>   	if (ret < 0)
> > > >> @@ -406,6 +412,11 @@ out_drm:
> > > >>   out_ipp:
> > > >>   #endif
> > > >>
> > > >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> > > >> +	platform_driver_unregister(&rotator_driver);
> > > >> +out_rotator:
> > > >> +#endif
> > > >> +
> > > >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> > > >>   	platform_driver_unregister(&fimc_driver);
> > > >>   out_fimc:
> > > >> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
> > > >>   	platform_driver_unregister(&ipp_driver);
> > > >>   #endif
> > > >>
> > > >> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
> > > >> +	platform_driver_unregister(&rotator_driver);
> > > >> +#endif
> > > >> +
> > > >>   #ifdef CONFIG_DRM_EXYNOS_FIMC
> > > >>   	platform_driver_unregister(&fimc_driver);
> > > >>   #endif
> > > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > > >> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > > >> index 14f9490..a74e37c 100644
> > > >> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
> > > >> @@ -353,5 +353,6 @@ extern struct platform_driver
> > > >> exynos_drm_common_hdmi_driver;
> > > >>   extern struct platform_driver vidi_driver;
> > > >>   extern struct platform_driver g2d_driver;
> > > >>   extern struct platform_driver fimc_driver;
> > > >> +extern struct platform_driver rotator_driver;
> > > >>   extern struct platform_driver ipp_driver;
> > > >>   #endif
> > > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > > >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > > >> new file mode 100644
> > > >> index 0000000..121569c
> > > >> --- /dev/null
> > > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
> > > >> @@ -0,0 +1,829 @@
> > > >> +/*
> > > >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> > > >> + * Authors:
> > > >> + *	YoungJun Cho <yj44.cho@samsung.com>
> > > >> + *	Eunchul Kim <chulspro.kim@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 Foundationr
> > > >> + */
> > > >> +
> > > >> +#include <linux/kernel.h>
> > > >> +#include <linux/module.h>
> > > >> +#include <linux/err.h>
> > > >> +#include <linux/interrupt.h>
> > > >> +#include <linux/io.h>
> > > >> +#include <linux/platform_device.h>
> > > >> +#include <linux/clk.h>
> > > >> +#include <linux/pm_runtime.h>
> > > >> +
> > > >> +#include <drm/drmP.h>
> > > >> +#include <drm/exynos_drm.h>
> > > >> +#include "regs-rotator.h"
> > > >> +#include "exynos_drm.h"
> > > >> +#include "exynos_drm_ipp.h"
> > > >> +
> > > >> +/*
> > > >> + * Rotator supports image crop/rotator and input/output DMA
> > operations.
> > > >> + * input DMA reads image data from the memory.
> > > >> + * output DMA writes image data to memory.
> > > >> + */
> > > >> +
> > > >> +#define get_rot_context(dev)
> > > >> 	platform_get_drvdata(to_platform_device(dev))
> > > >> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
> > > >> +					struct rot_context, ippdrv);
> > > >> +#define rot_read(offset)		readl(rot->regs + (offset))
> > > >> +#define rot_write(cfg, offset)	writel(cfg, rot->regs +
(offset))
> > > >> +
> > > >> +enum rot_irq_status {
> > > >> +	ROT_IRQ_STATUS_COMPLETE	= 8,
> > > >> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
> > > >> +};
> > > >> +
> > > >> +/*
> > > >> + * A structure of limitation.
> > > >> + *
> > > >> + * @min_w: minimum width.
> > > >> + * @min_h: minimum height.
> > > >> + * @max_w: maximum width.
> > > >> + * @max_h: maximum height.
> > > >> + * @align: align size.
> > > >> + */
> > > >> +struct rot_limit {
> > > >> +	u32	min_w;
> > > >> +	u32	min_h;
> > > >> +	u32	max_w;
> > > >> +	u32	max_h;
> > > >> +	u32	align;
> > > >> +};
> > > >> +
> > > >> +/*
> > > >> + * A structure of limitation table.
> > > >> + *
> > > >> + * @ycbcr420_2p: case of YUV.
> > > >> + * @rgb888: case of RGB.
> > > >> + */
> > > >> +struct rot_limit_table {
> > > >> +	struct rot_limit	ycbcr420_2p;
> > > >> +	struct rot_limit	rgb888;
> > > >> +};
> > > >> +
> > > >> +/*
> > > >> + * A structure of rotator context.
> > > >> + * @ippdrv: prepare initialization using ippdrv.
> > > >> + * @regs_res: register resources.
> > > >> + * @regs: memory mapped io registers.
> > > >> + * @clock: rotator gate clock.
> > > >> + * @limit_tbl: limitation of rotator.
> > > >> + * @irq: irq number.
> > > >> + * @cur_buf_id: current operation buffer id.
> > > >> + * @suspended: suspended state.
> > > >> + */
> > > >> +struct rot_context {
> > > >> +	struct exynos_drm_ippdrv	ippdrv;
> > > >> +	struct resource	*regs_res;
> > > >> +	void __iomem	*regs;
> > > >> +	struct clk	*clock;
> > > >> +	struct rot_limit_table	*limit_tbl;
> > > >> +	int	irq;
> > > >> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
> > > >> +	bool	suspended;
> > > >> +};
> > > >> +
> > > >> +static void rotator_reg_set_irq(struct rot_context *rot, bool
> enable)
> > > >> +{
> > > >> +	u32 val = rot_read(ROT_CONFIG);
> > > >> +
> > > >> +	if (enable == true)
> > > >> +		val |= ROT_CONFIG_IRQ;
> > > >> +	else
> > > >> +		val &= ~ROT_CONFIG_IRQ;
> > > >> +
> > > >> +	rot_write(val, ROT_CONFIG);
> > > >> +}
> > > >> +
> > > >> +static u32 rotator_reg_get_format(struct rot_context *rot)
> > > >> +{
> > > >> +	u32 val = rot_read(ROT_CONTROL);
> > > >> +
> > > >> +	val &= ROT_CONTROL_FMT_MASK;
> > > >> +
> > > >> +	return val;
> > > >> +}
> > > >> +
> > > >> +static enum rot_irq_status rotator_reg_get_irq_status(struct
> > > rot_context
> > > >> *rot)
> > > >> +{
> > > >> +	u32 val = rot_read(ROT_STATUS);
> > > >> +
> > > >> +	val = ROT_STATUS_IRQ(val);
> > > >> +
> > > >> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
> > > >> +		return ROT_IRQ_STATUS_COMPLETE;
> > > >> +	else
> > > >
> > > > Remove else.
> > >
> > > - done.
> > >
> > > >
> > > >> +		return ROT_IRQ_STATUS_ILLEGAL;
> > > >> +}
> > > >> +
> > > >> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
> > > >> +{
> > > >> +	struct rot_context *rot = arg;
> > > >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> > > >> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
> > > >> +	struct drm_exynos_ipp_event_work *event_work =
> c_node->event_work;
> > > >> +	enum rot_irq_status irq_status;
> > > >> +	u32 val;
> > > >> +
> > > >> +	/* Get execution result */
> > > >> +	irq_status = rotator_reg_get_irq_status(rot);
> > > >> +
> > > >> +	/* clear status */
> > > >> +	val = rot_read(ROT_STATUS);
> > > >> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
> > > >> +	rot_write(val, ROT_STATUS);
> > > >> +
> > > >> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
> > > >> +		event_work->ippdrv = ippdrv;
> > > >> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
> > > >> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
> > > >> +		queue_work(ippdrv->event_workq,
> > > >> +			(struct work_struct *)event_work);
> > > >> +	} else
> > > >> +		DRM_ERROR("the SFR is set illegally\n");
> > > >> +
> > > >> +	return IRQ_HANDLED;
> > > >> +}
> > > >> +
> > > >> +static void rotator_align_size(struct rot_context *rot, u32 fmt,
> u32
> > > >> *hsize,
> > > >> +		u32 *vsize)
> > > >> +{
> > > >> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
> > > >> +	struct rot_limit *limit;
> > > >> +	u32 mask, val;
> > > >> +
> > > >> +	/* Get size limit */
> > > >> +	if (fmt == ROT_CONTROL_FMT_RGB888)
> > > >> +		limit = &limit_tbl->rgb888;
> > > >> +	else
> > > >> +		limit = &limit_tbl->ycbcr420_2p;
> > > >> +
> > > >> +	/* Get mask for rounding to nearest aligned val */
> > > >> +	mask = ~((1 << limit->align) - 1);
> > > >> +
> > > >> +	/* Set aligned width */
> > > >> +	val = ROT_ALIGN(*hsize, limit->align, mask);
> > > >> +	if (val < limit->min_w)
> > > >> +		*hsize = ROT_MIN(limit->min_w, mask);
> > > >> +	else if (val > limit->max_w)
> > > >> +		*hsize = ROT_MAX(limit->max_w, mask);
> > > >> +	else
> > > >> +		*hsize = val;
> > > >> +
> > > >> +	/* Set aligned height */
> > > >> +	val = ROT_ALIGN(*vsize, limit->align, mask);
> > > >> +	if (val < limit->min_h)
> > > >> +		*vsize = ROT_MIN(limit->min_h, mask);
> > > >> +	else if (val > limit->max_h)
> > > >> +		*vsize = ROT_MAX(limit->max_h, mask);
> > > >> +	else
> > > >> +		*vsize = val;
> > > >> +}
> > > >> +
> > > >> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	u32 val;
> > > >> +
> > > >> +	val = rot_read(ROT_CONTROL);
> > > >> +	val &= ~ROT_CONTROL_FMT_MASK;
> > > >> +
> > > >> +	switch (fmt) {
> > > >> +	case DRM_FORMAT_NV12:
> > > >> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
> > > >> +		break;
> > > >> +	case DRM_FORMAT_XRGB8888:
> > > >> +		val |= ROT_CONTROL_FMT_RGB888;
> > > >> +		break;
> > > >> +	default:
> > > >> +		DRM_ERROR("invalid image format\n");
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	rot_write(val, ROT_CONTROL);
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static int rotator_src_set_size(struct device *dev, int swap,
> > > >> +		struct drm_exynos_pos *pos,
> > > >> +		struct drm_exynos_sz *sz)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	u32 fmt, hsize, vsize;
> > > >> +	u32 val;
> > > >> +
> > > >> +	/* Get format */
> > > >> +	fmt = rotator_reg_get_format(rot);
> > > >> +
> > > >> +	/* Align buffer size */
> > > >> +	hsize = sz->hsize;
> > > >> +	vsize = sz->vsize;
> > > >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> > > >> +
> > > >> +	/* Set buffer size configuration */
> > > >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> > > >> +	rot_write(val, ROT_SRC_BUF_SIZE);
> > > >> +
> > > >> +	/* Set crop image position configuration */
> > > >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> > > >> +	rot_write(val, ROT_SRC_CROP_POS);
> > > >> +	val = ROT_SRC_CROP_SIZE_H(pos->h) |
> ROT_SRC_CROP_SIZE_W(pos->w);
> > > >> +	rot_write(val, ROT_SRC_CROP_SIZE);
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static int rotator_src_set_addr(struct device *dev,
> > > >> +		struct drm_exynos_ipp_buf_info *buf_info,
> > > >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> > > >> +	u32 val, fmt, hsize, vsize;
> > > >> +	int i;
> > > >> +
> > > >> +	/* Set current buf_id */
> > > >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
> > > >> +
> > > >> +	switch (buf_type) {
> > > >> +	case IPP_BUF_ENQUEUE:
> > > >> +		/* Set address configuration */
> > > >> +		for_each_ipp_planar(i)
> > > >> +			addr[i] = buf_info->base[i];
> > > >
> > > > Check NULL.
> > >
> > > - If not copy this state, we need to memset about addr.
> > >    no need check null.
> > >
> > > >
> > > >> +
> > > >> +		/* Get format */
> > > >> +		fmt = rotator_reg_get_format(rot);
> > > >> +
> > > >> +		/* Re-set cb planar for NV12 format */
> > > >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> > > >> +					(addr[EXYNOS_DRM_PLANAR_CB]
> ==
> > > > 0x00)) {
> > > >
> > > > What is 0x00?
> > >
> > > - It is NULL, I changed !addr instead of addr == 0x00
> > >
> > > >
> > > >> +
> > > >> +			val = rot_read(ROT_SRC_BUF_SIZE);
> > > >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> > > >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> > > >> +
> > > >> +			/* Set cb planar */
> > > >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> > > >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize *
> vsize;
> > > >> +		}
> > > >> +
> > > >> +		for_each_ipp_planar(i)
> > > >> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
> > > >> +		break;
> > > >> +	case IPP_BUF_DEQUEUE:
> > > >> +		for_each_ipp_planar(i)
> > > >> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
> > > >> +		break;
> > > >> +	default:
> > > >> +		/* Nothing to do */
> > > >> +		break;
> > > >> +	}
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static int rotator_dst_set_transf(struct device *dev,
> > > >> +		enum drm_exynos_degree degree,
> > > >> +		enum drm_exynos_flip flip)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	u32 val;
> > > >> +
> > > >> +	/* Set transform configuration */
> > > >> +	val = rot_read(ROT_CONTROL);
> > > >> +	val &= ~ROT_CONTROL_FLIP_MASK;
> > > >> +
> > > >> +	switch (flip) {
> > > >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> > > >> +		val |= ROT_CONTROL_FLIP_VERTICAL;
> > > >> +		break;
> > > >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> > > >> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
> > > >> +		break;
> > > >> +	default:
> > > >> +		/* Flip None */
> > > >> +		break;
> > > >> +	}
> > > >> +
> > > >> +	val &= ~ROT_CONTROL_ROT_MASK;
> > > >> +
> > > >> +	switch (degree) {
> > > >> +	case EXYNOS_DRM_DEGREE_90:
> > > >> +		val |= ROT_CONTROL_ROT_90;
> > > >> +		break;
> > > >> +	case EXYNOS_DRM_DEGREE_180:
> > > >> +		val |= ROT_CONTROL_ROT_180;
> > > >> +		break;
> > > >> +	case EXYNOS_DRM_DEGREE_270:
> > > >> +		val |= ROT_CONTROL_ROT_270;
> > > >> +		break;
> > > >> +	default:
> > > >> +		/* Rotation 0 Degree */
> > > >> +		break;
> > > >> +	}
> > > >> +
> > > >> +	rot_write(val, ROT_CONTROL);
> > > >> +
> > > >> +	/* Check degree for setting buffer size swap */
> > > >> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
> > > >> +		(degree == EXYNOS_DRM_DEGREE_270))
> > > >> +		return 1;
> > > >
> > > > Correct return type. This function should return 0 or negative.
> > >
> > > - no ~ this return type is boolean true or false.
> > >    but we need to error handling so, we use integer as you know
> > >    we reviewed this routine at our local git from our team(you and
me).
> > >
> > > >
> > > >> +	else
> > > >> +		return 0;
> > > >
> > > > Ditto.
> > >
> > > - ditto.
> > >
> > > >
> > > >> +}
> > > >> +
> > > >> +static int rotator_dst_set_size(struct device *dev, int swap,
> > > >> +		struct drm_exynos_pos *pos,
> > > >> +		struct drm_exynos_sz *sz)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	u32 val, fmt, hsize, vsize;
> > > >> +
> > > >> +	/* Get format */
> > > >> +	fmt = rotator_reg_get_format(rot);
> > > >
> > > > Check if fmt is valid or not.
> > >
> > > - added rotator_check_fmt()
> > >
> > > >
> > > >> +
> > > >> +	/* Align buffer size */
> > > >> +	hsize = sz->hsize;
> > > >> +	vsize = sz->vsize;
> > > >> +	rotator_align_size(rot, fmt, &hsize, &vsize);
> > > >> +
> > > >> +	/* Set buffer size configuration */
> > > >> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
> > > >> +	rot_write(val, ROT_DST_BUF_SIZE);
> > > >> +
> > > >> +	/* Set crop image position configuration */
> > > >> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
> > > >> +	rot_write(val, ROT_DST_CROP_POS);
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static int rotator_dst_set_addr(struct device *dev,
> > > >> +		struct drm_exynos_ipp_buf_info *buf_info,
> > > >> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
> > > >> +	u32 val, fmt, hsize, vsize;
> > > >> +	int i;
> > > >> +
> > > >> +	/* Set current buf_id */
> > > >> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
> > > >> +
> > > >> +	switch (buf_type) {
> > > >> +	case IPP_BUF_ENQUEUE:
> > > >> +		/* Set address configuration */
> > > >> +		for_each_ipp_planar(i)
> > > >> +			addr[i] = buf_info->base[i];
> > > >> +
> > > >> +		/* Get format */
> > > >> +		fmt = rotator_reg_get_format(rot);
> > > >
> > > > Check if fmt is valid or not.
> > >
> > > - done.
> > >
> > > >
> > > >> +
> > > >> +		/* Re-set cb planar for NV12 format */
> > > >> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
> > > >> +					(addr[EXYNOS_DRM_PLANAR_CB]
> ==
> > > > 0x00)) {
> > > >> +			/* Get buf size */
> > > >> +			val = rot_read(ROT_DST_BUF_SIZE);
> > > >> +
> > > >> +			hsize = ROT_GET_BUF_SIZE_W(val);
> > > >> +			vsize = ROT_GET_BUF_SIZE_H(val);
> > > >> +
> > > >> +			/* Set cb planar */
> > > >> +			addr[EXYNOS_DRM_PLANAR_CB] =
> > > >> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize *
> vsize;
> > > >> +		}
> > > >> +
> > > >> +		for_each_ipp_planar(i)
> > > >> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
> > > >> +		break;
> > > >> +	case IPP_BUF_DEQUEUE:
> > > >> +		for_each_ipp_planar(i)
> > > >> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
> > > >> +		break;
> > > >> +	default:
> > > >> +		/* Nothing to do */
> > > >> +		break;
> > > >> +	}
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static struct exynos_drm_ipp_ops rot_src_ops = {
> > > >> +	.set_fmt	=	rotator_src_set_fmt,
> > > >> +	.set_size	=	rotator_src_set_size,
> > > >> +	.set_addr	=	rotator_src_set_addr,
> > > >> +};
> > > >> +
> > > >> +static struct exynos_drm_ipp_ops rot_dst_ops = {
> > > >> +	.set_transf	=	rotator_dst_set_transf,
> > > >> +	.set_size	=	rotator_dst_set_size,
> > > >> +	.set_addr	=	rotator_dst_set_addr,
> > > >> +};
> > > >> +
> > > >> +static int rotator_init_prop_list(struct exynos_drm_ippdrv
*ippdrv)
> > > >> +{
> > > >> +	struct drm_exynos_ipp_prop_list *prop_list;
> > > >> +
> > > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > > >> +
> > > >> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
> > > >> +	if (!prop_list) {
> > > >> +		DRM_ERROR("failed to alloc property list.\n");
> > > >> +		return -ENOMEM;
> > > >> +	}
> > > >> +
> > > >> +	/*ToDo fix support function list*/
> > > >> +
> > > >> +	prop_list->version = 1;
> > > >> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
> > > >> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
> > > >> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
> > > >> +				(1 << EXYNOS_DRM_DEGREE_90) |
> > > >> +				(1 << EXYNOS_DRM_DEGREE_180) |
> > > >> +				(1 << EXYNOS_DRM_DEGREE_270);
> > > >> +	prop_list->csc = 0;
> > > >> +	prop_list->crop = 0;
> > > >> +	prop_list->scale = 0;
> > > >> +
> > > >> +	ippdrv->prop_list = prop_list;
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static int rotator_ippdrv_check_property(struct device *dev,
> > > >> +		struct drm_exynos_ipp_property *property)
> > > >> +{
> > > >> +	struct drm_exynos_ipp_config *src_config =
> > > >> +
> > > > &property->config[EXYNOS_DRM_OPS_SRC];
> > > >> +	struct drm_exynos_ipp_config *dst_config =
> > > >> +
> > > > &property->config[EXYNOS_DRM_OPS_DST];
> > > >> +	struct drm_exynos_pos *src_pos = &src_config->pos;
> > > >> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
> > > >> +	struct drm_exynos_sz *src_sz = &src_config->sz;
> > > >> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
> > > >> +	bool swap = false;
> > > >> +
> > > >> +	/* Check format configuration */
> > > >> +	if (src_config->fmt != dst_config->fmt) {
> > > >> +		DRM_DEBUG_KMS("%s:not support csc feature\n",
> __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	switch (src_config->fmt) {
> > > >> +	case DRM_FORMAT_XRGB8888:
> > > >> +	case DRM_FORMAT_NV12:
> > > >> +		/* No problem */
> > > >> +		break;
> > > >> +	default:
> > > >> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >
> > > > Use macro instead of switch-case. this just checks only format type.
> > >
> > > - I don't thing so, If we make macro about this.
> > >    then we got some confusion about this macro at next change.
> > >    this case switch-case is better.
> > >    Do you prefer this ? please one more comment.
> > >
> > > #define drm_check_fmt(fmt) if (fmt == DRM_FORMAT_XRGB8888 || fmt ==
> > > DRM_FORMAT_NV12) \
> > > 					return true;
> >
> > #define drm_check_fmt(fmt)		((fmt & (DRM_FORMAT_XRGB8888 |
> > DRM_FORMAT_NV12)) ? true : false)
> >
> > if (drm_check_fmt(src_config->fmt))
> > 	Something;
> > else
> > 	Something;
> 
> Again,
> 
> #define DRM_FORMAT_MASK			(DRM_FORMAT_XRGB888 |
> DRM_FORMAT_NV12)
>  #define drm_check_fmt(fmt)		(((fmt & DRM_FORMAT_MASK) &
> (DRM_FORMAT_XRGB8888 | \
> 
> DRM_FORMAT_NV12)) ? true : false)
> 
>  if (drm_check_fmt(src_config->fmt))
>  	Something;
>  else
>  	Something;
> 

Ah, these format types have no bit field base. Just use switch-case and make
this code  to one function. And then just call that function.


> 
> >
> > >
> > > >
> > > >> +
> > > >> +	/* Check transform configuration */
> > > >> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
> > > >> +		DRM_DEBUG_KMS("%s:not support source-side
> rotation\n",
> > > >> +
> __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	switch (dst_config->degree) {
> > > >> +	case EXYNOS_DRM_DEGREE_90:
> > > >> +	case EXYNOS_DRM_DEGREE_270:
> > > >> +		swap = true;
> > > >> +	case EXYNOS_DRM_DEGREE_0:
> > > >> +	case EXYNOS_DRM_DEGREE_180:
> > > >> +		/* No problem */
> > > >> +		break;
> > > >> +	default:
> > > >> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
> > > >> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
> > > > __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	switch (dst_config->flip) {
> > > >> +	case EXYNOS_DRM_FLIP_NONE:
> > > >> +	case EXYNOS_DRM_FLIP_VERTICAL:
> > > >> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
> > > >> +		/* No problem */
> > > >> +		break;
> > > >> +	default:
> > > >> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >
> > > > Use macro instead of switch-case. this just checks only flip type.
> > >
> > > - ditto. please one more comment.
> > >   in my opinion: I prefer enumeration use switch-case.
> >
> > What does each case do? Just check? If so, use macro as I mentioned. We
> > can't accept this codes.
> >
> > >
> > > >
> > > >> +
> > > >> +	/* Check size configuration */
> > > >> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
> > > >> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
> > > >> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n",
> __func__);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	if (swap) {
> > > >> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
> > > >> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
> > > >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > > > bound\n",
> > > >> +
> __func__);
> > > >> +			return -EINVAL;
> > > >> +		}
> > > >> +
> > > >> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
> > > > dst_pos->w))
> > > >> {
> > > >> +			DRM_DEBUG_KMS("%s:not support scale
> feature\n",
> > > >> +
> __func__);
> > > >> +			return -EINVAL;
> > > >> +		}
> > > >> +	} else {
> > > >> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
> > > >> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
> > > >> +			DRM_DEBUG_KMS("%s:out of destination buffer
> > > > bound\n",
> > > >> +
> __func__);
> > > >> +			return -EINVAL;
> > > >> +		}
> > > >> +
> > > >> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
> > > > dst_pos->h))
> > > >> {
> > > >> +			DRM_DEBUG_KMS("%s:not support scale
> feature\n",
> > > >> +
> __func__);
> > > >> +			return -EINVAL;
> > > >> +		}
> > > >> +	}
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static int rotator_ippdrv_start(struct device *dev, enum
> > > >> drm_exynos_ipp_cmd cmd)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	u32 val;
> > > >> +
> > > >> +	if (rot->suspended) {
> > > >> +		DRM_ERROR("suspended state\n");
> > > >> +		return -EPERM;
> > > >> +	}
> > > >> +
> > > >> +	if (cmd != IPP_CMD_M2M) {
> > > >> +		DRM_ERROR("not support cmd: %d\n", cmd);
> > > >> +		return -EINVAL;
> > > >> +	}
> > > >> +
> > > >> +	/* Set interrupt enable */
> > > >> +	rotator_reg_set_irq(rot, true);
> > > >> +
> > > >> +	val = rot_read(ROT_CONTROL);
> > > >> +	val |= ROT_CONTROL_START;
> > > >> +
> > > >> +	rot_write(val, ROT_CONTROL);
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +static int __devinit rotator_probe(struct platform_device *pdev)
> > > >> +{
> > > >> +	struct device *dev = &pdev->dev;
> > > >> +	struct rot_context *rot;
> > > >> +	struct resource *res;
> > > >> +	struct exynos_drm_ippdrv *ippdrv;
> > > >> +	int ret;
> > > >> +
> > > >> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
> > > >> +	if (!rot) {
> > > >> +		dev_err(dev, "failed to allocate rot\n");
> > > >> +		return -ENOMEM;
> > > >> +	}
> > > >> +
> > > >> +	rot->limit_tbl = (struct rot_limit_table *)
> > > >> +
> platform_get_device_id(pdev)->driver_data;
> > > >> +
> > > >> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > >> +	if (!res) {
> > > >> +		dev_err(dev, "failed to find registers\n");
> > > >> +		ret = -ENOENT;
> > > >> +		goto err_get_resource;
> > > >> +	}
> > > >> +
> > > >> +	rot->regs_res = request_mem_region(res->start,
> resource_size(res),
> > > >> +
> > > > dev_name(dev));
> > > >> +	if (!rot->regs_res) {
> > > >> +		dev_err(dev, "failed to claim register region\n");
> > > >> +		ret = -ENOENT;
> > > >> +		goto err_get_resource;
> > > >> +	}
> > > >> +
> > > >> +	rot->regs = ioremap(res->start, resource_size(res));
> > > >> +	if (!rot->regs) {
> > > >> +		dev_err(dev, "failed to map register\n");
> > > >> +		ret = -ENXIO;
> > > >> +		goto err_ioremap;
> > > >> +	}
> > > >> +
> > > >> +	rot->irq = platform_get_irq(pdev, 0);
> > > >> +	if (rot->irq < 0) {
> > > >> +		dev_err(dev, "failed to get irq\n");
> > > >> +		ret = rot->irq;
> > > >> +		goto err_get_irq;
> > > >> +	}
> > > >> +
> > > >> +	ret = request_threaded_irq(rot->irq, NULL,
> rotator_irq_handler,
> > > >> +					IRQF_ONESHOT, "drm_rotator",
> rot);
> > > >> +	if (ret < 0) {
> > > >> +		dev_err(dev, "failed to request irq\n");
> > > >> +		goto err_get_irq;
> > > >> +	}
> > > >> +
> > > >> +	rot->clock = clk_get(dev, "rotator");
> > > >> +	if (IS_ERR_OR_NULL(rot->clock)) {
> > > >> +		dev_err(dev, "failed to get clock\n");
> > > >> +		ret = PTR_ERR(rot->clock);
> > > >> +		goto err_clk_get;
> > > >> +	}
> > > >> +
> > > >> +	pm_runtime_enable(dev);
> > > >> +
> > > >> +	ippdrv = &rot->ippdrv;
> > > >> +	ippdrv->dev = dev;
> > > >> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
> > > >> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
> > > >> +	ippdrv->check_property = rotator_ippdrv_check_property;
> > > >> +	ippdrv->start = rotator_ippdrv_start;
> > > >> +	ret = rotator_init_prop_list(ippdrv);
> > > >> +	if (ret < 0) {
> > > >> +		dev_err(dev, "failed to init property list.\n");
> > > >> +		goto err_ippdrv_register;
> > > >> +	}
> > > >> +
> > > >> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
> > > >> +
> > > >> +	platform_set_drvdata(pdev, rot);
> > > >> +
> > > >> +	ret = exynos_drm_ippdrv_register(ippdrv);
> > > >> +	if (ret < 0) {
> > > >> +		dev_err(dev, "failed to register drm rotator
> device\n");
> > > >> +		kfree(ippdrv->prop_list);
> > > >> +		goto err_ippdrv_register;
> > > >> +	}
> > > >> +
> > > >> +	dev_info(dev, "The exynos rotator is probed
> successfully\n");
> > > >> +
> > > >> +	return 0;
> > > >> +
> > > >> +err_ippdrv_register:
> > > >> +	pm_runtime_disable(dev);
> > > >> +	clk_put(rot->clock);
> > > >> +err_clk_get:
> > > >> +	free_irq(rot->irq, rot);
> > > >> +err_get_irq:
> > > >> +	iounmap(rot->regs);
> > > >> +err_ioremap:
> > > >> +	release_resource(rot->regs_res);
> > > >> +	kfree(rot->regs_res);
> > > >> +err_get_resource:
> > > >> +	kfree(rot);
> > > >> +	return ret;
> > > >> +}
> > > >> +
> > > >> +static int __devexit rotator_remove(struct platform_device *pdev)
> > > >> +{
> > > >> +	struct device *dev = &pdev->dev;
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
> > > >> +
> > > >> +	kfree(ippdrv->prop_list);
> > > >> +	exynos_drm_ippdrv_unregister(ippdrv);
> > > >> +
> > > >> +	pm_runtime_disable(dev);
> > > >> +	clk_put(rot->clock);
> > > >> +
> > > >> +	free_irq(rot->irq, rot);
> > > >> +
> > > >> +	iounmap(rot->regs);
> > > >> +
> > > >> +	release_resource(rot->regs_res);
> > > >> +	kfree(rot->regs_res);
> > > >> +
> > > >> +	kfree(rot);
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +struct rot_limit_table rot_limit_tbl = {
> > > >> +	.ycbcr420_2p = {
> > > >> +		.min_w = 32,
> > > >> +		.min_h = 32,
> > > >> +		.max_w = SZ_32K,
> > > >> +		.max_h = SZ_32K,
> > > >> +		.align = 3,
> > > >> +	},
> > > >> +	.rgb888 = {
> > > >> +		.min_w = 8,
> > > >> +		.min_h = 8,
> > > >> +		.max_w = SZ_8K,
> > > >> +		.max_h = SZ_8K,
> > > >> +		.align = 2,
> > > >> +	},
> > > >> +};
> > > >> +
> > > >> +struct platform_device_id rotator_driver_ids[] = {
> > > >> +	{
> > > >> +		.name		= "exynos-rot",
> > > >> +		.driver_data	= (unsigned long)&rot_limit_tbl,
> > > >> +	},
> > > >> +	{},
> > > >> +};
> > > >> +
> > > >> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
> > > >> +{
> > > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > > >> +
> > > >> +	if (enable) {
> > > >> +		clk_enable(rot->clock);
> > > >> +		rot->suspended = false;
> > > >> +	} else {
> > > >> +		clk_disable(rot->clock);
> > > >> +		rot->suspended = true;
> > > >> +	}
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +
> > > >> +
> > > >> +#ifdef CONFIG_PM_SLEEP
> > > >> +static int rotator_suspend(struct device *dev)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +
> > > >> +	/* ToDo */
> > > >> +
> > > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > > >> +
> > > >> +	if (pm_runtime_suspended(dev))
> > > >> +		return 0;
> > > >> +
> > > >> +	return rotator_clk_crtl(rot, false);
> > > >> +}
> > > >> +
> > > >> +static int rotator_resume(struct device *dev)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +
> > > >> +	/* ToDo */
> > > >> +
> > > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > > >> +
> > > >> +	if (!pm_runtime_suspended(dev))
> > > >> +		return rotator_clk_crtl(rot, true);
> > > >> +
> > > >> +	return 0;
> > > >> +}
> > > >> +#endif
> > > >> +
> > > >> +#ifdef CONFIG_PM_RUNTIME
> > > >> +static int rotator_runtime_suspend(struct device *dev)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +
> > > >> +	/* ToDo */
> > > >> +
> > > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > > >> +
> > > >> +	return  rotator_clk_crtl(rot, false);
> > > >> +}
> > > >> +
> > > >> +static int rotator_runtime_resume(struct device *dev)
> > > >> +{
> > > >> +	struct rot_context *rot = dev_get_drvdata(dev);
> > > >> +
> > > >> +	/* ToDo */
> > > >> +
> > > >> +	DRM_DEBUG_KMS("%s\n", __func__);
> > > >> +
> > > >> +	return  rotator_clk_crtl(rot, true);
> > > >> +}
> > > >> +#endif
> > > >> +
> > > >> +static const struct dev_pm_ops rotator_pm_ops = {
> > > >> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
> > > >> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend,
> rotator_runtime_resume,
> > > >> +
> > > > NULL)
> > > >> +};
> > > >> +
> > > >> +struct platform_driver rotator_driver = {
> > > >> +	.probe		= rotator_probe,
> > > >> +	.remove		= __devexit_p(rotator_remove),
> > > >> +	.id_table	= rotator_driver_ids,
> > > >> +	.driver		= {
> > > >> +		.name	= "exynos-rot",
> > > >> +		.owner	= THIS_MODULE,
> > > >> +		.pm	= &rotator_pm_ops,
> > > >> +	},
> > > >> +};
> > > >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > > >> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > > >> new file mode 100644
> > > >> index 0000000..fe929c9
> > > >> --- /dev/null
> > > >> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
> > > >> @@ -0,0 +1,13 @@
> > > >> +/*
> > > >> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
> > > >> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
> > > >> + */
> > > >> +
> > > >> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
> > > >> +#define	_EXYNOS_DRM_ROTATOR_H_
> > > >> +
> > > >> +#endif
> > > >> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
> > > >> b/drivers/gpu/drm/exynos/regs-rotator.h
> > > >> new file mode 100644
> > > >> index 0000000..a09ac6e
> > > >> --- /dev/null
> > > >> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
> > > >> @@ -0,0 +1,73 @@
> > > >> +/* drivers/gpu/drm/exynos/regs-rotator.h
> > > >> + *
> > > >> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> > > >> + *		http://www.samsung.com/
> > > >> + *
> > > >> + * Register definition file for Samsung Rotator Interface
(Rotator)
> > > >> driver
> > > >> + *
> > > >> + * This program is free software; you can redistribute it and/or
> > > modify
> > > >> + * it under the terms of the GNU General Public License version 2
> as
> > > >> + * published by the Free Software Foundation.
> > > >> +*/
> > > >> +
> > > >> +#ifndef EXYNOS_REGS_ROTATOR_H
> > > >> +#define EXYNOS_REGS_ROTATOR_H
> > > >> +
> > > >> +/* Configuration */
> > > >> +#define ROT_CONFIG			0x00
> > > >> +#define ROT_CONFIG_IRQ			(3 << 8)
> > > >> +
> > > >> +/* Image Control */
> > > >> +#define ROT_CONTROL			0x10
> > > >> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
> > > >> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
> > > >> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
> > > >> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
> > > >> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
> > > >> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
> > > >> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
> > > >> +#define ROT_CONTROL_ROT_90		(1 << 4)
> > > >> +#define ROT_CONTROL_ROT_180		(2 << 4)
> > > >> +#define ROT_CONTROL_ROT_270		(3 << 4)
> > > >> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
> > > >> +#define ROT_CONTROL_START		(1 << 0)
> > > >> +
> > > >> +/* Status */
> > > >> +#define ROT_STATUS			0x20
> > > >> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
> > > >> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
> > > >> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
> > > >> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
> > > >> +
> > > >> +/* Buffer Address */
> > > >> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
> > > >> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
> > > >> +
> > > >> +/* Buffer Size */
> > > >> +#define ROT_SRC_BUF_SIZE		0x3c
> > > >> +#define ROT_DST_BUF_SIZE		0x5c
> > > >> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
> > > >> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
> > > >> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
> > > >> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
> > > >> +
> > > >> +/* Crop Position */
> > > >> +#define ROT_SRC_CROP_POS		0x40
> > > >> +#define ROT_DST_CROP_POS		0x60
> > > >> +#define ROT_CROP_POS_Y(x)		((x) << 16)
> > > >> +#define ROT_CROP_POS_X(x)		((x) << 0)
> > > >> +
> > > >> +/* Source Crop Size */
> > > >> +#define ROT_SRC_CROP_SIZE		0x44
> > > >> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
> > > >> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
> > > >> +
> > > >> +/* Round to nearest aligned value */
> > > >> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) -
> 1))) &
> > > >> (mask))
> > > >> +/* Minimum limit value */
> > > >> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
> > > >> +/* Maximum limit value */
> > > >> +#define ROT_MAX(max, mask)		((max) & (mask))
> > > >> +
> > > >> +#endif /* EXYNOS_REGS_ROTATOR_H */
> > > >> +
> > > >> --
> > > >> 1.7.0.4
> > > >
> > > >
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/dri-devel
Eunchul Kim Dec. 12, 2012, 10:51 a.m. UTC | #7
Thank's your comment

BR
Eunchul Kim

On 12/12/2012 07:39 PM, Inki Dae wrote:
>
>
>> -----Original Message-----
>> From: Inki Dae [mailto:inki.dae@samsung.com]
>> Sent: Wednesday, December 12, 2012 7:01 PM
>> To: 'Inki Dae'; 'Eunchul Kim'
>> Cc: jy0.jeon@samsung.com; sw0312.kim@samsung.com; jaejoon.seo@samsung.com;
>> kyungmin.park@samsung.com; dri-devel@lists.freedesktop.org;
>> jmock.shin@samsung.com
>> Subject: RE: [RFC v3 4/5] drm/exynos: add rotator ipp driver
>>
>>
>>
>>> -----Original Message-----
>>> From: dri-devel-bounces+inki.dae=samsung.com@lists.freedesktop.org
>>> [mailto:dri-devel-bounces+inki.dae=samsung.com@lists.freedesktop.org] On
>>> Behalf Of Inki Dae
>>> Sent: Wednesday, December 12, 2012 6:42 PM
>>> To: 'Eunchul Kim'
>>> Cc: jy0.jeon@samsung.com; sw0312.kim@samsung.com;
>> jaejoon.seo@samsung.com;
>>> kyungmin.park@samsung.com; dri-devel@lists.freedesktop.org;
>>> jmock.shin@samsung.com
>>> Subject: RE: [RFC v3 4/5] drm/exynos: add rotator ipp driver
>>>
>>>
>>>
>>>> -----Original Message-----
>>>> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
>>>> Sent: Wednesday, December 12, 2012 6:26 PM
>>>> To: Inki Dae
>>>> Cc: dri-devel@lists.freedesktop.org; jy0.jeon@samsung.com;
>>>> yj44.cho@samsung.com; jmock.shin@samsung.com; jaejoon.seo@samsung.com;
>>>> kyungmin.park@samsung.com; sw0312.kim@samsung.com; Joonyoung Shim
>>>> Subject: Re: [RFC v3 4/5] drm/exynos: add rotator ipp driver
>>>>
>>>> Thank's your comment.
>>>>
>>>> I answer your comment. please check that.
>>>>
>>>> Thank's
>>>>
>>>> BR
>>>> Eunchul Kim
>>>>
>>>> On 12/12/2012 05:29 PM, Inki Dae wrote:
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
>>>>>> Sent: Wednesday, December 12, 2012 4:35 PM
>>>>>> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
>>>>>> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com;
>>> jmock.shin@samsung.com;
>>>>>> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
>>>>>> chulspro.kim@samsung.com
>>>>>> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
>>>>>>
>>>>>> Rotator supports rotation/crop/flip and input/output DMA operations
>>>>>> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
>>>>>> horizontal flip.
>>>>>> and has some limitations(source and destination format have to be
>>> same,
>>>> no
>>>>>> scaler)
>>>>>>
>>>>>> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
>>>>>> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
>>>>>> ---
>>>>>>    drivers/gpu/drm/exynos/Kconfig              |    7 +
>>>>>>    drivers/gpu/drm/exynos/Makefile             |    1 +
>>>>>>    drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
>>>>>>    drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
>>>>>>    drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
>>>>>> +++++++++++++++++++++++++++
>>>>>>    drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
>>>>>>    drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
>>>>>>    7 files changed, 939 insertions(+), 0 deletions(-)
>>>>>>    create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>>>>    create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>>>>    create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/exynos/Kconfig
>>>>>> b/drivers/gpu/drm/exynos/Kconfig
>>>>>> index 4915ab6..4860835 100644
>>>>>> --- a/drivers/gpu/drm/exynos/Kconfig
>>>>>> +++ b/drivers/gpu/drm/exynos/Kconfig
>>>>>> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
>>>>>>    	depends on DRM_EXYNOS_IPP
>>>>>>    	help
>>>>>>    	  Choose this option if you want to use Exynos FIMC for DRM.
>>>>>> +
>>>>>> +config DRM_EXYNOS_ROTATOR
>>>>>> +	bool "Exynos DRM Rotator"
>>>>>> +	depends on DRM_EXYNOS_IPP
>>>>>> +	help
>>>>>> +	  Choose this option if you want to use Exynos Rotator for
>> DRM.
>>>>>> +
>>>>>> diff --git a/drivers/gpu/drm/exynos/Makefile
>>>>>> b/drivers/gpu/drm/exynos/Makefile
>>>>>> index 9710024..3b70668 100644
>>>>>> --- a/drivers/gpu/drm/exynos/Makefile
>>>>>> +++ b/drivers/gpu/drm/exynos/Makefile
>>>>>> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
>>>>>> exynos_drm_vidi.o
>>>>>>    exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
>>>>>>    exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
>>>>>>    exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
>>>>>> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+=
> exynos_drm_rotator.o
>>>>>>
>>>>>>    obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
>>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>>>> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>>>> index 73f02ac..09d884b 100644
>>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>>>> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
>>>>>>    		goto out_fimc;
>>>>>>    #endif
>>>>>>
>>>>>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>>>>>> +	ret = platform_driver_register(&rotator_driver);
>>>>>> +	if (ret < 0)
>>>>>> +		goto out_rotator;
>>>>>> +#endif
>>>>>> +
>>>>>>    #ifdef CONFIG_DRM_EXYNOS_IPP
>>>>>>    	ret = platform_driver_register(&ipp_driver);
>>>>>>    	if (ret < 0)
>>>>>> @@ -406,6 +412,11 @@ out_drm:
>>>>>>    out_ipp:
>>>>>>    #endif
>>>>>>
>>>>>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>>>>>> +	platform_driver_unregister(&rotator_driver);
>>>>>> +out_rotator:
>>>>>> +#endif
>>>>>> +
>>>>>>    #ifdef CONFIG_DRM_EXYNOS_FIMC
>>>>>>    	platform_driver_unregister(&fimc_driver);
>>>>>>    out_fimc:
>>>>>> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
>>>>>>    	platform_driver_unregister(&ipp_driver);
>>>>>>    #endif
>>>>>>
>>>>>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>>>>>> +	platform_driver_unregister(&rotator_driver);
>>>>>> +#endif
>>>>>> +
>>>>>>    #ifdef CONFIG_DRM_EXYNOS_FIMC
>>>>>>    	platform_driver_unregister(&fimc_driver);
>>>>>>    #endif
>>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>>>> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>>>> index 14f9490..a74e37c 100644
>>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>>>> @@ -353,5 +353,6 @@ extern struct platform_driver
>>>>>> exynos_drm_common_hdmi_driver;
>>>>>>    extern struct platform_driver vidi_driver;
>>>>>>    extern struct platform_driver g2d_driver;
>>>>>>    extern struct platform_driver fimc_driver;
>>>>>> +extern struct platform_driver rotator_driver;
>>>>>>    extern struct platform_driver ipp_driver;
>>>>>>    #endif
>>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>>>> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>>>> new file mode 100644
>>>>>> index 0000000..121569c
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>>>> @@ -0,0 +1,829 @@
>>>>>> +/*
>>>>>> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
>>>>>> + * Authors:
>>>>>> + *	YoungJun Cho <yj44.cho@samsung.com>
>>>>>> + *	Eunchul Kim <chulspro.kim@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 Foundationr
>>>>>> + */
>>>>>> +
>>>>>> +#include <linux/kernel.h>
>>>>>> +#include <linux/module.h>
>>>>>> +#include <linux/err.h>
>>>>>> +#include <linux/interrupt.h>
>>>>>> +#include <linux/io.h>
>>>>>> +#include <linux/platform_device.h>
>>>>>> +#include <linux/clk.h>
>>>>>> +#include <linux/pm_runtime.h>
>>>>>> +
>>>>>> +#include <drm/drmP.h>
>>>>>> +#include <drm/exynos_drm.h>
>>>>>> +#include "regs-rotator.h"
>>>>>> +#include "exynos_drm.h"
>>>>>> +#include "exynos_drm_ipp.h"
>>>>>> +
>>>>>> +/*
>>>>>> + * Rotator supports image crop/rotator and input/output DMA
>>> operations.
>>>>>> + * input DMA reads image data from the memory.
>>>>>> + * output DMA writes image data to memory.
>>>>>> + */
>>>>>> +
>>>>>> +#define get_rot_context(dev)
>>>>>> 	platform_get_drvdata(to_platform_device(dev))
>>>>>> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
>>>>>> +					struct rot_context, ippdrv);
>>>>>> +#define rot_read(offset)		readl(rot->regs + (offset))
>>>>>> +#define rot_write(cfg, offset)	writel(cfg, rot->regs +
> (offset))
>>>>>> +
>>>>>> +enum rot_irq_status {
>>>>>> +	ROT_IRQ_STATUS_COMPLETE	= 8,
>>>>>> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
>>>>>> +};
>>>>>> +
>>>>>> +/*
>>>>>> + * A structure of limitation.
>>>>>> + *
>>>>>> + * @min_w: minimum width.
>>>>>> + * @min_h: minimum height.
>>>>>> + * @max_w: maximum width.
>>>>>> + * @max_h: maximum height.
>>>>>> + * @align: align size.
>>>>>> + */
>>>>>> +struct rot_limit {
>>>>>> +	u32	min_w;
>>>>>> +	u32	min_h;
>>>>>> +	u32	max_w;
>>>>>> +	u32	max_h;
>>>>>> +	u32	align;
>>>>>> +};
>>>>>> +
>>>>>> +/*
>>>>>> + * A structure of limitation table.
>>>>>> + *
>>>>>> + * @ycbcr420_2p: case of YUV.
>>>>>> + * @rgb888: case of RGB.
>>>>>> + */
>>>>>> +struct rot_limit_table {
>>>>>> +	struct rot_limit	ycbcr420_2p;
>>>>>> +	struct rot_limit	rgb888;
>>>>>> +};
>>>>>> +
>>>>>> +/*
>>>>>> + * A structure of rotator context.
>>>>>> + * @ippdrv: prepare initialization using ippdrv.
>>>>>> + * @regs_res: register resources.
>>>>>> + * @regs: memory mapped io registers.
>>>>>> + * @clock: rotator gate clock.
>>>>>> + * @limit_tbl: limitation of rotator.
>>>>>> + * @irq: irq number.
>>>>>> + * @cur_buf_id: current operation buffer id.
>>>>>> + * @suspended: suspended state.
>>>>>> + */
>>>>>> +struct rot_context {
>>>>>> +	struct exynos_drm_ippdrv	ippdrv;
>>>>>> +	struct resource	*regs_res;
>>>>>> +	void __iomem	*regs;
>>>>>> +	struct clk	*clock;
>>>>>> +	struct rot_limit_table	*limit_tbl;
>>>>>> +	int	irq;
>>>>>> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
>>>>>> +	bool	suspended;
>>>>>> +};
>>>>>> +
>>>>>> +static void rotator_reg_set_irq(struct rot_context *rot, bool
>> enable)
>>>>>> +{
>>>>>> +	u32 val = rot_read(ROT_CONFIG);
>>>>>> +
>>>>>> +	if (enable == true)
>>>>>> +		val |= ROT_CONFIG_IRQ;
>>>>>> +	else
>>>>>> +		val &= ~ROT_CONFIG_IRQ;
>>>>>> +
>>>>>> +	rot_write(val, ROT_CONFIG);
>>>>>> +}
>>>>>> +
>>>>>> +static u32 rotator_reg_get_format(struct rot_context *rot)
>>>>>> +{
>>>>>> +	u32 val = rot_read(ROT_CONTROL);
>>>>>> +
>>>>>> +	val &= ROT_CONTROL_FMT_MASK;
>>>>>> +
>>>>>> +	return val;
>>>>>> +}
>>>>>> +
>>>>>> +static enum rot_irq_status rotator_reg_get_irq_status(struct
>>>> rot_context
>>>>>> *rot)
>>>>>> +{
>>>>>> +	u32 val = rot_read(ROT_STATUS);
>>>>>> +
>>>>>> +	val = ROT_STATUS_IRQ(val);
>>>>>> +
>>>>>> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
>>>>>> +		return ROT_IRQ_STATUS_COMPLETE;
>>>>>> +	else
>>>>>
>>>>> Remove else.
>>>>
>>>> - done.
>>>>
>>>>>
>>>>>> +		return ROT_IRQ_STATUS_ILLEGAL;
>>>>>> +}
>>>>>> +
>>>>>> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
>>>>>> +{
>>>>>> +	struct rot_context *rot = arg;
>>>>>> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
>>>>>> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
>>>>>> +	struct drm_exynos_ipp_event_work *event_work =
>> c_node->event_work;
>>>>>> +	enum rot_irq_status irq_status;
>>>>>> +	u32 val;
>>>>>> +
>>>>>> +	/* Get execution result */
>>>>>> +	irq_status = rotator_reg_get_irq_status(rot);
>>>>>> +
>>>>>> +	/* clear status */
>>>>>> +	val = rot_read(ROT_STATUS);
>>>>>> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
>>>>>> +	rot_write(val, ROT_STATUS);
>>>>>> +
>>>>>> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
>>>>>> +		event_work->ippdrv = ippdrv;
>>>>>> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
>>>>>> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
>>>>>> +		queue_work(ippdrv->event_workq,
>>>>>> +			(struct work_struct *)event_work);
>>>>>> +	} else
>>>>>> +		DRM_ERROR("the SFR is set illegally\n");
>>>>>> +
>>>>>> +	return IRQ_HANDLED;
>>>>>> +}
>>>>>> +
>>>>>> +static void rotator_align_size(struct rot_context *rot, u32 fmt,
>> u32
>>>>>> *hsize,
>>>>>> +		u32 *vsize)
>>>>>> +{
>>>>>> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
>>>>>> +	struct rot_limit *limit;
>>>>>> +	u32 mask, val;
>>>>>> +
>>>>>> +	/* Get size limit */
>>>>>> +	if (fmt == ROT_CONTROL_FMT_RGB888)
>>>>>> +		limit = &limit_tbl->rgb888;
>>>>>> +	else
>>>>>> +		limit = &limit_tbl->ycbcr420_2p;
>>>>>> +
>>>>>> +	/* Get mask for rounding to nearest aligned val */
>>>>>> +	mask = ~((1 << limit->align) - 1);
>>>>>> +
>>>>>> +	/* Set aligned width */
>>>>>> +	val = ROT_ALIGN(*hsize, limit->align, mask);
>>>>>> +	if (val < limit->min_w)
>>>>>> +		*hsize = ROT_MIN(limit->min_w, mask);
>>>>>> +	else if (val > limit->max_w)
>>>>>> +		*hsize = ROT_MAX(limit->max_w, mask);
>>>>>> +	else
>>>>>> +		*hsize = val;
>>>>>> +
>>>>>> +	/* Set aligned height */
>>>>>> +	val = ROT_ALIGN(*vsize, limit->align, mask);
>>>>>> +	if (val < limit->min_h)
>>>>>> +		*vsize = ROT_MIN(limit->min_h, mask);
>>>>>> +	else if (val > limit->max_h)
>>>>>> +		*vsize = ROT_MAX(limit->max_h, mask);
>>>>>> +	else
>>>>>> +		*vsize = val;
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	u32 val;
>>>>>> +
>>>>>> +	val = rot_read(ROT_CONTROL);
>>>>>> +	val &= ~ROT_CONTROL_FMT_MASK;
>>>>>> +
>>>>>> +	switch (fmt) {
>>>>>> +	case DRM_FORMAT_NV12:
>>>>>> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
>>>>>> +		break;
>>>>>> +	case DRM_FORMAT_XRGB8888:
>>>>>> +		val |= ROT_CONTROL_FMT_RGB888;
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		DRM_ERROR("invalid image format\n");
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	rot_write(val, ROT_CONTROL);
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_src_set_size(struct device *dev, int swap,
>>>>>> +		struct drm_exynos_pos *pos,
>>>>>> +		struct drm_exynos_sz *sz)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	u32 fmt, hsize, vsize;
>>>>>> +	u32 val;
>>>>>> +
>>>>>> +	/* Get format */
>>>>>> +	fmt = rotator_reg_get_format(rot);
>>>>>> +
>>>>>> +	/* Align buffer size */
>>>>>> +	hsize = sz->hsize;
>>>>>> +	vsize = sz->vsize;
>>>>>> +	rotator_align_size(rot, fmt, &hsize, &vsize);
>>>>>> +
>>>>>> +	/* Set buffer size configuration */
>>>>>> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
>>>>>> +	rot_write(val, ROT_SRC_BUF_SIZE);
>>>>>> +
>>>>>> +	/* Set crop image position configuration */
>>>>>> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
>>>>>> +	rot_write(val, ROT_SRC_CROP_POS);
>>>>>> +	val = ROT_SRC_CROP_SIZE_H(pos->h) |
>> ROT_SRC_CROP_SIZE_W(pos->w);
>>>>>> +	rot_write(val, ROT_SRC_CROP_SIZE);
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_src_set_addr(struct device *dev,
>>>>>> +		struct drm_exynos_ipp_buf_info *buf_info,
>>>>>> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
>>>>>> +	u32 val, fmt, hsize, vsize;
>>>>>> +	int i;
>>>>>> +
>>>>>> +	/* Set current buf_id */
>>>>>> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
>>>>>> +
>>>>>> +	switch (buf_type) {
>>>>>> +	case IPP_BUF_ENQUEUE:
>>>>>> +		/* Set address configuration */
>>>>>> +		for_each_ipp_planar(i)
>>>>>> +			addr[i] = buf_info->base[i];
>>>>>
>>>>> Check NULL.
>>>>
>>>> - If not copy this state, we need to memset about addr.
>>>>     no need check null.
>>>>
>>>>>
>>>>>> +
>>>>>> +		/* Get format */
>>>>>> +		fmt = rotator_reg_get_format(rot);
>>>>>> +
>>>>>> +		/* Re-set cb planar for NV12 format */
>>>>>> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
>>>>>> +					(addr[EXYNOS_DRM_PLANAR_CB]
>> ==
>>>>> 0x00)) {
>>>>>
>>>>> What is 0x00?
>>>>
>>>> - It is NULL, I changed !addr instead of addr == 0x00
>>>>
>>>>>
>>>>>> +
>>>>>> +			val = rot_read(ROT_SRC_BUF_SIZE);
>>>>>> +			hsize = ROT_GET_BUF_SIZE_W(val);
>>>>>> +			vsize = ROT_GET_BUF_SIZE_H(val);
>>>>>> +
>>>>>> +			/* Set cb planar */
>>>>>> +			addr[EXYNOS_DRM_PLANAR_CB] =
>>>>>> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize *
>> vsize;
>>>>>> +		}
>>>>>> +
>>>>>> +		for_each_ipp_planar(i)
>>>>>> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
>>>>>> +		break;
>>>>>> +	case IPP_BUF_DEQUEUE:
>>>>>> +		for_each_ipp_planar(i)
>>>>>> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		/* Nothing to do */
>>>>>> +		break;
>>>>>> +	}
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_dst_set_transf(struct device *dev,
>>>>>> +		enum drm_exynos_degree degree,
>>>>>> +		enum drm_exynos_flip flip)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	u32 val;
>>>>>> +
>>>>>> +	/* Set transform configuration */
>>>>>> +	val = rot_read(ROT_CONTROL);
>>>>>> +	val &= ~ROT_CONTROL_FLIP_MASK;
>>>>>> +
>>>>>> +	switch (flip) {
>>>>>> +	case EXYNOS_DRM_FLIP_VERTICAL:
>>>>>> +		val |= ROT_CONTROL_FLIP_VERTICAL;
>>>>>> +		break;
>>>>>> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
>>>>>> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		/* Flip None */
>>>>>> +		break;
>>>>>> +	}
>>>>>> +
>>>>>> +	val &= ~ROT_CONTROL_ROT_MASK;
>>>>>> +
>>>>>> +	switch (degree) {
>>>>>> +	case EXYNOS_DRM_DEGREE_90:
>>>>>> +		val |= ROT_CONTROL_ROT_90;
>>>>>> +		break;
>>>>>> +	case EXYNOS_DRM_DEGREE_180:
>>>>>> +		val |= ROT_CONTROL_ROT_180;
>>>>>> +		break;
>>>>>> +	case EXYNOS_DRM_DEGREE_270:
>>>>>> +		val |= ROT_CONTROL_ROT_270;
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		/* Rotation 0 Degree */
>>>>>> +		break;
>>>>>> +	}
>>>>>> +
>>>>>> +	rot_write(val, ROT_CONTROL);
>>>>>> +
>>>>>> +	/* Check degree for setting buffer size swap */
>>>>>> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
>>>>>> +		(degree == EXYNOS_DRM_DEGREE_270))
>>>>>> +		return 1;
>>>>>
>>>>> Correct return type. This function should return 0 or negative.
>>>>
>>>> - no ~ this return type is boolean true or false.
>>>>     but we need to error handling so, we use integer as you know
>>>>     we reviewed this routine at our local git from our team(you and
> me).
>>>>
>>>>>
>>>>>> +	else
>>>>>> +		return 0;
>>>>>
>>>>> Ditto.
>>>>
>>>> - ditto.

- modified it.

>>>>
>>>>>
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_dst_set_size(struct device *dev, int swap,
>>>>>> +		struct drm_exynos_pos *pos,
>>>>>> +		struct drm_exynos_sz *sz)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	u32 val, fmt, hsize, vsize;
>>>>>> +
>>>>>> +	/* Get format */
>>>>>> +	fmt = rotator_reg_get_format(rot);
>>>>>
>>>>> Check if fmt is valid or not.
>>>>
>>>> - added rotator_check_fmt()
>>>>
>>>>>
>>>>>> +
>>>>>> +	/* Align buffer size */
>>>>>> +	hsize = sz->hsize;
>>>>>> +	vsize = sz->vsize;
>>>>>> +	rotator_align_size(rot, fmt, &hsize, &vsize);
>>>>>> +
>>>>>> +	/* Set buffer size configuration */
>>>>>> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
>>>>>> +	rot_write(val, ROT_DST_BUF_SIZE);
>>>>>> +
>>>>>> +	/* Set crop image position configuration */
>>>>>> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
>>>>>> +	rot_write(val, ROT_DST_CROP_POS);
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_dst_set_addr(struct device *dev,
>>>>>> +		struct drm_exynos_ipp_buf_info *buf_info,
>>>>>> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
>>>>>> +	u32 val, fmt, hsize, vsize;
>>>>>> +	int i;
>>>>>> +
>>>>>> +	/* Set current buf_id */
>>>>>> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
>>>>>> +
>>>>>> +	switch (buf_type) {
>>>>>> +	case IPP_BUF_ENQUEUE:
>>>>>> +		/* Set address configuration */
>>>>>> +		for_each_ipp_planar(i)
>>>>>> +			addr[i] = buf_info->base[i];
>>>>>> +
>>>>>> +		/* Get format */
>>>>>> +		fmt = rotator_reg_get_format(rot);
>>>>>
>>>>> Check if fmt is valid or not.
>>>>
>>>> - done.
>>>>
>>>>>
>>>>>> +
>>>>>> +		/* Re-set cb planar for NV12 format */
>>>>>> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
>>>>>> +					(addr[EXYNOS_DRM_PLANAR_CB]
>> ==
>>>>> 0x00)) {
>>>>>> +			/* Get buf size */
>>>>>> +			val = rot_read(ROT_DST_BUF_SIZE);
>>>>>> +
>>>>>> +			hsize = ROT_GET_BUF_SIZE_W(val);
>>>>>> +			vsize = ROT_GET_BUF_SIZE_H(val);
>>>>>> +
>>>>>> +			/* Set cb planar */
>>>>>> +			addr[EXYNOS_DRM_PLANAR_CB] =
>>>>>> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize *
>> vsize;
>>>>>> +		}
>>>>>> +
>>>>>> +		for_each_ipp_planar(i)
>>>>>> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
>>>>>> +		break;
>>>>>> +	case IPP_BUF_DEQUEUE:
>>>>>> +		for_each_ipp_planar(i)
>>>>>> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		/* Nothing to do */
>>>>>> +		break;
>>>>>> +	}
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static struct exynos_drm_ipp_ops rot_src_ops = {
>>>>>> +	.set_fmt	=	rotator_src_set_fmt,
>>>>>> +	.set_size	=	rotator_src_set_size,
>>>>>> +	.set_addr	=	rotator_src_set_addr,
>>>>>> +};
>>>>>> +
>>>>>> +static struct exynos_drm_ipp_ops rot_dst_ops = {
>>>>>> +	.set_transf	=	rotator_dst_set_transf,
>>>>>> +	.set_size	=	rotator_dst_set_size,
>>>>>> +	.set_addr	=	rotator_dst_set_addr,
>>>>>> +};
>>>>>> +
>>>>>> +static int rotator_init_prop_list(struct exynos_drm_ippdrv
> *ippdrv)
>>>>>> +{
>>>>>> +	struct drm_exynos_ipp_prop_list *prop_list;
>>>>>> +
>>>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>>>> +
>>>>>> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
>>>>>> +	if (!prop_list) {
>>>>>> +		DRM_ERROR("failed to alloc property list.\n");
>>>>>> +		return -ENOMEM;
>>>>>> +	}
>>>>>> +
>>>>>> +	/*ToDo fix support function list*/
>>>>>> +
>>>>>> +	prop_list->version = 1;
>>>>>> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
>>>>>> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
>>>>>> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
>>>>>> +				(1 << EXYNOS_DRM_DEGREE_90) |
>>>>>> +				(1 << EXYNOS_DRM_DEGREE_180) |
>>>>>> +				(1 << EXYNOS_DRM_DEGREE_270);
>>>>>> +	prop_list->csc = 0;
>>>>>> +	prop_list->crop = 0;
>>>>>> +	prop_list->scale = 0;
>>>>>> +
>>>>>> +	ippdrv->prop_list = prop_list;
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_ippdrv_check_property(struct device *dev,
>>>>>> +		struct drm_exynos_ipp_property *property)
>>>>>> +{
>>>>>> +	struct drm_exynos_ipp_config *src_config =
>>>>>> +
>>>>> &property->config[EXYNOS_DRM_OPS_SRC];
>>>>>> +	struct drm_exynos_ipp_config *dst_config =
>>>>>> +
>>>>> &property->config[EXYNOS_DRM_OPS_DST];
>>>>>> +	struct drm_exynos_pos *src_pos = &src_config->pos;
>>>>>> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
>>>>>> +	struct drm_exynos_sz *src_sz = &src_config->sz;
>>>>>> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
>>>>>> +	bool swap = false;
>>>>>> +
>>>>>> +	/* Check format configuration */
>>>>>> +	if (src_config->fmt != dst_config->fmt) {
>>>>>> +		DRM_DEBUG_KMS("%s:not support csc feature\n",
>> __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	switch (src_config->fmt) {
>>>>>> +	case DRM_FORMAT_XRGB8888:
>>>>>> +	case DRM_FORMAT_NV12:
>>>>>> +		/* No problem */
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>
>>>>> Use macro instead of switch-case. this just checks only format type.
>>>>
>>>> - I don't thing so, If we make macro about this.
>>>>     then we got some confusion about this macro at next change.
>>>>     this case switch-case is better.
>>>>     Do you prefer this ? please one more comment.
>>>>
>>>> #define drm_check_fmt(fmt) if (fmt == DRM_FORMAT_XRGB8888 || fmt ==
>>>> DRM_FORMAT_NV12) \
>>>> 					return true;
>>>
>>> #define drm_check_fmt(fmt)		((fmt & (DRM_FORMAT_XRGB8888 |
>>> DRM_FORMAT_NV12)) ? true : false)
>>>
>>> if (drm_check_fmt(src_config->fmt))
>>> 	Something;
>>> else
>>> 	Something;
>>
>> Again,
>>
>> #define DRM_FORMAT_MASK			(DRM_FORMAT_XRGB888 |
>> DRM_FORMAT_NV12)
>>   #define drm_check_fmt(fmt)		(((fmt & DRM_FORMAT_MASK) &
>> (DRM_FORMAT_XRGB8888 | \
>>
>> DRM_FORMAT_NV12)) ? true : false)
>>
>>   if (drm_check_fmt(src_config->fmt))
>>   	Something;
>>   else
>>   	Something;
>>
>
> Ah, these format types have no bit field base. Just use switch-case and make
> this code  to one function. And then just call that function.

- OK I make one function and used it.

>
>
>>
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +	/* Check transform configuration */
>>>>>> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
>>>>>> +		DRM_DEBUG_KMS("%s:not support source-side
>> rotation\n",
>>>>>> +
>> __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	switch (dst_config->degree) {
>>>>>> +	case EXYNOS_DRM_DEGREE_90:
>>>>>> +	case EXYNOS_DRM_DEGREE_270:
>>>>>> +		swap = true;
>>>>>> +	case EXYNOS_DRM_DEGREE_0:
>>>>>> +	case EXYNOS_DRM_DEGREE_180:
>>>>>> +		/* No problem */
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
>>>>>> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
>>>>> __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	switch (dst_config->flip) {
>>>>>> +	case EXYNOS_DRM_FLIP_NONE:
>>>>>> +	case EXYNOS_DRM_FLIP_VERTICAL:
>>>>>> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
>>>>>> +		/* No problem */
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>
>>>>> Use macro instead of switch-case. this just checks only flip type.
>>>>
>>>> - ditto. please one more comment.
>>>>    in my opinion: I prefer enumeration use switch-case.
>>>
>>> What does each case do? Just check? If so, use macro as I mentioned. We
>>> can't accept this codes.

- ditto.

>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +	/* Check size configuration */
>>>>>> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
>>>>>> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
>>>>>> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n",
>> __func__);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	if (swap) {
>>>>>> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
>>>>>> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
>>>>>> +			DRM_DEBUG_KMS("%s:out of destination buffer
>>>>> bound\n",
>>>>>> +
>> __func__);
>>>>>> +			return -EINVAL;
>>>>>> +		}
>>>>>> +
>>>>>> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
>>>>> dst_pos->w))
>>>>>> {
>>>>>> +			DRM_DEBUG_KMS("%s:not support scale
>> feature\n",
>>>>>> +
>> __func__);
>>>>>> +			return -EINVAL;
>>>>>> +		}
>>>>>> +	} else {
>>>>>> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
>>>>>> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
>>>>>> +			DRM_DEBUG_KMS("%s:out of destination buffer
>>>>> bound\n",
>>>>>> +
>> __func__);
>>>>>> +			return -EINVAL;
>>>>>> +		}
>>>>>> +
>>>>>> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
>>>>> dst_pos->h))
>>>>>> {
>>>>>> +			DRM_DEBUG_KMS("%s:not support scale
>> feature\n",
>>>>>> +
>> __func__);
>>>>>> +			return -EINVAL;
>>>>>> +		}
>>>>>> +	}
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_ippdrv_start(struct device *dev, enum
>>>>>> drm_exynos_ipp_cmd cmd)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	u32 val;
>>>>>> +
>>>>>> +	if (rot->suspended) {
>>>>>> +		DRM_ERROR("suspended state\n");
>>>>>> +		return -EPERM;
>>>>>> +	}
>>>>>> +
>>>>>> +	if (cmd != IPP_CMD_M2M) {
>>>>>> +		DRM_ERROR("not support cmd: %d\n", cmd);
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	/* Set interrupt enable */
>>>>>> +	rotator_reg_set_irq(rot, true);
>>>>>> +
>>>>>> +	val = rot_read(ROT_CONTROL);
>>>>>> +	val |= ROT_CONTROL_START;
>>>>>> +
>>>>>> +	rot_write(val, ROT_CONTROL);
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int __devinit rotator_probe(struct platform_device *pdev)
>>>>>> +{
>>>>>> +	struct device *dev = &pdev->dev;
>>>>>> +	struct rot_context *rot;
>>>>>> +	struct resource *res;
>>>>>> +	struct exynos_drm_ippdrv *ippdrv;
>>>>>> +	int ret;
>>>>>> +
>>>>>> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
>>>>>> +	if (!rot) {
>>>>>> +		dev_err(dev, "failed to allocate rot\n");
>>>>>> +		return -ENOMEM;
>>>>>> +	}
>>>>>> +
>>>>>> +	rot->limit_tbl = (struct rot_limit_table *)
>>>>>> +
>> platform_get_device_id(pdev)->driver_data;
>>>>>> +
>>>>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>>> +	if (!res) {
>>>>>> +		dev_err(dev, "failed to find registers\n");
>>>>>> +		ret = -ENOENT;
>>>>>> +		goto err_get_resource;
>>>>>> +	}
>>>>>> +
>>>>>> +	rot->regs_res = request_mem_region(res->start,
>> resource_size(res),
>>>>>> +
>>>>> dev_name(dev));
>>>>>> +	if (!rot->regs_res) {
>>>>>> +		dev_err(dev, "failed to claim register region\n");
>>>>>> +		ret = -ENOENT;
>>>>>> +		goto err_get_resource;
>>>>>> +	}
>>>>>> +
>>>>>> +	rot->regs = ioremap(res->start, resource_size(res));
>>>>>> +	if (!rot->regs) {
>>>>>> +		dev_err(dev, "failed to map register\n");
>>>>>> +		ret = -ENXIO;
>>>>>> +		goto err_ioremap;
>>>>>> +	}
>>>>>> +
>>>>>> +	rot->irq = platform_get_irq(pdev, 0);
>>>>>> +	if (rot->irq < 0) {
>>>>>> +		dev_err(dev, "failed to get irq\n");
>>>>>> +		ret = rot->irq;
>>>>>> +		goto err_get_irq;
>>>>>> +	}
>>>>>> +
>>>>>> +	ret = request_threaded_irq(rot->irq, NULL,
>> rotator_irq_handler,
>>>>>> +					IRQF_ONESHOT, "drm_rotator",
>> rot);
>>>>>> +	if (ret < 0) {
>>>>>> +		dev_err(dev, "failed to request irq\n");
>>>>>> +		goto err_get_irq;
>>>>>> +	}
>>>>>> +
>>>>>> +	rot->clock = clk_get(dev, "rotator");
>>>>>> +	if (IS_ERR_OR_NULL(rot->clock)) {
>>>>>> +		dev_err(dev, "failed to get clock\n");
>>>>>> +		ret = PTR_ERR(rot->clock);
>>>>>> +		goto err_clk_get;
>>>>>> +	}
>>>>>> +
>>>>>> +	pm_runtime_enable(dev);
>>>>>> +
>>>>>> +	ippdrv = &rot->ippdrv;
>>>>>> +	ippdrv->dev = dev;
>>>>>> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
>>>>>> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
>>>>>> +	ippdrv->check_property = rotator_ippdrv_check_property;
>>>>>> +	ippdrv->start = rotator_ippdrv_start;
>>>>>> +	ret = rotator_init_prop_list(ippdrv);
>>>>>> +	if (ret < 0) {
>>>>>> +		dev_err(dev, "failed to init property list.\n");
>>>>>> +		goto err_ippdrv_register;
>>>>>> +	}
>>>>>> +
>>>>>> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
>>>>>> +
>>>>>> +	platform_set_drvdata(pdev, rot);
>>>>>> +
>>>>>> +	ret = exynos_drm_ippdrv_register(ippdrv);
>>>>>> +	if (ret < 0) {
>>>>>> +		dev_err(dev, "failed to register drm rotator
>> device\n");
>>>>>> +		kfree(ippdrv->prop_list);
>>>>>> +		goto err_ippdrv_register;
>>>>>> +	}
>>>>>> +
>>>>>> +	dev_info(dev, "The exynos rotator is probed
>> successfully\n");
>>>>>> +
>>>>>> +	return 0;
>>>>>> +
>>>>>> +err_ippdrv_register:
>>>>>> +	pm_runtime_disable(dev);
>>>>>> +	clk_put(rot->clock);
>>>>>> +err_clk_get:
>>>>>> +	free_irq(rot->irq, rot);
>>>>>> +err_get_irq:
>>>>>> +	iounmap(rot->regs);
>>>>>> +err_ioremap:
>>>>>> +	release_resource(rot->regs_res);
>>>>>> +	kfree(rot->regs_res);
>>>>>> +err_get_resource:
>>>>>> +	kfree(rot);
>>>>>> +	return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int __devexit rotator_remove(struct platform_device *pdev)
>>>>>> +{
>>>>>> +	struct device *dev = &pdev->dev;
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
>>>>>> +
>>>>>> +	kfree(ippdrv->prop_list);
>>>>>> +	exynos_drm_ippdrv_unregister(ippdrv);
>>>>>> +
>>>>>> +	pm_runtime_disable(dev);
>>>>>> +	clk_put(rot->clock);
>>>>>> +
>>>>>> +	free_irq(rot->irq, rot);
>>>>>> +
>>>>>> +	iounmap(rot->regs);
>>>>>> +
>>>>>> +	release_resource(rot->regs_res);
>>>>>> +	kfree(rot->regs_res);
>>>>>> +
>>>>>> +	kfree(rot);
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +struct rot_limit_table rot_limit_tbl = {
>>>>>> +	.ycbcr420_2p = {
>>>>>> +		.min_w = 32,
>>>>>> +		.min_h = 32,
>>>>>> +		.max_w = SZ_32K,
>>>>>> +		.max_h = SZ_32K,
>>>>>> +		.align = 3,
>>>>>> +	},
>>>>>> +	.rgb888 = {
>>>>>> +		.min_w = 8,
>>>>>> +		.min_h = 8,
>>>>>> +		.max_w = SZ_8K,
>>>>>> +		.max_h = SZ_8K,
>>>>>> +		.align = 2,
>>>>>> +	},
>>>>>> +};
>>>>>> +
>>>>>> +struct platform_device_id rotator_driver_ids[] = {
>>>>>> +	{
>>>>>> +		.name		= "exynos-rot",
>>>>>> +		.driver_data	= (unsigned long)&rot_limit_tbl,
>>>>>> +	},
>>>>>> +	{},
>>>>>> +};
>>>>>> +
>>>>>> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
>>>>>> +{
>>>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>>>> +
>>>>>> +	if (enable) {
>>>>>> +		clk_enable(rot->clock);
>>>>>> +		rot->suspended = false;
>>>>>> +	} else {
>>>>>> +		clk_disable(rot->clock);
>>>>>> +		rot->suspended = true;
>>>>>> +	}
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +
>>>>>> +#ifdef CONFIG_PM_SLEEP
>>>>>> +static int rotator_suspend(struct device *dev)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +
>>>>>> +	/* ToDo */
>>>>>> +
>>>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>>>> +
>>>>>> +	if (pm_runtime_suspended(dev))
>>>>>> +		return 0;
>>>>>> +
>>>>>> +	return rotator_clk_crtl(rot, false);
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_resume(struct device *dev)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +
>>>>>> +	/* ToDo */
>>>>>> +
>>>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>>>> +
>>>>>> +	if (!pm_runtime_suspended(dev))
>>>>>> +		return rotator_clk_crtl(rot, true);
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +#endif
>>>>>> +
>>>>>> +#ifdef CONFIG_PM_RUNTIME
>>>>>> +static int rotator_runtime_suspend(struct device *dev)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +
>>>>>> +	/* ToDo */
>>>>>> +
>>>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>>>> +
>>>>>> +	return  rotator_clk_crtl(rot, false);
>>>>>> +}
>>>>>> +
>>>>>> +static int rotator_runtime_resume(struct device *dev)
>>>>>> +{
>>>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>>>> +
>>>>>> +	/* ToDo */
>>>>>> +
>>>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>>>> +
>>>>>> +	return  rotator_clk_crtl(rot, true);
>>>>>> +}
>>>>>> +#endif
>>>>>> +
>>>>>> +static const struct dev_pm_ops rotator_pm_ops = {
>>>>>> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
>>>>>> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend,
>> rotator_runtime_resume,
>>>>>> +
>>>>> NULL)
>>>>>> +};
>>>>>> +
>>>>>> +struct platform_driver rotator_driver = {
>>>>>> +	.probe		= rotator_probe,
>>>>>> +	.remove		= __devexit_p(rotator_remove),
>>>>>> +	.id_table	= rotator_driver_ids,
>>>>>> +	.driver		= {
>>>>>> +		.name	= "exynos-rot",
>>>>>> +		.owner	= THIS_MODULE,
>>>>>> +		.pm	= &rotator_pm_ops,
>>>>>> +	},
>>>>>> +};
>>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>>>> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>>>> new file mode 100644
>>>>>> index 0000000..fe929c9
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>>>> @@ -0,0 +1,13 @@
>>>>>> +/*
>>>>>> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
>>>>>> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
>>>>>> + */
>>>>>> +
>>>>>> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
>>>>>> +#define	_EXYNOS_DRM_ROTATOR_H_
>>>>>> +
>>>>>> +#endif
>>>>>> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
>>>>>> b/drivers/gpu/drm/exynos/regs-rotator.h
>>>>>> new file mode 100644
>>>>>> index 0000000..a09ac6e
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
>>>>>> @@ -0,0 +1,73 @@
>>>>>> +/* drivers/gpu/drm/exynos/regs-rotator.h
>>>>>> + *
>>>>>> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
>>>>>> + *		http://www.samsung.com/
>>>>>> + *
>>>>>> + * Register definition file for Samsung Rotator Interface
> (Rotator)
>>>>>> driver
>>>>>> + *
>>>>>> + * This program is free software; you can redistribute it and/or
>>>> modify
>>>>>> + * it under the terms of the GNU General Public License version 2
>> as
>>>>>> + * published by the Free Software Foundation.
>>>>>> +*/
>>>>>> +
>>>>>> +#ifndef EXYNOS_REGS_ROTATOR_H
>>>>>> +#define EXYNOS_REGS_ROTATOR_H
>>>>>> +
>>>>>> +/* Configuration */
>>>>>> +#define ROT_CONFIG			0x00
>>>>>> +#define ROT_CONFIG_IRQ			(3 << 8)
>>>>>> +
>>>>>> +/* Image Control */
>>>>>> +#define ROT_CONTROL			0x10
>>>>>> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
>>>>>> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
>>>>>> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
>>>>>> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
>>>>>> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
>>>>>> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
>>>>>> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
>>>>>> +#define ROT_CONTROL_ROT_90		(1 << 4)
>>>>>> +#define ROT_CONTROL_ROT_180		(2 << 4)
>>>>>> +#define ROT_CONTROL_ROT_270		(3 << 4)
>>>>>> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
>>>>>> +#define ROT_CONTROL_START		(1 << 0)
>>>>>> +
>>>>>> +/* Status */
>>>>>> +#define ROT_STATUS			0x20
>>>>>> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
>>>>>> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
>>>>>> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
>>>>>> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
>>>>>> +
>>>>>> +/* Buffer Address */
>>>>>> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
>>>>>> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
>>>>>> +
>>>>>> +/* Buffer Size */
>>>>>> +#define ROT_SRC_BUF_SIZE		0x3c
>>>>>> +#define ROT_DST_BUF_SIZE		0x5c
>>>>>> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
>>>>>> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
>>>>>> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
>>>>>> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
>>>>>> +
>>>>>> +/* Crop Position */
>>>>>> +#define ROT_SRC_CROP_POS		0x40
>>>>>> +#define ROT_DST_CROP_POS		0x60
>>>>>> +#define ROT_CROP_POS_Y(x)		((x) << 16)
>>>>>> +#define ROT_CROP_POS_X(x)		((x) << 0)
>>>>>> +
>>>>>> +/* Source Crop Size */
>>>>>> +#define ROT_SRC_CROP_SIZE		0x44
>>>>>> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
>>>>>> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
>>>>>> +
>>>>>> +/* Round to nearest aligned value */
>>>>>> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) -
>> 1))) &
>>>>>> (mask))
>>>>>> +/* Minimum limit value */
>>>>>> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
>>>>>> +/* Maximum limit value */
>>>>>> +#define ROT_MAX(max, mask)		((max) & (mask))
>>>>>> +
>>>>>> +#endif /* EXYNOS_REGS_ROTATOR_H */
>>>>>> +
>>>>>> --
>>>>>> 1.7.0.4
>>>>>
>>>>>
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>
Eunchul Kim Dec. 12, 2012, 11:10 a.m. UTC | #8
Thank's for your comment.

BR
Eunchul Kim.

On 12/12/2012 06:46 PM, Inki Dae wrote:
>
> One more comment.
>
>
>> -----Original Message-----
>> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
>> Sent: Wednesday, December 12, 2012 6:26 PM
>> To: Inki Dae
>> Cc: dri-devel@lists.freedesktop.org; jy0.jeon@samsung.com;
>> yj44.cho@samsung.com; jmock.shin@samsung.com; jaejoon.seo@samsung.com;
>> kyungmin.park@samsung.com; sw0312.kim@samsung.com; Joonyoung Shim
>> Subject: Re: [RFC v3 4/5] drm/exynos: add rotator ipp driver
>>
>> Thank's your comment.
>>
>> I answer your comment. please check that.
>>
>> Thank's
>>
>> BR
>> Eunchul Kim
>>
>> On 12/12/2012 05:29 PM, Inki Dae wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Eunchul Kim [mailto:chulspro.kim@samsung.com]
>>>> Sent: Wednesday, December 12, 2012 4:35 PM
>>>> To: dri-devel@lists.freedesktop.org; inki.dae@samsung.com
>>>> Cc: jy0.jeon@samsung.com; yj44.cho@samsung.com; jmock.shin@samsung.com;
>>>> jaejoon.seo@samsung.com; kyungmin.park@samsung.com;
>>>> chulspro.kim@samsung.com
>>>> Subject: [RFC v3 4/5] drm/exynos: add rotator ipp driver
>>>>
>>>> Rotator supports rotation/crop/flip and input/output DMA operations
>>>> Rotator ipp driver supports 90,180,270 degree rotaion and vertical,
>>>> horizontal flip.
>>>> and has some limitations(source and destination format have to be same,
>> no
>>>> scaler)
>>>>
>>>> Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
>>>> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com>
>>>> ---
>>>>    drivers/gpu/drm/exynos/Kconfig              |    7 +
>>>>    drivers/gpu/drm/exynos/Makefile             |    1 +
>>>>    drivers/gpu/drm/exynos/exynos_drm_drv.c     |   15 +
>>>>    drivers/gpu/drm/exynos/exynos_drm_drv.h     |    1 +
>>>>    drivers/gpu/drm/exynos/exynos_drm_rotator.c |  829
>>>> +++++++++++++++++++++++++++
>>>>    drivers/gpu/drm/exynos/exynos_drm_rotator.h |   13 +
>>>>    drivers/gpu/drm/exynos/regs-rotator.h       |   73 +++
>>>>    7 files changed, 939 insertions(+), 0 deletions(-)
>>>>    create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>>    create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>>    create mode 100644 drivers/gpu/drm/exynos/regs-rotator.h
>>>>
>>>> diff --git a/drivers/gpu/drm/exynos/Kconfig
>>>> b/drivers/gpu/drm/exynos/Kconfig
>>>> index 4915ab6..4860835 100644
>>>> --- a/drivers/gpu/drm/exynos/Kconfig
>>>> +++ b/drivers/gpu/drm/exynos/Kconfig
>>>> @@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
>>>>    	depends on DRM_EXYNOS_IPP
>>>>    	help
>>>>    	  Choose this option if you want to use Exynos FIMC for DRM.
>>>> +
>>>> +config DRM_EXYNOS_ROTATOR
>>>> +	bool "Exynos DRM Rotator"
>>>> +	depends on DRM_EXYNOS_IPP
>>>> +	help
>>>> +	  Choose this option if you want to use Exynos Rotator for DRM.
>>>> +
>>>> diff --git a/drivers/gpu/drm/exynos/Makefile
>>>> b/drivers/gpu/drm/exynos/Makefile
>>>> index 9710024..3b70668 100644
>>>> --- a/drivers/gpu/drm/exynos/Makefile
>>>> +++ b/drivers/gpu/drm/exynos/Makefile
>>>> @@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+=
>>>> exynos_drm_vidi.o
>>>>    exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
>>>>    exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
>>>>    exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
>>>> +exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+= exynos_drm_rotator.o
>>>>
>>>>    obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>> b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>> index 73f02ac..09d884b 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
>>>> @@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
>>>>    		goto out_fimc;
>>>>    #endif
>>>>
>>>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>>>> +	ret = platform_driver_register(&rotator_driver);
>>>> +	if (ret < 0)
>>>> +		goto out_rotator;
>>>> +#endif
>>>> +
>>>>    #ifdef CONFIG_DRM_EXYNOS_IPP
>>>>    	ret = platform_driver_register(&ipp_driver);
>>>>    	if (ret < 0)
>>>> @@ -406,6 +412,11 @@ out_drm:
>>>>    out_ipp:
>>>>    #endif
>>>>
>>>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>>>> +	platform_driver_unregister(&rotator_driver);
>>>> +out_rotator:
>>>> +#endif
>>>> +
>>>>    #ifdef CONFIG_DRM_EXYNOS_FIMC
>>>>    	platform_driver_unregister(&fimc_driver);
>>>>    out_fimc:
>>>> @@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
>>>>    	platform_driver_unregister(&ipp_driver);
>>>>    #endif
>>>>
>>>> +#ifdef CONFIG_DRM_EXYNOS_ROTATOR
>>>> +	platform_driver_unregister(&rotator_driver);
>>>> +#endif
>>>> +
>>>>    #ifdef CONFIG_DRM_EXYNOS_FIMC
>>>>    	platform_driver_unregister(&fimc_driver);
>>>>    #endif
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>> b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>> index 14f9490..a74e37c 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
>>>> @@ -353,5 +353,6 @@ extern struct platform_driver
>>>> exynos_drm_common_hdmi_driver;
>>>>    extern struct platform_driver vidi_driver;
>>>>    extern struct platform_driver g2d_driver;
>>>>    extern struct platform_driver fimc_driver;
>>>> +extern struct platform_driver rotator_driver;
>>>>    extern struct platform_driver ipp_driver;
>>>>    #endif
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>> b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>> new file mode 100644
>>>> index 0000000..121569c
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
>>>> @@ -0,0 +1,829 @@
>>>> +/*
>>>> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
>>>> + * Authors:
>>>> + *	YoungJun Cho <yj44.cho@samsung.com>
>>>> + *	Eunchul Kim <chulspro.kim@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 Foundationr
>>>> + */
>>>> +
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/err.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/clk.h>
>>>> +#include <linux/pm_runtime.h>
>>>> +
>>>> +#include <drm/drmP.h>
>>>> +#include <drm/exynos_drm.h>
>>>> +#include "regs-rotator.h"
>>>> +#include "exynos_drm.h"
>>>> +#include "exynos_drm_ipp.h"
>>>> +
>>>> +/*
>>>> + * Rotator supports image crop/rotator and input/output DMA
> operations.
>>>> + * input DMA reads image data from the memory.
>>>> + * output DMA writes image data to memory.
>>>> + */
>>>> +
>>>> +#define get_rot_context(dev)
>>>> 	platform_get_drvdata(to_platform_device(dev))
>>>> +#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
>>>> +					struct rot_context, ippdrv);
>>>> +#define rot_read(offset)		readl(rot->regs + (offset))
>>>> +#define rot_write(cfg, offset)	writel(cfg, rot->regs + (offset))
>>>> +
>>>> +enum rot_irq_status {
>>>> +	ROT_IRQ_STATUS_COMPLETE	= 8,
>>>> +	ROT_IRQ_STATUS_ILLEGAL	= 9,
>>>> +};
>>>> +
>>>> +/*
>>>> + * A structure of limitation.
>>>> + *
>>>> + * @min_w: minimum width.
>>>> + * @min_h: minimum height.
>>>> + * @max_w: maximum width.
>>>> + * @max_h: maximum height.
>>>> + * @align: align size.
>>>> + */
>>>> +struct rot_limit {
>>>> +	u32	min_w;
>>>> +	u32	min_h;
>>>> +	u32	max_w;
>>>> +	u32	max_h;
>>>> +	u32	align;
>>>> +};
>>>> +
>>>> +/*
>>>> + * A structure of limitation table.
>>>> + *
>>>> + * @ycbcr420_2p: case of YUV.
>>>> + * @rgb888: case of RGB.
>>>> + */
>>>> +struct rot_limit_table {
>>>> +	struct rot_limit	ycbcr420_2p;
>>>> +	struct rot_limit	rgb888;
>>>> +};
>>>> +
>>>> +/*
>>>> + * A structure of rotator context.
>>>> + * @ippdrv: prepare initialization using ippdrv.
>>>> + * @regs_res: register resources.
>>>> + * @regs: memory mapped io registers.
>>>> + * @clock: rotator gate clock.
>>>> + * @limit_tbl: limitation of rotator.
>>>> + * @irq: irq number.
>>>> + * @cur_buf_id: current operation buffer id.
>>>> + * @suspended: suspended state.
>>>> + */
>>>> +struct rot_context {
>>>> +	struct exynos_drm_ippdrv	ippdrv;
>>>> +	struct resource	*regs_res;
>>>> +	void __iomem	*regs;
>>>> +	struct clk	*clock;
>>>> +	struct rot_limit_table	*limit_tbl;
>>>> +	int	irq;
>>>> +	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
>>>> +	bool	suspended;
>>>> +};
>>>> +
>>>> +static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
>>>> +{
>>>> +	u32 val = rot_read(ROT_CONFIG);
>>>> +
>>>> +	if (enable == true)
>>>> +		val |= ROT_CONFIG_IRQ;
>>>> +	else
>>>> +		val &= ~ROT_CONFIG_IRQ;
>>>> +
>>>> +	rot_write(val, ROT_CONFIG);
>>>> +}
>>>> +
>>>> +static u32 rotator_reg_get_format(struct rot_context *rot)
>>>> +{
>>>> +	u32 val = rot_read(ROT_CONTROL);
>>>> +
>>>> +	val &= ROT_CONTROL_FMT_MASK;
>>>> +
>>>> +	return val;
>>>> +}
>>>> +
>>>> +static enum rot_irq_status rotator_reg_get_irq_status(struct
>> rot_context
>>>> *rot)
>>>> +{
>>>> +	u32 val = rot_read(ROT_STATUS);
>>>> +
>>>> +	val = ROT_STATUS_IRQ(val);
>>>> +
>>>> +	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
>>>> +		return ROT_IRQ_STATUS_COMPLETE;
>>>> +	else
>>>
>>> Remove else.
>>
>> - done.
>>
>>>
>>>> +		return ROT_IRQ_STATUS_ILLEGAL;
>>>> +}
>>>> +
>>>> +static irqreturn_t rotator_irq_handler(int irq, void *arg)
>>>> +{
>>>> +	struct rot_context *rot = arg;
>>>> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
>>>> +	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
>>>> +	struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
>>>> +	enum rot_irq_status irq_status;
>>>> +	u32 val;
>>>> +
>>>> +	/* Get execution result */
>>>> +	irq_status = rotator_reg_get_irq_status(rot);
>>>> +
>>>> +	/* clear status */
>>>> +	val = rot_read(ROT_STATUS);
>>>> +	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
>>>> +	rot_write(val, ROT_STATUS);
>>>> +
>>>> +	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
>>>> +		event_work->ippdrv = ippdrv;
>>>> +		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
>>>> +			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
>>>> +		queue_work(ippdrv->event_workq,
>>>> +			(struct work_struct *)event_work);
>>>> +	} else
>>>> +		DRM_ERROR("the SFR is set illegally\n");
>>>> +
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static void rotator_align_size(struct rot_context *rot, u32 fmt, u32
>>>> *hsize,
>>>> +		u32 *vsize)
>>>> +{
>>>> +	struct rot_limit_table *limit_tbl = rot->limit_tbl;
>>>> +	struct rot_limit *limit;
>>>> +	u32 mask, val;
>>>> +
>>>> +	/* Get size limit */
>>>> +	if (fmt == ROT_CONTROL_FMT_RGB888)
>>>> +		limit = &limit_tbl->rgb888;
>>>> +	else
>>>> +		limit = &limit_tbl->ycbcr420_2p;
>>>> +
>>>> +	/* Get mask for rounding to nearest aligned val */
>>>> +	mask = ~((1 << limit->align) - 1);
>>>> +
>>>> +	/* Set aligned width */
>>>> +	val = ROT_ALIGN(*hsize, limit->align, mask);
>>>> +	if (val < limit->min_w)
>>>> +		*hsize = ROT_MIN(limit->min_w, mask);
>>>> +	else if (val > limit->max_w)
>>>> +		*hsize = ROT_MAX(limit->max_w, mask);
>>>> +	else
>>>> +		*hsize = val;
>>>> +
>>>> +	/* Set aligned height */
>>>> +	val = ROT_ALIGN(*vsize, limit->align, mask);
>>>> +	if (val < limit->min_h)
>>>> +		*vsize = ROT_MIN(limit->min_h, mask);
>>>> +	else if (val > limit->max_h)
>>>> +		*vsize = ROT_MAX(limit->max_h, mask);
>>>> +	else
>>>> +		*vsize = val;
>>>> +}
>>>> +
>>>> +static int rotator_src_set_fmt(struct device *dev, u32 fmt)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	u32 val;
>>>> +
>>>> +	val = rot_read(ROT_CONTROL);
>>>> +	val &= ~ROT_CONTROL_FMT_MASK;
>>>> +
>>>> +	switch (fmt) {
>>>> +	case DRM_FORMAT_NV12:
>>>> +		val |= ROT_CONTROL_FMT_YCBCR420_2P;
>>>> +		break;
>>>> +	case DRM_FORMAT_XRGB8888:
>>>> +		val |= ROT_CONTROL_FMT_RGB888;
>>>> +		break;
>>>> +	default:
>>>> +		DRM_ERROR("invalid image format\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	rot_write(val, ROT_CONTROL);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int rotator_src_set_size(struct device *dev, int swap,
>>>> +		struct drm_exynos_pos *pos,
>>>> +		struct drm_exynos_sz *sz)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	u32 fmt, hsize, vsize;
>>>> +	u32 val;
>>>> +
>>>> +	/* Get format */
>>>> +	fmt = rotator_reg_get_format(rot);
>>>> +
>>>> +	/* Align buffer size */
>>>> +	hsize = sz->hsize;
>>>> +	vsize = sz->vsize;
>>>> +	rotator_align_size(rot, fmt, &hsize, &vsize);
>>>> +
>>>> +	/* Set buffer size configuration */
>>>> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
>>>> +	rot_write(val, ROT_SRC_BUF_SIZE);
>>>> +
>>>> +	/* Set crop image position configuration */
>>>> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
>>>> +	rot_write(val, ROT_SRC_CROP_POS);
>>>> +	val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
>>>> +	rot_write(val, ROT_SRC_CROP_SIZE);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int rotator_src_set_addr(struct device *dev,
>>>> +		struct drm_exynos_ipp_buf_info *buf_info,
>>>> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
>>>> +	u32 val, fmt, hsize, vsize;
>>>> +	int i;
>>>> +
>>>> +	/* Set current buf_id */
>>>> +	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
>>>> +
>>>> +	switch (buf_type) {
>>>> +	case IPP_BUF_ENQUEUE:
>>>> +		/* Set address configuration */
>>>> +		for_each_ipp_planar(i)
>>>> +			addr[i] = buf_info->base[i];
>>>
>>> Check NULL.
>>
>> - If not copy this state, we need to memset about addr.
>>     no need check null.
>>
>>>
>>>> +
>>>> +		/* Get format */
>>>> +		fmt = rotator_reg_get_format(rot);
>>>> +
>>>> +		/* Re-set cb planar for NV12 format */
>>>> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
>>>> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
>>> 0x00)) {
>>>
>>> What is 0x00?
>>
>> - It is NULL, I changed !addr instead of addr == 0x00
>>
>>>
>>>> +
>>>> +			val = rot_read(ROT_SRC_BUF_SIZE);
>>>> +			hsize = ROT_GET_BUF_SIZE_W(val);
>>>> +			vsize = ROT_GET_BUF_SIZE_H(val);
>>>> +
>>>> +			/* Set cb planar */
>>>> +			addr[EXYNOS_DRM_PLANAR_CB] =
>>>> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
>>>> +		}
>>>> +
>>>> +		for_each_ipp_planar(i)
>>>> +			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
>>>> +		break;
>>>> +	case IPP_BUF_DEQUEUE:
>>>> +		for_each_ipp_planar(i)
>>>> +			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
>>>> +		break;
>>>> +	default:
>>>> +		/* Nothing to do */
>>>> +		break;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int rotator_dst_set_transf(struct device *dev,
>>>> +		enum drm_exynos_degree degree,
>>>> +		enum drm_exynos_flip flip)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	u32 val;
>>>> +
>>>> +	/* Set transform configuration */
>>>> +	val = rot_read(ROT_CONTROL);
>>>> +	val &= ~ROT_CONTROL_FLIP_MASK;
>>>> +
>>>> +	switch (flip) {
>>>> +	case EXYNOS_DRM_FLIP_VERTICAL:
>>>> +		val |= ROT_CONTROL_FLIP_VERTICAL;
>>>> +		break;
>>>> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
>>>> +		val |= ROT_CONTROL_FLIP_HORIZONTAL;
>>>> +		break;
>>>> +	default:
>>>> +		/* Flip None */
>>>> +		break;
>>>> +	}
>>>> +
>>>> +	val &= ~ROT_CONTROL_ROT_MASK;
>>>> +
>>>> +	switch (degree) {
>>>> +	case EXYNOS_DRM_DEGREE_90:
>>>> +		val |= ROT_CONTROL_ROT_90;
>>>> +		break;
>>>> +	case EXYNOS_DRM_DEGREE_180:
>>>> +		val |= ROT_CONTROL_ROT_180;
>>>> +		break;
>>>> +	case EXYNOS_DRM_DEGREE_270:
>>>> +		val |= ROT_CONTROL_ROT_270;
>>>> +		break;
>>>> +	default:
>>>> +		/* Rotation 0 Degree */
>>>> +		break;
>>>> +	}
>>>> +
>>>> +	rot_write(val, ROT_CONTROL);
>>>> +
>>>> +	/* Check degree for setting buffer size swap */
>>>> +	if ((degree == EXYNOS_DRM_DEGREE_90) ||
>>>> +		(degree == EXYNOS_DRM_DEGREE_270))
>>>> +		return 1;
>>>
>>> Correct return type. This function should return 0 or negative.
>>
>> - no ~ this return type is boolean true or false.
>>     but we need to error handling so, we use integer as you know
>>     we reviewed this routine at our local git from our team(you and me).
>>
>
> Ok, then what do the below codes mean?
>
> 	/* set transform for rotation, flip */
> 	if (ops->set_transf) {
> 		swap = ops->set_transf(ippdrv->dev, config->degree,
> 			config->flip);
> 		if (swap < 0) {
> 			DRM_ERROR("not support tranf.\n");
> 			return -EINVAL;
> 		}
>
> If return value should be true or false then make sure error checking.

- modified it. using pointer.

>
>>>
>>>> +	else
>>>> +		return 0;
>>>
>>> Ditto.
>>
>> - ditto.
>>
>>>
>>>> +}
>>>> +
>>>> +static int rotator_dst_set_size(struct device *dev, int swap,
>>>> +		struct drm_exynos_pos *pos,
>>>> +		struct drm_exynos_sz *sz)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	u32 val, fmt, hsize, vsize;
>>>> +
>>>> +	/* Get format */
>>>> +	fmt = rotator_reg_get_format(rot);
>>>
>>> Check if fmt is valid or not.
>>
>> - added rotator_check_fmt()
>>
>>>
>>>> +
>>>> +	/* Align buffer size */
>>>> +	hsize = sz->hsize;
>>>> +	vsize = sz->vsize;
>>>> +	rotator_align_size(rot, fmt, &hsize, &vsize);
>>>> +
>>>> +	/* Set buffer size configuration */
>>>> +	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
>>>> +	rot_write(val, ROT_DST_BUF_SIZE);
>>>> +
>>>> +	/* Set crop image position configuration */
>>>> +	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
>>>> +	rot_write(val, ROT_DST_CROP_POS);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int rotator_dst_set_addr(struct device *dev,
>>>> +		struct drm_exynos_ipp_buf_info *buf_info,
>>>> +		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
>>>> +	u32 val, fmt, hsize, vsize;
>>>> +	int i;
>>>> +
>>>> +	/* Set current buf_id */
>>>> +	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
>>>> +
>>>> +	switch (buf_type) {
>>>> +	case IPP_BUF_ENQUEUE:
>>>> +		/* Set address configuration */
>>>> +		for_each_ipp_planar(i)
>>>> +			addr[i] = buf_info->base[i];
>>>> +
>>>> +		/* Get format */
>>>> +		fmt = rotator_reg_get_format(rot);
>>>
>>> Check if fmt is valid or not.
>>
>> - done.
>>
>>>
>>>> +
>>>> +		/* Re-set cb planar for NV12 format */
>>>> +		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
>>>> +					(addr[EXYNOS_DRM_PLANAR_CB] ==
>>> 0x00)) {
>>>> +			/* Get buf size */
>>>> +			val = rot_read(ROT_DST_BUF_SIZE);
>>>> +
>>>> +			hsize = ROT_GET_BUF_SIZE_W(val);
>>>> +			vsize = ROT_GET_BUF_SIZE_H(val);
>>>> +
>>>> +			/* Set cb planar */
>>>> +			addr[EXYNOS_DRM_PLANAR_CB] =
>>>> +				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
>>>> +		}
>>>> +
>>>> +		for_each_ipp_planar(i)
>>>> +			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
>>>> +		break;
>>>> +	case IPP_BUF_DEQUEUE:
>>>> +		for_each_ipp_planar(i)
>>>> +			rot_write(0x0, ROT_DST_BUF_ADDR(i));
>>>> +		break;
>>>> +	default:
>>>> +		/* Nothing to do */
>>>> +		break;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static struct exynos_drm_ipp_ops rot_src_ops = {
>>>> +	.set_fmt	=	rotator_src_set_fmt,
>>>> +	.set_size	=	rotator_src_set_size,
>>>> +	.set_addr	=	rotator_src_set_addr,
>>>> +};
>>>> +
>>>> +static struct exynos_drm_ipp_ops rot_dst_ops = {
>>>> +	.set_transf	=	rotator_dst_set_transf,
>>>> +	.set_size	=	rotator_dst_set_size,
>>>> +	.set_addr	=	rotator_dst_set_addr,
>>>> +};
>>>> +
>>>> +static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
>>>> +{
>>>> +	struct drm_exynos_ipp_prop_list *prop_list;
>>>> +
>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>> +
>>>> +	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
>>>> +	if (!prop_list) {
>>>> +		DRM_ERROR("failed to alloc property list.\n");
>>>> +		return -ENOMEM;
>>>> +	}
>>>> +
>>>> +	/*ToDo fix support function list*/
>>>> +
>>>> +	prop_list->version = 1;
>>>> +	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
>>>> +				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
>>>> +	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
>>>> +				(1 << EXYNOS_DRM_DEGREE_90) |
>>>> +				(1 << EXYNOS_DRM_DEGREE_180) |
>>>> +				(1 << EXYNOS_DRM_DEGREE_270);
>>>> +	prop_list->csc = 0;
>>>> +	prop_list->crop = 0;
>>>> +	prop_list->scale = 0;
>>>> +
>>>> +	ippdrv->prop_list = prop_list;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int rotator_ippdrv_check_property(struct device *dev,
>>>> +		struct drm_exynos_ipp_property *property)
>>>> +{
>>>> +	struct drm_exynos_ipp_config *src_config =
>>>> +
>>> &property->config[EXYNOS_DRM_OPS_SRC];
>>>> +	struct drm_exynos_ipp_config *dst_config =
>>>> +
>>> &property->config[EXYNOS_DRM_OPS_DST];
>>>> +	struct drm_exynos_pos *src_pos = &src_config->pos;
>>>> +	struct drm_exynos_pos *dst_pos = &dst_config->pos;
>>>> +	struct drm_exynos_sz *src_sz = &src_config->sz;
>>>> +	struct drm_exynos_sz *dst_sz = &dst_config->sz;
>>>> +	bool swap = false;
>>>> +
>>>> +	/* Check format configuration */
>>>> +	if (src_config->fmt != dst_config->fmt) {
>>>> +		DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	switch (src_config->fmt) {
>>>> +	case DRM_FORMAT_XRGB8888:
>>>> +	case DRM_FORMAT_NV12:
>>>> +		/* No problem */
>>>> +		break;
>>>> +	default:
>>>> +		DRM_DEBUG_KMS("%s:not support format\n", __func__);
>>>> +		return -EINVAL;
>>>> +	}
>>>
>>> Use macro instead of switch-case. this just checks only format type.
>>
>> - I don't thing so, If we make macro about this.
>>     then we got some confusion about this macro at next change.
>>     this case switch-case is better.
>>     Do you prefer this ? please one more comment.
>>
>> #define drm_check_fmt(fmt) if (fmt == DRM_FORMAT_XRGB8888 || fmt ==
>> DRM_FORMAT_NV12) \
>> 					return true;
>>
>>>
>>>> +
>>>> +	/* Check transform configuration */
>>>> +	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
>>>> +		DRM_DEBUG_KMS("%s:not support source-side rotation\n",
>>>> +								__func__);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	switch (dst_config->degree) {
>>>> +	case EXYNOS_DRM_DEGREE_90:
>>>> +	case EXYNOS_DRM_DEGREE_270:
>>>> +		swap = true;
>>>> +	case EXYNOS_DRM_DEGREE_0:
>>>> +	case EXYNOS_DRM_DEGREE_180:
>>>> +		/* No problem */
>>>> +		break;
>>>> +	default:
>>>> +		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
>>>> +		DRM_DEBUG_KMS("%s:not support source-side flip\n",
>>> __func__);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	switch (dst_config->flip) {
>>>> +	case EXYNOS_DRM_FLIP_NONE:
>>>> +	case EXYNOS_DRM_FLIP_VERTICAL:
>>>> +	case EXYNOS_DRM_FLIP_HORIZONTAL:
>>>> +		/* No problem */
>>>> +		break;
>>>> +	default:
>>>> +		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
>>>> +		return -EINVAL;
>>>> +	}
>>>
>>> Use macro instead of switch-case. this just checks only flip type.
>>
>> - ditto. please one more comment.
>>    in my opinion: I prefer enumeration use switch-case.
>>
>>>
>>>> +
>>>> +	/* Check size configuration */
>>>> +	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
>>>> +		(src_pos->y + src_pos->h > src_sz->vsize)) {
>>>> +		DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	if (swap) {
>>>> +		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
>>>> +			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
>>>> +			DRM_DEBUG_KMS("%s:out of destination buffer
>>> bound\n",
>>>> +								__func__);
>>>> +			return -EINVAL;
>>>> +		}
>>>> +
>>>> +		if ((src_pos->w != dst_pos->h) || (src_pos->h !=
>>> dst_pos->w))
>>>> {
>>>> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
>>>> +								__func__);
>>>> +			return -EINVAL;
>>>> +		}
>>>> +	} else {
>>>> +		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
>>>> +			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
>>>> +			DRM_DEBUG_KMS("%s:out of destination buffer
>>> bound\n",
>>>> +								__func__);
>>>> +			return -EINVAL;
>>>> +		}
>>>> +
>>>> +		if ((src_pos->w != dst_pos->w) || (src_pos->h !=
>>> dst_pos->h))
>>>> {
>>>> +			DRM_DEBUG_KMS("%s:not support scale feature\n",
>>>> +								__func__);
>>>> +			return -EINVAL;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int rotator_ippdrv_start(struct device *dev, enum
>>>> drm_exynos_ipp_cmd cmd)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	u32 val;
>>>> +
>>>> +	if (rot->suspended) {
>>>> +		DRM_ERROR("suspended state\n");
>>>> +		return -EPERM;
>>>> +	}
>>>> +
>>>> +	if (cmd != IPP_CMD_M2M) {
>>>> +		DRM_ERROR("not support cmd: %d\n", cmd);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	/* Set interrupt enable */
>>>> +	rotator_reg_set_irq(rot, true);
>>>> +
>>>> +	val = rot_read(ROT_CONTROL);
>>>> +	val |= ROT_CONTROL_START;
>>>> +
>>>> +	rot_write(val, ROT_CONTROL);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int __devinit rotator_probe(struct platform_device *pdev)
>>>> +{
>>>> +	struct device *dev = &pdev->dev;
>>>> +	struct rot_context *rot;
>>>> +	struct resource *res;
>>>> +	struct exynos_drm_ippdrv *ippdrv;
>>>> +	int ret;
>>>> +
>>>> +	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
>>>> +	if (!rot) {
>>>> +		dev_err(dev, "failed to allocate rot\n");
>>>> +		return -ENOMEM;
>>>> +	}
>>>> +
>>>> +	rot->limit_tbl = (struct rot_limit_table *)
>>>> +				platform_get_device_id(pdev)->driver_data;
>>>> +
>>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> +	if (!res) {
>>>> +		dev_err(dev, "failed to find registers\n");
>>>> +		ret = -ENOENT;
>>>> +		goto err_get_resource;
>>>> +	}
>>>> +
>>>> +	rot->regs_res = request_mem_region(res->start, resource_size(res),
>>>> +
>>> dev_name(dev));
>>>> +	if (!rot->regs_res) {
>>>> +		dev_err(dev, "failed to claim register region\n");
>>>> +		ret = -ENOENT;
>>>> +		goto err_get_resource;
>>>> +	}
>>>> +
>>>> +	rot->regs = ioremap(res->start, resource_size(res));
>>>> +	if (!rot->regs) {
>>>> +		dev_err(dev, "failed to map register\n");
>>>> +		ret = -ENXIO;
>>>> +		goto err_ioremap;
>>>> +	}
>>>> +
>>>> +	rot->irq = platform_get_irq(pdev, 0);
>>>> +	if (rot->irq < 0) {
>>>> +		dev_err(dev, "failed to get irq\n");
>>>> +		ret = rot->irq;
>>>> +		goto err_get_irq;
>>>> +	}
>>>> +
>>>> +	ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler,
>>>> +					IRQF_ONESHOT, "drm_rotator", rot);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "failed to request irq\n");
>>>> +		goto err_get_irq;
>>>> +	}
>>>> +
>>>> +	rot->clock = clk_get(dev, "rotator");
>>>> +	if (IS_ERR_OR_NULL(rot->clock)) {
>>>> +		dev_err(dev, "failed to get clock\n");
>>>> +		ret = PTR_ERR(rot->clock);
>>>> +		goto err_clk_get;
>>>> +	}
>>>> +
>>>> +	pm_runtime_enable(dev);
>>>> +
>>>> +	ippdrv = &rot->ippdrv;
>>>> +	ippdrv->dev = dev;
>>>> +	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
>>>> +	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
>>>> +	ippdrv->check_property = rotator_ippdrv_check_property;
>>>> +	ippdrv->start = rotator_ippdrv_start;
>>>> +	ret = rotator_init_prop_list(ippdrv);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "failed to init property list.\n");
>>>> +		goto err_ippdrv_register;
>>>> +	}
>>>> +
>>>> +	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
>>>> +
>>>> +	platform_set_drvdata(pdev, rot);
>>>> +
>>>> +	ret = exynos_drm_ippdrv_register(ippdrv);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "failed to register drm rotator device\n");
>>>> +		kfree(ippdrv->prop_list);
>>>> +		goto err_ippdrv_register;
>>>> +	}
>>>> +
>>>> +	dev_info(dev, "The exynos rotator is probed successfully\n");
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_ippdrv_register:
>>>> +	pm_runtime_disable(dev);
>>>> +	clk_put(rot->clock);
>>>> +err_clk_get:
>>>> +	free_irq(rot->irq, rot);
>>>> +err_get_irq:
>>>> +	iounmap(rot->regs);
>>>> +err_ioremap:
>>>> +	release_resource(rot->regs_res);
>>>> +	kfree(rot->regs_res);
>>>> +err_get_resource:
>>>> +	kfree(rot);
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int __devexit rotator_remove(struct platform_device *pdev)
>>>> +{
>>>> +	struct device *dev = &pdev->dev;
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
>>>> +
>>>> +	kfree(ippdrv->prop_list);
>>>> +	exynos_drm_ippdrv_unregister(ippdrv);
>>>> +
>>>> +	pm_runtime_disable(dev);
>>>> +	clk_put(rot->clock);
>>>> +
>>>> +	free_irq(rot->irq, rot);
>>>> +
>>>> +	iounmap(rot->regs);
>>>> +
>>>> +	release_resource(rot->regs_res);
>>>> +	kfree(rot->regs_res);
>>>> +
>>>> +	kfree(rot);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +struct rot_limit_table rot_limit_tbl = {
>>>> +	.ycbcr420_2p = {
>>>> +		.min_w = 32,
>>>> +		.min_h = 32,
>>>> +		.max_w = SZ_32K,
>>>> +		.max_h = SZ_32K,
>>>> +		.align = 3,
>>>> +	},
>>>> +	.rgb888 = {
>>>> +		.min_w = 8,
>>>> +		.min_h = 8,
>>>> +		.max_w = SZ_8K,
>>>> +		.max_h = SZ_8K,
>>>> +		.align = 2,
>>>> +	},
>>>> +};
>>>> +
>>>> +struct platform_device_id rotator_driver_ids[] = {
>>>> +	{
>>>> +		.name		= "exynos-rot",
>>>> +		.driver_data	= (unsigned long)&rot_limit_tbl,
>>>> +	},
>>>> +	{},
>>>> +};
>>>> +
>>>> +static int rotator_clk_crtl(struct rot_context *rot, bool enable)
>>>> +{
>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>> +
>>>> +	if (enable) {
>>>> +		clk_enable(rot->clock);
>>>> +		rot->suspended = false;
>>>> +	} else {
>>>> +		clk_disable(rot->clock);
>>>> +		rot->suspended = true;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +
>>>> +#ifdef CONFIG_PM_SLEEP
>>>> +static int rotator_suspend(struct device *dev)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +
>>>> +	/* ToDo */
>>>> +
>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>> +
>>>> +	if (pm_runtime_suspended(dev))
>>>> +		return 0;
>>>> +
>>>> +	return rotator_clk_crtl(rot, false);
>>>> +}
>>>> +
>>>> +static int rotator_resume(struct device *dev)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +
>>>> +	/* ToDo */
>>>> +
>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>> +
>>>> +	if (!pm_runtime_suspended(dev))
>>>> +		return rotator_clk_crtl(rot, true);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +#endif
>>>> +
>>>> +#ifdef CONFIG_PM_RUNTIME
>>>> +static int rotator_runtime_suspend(struct device *dev)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +
>>>> +	/* ToDo */
>>>> +
>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>> +
>>>> +	return  rotator_clk_crtl(rot, false);
>>>> +}
>>>> +
>>>> +static int rotator_runtime_resume(struct device *dev)
>>>> +{
>>>> +	struct rot_context *rot = dev_get_drvdata(dev);
>>>> +
>>>> +	/* ToDo */
>>>> +
>>>> +	DRM_DEBUG_KMS("%s\n", __func__);
>>>> +
>>>> +	return  rotator_clk_crtl(rot, true);
>>>> +}
>>>> +#endif
>>>> +
>>>> +static const struct dev_pm_ops rotator_pm_ops = {
>>>> +	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
>>>> +	SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
>>>> +
>>> NULL)
>>>> +};
>>>> +
>>>> +struct platform_driver rotator_driver = {
>>>> +	.probe		= rotator_probe,
>>>> +	.remove		= __devexit_p(rotator_remove),
>>>> +	.id_table	= rotator_driver_ids,
>>>> +	.driver		= {
>>>> +		.name	= "exynos-rot",
>>>> +		.owner	= THIS_MODULE,
>>>> +		.pm	= &rotator_pm_ops,
>>>> +	},
>>>> +};
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>> b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>> new file mode 100644
>>>> index 0000000..fe929c9
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
>>>> @@ -0,0 +1,13 @@
>>>> +/*
>>>> + * Copyright (C) 2012 Samsung Electronics Co.Ltd
>>>> + * Authors: YoungJun Cho <yj44.cho@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 Foundationr
>>>> + */
>>>> +
>>>> +#ifndef	_EXYNOS_DRM_ROTATOR_H_
>>>> +#define	_EXYNOS_DRM_ROTATOR_H_
>>>> +
>>>> +#endif
>>>> diff --git a/drivers/gpu/drm/exynos/regs-rotator.h
>>>> b/drivers/gpu/drm/exynos/regs-rotator.h
>>>> new file mode 100644
>>>> index 0000000..a09ac6e
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/exynos/regs-rotator.h
>>>> @@ -0,0 +1,73 @@
>>>> +/* drivers/gpu/drm/exynos/regs-rotator.h
>>>> + *
>>>> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
>>>> + *		http://www.samsung.com/
>>>> + *
>>>> + * Register definition file for Samsung Rotator Interface (Rotator)
>>>> driver
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or
>> modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> +*/
>>>> +
>>>> +#ifndef EXYNOS_REGS_ROTATOR_H
>>>> +#define EXYNOS_REGS_ROTATOR_H
>>>> +
>>>> +/* Configuration */
>>>> +#define ROT_CONFIG			0x00
>>>> +#define ROT_CONFIG_IRQ			(3 << 8)
>>>> +
>>>> +/* Image Control */
>>>> +#define ROT_CONTROL			0x10
>>>> +#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
>>>> +#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
>>>> +#define ROT_CONTROL_FMT_RGB888		(6 << 8)
>>>> +#define ROT_CONTROL_FMT_MASK		(7 << 8)
>>>> +#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
>>>> +#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
>>>> +#define ROT_CONTROL_FLIP_MASK		(3 << 6)
>>>> +#define ROT_CONTROL_ROT_90		(1 << 4)
>>>> +#define ROT_CONTROL_ROT_180		(2 << 4)
>>>> +#define ROT_CONTROL_ROT_270		(3 << 4)
>>>> +#define ROT_CONTROL_ROT_MASK		(3 << 4)
>>>> +#define ROT_CONTROL_START		(1 << 0)
>>>> +
>>>> +/* Status */
>>>> +#define ROT_STATUS			0x20
>>>> +#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
>>>> +#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
>>>> +#define ROT_STATUS_IRQ_VAL_COMPLETE	1
>>>> +#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
>>>> +
>>>> +/* Buffer Address */
>>>> +#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
>>>> +#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
>>>> +
>>>> +/* Buffer Size */
>>>> +#define ROT_SRC_BUF_SIZE		0x3c
>>>> +#define ROT_DST_BUF_SIZE		0x5c
>>>> +#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
>>>> +#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
>>>> +#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
>>>> +#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
>>>> +
>>>> +/* Crop Position */
>>>> +#define ROT_SRC_CROP_POS		0x40
>>>> +#define ROT_DST_CROP_POS		0x60
>>>> +#define ROT_CROP_POS_Y(x)		((x) << 16)
>>>> +#define ROT_CROP_POS_X(x)		((x) << 0)
>>>> +
>>>> +/* Source Crop Size */
>>>> +#define ROT_SRC_CROP_SIZE		0x44
>>>> +#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
>>>> +#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
>>>> +
>>>> +/* Round to nearest aligned value */
>>>> +#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) - 1))) &
>>>> (mask))
>>>> +/* Minimum limit value */
>>>> +#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
>>>> +/* Maximum limit value */
>>>> +#define ROT_MAX(max, mask)		((max) & (mask))
>>>> +
>>>> +#endif /* EXYNOS_REGS_ROTATOR_H */
>>>> +
>>>> --
>>>> 1.7.0.4
>>>
>>>
>
>
diff mbox

Patch

diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 4915ab6..4860835 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -57,3 +57,10 @@  config DRM_EXYNOS_FIMC
 	depends on DRM_EXYNOS_IPP
 	help
 	  Choose this option if you want to use Exynos FIMC for DRM.
+
+config DRM_EXYNOS_ROTATOR
+	bool "Exynos DRM Rotator"
+	depends on DRM_EXYNOS_IPP
+	help
+	  Choose this option if you want to use Exynos Rotator for DRM.
+
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 9710024..3b70668 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -18,5 +18,6 @@  exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+= exynos_drm_vidi.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_G2D)	+= exynos_drm_g2d.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_IPP)	+= exynos_drm_ipp.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC)	+= exynos_drm_fimc.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR)	+= exynos_drm_rotator.o
 
 obj-$(CONFIG_DRM_EXYNOS)		+= exynosdrm.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 73f02ac..09d884b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -378,6 +378,12 @@  static int __init exynos_drm_init(void)
 		goto out_fimc;
 #endif
 
+#ifdef CONFIG_DRM_EXYNOS_ROTATOR
+	ret = platform_driver_register(&rotator_driver);
+	if (ret < 0)
+		goto out_rotator;
+#endif
+
 #ifdef CONFIG_DRM_EXYNOS_IPP
 	ret = platform_driver_register(&ipp_driver);
 	if (ret < 0)
@@ -406,6 +412,11 @@  out_drm:
 out_ipp:
 #endif
 
+#ifdef CONFIG_DRM_EXYNOS_ROTATOR
+	platform_driver_unregister(&rotator_driver);
+out_rotator:
+#endif
+
 #ifdef CONFIG_DRM_EXYNOS_FIMC
 	platform_driver_unregister(&fimc_driver);
 out_fimc:
@@ -451,6 +462,10 @@  static void __exit exynos_drm_exit(void)
 	platform_driver_unregister(&ipp_driver);
 #endif
 
+#ifdef CONFIG_DRM_EXYNOS_ROTATOR
+	platform_driver_unregister(&rotator_driver);
+#endif
+
 #ifdef CONFIG_DRM_EXYNOS_FIMC
 	platform_driver_unregister(&fimc_driver);
 #endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 14f9490..a74e37c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -353,5 +353,6 @@  extern struct platform_driver exynos_drm_common_hdmi_driver;
 extern struct platform_driver vidi_driver;
 extern struct platform_driver g2d_driver;
 extern struct platform_driver fimc_driver;
+extern struct platform_driver rotator_driver;
 extern struct platform_driver ipp_driver;
 #endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
new file mode 100644
index 0000000..121569c
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -0,0 +1,829 @@ 
+/*
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Authors:
+ *	YoungJun Cho <yj44.cho@samsung.com>
+ *	Eunchul Kim <chulspro.kim@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 Foundationr
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drmP.h>
+#include <drm/exynos_drm.h>
+#include "regs-rotator.h"
+#include "exynos_drm.h"
+#include "exynos_drm_ipp.h"
+
+/*
+ * Rotator supports image crop/rotator and input/output DMA operations.
+ * input DMA reads image data from the memory.
+ * output DMA writes image data to memory.
+ */
+
+#define get_rot_context(dev)	platform_get_drvdata(to_platform_device(dev))
+#define get_ctx_from_ippdrv(ippdrv)	container_of(ippdrv,\
+					struct rot_context, ippdrv);
+#define rot_read(offset)		readl(rot->regs + (offset))
+#define rot_write(cfg, offset)	writel(cfg, rot->regs + (offset))
+
+enum rot_irq_status {
+	ROT_IRQ_STATUS_COMPLETE	= 8,
+	ROT_IRQ_STATUS_ILLEGAL	= 9,
+};
+
+/*
+ * A structure of limitation.
+ *
+ * @min_w: minimum width.
+ * @min_h: minimum height.
+ * @max_w: maximum width.
+ * @max_h: maximum height.
+ * @align: align size.
+ */
+struct rot_limit {
+	u32	min_w;
+	u32	min_h;
+	u32	max_w;
+	u32	max_h;
+	u32	align;
+};
+
+/*
+ * A structure of limitation table.
+ *
+ * @ycbcr420_2p: case of YUV.
+ * @rgb888: case of RGB.
+ */
+struct rot_limit_table {
+	struct rot_limit	ycbcr420_2p;
+	struct rot_limit	rgb888;
+};
+
+/*
+ * A structure of rotator context.
+ * @ippdrv: prepare initialization using ippdrv.
+ * @regs_res: register resources.
+ * @regs: memory mapped io registers.
+ * @clock: rotator gate clock.
+ * @limit_tbl: limitation of rotator.
+ * @irq: irq number.
+ * @cur_buf_id: current operation buffer id.
+ * @suspended: suspended state.
+ */
+struct rot_context {
+	struct exynos_drm_ippdrv	ippdrv;
+	struct resource	*regs_res;
+	void __iomem	*regs;
+	struct clk	*clock;
+	struct rot_limit_table	*limit_tbl;
+	int	irq;
+	int	cur_buf_id[EXYNOS_DRM_OPS_MAX];
+	bool	suspended;
+};
+
+static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
+{
+	u32 val = rot_read(ROT_CONFIG);
+
+	if (enable == true)
+		val |= ROT_CONFIG_IRQ;
+	else
+		val &= ~ROT_CONFIG_IRQ;
+
+	rot_write(val, ROT_CONFIG);
+}
+
+static u32 rotator_reg_get_format(struct rot_context *rot)
+{
+	u32 val = rot_read(ROT_CONTROL);
+
+	val &= ROT_CONTROL_FMT_MASK;
+
+	return val;
+}
+
+static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
+{
+	u32 val = rot_read(ROT_STATUS);
+
+	val = ROT_STATUS_IRQ(val);
+
+	if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
+		return ROT_IRQ_STATUS_COMPLETE;
+	else
+		return ROT_IRQ_STATUS_ILLEGAL;
+}
+
+static irqreturn_t rotator_irq_handler(int irq, void *arg)
+{
+	struct rot_context *rot = arg;
+	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
+	struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
+	struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
+	enum rot_irq_status irq_status;
+	u32 val;
+
+	/* Get execution result */
+	irq_status = rotator_reg_get_irq_status(rot);
+
+	/* clear status */
+	val = rot_read(ROT_STATUS);
+	val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
+	rot_write(val, ROT_STATUS);
+
+	if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
+		event_work->ippdrv = ippdrv;
+		event_work->buf_id[EXYNOS_DRM_OPS_DST] =
+			rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
+		queue_work(ippdrv->event_workq,
+			(struct work_struct *)event_work);
+	} else
+		DRM_ERROR("the SFR is set illegally\n");
+
+	return IRQ_HANDLED;
+}
+
+static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize,
+		u32 *vsize)
+{
+	struct rot_limit_table *limit_tbl = rot->limit_tbl;
+	struct rot_limit *limit;
+	u32 mask, val;
+
+	/* Get size limit */
+	if (fmt == ROT_CONTROL_FMT_RGB888)
+		limit = &limit_tbl->rgb888;
+	else
+		limit = &limit_tbl->ycbcr420_2p;
+
+	/* Get mask for rounding to nearest aligned val */
+	mask = ~((1 << limit->align) - 1);
+
+	/* Set aligned width */
+	val = ROT_ALIGN(*hsize, limit->align, mask);
+	if (val < limit->min_w)
+		*hsize = ROT_MIN(limit->min_w, mask);
+	else if (val > limit->max_w)
+		*hsize = ROT_MAX(limit->max_w, mask);
+	else
+		*hsize = val;
+
+	/* Set aligned height */
+	val = ROT_ALIGN(*vsize, limit->align, mask);
+	if (val < limit->min_h)
+		*vsize = ROT_MIN(limit->min_h, mask);
+	else if (val > limit->max_h)
+		*vsize = ROT_MAX(limit->max_h, mask);
+	else
+		*vsize = val;
+}
+
+static int rotator_src_set_fmt(struct device *dev, u32 fmt)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+	u32 val;
+
+	val = rot_read(ROT_CONTROL);
+	val &= ~ROT_CONTROL_FMT_MASK;
+
+	switch (fmt) {
+	case DRM_FORMAT_NV12:
+		val |= ROT_CONTROL_FMT_YCBCR420_2P;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		val |= ROT_CONTROL_FMT_RGB888;
+		break;
+	default:
+		DRM_ERROR("invalid image format\n");
+		return -EINVAL;
+	}
+
+	rot_write(val, ROT_CONTROL);
+
+	return 0;
+}
+
+static int rotator_src_set_size(struct device *dev, int swap,
+		struct drm_exynos_pos *pos,
+		struct drm_exynos_sz *sz)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+	u32 fmt, hsize, vsize;
+	u32 val;
+
+	/* Get format */
+	fmt = rotator_reg_get_format(rot);
+
+	/* Align buffer size */
+	hsize = sz->hsize;
+	vsize = sz->vsize;
+	rotator_align_size(rot, fmt, &hsize, &vsize);
+
+	/* Set buffer size configuration */
+	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
+	rot_write(val, ROT_SRC_BUF_SIZE);
+
+	/* Set crop image position configuration */
+	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
+	rot_write(val, ROT_SRC_CROP_POS);
+	val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
+	rot_write(val, ROT_SRC_CROP_SIZE);
+
+	return 0;
+}
+
+static int rotator_src_set_addr(struct device *dev,
+		struct drm_exynos_ipp_buf_info *buf_info,
+		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
+	u32 val, fmt, hsize, vsize;
+	int i;
+
+	/* Set current buf_id */
+	rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
+
+	switch (buf_type) {
+	case IPP_BUF_ENQUEUE:
+		/* Set address configuration */
+		for_each_ipp_planar(i)
+			addr[i] = buf_info->base[i];
+
+		/* Get format */
+		fmt = rotator_reg_get_format(rot);
+
+		/* Re-set cb planar for NV12 format */
+		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
+					(addr[EXYNOS_DRM_PLANAR_CB] == 0x00)) {
+
+			val = rot_read(ROT_SRC_BUF_SIZE);
+			hsize = ROT_GET_BUF_SIZE_W(val);
+			vsize = ROT_GET_BUF_SIZE_H(val);
+
+			/* Set cb planar */
+			addr[EXYNOS_DRM_PLANAR_CB] =
+				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
+		}
+
+		for_each_ipp_planar(i)
+			rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
+		break;
+	case IPP_BUF_DEQUEUE:
+		for_each_ipp_planar(i)
+			rot_write(0x0, ROT_SRC_BUF_ADDR(i));
+		break;
+	default:
+		/* Nothing to do */
+		break;
+	}
+
+	return 0;
+}
+
+static int rotator_dst_set_transf(struct device *dev,
+		enum drm_exynos_degree degree,
+		enum drm_exynos_flip flip)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+	u32 val;
+
+	/* Set transform configuration */
+	val = rot_read(ROT_CONTROL);
+	val &= ~ROT_CONTROL_FLIP_MASK;
+
+	switch (flip) {
+	case EXYNOS_DRM_FLIP_VERTICAL:
+		val |= ROT_CONTROL_FLIP_VERTICAL;
+		break;
+	case EXYNOS_DRM_FLIP_HORIZONTAL:
+		val |= ROT_CONTROL_FLIP_HORIZONTAL;
+		break;
+	default:
+		/* Flip None */
+		break;
+	}
+
+	val &= ~ROT_CONTROL_ROT_MASK;
+
+	switch (degree) {
+	case EXYNOS_DRM_DEGREE_90:
+		val |= ROT_CONTROL_ROT_90;
+		break;
+	case EXYNOS_DRM_DEGREE_180:
+		val |= ROT_CONTROL_ROT_180;
+		break;
+	case EXYNOS_DRM_DEGREE_270:
+		val |= ROT_CONTROL_ROT_270;
+		break;
+	default:
+		/* Rotation 0 Degree */
+		break;
+	}
+
+	rot_write(val, ROT_CONTROL);
+
+	/* Check degree for setting buffer size swap */
+	if ((degree == EXYNOS_DRM_DEGREE_90) ||
+		(degree == EXYNOS_DRM_DEGREE_270))
+		return 1;
+	else
+		return 0;
+}
+
+static int rotator_dst_set_size(struct device *dev, int swap,
+		struct drm_exynos_pos *pos,
+		struct drm_exynos_sz *sz)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+	u32 val, fmt, hsize, vsize;
+
+	/* Get format */
+	fmt = rotator_reg_get_format(rot);
+
+	/* Align buffer size */
+	hsize = sz->hsize;
+	vsize = sz->vsize;
+	rotator_align_size(rot, fmt, &hsize, &vsize);
+
+	/* Set buffer size configuration */
+	val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
+	rot_write(val, ROT_DST_BUF_SIZE);
+
+	/* Set crop image position configuration */
+	val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
+	rot_write(val, ROT_DST_CROP_POS);
+
+	return 0;
+}
+
+static int rotator_dst_set_addr(struct device *dev,
+		struct drm_exynos_ipp_buf_info *buf_info,
+		u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+	dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
+	u32 val, fmt, hsize, vsize;
+	int i;
+
+	/* Set current buf_id */
+	rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
+
+	switch (buf_type) {
+	case IPP_BUF_ENQUEUE:
+		/* Set address configuration */
+		for_each_ipp_planar(i)
+			addr[i] = buf_info->base[i];
+
+		/* Get format */
+		fmt = rotator_reg_get_format(rot);
+
+		/* Re-set cb planar for NV12 format */
+		if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
+					(addr[EXYNOS_DRM_PLANAR_CB] == 0x00)) {
+			/* Get buf size */
+			val = rot_read(ROT_DST_BUF_SIZE);
+
+			hsize = ROT_GET_BUF_SIZE_W(val);
+			vsize = ROT_GET_BUF_SIZE_H(val);
+
+			/* Set cb planar */
+			addr[EXYNOS_DRM_PLANAR_CB] =
+				addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
+		}
+
+		for_each_ipp_planar(i)
+			rot_write(addr[i], ROT_DST_BUF_ADDR(i));
+		break;
+	case IPP_BUF_DEQUEUE:
+		for_each_ipp_planar(i)
+			rot_write(0x0, ROT_DST_BUF_ADDR(i));
+		break;
+	default:
+		/* Nothing to do */
+		break;
+	}
+
+	return 0;
+}
+
+static struct exynos_drm_ipp_ops rot_src_ops = {
+	.set_fmt	=	rotator_src_set_fmt,
+	.set_size	=	rotator_src_set_size,
+	.set_addr	=	rotator_src_set_addr,
+};
+
+static struct exynos_drm_ipp_ops rot_dst_ops = {
+	.set_transf	=	rotator_dst_set_transf,
+	.set_size	=	rotator_dst_set_size,
+	.set_addr	=	rotator_dst_set_addr,
+};
+
+static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
+{
+	struct drm_exynos_ipp_prop_list *prop_list;
+
+	DRM_DEBUG_KMS("%s\n", __func__);
+
+	prop_list = kzalloc(sizeof(*prop_list), GFP_KERNEL);
+	if (!prop_list) {
+		DRM_ERROR("failed to alloc property list.\n");
+		return -ENOMEM;
+	}
+
+	/*ToDo fix support function list*/
+
+	prop_list->version = 1;
+	prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
+				(1 << EXYNOS_DRM_FLIP_HORIZONTAL);
+	prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
+				(1 << EXYNOS_DRM_DEGREE_90) |
+				(1 << EXYNOS_DRM_DEGREE_180) |
+				(1 << EXYNOS_DRM_DEGREE_270);
+	prop_list->csc = 0;
+	prop_list->crop = 0;
+	prop_list->scale = 0;
+
+	ippdrv->prop_list = prop_list;
+
+	return 0;
+}
+
+static int rotator_ippdrv_check_property(struct device *dev,
+		struct drm_exynos_ipp_property *property)
+{
+	struct drm_exynos_ipp_config *src_config =
+					&property->config[EXYNOS_DRM_OPS_SRC];
+	struct drm_exynos_ipp_config *dst_config =
+					&property->config[EXYNOS_DRM_OPS_DST];
+	struct drm_exynos_pos *src_pos = &src_config->pos;
+	struct drm_exynos_pos *dst_pos = &dst_config->pos;
+	struct drm_exynos_sz *src_sz = &src_config->sz;
+	struct drm_exynos_sz *dst_sz = &dst_config->sz;
+	bool swap = false;
+
+	/* Check format configuration */
+	if (src_config->fmt != dst_config->fmt) {
+		DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (src_config->fmt) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_NV12:
+		/* No problem */
+		break;
+	default:
+		DRM_DEBUG_KMS("%s:not support format\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Check transform configuration */
+	if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
+		DRM_DEBUG_KMS("%s:not support source-side rotation\n",
+								__func__);
+		return -EINVAL;
+	}
+
+	switch (dst_config->degree) {
+	case EXYNOS_DRM_DEGREE_90:
+	case EXYNOS_DRM_DEGREE_270:
+		swap = true;
+	case EXYNOS_DRM_DEGREE_0:
+	case EXYNOS_DRM_DEGREE_180:
+		/* No problem */
+		break;
+	default:
+		DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
+		return -EINVAL;
+	}
+
+	if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
+		DRM_DEBUG_KMS("%s:not support source-side flip\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (dst_config->flip) {
+	case EXYNOS_DRM_FLIP_NONE:
+	case EXYNOS_DRM_FLIP_VERTICAL:
+	case EXYNOS_DRM_FLIP_HORIZONTAL:
+		/* No problem */
+		break;
+	default:
+		DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Check size configuration */
+	if ((src_pos->x + src_pos->w > src_sz->hsize) ||
+		(src_pos->y + src_pos->h > src_sz->vsize)) {
+		DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
+		return -EINVAL;
+	}
+
+	if (swap) {
+		if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
+			(dst_pos->y + dst_pos->w > dst_sz->hsize)) {
+			DRM_DEBUG_KMS("%s:out of destination buffer bound\n",
+								__func__);
+			return -EINVAL;
+		}
+
+		if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) {
+			DRM_DEBUG_KMS("%s:not support scale feature\n",
+								__func__);
+			return -EINVAL;
+		}
+	} else {
+		if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
+			(dst_pos->y + dst_pos->h > dst_sz->vsize)) {
+			DRM_DEBUG_KMS("%s:out of destination buffer bound\n",
+								__func__);
+			return -EINVAL;
+		}
+
+		if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) {
+			DRM_DEBUG_KMS("%s:not support scale feature\n",
+								__func__);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+	u32 val;
+
+	if (rot->suspended) {
+		DRM_ERROR("suspended state\n");
+		return -EPERM;
+	}
+
+	if (cmd != IPP_CMD_M2M) {
+		DRM_ERROR("not support cmd: %d\n", cmd);
+		return -EINVAL;
+	}
+
+	/* Set interrupt enable */
+	rotator_reg_set_irq(rot, true);
+
+	val = rot_read(ROT_CONTROL);
+	val |= ROT_CONTROL_START;
+
+	rot_write(val, ROT_CONTROL);
+
+	return 0;
+}
+
+static int __devinit rotator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rot_context *rot;
+	struct resource *res;
+	struct exynos_drm_ippdrv *ippdrv;
+	int ret;
+
+	rot = kzalloc(sizeof(*rot), GFP_KERNEL);
+	if (!rot) {
+		dev_err(dev, "failed to allocate rot\n");
+		return -ENOMEM;
+	}
+
+	rot->limit_tbl = (struct rot_limit_table *)
+				platform_get_device_id(pdev)->driver_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "failed to find registers\n");
+		ret = -ENOENT;
+		goto err_get_resource;
+	}
+
+	rot->regs_res = request_mem_region(res->start, resource_size(res),
+								dev_name(dev));
+	if (!rot->regs_res) {
+		dev_err(dev, "failed to claim register region\n");
+		ret = -ENOENT;
+		goto err_get_resource;
+	}
+
+	rot->regs = ioremap(res->start, resource_size(res));
+	if (!rot->regs) {
+		dev_err(dev, "failed to map register\n");
+		ret = -ENXIO;
+		goto err_ioremap;
+	}
+
+	rot->irq = platform_get_irq(pdev, 0);
+	if (rot->irq < 0) {
+		dev_err(dev, "failed to get irq\n");
+		ret = rot->irq;
+		goto err_get_irq;
+	}
+
+	ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler,
+					IRQF_ONESHOT, "drm_rotator", rot);
+	if (ret < 0) {
+		dev_err(dev, "failed to request irq\n");
+		goto err_get_irq;
+	}
+
+	rot->clock = clk_get(dev, "rotator");
+	if (IS_ERR_OR_NULL(rot->clock)) {
+		dev_err(dev, "failed to get clock\n");
+		ret = PTR_ERR(rot->clock);
+		goto err_clk_get;
+	}
+
+	pm_runtime_enable(dev);
+
+	ippdrv = &rot->ippdrv;
+	ippdrv->dev = dev;
+	ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
+	ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
+	ippdrv->check_property = rotator_ippdrv_check_property;
+	ippdrv->start = rotator_ippdrv_start;
+	ret = rotator_init_prop_list(ippdrv);
+	if (ret < 0) {
+		dev_err(dev, "failed to init property list.\n");
+		goto err_ippdrv_register;
+	}
+
+	DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
+
+	platform_set_drvdata(pdev, rot);
+
+	ret = exynos_drm_ippdrv_register(ippdrv);
+	if (ret < 0) {
+		dev_err(dev, "failed to register drm rotator device\n");
+		kfree(ippdrv->prop_list);
+		goto err_ippdrv_register;
+	}
+
+	dev_info(dev, "The exynos rotator is probed successfully\n");
+
+	return 0;
+
+err_ippdrv_register:
+	pm_runtime_disable(dev);
+	clk_put(rot->clock);
+err_clk_get:
+	free_irq(rot->irq, rot);
+err_get_irq:
+	iounmap(rot->regs);
+err_ioremap:
+	release_resource(rot->regs_res);
+	kfree(rot->regs_res);
+err_get_resource:
+	kfree(rot);
+	return ret;
+}
+
+static int __devexit rotator_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rot_context *rot = dev_get_drvdata(dev);
+	struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
+
+	kfree(ippdrv->prop_list);
+	exynos_drm_ippdrv_unregister(ippdrv);
+
+	pm_runtime_disable(dev);
+	clk_put(rot->clock);
+
+	free_irq(rot->irq, rot);
+
+	iounmap(rot->regs);
+
+	release_resource(rot->regs_res);
+	kfree(rot->regs_res);
+
+	kfree(rot);
+
+	return 0;
+}
+
+struct rot_limit_table rot_limit_tbl = {
+	.ycbcr420_2p = {
+		.min_w = 32,
+		.min_h = 32,
+		.max_w = SZ_32K,
+		.max_h = SZ_32K,
+		.align = 3,
+	},
+	.rgb888 = {
+		.min_w = 8,
+		.min_h = 8,
+		.max_w = SZ_8K,
+		.max_h = SZ_8K,
+		.align = 2,
+	},
+};
+
+struct platform_device_id rotator_driver_ids[] = {
+	{
+		.name		= "exynos-rot",
+		.driver_data	= (unsigned long)&rot_limit_tbl,
+	},
+	{},
+};
+
+static int rotator_clk_crtl(struct rot_context *rot, bool enable)
+{
+	DRM_DEBUG_KMS("%s\n", __func__);
+
+	if (enable) {
+		clk_enable(rot->clock);
+		rot->suspended = false;
+	} else {
+		clk_disable(rot->clock);
+		rot->suspended = true;
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int rotator_suspend(struct device *dev)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+
+	/* ToDo */
+
+	DRM_DEBUG_KMS("%s\n", __func__);
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	return rotator_clk_crtl(rot, false);
+}
+
+static int rotator_resume(struct device *dev)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+
+	/* ToDo */
+
+	DRM_DEBUG_KMS("%s\n", __func__);
+
+	if (!pm_runtime_suspended(dev))
+		return rotator_clk_crtl(rot, true);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int rotator_runtime_suspend(struct device *dev)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+
+	/* ToDo */
+
+	DRM_DEBUG_KMS("%s\n", __func__);
+
+	return  rotator_clk_crtl(rot, false);
+}
+
+static int rotator_runtime_resume(struct device *dev)
+{
+	struct rot_context *rot = dev_get_drvdata(dev);
+
+	/* ToDo */
+
+	DRM_DEBUG_KMS("%s\n", __func__);
+
+	return  rotator_clk_crtl(rot, true);
+}
+#endif
+
+static const struct dev_pm_ops rotator_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
+	SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
+									NULL)
+};
+
+struct platform_driver rotator_driver = {
+	.probe		= rotator_probe,
+	.remove		= __devexit_p(rotator_remove),
+	.id_table	= rotator_driver_ids,
+	.driver		= {
+		.name	= "exynos-rot",
+		.owner	= THIS_MODULE,
+		.pm	= &rotator_pm_ops,
+	},
+};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
new file mode 100644
index 0000000..fe929c9
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
@@ -0,0 +1,13 @@ 
+/*
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Authors: YoungJun Cho <yj44.cho@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 Foundationr
+ */
+
+#ifndef	_EXYNOS_DRM_ROTATOR_H_
+#define	_EXYNOS_DRM_ROTATOR_H_
+
+#endif
diff --git a/drivers/gpu/drm/exynos/regs-rotator.h b/drivers/gpu/drm/exynos/regs-rotator.h
new file mode 100644
index 0000000..a09ac6e
--- /dev/null
+++ b/drivers/gpu/drm/exynos/regs-rotator.h
@@ -0,0 +1,73 @@ 
+/* drivers/gpu/drm/exynos/regs-rotator.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * Register definition file for Samsung Rotator Interface (Rotator) driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef EXYNOS_REGS_ROTATOR_H
+#define EXYNOS_REGS_ROTATOR_H
+
+/* Configuration */
+#define ROT_CONFIG			0x00
+#define ROT_CONFIG_IRQ			(3 << 8)
+
+/* Image Control */
+#define ROT_CONTROL			0x10
+#define ROT_CONTROL_PATTERN_WRITE	(1 << 16)
+#define ROT_CONTROL_FMT_YCBCR420_2P	(1 << 8)
+#define ROT_CONTROL_FMT_RGB888		(6 << 8)
+#define ROT_CONTROL_FMT_MASK		(7 << 8)
+#define ROT_CONTROL_FLIP_VERTICAL	(2 << 6)
+#define ROT_CONTROL_FLIP_HORIZONTAL	(3 << 6)
+#define ROT_CONTROL_FLIP_MASK		(3 << 6)
+#define ROT_CONTROL_ROT_90		(1 << 4)
+#define ROT_CONTROL_ROT_180		(2 << 4)
+#define ROT_CONTROL_ROT_270		(3 << 4)
+#define ROT_CONTROL_ROT_MASK		(3 << 4)
+#define ROT_CONTROL_START		(1 << 0)
+
+/* Status */
+#define ROT_STATUS			0x20
+#define ROT_STATUS_IRQ_PENDING(x)	(1 << (x))
+#define ROT_STATUS_IRQ(x)		(((x) >> 8) & 0x3)
+#define ROT_STATUS_IRQ_VAL_COMPLETE	1
+#define ROT_STATUS_IRQ_VAL_ILLEGAL	2
+
+/* Buffer Address */
+#define ROT_SRC_BUF_ADDR(n)		(0x30 + ((n) << 2))
+#define ROT_DST_BUF_ADDR(n)		(0x50 + ((n) << 2))
+
+/* Buffer Size */
+#define ROT_SRC_BUF_SIZE		0x3c
+#define ROT_DST_BUF_SIZE		0x5c
+#define ROT_SET_BUF_SIZE_H(x)		((x) << 16)
+#define ROT_SET_BUF_SIZE_W(x)		((x) << 0)
+#define ROT_GET_BUF_SIZE_H(x)		((x) >> 16)
+#define ROT_GET_BUF_SIZE_W(x)		((x) & 0xffff)
+
+/* Crop Position */
+#define ROT_SRC_CROP_POS		0x40
+#define ROT_DST_CROP_POS		0x60
+#define ROT_CROP_POS_Y(x)		((x) << 16)
+#define ROT_CROP_POS_X(x)		((x) << 0)
+
+/* Source Crop Size */
+#define ROT_SRC_CROP_SIZE		0x44
+#define ROT_SRC_CROP_SIZE_H(x)		((x) << 16)
+#define ROT_SRC_CROP_SIZE_W(x)		((x) << 0)
+
+/* Round to nearest aligned value */
+#define ROT_ALIGN(x, align, mask)	(((x) + (1 << ((align) - 1))) & (mask))
+/* Minimum limit value */
+#define ROT_MIN(min, mask)		(((min) + ~(mask)) & (mask))
+/* Maximum limit value */
+#define ROT_MAX(max, mask)		((max) & (mask))
+
+#endif /* EXYNOS_REGS_ROTATOR_H */
+