lineage_kernel_xcoverpro/drivers/media/platform/exynos/smfc/smfc-v4l2-ioctls.c

952 lines
26 KiB
C
Executable File

/*
* 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 <linux/kernel.h>
#include "smfc.h"
/* SMFC SPECIFIC CONTROLS */
#define V4L2_CID_JPEG_SEC_COMP_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 20)
#define V4L2_CID_JPEG_QTABLES2 (V4L2_CID_JPEG_CLASS_BASE + 22)
#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:
if (!smfc_is_capable(ctx->smfc,
V4L2_CAP_EXYNOS_JPEG_HWFC)) {
dev_err(ctx->smfc->dev,
"HWFC(OTF mode) not supported (ver.%08X)",
ctx->smfc->hwver);
return -EINVAL;
}
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;
case V4L2_CID_JPEG_QTABLES2:
/*
* reset the quality factor to indicate that the custom q-table
* is configured. It is sustained until the new quality factor
* is configured.
*/
ctx->quality_factor = 0;
break;
default:
dev_err(ctx->smfc->dev, "Unsupported CID %#x\n", ctrl->id);
return -EINVAL;
}
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)
{
struct smfc_ctx *ctx =
container_of(hdlr, struct smfc_ctx, v4l2_ctrlhdlr);
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;
}
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;
}
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;
}
memset(&ctrlcfg, 0, sizeof(ctrlcfg));
ctrlcfg.ops = &smfc_ctrl_ops;
ctrlcfg.id = V4L2_CID_JPEG_QTABLES2;
ctrlcfg.name = "Quantization table configration";
ctrlcfg.type = V4L2_CTRL_TYPE_U8;
ctrlcfg.min = 1;
ctrlcfg.max = 255;
ctrlcfg.step = 1;
ctrlcfg.def = 1;
ctrlcfg.dims[0] = SMFC_MCU_SIZE; /* A qtable has 64 values */
ctrlcfg.dims[1] = 2; /* 2 qtables are required for 0:luma, 1:chroma */
ctx->ctrl_qtbl2 = v4l2_ctrl_new_custom(hdlr, &ctrlcfg, NULL);
if (!ctx->ctrl_qtbl2) {
msg = "Q-Table";
goto err;
}
return 0;
err:
v4l2_ctrl_handler_free(hdlr);
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 = smfc->devdata->device_caps;
cap->device_caps |= V4L2_CAP_EXYNOS_JPEG_DMABUF_OFFSET;
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_check_capable_of_decompression(const struct smfc_dev *smfc,
const struct smfc_image_format *smfc_fmt, __u32 type)
{
if (smfc_is_capable(smfc, V4L2_CAP_EXYNOS_JPEG_DECOMPRESSION))
return true;
if (is_jpeg(smfc_fmt) != V4L2_TYPE_IS_OUTPUT(type)) /* compression? */
return true;
dev_err(smfc->dev, "Decompression is not supported (ver.%08X)",
smfc->hwver);
return false;
}
static int smfc_calc_crop(struct smfc_ctx *ctx, const struct v4l2_crop *cr)
{
u32 left = cr->c.left;
u32 top = cr->c.top;
u32 frame_width = ctx->width;
u32 crop_width = cr->c.width;
unsigned char bpp_pix = ctx->img_fmt->bpp_pix[0]/8;
unsigned char chroma_hfactor = ctx->img_fmt->chroma_hfactor;
unsigned char chroma_vfactor = ctx->img_fmt->chroma_vfactor;
unsigned int i;
ctx->crop.po[0] = (frame_width * top + left) * bpp_pix;
ctx->crop.so[0] = (frame_width - crop_width) * bpp_pix;
for(i = 1; i < ctx->img_fmt->num_planes; i++) {
ctx->crop.po[i] = (frame_width * top / chroma_vfactor + left )
* bpp_pix * 2 / chroma_hfactor / i;
ctx->crop.so[i] = (frame_width - crop_width) * bpp_pix
* 2 / chroma_hfactor / i;
if(i == 2) {
ctx->crop.po[i-1] = ctx->crop.po[i];
ctx->crop.so[i-1] = ctx->crop.so[i];
}
}
return 0;
}
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_capable_of_decompression(ctx->smfc, smfc_fmt, type))
return false;
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_prepare_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_prepare_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;
if(!is_jpeg(smfc_fmt)) {
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);
memset(&ctx->crop, 0, sizeof(ctx->crop));
ctx->crop.width = ctx->width;
ctx->crop.height = ctx->height;
}
if (ctx->thumb_width && ctx->thumb_height) {
if (!smfc_is_capable(ctx->smfc,
V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION)) {
dev_err(ctx->smfc->dev,
"back-to-back mode not supported (ver.%08X)",
ctx->smfc->hwver);
return -EINVAL;
}
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_prepare_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)
{
if (V4L2_TYPE_IS_MULTIPLANAR(pix->pixelformat)) {
dev_err(ctx->smfc->dev,
"Multi-planar format is not permitted.\n");
return false;
}
if (!smfc_check_capable_of_decompression(ctx->smfc, smfc_fmt, type))
return false;
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_prepare_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;
if(!is_jpeg(smfc_fmt)) {
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;
memset(&ctx->crop, 0, sizeof(ctx->crop));
ctx->crop.width = ctx->width;
ctx->crop.height = ctx->height;
}
if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
ctx->img_fmt = smfc_fmt;
return 0;
}
static int smfc_v4l2_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cr)
{
cr->bounds.left = 0;
cr->bounds.top = 0;
cr->bounds.width = SMFC_MAX_WIDTH;
cr->bounds.height = SMFC_MAX_HEIGHT;
cr->defrect = cr->bounds;
return 0;
}
static int smfc_v4l2_s_crop(struct file *file, void *fh,
const struct v4l2_crop *cr)
{
struct smfc_ctx *ctx = v4l2_fh_to_smfc_ctx(fh);
unsigned int i;
int ret = 0;
if ((cr->c.left < 0) || (cr->c.top < 0) ||
(cr->c.width < 16) || (cr->c.height < 16)) {
v4l2_err(&ctx->smfc->v4l2_dev,
"Invalid crop region (%d,%d):%dx%d\n",
cr->c.left, cr->c.top, cr->c.width, cr->c.height);
return -EINVAL;
}
if (((cr->c.left + cr->c.width) > ctx->width) ||
((cr->c.top + cr->c.height) > ctx->height)) {
v4l2_err(&ctx->smfc->v4l2_dev,
"Crop (%d,%d):%dx%d overflows the image %dx%d\n",
cr->c.left, cr->c.top, cr->c.width, cr->c.height,
ctx->width, ctx->height);
return -EINVAL;
}
if(!V4L2_TYPE_IS_OUTPUT(cr->type)) {
v4l2_err(&ctx->smfc->v4l2_dev,
"Cropping on the capture buffer is not supported\n");
return -EINVAL;
}
ctx->crop.width = cr->c.width;
ctx->crop.height = cr->c.height;
for (i = 0 ; i < SMFC_MAX_NUM_COMP; i++) {
ctx->crop.po[i] = 0;
ctx->crop.so[i] = 0;
}
if (!!smfc_calc_crop(ctx, cr))
return -EINVAL;
return ret;
}
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_cropcap = smfc_v4l2_cropcap,
.vidioc_s_crop = smfc_v4l2_s_crop,
.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,
};