From patchwork Mon Oct 29 13:10:45 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eunchul Kim X-Patchwork-Id: 1663431 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by patchwork1.kernel.org (Postfix) with ESMTP id A10A43FDDA for ; Mon, 29 Oct 2012 13:17:14 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 7A9969E93C for ; Mon, 29 Oct 2012 06:17:14 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) by gabe.freedesktop.org (Postfix) with ESMTP id 325D79E93C for ; Mon, 29 Oct 2012 06:13:07 -0700 (PDT) Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout4.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MCN0092AOPPCW41@mailout4.samsung.com> for dri-devel@lists.freedesktop.org; Mon, 29 Oct 2012 22:13:05 +0900 (KST) Received: from epcpsbgm1.samsung.com ( [203.254.230.44]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id F7.D7.01231.1E08E805; Mon, 29 Oct 2012 22:13:05 +0900 (KST) X-AuditID: cbfee61a-b7fa66d0000004cf-31-508e80e15e26 Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 57.D7.01231.1E08E805; Mon, 29 Oct 2012 22:13:05 +0900 (KST) Received: from localhost.localdomain ([10.90.54.32]) by mmp1.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0MCN000YPOOTY020@mmp1.samsung.com> for dri-devel@lists.freedesktop.org; Mon, 29 Oct 2012 22:13:05 +0900 (KST) From: Eunchul Kim To: dri-devel@lists.freedesktop.org, inki.dae@samsung.com Subject: [RFC 3/4] drm/exynos: add rotator ipp driver Date: Mon, 29 Oct 2012 22:10:45 +0900 Message-id: <1351516246-25853-4-git-send-email-chulspro.kim@samsung.com> X-Mailer: git-send-email 1.7.0.4 In-reply-to: <1351516246-25853-1-git-send-email-chulspro.kim@samsung.com> References: <1351516246-25853-1-git-send-email-chulspro.kim@samsung.com> DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrPLMWRmVeSWpSXmKPExsVy+t8zHd2HDX0BBi1HJC2ufH3P5sDocb/7 OFMAYxSXTUpqTmZZapG+XQJXxtRrq5gLuq4wVpx88ZWpgfHwWsYuRk4OCQETid/TGpggbDGJ C/fWs3UxcnEICSxjlDg7+xM7TNGK0+eZQWwhgUWMEvuXeUIUrWCSWNO3igUkwSagI3HkVjNY kYiApcSDrb+ZQYqYBaYxSuyZfQ4sISxgKrF5UwvYahYBVYnHe2aAreYV8JBY1toGtU1BonXZ ITCbU8BT4uf9DhaIzR4St17PYIXoFZD4NvkQUJwDqF5WYtMBsF0SAtfZJF6tmswCMUdS4uCK GywTGIUXMDKsYhRNLUguKE5KzzXUK07MLS7NS9dLzs/dxAgJRakdjCsbLA4xCnAwKvHwNr7p DRBiTSwrrsw9xCjBwawkwrsuvS9AiDclsbIqtSg/vqg0J7X4EKMP0CUTmaVEk/OBcZJXEm9o bGBsaGhpaGZqaWqAQ1hJnLfZIyVASCA9sSQ1OzW1ILUIZhwTB6dUA2O9gv7St0Uvpu28VrG8 y8leVccxYDF/PM/lyBdGlbKLHD5L/uF00Ts5Q9Dw2P5/6lmvV71cdvRL0oHzJtd42TiVV13T cLuTVBaqcDkuhFWvzrQ9RXjOOo7Db2UcTpYUPn28VUDhuORcm+jZSd8luHbt1S6bUFO08mf2 t+6ajbkef0NU5ZQNxZVYijMSDbWYi4oTAcU/H35yAgAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupjkeLIzCtJLcpLzFFi42I5/e+xgO7Dhr4Ag+7dQhZXvr5nc2D0uN99 nCmAMaqB0SYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ0sJcSSEvMTfVVsnFJ0DXLTMH aKySQlliTilQKCCxuFhJ3w7ThNAQN10LmMYIXd+QILgeIwM0kLCOMWPqtVXMBV1XGCtOvvjK 1MB4eC1jFyMnh4SAicSK0+eZIWwxiQv31rOB2EICixgl9i/z7GLkArJXMEms6VvFApJgE9CR OHKrGaxBRMBS4sHW38wgRcwC0xgl9sw+B5YQFjCV2LypBWwDi4CqxOM9M5hAbF4BD4llrW3s ENsUJFqXHQKzOQU8JX7e72CB2Owhcev1DNYJjLwLGBlWMYqmFiQXFCel5xrqFSfmFpfmpesl 5+duYgQH+jOpHYwrGywOMQpwMCrx8Da+6Q0QYk0sK67MPcQowcGsJMK7Lr0vQIg3JbGyKrUo P76oNCe1+BCjD9BVE5mlRJPzgVGYVxJvaGxiZmRpZGZsYm5sjENYSZy32SMlQEggPbEkNTs1 tSC1CGYcEwenVANj2R0G3T2vvyza/m7tOzGO6dkRE+JW+pm803zt78ca9ZTR97xF6N6T39LW lAvlynR4ZnE3z2w22RbUbPfqcNSnes0VeQGKq86W75gbPrco3mzekbcpG4U+81kY5P3qy5Dd 9Sr2mXGvaNrtH/nPmJ7+Xy36aSZ3nWld5iotu+Jv2yWbLxc/jp6pxFKckWioxVxUnAgAOoxc wqECAAA= X-CFilter-Loop: Reflected Cc: jy0.jeon@samsung.com, jaejoon.seo@samsung.com, yj44.cho@samsung.com, kyungmin.park@samsung.com, jmock.shin@samsung.com X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org 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 Signed-off-by: Youngjun Cho --- drivers/gpu/drm/exynos/Kconfig | 6 + 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 | 801 +++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_rotator.h | 13 + drivers/gpu/drm/exynos/regs-rotator.h | 73 +++ 7 files changed, 910 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 c1ecf6a..0b35425 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -51,3 +51,9 @@ 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 7f18e5f..f1bb744 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -17,5 +17,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 3ceaff4..7d6ba9a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -361,6 +361,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) @@ -389,6 +395,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: @@ -434,6 +445,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 b355bd3..1817d66 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -342,5 +342,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..347de99 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * YoungJun Cho + * Eunchul Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "regs-rotator.h" +#include "exynos_drm.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_ipp.h" + +enum rot_irq_status { + ROT_IRQ_STATUS_COMPLETE = 8, + ROT_IRQ_STATUS_ILLEGAL = 9, +}; + +struct rot_limit { + u32 min_w; + u32 min_h; + u32 max_w; + u32 max_h; + u32 align; +}; + +struct rot_limit_table { + struct rot_limit ycbcr420_2p; + struct rot_limit rgb888; +}; + +struct rot_context { + struct rot_limit_table *limit_tbl; + struct clk *clock; + struct resource *regs_res; + void __iomem *regs; + int irq; + struct exynos_drm_ippdrv ippdrv; + int cur_buf_id[EXYNOS_DRM_OPS_MAX]; + bool suspended; +}; + +static void rotator_reg_set_irq(struct rot_context *rot, bool enable) +{ + u32 val = readl(rot->regs + ROT_CONFIG); + + if (enable == true) + val |= ROT_CONFIG_IRQ; + else + val &= ~ROT_CONFIG_IRQ; + + writel(val, rot->regs + ROT_CONFIG); +} + +static u32 rotator_reg_get_format(struct rot_context *rot) +{ + u32 val = readl(rot->regs + 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 = readl(rot->regs + 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 void rotator_reg_set_irq_status_clear(struct rot_context *rot, + enum rot_irq_status status) +{ + u32 val = readl(rot->regs + ROT_STATUS); + + val |= ROT_STATUS_IRQ_PENDING((u32)status); + + writel(val, rot->regs + ROT_STATUS); +} + +static void rotator_reg_get_dump(struct rot_context *rot) +{ + u32 val, i; + + for (i = 0; i <= ROT_DST_CROP_POS; i += 0x4) { + val = readl(rot->regs + i); + DRM_DEBUG_KMS("[%s] [0x%x] : 0x%x\n", __func__, i, val); + } +} + +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; + + /* Get execution result */ + irq_status = rotator_reg_get_irq_status(rot); + rotator_reg_set_irq_status_clear(rot, irq_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"); + rotator_reg_get_dump(rot); + } + + 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 = readl(rot->regs + 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; + } + + writel(val, rot->regs + 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); + writel(val, rot->regs + ROT_SRC_BUF_SIZE); + + /* Set crop image position configuration */ + val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); + writel(val, rot->regs + ROT_SRC_CROP_POS); + val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w); + writel(val, rot->regs + 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 = readl(rot->regs + 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) + writel(addr[i], rot->regs + ROT_SRC_BUF_ADDR(i)); + break; + case IPP_BUF_DEQUEUE: + for_each_ipp_planar(i) + writel(0x0, rot->regs + 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 = readl(rot->regs + 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; + } + + writel(val, rot->regs + 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); + writel(val, rot->regs + ROT_DST_BUF_SIZE); + + /* Set crop image position configuration */ + val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); + writel(val, rot->regs + 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 = readl(rot->regs + 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) + writel(addr[i], rot->regs + ROT_DST_BUF_ADDR(i)); + break; + case IPP_BUF_DEQUEUE: + for_each_ipp_planar(i) + writel(0x0, rot->regs + 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 drm_exynos_ipp_prop_list **prop_list) +{ + DRM_DEBUG_KMS("%s\n", __func__); + + if (!prop_list) { + DRM_ERROR("empty prop_list.\n"); + return -EINVAL; + } + + *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; + + 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 = readl(rot->regs + ROT_CONTROL); + val |= ROT_CONTROL_START; + + writel(val, rot->regs + 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, "faild 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, "faild 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->prop_list); + 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_power_on(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); + + DRM_DEBUG_KMS("%s:\n", __func__); + + if (pm_runtime_suspended(dev)) + return 0; + /* ToDo */ + return rotator_power_on(rot, false); +} + +static int rotator_resume(struct device *dev) +{ + struct rot_context *rot = dev_get_drvdata(dev); + + DRM_DEBUG_KMS("%s:\n", __func__); + + if (!pm_runtime_suspended(dev)) + return rotator_power_on(rot, true); + /* ToDo */ + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int rotator_runtime_suspend(struct device *dev) +{ + struct rot_context *rot = dev_get_drvdata(dev); + + DRM_DEBUG_KMS("%s:\n", __func__); + + /* ToDo */ + return rotator_power_on(rot, false); +} + +static int rotator_runtime_resume(struct device *dev) +{ + struct rot_context *rot = dev_get_drvdata(dev); + + DRM_DEBUG_KMS("%s:\n", __func__); + /* ToDo */ + return rotator_power_on(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 + * + * 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 */ +