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 at samsung.com] >> Sent: Wednesday, December 12, 2012 6:26 PM >> To: Inki Dae >> Cc: dri-devel at lists.freedesktop.org; jy0.jeon at samsung.com; >> yj44.cho at samsung.com; jmock.shin at samsung.com; jaejoon.seo at >> samsung.com; >> kyungmin.park at samsung.com; sw0312.kim at 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 at samsung.com] >>>> Sent: Wednesday, December 12, 2012 4:35 PM >>>> To: dri-devel at lists.freedesktop.org; inki.dae at samsung.com >>>> Cc: jy0.jeon at samsung.com; yj44.cho at samsung.com; jmock.shin at >>>> samsung.com; >>>> jaejoon.seo at samsung.com; kyungmin.park at samsung.com; >>>> chulspro.kim at 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 at samsung.com> >>>> Signed-off-by: Youngjun Cho <yj44.cho at 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 at samsung.com> >>>> + * Eunchul Kim <chulspro.kim at samsung.com> >>>> + * >>>> + * This program is free software; you can redistribute it and/or >> modify >>>> + * it under the terms of the GNU General Public License version 2 as >>>> + * published by the Free Software 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 at samsung.com> >>>> + * >>>> + * This program is free software; you can redistribute it and/or >> modify >>>> + * it under the terms of the GNU General Public License version 2 as >>>> + * published by the Free Software 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 >>> >>> > >