From: Sylwester Nawrocki <snjw23@gmail.com>
To: sungchun.kang@samsung.com
Cc: linux-media@vger.kernel.org, mchehab@infradead.org,
jonghun.han@samsung.com, sy0816.kang@samsung.com,
khw0178.kim@samsung.com, laurent.pinchart@ideasonboard.com
Subject: Re: [PATCH] media: gscaler: Add new driver for general scaler
Date: Fri, 09 Mar 2012 00:38:20 +0100 [thread overview]
Message-ID: <4F5942EC.9030502@gmail.com> (raw)
In-Reply-To: <005501cceba8$5dcab2e0$196018a0$%kang@samsung.com>
Hi Hynwoong, Sungchun,
please see my comments below. It's still not a complete review, but
I don't feel like spending any more than 3 hours today on this ;)
On 02/15/2012 07:09 AM, Sungchun Kang wrote:
> This patch add support gscaler device which is a new device
> for scaling and color space conversion on EXYNOS5 SoCs.
>
> This device supports the followings as key feature.
> 1) Input image format
> - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, TILE
> 2) Output image format
> - RGB888/565, YUV422 1P/2P, YUV420 2P/3P, YUV444
> 3) Input rotation
> - 0/90/180/270 degree, X/Y Flip
> 4) Scale ratio
> - 1/16 scale down to 8 scale up
> 5) CSC
> - RGB to YUV / YUV to RGB
> 6) Size
> - 2048 x 2048 for tile or rotation
> - 4800 x 3344 other case
>
> Signed-off-by: Hynwoong Kim<khw0178.kim@samsung.com>
> Signed-off-by: Sungchun Kang<sungchun.kang@samsung.com>
>
> NOTE: This patch is based on
> "media: fimc-lite: Add new driver for camera interface"
These two lines should be below the scissors line ("---"). Please fix this
in other patches as well.
> ---
> drivers/media/video/exynos/Kconfig | 1 +
> drivers/media/video/exynos/Makefile | 2 +-
> drivers/media/video/exynos/gsc/Kconfig | 28 +
> drivers/media/video/exynos/gsc/Makefile | 2 +
> drivers/media/video/exynos/gsc/gsc-capture.c | 1593 ++++++++++++++++++++++++++
> drivers/media/video/exynos/gsc/gsc-core.c | 1315 +++++++++++++++++++++
> drivers/media/video/exynos/gsc/gsc-core.h | 752 ++++++++++++
> drivers/media/video/exynos/gsc/gsc-m2m.c | 696 +++++++++++
> drivers/media/video/exynos/gsc/gsc-output.c | 1034 +++++++++++++++++
> drivers/media/video/exynos/gsc/gsc-regs.c | 671 +++++++++++
> drivers/media/video/exynos/gsc/regs-gsc.h | 224 ++++
> include/media/exynos_gscaler.h | 49 +
> 12 files changed, 6366 insertions(+), 1 deletions(-)
> create mode 100644 drivers/media/video/exynos/gsc/Kconfig
> create mode 100644 drivers/media/video/exynos/gsc/Makefile
> create mode 100644 drivers/media/video/exynos/gsc/gsc-capture.c
> create mode 100644 drivers/media/video/exynos/gsc/gsc-core.c
> create mode 100644 drivers/media/video/exynos/gsc/gsc-core.h
> create mode 100644 drivers/media/video/exynos/gsc/gsc-m2m.c
> create mode 100644 drivers/media/video/exynos/gsc/gsc-output.c
> create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.c
> create mode 100644 drivers/media/video/exynos/gsc/regs-gsc.h
> create mode 100644 include/media/exynos_gscaler.h
>
> diff --git a/drivers/media/video/exynos/Kconfig b/drivers/media/video/exynos/Kconfig
> index a84097d..2bd7e56 100644
> --- a/drivers/media/video/exynos/Kconfig
> +++ b/drivers/media/video/exynos/Kconfig
> @@ -12,6 +12,7 @@ config VIDEO_EXYNOS
> if VIDEO_EXYNOS
> source "drivers/media/video/exynos/mdev/Kconfig"
> source "drivers/media/video/exynos/fimc-lite/Kconfig"
> + source "drivers/media/video/exynos/gsc/Kconfig"
> endif
>
> config MEDIA_EXYNOS
> diff --git a/drivers/media/video/exynos/Makefile b/drivers/media/video/exynos/Makefile
> index 56cb7b2..53d58f6 100644
> --- a/drivers/media/video/exynos/Makefile
> +++ b/drivers/media/video/exynos/Makefile
> @@ -1,4 +1,4 @@
> obj-$(CONFIG_EXYNOS_MEDIA_DEVICE) += mdev/
> obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += fimc-lite/
> -
> +obj-$(CONFIG_VIDEO_EXYNOS_GSCALER) += gsc/
> EXTRA_CLAGS += -Idrivers/media/video
> diff --git a/drivers/media/video/exynos/gsc/Kconfig b/drivers/media/video/exynos/gsc/Kconfig
> new file mode 100644
> index 0000000..8d8b49d
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/Kconfig
> @@ -0,0 +1,28 @@
> +config VIDEO_EXYNOS_GSCALER
> + bool "Exynos G-Scaler driver"
> + depends on VIDEO_EXYNOS
> + select MEDIA_EXYNOS
> + select V4L2_MEM2MEM_DEV
> + default n
The default is "n", so this line is redundant.
> + help
> + This is a v4l2 driver for exynos G-Scaler device.
> +
> +if VIDEO_EXYNOS_GSCALER&& VIDEOBUF2_CMA_PHYS
> +comment "Reserved memory configurations"
> +config VIDEO_SAMSUNG_MEMSIZE_GSC0
> + int "Memory size in kbytes for GSC0"
> + default "5120"
> +
> +config VIDEO_SAMSUNG_MEMSIZE_GSC1
> + int "Memory size in kbytes for GSC1"
> + default "5120"
> +
> +config VIDEO_SAMSUNG_MEMSIZE_GSC2
> + int "Memory size in kbytes for GSC2"
> + default "5120"
> +
> +config VIDEO_SAMSUNG_MEMSIZE_GSC3
> + int "Memory size in kbytes for GSC3"
> + default "5120"
> +endif
Please remove that.....
> diff --git a/drivers/media/video/exynos/gsc/Makefile b/drivers/media/video/exynos/gsc/Makefile
> new file mode 100644
> index 0000000..83ee67d
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/Makefile
> @@ -0,0 +1,2 @@
> +gsc-objs := gsc-core.o gsc-m2m.o gsc-output.o gsc-capture.o gsc-regs.o
> +obj-$(CONFIG_VIDEO_EXYNOS_GSCALER) += gsc.o
> diff --git a/drivers/media/video/exynos/gsc/gsc-capture.c b/drivers/media/video/exynos/gsc/gsc-capture.c
> new file mode 100644
> index 0000000..d7c6ded
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-capture.c
> @@ -0,0 +1,1593 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-capture.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
2011 - 2012 ?
> + * http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/types.h>
> +#include<linux/errno.h>
> +#include<linux/bug.h>
> +#include<linux/interrupt.h>
> +#include<linux/workqueue.h>
> +#include<linux/device.h>
> +#include<linux/platform_device.h>
> +#include<linux/list.h>
> +#include<linux/io.h>
> +#include<linux/slab.h>
> +#include<linux/clk.h>
> +#include<linux/string.h>
> +#include<linux/i2c.h>
> +#include<media/v4l2-ioctl.h>
> +#include<media/exynos_gscaler.h>
> +
> +#include "gsc-core.h"
> +
> +static int gsc_capture_queue_setup(struct vb2_queue *vq,
> + const struct v4l2_format *fmt, unsigned int *num_buffers,
> + unsigned int *num_planes, unsigned int sizes[],
> + void *allocators[])
> +{
> + struct gsc_ctx *ctx = vq->drv_priv;
> + struct gsc_fmt *ffmt = ctx->d_frame.fmt;
> + int i;
> +
> + if (!ffmt)
> + return -EINVAL;
> +
> + *num_planes = ffmt->num_planes;
> +
> + for (i = 0; i< ffmt->num_planes; i++) {
> + sizes[i] = get_plane_size(&ctx->d_frame, i);
> + allocators[i] = ctx->gsc_dev->alloc_ctx;
> + }
> +
> + return 0;
> +}
> +static int gsc_capture_buf_prepare(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *vq = vb->vb2_queue;
> + struct gsc_ctx *ctx = vq->drv_priv;
> + struct gsc_frame *frame =&ctx->d_frame;
> + int i;
> +
> + if (frame->fmt == NULL)
> + return -EINVAL;
> +
> + for (i = 0; i< frame->fmt->num_planes; i++) {
> + unsigned long size = frame->payload[i];
> +
> + if (vb2_plane_size(vb, i)< size) {
> + v4l2_err(ctx->gsc_dev->cap.vfd,
> + "User buffer too small (%ld< %ld)\n",
> + vb2_plane_size(vb, i), size);
> + return -EINVAL;
> + }
> + vb2_set_plane_payload(vb, i, size);
> + }
> +
> + return 0;
> +}
> +
> +int gsc_cap_pipeline_s_stream(struct gsc_dev *gsc, int on)
> +{
> + struct gsc_pipeline *p =&gsc->pipeline;
> + int ret = 0;
> +
> + if ((!p->sensor || !p->flite)&& (!p->disp))
> + return -ENODEV;
> +
> + if (on) {
> + ret = v4l2_subdev_call(p->sd_gsc, video, s_stream, 1);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + if (p->disp) {
> + ret = v4l2_subdev_call(p->disp, video, s_stream, 1);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + } else {
> + ret = v4l2_subdev_call(p->flite, video, s_stream, 1);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + ret = v4l2_subdev_call(p->csis, video, s_stream, 1);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + ret = v4l2_subdev_call(p->sensor, video, s_stream, 1);
This is getting repetitive, maybe we need some sort of list operations here.
I need to think some more about it.
> + }
> + } else {
> + ret = v4l2_subdev_call(p->sd_gsc, video, s_stream, 0);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + if (p->disp) {
> + ret = v4l2_subdev_call(p->disp, video, s_stream, 0);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + } else {
> + ret = v4l2_subdev_call(p->sensor, video, s_stream, 0);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + ret = v4l2_subdev_call(p->csis, video, s_stream, 0);
> + if (ret< 0&& ret != -ENOIOCTLCMD)
> + return ret;
> + ret = v4l2_subdev_call(p->flite, video, s_stream, 0);
> + }
> + }
> +
> + return ret == -ENOIOCTLCMD ? 0 : ret;
> +}
> +
> +static int gsc_capture_set_addr(struct vb2_buffer *vb)
> +{
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + int ret;
> +
> + ret = gsc_prepare_addr(ctx, vb,&ctx->d_frame,&ctx->d_frame.addr);
> + if (ret) {
> + gsc_err("Prepare G-Scaler address failed\n");
Would you mind using the v4l2_dbg/err/.. macros instead where possible ?
> + return -EINVAL;
> + }
> +
> + gsc_hw_set_output_addr(gsc,&ctx->d_frame.addr, vb->v4l2_buf.index);
> +
> + return 0;
> +}
> +
> +static void gsc_capture_buf_queue(struct vb2_buffer *vb)
> +{
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + struct gsc_capture_device *cap =&gsc->cap;
> + int min_bufs, ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&gsc->slock, flags);
> + ret = gsc_capture_set_addr(vb);
> + if (ret)
> + gsc_err("Failed to prepare output addr");
> +
> + if (!test_bit(ST_CAPT_SUSPENDED,&gsc->state)) {
> + gsc_info("buf_index : %d", vb->v4l2_buf.index);
> + gsc_hw_set_output_buf_masking(gsc, vb->v4l2_buf.index, 0);
> + }
> +
> + min_bufs = cap->reqbufs_cnt> 1 ? 2 : 1;
> +
> + if (vb2_is_streaming(&cap->vbq)&&
> + (gsc_hw_get_nr_unmask_bits(gsc)>= min_bufs)&&
> + !test_bit(ST_CAPT_STREAM,&gsc->state)) {
> + if (!test_and_set_bit(ST_CAPT_PIPE_STREAM,&gsc->state)) {
> + spin_unlock_irqrestore(&gsc->slock, flags);
> + gsc_cap_pipeline_s_stream(gsc, 1);
> + return;
> + }
> +
> + if (!test_bit(ST_CAPT_STREAM,&gsc->state)) {
> + gsc_info("G-Scaler h/w enable control");
> + gsc_hw_enable_control(gsc, true);
> + set_bit(ST_CAPT_STREAM,&gsc->state);
> + }
> + }
> + spin_unlock_irqrestore(&gsc->slock, flags);
> +
> + return;
> +}
> +
> +static int gsc_capture_get_scaler_factor(u32 src, u32 tar, u32 *ratio)
> +{
> + u32 sh = 3;
> + tar *= 4;
> + if (tar>= src) {
> + *ratio = 1;
> + return 0;
> + }
> +
> + while (--sh) {
> + u32 tmp = 1<< sh;
> + if (src>= tar * tmp)
> + *ratio = sh;
> + }
> + return 0;
> +}
> +
> +static int gsc_capture_scaler_info(struct gsc_ctx *ctx)
> +{
> + struct gsc_frame *s_frame =&ctx->s_frame;
> + struct gsc_frame *d_frame =&ctx->d_frame;
> + struct gsc_scaler *sc =&ctx->scaler;
> +
> + gsc_capture_get_scaler_factor(s_frame->crop.width, d_frame->crop.width,
> + &sc->pre_hratio);
> + gsc_capture_get_scaler_factor(s_frame->crop.height, d_frame->crop.width,
> + &sc->pre_vratio);
> +
> + sc->main_hratio = (s_frame->crop.width<< 16) / d_frame->crop.width;
> + sc->main_vratio = (s_frame->crop.height<< 16) / d_frame->crop.height;
> +
> + gsc_info("src width : %d, src height : %d, dst width : %d,\
> + dst height : %d", s_frame->crop.width, s_frame->crop.height,\
> + d_frame->crop.width, d_frame->crop.height);
> + gsc_info("pre_hratio : 0x%x, pre_vratio : 0x%x, main_hratio : 0x%lx,\
> + main_vratio : 0x%lx", sc->pre_hratio,\
> + sc->pre_vratio, sc->main_hratio, sc->main_vratio);
> +
> + return 0;
> +}
> +
> +static int gsc_capture_subdev_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct gsc_dev *gsc = v4l2_get_subdevdata(sd);
> + struct gsc_capture_device *cap =&gsc->cap;
> + struct gsc_ctx *ctx = cap->ctx;
> +
> + gsc_info("");
> +
> + gsc_hw_set_frm_done_irq_mask(gsc, false);
> + gsc_hw_set_overflow_irq_mask(gsc, false);
> + gsc_hw_set_one_frm_mode(gsc, false);
> + gsc_hw_set_gsc_irq_enable(gsc, true);
> +
> + if (gsc->pipeline.disp)
> + gsc_hw_set_sysreg_writeback(ctx);
> + else
> + gsc_hw_set_sysreg_camif(true);
> +
> + gsc_hw_set_input_path(ctx);
> + gsc_hw_set_in_size(ctx);
> + gsc_hw_set_in_image_format(ctx);
> + gsc_hw_set_output_path(ctx);
> + gsc_hw_set_out_size(ctx);
> + gsc_hw_set_out_image_format(ctx);
> + gsc_hw_set_global_alpha(ctx);
> +
> + gsc_capture_scaler_info(ctx);
> + gsc_hw_set_prescaler(ctx);
> + gsc_hw_set_mainscaler(ctx);
> +
> + set_bit(ST_CAPT_PEND,&gsc->state);
> +
> + gsc_hw_enable_control(gsc, true);
> + set_bit(ST_CAPT_STREAM,&gsc->state);
> +
> + return 0;
> +
> +}
> +
> +static int gsc_capture_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> + struct gsc_ctx *ctx = q->drv_priv;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + struct gsc_capture_device *cap =&gsc->cap;
> + int min_bufs;
> +
> + gsc_hw_set_sw_reset(gsc);
> + gsc_wait_reset(gsc);
> + gsc_hw_set_output_buf_mask_all(gsc);
> +
> + min_bufs = cap->reqbufs_cnt> 1 ? 2 : 1;
> + if ((gsc_hw_get_nr_unmask_bits(gsc)>= min_bufs)&&
> + !test_bit(ST_CAPT_STREAM,&gsc->state)) {
> + if (!test_and_set_bit(ST_CAPT_PIPE_STREAM,&gsc->state)) {
> + gsc_info("");
> + gsc_cap_pipeline_s_stream(gsc, 1);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int gsc_capture_state_cleanup(struct gsc_dev *gsc)
> +{
> + unsigned long flags;
> + bool streaming;
> +
> + spin_lock_irqsave(&gsc->slock, flags);
> + streaming = gsc->state& (1<< ST_CAPT_PIPE_STREAM);
> +
> + gsc->state&= ~(1<< ST_CAPT_RUN | 1<< ST_CAPT_STREAM |
> + 1<< ST_CAPT_PIPE_STREAM | 1<< ST_CAPT_PEND);
> +
> + set_bit(ST_CAPT_SUSPENDED,&gsc->state);
> + spin_unlock_irqrestore(&gsc->slock, flags);
> +
> + if (streaming)
> + return gsc_cap_pipeline_s_stream(gsc, 0);
> + else
> + return 0;
> +}
> +
> +static int gsc_cap_stop_capture(struct gsc_dev *gsc)
> +{
> + int ret;
Would be good to have en empty line here.
> + if (!gsc_cap_active(gsc)) {
> + gsc_warn("already stopped\n");
> + return 0;
> + }
> + gsc_info("G-Scaler h/w disable control");
> + gsc_hw_enable_control(gsc, false);
> + clear_bit(ST_CAPT_STREAM,&gsc->state);
> + ret = gsc_wait_operating(gsc);
> + if (ret) {
> + gsc_err("GSCALER_OP_STATUS is operating\n");
> + return ret;
> + }
> +
> + return gsc_capture_state_cleanup(gsc);
> +}
> +
> +static int gsc_capture_stop_streaming(struct vb2_queue *q)
> +{
> + struct gsc_ctx *ctx = q->drv_priv;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> +
> + if (!gsc_cap_active(gsc))
> + return -EINVAL;
> +
> + return gsc_cap_stop_capture(gsc);
> +}
> +
> +static struct vb2_ops gsc_capture_qops = {
> + .queue_setup = gsc_capture_queue_setup,
> + .buf_prepare = gsc_capture_buf_prepare,
> + .buf_queue = gsc_capture_buf_queue,
> + .wait_prepare = gsc_unlock,
> + .wait_finish = gsc_lock,
> + .start_streaming = gsc_capture_start_streaming,
> + .stop_streaming = gsc_capture_stop_streaming,
> +};
> +
> +/*
> + * The video node ioctl operations
> + */
> +static int gsc_vidioc_querycap_capture(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
> + strncpy(cap->card, gsc->pdev->name, sizeof(cap->card) - 1);
> + cap->bus_info[0] = 0;
> + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
> +
> + return 0;
> +}
> +
> +static int gsc_capture_enum_fmt_mplane(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + return gsc_enum_fmt_mplane(f);
> +}
> +
> +static int gsc_capture_try_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
The check is redundant, it already done in the v4l core.
> +
> + return gsc_try_fmt_mplane(gsc->cap.ctx, f);
> +}
> +
> +static int gsc_capture_s_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> + struct gsc_frame *frame;
> + struct v4l2_pix_format_mplane *pix;
> + int i, ret = 0;
> +
> + ret = gsc_capture_try_fmt_mplane(file, fh, f);
> + if (ret)
> + return ret;
> +
> + if (vb2_is_streaming(&gsc->cap.vbq)) {
> + gsc_err("queue (%d) busy", f->type);
> + return -EBUSY;
> + }
> +
> + frame =&ctx->d_frame;
> +
> + pix =&f->fmt.pix_mp;
> + frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
> + if (!frame->fmt)
> + return -EINVAL;
> +
> + for (i = 0; i< frame->fmt->nr_comp; i++)
> + frame->payload[i] =
> + pix->plane_fmt[i].bytesperline * pix->height;
> +
> + gsc_set_frame_size(frame, pix->width, pix->height);
> +
> + gsc_info("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
> +
> + return 0;
> +}
> +
> +static int gsc_capture_g_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
Ditto.
> +
> + return gsc_g_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_capture_reqbufs(struct file *file, void *priv,
> + struct v4l2_requestbuffers *reqbufs)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_capture_device *cap =&gsc->cap;
> + struct gsc_frame *frame;
> + int ret;
> +
> + frame = ctx_get_frame(cap->ctx, reqbufs->type);
> +
> + ret = vb2_reqbufs(&cap->vbq, reqbufs);
> + if (!ret)
> + cap->reqbufs_cnt = reqbufs->count;
> +
> + return ret;
> +
> +}
> +
> +static int gsc_capture_querybuf(struct file *file, void *priv,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_capture_device *cap =&gsc->cap;
> +
> + return vb2_querybuf(&cap->vbq, buf);
> +}
> +
> +static int gsc_capture_qbuf(struct file *file, void *priv,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_capture_device *cap =&gsc->cap;
> +
> + return vb2_qbuf(&cap->vbq, buf);
> +}
> +
> +static int gsc_capture_dqbuf(struct file *file, void *priv,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + return vb2_dqbuf(&gsc->cap.vbq, buf,
> + file->f_flags& O_NONBLOCK);
> +}
> +
> +static int gsc_capture_cropcap(struct file *file, void *fh,
> + struct v4l2_cropcap *cr)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> +
> + if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
> +
> + cr->bounds.left = 0;
> + cr->bounds.top = 0;
> + cr->bounds.width = ctx->d_frame.f_width;
> + cr->bounds.height = ctx->d_frame.f_height;
> + cr->defrect = cr->bounds;
> +
> + return 0;
> +}
I suggest switching to the selection API.
http://git.infradead.org/users/kmpark/linux-2.6-samsung/commitdiff/06a208bf5925df449a79b600bd33954e1d31a1d3
> +
> +static int gsc_capture_enum_input(struct file *file, void *priv,
> + struct v4l2_input *i)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct exynos_platform_gscaler *pdata = gsc->pdata;
> + struct exynos_isp_info *isp_info;
> +
> + if (i->index>= MAX_CAMIF_CLIENTS)
> + return -EINVAL;
> +
> + isp_info = pdata->isp_info[i->index];
> + if (isp_info == NULL)
> + return -EINVAL;
> +
> + i->type = V4L2_INPUT_TYPE_CAMERA;
> +
> + strncpy(i->name, isp_info->board_info->type, 32);
> +
> + return 0;
> +}
> +
> +static int gsc_capture_s_input(struct file *file, void *priv, unsigned int i)
> +{
> + return i == 0 ? 0 : -EINVAL;
> +}
> +
> +static int gsc_capture_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> + *i = 0;
> + return 0;
> +}
I think you can drop those enum/s/g_input ioctls. They don't do anything useful.
Suport for VIDIOC_ENUM/S/G_INPUT ioctls should be added at a user space library
level.
> +
> +int gsc_capture_ctrls_create(struct gsc_dev *gsc)
> +{
> + int ret;
> +
> + if (WARN_ON(gsc->cap.ctx == NULL))
> + return -ENXIO;
> + if (gsc->cap.ctx->ctrls_rdy)
> + return 0;
> + ret = gsc_ctrls_create(gsc->cap.ctx);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +void gsc_cap_pipeline_prepare(struct gsc_dev *gsc, struct media_entity *me)
> +{
> + struct media_entity_graph graph;
> + struct v4l2_subdev *sd;
> +
> + media_entity_graph_walk_start(&graph, me);
> +
> + while ((me = media_entity_graph_walk_next(&graph))) {
> + gsc_info("me->name : %s", me->name);
> + if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV)
> + continue;
> + sd = media_entity_to_v4l2_subdev(me);
> +
> + switch (sd->grp_id) {
> + case GSC_CAP_GRP_ID:
> + gsc->pipeline.sd_gsc = sd;
> + break;
> + case FLITE_GRP_ID:
> + gsc->pipeline.flite = sd;
> + break;
> + case SENSOR_GRP_ID:
> + gsc->pipeline.sensor = sd;
> + break;
> + case CSIS_GRP_ID:
> + gsc->pipeline.csis = sd;
> + break;
> + case FIMD_GRP_ID:
> + gsc->pipeline.disp = sd;
> + break;
> + default:
> + gsc_err("Unsupported group id");
> + break;
> + }
> + }
> +
> + gsc_info("gsc->pipeline.sd_gsc : 0x%p", gsc->pipeline.sd_gsc);
> + gsc_info("gsc->pipeline.flite : 0x%p", gsc->pipeline.flite);
> + gsc_info("gsc->pipeline.sensor : 0x%p", gsc->pipeline.sensor);
> + gsc_info("gsc->pipeline.csis : 0x%p", gsc->pipeline.csis);
> + gsc_info("gsc->pipeline.disp : 0x%p", gsc->pipeline.disp);
> +}
> +
> +static int __subdev_set_power(struct v4l2_subdev *sd, int on)
> +{
> + int *use_count;
> + int ret;
> +
> + if (sd == NULL)
> + return -ENXIO;
> +
> + use_count =&sd->entity.use_count;
> + if (on&& (*use_count)++> 0)
> + return 0;
> + else if (!on&& (*use_count == 0 || --(*use_count)> 0))
> + return 0;
> + ret = v4l2_subdev_call(sd, core, s_power, on);
> +
> + return ret != -ENOIOCTLCMD ? ret : 0;
> +}
> +
> +int gsc_cap_pipeline_s_power(struct gsc_dev *gsc, int state)
> +{
> + int ret = 0;
> +
> + if (!gsc->pipeline.sensor || !gsc->pipeline.flite)
> + return -ENXIO;
> +
> + if (state) {
> + ret = __subdev_set_power(gsc->pipeline.flite, 1);
> + if (ret&& ret != -ENXIO)
> + return ret;
> + ret = __subdev_set_power(gsc->pipeline.csis, 1);
> + if (ret&& ret != -ENXIO)
> + return ret;
> + ret = __subdev_set_power(gsc->pipeline.sensor, 1);
> + } else {
> + ret = __subdev_set_power(gsc->pipeline.flite, 0);
> + if (ret&& ret != -ENXIO)
> + return ret;
> + ret = __subdev_set_power(gsc->pipeline.sensor, 0);
> + if (ret&& ret != -ENXIO)
> + return ret;
> + ret = __subdev_set_power(gsc->pipeline.csis, 0);
> + }
> + return ret == -ENXIO ? 0 : ret;
> +}
> +
> +static void gsc_set_cam_clock(struct gsc_dev *gsc, bool on)
> +{
> + struct v4l2_subdev *sd = NULL;
> + struct gsc_sensor_info *s_info = NULL;
> +
> + if (gsc->pipeline.sensor) {
> + sd = gsc->pipeline.sensor;
> + s_info = v4l2_get_subdev_hostdata(sd);
> + }
> + if (on) {
> + clk_enable(gsc->clock);
> + if (gsc->pipeline.sensor)
> + clk_enable(s_info->camclk);
> + } else {
> + clk_disable(gsc->clock);
> + if (gsc->pipeline.sensor)
> + clk_disable(s_info->camclk);
> + }
> +}
> +
> +static int __gsc_cap_pipeline_initialize(struct gsc_dev *gsc,
> + struct media_entity *me, bool prep)
> +{
> + int ret = 0;
> +
> + if (prep)
> + gsc_cap_pipeline_prepare(gsc, me);
> + if ((!gsc->pipeline.sensor || !gsc->pipeline.flite)&&
> + !gsc->pipeline.disp)
> + return -EINVAL;
> +
> + gsc_set_cam_clock(gsc, true);
> +
> + if (gsc->pipeline.sensor&& gsc->pipeline.flite)
> + ret = gsc_cap_pipeline_s_power(gsc, 1);
> +
> + return ret;
> +}
> +
> +int gsc_cap_pipeline_initialize(struct gsc_dev *gsc, struct media_entity *me,
> + bool prep)
> +{
> + int ret;
> +
> + mutex_lock(&me->parent->graph_mutex);
> + ret = __gsc_cap_pipeline_initialize(gsc, me, prep);
> + mutex_unlock(&me->parent->graph_mutex);
> +
> + return ret;
> +}
> +
> +static int gsc_capture_open(struct file *file)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + int ret = v4l2_fh_open(file);
> +
> + if (ret)
> + return ret;
> +
> + if (gsc_m2m_opened(gsc) || gsc_out_opened(gsc) || gsc_cap_opened(gsc)) {
Can't you just call v4l2_fh_open() after this check ?
> + v4l2_fh_release(file);
> + return -EBUSY;
> + }
> +
> + set_bit(ST_CAPT_OPEN,&gsc->state);
> + pm_runtime_get_sync(&gsc->pdev->dev);
> +
> + if (++gsc->cap.refcnt == 1) {
> + ret = gsc_cap_pipeline_initialize(gsc,&gsc->cap.vfd->entity, true);
> + if (ret< 0) {
> + gsc_err("gsc pipeline initialization failed\n");
> + goto err;
> + }
> +
> + ret = gsc_capture_ctrls_create(gsc);
If you don't inherit controls from the sensor or other subdevs on the pipeline
reconnection this can and should be done in probe().
> + if (ret) {
> + gsc_err("failed to create controls\n");
> + goto err;
> + }
> + }
> +
> + gsc_info("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> + return 0;
> +
> +err:
> + pm_runtime_put_sync(&gsc->pdev->dev);
> + v4l2_fh_release(file);
> + clear_bit(ST_CAPT_OPEN,&gsc->state);
> + return ret;
> +}
> +
> +int __gsc_cap_pipeline_shutdown(struct gsc_dev *gsc)
> +{
> + int ret = 0;
> +
> + if (gsc->pipeline.sensor&& gsc->pipeline.flite)
> + ret = gsc_cap_pipeline_s_power(gsc, 0);
> +
> + if (ret&& ret != -ENXIO)
> + gsc_set_cam_clock(gsc, false);
> +
> + return ret == -ENXIO ? 0 : ret;
> +}
> +
> +int gsc_cap_pipeline_shutdown(struct gsc_dev *gsc)
> +{
> + struct media_entity *me =&gsc->cap.vfd->entity;
> + int ret;
> +
> + mutex_lock(&me->parent->graph_mutex);
> + ret = __gsc_cap_pipeline_shutdown(gsc);
> + mutex_unlock(&me->parent->graph_mutex);
> +
> + return ret;
> +}
> +static int gsc_capture_close(struct file *file)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + gsc_info("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> + if (--gsc->cap.refcnt == 0) {
> + clear_bit(ST_CAPT_OPEN,&gsc->state);
> + gsc_info("G-Scaler h/w disable control");
> + gsc_hw_enable_control(gsc, false);
> + clear_bit(ST_CAPT_STREAM,&gsc->state);
> + gsc_cap_pipeline_shutdown(gsc);
> + clear_bit(ST_CAPT_SUSPENDED,&gsc->state);
> + }
> +
> + pm_runtime_put(&gsc->pdev->dev);
> +
> + if (gsc->cap.refcnt == 0) {
> + vb2_queue_release(&gsc->cap.vbq);
> + gsc_ctrls_delete(gsc->cap.ctx);
> + }
> +
> + return v4l2_fh_release(file);
> +}
> +
> +static unsigned int gsc_capture_poll(struct file *file,
> + struct poll_table_struct *wait)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + return vb2_poll(&gsc->cap.vbq, file, wait);
> +}
> +
> +static int gsc_capture_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + return vb2_mmap(&gsc->cap.vbq, vma);
> +}
> +
> +static int gsc_cap_link_validate(struct gsc_dev *gsc)
> +{
> + struct gsc_capture_device *cap =&gsc->cap;
> + struct v4l2_subdev_format sink_fmt, src_fmt;
> + struct v4l2_subdev *sd;
> + struct media_pad *pad;
> + int ret;
> +
> + /* Get the source pad connected with gsc-video */
> + pad = media_entity_remote_source(&cap->vd_pad);
> + if (pad == NULL)
> + return -EPIPE;
> + /* Get the subdev of source pad */
> + sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> + while (1) {
> + /* Find sink pad of the subdev*/
> + pad =&sd->entity.pads[0];
> + if (!(pad->flags& MEDIA_PAD_FL_SINK))
> + break;
> + if (sd == cap->sd_cap) {
> + struct gsc_frame *gf =&cap->ctx->s_frame;
> + sink_fmt.format.width = gf->crop.width;
> + sink_fmt.format.height = gf->crop.height;
> + sink_fmt.format.code = gf->fmt ? gf->fmt->mbus_code : 0;
> + } else {
> + sink_fmt.pad = pad->index;
> + sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL,&sink_fmt);
> + if (ret< 0&& ret != -ENOIOCTLCMD) {
> + gsc_err("failed %s subdev get_fmt", sd->name);
> + return -EPIPE;
> + }
> + }
> + gsc_info("sink sd name : %s", sd->name);
> + /* Get the source pad connected with remote sink pad */
> + pad = media_entity_remote_source(pad);
> + if (pad == NULL ||
> + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> + break;
> +
> + /* Get the subdev of source pad */
> + sd = media_entity_to_v4l2_subdev(pad->entity);
> + gsc_info("source sd name : %s", sd->name);
> +
> + src_fmt.pad = pad->index;
> + src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL,&src_fmt);
> + if (ret< 0&& ret != -ENOIOCTLCMD) {
> + gsc_err("failed %s subdev get_fmt", sd->name);
> + return -EPIPE;
> + }
> +
> + gsc_info("src_width : %d, src_height : %d, src_code : %d",
> + src_fmt.format.width, src_fmt.format.height,
> + src_fmt.format.code);
> + gsc_info("sink_width : %d, sink_height : %d, sink_code : %d",
> + sink_fmt.format.width, sink_fmt.format.height,
> + sink_fmt.format.code);
> +
> + if (src_fmt.format.width != sink_fmt.format.width ||
> + src_fmt.format.height != sink_fmt.format.height ||
> + src_fmt.format.code != sink_fmt.format.code) {
> + gsc_err("mismatch sink and source");
> + return -EPIPE;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int gsc_capture_streamon(struct file *file, void *priv,
> + enum v4l2_buf_type type)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_pipeline *p =&gsc->pipeline;
> + int ret;
> +
> + if (gsc_cap_active(gsc))
> + return -EBUSY;
> +
> + if (p->disp) {
> + media_entity_pipeline_start(&p->disp->entity, p->pipe);
> + } else if (p->sensor) {
> + media_entity_pipeline_start(&p->sensor->entity, p->pipe);
> + } else {
> + gsc_err("Error pipeline");
> + return -EPIPE;
> + }
> +
> + ret = gsc_cap_link_validate(gsc);
> + if (ret)
> + return ret;
> +
> + return vb2_streamon(&gsc->cap.vbq, type);
> +}
> +
> +static int gsc_capture_streamoff(struct file *file, void *priv,
> + enum v4l2_buf_type type)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct v4l2_subdev *sd = gsc->pipeline.sensor;
> + int ret;
> +
> + ret = vb2_streamoff(&gsc->cap.vbq, type);
> + if (ret == 0)
> + media_entity_pipeline_stop(&sd->entity);
> + return ret;
> +}
> +
> +static struct v4l2_subdev *gsc_cap_remote_subdev(struct gsc_dev *gsc, u32 *pad)
> +{
> + struct media_pad *remote;
> +
> + remote = media_entity_remote_source(&gsc->cap.vd_pad);
> +
> + if (remote == NULL ||
> + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> + return NULL;
> +
> + if (pad)
> + *pad = remote->index;
> +
> + return media_entity_to_v4l2_subdev(remote->entity);
> +}
> +
> +static int gsc_capture_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct v4l2_subdev_format format;
> + struct v4l2_subdev *subdev;
> + u32 pad;
> + int ret;
> +
> + subdev = gsc_cap_remote_subdev(gsc,&pad);
> + if (subdev == NULL)
> + return -EINVAL;
> +
> + /* Try the get crop operation first and fallback to get format if not
> + * implemented.
> + */
> + ret = v4l2_subdev_call(subdev, video, g_crop, crop);
> + if (ret != -ENOIOCTLCMD)
> + return ret;
> +
> + format.pad = pad;
> + format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL,&format);
> + if (ret< 0)
> + return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +
> + crop->c.left = 0;
> + crop->c.top = 0;
> + crop->c.width = format.format.width;
> + crop->c.height = format.format.height;
> +
> + return 0;
> +}
> +
> +static int gsc_capture_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct v4l2_subdev *subdev;
> + int ret;
> +
> + subdev = gsc_cap_remote_subdev(gsc, NULL);
> + if (subdev == NULL)
> + return -EINVAL;
> +
> + ret = v4l2_subdev_call(subdev, video, s_crop, crop);
s_crop should only be changing parameter of the vide node (DMA engine).
Subdevs are accessed directly through their device nodes. Similar applies
to g_crop.
> +
> + return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +}
> +
> +static const struct v4l2_ioctl_ops gsc_capture_ioctl_ops = {
> + .vidioc_querycap = gsc_vidioc_querycap_capture,
> +
> + .vidioc_enum_fmt_vid_cap_mplane = gsc_capture_enum_fmt_mplane,
> + .vidioc_try_fmt_vid_cap_mplane = gsc_capture_try_fmt_mplane,
> + .vidioc_s_fmt_vid_cap_mplane = gsc_capture_s_fmt_mplane,
> + .vidioc_g_fmt_vid_cap_mplane = gsc_capture_g_fmt_mplane,
> +
> + .vidioc_reqbufs = gsc_capture_reqbufs,
> + .vidioc_querybuf = gsc_capture_querybuf,
> +
> + .vidioc_qbuf = gsc_capture_qbuf,
> + .vidioc_dqbuf = gsc_capture_dqbuf,
> +
> + .vidioc_streamon = gsc_capture_streamon,
> + .vidioc_streamoff = gsc_capture_streamoff,
> +
> + .vidioc_g_crop = gsc_capture_g_crop,
> + .vidioc_s_crop = gsc_capture_s_crop,
> + .vidioc_cropcap = gsc_capture_cropcap,
> +
> + .vidioc_enum_input = gsc_capture_enum_input,
> + .vidioc_s_input = gsc_capture_s_input,
> + .vidioc_g_input = gsc_capture_g_input,
These three are good enough for removal, as I noted above.
> +};
> +
> +static const struct v4l2_file_operations gsc_capture_fops = {
> + .owner = THIS_MODULE,
> + .open = gsc_capture_open,
> + .release = gsc_capture_close,
> + .poll = gsc_capture_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = gsc_capture_mmap,
> +};
> +
> +/*
> + * __gsc_cap_get_format - helper function for getting gscaler format
> + * @res : pointer to resizer private structure
> + * @pad : pad number
> + * @fh : V4L2 subdev file handle
> + * @which : wanted subdev format
> + * return zero
> + */
> +static struct v4l2_mbus_framefmt *__gsc_cap_get_format(struct gsc_dev *gsc,
> + struct v4l2_subdev_fh *fh, unsigned int pad,
> + enum v4l2_subdev_format_whence which)
> +{
> + if (which == V4L2_SUBDEV_FORMAT_TRY)
> + return v4l2_subdev_get_try_format(fh, pad);
> + else
> + return&gsc->cap.mbus_fmt[pad];
> +}
> +
> +static void gsc_cap_check_limit_size(struct gsc_dev *gsc, unsigned int pad,
> + struct v4l2_mbus_framefmt *fmt)
> +{
> + struct gsc_variant *variant = gsc->variant;
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> + u32 min_w, min_h, max_w, max_h;
> +
> + switch (pad) {
> + case GSC_PAD_SINK:
> + if (gsc_cap_opened(gsc)&&
> + (ctx->gsc_ctrls.rotate->val == 90 ||
> + ctx->gsc_ctrls.rotate->val == 270)) {
> + min_w = variant->pix_min->real_w;
> + min_h = variant->pix_min->real_h;
> + max_w = variant->pix_max->real_rot_en_w;
> + max_h = variant->pix_max->real_rot_en_h;
> + } else {
> + min_w = variant->pix_min->real_w;
> + min_h = variant->pix_min->real_h;
> + max_w = variant->pix_max->real_rot_dis_w;
> + max_h = variant->pix_max->real_rot_dis_h;
> + }
> + break;
> +
> + case GSC_PAD_SOURCE:
> + min_w = variant->pix_min->target_rot_dis_w;
> + min_h = variant->pix_min->target_rot_dis_h;
> + max_w = variant->pix_max->target_rot_dis_w;
> + max_h = variant->pix_max->target_rot_dis_h;
> + break;
> + }
> +
> + fmt->width = clamp_t(u32, fmt->width, min_w, max_w);
> + fmt->height = clamp_t(u32, fmt->height , min_h, max_h);
> +}
> +
> +static void gsc_cap_try_format(struct gsc_dev *gsc,
> + struct v4l2_subdev_fh *fh, unsigned int pad,
> + struct v4l2_mbus_framefmt *fmt,
> + enum v4l2_subdev_format_whence which)
> +{
> + struct gsc_fmt *gfmt;
> +
> + gfmt = find_fmt(NULL,&fmt->code, 0);
> + WARN_ON(!gfmt);
> +
> + if (pad == GSC_PAD_SINK) {
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> + struct gsc_frame *frame =&ctx->s_frame;
> +
> + frame->fmt = gfmt;
> + }
> +
> + gsc_cap_check_limit_size(gsc, pad, fmt);
> +
> + fmt->colorspace = V4L2_COLORSPACE_JPEG;
> + fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +static int gsc_capture_subdev_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct gsc_dev *gsc = v4l2_get_subdevdata(sd);
> + struct v4l2_mbus_framefmt *mf;
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> + struct gsc_frame *frame;
> +
> + mf = __gsc_cap_get_format(gsc, fh, fmt->pad, fmt->which);
> + if (mf == NULL)
> + return -EINVAL;
> +
> + gsc_cap_try_format(gsc, fh, fmt->pad,&fmt->format, fmt->which);
> + *mf = fmt->format;
> +
> + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
> + return 0;
> +
> + frame = gsc_capture_get_frame(ctx, fmt->pad);
> +
> + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> + frame->crop.left = 0;
> + frame->crop.top = 0;
> + frame->f_width = mf->width;
> + frame->f_height = mf->height;
> + frame->crop.width = mf->width;
> + frame->crop.height = mf->height;
> + }
> + gsc_dbg("offs_h : %d, offs_v : %d, f_width : %d, f_height :%d,\
> + width : %d, height : %d", frame->crop.left,\
> + frame->crop.top, frame->f_width,
> + frame->f_height,\
I don't know where the trailing "\" came from, but those certainly shouldn't
be here. Please remove.
> + frame->crop.width, frame->crop.height);
> +
> + return 0;
> +}
> +
> +static int gsc_capture_subdev_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct gsc_dev *gsc = v4l2_get_subdevdata(sd);
> + struct v4l2_mbus_framefmt *mf;
> +
> + mf = __gsc_cap_get_format(gsc, fh, fmt->pad, fmt->which);
> + if (mf == NULL)
> + return -EINVAL;
> +
> + fmt->format = *mf;
> +
> + return 0;
> +}
> +
> +static int __gsc_cap_get_crop(struct gsc_dev *gsc, struct v4l2_subdev_fh *fh,
> + unsigned int pad, enum v4l2_subdev_format_whence which,
> + struct v4l2_rect *crop)
> +{
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> + struct gsc_frame *frame = gsc_capture_get_frame(ctx, pad);
> +
> + if (which == V4L2_SUBDEV_FORMAT_TRY) {
> + crop = v4l2_subdev_get_try_crop(fh, pad);
> + } else {
> + crop->left = frame->crop.left;
> + crop->top = frame->crop.top;
> + crop->width = frame->crop.width;
> + crop->height = frame->crop.height;
> + }
> +
> + return 0;
> +}
> +
> +static void gsc_cap_try_crop(struct gsc_dev *gsc, struct v4l2_rect *crop,
> + u32 pad)
> +{
> + struct gsc_variant *variant = gsc->variant;
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> + struct gsc_frame *frame = gsc_capture_get_frame(ctx, pad);
> +
> + u32 crop_min_w = variant->pix_min->target_rot_dis_w;
> + u32 crop_min_h = variant->pix_min->target_rot_dis_h;
> + u32 crop_max_w = frame->f_width;
> + u32 crop_max_h = frame->f_height;
> +
> + crop->left = clamp_t(u32, crop->left, 0, crop_max_w - crop_min_w);
> + crop->top = clamp_t(u32, crop->top, 0, crop_max_h - crop_min_h);
> + crop->width = clamp_t(u32, crop->width, crop_min_w, crop_max_w);
> + crop->height = clamp_t(u32, crop->height, crop_min_h, crop_max_h);
> +}
> +
> +static int gsc_capture_subdev_set_crop(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_crop *crop)
> +{
> + struct gsc_dev *gsc = v4l2_get_subdevdata(sd);
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> + struct gsc_frame *frame = gsc_capture_get_frame(ctx, crop->pad);
> +
> + gsc_cap_try_crop(gsc,&crop->rect, crop->pad);
> +
> + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
> + frame->crop = crop->rect;
> +
> + return 0;
> +}
> +
> +static int gsc_capture_subdev_get_crop(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_crop *crop)
> +{
> + struct gsc_dev *gsc = v4l2_get_subdevdata(sd);
> + struct v4l2_rect gcrop = {0, };
Is an extra comma really needed here ?
> +
> + __gsc_cap_get_crop(gsc, fh, crop->pad, crop->which,&gcrop);
> + crop->rect = gcrop;
> +
> + return 0;
> +}
> +
> +static struct v4l2_subdev_pad_ops gsc_cap_subdev_pad_ops = {
> + .get_fmt = gsc_capture_subdev_get_fmt,
> + .set_fmt = gsc_capture_subdev_set_fmt,
> + .get_crop = gsc_capture_subdev_get_crop,
> + .set_crop = gsc_capture_subdev_set_crop,
> +};
> +
> +static struct v4l2_subdev_video_ops gsc_cap_subdev_video_ops = {
> + .s_stream = gsc_capture_subdev_s_stream,
> +};
> +
> +static struct v4l2_subdev_ops gsc_cap_subdev_ops = {
> + .pad =&gsc_cap_subdev_pad_ops,
> + .video =&gsc_cap_subdev_video_ops,
> +};
> +
> +static int gsc_capture_init_formats(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh)
> +{
> + struct v4l2_subdev_format format;
> + struct gsc_dev *gsc = v4l2_get_subdevdata(sd);
> + struct gsc_ctx *ctx = gsc->cap.ctx;
> +
> + ctx->s_frame.fmt = get_format(2);
> + memset(&format, 0, sizeof(format));
> + format.pad = GSC_PAD_SINK;
> + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
> + format.format.code = ctx->s_frame.fmt->mbus_code;
> + format.format.width = DEFAULT_GSC_SINK_WIDTH;
> + format.format.height = DEFAULT_GSC_SINK_HEIGHT;
> + gsc_capture_subdev_set_fmt(sd, fh,&format);
> +
> + /* G-scaler should not propagate, because it is possible that sink
> + * format different from source format. But the operation of source pad
s/format/format is
> + * is not needed.
> + */
> + ctx->d_frame.fmt = get_format(2);
> +
> + return 0;
> +}
> +
> +static int gsc_capture_subdev_close(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh)
> +{
> + gsc_dbg("");
> +
> + return 0;
> +}
> +
> +static int gsc_capture_subdev_registered(struct v4l2_subdev *sd)
> +{
> + gsc_dbg("");
> +
> + return 0;
> +}
> +
> +static void gsc_capture_subdev_unregistered(struct v4l2_subdev *sd)
> +{
> + gsc_dbg("");
> +}
Empty callbacks can be removed.
> +static const struct v4l2_subdev_internal_ops gsc_cap_v4l2_internal_ops = {
> + .open = gsc_capture_init_formats,
> + .close = gsc_capture_subdev_close,
> + .registered = gsc_capture_subdev_registered,
> + .unregistered = gsc_capture_subdev_unregistered,
> +};
> +
> +static int gsc_capture_link_setup(struct media_entity *entity,
> + const struct media_pad *local,
> + const struct media_pad *remote, u32 flags)
> +{
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> + struct gsc_dev *gsc = v4l2_get_subdevdata(sd);
> + struct gsc_capture_device *cap =&gsc->cap;
> +
> + gsc_info("");
> + switch (local->index | media_entity_type(remote->entity)) {
> + case GSC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
> + if (flags& MEDIA_LNK_FL_ENABLED) {
> + if (cap->input != 0)
> + return -EBUSY;
> + /* Write-Back link enabled */
> + if (!strcmp(remote->entity->name, FIMD_MODULE_NAME)) {
> + gsc->cap.sd_disp =
> + media_entity_to_v4l2_subdev(remote->entity);
> + gsc->cap.sd_disp->grp_id = FIMD_GRP_ID;
> + cap->ctx->in_path = GSC_WRITEBACK;
> + cap->input |= GSC_IN_FIMD_WRITEBACK;
> + } else if (remote->index == FLITE_PAD_SOURCE_PREV) {
> + cap->input |= GSC_IN_FLITE_PREVIEW;
> + } else {
> + cap->input |= GSC_IN_FLITE_CAMCORDING;
> + }
> + } else {
> + cap->input = GSC_IN_NONE;
> + }
> + break;
> + case GSC_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
> + /* gsc-cap always write to memory */
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static const struct media_entity_operations gsc_cap_media_ops = {
> + .link_setup = gsc_capture_link_setup,
> +};
> +
> +static int gsc_capture_create_subdev(struct gsc_dev *gsc)
> +{
> + struct v4l2_device *v4l2_dev;
> + struct v4l2_subdev *sd;
> + int ret;
> +
> + sd = kzalloc(sizeof(*sd), GFP_KERNEL);
> + if (!sd)
> + return -ENOMEM;
> +
> + v4l2_subdev_init(sd,&gsc_cap_subdev_ops);
> + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> + snprintf(sd->name, sizeof(sd->name), "gsc-cap-subdev.%d", gsc->id);
> +
> + gsc->cap.sd_pads[GSC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> + gsc->cap.sd_pads[GSC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> + ret = media_entity_init(&sd->entity, GSC_PADS_NUM,
> + gsc->cap.sd_pads, 0);
> + if (ret)
> + goto err_ent;
> +
> + sd->internal_ops =&gsc_cap_v4l2_internal_ops;
> + sd->entity.ops =&gsc_cap_media_ops;
> + sd->grp_id = GSC_CAP_GRP_ID;
> + v4l2_dev =&gsc->mdev[MDEV_CAPTURE]->v4l2_dev;
> +
> + ret = v4l2_device_register_subdev(v4l2_dev, sd);
> + if (ret)
> + goto err_sub;
> +
> + gsc->mdev[MDEV_CAPTURE]->gsc_cap_sd[gsc->id] = sd;
> + gsc->cap.sd_cap = sd;
> + v4l2_set_subdevdata(sd, gsc);
> + gsc_capture_init_formats(sd, NULL);
> +
> + return 0;
> +
> +err_sub:
> + media_entity_cleanup(&sd->entity);
> +err_ent:
> + kfree(sd);
> + return ret;
> +}
> +
> +static int gsc_capture_create_link(struct gsc_dev *gsc)
> +{
> + struct media_entity *source, *sink;
> + struct exynos_platform_gscaler *pdata = gsc->pdata;
> + struct exynos_isp_info *isp_info;
> + u32 num_clients = pdata->num_clients;
> + int ret, i;
> + enum cam_port id;
> +
> + /* GSC-SUBDEV ------> GSC-VIDEO (Always link enable) */
> + source =&gsc->cap.sd_cap->entity;
> + sink =&gsc->cap.vfd->entity;
> + if (source&& sink) {
> + ret = media_entity_create_link(source, GSC_PAD_SOURCE, sink,
> + 0, MEDIA_LNK_FL_IMMUTABLE |
> + MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + gsc_err("failed link flite to gsc\n");
> + return ret;
> + }
> + }
> + for (i = 0; i< num_clients; i++) {
> + isp_info = pdata->isp_info[i];
> + id = isp_info->cam_port;
> + /* FIMC-LITE ------> GSC-SUBDEV (ITU& MIPI common) */
> + source =&gsc->cap.sd_flite[id]->entity;
> + sink =&gsc->cap.sd_cap->entity;
> + if (source&& sink) {
> + if (pdata->cam_preview)
> + ret = media_entity_create_link(source,
> + FLITE_PAD_SOURCE_PREV,
> + sink, GSC_PAD_SINK, 0);
> + if (!ret&& pdata->cam_camcording)
> + ret = media_entity_create_link(source,
> + FLITE_PAD_SOURCE_CAMCORD,
> + sink, GSC_PAD_SINK, 0);
> + if (ret) {
> + gsc_err("failed link flite to gsc\n");
> + return ret;
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +static struct v4l2_subdev *gsc_cap_register_sensor(struct gsc_dev *gsc, int i)
> +{
> + struct exynos_md *mdev = gsc->mdev[MDEV_CAPTURE];
> + struct v4l2_subdev *sd = NULL;
> +
> + sd = mdev->sensor_sd[i];
> + if (!sd)
> + return NULL;
> +
> + v4l2_set_subdev_hostdata(sd,&gsc->cap.sensor[i]);
> +
> + return sd;
> +}
> +
> +static int gsc_cap_register_sensor_entities(struct gsc_dev *gsc)
> +{
> + struct exynos_platform_gscaler *pdata = gsc->pdata;
> + u32 num_clients = pdata->num_clients;
> + int i;
> +
> + for (i = 0; i< num_clients; i++) {
> + gsc->cap.sensor[i].pdata = pdata->isp_info[i];
> + gsc->cap.sensor[i].sd = gsc_cap_register_sensor(gsc, i);
> + if (IS_ERR_OR_NULL(gsc->cap.sensor[i].sd)) {
> + gsc_err("failed to get register sensor");
> + return -EINVAL;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int gsc_cap_config_camclk(struct gsc_dev *gsc,
> + struct exynos_isp_info *isp_info, int i)
> +{
> + struct gsc_capture_device *gsc_cap =&gsc->cap;
> + struct clk *camclk;
> + struct clk *srclk;
> +
> + camclk = clk_get(&gsc->pdev->dev, isp_info->cam_clk_name);
> + if (IS_ERR_OR_NULL(camclk)) {
> + gsc_err("failed to get cam clk");
> + return -ENXIO;
> + }
> + gsc_cap->sensor[i].camclk = camclk;
> +
> + srclk = clk_get(&gsc->pdev->dev, isp_info->cam_srclk_name);
> + if (IS_ERR_OR_NULL(srclk)) {
> + clk_put(camclk);
> + gsc_err("failed to get cam source clk\n");
> + return -ENXIO;
> + }
> + clk_set_parent(camclk, srclk);
> + clk_set_rate(camclk, isp_info->clk_frequency);
> + clk_put(srclk);
> +
> + return 0;
> +}
> +
> +int gsc_register_capture_device(struct gsc_dev *gsc)
> +{
> + struct video_device *vfd;
> + struct gsc_capture_device *gsc_cap;
> + struct gsc_ctx *ctx;
> + struct vb2_queue *q;
> + struct exynos_platform_gscaler *pdata = gsc->pdata;
> + struct exynos_isp_info *isp_info;
> + int ret = -ENOMEM;
> + int i;
> +
> + ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->gsc_dev = gsc;
> + ctx->in_path = GSC_CAMERA;
> + ctx->out_path = GSC_DMA;
> + ctx->state = GSC_CTX_CAP;
> +
> + vfd = video_device_alloc();
> + if (!vfd) {
> + printk("Failed to allocate video device\n");
> + goto err_ctx_alloc;
> + }
> +
> + snprintf(vfd->name, sizeof(vfd->name), "%s.capture",
> + dev_name(&gsc->pdev->dev));
> +
> + vfd->fops =&gsc_capture_fops;
> + vfd->ioctl_ops =&gsc_capture_ioctl_ops;
> + vfd->v4l2_dev =&gsc->mdev[MDEV_CAPTURE]->v4l2_dev;
> + vfd->minor = -1;
> + vfd->release = video_device_release;
> + vfd->lock =&gsc->lock;
> + video_set_drvdata(vfd, gsc);
> +
> + gsc_cap =&gsc->cap;
> + gsc_cap->vfd = vfd;
> + gsc_cap->refcnt = 0;
> + gsc_cap->active_buf_cnt = 0;
> + gsc_cap->reqbufs_cnt = 0;
> +
> + spin_lock_init(&ctx->slock);
> + gsc_cap->ctx = ctx;
> +
> + q =&gsc->cap.vbq;
> + memset(q, 0, sizeof(*q));
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> + q->io_modes = VB2_MMAP | VB2_USERPTR;
> + q->drv_priv = gsc->cap.ctx;
> + q->ops =&gsc_capture_qops;
> + q->mem_ops =&vb2_dma_contig_memops;
> +
> + vb2_queue_init(q);
> +
> + /* Get mipi-csis and fimc-lite subdev ptr using mdev */
> + for (i = 0; i< FLITE_MAX_ENTITIES; i++)
> + gsc->cap.sd_flite[i] = gsc->mdev[MDEV_CAPTURE]->flite_sd[i];
> +
> + for (i = 0; i< CSIS_MAX_ENTITIES; i++)
> + gsc->cap.sd_csis[i] = gsc->mdev[MDEV_CAPTURE]->csis_sd[i];
> +
> + for (i = 0; i< pdata->num_clients; i++) {
> + isp_info = pdata->isp_info[i];
> + ret = gsc_cap_config_camclk(gsc, isp_info, i);
> + if (ret) {
> + gsc_err("failed setup cam clk");
> + goto err_ctx_alloc;
> + }
> + }
> +
> + ret = gsc_cap_register_sensor_entities(gsc);
All sensors should be registered be the media device and proper media links
created. Can a single camera sensor be attached to FIMC-LITE or GSCALER
at runtime ?
> + if (ret) {
> + gsc_err("failed register sensor entities");
> + goto err_clk;
> + }
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
> + if (ret) {
> + gsc_err("failed to register video device");
> + goto err_clk;
> + }
> +
> + gsc->cap.vd_pad.flags = MEDIA_PAD_FL_SOURCE;
> + ret = media_entity_init(&vfd->entity, 1,&gsc->cap.vd_pad, 0);
> + if (ret) {
> + gsc_err("failed to initialize entity");
> + goto err_ent;
> + }
> +
> + ret = gsc_capture_create_subdev(gsc);
> + if (ret) {
> + gsc_err("failed create subdev");
> + goto err_sd_reg;
> + }
> +
> + ret = gsc_capture_create_link(gsc);
> + if (ret) {
> + gsc_err("failed create link");
> + goto err_sd_reg;
> + }
> +
> + vfd->ctrl_handler =&ctx->ctrl_handler;
> + gsc_dbg("gsc capture driver registered as /dev/video%d", vfd->num);
> +
> + return 0;
> +
> +err_sd_reg:
> + media_entity_cleanup(&vfd->entity);
> +err_ent:
> + video_device_release(vfd);
> +err_clk:
> + for (i = 0; i< pdata->num_clients; i++)
> + clk_put(gsc_cap->sensor[i].camclk);
> +err_ctx_alloc:
> + kfree(ctx);
> +
> + return ret;
> +}
> +
> +static void gsc_capture_destroy_subdev(struct gsc_dev *gsc)
> +{
> + struct v4l2_subdev *sd = gsc->cap.sd_cap;
> +
> + if (!sd)
> + return;
> + media_entity_cleanup(&sd->entity);
> + v4l2_device_unregister_subdev(sd);
> + kfree(sd);
> + sd = NULL;
Same comment as for fimc-lite.
> +}
> +
> +void gsc_unregister_capture_device(struct gsc_dev *gsc)
> +{
> + struct video_device *vfd = gsc->cap.vfd;
> +
> + if (vfd) {
> + media_entity_cleanup(&vfd->entity);
> + /* Can also be called if video device was
> + not registered */
> + video_unregister_device(vfd);
> + }
> + gsc_capture_destroy_subdev(gsc);
> + kfree(gsc->cap.ctx);
> + gsc->cap.ctx = NULL;
> +}
> +
> diff --git a/drivers/media/video/exynos/gsc/gsc-core.c b/drivers/media/video/exynos/gsc/gsc-core.c
> new file mode 100644
> index 0000000..9c8e9ce
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-core.c
> @@ -0,0 +1,1315 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-core.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
2011 - 2012
> + * http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/types.h>
> +#include<linux/errno.h>
> +#include<linux/bug.h>
> +#include<linux/interrupt.h>
> +#include<linux/workqueue.h>
> +#include<linux/device.h>
> +#include<linux/platform_device.h>
> +#include<linux/list.h>
> +#include<linux/io.h>
> +#include<linux/slab.h>
> +#include<linux/clk.h>
> +#include<media/v4l2-ioctl.h>
> +
> +#include "gsc-core.h"
> +#define GSC_CLOCK_GATE_NAME "gscl"
> +
> +int gsc_dbg = 6;
You may want to set it to 0 eventually.
> +module_param(gsc_dbg, int, 0644);
> +
> +static struct gsc_fmt gsc_formats[] = {
> + {
> + .name = "RGB565",
> + .pixelformat = V4L2_PIX_FMT_RGB565X,
> + .depth = { 16 },
> + .color = GSC_RGB,
> + .num_planes = 1,
> + .nr_comp = 1,
s/nr_comp/num_comp ?
> + }, {
> + .name = "XRGB-8-8-8-8, 32 bpp",
> + .pixelformat = V4L2_PIX_FMT_RGB32,
> + .depth = { 32 },
> + .color = GSC_RGB,
> + .num_planes = 1,
> + .nr_comp = 1,
> + }, {
> + .name = "YUV 4:2:2 packed, YCbYCr",
> + .pixelformat = V4L2_PIX_FMT_YUYV,
> + .depth = { 16 },
> + .color = GSC_YUV422,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 1,
> + .nr_comp = 1,
> + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
> + }, {
> + .name = "YUV 4:2:2 packed, CbYCrY",
> + .pixelformat = V4L2_PIX_FMT_UYVY,
> + .depth = { 16 },
> + .color = GSC_YUV422,
> + .yorder = GSC_LSB_C,
> + .corder = GSC_CBCR,
> + .num_planes = 1,
> + .nr_comp = 1,
> + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
> + }, {
> + .name = "YUV 4:2:2 packed, CrYCbY",
> + .pixelformat = V4L2_PIX_FMT_VYUY,
> + .depth = { 16 },
> + .color = GSC_YUV422,
> + .yorder = GSC_LSB_C,
> + .corder = GSC_CRCB,
> + .num_planes = 1,
> + .nr_comp = 1,
> + .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
> + }, {
> + .name = "YUV 4:2:2 packed, YCrYCb",
> + .pixelformat = V4L2_PIX_FMT_YVYU,
> + .depth = { 16 },
> + .color = GSC_YUV422,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CRCB,
> + .num_planes = 1,
> + .nr_comp = 1,
> + .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
> + }, {
> + .name = "YUV 4:4:4 planar, YCbYCr",
> + .pixelformat = V4L2_PIX_FMT_YUV32,
> + .depth = { 32 },
> + .color = GSC_YUV444,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 1,
> + .nr_comp = 1,
> + }, {
> + .name = "YUV 4:2:2 planar, Y/Cb/Cr",
> + .pixelformat = V4L2_PIX_FMT_YUV422P,
> + .depth = { 16 },
> + .color = GSC_YUV422,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 1,
> + .nr_comp = 3,
> + }, {
> + .name = "YUV 4:2:2 planar, Y/CbCr",
> + .pixelformat = V4L2_PIX_FMT_NV16,
> + .depth = { 16 },
> + .color = GSC_YUV422,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 1,
> + .nr_comp = 2,
> + }, {
> + .name = "YUV 4:2:2 planar, Y/CrCb",
> + .pixelformat = V4L2_PIX_FMT_NV61,
> + .depth = { 16 },
> + .color = GSC_YUV422,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CRCB,
> + .num_planes = 1,
> + .nr_comp = 2,
> + }, {
> + .name = "YUV 4:2:0 planar, YCbCr",
> + .pixelformat = V4L2_PIX_FMT_YUV420,
> + .depth = { 12 },
> + .color = GSC_YUV420,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 1,
> + .nr_comp = 3,
> + }, {
> + .name = "YUV 4:2:0 planar, YCbCr",
> + .pixelformat = V4L2_PIX_FMT_YVU420,
> + .depth = { 12 },
> + .color = GSC_YUV420,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CRCB,
> + .num_planes = 1,
> + .nr_comp = 3,
> +
> + }, {
> + .name = "YUV 4:2:0 planar, Y/CbCr",
> + .pixelformat = V4L2_PIX_FMT_NV12,
> + .depth = { 12 },
> + .color = GSC_YUV420,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 1,
> + .nr_comp = 2,
> + }, {
> + .name = "YUV 4:2:0 planar, Y/CrCb",
> + .pixelformat = V4L2_PIX_FMT_NV21,
> + .depth = { 12 },
> + .color = GSC_YUV420,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CRCB,
> + .num_planes = 1,
> + .nr_comp = 2,
> + }, {
> + .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
> + .pixelformat = V4L2_PIX_FMT_NV12M,
> + .depth = { 8, 4 },
> + .color = GSC_YUV420,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 2,
> + .nr_comp = 2,
> + }, {
> + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
> + .pixelformat = V4L2_PIX_FMT_YUV420M,
> + .depth = { 8, 2, 2 },
> + .color = GSC_YUV420,
> + .yorder = GSC_LSB_Y,
> + .corder = GSC_CBCR,
> + .num_planes = 3,
> + .nr_comp = 3,
> + },
> +};
> +
> +struct gsc_fmt *get_format(int index)
> +{
> + return&gsc_formats[index];
> +}
> +
> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index)
> +{
> + struct gsc_fmt *fmt, *def_fmt = NULL;
> + unsigned int i;
> +
> + if (index>= ARRAY_SIZE(gsc_formats))
> + return NULL;
> +
> + for (i = 0; i< ARRAY_SIZE(gsc_formats); ++i) {
> + fmt = get_format(i);
> + if (pixelformat&& fmt->pixelformat == *pixelformat)
> + return fmt;
> + if (mbus_code&& fmt->mbus_code == *mbus_code)
> + return fmt;
> + if (index == i)
> + def_fmt = fmt;
> + }
> + return def_fmt;
> +
> +}
> +
> +void gsc_set_frame_size(struct gsc_frame *frame, int width, int height)
Shouldn't it be static ?
> +{
> + frame->f_width = width;
> + frame->f_height = height;
> + frame->crop.width = width;
> + frame->crop.height = height;
> + frame->crop.left = 0;
> + frame->crop.top = 0;
> +}
> +
> +int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst, u32 *ratio)
> +{
> + if ((dst> src) || (dst>= src / var->poly_sc_down_max)) {
> + *ratio = 1;
> + return 0;
> + }
> +
> + if ((src / var->poly_sc_down_max / var->pre_sc_down_max)> dst) {
> + gsc_err("scale ratio exceeded maximun scale down ratio(1/16)");
> + return -EINVAL;
> + }
> +
> + *ratio = (dst> (src / 8)) ? 2 : 4;
> +
> + return 0;
> +}
> +
> +void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh)
> +{
> + if (hratio == 4&& vratio == 4)
> + *sh = 4;
> + else if ((hratio == 4&& vratio == 2) ||
> + (hratio == 2&& vratio == 4))
> + *sh = 3;
> + else if ((hratio == 4&& vratio == 1) ||
> + (hratio == 1&& vratio == 4) ||
> + (hratio == 2&& vratio == 2))
> + *sh = 2;
> + else if (hratio == 1&& vratio == 1)
> + *sh = 0;
> + else
> + *sh = 1;
> +}
> +
> +void gsc_check_src_scale_info(struct gsc_variant *var, struct gsc_frame *s_frame,
> + u32 *wratio, u32 tx, u32 ty, u32 *hratio)
> +{
> + int remainder = 0, walign, halign;
> +
> + if (is_yuv420(s_frame->fmt->color)) {
> + walign = GSC_SC_ALIGN_4;
> + halign = GSC_SC_ALIGN_4;
> + } else if (is_yuv422(s_frame->fmt->color)) {
> + walign = GSC_SC_ALIGN_4;
> + halign = GSC_SC_ALIGN_2;
> + } else {
> + walign = GSC_SC_ALIGN_2;
> + halign = GSC_SC_ALIGN_2;
> + }
> +
> + remainder = s_frame->crop.width % (*wratio * walign);
> + if (remainder) {
> + s_frame->crop.width -= remainder;
> + gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio);
> + gsc_info("cropped src width size is recalculated from %d to %d",
> + s_frame->crop.width + remainder, s_frame->crop.width);
> + }
> +
> + remainder = s_frame->crop.height % (*hratio * halign);
> + if (remainder) {
> + s_frame->crop.height -= remainder;
> + gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio);
> + gsc_info("cropped src height size is recalculated from %d to %d",
> + s_frame->crop.height + remainder, s_frame->crop.height);
> + }
> +}
> +
> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
> +{
> + struct gsc_fmt *fmt;
> +
> + fmt = find_fmt(NULL, NULL, f->index);
> + if (!fmt)
> + return -EINVAL;
> +
> + strncpy(f->description, fmt->name, sizeof(f->description) - 1);
> + f->pixelformat = fmt->pixelformat;
> +
> + return 0;
> +}
> +
> +u32 get_plane_size(struct gsc_frame *frame, unsigned int plane)
> +{
> + if (!frame || plane>= frame->fmt->num_planes) {
> + gsc_err("Invalid argument");
> + return 0;
> + }
> +
> + return frame->payload[plane];
> +}
> +
> +u32 get_plane_info(struct gsc_frame frm, u32 addr, u32 *index)
> +{
> + if (frm.addr.y == addr) {
> + *index = 0;
> + return frm.addr.y;
> + } else if (frm.addr.cb == addr) {
> + *index = 1;
> + return frm.addr.cb;
> + } else if (frm.addr.cr == addr) {
> + *index = 2;
> + return frm.addr.cr;
> + } else {
> + gsc_err("Plane address is wrong");
> + return -EINVAL;
> + }
> +}
> +
> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm)
> +{
> + u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
> + f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
> +
> + f_chk_addr = frm.addr.y;
> + f_chk_len = frm.payload[0];
> + if (frm.fmt->num_planes == 2) {
> + s_chk_addr = frm.addr.cb;
> + s_chk_len = frm.payload[1];
> + } else if (frm.fmt->num_planes == 3) {
> + u32 low_addr, low_plane, mid_addr, mid_plane, high_addr, high_plane;
> + u32 t_min, t_max;
> +
> + t_min = min3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> + low_addr = get_plane_info(frm, t_min,&low_plane);
> + t_max = max3(frm.addr.y, frm.addr.cb, frm.addr.cr);
> + high_addr = get_plane_info(frm, t_max,&high_plane);
> +
> + mid_plane = 3 - (low_plane + high_plane);
> + if (mid_plane == 0)
> + mid_addr = frm.addr.y;
> + else if (mid_plane == 1)
> + mid_addr = frm.addr.cb;
> + else if (mid_plane == 2)
> + mid_addr = frm.addr.cr;
> + else
> + return;
> +
> + f_chk_addr = low_addr;
> + if (mid_addr + frm.payload[mid_plane] - low_addr>
> + high_addr + frm.payload[high_plane] - mid_addr) {
> + f_chk_len = frm.payload[low_plane];
> + s_chk_addr = mid_addr;
> + s_chk_len = high_addr + frm.payload[high_plane] - mid_addr;
> + } else {
> + f_chk_len = mid_addr + frm.payload[mid_plane] - low_addr;
> + s_chk_addr = high_addr;
> + s_chk_len = frm.payload[high_plane];
> + }
> + }
> + gsc_dbg("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
> + f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
> +}
> +
> +int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
> +{
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + struct gsc_variant *variant = gsc->variant;
> + struct v4l2_pix_format_mplane *pix_mp =&f->fmt.pix_mp;
> + struct gsc_fmt *fmt;
> + u32 max_w, max_h, mod_x, mod_y;
> + u32 min_w, min_h, tmp_w, tmp_h;
> + int i;
> +
> + gsc_dbg("user put w: %d, h: %d", pix_mp->width, pix_mp->height);
> +
> + fmt = find_fmt(&pix_mp->pixelformat, NULL, 0);
> + if (!fmt) {
> + gsc_err("pixelformat format (0x%X) invalid\n", pix_mp->pixelformat);
> + return -EINVAL;
> + }
> +
> + if (pix_mp->field == V4L2_FIELD_ANY)
> + pix_mp->field = V4L2_FIELD_NONE;
> + else if (pix_mp->field != V4L2_FIELD_NONE) {
> + gsc_err("Not supported field order(%d)\n", pix_mp->field);
> + return -EINVAL;
> + }
> +
> + max_w = variant->pix_max->target_rot_dis_w;
> + max_h = variant->pix_max->target_rot_dis_h;
> + if (V4L2_TYPE_IS_OUTPUT(f->type)) {
> + mod_x = ffs(variant->pix_align->org_w) - 1;
> + if (is_yuv420(fmt->color))
> + mod_y = ffs(variant->pix_align->org_h) - 1;
> + else
> + mod_y = ffs(variant->pix_align->org_h) - 2;
> + min_w = variant->pix_min->org_w;
> + min_h = variant->pix_min->org_h;
> + } else {
> + mod_x = ffs(variant->pix_align->org_w) - 1;
> + if (is_yuv420(fmt->color))
> + mod_y = ffs(variant->pix_align->org_h) - 1;
> + else
> + mod_y = ffs(variant->pix_align->org_h) - 2;
> + min_w = variant->pix_min->target_rot_dis_w;
> + min_h = variant->pix_min->target_rot_dis_h;
> + }
> + gsc_dbg("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
> + mod_x, mod_y, max_w, max_h);
> + /* To check if image size is modified to adjust parameter against
> + hardware abilities */
> + tmp_w = pix_mp->width;
> + tmp_h = pix_mp->height;
> +
> + v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
> + &pix_mp->height, min_h, max_h, mod_y, 0);
> + if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
> + gsc_info("Image size has been modified from %dx%d to %dx%d",
> + tmp_w, tmp_h, pix_mp->width, pix_mp->height);
> +
> + pix_mp->num_planes = fmt->num_planes;
> +
> + if (ctx->gsc_ctrls.csc_eq_mode->val)
> + ctx->gsc_ctrls.csc_eq->val =
> + (pix_mp->width>= 1280) ? 1 : 0;
> + if (ctx->gsc_ctrls.csc_eq->val) /* HD */
> + pix_mp->colorspace = V4L2_COLORSPACE_REC709;
> + else /* SD */
> + pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> +
> + for (i = 0; i< pix_mp->num_planes; ++i) {
> + int bpl = (pix_mp->width * fmt->depth[i])>> 3;
> + pix_mp->plane_fmt[i].bytesperline = bpl;
> + pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
> +
> + gsc_dbg("[%d]: bpl: %d, sizeimage: %d",
> + i, bpl, pix_mp->plane_fmt[i].sizeimage);
> + }
> +
> + return 0;
> +}
> +
> +int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
> +{
> + struct gsc_frame *frame;
> + struct v4l2_pix_format_mplane *pix_mp;
> + int i;
> +
> + frame = ctx_get_frame(ctx, f->type);
> + if (IS_ERR(frame))
> + return PTR_ERR(frame);
> +
> + pix_mp =&f->fmt.pix_mp;
> +
> + pix_mp->width = frame->f_width;
> + pix_mp->height = frame->f_height;
> + pix_mp->field = V4L2_FIELD_NONE;
> + pix_mp->pixelformat = frame->fmt->pixelformat;
> + pix_mp->colorspace = V4L2_COLORSPACE_JPEG;
> + pix_mp->num_planes = frame->fmt->num_planes;
> +
> + for (i = 0; i< pix_mp->num_planes; ++i) {
> + pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
> + frame->fmt->depth[i]) / 8;
> + pix_mp->plane_fmt[i].sizeimage = pix_mp->plane_fmt[i].bytesperline *
> + frame->f_height;
> + }
> +
> + return 0;
> +}
> +
> +void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
> +{
> + if (tmp_w != *w || tmp_h != *h) {
> + gsc_info("Image cropped size has been modified from %dx%d to %dx%d",
> + *w, *h, tmp_w, tmp_h);
> + *w = tmp_w;
> + *h = tmp_h;
> + }
> +}
> +
> +int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
> +{
> + struct gsc_frame *frame;
> +
> + frame = ctx_get_frame(ctx, cr->type);
> + if (IS_ERR(frame))
> + return PTR_ERR(frame);
> +
> + memcpy(&cr->c,&frame->crop, sizeof(struct v4l2_rect));
cr->c = frame->crop; ?
> +
> + return 0;
> +}
> +
> +int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
> +{
> + struct gsc_frame *f;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + struct gsc_variant *variant = gsc->variant;
> + u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h;
> + u32 min_w, min_h, max_w, max_h;
> +
> + if (cr->c.top< 0 || cr->c.left< 0) {
> + gsc_err("doesn't support negative values for top& left\n");
> + return -EINVAL;
> + }
> + gsc_dbg("user put w: %d, h: %d", cr->c.width, cr->c.height);
> +
> + if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + f =&ctx->d_frame;
> + else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + f =&ctx->s_frame;
> + else
> + return -EINVAL;
> +
> + max_w = f->f_width;
> + max_h = f->f_height;
> + tmp_w = cr->c.width;
> + tmp_h = cr->c.height;
> +
> + if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
> + if ((is_yuv422(f->fmt->color)&& f->fmt->nr_comp == 1) ||
> + is_rgb(f->fmt->color))
> + min_w = 32;
> + else
> + min_w = 64;
> + if ((is_yuv422(f->fmt->color)&& f->fmt->nr_comp == 3) ||
> + is_yuv420(f->fmt->color))
> + min_h = 32;
> + else
> + min_h = 16;
> + } else {
> + if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
> + mod_x = ffs(variant->pix_align->target_w) - 1;
> + if (is_yuv420(f->fmt->color))
> + mod_y = ffs(variant->pix_align->target_h) - 1;
> + if (ctx->gsc_ctrls.rotate->val == 90 ||
> + ctx->gsc_ctrls.rotate->val == 270) {
> + max_w = f->f_height;
> + max_h = f->f_width;
> + min_w = variant->pix_min->target_rot_en_w;
> + min_h = variant->pix_min->target_rot_en_h;
> + tmp_w = cr->c.height;
> + tmp_h = cr->c.width;
> + } else {
> + min_w = variant->pix_min->target_rot_dis_w;
> + min_h = variant->pix_min->target_rot_dis_h;
> + }
> + }
> + gsc_dbg("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d,\
> + tmp_w : %d, tmp_h : %d",
> + mod_x, mod_y, min_w, min_h, tmp_w, tmp_h);
> +
> + v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
> + &tmp_h, min_h, max_h, mod_y, 0);
> +
> + if (!V4L2_TYPE_IS_OUTPUT(cr->type)&&
> + (ctx->gsc_ctrls.rotate->val == 90 ||
> + ctx->gsc_ctrls.rotate->val == 270)) {
> + gsc_check_crop_change(tmp_h, tmp_w,&cr->c.width,&cr->c.height);
> + } else {
> + gsc_check_crop_change(tmp_w, tmp_h,&cr->c.width,&cr->c.height);
> + }
> +
> + /* adjust left/top if cropping rectangle is out of bounds */
> + /* Need to add code to algin left value with 2's multiple */
> + if (cr->c.left + tmp_w> max_w)
> + cr->c.left = max_w - tmp_w;
> + if (cr->c.top + tmp_h> max_h)
> + cr->c.top = max_h - tmp_h;
> +
> + if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
> + if (cr->c.left % 2)
> + cr->c.left -= 1;
> +
> + gsc_dbg("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
> + cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
> +
> + return 0;
> +}
> +
> +int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
> + int dh, int rot, int out_path)
> +{
> + int tmp_w, tmp_h, sc_down_max;
> + sc_down_max =
> + (out_path == GSC_DMA) ? var->sc_down_max : var->local_sc_down;
> +
> + if (rot == 90 || rot == 270) {
> + tmp_w = dh;
> + tmp_h = dw;
> + } else {
> + tmp_w = dw;
> + tmp_h = dh;
> + }
> +
> + if ((sw / tmp_w)> sc_down_max ||
> + (sh / tmp_h)> sc_down_max ||
> + (tmp_w / sw)> var->sc_up_max ||
> + (tmp_h / sh)> var->sc_up_max)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +int gsc_set_scaler_info(struct gsc_ctx *ctx)
> +{
> + struct gsc_scaler *sc =&ctx->scaler;
> + struct gsc_frame *s_frame =&ctx->s_frame;
> + struct gsc_frame *d_frame =&ctx->d_frame;
> + struct gsc_variant *variant = ctx->gsc_dev->variant;
> + int tx, ty;
> + int ret;
> +
> + ret = gsc_check_scaler_ratio(variant, s_frame->crop.width,
> + s_frame->crop.height, d_frame->crop.width, d_frame->crop.height,
> + ctx->gsc_ctrls.rotate->val, ctx->out_path);
> + if (ret) {
> + gsc_err("out of scaler range");
> + return ret;
> + }
> +
> + if (ctx->gsc_ctrls.rotate->val == 90 ||
> + ctx->gsc_ctrls.rotate->val == 270) {
> + ty = d_frame->crop.width;
> + tx = d_frame->crop.height;
> + } else {
> + tx = d_frame->crop.width;
> + ty = d_frame->crop.height;
> + }
> +
> + ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width,
> + tx,&sc->pre_hratio);
> + if (ret) {
> + gsc_err("Horizontal scale ratio is out of range");
> + return ret;
> + }
> +
> + ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height,
> + ty,&sc->pre_vratio);
> + if (ret) {
> + gsc_err("Vertical scale ratio is out of range");
> + return ret;
> + }
> +
> + gsc_check_src_scale_info(variant, s_frame,&sc->pre_hratio,
> + tx, ty,&sc->pre_vratio);
> +
> + gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
> + &sc->pre_shfactor);
> +
> + sc->main_hratio = (s_frame->crop.width<< 16) / tx;
> + sc->main_vratio = (s_frame->crop.height<< 16) / ty;
> +
> + gsc_dbg("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d",
> + s_frame->crop.width, s_frame->crop.height, tx, ty);
> + gsc_dbg("scaler ratio info : pre_shfactor : %d, pre_h : %d, pre_v :%d,\
> + main_h : %ld, main_v : %ld", sc->pre_shfactor, sc->pre_hratio,
> + sc->pre_vratio, sc->main_hratio, sc->main_vratio);
> +
> + return 0;
> +}
> +
> +int gsc_pipeline_s_stream(struct gsc_dev *gsc, bool on)
> +{
> + struct gsc_pipeline *p =&gsc->pipeline;
> + struct exynos_entity_data md_data;
> + int ret = 0;
> +
> + /* If gscaler subdev calls the mixer's s_stream, the gscaler must
> + inform the mixer subdev pipeline started from gscaler */
> + if (!strncmp(p->disp->name, MXR_SUBDEV_NAME,
> + sizeof(MXR_SUBDEV_NAME) - 1)) {
> + md_data.mxr_data_from = FROM_GSC_SD;
> + v4l2_set_subdevdata(p->disp,&md_data);
> + }
> +
> + ret = v4l2_subdev_call(p->disp, video, s_stream, on);
> + if (ret)
> + gsc_err("Display s_stream on failed\n");
> +
> + return ret;
> +}
> +
> +int gsc_out_link_validate(const struct media_pad *source,
> + const struct media_pad *sink)
> +{
> + struct v4l2_subdev_format src_fmt;
> + struct v4l2_subdev_crop dst_crop;
> + struct v4l2_subdev *sd;
> + struct gsc_dev *gsc;
> + struct gsc_frame *f;
> + int ret;
> +
> + if (media_entity_type(source->entity) != MEDIA_ENT_T_V4L2_SUBDEV ||
> + media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) {
> + gsc_err("media entity type isn't subdev\n");
> + return 0;
> + }
> +
> + sd = media_entity_to_v4l2_subdev(source->entity);
> + gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd));
> + f =&gsc->out.ctx->d_frame;
> +
> + src_fmt.format.width = f->crop.width;
> + src_fmt.format.height = f->crop.height;
> + src_fmt.format.code = f->fmt->mbus_code;
> +
> + sd = media_entity_to_v4l2_subdev(sink->entity);
> + /* To check if G-Scaler destination size and Mixer destinatin size
> + are the same */
> + dst_crop.pad = sink->index;
> + dst_crop.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + ret = v4l2_subdev_call(sd, pad, get_crop, NULL,&dst_crop);
> + if (ret< 0&& ret != -ENOIOCTLCMD) {
> + gsc_err("subdev get_fmt is failed\n");
> + return -EPIPE;
> + }
> +
> + if (src_fmt.format.width != dst_crop.rect.width ||
> + src_fmt.format.height != dst_crop.rect.height) {
> + gsc_err("sink and source format is different\
> + src_fmt.w = %d, src_fmt.h = %d,\
> + dst_crop.w = %d, dst_crop.h = %d, rotation = %d",
> + src_fmt.format.width, src_fmt.format.height,
> + dst_crop.rect.width, dst_crop.rect.height,
> + gsc->out.ctx->gsc_ctrls.rotate->val);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * V4L2 controls handling
> + */
> +static int gsc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct gsc_ctx *ctx = ctrl_to_ctx(ctrl);
> +
> + switch (ctrl->id) {
> + case V4L2_CID_HFLIP:
> + user_to_drv(ctx->gsc_ctrls.hflip, ctrl->val);
> + break;
> +
> + case V4L2_CID_VFLIP:
> + user_to_drv(ctx->gsc_ctrls.vflip, ctrl->val);
> + break;
> +
> + case V4L2_CID_ROTATE:
> + user_to_drv(ctx->gsc_ctrls.rotate, ctrl->val);
> + break;
> +
> + default:
> + break;
> + }
> +
> + if (gsc_m2m_opened(ctx->gsc_dev))
> + gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
> +
> + return 0;
> +}
> +
> +const struct v4l2_ctrl_ops gsc_ctrl_ops = {
> + .s_ctrl = gsc_s_ctrl,
> +};
> +
> +int gsc_ctrls_create(struct gsc_ctx *ctx)
> +{
> + if (ctx->ctrls_rdy) {
> + gsc_err("Control handler of this context was created already");
> + return 0;
> + }
> +
> + v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM);
> +
> + ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> + &gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
> + ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> + &gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> + ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
> + &gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> +
> + if (ctx->ctrl_handler.error) {
> + int err = ctx->ctrl_handler.error;
> + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> + gsc_err("Failed to gscaler control hander create");
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +void gsc_ctrls_delete(struct gsc_ctx *ctx)
> +{
> + if (ctx->ctrls_rdy) {
> + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> + ctx->ctrls_rdy = false;
> + }
> +}
> +
> +/* The color format (nr_comp, num_planes) must be already configured. */
> +int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
> + struct gsc_frame *frame, struct gsc_addr *addr)
> +{
> + int ret = 0;
> + u32 pix_size;
> +
> + if (IS_ERR(vb) || IS_ERR(frame)) {
> + gsc_err("Invalid argument");
> + return -EINVAL;
> + }
> +
> + pix_size = frame->f_width * frame->f_height;
> +
> + gsc_dbg("num_planes= %d, nr_comp= %d, pix_size= %d",
> + frame->fmt->num_planes, frame->fmt->nr_comp, pix_size);
> +
> + addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
> +
> + if (frame->fmt->num_planes == 1) {
> + switch (frame->fmt->nr_comp) {
> + case 1:
> + addr->cb = 0;
> + addr->cr = 0;
> + break;
> + case 2:
> + /* decompose Y into Y/Cb */
> + addr->cb = (dma_addr_t)(addr->y + pix_size);
> + addr->cr = 0;
> + break;
> + case 3:
> + addr->cb = (dma_addr_t)(addr->y + pix_size);
> + addr->cr = (dma_addr_t)(addr->cb + (pix_size>> 2));
> + break;
> + default:
> + gsc_err("Invalid the number of color planes");
> + return -EINVAL;
> + }
> + } else {
> + if (frame->fmt->num_planes>= 2)
> + addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
> +
> + if (frame->fmt->num_planes == 3)
> + addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
> + }
> +
> + if (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) {
> + u32 t_cb = addr->cb;
> + addr->cb = addr->cr;
> + addr->cr = t_cb;
> + }
> +
> + gsc_dbg("ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
> + addr->y, addr->cb, addr->cr, ret);
> +
> + return ret;
> +}
> +
> +void gsc_wq_suspend(struct work_struct *work)
> +{
> + struct gsc_dev *gsc = container_of(work, struct gsc_dev,
> + work_struct);
> + pm_runtime_put_sync(&gsc->pdev->dev);
> +}
> +
> +void gsc_cap_irq_handler(struct gsc_dev *gsc)
> +{
> + int done_index;
> +
> + done_index = gsc_hw_get_done_output_buf_index(gsc);
> + gsc_info("done_index : %d", done_index);
> + if (done_index< 0)
> + gsc_err("All buffers are masked\n");
> + test_bit(ST_CAPT_RUN,&gsc->state) ? :
> + set_bit(ST_CAPT_RUN,&gsc->state);
> + vb2_buffer_done(gsc->cap.vbq.bufs[done_index], VB2_BUF_STATE_DONE);
> +}
> +
> +static irqreturn_t gsc_irq_handler(int irq, void *priv)
> +{
> + struct gsc_dev *gsc = priv;
> + int gsc_irq;
> +
> + gsc_irq = gsc_hw_get_irq_status(gsc);
> + gsc_hw_clear_irq(gsc, gsc_irq);
> +
> + if (gsc_irq == GSC_OR_IRQ) {
> + gsc_err("Local path input over-run interrupt has occurred!\n");
> + return IRQ_HANDLED;
> + }
> +
> + spin_lock(&gsc->slock);
> +
> + if (test_and_clear_bit(ST_M2M_RUN,&gsc->state)) {
> + struct vb2_buffer *src_vb, *dst_vb;
> + struct gsc_ctx *ctx =
> + v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> +
> + if (!ctx || !ctx->m2m_ctx)
> + goto isr_unlock;
> +
> + 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_and_clear_bit(ST_STOP_REQ,&gsc->state))
> + wake_up(&gsc->irq_queue);
> + else
> + v4l2_m2m_job_finish(gsc->m2m.m2m_dev, ctx->m2m_ctx);
> +
> + /* wake_up job_abort, stop_streaming */
> + spin_lock(&ctx->slock);
Do you think you could remove the spinlock from struct gsc_ctx and use
struct gsc_dev::slock only, where necessary ?
All in all, the video node ioctls are protected by single mutex anyway..
This mutex has long history in the s5p-fimc driver and I would love to
find some time to simplify the driver by removing it.
> + if (ctx->state& GSC_CTX_STOP_REQ) {
> + ctx->state&= ~GSC_CTX_STOP_REQ;
> + wake_up(&gsc->irq_queue);
> + }
> + spin_unlock(&ctx->slock);
> + }
> + /* schedule pm_runtime_put_sync */
> + queue_work(gsc->irq_workqueue,&gsc->work_struct);
> + } else if (test_bit(ST_OUTPUT_STREAMON,&gsc->state)) {
> + if (!list_empty(&gsc->out.active_buf_q)) {
> + struct gsc_input_buf *done_buf;
> + done_buf = active_queue_pop(&gsc->out, gsc);
> + gsc_hw_set_input_buf_masking(gsc, done_buf->idx, true);
> + if (!list_is_last(&done_buf->list,&gsc->out.active_buf_q)) {
> + vb2_buffer_done(&done_buf->vb, VB2_BUF_STATE_DONE);
> + list_del(&done_buf->list);
> + }
> + }
> + } else if (test_bit(ST_CAPT_PEND,&gsc->state)) {
> + gsc_cap_irq_handler(gsc);
> + }
> +
> +isr_unlock:
> + spin_unlock(&gsc->slock);
> + return IRQ_HANDLED;
> +}
> +
> +static int gsc_get_media_info(struct device *dev, void *p)
> +{
> + struct exynos_md **mdev = p;
> + struct platform_device *pdev = to_platform_device(dev);
> +
> + mdev[pdev->id] = dev_get_drvdata(dev);
> + if (!mdev[pdev->id])
> + return -ENODEV;
> +
> + return 0;
> +}
> +
> +static int gsc_runtime_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
> +
> + if (gsc_m2m_opened(gsc))
> + gsc->m2m.ctx = NULL;
> +
> + clk_disable(gsc->clock);
You should check return value from clk_disable().
> + clear_bit(ST_PWR_ON,&gsc->state);
> +
> + return 0;
> +}
> +
> +static int gsc_runtime_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct gsc_dev *gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
> +
> + clk_enable(gsc->clock);
clk_enable may fail.
> + set_bit(ST_PWR_ON,&gsc->state);
> + return 0;
> +}
> +
> +static int gsc_probe(struct platform_device *pdev)
> +{
> + struct gsc_dev *gsc;
> + struct resource *res;
> + struct gsc_driverdata *drv_data;
> + struct device_driver *driver;
> + struct exynos_md *mdev[MDEV_MAX_NUM] = {NULL,};
> + int ret = 0;
> + char workqueue_name[WORKQUEUE_NAME_SIZE];
> +
> + dev_dbg(&pdev->dev, "%s():\n", __func__);
> + drv_data = (struct gsc_driverdata *)
> + platform_get_device_id(pdev)->driver_data;
> +
> + if (pdev->id>= drv_data->num_entities) {
> + dev_err(&pdev->dev, "Invalid platform device id: %d\n",
> + pdev->id);
> + return -EINVAL;
> + }
> +
> + gsc = kzalloc(sizeof(struct gsc_dev), GFP_KERNEL);
What about using devm_kzalloc and other devm_* functions ?
Or you can't use it because the driver is being developed on older kernels
that don't have the devm_* API yet ?
> + if (!gsc)
> + return -ENOMEM;
> +
> + gsc->id = pdev->id;
> + gsc->variant = drv_data->variant[gsc->id];
> + gsc->pdev = pdev;
> + gsc->pdata = pdev->dev.platform_data;
> +
> + init_waitqueue_head(&gsc->irq_queue);
> + spin_lock_init(&gsc->slock);
> + mutex_init(&gsc->lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "failed to find the registers\n");
> + ret = -ENOENT;
> + goto err_info;
> + }
> +
> + gsc->regs_res = request_mem_region(res->start, resource_size(res),
> + dev_name(&pdev->dev));
> + if (!gsc->regs_res) {
> + dev_err(&pdev->dev, "failed to obtain register region\n");
> + ret = -ENOENT;
> + goto err_info;
> + }
> +
> + gsc->regs = ioremap(res->start, resource_size(res));
> + if (!gsc->regs) {
> + dev_err(&pdev->dev, "failed to map registers\n");
> + ret = -ENXIO;
> + goto err_req_region;
> + }
> +
> + /* Get Gscaler clock */
> + gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
> + if (IS_ERR(gsc->clock)) {
> + gsc_err("failed to get gscaler.%d clock", gsc->id);
> + goto err_regs_unmap;
> + }
> + clk_put(gsc->clock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "failed to get IRQ resource\n");
> + ret = -ENXIO;
> + goto err_regs_unmap;
> + }
> + gsc->irq = res->start;
> +
> + ret = request_irq(gsc->irq, gsc_irq_handler, 0, pdev->name, gsc);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
> + goto err_regs_unmap;
> + }
> +
> + platform_set_drvdata(pdev, gsc);
> +
> + ret = gsc_register_m2m_device(gsc);
> + if (ret)
> + goto err_irq;
> +
> + /* find media device */
> + driver = driver_find(MDEV_MODULE_NAME,&platform_bus_type);
> + if (!driver)
> + goto err_irq;
> +
> + ret = driver_for_each_device(driver, NULL,&mdev[0],
> + gsc_get_media_info);
> + put_driver(driver);
> + if (ret)
> + goto err_irq;
> +
> + gsc->mdev[MDEV_OUTPUT] = mdev[MDEV_OUTPUT];
> + gsc->mdev[MDEV_CAPTURE] = mdev[MDEV_CAPTURE];
> +
> + gsc_dbg("mdev->mdev[%d] = 0x%08x, mdev->mdev[%d] = 0x%08x",
> + MDEV_OUTPUT, (u32)gsc->mdev[MDEV_OUTPUT], MDEV_CAPTURE,
> + (u32)gsc->mdev[MDEV_CAPTURE]);
> +
> + ret = gsc_register_output_device(gsc);
> + if (ret)
> + goto err_irq;
> +
> + if (gsc->pdata) {
> + ret = gsc_register_capture_device(gsc);
> + if (ret)
> + goto err_irq;
> + }
You probably want to register video nodes at the very end of this function,
when all the resources are prepared.
> +
> + sprintf(workqueue_name, "gsc%d_irq_wq_name", gsc->id);
> + gsc->irq_workqueue = create_singlethread_workqueue(workqueue_name);
> + if (gsc->irq_workqueue == NULL) {
> + dev_err(&pdev->dev, "failed to create workqueue for gsc\n");
> + goto err_irq;
> + }
> + INIT_WORK(&gsc->work_struct, gsc_wq_suspend);
> +
> + gsc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> + if (IS_ERR(gsc->alloc_ctx)) {
> + ret = PTR_ERR(gsc->alloc_ctx);
> + goto err_wq;
> + }
> + pm_runtime_enable(&pdev->dev);
> +
> + gsc_info("gsc-%d registered successfully", gsc->id);
> +
> + return 0;
> +
> +err_wq:
> + destroy_workqueue(gsc->irq_workqueue);
> +err_irq:
> + free_irq(gsc->irq, gsc);
> +err_regs_unmap:
> + iounmap(gsc->regs);
> +err_req_region:
> + release_resource(gsc->regs_res);
> + kfree(gsc->regs_res);
> +err_info:
> + kfree(gsc);
> +
> + return ret;
> +}
> +
> +static int __devexit gsc_remove(struct platform_device *pdev)
> +{
> + struct gsc_dev *gsc =
> + (struct gsc_dev *)platform_get_drvdata(pdev);
> +
> + free_irq(gsc->irq, gsc);
> +
> + gsc_unregister_m2m_device(gsc);
> + gsc_unregister_output_device(gsc);
> + gsc_unregister_capture_device(gsc);
> +
> + vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
> + pm_runtime_disable(&pdev->dev);
> +
> + iounmap(gsc->regs);
> + release_resource(gsc->regs_res);
> + kfree(gsc->regs_res);
> + kfree(gsc);
> +
> + dev_info(&pdev->dev, "%s driver unloaded\n", pdev->name);
> + return 0;
> +}
> +
> +static int gsc_suspend(struct device *dev)
> +{
> + struct platform_device *pdev;
> + struct gsc_dev *gsc;
> + int ret = 0;
> +
> + pdev = to_platform_device(dev);
> + gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
> +
> + if (gsc_m2m_run(gsc)) {
> + set_bit(ST_STOP_REQ,&gsc->state);
> + ret = wait_event_timeout(gsc->irq_queue,
> + !test_bit(ST_STOP_REQ,&gsc->state),
> + GSC_SHUTDOWN_TIMEOUT);
> + if (ret == 0)
> + dev_err(&gsc->pdev->dev, "wait timeout : %s\n",
> + __func__);
> + }
> + if (gsc_cap_active(gsc)) {
> + gsc_err("capture device is running!!");
> + return -EINVAL;
> + }
> +
> + pm_runtime_put_sync(dev);
> +
> + return ret;
> +}
> +
> +static int gsc_resume(struct device *dev)
> +{
> + struct platform_device *pdev;
> + struct gsc_driverdata *drv_data;
> + struct gsc_dev *gsc;
> + struct gsc_ctx *ctx;
> +
> + pdev = to_platform_device(dev);
You could initalize pdev at the declaration line above.
> + gsc = (struct gsc_dev *)platform_get_drvdata(pdev);
Just do:
gsc = dev_get_drvdata(dev);
> + drv_data = (struct gsc_driverdata *)
> + platform_get_device_id(pdev)->driver_data;
drv_data is unused AFAICS.
> +
> + pm_runtime_get_sync(dev);
> + if (gsc_m2m_opened(gsc)) {
> + ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> + if (ctx != NULL) {
> + gsc->m2m.ctx = NULL;
> + v4l2_m2m_job_finish(gsc->m2m.m2m_dev, ctx->m2m_ctx);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops gsc_pm_ops = {
> + .suspend = gsc_suspend,
> + .resume = gsc_resume,
> + .runtime_suspend = gsc_runtime_suspend,
> + .runtime_resume = gsc_runtime_resume,
> +};
> +
> +struct gsc_pix_max gsc_v_100_max = {
> + .org_scaler_bypass_w = 8192,
> + .org_scaler_bypass_h = 8192,
> + .org_scaler_input_w = 4800,
> + .org_scaler_input_h = 3344,
> + .real_rot_dis_w = 4800,
> + .real_rot_dis_h = 3344,
> + .real_rot_en_w = 2047,
> + .real_rot_en_h = 2047,
> + .target_rot_dis_w = 4800,
> + .target_rot_dis_h = 3344,
> + .target_rot_en_w = 2016,
> + .target_rot_en_h = 2016,
> +};
> +
> +struct gsc_pix_min gsc_v_100_min = {
> + .org_w = 64,
> + .org_h = 32,
> + .real_w = 64,
> + .real_h = 32,
> + .target_rot_dis_w = 64,
> + .target_rot_dis_h = 32,
> + .target_rot_en_w = 32,
> + .target_rot_en_h = 16,
> +};
> +
> +struct gsc_pix_align gsc_v_100_align = {
> + .org_h = 16,
> + .org_w = 16, /* yuv420 : 16, others : 8 */
> + .offset_h = 2, /* yuv420/422 : 2, others : 1 */
> + .real_w = 16, /* yuv420/422 : 4~16, others : 2~8 */
> + .real_h = 16, /* yuv420 : 4~16, others : 1 */
> + .target_w = 2, /* yuv420/422 : 2, others : 1 */
> + .target_h = 2, /* yuv420 : 2, others : 1 */
> +};
> +
> +struct gsc_variant gsc_v_100_variant = {
> + .pix_max =&gsc_v_100_max,
> + .pix_min =&gsc_v_100_min,
> + .pix_align =&gsc_v_100_align,
> + .in_buf_cnt = 8,
> + .out_buf_cnt = 16,
> + .sc_up_max = 8,
> + .sc_down_max = 16,
> + .poly_sc_down_max = 4,
> + .pre_sc_down_max = 4,
> + .local_sc_down = 2,
> +};
> +
> +static struct gsc_driverdata gsc_v_100_drvdata = {
> + .variant = {
> + [0] =&gsc_v_100_variant,
> + [1] =&gsc_v_100_variant,
> + [2] =&gsc_v_100_variant,
> + [3] =&gsc_v_100_variant,
> + },
> + .num_entities = 4,
> + .lclk_frequency = 266000000UL,
> +};
> +
> +static struct platform_device_id gsc_driver_ids[] = {
> + {
> + .name = "exynos-gsc",
> + .driver_data = (unsigned long)&gsc_v_100_drvdata,
> + },
> + {},
> +};
> +MODULE_DEVICE_TABLE(platform, gsc_driver_ids);
> +
> +static struct platform_driver gsc_driver = {
> + .probe = gsc_probe,
> + .remove = __devexit_p(gsc_remove),
> + .id_table = gsc_driver_ids,
> + .driver = {
> + .name = GSC_MODULE_NAME,
> + .owner = THIS_MODULE,
> + .pm =&gsc_pm_ops,
> + }
> +};
> +
> +static int __init gsc_init(void)
> +{
> + int ret = platform_driver_register(&gsc_driver);
> + if (ret)
> + gsc_err("platform_driver_register failed: %d\n", ret);
> + return ret;
> +}
> +
> +static void __exit gsc_exit(void)
> +{
> + platform_driver_unregister(&gsc_driver);
> +}
> +
> +module_init(gsc_init);
> +module_exit(gsc_exit);
> +
> +MODULE_AUTHOR("Hyunwong Kim<khw0178.kim@samsung.com>");
> +MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/video/exynos/gsc/gsc-core.h b/drivers/media/video/exynos/gsc/gsc-core.h
> new file mode 100644
> index 0000000..5c65446
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-core.h
> @@ -0,0 +1,752 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-core.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * header file for Samsung EXYNOS5 SoC series G-scaler 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 GSC_CORE_H_
> +#define GSC_CORE_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>
> +#include<media/videobuf2-core.h>
> +#include<media/v4l2-ctrls.h>
> +#include<media/v4l2-device.h>
> +#include<media/v4l2-mem2mem.h>
> +#include<media/v4l2-mediabus.h>
> +#include<media/exynos_mc.h>
> +#include<media/exynos_gscaler.h>
> +#include<media/videobuf2-dma-contig.h>
> +#include "regs-gsc.h"
> +
> +extern int gsc_dbg;
> +
> +#define gsc_info(fmt, args...) \
> + do { \
> + if (gsc_dbg>= 6) \
> + printk(KERN_INFO "[INFO]%s:%d: "fmt "\n", \
> + __func__, __LINE__, ##args); \
> + } while (0)
> +
> +#define gsc_err(fmt, args...) \
> + do { \
> + if (gsc_dbg>= 3) \
> + printk(KERN_ERR "[ERROR]%s:%d: "fmt "\n", \
> + __func__, __LINE__, ##args); \
> + } while (0)
> +
> +#define gsc_warn(fmt, args...) \
> + do { \
> + if (gsc_dbg>= 4) \
> + printk(KERN_WARNING "[WARN]%s:%d: "fmt "\n", \
> + __func__, __LINE__, ##args); \
> + } while (0)
> +
> +#define gsc_dbg(fmt, args...) \
> + do { \
> + if (gsc_dbg>= 7) \
> + printk(KERN_DEBUG "[DEBUG]%s:%d: "fmt "\n", \
> + __func__, __LINE__, ##args); \
> + } while (0)
> +
> +#define GSC_MAX_CLOCKS 3
> +#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
> +#define GSC_MAX_DEVS 4
> +#define WORKQUEUE_NAME_SIZE 32
> +#define FIMD_NAME_SIZE 32
> +#define GSC_M2M_BUF_NUM 0
> +#define GSC_OUT_BUF_MAX 2
> +#define GSC_MAX_CTRL_NUM 10
> +#define GSC_OUT_MAX_MASK_NUM 7
> +#define GSC_SC_ALIGN_4 4
> +#define GSC_SC_ALIGN_2 2
> +#define GSC_OUT_DEF_SRC 15
> +#define GSC_OUT_DEF_DST 7
> +#define DEFAULT_GSC_SINK_WIDTH 800
> +#define DEFAULT_GSC_SINK_HEIGHT 480
> +#define DEFAULT_GSC_SOURCE_WIDTH 800
> +#define DEFAULT_GSC_SOURCE_HEIGHT 480
> +#define DEFAULT_CSC_EQ 1
> +#define DEFAULT_CSC_RANGE 1
> +
> +#define GSC_LAST_DEV_ID 3
> +#define GSC_PAD_SINK 0
> +#define GSC_PAD_SOURCE 1
> +#define GSC_PADS_NUM 2
> +
> +#define GSC_PARAMS (1<< 0)
> +#define GSC_SRC_FMT (1<< 1)
> +#define GSC_DST_FMT (1<< 2)
> +#define GSC_CTX_M2M (1<< 3)
> +#define GSC_CTX_OUTPUT (1<< 4)
> +#define GSC_CTX_START (1<< 5)
> +#define GSC_CTX_STOP_REQ (1<< 6)
> +#define GSC_CTX_CAP (1<< 10)
> +#define MAX_MDEV 2
> +
> +enum gsc_dev_flags {
> + /* for global */
> + ST_PWR_ON,
> + ST_STOP_REQ,
> + /* for m2m node */
> + ST_M2M_OPEN,
> + ST_M2M_RUN,
> + /* for output node */
> + ST_OUTPUT_OPEN,
> + ST_OUTPUT_STREAMON,
> + /* for capture node */
> + ST_CAPT_OPEN,
> + ST_CAPT_PEND,
> + ST_CAPT_RUN,
> + ST_CAPT_STREAM,
> + ST_CAPT_PIPE_STREAM,
> + ST_CAPT_SUSPENDED,
> + ST_CAPT_SHUT,
> + ST_CAPT_APPLY_CFG,
> + ST_CAPT_JPEG,
> +};
> +
> +enum gsc_cap_input_entity {
> + GSC_IN_NONE,
> + GSC_IN_FLITE_PREVIEW,
> + GSC_IN_FLITE_CAMCORDING,
> + GSC_IN_FIMD_WRITEBACK,
> +};
> +
> +enum gsc_irq {
> + GSC_OR_IRQ = 17,
> + GSC_DONE_IRQ = 16,
> +};
> +
> +/**
> + * enum gsc_datapath - the path of data used for gscaler
> + * @GSC_CAMERA: from camera
> + * @GSC_DMA: from/to DMA
> + * @GSC_LOCAL: to local path
> + * @GSC_WRITEBACK: from FIMD
> + */
> +enum gsc_datapath {
> + GSC_CAMERA = 0x1,
> + GSC_DMA,
> + GSC_MIXER,
> + GSC_FIMD,
> + GSC_WRITEBACK,
> +};
> +
> +enum gsc_color_fmt {
> + GSC_RGB = 0x1,
> + GSC_YUV420 = 0x2,
> + GSC_YUV422 = 0x4,
> + GSC_YUV444 = 0x8,
> +};
> +
> +enum gsc_yuv_fmt {
> + GSC_LSB_Y = 0x10,
> + GSC_LSB_C,
> + GSC_CBCR = 0x20,
> + GSC_CRCB,
> +};
> +
> +#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh)
> +#define is_rgb(x) (!!((x)& 0x1))
> +#define is_yuv420(x) (!!((x)& 0x2))
> +#define is_yuv422(x) (!!((x)& 0x4))
> +#define gsc_m2m_run(dev) test_bit(ST_M2M_RUN,&(dev)->state)
> +#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN,&(dev)->state)
> +#define gsc_out_run(dev) test_bit(ST_OUTPUT_STREAMON,&(dev)->state)
> +#define gsc_out_opened(dev) test_bit(ST_OUTPUT_OPEN,&(dev)->state)
> +#define gsc_cap_opened(dev) test_bit(ST_CAPT_OPEN,&(dev)->state)
> +#define gsc_cap_active(dev) test_bit(ST_CAPT_RUN,&(dev)->state)
> +
> +#define ctrl_to_ctx(__ctrl) \
> + container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler)
> +#define entity_data_to_gsc(data) \
> + container_of(data, struct gsc_dev, md_data)
> +#define gsc_capture_get_frame(ctx, pad)\
> + ((pad == GSC_PAD_SINK) ?&ctx->s_frame :&ctx->d_frame)
> +/**
> + * struct gsc_fmt - the driver's internal color format data
> + * @mbus_code: Media Bus pixel code, -1 if not applicable
> + * @name: format description
> + * @pixelformat: the fourcc code for this format, 0 if not applicable
> + * @yorder: Y/C order
> + * @corder: Chrominance order control
> + * @num_planes: number of physically non-contiguous data planes
> + * @nr_comp: number of physically contiguous data planes
> + * @depth: per plane driver's private 'number of bits per pixel'
> + * @flags: flags indicating which operation mode format applies to
> + */
> +struct gsc_fmt {
> + enum v4l2_mbus_pixelcode mbus_code;
> + char *name;
> + u32 pixelformat;
> + u32 color;
> + u32 yorder;
> + u32 corder;
> + u16 num_planes;
> + u16 nr_comp;
> + u8 depth[VIDEO_MAX_PLANES];
> + u32 flags;
> +};
> +
> +/**
> + * struct gsc_input_buf - the driver's video buffer
> + * @vb: videobuf2 buffer
> + * @list : linked list structure for buffer queue
> + * @idx : index of G-Scaler input buffer
> + */
> +struct gsc_input_buf {
> + struct vb2_buffer vb;
> + struct list_head list;
> + int idx;
> +};
> +
> +/**
> + * struct gsc_addr - the G-Scaler physical address set
> + * @y: luminance plane address
> + * @cb: Cb plane address
> + * @cr: Cr plane address
> + */
> +struct gsc_addr {
> + dma_addr_t y;
> + dma_addr_t cb;
> + dma_addr_t cr;
> +};
> +
> +/* struct gsc_ctrls - the G-Scaler control set
> + * @rotate: rotation degree
> + * @hflip: horizontal flip
> + * @vflip: vertical flip
> + * @global_alpha: the alpha value of current frame
> + * @layer_blend_en: enable mixer layer alpha blending
> + * @layer_alpha: set alpha value for mixer layer
> + * @pixel_blend_en: enable mixer pixel alpha blending
> + * @chroma_en: enable chromakey
> + * @chroma_val: set value for chromakey
> + * @csc_eq_mode: mode to select csc equation of current frame
> + * @csc_eq: csc equation of current frame
> + * @csc_range: csc range of current frame
> + */
> +struct gsc_ctrls {
> + struct v4l2_ctrl *rotate;
> + struct v4l2_ctrl *hflip;
> + struct v4l2_ctrl *vflip;
> + struct v4l2_ctrl *global_alpha;
> + struct v4l2_ctrl *layer_blend_en;
> + struct v4l2_ctrl *layer_alpha;
> + struct v4l2_ctrl *pixel_blend_en;
> + struct v4l2_ctrl *chroma_en;
> + struct v4l2_ctrl *chroma_val;
> + struct v4l2_ctrl *csc_eq_mode;
> + struct v4l2_ctrl *csc_eq;
> + struct v4l2_ctrl *csc_range;
Nice set of control pointers, but only rotate, hflip and vflip are used,
aren't they ?
> +};
> +
> +/**
> + * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler
> + * @pre_shfactor: pre sclaer shift factor
> + * @pre_hratio: horizontal ratio of the prescaler
> + * @pre_vratio: vertical ratio of the prescaler
> + * @main_hratio: the main scaler's horizontal ratio
> + * @main_vratio: the main scaler's vertical ratio
> + */
> +struct gsc_scaler {
> + u32 pre_shfactor;
> + u32 pre_hratio;
> + u32 pre_vratio;
> + unsigned long main_hratio;
> + unsigned long main_vratio;
> +};
> +
> +struct gsc_dev;
> +
> +struct gsc_ctx;
> +
> +/**
> + * struct gsc_frame - source/target frame properties
> + * @f_width: SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
> + * @f_height: SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
> + * @crop: cropped(source)/scaled(destination) size
> + * @payload: image size in bytes (w x h x bpp)
> + * @addr: image frame buffer physical addresses
> + * @fmt: G-scaler color format pointer
> + * @alph: frame's alpha value
> + */
> +struct gsc_frame {
> + u32 f_width;
> + u32 f_height;
> + struct v4l2_rect crop;
> + unsigned long payload[VIDEO_MAX_PLANES];
> + struct gsc_addr addr;
> + struct gsc_fmt *fmt;
> + u8 alpha;
> +};
> +
> +struct gsc_sensor_info {
> + struct exynos_isp_info *pdata;
> + struct v4l2_subdev *sd;
> + struct clk *camclk;
> +};
> +
> +struct gsc_capture_device {
> + struct gsc_ctx *ctx;
> + struct video_device *vfd;
> + struct v4l2_subdev *sd_cap;
> + struct v4l2_subdev *sd_disp;
> + struct v4l2_subdev *sd_flite[FLITE_MAX_ENTITIES];
> + struct v4l2_subdev *sd_csis[CSIS_MAX_ENTITIES];
> + struct gsc_sensor_info sensor[SENSOR_MAX_ENTITIES];
> + struct media_pad vd_pad;
> + struct media_pad sd_pads[GSC_PADS_NUM];
> + struct v4l2_mbus_framefmt mbus_fmt[GSC_PADS_NUM];
> + struct vb2_queue vbq;
> + int active_buf_cnt;
> + int buf_index;
> + int input_index;
> + int refcnt;
> + u32 frame_cnt;
> + u32 reqbufs_cnt;
> + enum gsc_cap_input_entity input;
> + u32 cam_index;
> +};
> +
> +/**
> + * struct gsc_output_device - v4l2 output device data
> + * @vfd: the video device node for v4l2 output mode
> + * @alloc_ctx: v4l2 memory-to-memory device data
> + * @ctx: hardware context data
> + * @sd: v4l2 subdev pointer of gscaler
> + * @vbq: videobuf2 queue of gscaler output device
> + * @vb_pad: the pad of gscaler video entity
> + * @sd_pads: pads of gscaler subdev entity
> + * @active_buf_q: linked list structure of input buffer
> + * @req_cnt: the number of requested buffer
> + */
> +struct gsc_output_device {
> + struct video_device *vfd;
> + struct vb2_alloc_ctx *alloc_ctx;
> + struct gsc_ctx *ctx;
> + struct v4l2_subdev *sd;
> + struct vb2_queue vbq;
> + struct media_pad vd_pad;
> + struct media_pad sd_pads[GSC_PADS_NUM];
> + struct list_head active_buf_q;
> + int req_cnt;
> +};
> +
> +/**
> + * struct gsc_m2m_device - v4l2 memory-to-memory device data
> + * @vfd: the video device node for v4l2 m2m mode
> + * @m2m_dev: v4l2 memory-to-memory device data
> + * @ctx: hardware context data
> + * @refcnt: the reference counter
> + */
> +struct gsc_m2m_device {
> + struct video_device *vfd;
> + struct v4l2_m2m_dev *m2m_dev;
> + struct gsc_ctx *ctx;
> + int refcnt;
> +};
> +
> +/**
> + * struct gsc_pix_max - image pixel size limits in various IP configurations
> + *
> + * @org_scaler_bypass_w: max pixel width when the scaler is disabled
> + * @org_scaler_bypass_h: max pixel height when the scaler is disabled
> + * @org_scaler_input_w: max pixel width when the scaler is enabled
> + * @org_scaler_input_h: max pixel height when the scaler is enabled
> + * @real_rot_dis_w: max pixel src cropped height with the rotator is off
> + * @real_rot_dis_h: max pixel src croppped width with the rotator is off
> + * @real_rot_en_w: max pixel src cropped width with the rotator is on
> + * @real_rot_en_h: max pixel src cropped height with the rotator is on
> + * @target_rot_dis_w: max pixel dst scaled width with the rotator is off
> + * @target_rot_dis_h: max pixel dst scaled height with the rotator is off
> + * @target_rot_en_w: max pixel dst scaled width with the rotator is on
> + * @target_rot_en_h: max pixel dst scaled height with the rotator is on
> + */
> +struct gsc_pix_max {
> + u16 org_scaler_bypass_w;
> + u16 org_scaler_bypass_h;
> + u16 org_scaler_input_w;
> + u16 org_scaler_input_h;
> + u16 real_rot_dis_w;
> + u16 real_rot_dis_h;
> + u16 real_rot_en_w;
> + u16 real_rot_en_h;
> + u16 target_rot_dis_w;
> + u16 target_rot_dis_h;
> + u16 target_rot_en_w;
> + u16 target_rot_en_h;
> +};
> +
> +/**
> + * struct gsc_pix_min - image pixel size limits in various IP configurations
> + *
> + * @org_w: minimum source pixel width
> + * @org_h: minimum source pixel height
> + * @real_w: minimum input crop pixel width
> + * @real_h: minimum input crop pixel height
> + * @target_rot_dis_w: minimum output scaled pixel height when rotator is off
> + * @target_rot_dis_h: minimum output scaled pixel height when rotator is off
> + * @target_rot_en_w: minimum output scaled pixel height when rotator is on
> + * @target_rot_en_h: minimum output scaled pixel height when rotator is on
> + */
> +struct gsc_pix_min {
> + u16 org_w;
> + u16 org_h;
> + u16 real_w;
> + u16 real_h;
> + u16 target_rot_dis_w;
> + u16 target_rot_dis_h;
> + u16 target_rot_en_w;
> + u16 target_rot_en_h;
> +};
> +
> +struct gsc_pix_align {
> + u16 org_h;
> + u16 org_w;
> + u16 offset_h;
> + u16 real_w;
> + u16 real_h;
> + u16 target_w;
> + u16 target_h;
> +};
> +
> +/**
> + * struct gsc_variant - G-Scaler variant information
> + */
> +struct gsc_variant {
> + struct gsc_pix_max *pix_max;
> + struct gsc_pix_min *pix_min;
> + struct gsc_pix_align *pix_align;
> + u16 in_buf_cnt;
> + u16 out_buf_cnt;
> + u16 sc_up_max;
> + u16 sc_down_max;
> + u16 poly_sc_down_max;
> + u16 pre_sc_down_max;
> + u16 local_sc_down;
> +};
> +
> +/**
> + * struct gsc_driverdata - per device type driver data for init time.
> + *
> + * @variant: the variant information for this driver.
> + * @lclk_frequency: g-scaler clock frequency
> + * @num_entities: the number of g-scalers
> + */
> +struct gsc_driverdata {
> + struct gsc_variant *variant[GSC_MAX_DEVS];
> + unsigned long lclk_frequency;
> + int num_entities;
> +};
> +
> +struct gsc_pipeline {
> + struct media_pipeline *pipe;
> + struct v4l2_subdev *sd_gsc;
> + struct v4l2_subdev *disp;
> + struct v4l2_subdev *flite;
> + struct v4l2_subdev *csis;
> + struct v4l2_subdev *sensor;
> +};
> +
> +/**
> + * struct gsc_dev - abstraction for G-Scaler entity
> + * @slock: the spinlock protecting this data structure
> + * @lock: the mutex protecting this data structure
> + * @pdev: pointer to the G-Scaler platform device
> + * @variant: the IP variant information
> + * @id: g_scaler device index (0..GSC_MAX_DEVS)
> + * @regs: the mapped hardware registers
> + * @regs_res: the resource claimed for IO registers
> + * @irq: G-scaler interrupt number
> + * @irq_queue: interrupt handler waitqueue
> + * @m2m: memory-to-memory V4L2 device information
> + * @out: memory-to-local V4L2 output device information
> + * @state: flags used to synchronize m2m and capture mode operation
> + * @alloc_ctx: videobuf2 memory allocator context
> + * @vb2: videobuf2 memory allocator call-back functions
> + * @mdev: pointer to exynos media device
> + * @pipeline: pointer to subdevs that are connected with gscaler
> + */
> +struct gsc_dev {
> + spinlock_t slock;
> + struct mutex lock;
> + struct platform_device *pdev;
> + struct gsc_variant *variant;
> + u16 id;
> + struct clk *clock;
> + void __iomem *regs;
> + struct resource *regs_res;
> + int irq;
> + wait_queue_head_t irq_queue;
> + struct work_struct work_struct;
> + struct workqueue_struct *irq_workqueue;
> + struct gsc_m2m_device m2m;
> + struct gsc_output_device out;
> + struct gsc_capture_device cap;
> + struct exynos_platform_gscaler *pdata;
> + unsigned long state;
> + struct vb2_alloc_ctx *alloc_ctx;
> + struct exynos_md *mdev[MAX_MDEV];
> + struct gsc_pipeline pipeline;
> + struct exynos_entity_data md_data;
> +};
> +
> +/**
> + * gsc_ctx - the device context data
> + * @slock: spinlock protecting this data structure
> + * @s_frame: source frame properties
> + * @d_frame: destination frame properties
> + * @in_path: input mode (DMA or camera)
> + * @out_path: output mode (DMA or FIFO)
> + * @scaler: image scaler properties
> + * @flags: additional flags for image conversion
> + * @state: flags to keep track of user configuration
> + * @gsc_dev: the g-scaler device this context applies to
> + * @m2m_ctx: memory-to-memory device context
> + * @fh: v4l2 file handle
> + * @ctrl_handler: v4l2 controls handler
> + * @ctrls_rdy: true if the control handler is initialized
> + * @gsc_ctrls G-Scaler control set
> + * @m2m_ctx: memory-to-memory device context
> + */
> +struct gsc_ctx {
> + spinlock_t slock;
> + struct gsc_frame s_frame;
> + struct gsc_frame d_frame;
> + enum gsc_datapath in_path;
> + enum gsc_datapath out_path;
> + struct gsc_scaler scaler;
> + u32 flags;
> + u32 state;
> + struct gsc_dev *gsc_dev;
> + struct v4l2_m2m_ctx *m2m_ctx;
> + struct v4l2_fh fh;
> + struct v4l2_ctrl_handler ctrl_handler;
> + struct gsc_ctrls gsc_ctrls;
> + bool ctrls_rdy;
> +};
> +
> +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame frm);
> +void gsc_clk_release(struct gsc_dev *gsc);
> +int gsc_register_m2m_device(struct gsc_dev *gsc);
> +void gsc_unregister_m2m_device(struct gsc_dev *gsc);
> +int gsc_register_output_device(struct gsc_dev *gsc);
> +void gsc_unregister_output_device(struct gsc_dev *gsc);
> +int gsc_register_capture_device(struct gsc_dev *gsc);
> +void gsc_unregister_capture_device(struct gsc_dev *gsc);
> +
> +u32 get_plane_size(struct gsc_frame *fr, unsigned int plane);
> +char gsc_total_fmts(void);
> +struct gsc_fmt *get_format(int index);
> +struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, int index);
> +int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f);
> +int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
> +void gsc_set_frame_size(struct gsc_frame *frame, int width, int height);
> +int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
> +void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h);
> +int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
> +int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
> +int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst, u32 *ratio);
> +void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh);
> +void gsc_check_src_scale_info(struct gsc_variant *var, struct gsc_frame *s_frame,
> + u32 *wratio, u32 tx, u32 ty, u32 *hratio);
> +int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
> + int dh, int rot, int out_path);
> +int gsc_set_scaler_info(struct gsc_ctx *ctx);
> +int gsc_ctrls_create(struct gsc_ctx *ctx);
> +void gsc_ctrls_delete(struct gsc_ctx *ctx);
> +int gsc_out_hw_set(struct gsc_ctx *ctx);
> +int gsc_out_set_in_addr(struct gsc_dev *gsc, struct gsc_ctx *ctx,
> + struct gsc_input_buf *buf, int index);
> +int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
> + struct gsc_frame *frame, struct gsc_addr *addr);
> +int gsc_out_link_validate(const struct media_pad *source,
> + const struct media_pad *sink);
> +int gsc_pipeline_s_stream(struct gsc_dev *gsc, bool on);
> +
> +static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ctx->slock, flags);
> + ctx->state |= state;
> + spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
> +static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ctx->slock, flags);
> + ctx->state&= ~state;
> + spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
> +static inline int get_win_num(struct gsc_dev *dev)
> +{
> + return (dev->id == 3) ? 2 : dev->id;
> +}
> +
> +static inline int is_output(enum v4l2_buf_type type)
> +{
> + return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
> + type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 1 : 0;
> +}
> +
> +static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on)
> +{
> + u32 cfg = readl(dev->regs + GSC_ENABLE);
> +
> + if (on)
> + cfg |= GSC_ENABLE_ON;
> + else
> + cfg&= ~GSC_ENABLE_ON;
> +
> + writel(cfg, dev->regs + GSC_ENABLE);
> +}
> +
> +static inline int gsc_hw_get_irq_status(struct gsc_dev *dev)
> +{
> + u32 cfg = readl(dev->regs + GSC_IRQ);
> + if (cfg& (1<< GSC_OR_IRQ))
> + return GSC_OR_IRQ;
> + else
> + return GSC_DONE_IRQ;
> +
> +}
> +
> +static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq)
> +{
> + u32 cfg = readl(dev->regs + GSC_IRQ);
> + if (irq == GSC_OR_IRQ)
> + cfg |= GSC_IRQ_STATUS_OR_IRQ;
> + else if (irq == GSC_DONE_IRQ)
> + cfg |= GSC_IRQ_STATUS_OR_FRM_DONE;
> + writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +static inline void gsc_lock(struct vb2_queue *vq)
> +{
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
> + mutex_lock(&ctx->gsc_dev->lock);
> +}
> +
> +static inline void gsc_unlock(struct vb2_queue *vq)
> +{
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
> + mutex_unlock(&ctx->gsc_dev->lock);
> +}
> +
> +static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx)
> +{
> + unsigned long flags;
> + bool ret;
> +
> + spin_lock_irqsave(&ctx->slock, flags);
> + ret = (ctx->state& mask) == mask;
> + spin_unlock_irqrestore(&ctx->slock, flags);
> + return ret;
> +}
> +
> +static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx,
> + enum v4l2_buf_type type)
> +{
> + struct gsc_frame *frame;
> +
> + if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
> + frame =&ctx->s_frame;
> + } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
> + frame =&ctx->d_frame;
> + } else {
> + gsc_err("Wrong buffer/video queue type (%d)", type);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + return frame;
> +}
> +
> +static inline struct gsc_input_buf *
> +active_queue_pop(struct gsc_output_device *vid_out, struct gsc_dev *dev)
> +{
> + struct gsc_input_buf *buf;
> +
> + buf = list_entry(vid_out->active_buf_q.next, struct gsc_input_buf, list);
> + return buf;
> +}
> +
> +static inline void active_queue_push(struct gsc_output_device *vid_out,
> + struct gsc_input_buf *buf, struct gsc_dev *dev)
> +{
> + unsigned long flags;
> + spin_lock_irqsave(&dev->slock, flags);
> + list_add_tail(&buf->list,&vid_out->active_buf_q);
> + spin_unlock_irqrestore(&dev->slock, flags);
> +}
> +
> +static inline struct gsc_dev *entity_to_gsc(struct media_entity *me)
> +{
> + struct v4l2_subdev *sd;
> +
> + sd = container_of(me, struct v4l2_subdev, entity);
> + return entity_data_to_gsc(v4l2_get_subdevdata(sd));
> +}
> +
> +static inline void user_to_drv(struct v4l2_ctrl *ctrl, s32 value)
> +{
> + ctrl->cur.val = ctrl->val = value;
> +}
> +
> +void gsc_hw_set_sw_reset(struct gsc_dev *dev);
> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask);
> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev);
> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev);
> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, int index);
> +void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr, int index);
> +void gsc_hw_set_input_path(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_size(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx);
> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx);
> +void gsc_hw_set_output_path(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_size(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx);
> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx);
> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx);
> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx);
> +void gsc_hw_set_rotation(struct gsc_ctx *ctx);
> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx);
> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx);
> +void gsc_hw_set_local_dst(int id, bool on);
> +void gsc_hw_set_sysreg_writeback(struct gsc_ctx *ctx);
> +void gsc_hw_set_sysreg_camif(bool on);
> +
> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev);
> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev);
> +int gsc_hw_get_done_output_buf_index(struct gsc_dev *dev);
> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev);
> +int gsc_wait_reset(struct gsc_dev *dev);
> +int gsc_wait_operating(struct gsc_dev *dev);
> +int gsc_wait_stop(struct gsc_dev *dev);
> +
> +void gsc_disp_fifo_sw_reset(struct gsc_dev *dev);
> +void gsc_pixelasync_sw_reset(struct gsc_dev *dev);
> +
> +
> +#endif /* GSC_CORE_H_ */
> diff --git a/drivers/media/video/exynos/gsc/gsc-m2m.c b/drivers/media/video/exynos/gsc/gsc-m2m.c
> new file mode 100644
> index 0000000..a00a642
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-m2m.c
> @@ -0,0 +1,696 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-m2m.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/types.h>
> +#include<linux/errno.h>
> +#include<linux/bug.h>
> +#include<linux/interrupt.h>
> +#include<linux/workqueue.h>
> +#include<linux/device.h>
> +#include<linux/platform_device.h>
> +#include<linux/list.h>
> +#include<linux/io.h>
> +#include<linux/slab.h>
> +#include<linux/clk.h>
> +#include<media/v4l2-ioctl.h>
> +
> +#include "gsc-core.h"
> +
> +static int gsc_ctx_stop_req(struct gsc_ctx *ctx)
> +{
> + struct gsc_ctx *curr_ctx;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + int ret = 0;
> +
> + curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
> + if (!gsc_m2m_run(gsc) || (curr_ctx != ctx))
> + return 0;
> + ctx->state |= GSC_CTX_STOP_REQ;
> + ret = wait_event_timeout(gsc->irq_queue,
> + !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
> + GSC_SHUTDOWN_TIMEOUT);
> + if (!ret)
> + ret = -EBUSY;
> +
> + return ret;
> +}
> +
> +static int gsc_m2m_stop_streaming(struct vb2_queue *q)
> +{
> + struct gsc_ctx *ctx = q->drv_priv;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + int ret;
> +
> + ret = gsc_ctx_stop_req(ctx);
> + /* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
> + if (ret< 0)
> + dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
> +
> + return 0;
> +}
> +
> +static void gsc_m2m_job_abort(void *priv)
> +{
> + struct gsc_ctx *ctx = priv;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + int ret;
> +
> + ret = gsc_ctx_stop_req(ctx);
> + /* FIXME: need to add v4l2_m2m_job_finish(fail) if ret is timeout */
> + if (ret< 0)
> + dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__);
> +}
> +
> +int gsc_fill_addr(struct gsc_ctx *ctx)
> +{
> + struct gsc_frame *s_frame, *d_frame;
> + struct vb2_buffer *vb = NULL;
> + int ret = 0;
> +
> + s_frame =&ctx->s_frame;
> + d_frame =&ctx->d_frame;
> +
> + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
> + ret = gsc_prepare_addr(ctx, vb, s_frame,&s_frame->addr);
> + if (ret)
> + return ret;
> +
> + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
> + ret = gsc_prepare_addr(ctx, vb, d_frame,&d_frame->addr);
> +
> + return ret;
> +}
> +
> +static void gsc_m2m_device_run(void *priv)
> +{
> + struct gsc_ctx *ctx = priv;
> + struct gsc_dev *gsc;
> + unsigned long flags;
> + u32 ret;
> + bool is_set = false;
> +
> + if (WARN(!ctx, "null hardware context\n"))
> + return;
> +
> + gsc = ctx->gsc_dev;
> + pm_runtime_get_sync(&gsc->pdev->dev);
> +
> + spin_lock_irqsave(&ctx->slock, flags);
> + /* Reconfigure hardware if the context has changed. */
> + if (gsc->m2m.ctx != ctx) {
> + gsc_dbg("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
> + gsc->m2m.ctx, ctx);
> + ctx->state |= GSC_PARAMS;
> + gsc->m2m.ctx = ctx;
> + }
> +
> + is_set = (ctx->state& GSC_CTX_STOP_REQ) ? 1 : 0;
> + ctx->state&= ~GSC_CTX_STOP_REQ;
> + if (is_set) {
> + wake_up(&gsc->irq_queue);
> + goto put_device;
> + }
> +
> + ret = gsc_fill_addr(ctx);
> + if (ret) {
> + gsc_err("Wrong address");
> + goto put_device;
> + }
> +
> + gsc_set_prefbuf(gsc, ctx->s_frame);
> + gsc_hw_set_input_addr(gsc,&ctx->s_frame.addr, GSC_M2M_BUF_NUM);
> + gsc_hw_set_output_addr(gsc,&ctx->d_frame.addr, GSC_M2M_BUF_NUM);
> +
> + if (ctx->state& GSC_PARAMS) {
> + gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
> + gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
> + gsc_hw_set_frm_done_irq_mask(gsc, false);
> + gsc_hw_set_gsc_irq_enable(gsc, true);
> +
> + if (gsc_set_scaler_info(ctx)) {
> + gsc_err("Scaler setup error");
> + goto put_device;
> + }
> +
> + gsc_hw_set_input_path(ctx);
> + gsc_hw_set_in_size(ctx);
> + gsc_hw_set_in_image_format(ctx);
> +
> + gsc_hw_set_output_path(ctx);
> + gsc_hw_set_out_size(ctx);
> + gsc_hw_set_out_image_format(ctx);
> +
> + gsc_hw_set_prescaler(ctx);
> + gsc_hw_set_mainscaler(ctx);
> + gsc_hw_set_rotation(ctx);
> + gsc_hw_set_global_alpha(ctx);
> + }
> + /* When you update SFRs in the middle of operating
> + gsc_hw_set_sfr_update(ctx);
> + */
> +
> + ctx->state&= ~GSC_PARAMS;
> +
> + if (!test_and_set_bit(ST_M2M_RUN,&gsc->state)) {
> + /* One frame mode sequence
> + GSCALER_ON on -> GSCALER_OP_STATUS is operating ->
> + GSCALER_ON off */
> + gsc_hw_enable_control(gsc, true);
> + ret = gsc_wait_operating(gsc);
> + if (ret< 0) {
> + gsc_err("gscaler wait operating timeout");
> + goto put_device;
> + }
> + gsc_hw_enable_control(gsc, false);
> + }
> +
> + spin_unlock_irqrestore(&ctx->slock, flags);
> + return;
> +
> +put_device:
> + ctx->state&= ~GSC_PARAMS;
> + spin_unlock_irqrestore(&ctx->slock, flags);
> + pm_runtime_put_sync(&gsc->pdev->dev);
> +}
> +
> +static int gsc_m2m_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> + unsigned int *num_buffers, unsigned int *num_planes,
> + unsigned int sizes[], void *allocators[])
> +{
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
> + struct gsc_frame *frame;
> + int i;
> +
> + frame = ctx_get_frame(ctx, vq->type);
> + if (IS_ERR(frame))
> + return PTR_ERR(frame);
> +
> + if (!frame->fmt)
> + return -EINVAL;
> +
> + *num_planes = frame->fmt->num_planes;
> + for (i = 0; i< frame->fmt->num_planes; i++) {
> + sizes[i] = get_plane_size(frame, i);
> + allocators[i] = ctx->gsc_dev->alloc_ctx;
> + }
> + return 0;
> +}
> +
> +static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
> +{
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> + struct gsc_frame *frame;
> + int i;
> +
> + frame = ctx_get_frame(ctx, vb->vb2_queue->type);
> + if (IS_ERR(frame))
> + return PTR_ERR(frame);
> +
> + if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
> + for (i = 0; i< frame->fmt->num_planes; i++)
> + vb2_set_plane_payload(vb, i, frame->payload[i]);
> + }
> +
> + return 0;
> +}
> +
> +static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
> +{
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +
> + gsc_dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
> +
> + if (ctx->m2m_ctx)
> + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
> +}
> +
> +struct vb2_ops gsc_m2m_qops = {
> + .queue_setup = gsc_m2m_queue_setup,
> + .buf_prepare = gsc_m2m_buf_prepare,
> + .buf_queue = gsc_m2m_buf_queue,
> + .wait_prepare = gsc_unlock,
> + .wait_finish = gsc_lock,
> + .stop_streaming = gsc_m2m_stop_streaming,
> +};
> +
> +static int gsc_m2m_querycap(struct file *file, void *fh,
> + struct v4l2_capability *cap)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + struct gsc_dev *gsc = ctx->gsc_dev;
> +
> + strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
> + strncpy(cap->card, gsc->pdev->name, sizeof(cap->card) - 1);
> + cap->bus_info[0] = 0;
> + cap->capabilities = V4L2_CAP_STREAMING |
> + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
This line should be removed, only multi-planar API is supported, right ?
> + V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
> +
> + return 0;
> +}
> +
> +static int gsc_m2m_enum_fmt_mplane(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + return gsc_enum_fmt_mplane(f);
> +}
> +
> +static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> + if ((f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)&&
> + (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
> + return -EINVAL;
This check is superfluous.
> +
> + return gsc_g_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> + if ((f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)&&
> + (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
> + return -EINVAL;
Ditto.
> + return gsc_try_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + struct vb2_queue *vq;
> + struct gsc_frame *frame;
> + struct v4l2_pix_format_mplane *pix;
> + int i, ret = 0;
> +
> + ret = gsc_m2m_try_fmt_mplane(file, fh, f);
> + if (ret)
> + return ret;
> +
> + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
> +
> + if (vb2_is_streaming(vq)) {
> + gsc_err("queue (%d) busy", f->type);
> + return -EBUSY;
> + }
> +
> + if (V4L2_TYPE_IS_OUTPUT(f->type)) {
> + frame =&ctx->s_frame;
> + } else {
> + frame =&ctx->d_frame;
> + }
No need for brackets.
> +
> + pix =&f->fmt.pix_mp;
> + frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
> + if (!frame->fmt)
> + return -EINVAL;
> +
> + for (i = 0; i< frame->fmt->num_planes; i++)
> + frame->payload[i] = pix->plane_fmt[i].sizeimage;
> +
> + gsc_set_frame_size(frame, pix->width, pix->height);
> +
> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
> + else
> + gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
> +
> + gsc_dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
> +
> + return 0;
> +}
> +
> +static int gsc_m2m_reqbufs(struct file *file, void *fh,
> + struct v4l2_requestbuffers *reqbufs)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + struct gsc_frame *frame;
> + u32 max_cnt;
> +
> + max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
> + gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
> + if (reqbufs->count> max_cnt)
> + return -EINVAL;
> + else if (reqbufs->count == 0) {
> + if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
> + else
> + gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
> + }
> +
> + frame = ctx_get_frame(ctx, reqbufs->type);
> +
> + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
> +}
> +
> +static int gsc_m2m_querybuf(struct file *file, void *fh,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_qbuf(struct file *file, void *fh,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_dqbuf(struct file *file, void *fh,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int gsc_m2m_streamon(struct file *file, void *fh,
> + enum v4l2_buf_type type)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> + /* The source and target color format need to be set */
> + if (V4L2_TYPE_IS_OUTPUT(type)) {
> + if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
> + return -EINVAL;
> + } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
> + return -EINVAL;
> + }
> +
> + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
> +}
> +
> +static int gsc_m2m_streamoff(struct file *file, void *fh,
> + enum v4l2_buf_type type)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
> +}
> +
> +static int gsc_m2m_cropcap(struct file *file, void *fh,
> + struct v4l2_cropcap *cr)
> +{
> + struct gsc_frame *frame;
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> + frame = ctx_get_frame(ctx, cr->type);
> + if (IS_ERR(frame))
> + return PTR_ERR(frame);
> +
> + cr->bounds.left = 0;
> + cr->bounds.top = 0;
> + cr->bounds.width = frame->f_width;
> + cr->bounds.height = frame->f_height;
> + cr->defrect = cr->bounds;
> +
> + return 0;
> +}
> +
> +static int gsc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> +
> + return gsc_g_crop(ctx, cr);
> +}
> +
> +static int gsc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(fh);
> + struct gsc_variant *variant = ctx->gsc_dev->variant;
> + struct gsc_frame *f;
> + int ret;
> +
> + ret = gsc_try_crop(ctx, cr);
> + if (ret)
> + return ret;
> +
> + f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
> + &ctx->s_frame :&ctx->d_frame;
> +
> + /* Check to see if scaling ratio is within supported range */
> + if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
> + if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + ret = gsc_check_scaler_ratio(variant, cr->c.width,
> + cr->c.height, ctx->d_frame.crop.width,
> + ctx->d_frame.crop.height,
> + ctx->gsc_ctrls.rotate->val, ctx->out_path);
> + } else {
> + ret = gsc_check_scaler_ratio(variant, ctx->s_frame.crop.width,
> + ctx->s_frame.crop.height, cr->c.width,
> + cr->c.height, ctx->gsc_ctrls.rotate->val,
> + ctx->out_path);
> + }
> + if (ret) {
> + gsc_err("Out of scaler range");
> + return -EINVAL;
> + }
> + }
> +
> + f->crop.left = cr->c.left;
> + f->crop.top = cr->c.top;
> + f->crop.width = cr->c.width;
> + f->crop.height = cr->c.height;
> +
> + gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
> +
> + return 0;
> +}
What about using the selection API instead ?
> +
> +static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
> + .vidioc_querycap = gsc_m2m_querycap,
> +
> + .vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane,
> + .vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane,
> +
> + .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane,
> + .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane,
> +
> + .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane,
> + .vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane,
> +
> + .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane,
> + .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane,
> +
> + .vidioc_reqbufs = gsc_m2m_reqbufs,
> + .vidioc_querybuf = gsc_m2m_querybuf,
> +
> + .vidioc_qbuf = gsc_m2m_qbuf,
> + .vidioc_dqbuf = gsc_m2m_dqbuf,
> +
> + .vidioc_streamon = gsc_m2m_streamon,
> + .vidioc_streamoff = gsc_m2m_streamoff,
> +
> + .vidioc_g_crop = gsc_m2m_g_crop,
> + .vidioc_s_crop = gsc_m2m_s_crop,
> + .vidioc_cropcap = gsc_m2m_cropcap
> +
> +};
> +
> +static int queue_init(void *priv, struct vb2_queue *src_vq,
> + struct vb2_queue *dst_vq)
> +{
> + struct gsc_ctx *ctx = priv;
> + int ret;
> +
> + memset(src_vq, 0, sizeof(*src_vq));
> + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> + src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
> + src_vq->drv_priv = ctx;
> + src_vq->ops =&gsc_m2m_qops;
> + src_vq->mem_ops =&vb2_dma_contig_memops;
> + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> +
> + ret = vb2_queue_init(src_vq);
> + if (ret)
> + return ret;
> +
> + memset(dst_vq, 0, sizeof(*dst_vq));
> + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
> + dst_vq->drv_priv = ctx;
> + dst_vq->ops =&gsc_m2m_qops;
> + dst_vq->mem_ops =&vb2_dma_contig_memops;
> + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> +
> + return vb2_queue_init(dst_vq);
> +}
> +
> +static int gsc_m2m_open(struct file *file)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = NULL;
> + int ret;
> +
> + gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> + if (gsc_out_opened(gsc) || gsc_cap_opened(gsc))
> + return -EBUSY;
> +
> + ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
> + ret = gsc_ctrls_create(ctx);
> + if (ret)
> + goto error_fh;
> +
> + /* Use separate control handler per file handle */
> + ctx->fh.ctrl_handler =&ctx->ctrl_handler;
> + file->private_data =&ctx->fh;
> + v4l2_fh_add(&ctx->fh);
> +
> + ctx->gsc_dev = gsc;
> + /* Default color format */
> + ctx->s_frame.fmt = get_format(0);
> + ctx->d_frame.fmt = get_format(0);
> + /* Setup the device context for mem2mem mode. */
> + ctx->state |= GSC_CTX_M2M;
> + ctx->flags = 0;
> + ctx->in_path = GSC_DMA;
> + ctx->out_path = GSC_DMA;
> + spin_lock_init(&ctx->slock);
> +
> + ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
> + if (IS_ERR(ctx->m2m_ctx)) {
> + gsc_err("Failed to initialize m2m context");
> + ret = PTR_ERR(ctx->m2m_ctx);
> + goto error_fh;
> + }
> +
> + if (gsc->m2m.refcnt++ == 0)
> + set_bit(ST_M2M_OPEN,&gsc->state);
> +
> + gsc_dbg("gsc m2m driver is opened, ctx(0x%p)", ctx);
> + return 0;
> +
> +error_fh:
> + v4l2_fh_del(&ctx->fh);
> + v4l2_fh_exit(&ctx->fh);
> + kfree(ctx);
> + return ret;
> +}
> +
> +static int gsc_m2m_release(struct file *file)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> + struct gsc_dev *gsc = ctx->gsc_dev;
> +
> + gsc_dbg("pid: %d, state: 0x%lx, refcnt= %d",
> + task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
> +
> + v4l2_m2m_ctx_release(ctx->m2m_ctx);
> + gsc_ctrls_delete(ctx);
> + v4l2_fh_del(&ctx->fh);
> + v4l2_fh_exit(&ctx->fh);
> +
> + if (--gsc->m2m.refcnt<= 0)
> + clear_bit(ST_M2M_OPEN,&gsc->state);
> + kfree(ctx);
> + return 0;
> +}
> +
> +static unsigned int gsc_m2m_poll(struct file *file,
> + struct poll_table_struct *wait)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
> +}
> +
> +static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> + struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
> +
> + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
> +}
> +static const struct v4l2_file_operations gsc_m2m_fops = {
> + .owner = THIS_MODULE,
> + .open = gsc_m2m_open,
> + .release = gsc_m2m_release,
> + .poll = gsc_m2m_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = gsc_m2m_mmap,
> +};
> +
> +static struct v4l2_m2m_ops gsc_m2m_ops = {
> + .device_run = gsc_m2m_device_run,
> + .job_abort = gsc_m2m_job_abort,
> +};
> +
> +int gsc_register_m2m_device(struct gsc_dev *gsc)
> +{
> + struct video_device *vfd;
> + struct platform_device *pdev;
> + int ret = 0;
> +
> + if (!gsc)
> + return -ENODEV;
> +
> + pdev = gsc->pdev;
> +
> + vfd = video_device_alloc();
> + if (!vfd) {
> + dev_err(&pdev->dev, "Failed to allocate video device\n");
> + return -ENOMEM;
> + }
> +
> + vfd->fops =&gsc_m2m_fops;
> + vfd->ioctl_ops =&gsc_m2m_ioctl_ops;
> + vfd->release = video_device_release;
> + vfd->lock =&gsc->lock;
> + snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev));
Please use some hard coded name, parametrized with pdev->id. Using dev_name()
will become problematic on DT platforms.
> + video_set_drvdata(vfd, gsc);
> +
> + gsc->m2m.vfd = vfd;
> + gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
> + if (IS_ERR(gsc->m2m.m2m_dev)) {
> + dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
> + ret = PTR_ERR(gsc->m2m.m2m_dev);
> + goto err_m2m_r1;
> + }
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "%s(): failed to register video device\n", __func__);
> + goto err_m2m_r2;
> + }
> +
> + gsc_dbg("gsc m2m driver registered as /dev/video%d", vfd->num);
> +
> + return 0;
> +
> +err_m2m_r2:
> + v4l2_m2m_release(gsc->m2m.m2m_dev);
> +err_m2m_r1:
> + video_device_release(gsc->m2m.vfd);
> +
> + return ret;
> +}
> +
> +void gsc_unregister_m2m_device(struct gsc_dev *gsc)
> +{
> + if (gsc)
> + v4l2_m2m_release(gsc->m2m.m2m_dev);
> +}
> diff --git a/drivers/media/video/exynos/gsc/gsc-output.c b/drivers/media/video/exynos/gsc/gsc-output.c
> new file mode 100644
> index 0000000..c460d7c
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-output.c
> @@ -0,0 +1,1034 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-output.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/version.h>
> +#include<linux/types.h>
> +#include<linux/errno.h>
> +#include<linux/bug.h>
> +#include<linux/interrupt.h>
> +#include<linux/workqueue.h>
> +#include<linux/device.h>
> +#include<linux/platform_device.h>
> +#include<linux/list.h>
> +#include<linux/io.h>
> +#include<linux/slab.h>
> +#include<linux/clk.h>
> +#include<linux/string.h>
> +#include<linux/delay.h>
> +#include<media/v4l2-ioctl.h>
> +
> +#include "gsc-core.h"
> +
> +int gsc_out_hw_reset_off (struct gsc_dev *gsc)
> +{
> + int ret;
> +
> + mdelay(1);
usleep_range() might be better.
> + gsc_hw_set_sw_reset(gsc);
> + ret = gsc_wait_reset(gsc);
> + if (ret< 0) {
> + gsc_err("gscaler s/w reset timeout");
> + return ret;
> + }
> + gsc_pixelasync_sw_reset(gsc);
> + gsc_disp_fifo_sw_reset(gsc);
> + gsc_hw_enable_control(gsc, false);
> + ret = gsc_wait_stop(gsc);
> + if (ret< 0) {
> + gsc_err("gscaler stop timeout");
> + return ret;
> + }
> +
> + return 0;
it could be simplified to:
if (ret < 0)
gsc_err("gscaler stop timeout");
return ret;
}
> +}
> +
> +int gsc_out_hw_set(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + int ret = 0;
> +
> + ret = gsc_set_scaler_info(ctx);
> + if (ret) {
> + gsc_err("Scaler setup error");
> + return ret;
> + }
> + gsc_hw_set_frm_done_irq_mask(gsc, false);
> + gsc_hw_set_gsc_irq_enable(gsc, true);
> +
> + gsc_hw_set_input_path(ctx);
> + gsc_hw_set_in_size(ctx);
> + gsc_hw_set_in_image_format(ctx);
> +
> + gsc_hw_set_output_path(ctx);
> + gsc_hw_set_out_size(ctx);
> + gsc_hw_set_out_image_format(ctx);
> +
> + gsc_hw_set_prescaler(ctx);
> + gsc_hw_set_mainscaler(ctx);
> + gsc_hw_set_rotation(ctx);
> + gsc_hw_set_global_alpha(ctx);
> + gsc_hw_set_input_buf_mask_all(gsc);
> +
> + return 0;
> +}
> +
> +static void gsc_subdev_try_crop(struct gsc_dev *gsc, struct v4l2_rect *cr)
> +{
> + struct gsc_variant *variant = gsc->variant;
> + u32 max_w, max_h, min_w, min_h;
> + u32 tmp_w, tmp_h;
> +
> + if (gsc->out.ctx->gsc_ctrls.rotate->val == 90 ||
> + gsc->out.ctx->gsc_ctrls.rotate->val == 270) {
> + max_w = variant->pix_max->target_rot_en_w;
> + max_h = variant->pix_max->target_rot_en_h;
> + min_w = variant->pix_min->target_rot_en_w;
> + min_h = variant->pix_min->target_rot_en_h;
> + tmp_w = cr->height;
> + tmp_h = cr->width;
> + } else {
> + max_w = variant->pix_max->target_rot_dis_w;
> + max_h = variant->pix_max->target_rot_dis_h;
> + min_w = variant->pix_min->target_rot_dis_w;
> + min_h = variant->pix_min->target_rot_dis_h;
> + tmp_w = cr->width;
> + tmp_h = cr->height;
> + }
> +
> + gsc_dbg("min_w: %d, min_h: %d, max_w: %d, max_h = %d",
> + min_w, min_h, max_w, max_h);
> +
> + v4l_bound_align_image(&tmp_w, min_w, max_w, 0,
> + &tmp_h, min_h, max_h, 0, 0);
> +
> + if (gsc->out.ctx->gsc_ctrls.rotate->val == 90 ||
> + gsc->out.ctx->gsc_ctrls.rotate->val == 270)
> + gsc_check_crop_change(tmp_h, tmp_w,&cr->width,&cr->height);
> + else
> + gsc_check_crop_change(tmp_w, tmp_h,&cr->width,&cr->height);
> +
> + gsc_dbg("Aligned l:%d, t:%d, w:%d, h:%d", cr->left, cr->top,
> + cr->width, cr->height);
> +}
> +
> +static int gsc_subdev_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct gsc_dev *gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd));
> + struct gsc_ctx *ctx = gsc->out.ctx;
> + struct v4l2_mbus_framefmt *mf =&fmt->format;
> + struct gsc_frame *f;
> +
> + if (fmt->pad == GSC_PAD_SINK) {
> + gsc_err("Sink pad get_fmt is not supported");
> + return 0;
> + }
> +
> + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad);
> + return 0;
> + }
> +
> + f =&ctx->d_frame;
> + mf->code = f->fmt->mbus_code;
> + mf->width = f->f_width;
> + mf->height = f->f_height;
> + mf->colorspace = V4L2_COLORSPACE_JPEG;
> +
> + return 0;
> +}
> +
> +static int gsc_subdev_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct gsc_dev *gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd));
> + struct v4l2_mbus_framefmt *mf;
> + struct gsc_ctx *ctx = gsc->out.ctx;
> + struct gsc_frame *f;
> +
> + gsc_dbg("pad%d: code: 0x%x, %dx%d",
> + fmt->pad, fmt->format.code, fmt->format.width, fmt->format.height);
> +
> + if (fmt->pad == GSC_PAD_SINK) {
> + gsc_err("Sink pad set_fmt is not supported");
> + return 0;
> + }
> +
> + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> + mf = v4l2_subdev_get_try_format(fh, fmt->pad);
> + mf->width = fmt->format.width;
> + mf->height = fmt->format.height;
> + mf->code = fmt->format.code;
> + mf->colorspace = V4L2_COLORSPACE_JPEG;
> + } else {
> + f =&ctx->d_frame;
> + gsc_set_frame_size(f, fmt->format.width, fmt->format.height);
> + f->fmt = find_fmt(NULL,&fmt->format.code, 0);
> + ctx->state |= GSC_DST_FMT;
> + }
> +
> + return 0;
> +}
> +
> +static int gsc_subdev_get_crop(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_crop *crop)
> +{
> + struct gsc_dev *gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd));
> + struct gsc_ctx *ctx = gsc->out.ctx;
> + struct v4l2_rect *r =&crop->rect;
> + struct gsc_frame *f;
> +
> + if (crop->pad == GSC_PAD_SINK) {
> + gsc_err("Sink pad get_crop is not supported");
> + return 0;
> + }
> +
> + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
> + crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad);
> + return 0;
> + }
> +
> + f =&ctx->d_frame;
> + r->left = f->crop.left;
> + r->top = f->crop.top;
> + r->width = f->crop.width;
> + r->height = f->crop.height;
> +
> + gsc_dbg("f:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
> + f, crop->pad, r->left, r->top, r->width, r->height,
> + f->f_width, f->f_height);
> +
> + return 0;
> +}
> +
> +static int gsc_subdev_set_crop(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh,
> + struct v4l2_subdev_crop *crop)
> +{
> + struct gsc_dev *gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd));
> + struct gsc_ctx *ctx = gsc->out.ctx;
> + struct v4l2_rect *r;
> + struct gsc_frame *f;
> +
> + gsc_dbg("(%d,%d)/%dx%d", crop->rect.left, crop->rect.top, crop->rect.width, crop->rect.height);
> +
> + if (crop->pad == GSC_PAD_SINK) {
> + gsc_err("Sink pad set_fmt is not supported\n");
> + return 0;
> + }
> +
> + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
> + r = v4l2_subdev_get_try_crop(fh, crop->pad);
> + r->left = crop->rect.left;
> + r->top = crop->rect.top;
> + r->width = crop->rect.width;
> + r->height = crop->rect.height;
> + } else {
> + f =&ctx->d_frame;
> + f->crop.left = crop->rect.left;
> + f->crop.top = crop->rect.top;
> + f->crop.width = crop->rect.width;
> + f->crop.height = crop->rect.height;
> + }
> +
> + gsc_dbg("pad%d: (%d,%d)/%dx%d", crop->pad, crop->rect.left, crop->rect.top,
> + crop->rect.width, crop->rect.height);
> +
> + return 0;
> +}
> +
> +static int gsc_subdev_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct gsc_dev *gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd));
> + int ret;
> +
> + if (enable) {
> + pm_runtime_get_sync(&gsc->pdev->dev);
> + ret = gsc_out_hw_set(gsc->out.ctx);
> + if (ret) {
> + gsc_err("GSC H/W setting is failed");
s/is failed/failed
> + return -EINVAL;
> + }
> + } else {
> + INIT_LIST_HEAD(&gsc->out.active_buf_q);
> + clear_bit(ST_OUTPUT_STREAMON,&gsc->state);
> + pm_runtime_put_sync(&gsc->pdev->dev);
> + }
> +
> + return 0;
> +}
> +
> +static struct v4l2_subdev_pad_ops gsc_subdev_pad_ops = {
> + .get_fmt = gsc_subdev_get_fmt,
> + .set_fmt = gsc_subdev_set_fmt,
> + .get_crop = gsc_subdev_get_crop,
> + .set_crop = gsc_subdev_set_crop,
> +};
> +
> +static struct v4l2_subdev_video_ops gsc_subdev_video_ops = {
> + .s_stream = gsc_subdev_s_stream,
> +};
> +
> +static struct v4l2_subdev_ops gsc_subdev_ops = {
> + .pad =&gsc_subdev_pad_ops,
> + .video =&gsc_subdev_video_ops,
> +};
> +
> +static int gsc_out_power_off(struct v4l2_subdev *sd)
> +{
> + struct gsc_dev *gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd));
> + int ret;
> +
> + ret = gsc_out_hw_reset_off(gsc);
> + if (ret< 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static struct exynos_media_ops gsc_out_link_callback = {
> + .power_off = gsc_out_power_off,
> +};
> +
> +/*
> + * The video node ioctl operations
> + */
> +static int gsc_output_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + strncpy(cap->driver, gsc->pdev->name, sizeof(cap->driver) - 1);
> + strncpy(cap->card, gsc->pdev->name, sizeof(cap->card) - 1);
> + cap->bus_info[0] = 0;
> + cap->capabilities = V4L2_CAP_STREAMING |
> + V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
> +
> + return 0;
> +}
> +
> +static int gsc_output_enum_fmt_mplane(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + return gsc_enum_fmt_mplane(f);
> +}
> +
> +static int gsc_output_try_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + if (!is_output(f->type)) {
> + gsc_err("Not supported buffer type");
> + return -EINVAL;
> + }
> +
> + return gsc_try_fmt_mplane(gsc->out.ctx, f);
> +}
> +
> +static int gsc_output_s_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = gsc->out.ctx;
> + struct gsc_frame *frame;
> + struct v4l2_pix_format_mplane *pix;
> + int i, ret = 0;
> +
> + ret = gsc_output_try_fmt_mplane(file, fh, f);
> + if (ret) {
> + gsc_err("Invalid argument");
> + return ret;
> + }
> +
> + if (vb2_is_streaming(&gsc->out.vbq)) {
> + gsc_err("queue (%d) busy", f->type);
> + return -EBUSY;
> + }
> +
> + frame =&ctx->s_frame;
> +
> + pix =&f->fmt.pix_mp;
> + frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
> + if (!frame->fmt) {
> + gsc_err("Not supported pixel format");
> + return -EINVAL;
> + }
> +
> + for (i = 0; i< frame->fmt->num_planes; i++)
> + frame->payload[i] = pix->plane_fmt[i].sizeimage;
> +
> + gsc_set_frame_size(frame, pix->width, pix->height);
> +
> + ctx->state |= GSC_SRC_FMT;
> +
> + gsc_dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
> +
> + return 0;
> +}
> +
> +static int gsc_output_g_fmt_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = gsc->out.ctx;
> +
> + if (!is_output(f->type)) {
> + gsc_err("Not supported buffer type");
> + return -EINVAL;
> + }
> +
> + return gsc_g_fmt_mplane(ctx, f);
> +}
> +
> +static int gsc_output_reqbufs(struct file *file, void *priv,
> + struct v4l2_requestbuffers *reqbufs)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_output_device *out =&gsc->out;
> + struct gsc_frame *frame;
> + int ret;
> +
> + if (reqbufs->count> gsc->variant->in_buf_cnt) {
> + gsc_err("Requested count exceeds maximun count of input buffer");
> + return -EINVAL;
s/buffer/buffers
> + } else if (reqbufs->count == 0)
> + gsc_ctx_state_lock_clear(GSC_SRC_FMT | GSC_DST_FMT,
> + out->ctx);
> +
> + frame = ctx_get_frame(out->ctx, reqbufs->type);
> +
> + ret = vb2_reqbufs(&out->vbq, reqbufs);
> + if (ret)
> + return ret;
> + out->req_cnt = reqbufs->count;
> +
> + return ret;
> +}
> +
> +static int gsc_output_querybuf(struct file *file, void *priv,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_output_device *out =&gsc->out;
> +
> + return vb2_querybuf(&out->vbq, buf);
> +}
> +
> +static int gsc_output_streamon(struct file *file, void *priv,
> + enum v4l2_buf_type type)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_output_device *out =&gsc->out;
> + struct media_pad *sink_pad;
> + int ret;
> +
> + sink_pad = media_entity_remote_source(&out->sd_pads[GSC_PAD_SOURCE]);
> + if (IS_ERR(sink_pad)) {
> + gsc_err("No sink pad conncted with a gscaler source pad");
> + return PTR_ERR(sink_pad);
> + }
> +
> + ret = gsc_out_link_validate(&out->sd_pads[GSC_PAD_SOURCE], sink_pad);
> + if (ret) {
> + gsc_err("Output link validation is failed");
> + return ret;
> + }
> +
> + media_entity_pipeline_start(&out->vfd->entity, gsc->pipeline.pipe);
> +
> + return vb2_streamon(&gsc->out.vbq, type);
> +}
> +
> +static int gsc_output_streamoff(struct file *file, void *priv,
> + enum v4l2_buf_type type)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + return vb2_streamoff(&gsc->out.vbq, type);
> +}
> +
> +static int gsc_output_qbuf(struct file *file, void *priv,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_output_device *out =&gsc->out;
> +
> + return vb2_qbuf(&out->vbq, buf);
> +}
> +
> +static int gsc_output_dqbuf(struct file *file, void *priv,
> + struct v4l2_buffer *buf)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + return vb2_dqbuf(&gsc->out.vbq, buf,
> + file->f_flags& O_NONBLOCK);
> +}
> +
> +static int gsc_output_cropcap(struct file *file, void *fh,
> + struct v4l2_cropcap *cr)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = gsc->out.ctx;
> +
> + if (!is_output(cr->type)) {
> + gsc_err("Not supported buffer type");
> + return -EINVAL;
> + }
> +
> + cr->bounds.left = 0;
> + cr->bounds.top = 0;
> + cr->bounds.width = ctx->s_frame.f_width;
> + cr->bounds.height = ctx->s_frame.f_height;
> + cr->defrect = cr->bounds;
> +
> + return 0;
> +
> +}
> +
> +static int gsc_output_g_crop(struct file *file, void *fh,
> + struct v4l2_crop *cr)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + if (!is_output(cr->type)) {
> + gsc_err("Not supported buffer type");
> + return -EINVAL;
> + }
> +
> + return gsc_g_crop(gsc->out.ctx, cr);
> +}
> +
> +static int gsc_output_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + struct gsc_ctx *ctx = gsc->out.ctx;
> + struct gsc_variant *variant = gsc->variant;
> + struct gsc_frame *f;
> + unsigned int mask = GSC_DST_FMT | GSC_SRC_FMT;
> + int ret;
> +
> + if (!is_output(cr->type)) {
> + gsc_err("Not supported buffer type");
> + return -EINVAL;
> + }
> +
> + ret = gsc_try_crop(ctx, cr);
> + if (ret)
> + return ret;
> +
> + f =&ctx->s_frame;
> +
> + /* Check to see if scaling ratio is within supported range */
> + if ((ctx->state& (GSC_DST_FMT | GSC_SRC_FMT)) == mask) {
> + ret = gsc_check_scaler_ratio(variant, f->crop.width,
> + f->crop.height, ctx->d_frame.crop.width,
> + ctx->d_frame.crop.height,
> + ctx->gsc_ctrls.rotate->val, ctx->out_path);
> + if (ret) {
> + gsc_err("Out of scaler range");
> + return -EINVAL;
> + }
> + gsc_subdev_try_crop(gsc,&ctx->d_frame.crop);
Subdevs should be accessed through their device nodes.
> + }
> +
> + f->crop.left = cr->c.left;
> + f->crop.top = cr->c.top;
> + f->crop.width = cr->c.width;
> + f->crop.height = cr->c.height;
> +
> + return 0;
> +}
Selection API ?
Feels like I am just seing a few copies of same driver. :)
> +
> +static const struct v4l2_ioctl_ops gsc_output_ioctl_ops = {
> + .vidioc_querycap = gsc_output_querycap,
> + .vidioc_enum_fmt_vid_out_mplane = gsc_output_enum_fmt_mplane,
> +
> + .vidioc_try_fmt_vid_out_mplane = gsc_output_try_fmt_mplane,
> + .vidioc_s_fmt_vid_out_mplane = gsc_output_s_fmt_mplane,
> + .vidioc_g_fmt_vid_out_mplane = gsc_output_g_fmt_mplane,
> +
> + .vidioc_reqbufs = gsc_output_reqbufs,
> + .vidioc_querybuf = gsc_output_querybuf,
> +
> + .vidioc_qbuf = gsc_output_qbuf,
> + .vidioc_dqbuf = gsc_output_dqbuf,
> +
> + .vidioc_streamon = gsc_output_streamon,
> + .vidioc_streamoff = gsc_output_streamoff,
> +
> + .vidioc_g_crop = gsc_output_g_crop,
> + .vidioc_s_crop = gsc_output_s_crop,
> + .vidioc_cropcap = gsc_output_cropcap,
> +};
> +
> +static int gsc_out_video_s_stream(struct gsc_dev *gsc, int enable)
> +{
> + struct gsc_output_device *out =&gsc->out;
> + struct media_pad *sink_pad;
> + struct v4l2_subdev *sd;
> + int ret = 0;
> +
> + sink_pad = media_entity_remote_source(&out->vd_pad);
> + if (IS_ERR(sink_pad)) {
> + gsc_err("No sink pad conncted with a gscaler video source pad");
> + return PTR_ERR(sink_pad);
> + }
> + sd = media_entity_to_v4l2_subdev(sink_pad->entity);
> + ret = v4l2_subdev_call(sd, video, s_stream, enable);
> + if (ret)
> + gsc_err("G-Scaler subdev s_stream[%d] failed", enable);
> +
> + return ret;
> +}
> +
> +static int gsc_out_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> + struct gsc_ctx *ctx = q->drv_priv;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> +
> + return gsc_out_video_s_stream(gsc, 1);
> +}
> +
> +static int gsc_out_stop_streaming(struct vb2_queue *q)
> +{
> + struct gsc_ctx *ctx = q->drv_priv;
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + int ret = 0;
> +
> + ret = gsc_pipeline_s_stream(gsc, false);
> + if (ret)
> + return ret;
> +
> + if (ctx->out_path == GSC_FIMD) {
> + gsc_hw_enable_control(gsc, false);
> + ret = gsc_wait_stop(gsc);
> + if (ret< 0)
> + return ret;
> + }
> + gsc_hw_set_input_buf_mask_all(gsc);
> +
> + /* TODO: Add gscaler clock off function */
> + ret = gsc_out_video_s_stream(gsc, 0);
> + if (ret) {
> + gsc_err("G-Scaler video s_stream off failed");
> + return ret;
> + }
> + media_entity_pipeline_stop(&gsc->out.vfd->entity);
> +
> + return ret;
> +}
> +
> +static int gsc_out_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> + unsigned int *num_buffers, unsigned int *num_planes,
> + unsigned int sizes[], void *allocators[])
> +{
> + struct gsc_ctx *ctx = vq->drv_priv;
> + struct gsc_fmt *ffmt = ctx->s_frame.fmt;
> + int i;
> +
> + if (IS_ERR(ffmt)) {
> + gsc_err("Invalid source format");
> + return PTR_ERR(ffmt);
> + }
> +
> + *num_planes = ffmt->num_planes;
> +
> + for (i = 0; i< ffmt->num_planes; i++) {
> + sizes[i] = get_plane_size(&ctx->s_frame, i);
> + allocators[i] = ctx->gsc_dev->alloc_ctx;
> + }
> +
> + return 0;
> +}
> +
> +static int gsc_out_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct vb2_queue *vq = vb->vb2_queue;
> + struct gsc_ctx *ctx = vq->drv_priv;
> +
> + if (!ctx->s_frame.fmt || !is_output(vq->type)) {
> + gsc_err("Invalid argument");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int gsc_out_set_in_addr(struct gsc_dev *gsc, struct gsc_ctx *ctx,
> + struct gsc_input_buf *buf, int index)
> +{
> + int ret;
> +
> + ret = gsc_prepare_addr(ctx,&buf->vb,&ctx->s_frame,&ctx->s_frame.addr);
> + if (ret) {
> + gsc_err("Fail to prepare G-Scaler address");
> + return -EINVAL;
> + }
> + gsc_hw_set_input_addr(gsc,&ctx->s_frame.addr, index);
> + active_queue_push(&gsc->out, buf, gsc);
> + buf->idx = index;
> +
> + return 0;
> +}
> +
> +static void gsc_out_buffer_queue(struct vb2_buffer *vb)
> +{
> + struct gsc_input_buf *buf
> + = container_of(vb, struct gsc_input_buf, vb);
> + struct vb2_queue *q = vb->vb2_queue;
> + struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> + struct gsc_dev *gsc = ctx->gsc_dev;
> + int ret;
> +
> + if (gsc->out.req_cnt>= atomic_read(&q->queued_count)) {
> + ret = gsc_out_set_in_addr(gsc, ctx, buf, vb->v4l2_buf.index);
> + if (ret) {
> + gsc_err("Failed to prepare G-Scaler address");
> + return;
> + }
> + gsc_hw_set_input_buf_masking(gsc, vb->v4l2_buf.index, false);
> + } else {
> + gsc_err("All requested buffers have been queued already");
> + return;
> + }
> +
> + if (!test_and_set_bit(ST_OUTPUT_STREAMON,&gsc->state)) {
> + gsc_disp_fifo_sw_reset(gsc);
> + gsc_pixelasync_sw_reset(gsc);
> + gsc_hw_enable_control(gsc, true);
> + ret = gsc_wait_operating(gsc);
> + if (ret< 0) {
> + gsc_err("wait operation timeout");
> + return;
> + }
> + gsc_pipeline_s_stream(gsc, true);
> + }
> +}
> +
> +static struct vb2_ops gsc_output_qops = {
> + .queue_setup = gsc_out_queue_setup,
> + .buf_prepare = gsc_out_buffer_prepare,
> + .buf_queue = gsc_out_buffer_queue,
> + .wait_prepare = gsc_unlock,
> + .wait_finish = gsc_lock,
> + .start_streaming = gsc_out_start_streaming,
> + .stop_streaming = gsc_out_stop_streaming,
> +};
> +
> +static int gsc_out_link_setup(struct media_entity *entity,
> + const struct media_pad *local,
> + const struct media_pad *remote, u32 flags)
> +{
> + if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> + return 0;
> +
> + if (local->flags == MEDIA_PAD_FL_SOURCE) {
> + struct gsc_dev *gsc = entity_to_gsc(entity);
> + struct v4l2_subdev *sd;
> + if (flags& MEDIA_LNK_FL_ENABLED) {
> + if (gsc->pipeline.disp == NULL) {
> + /* Gscaler 0 --> Winwow 0, Gscaler 1 --> Window 1,
> + Gscaler 2 --> Window 2, Gscaler 3 --> Window 2 */
> + char name[FIMD_NAME_SIZE];
> + sprintf(name, "%s%d", FIMD_ENTITY_NAME, get_win_num(gsc));
> + gsc_hw_set_local_dst(gsc->id, true);
> + sd = media_entity_to_v4l2_subdev(remote->entity);
> + gsc->pipeline.disp = sd;
> + if (!strcmp(sd->name, name))
> + gsc->out.ctx->out_path = GSC_FIMD;
> + else
> + gsc->out.ctx->out_path = GSC_MIXER;
> + } else
> + gsc_err("G-Scaler source pad was linked already");
> + } else if (!(flags& ~MEDIA_LNK_FL_ENABLED)) {
> + if (gsc->pipeline.disp != NULL) {
> + gsc_hw_set_local_dst(gsc->id, false);
> + gsc->pipeline.disp = NULL;
> + gsc->out.ctx->out_path = 0;
> + } else
> + gsc_err("G-Scaler source pad was unlinked already");
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct media_entity_operations gsc_out_media_ops = {
> + .link_setup = gsc_out_link_setup,
> +};
> +
> +int gsc_output_ctrls_create(struct gsc_dev *gsc)
> +{
> + int ret;
> +
> + ret = gsc_ctrls_create(gsc->out.ctx);
> + if (ret) {
> + gsc_err("Failed to create controls of G-Scaler");
> + return ret;
> + }
> +
> + return 0;
if (ret)
gsc_err("Failed to create controls of G-Scaler");
return ret;
> +}
> +
> +static int gsc_output_open(struct file *file)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> + int ret = v4l2_fh_open(file);
> +
> + if (ret)
> + return ret;
> +
> + gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> + /* Return if the corresponding mem2mem/output/capture video node
> + is already opened. */
> + if (gsc_m2m_opened(gsc) || gsc_cap_opened(gsc) || gsc_out_opened(gsc)) {
> + gsc_err("G-Scaler%d has been opened already", gsc->id);
> + return -EBUSY;
> + }
> +
> + if (WARN_ON(gsc->out.ctx == NULL)) {
> + gsc_err("G-Scaler output context is NULL");
> + return -ENXIO;
Don't you need to call v4l2_fh_release() before returning here and above ?
> + }
> +
> + set_bit(ST_OUTPUT_OPEN,&gsc->state);
> +
> + ret = gsc_ctrls_create(gsc->out.ctx);
> + if (ret< 0) {
> + v4l2_fh_release(file);
> + clear_bit(ST_OUTPUT_OPEN,&gsc->state);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int gsc_output_close(struct file *file)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + gsc_dbg("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
> +
> + clear_bit(ST_OUTPUT_OPEN,&gsc->state);
> + vb2_queue_release(&gsc->out.vbq);
> + gsc_ctrls_delete(gsc->out.ctx);
> + v4l2_fh_release(file);
> +
> + return 0;
> +}
> +
> +static unsigned int gsc_output_poll(struct file *file,
> + struct poll_table_struct *wait)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + return vb2_poll(&gsc->out.vbq, file, wait);
> +}
> +
> +static int gsc_output_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> + struct gsc_dev *gsc = video_drvdata(file);
> +
> + return vb2_mmap(&gsc->out.vbq, vma);
> +}
> +
> +static const struct v4l2_file_operations gsc_output_fops = {
> + .owner = THIS_MODULE,
> + .open = gsc_output_open,
> + .release = gsc_output_close,
> + .poll = gsc_output_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = gsc_output_mmap,
> +};
> +
> +static int gsc_create_link(struct gsc_dev *gsc)
> +{
> + struct media_entity *source, *sink;
> + int ret;
> +
> + source =&gsc->out.vfd->entity;
> + sink =&gsc->out.sd->entity;
> + ret = media_entity_create_link(source, 0, sink, GSC_PAD_SINK,
> + MEDIA_LNK_FL_IMMUTABLE |
> + MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + gsc_err("Failed to create link between G-Scaler vfd and subdev");
> + return ret;
> + }
> +
> + return 0;
OK, I'm too tired to comment on that ;P
> +}
> +
> +
> +static int gsc_create_subdev(struct gsc_dev *gsc)
> +{
> + struct v4l2_subdev *sd;
> + int ret;
> +
> + sd = kzalloc(sizeof(*sd), GFP_KERNEL);
devm_kzalloc ?
> + if (!sd)
> + return -ENOMEM;
> +
> + v4l2_subdev_init(sd,&gsc_subdev_ops);
> + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> + snprintf(sd->name, sizeof(sd->name), "%s.%d", GSC_SUBDEV_NAME, gsc->id);
> +
> + gsc->out.sd_pads[GSC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> + gsc->out.sd_pads[GSC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> + ret = media_entity_init(&sd->entity, GSC_PADS_NUM,
> + gsc->out.sd_pads, 0);
> + if (ret) {
> + gsc_err("Failed to initialize the G-Scaler media entity");
> + goto error;
> + }
> +
> + sd->entity.ops =&gsc_out_media_ops;
> + ret = v4l2_device_register_subdev(&gsc->mdev[MDEV_OUTPUT]->v4l2_dev, sd);
> + if (ret) {
> + media_entity_cleanup(&sd->entity);
> + goto error;
> + }
> + gsc->mdev[MDEV_OUTPUT]->gsc_sd[gsc->id] = sd;
> + gsc_dbg("gsc_sd[%d] = 0x%08x\n", gsc->id,
> + (u32)gsc->mdev[MDEV_OUTPUT]->gsc_sd[gsc->id]);
> + gsc->out.sd = sd;
> + gsc->md_data.media_ops =&gsc_out_link_callback;
> + v4l2_set_subdevdata(sd,&gsc->md_data);
> +
> + return 0;
> +error:
> + kfree(sd);
> + return ret;
> +}
> +
> +int gsc_register_output_device(struct gsc_dev *gsc)
> +{
> + struct video_device *vfd;
> + struct gsc_output_device *gsc_out;
> + struct gsc_ctx *ctx;
> + struct vb2_queue *q;
> + int ret = -ENOMEM;
> +
> + ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
devm_kzalloc ?
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->gsc_dev = gsc;
> + ctx->s_frame.fmt = get_format(GSC_OUT_DEF_SRC);
> + ctx->d_frame.fmt = get_format(GSC_OUT_DEF_DST);
> + ctx->in_path = GSC_DMA;
> + ctx->state = GSC_CTX_OUTPUT;
> +
> + vfd = video_device_alloc();
> + if (!vfd) {
> + gsc_err("Failed to allocate video device");
> + goto err_ctx_alloc;
> + }
> +
> + snprintf(vfd->name, sizeof(vfd->name), "%s.output",
> + dev_name(&gsc->pdev->dev));
Same comment as for the m2m video node.
> +
> + vfd->fops =&gsc_output_fops;
> + vfd->ioctl_ops =&gsc_output_ioctl_ops;
> + vfd->v4l2_dev =&gsc->mdev[MDEV_OUTPUT]->v4l2_dev;
> + vfd->release = video_device_release;
> + vfd->lock =&gsc->lock;
> + video_set_drvdata(vfd, gsc);
> +
> + gsc_out =&gsc->out;
> + gsc_out->vfd = vfd;
> +
> + INIT_LIST_HEAD(&gsc_out->active_buf_q);
> + spin_lock_init(&ctx->slock);
> + gsc_out->ctx = ctx;
> +
> + q =&gsc->out.vbq;
> + memset(q, 0, sizeof(*q));
> + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> + q->io_modes = VB2_MMAP | VB2_USERPTR;
> + q->drv_priv = gsc->out.ctx;
> + q->ops =&gsc_output_qops;
> + q->mem_ops =&vb2_dma_contig_memops;;
> + q->buf_struct_size = sizeof(struct gsc_input_buf);
> +
> + vb2_queue_init(q);
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
Vide node registered too early.
> + if (ret) {
> + gsc_err("Failed to register video device");
> + goto err_ent;
> + }
> +
> + gsc->out.vd_pad.flags = MEDIA_PAD_FL_SOURCE;
> + ret = media_entity_init(&vfd->entity, 1,&gsc->out.vd_pad, 0);
> + if (ret)
> + goto err_ent;
> +
> + ret = gsc_create_subdev(gsc);
> + if (ret)
> + goto err_sd_reg;
> +
> + ret = gsc_create_link(gsc);
> + if (ret)
> + goto err_sd_reg;
> +
> + vfd->ctrl_handler =&ctx->ctrl_handler;
> + gsc_dbg("gsc output driver registered as /dev/video%d, ctx(0x%08x)",
> + vfd->num, (u32)ctx);
> + return 0;
> +
> +err_sd_reg:
> + media_entity_cleanup(&vfd->entity);
> +err_ent:
> + video_device_release(vfd);
> +err_ctx_alloc:
> + kfree(ctx);
> + return ret;
> +}
> +
> +static void gsc_destroy_subdev(struct gsc_dev *gsc)
> +{
> + struct v4l2_subdev *sd = gsc->out.sd;
> +
> + if (!sd)
> + return;
> + media_entity_cleanup(&sd->entity);
> + v4l2_device_unregister_subdev(sd);
> + kfree(sd);
> + sd = NULL;
Same comment as for fimc-lite.
> +}
> +
> +void gsc_unregister_output_device(struct gsc_dev *gsc)
> +{
> + struct video_device *vfd = gsc->out.vfd;
> +
> + if (vfd) {
> + media_entity_cleanup(&vfd->entity);
> + /* Can also be called if video device was
> + not registered */
> + video_unregister_device(vfd);
> + }
> + gsc_destroy_subdev(gsc);
> + kfree(gsc->out.ctx);
> + gsc->out.ctx = NULL;
> +}
> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c
> new file mode 100644
> index 0000000..81e33fd
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/gsc-regs.c
> @@ -0,0 +1,671 @@
> +/* linux/drivers/media/video/exynos/gsc/gsc-regs.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung EXYNOS5 SoC series G-scaler driver
> + *
> + * 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.
> + */
> +
> +#include<linux/io.h>
> +#include<linux/delay.h>
> +#include<mach/map.h>
> +#include "gsc-core.h"
> +
> +void gsc_hw_set_sw_reset(struct gsc_dev *dev)
> +{
> + u32 cfg = 0;
> +
> + cfg |= GSC_SW_RESET_SRESET;
> + writel(cfg, dev->regs + GSC_SW_RESET);
> +}
> +
> +void gsc_disp_fifo_sw_reset(struct gsc_dev *dev)
> +{
> + u32 cfg = readl(SYSREG_DISP1BLK_CFG);
> + /* DISPBLK1 FIFO S/W reset sequence
> + set FIFORST_DISP1 as 0 then, set FIFORST_DISP1 as 1 again */
> + cfg&= ~FIFORST_DISP1;
> + writel(cfg, SYSREG_DISP1BLK_CFG);
> + cfg |= FIFORST_DISP1;
> + writel(cfg, SYSREG_DISP1BLK_CFG);
> +}
> +
> +void gsc_pixelasync_sw_reset(struct gsc_dev *dev)
> +{
> + u32 cfg = readl(SYSREG_GSCBLK_CFG0);
> + /* GSCBLK Pixel asyncy FIFO S/W reset sequence
> + set PXLASYNC_SW_RESET as 0 then, set PXLASYNC_SW_RESET as 1 again */
> + cfg&= ~GSC_PXLASYNC_RST(dev->id);
> + writel(cfg, SYSREG_GSCBLK_CFG0);
> + cfg |= GSC_PXLASYNC_RST(dev->id);
> + writel(cfg, SYSREG_GSCBLK_CFG0);
> +}
> +
> +int gsc_wait_reset(struct gsc_dev *dev)
> +{
> + unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> + u32 cfg;
> +
> + while (time_before(jiffies, timeo)) {
> + cfg = readl(dev->regs + GSC_SW_RESET);
> + if (!cfg)
> + return 0;
> + usleep_range(10, 20);
> + }
> + gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
> +
> + return -EBUSY;
> +}
> +
> +int gsc_wait_operating(struct gsc_dev *dev)
> +{
> + unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> + u32 cfg;
> +
> + while (time_before(jiffies, timeo)) {
> + cfg = readl(dev->regs + GSC_ENABLE);
> + if ((cfg& GSC_ENABLE_OP_STATUS) == GSC_ENABLE_OP_STATUS)
> + return 0;
> + usleep_range(10, 20);
> + }
> + gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
> +
> + return -EBUSY;
> +}
> +
> +int gsc_wait_stop(struct gsc_dev *dev)
> +{
> + unsigned long timeo = jiffies + 10; /* timeout of 50ms */
> + u32 cfg;
> +
> + while (time_before(jiffies, timeo)) {
> + cfg = readl(dev->regs + GSC_ENABLE);
> + if (!(cfg& GSC_ENABLE_OP_STATUS))
> + return 0;
> + usleep_range(10, 20);
> + }
> + gsc_dbg("wait time : %d ms", jiffies_to_msecs(jiffies - timeo + 20));
> +
> + return -EBUSY;
> +}
> +
> +
> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask)
> +{
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_ENABLE);
> + if (mask)
> + cfg |= GSC_ENABLE_ON_CLEAR;
> + else
> + cfg&= ~GSC_ENABLE_ON_CLEAR;
> + writel(cfg, dev->regs + GSC_ENABLE);
> +}
> +
> +int gsc_hw_get_input_buf_mask_status(struct gsc_dev *dev)
> +{
> + u32 cfg, status, bits = 0;
> +
> + cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> + status = cfg& GSC_IN_BASE_ADDR_MASK;
> + while (status) {
> + status = status& (status - 1);
> + bits++;
> + }
> + return bits;
> +}
> +
> +int gsc_hw_get_done_input_buf_index(struct gsc_dev *dev)
> +{
> + u32 cfg, curr_index, i;
> +
> + cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> + curr_index = GSC_IN_CURR_GET_INDEX(cfg);
> + for (i = curr_index; i> 1; i--) {
> + if (cfg ^ (1<< (i - 2)))
> + return i - 2;
> + }
> +
> + for (i = dev->variant->in_buf_cnt; i> curr_index; i--) {
> + if (cfg ^ (1<< (i - 1)))
> + return i - 1;
> + }
> +
> + return curr_index - 1;
> +}
> +
> +int gsc_hw_get_done_output_buf_index(struct gsc_dev *dev)
> +{
> + u32 cfg, curr_index, done_buf_index;
> + unsigned long state_mask;
> + u32 reqbufs_cnt = dev->cap.reqbufs_cnt;
> +
> + cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> + curr_index = GSC_OUT_CURR_GET_INDEX(cfg);
> + gsc_dbg("curr_index : %d", curr_index);
> + state_mask = cfg& GSC_OUT_BASE_ADDR_MASK;
> +
> + done_buf_index = (curr_index == 0) ? reqbufs_cnt - 1 : curr_index - 1;
> +
> + do {
> + /* Test done_buf_index whether masking or not */
> + if (test_bit(done_buf_index,&state_mask))
> + done_buf_index = (done_buf_index == 0) ?
> + reqbufs_cnt - 1 : done_buf_index - 1;
> + else
> + return done_buf_index;
> + } while (done_buf_index != curr_index);
> +
> + return -EBUSY;
> +}
> +
> +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask)
> +{
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_IRQ);
> + if (mask)
> + cfg |= GSC_IRQ_FRMDONE_MASK;
> + else
> + cfg&= ~GSC_IRQ_FRMDONE_MASK;
> + writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_overflow_irq_mask(struct gsc_dev *dev, bool mask)
> +{
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_IRQ);
> + if (mask)
> + cfg |= GSC_IRQ_OR_MASK;
> + else
> + cfg&= ~GSC_IRQ_OR_MASK;
> + writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask)
> +{
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_IRQ);
> + if (mask)
> + cfg |= GSC_IRQ_ENABLE;
> + else
> + cfg&= ~GSC_IRQ_ENABLE;
> + writel(cfg, dev->regs + GSC_IRQ);
> +}
> +
> +void gsc_hw_set_input_buf_mask_all(struct gsc_dev *dev)
> +{
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> + cfg |= GSC_IN_BASE_ADDR_MASK;
> + cfg |= GSC_IN_BASE_ADDR_PINGPONG(dev->variant->in_buf_cnt);
> +
> + writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> + writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
> + writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_output_buf_mask_all(struct gsc_dev *dev)
> +{
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> + cfg |= GSC_OUT_BASE_ADDR_MASK;
> + cfg |= GSC_OUT_BASE_ADDR_PINGPONG(dev->variant->out_buf_cnt);
> +
> + writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> + writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
> + writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift,
> + bool enable)
> +{
> + u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> + u32 mask = 1<< shift;
> +
> + cfg&= (~mask);
> + cfg |= enable<< shift;
> +
> + writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
> + writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
> + writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
> +}
> +
> +void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
> + bool enable)
> +{
> + u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> + u32 mask = 1<< shift;
> +
> + cfg&= (~mask);
> + cfg |= enable<< shift;
> +
> + writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> + writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
> + writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
> +}
> +
> +int gsc_hw_get_nr_unmask_bits(struct gsc_dev *dev)
> +{
> + u32 bits = 0;
> + u32 mask_bits = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
> + mask_bits&= GSC_OUT_BASE_ADDR_MASK;
> +
> + while (mask_bits) {
> + mask_bits = mask_bits& (mask_bits - 1);
> + bits++;
> + }
> + bits = 16 - bits;
> +
> + return bits;
> +}
> +
> +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
> + int index)
> +{
> + gsc_dbg("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
> + addr->y, addr->cb, addr->cr);
> + writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
> + writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
> + writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
> +
> +}
> +
> +void gsc_hw_set_output_addr(struct gsc_dev *dev,
> + struct gsc_addr *addr, int index)
> +{
> + gsc_dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
> + index, addr->y, addr->cb, addr->cr);
> + writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
> + writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
> + writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
> +}
> +
> +void gsc_hw_set_input_path(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> +
> + u32 cfg = readl(dev->regs + GSC_IN_CON);
> + cfg&= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
> +
> + if (ctx->in_path == GSC_DMA) {
> + cfg |= GSC_IN_PATH_MEMORY;
> + } else {
> + cfg |= GSC_IN_PATH_LOCAL;
> + if (ctx->in_path == GSC_WRITEBACK) {
> + cfg |= GSC_IN_LOCAL_FIMD_WB;
> + } else {
> + struct v4l2_subdev *sd = dev->pipeline.sensor;
> + struct gsc_sensor_info *s_info =
> + v4l2_get_subdev_hostdata(sd);
> + if (s_info->pdata->cam_port == CAM_PORT_A)
> + cfg |= GSC_IN_LOCAL_CAM0;
> + else
> + cfg |= GSC_IN_LOCAL_CAM1;
> + }
> + }
> +
> + writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_in_size(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_frame *frame =&ctx->s_frame;
> + u32 cfg;
> +
> + /* Set input pixel offset */
> + cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left);
> + cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top);
> + writel(cfg, dev->regs + GSC_SRCIMG_OFFSET);
> +
> + /* Set input original size */
> + cfg = GSC_SRCIMG_WIDTH(frame->f_width);
> + cfg |= GSC_SRCIMG_HEIGHT(frame->f_height);
> + writel(cfg, dev->regs + GSC_SRCIMG_SIZE);
> +
> + /* Set input cropped size */
> + cfg = GSC_CROPPED_WIDTH(frame->crop.width);
> + cfg |= GSC_CROPPED_HEIGHT(frame->crop.height);
> + writel(cfg, dev->regs + GSC_CROPPED_SIZE);
> +}
> +
> +void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_frame *frame =&ctx->s_frame;
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_IN_CON);
> + if (ctx->gsc_ctrls.csc_eq->val) {
> + if (ctx->gsc_ctrls.csc_range->val)
> + cfg |= GSC_IN_RGB_HD_WIDE;
> + else
> + cfg |= GSC_IN_RGB_HD_NARROW;
> + } else {
> + if (ctx->gsc_ctrls.csc_range->val)
> + cfg |= GSC_IN_RGB_SD_WIDE;
> + else
> + cfg |= GSC_IN_RGB_SD_NARROW;
> + }
> +
> + if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
> + cfg |= GSC_IN_RGB565;
> + else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
> + cfg |= GSC_IN_XRGB8888;
> +
> + writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_frame *frame =&ctx->s_frame;
> + u32 i, depth = 0;
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_IN_CON);
> + cfg&= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
> + GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK |
> + GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE);
> + writel(cfg, dev->regs + GSC_IN_CON);
> +
> + if (is_rgb(frame->fmt->color)) {
> + gsc_hw_set_in_image_rgb(ctx);
> + return;
> + }
> + for (i = 0; i< frame->fmt->num_planes; i++)
> + depth += frame->fmt->depth[i];
> +
> + switch (frame->fmt->nr_comp) {
> + case 1:
> + cfg |= GSC_IN_YUV422_1P;
> + if (frame->fmt->yorder == GSC_LSB_Y)
> + cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y;
> + else
> + cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C;
> + if (frame->fmt->corder == GSC_CBCR)
> + cfg |= GSC_IN_CHROMA_ORDER_CBCR;
> + else
> + cfg |= GSC_IN_CHROMA_ORDER_CRCB;
> + break;
> + case 2:
> + if (depth == 12)
> + cfg |= GSC_IN_YUV420_2P;
> + else
> + cfg |= GSC_IN_YUV422_2P;
> + if (frame->fmt->corder == GSC_CBCR)
> + cfg |= GSC_IN_CHROMA_ORDER_CBCR;
> + else
> + cfg |= GSC_IN_CHROMA_ORDER_CRCB;
> + break;
> + case 3:
> + if (depth == 12)
> + cfg |= GSC_IN_YUV420_3P;
> + else
> + cfg |= GSC_IN_YUV422_3P;
> + break;
> + };
> +
> + writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_output_path(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> +
> + u32 cfg = readl(dev->regs + GSC_OUT_CON);
> + cfg&= ~GSC_OUT_PATH_MASK;
> +
> + if (ctx->out_path == GSC_DMA) {
> + cfg |= GSC_OUT_PATH_MEMORY;
> + } else {
> + cfg |= GSC_OUT_PATH_LOCAL;
> + }
> +
> + writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_out_size(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_frame *frame =&ctx->d_frame;
> + u32 cfg;
> +
> + /* Set output original size */
> + if (ctx->out_path == GSC_DMA) {
> + cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left);
> + cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top);
> + writel(cfg, dev->regs + GSC_DSTIMG_OFFSET);
> +
> + cfg = GSC_DSTIMG_WIDTH(frame->f_width);
> + cfg |= GSC_DSTIMG_HEIGHT(frame->f_height);
> + writel(cfg, dev->regs + GSC_DSTIMG_SIZE);
> + }
> +
> + /* Set output scaled size */
> + if (ctx->gsc_ctrls.rotate->val == 90 ||
> + ctx->gsc_ctrls.rotate->val == 270) {
> + cfg = GSC_SCALED_WIDTH(frame->crop.height);
> + cfg |= GSC_SCALED_HEIGHT(frame->crop.width);
> + } else {
> + cfg = GSC_SCALED_WIDTH(frame->crop.width);
> + cfg |= GSC_SCALED_HEIGHT(frame->crop.height);
> + }
> + writel(cfg, dev->regs + GSC_SCALED_SIZE);
> +}
> +
> +void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_frame *frame =&ctx->d_frame;
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_OUT_CON);
> + if (ctx->gsc_ctrls.csc_eq->val) {
> + if (ctx->gsc_ctrls.csc_range->val)
> + cfg |= GSC_OUT_RGB_HD_WIDE;
> + else
> + cfg |= GSC_OUT_RGB_HD_NARROW;
> + } else {
> + if (ctx->gsc_ctrls.csc_range->val)
> + cfg |= GSC_OUT_RGB_SD_WIDE;
> + else
> + cfg |= GSC_OUT_RGB_SD_NARROW;
> + }
> +
> + if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
> + cfg |= GSC_OUT_RGB565;
> + else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
> + cfg |= GSC_OUT_XRGB8888;
> +
> + writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_frame *frame =&ctx->d_frame;
> + u32 i, depth = 0;
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_OUT_CON);
> + cfg&= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
> + GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK |
> + GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE);
> + writel(cfg, dev->regs + GSC_OUT_CON);
> +
> + if (is_rgb(frame->fmt->color)) {
> + gsc_hw_set_out_image_rgb(ctx);
> + return;
> + }
> +
> + if (ctx->out_path != GSC_DMA) {
> + cfg |= GSC_OUT_YUV444;
> + goto end_set;
> + }
> +
> + for (i = 0; i< frame->fmt->num_planes; i++)
> + depth += frame->fmt->depth[i];
> +
> + switch (frame->fmt->nr_comp) {
> + case 1:
> + cfg |= GSC_OUT_YUV422_1P;
> + if (frame->fmt->yorder == GSC_LSB_Y)
> + cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y;
> + else
> + cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C;
> + if (frame->fmt->corder == GSC_CBCR)
> + cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
> + else
> + cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
> + break;
> + case 2:
> + if (depth == 12)
> + cfg |= GSC_OUT_YUV420_2P;
> + else
> + cfg |= GSC_OUT_YUV422_2P;
> + if (frame->fmt->corder == GSC_CBCR)
> + cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
> + else
> + cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
> + break;
> + case 3:
> + cfg |= GSC_OUT_YUV420_3P;
> + break;
> + };
> +
> +end_set:
> + writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_prescaler(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_scaler *sc =&ctx->scaler;
> + u32 cfg;
> +
> + cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor);
> + cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio);
> + cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio);
> + writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO);
> +}
> +
> +void gsc_hw_set_mainscaler(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_scaler *sc =&ctx->scaler;
> + u32 cfg;
> +
> + cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
> + writel(cfg, dev->regs + GSC_MAIN_H_RATIO);
> +
> + cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio);
> + writel(cfg, dev->regs + GSC_MAIN_V_RATIO);
> +}
> +
> +void gsc_hw_set_rotation(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_IN_CON);
> + cfg&= ~GSC_IN_ROT_MASK;
> +
> + switch (ctx->gsc_ctrls.rotate->val) {
> + case 270:
> + cfg |= GSC_IN_ROT_270;
> + break;
> + case 180:
> + cfg |= GSC_IN_ROT_180;
> + break;
> + case 90:
> + if (ctx->gsc_ctrls.hflip->val)
> + cfg |= GSC_IN_ROT_90_XFLIP;
> + else if (ctx->gsc_ctrls.vflip->val)
> + cfg |= GSC_IN_ROT_90_YFLIP;
> + else
> + cfg |= GSC_IN_ROT_90;
> + break;
> + case 0:
> + if (ctx->gsc_ctrls.hflip->val)
> + cfg |= GSC_IN_ROT_XFLIP;
> + else if (ctx->gsc_ctrls.vflip->val)
> + cfg |= GSC_IN_ROT_YFLIP;
> + }
> +
> + writel(cfg, dev->regs + GSC_IN_CON);
> +}
> +
> +void gsc_hw_set_global_alpha(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + struct gsc_frame *frame =&ctx->d_frame;
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_OUT_CON);
> + cfg&= ~GSC_OUT_GLOBAL_ALPHA_MASK;
> +
> + if (!is_rgb(frame->fmt->color)) {
I would check that before the register read.
> + gsc_dbg("Not a RGB format");
> + return;
> + }
> +
> + cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val);
> + writel(cfg, dev->regs + GSC_OUT_CON);
> +}
> +
> +void gsc_hw_set_sfr_update(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> + u32 cfg;
> +
> + cfg = readl(dev->regs + GSC_ENABLE);
> + cfg |= GSC_ENABLE_SFR_UPDATE;
> + writel(cfg, dev->regs + GSC_ENABLE);
> +}
> +
> +void gsc_hw_set_local_dst(int id, bool on)
> +{
> + u32 cfg = readl(SYSREG_GSCBLK_CFG0);
> +
> + if (on)
> + cfg |= GSC_OUT_DST_SEL(id);
> + else
> + cfg&= ~(GSC_OUT_DST_SEL(id));
> + writel(cfg, SYSREG_GSCBLK_CFG0);
> +}
> +
> +void gsc_hw_set_sysreg_writeback(struct gsc_ctx *ctx)
> +{
> + struct gsc_dev *dev = ctx->gsc_dev;
> +
> + u32 cfg = readl(SYSREG_GSCBLK_CFG1);
> +
> + cfg |= GSC_BLK_DISP1WB_DEST(dev->id);
> + cfg |= GSC_BLK_GSCL_WB_IN_SRC_SEL(dev->id);
> + cfg |= GSC_BLK_SW_RESET_WB_DEST(dev->id);
> +
> + writel(cfg, SYSREG_GSCBLK_CFG1);
> +}
> +
> +void gsc_hw_set_sysreg_camif(bool on)
> +{
> + u32 cfg = readl(SYSREG_GSCBLK_CFG0);
> +
> + if (on)
> + cfg |= GSC_PXLASYNC_CAMIF_TOP;
> + else
> + cfg&= ~(GSC_PXLASYNC_CAMIF_TOP);
> +
> + writel(cfg, SYSREG_GSCBLK_CFG0);
> +}
> diff --git a/drivers/media/video/exynos/gsc/regs-gsc.h b/drivers/media/video/exynos/gsc/regs-gsc.h
> new file mode 100644
> index 0000000..9345d5c
> --- /dev/null
> +++ b/drivers/media/video/exynos/gsc/regs-gsc.h
> @@ -0,0 +1,224 @@
> +/* linux/drivers/media/video/exynos/gsc/regs-gsc.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Register definition file for Samsung G-Scaler 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 REGS_GSC_H_
> +#define REGS_GSC_H_
> +
> +/* SYSCON. GSCBLK_CFG */
> +#include<plat/map-base.h>
> +#define SYSREG_DISP1BLK_CFG (S3C_VA_SYS + 0x0214)
> +#define FIFORST_DISP1 (1<< 23)
> +#define SYSREG_GSCBLK_CFG0 (S3C_VA_SYS + 0x0220)
> +#define GSC_OUT_DST_SEL(x) (1<< (8 + 2 * (x)))
> +#define GSC_PXLASYNC_RST(x) (1<< (x))
> +#define GSC_PXLASYNC_CAMIF_TOP (1<< 20)
> +#define SYSREG_GSCBLK_CFG1 (S3C_VA_SYS + 0x0224)
Oh man, that's an ugly platform dependency, black magic, I'm giving up :P
Can't this be mitigated somehow, to avoid plaform specific headers ?
> +#define GSC_BLK_DISP1WB_DEST(x) (x<< 10)
> +#define GSC_BLK_SW_RESET_WB_DEST(x) (1<< (18 + x))
> +#define GSC_BLK_GSCL_WB_IN_SRC_SEL(x) (1<< (2 * x))
> +
> +/* G-Scaler enable */
> +#define GSC_ENABLE 0x00
> +#define GSC_ENABLE_ON_CLEAR (1<< 4)
> +#define GSC_ENABLE_QOS_ENABLE (1<< 3)
> +#define GSC_ENABLE_OP_STATUS (1<< 2)
> +#define GSC_ENABLE_SFR_UPDATE (1<< 1)
> +#define GSC_ENABLE_ON (1<< 0)
> +
> +/* G-Scaler S/W reset */
> +#define GSC_SW_RESET 0x04
> +#define GSC_SW_RESET_SRESET (1<< 0)
> +
> +/* G-Scaler IRQ */
> +#define GSC_IRQ 0x08
> +#define GSC_IRQ_STATUS_OR_IRQ (1<< 17)
> +#define GSC_IRQ_STATUS_OR_FRM_DONE (1<< 16)
> +#define GSC_IRQ_OR_MASK (1<< 2)
> +#define GSC_IRQ_FRMDONE_MASK (1<< 1)
> +#define GSC_IRQ_ENABLE (1<< 0)
> +
> +/* G-Scaler input control */
> +#define GSC_IN_CON 0x10
> +#define GSC_IN_ROT_MASK (7<< 16)
> +#define GSC_IN_ROT_270 (7<< 16)
> +#define GSC_IN_ROT_90_YFLIP (6<< 16)
> +#define GSC_IN_ROT_90_XFLIP (5<< 16)
> +#define GSC_IN_ROT_90 (4<< 16)
> +#define GSC_IN_ROT_180 (3<< 16)
> +#define GSC_IN_ROT_YFLIP (2<< 16)
> +#define GSC_IN_ROT_XFLIP (1<< 16)
> +#define GSC_IN_RGB_TYPE_MASK (3<< 14)
> +#define GSC_IN_RGB_HD_WIDE (3<< 14)
> +#define GSC_IN_RGB_HD_NARROW (2<< 14)
> +#define GSC_IN_RGB_SD_WIDE (1<< 14)
> +#define GSC_IN_RGB_SD_NARROW (0<< 14)
> +#define GSC_IN_YUV422_1P_ORDER_MASK (1<< 13)
> +#define GSC_IN_YUV422_1P_ORDER_LSB_Y (0<< 13)
> +#define GSC_IN_YUV422_1P_OEDER_LSB_C (1<< 13)
> +#define GSC_IN_CHROMA_ORDER_MASK (1<< 12)
> +#define GSC_IN_CHROMA_ORDER_CBCR (0<< 12)
> +#define GSC_IN_CHROMA_ORDER_CRCB (1<< 12)
> +#define GSC_IN_FORMAT_MASK (7<< 8)
> +#define GSC_IN_XRGB8888 (0<< 8)
> +#define GSC_IN_RGB565 (1<< 8)
> +#define GSC_IN_YUV420_2P (2<< 8)
> +#define GSC_IN_YUV420_3P (3<< 8)
> +#define GSC_IN_YUV422_1P (4<< 8)
> +#define GSC_IN_YUV422_2P (5<< 8)
> +#define GSC_IN_YUV422_3P (6<< 8)
> +#define GSC_IN_TILE_TYPE_MASK (1<< 4)
> +#define GSC_IN_TILE_C_16x8 (0<< 4)
> +#define GSC_IN_TILE_C_16x16 (1<< 4)
> +#define GSC_IN_TILE_MODE (1<< 3)
> +#define GSC_IN_LOCAL_SEL_MASK (3<< 1)
> +#define GSC_IN_LOCAL_FIMD_WB (2<< 1)
> +#define GSC_IN_LOCAL_CAM1 (1<< 1)
> +#define GSC_IN_LOCAL_CAM0 (0<< 1)
> +#define GSC_IN_PATH_MASK (1<< 0)
> +#define GSC_IN_PATH_LOCAL (1<< 0)
> +#define GSC_IN_PATH_MEMORY (0<< 0)
> +
> +/* G-Scaler source image size */
> +#define GSC_SRCIMG_SIZE 0x14
> +#define GSC_SRCIMG_HEIGHT_MASK (0x1fff<< 16)
> +#define GSC_SRCIMG_HEIGHT(x) ((x)<< 16)
> +#define GSC_SRCIMG_WIDTH_MASK (0x1fff<< 0)
> +#define GSC_SRCIMG_WIDTH(x) ((x)<< 0)
> +
> +/* G-Scaler source image offset */
> +#define GSC_SRCIMG_OFFSET 0x18
> +#define GSC_SRCIMG_OFFSET_Y_MASK (0x1fff<< 16)
> +#define GSC_SRCIMG_OFFSET_Y(x) ((x)<< 16)
> +#define GSC_SRCIMG_OFFSET_X_MASK (0x1fff<< 0)
> +#define GSC_SRCIMG_OFFSET_X(x) ((x)<< 0)
> +
> +/* G-Scaler cropped source image size */
> +#define GSC_CROPPED_SIZE 0x1C
Would be nice to use lower case, but it's up to you.
> +#define GSC_CROPPED_HEIGHT_MASK (0x1fff<< 16)
> +#define GSC_CROPPED_HEIGHT(x) ((x)<< 16)
> +#define GSC_CROPPED_WIDTH_MASK (0x1fff<< 0)
> +#define GSC_CROPPED_WIDTH(x) ((x)<< 0)
> +
> +/* G-Scaler output control */
> +#define GSC_OUT_CON 0x20
> +#define GSC_OUT_GLOBAL_ALPHA_MASK (0xff<< 24)
> +#define GSC_OUT_GLOBAL_ALPHA(x) ((x)<< 24)
> +#define GSC_OUT_RGB_TYPE_MASK (3<< 10)
> +#define GSC_OUT_RGB_HD_NARROW (3<< 10)
> +#define GSC_OUT_RGB_HD_WIDE (2<< 10)
> +#define GSC_OUT_RGB_SD_NARROW (1<< 10)
> +#define GSC_OUT_RGB_SD_WIDE (0<< 10)
> +#define GSC_OUT_YUV422_1P_ORDER_MASK (1<< 9)
> +#define GSC_OUT_YUV422_1P_ORDER_LSB_Y (0<< 9)
> +#define GSC_OUT_YUV422_1P_OEDER_LSB_C (1<< 9)
> +#define GSC_OUT_CHROMA_ORDER_MASK (1<< 8)
> +#define GSC_OUT_CHROMA_ORDER_CBCR (0<< 8)
> +#define GSC_OUT_CHROMA_ORDER_CRCB (1<< 8)
> +#define GSC_OUT_FORMAT_MASK (7<< 4)
> +#define GSC_OUT_XRGB8888 (0<< 4)
> +#define GSC_OUT_RGB565 (1<< 4)
> +#define GSC_OUT_YUV420_2P (2<< 4)
> +#define GSC_OUT_YUV420_3P (3<< 4)
> +#define GSC_OUT_YUV422_1P (4<< 4)
> +#define GSC_OUT_YUV422_2P (5<< 4)
> +#define GSC_OUT_YUV444 (7<< 4)
> +#define GSC_OUT_TILE_TYPE_MASK (1<< 2)
> +#define GSC_OUT_TILE_C_16x8 (0<< 2)
> +#define GSC_OUT_TILE_C_16x16 (1<< 2)
> +#define GSC_OUT_TILE_MODE (1<< 1)
> +#define GSC_OUT_PATH_MASK (1<< 0)
> +#define GSC_OUT_PATH_LOCAL (1<< 0)
> +#define GSC_OUT_PATH_MEMORY (0<< 0)
> +
> +/* G-Scaler scaled destination image size */
> +#define GSC_SCALED_SIZE 0x24
> +#define GSC_SCALED_HEIGHT_MASK (0x1fff<< 16)
> +#define GSC_SCALED_HEIGHT(x) ((x)<< 16)
> +#define GSC_SCALED_WIDTH_MASK (0x1fff<< 0)
> +#define GSC_SCALED_WIDTH(x) ((x)<< 0)
> +
> +/* G-Scaler pre scale ratio */
> +#define GSC_PRE_SCALE_RATIO 0x28
> +#define GSC_PRESC_SHFACTOR_MASK (7<< 28)
> +#define GSC_PRESC_SHFACTOR(x) ((x)<< 28)
> +#define GSC_PRESC_V_RATIO_MASK (7<< 16)
> +#define GSC_PRESC_V_RATIO(x) ((x)<< 16)
> +#define GSC_PRESC_H_RATIO_MASK (7<< 0)
> +#define GSC_PRESC_H_RATIO(x) ((x)<< 0)
> +
> +/* G-Scaler main scale horizontal ratio */
> +#define GSC_MAIN_H_RATIO 0x2C
> +#define GSC_MAIN_H_RATIO_MASK (0xfffff<< 0)
> +#define GSC_MAIN_H_RATIO_VALUE(x) ((x)<< 0)
> +
> +/* G-Scaler main scale vertical ratio */
> +#define GSC_MAIN_V_RATIO 0x30
> +#define GSC_MAIN_V_RATIO_MASK (0xfffff<< 0)
> +#define GSC_MAIN_V_RATIO_VALUE(x) ((x)<< 0)
> +
> +/* G-Scaler destination image size */
> +#define GSC_DSTIMG_SIZE 0x40
> +#define GSC_DSTIMG_HEIGHT_MASK (0x1fff<< 16)
> +#define GSC_DSTIMG_HEIGHT(x) ((x)<< 16)
> +#define GSC_DSTIMG_WIDTH_MASK (0x1fff<< 0)
> +#define GSC_DSTIMG_WIDTH(x) ((x)<< 0)
> +
> +/* G-Scaler destination image offset */
> +#define GSC_DSTIMG_OFFSET 0x44
> +#define GSC_DSTIMG_OFFSET_Y_MASK (0x1fff<< 16)
> +#define GSC_DSTIMG_OFFSET_Y(x) ((x)<< 16)
> +#define GSC_DSTIMG_OFFSET_X_MASK (0x1fff<< 0)
> +#define GSC_DSTIMG_OFFSET_X(x) ((x)<< 0)
> +
> +/* G-Scaler input y address mask */
> +#define GSC_IN_BASE_ADDR_Y_MASK 0x4C
> +/* G-Scaler input y base address */
> +#define GSC_IN_BASE_ADDR_Y(n) (0x50 + (n) * 0x4)
> +
> +/* G-Scaler input cb address mask */
> +#define GSC_IN_BASE_ADDR_CB_MASK 0x7C
> +/* G-Scaler input cb base address */
> +#define GSC_IN_BASE_ADDR_CB(n) (0x80 + (n) * 0x4)
> +
> +/* G-Scaler input cr address mask */
> +#define GSC_IN_BASE_ADDR_CR_MASK 0xAC
> +/* G-Scaler input cr base address */
> +#define GSC_IN_BASE_ADDR_CR(n) (0xB0 + (n) * 0x4)
> +
> +/* G-Scaler input address mask */
> +#define GSC_IN_CURR_ADDR_INDEX (0xf<< 12)
> +#define GSC_IN_CURR_GET_INDEX(x) ((x)>> 12)
> +#define GSC_IN_BASE_ADDR_PINGPONG(x) ((x)<< 8)
> +#define GSC_IN_BASE_ADDR_MASK (0xff<< 0)
> +
> +/* G-Scaler output y address mask */
> +#define GSC_OUT_BASE_ADDR_Y_MASK 0x10C
> +/* G-Scaler output y base address */
> +#define GSC_OUT_BASE_ADDR_Y(n) (0x110 + (n) * 0x4)
> +
> +/* G-Scaler output cb address mask */
> +#define GSC_OUT_BASE_ADDR_CB_MASK 0x15C
> +/* G-Scaler output cb base address */
> +#define GSC_OUT_BASE_ADDR_CB(n) (0x160 + (n) * 0x4)
> +
> +/* G-Scaler output cr address mask */
> +#define GSC_OUT_BASE_ADDR_CR_MASK 0x1AC
> +/* G-Scaler output cr base address */
> +#define GSC_OUT_BASE_ADDR_CR(n) (0x1B0 + (n) * 0x4)
> +
> +/* G-Scaler output address mask */
> +#define GSC_OUT_CURR_ADDR_INDEX (0xf<< 24)
> +#define GSC_OUT_CURR_GET_INDEX(x) ((x)>> 24)
> +#define GSC_OUT_BASE_ADDR_PINGPONG(x) ((x)<< 16)
> +#define GSC_OUT_BASE_ADDR_MASK (0xffff<< 0)
> +
> +#endif /* REGS_GSC_H_ */
> diff --git a/include/media/exynos_gscaler.h b/include/media/exynos_gscaler.h
> new file mode 100644
> index 0000000..e468fb5
> --- /dev/null
> +++ b/include/media/exynos_gscaler.h
> @@ -0,0 +1,49 @@
> +/* include/media/exynos_gscaler.h
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * Samsung EXYNOS SoC Gscaler driver header
> + *
> + * 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_GSCALER_H_
> +#define EXYNOS_GSCALER_H_
> +
> +#include<media/exynos_camera.h>
> +
> +/**
> + * struct exynos_platform_gscaler - camera host interface platform data
> + *
> + * @isp_info: properties of camera sensor required for host interface setup
> + */
> +struct exynos_platform_gscaler {
> + struct exynos_isp_info *isp_info[MAX_CAMIF_CLIENTS];
I suggest renaming struct exynos_isp_info to struct exynos_sensor_info.
> + u32 active_cam_index;
> + u32 num_clients;
> + u32 cam_preview:1;
> + u32 cam_camcording:1;
> +};
> +
> +extern struct exynos_platform_gscaler exynos_gsc0_default_data;
> +extern struct exynos_platform_gscaler exynos_gsc1_default_data;
> +extern struct exynos_platform_gscaler exynos_gsc2_default_data;
> +extern struct exynos_platform_gscaler exynos_gsc3_default_data;
Is this really needed in this header file ? Would be nice to move it
somewhere else.
> +
> +/**
> + * exynos5_gsc_set_parent_clock() = Exynos5 setup function for parent clock.
> + * @child: child clock used for gscaler
> + * @parent: parent clock used for gscaler
> + */
> +int __init exynos5_gsc_set_parent_clock(const char *child, const char *parent);
> +
> +/**
> + * exynos5_gsc_set_clock_rate() = Exynos5 setup function for clock rate.
> + * @clk: name of clock used for gscaler
> + * @clk_rate: clock_rate for gscaler clock
> + */
> +int __init exynos5_gsc_set_clock_rate(const char *clk, unsigned long clk_rate);
> +#endif /* EXYNOS_GSCALER_H_ */
--
Regards,
Sylwester
prev parent reply other threads:[~2012-03-08 23:38 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-02-15 6:09 [PATCH] media: gscaler: Add new driver for general scaler Sungchun Kang
2012-03-08 23:38 ` Sylwester Nawrocki [this message]
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=4F5942EC.9030502@gmail.com \
--to=snjw23@gmail.com \
--cc=jonghun.han@samsung.com \
--cc=khw0178.kim@samsung.com \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@infradead.org \
--cc=sungchun.kang@samsung.com \
--cc=sy0816.kang@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.