From: Sylwester Nawrocki <snjw23@gmail.com>
To: Sunyoung Kang <sy0816.kang@samsung.com>
Cc: linux-media@vger.kernel.org,
'Mauro Carvalho Chehab' <mchehab@infradead.org>,
jonghun.han@samsung.com, khw0178.kim@samsung.com,
sungchun.kang@samsung.com, younglak1004.kim@samsung.com,
kgene.kim@samsung.com, a.sim@samsung.com
Subject: Re: [PATCH v2] media: rotator: Add new image rotator driver for EXYNOS
Date: Wed, 14 Mar 2012 22:21:08 +0100 [thread overview]
Message-ID: <4F610BC4.4080408@gmail.com> (raw)
In-Reply-To: <001601cd01b8$873dd8c0$95b98a40$%kang@samsung.com>
Hi Sunyoung,
thank you for addressing my previous comments. It looks much better now.
I have is a few more comments...
On 03/14/2012 09:00 AM, Sunyoung Kang wrote:
> This patch adds new rotator driver which is a device for
> rotation on EXYNOS SoCs.
>
> This rotator device supports the belows as key feature.
> 1) Image format
> : RGB565/888, YUV422 1p, YUV420 2p/3p
> 2) rotation
> : 0/90/180/270 and X/Y Flip
>
> Signed-off-by: Sunyoung Kang<sy0816.kang@samsung.com>
> Signed-off-by: Ayoung Sim<a.sim@samsung.com>
> ---NOTE:
It's almost right;) After the "---" there must be immediately a new
line character. So "NOTE" should be in a new line.
> This patch has been created based on following
> - media: media-dev: Add media devices for EXYNOS5 by Sungchun Kang
> - media: fimc-lite: Add new driver for camera interface by Sungchun Kang
>
> Changes since v1:
> - address comments from Sylwester Nawrocki
>
> drivers/media/video/exynos/Kconfig | 1 +
> drivers/media/video/exynos/Makefile | 1 +
> drivers/media/video/exynos/rotator/Kconfig | 7 +
> drivers/media/video/exynos/rotator/Makefile | 9 +
> drivers/media/video/exynos/rotator/rotator-core.c | 1344 +++++++++++++++++++++
> drivers/media/video/exynos/rotator/rotator-regs.c | 215 ++++
> drivers/media/video/exynos/rotator/rotator-regs.h | 70 ++
> drivers/media/video/exynos/rotator/rotator.h | 286 +++++
> 8 files changed, 1933 insertions(+), 0 deletions(-)
> create mode 100644 drivers/media/video/exynos/rotator/Kconfig
> create mode 100644 drivers/media/video/exynos/rotator/Makefile
> create mode 100644 drivers/media/video/exynos/rotator/rotator-core.c
> create mode 100644 drivers/media/video/exynos/rotator/rotator-regs.c
> create mode 100644 drivers/media/video/exynos/rotator/rotator-regs.h
> create mode 100644 drivers/media/video/exynos/rotator/rotator.h
>
...
> diff --git a/drivers/media/video/exynos/rotator/Kconfig b/drivers/media/video/exynos/rotator/Kconfig
> new file mode 100644
> index 0000000..977423a
> --- /dev/null
> +++ b/drivers/media/video/exynos/rotator/Kconfig
> @@ -0,0 +1,7 @@
> +config VIDEO_EXYNOS_ROTATOR
> + bool "EXYNOS Image Rotator Driver"
> + select V4L2_MEM2MEM_DEV
> + select VIDEOBUF2_DMA_CONTIG
> + default n
No need, the default default already is "n".
> + ---help---
> + Support for Image Rotator Driver with v4l2 on EXYNOS SoCs
> diff --git a/drivers/media/video/exynos/rotator/Makefile b/drivers/media/video/exynos/rotator/Makefile
> new file mode 100644
> index 0000000..6f74403
> --- /dev/null
> +++ b/drivers/media/video/exynos/rotator/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Copyright (c) 2012 Samsung Electronics Co., Ltd.
> +# http://www.samsung.com
> +#
> +# Licensed under GPLv2
> +#
> +
> +rotator-objs := rotator-core.o rotator-regs.o
> +obj-$(CONFIG_VIDEO_EXYNOS_ROTATOR) += rotator.o
> diff --git a/drivers/media/video/exynos/rotator/rotator-core.c b/drivers/media/video/exynos/rotator/rotator-core.c
> new file mode 100644
> index 0000000..75d28f2
> --- /dev/null
> +++ b/drivers/media/video/exynos/rotator/rotator-core.c
> @@ -0,0 +1,1344 @@
> +/*
> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Core file for Samsung EXYNOS Image 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.
> +*/
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/platform_device.h>
> +#include<linux/interrupt.h>
> +#include<linux/clk.h>
> +#include<linux/slab.h>
> +
> +#include<media/v4l2-ioctl.h>
> +#include<media/videobuf2-dma-contig.h>
> +
> +#include "rotator.h"
> +
> +int log_level;
> +module_param_named(log_level, log_level, uint, 0644);
> +
> +static struct rot_fmt rot_formats[] = {
> + {
> + .name = "RGB565",
> + .pixelformat = V4L2_PIX_FMT_RGB565,
> + .num_planes = 1,
> + .num_comp = 1,
> + .bitperpixel = { 16 },
> + }, {
> + .name = "XRGB-8888, 32 bps",
What 'bps' stands for ?
> + .pixelformat = V4L2_PIX_FMT_RGB32,
> + .num_planes = 1,
> + .num_comp = 1,
> + .bitperpixel = { 32 },
> + }, {
> + .name = "YUV 4:2:2 packed, YCbYCr",
> + .pixelformat = V4L2_PIX_FMT_YUYV,
> + .num_planes = 1,
> + .num_comp = 1,
> + .bitperpixel = { 16 },
> + }, {
> + .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
> + .pixelformat = V4L2_PIX_FMT_NV12M,
> + .num_planes = 2,
> + .num_comp = 2,
> + .bitperpixel = { 8, 4 },
> + }, {
> + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
> + .pixelformat = V4L2_PIX_FMT_YUV420M,
> + .num_planes = 3,
> + .num_comp = 3,
> + .bitperpixel = { 8, 2, 2 },
> + },
> +};
> +
...
> +static int rot_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct rot_ctx *ctx;
> + unsigned long flags;
> + int ret = 0;
> +
> + ctx = container_of(ctrl->handler, struct rot_ctx, ctrl_handler);
> + spin_lock_irqsave(&ctx->slock, flags);
> +
> + rot_dbg("ctrl ID:%d, value:%d\n", ctrl->id, ctrl->val);
> + switch (ctrl->id) {
> + case V4L2_CID_VFLIP:
> + if (ctrl->val)
> + ctx->flip |= ROT_VFLIP;
> + else
> + ctx->flip&= ~ROT_VFLIP;
> + break;
> + case V4L2_CID_HFLIP:
> + if (ctrl->val)
> + ctx->flip |= ROT_HFLIP;
> + else
> + ctx->flip&= ~ROT_HFLIP;
> + break;
> + case V4L2_CID_ROTATE:
> + ctx->rotation = ctrl->val;
> + break;
> + default:
> + v4l2_err(&ctx->rot_dev->m2m.v4l2_dev, "invalid control id\n");
> + ret = -EINVAL;
These 3 lines can be removed, rot_s_ctrl can be called only with ctrl->id
from the set of controls added to control handler.
> + }
> +
> + spin_unlock_irqrestore(&ctx->slock, flags);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops rot_ctrl_ops = {
> + .s_ctrl = rot_s_ctrl,
> +};
> +
> +static int rot_add_ctrls(struct rot_ctx *ctx)
> +{
> + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
There are only 3 controls, so s/3/4.
> + v4l2_ctrl_new_std(&ctx->ctrl_handler,&rot_ctrl_ops,
> + V4L2_CID_VFLIP, 0, 1, 1, 0);
> + v4l2_ctrl_new_std(&ctx->ctrl_handler,&rot_ctrl_ops,
> + V4L2_CID_HFLIP, 0, 1, 1, 0);
> + v4l2_ctrl_new_std(&ctx->ctrl_handler,&rot_ctrl_ops,
> + V4L2_CID_ROTATE, 0, 270, 90, 0);
> +
> + if (ctx->ctrl_handler.error) {
> + int err = ctx->ctrl_handler.error;
> + v4l2_err(&ctx->rot_dev->m2m.v4l2_dev,
> + "v4l2_ctrl_handler_init failed\n");
> + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int rot_open(struct file *file)
> +{
> + struct rot_dev *rot = video_drvdata(file);
> + struct rot_ctx *ctx = NULL;
No need to initialize here.
> + int ret;
> +
> + ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> + if (!ctx) {
> + dev_err(rot->dev, "no memory for open context\n");
> + return -ENOMEM;
> + }
> +
> + atomic_inc(&rot->m2m.in_use);
> + ctx->rot_dev = rot;
> +
> + v4l2_fh_init(&ctx->fh, rot->m2m.vfd);
> + ret = rot_add_ctrls(ctx);
> + if (ret)
> + goto err_fh;
> +
> + ctx->fh.ctrl_handler =&ctx->ctrl_handler;
> + file->private_data =&ctx->fh;
> + v4l2_fh_add(&ctx->fh);
> +
> + /* Default color format */
> + ctx->s_frame.rot_fmt =&rot_formats[0];
> + ctx->d_frame.rot_fmt =&rot_formats[0];
> + init_waitqueue_head(&rot->irq.wait);
> + spin_lock_init(&ctx->slock);
> +
> + /* Setup the device context for mem2mem mode. */
> + ctx->m2m_ctx = v4l2_m2m_ctx_init(rot->m2m.m2m_dev, ctx, queue_init);
> + if (IS_ERR(ctx->m2m_ctx)) {
> + ret = -EINVAL;
> + goto err_ctx;
> + }
> +
> + return 0;
> +
> +err_ctx:
> + v4l2_fh_del(&ctx->fh);
> +err_fh:
> + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> + v4l2_fh_exit(&ctx->fh);
> + atomic_dec(&rot->m2m.in_use);
> + kfree(ctx);
> +
> + return ret;
> +}
> +
...
> +static irqreturn_t rot_irq_handler(int irq, void *priv)
> +{
> + struct rot_dev *rot = priv;
> + struct rot_ctx *ctx;
> + struct vb2_buffer *src_vb, *dst_vb;
> + unsigned int irq_src;
> +
> + spin_lock(&rot->slock);
> +
> + clear_bit(DEV_RUN,&rot->state);
> + if (timer_pending(&rot->wdt.timer))
> + del_timer(&rot->wdt.timer);
> +
> + rot_hwget_irq_src(rot,&irq_src);
> + rot_hwset_irq_clear(rot,&irq_src);
> +
> + if (irq_src != ISR_PEND_DONE) {
> + dev_err(rot->dev, "####################\n");
> + dev_err(rot->dev, "set SFR illegally\n");
> + dev_err(rot->dev, "maybe the result is wrong\n");
> + dev_err(rot->dev, "####################\n");
> + rot_dump_register(rot);
> + }
How about following instead:
if (WARN_ON(irq_src != ISR_PEND_DONE),
"Illegal SFR configuration, the result might be wrong\n") {
rot_dump_register(rot);
}
?
> +
> + ctx = v4l2_m2m_get_curr_priv(rot->m2m.m2m_dev);
> + if (!ctx || !ctx->m2m_ctx) {
> + dev_err(rot->dev, "current ctx is NULL\n");
> + goto isr_unlock;
> + }
> +
> + clear_bit(CTX_RUN,&ctx->flags);
> +
> + src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
> + dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
> +
> + if (src_vb&& dst_vb) {
> + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
> + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
> +
> + if (test_bit(DEV_SUSPEND,&rot->state)) {
> + rot_dbg("wake up blocked process by suspend\n");
> + wake_up(&rot->irq.wait);
> + } else {
> + v4l2_m2m_job_finish(rot->m2m.m2m_dev, ctx->m2m_ctx);
> + }
> +
> + /* Wake up from CTX_ABORT state */
> + if (test_and_clear_bit(CTX_ABORT,&ctx->flags))
> + wake_up(&rot->irq.wait);
> +
> + pm_runtime_put(rot->dev);
> + } else {
> + dev_err(rot->dev, "failed to get the buffer done\n");
> + }
> +
> +isr_unlock:
> + spin_unlock(&rot->slock);
> +
> + return IRQ_HANDLED;
> +}
> +
...
> +static void rot_m2m_device_run(void *priv)
> +{
> + struct rot_ctx *ctx = priv;
> + struct rot_frame *s_frame, *d_frame;
> + struct rot_dev *rot;
> + unsigned long flags, tmp;
> + u32 degree = 0, flip = 0;
> +
> + spin_lock_irqsave(&ctx->slock, flags);
> +
> + rot = ctx->rot_dev;
> +
> + if (test_bit(DEV_RUN,&rot->state)) {
> + dev_err(rot->dev, "Rotator is already in progress\n");
> + goto run_unlock;
> + }
> +
> + if (test_bit(DEV_SUSPEND,&rot->state)) {
> + dev_err(rot->dev, "Rotator is in suspend state\n");
> + goto run_unlock;
> + }
> +
> + if (test_bit(CTX_ABORT,&ctx->flags)) {
> + rot_dbg("aborted rot device run\n");
> + goto run_unlock;
> + }
> +
> + pm_runtime_get_sync(ctx->rot_dev->dev);
> +
> + s_frame =&ctx->s_frame;
> + d_frame =&ctx->d_frame;
> +
> + /* Configuration rotator registers */
> + rot_hwset_image_format(rot, s_frame->rot_fmt->pixelformat);
> + rot_mapping_flip(ctx,°ree,&flip);
> + rot_hwset_flip(rot, flip);
> + rot_hwset_rotation(rot, degree);
> +
> + if (ctx->rotation == 90 || ctx->rotation == 270) {
> + tmp = d_frame->pix_mp.height;
> + d_frame->pix_mp.height = d_frame->pix_mp.width;
> + d_frame->pix_mp.width = tmp;
Do you mean:
swap(d_frame->pix_mp.height, d_frame->pix_mp.width);
?
Does it mean setting rotation to 90 or 270 deg changes the output (capture)
format ? Maybe you want to do this swapping on temporary variables, that
would be used to configure the hardware ?
The rotation is a bit tricky, AFAIK the application should swap width/and
height. And the driver should scale an image (change aspect ratio) when
width/height isn't swapped and 90/270 deg rotation is set. Or return an
error when the device doesn't support resizing.
> + }
> +
> + rot_hwset_src_imgsize(rot, s_frame);
> + rot_hwset_dst_imgsize(rot, d_frame);
> +
> + rot_hwset_src_crop(rot,&s_frame->crop);
> + rot_hwset_dst_crop(rot,&d_frame->crop);
> +
> + rot_set_frame_addr(ctx);
> +
> + /* Enable rotator interrupt */
> + rot_hwset_irq_frame_done(rot, 1);
> + rot_hwset_irq_illegal_config(rot, 1);
> +
> + set_bit(DEV_RUN,&rot->state);
> + set_bit(CTX_RUN,&ctx->flags);
> +
> + /* Start rotate operation */
> + rot_hwset_start(rot);
> +
> + /* Start watchdog timer */
> + rot->wdt.timer.expires = jiffies + ROT_TIMEOUT;
> + if (timer_pending(&rot->wdt.timer) == 0)
> + add_timer(&rot->wdt.timer);
> + else
> + mod_timer(&rot->wdt.timer, rot->wdt.timer.expires);
> +
> +run_unlock:
> + spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
...
> +static int rot_suspend(struct device *dev)
> +{
> + struct rot_dev *rot;
How about assigning here directly,
struct rot_dev *rot = dev_get_drvdata(dev);
?
> + int ret = 0;
No need to initialize.
> +
> + rot = dev_get_drvdata(dev);
> + set_bit(DEV_SUSPEND,&rot->state);
> +
> + ret = wait_event_timeout(rot->irq.wait,
> + !test_bit(DEV_RUN,&rot->state), ROT_TIMEOUT);
> + if (ret == 0)
> + dev_err(rot->dev, "wait timeout\n");
> +
> + return 0;
> +}
> +
> +static int rot_resume(struct device *dev)
> +{
> + struct rot_dev *rot;
> +
> + rot = dev_get_drvdata(dev);
struct rot_dev *rot = dev_get_drvdata(dev);
?
> + clear_bit(DEV_SUSPEND,&rot->state);
> +
> + return 0;
> +}
> +
> +static int rot_runtime_suspend(struct device *dev)
> +{
> + struct rot_dev *rot;
> + struct platform_device *pdev;
> +
> + pdev = to_platform_device(dev);
> + rot = (struct rot_dev *)platform_get_drvdata(pdev);
I think, you've forgotten to update that one?
> + clk_disable(rot->clock);
> +
> + return 0;
> +}
> +
> +static int rot_runtime_resume(struct device *dev)
> +{
> + struct rot_dev *rot;
> + struct platform_device *pdev;
> +
> + pdev = to_platform_device(dev);
> + rot = (struct rot_dev *)platform_get_drvdata(pdev);
And here (struct rot_dev *rot = dev_get_drvdata(dev);) ?
> + clk_enable(rot->clock);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops rot_pm_ops = {
> + .suspend = rot_suspend,
> + .resume = rot_resume,
> + .runtime_suspend = rot_runtime_suspend,
> + .runtime_resume = rot_runtime_resume,
> +};
> +
> +static int rot_probe(struct platform_device *pdev)
> +{
> + struct exynos_rot_driverdata *drv_data;
> + struct rot_dev *rot;
> + struct resource *res;
> + int variant_num, ret = 0;
> +
> + dev_info(&pdev->dev, "++%s\n", __func__);
> + drv_data = (struct exynos_rot_driverdata *)
> + platform_get_device_id(pdev)->driver_data;
> +
> + if (pdev->id>= drv_data->nr_dev) {
> + dev_err(&pdev->dev, "Invalid platform device id\n");
> + return -EINVAL;
> + }
If there is always only one device, is this needed ?
> + rot = devm_kzalloc(&pdev->dev, sizeof(struct rot_dev), GFP_KERNEL);
> + if (!rot) {
> + dev_err(&pdev->dev, "no memory for rotator device\n");
> + return -ENOMEM;
> + }
> +
> + rot->dev =&pdev->dev;
> + rot->id = pdev->id;
> + variant_num = (rot->id< 0) ? 0 : rot->id;
> + rot->variant = drv_data->variant[variant_num];
> +
> + spin_lock_init(&rot->slock);
> +
> + /* Get memory resource and map SFR region. */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + rot->regs = devm_request_and_ioremap(&pdev->dev, res);
> + if (rot->regs == NULL) {
> + dev_err(&pdev->dev, "failed to claim register region\n");
> + return -ENOENT;
> + }
> +
> + /* Get IRQ resource and register IRQ handler. */
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "failed to get IRQ resource\n");
> + return -ENXIO;
> + }
> + rot->irq.num = res->start;
> +
> + ret = devm_request_irq(&pdev->dev, rot->irq.num, rot_irq_handler, 0,
I think you can use res->start directly, and remove irq.num, since the interrupt
is now being released dynamically and we don't need to sore the interrupt number.
> + pdev->name, rot);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to install irq\n");
> + return ret;
> + }
> +
> + atomic_set(&rot->wdt.cnt, 0);
> + setup_timer(&rot->wdt.timer, rot_watchdog, (unsigned long)rot);
> +
> + rot->clock = clk_get(rot->dev, "rotator");
> + if (IS_ERR(rot->clock)) {
> + dev_err(&pdev->dev, "failed to get clock for rotator\n");
> + return -ENXIO;
> + }
> +
> + rot->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> + if (IS_ERR(rot->alloc_ctx)) {
> + dev_err(&pdev->dev, "failed to get alloc_ctx\n");
> + ret = -EPERM;
> + goto err;
> + }
> +
> + ret = rot_register_m2m_device(rot);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to register m2m device\n");
> + ret = -EPERM;
> + goto err;
> + }
> +
> +#ifdef CONFIG_PM_RUNTIME
> + pm_runtime_enable(&pdev->dev);
> +#else
> + rot_runtime_resume(&pdev->dev);
> +#endif
Hmm, not very pretty. At least it could be simplified to:
pm_runtime_enable(&pdev->dev);
#ifndef CONFIG_PM_RUNTIME
rot_runtime_resume(&pdev->dev);
#endif
> + dev_info(&pdev->dev, "rotator registered successfully\n");
> +
> + return 0;
> +
> +err:
> + clk_put(rot->clock);
> + return ret;
> +}
> +
> +static int rot_remove(struct platform_device *pdev)
> +{
> + struct rot_dev *rot = (struct rot_dev *)platform_get_drvdata(pdev);
The is no need for casting from void *.
> + clk_put(rot->clock);
> +#ifdef CONFIG_PM_RUNTIME
> + pm_runtime_disable(&pdev->dev);
> +#else
> + rot_runtime_suspend(&pdev->dev);
> +#endif
pm_runtime_disable() is a no op when CONFIG_PM_RUNTIME is disabled.
So it could be changed to:
pm_runtime_disable(&pdev->dev);
#ifndef CONFIG_PM_RUNTIME
rot_runtime_suspend(&pdev->dev);
#endif
> + if (timer_pending(&rot->wdt.timer))
> + del_timer(&rot->wdt.timer);
> +
> + return 0;
> +}
> +
...
> +void rot_dump_register(struct rot_dev *rot)
rot_dump_registers ?
> +{
> + unsigned int tmp, i;
> +
> + rot_dbg("dump rotator registers\n");
> + for (i = 0; i<= ROTATOR_DST; i += 0x4) {
> + tmp = readl(rot->regs + i);
> + rot_dbg("0x%08x: 0x%08x", i, tmp);
> + }
> +}
...
> diff --git a/drivers/media/video/exynos/rotator/rotator.h b/drivers/media/video/exynos/rotator/rotator.h
> new file mode 100644
> index 0000000..4034383
> --- /dev/null
> +++ b/drivers/media/video/exynos/rotator/rotator.h
> @@ -0,0 +1,286 @@
> +/*
> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Header file for Exynos 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 ROTATOR__H_
> +#define ROTATOR__H_
> +
> +#include<linux/delay.h>
> +#include<linux/sched.h>
> +#include<linux/spinlock.h>
> +#include<linux/types.h>
> +#include<linux/videodev2.h>
> +#include<linux/io.h>
> +#include<linux/pm_runtime.h>
Probably this one should be included explicitly only in the .c files.
> +#include<media/videobuf2-core.h>
> +#include<media/v4l2-ctrls.h>
> +#include<media/v4l2-device.h>
> +#include<media/v4l2-mem2mem.h>
> +
> +#include "rotator-regs.h"
> +
> +extern int log_level;
> +
> +#define rot_dbg(fmt, args...) \
> + do { \
> + if (log_level) \
> + printk(KERN_DEBUG "[%s:%d] " \
> + fmt, __func__, __LINE__, ##args); \
> + } while (0)
> +
> +/* Time to wait for frame done interrupt */
> +#define ROT_TIMEOUT (2 * HZ)
> +#define ROT_WDT_CNT 5
> +#define MODULE_NAME "exynos-rot"
> +#define ROT_MAX_DEVS 1
If there is no more than one hardware instance in each SoC maybe it's worth
to drop ROT_MAX_DEVS and all the code around it ?
> +
> +/* Address index */
> +#define ROT_ADDR_RGB 0
> +#define ROT_ADDR_Y 0
> +#define ROT_ADDR_CB 1
> +#define ROT_ADDR_CBCR 1
> +#define ROT_ADDR_CR 2
> +
> +/* Rotator flip direction */
> +#define ROT_NOFLIP (1<< 0)
> +#define ROT_VFLIP (1<< 1)
> +#define ROT_HFLIP (1<< 2)
> +
> +/* Rotator hardware device state */
> +#define DEV_RUN (1<< 0)
> +#define DEV_SUSPEND (1<< 1)
> +
> +/* Rotator m2m context state */
> +#define CTX_PARAMS (1<< 0)
> +#define CTX_STREAMING (1<< 1)
> +#define CTX_RUN (1<< 2)
> +#define CTX_ABORT (1<< 3)
> +#define CTX_SRC (1<< 4)
> +#define CTX_DST (1<< 5)
> +
> +enum rot_irq_src {
> + ISR_PEND_DONE = 8,
> + ISR_PEND_ILLEGAL = 9,
> +};
> +
> +enum rot_status {
> + ROT_IDLE,
> + ROT_RESERVED,
> + ROT_RUNNING,
> + ROT_RUNNING_REMAIN,
> +};
> +
> +/*
> + * struct exynos_rot_size_limit - Rotator variant size information
> + *
> + * @min_x: minimum pixel x size
> + * @min_y: minimum pixel y size
> + * @max_x: maximum pixel x size
> + * @max_y: maximum pixel y size
> + */
> +struct exynos_rot_size_limit {
> + u32 min_x;
> + u32 min_y;
> + u32 max_x;
> + u32 max_y;
> + u32 align;
> +};
> +
> +struct exynos_rot_variant {
> + struct exynos_rot_size_limit limit_rgb565;
> + struct exynos_rot_size_limit limit_rgb888;
> + struct exynos_rot_size_limit limit_yuv422;
> + struct exynos_rot_size_limit limit_yuv420_2p;
> + struct exynos_rot_size_limit limit_yuv420_3p;
> +};
> +
> +/*
> + * struct exynos_rot_driverdata - per device type driver data for init time.
> + *
> + * @variant: the variant information for this driver.
> + * @nr_dev: number of devices available in SoC
> + */
> +struct exynos_rot_driverdata {
> + struct exynos_rot_variant *variant[ROT_MAX_DEVS];
> + int nr_dev;
Probably not needed.
> +};
> +
> +/**
> + * struct rot_fmt - the driver's internal color format data
> + * @name: format description
> + * @pixelformat: the fourcc code for this format, 0 if not applicable
> + * @num_planes: number of physically non-contiguous data planes
> + * @num_comp: number of color components(ex. RGB, Y, Cb, Cr)
> + * @bitperpixel: bits per pixel
> + */
> +struct rot_fmt {
> + char *name;
> + u32 pixelformat;
> + u16 num_planes;
> + u16 num_comp;
> + u32 bitperpixel[VIDEO_MAX_PLANES];
> +};
> +
> +struct rot_addr {
> + dma_addr_t y;
> + dma_addr_t cb;
> + dma_addr_t cr;
> +};
> +
> +/*
> + * struct rot_frame - source/target frame properties
> + * @fmt: buffer format(like virtual screen)
> + * @crop: image size / position
> + * @addr: buffer start address(access using ROT_ADDR_XXX)
> + * @bytesused: image size in bytes (w x h x bpp)
> + * @cacheable: cache property for image address
> + */
> +struct rot_frame {
> + struct rot_fmt *rot_fmt;
> + struct v4l2_pix_format_mplane pix_mp;
> + struct v4l2_rect crop;
> + struct rot_addr addr;
> + unsigned long bytesused[VIDEO_MAX_PLANES];
> + bool cacheable;
It's also unused, what was this needed for (if at all) ?
> +};
> +
> +/*
> + * struct rot_m2m_device - v4l2 memory-to-memory device data
> + * @v4l2_dev: v4l2 device
> + * @vfd: the video device node
> + * @m2m_dev: v4l2 memory-to-memory device data
> + * @in_use: the open count
> + */
> +struct rot_m2m_device {
> + struct v4l2_device v4l2_dev;
> + struct video_device *vfd;
> + struct v4l2_m2m_dev *m2m_dev;
> + atomic_t in_use;
> +};
> +
> +/*
> + * struct rot_irq - Rotator irq information
> + * @num: Rotator interrupt number
> + * @wait: interrupt handler waitqueue
> + */
> +struct rot_irq {
> + int num;
It's unused, you can remove it.
> + wait_queue_head_t wait;
> +};
> +
> +struct rot_wdt {
> + struct timer_list timer;
> + atomic_t cnt;
> +};
> +
> +struct rot_ctx;
> +struct rot_vb2;
> +
> +/*
> + * struct rot_dev - the abstraction for Rotator device
> + * @dev: pointer to the Rotator device
> + * @pdata: pointer to the device platform data
> + * @variant: the IP variant information
> + * @m2m: memory-to-memory V4L2 device information
> + * @id: Rotator device index (0..ROT_MAX_DEVS)
> + * @clock: clock required for Rotator operation
> + * @regs: the mapped hardware registers
> + * @regs_res: the resource claimed for IO registers
> + * @irq: irq information
> + * @ws: work struct
> + * @state: device state flags
> + * @alloc_ctx: videobuf2 memory allocator context
> + * @rot_vb2: videobuf2 memory allocator callbacks
> + * @slock: the spinlock protecting this data structure
> + * @lock: the mutex protecting this data structure
> + * @wdt: watchdog timer information
> + */
> +struct rot_dev {
> + struct device *dev;
> + struct exynos_platform_rot *pdata;
> + struct exynos_rot_variant *variant;
> + struct rot_m2m_device m2m;
> + int id;
> + struct clk *clock;
> + void __iomem *regs;
> + struct rot_irq irq;
> + struct work_struct ws;
irq and ws are not used, are they ?
> + unsigned long state;
> + struct vb2_alloc_ctx *alloc_ctx;
> + const struct rot_vb2 *vb2;
> + spinlock_t slock;
> + struct mutex lock;
> + struct rot_wdt wdt;
> +};
> +
> +/*
> + * rot_ctx - the abstration for Rotator open context
> + * @rot_dev: the Rotator device this context applies to
> + * @m2m_ctx: memory-to-memory device context
> + * @frame: source frame properties
> + * @ctrl_handler: v4l2 controls handler
> + * @fh: v4l2 file handle
> + * @rotation: image clockwise rotation in degrees
> + * @flip: image flip mode
> + * @state: context state flags
> + * @slock: spinlock protecting this data structure
> + */
> +struct rot_ctx {
> + struct rot_dev *rot_dev;
> + struct v4l2_m2m_ctx *m2m_ctx;
> + struct rot_frame s_frame;
> + struct rot_frame d_frame;
> + struct v4l2_ctrl_handler ctrl_handler;
> + struct v4l2_fh fh;
> + int rotation;
> + u32 flip;
> + unsigned long flags;
> + spinlock_t slock;
> +};
...
> +#endif /* ROTATOR__H_ */
Otherwise looks good.
--
Regards,
Sylwester
next prev parent reply other threads:[~2012-03-14 21:21 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-03-14 8:00 [PATCH v2] media: rotator: Add new image rotator driver for EXYNOS Sunyoung Kang
2012-03-14 21:21 ` Sylwester Nawrocki [this message]
2012-03-31 4:58 ` Sunyoung Kang
2012-03-31 11:56 ` Sylwester Nawrocki
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4F610BC4.4080408@gmail.com \
--to=snjw23@gmail.com \
--cc=a.sim@samsung.com \
--cc=jonghun.han@samsung.com \
--cc=kgene.kim@samsung.com \
--cc=khw0178.kim@samsung.com \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@infradead.org \
--cc=sungchun.kang@samsung.com \
--cc=sy0816.kang@samsung.com \
--cc=younglak1004.kim@samsung.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).