Hi Pawel, Thanks for the review comments., I will try to release the v3 patch in a day or two with your comments addressed ... please find my review comments inline... On Wed, Jul 11, 2012 at 11:22 PM, Pawel Osciak <pawel@xxxxxxxxxx> wrote: > Hi Shaik, > Thanks for the patches. Please find my comments inline. > Please also make sure you run checkpatch. I will make sure this... > > On Thu, Jul 5, 2012 at 3:27 AM, Shaik Ameer Basha > <shaik.ameer@xxxxxxxxxxx> wrote: >> This patch add support for gscaler (generic scaler) 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> >> Signed-off-by: Shaik Ameer Basha <shaik.ameer@xxxxxxxxxxx> >> --- >> drivers/media/video/Kconfig | 10 + >> drivers/media/video/Makefile | 1 + >> drivers/media/video/exynos/Kconfig | 11 + >> drivers/media/video/exynos/Makefile | 1 + >> drivers/media/video/exynos/gsc/Kconfig | 7 + >> drivers/media/video/exynos/gsc/Makefile | 3 + >> drivers/media/video/exynos/gsc/gsc-core.c | 1304 +++++++++++++++++++++++++++++ >> drivers/media/video/exynos/gsc/gsc-core.h | 652 ++++++++++++++ >> drivers/media/video/exynos/gsc/gsc-m2m.c | 751 +++++++++++++++++ >> drivers/media/video/exynos/gsc/gsc-regs.c | 579 +++++++++++++ >> drivers/media/video/exynos/gsc/gsc-regs.h | 211 +++++ >> include/linux/videodev2.h | 2 + >> 12 files changed, 3532 insertions(+), 0 deletions(-) >> create mode 100644 drivers/media/video/exynos/Kconfig >> create mode 100644 drivers/media/video/exynos/Makefile >> 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-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-regs.c >> create mode 100644 drivers/media/video/exynos/gsc/gsc-regs.h >> >> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig >> index 99937c9..0ff1abf 100644 >> --- a/drivers/media/video/Kconfig >> +++ b/drivers/media/video/Kconfig >> @@ -1153,6 +1153,7 @@ config VIDEO_ATMEL_ISI >> >> source "drivers/media/video/s5p-fimc/Kconfig" >> source "drivers/media/video/s5p-tv/Kconfig" >> +source "drivers/media/video/exynos/Kconfig" > > I believe we'd prefer to have it under exynos-gscaler directory and > not create general platform directories. > OK. I will create "exynos-gsc" folder. >> >> endif # V4L_PLATFORM_DRIVERS >> endif # VIDEO_CAPTURE_DRIVERS >> @@ -1215,4 +1216,13 @@ config VIDEO_MX2_EMMAPRP >> memory to memory. Operations include resizing and format >> conversion. >> >> +config VIDEO_SAMSUNG_GSCALER >> + tristate "Samsung Exynos GSC driver" >> + depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P >> + select VIDEOBUF2_DMA_CONTIG >> + select V4L2_MEM2MEM_DEV >> + select DMA_SHARED_BUFFER >> + help >> + This is v4l2 based g-scaler driver for EXYNOS5 >> + >> endif # V4L_MEM2MEM_DRIVERS >> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile >> index d209de0..4389f3e 100644 >> --- a/drivers/media/video/Makefile >> +++ b/drivers/media/video/Makefile >> @@ -192,6 +192,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ >> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ >> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ >> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ >> +obj-$(CONFIG_VIDEO_EXYNOS) += exynos/ >> >> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ >> >> diff --git a/drivers/media/video/exynos/Kconfig b/drivers/media/video/exynos/Kconfig >> new file mode 100644 >> index 0000000..f5836a2 >> --- /dev/null >> +++ b/drivers/media/video/exynos/Kconfig >> @@ -0,0 +1,11 @@ >> +config VIDEO_EXYNOS >> + bool "Exynos Multimedia Devices" >> + depends on ARCH_EXYNOS5 >> + select VIDEO_FIXED_MINOR_RANGES >> + select VIDEOBUF2_DMA_CONTIG >> + help >> + This is a representative exynos multimedia device. >> + >> +if VIDEO_EXYNOS >> + source "drivers/media/video/exynos/gsc/Kconfig" >> +endif > > Looks like you have two different Kconfigs and Makefiles for the same > thing. Please don't create an exynos/gsc directories, just exynos-gsc > and put everything in there. Don't create any Kconfigs, just add the > entry to the drivers/media/video/Kconfig and only have a Makefile in > exynos-gsc. s5p-g2d is a good example. > >> diff --git a/drivers/media/video/exynos/Makefile b/drivers/media/video/exynos/Makefile >> new file mode 100644 >> index 0000000..1666724 >> --- /dev/null >> +++ b/drivers/media/video/exynos/Makefile >> @@ -0,0 +1 @@ >> +obj-$(CONFIG_VIDEO_EXYNOS_GSCALER) += gsc/ >> diff --git a/drivers/media/video/exynos/gsc/Kconfig b/drivers/media/video/exynos/gsc/Kconfig >> new file mode 100644 >> index 0000000..9036078 >> --- /dev/null >> +++ b/drivers/media/video/exynos/gsc/Kconfig >> @@ -0,0 +1,7 @@ >> +config VIDEO_EXYNOS_GSCALER >> + bool "Exynos G-Scaler driver" >> + depends on VIDEO_EXYNOS >> + select MEDIA_EXYNOS >> + select V4L2_MEM2MEM_DEV >> + help >> + This is a v4l2 driver for exynos G-Scaler device. >> diff --git a/drivers/media/video/exynos/gsc/Makefile b/drivers/media/video/exynos/gsc/Makefile >> new file mode 100644 >> index 0000000..d3f66e4 >> --- /dev/null >> +++ b/drivers/media/video/exynos/gsc/Makefile >> @@ -0,0 +1,3 @@ >> +gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o >> + >> +obj-$(CONFIG_VIDEO_SAMSUNG_GSCALER) += gsc.o >> 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..06bd24f >> --- /dev/null >> +++ b/drivers/media/video/exynos/gsc/gsc-core.c >> @@ -0,0 +1,1304 @@ >> +/* linux/drivers/media/video/exynos/gsc/gsc-core.c > > No need for this line. OK. I will remove that. > >> + * >> + * Copyright (c) 2011 - 2012 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 <linux/of.h> >> +#include "gsc-core.h" >> + >> +#define GSC_CLOCK_GATE_NAME "gscl" >> + >> +int gsc_dbg; >> +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, >> + .num_comp = 1, >> + }, { >> + .name = "XRGB-8-8-8-8, 32 bpp", >> + .pixelformat = V4L2_PIX_FMT_RGB32, >> + .depth = { 32 }, >> + .color = GSC_RGB, >> + .num_planes = 1, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_comp = 3, >> + }, { >> + .name = "YUV 4:2:0 planar, YCbCr", > > Same name as above, should be YCrCb? Ah... I will change that. > >> + .pixelformat = V4L2_PIX_FMT_YVU420, >> + .depth = { 12 }, >> + .color = GSC_YUV420, > > Is this a copy-paste bug or should be the same as above? ".color" remains same, the VU order is taken care under ".corder" field > >> + .yorder = GSC_LSB_Y, >> + .corder = GSC_CRCB, >> + .num_planes = 1, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_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, >> + .num_comp = 3, >> + }, { >> + .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cr/Cb", > > YUV -> YVU? Its same.. YUV. as you can see, the plane order is changed in the name (Y/Cr/Cb) > >> + .pixelformat = V4L2_PIX_FMT_YVU420M, >> + .depth = { 8, 2, 2 }, >> + .color = GSC_YUV420, >> + .yorder = GSC_LSB_Y, >> + .corder = GSC_CRCB, >> + .num_planes = 3, >> + .num_comp = 3, >> + }, { >> + .name = >> + "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled", >> + .pixelformat = V4L2_PIX_FMT_NV12MT_16X16, >> + .depth = { 8, 4 }, >> + .color = GSC_YUV420, >> + .yorder = GSC_LSB_Y, >> + .corder = GSC_CBCR, >> + .num_planes = 2, >> + .num_comp = 2, >> + }, >> +}; >> + >> +struct gsc_fmt *get_format(int index) >> +{ > > if (index >= ARRAY_SIZE(gsc_formats)) > return NULL; > Ok. I will change that. >> + 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) >> +{ >> + 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; >> + u32 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; >> + } > > Looks to me like only the last 2 lines in both branches are different, > so the first 5 could be abstracted out. Ok. I will change that. > >> + 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; > > I don't know the right answer, but why JPEG not SRGB? Gscaler supports RGB Types RGB601 (SD, 16 ~ 235) RGB601 (SD, 0 ~ 255) RGB709 (HD, 16 ~ 235) RGB709 (HD, 0 ~ 255) so i will change this field to either V4L2_COLORSPACE_REC709 or V4L2_COLORSPACE_SMPTE170M as default. Is it fine ??? > >> + 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("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); >> + >> + 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->num_comp == 1) || >> + is_rgb(f->fmt->color)) >> + min_w = 32; >> + else >> + min_w = 64; >> + if ((is_yuv422(f->fmt->color) && f->fmt->num_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", >> + mod_x, mod_y, min_w, min_h); >> + gsc_dbg("tmp_w : %d, tmp_h : %d", 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); >> + } > > No need for braces. > Ok. I will change this. >> + >> + /* 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; >> + > > Would be nice to ensure tx and ty were not zeros, unless it's done somewhere > else. > Ok. I will check for zero parameters. >> + 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", >> + sc->pre_shfactor, sc->pre_hratio); >> + gsc_dbg("pre_v :%d, main_h : %ld, main_v : %ld", >> + sc->pre_vratio, sc->main_hratio, sc->main_vratio); >> + >> + 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, >> +}; >> + >> +static const struct v4l2_ctrl_config gsc_custom_ctrl[] = { >> + { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_GLOBAL_ALPHA, >> + .name = "Set RGB alpha", >> + .type = V4L2_CTRL_TYPE_INTEGER, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + .max = 255, >> + .step = 1, >> + .def = 0, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_CACHEABLE, >> + .name = "Set cacheable", >> + .type = V4L2_CTRL_TYPE_BOOLEAN, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + .max = 1, >> + .def = true, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_TV_LAYER_BLEND_ENABLE, >> + .name = "Enable layer alpha blending", >> + .type = V4L2_CTRL_TYPE_BOOLEAN, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_TV_LAYER_BLEND_ALPHA, >> + .name = "Set alpha for layer blending", >> + .type = V4L2_CTRL_TYPE_INTEGER, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + .min = 0, >> + .max = 255, >> + .step = 1, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_TV_PIXEL_BLEND_ENABLE, >> + .name = "Enable pixel alpha blending", >> + .type = V4L2_CTRL_TYPE_BOOLEAN, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_TV_CHROMA_ENABLE, >> + .name = "Enable chromakey", >> + .type = V4L2_CTRL_TYPE_BOOLEAN, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_TV_CHROMA_VALUE, >> + .name = "Set chromakey value", >> + .type = V4L2_CTRL_TYPE_INTEGER, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + .min = 0, >> + .max = 255, >> + .step = 1, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_CSC_EQ_MODE, >> + .name = "Set CSC equation mode", >> + .type = V4L2_CTRL_TYPE_BOOLEAN, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + .max = DEFAULT_CSC_EQ, >> + .def = DEFAULT_CSC_EQ, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_CSC_EQ, >> + .name = "Set CSC equation", >> + .type = V4L2_CTRL_TYPE_INTEGER, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + .step = 1, >> + .max = 8, >> + .def = V4L2_COLORSPACE_REC709, >> + }, { >> + .ops = &gsc_ctrl_ops, >> + .id = V4L2_CID_CSC_RANGE, >> + .name = "Set CSC range", >> + .type = V4L2_CTRL_TYPE_BOOLEAN, >> + .flags = V4L2_CTRL_FLAG_SLIDER, >> + .max = DEFAULT_CSC_RANGE, >> + .def = DEFAULT_CSC_RANGE, >> + }, >> +}; >> + >> +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); >> + >> + >> + ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[0], NULL); >> + ctx->gsc_ctrls.layer_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[2], NULL); >> + ctx->gsc_ctrls.layer_alpha = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[3], NULL); >> + ctx->gsc_ctrls.pixel_blend_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[4], NULL); >> + ctx->gsc_ctrls.chroma_en = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[5], NULL); >> + ctx->gsc_ctrls.chroma_val = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[6], NULL); >> + >> +/* for CSC equation */ >> + ctx->gsc_ctrls.csc_eq_mode = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[7], NULL); >> + ctx->gsc_ctrls.csc_eq = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[8], NULL); >> + ctx->gsc_ctrls.csc_range = v4l2_ctrl_new_custom(&ctx->ctrl_handler, >> + &gsc_custom_ctrl[9], NULL); >> + >> + ctx->ctrls_rdy = ctx->ctrl_handler.error == 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 (num_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)) { > > Can vb pointer really contain ERRors? I think you should use !vb here. > sorry. i will change this. >> + gsc_err("Invalid argument"); >> + return -EINVAL; >> + } >> + >> + pix_size = frame->f_width * frame->f_height; >> + >> + gsc_dbg("num_planes= %d, num_comp= %d, pix_size= %d", >> + frame->fmt->num_planes, frame->fmt->num_comp, pix_size); >> + >> + addr->y = vb2_dma_contig_plane_dma_addr(vb, 0); >> + >> + if (frame->fmt->num_planes == 1) { >> + switch (frame->fmt->num_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)); > > No need to distinguish between 420 and 422? Yes. I will update the code. > >> + 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) { > > What about other YVU formats? Ok. I will update the code. > >> + 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; >> +} >> + >> +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); > > Why not job_finish before wake_up if we are shutting down? yes. you are right.. i will update the code. > >> + /* wake_up job_abort, stop_streaming */ >> + spin_lock(&ctx->slock); >> + if (ctx->state & GSC_CTX_STOP_REQ) { >> + ctx->state &= ~GSC_CTX_STOP_REQ; >> + wake_up(&gsc->irq_queue); >> + } >> + spin_unlock(&ctx->slock); >> + } >> + } >> + >> +isr_unlock: >> + spin_unlock(&gsc->slock); >> + return IRQ_HANDLED; >> +} >> + >> +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); >> + 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); >> + int ret; >> + >> + ret = clk_enable(gsc->clock); >> + if (ret) >> + return ret; >> + >> + set_bit(ST_PWR_ON, &gsc->state); >> + return 0; >> +} >> + >> +static inline void *gsc_get_drv_data(struct platform_device *pdev); >> + >> +static int gsc_probe(struct platform_device *pdev) >> +{ >> + struct gsc_dev *gsc; >> + struct resource *res; >> + struct gsc_driverdata *drv_data; >> + >> + int ret = 0; >> + >> + dev_dbg(&pdev->dev, "%s():\n", __func__); >> + drv_data = (struct gsc_driverdata *) >> + gsc_get_drv_data(pdev); >> + >> + if (pdev->dev.of_node) { >> + pdev->id = of_alias_get_id(pdev->dev.of_node, "gsc"); >> + if (pdev->id < 0) >> + dev_err(&pdev->dev, >> + "failed to get alias id, errno %d\n", ret); >> + } >> + >> + if (pdev->id >= drv_data->num_entities) { >> + dev_err(&pdev->dev, "Invalid platform device id: %d\n", >> + pdev->id); >> + return -EINVAL; >> + } >> + >> + gsc = devm_kzalloc(&pdev->dev, sizeof(struct gsc_dev), GFP_KERNEL); >> + 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); >> + gsc->regs = devm_request_and_ioremap(&pdev->dev, res); >> + if (!gsc->regs) { >> + dev_err(&pdev->dev, "failed to map registers\n"); >> + return -ENOENT; >> + } >> + >> + /* 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); >> + return -ENXIO; >> + } >> + clk_put(gsc->clock); >> + >> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "failed to get IRQ resource\n"); >> + return -ENXIO; >> + } >> + gsc->irq = res->start; >> + >> + ret = devm_request_irq(&pdev->dev, gsc->irq, gsc_irq_handler, 0, >> + pdev->name, gsc); >> + if (ret) { >> + dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); >> + return ret; >> + } >> + >> + gsc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); >> + if (IS_ERR(gsc->alloc_ctx)) { >> + ret = PTR_ERR(gsc->alloc_ctx); >> + goto err_irq; >> + } >> + >> + platform_set_drvdata(pdev, gsc); >> + >> + ret = gsc_register_m2m_device(gsc); >> + if (ret) >> + goto err_irq; >> + >> + gsc_runtime_resume(&pdev->dev); >> + pm_runtime_enable(&pdev->dev); >> + >> + gsc_info("gsc-%d registered successfully", gsc->id); >> + return 0; >> + > > I think you are missing additional error path here with > platform_release_resource()s > that you might have acquired with platform_get_resource(). > platform_get_resource() is getting some references from platform_device. I don't think we need to explicitly release these resources.. Is there anything i am missing?? i mean any API's ??? >> +err_irq: >> + free_irq(gsc->irq, 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); >> + >> + 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__); >> + } >> + >> + pm_runtime_put_sync(dev); >> + >> + return ret; >> +} >> + >> +static int gsc_resume(struct device *dev) >> +{ >> + struct gsc_dev *gsc; >> + struct gsc_ctx *ctx; >> + >> + gsc = dev_get_drvdata(dev); >> + >> + 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 structs? > Ok. I will change this. >> + >> +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); >> + >> +#ifdef CONFIG_OF >> +static const struct of_device_id exynos_gsc_match[] = { >> + { .compatible = "samsung,exynos-gsc", >> + .data = &gsc_v_100_drvdata, }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, exynos_gsc_match); >> +#else >> +#define exynos_gsc_match NULL >> +#endif >> + >> +static inline void *gsc_get_drv_data(struct platform_device *pdev) >> +{ >> +#ifdef CONFIG_OF >> + if (pdev->dev.of_node) { >> + const struct of_device_id *match; >> + match = of_match_node(exynos_gsc_match, pdev->dev.of_node); >> + return (struct gsc_driverdata *) match->data; >> + } >> +#endif >> + return (struct gsc_driverdata *) >> + platform_get_device_id(pdev)->driver_data; >> +} >> + >> +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, >> + .of_match_table = exynos_gsc_match, >> + } >> +}; >> + >> +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..3a236ec >> --- /dev/null >> +++ b/drivers/media/video/exynos/gsc/gsc-core.h >> @@ -0,0 +1,652 @@ >> +/* 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> >> +#define CONFIG_VB2_GSC_DMA_CONTIG 1 >> +#include <media/videobuf2-dma-contig.h> >> +#include "gsc-regs.h" >> +extern int gsc_dbg; >> + >> +#define GSC_MODULE_NAME "exynos-gsc" >> + >> +#define gsc_info(fmt, args...) \ >> + do { \ >> + if (gsc_dbg >= 6) \ >> + pr_info("[INFO]%s:%d: "fmt "\n", \ >> + __func__, __LINE__, ##args); \ >> + } while (0) >> + >> +#define gsc_err(fmt, args...) \ >> + do { \ >> + if (gsc_dbg >= 3) \ >> + pr_err("[ERROR]%s:%d: "fmt "\n", \ >> + __func__, __LINE__, ##args); \ >> + } while (0) >> + >> +#define gsc_warn(fmt, args...) \ >> + do { \ >> + if (gsc_dbg >= 4) \ >> + pr_warn("[WARN]%s:%d: "fmt "\n", \ >> + __func__, __LINE__, ##args); \ >> + } while (0) >> + >> +#define gsc_dbg(fmt, args...) \ >> + do { \ >> + if (gsc_dbg >= 7) \ >> + pr_debug("[DEBUG]%s:%d: "fmt "\n", \ >> + __func__, __LINE__, ##args); \ >> + } while (0) >> + >> +#define GSC_MAX_CLOCKS 3 > > never used > Ok. i will remove all the unused code... >> +#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) >> +#define GSC_MAX_DEVS 4 >> +#define WORKQUEUE_NAME_SIZE 32 > > never used > >> +#define FIMD_NAME_SIZE 32 > > ditto > >> +#define GSC_M2M_BUF_NUM 0 >> +#define GSC_OUT_BUF_MAX 2 > > ditto > >> +#define GSC_MAX_CTRL_NUM 10 >> +#define GSC_OUT_MAX_MASK_NUM 7 > > ditto > >> +#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 > > ditto for all 6 above. > >> +#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 > > ditto for all 4 above. > >> + >> +#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 > > ditto > >> + >> +#define V4L2_CID_CACHEABLE (V4L2_CID_LASTP1 + 1) >> +#define V4L2_CID_TV_LAYER_BLEND_ENABLE (V4L2_CID_LASTP1 + 2) >> +#define V4L2_CID_TV_LAYER_BLEND_ALPHA (V4L2_CID_LASTP1 + 3) >> +#define V4L2_CID_TV_PIXEL_BLEND_ENABLE (V4L2_CID_LASTP1 + 4) >> +#define V4L2_CID_TV_CHROMA_ENABLE (V4L2_CID_LASTP1 + 5) >> +#define V4L2_CID_TV_CHROMA_VALUE (V4L2_CID_LASTP1 + 6) >> +/* for color space conversion equation selection */ >> +#define V4L2_CID_CSC_EQ_MODE (V4L2_CID_LASTP1 + 8) >> +#define V4L2_CID_CSC_EQ (V4L2_CID_LASTP1 + 9) >> +#define V4L2_CID_CSC_RANGE (V4L2_CID_LASTP1 + 10) >> +#define V4L2_CID_GLOBAL_ALPHA (V4L2_CID_LASTP1 + 11) >> + >> +enum gsc_dev_flags { >> + /* for global */ >> + ST_PWR_ON, >> + ST_STOP_REQ, >> + /* for m2m node */ >> + ST_M2M_OPEN, >> + ST_M2M_RUN, >> +}; >> + >> +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 ctrl_to_ctx(__ctrl) \ >> + container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler) >> +/** >> + * 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 num_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; >> +}; > > Can you do this? Keep the pointer to the v4l2_ctrl struct passed to s_ctrl > after it returns? Yes. I think we can keep the reference.. but updating the values are not valid... which i was doing in the user_to_drv() funciton. I will change that.. > >> + >> +/** >> + * 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_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_vb2 { >> + const struct vb2_mem_ops *ops; >> + void *(*init)(struct gsc_dev *gsc); >> + void (*cleanup)(void *alloc_ctx); >> + >> + void (*resume)(void *alloc_ctx); >> + void (*suspend)(void *alloc_ctx); >> + >> + int (*cache_flush)(struct vb2_buffer *vb, u32 num_planes); >> + void (*set_cacheable)(void *alloc_ctx, bool cacheable); >> + void (*set_sharable)(void *alloc_ctx, bool sharable); >> +}; > > I don't see anything from here being used apart from resume/suspend. > Am I missing something? > >> + >> +/** >> + * 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 gsc_m2m_device m2m; >> + struct exynos_platform_gscaler *pdata; >> + unsigned long state; >> + struct vb2_alloc_ctx *alloc_ctx; >> + const struct gsc_vb2 *vb2; >> +}; >> + >> +/** >> + * 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); > > no such function > >> +int gsc_register_m2m_device(struct gsc_dev *gsc); >> +void gsc_unregister_m2m_device(struct gsc_dev *gsc); >> + >> +u32 get_plane_size(struct gsc_frame *fr, unsigned int plane); >> +char gsc_total_fmts(void); > > no such function > >> +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); > > Above two don't exist. > >> +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); > > Ditto for above two. > >> + >> +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; >> +} > > unused > >> +static inline int is_tiled(struct gsc_fmt *fmt) >> +{ >> + return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; >> +} >> + >> +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; >> +} > > never used > >> + >> +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 void user_to_drv(struct v4l2_ctrl *ctrl, s32 value) >> +{ >> + ctrl->cur.val = ctrl->val = value; >> +} > > This is fishy. Can you store the pointer to the control after s_ctrl returns? > Sorry.. i read the documentation... we can store the pointer but we should not update values explicitly... I will change this implementation. >> + >> +void gsc_hw_set_sw_reset(struct gsc_dev *dev); >> +void gsc_hw_set_one_frm_mode(struct gsc_dev *dev, bool mask); > > Both never used. > >> +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); > > Never used. > >> +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); > > both never used > >> +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); >> + >> +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_nr_unmask_bits(struct gsc_dev *dev); >> +int gsc_wait_reset(struct gsc_dev *dev); > > Above 4 never used. > >> +int gsc_wait_operating(struct gsc_dev *dev); >> +int gsc_wait_stop(struct gsc_dev *dev); > > Never used. > >> + >> +#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..ead59a0 >> --- /dev/null >> +++ b/drivers/media/video/exynos/gsc/gsc-m2m.c >> @@ -0,0 +1,751 @@ >> +/* 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_start_streaming(struct vb2_queue *q, unsigned int count) >> +{ >> + struct gsc_ctx *ctx = q->drv_priv; >> + int ret; >> + >> + ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev); >> + return ret > 0 ? 0 : 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 */ > > Can we fix this? Ok. I will fix this and all others... > >> + if (ret < 0) >> + dev_err(&gsc->pdev->dev, "wait timeout : %s\n", __func__); >> + >> + pm_runtime_put(&ctx->gsc_dev->pdev->dev); >> + >> + 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 */ > > Can we fix this? > >> + 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; >> + >> + 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); >> + */ > > Leftover? > >> + >> + 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); >> +} >> + >> +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, >> + .start_streaming = gsc_m2m_start_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_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); >> + >> + 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); >> + >> + 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; >> + >> + 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; > > Needs braces. Ok. I will fix. > >> + 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); >> +} >> + >> +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ >> +static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) >> +{ >> + if (a->left < b->left || a->top < b->top) >> + return 0; >> + >> + if (a->left + a->width > b->left + b->width) >> + return 0; >> + >> + if (a->top + a->height > b->top + b->height) >> + return 0; >> + >> + return 1; >> +} >> + >> +static int gsc_m2m_g_selection(struct file *file, void *fh, >> + struct v4l2_selection *s) >> +{ >> + struct gsc_frame *frame; >> + struct gsc_ctx *ctx = fh_to_ctx(fh); >> + >> + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && >> + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) >> + return -EINVAL; >> + >> + frame = ctx_get_frame(ctx, s->type); >> + if (IS_ERR(frame)) >> + return PTR_ERR(frame); >> + >> + switch (s->target) { >> + case V4L2_SEL_TGT_COMPOSE_DEFAULT: >> + case V4L2_SEL_TGT_COMPOSE_BOUNDS: >> + case V4L2_SEL_TGT_CROP_BOUNDS: >> + case V4L2_SEL_TGT_CROP_DEFAULT: >> + s->r.left = 0; >> + s->r.top = 0; >> + s->r.width = frame->f_width; >> + s->r.height = frame->f_height; >> + return 0; >> + >> + case V4L2_SEL_TGT_COMPOSE_ACTIVE: >> + case V4L2_SEL_TGT_CROP_ACTIVE: >> + s->r.left = frame->crop.left; >> + s->r.top = frame->crop.top; >> + s->r.width = frame->crop.width; >> + s->r.height = frame->crop.height; >> + return 0; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int gsc_m2m_s_selection(struct file *file, void *fh, >> + struct v4l2_selection *s) >> +{ >> + struct gsc_frame *frame; >> + struct gsc_ctx *ctx = fh_to_ctx(fh); >> + struct v4l2_crop cr; >> + struct gsc_variant *variant = ctx->gsc_dev->variant; >> + int ret; >> + >> + cr.type = s->type; >> + cr.c = s->r; >> + >> + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && >> + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) >> + return -EINVAL; >> + >> + ret = gsc_try_crop(ctx, &cr); >> + if (ret) >> + return ret; >> + >> + if (s->flags & V4L2_SEL_FLAG_LE && >> + !is_rectangle_enclosed(&cr.c, &s->r)) >> + return -ERANGE; >> + >> + if (s->flags & V4L2_SEL_FLAG_GE && >> + !is_rectangle_enclosed(&s->r, &cr.c)) >> + return -ERANGE; >> + >> + s->r = cr.c; >> + >> + switch (s->target) { >> + case V4L2_SEL_TGT_COMPOSE_BOUNDS: >> + case V4L2_SEL_TGT_COMPOSE_DEFAULT: >> + case V4L2_SEL_TGT_COMPOSE_ACTIVE: >> + frame = &ctx->s_frame; >> + break; >> + >> + case V4L2_SEL_TGT_CROP_BOUNDS: >> + case V4L2_SEL_TGT_CROP_ACTIVE: >> + case V4L2_SEL_TGT_CROP_DEFAULT: >> + frame = &ctx->d_frame; >> + break; >> + >> + default: >> + return -EINVAL; >> + } >> + >> + /* Check to see if scaling ratio is within supported range */ >> + if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) { >> + if (s->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; >> + } >> + } >> + >> + frame->crop = cr.c; >> + >> + gsc_ctx_state_lock_set(GSC_PARAMS, ctx); >> + return 0; >> +} >> + >> +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_selection = gsc_m2m_g_selection, >> + .vidioc_s_selection = gsc_m2m_s_selection >> + >> +}; >> + >> +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); >> + >> + 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: > > No need to ctrls_delete somewhere here as well? > Ok. I will fix. >> + 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.%d:m2m", >> + GSC_MODULE_NAME, gsc->id); >> + >> + 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-regs.c b/drivers/media/video/exynos/gsc/gsc-regs.c >> new file mode 100644 >> index 0000000..7c5cce2 >> --- /dev/null >> +++ b/drivers/media/video/exynos/gsc/gsc-regs.c >> @@ -0,0 +1,579 @@ >> +/* 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); >> +} >> + >> +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; >> +} >> + >> +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); > > mask_bits &= > >> + 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; >> + >> + 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->num_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; >> + }; >> + >> + if (is_tiled(frame->fmt)) >> + cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE; >> + >> + 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->num_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; >> + }; >> + >> + if (is_tiled(frame->fmt)) >> + cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE; >> + >> +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; >> + >> + if (!is_rgb(frame->fmt->color)) { >> + gsc_dbg("Not a RGB format"); >> + return; >> + } >> + >> + cfg = readl(dev->regs + GSC_OUT_CON); >> + cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK; >> + >> + 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); >> +} >> diff --git a/drivers/media/video/exynos/gsc/gsc-regs.h b/drivers/media/video/exynos/gsc/gsc-regs.h >> new file mode 100644 >> index 0000000..50ddabe >> --- /dev/null >> +++ b/drivers/media/video/exynos/gsc/gsc-regs.h >> @@ -0,0 +1,211 @@ >> +/* 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_ >> + >> +/* 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 >> +#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) > > Some of the defines in this file are not used like those above. > I will remove the unused code... >> + >> +#endif /* REGS_GSC_H_ */ >> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h >> index f79d0cc..5b4a33b 100644 >> --- a/include/linux/videodev2.h >> +++ b/include/linux/videodev2.h >> @@ -360,9 +360,11 @@ struct v4l2_pix_format { >> /* two non contiguous planes - one Y, one Cr + Cb interleaved */ >> #define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ >> #define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */ > > Already defined in videodev2.h > >> +#define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 macroblocks */ >> >> /* three non contiguous planes - Y, Cb, Cr */ >> #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */ >> +#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'V', 'U', 'M') /* 12 YVU420 planar */ >> >> /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ >> #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ > > Some of those formats are defined in videodev2.h. For those that are not, > you should add them there instead. > >> -- >> 1.7.0.4 >> >> -- >> 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 > > > > -- > Best regards, > Pawel Osciak Regards, Shaik Ameer Basha -- 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