[RFC] drm/exynos: add rotator driver
Inki Dae
inki.dae at samsung.com
Fri Apr 27 02:43:05 PDT 2012
From: YoungJun Cho <yj44.cho at samsung.com>
The rotator supports rotating and flipping image data.
This rotator driver is exynos drm specific and supports only Exynos series from
Exynos4X12 and this patch is just for RFC.
The rotator is performed by 1 task simply.
1. Configures control, buffer, cropped image information then start the relevant
operation. And it is synchronous operation, so there is no special event to
notify its completion.
The rotator driver supports following features.
1. Rotation : 90, 180, 270 degree
2. Flipping : vertical, horizontal flip
3. Image format : RGB888, NV12, NV12M format
The rotator driver has following limitations.
1. Source and destination image format have to be same
2. No scale feature
We add one ioctl for Exynos rotator.
- ioctls
DRM_EXYNOS_ROTATOR_EXEC : set configuration and execute operation to driver
Signed-off-by: YoungJun Cho <yj44.cho at samsung.com>
Signed-off-by: Inki Dae <inki.dae at samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
---
drivers/gpu/drm/exynos/Kconfig | 6 +
drivers/gpu/drm/exynos/Makefile | 1 +
drivers/gpu/drm/exynos/exynos_drm_drv.c | 18 +
drivers/gpu/drm/exynos/exynos_drm_drv.h | 13 +
drivers/gpu/drm/exynos/exynos_drm_rotator.c | 853 +++++++++++++++++++++++++++
drivers/gpu/drm/exynos/exynos_drm_rotator.h | 25 +
include/drm/exynos_drm.h | 99 ++++
7 files changed, 1015 insertions(+)
create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.c
create mode 100644 drivers/gpu/drm/exynos/exynos_drm_rotator.h
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 3343ac4..35166a8 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -27,3 +27,9 @@ config DRM_EXYNOS_VIDI
depends on DRM_EXYNOS
help
Choose this option if you want to use Exynos VIDI for DRM.
+
+config DRM_EXYNOS_ROTATOR
+ bool "Samsung DRM Rotator"
+ depends on DRM_EXYNOS
+ help
+ Choose this option if you want to use Samsung Rotator for DRM.
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 9e0bff8..71968bf 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -13,5 +13,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
exynos_ddc.o exynos_hdmiphy.o \
exynos_drm_hdmi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.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 a6819b5..9e4d5e1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -37,6 +37,7 @@
#include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
+#include "exynos_drm_rotator.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_vidi.h"
@@ -211,6 +212,8 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(EXYNOS_ROTATOR_EXEC,
+ exynos_drm_rotator_exec_ioctl, DRM_UNLOCKED | DRM_AUTH),
};
static const struct file_operations exynos_drm_driver_fops = {
@@ -307,6 +310,12 @@ static int __init exynos_drm_init(void)
goto out_vidi;
#endif
+#ifdef CONFIG_DRM_EXYNOS_ROTATOR
+ ret = platform_driver_register(&rotator_driver);
+ if (ret < 0)
+ goto out_rotator;
+#endif
+
ret = platform_driver_register(&exynos_drm_platform_driver);
if (ret < 0)
goto out;
@@ -314,6 +323,11 @@ static int __init exynos_drm_init(void)
return 0;
out:
+#ifdef CONFIG_DRM_EXYNOS_ROTATOR
+ platform_driver_unregister(&rotator_driver);
+out_rotator:
+#endif
+
#ifdef CONFIG_DRM_EXYNOS_VIDI
out_vidi:
platform_driver_unregister(&vidi_driver);
@@ -341,6 +355,10 @@ static void __exit exynos_drm_exit(void)
platform_driver_unregister(&exynos_drm_platform_driver);
+#ifdef CONFIG_DRM_EXYNOS_ROTATOR
+ platform_driver_unregister(&rotator_driver);
+#endif
+
#ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
platform_driver_unregister(&mixer_driver);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 1d81417..9c60b30 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -206,6 +206,19 @@ struct exynos_drm_manager {
};
/*
+ * Exynos drm rotator private structure
+ *
+ * @dev: device object to device driver for using driver data.
+ */
+struct exynos_drm_rot_private {
+ struct device *dev;
+};
+
+struct drm_exynos_file_private {
+ struct exynos_drm_rot_private *rot_priv;
+};
+
+/*
* Exynos drm private structure.
*/
struct exynos_drm_private {
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..186dd07
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -0,0 +1,853 @@
+/*
+ * 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
+ */
+
+#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 <linux/pm_qos_params.h>
+
+#include "drmP.h"
+#include "exynos_drm.h"
+#include "exynos_drm_drv.h"
+#include "exynos_drm_gem.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
+
+/* Sourc Buffer Address */
+#define ROT_SRC_BUF_ADDR(n) (0x30 + ((n) << 2))
+
+/* Source Buffer Size */
+#define ROT_SRC_BUF_SIZE 0x3c
+#define ROT_SRC_BUF_SIZE_H(x) ((x) << 16)
+#define ROT_SRC_BUF_SIZE_W(x) ((x) << 0)
+
+/* Source Crop Position */
+#define ROT_SRC_CROP_POS 0x40
+#define ROT_SRC_CROP_POS_Y(x) ((x) << 16)
+#define ROT_SRC_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)
+
+/* Destination Buffer Address */
+#define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2))
+
+/* Destination Buffer Size */
+#define ROT_DST_BUF_SIZE 0x5c
+#define ROT_DST_BUF_SIZE_H(x) ((x) << 16)
+#define ROT_DST_BUF_SIZE_W(x) ((x) << 0)
+
+/* Destination Crop Position */
+#define ROT_DST_CROP_POS 0x60
+#define ROT_DST_CROP_POS_Y(x) ((x) << 16)
+#define ROT_DST_CROP_POS_X(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))
+
+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;
+ int exec_ret;
+ struct exynos_drm_subdrv subdrv;
+ struct completion complete;
+ struct mutex exec_mutex;
+ spinlock_t irq_lock;
+ struct pm_qos_request_list pm_qos;
+ bool suspended;
+};
+
+struct rot_buffer {
+ dma_addr_t src_addr[DRM_EXYNOS_ROT_MAX_BUF];
+ dma_addr_t dst_addr[DRM_EXYNOS_ROT_MAX_BUF];
+ u32 src_cnt;
+ u32 dst_cnt;
+ u32 src_w;
+ u32 src_h;
+ u32 dst_w;
+ u32 dst_h;
+};
+
+static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
+{
+ u32 value = readl(rot->regs + ROT_CONFIG);
+
+ if (enable == true)
+ value |= ROT_CONFIG_IRQ;
+ else
+ value &= ~ROT_CONFIG_IRQ;
+
+ writel(value, rot->regs + ROT_CONFIG);
+}
+
+static void rotator_reg_set_format(struct rot_context *rot, u32 img_fmt)
+{
+ u32 value = readl(rot->regs + ROT_CONTROL);
+ value &= ~ROT_CONTROL_FMT_MASK;
+
+ switch (img_fmt) {
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV12M:
+ value |= ROT_CONTROL_FMT_YCBCR420_2P;
+ break;
+ case DRM_FORMAT_RGB888:
+ value |= ROT_CONTROL_FMT_RGB888;
+ break;
+ default:
+ DRM_ERROR("invalid image format\n");
+ return;
+ }
+
+ writel(value, rot->regs + ROT_CONTROL);
+}
+
+static void rotator_reg_set_flip(struct rot_context *rot,
+ enum drm_exynos_rot_flip flip)
+{
+ u32 value = readl(rot->regs + ROT_CONTROL);
+ value &= ~ROT_CONTROL_FLIP_MASK;
+
+ switch (flip) {
+ case ROT_FLIP_VERTICAL:
+ value |= ROT_CONTROL_FLIP_VERTICAL;
+ break;
+ case ROT_FLIP_HORIZONTAL:
+ value |= ROT_CONTROL_FLIP_HORIZONTAL;
+ break;
+ default:
+ /* Flip None */
+ break;
+ }
+
+ writel(value, rot->regs + ROT_CONTROL);
+}
+
+static void rotator_reg_set_rotation(struct rot_context *rot,
+ enum drm_exynos_rot_degree degree)
+{
+ u32 value = readl(rot->regs + ROT_CONTROL);
+ value &= ~ROT_CONTROL_ROT_MASK;
+
+ switch (degree) {
+ case ROT_DEGREE_90:
+ value |= ROT_CONTROL_ROT_90;
+ break;
+ case ROT_DEGREE_180:
+ value |= ROT_CONTROL_ROT_180;
+ break;
+ case ROT_DEGREE_270:
+ value |= ROT_CONTROL_ROT_270;
+ break;
+ default:
+ /* Rotation 0 Degree */
+ break;
+ }
+
+ writel(value, rot->regs + ROT_CONTROL);
+}
+
+static void rotator_reg_set_start(struct rot_context *rot)
+{
+ u32 value = readl(rot->regs + ROT_CONTROL);
+
+ value |= ROT_CONTROL_START;
+
+ writel(value, rot->regs + ROT_CONTROL);
+}
+
+static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
+{
+ u32 value = readl(rot->regs + ROT_STATUS);
+ value = ROT_STATUS_IRQ(value);
+
+ if (value == 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 value = readl(rot->regs + ROT_STATUS);
+
+ value |= ROT_STATUS_IRQ_PENDING((u32)status);
+
+ writel(value, rot->regs + ROT_STATUS);
+}
+
+static void rotator_reg_set_src_buf_addr(struct rot_context *rot,
+ dma_addr_t addr, int i)
+{
+ writel(addr, rot->regs + ROT_SRC_BUF_ADDR(i));
+}
+
+static void rotator_reg_set_src_buf_size(struct rot_context *rot, u32 w, u32 h)
+{
+ u32 value = ROT_SRC_BUF_SIZE_H(h) | ROT_SRC_BUF_SIZE_W(w);
+
+ writel(value, rot->regs + ROT_SRC_BUF_SIZE);
+}
+
+static void rotator_reg_set_src_crop_pos(struct rot_context *rot, u32 x, u32 y)
+{
+ u32 value = ROT_SRC_CROP_POS_Y(y) | ROT_SRC_CROP_POS_X(x);
+
+ writel(value, rot->regs + ROT_SRC_CROP_POS);
+}
+
+static void rotator_reg_set_src_crop_size(struct rot_context *rot, u32 w, u32 h)
+{
+ u32 value = ROT_SRC_CROP_SIZE_H(h) | ROT_SRC_CROP_SIZE_W(w);
+
+ writel(value, rot->regs + ROT_SRC_CROP_SIZE);
+}
+
+static void rotator_reg_set_dst_buf_addr(struct rot_context *rot,
+ dma_addr_t addr, int i)
+{
+ writel(addr, rot->regs + ROT_DST_BUF_ADDR(i));
+}
+
+static void rotator_reg_set_dst_buf_size(struct rot_context *rot, u32 w, u32 h)
+{
+ u32 value = ROT_DST_BUF_SIZE_H(h) | ROT_DST_BUF_SIZE_W(w);
+
+ writel(value, rot->regs + ROT_DST_BUF_SIZE);
+}
+
+static void rotator_reg_set_dst_crop_pos(struct rot_context *rot, u32 x, u32 y)
+{
+ u32 value = ROT_DST_CROP_POS_Y(y) | ROT_DST_CROP_POS_X(x);
+
+ writel(value, rot->regs + ROT_DST_CROP_POS);
+}
+
+static void rotator_reg_get_dump(struct rot_context *rot)
+{
+ u32 value, i;
+
+ for (i = 0; i <= ROT_DST_CROP_POS; i += 0x4) {
+ value = readl(rot->regs + i);
+ DRM_INFO("+0x%x: 0x%x", i, value);
+ }
+}
+
+static bool rotator_check_format_n_handle_valid(u32 img_fmt,
+ u32 src_buf_handle_cnt,
+ u32 dst_buf_handle_cnt)
+{
+ bool ret = false;
+
+ if ((src_buf_handle_cnt != dst_buf_handle_cnt)
+ || (src_buf_handle_cnt == 0))
+ return ret;
+
+ switch (img_fmt) {
+ case DRM_FORMAT_NV12M:
+ if (src_buf_handle_cnt == 2)
+ ret = true;
+ break;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_RGB888:
+ if (src_buf_handle_cnt == 1)
+ ret = true;
+ break;
+ default:
+ DRM_ERROR("invalid image format\n");
+ break;
+ }
+
+ return ret;
+}
+
+static void rotator_align_size(struct rot_limit *limit, u32 mask, u32 *w,
+ u32 *h)
+{
+ u32 value;
+
+ value = ROT_ALIGN(w, limit->align, mask);
+ if (value < limit->min_w)
+ *w = ROT_MIN(limit->min_w, mask);
+ else if (value > limit->max_w)
+ *w = ROT_MAX(limit->max_w, mask);
+ else
+ *w = value;
+
+ value = ROT_ALIGN(h, limit->align, mask);
+ if (value < limit->min_h)
+ *h = ROT_MIN(limit->min_h, mask);
+ else if (value > limit->max_h)
+ *h = ROT_MAX(limit->max_h, mask);
+ else
+ *h = value;
+}
+
+static void rotator_align_buffer(struct rot_context *rot,
+ struct rot_buffer *buf,
+ struct drm_exynos_rot_buffer *req_buf,
+ struct drm_exynos_rot_control *control)
+{
+ struct rot_limit_table *limit_tbl = rot->limit_tbl;
+ struct rot_limit *limit;
+ u32 mask;
+
+ /* Get size limit */
+ if (control->img_fmt == DRM_FORMAT_RGB888)
+ limit = &limit_tbl->rgb888;
+ else
+ limit = &limit_tbl->ycbcr420_2p;
+
+ /* Get mask for rounding to nearest aligned value */
+ mask = ~((1 << limit->align) - 1);
+
+ /* For source buffer */
+ buf->src_w = req_buf->src_w;
+ buf->src_h = req_buf->src_h;
+ rotator_align_size(limit, mask, &buf->src_w, &buf->src_h);
+
+ /* For destination buffer */
+ buf->dst_w = req_buf->dst_w;
+ buf->dst_h = req_buf->dst_h;
+ rotator_align_size(limit, mask, &buf->dst_w, &buf->dst_h);
+}
+
+static bool rotator_check_crop_boundary(struct rot_buffer *buf,
+ struct drm_exynos_rot_control *control,
+ struct drm_exynos_rot_crop *crop)
+{
+ bool ret = true;
+
+ /* Check source crop position */
+ if ((crop->src_x + crop->src_w > buf->src_w)
+ || (crop->src_y + crop->src_h > buf->src_h))
+ return false;
+
+ /* Check destination crop position */
+ switch (control->degree) {
+ case ROT_DEGREE_90:
+ case ROT_DEGREE_270:
+ if ((crop->dst_x + crop->src_h > buf->dst_w)
+ || (crop->dst_y + crop->src_w > buf->dst_h))
+ ret = false;
+ break;
+ default:
+ if ((crop->dst_x + crop->src_w > buf->dst_w)
+ || (crop->dst_y + crop->src_h > buf->dst_h))
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+static int rotator_get_dma_addr(struct rot_buffer *buf,
+ struct drm_exynos_rot_buffer *req_buf,
+ struct drm_device *drm_dev,
+ struct drm_file *file)
+{
+ void *addr;
+
+ /* For source buffer */
+ buf->src_cnt = 0;
+ while (buf->src_cnt < req_buf->src_cnt) {
+ addr = exynos_drm_gem_get_dma_addr(drm_dev,
+ req_buf->src_handle[buf->src_cnt],
+ file);
+ if (IS_ERR_OR_NULL(addr)) {
+ DRM_ERROR("failed to get src dma_addr [%u]\n",
+ buf->src_cnt);
+ return -EINVAL;
+ }
+ buf->src_addr[(buf->src_cnt)++] = *(dma_addr_t *)addr;
+ }
+
+ /* For destination buffer */
+ buf->dst_cnt = 0;
+ while (buf->dst_cnt < req_buf->dst_cnt) {
+ addr = exynos_drm_gem_get_dma_addr(drm_dev,
+ req_buf->dst_handle[buf->dst_cnt],
+ file);
+ if (IS_ERR_OR_NULL(addr)) {
+ DRM_ERROR("failed to get dst dma_addr [%u]\n",
+ buf->dst_cnt);
+ return -EINVAL;
+ }
+ buf->dst_addr[(buf->dst_cnt)++] = *(dma_addr_t *)addr;
+ }
+
+ return 0;
+}
+
+static void rotator_put_dma_addr(struct rot_buffer *buf,
+ struct drm_exynos_rot_buffer *req_buf,
+ struct drm_device *drm_dev,
+ struct drm_file *file)
+{
+ /* For destination buffer */
+ while (buf->dst_cnt > 0)
+ exynos_drm_gem_put_dma_addr(drm_dev,
+ req_buf->dst_handle[--(buf->dst_cnt)],
+ file);
+
+ /* For source buffer */
+ while (buf->src_cnt > 0)
+ exynos_drm_gem_put_dma_addr(drm_dev,
+ req_buf->src_handle[--(buf->src_cnt)],
+ file);
+}
+
+static void rotator_execute(struct rot_context *rot,
+ struct rot_buffer *buf,
+ struct drm_exynos_rot_control *control,
+ struct drm_exynos_rot_crop *crop)
+{
+ int i;
+
+ pm_runtime_get_sync(rot->subdrv.dev);
+
+ /* Set interrupt enable */
+ rotator_reg_set_irq(rot, true);
+
+ /* Set control registers */
+ rotator_reg_set_format(rot, control->img_fmt);
+ rotator_reg_set_flip(rot, control->flip);
+ rotator_reg_set_rotation(rot, control->degree);
+
+ /* Set source buffer address */
+ for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++)
+ rotator_reg_set_src_buf_addr(rot, buf->src_addr[i], i);
+
+ /* Set source buffer size */
+ rotator_reg_set_src_buf_size(rot, buf->src_w, buf->src_h);
+
+ /* Set destination buffer address */
+ for (i = 0; i < DRM_EXYNOS_ROT_MAX_BUF; i++)
+ rotator_reg_set_dst_buf_addr(rot, buf->dst_addr[i], i);
+
+ /* Set destination buffer size */
+ rotator_reg_set_dst_buf_size(rot, buf->dst_w, buf->dst_h);
+
+ /* Set source crop image position */
+ rotator_reg_set_src_crop_pos(rot, crop->src_x, crop->src_y);
+
+ /* Set source crop image size */
+ rotator_reg_set_src_crop_size(rot, crop->src_w, crop->src_h);
+
+ /* Set destination crop image position */
+ rotator_reg_set_dst_crop_pos(rot, crop->dst_x, crop->dst_y);
+
+ /* Start rotator operation */
+ rotator_reg_set_start(rot);
+}
+
+int exynos_drm_rotator_exec_ioctl(struct drm_device *drm_dev, void *data,
+ struct drm_file *file)
+{
+ struct drm_exynos_file_private *file_priv = file->driver_priv;
+ struct exynos_drm_rot_private *priv = file_priv->rot_priv;
+ struct device *dev = priv->dev;
+ struct rot_context *rot;
+ struct drm_exynos_rot_exec_data *req = data;
+ struct drm_exynos_rot_buffer *req_buf = &req->buf;
+ struct drm_exynos_rot_control *control = &req->control;
+ struct drm_exynos_rot_crop *crop = &req->crop;
+ struct rot_buffer buf;
+
+ if (!dev) {
+ DRM_ERROR("failed to get dev\n");
+ return -ENODEV;
+ }
+
+ rot = dev_get_drvdata(dev);
+ if (!rot) {
+ DRM_ERROR("failed to get drvdata\n");
+ return -EFAULT;
+ }
+
+ if (rot->suspended) {
+ DRM_ERROR("suspended state\n");
+ return -EPERM;
+ }
+
+ if (!rotator_check_format_n_handle_valid(control->img_fmt,
+ req_buf->src_cnt,
+ req_buf->dst_cnt)) {
+ DRM_ERROR("format or handles are invalid\n");
+ return -EINVAL;
+ }
+
+ init_completion(&rot->complete);
+
+ /* Align buffer */
+ rotator_align_buffer(rot, &buf, req_buf, control);
+
+ /* Check crop boundary */
+ if (!rotator_check_crop_boundary(&buf, control, crop)) {
+ DRM_ERROR("boundary errror\n");
+ return -EINVAL;
+ }
+
+ /* Get DMA address */
+ rot->exec_ret = rotator_get_dma_addr(&buf, req_buf, drm_dev, file);
+ if (rot->exec_ret < 0)
+ goto err_get_dma_addr;
+
+ /* Assign another src/dst_addr for NV12 image format */
+ if (control->img_fmt == DRM_FORMAT_NV12) {
+ u32 size = crop->src_w * crop->src_h;
+
+ buf.src_addr[buf.src_cnt + 1] =
+ buf.src_addr[buf.src_cnt] + size;
+ buf.dst_addr[buf.dst_cnt + 1] =
+ buf.dst_addr[buf.dst_cnt] + size;
+ }
+
+ /* Execute */
+ mutex_lock(&rot->exec_mutex);
+ rotator_execute(rot, &buf, control, crop);
+ if (!wait_for_completion_timeout(&rot->complete, 2 * HZ)) {
+ DRM_ERROR("timeout error\n");
+ rot->exec_ret = -ETIMEDOUT;
+ mutex_unlock(&rot->exec_mutex);
+ goto err_get_dma_addr;
+ }
+ mutex_unlock(&rot->exec_mutex);
+
+ /* Put DMA address */
+ rotator_put_dma_addr(&buf, req_buf, drm_dev, file);
+
+ return rot->exec_ret;
+
+err_get_dma_addr:
+ rotator_put_dma_addr(&buf, req_buf, drm_dev, file);
+ return rot->exec_ret;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_rotator_exec_ioctl);
+
+static irqreturn_t rotator_irq_thread(int irq, void *arg)
+{
+ struct rot_context *rot = (struct rot_context *)arg;
+ enum rot_irq_status irq_status;
+ unsigned long flags;
+
+ pm_qos_update_request(&rot->pm_qos, 0);
+
+ /* Get execution result */
+ spin_lock_irqsave(&rot->irq_lock, flags);
+ irq_status = rotator_reg_get_irq_status(rot);
+ rotator_reg_set_irq_status_clear(rot, irq_status);
+ spin_unlock_irqrestore(&rot->irq_lock, flags);
+
+ rot->exec_ret = 0;
+ if (irq_status != ROT_IRQ_STATUS_COMPLETE) {
+ DRM_ERROR("the SFR is set illegally\n");
+ rot->exec_ret = -EINVAL;
+ rotator_reg_get_dump(rot);
+ }
+
+ pm_runtime_put(rot->subdrv.dev);
+
+ complete(&rot->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int rotator_subdrv_open(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file)
+{
+ struct drm_exynos_file_private *file_priv = file->driver_priv;
+ struct exynos_drm_rot_private *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(dev, "failed to allocate priv\n");
+ return -ENOMEM;
+ }
+
+ priv->dev = dev;
+
+ file_priv->rot_priv = priv;
+
+ return 0;
+}
+
+static void rotator_subdrv_close(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file)
+{
+ struct drm_exynos_file_private *file_priv = file->driver_priv;
+
+ kfree(file_priv->rot_priv);
+
+ return;
+}
+
+static int __devinit rotator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rot_context *rot;
+ struct resource *res;
+ struct exynos_drm_subdrv *subdrv;
+ 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;
+
+ mutex_init(&rot->exec_mutex);
+ spin_lock_init(&rot->irq_lock);
+
+ 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_thread,
+ 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);
+ pm_qos_add_request(&rot->pm_qos, PM_QOS_BUS_DMA_THROUGHPUT, 0);
+
+ subdrv = &rot->subdrv;
+ subdrv->dev = dev;
+ subdrv->open = rotator_subdrv_open;
+ subdrv->close = rotator_subdrv_close;
+
+ platform_set_drvdata(pdev, rot);
+
+ ret = exynos_drm_subdrv_register(subdrv);
+ if (ret < 0) {
+ dev_err(dev, "failed to register drm rotator device\n");
+ goto err_subdrv_register;
+ }
+
+ dev_info(dev, "The exynos rotator is probed successfully\n");
+
+ return 0;
+
+err_subdrv_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 rot_context *rot = platform_get_drvdata(pdev);
+
+ pm_qos_remove_request(&rot->pm_qos);
+
+ exynos_drm_subdrv_unregister(&rot->subdrv);
+
+ pm_runtime_disable(&pdev->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,
+ },
+ {},
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rotator_suspend(struct device *dev)
+{
+ struct rot_context *rot = dev_get_drvdata(dev);
+
+ /* Check & wait for running state */
+ mutex_lock(&rot->exec_mutex);
+ mutex_unlock(&rot->exec_mutex);
+
+ rot->suspended = true;
+
+ return 0;
+}
+
+static int rotator_resume(struct device *dev)
+{
+ struct rot_context *rot = dev_get_drvdata(dev);
+
+ rot->suspended = false;
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int rotator_runtime_suspend(struct device *dev)
+{
+ struct rot_context *rot = dev_get_drvdata(dev);
+
+ clk_disable(rot->clock);
+
+ return 0;
+}
+
+static int rotator_runtime_resume(struct device *dev)
+{
+ struct rot_context *rot = dev_get_drvdata(dev);
+
+ clk_enable(rot->clock);
+ pm_qos_update_request(&rot->pm_qos, 400000);
+
+ return 0;
+}
+#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..5f383d5
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
@@ -0,0 +1,25 @@
+/*
+ * 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_
+
+#ifdef CONFIG_DRM_EXYNOS_ROTATOR
+extern int exynos_drm_rotator_exec_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+#else
+static inline int exynos_drm_rotator_exec_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *file_priv)
+{
+ return -ENOTTY;
+}
+#endif
+
+#endif
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h
index e478de4..772cac1 100644
--- a/include/drm/exynos_drm.h
+++ b/include/drm/exynos_drm.h
@@ -100,6 +100,99 @@ enum e_drm_exynos_gem_mem_type {
EXYNOS_BO_MASK = EXYNOS_BO_NONCONTIG
};
+enum drm_exynos_rot_flip {
+ ROT_FLIP_NONE,
+ ROT_FLIP_VERTICAL,
+ ROT_FLIP_HORIZONTAL,
+};
+
+enum drm_exynos_rot_degree {
+ ROT_DEGREE_0,
+ ROT_DEGREE_90,
+ ROT_DEGREE_180,
+ ROT_DEGREE_270,
+};
+
+#define DRM_EXYNOS_ROT_MAX_BUF 3
+
+/**
+ * A structure for rotator buffer
+ *
+ * @src_handle: Source GEM handles.
+ * @dst_handle: Destination GEM handles.
+ * - *_handle[0] : For RGB or Y buffer.
+ * - *_handle[1] : For CbCr or Cb buffer.
+ * - *_handle[2] : For Cr buffer.
+ * @src_cnt: Number of source GEM handles.
+ * @dst_cnt: Number of destination GEM handles.
+ * @src_w: Source Buffer width.
+ * @src_h: Source Buffer height.
+ * @dst_w: Destination Buffer width.
+ * @dst_h: Destination Buffer height.
+ */
+struct drm_exynos_rot_buffer {
+ __u32 src_handle[DRM_EXYNOS_ROT_MAX_BUF];
+ __u32 dst_handle[DRM_EXYNOS_ROT_MAX_BUF];
+ __u32 src_cnt;
+ __u32 dst_cnt;
+ __u32 src_w;
+ __u32 src_h;
+ __u32 dst_w;
+ __u32 dst_h;
+};
+
+/**
+ * A structure for rotator control.
+ *
+ * @img_fmt: Source / destination buffer (image)format.
+ * - fourcc code from drm_fourcc.h
+ * - DRM_FORMAT_RGB888
+ * - DRM_FORMAT_NV12
+ * - DRM_FORMAT_NV12M
+ * @flip: Flip operation value.
+ * @degree: Rotation operation degree value.
+ */
+struct drm_exynos_rot_control {
+ __u32 img_fmt;
+ enum drm_exynos_rot_flip flip;
+ enum drm_exynos_rot_degree degree;
+ __u32 reserved;
+};
+
+/**
+ * A structure for rotator crop.
+ *
+ * @src_x: Cropped image position x in source buffer[FROM].
+ * @src_y: Cropped image position y in source buffer[FROM].
+ * @src_w: Cropped image width in source buffer.
+ * @src_h: Cropped image height in source buffer.
+ * @dst_x: Cropped image position x in destination buffer[TO].
+ * @dst_y: Cropped image position y in destination buffer[TO].
+ */
+struct drm_exynos_rot_crop {
+ __u32 src_x;
+ __u32 src_y;
+ __u32 src_w;
+ __u32 src_h;
+ __u32 dst_x;
+ __u32 dst_y;
+};
+
+/**
+ * A structure for rotator operation.
+ *
+ * @buf: (Image)Buffer data.
+ * @control: Control data.
+ * @crop: Cropped image data.
+ * @user_data: Not used yet.
+ */
+struct drm_exynos_rot_exec_data {
+ struct drm_exynos_rot_buffer buf;
+ struct drm_exynos_rot_control control;
+ struct drm_exynos_rot_crop crop;
+ __u64 user_data;
+};
+
#define DRM_EXYNOS_GEM_CREATE 0x00
#define DRM_EXYNOS_GEM_MAP_OFFSET 0x01
#define DRM_EXYNOS_GEM_MMAP 0x02
@@ -107,6 +200,9 @@ enum e_drm_exynos_gem_mem_type {
#define DRM_EXYNOS_PLANE_SET_ZPOS 0x06
#define DRM_EXYNOS_VIDI_CONNECTION 0x07
+/* Rotator */
+#define DRM_EXYNOS_ROTATOR_EXEC 0x30
+
#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -122,6 +218,9 @@ enum e_drm_exynos_gem_mem_type {
#define DRM_IOCTL_EXYNOS_VIDI_CONNECTION DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_VIDI_CONNECTION, struct drm_exynos_vidi_connection)
+#define DRM_IOCTL_EXYNOS_ROTATOR_EXEC DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_ROTATOR_EXEC, struct drm_exynos_rot_exec_data)
+
#ifdef __KERNEL__
/**
--
1.7.9.5
More information about the dri-devel
mailing list