From 3bae07d4b44cf10bbffc6270e00816dda57e6e70 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 13 Mar 2022 11:18:16 +0100 Subject: media: platform: rename exynos-gsc/ to samsung/exynos-gsc/ As the end goal is to have platform drivers split by vendor, rename exynos-gsc/ to samsung/exynos-gsc/. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/Makefile | 2 +- drivers/media/platform/exynos-gsc/Kconfig | 10 - drivers/media/platform/exynos-gsc/Makefile | 4 - drivers/media/platform/exynos-gsc/gsc-core.c | 1327 -------------------- drivers/media/platform/exynos-gsc/gsc-core.h | 521 -------- drivers/media/platform/exynos-gsc/gsc-m2m.c | 794 ------------ drivers/media/platform/exynos-gsc/gsc-regs.c | 426 ------- drivers/media/platform/exynos-gsc/gsc-regs.h | 169 --- drivers/media/platform/samsung/exynos-gsc/Kconfig | 10 + drivers/media/platform/samsung/exynos-gsc/Makefile | 4 + .../media/platform/samsung/exynos-gsc/gsc-core.c | 1327 ++++++++++++++++++++ .../media/platform/samsung/exynos-gsc/gsc-core.h | 521 ++++++++ .../media/platform/samsung/exynos-gsc/gsc-m2m.c | 794 ++++++++++++ .../media/platform/samsung/exynos-gsc/gsc-regs.c | 426 +++++++ .../media/platform/samsung/exynos-gsc/gsc-regs.h | 169 +++ 16 files changed, 3253 insertions(+), 3253 deletions(-) delete mode 100644 drivers/media/platform/exynos-gsc/Kconfig delete mode 100644 drivers/media/platform/exynos-gsc/Makefile delete mode 100644 drivers/media/platform/exynos-gsc/gsc-core.c delete mode 100644 drivers/media/platform/exynos-gsc/gsc-core.h delete mode 100644 drivers/media/platform/exynos-gsc/gsc-m2m.c delete mode 100644 drivers/media/platform/exynos-gsc/gsc-regs.c delete mode 100644 drivers/media/platform/exynos-gsc/gsc-regs.h create mode 100644 drivers/media/platform/samsung/exynos-gsc/Kconfig create mode 100644 drivers/media/platform/samsung/exynos-gsc/Makefile create mode 100644 drivers/media/platform/samsung/exynos-gsc/gsc-core.c create mode 100644 drivers/media/platform/samsung/exynos-gsc/gsc-core.h create mode 100644 drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c create mode 100644 drivers/media/platform/samsung/exynos-gsc/gsc-regs.c create mode 100644 drivers/media/platform/samsung/exynos-gsc/gsc-regs.h diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 8654961ad5db..48210ad0eb50 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -74,7 +74,6 @@ source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" source "drivers/media/platform/davinci/Kconfig" -source "drivers/media/platform/exynos-gsc/Kconfig" source "drivers/media/platform/intel/Kconfig" source "drivers/media/platform/marvell/Kconfig" source "drivers/media/platform/mediatek/mtk-jpeg/Kconfig" @@ -92,6 +91,7 @@ source "drivers/media/platform/s3c-camif/Kconfig" source "drivers/media/platform/s5p-g2d/Kconfig" source "drivers/media/platform/s5p-jpeg/Kconfig" source "drivers/media/platform/s5p-mfc/Kconfig" +source "drivers/media/platform/samsung/exynos-gsc/Kconfig" source "drivers/media/platform/samsung/exynos4-is/Kconfig" source "drivers/media/platform/sti/Kconfig" source "drivers/media/platform/stm32/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 3457866412b1..354796d2bd20 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -14,7 +14,6 @@ obj-y += atmel/ obj-y += cadence/ obj-y += chips-media/ obj-y += davinci/ -obj-y += exynos-gsc/ obj-y += intel/ obj-y += marvell/ obj-y += mediatek/mtk-jpeg/ @@ -34,6 +33,7 @@ obj-y += s3c-camif/ obj-y += s5p-g2d/ obj-y += s5p-jpeg/ obj-y += s5p-mfc/ +obj-y += samsung/exynos-gsc/ obj-y += samsung/exynos4-is/ obj-y += sti/bdisp/ obj-y += sti/c8sectpfe/ diff --git a/drivers/media/platform/exynos-gsc/Kconfig b/drivers/media/platform/exynos-gsc/Kconfig deleted file mode 100644 index f9bdffe915b4..000000000000 --- a/drivers/media/platform/exynos-gsc/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_SAMSUNG_EXYNOS_GSC - tristate "Samsung Exynos G-Scaler driver" - depends on V4L_MEM2MEM_DRIVERS - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_EXYNOS || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - help - This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. diff --git a/drivers/media/platform/exynos-gsc/Makefile b/drivers/media/platform/exynos-gsc/Makefile deleted file mode 100644 index bcefbad17a73..000000000000 --- a/drivers/media/platform/exynos-gsc/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -exynos-gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o - -obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc.o diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c deleted file mode 100644 index e3559b047092..000000000000 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ /dev/null @@ -1,1327 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Samsung EXYNOS5 SoC series G-Scaler driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gsc-core.h" - -static const struct gsc_fmt gsc_formats[] = { - { - .pixelformat = V4L2_PIX_FMT_RGB565X, - .depth = { 16 }, - .color = GSC_RGB, - .num_planes = 1, - .num_comp = 1, - }, { - .pixelformat = V4L2_PIX_FMT_BGR32, - .depth = { 32 }, - .color = GSC_RGB, - .num_planes = 1, - .num_comp = 1, - }, { - .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 = MEDIA_BUS_FMT_YUYV8_2X8, - }, { - .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 = MEDIA_BUS_FMT_UYVY8_2X8, - }, { - .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 = MEDIA_BUS_FMT_VYUY8_2X8, - }, { - .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 = MEDIA_BUS_FMT_YVYU8_2X8, - }, { - .pixelformat = V4L2_PIX_FMT_YUV32, - .depth = { 32 }, - .color = GSC_YUV444, - .yorder = GSC_LSB_Y, - .corder = GSC_CBCR, - .num_planes = 1, - .num_comp = 1, - }, { - .pixelformat = V4L2_PIX_FMT_YUV422P, - .depth = { 16 }, - .color = GSC_YUV422, - .yorder = GSC_LSB_Y, - .corder = GSC_CBCR, - .num_planes = 1, - .num_comp = 3, - }, { - .pixelformat = V4L2_PIX_FMT_NV16, - .depth = { 16 }, - .color = GSC_YUV422, - .yorder = GSC_LSB_Y, - .corder = GSC_CBCR, - .num_planes = 1, - .num_comp = 2, - }, { - .pixelformat = V4L2_PIX_FMT_NV16M, - .depth = { 8, 8 }, - .color = GSC_YUV422, - .yorder = GSC_LSB_Y, - .corder = GSC_CBCR, - .num_planes = 2, - .num_comp = 2, - }, { - .pixelformat = V4L2_PIX_FMT_NV61, - .depth = { 16 }, - .color = GSC_YUV422, - .yorder = GSC_LSB_Y, - .corder = GSC_CRCB, - .num_planes = 1, - .num_comp = 2, - }, { - .pixelformat = V4L2_PIX_FMT_NV61M, - .depth = { 8, 8 }, - .color = GSC_YUV422, - .yorder = GSC_LSB_Y, - .corder = GSC_CRCB, - .num_planes = 2, - .num_comp = 2, - }, { - .pixelformat = V4L2_PIX_FMT_YUV420, - .depth = { 12 }, - .color = GSC_YUV420, - .yorder = GSC_LSB_Y, - .corder = GSC_CBCR, - .num_planes = 1, - .num_comp = 3, - }, { - .pixelformat = V4L2_PIX_FMT_YVU420, - .depth = { 12 }, - .color = GSC_YUV420, - .yorder = GSC_LSB_Y, - .corder = GSC_CRCB, - .num_planes = 1, - .num_comp = 3, - - }, { - .pixelformat = V4L2_PIX_FMT_NV12, - .depth = { 12 }, - .color = GSC_YUV420, - .yorder = GSC_LSB_Y, - .corder = GSC_CBCR, - .num_planes = 1, - .num_comp = 2, - }, { - .pixelformat = V4L2_PIX_FMT_NV21, - .depth = { 12 }, - .color = GSC_YUV420, - .yorder = GSC_LSB_Y, - .corder = GSC_CRCB, - .num_planes = 1, - .num_comp = 2, - }, { - .pixelformat = V4L2_PIX_FMT_NV21M, - .depth = { 8, 4 }, - .color = GSC_YUV420, - .yorder = GSC_LSB_Y, - .corder = GSC_CRCB, - .num_planes = 2, - .num_comp = 2, - }, { - .pixelformat = V4L2_PIX_FMT_NV12M, - .depth = { 8, 4 }, - .color = GSC_YUV420, - .yorder = GSC_LSB_Y, - .corder = GSC_CBCR, - .num_planes = 2, - .num_comp = 2, - }, { - .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, - }, { - .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, - }, { - .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, - } -}; - -const struct gsc_fmt *get_format(int index) -{ - if (index >= ARRAY_SIZE(gsc_formats)) - return NULL; - - return (struct gsc_fmt *)&gsc_formats[index]; -} - -const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index) -{ - const 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) { - pr_err("Exceeded maximum downscaling 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); - pr_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); - pr_info("cropped src height size is recalculated from %d to %d", - s_frame->crop.height + remainder, s_frame->crop.height); - } -} - -int gsc_enum_fmt(struct v4l2_fmtdesc *f) -{ - const struct gsc_fmt *fmt; - - fmt = find_fmt(NULL, NULL, f->index); - if (!fmt) - return -EINVAL; - - f->pixelformat = fmt->pixelformat; - - return 0; -} - -static int get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index, u32 *ret_addr) -{ - if (frm->addr.y == addr) { - *index = 0; - *ret_addr = frm->addr.y; - } else if (frm->addr.cb == addr) { - *index = 1; - *ret_addr = frm->addr.cb; - } else if (frm->addr.cr == addr) { - *index = 2; - *ret_addr = frm->addr.cr; - } else { - pr_err("Plane address is wrong"); - return -EINVAL; - } - return 0; -} - -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); - if (get_plane_info(frm, t_min, &low_plane, &low_addr)) - return; - t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr); - if (get_plane_info(frm, t_max, &high_plane, &high_addr)) - return; - - 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]; - } - } - pr_debug("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; - const struct gsc_fmt *fmt; - u32 max_w, max_h, mod_x, mod_y; - u32 min_w, min_h, tmp_w, tmp_h; - int i; - - pr_debug("user put w: %d, h: %d", pix_mp->width, pix_mp->height); - - fmt = find_fmt(&pix_mp->pixelformat, NULL, 0); - if (!fmt) { - pr_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) { - pr_debug("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; - - 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; - - if (V4L2_TYPE_IS_OUTPUT(f->type)) { - min_w = variant->pix_min->org_w; - min_h = variant->pix_min->org_h; - } else { - min_w = variant->pix_min->target_rot_dis_w; - min_h = variant->pix_min->target_rot_dis_h; - pix_mp->colorspace = ctx->out_colorspace; - } - - pr_debug("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) - pr_debug("Image size has been modified from %dx%d to %dx%d\n", - tmp_w, tmp_h, pix_mp->width, pix_mp->height); - - pix_mp->num_planes = fmt->num_planes; - - if (V4L2_TYPE_IS_OUTPUT(f->type)) - ctx->out_colorspace = pix_mp->colorspace; - - for (i = 0; i < pix_mp->num_planes; ++i) { - struct v4l2_plane_pix_format *plane_fmt = &pix_mp->plane_fmt[i]; - u32 bpl = plane_fmt->bytesperline; - - if (fmt->num_comp == 1 && /* Packed */ - (bpl == 0 || (bpl * 8 / fmt->depth[i]) < pix_mp->width)) - bpl = pix_mp->width * fmt->depth[i] / 8; - - if (fmt->num_comp > 1 && /* Planar */ - (bpl == 0 || bpl < pix_mp->width)) - bpl = pix_mp->width; - - if (i != 0 && fmt->num_comp == 3) - bpl /= 2; - - plane_fmt->bytesperline = bpl; - plane_fmt->sizeimage = max(pix_mp->width * pix_mp->height * - fmt->depth[i] / 8, - plane_fmt->sizeimage); - pr_debug("[%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->num_planes = frame->fmt->num_planes; - pix_mp->colorspace = ctx->out_colorspace; - - 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) { - pr_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_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s) -{ - 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 (s->r.top < 0 || s->r.left < 0) { - pr_err("doesn't support negative values for top & left\n"); - return -EINVAL; - } - pr_debug("user put w: %d, h: %d", s->r.width, s->r.height); - - if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - f = &ctx->d_frame; - else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - f = &ctx->s_frame; - else - return -EINVAL; - - max_w = f->f_width; - max_h = f->f_height; - tmp_w = s->r.width; - tmp_h = s->r.height; - - if (V4L2_TYPE_IS_OUTPUT(s->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 = s->r.height; - tmp_h = s->r.width; - } else { - min_w = variant->pix_min->target_rot_dis_w; - min_h = variant->pix_min->target_rot_dis_h; - } - } - pr_debug("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d", - mod_x, mod_y, min_w, min_h); - pr_debug("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_CAPTURE(s->type) && - (ctx->gsc_ctrls.rotate->val == 90 || - ctx->gsc_ctrls.rotate->val == 270)) - gsc_check_crop_change(tmp_h, tmp_w, - &s->r.width, &s->r.height); - else - gsc_check_crop_change(tmp_w, tmp_h, - &s->r.width, &s->r.height); - - - /* adjust left/top if cropping rectangle is out of bounds */ - /* Need to add code to algin left value with 2's multiple */ - if (s->r.left + tmp_w > max_w) - s->r.left = max_w - tmp_w; - if (s->r.top + tmp_h > max_h) - s->r.top = max_h - tmp_h; - - if ((is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) && - s->r.left & 1) - s->r.left -= 1; - - pr_debug("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", - s->r.left, s->r.top, s->r.width, s->r.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; - - if (out_path == GSC_DMA) - sc_down_max = var->sc_down_max; - else - 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; - struct device *dev = &ctx->gsc_dev->pdev->dev; - 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) { - pr_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; - } - - if (tx <= 0 || ty <= 0) { - dev_err(dev, "Invalid target size: %dx%d", tx, ty); - return -EINVAL; - } - - ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width, - tx, &sc->pre_hratio); - if (ret) { - pr_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) { - pr_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; - - pr_debug("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d", - s_frame->crop.width, s_frame->crop.height, tx, ty); - pr_debug("scaler ratio info : pre_shfactor : %d, pre_h : %d", - sc->pre_shfactor, sc->pre_hratio); - pr_debug("pre_v :%d, main_h : %d, main_v : %d", - sc->pre_vratio, sc->main_hratio, sc->main_vratio); - - return 0; -} - -static int __gsc_s_ctrl(struct gsc_ctx *ctx, struct v4l2_ctrl *ctrl) -{ - struct gsc_dev *gsc = ctx->gsc_dev; - struct gsc_variant *variant = gsc->variant; - unsigned int flags = GSC_DST_FMT | GSC_SRC_FMT; - int ret = 0; - - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - return 0; - - switch (ctrl->id) { - case V4L2_CID_HFLIP: - ctx->hflip = ctrl->val; - break; - - case V4L2_CID_VFLIP: - ctx->vflip = ctrl->val; - break; - - case V4L2_CID_ROTATE: - if ((ctx->state & flags) == flags) { - ret = gsc_check_scaler_ratio(variant, - ctx->s_frame.crop.width, - ctx->s_frame.crop.height, - ctx->d_frame.crop.width, - ctx->d_frame.crop.height, - ctx->gsc_ctrls.rotate->val, - ctx->out_path); - - if (ret) - return -EINVAL; - } - - ctx->rotation = ctrl->val; - break; - - case V4L2_CID_ALPHA_COMPONENT: - ctx->d_frame.alpha = ctrl->val; - break; - } - - ctx->state |= GSC_PARAMS; - return 0; -} - -static int gsc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct gsc_ctx *ctx = ctrl_to_ctx(ctrl); - unsigned long flags; - int ret; - - spin_lock_irqsave(&ctx->gsc_dev->slock, flags); - ret = __gsc_s_ctrl(ctx, ctrl); - spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); - - return ret; -} - -static const struct v4l2_ctrl_ops gsc_ctrl_ops = { - .s_ctrl = gsc_s_ctrl, -}; - -int gsc_ctrls_create(struct gsc_ctx *ctx) -{ - if (ctx->ctrls_rdy) { - pr_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_std(&ctx->ctrl_handler, - &gsc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); - - 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); - pr_err("Failed to create G-Scaler control handlers"); - 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 ((vb == NULL) || (frame == NULL)) - return -EINVAL; - - pix_size = frame->f_width * frame->f_height; - - pr_debug("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: - /* decompose Y into Y/Cb/Cr */ - addr->cb = (dma_addr_t)(addr->y + pix_size); - if (GSC_YUV420 == frame->fmt->color) - addr->cr = (dma_addr_t)(addr->cb - + (pix_size >> 2)); - else /* 422 */ - addr->cr = (dma_addr_t)(addr->cb - + (pix_size >> 1)); - break; - default: - pr_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_VYUY) || - (frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) || - (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) || - (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M)) - swap(addr->cb, addr->cr); - - pr_debug("ADDR: y= %pad cb= %pad cr= %pad 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; - struct gsc_ctx *ctx; - int gsc_irq; - - gsc_irq = gsc_hw_get_irq_status(gsc); - gsc_hw_clear_irq(gsc, gsc_irq); - - if (gsc_irq == GSC_IRQ_OVERRUN) { - pr_err("Local path input over-run interrupt has occurred!\n"); - return IRQ_HANDLED; - } - - spin_lock(&gsc->slock); - - if (test_and_clear_bit(ST_M2M_PEND, &gsc->state)) { - - gsc_hw_enable_control(gsc, false); - - if (test_and_clear_bit(ST_M2M_SUSPENDING, &gsc->state)) { - set_bit(ST_M2M_SUSPENDED, &gsc->state); - wake_up(&gsc->irq_queue); - goto isr_unlock; - } - ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); - - if (!ctx || !ctx->m2m_ctx) - goto isr_unlock; - - spin_unlock(&gsc->slock); - gsc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); - - /* wake_up job_abort, stop_streaming */ - if (ctx->state & GSC_CTX_STOP_REQ) { - ctx->state &= ~GSC_CTX_STOP_REQ; - wake_up(&gsc->irq_queue); - } - return IRQ_HANDLED; - } - -isr_unlock: - spin_unlock(&gsc->slock); - return IRQ_HANDLED; -} - -static 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, -}; - -static struct gsc_pix_max gsc_v_5250_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 = 2016, - .real_rot_en_h = 2016, - .target_rot_dis_w = 4800, - .target_rot_dis_h = 3344, - .target_rot_en_w = 2016, - .target_rot_en_h = 2016, -}; - -static struct gsc_pix_max gsc_v_5420_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 = 2048, - .real_rot_en_h = 2048, - .target_rot_dis_w = 4800, - .target_rot_dis_h = 3344, - .target_rot_en_w = 2016, - .target_rot_en_h = 2016, -}; - -static struct gsc_pix_max gsc_v_5433_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, -}; - -static 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, -}; - -static 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 */ -}; - -static 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 = 32, - .out_buf_cnt = 32, - .sc_up_max = 8, - .sc_down_max = 16, - .poly_sc_down_max = 4, - .pre_sc_down_max = 4, - .local_sc_down = 2, -}; - -static struct gsc_variant gsc_v_5250_variant = { - .pix_max = &gsc_v_5250_max, - .pix_min = &gsc_v_100_min, - .pix_align = &gsc_v_100_align, - .in_buf_cnt = 32, - .out_buf_cnt = 32, - .sc_up_max = 8, - .sc_down_max = 16, - .poly_sc_down_max = 4, - .pre_sc_down_max = 4, - .local_sc_down = 2, -}; - -static struct gsc_variant gsc_v_5420_variant = { - .pix_max = &gsc_v_5420_max, - .pix_min = &gsc_v_100_min, - .pix_align = &gsc_v_100_align, - .in_buf_cnt = 32, - .out_buf_cnt = 32, - .sc_up_max = 8, - .sc_down_max = 16, - .poly_sc_down_max = 4, - .pre_sc_down_max = 4, - .local_sc_down = 2, -}; - -static struct gsc_variant gsc_v_5433_variant = { - .pix_max = &gsc_v_5433_max, - .pix_min = &gsc_v_100_min, - .pix_align = &gsc_v_100_align, - .in_buf_cnt = 32, - .out_buf_cnt = 32, - .sc_up_max = 8, - .sc_down_max = 16, - .poly_sc_down_max = 4, - .pre_sc_down_max = 4, - .local_sc_down = 2, -}; - -static struct gsc_driverdata gsc_v_100_drvdata = { - .variant = { - [0] = &gsc_v_100_variant, - [1] = &gsc_v_100_variant, - [2] = &gsc_v_100_variant, - [3] = &gsc_v_100_variant, - }, - .num_entities = 4, - .clk_names = { "gscl" }, - .num_clocks = 1, -}; - -static struct gsc_driverdata gsc_v_5250_drvdata = { - .variant = { - [0] = &gsc_v_5250_variant, - [1] = &gsc_v_5250_variant, - [2] = &gsc_v_5250_variant, - [3] = &gsc_v_5250_variant, - }, - .num_entities = 4, - .clk_names = { "gscl" }, - .num_clocks = 1, -}; - -static struct gsc_driverdata gsc_v_5420_drvdata = { - .variant = { - [0] = &gsc_v_5420_variant, - [1] = &gsc_v_5420_variant, - }, - .num_entities = 2, - .clk_names = { "gscl" }, - .num_clocks = 1, -}; - -static struct gsc_driverdata gsc_5433_drvdata = { - .variant = { - [0] = &gsc_v_5433_variant, - [1] = &gsc_v_5433_variant, - [2] = &gsc_v_5433_variant, - }, - .num_entities = 3, - .clk_names = { "pclk", "aclk", "aclk_xiu", "aclk_gsclbend" }, - .num_clocks = 4, -}; - -static const struct of_device_id exynos_gsc_match[] = { - { - .compatible = "samsung,exynos5250-gsc", - .data = &gsc_v_5250_drvdata, - }, - { - .compatible = "samsung,exynos5420-gsc", - .data = &gsc_v_5420_drvdata, - }, - { - .compatible = "samsung,exynos5433-gsc", - .data = &gsc_5433_drvdata, - }, - { - .compatible = "samsung,exynos5-gsc", - .data = &gsc_v_100_drvdata, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, exynos_gsc_match); - -static int gsc_probe(struct platform_device *pdev) -{ - struct gsc_dev *gsc; - struct device *dev = &pdev->dev; - const struct gsc_driverdata *drv_data = of_device_get_match_data(dev); - int irq; - int ret; - int i; - - gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL); - if (!gsc) - return -ENOMEM; - - ret = of_alias_get_id(pdev->dev.of_node, "gsc"); - if (ret < 0) - return ret; - - if (drv_data == &gsc_v_100_drvdata) - dev_info(dev, "compatible 'exynos5-gsc' is deprecated\n"); - - gsc->id = ret; - if (gsc->id >= drv_data->num_entities) { - dev_err(dev, "Invalid platform device id: %d\n", gsc->id); - return -EINVAL; - } - - gsc->num_clocks = drv_data->num_clocks; - gsc->variant = drv_data->variant[gsc->id]; - gsc->pdev = pdev; - - init_waitqueue_head(&gsc->irq_queue); - spin_lock_init(&gsc->slock); - mutex_init(&gsc->lock); - - gsc->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(gsc->regs)) - return PTR_ERR(gsc->regs); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - for (i = 0; i < gsc->num_clocks; i++) { - gsc->clock[i] = devm_clk_get(dev, drv_data->clk_names[i]); - if (IS_ERR(gsc->clock[i])) { - dev_err(dev, "failed to get clock: %s\n", - drv_data->clk_names[i]); - return PTR_ERR(gsc->clock[i]); - } - } - - for (i = 0; i < gsc->num_clocks; i++) { - ret = clk_prepare_enable(gsc->clock[i]); - if (ret) { - dev_err(dev, "clock prepare failed for clock: %s\n", - drv_data->clk_names[i]); - while (--i >= 0) - clk_disable_unprepare(gsc->clock[i]); - return ret; - } - } - - ret = devm_request_irq(dev, irq, gsc_irq_handler, - 0, pdev->name, gsc); - if (ret) { - dev_err(dev, "failed to install irq (%d)\n", ret); - goto err_clk; - } - - ret = v4l2_device_register(dev, &gsc->v4l2_dev); - if (ret) - goto err_clk; - - ret = gsc_register_m2m_device(gsc); - if (ret) - goto err_v4l2; - - platform_set_drvdata(pdev, gsc); - - gsc_hw_set_sw_reset(gsc); - gsc_wait_reset(gsc); - - vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); - - dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id); - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - return 0; - -err_v4l2: - v4l2_device_unregister(&gsc->v4l2_dev); -err_clk: - for (i = gsc->num_clocks - 1; i >= 0; i--) - clk_disable_unprepare(gsc->clock[i]); - return ret; -} - -static int gsc_remove(struct platform_device *pdev) -{ - struct gsc_dev *gsc = platform_get_drvdata(pdev); - int i; - - gsc_unregister_m2m_device(gsc); - v4l2_device_unregister(&gsc->v4l2_dev); - - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - - pm_runtime_disable(&pdev->dev); - - if (!pm_runtime_status_suspended(&pdev->dev)) - for (i = 0; i < gsc->num_clocks; i++) - clk_disable_unprepare(gsc->clock[i]); - - pm_runtime_set_suspended(&pdev->dev); - - dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); - return 0; -} - -#ifdef CONFIG_PM -static int gsc_m2m_suspend(struct gsc_dev *gsc) -{ - unsigned long flags; - int timeout; - - spin_lock_irqsave(&gsc->slock, flags); - if (!gsc_m2m_pending(gsc)) { - spin_unlock_irqrestore(&gsc->slock, flags); - return 0; - } - clear_bit(ST_M2M_SUSPENDED, &gsc->state); - set_bit(ST_M2M_SUSPENDING, &gsc->state); - spin_unlock_irqrestore(&gsc->slock, flags); - - timeout = wait_event_timeout(gsc->irq_queue, - test_bit(ST_M2M_SUSPENDED, &gsc->state), - GSC_SHUTDOWN_TIMEOUT); - - clear_bit(ST_M2M_SUSPENDING, &gsc->state); - return timeout == 0 ? -EAGAIN : 0; -} - -static void gsc_m2m_resume(struct gsc_dev *gsc) -{ - struct gsc_ctx *ctx; - unsigned long flags; - - spin_lock_irqsave(&gsc->slock, flags); - /* Clear for full H/W setup in first run after resume */ - ctx = gsc->m2m.ctx; - gsc->m2m.ctx = NULL; - spin_unlock_irqrestore(&gsc->slock, flags); - - if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state)) - gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); -} - -static int gsc_runtime_resume(struct device *dev) -{ - struct gsc_dev *gsc = dev_get_drvdata(dev); - int ret = 0; - int i; - - pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state); - - for (i = 0; i < gsc->num_clocks; i++) { - ret = clk_prepare_enable(gsc->clock[i]); - if (ret) { - while (--i >= 0) - clk_disable_unprepare(gsc->clock[i]); - return ret; - } - } - - gsc_hw_set_sw_reset(gsc); - gsc_wait_reset(gsc); - gsc_m2m_resume(gsc); - - return 0; -} - -static int gsc_runtime_suspend(struct device *dev) -{ - struct gsc_dev *gsc = dev_get_drvdata(dev); - int ret = 0; - int i; - - ret = gsc_m2m_suspend(gsc); - if (ret) - return ret; - - for (i = gsc->num_clocks - 1; i >= 0; i--) - clk_disable_unprepare(gsc->clock[i]); - - pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state); - return ret; -} -#endif - -static const struct dev_pm_ops gsc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) -}; - -static struct platform_driver gsc_driver = { - .probe = gsc_probe, - .remove = gsc_remove, - .driver = { - .name = GSC_MODULE_NAME, - .pm = &gsc_pm_ops, - .of_match_table = exynos_gsc_match, - } -}; - -module_platform_driver(gsc_driver); - -MODULE_AUTHOR("Hyunwong Kim "); -MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h deleted file mode 100644 index e894e85e84a4..000000000000 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ /dev/null @@ -1,521 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * header file for Samsung EXYNOS5 SoC series G-Scaler driver - - */ - -#ifndef GSC_CORE_H_ -#define GSC_CORE_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gsc-regs.h" - -#define CONFIG_VB2_GSC_DMA_CONTIG 1 -#define GSC_MODULE_NAME "exynos-gsc" - -#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) -#define GSC_MAX_DEVS 4 -#define GSC_MAX_CLOCKS 4 -#define GSC_M2M_BUF_NUM 0 -#define GSC_MAX_CTRL_NUM 10 -#define GSC_SC_ALIGN_4 4 -#define GSC_SC_ALIGN_2 2 -#define DEFAULT_CSC_EQ 1 -#define DEFAULT_CSC_RANGE 1 - -#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_STOP_REQ (1 << 6) -#define GSC_CTX_ABORT (1 << 7) - -enum gsc_dev_flags { - /* for m2m node */ - ST_M2M_OPEN, - ST_M2M_RUN, - ST_M2M_PEND, - ST_M2M_SUSPENDED, - ST_M2M_SUSPENDING, -}; - -enum gsc_irq { - GSC_IRQ_DONE, - GSC_IRQ_OVERRUN -}; - -/** - * enum gsc_datapath - the path of data used for G-Scaler - * @GSC_CAMERA: from camera - * @GSC_DMA: from/to DMA - * @GSC_WRITEBACK: from FIMD - */ -enum gsc_datapath { - GSC_CAMERA = 0x1, - GSC_DMA, - 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_active(dev) test_bit(ST_M2M_RUN, &(dev)->state) -#define gsc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(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 - * @pixelformat: the fourcc code for this format, 0 if not applicable - * @color: color encoding - * @yorder: Y/C order - * @corder: Chrominance order control - * @num_planes: number of physically non-contiguous data planes - * @num_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 { - u32 mbus_code; - 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_v4l2_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 - */ -struct gsc_ctrls { - struct v4l2_ctrl *rotate; - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - struct v4l2_ctrl *global_alpha; -}; - -/** - * 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; - u32 main_hratio; - u32 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 - * @colorspace: value indicating v4l2_colorspace - * @alpha: 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; - const struct gsc_fmt *fmt; - u32 colorspace; - 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. - * @num_entities: the number of g-scalers - * @clk_names: clock names - * @num_clocks: the number of clocks in @clk_names - * @num_entities: the number of g-scalers - */ -struct gsc_driverdata { - struct gsc_variant *variant[GSC_MAX_DEVS]; - const char *clk_names[GSC_MAX_CLOCKS]; - int num_clocks; - int num_entities; -}; - -/** - * 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) - * @num_clocks: number of clocks required for G-Scaler operation - * @clock: clocks required for G-Scaler operation - * @regs: the mapped hardware registers - * @irq_queue: interrupt handler waitqueue - * @m2m: memory-to-memory V4L2 device information - * @state: flags used to synchronize m2m and capture mode operation - * @vdev: video device for G-Scaler instance - * @v4l2_dev: v4l2_device for G-Scaler instance - */ -struct gsc_dev { - spinlock_t slock; - struct mutex lock; - struct platform_device *pdev; - struct gsc_variant *variant; - u16 id; - int num_clocks; - struct clk *clock[GSC_MAX_CLOCKS]; - void __iomem *regs; - wait_queue_head_t irq_queue; - struct gsc_m2m_device m2m; - unsigned long state; - struct video_device vdev; - struct v4l2_device v4l2_dev; -}; - -/** - * struct gsc_ctx - the device context data - * @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 - * @rotation: rotation - * @hflip: horizontal flip - * @vflip: vertical flip - * @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 - * @gsc_ctrls: G-Scaler control set - * @ctrls_rdy: true if the control handler is initialized - * @out_colorspace: the colorspace of the OUTPUT queue - */ -struct gsc_ctx { - 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; - int rotation; - unsigned int hflip:1; - unsigned int vflip:1; - 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; - enum v4l2_colorspace out_colorspace; -}; - -void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm); -int gsc_register_m2m_device(struct gsc_dev *gsc); -void gsc_unregister_m2m_device(struct gsc_dev *gsc); -void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state); - -u32 get_plane_size(struct gsc_frame *fr, unsigned int plane); -const struct gsc_fmt *get_format(int index); -const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index); -int gsc_enum_fmt(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_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s); -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_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, - struct gsc_frame *frame, struct gsc_addr *addr); - -static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx) -{ - unsigned long flags; - - spin_lock_irqsave(&ctx->gsc_dev->slock, flags); - ctx->state |= state; - spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); -} - -static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) -{ - unsigned long flags; - - spin_lock_irqsave(&ctx->gsc_dev->slock, flags); - ctx->state &= ~state; - spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); -} - -static inline int is_tiled(const struct gsc_fmt *fmt) -{ - return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; -} - -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 & GSC_IRQ_STATUS_OR_IRQ) - return GSC_IRQ_OVERRUN; - else - return GSC_IRQ_DONE; - -} - -static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq) -{ - u32 cfg = readl(dev->regs + GSC_IRQ); - if (irq == GSC_IRQ_OVERRUN) - cfg |= GSC_IRQ_STATUS_OR_IRQ; - else if (irq == GSC_IRQ_DONE) - cfg |= GSC_IRQ_STATUS_FRM_DONE_IRQ; - writel(cfg, dev->regs + GSC_IRQ); -} - -static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx) -{ - unsigned long flags; - bool ret; - - spin_lock_irqsave(&ctx->gsc_dev->slock, flags); - ret = (ctx->state & mask) == mask; - spin_unlock_irqrestore(&ctx->gsc_dev->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 { - pr_err("Wrong buffer/video queue type (%d)", type); - return ERR_PTR(-EINVAL); - } - - return frame; -} - -void gsc_hw_set_sw_reset(struct gsc_dev *dev); -int gsc_wait_reset(struct gsc_dev *dev); - -void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask); -void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask); -void gsc_hw_set_input_buf_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); - -#endif /* GSC_CORE_H_ */ diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c deleted file mode 100644 index f1cf847d1cc2..000000000000 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ /dev/null @@ -1,794 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Samsung EXYNOS5 SoC series G-Scaler driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "gsc-core.h" - -static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx) -{ - struct gsc_ctx *curr_ctx; - struct gsc_dev *gsc = ctx->gsc_dev; - int ret; - - curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); - if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx)) - return 0; - - gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx); - ret = wait_event_timeout(gsc->irq_queue, - !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx), - GSC_SHUTDOWN_TIMEOUT); - - return ret == 0 ? -ETIMEDOUT : ret; -} - -static void __gsc_m2m_job_abort(struct gsc_ctx *ctx) -{ - int ret; - - ret = gsc_m2m_ctx_stop_req(ctx); - if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) { - gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx); - gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); - } -} - -static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct gsc_ctx *ctx = q->drv_priv; - - return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev); -} - -static void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_vb, *dst_vb; - - while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) { - src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); - } - - while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) { - dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); - } -} - -static void gsc_m2m_stop_streaming(struct vb2_queue *q) -{ - struct gsc_ctx *ctx = q->drv_priv; - - __gsc_m2m_job_abort(ctx); - - __gsc_m2m_cleanup_queue(ctx); - - pm_runtime_put(&ctx->gsc_dev->pdev->dev); -} - -void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) -{ - struct vb2_v4l2_buffer *src_vb, *dst_vb; - - if (!ctx || !ctx->m2m_ctx) - return; - - 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) { - dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; - dst_vb->timecode = src_vb->timecode; - dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_vb->flags |= - src_vb->flags - & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - - v4l2_m2m_buf_done(src_vb, vb_state); - v4l2_m2m_buf_done(dst_vb, vb_state); - - v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev, - ctx->m2m_ctx); - } -} - -static void gsc_m2m_job_abort(void *priv) -{ - __gsc_m2m_job_abort((struct gsc_ctx *)priv); -} - -static int gsc_get_bufs(struct gsc_ctx *ctx) -{ - struct gsc_frame *s_frame, *d_frame; - struct vb2_v4l2_buffer *src_vb, *dst_vb; - int ret; - - s_frame = &ctx->s_frame; - d_frame = &ctx->d_frame; - - src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr); - if (ret) - return ret; - - dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr); - if (ret) - return ret; - - dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; - - return 0; -} - -static void gsc_m2m_device_run(void *priv) -{ - struct gsc_ctx *ctx = priv; - struct gsc_dev *gsc; - unsigned long flags; - int ret; - bool is_set = false; - - if (WARN(!ctx, "null hardware context\n")) - return; - - gsc = ctx->gsc_dev; - spin_lock_irqsave(&gsc->slock, flags); - - set_bit(ST_M2M_PEND, &gsc->state); - - /* Reconfigure hardware if the context has changed. */ - if (gsc->m2m.ctx != ctx) { - pr_debug("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; - if (is_set) { - ctx->state &= ~GSC_CTX_STOP_REQ; - ctx->state |= GSC_CTX_ABORT; - wake_up(&gsc->irq_queue); - goto put_device; - } - - ret = gsc_get_bufs(ctx); - if (ret) { - pr_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)) { - pr_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); - } - - /* update shadow registers */ - gsc_hw_set_sfr_update(ctx); - - ctx->state &= ~GSC_PARAMS; - gsc_hw_enable_control(gsc, true); - - spin_unlock_irqrestore(&gsc->slock, flags); - return; - -put_device: - ctx->state &= ~GSC_PARAMS; - spin_unlock_irqrestore(&gsc->slock, flags); -} - -static int gsc_m2m_queue_setup(struct vb2_queue *vq, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - 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] = frame->payload[i]; - 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_CAPTURE(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 vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); - - if (ctx->m2m_ctx) - v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); -} - -static const 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 = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .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; - - strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - dev_name(&gsc->pdev->dev)); - return 0; -} - -static int gsc_m2m_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return gsc_enum_fmt(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)) { - pr_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); - frame->colorspace = pix->colorspace; - 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); - - pr_debug("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; - 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; - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int gsc_m2m_expbuf(struct file *file, void *fh, - struct v4l2_exportbuffer *eb) -{ - struct gsc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); -} - -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) && - (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) - 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: - case V4L2_SEL_TGT_CROP: - 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 gsc_variant *variant = ctx->gsc_dev->variant; - struct v4l2_selection sel = *s; - int ret; - - if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && - (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) - return -EINVAL; - - ret = gsc_try_selection(ctx, &sel); - if (ret) - return ret; - - if (s->flags & V4L2_SEL_FLAG_LE && - !is_rectangle_enclosed(&sel.r, &s->r)) - return -ERANGE; - - if (s->flags & V4L2_SEL_FLAG_GE && - !is_rectangle_enclosed(&s->r, &sel.r)) - return -ERANGE; - - s->r = sel.r; - - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE: - frame = &ctx->s_frame; - break; - - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - 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, sel.r.width, - sel.r.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, sel.r.width, - sel.r.height, ctx->gsc_ctrls.rotate->val, - ctx->out_path); - } - - if (ret) { - pr_err("Out of scaler range"); - return -EINVAL; - } - } - - frame->crop = sel.r; - - 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 = gsc_m2m_enum_fmt, - .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt, - .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_expbuf = gsc_m2m_expbuf, - .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 | VB2_DMABUF; - 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); - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->gsc_dev->lock; - src_vq->dev = &ctx->gsc_dev->pdev->dev; - - 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 | VB2_DMABUF; - 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); - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->gsc_dev->lock; - dst_vq->dev = &ctx->gsc_dev->pdev->dev; - - 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; - - pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state); - - if (mutex_lock_interruptible(&gsc->lock)) - return -ERESTARTSYS; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - ret = -ENOMEM; - goto unlock; - } - - 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; - - ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - pr_err("Failed to initialize m2m context"); - ret = PTR_ERR(ctx->m2m_ctx); - goto error_ctrls; - } - - if (gsc->m2m.refcnt++ == 0) - set_bit(ST_M2M_OPEN, &gsc->state); - - pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx); - - mutex_unlock(&gsc->lock); - return 0; - -error_ctrls: - gsc_ctrls_delete(ctx); - v4l2_fh_del(&ctx->fh); -error_fh: - v4l2_fh_exit(&ctx->fh); - kfree(ctx); -unlock: - mutex_unlock(&gsc->lock); - 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; - - pr_debug("pid: %d, state: 0x%lx, refcnt= %d", - task_pid_nr(current), gsc->state, gsc->m2m.refcnt); - - mutex_lock(&gsc->lock); - - 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); - - mutex_unlock(&gsc->lock); - return 0; -} - -static __poll_t gsc_m2m_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct gsc_ctx *ctx = fh_to_ctx(file->private_data); - struct gsc_dev *gsc = ctx->gsc_dev; - __poll_t ret; - - if (mutex_lock_interruptible(&gsc->lock)) - return EPOLLERR; - - ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - mutex_unlock(&gsc->lock); - - return ret; -} - -static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct gsc_ctx *ctx = fh_to_ctx(file->private_data); - struct gsc_dev *gsc = ctx->gsc_dev; - int ret; - - if (mutex_lock_interruptible(&gsc->lock)) - return -ERESTARTSYS; - - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&gsc->lock); - - return ret; -} - -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 const 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 platform_device *pdev; - int ret; - - if (!gsc) - return -ENODEV; - - pdev = gsc->pdev; - - gsc->vdev.fops = &gsc_m2m_fops; - gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops; - gsc->vdev.release = video_device_release_empty; - gsc->vdev.lock = &gsc->lock; - gsc->vdev.vfl_dir = VFL_DIR_M2M; - gsc->vdev.v4l2_dev = &gsc->v4l2_dev; - gsc->vdev.device_caps = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_M2M_MPLANE; - snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", - GSC_MODULE_NAME, gsc->id); - - video_set_drvdata(&gsc->vdev, gsc); - - gsc->m2m.vfd = &gsc->vdev; - 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"); - return PTR_ERR(gsc->m2m.m2m_dev); - } - - ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1); - if (ret) { - dev_err(&pdev->dev, - "%s(): failed to register video device\n", __func__); - goto err_m2m_release; - } - - pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num); - return 0; - -err_m2m_release: - v4l2_m2m_release(gsc->m2m.m2m_dev); - - return ret; -} - -void gsc_unregister_m2m_device(struct gsc_dev *gsc) -{ - if (gsc) { - v4l2_m2m_release(gsc->m2m.m2m_dev); - video_unregister_device(&gsc->vdev); - } -} diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c deleted file mode 100644 index 995a1f0f875d..000000000000 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ /dev/null @@ -1,426 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Samsung EXYNOS5 SoC series G-Scaler driver - */ - -#include -#include - -#include "gsc-core.h" - -void gsc_hw_set_sw_reset(struct gsc_dev *dev) -{ - writel(GSC_SW_RESET_SRESET, dev->regs + GSC_SW_RESET); -} - -int gsc_wait_reset(struct gsc_dev *dev) -{ - unsigned long end = jiffies + msecs_to_jiffies(50); - u32 cfg; - - while (time_before(jiffies, end)) { - cfg = readl(dev->regs + GSC_SW_RESET); - if (!cfg) - return 0; - usleep_range(10, 20); - } - - return -EBUSY; -} - -void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask) -{ - u32 cfg; - - cfg = readl(dev->regs + GSC_IRQ); - if (mask) - cfg |= GSC_IRQ_FRMDONE_MASK; - else - cfg &= ~GSC_IRQ_FRMDONE_MASK; - writel(cfg, dev->regs + GSC_IRQ); -} - -void gsc_hw_set_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_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); -} - -void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, - int index) -{ - pr_debug("src_buf[%d]: %pad, cb: %pad, cr: %pad", 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) -{ - pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad", - 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 (frame->colorspace == V4L2_COLORSPACE_REC709) - cfg |= GSC_IN_RGB_HD_WIDE; - else - cfg |= GSC_IN_RGB_SD_WIDE; - - 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 (frame->colorspace == V4L2_COLORSPACE_REC709) - cfg |= GSC_OUT_RGB_HD_WIDE; - else - cfg |= GSC_OUT_RGB_SD_WIDE; - - 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)) { - pr_debug("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/platform/exynos-gsc/gsc-regs.h b/drivers/media/platform/exynos-gsc/gsc-regs.h deleted file mode 100644 index d4f7ead6b322..000000000000 --- a/drivers/media/platform/exynos-gsc/gsc-regs.h +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Register definition file for Samsung G-Scaler driver - */ - -#ifndef REGS_GSC_H_ -#define REGS_GSC_H_ - -/* G-Scaler enable */ -#define GSC_ENABLE 0x00 -#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_FRM_DONE_IRQ (1 << 16) -#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_NARROW (3 << 14) -#define GSC_IN_RGB_HD_WIDE (2 << 14) -#define GSC_IN_RGB_SD_NARROW (1 << 14) -#define GSC_IN_RGB_SD_WIDE (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_MODE (1 << 3) -#define GSC_IN_LOCAL_SEL_MASK (3 << 1) -#define GSC_IN_PATH_MASK (1 << 0) -#define GSC_IN_PATH_MEMORY (0 << 0) - -/* G-Scaler source image size */ -#define GSC_SRCIMG_SIZE 0x14 -#define GSC_SRCIMG_HEIGHT(x) ((x) << 16) -#define GSC_SRCIMG_WIDTH(x) ((x) << 0) - -/* G-Scaler source image offset */ -#define GSC_SRCIMG_OFFSET 0x18 -#define GSC_SRCIMG_OFFSET_Y(x) ((x) << 16) -#define GSC_SRCIMG_OFFSET_X(x) ((x) << 0) - -/* G-Scaler cropped source image size */ -#define GSC_CROPPED_SIZE 0x1c -#define GSC_CROPPED_HEIGHT(x) ((x) << 16) -#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_WIDE (3 << 10) -#define GSC_OUT_RGB_HD_NARROW (2 << 10) -#define GSC_OUT_RGB_SD_WIDE (1 << 10) -#define GSC_OUT_RGB_SD_NARROW (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_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(x) ((x) << 16) -#define GSC_SCALED_WIDTH(x) ((x) << 0) - -/* G-Scaler pre scale ratio */ -#define GSC_PRE_SCALE_RATIO 0x28 -#define GSC_PRESC_SHFACTOR(x) ((x) << 28) -#define GSC_PRESC_V_RATIO(x) ((x) << 16) -#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_VALUE(x) ((x) << 0) - -/* G-Scaler main scale vertical ratio */ -#define GSC_MAIN_V_RATIO 0x30 -#define GSC_MAIN_V_RATIO_VALUE(x) ((x) << 0) - -/* G-Scaler destination image size */ -#define GSC_DSTIMG_SIZE 0x40 -#define GSC_DSTIMG_HEIGHT(x) ((x) << 16) -#define GSC_DSTIMG_WIDTH(x) ((x) << 0) - -/* G-Scaler destination image offset */ -#define GSC_DSTIMG_OFFSET 0x44 -#define GSC_DSTIMG_OFFSET_Y(x) ((x) << 16) -#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 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) - -#endif /* REGS_GSC_H_ */ diff --git a/drivers/media/platform/samsung/exynos-gsc/Kconfig b/drivers/media/platform/samsung/exynos-gsc/Kconfig new file mode 100644 index 000000000000..f9bdffe915b4 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SAMSUNG_EXYNOS_GSC + tristate "Samsung Exynos G-Scaler driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_EXYNOS || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. diff --git a/drivers/media/platform/samsung/exynos-gsc/Makefile b/drivers/media/platform/samsung/exynos-gsc/Makefile new file mode 100644 index 000000000000..bcefbad17a73 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +exynos-gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o + +obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc.o diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c new file mode 100644 index 000000000000..e3559b047092 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c @@ -0,0 +1,1327 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung EXYNOS5 SoC series G-Scaler driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gsc-core.h" + +static const struct gsc_fmt gsc_formats[] = { + { + .pixelformat = V4L2_PIX_FMT_RGB565X, + .depth = { 16 }, + .color = GSC_RGB, + .num_planes = 1, + .num_comp = 1, + }, { + .pixelformat = V4L2_PIX_FMT_BGR32, + .depth = { 32 }, + .color = GSC_RGB, + .num_planes = 1, + .num_comp = 1, + }, { + .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 = MEDIA_BUS_FMT_YUYV8_2X8, + }, { + .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 = MEDIA_BUS_FMT_UYVY8_2X8, + }, { + .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 = MEDIA_BUS_FMT_VYUY8_2X8, + }, { + .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 = MEDIA_BUS_FMT_YVYU8_2X8, + }, { + .pixelformat = V4L2_PIX_FMT_YUV32, + .depth = { 32 }, + .color = GSC_YUV444, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 1, + .num_comp = 1, + }, { + .pixelformat = V4L2_PIX_FMT_YUV422P, + .depth = { 16 }, + .color = GSC_YUV422, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 1, + .num_comp = 3, + }, { + .pixelformat = V4L2_PIX_FMT_NV16, + .depth = { 16 }, + .color = GSC_YUV422, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 1, + .num_comp = 2, + }, { + .pixelformat = V4L2_PIX_FMT_NV16M, + .depth = { 8, 8 }, + .color = GSC_YUV422, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 2, + .num_comp = 2, + }, { + .pixelformat = V4L2_PIX_FMT_NV61, + .depth = { 16 }, + .color = GSC_YUV422, + .yorder = GSC_LSB_Y, + .corder = GSC_CRCB, + .num_planes = 1, + .num_comp = 2, + }, { + .pixelformat = V4L2_PIX_FMT_NV61M, + .depth = { 8, 8 }, + .color = GSC_YUV422, + .yorder = GSC_LSB_Y, + .corder = GSC_CRCB, + .num_planes = 2, + .num_comp = 2, + }, { + .pixelformat = V4L2_PIX_FMT_YUV420, + .depth = { 12 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 1, + .num_comp = 3, + }, { + .pixelformat = V4L2_PIX_FMT_YVU420, + .depth = { 12 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CRCB, + .num_planes = 1, + .num_comp = 3, + + }, { + .pixelformat = V4L2_PIX_FMT_NV12, + .depth = { 12 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 1, + .num_comp = 2, + }, { + .pixelformat = V4L2_PIX_FMT_NV21, + .depth = { 12 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CRCB, + .num_planes = 1, + .num_comp = 2, + }, { + .pixelformat = V4L2_PIX_FMT_NV21M, + .depth = { 8, 4 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CRCB, + .num_planes = 2, + .num_comp = 2, + }, { + .pixelformat = V4L2_PIX_FMT_NV12M, + .depth = { 8, 4 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 2, + .num_comp = 2, + }, { + .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, + }, { + .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, + }, { + .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, + } +}; + +const struct gsc_fmt *get_format(int index) +{ + if (index >= ARRAY_SIZE(gsc_formats)) + return NULL; + + return (struct gsc_fmt *)&gsc_formats[index]; +} + +const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index) +{ + const 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) { + pr_err("Exceeded maximum downscaling 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); + pr_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); + pr_info("cropped src height size is recalculated from %d to %d", + s_frame->crop.height + remainder, s_frame->crop.height); + } +} + +int gsc_enum_fmt(struct v4l2_fmtdesc *f) +{ + const struct gsc_fmt *fmt; + + fmt = find_fmt(NULL, NULL, f->index); + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->pixelformat; + + return 0; +} + +static int get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index, u32 *ret_addr) +{ + if (frm->addr.y == addr) { + *index = 0; + *ret_addr = frm->addr.y; + } else if (frm->addr.cb == addr) { + *index = 1; + *ret_addr = frm->addr.cb; + } else if (frm->addr.cr == addr) { + *index = 2; + *ret_addr = frm->addr.cr; + } else { + pr_err("Plane address is wrong"); + return -EINVAL; + } + return 0; +} + +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); + if (get_plane_info(frm, t_min, &low_plane, &low_addr)) + return; + t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr); + if (get_plane_info(frm, t_max, &high_plane, &high_addr)) + return; + + 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]; + } + } + pr_debug("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; + const struct gsc_fmt *fmt; + u32 max_w, max_h, mod_x, mod_y; + u32 min_w, min_h, tmp_w, tmp_h; + int i; + + pr_debug("user put w: %d, h: %d", pix_mp->width, pix_mp->height); + + fmt = find_fmt(&pix_mp->pixelformat, NULL, 0); + if (!fmt) { + pr_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) { + pr_debug("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; + + 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; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + min_w = variant->pix_min->org_w; + min_h = variant->pix_min->org_h; + } else { + min_w = variant->pix_min->target_rot_dis_w; + min_h = variant->pix_min->target_rot_dis_h; + pix_mp->colorspace = ctx->out_colorspace; + } + + pr_debug("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) + pr_debug("Image size has been modified from %dx%d to %dx%d\n", + tmp_w, tmp_h, pix_mp->width, pix_mp->height); + + pix_mp->num_planes = fmt->num_planes; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) + ctx->out_colorspace = pix_mp->colorspace; + + for (i = 0; i < pix_mp->num_planes; ++i) { + struct v4l2_plane_pix_format *plane_fmt = &pix_mp->plane_fmt[i]; + u32 bpl = plane_fmt->bytesperline; + + if (fmt->num_comp == 1 && /* Packed */ + (bpl == 0 || (bpl * 8 / fmt->depth[i]) < pix_mp->width)) + bpl = pix_mp->width * fmt->depth[i] / 8; + + if (fmt->num_comp > 1 && /* Planar */ + (bpl == 0 || bpl < pix_mp->width)) + bpl = pix_mp->width; + + if (i != 0 && fmt->num_comp == 3) + bpl /= 2; + + plane_fmt->bytesperline = bpl; + plane_fmt->sizeimage = max(pix_mp->width * pix_mp->height * + fmt->depth[i] / 8, + plane_fmt->sizeimage); + pr_debug("[%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->num_planes = frame->fmt->num_planes; + pix_mp->colorspace = ctx->out_colorspace; + + 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) { + pr_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_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s) +{ + 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 (s->r.top < 0 || s->r.left < 0) { + pr_err("doesn't support negative values for top & left\n"); + return -EINVAL; + } + pr_debug("user put w: %d, h: %d", s->r.width, s->r.height); + + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + f = &ctx->d_frame; + else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + f = &ctx->s_frame; + else + return -EINVAL; + + max_w = f->f_width; + max_h = f->f_height; + tmp_w = s->r.width; + tmp_h = s->r.height; + + if (V4L2_TYPE_IS_OUTPUT(s->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 = s->r.height; + tmp_h = s->r.width; + } else { + min_w = variant->pix_min->target_rot_dis_w; + min_h = variant->pix_min->target_rot_dis_h; + } + } + pr_debug("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d", + mod_x, mod_y, min_w, min_h); + pr_debug("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_CAPTURE(s->type) && + (ctx->gsc_ctrls.rotate->val == 90 || + ctx->gsc_ctrls.rotate->val == 270)) + gsc_check_crop_change(tmp_h, tmp_w, + &s->r.width, &s->r.height); + else + gsc_check_crop_change(tmp_w, tmp_h, + &s->r.width, &s->r.height); + + + /* adjust left/top if cropping rectangle is out of bounds */ + /* Need to add code to algin left value with 2's multiple */ + if (s->r.left + tmp_w > max_w) + s->r.left = max_w - tmp_w; + if (s->r.top + tmp_h > max_h) + s->r.top = max_h - tmp_h; + + if ((is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) && + s->r.left & 1) + s->r.left -= 1; + + pr_debug("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", + s->r.left, s->r.top, s->r.width, s->r.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; + + if (out_path == GSC_DMA) + sc_down_max = var->sc_down_max; + else + 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; + struct device *dev = &ctx->gsc_dev->pdev->dev; + 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) { + pr_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; + } + + if (tx <= 0 || ty <= 0) { + dev_err(dev, "Invalid target size: %dx%d", tx, ty); + return -EINVAL; + } + + ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width, + tx, &sc->pre_hratio); + if (ret) { + pr_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) { + pr_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; + + pr_debug("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d", + s_frame->crop.width, s_frame->crop.height, tx, ty); + pr_debug("scaler ratio info : pre_shfactor : %d, pre_h : %d", + sc->pre_shfactor, sc->pre_hratio); + pr_debug("pre_v :%d, main_h : %d, main_v : %d", + sc->pre_vratio, sc->main_hratio, sc->main_vratio); + + return 0; +} + +static int __gsc_s_ctrl(struct gsc_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + struct gsc_dev *gsc = ctx->gsc_dev; + struct gsc_variant *variant = gsc->variant; + unsigned int flags = GSC_DST_FMT | GSC_SRC_FMT; + int ret = 0; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ctx->hflip = ctrl->val; + break; + + case V4L2_CID_VFLIP: + ctx->vflip = ctrl->val; + break; + + case V4L2_CID_ROTATE: + if ((ctx->state & flags) == flags) { + ret = gsc_check_scaler_ratio(variant, + ctx->s_frame.crop.width, + ctx->s_frame.crop.height, + ctx->d_frame.crop.width, + ctx->d_frame.crop.height, + ctx->gsc_ctrls.rotate->val, + ctx->out_path); + + if (ret) + return -EINVAL; + } + + ctx->rotation = ctrl->val; + break; + + case V4L2_CID_ALPHA_COMPONENT: + ctx->d_frame.alpha = ctrl->val; + break; + } + + ctx->state |= GSC_PARAMS; + return 0; +} + +static int gsc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gsc_ctx *ctx = ctrl_to_ctx(ctrl); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ctx->gsc_dev->slock, flags); + ret = __gsc_s_ctrl(ctx, ctrl); + spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); + + return ret; +} + +static const struct v4l2_ctrl_ops gsc_ctrl_ops = { + .s_ctrl = gsc_s_ctrl, +}; + +int gsc_ctrls_create(struct gsc_ctx *ctx) +{ + if (ctx->ctrls_rdy) { + pr_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_std(&ctx->ctrl_handler, + &gsc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); + + 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); + pr_err("Failed to create G-Scaler control handlers"); + 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 ((vb == NULL) || (frame == NULL)) + return -EINVAL; + + pix_size = frame->f_width * frame->f_height; + + pr_debug("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: + /* decompose Y into Y/Cb/Cr */ + addr->cb = (dma_addr_t)(addr->y + pix_size); + if (GSC_YUV420 == frame->fmt->color) + addr->cr = (dma_addr_t)(addr->cb + + (pix_size >> 2)); + else /* 422 */ + addr->cr = (dma_addr_t)(addr->cb + + (pix_size >> 1)); + break; + default: + pr_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_VYUY) || + (frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) || + (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) || + (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M)) + swap(addr->cb, addr->cr); + + pr_debug("ADDR: y= %pad cb= %pad cr= %pad 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; + struct gsc_ctx *ctx; + int gsc_irq; + + gsc_irq = gsc_hw_get_irq_status(gsc); + gsc_hw_clear_irq(gsc, gsc_irq); + + if (gsc_irq == GSC_IRQ_OVERRUN) { + pr_err("Local path input over-run interrupt has occurred!\n"); + return IRQ_HANDLED; + } + + spin_lock(&gsc->slock); + + if (test_and_clear_bit(ST_M2M_PEND, &gsc->state)) { + + gsc_hw_enable_control(gsc, false); + + if (test_and_clear_bit(ST_M2M_SUSPENDING, &gsc->state)) { + set_bit(ST_M2M_SUSPENDED, &gsc->state); + wake_up(&gsc->irq_queue); + goto isr_unlock; + } + ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); + + if (!ctx || !ctx->m2m_ctx) + goto isr_unlock; + + spin_unlock(&gsc->slock); + gsc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); + + /* wake_up job_abort, stop_streaming */ + if (ctx->state & GSC_CTX_STOP_REQ) { + ctx->state &= ~GSC_CTX_STOP_REQ; + wake_up(&gsc->irq_queue); + } + return IRQ_HANDLED; + } + +isr_unlock: + spin_unlock(&gsc->slock); + return IRQ_HANDLED; +} + +static 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, +}; + +static struct gsc_pix_max gsc_v_5250_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 = 2016, + .real_rot_en_h = 2016, + .target_rot_dis_w = 4800, + .target_rot_dis_h = 3344, + .target_rot_en_w = 2016, + .target_rot_en_h = 2016, +}; + +static struct gsc_pix_max gsc_v_5420_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 = 2048, + .real_rot_en_h = 2048, + .target_rot_dis_w = 4800, + .target_rot_dis_h = 3344, + .target_rot_en_w = 2016, + .target_rot_en_h = 2016, +}; + +static struct gsc_pix_max gsc_v_5433_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, +}; + +static 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, +}; + +static 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 */ +}; + +static 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 = 32, + .out_buf_cnt = 32, + .sc_up_max = 8, + .sc_down_max = 16, + .poly_sc_down_max = 4, + .pre_sc_down_max = 4, + .local_sc_down = 2, +}; + +static struct gsc_variant gsc_v_5250_variant = { + .pix_max = &gsc_v_5250_max, + .pix_min = &gsc_v_100_min, + .pix_align = &gsc_v_100_align, + .in_buf_cnt = 32, + .out_buf_cnt = 32, + .sc_up_max = 8, + .sc_down_max = 16, + .poly_sc_down_max = 4, + .pre_sc_down_max = 4, + .local_sc_down = 2, +}; + +static struct gsc_variant gsc_v_5420_variant = { + .pix_max = &gsc_v_5420_max, + .pix_min = &gsc_v_100_min, + .pix_align = &gsc_v_100_align, + .in_buf_cnt = 32, + .out_buf_cnt = 32, + .sc_up_max = 8, + .sc_down_max = 16, + .poly_sc_down_max = 4, + .pre_sc_down_max = 4, + .local_sc_down = 2, +}; + +static struct gsc_variant gsc_v_5433_variant = { + .pix_max = &gsc_v_5433_max, + .pix_min = &gsc_v_100_min, + .pix_align = &gsc_v_100_align, + .in_buf_cnt = 32, + .out_buf_cnt = 32, + .sc_up_max = 8, + .sc_down_max = 16, + .poly_sc_down_max = 4, + .pre_sc_down_max = 4, + .local_sc_down = 2, +}; + +static struct gsc_driverdata gsc_v_100_drvdata = { + .variant = { + [0] = &gsc_v_100_variant, + [1] = &gsc_v_100_variant, + [2] = &gsc_v_100_variant, + [3] = &gsc_v_100_variant, + }, + .num_entities = 4, + .clk_names = { "gscl" }, + .num_clocks = 1, +}; + +static struct gsc_driverdata gsc_v_5250_drvdata = { + .variant = { + [0] = &gsc_v_5250_variant, + [1] = &gsc_v_5250_variant, + [2] = &gsc_v_5250_variant, + [3] = &gsc_v_5250_variant, + }, + .num_entities = 4, + .clk_names = { "gscl" }, + .num_clocks = 1, +}; + +static struct gsc_driverdata gsc_v_5420_drvdata = { + .variant = { + [0] = &gsc_v_5420_variant, + [1] = &gsc_v_5420_variant, + }, + .num_entities = 2, + .clk_names = { "gscl" }, + .num_clocks = 1, +}; + +static struct gsc_driverdata gsc_5433_drvdata = { + .variant = { + [0] = &gsc_v_5433_variant, + [1] = &gsc_v_5433_variant, + [2] = &gsc_v_5433_variant, + }, + .num_entities = 3, + .clk_names = { "pclk", "aclk", "aclk_xiu", "aclk_gsclbend" }, + .num_clocks = 4, +}; + +static const struct of_device_id exynos_gsc_match[] = { + { + .compatible = "samsung,exynos5250-gsc", + .data = &gsc_v_5250_drvdata, + }, + { + .compatible = "samsung,exynos5420-gsc", + .data = &gsc_v_5420_drvdata, + }, + { + .compatible = "samsung,exynos5433-gsc", + .data = &gsc_5433_drvdata, + }, + { + .compatible = "samsung,exynos5-gsc", + .data = &gsc_v_100_drvdata, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_gsc_match); + +static int gsc_probe(struct platform_device *pdev) +{ + struct gsc_dev *gsc; + struct device *dev = &pdev->dev; + const struct gsc_driverdata *drv_data = of_device_get_match_data(dev); + int irq; + int ret; + int i; + + gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL); + if (!gsc) + return -ENOMEM; + + ret = of_alias_get_id(pdev->dev.of_node, "gsc"); + if (ret < 0) + return ret; + + if (drv_data == &gsc_v_100_drvdata) + dev_info(dev, "compatible 'exynos5-gsc' is deprecated\n"); + + gsc->id = ret; + if (gsc->id >= drv_data->num_entities) { + dev_err(dev, "Invalid platform device id: %d\n", gsc->id); + return -EINVAL; + } + + gsc->num_clocks = drv_data->num_clocks; + gsc->variant = drv_data->variant[gsc->id]; + gsc->pdev = pdev; + + init_waitqueue_head(&gsc->irq_queue); + spin_lock_init(&gsc->slock); + mutex_init(&gsc->lock); + + gsc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(gsc->regs)) + return PTR_ERR(gsc->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + for (i = 0; i < gsc->num_clocks; i++) { + gsc->clock[i] = devm_clk_get(dev, drv_data->clk_names[i]); + if (IS_ERR(gsc->clock[i])) { + dev_err(dev, "failed to get clock: %s\n", + drv_data->clk_names[i]); + return PTR_ERR(gsc->clock[i]); + } + } + + for (i = 0; i < gsc->num_clocks; i++) { + ret = clk_prepare_enable(gsc->clock[i]); + if (ret) { + dev_err(dev, "clock prepare failed for clock: %s\n", + drv_data->clk_names[i]); + while (--i >= 0) + clk_disable_unprepare(gsc->clock[i]); + return ret; + } + } + + ret = devm_request_irq(dev, irq, gsc_irq_handler, + 0, pdev->name, gsc); + if (ret) { + dev_err(dev, "failed to install irq (%d)\n", ret); + goto err_clk; + } + + ret = v4l2_device_register(dev, &gsc->v4l2_dev); + if (ret) + goto err_clk; + + ret = gsc_register_m2m_device(gsc); + if (ret) + goto err_v4l2; + + platform_set_drvdata(pdev, gsc); + + gsc_hw_set_sw_reset(gsc); + gsc_wait_reset(gsc); + + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); + + dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; + +err_v4l2: + v4l2_device_unregister(&gsc->v4l2_dev); +err_clk: + for (i = gsc->num_clocks - 1; i >= 0; i--) + clk_disable_unprepare(gsc->clock[i]); + return ret; +} + +static int gsc_remove(struct platform_device *pdev) +{ + struct gsc_dev *gsc = platform_get_drvdata(pdev); + int i; + + gsc_unregister_m2m_device(gsc); + v4l2_device_unregister(&gsc->v4l2_dev); + + vb2_dma_contig_clear_max_seg_size(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + if (!pm_runtime_status_suspended(&pdev->dev)) + for (i = 0; i < gsc->num_clocks; i++) + clk_disable_unprepare(gsc->clock[i]); + + pm_runtime_set_suspended(&pdev->dev); + + dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); + return 0; +} + +#ifdef CONFIG_PM +static int gsc_m2m_suspend(struct gsc_dev *gsc) +{ + unsigned long flags; + int timeout; + + spin_lock_irqsave(&gsc->slock, flags); + if (!gsc_m2m_pending(gsc)) { + spin_unlock_irqrestore(&gsc->slock, flags); + return 0; + } + clear_bit(ST_M2M_SUSPENDED, &gsc->state); + set_bit(ST_M2M_SUSPENDING, &gsc->state); + spin_unlock_irqrestore(&gsc->slock, flags); + + timeout = wait_event_timeout(gsc->irq_queue, + test_bit(ST_M2M_SUSPENDED, &gsc->state), + GSC_SHUTDOWN_TIMEOUT); + + clear_bit(ST_M2M_SUSPENDING, &gsc->state); + return timeout == 0 ? -EAGAIN : 0; +} + +static void gsc_m2m_resume(struct gsc_dev *gsc) +{ + struct gsc_ctx *ctx; + unsigned long flags; + + spin_lock_irqsave(&gsc->slock, flags); + /* Clear for full H/W setup in first run after resume */ + ctx = gsc->m2m.ctx; + gsc->m2m.ctx = NULL; + spin_unlock_irqrestore(&gsc->slock, flags); + + if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state)) + gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); +} + +static int gsc_runtime_resume(struct device *dev) +{ + struct gsc_dev *gsc = dev_get_drvdata(dev); + int ret = 0; + int i; + + pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state); + + for (i = 0; i < gsc->num_clocks; i++) { + ret = clk_prepare_enable(gsc->clock[i]); + if (ret) { + while (--i >= 0) + clk_disable_unprepare(gsc->clock[i]); + return ret; + } + } + + gsc_hw_set_sw_reset(gsc); + gsc_wait_reset(gsc); + gsc_m2m_resume(gsc); + + return 0; +} + +static int gsc_runtime_suspend(struct device *dev) +{ + struct gsc_dev *gsc = dev_get_drvdata(dev); + int ret = 0; + int i; + + ret = gsc_m2m_suspend(gsc); + if (ret) + return ret; + + for (i = gsc->num_clocks - 1; i >= 0; i--) + clk_disable_unprepare(gsc->clock[i]); + + pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state); + return ret; +} +#endif + +static const struct dev_pm_ops gsc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) +}; + +static struct platform_driver gsc_driver = { + .probe = gsc_probe, + .remove = gsc_remove, + .driver = { + .name = GSC_MODULE_NAME, + .pm = &gsc_pm_ops, + .of_match_table = exynos_gsc_match, + } +}; + +module_platform_driver(gsc_driver); + +MODULE_AUTHOR("Hyunwong Kim "); +MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.h b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h new file mode 100644 index 000000000000..e894e85e84a4 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h @@ -0,0 +1,521 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * header file for Samsung EXYNOS5 SoC series G-Scaler driver + + */ + +#ifndef GSC_CORE_H_ +#define GSC_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gsc-regs.h" + +#define CONFIG_VB2_GSC_DMA_CONTIG 1 +#define GSC_MODULE_NAME "exynos-gsc" + +#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) +#define GSC_MAX_DEVS 4 +#define GSC_MAX_CLOCKS 4 +#define GSC_M2M_BUF_NUM 0 +#define GSC_MAX_CTRL_NUM 10 +#define GSC_SC_ALIGN_4 4 +#define GSC_SC_ALIGN_2 2 +#define DEFAULT_CSC_EQ 1 +#define DEFAULT_CSC_RANGE 1 + +#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_STOP_REQ (1 << 6) +#define GSC_CTX_ABORT (1 << 7) + +enum gsc_dev_flags { + /* for m2m node */ + ST_M2M_OPEN, + ST_M2M_RUN, + ST_M2M_PEND, + ST_M2M_SUSPENDED, + ST_M2M_SUSPENDING, +}; + +enum gsc_irq { + GSC_IRQ_DONE, + GSC_IRQ_OVERRUN +}; + +/** + * enum gsc_datapath - the path of data used for G-Scaler + * @GSC_CAMERA: from camera + * @GSC_DMA: from/to DMA + * @GSC_WRITEBACK: from FIMD + */ +enum gsc_datapath { + GSC_CAMERA = 0x1, + GSC_DMA, + 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_active(dev) test_bit(ST_M2M_RUN, &(dev)->state) +#define gsc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(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 + * @pixelformat: the fourcc code for this format, 0 if not applicable + * @color: color encoding + * @yorder: Y/C order + * @corder: Chrominance order control + * @num_planes: number of physically non-contiguous data planes + * @num_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 { + u32 mbus_code; + 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_v4l2_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 + */ +struct gsc_ctrls { + struct v4l2_ctrl *rotate; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *global_alpha; +}; + +/** + * 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; + u32 main_hratio; + u32 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 + * @colorspace: value indicating v4l2_colorspace + * @alpha: 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; + const struct gsc_fmt *fmt; + u32 colorspace; + 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. + * @num_entities: the number of g-scalers + * @clk_names: clock names + * @num_clocks: the number of clocks in @clk_names + * @num_entities: the number of g-scalers + */ +struct gsc_driverdata { + struct gsc_variant *variant[GSC_MAX_DEVS]; + const char *clk_names[GSC_MAX_CLOCKS]; + int num_clocks; + int num_entities; +}; + +/** + * 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) + * @num_clocks: number of clocks required for G-Scaler operation + * @clock: clocks required for G-Scaler operation + * @regs: the mapped hardware registers + * @irq_queue: interrupt handler waitqueue + * @m2m: memory-to-memory V4L2 device information + * @state: flags used to synchronize m2m and capture mode operation + * @vdev: video device for G-Scaler instance + * @v4l2_dev: v4l2_device for G-Scaler instance + */ +struct gsc_dev { + spinlock_t slock; + struct mutex lock; + struct platform_device *pdev; + struct gsc_variant *variant; + u16 id; + int num_clocks; + struct clk *clock[GSC_MAX_CLOCKS]; + void __iomem *regs; + wait_queue_head_t irq_queue; + struct gsc_m2m_device m2m; + unsigned long state; + struct video_device vdev; + struct v4l2_device v4l2_dev; +}; + +/** + * struct gsc_ctx - the device context data + * @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 + * @rotation: rotation + * @hflip: horizontal flip + * @vflip: vertical flip + * @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 + * @gsc_ctrls: G-Scaler control set + * @ctrls_rdy: true if the control handler is initialized + * @out_colorspace: the colorspace of the OUTPUT queue + */ +struct gsc_ctx { + 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; + int rotation; + unsigned int hflip:1; + unsigned int vflip:1; + 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; + enum v4l2_colorspace out_colorspace; +}; + +void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm); +int gsc_register_m2m_device(struct gsc_dev *gsc); +void gsc_unregister_m2m_device(struct gsc_dev *gsc); +void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state); + +u32 get_plane_size(struct gsc_frame *fr, unsigned int plane); +const struct gsc_fmt *get_format(int index); +const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index); +int gsc_enum_fmt(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_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s); +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_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, + struct gsc_frame *frame, struct gsc_addr *addr); + +static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->gsc_dev->slock, flags); + ctx->state |= state; + spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); +} + +static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->gsc_dev->slock, flags); + ctx->state &= ~state; + spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); +} + +static inline int is_tiled(const struct gsc_fmt *fmt) +{ + return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; +} + +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 & GSC_IRQ_STATUS_OR_IRQ) + return GSC_IRQ_OVERRUN; + else + return GSC_IRQ_DONE; + +} + +static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq) +{ + u32 cfg = readl(dev->regs + GSC_IRQ); + if (irq == GSC_IRQ_OVERRUN) + cfg |= GSC_IRQ_STATUS_OR_IRQ; + else if (irq == GSC_IRQ_DONE) + cfg |= GSC_IRQ_STATUS_FRM_DONE_IRQ; + writel(cfg, dev->regs + GSC_IRQ); +} + +static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&ctx->gsc_dev->slock, flags); + ret = (ctx->state & mask) == mask; + spin_unlock_irqrestore(&ctx->gsc_dev->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 { + pr_err("Wrong buffer/video queue type (%d)", type); + return ERR_PTR(-EINVAL); + } + + return frame; +} + +void gsc_hw_set_sw_reset(struct gsc_dev *dev); +int gsc_wait_reset(struct gsc_dev *dev); + +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask); +void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask); +void gsc_hw_set_input_buf_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); + +#endif /* GSC_CORE_H_ */ diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c new file mode 100644 index 000000000000..f1cf847d1cc2 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c @@ -0,0 +1,794 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung EXYNOS5 SoC series G-Scaler driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gsc-core.h" + +static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx) +{ + struct gsc_ctx *curr_ctx; + struct gsc_dev *gsc = ctx->gsc_dev; + int ret; + + curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); + if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx)) + return 0; + + gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx); + ret = wait_event_timeout(gsc->irq_queue, + !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx), + GSC_SHUTDOWN_TIMEOUT); + + return ret == 0 ? -ETIMEDOUT : ret; +} + +static void __gsc_m2m_job_abort(struct gsc_ctx *ctx) +{ + int ret; + + ret = gsc_m2m_ctx_stop_req(ctx); + if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) { + gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx); + gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + } +} + +static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct gsc_ctx *ctx = q->drv_priv; + + return pm_runtime_resume_and_get(&ctx->gsc_dev->pdev->dev); +} + +static void __gsc_m2m_cleanup_queue(struct gsc_ctx *ctx) +{ + struct vb2_v4l2_buffer *src_vb, *dst_vb; + + while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) { + src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); + } + + while (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) { + dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); + } +} + +static void gsc_m2m_stop_streaming(struct vb2_queue *q) +{ + struct gsc_ctx *ctx = q->drv_priv; + + __gsc_m2m_job_abort(ctx); + + __gsc_m2m_cleanup_queue(ctx); + + pm_runtime_put(&ctx->gsc_dev->pdev->dev); +} + +void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) +{ + struct vb2_v4l2_buffer *src_vb, *dst_vb; + + if (!ctx || !ctx->m2m_ctx) + return; + + 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) { + dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; + dst_vb->timecode = src_vb->timecode; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->flags |= + src_vb->flags + & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + + v4l2_m2m_buf_done(src_vb, vb_state); + v4l2_m2m_buf_done(dst_vb, vb_state); + + v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev, + ctx->m2m_ctx); + } +} + +static void gsc_m2m_job_abort(void *priv) +{ + __gsc_m2m_job_abort((struct gsc_ctx *)priv); +} + +static int gsc_get_bufs(struct gsc_ctx *ctx) +{ + struct gsc_frame *s_frame, *d_frame; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + int ret; + + s_frame = &ctx->s_frame; + d_frame = &ctx->d_frame; + + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, &src_vb->vb2_buf, s_frame, &s_frame->addr); + if (ret) + return ret; + + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, &dst_vb->vb2_buf, d_frame, &d_frame->addr); + if (ret) + return ret; + + dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; + + return 0; +} + +static void gsc_m2m_device_run(void *priv) +{ + struct gsc_ctx *ctx = priv; + struct gsc_dev *gsc; + unsigned long flags; + int ret; + bool is_set = false; + + if (WARN(!ctx, "null hardware context\n")) + return; + + gsc = ctx->gsc_dev; + spin_lock_irqsave(&gsc->slock, flags); + + set_bit(ST_M2M_PEND, &gsc->state); + + /* Reconfigure hardware if the context has changed. */ + if (gsc->m2m.ctx != ctx) { + pr_debug("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; + if (is_set) { + ctx->state &= ~GSC_CTX_STOP_REQ; + ctx->state |= GSC_CTX_ABORT; + wake_up(&gsc->irq_queue); + goto put_device; + } + + ret = gsc_get_bufs(ctx); + if (ret) { + pr_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)) { + pr_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); + } + + /* update shadow registers */ + gsc_hw_set_sfr_update(ctx); + + ctx->state &= ~GSC_PARAMS; + gsc_hw_enable_control(gsc, true); + + spin_unlock_irqrestore(&gsc->slock, flags); + return; + +put_device: + ctx->state &= ~GSC_PARAMS; + spin_unlock_irqrestore(&gsc->slock, flags); +} + +static int gsc_m2m_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + 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] = frame->payload[i]; + 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_CAPTURE(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 vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); + + if (ctx->m2m_ctx) + v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); +} + +static const 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 = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .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; + + strscpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&gsc->pdev->dev)); + return 0; +} + +static int gsc_m2m_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return gsc_enum_fmt(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)) { + pr_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); + frame->colorspace = pix->colorspace; + 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); + + pr_debug("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; + 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; + + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int gsc_m2m_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *eb) +{ + struct gsc_ctx *ctx = fh_to_ctx(fh); + return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); +} + +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) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + 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: + case V4L2_SEL_TGT_CROP: + 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 gsc_variant *variant = ctx->gsc_dev->variant; + struct v4l2_selection sel = *s; + int ret; + + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + ret = gsc_try_selection(ctx, &sel); + if (ret) + return ret; + + if (s->flags & V4L2_SEL_FLAG_LE && + !is_rectangle_enclosed(&sel.r, &s->r)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && + !is_rectangle_enclosed(&s->r, &sel.r)) + return -ERANGE; + + s->r = sel.r; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE: + frame = &ctx->s_frame; + break; + + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + 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, sel.r.width, + sel.r.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, sel.r.width, + sel.r.height, ctx->gsc_ctrls.rotate->val, + ctx->out_path); + } + + if (ret) { + pr_err("Out of scaler range"); + return -EINVAL; + } + } + + frame->crop = sel.r; + + 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 = gsc_m2m_enum_fmt, + .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt, + .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_expbuf = gsc_m2m_expbuf, + .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 | VB2_DMABUF; + 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); + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->gsc_dev->lock; + src_vq->dev = &ctx->gsc_dev->pdev->dev; + + 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 | VB2_DMABUF; + 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); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->gsc_dev->lock; + dst_vq->dev = &ctx->gsc_dev->pdev->dev; + + 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; + + pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state); + + if (mutex_lock_interruptible(&gsc->lock)) + return -ERESTARTSYS; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto unlock; + } + + 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; + + ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + pr_err("Failed to initialize m2m context"); + ret = PTR_ERR(ctx->m2m_ctx); + goto error_ctrls; + } + + if (gsc->m2m.refcnt++ == 0) + set_bit(ST_M2M_OPEN, &gsc->state); + + pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx); + + mutex_unlock(&gsc->lock); + return 0; + +error_ctrls: + gsc_ctrls_delete(ctx); + v4l2_fh_del(&ctx->fh); +error_fh: + v4l2_fh_exit(&ctx->fh); + kfree(ctx); +unlock: + mutex_unlock(&gsc->lock); + 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; + + pr_debug("pid: %d, state: 0x%lx, refcnt= %d", + task_pid_nr(current), gsc->state, gsc->m2m.refcnt); + + mutex_lock(&gsc->lock); + + 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); + + mutex_unlock(&gsc->lock); + return 0; +} + +static __poll_t gsc_m2m_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct gsc_ctx *ctx = fh_to_ctx(file->private_data); + struct gsc_dev *gsc = ctx->gsc_dev; + __poll_t ret; + + if (mutex_lock_interruptible(&gsc->lock)) + return EPOLLERR; + + ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); + mutex_unlock(&gsc->lock); + + return ret; +} + +static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct gsc_ctx *ctx = fh_to_ctx(file->private_data); + struct gsc_dev *gsc = ctx->gsc_dev; + int ret; + + if (mutex_lock_interruptible(&gsc->lock)) + return -ERESTARTSYS; + + ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); + mutex_unlock(&gsc->lock); + + return ret; +} + +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 const 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 platform_device *pdev; + int ret; + + if (!gsc) + return -ENODEV; + + pdev = gsc->pdev; + + gsc->vdev.fops = &gsc_m2m_fops; + gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops; + gsc->vdev.release = video_device_release_empty; + gsc->vdev.lock = &gsc->lock; + gsc->vdev.vfl_dir = VFL_DIR_M2M; + gsc->vdev.v4l2_dev = &gsc->v4l2_dev; + gsc->vdev.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; + snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", + GSC_MODULE_NAME, gsc->id); + + video_set_drvdata(&gsc->vdev, gsc); + + gsc->m2m.vfd = &gsc->vdev; + 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"); + return PTR_ERR(gsc->m2m.m2m_dev); + } + + ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(&pdev->dev, + "%s(): failed to register video device\n", __func__); + goto err_m2m_release; + } + + pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num); + return 0; + +err_m2m_release: + v4l2_m2m_release(gsc->m2m.m2m_dev); + + return ret; +} + +void gsc_unregister_m2m_device(struct gsc_dev *gsc) +{ + if (gsc) { + v4l2_m2m_release(gsc->m2m.m2m_dev); + video_unregister_device(&gsc->vdev); + } +} diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-regs.c b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.c new file mode 100644 index 000000000000..995a1f0f875d --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung EXYNOS5 SoC series G-Scaler driver + */ + +#include +#include + +#include "gsc-core.h" + +void gsc_hw_set_sw_reset(struct gsc_dev *dev) +{ + writel(GSC_SW_RESET_SRESET, dev->regs + GSC_SW_RESET); +} + +int gsc_wait_reset(struct gsc_dev *dev) +{ + unsigned long end = jiffies + msecs_to_jiffies(50); + u32 cfg; + + while (time_before(jiffies, end)) { + cfg = readl(dev->regs + GSC_SW_RESET); + if (!cfg) + return 0; + usleep_range(10, 20); + } + + return -EBUSY; +} + +void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask) +{ + u32 cfg; + + cfg = readl(dev->regs + GSC_IRQ); + if (mask) + cfg |= GSC_IRQ_FRMDONE_MASK; + else + cfg &= ~GSC_IRQ_FRMDONE_MASK; + writel(cfg, dev->regs + GSC_IRQ); +} + +void gsc_hw_set_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_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); +} + +void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, + int index) +{ + pr_debug("src_buf[%d]: %pad, cb: %pad, cr: %pad", 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) +{ + pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad", + 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 (frame->colorspace == V4L2_COLORSPACE_REC709) + cfg |= GSC_IN_RGB_HD_WIDE; + else + cfg |= GSC_IN_RGB_SD_WIDE; + + 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 (frame->colorspace == V4L2_COLORSPACE_REC709) + cfg |= GSC_OUT_RGB_HD_WIDE; + else + cfg |= GSC_OUT_RGB_SD_WIDE; + + 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)) { + pr_debug("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/platform/samsung/exynos-gsc/gsc-regs.h b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.h new file mode 100644 index 000000000000..d4f7ead6b322 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Register definition file for Samsung G-Scaler driver + */ + +#ifndef REGS_GSC_H_ +#define REGS_GSC_H_ + +/* G-Scaler enable */ +#define GSC_ENABLE 0x00 +#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_FRM_DONE_IRQ (1 << 16) +#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_NARROW (3 << 14) +#define GSC_IN_RGB_HD_WIDE (2 << 14) +#define GSC_IN_RGB_SD_NARROW (1 << 14) +#define GSC_IN_RGB_SD_WIDE (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_MODE (1 << 3) +#define GSC_IN_LOCAL_SEL_MASK (3 << 1) +#define GSC_IN_PATH_MASK (1 << 0) +#define GSC_IN_PATH_MEMORY (0 << 0) + +/* G-Scaler source image size */ +#define GSC_SRCIMG_SIZE 0x14 +#define GSC_SRCIMG_HEIGHT(x) ((x) << 16) +#define GSC_SRCIMG_WIDTH(x) ((x) << 0) + +/* G-Scaler source image offset */ +#define GSC_SRCIMG_OFFSET 0x18 +#define GSC_SRCIMG_OFFSET_Y(x) ((x) << 16) +#define GSC_SRCIMG_OFFSET_X(x) ((x) << 0) + +/* G-Scaler cropped source image size */ +#define GSC_CROPPED_SIZE 0x1c +#define GSC_CROPPED_HEIGHT(x) ((x) << 16) +#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_WIDE (3 << 10) +#define GSC_OUT_RGB_HD_NARROW (2 << 10) +#define GSC_OUT_RGB_SD_WIDE (1 << 10) +#define GSC_OUT_RGB_SD_NARROW (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_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(x) ((x) << 16) +#define GSC_SCALED_WIDTH(x) ((x) << 0) + +/* G-Scaler pre scale ratio */ +#define GSC_PRE_SCALE_RATIO 0x28 +#define GSC_PRESC_SHFACTOR(x) ((x) << 28) +#define GSC_PRESC_V_RATIO(x) ((x) << 16) +#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_VALUE(x) ((x) << 0) + +/* G-Scaler main scale vertical ratio */ +#define GSC_MAIN_V_RATIO 0x30 +#define GSC_MAIN_V_RATIO_VALUE(x) ((x) << 0) + +/* G-Scaler destination image size */ +#define GSC_DSTIMG_SIZE 0x40 +#define GSC_DSTIMG_HEIGHT(x) ((x) << 16) +#define GSC_DSTIMG_WIDTH(x) ((x) << 0) + +/* G-Scaler destination image offset */ +#define GSC_DSTIMG_OFFSET 0x44 +#define GSC_DSTIMG_OFFSET_Y(x) ((x) << 16) +#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 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) + +#endif /* REGS_GSC_H_ */ -- cgit v1.2.3