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@xxxxxxxxxxx> > Signed-off-by: Sungchun Kang<sungchun.kang@xxxxxxxxxxx> > > 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@xxxxxxxxxxx>"); > +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 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html