This is a quick conversion of Exynos DRM rotator driver from Exynos IPP framework to generic DRM FBProc API to demonstrate how to use it from the driver side. Not-for-merge-yet: the code of the driver must be cleaned up first, because its internals still depends on Exynos IPP structures. Signed-off-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx> --- drivers/gpu/drm/exynos/Kconfig | 1 - drivers/gpu/drm/exynos/exynos_drm_drv.c | 3 +- drivers/gpu/drm/exynos/exynos_drm_rotator.c | 353 +++++++++++++++------------- drivers/gpu/drm/exynos/exynos_drm_rotator.h | 19 -- 4 files changed, 194 insertions(+), 182 deletions(-) delete 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 83f61c513b7e..6eebba9be797 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -109,7 +109,6 @@ config DRM_EXYNOS_FIMC config DRM_EXYNOS_ROTATOR bool "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/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 877d2efa28e2..f536a63531bb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -395,7 +395,7 @@ static const struct file_operations exynos_drm_driver_fops = { static struct drm_driver exynos_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME - | DRIVER_ATOMIC | DRIVER_RENDER, + | DRIVER_ATOMIC | DRIVER_RENDER | DRIVER_FBPROC, .load = exynos_drm_load, .unload = exynos_drm_unload, .open = exynos_drm_open, @@ -532,6 +532,7 @@ static struct exynos_drm_driver_info exynos_drm_drivers[] = { DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC), }, { DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR), + DRM_COMPONENT_DRIVER }, { DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), }, { diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index 404367a430b5..8ecd8db0649a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -10,6 +10,7 @@ */ #include <linux/kernel.h> +#include <linux/component.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -21,8 +22,10 @@ #include <drm/drmP.h> #include <drm/exynos_drm.h> #include "regs-rotator.h" +#include "exynos_drm_fb.h" #include "exynos_drm_drv.h" #include "exynos_drm_ipp.h" +#include "exynos_drm_iommu.h" /* * Rotator supports image crop/rotator and input/output DMA operations. @@ -92,7 +95,9 @@ struct rot_limit_table { * @suspended: suspended state. */ struct rot_context { - struct exynos_drm_ippdrv ippdrv; + struct drm_fbproc fbproc; + struct drm_device *drm_dev; + struct device *dev; struct resource *regs_res; void __iomem *regs; struct clk *clock; @@ -100,6 +105,10 @@ struct rot_context { int irq; int cur_buf_id[EXYNOS_DRM_OPS_MAX]; bool suspended; + spinlock_t lock; + struct drm_fbproc_task *task; + wait_queue_head_t done_wq; + bool done; }; static void rotator_reg_set_irq(struct rot_context *rot, bool enable) @@ -138,9 +147,6 @@ static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) 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->c_node; - struct drm_exynos_ipp_event_work *event_work = c_node->event_work; enum rot_irq_status irq_status; u32 val; @@ -152,12 +158,14 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg) 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, &event_work->work); - } else { + spin_lock(&rot->lock); + rot->task = NULL; + rot->done = true; + spin_unlock(&rot->lock); + + wake_up(&rot->done_wq); + + if (irq_status != ROT_IRQ_STATUS_COMPLETE) { DRM_ERROR("the SFR is set illegally\n"); } @@ -455,36 +463,6 @@ static int rotator_dst_set_addr(struct device *dev, 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 = &ippdrv->prop_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 inline bool rotator_check_drm_fmt(u32 fmt) { switch (fmt) { @@ -511,93 +489,6 @@ static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip) } } -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("not support csc feature\n"); - return -EINVAL; - } - - if (!rotator_check_drm_fmt(dst_config->fmt)) { - DRM_DEBUG_KMS("invalid format\n"); - return -EINVAL; - } - - /* Check transform configuration */ - if (src_config->degree != EXYNOS_DRM_DEGREE_0) { - DRM_DEBUG_KMS("not support source-side rotation\n"); - 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("invalid degree\n"); - return -EINVAL; - } - - if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { - DRM_DEBUG_KMS("not support source-side flip\n"); - return -EINVAL; - } - - if (!rotator_check_drm_flip(dst_config->flip)) { - DRM_DEBUG_KMS("invalid flip\n"); - 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("out of source buffer bound\n"); - 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("out of destination buffer bound\n"); - return -EINVAL; - } - - if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { - DRM_DEBUG_KMS("not support scale feature\n"); - 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("out of destination buffer bound\n"); - return -EINVAL; - } - - if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { - DRM_DEBUG_KMS("not support scale feature\n"); - 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); @@ -692,24 +583,184 @@ static const struct of_device_id exynos_rotator_match[] = { }; MODULE_DEVICE_TABLE(of, exynos_rotator_match); +static int rotator_get_exclusive_access(struct rot_context *rot, + struct drm_fbproc_task *task) +{ + unsigned long flags; + int got_access = false; + int ret = 0; + +try_again: + spin_lock_irqsave(&rot->lock, flags); + if (rot->task == NULL) { + rot->task = task; + got_access = true; + } + spin_unlock_irqrestore(&rot->lock, flags); + + if (!got_access) { + ret = wait_event_interruptible(rot->done_wq, rot->done); + if (ret) + return ret; + goto try_again; + } + return ret; +} + +static int rotator_commit(struct drm_fbproc *fbproc, + struct drm_fbproc_task *task) +{ + struct rot_context *rot = + container_of(fbproc, struct rot_context, fbproc); + struct device *dev = rot->dev; + dma_addr_t src_dma = exynos_drm_fb_dma_addr(task->src_fb, 0); + dma_addr_t dst_dma = exynos_drm_fb_dma_addr(task->dst_fb, 0); + int r = task->rotation; + int w = task->src_w >> 16; + int h = task->src_h >> 16; + int sx = task->src_x >> 16; + int dx = task->dst_x >> 16; + int sy = task->src_y >> 16; + int dy = task->dst_y >> 16; + int sp = task->src_fb->pitches[0]; + int dp = task->dst_fb->pitches[0]; + + struct drm_exynos_ipp_buf_info src_buf_info = { .base[0] = src_dma, }; + struct drm_exynos_ipp_buf_info dst_buf_info = { .base[0] = dst_dma, }; + struct drm_exynos_pos src_pos = { sx, sy, w, h, }; + struct drm_exynos_pos dst_pos = { dx, dy, w, h, }; + struct drm_exynos_sz src_sz = { sp/4, h+sy, }; + struct drm_exynos_sz dst_sz = { dp/4, h+dy, }; + int ret; + int rotation = 0, flip = 0; + bool swap; + + if (r & DRM_ROTATE_180) + rotation = EXYNOS_DRM_DEGREE_180; + else if (r & DRM_ROTATE_90) + rotation = EXYNOS_DRM_DEGREE_90; + else if (r & DRM_ROTATE_270) + rotation = EXYNOS_DRM_DEGREE_270; + + if (r & DRM_REFLECT_X) + flip |= EXYNOS_DRM_FLIP_HORIZONTAL; + if (r & DRM_REFLECT_Y) + flip |= EXYNOS_DRM_FLIP_VERTICAL; + + ret = rotator_get_exclusive_access(rot, task); + if (ret < 0) + return ret; + + pm_runtime_get_sync(dev); + + dev_dbg(dev, "r %d w %d h %d sx %d sy %d sp %d dx %d dy %d pd %d\n", + r, w, h, sx, sy, sp, dx, dy, dp); + rot->done = 0; + + ret = rotator_src_set_fmt(dev, DRM_FORMAT_XRGB8888); + if (ret) + printk("error setting rotator %d\n", __LINE__); + ret = rotator_src_set_size(dev, 0, &src_pos, &src_sz); + if (ret) + printk("error setting rotator %d\n", __LINE__); + ret = rotator_src_set_addr(dev, &src_buf_info, 0, IPP_BUF_ENQUEUE); + if (ret) + printk("error setting rotator %d\n", __LINE__); + + ret = rotator_dst_set_transf(dev, rotation, flip, &swap); + if (ret) + printk("error setting rotator %d\n", __LINE__); + + if (swap) { + swap(dst_pos.w, dst_pos.h); + dst_sz.vsize = w+dy; + } + ret = rotator_dst_set_size(dev, 0, &dst_pos, &dst_sz); + if (ret) + printk("error setting rotator %d\n", __LINE__); + ret = rotator_dst_set_addr(dev, &dst_buf_info, 0, IPP_BUF_ENQUEUE); + if (ret) + printk("error setting rotator %d\n", __LINE__); + + rotator_ippdrv_start(dev, IPP_CMD_M2M); + + wait_event(rot->done_wq, rot->done); + + pm_runtime_put(dev); + + return 0; +} + +struct drm_fbproc_funcs fbproc_funcs = { + .commit = rotator_commit, +}; + +static const uint32_t rotator_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static int rotator_bind(struct device *dev, struct device *master, void *data) +{ + struct rot_context *rot = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct drm_fbproc *fbproc = &rot->fbproc; + struct drm_property *prop; + + rot->drm_dev = drm_dev; + drm_iommu_attach_device(drm_dev, dev); + + + drm_fbproc_init(drm_dev, fbproc, &fbproc_funcs, + DRM_FBPROC_CAP_CROP | DRM_FBPROC_CAP_ROTATE, + rotator_formats, ARRAY_SIZE(rotator_formats), + rotator_formats, ARRAY_SIZE(rotator_formats), + "rotator"); + + prop = drm_mode_create_rotation_property(drm_dev, + DRM_ROTATE_0 | DRM_ROTATE_90 | DRM_ROTATE_180 | + DRM_ROTATE_270 | DRM_REFLECT_X | DRM_REFLECT_Y); + + fbproc->rotation_property = prop; + drm_object_attach_property(&fbproc->base, prop, DRM_ROTATE_0); + + dev_info(dev, "The exynos rotator is probed successfully\n"); + + return 0; +} + +static void rotator_unbind(struct device *dev, struct device *master, + void *data) +{ + struct rot_context *rot = dev_get_drvdata(dev); + + drm_iommu_detach_device(rot->drm_dev, rot->dev); +} + +static const struct component_ops rotator_component_ops = { + .bind = rotator_bind, + .unbind = rotator_unbind, +}; + static int rotator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rot_context *rot; - struct exynos_drm_ippdrv *ippdrv; + const struct of_device_id *match; int ret; - if (!dev->of_node) { - dev_err(dev, "cannot find of_node.\n"); - return -ENODEV; - } - rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL); if (!rot) return -ENOMEM; - rot->limit_tbl = (struct rot_limit_table *) - of_device_get_match_data(dev); + match = of_match_node(exynos_rotator_match, dev->of_node); + if (!match) { + dev_err(dev, "failed to match node\n"); + return -ENODEV; + } + + spin_lock_init(&rot->lock); + rot->limit_tbl = (struct rot_limit_table *)match->data; + rot->dev = dev; rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rot->regs = devm_ioremap_resource(dev, rot->regs_res); if (IS_ERR(rot->regs)) @@ -721,6 +772,8 @@ static int rotator_probe(struct platform_device *pdev) return rot->irq; } + init_waitqueue_head(&rot->done_wq); + ret = devm_request_threaded_irq(dev, rot->irq, NULL, rotator_irq_handler, IRQF_ONESHOT, "drm_rotator", rot); if (ret < 0) { @@ -734,31 +787,12 @@ static int rotator_probe(struct platform_device *pdev) return PTR_ERR(rot->clock); } - 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("ippdrv[%p]\n", ippdrv); - platform_set_drvdata(pdev, rot); + pm_runtime_enable(dev); - ret = exynos_drm_ippdrv_register(ippdrv); - if (ret < 0) { - dev_err(dev, "failed to register drm rotator device\n"); + ret = component_add(dev, &rotator_component_ops); + if (ret) goto err_ippdrv_register; - } - - dev_info(dev, "The exynos rotator is probed successfully\n"); return 0; @@ -770,11 +804,8 @@ err_ippdrv_register: static int 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; - - exynos_drm_ippdrv_unregister(ippdrv); + component_del(dev, &rotator_component_ops); pm_runtime_disable(dev); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h b/drivers/gpu/drm/exynos/exynos_drm_rotator.h deleted file mode 100644 index 71a0b4c0c1e8..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * - * Authors: - * YoungJun Cho <yj44.cho@xxxxxxxxxxx> - * Eunchul Kim <chulspro.kim@xxxxxxxxxxx> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef _EXYNOS_DRM_ROTATOR_H_ -#define _EXYNOS_DRM_ROTATOR_H_ - -/* TODO */ - -#endif -- 1.9.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel