Hi Devarsh, Some small notes: On 28/02/2024 3:11 pm, Devarsh Thakkar wrote: > This adds support for stateful V4L2 M2M based driver for Imagination E5010 > JPEG Encoder [1] which supports baseline encoding with two different > quantization tables and compression ratio as demanded. > > Support for both contiguous and non-contiguous YUV420 and YUV422 semiplanar > formats is added along with alignment restrictions as required by the > hardware. > > System and runtime PM hooks are added in the driver along with v4l2 crop > and selection API support. > > Minimum resolution supported is 64x64 and > Maximum resolution supported is 8192x8192. > > All v4l2-compliance tests are passing [2] : > v4l2-compliance -s -f -a -d /dev/video0 -e /dev/video1 > > Total for e5010 device /dev/video0: 79, Succeeded: 79, Failed: 0, > Warnings: 0 > > NOTE: video1 here is VIVID test pattern generator > > Also tests [3] were run manually to verify below driver features: > - Runtime Power Management > - Multi-instance JPEG Encoding > - DMABUF import, export support > - NV12, NV21, NV16, NV61 video format support > - Compression quality S_CTRL > > Existing V4L2 M2M based JPEG drivers namely s5p-jpeg, imx-jpeg and rcar_jpu > were referred while making this. > > TODO: > Add MMU and memory tiling support > > [1]: AM62A TRM (Section 7.6 is for JPEG Encoder) : > Link: https://www.ti.com/lit/pdf/spruj16 > > [2]: v4l2-compliance test : > Link: https://gist.github.com/devarsht/39c1197742cc858c38860e8cab61666d > > [3]: E5010 JPEG Encoder Manual tests : > > Performance: > Link: https://gist.github.com/devarsht/2f9b30f28768d9f6e338fc5b7c1d1758 > > Functionality: > Link: https://gist.github.com/devarsht/f12743405b285f47a0ce9b0f9681acd3 > > Compression Quality: > Link: https://gist.github.com/devarsht/8fd9edbdbfda7b2f2fe464b17484ceaf > > Multi Instance: > Link: https://gist.github.com/devarsht/dfcc17e85569bc05c62b069af82a289d > > Co-developed-by: David Huang <d-huang@xxxxxx> > Signed-off-by: David Huang <d-huang@xxxxxx> > Signed-off-by: Devarsh Thakkar <devarsht@xxxxxx> > --- > V2: > No change > > V3: > - Correct license headers > - Use more generic name core instead of jasper for base registers > - Add Comment for forward declarations > - Simplify quantization table calculations > - Use v4l2_apply_frmsize_constraints for updating framesize and remove unrequired functions > - Place TODO at top of file and in commit message too > - Use dev_err_probe helper in probe function > - Fix return value checking for failure scenarios in probe function > - Use v4l2_err/info/warn helpers instead of dev_err/info/warn helpers > - Fix unexpected indentation > - Correct commit message > - Remove dependency on ARCH_K3 as driver is not specific to that > > V4: > - Fix issue with default params setting > - Correct v4l2 error prints > - Simplify register write functions with single statement return values > - Remove unrequired error checks from get_queue() > - Drop explicit device_caps setting as it is already taken care by v4l2 > core > - Remove unrequired multiplanar checks and memset from s_fmt, g_fmt callback > functions > - Fix try_fmt callback to not update the queues > - Remove unrequired contiguous format attribute from queue_init > - Use dynamic allocation for video_device and remove unrequired > assignments in probe() > - Remove unrequired checks from queue_setup function > - Return queued buffers back if start_streaming fails > - Use ARRAY_SIZE in place of hard-coding > - Use huffman and quantization tables from reference header file > > V5: > - Sort the #includes alphabetically in e5010-jpeg-enc.c > - Update commit message to point to V5 test reports > > V6: > - Fix sparse warnings and maintain uniform usage of dev ptr to avoid > future such bugs. No more errors received as shown below : > > `smatch/smatch_scripts/kchecker > drivers/media/platform/imagination/e5010*.c > CHECK scripts/mod/empty.c > CALL scripts/checksyscalls.sh > CHECK arch/arm64/kernel/vdso/vgettimeofday.c > CC [M] drivers/media/platform/imagination/e5010-jpeg-enc.o > CHECK drivers/media/platform/imagination/e5010-jpeg-enc.c` > > - Drop Reviewed-by: Benjamin Gaignard <benjamin.gaignard@xxxxxxxxxxxxx> > as patchset got updated to fix sparse warnings mentinoed at > https://lore.kernel.org/all/f59b2d27-dc58-40fb-b899-647feb9d72e4@xxxxxxxxxxxxx/#t > > Link to previous patch revisions: > V3: https://lore.kernel.org/all/20230816152210.4080779-3-devarsht@xxxxxx/ > V4: https://lore.kernel.org/all/20240205114239.924697-4-devarsht@xxxxxx/ > V5: https://lore.kernel.org/all/20240215134641.3381478-4-devarsht@xxxxxx/ > > MAINTAINERS | 2 + > drivers/media/platform/Kconfig | 1 + > drivers/media/platform/Makefile | 1 + > drivers/media/platform/imagination/Kconfig | 12 + > drivers/media/platform/imagination/Makefile | 3 + > .../platform/imagination/e5010-core-regs.h | 585 +++++++ > .../platform/imagination/e5010-jpeg-enc-hw.c | 267 +++ > .../platform/imagination/e5010-jpeg-enc-hw.h | 42 + > .../platform/imagination/e5010-jpeg-enc.c | 1553 +++++++++++++++++ > .../platform/imagination/e5010-jpeg-enc.h | 169 ++ > .../platform/imagination/e5010-mmu-regs.h | 311 ++++ > 11 files changed, 2946 insertions(+) > create mode 100644 drivers/media/platform/imagination/Kconfig > create mode 100644 drivers/media/platform/imagination/Makefile > create mode 100644 drivers/media/platform/imagination/e5010-core-regs.h > create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc-hw.c > create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc-hw.h > create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc.c > create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc.h > create mode 100644 drivers/media/platform/imagination/e5010-mmu-regs.h > <snip> > diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c > new file mode 100644 > index 000000000000..8805e9089e65 > --- /dev/null > +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c > @@ -0,0 +1,1553 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Imagination E5010 JPEG Encoder driver. > + * > + * TODO: Add MMU and memory tiling support > + * > + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ > + * > + * Author: David Huang <d-huang@xxxxxx> > + * Author: Devarsh Thakkar <devarsht@xxxxxx> > + */ > + > +#include <linux/clk.h> > +#include <linux/dma-mapping.h> > +#include <linux/err.h> > +#include <linux/interrupt.h> > +#include <linux/ioctl.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/pm_runtime.h> > +#include <media/jpeg.h> > +#include <media/jpeg-enc-reftables.h> > +#include <media/v4l2-common.h> > +#include <media/v4l2-ctrls.h> > +#include <media/v4l2-device.h> > +#include <media/v4l2-event.h> > +#include <media/v4l2-ioctl.h> > +#include <media/v4l2-mem2mem.h> > +#include <media/videobuf2-dma-contig.h> > +#include <media/videobuf2-v4l2.h> > +#include "e5010-jpeg-enc.h" > +#include "e5010-jpeg-enc-hw.h" > + > +/* forward declarations */ > +static const struct of_device_id e5010_of_match[]; > + > +static const struct v4l2_file_operations e5010_fops; > + > +static const struct v4l2_ioctl_ops e5010_ioctl_ops; > + > +static const struct vb2_ops e5010_video_ops; > + > +static const struct v4l2_m2m_ops e5010_m2m_ops; > + > +static struct e5010_fmt e5010_formats[] = { > + { > + .fourcc = V4L2_PIX_FMT_NV12, > + .num_planes = 1, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, > + .chroma_order = CHROMA_ORDER_CB_CR, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > + { > + .fourcc = V4L2_PIX_FMT_NV12M, > + .num_planes = 2, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, > + .chroma_order = CHROMA_ORDER_CB_CR, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > + { > + .fourcc = V4L2_PIX_FMT_NV21, > + .num_planes = 1, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, > + .chroma_order = CHROMA_ORDER_CR_CB, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > + { > + .fourcc = V4L2_PIX_FMT_NV21M, > + .num_planes = 2, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, > + .chroma_order = CHROMA_ORDER_CR_CB, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > + { > + .fourcc = V4L2_PIX_FMT_NV16, > + .num_planes = 1, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, > + .chroma_order = CHROMA_ORDER_CB_CR, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > + { > + .fourcc = V4L2_PIX_FMT_NV16M, > + .num_planes = 2, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, > + .chroma_order = CHROMA_ORDER_CB_CR, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + > + }, > + { > + .fourcc = V4L2_PIX_FMT_NV61, > + .num_planes = 1, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, > + .chroma_order = CHROMA_ORDER_CR_CB, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > + { > + .fourcc = V4L2_PIX_FMT_NV61M, > + .num_planes = 2, > + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, > + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, > + .chroma_order = CHROMA_ORDER_CR_CB, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > + { > + .fourcc = V4L2_PIX_FMT_JPEG, > + .num_planes = 1, > + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, > + .subsampling = 0, > + .chroma_order = 0, > + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 16, > + MIN_DIMENSION, MAX_DIMENSION, 8 }, > + }, > +}; > + > +static unsigned int debug; > +module_param(debug, uint, 0644); > +MODULE_PARM_DESC(debug, "debug level"); > + > +#define dprintk(dev, lvl, fmt, arg...) \ > + v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg) > + > +static const struct v4l2_event e5010_eos_event = { > + .type = V4L2_EVENT_EOS > +}; > + > +static const char *type_name(enum v4l2_buf_type type) > +{ > + switch (type) { > + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: > + return "Output"; > + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: > + return "Capture"; > + default: > + return "Invalid"; > + } > +} > + > +static struct e5010_q_data *get_queue(struct e5010_context *ctx, enum v4l2_buf_type type) > +{ > + return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->out_queue : &ctx->cap_queue; > +} > + > +static void calculate_qp_tables(struct e5010_context *ctx) > +{ > + long long luminosity, contrast; > + int quality, i; > + > + quality = 50 - ctx->quality; > + > + luminosity = LUMINOSITY * quality / 50; > + contrast = CONTRAST * quality / 50; > + > + if (quality > 0) { > + luminosity *= INCREASE; > + contrast *= INCREASE; > + } > + > + for (i = 0; i < ARRAY_SIZE(luma_q_table); i++) { > + long long delta = chroma_q_table[i] * contrast + luminosity; > + int val = (int)(chroma_q_table[i] + delta); > + > + clamp(val, 1, 255); > + ctx->chroma_qp[i] = quality == -50 ? 1 : val; > + > + delta = luma_q_table[i] * contrast + luminosity; > + val = (int)(luma_q_table[i] + delta); > + clamp(val, 1, 255); > + ctx->luma_qp[i] = quality == -50 ? 1 : val; > + } > + > + ctx->update_qp = true; > +} > + > +static int update_qp_tables(struct e5010_context *ctx) > +{ > + struct e5010_dev *e5010 = ctx->e5010; > + int i, ret = 0; > + u32 lvalue, cvalue; > + > + lvalue = 0; > + cvalue = 0; > + > + for (i = 0; i < QP_TABLE_SIZE; i++) { > + lvalue |= ctx->luma_qp[i] << (8 * (i % 4)); > + cvalue |= ctx->chroma_qp[i] << (8 * (i % 4)); > + if (i % 4 == 3) { > + ret |= e5010_hw_set_qpvalue(e5010->core_base, > + JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET > + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), > + lvalue); > + ret |= e5010_hw_set_qpvalue(e5010->core_base, > + JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET > + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), > + cvalue); > + lvalue = 0; > + cvalue = 0; > + } > + } > + > + return ret; > +} > + > +static int e5010_set_input_subsampling(void __iomem *core_base, int subsampling) > +{ > + switch (subsampling) { > + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: > + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_420); > + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: > + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_422); > + default: > + return -EINVAL; > + }; > +} > + > +static int e5010_querycap(struct file *file, void *priv, struct v4l2_capability *cap) > +{ > + strscpy(cap->driver, E5010_MODULE_NAME, sizeof(cap->driver)); > + strscpy(cap->card, E5010_MODULE_NAME, sizeof(cap->card)); > + > + return 0; > +} > + > +static struct e5010_fmt *find_format(struct v4l2_format *f) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { > + if (e5010_formats[i].fourcc == f->fmt.pix_mp.pixelformat && > + e5010_formats[i].type == f->type) > + return &e5010_formats[i]; > + } > + > + return NULL; > +} > + > +static int e5010_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) > +{ > + int i, index = 0; > + struct e5010_fmt *fmt = NULL; > + struct e5010_context *ctx = file->private_data; > + > + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) { > + v4l2_err(&ctx->e5010->v4l2_dev, "ENUMFMT with Invalid type: %d\n", f->type); > + return -EINVAL; > + } > + > + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { > + if (e5010_formats[i].type == f->type) { > + if (index == f->index) { > + fmt = &e5010_formats[i]; > + break; > + } > + index++; > + } > + } > + > + if (!fmt) > + return -EINVAL; > + > + f->pixelformat = fmt->fourcc; > + return 0; > +} > + > +static int e5010_g_fmt(struct file *file, void *priv, struct v4l2_format *f) > +{ > + struct e5010_context *ctx = file->private_data; > + struct e5010_q_data *queue; > + int i; > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; > + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; > + > + queue = get_queue(ctx, f->type); > + > + pix_mp->flags = 0; > + pix_mp->field = V4L2_FIELD_NONE; > + pix_mp->pixelformat = queue->fmt->fourcc; > + pix_mp->width = queue->width_adjusted; > + pix_mp->height = queue->height_adjusted; > + pix_mp->num_planes = queue->fmt->num_planes; > + > + if (V4L2_TYPE_IS_OUTPUT(f->type)) { > + if (!pix_mp->colorspace) > + pix_mp->colorspace = V4L2_COLORSPACE_SRGB; > + > + for (i = 0; i < queue->fmt->num_planes; i++) { > + plane_fmt[i].sizeimage = queue->sizeimage[i]; > + plane_fmt[i].bytesperline = queue->bytesperline[i]; > + } > + > + } else { > + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; > + plane_fmt[0].bytesperline = 0; > + plane_fmt[0].sizeimage = queue->sizeimage[0]; > + } > + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; > + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; > + > + return 0; > +} > + > +static int e5010_jpeg_try_fmt(struct v4l2_format *f, struct e5010_context *ctx) > +{ > + struct e5010_fmt *fmt; > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; > + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; > + > + fmt = find_format(f); > + if (!fmt) { > + if (V4L2_TYPE_IS_OUTPUT(f->type)) > + pix_mp->pixelformat = V4L2_PIX_FMT_NV12; > + else > + pix_mp->pixelformat = V4L2_PIX_FMT_JPEG; > + fmt = find_format(f); > + if (!fmt) > + return -EINVAL; > + } > + > + if (V4L2_TYPE_IS_OUTPUT(f->type)) { > + if (!pix_mp->colorspace) > + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; > + if (!pix_mp->ycbcr_enc) > + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > + if (!pix_mp->quantization) > + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; > + if (!pix_mp->xfer_func) > + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; > + > + v4l2_apply_frmsize_constraints(&pix_mp->width, > + &pix_mp->height, > + &fmt->frmsize); > + > + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, > + pix_mp->width, pix_mp->height); > + > + } else { > + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; > + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; > + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; > + v4l2_apply_frmsize_constraints(&pix_mp->width, > + &pix_mp->height, > + &fmt->frmsize); > + plane_fmt[0].sizeimage = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; > + plane_fmt[0].sizeimage += HEADER_SIZE; > + plane_fmt[0].bytesperline = 0; > + pix_mp->pixelformat = fmt->fourcc; > + pix_mp->num_planes = fmt->num_planes; > + } > + pix_mp->flags = 0; > + pix_mp->field = V4L2_FIELD_NONE; > + > + dprintk(ctx->e5010, 2, > + "ctx: 0x%p: format type %s:, wxh: %dx%d (plane0 : %d bytes, plane1 : %d bytes),fmt: %c%c%c%c\n", > + ctx, type_name(f->type), pix_mp->width, pix_mp->height, > + plane_fmt[0].sizeimage, plane_fmt[1].sizeimage, > + (fmt->fourcc & 0xff), > + (fmt->fourcc >> 8) & 0xff, > + (fmt->fourcc >> 16) & 0xff, > + (fmt->fourcc >> 24) & 0xff); > + > + return 0; > +} > + > +static int e5010_try_fmt(struct file *file, void *priv, struct v4l2_format *f) > +{ > + struct e5010_context *ctx = file->private_data; > + > + return e5010_jpeg_try_fmt(f, ctx); > +} > + > +static int e5010_s_fmt(struct file *file, void *priv, struct v4l2_format *f) > +{ > + struct e5010_context *ctx = file->private_data; > + struct vb2_queue *vq; > + int ret = 0, i = 0; > + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; > + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; > + struct e5010_q_data *queue; > + struct e5010_fmt *fmt; > + > + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); > + if (!vq) > + return -EINVAL; > + > + if (vb2_is_busy(vq)) { > + v4l2_err(&ctx->e5010->v4l2_dev, "queue busy\n"); > + return -EBUSY; > + } > + > + ret = e5010_jpeg_try_fmt(f, ctx); > + if (ret) > + return ret; > + > + fmt = find_format(f); > + queue = get_queue(ctx, f->type); > + > + queue->fmt = fmt; > + queue->width = pix_mp->width; > + queue->height = pix_mp->height; > + > + if (V4L2_TYPE_IS_OUTPUT(f->type)) { > + for (i = 0; i < fmt->num_planes; i++) { > + queue->bytesperline[i] = plane_fmt[i].bytesperline; > + queue->sizeimage[i] = plane_fmt[i].sizeimage; > + } > + } else { > + queue->sizeimage[0] = plane_fmt[0].sizeimage; > + queue->sizeimage[1] = 0; > + queue->bytesperline[0] = 0; > + queue->bytesperline[1] = 0; > + } > + > + return 0; > +} > + > +static int e5010_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) > +{ > + struct v4l2_format f; > + struct e5010_fmt *fmt; > + > + if (fsize->index != 0) > + return -EINVAL; > + > + f.fmt.pix_mp.pixelformat = fsize->pixel_format; > + if (f.fmt.pix_mp.pixelformat == V4L2_PIX_FMT_JPEG) > + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; > + else > + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; > + > + fmt = find_format(&f); > + if (!fmt) > + return -EINVAL; > + > + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; > + fsize->stepwise = fmt->frmsize; > + fsize->reserved[0] = 0; > + fsize->reserved[1] = 0; > + > + return 0; > +} > + > +static int e5010_g_selection(struct file *file, void *fh, struct v4l2_selection *s) > +{ > + struct e5010_context *ctx = file->private_data; > + struct e5010_q_data *queue; > + > + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) This is wrong: check against V4L2_BUF_TYPE_VIDEO_OUTPUT. See drivers/media/v4l2-core/v4l2-ioctl.c, the comment before function v4l_g_selection. This causes v4l2-compliance to assume that cropping is not supported. Same for s_selection below. > + return -EINVAL; > + > + queue = get_queue(ctx, s->type); > + > + switch (s->target) { > + case V4L2_SEL_TGT_CROP_DEFAULT: > + case V4L2_SEL_TGT_CROP_BOUNDS: > + s->r.left = 0; > + s->r.top = 0; > + s->r.width = queue->width; > + s->r.height = queue->height; > + break; > + case V4L2_SEL_TGT_CROP: > + s->r = queue->crop; > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int e5010_s_selection(struct file *file, void *fh, struct v4l2_selection *s) > +{ > + struct e5010_context *ctx = file->private_data; > + struct e5010_q_data *queue; > + > + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) > + return -EINVAL; This doesn't check if vb2_is_streaming(queue): it should return -EBUSY in that case. It also doesn't check s->target: only V4L2_SEL_TGT_CROP is supported. Are there no requirements w.r.t. width and height? E.g. a multiple of 2 or 4? > + > + queue = get_queue(ctx, s->type); > + > + queue->crop.left = 0; > + queue->crop.top = 0; > + queue->crop.width = s->r.width; > + queue->crop.height = s->r.height; I think s->r.left and s->r.top should be set to 0 as well, so userspace sees the actual crop rectangle that is used. Note that this is not done in drivers/media/platform/verisilicon/hantro_v4l2.c either, that's a bug. There is also no check if the width/height are too large. > + > + return 0; > +} > + > +static int e5010_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) > +{ > + switch (sub->type) { > + case V4L2_EVENT_EOS: > + return v4l2_event_subscribe(fh, sub, 0, NULL); > + case V4L2_EVENT_CTRL: > + return v4l2_ctrl_subscribe_event(fh, sub); > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) > +{ > + struct e5010_context *ctx = priv; > + struct e5010_dev *e5010 = ctx->e5010; > + int ret = 0; > + > + /* src_vq */ > + memset(src_vq, 0, sizeof(*src_vq)); > + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; > + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; > + src_vq->drv_priv = ctx; > + src_vq->buf_struct_size = sizeof(struct e5010_buffer); > + src_vq->ops = &e5010_video_ops; > + src_vq->mem_ops = &vb2_dma_contig_memops; > + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + src_vq->lock = &e5010->mutex; > + src_vq->dev = e5010->v4l2_dev.dev; > + > + ret = vb2_queue_init(src_vq); > + if (ret) > + return ret; > + > + /* dst_vq */ > + memset(dst_vq, 0, sizeof(*dst_vq)); > + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; > + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; > + dst_vq->drv_priv = ctx; > + dst_vq->buf_struct_size = sizeof(struct e5010_buffer); > + dst_vq->ops = &e5010_video_ops; > + dst_vq->mem_ops = &vb2_dma_contig_memops; > + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + dst_vq->lock = &e5010->mutex; > + dst_vq->dev = e5010->v4l2_dev.dev; > + > + ret = vb2_queue_init(dst_vq); > + if (ret) { > + vb2_queue_release(src_vq); > + return ret; > + } > + > + return 0; > +} > + > +static int e5010_s_ctrl(struct v4l2_ctrl *ctrl) > +{ > + struct e5010_context *ctx = > + container_of(ctrl->handler, struct e5010_context, ctrl_handler); > + > + switch (ctrl->id) { > + case V4L2_CID_JPEG_COMPRESSION_QUALITY: > + ctx->quality = ctrl->val; > + calculate_qp_tables(ctx); > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static const struct v4l2_ctrl_ops e5010_ctrl_ops = { > + .s_ctrl = e5010_s_ctrl, > +}; > + > +static void e5010_encode_ctrls(struct e5010_context *ctx) > +{ > + v4l2_ctrl_new_std(&ctx->ctrl_handler, &e5010_ctrl_ops, > + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 75); > +} > + > +static int e5010_ctrls_setup(struct e5010_context *ctx) > +{ > + int err; > + > + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); > + > + e5010_encode_ctrls(ctx); > + > + if (ctx->ctrl_handler.error) { > + err = ctx->ctrl_handler.error; > + v4l2_ctrl_handler_free(&ctx->ctrl_handler); > + > + return err; > + } > + > + err = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); > + if (err) > + v4l2_ctrl_handler_free(&ctx->ctrl_handler); > + > + return err; > +} > + > +static void e5010_jpeg_set_default_params(struct e5010_context *ctx) > +{ > + struct e5010_q_data *queue; > + struct v4l2_format f; > + struct e5010_fmt *fmt; > + struct v4l2_pix_format_mplane *pix_mp = &f.fmt.pix_mp; > + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; > + int i = 0; > + > + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; > + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; > + fmt = find_format(&f); > + queue = &ctx->out_queue; > + queue->fmt = fmt; > + queue->width = DEFAULT_WIDTH; > + queue->height = DEFAULT_HEIGHT; > + pix_mp->width = queue->width; > + pix_mp->height = queue->height; > + v4l2_apply_frmsize_constraints(&pix_mp->width, > + &pix_mp->height, > + &fmt->frmsize); > + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, > + pix_mp->width, pix_mp->height); > + for (i = 0; i < fmt->num_planes; i++) { > + queue->bytesperline[i] = plane_fmt[i].bytesperline; > + queue->sizeimage[i] = plane_fmt[i].sizeimage; > + } > + queue->width_adjusted = pix_mp->width; > + queue->height_adjusted = pix_mp->height; > + queue->format_set = false; This seems to be unused. > + queue->streaming = false; Please don't duplicate state information. You can get the streaming state directly from the queue by calling vb2_start_streaming_called(). > + > + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; > + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG; > + fmt = find_format(&f); > + queue = &ctx->cap_queue; > + queue->fmt = fmt; > + queue->width = DEFAULT_WIDTH; > + queue->height = DEFAULT_HEIGHT; This also feels like duplication. Doesn't the queue->fmt already contain most of this? > + pix_mp->width = queue->width; > + pix_mp->height = queue->height; > + v4l2_apply_frmsize_constraints(&pix_mp->width, > + &pix_mp->height, > + &fmt->frmsize); > + queue->sizeimage[0] = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; > + queue->sizeimage[0] += HEADER_SIZE; > + queue->sizeimage[1] = 0; > + queue->bytesperline[0] = 0; > + queue->bytesperline[1] = 0; > + queue->format_set = false; > + queue->streaming = false; > + queue->width_adjusted = pix_mp->width; > + queue->height_adjusted = pix_mp->height; > +} Regards, Hans