From 45561c9bc245811c822c5c4078faa6ef030c648e Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Thu, 23 Jul 2015 17:07:25 +0900 Subject: [PATCH] [COMMON] media: smfc: move v4l2 ioctl handlers in separate file The implementation of v4l2 ioctl handlers occupies the half of smfc.c The handlers are moved to another file for easier maintenance. Change-Id: Ic65243aef0e173177f819be9b723bdea36c81ba9 Signed-off-by: Cho KyongHo --- drivers/media/platform/exynos/smfc/Makefile | 2 +- .../platform/exynos/smfc/smfc-v4l2-ioctls.c | 797 ++++++++++++++++++ drivers/media/platform/exynos/smfc/smfc.c | 780 ----------------- drivers/media/platform/exynos/smfc/smfc.h | 9 + 4 files changed, 807 insertions(+), 781 deletions(-) create mode 100644 drivers/media/platform/exynos/smfc/smfc-v4l2-ioctls.c diff --git a/drivers/media/platform/exynos/smfc/Makefile b/drivers/media/platform/exynos/smfc/Makefile index 3b802490fe8e..6977f942f4a3 100644 --- a/drivers/media/platform/exynos/smfc/Makefile +++ b/drivers/media/platform/exynos/smfc/Makefile @@ -3,4 +3,4 @@ # http://www.samsung.com # -obj-$(CONFIG_VIDEO_EXYNOS_SMFC) += smfc.o smfc-regs.o +obj-$(CONFIG_VIDEO_EXYNOS_SMFC) += smfc.o smfc-v4l2-ioctls.o smfc-regs.o diff --git a/drivers/media/platform/exynos/smfc/smfc-v4l2-ioctls.c b/drivers/media/platform/exynos/smfc/smfc-v4l2-ioctls.c new file mode 100644 index 000000000000..dfef00825a04 --- /dev/null +++ b/drivers/media/platform/exynos/smfc/smfc-v4l2-ioctls.c @@ -0,0 +1,797 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * The main source file of Samsung Exynos SMFC Driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include "smfc.h" + +/* SMFC SPECIFIC DEVICE CAPABILITIES */ +/* set if H/W supports for decompression */ +#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION 0x0100 +/* set if H/W can compress dual images */ +#define V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION 0x0200 +/* set if H/W supports for Hardware Flow Control */ +#define V4L2_CAP_EXYNOS_JPEG_HWFC 0x0400 +/* set if H/W supports for HWFC on internal buffers */ +#define V4L2_CAP_EXYNOS_JPEG_HWFC_EMBEDDED 0x0800 +/* set if H/W has a register to configure stream buffer size */ +#define V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE 0x1000 +/* set if H/W does not have 128-bit alignment constraint for stream base */ +#define V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN 0x2000 +/* set if H/W does not have 128-bit alignment constraint for image base */ +#define V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN 0x4000 + +/* SMFC SPECIFIC CONTROLS */ +#define V4L2_CID_JPEG_SEC_COMP_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 20) +#define V4L2_CID_JPEG_HWFC_ENABLE (V4L2_CID_JPEG_CLASS_BASE + 25) + +#define SMFC_FMT_MAIN_SIZE(val) ((val) & 0xFFFF) +#define SMFC_FMT_SEC_SIZE(val) (((val) >> 16) & 0xFFFF) + +const struct smfc_image_format smfc_image_formats[] = { + { + /* JPEG should be the first format */ + .description = "Baseline JPEG(Sequential DCT)", + .v4l2_pixfmt = V4L2_PIX_FMT_JPEG, + .regcfg = 0, /* Chagneable accroding to chroma factor */ + .bpp_buf = {0}, /* undeterministic */ + .bpp_pix = {0}, /* undeterministic */ + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 1, /* dummy chroma subsampling factor */ + .chroma_vfactor = 1, /* These will not affect to H/W config. */ + }, { + /* YUYV is the default format */ + .description = "YUV4:2:2 1-Plane 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YUYV, + .regcfg = SMFC_IMGFMT_YUYV, + .bpp_buf = {16}, + .bpp_pix = {16}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 1, + }, { + .description = "YUV4:2:2 1-Plane 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YVYU, + .regcfg = SMFC_IMGFMT_YVYU, + .bpp_buf = {16}, + .bpp_pix = {16}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 1, + }, { + .description = "YUV4:2:2 2-Plane 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_NV16, + .regcfg = SMFC_IMGFMT_NV16, + .bpp_buf = {16}, + .bpp_pix = {8, 8, 0}, + .num_planes = 2, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 1, + }, { + .description = "YUV4:2:2 2-Plane 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_NV61, + .regcfg = SMFC_IMGFMT_NV61, + .bpp_buf = {16}, + .bpp_pix = {8, 8, 0}, + .num_planes = 2, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 1, + }, { + .description = "YUV4:2:2 3-Plane 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YUV422P, + .regcfg = SMFC_IMGFMT_YUV422, + .bpp_buf = {16}, + .bpp_pix = {8, 2, 2}, + .num_planes = 3, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 1, + }, { + .description = "YUV4:2:0 2-Plane(Cb/Cr) 12BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_NV12, + .regcfg = SMFC_IMGFMT_NV12, + .bpp_buf = {12}, + .bpp_pix = {8, 4, 0}, + .num_planes = 2, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + }, { + .description = "YUV4:2:0 2-Plane(Cr/Cb) 12BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_NV21, + .regcfg = SMFC_IMGFMT_NV21, + .bpp_buf = {12}, + .bpp_pix = {8, 4, 0}, + .num_planes = 2, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + }, { + .description = "YUV4:2:0 3-Plane 12BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YUV420, + .regcfg = SMFC_IMGFMT_YUV420P, + .bpp_buf = {12}, + .bpp_pix = {8, 2, 2}, + .num_planes = 3, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + }, { + .description = "YVU4:2:0 3-Plane 12BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YVU420, + .regcfg = SMFC_IMGFMT_YVU420P, + .bpp_buf = {12}, + .bpp_pix = {8, 2, 2}, + .num_planes = 3, + .num_buffers = 1, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + }, { + .description = "YUV4:4:4 3-Plane 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YUV444, + .regcfg = SMFC_IMGFMT_YUV444, + .bpp_buf = {24}, + .bpp_pix = {8, 8, 8}, + .num_planes = 3, + .num_buffers = 1, + .chroma_hfactor = 1, + .chroma_vfactor = 1, + }, { + .description = "RGB565 LE 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_RGB565, + .regcfg = SMFC_IMGFMT_RGB565, + .bpp_buf = {16}, + .bpp_pix = {16}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 1, + .chroma_vfactor = 1, + }, { + .description = "RGB565 BE 16BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_RGB565X, + .regcfg = SMFC_IMGFMT_BGR565, + .bpp_buf = {16}, + .bpp_pix = {16}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 1, + .chroma_vfactor = 1, + }, { + .description = "RGB888 LE 24BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_RGB24, + .regcfg = SMFC_IMGFMT_RGB24, + .bpp_buf = {24}, + .bpp_pix = {24}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 1, + .chroma_vfactor = 1, + }, { + .description = "RGB888 BE 24BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_BGR24, + .regcfg = SMFC_IMGFMT_BGR24, + .bpp_buf = {24}, + .bpp_pix = {24}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 1, + .chroma_vfactor = 1, + }, { + .description = "ABGR8888 LE 32BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_RGB32, + .regcfg = SMFC_IMGFMT_RGBA32, + .bpp_buf = {32}, + .bpp_pix = {32}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 1, + .chroma_vfactor = 1, + }, { + .description = "ARGB8888 BE 32BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_BGR32, + .regcfg = SMFC_IMGFMT_BGRA32, + .bpp_buf = {32}, + .bpp_pix = {32}, + .num_planes = 1, + .num_buffers = 1, + .chroma_hfactor = 1, + .chroma_vfactor = 1, + }, + /* multi-planar formats must be at the last becuse of VIDOC_ENUM_FMT */ + { + .description = "YUV4:2:0 2-MPlane(Cb/Cr) 8+4BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_NV12M, + .regcfg = SMFC_IMGFMT_NV12, + .bpp_buf = {8, 4}, + .bpp_pix = {8, 4}, + .num_planes = 2, + .num_buffers = 2, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + }, { + .description = "YUV4:2:0 2-MPlane(Cr/Cb) 8+4BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_NV21M, + .regcfg = SMFC_IMGFMT_NV21, + .bpp_buf = {8, 4}, + .bpp_pix = {8, 4}, + .num_planes = 2, + .num_buffers = 2, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + }, { + .description = "YUV4:2:0 3-MPlane 8+2+2BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YUV420M, + .regcfg = SMFC_IMGFMT_YUV420P, + .bpp_buf = {8, 2, 2}, + .bpp_pix = {8, 2, 2}, + .num_planes = 3, + .num_buffers = 3, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + }, { + .description = "YVU4:2:0 3-MPlane 8+2+2BPP", + .v4l2_pixfmt = V4L2_PIX_FMT_YVU420M, + .regcfg = SMFC_IMGFMT_YVU420P, + .bpp_buf = {8, 2, 2}, + .bpp_pix = {8, 2, 2}, + .num_planes = 3, + .num_buffers = 3, + .chroma_hfactor = 2, + .chroma_vfactor = 2, + } +}; + +static const struct smfc_image_format *smfc_find_format( + struct smfc_dev *smfc, __u32 v4l2_pixfmt) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(smfc_image_formats); i++) + if (smfc_image_formats[i].v4l2_pixfmt == v4l2_pixfmt) + return &smfc_image_formats[i]; + + dev_warn(smfc->dev, "Pixel format '%08X' not found, YUYV is forced.\n", + v4l2_pixfmt); + + return V4L2_TYPE_IS_OUTPUT(v4l2_pixfmt) ? SMFC_DEFAULT_OUTPUT_FORMAT + : SMFC_DEFAULT_CAPTURE_FORMAT; +} + +static const char *buf_type_name(__u32 type) +{ + return V4L2_TYPE_IS_OUTPUT(type) ? "capture" : "output"; +} + +static int smfc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct smfc_ctx *ctx = container_of(ctrl->handler, + struct smfc_ctx, v4l2_ctrlhdlr); + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->quality_factor = (unsigned char)ctrl->val; + break; + case V4L2_CID_JPEG_SEC_COMP_QUALITY: + ctx->thumb_quality_factor = (unsigned char)ctrl->val; + break; + case V4L2_CID_JPEG_HWFC_ENABLE: + ctx->enable_hwfc = (unsigned char)ctrl->val; + break; + case V4L2_CID_JPEG_RESTART_INTERVAL: + ctx->restart_interval = (unsigned char)ctrl->val; + break; + case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + switch (ctrl->val) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_444: + ctx->chroma_hfactor = 1; + ctx->chroma_vfactor = 1; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + ctx->chroma_hfactor = 2; + ctx->chroma_vfactor = 2; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_411: + ctx->chroma_hfactor = 4; + ctx->chroma_vfactor = 1; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: + ctx->chroma_hfactor = 0; + ctx->chroma_vfactor = 0; + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_410: + dev_info(ctx->smfc->dev, + "Compression to YUV410 is not supported\n"); + /* pass through to 422 */ + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + default: + ctx->chroma_hfactor = 2; + ctx->chroma_vfactor = 1; + break; + } + break; + default: + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops smfc_ctrl_ops = { + .s_ctrl = smfc_s_ctrl, +}; + +int smfc_init_controls(struct smfc_dev *smfc, + struct v4l2_ctrl_handler *hdlr) +{ + const char *msg; + struct v4l2_ctrl_config ctrlcfg; + + v4l2_ctrl_handler_init(hdlr, 1); + + if (!v4l2_ctrl_new_std(hdlr, &smfc_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 1, 100, 1, 96)) { + msg = "quality factor"; + goto err; + } + + memset(&ctrlcfg, 0, sizeof(ctrlcfg)); + ctrlcfg.ops = &smfc_ctrl_ops; + ctrlcfg.id = V4L2_CID_JPEG_SEC_COMP_QUALITY; + ctrlcfg.name = "Quality factor of secondray image"; + ctrlcfg.type = V4L2_CTRL_TYPE_INTEGER; + ctrlcfg.min = 1; + ctrlcfg.max = 100; + ctrlcfg.step = 1; + ctrlcfg.def = 50; + if (!v4l2_ctrl_new_custom(hdlr, &ctrlcfg, NULL)) { + msg = "secondary quality factor"; + goto err; + } + + memset(&ctrlcfg, 0, sizeof(ctrlcfg)); + ctrlcfg.ops = &smfc_ctrl_ops; + ctrlcfg.id = V4L2_CID_JPEG_HWFC_ENABLE; + ctrlcfg.name = "HWFC configuration"; + ctrlcfg.type = V4L2_CTRL_TYPE_BOOLEAN; + ctrlcfg.min = 0; + ctrlcfg.max = 1; + ctrlcfg.step = 1; + ctrlcfg.def = 0; + if (!v4l2_ctrl_new_custom(hdlr, &ctrlcfg, NULL)) { + msg = "secondary quality factor"; + goto err; + } + + if (!v4l2_ctrl_new_std(hdlr, &smfc_ctrl_ops, + V4L2_CID_JPEG_RESTART_INTERVAL, + 0, 64, 1, 0)) { + msg = "restart interval"; + goto err; + } + + if (!v4l2_ctrl_new_std_menu(hdlr, &smfc_ctrl_ops, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, 0, + V4L2_JPEG_CHROMA_SUBSAMPLING_422)) { + msg = "chroma subsampling"; + goto err; + } + + return 0; +err: + dev_err(smfc->dev, "Failed to install %s control (%d)\n", + msg, hdlr->error); + return hdlr->error; +} + +static int smfc_v4l2_querycap(struct file *filp, void *fh, + struct v4l2_capability *cap) +{ + struct smfc_dev *smfc = v4l2_fh_to_smfc_ctx(fh)->smfc; + + strncpy(cap->driver, MODULE_NAME, sizeof(cap->driver)); + strncpy(cap->bus_info, dev_name(smfc->dev), sizeof(cap->bus_info)); + scnprintf(cap->card, sizeof(cap->card), "Still MFC %02x.%02x.%04x", + (smfc->hwver >> 24) & 0xFF, (smfc->hwver >> 16) & 0xFF, + smfc->hwver & 0xFFFF); + + cap->driver[sizeof(cap->driver) - 1] = '\0'; + cap->card[sizeof(cap->card) - 1] = '\0'; + cap->bus_info[sizeof(cap->bus_info) - 1] = '\0'; + + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE + | V4L2_CAP_VIDEO_M2M; + cap->capabilities |= V4L2_CAP_DEVICE_CAPS; + + cap->device_caps = V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION; + cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_HWFC; + cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE; + cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN; + cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN; + + return 0; +} + +static int smfc_v4l2_enum_fmt_mplane(struct file *filp, void *fh, + struct v4l2_fmtdesc *f) +{ + const struct smfc_image_format *fmt; + + if (f->index >= ARRAY_SIZE(smfc_image_formats)) + return -EINVAL; + + fmt = &smfc_image_formats[f->index]; + strncpy(f->description, fmt->description, sizeof(f->description)); + f->description[sizeof(f->description) - 1] = '\0'; + f->pixelformat = fmt->v4l2_pixfmt; + if (fmt->bpp_buf[0] == 0) + f->flags = V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static int smfc_v4l2_enum_fmt(struct file *filp, void *fh, + struct v4l2_fmtdesc *f) +{ + int ret = smfc_v4l2_enum_fmt_mplane(filp, fh, f); + + if (ret < 0) + return ret; + + return smfc_image_formats[f->index].num_buffers > 1 ? -EINVAL : 0; +} + +static int smfc_v4l2_g_fmt_mplane(struct file *filp, void *fh, + struct v4l2_format *f) +{ + struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); + + f->fmt.pix_mp.width = ctx->width; + f->fmt.pix_mp.height = ctx->height; + + if (!smfc_is_compressed_type(ctx, f->type)) { + int i; + /* uncompressed image */ + f->fmt.pix_mp.pixelformat = ctx->img_fmt->v4l2_pixfmt; + f->fmt.pix_mp.num_planes = ctx->img_fmt->num_buffers; + for (i = 0; i < ctx->img_fmt->num_buffers; i++) { + f->fmt.pix_mp.plane_fmt[i].bytesperline = + (f->fmt.pix_mp.width * + ctx->img_fmt->bpp_buf[i]) / 8; + f->fmt.pix_mp.plane_fmt[i].sizeimage = + f->fmt.pix_mp.plane_fmt[i].bytesperline; + f->fmt.pix_mp.plane_fmt[i].sizeimage *= + f->fmt.pix_mp.height; + } + } else { + f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix_mp.num_planes = 1; + f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; + f->fmt.pix_mp.plane_fmt[0].sizeimage = 0; + } + + f->fmt.pix_mp.field = 0; + f->fmt.pix_mp.colorspace = 0; + + return 0; +} + +static int smfc_v4l2_g_fmt(struct file *filp, void *fh, struct v4l2_format *f) +{ + struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); + + f->fmt.pix.width = ctx->width; + f->fmt.pix.height = ctx->height; + + if (!smfc_is_compressed_type(ctx, f->type)) { + if (ctx->img_fmt->num_buffers > 1) { + dev_err(ctx->smfc->dev, + "Current format is a multi-planar format\n"); + return -EINVAL; + } + + /* uncompressed image */ + f->fmt.pix.pixelformat = ctx->img_fmt->v4l2_pixfmt; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * ctx->img_fmt->bpp_buf[0]) / 8; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline; + f->fmt.pix.sizeimage *= f->fmt.pix.height; + } else { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = 0; + } + + f->fmt.pix.field = 0; + f->fmt.pix.colorspace = 0; + + return 0; +} + +static bool __smfc_check_image_size(struct device *dev, __u32 type, + const struct smfc_image_format *smfc_fmt, + __u32 width, __u32 height) +{ + __u32 min_width = SMFC_MIN_WIDTH << smfc_fmt->chroma_hfactor; + __u32 min_height = SMFC_MIN_WIDTH << smfc_fmt->chroma_vfactor; + + if ((width < min_width) || (height < min_height)) { + dev_warn(dev, "Too small image size(%ux%u) for '%s'\n", + width, height, buf_type_name(type)); + return false; + } + + if ((width > SMFC_MAX_WIDTH) || (height > SMFC_MAX_HEIGHT)) { + dev_warn(dev, "Too large image size(%ux%u) for '%s'\n", + width, height, buf_type_name(type)); + return false; + } + + if (((width % smfc_fmt->chroma_hfactor) != 0) || + ((height % smfc_fmt->chroma_vfactor) != 0)) { + dev_err(dev, "Invalid size %ux%u for format '%s'\n", + width, height, smfc_fmt->description); + return false; + } + + return true; +} +static bool smfc_check_image_size(struct device *dev, __u32 type, + const struct smfc_image_format *smfc_fmt, + __u32 width, __u32 height) +{ + __u32 twidth = SMFC_FMT_SEC_SIZE(width); + __u32 theight = SMFC_FMT_SEC_SIZE(height); + + width = SMFC_FMT_MAIN_SIZE(width); + height = SMFC_FMT_MAIN_SIZE(height); + + if (!__smfc_check_image_size(dev, type, smfc_fmt, width, height)) + return false; + + if (!twidth || !theight) /* thumbnail image size is not specified */ + return true; + + if (!__smfc_check_image_size(dev, type, smfc_fmt, twidth, theight)) + return false; + + return true; +} + +static bool smfc_v4l2_init_fmt_mplane(const struct smfc_ctx *ctx, + const struct smfc_image_format *smfc_fmt, + __u32 type, struct v4l2_pix_format_mplane *pix_mp) +{ + unsigned int i; + + if (!smfc_check_image_size(ctx->smfc->dev, type, + smfc_fmt, pix_mp->width, pix_mp->height)) + return false; + /* informs the user that the format might be changed to the default */ + pix_mp->pixelformat = smfc_fmt->v4l2_pixfmt; + /* JPEG format has zero in smfc_fmt->bpp_buf[0] */ + for (i = 0; i < smfc_fmt->num_buffers; i++) { + pix_mp->plane_fmt[i].bytesperline = + (SMFC_FMT_MAIN_SIZE(pix_mp->width) * + smfc_fmt->bpp_buf[i]) / 8; + pix_mp->plane_fmt[i].sizeimage = + pix_mp->plane_fmt[i].bytesperline * + SMFC_FMT_MAIN_SIZE(pix_mp->height); + } + + pix_mp->field = 0; + pix_mp->num_planes = smfc_fmt->num_buffers; + + if (SMFC_FMT_SEC_SIZE(pix_mp->width) && + SMFC_FMT_SEC_SIZE(pix_mp->height)) { + /* + * Format information for the secondary image is stored after + * the last plane of the main image. It is okay to use more + * planes because the maximum valid planes are eight + */ + unsigned int j; + + for (j = 0; j < smfc_fmt->num_buffers; j++) { + pix_mp->plane_fmt[i + j].bytesperline = + (SMFC_FMT_SEC_SIZE(pix_mp->width) * + smfc_fmt->bpp_buf[j]) / 8; + pix_mp->plane_fmt[i + j].sizeimage = + pix_mp->plane_fmt[i + j].bytesperline * + SMFC_FMT_SEC_SIZE(pix_mp->height); + } + } + + return true; +} + +static int smfc_v4l2_try_fmt_mplane(struct file *filp, void *fh, + struct v4l2_format *f) +{ + struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); + const struct smfc_image_format *smfc_fmt = + smfc_find_format(ctx->smfc, f->fmt.pix_mp.pixelformat); + + return smfc_v4l2_init_fmt_mplane( + ctx, smfc_fmt, f->type, &f->fmt.pix_mp) ? 0 : -EINVAL; +} + +static int smfc_v4l2_check_s_fmt(struct smfc_ctx *ctx, + const struct smfc_image_format *smfc_fmt, + __u32 type) +{ + struct vb2_queue *thisvq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, type); + struct vb2_queue *othervq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_TYPE_IS_OUTPUT(type) ? + V4L2_BUF_TYPE_VIDEO_CAPTURE : + V4L2_BUF_TYPE_VIDEO_OUTPUT); + u32 flags; + + if (thisvq->num_buffers > 0) { + dev_err(ctx->smfc->dev, + "S_FMT after REQBUFS is not allowed\n"); + return -EBUSY; + } + + flags = smfc_config_ctxflag(ctx, SMFC_CTX_COMPRESS, + is_jpeg(smfc_fmt) != V4L2_TYPE_IS_OUTPUT(type)); + + if (othervq->num_buffers > 0) { /* REQBUFSed on other vq */ + if ((flags & SMFC_CTX_COMPRESS) != + (ctx->flags & SMFC_CTX_COMPRESS)) { + dev_err(ctx->smfc->dev, + "Changing mode is prohibited after reqbufs\n"); + ctx->flags = flags; + return -EBUSY; + } + } + + /* reset the buf type of vq with the given buf type */ + thisvq->type = type; + + return 0; +} + +static __u32 v4l2_to_multiplane_type(__u32 type) +{ + /* V4L2_BUF_TYPE_VIDEO_CAPTURE+8 = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ + return V4L2_TYPE_IS_MULTIPLANAR(type) ? type : type + 8; +} + +static int smfc_v4l2_s_fmt_mplane(struct file *filp, void *fh, + struct v4l2_format *f) +{ + struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); + const struct smfc_image_format *smfc_fmt = + smfc_find_format(ctx->smfc, f->fmt.pix_mp.pixelformat); + int ret = smfc_v4l2_check_s_fmt(ctx, smfc_fmt, f->type); + + if (ret) + return ret; + + if (!smfc_v4l2_init_fmt_mplane(ctx, smfc_fmt, f->type, &f->fmt.pix_mp)) + return -EINVAL; + + ctx->width = SMFC_FMT_MAIN_SIZE(f->fmt.pix_mp.width); + ctx->height = SMFC_FMT_MAIN_SIZE(f->fmt.pix_mp.height); + ctx->thumb_width = SMFC_FMT_SEC_SIZE(f->fmt.pix_mp.width); + ctx->thumb_height = SMFC_FMT_SEC_SIZE(f->fmt.pix_mp.height); + + if (ctx->thumb_width && ctx->thumb_height) + ctx->flags |= SMFC_CTX_B2B_COMPRESS; + else + ctx->flags &= ~SMFC_CTX_B2B_COMPRESS; + + if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_JPEG) + ctx->img_fmt = smfc_fmt; + + if (ctx->flags & SMFC_CTX_B2B_COMPRESS) { + struct vb2_queue *othervq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_TYPE_IS_OUTPUT(f->type) ? + V4L2_BUF_TYPE_VIDEO_CAPTURE : + V4L2_BUF_TYPE_VIDEO_OUTPUT); + /* + * type change of the current queue is completed in + * smfc_v4l2_check_s_fmt() + */ + othervq->type = v4l2_to_multiplane_type(othervq->type); + } + + return 0; +} + +static bool smfc_v4l2_init_fmt(const struct smfc_ctx *ctx, + const struct smfc_image_format *smfc_fmt, + __u32 type, struct v4l2_pix_format *pix) +{ + BUG_ON(V4L2_TYPE_IS_MULTIPLANAR(pix->pixelformat)); + + if (!smfc_check_image_size(ctx->smfc->dev, type, + smfc_fmt, pix->width, pix->height)) + return false; + /* informs the user that the format might be changed to the default */ + pix->pixelformat = smfc_fmt->v4l2_pixfmt; + /* JPEG format has zero in smfc_fmt->bpp_buf[0] */ + pix->bytesperline = (pix->width * smfc_fmt->bpp_buf[0]) / 8; + pix->sizeimage = pix->bytesperline * pix->height; + pix->field = 0; + + return true; +} + +static int smfc_v4l2_try_fmt(struct file *filp, void *fh, struct v4l2_format *f) +{ + struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); + const struct smfc_image_format *smfc_fmt = + smfc_find_format(ctx->smfc, f->fmt.pix.pixelformat); + + return smfc_v4l2_init_fmt(ctx, smfc_fmt, f->type, &f->fmt.pix) + ? 0 : -EINVAL; +} + +static int smfc_v4l2_s_fmt(struct file *filp, void *fh, struct v4l2_format *f) +{ + struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); + const struct smfc_image_format *smfc_fmt = + smfc_find_format(ctx->smfc, f->fmt.pix.pixelformat); + int ret = smfc_v4l2_check_s_fmt(ctx, smfc_fmt, f->type); + + if (ret) + return ret; + + if (!smfc_v4l2_init_fmt(ctx, smfc_fmt, f->type, &f->fmt.pix)) + return -EINVAL; + + ctx->width = SMFC_FMT_MAIN_SIZE(f->fmt.pix.width); + ctx->height = SMFC_FMT_MAIN_SIZE(f->fmt.pix.height); + /* back-to-back compression is not supported with single plane */ + ctx->thumb_width = 0; + ctx->thumb_height = 0; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + ctx->img_fmt = smfc_fmt; + + return 0; +} + +const struct v4l2_ioctl_ops smfc_v4l2_ioctl_ops = { + .vidioc_querycap = smfc_v4l2_querycap, + .vidioc_enum_fmt_vid_cap = smfc_v4l2_enum_fmt, + .vidioc_enum_fmt_vid_out = smfc_v4l2_enum_fmt, + .vidioc_enum_fmt_vid_cap_mplane = smfc_v4l2_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = smfc_v4l2_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap = smfc_v4l2_g_fmt, + .vidioc_g_fmt_vid_out = smfc_v4l2_g_fmt, + .vidioc_g_fmt_vid_cap_mplane = smfc_v4l2_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = smfc_v4l2_g_fmt_mplane, + .vidioc_try_fmt_vid_cap = smfc_v4l2_try_fmt, + .vidioc_try_fmt_vid_out = smfc_v4l2_try_fmt, + .vidioc_try_fmt_vid_cap_mplane = smfc_v4l2_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = smfc_v4l2_try_fmt_mplane, + .vidioc_s_fmt_vid_cap = smfc_v4l2_s_fmt, + .vidioc_s_fmt_vid_out = smfc_v4l2_s_fmt, + .vidioc_s_fmt_vid_cap_mplane = smfc_v4l2_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = smfc_v4l2_s_fmt_mplane, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, +}; diff --git a/drivers/media/platform/exynos/smfc/smfc.c b/drivers/media/platform/exynos/smfc/smfc.c index 9c2c9992cbad..e279aa5ac732 100644 --- a/drivers/media/platform/exynos/smfc/smfc.c +++ b/drivers/media/platform/exynos/smfc/smfc.c @@ -26,270 +26,6 @@ #include "smfc.h" -/* SMFC SPECIFIC DEVICE CAPABILITIES */ -/* set if H/W supports for decompression */ -#define V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION 0x0100 -/* set if H/W can compress dual images */ -#define V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION 0x0200 -/* set if H/W supports for Hardware Flow Control */ -#define V4L2_CAP_EXYNOS_JPEG_HWFC 0x0400 -/* set if H/W supports for HWFC on internal buffers */ -#define V4L2_CAP_EXYNOS_JPEG_HWFC_EMBEDDED 0x0800 -/* set if H/W has a register to configure stream buffer size */ -#define V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE 0x1000 -/* set if H/W does not have 128-bit alignment constraint for stream base */ -#define V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN 0x2000 -/* set if H/W does not have 128-bit alignment constraint for image base */ -#define V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN 0x4000 - -/* SMFC SPECIFIC CONTROLS */ -#define V4L2_CID_JPEG_SEC_COMP_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 20) -#define V4L2_CID_JPEG_HWFC_ENABLE (V4L2_CID_JPEG_CLASS_BASE + 25) - -#define SMFC_DEFAULT_OUTPUT_FORMAT (&smfc_image_formats[1]) -#define SMFC_DEFAULT_CAPTURE_FORMAT (&smfc_image_formats[0]) - -#define SMFC_FMT_MAIN_SIZE(val) ((val) & 0xFFFF) -#define SMFC_FMT_SEC_SIZE(val) (((val) >> 16) & 0xFFFF) - -const struct smfc_image_format smfc_image_formats[] = { - { - /* JPEG should be the first format */ - .description = "Baseline JPEG(Sequential DCT)", - .v4l2_pixfmt = V4L2_PIX_FMT_JPEG, - .regcfg = 0, /* Chagneable accroding to chroma factor */ - .bpp_buf = {0}, /* undeterministic */ - .bpp_pix = {0}, /* undeterministic */ - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 1, /* dummy chroma subsampling factor */ - .chroma_vfactor = 1, /* These will not affect to H/W config. */ - }, { - /* YUYV is the default format */ - .description = "YUV4:2:2 1-Plane 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YUYV, - .regcfg = SMFC_IMGFMT_YUYV, - .bpp_buf = {16}, - .bpp_pix = {16}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 1, - }, { - .description = "YUV4:2:2 1-Plane 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YVYU, - .regcfg = SMFC_IMGFMT_YVYU, - .bpp_buf = {16}, - .bpp_pix = {16}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 1, - }, { - .description = "YUV4:2:2 2-Plane 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_NV16, - .regcfg = SMFC_IMGFMT_NV16, - .bpp_buf = {16}, - .bpp_pix = {8, 8, 0}, - .num_planes = 2, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 1, - }, { - .description = "YUV4:2:2 2-Plane 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_NV61, - .regcfg = SMFC_IMGFMT_NV61, - .bpp_buf = {16}, - .bpp_pix = {8, 8, 0}, - .num_planes = 2, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 1, - }, { - .description = "YUV4:2:2 3-Plane 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YUV422P, - .regcfg = SMFC_IMGFMT_YUV422, - .bpp_buf = {16}, - .bpp_pix = {8, 2, 2}, - .num_planes = 3, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 1, - }, { - .description = "YUV4:2:0 2-Plane(Cb/Cr) 12BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_NV12, - .regcfg = SMFC_IMGFMT_NV12, - .bpp_buf = {12}, - .bpp_pix = {8, 4, 0}, - .num_planes = 2, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - }, { - .description = "YUV4:2:0 2-Plane(Cr/Cb) 12BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_NV21, - .regcfg = SMFC_IMGFMT_NV21, - .bpp_buf = {12}, - .bpp_pix = {8, 4, 0}, - .num_planes = 2, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - }, { - .description = "YUV4:2:0 3-Plane 12BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YUV420, - .regcfg = SMFC_IMGFMT_YUV420P, - .bpp_buf = {12}, - .bpp_pix = {8, 2, 2}, - .num_planes = 3, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - }, { - .description = "YVU4:2:0 3-Plane 12BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YVU420, - .regcfg = SMFC_IMGFMT_YVU420P, - .bpp_buf = {12}, - .bpp_pix = {8, 2, 2}, - .num_planes = 3, - .num_buffers = 1, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - }, { - .description = "YUV4:4:4 3-Plane 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YUV444, - .regcfg = SMFC_IMGFMT_YUV444, - .bpp_buf = {24}, - .bpp_pix = {8, 8, 8}, - .num_planes = 3, - .num_buffers = 1, - .chroma_hfactor = 1, - .chroma_vfactor = 1, - }, { - .description = "RGB565 LE 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_RGB565, - .regcfg = SMFC_IMGFMT_RGB565, - .bpp_buf = {16}, - .bpp_pix = {16}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 1, - .chroma_vfactor = 1, - }, { - .description = "RGB565 BE 16BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_RGB565X, - .regcfg = SMFC_IMGFMT_BGR565, - .bpp_buf = {16}, - .bpp_pix = {16}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 1, - .chroma_vfactor = 1, - }, { - .description = "RGB888 LE 24BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_RGB24, - .regcfg = SMFC_IMGFMT_RGB24, - .bpp_buf = {24}, - .bpp_pix = {24}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 1, - .chroma_vfactor = 1, - }, { - .description = "RGB888 BE 24BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_BGR24, - .regcfg = SMFC_IMGFMT_BGR24, - .bpp_buf = {24}, - .bpp_pix = {24}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 1, - .chroma_vfactor = 1, - }, { - .description = "ABGR8888 LE 32BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_RGB32, - .regcfg = SMFC_IMGFMT_RGBA32, - .bpp_buf = {32}, - .bpp_pix = {32}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 1, - .chroma_vfactor = 1, - }, { - .description = "ARGB8888 BE 32BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_BGR32, - .regcfg = SMFC_IMGFMT_BGRA32, - .bpp_buf = {32}, - .bpp_pix = {32}, - .num_planes = 1, - .num_buffers = 1, - .chroma_hfactor = 1, - .chroma_vfactor = 1, - }, - /* multi-planar formats must be at the last becuse of VIDOC_ENUM_FMT */ - { - .description = "YUV4:2:0 2-MPlane(Cb/Cr) 8+4BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_NV12M, - .regcfg = SMFC_IMGFMT_NV12, - .bpp_buf = {8, 4}, - .bpp_pix = {8, 4}, - .num_planes = 2, - .num_buffers = 2, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - }, { - .description = "YUV4:2:0 2-MPlane(Cr/Cb) 8+4BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_NV21M, - .regcfg = SMFC_IMGFMT_NV21, - .bpp_buf = {8, 4}, - .bpp_pix = {8, 4}, - .num_planes = 2, - .num_buffers = 2, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - }, { - .description = "YUV4:2:0 3-MPlane 8+2+2BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YUV420M, - .regcfg = SMFC_IMGFMT_YUV420P, - .bpp_buf = {8, 2, 2}, - .bpp_pix = {8, 2, 2}, - .num_planes = 3, - .num_buffers = 3, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - }, { - .description = "YVU4:2:0 3-MPlane 8+2+2BPP", - .v4l2_pixfmt = V4L2_PIX_FMT_YVU420M, - .regcfg = SMFC_IMGFMT_YVU420P, - .bpp_buf = {8, 2, 2}, - .bpp_pix = {8, 2, 2}, - .num_planes = 3, - .num_buffers = 3, - .chroma_hfactor = 2, - .chroma_vfactor = 2, - } -}; - -static const struct smfc_image_format *smfc_find_format( - struct smfc_dev *smfc, __u32 v4l2_pixfmt) -{ - size_t i; - for (i = 0; i < ARRAY_SIZE(smfc_image_formats); i++) - if (smfc_image_formats[i].v4l2_pixfmt == v4l2_pixfmt) - return &smfc_image_formats[i]; - - dev_warn(smfc->dev, "Pixel format '%08X' not found, YUYV is forced.\n", - v4l2_pixfmt); - - return V4L2_TYPE_IS_OUTPUT(v4l2_pixfmt) ? SMFC_DEFAULT_OUTPUT_FORMAT - : SMFC_DEFAULT_CAPTURE_FORMAT; -} - -static const char *buf_type_name(__u32 type) -{ - return V4L2_TYPE_IS_OUTPUT(type) ? "capture" : "output"; -} - static irqreturn_t exynos_smfc_irq_handler(int irq, void *priv) { struct smfc_dev *smfc = priv; @@ -588,128 +324,6 @@ static int smfc_queue_init(void *priv, struct vb2_queue *src_vq, return vb2_queue_init(dst_vq); } -static int smfc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct smfc_ctx *ctx = container_of(ctrl->handler, - struct smfc_ctx, v4l2_ctrlhdlr); - switch (ctrl->id) { - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ctx->quality_factor = (unsigned char)ctrl->val; - break; - case V4L2_CID_JPEG_SEC_COMP_QUALITY: - ctx->thumb_quality_factor = (unsigned char)ctrl->val; - break; - case V4L2_CID_JPEG_HWFC_ENABLE: - ctx->enable_hwfc = (unsigned char)ctrl->val; - break; - case V4L2_CID_JPEG_RESTART_INTERVAL: - ctx->restart_interval = (unsigned char)ctrl->val; - break; - case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: - switch (ctrl->val) { - case V4L2_JPEG_CHROMA_SUBSAMPLING_444: - ctx->chroma_hfactor = 1; - ctx->chroma_vfactor = 1; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_420: - ctx->chroma_hfactor = 2; - ctx->chroma_vfactor = 2; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_411: - ctx->chroma_hfactor = 4; - ctx->chroma_vfactor = 1; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: - ctx->chroma_hfactor = 0; - ctx->chroma_vfactor = 0; - break; - case V4L2_JPEG_CHROMA_SUBSAMPLING_410: - dev_info(ctx->smfc->dev, - "Compression to YUV410 is not supported\n"); - /* pass through to 422 */ - case V4L2_JPEG_CHROMA_SUBSAMPLING_422: - default: - ctx->chroma_hfactor = 2; - ctx->chroma_vfactor = 1; - break; - } - break; - default: - break; - } - - return 0; -} - -static const struct v4l2_ctrl_ops smfc_ctrl_ops = { - .s_ctrl = smfc_s_ctrl, -}; - -static int smfc_init_controls(struct smfc_dev *smfc, - struct v4l2_ctrl_handler *hdlr) -{ - const char *msg; - struct v4l2_ctrl_config ctrlcfg; - - v4l2_ctrl_handler_init(hdlr, 1); - - if (!v4l2_ctrl_new_std(hdlr, &smfc_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - 1, 100, 1, 96)) { - msg = "quality factor"; - goto err; - } - - memset(&ctrlcfg, 0, sizeof(ctrlcfg)); - ctrlcfg.ops = &smfc_ctrl_ops; - ctrlcfg.id = V4L2_CID_JPEG_SEC_COMP_QUALITY; - ctrlcfg.name = "Quality factor of secondray image"; - ctrlcfg.type = V4L2_CTRL_TYPE_INTEGER; - ctrlcfg.min = 1; - ctrlcfg.max = 100; - ctrlcfg.step = 1; - ctrlcfg.def = 50; - if (!v4l2_ctrl_new_custom(hdlr, &ctrlcfg, NULL)) { - msg = "secondary quality factor"; - goto err; - } - - memset(&ctrlcfg, 0, sizeof(ctrlcfg)); - ctrlcfg.ops = &smfc_ctrl_ops; - ctrlcfg.id = V4L2_CID_JPEG_HWFC_ENABLE; - ctrlcfg.name = "HWFC configuration"; - ctrlcfg.type = V4L2_CTRL_TYPE_BOOLEAN; - ctrlcfg.min = 0; - ctrlcfg.max = 1; - ctrlcfg.step = 1; - ctrlcfg.def = 0; - if (!v4l2_ctrl_new_custom(hdlr, &ctrlcfg, NULL)) { - msg = "secondary quality factor"; - goto err; - } - - if (!v4l2_ctrl_new_std(hdlr, &smfc_ctrl_ops, - V4L2_CID_JPEG_RESTART_INTERVAL, - 0, 64, 1, 0)) { - msg = "restart interval"; - goto err; - } - - if (!v4l2_ctrl_new_std_menu(hdlr, &smfc_ctrl_ops, - V4L2_CID_JPEG_CHROMA_SUBSAMPLING, - V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, 0, - V4L2_JPEG_CHROMA_SUBSAMPLING_422)) { - msg = "chroma subsampling"; - goto err; - } - - return 0; -err: - dev_err(smfc->dev, "Failed to install %s control (%d)\n", - msg, hdlr->error); - return hdlr->error; -} - static int exynos_smfc_open(struct file *filp) { struct smfc_dev *smfc = video_drvdata(filp); @@ -813,400 +427,6 @@ static const struct v4l2_file_operations smfc_v4l2_fops = { .mmap = v4l2_m2m_fop_mmap, }; -static int smfc_v4l2_querycap(struct file *filp, void *fh, - struct v4l2_capability *cap) -{ - struct smfc_dev *smfc = v4l2_fh_to_smfc_ctx(fh)->smfc; - - strncpy(cap->driver, MODULE_NAME, sizeof(cap->driver)); - strncpy(cap->bus_info, dev_name(smfc->dev), sizeof(cap->bus_info)); - scnprintf(cap->card, sizeof(cap->card), "Still MFC %02x.%02x.%04x", - (smfc->hwver >> 24) & 0xFF, (smfc->hwver >> 16) & 0xFF, - smfc->hwver & 0xFFFF); - - cap->driver[sizeof(cap->driver) - 1] = '\0'; - cap->card[sizeof(cap->card) - 1] = '\0'; - cap->bus_info[sizeof(cap->bus_info) - 1] = '\0'; - - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE - | V4L2_CAP_VIDEO_M2M; - cap->capabilities |= V4L2_CAP_DEVICE_CAPS; - - cap->device_caps = V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION; - cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_HWFC; - cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_MAX_STREAMSIZE; - cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN; - cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_NO_IMAGEBASE_ALIGN; - - return 0; -} - -static int smfc_v4l2_enum_fmt_mplane(struct file *filp, void *fh, - struct v4l2_fmtdesc *f) -{ - const struct smfc_image_format *fmt; - - if (f->index >= ARRAY_SIZE(smfc_image_formats)) - return -EINVAL; - - fmt = &smfc_image_formats[f->index]; - strncpy(f->description, fmt->description, sizeof(f->description)); - f->description[sizeof(f->description) - 1] = '\0'; - f->pixelformat = fmt->v4l2_pixfmt; - if (fmt->bpp_buf[0] == 0) - f->flags = V4L2_FMT_FLAG_COMPRESSED; - - return 0; -} - -static int smfc_v4l2_enum_fmt(struct file *filp, void *fh, - struct v4l2_fmtdesc *f) -{ - int ret = smfc_v4l2_enum_fmt_mplane(filp, fh, f); - if (ret < 0) - return ret; - - return smfc_image_formats[f->index].num_buffers > 1 ? -EINVAL : 0; -} - -static int smfc_v4l2_g_fmt_mplane(struct file *filp, void *fh, - struct v4l2_format *f) -{ - struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); - f->fmt.pix_mp.width = ctx->width; - f->fmt.pix_mp.height = ctx->height; - - if (!smfc_is_compressed_type(ctx, f->type)) { - int i; - /* uncompressed image */ - f->fmt.pix_mp.pixelformat = ctx->img_fmt->v4l2_pixfmt; - f->fmt.pix_mp.num_planes = ctx->img_fmt->num_buffers; - for (i = 0; i < ctx->img_fmt->num_buffers; i++) { - f->fmt.pix_mp.plane_fmt[i].bytesperline = - (f->fmt.pix_mp.width * - ctx->img_fmt->bpp_buf[i]) / 8; - f->fmt.pix_mp.plane_fmt[i].sizeimage = - f->fmt.pix_mp.plane_fmt[i].bytesperline; - f->fmt.pix_mp.plane_fmt[i].sizeimage *= - f->fmt.pix_mp.height; - } - } else { - f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG; - f->fmt.pix_mp.num_planes = 1; - f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; - f->fmt.pix_mp.plane_fmt[0].sizeimage = 0; - } - - f->fmt.pix_mp.field = 0; - f->fmt.pix_mp.colorspace = 0; - - return 0; -} - -static int smfc_v4l2_g_fmt(struct file *filp, void *fh, struct v4l2_format *f) -{ - struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); - - f->fmt.pix.width = ctx->width; - f->fmt.pix.height = ctx->height; - - if (!smfc_is_compressed_type(ctx, f->type)) { - if (ctx->img_fmt->num_buffers > 1) { - dev_err(ctx->smfc->dev, - "Current format is a multi-planar format\n"); - return -EINVAL; - } - - /* uncompressed image */ - f->fmt.pix.pixelformat = ctx->img_fmt->v4l2_pixfmt; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * ctx->img_fmt->bpp_buf[0]) / 8; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline; - f->fmt.pix.sizeimage *= f->fmt.pix.height; - } else { - f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = 0; - } - - f->fmt.pix.field = 0; - f->fmt.pix.colorspace = 0; - - return 0; -} - -static bool __smfc_check_image_size(struct device *dev, __u32 type, - const struct smfc_image_format *smfc_fmt, - __u32 width, __u32 height) -{ - __u32 min_width = SMFC_MIN_WIDTH << smfc_fmt->chroma_hfactor; - __u32 min_height = SMFC_MIN_WIDTH << smfc_fmt->chroma_vfactor; - - if ((width < min_width) || (height < min_height)) { - dev_warn(dev, "Too small image size(%ux%u) for '%s'\n", - width, height, buf_type_name(type)); - return false; - } - - if ((width > SMFC_MAX_WIDTH) || (height > SMFC_MAX_HEIGHT)) { - dev_warn(dev, "Too large image size(%ux%u) for '%s'\n", - width, height, buf_type_name(type)); - return false; - } - - if (((width % smfc_fmt->chroma_hfactor) != 0) || - ((height % smfc_fmt->chroma_vfactor) != 0)) { - dev_err(dev, "Invalid size %ux%u for format '%s'\n", - width, height, smfc_fmt->description); - return false; - } - - return true; -} -static bool smfc_check_image_size(struct device *dev, __u32 type, - const struct smfc_image_format *smfc_fmt, - __u32 width, __u32 height) -{ - __u32 twidth = SMFC_FMT_SEC_SIZE(width); - __u32 theight = SMFC_FMT_SEC_SIZE(height); - width = SMFC_FMT_MAIN_SIZE(width); - height = SMFC_FMT_MAIN_SIZE(height); - - if (!__smfc_check_image_size(dev, type, smfc_fmt, width, height)) - return false; - - if (!twidth || !theight) /* thumbnail image size is not specified */ - return true; - - if (!__smfc_check_image_size(dev, type, smfc_fmt, twidth, theight)) - return false; - - return true; -} - -static bool smfc_v4l2_init_fmt_mplane(const struct smfc_ctx *ctx, - const struct smfc_image_format *smfc_fmt, - __u32 type, struct v4l2_pix_format_mplane *pix_mp) -{ - unsigned int i; - - if (!smfc_check_image_size(ctx->smfc->dev, type, - smfc_fmt, pix_mp->width, pix_mp->height)) - return false; - /* informs the user that the format might be changed to the default */ - pix_mp->pixelformat = smfc_fmt->v4l2_pixfmt; - /* JPEG format has zero in smfc_fmt->bpp_buf[0] */ - for (i = 0; i < smfc_fmt->num_buffers; i++) { - pix_mp->plane_fmt[i].bytesperline = - (SMFC_FMT_MAIN_SIZE(pix_mp->width) * - smfc_fmt->bpp_buf[i]) / 8; - pix_mp->plane_fmt[i].sizeimage = - pix_mp->plane_fmt[i].bytesperline * - SMFC_FMT_MAIN_SIZE(pix_mp->height); - } - - pix_mp->field = 0; - pix_mp->num_planes = smfc_fmt->num_buffers; - - if (SMFC_FMT_SEC_SIZE(pix_mp->width) && - SMFC_FMT_SEC_SIZE(pix_mp->height)) { - /* - * Format information for the secondary image is stored after - * the last plane of the main image. It is okay to use more - * planes because the maximum valid planes are eight - */ - unsigned int j; - for (j = 0; j < smfc_fmt->num_buffers; j++) { - pix_mp->plane_fmt[i + j].bytesperline = - (SMFC_FMT_SEC_SIZE(pix_mp->width) * - smfc_fmt->bpp_buf[j]) / 8; - pix_mp->plane_fmt[i + j].sizeimage = - pix_mp->plane_fmt[i + j].bytesperline * - SMFC_FMT_SEC_SIZE(pix_mp->height); - } - } - - return true; -} - -static int smfc_v4l2_try_fmt_mplane(struct file *filp, void *fh, - struct v4l2_format *f) -{ - struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); - const struct smfc_image_format *smfc_fmt = - smfc_find_format(ctx->smfc, f->fmt.pix_mp.pixelformat); - - return smfc_v4l2_init_fmt_mplane( - ctx, smfc_fmt, f->type, &f->fmt.pix_mp) ? 0 : -EINVAL; -} - -static int smfc_v4l2_check_s_fmt(struct smfc_ctx *ctx, - const struct smfc_image_format *smfc_fmt, - __u32 type) -{ - struct vb2_queue *thisvq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, type); - struct vb2_queue *othervq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_TYPE_IS_OUTPUT(type) ? - V4L2_BUF_TYPE_VIDEO_CAPTURE : - V4L2_BUF_TYPE_VIDEO_OUTPUT); - u32 flags; - - if (thisvq->num_buffers > 0) { - dev_err(ctx->smfc->dev, - "S_FMT after REQBUFS is not allowed\n"); - return -EBUSY; - } - - flags = smfc_config_ctxflag(ctx, SMFC_CTX_COMPRESS, - is_jpeg(smfc_fmt) != V4L2_TYPE_IS_OUTPUT(type)); - - if (othervq->num_buffers > 0) { /* REQBUFSed on other vq */ - if ((flags & SMFC_CTX_COMPRESS) != - (ctx->flags & SMFC_CTX_COMPRESS)) { - dev_err(ctx->smfc->dev, - "Changing mode is prohibited after reqbufs\n"); - ctx->flags = flags; - return -EBUSY; - } - } - - /* reset the buf type of vq with the given buf type */ - thisvq->type = type; - - return 0; -} - -static __u32 v4l2_to_multiplane_type(__u32 type) -{ - /* V4L2_BUF_TYPE_VIDEO_CAPTURE+8 = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ - return V4L2_TYPE_IS_MULTIPLANAR(type) ? type : type + 8; -} - -static int smfc_v4l2_s_fmt_mplane(struct file *filp, void *fh, - struct v4l2_format *f) -{ - struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); - const struct smfc_image_format *smfc_fmt = - smfc_find_format(ctx->smfc, f->fmt.pix_mp.pixelformat); - int ret = smfc_v4l2_check_s_fmt(ctx, smfc_fmt, f->type); - if (ret) - return ret; - - if (!smfc_v4l2_init_fmt_mplane(ctx, smfc_fmt, f->type, &f->fmt.pix_mp)) - return -EINVAL; - - ctx->width = SMFC_FMT_MAIN_SIZE(f->fmt.pix_mp.width); - ctx->height = SMFC_FMT_MAIN_SIZE(f->fmt.pix_mp.height); - ctx->thumb_width = SMFC_FMT_SEC_SIZE(f->fmt.pix_mp.width); - ctx->thumb_height = SMFC_FMT_SEC_SIZE(f->fmt.pix_mp.height); - - if (ctx->thumb_width && ctx->thumb_height) - ctx->flags |= SMFC_CTX_B2B_COMPRESS; - else - ctx->flags &= ~SMFC_CTX_B2B_COMPRESS; - - if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_JPEG) - ctx->img_fmt = smfc_fmt; - - if (ctx->flags & SMFC_CTX_B2B_COMPRESS) { - struct vb2_queue *othervq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_TYPE_IS_OUTPUT(f->type) ? - V4L2_BUF_TYPE_VIDEO_CAPTURE : - V4L2_BUF_TYPE_VIDEO_OUTPUT); - /* - * type change of the current queue is completed in - * smfc_v4l2_check_s_fmt() - */ - othervq->type = v4l2_to_multiplane_type(othervq->type); - } - - return 0; -} - -static bool smfc_v4l2_init_fmt(const struct smfc_ctx *ctx, - const struct smfc_image_format *smfc_fmt, - __u32 type, struct v4l2_pix_format *pix) -{ - BUG_ON(V4L2_TYPE_IS_MULTIPLANAR(pix->pixelformat)); - - if (!smfc_check_image_size(ctx->smfc->dev, type, - smfc_fmt, pix->width, pix->height)) - return false; - /* informs the user that the format might be changed to the default */ - pix->pixelformat = smfc_fmt->v4l2_pixfmt; - /* JPEG format has zero in smfc_fmt->bpp_buf[0] */ - pix->bytesperline = (pix->width * smfc_fmt->bpp_buf[0]) / 8; - pix->sizeimage = pix->bytesperline * pix->height; - pix->field = 0; - - return true; -} - -static int smfc_v4l2_try_fmt(struct file *filp, void *fh, struct v4l2_format *f) -{ - struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); - const struct smfc_image_format *smfc_fmt = - smfc_find_format(ctx->smfc, f->fmt.pix.pixelformat); - - return smfc_v4l2_init_fmt(ctx, smfc_fmt, f->type, &f->fmt.pix) - ? 0 : -EINVAL; -} - -static int smfc_v4l2_s_fmt(struct file *filp, void *fh, struct v4l2_format *f) -{ - struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh); - const struct smfc_image_format *smfc_fmt = - smfc_find_format(ctx->smfc, f->fmt.pix.pixelformat); - int ret = smfc_v4l2_check_s_fmt(ctx, smfc_fmt, f->type); - if (ret) - return ret; - - if (!smfc_v4l2_init_fmt(ctx, smfc_fmt, f->type, &f->fmt.pix)) - return -EINVAL; - - ctx->width = SMFC_FMT_MAIN_SIZE(f->fmt.pix.width); - ctx->height = SMFC_FMT_MAIN_SIZE(f->fmt.pix.height); - /* back-to-back compression is not supported with single plane */ - ctx->thumb_width = 0; - ctx->thumb_height = 0; - - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - ctx->img_fmt = smfc_fmt; - - return 0; -} - - -static const struct v4l2_ioctl_ops smfc_v4l2_ioctl_ops = { - .vidioc_querycap = smfc_v4l2_querycap, - .vidioc_enum_fmt_vid_cap = smfc_v4l2_enum_fmt, - .vidioc_enum_fmt_vid_out = smfc_v4l2_enum_fmt, - .vidioc_enum_fmt_vid_cap_mplane = smfc_v4l2_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = smfc_v4l2_enum_fmt_mplane, - .vidioc_g_fmt_vid_cap = smfc_v4l2_g_fmt, - .vidioc_g_fmt_vid_out = smfc_v4l2_g_fmt, - .vidioc_g_fmt_vid_cap_mplane = smfc_v4l2_g_fmt_mplane, - .vidioc_g_fmt_vid_out_mplane = smfc_v4l2_g_fmt_mplane, - .vidioc_try_fmt_vid_cap = smfc_v4l2_try_fmt, - .vidioc_try_fmt_vid_out = smfc_v4l2_try_fmt, - .vidioc_try_fmt_vid_cap_mplane = smfc_v4l2_try_fmt_mplane, - .vidioc_try_fmt_vid_out_mplane = smfc_v4l2_try_fmt_mplane, - .vidioc_s_fmt_vid_cap = smfc_v4l2_s_fmt, - .vidioc_s_fmt_vid_out = smfc_v4l2_s_fmt, - .vidioc_s_fmt_vid_cap_mplane = smfc_v4l2_s_fmt_mplane, - .vidioc_s_fmt_vid_out_mplane = smfc_v4l2_s_fmt_mplane, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_log_status = v4l2_ctrl_log_status, -}; - static bool smfc_check_hwfc_configuration(struct smfc_ctx *ctx, bool hwfc_en) { const struct smfc_image_format *fmt = ctx->img_fmt; diff --git a/drivers/media/platform/exynos/smfc/smfc.h b/drivers/media/platform/exynos/smfc/smfc.h index d6778d73151e..e59a8948beb2 100644 --- a/drivers/media/platform/exynos/smfc/smfc.h +++ b/drivers/media/platform/exynos/smfc/smfc.h @@ -38,6 +38,11 @@ struct smfc_image_format { unsigned char chroma_vfactor; }; +extern const struct smfc_image_format smfc_image_formats[]; + +#define SMFC_DEFAULT_OUTPUT_FORMAT (&smfc_image_formats[1]) +#define SMFC_DEFAULT_CAPTURE_FORMAT (&smfc_image_formats[0]) + static inline bool is_jpeg(const struct smfc_image_format *fmt) { return fmt->bpp_buf[0] == 0; @@ -98,6 +103,8 @@ struct smfc_ctx { unsigned char enable_hwfc; }; +extern const struct v4l2_ioctl_ops smfc_v4l2_ioctl_ops; + static inline struct smfc_ctx *v4l2_fh_to_smfc_ctx(struct v4l2_fh *fh) { return container_of(fh, struct smfc_ctx, fh); @@ -116,6 +123,8 @@ static inline bool smfc_is_compressed_type(struct smfc_ctx *ctx, __u32 type) return !(ctx->flags & SMFC_CTX_COMPRESS) == V4L2_TYPE_IS_OUTPUT(type); } +int smfc_init_controls(struct smfc_dev *smfc, struct v4l2_ctrl_handler *hdlr); + /* H/W Configuration */ void smfc_hwconfigure_tables(struct smfc_ctx *ctx, unsigned int qfactor); void smfc_hwconfigure_image(struct smfc_ctx *ctx, -- 2.20.1