Most codec drivers do the same thing, let's extract those generic bits
and provide them as m2m_codec helpers.

Each compressed-format will have its own set of sub-helpers.

Signed-off-by: Boris Brezillon <boris.brezil...@collabora.com>
---
 drivers/media/v4l2-core/Kconfig              |    4 +
 drivers/media/v4l2-core/Makefile             |    1 +
 drivers/media/v4l2-core/v4l2-mem2mem-codec.c | 1170 ++++++++++++++++++
 include/media/v4l2-mem2mem-codec.h           |  317 +++++
 4 files changed, 1492 insertions(+)
 create mode 100644 drivers/media/v4l2-core/v4l2-mem2mem-codec.c
 create mode 100644 include/media/v4l2-mem2mem-codec.h

diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 7c5f62f196e5..2ee185b58c72 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -45,6 +45,10 @@ config V4L2_MEM2MEM_DEV
        tristate
        depends on VIDEOBUF2_CORE
 
+config V4L2_MEM2MEM_CODEC
+       bool
+       depends on V4L2_MEM2MEM_DEV
+
 # Used by LED subsystem flash drivers
 config V4L2_FLASH_LED_CLASS
        tristate "V4L2 flash API for LED flash class devices"
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index a8f8117a788f..9c18fed9ea5a 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_VIDEO_V4L2) += v4l2-dv-timings.o
 obj-$(CONFIG_VIDEO_TUNER) += tuner.o
 
 v4l2-mem2mem-y = v4l2-mem2mem-core.o
+v4l2-mem2mem-$(CONFIG_V4L2_MEM2MEM_CODEC) += v4l2-mem2mem-codec.o
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
 obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem-codec.c 
b/drivers/media/v4l2-core/v4l2-mem2mem-codec.c
new file mode 100644
index 000000000000..2dbc7dc9e4ba
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-mem2mem-codec.c
@@ -0,0 +1,1170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Memory-to-memory codec framework for Video for Linux 2.
+ *
+ * Helper functions for codec devices that use memory buffers for both source
+ * and destination.
+ *
+ * Copyright (c) 2019 Collabora Ltd.
+ *
+ * Author:
+ *      Boris Brezillon <boris.brezil...@collabora.com>
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem-codec.h>
+
+/**
+ * v4l2_m2m_codec_init() - Initializes a v4l2_m2m_codec object
+ * @codec: the codec to initialize
+ * @type: the type of codec (encoder or decoder)
+ * @m2m_dev: the M2M device backing this codec
+ * @v4l2_dev: the V4L2 device backing this codec
+ * @caps: the codec format capabilities
+ * @ops: codec operations
+ * @fops: video device file operations
+ * @ioctl_ops: video device ioctl operations
+ * @lock: the lock to attach to the video device
+ * @name: the name of the video device
+ * @drvdata: driver private data to attach to the video device
+ *
+ * This function initializes the codec object and the video device it contains.
+ * The caller is still responsible for registering the video dev.
+ *
+ * Return: 0 in case of success, an negative error code otherwise.
+ */
+int v4l2_m2m_codec_init(struct v4l2_m2m_codec *codec,
+                       enum v4l2_m2m_codec_type type,
+                       struct v4l2_m2m_dev *m2m_dev,
+                       struct v4l2_device *v4l2_dev,
+                       const struct v4l2_m2m_codec_caps *caps,
+                       const struct v4l2_m2m_codec_ops *ops,
+                       const struct v4l2_file_operations *fops,
+                       const struct v4l2_ioctl_ops *ioctl_ops,
+                       struct mutex *lock, const char *name, void *drvdata)
+{
+       struct video_device *vdev = v4l2_m2m_codec_to_vdev(codec);
+       unsigned int i;
+       ssize_t ret;
+
+       if (!codec || !caps || !m2m_dev || !ops ||
+           !caps->num_coded_fmts || !caps->num_decoded_fmts ||
+           !caps->coded_fmts || !caps->decoded_fmts || !ops->queue_init)
+               return -EINVAL;
+
+       for (i = 0; i < caps->num_coded_fmts; i++) {
+               if (!caps->coded_fmts[i].ops)
+                       return -EINVAL;
+       }
+
+       codec->type = type;
+       codec->m2m_dev = m2m_dev;
+       codec->caps = caps;
+       codec->ops = ops;
+       vdev->lock = lock;
+       vdev->v4l2_dev = v4l2_dev;
+       vdev->fops = fops;
+       vdev->release = video_device_release_empty;
+       vdev->vfl_dir = VFL_DIR_M2M;
+       vdev->device_caps = V4L2_CAP_STREAMING;
+       vdev->ioctl_ops = ioctl_ops;
+       video_set_drvdata(vdev, drvdata);
+
+       if (ioctl_ops->vidioc_g_fmt_vid_out_mplane)
+               vdev->device_caps |= V4L2_CAP_VIDEO_M2M_MPLANE;
+       else
+               vdev->device_caps |= V4L2_CAP_VIDEO_M2M;
+
+       ret = strscpy(vdev->name, name, sizeof(vdev->name));
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_init);
+
+static int v4l2_m2m_codec_add_ctrls(struct v4l2_m2m_codec_ctx *ctx,
+                                   const struct v4l2_m2m_codec_ctrls *ctrls)
+{
+       unsigned int i;
+
+       if (!ctrls)
+               return 0;
+
+       if (ctrls->num_ctrls && !ctrls->ctrls)
+               return -EINVAL;
+
+       for (i = 0; i < ctrls->num_ctrls; i++) {
+               const struct v4l2_ctrl_config *cfg = &ctrls->ctrls[i].cfg;
+
+               v4l2_ctrl_new_custom(&ctx->ctrl_hdl, cfg, ctx);
+               if (ctx->ctrl_hdl.error)
+                       return ctx->ctrl_hdl.error;
+       }
+
+       return 0;
+}
+
+static void v4l2_m2m_codec_cleanup_ctrls(struct v4l2_m2m_codec_ctx *ctx)
+{
+       v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+}
+
+static int v4l2_m2m_codec_init_ctrls(struct v4l2_m2m_codec_ctx *ctx)
+{
+       const struct v4l2_m2m_codec_coded_fmt_desc *fmts;
+       unsigned int i, nfmts, nctrls = 0;
+       int ret;
+
+       fmts = ctx->codec->caps->coded_fmts;
+       nfmts = ctx->codec->caps->num_coded_fmts;
+       for (i = 0; i < nfmts; i++)
+               nctrls += fmts[i].ctrls->num_ctrls;
+
+       v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls);
+
+       for (i = 0; i < nfmts; i++) {
+               ret = v4l2_m2m_codec_add_ctrls(ctx, fmts[i].ctrls);
+               if (ret)
+                       goto err_free_handler;
+       }
+
+       ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
+       if (ret)
+               goto err_free_handler;
+
+       ctx->fh.ctrl_handler = &ctx->ctrl_hdl;
+       return 0;
+
+err_free_handler:
+       v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+       return ret;
+}
+
+static void v4l2_m2m_codec_reset_fmt(struct v4l2_m2m_codec_ctx *ctx,
+                                    struct v4l2_format *f, u32 fourcc)
+{
+       const struct v4l2_ioctl_ops *ops = ctx->codec->vdev.ioctl_ops;
+
+       memset(f, 0, sizeof(*f));
+
+       if (ops->vidioc_g_fmt_vid_cap_mplane) {
+               f->fmt.pix_mp.pixelformat = fourcc;
+               f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+               f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_JPEG,
+               f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+               f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+               f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+       } else {
+               f->fmt.pix.pixelformat = fourcc;
+               f->fmt.pix.field = V4L2_FIELD_NONE;
+               f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG,
+               f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+               f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
+               f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+       }
+}
+
+static void v4l2_m2m_codec_reset_coded_fmt(struct v4l2_m2m_codec_ctx *ctx)
+{
+       struct v4l2_m2m_codec *codec = ctx->codec;
+       const struct v4l2_ioctl_ops *ops = codec->vdev.ioctl_ops;
+       const struct v4l2_m2m_codec_coded_fmt_desc *desc;
+       struct v4l2_format *f = &ctx->coded_fmt;
+
+       desc = &ctx->codec->caps->coded_fmts[0];
+       ctx->coded_fmt_desc = desc;
+       v4l2_m2m_codec_reset_fmt(ctx, f, desc->fourcc);
+
+       if (ops->vidioc_g_fmt_vid_cap_mplane) {
+               struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
+
+               if (codec->type == V4L2_M2M_DECODER)
+                       f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+               else
+                       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+               if (desc->frmsize) {
+                       fmt->width = desc->frmsize->min_width;
+                       fmt->height = desc->frmsize->min_height;
+               }
+
+       } else {
+               struct v4l2_pix_format *fmt = &f->fmt.pix;
+
+               if (codec->type == V4L2_M2M_DECODER)
+                       f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+               else
+                       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+               if (desc->frmsize) {
+                       fmt->width = desc->frmsize->min_width;
+                       fmt->height = desc->frmsize->min_height;
+               }
+       }
+
+       if (desc->ops->adjust_fmt)
+               desc->ops->adjust_fmt(ctx, &ctx->coded_fmt);
+}
+
+/**
+ * v4l2_m2m_codec_reset_decoded_fmt() - Reset the decoded format embedded in a
+ *                                     codec context
+ * @ctx: the context to reset the fmt on
+ *
+ * The decoded format might need to be reset when specific operations (like
+ * updating the format) are done on the coded end of the pipeline. This
+ * function is also called at context initialization time.
+ */
+void v4l2_m2m_codec_reset_decoded_fmt(struct v4l2_m2m_codec_ctx *ctx)
+{
+       struct v4l2_m2m_codec *codec = ctx->codec;
+       const struct v4l2_ioctl_ops *ops = codec->vdev.ioctl_ops;
+       const struct v4l2_m2m_codec_coded_fmt_desc *coded_desc;
+       struct v4l2_format *f = &ctx->decoded_fmt;
+
+       if (!ctx->coded_fmt_desc)
+               v4l2_m2m_codec_reset_coded_fmt(ctx);
+
+       coded_desc = ctx->coded_fmt_desc;
+       v4l2_m2m_codec_reset_fmt(ctx, f, codec->caps->decoded_fmts[0].fourcc);
+       if (ops->vidioc_g_fmt_vid_cap_mplane) {
+               struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
+
+               if (codec->type == V4L2_M2M_DECODER)
+                       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+               else
+                       f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+               if (coded_desc->frmsize) {
+                       fmt->width = coded_desc->frmsize->min_width;
+                       fmt->height = coded_desc->frmsize->min_height;
+               }
+
+               v4l2_fill_pixfmt_mp(fmt, fmt->pixelformat,
+                                   fmt->width, fmt->height);
+       } else {
+               struct v4l2_pix_format *fmt = &f->fmt.pix;
+
+               if (codec->type == V4L2_M2M_DECODER)
+                       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               else
+                       f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+               if (coded_desc->frmsize) {
+                       fmt->width = coded_desc->frmsize->min_width;
+                       fmt->height = coded_desc->frmsize->min_height;
+               }
+
+               v4l2_fill_pixfmt(fmt, fmt->pixelformat,
+                                fmt->width, fmt->height);
+       }
+
+       ctx->decoded_fmt_desc = &codec->caps->decoded_fmts[0];
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_reset_decoded_fmt);
+
+static int v4l2_m2m_codec_queue_init(void *priv, struct vb2_queue *src_vq,
+                                    struct vb2_queue *dst_vq)
+{
+       struct v4l2_m2m_codec_ctx *ctx = priv;
+
+       return ctx->codec->ops->queue_init(ctx, src_vq, dst_vq);
+}
+
+/**
+ * v4l2_m2m_codec_ctx_init() - Initialize a codec context
+ * @ctx: the context to initialize
+ * @file: the file instance to attach this context to
+ * @codec: the codec device creating this context
+ *
+ * Initializes a codec ctx. A new m2m context is created and the file handle
+ * embedded in the codec context is initialized too. We also reset the coded
+ * and decoded formats to start from a known state, and add the controls that
+ * are defined in the supported coded formats.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_ctx_init(struct v4l2_m2m_codec_ctx *ctx, struct file *file,
+                           struct v4l2_m2m_codec *codec)
+{
+       int ret;
+
+       v4l2_fh_init(&ctx->fh, video_devdata(file));
+       ctx->codec = codec;
+       ret = v4l2_m2m_codec_init_ctrls(ctx);
+       if (ret)
+               return ret;
+
+       ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(codec->m2m_dev, ctx,
+                                           v4l2_m2m_codec_queue_init);
+       if (IS_ERR(ctx->fh.m2m_ctx)) {
+               ret = PTR_ERR(ctx->fh.m2m_ctx);
+               goto err_cleanup_ctrls;
+       }
+
+       file->private_data = &ctx->fh;
+       v4l2_fh_add(&ctx->fh);
+
+       v4l2_m2m_codec_reset_coded_fmt(ctx);
+       v4l2_m2m_codec_reset_decoded_fmt(ctx);
+       return 0;
+
+err_cleanup_ctrls:
+       v4l2_m2m_codec_cleanup_ctrls(ctx);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_ctx_init);
+
+/**
+ * v4l2_m2m_codec_ctx_cleanup() - Clean the codec context up
+ * @ctx: codec context to cleanup
+ *
+ * Undoes what's been done in v4l2_m2m_codec_ctx_init().
+ */
+void v4l2_m2m_codec_ctx_cleanup(struct v4l2_m2m_codec_ctx *ctx)
+{
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+       v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+       v4l2_m2m_codec_cleanup_ctrls(ctx);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_ctx_cleanup);
+
+/**
+ * v4l2_m2m_codec_run_preamble() - Preamble to a codec run
+ * @ctx: the context the run is happening on
+ * @run: object describing a codec run
+ *
+ * Prepare a codec run. The src/dst buffer are retrieved and stored in
+ * the run object, and v4l2_ctrl_request_setup() is called on the media
+ * request attached to the src buffer if there's one.
+ * We also copy the src buffer metadata to the dst buffer.
+ */
+void v4l2_m2m_codec_run_preamble(struct v4l2_m2m_codec_ctx *ctx,
+                                struct v4l2_m2m_codec_run *run)
+{
+       struct media_request *src_req;
+
+       memset(run, 0, sizeof(*run));
+
+       run->bufs.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+       run->bufs.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+       /* Apply request(s) controls if needed. */
+       src_req = run->bufs.src->vb2_buf.req_obj.req;
+       if (src_req)
+               v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl);
+
+       v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst, true);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_run_preamble);
+
+/**
+ * v4l2_m2m_codec_run_postamble() - Postamble to a codec run
+ * @ctx: the context the run is happening on
+ * @run: the codec run object
+ *
+ * Finish a run by declaring the request attached to the src buffer as
+ * complete.
+ */
+void v4l2_m2m_codec_run_postamble(struct v4l2_m2m_codec_ctx *ctx,
+                                 struct v4l2_m2m_codec_run *run)
+{
+       struct media_request *src_req = run->bufs.src->vb2_buf.req_obj.req;
+
+       if (src_req)
+               v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_run_postamble);
+
+/**
+ * v4l2_m2m_codec_job_finish() - Declare the current job as finished
+ * @ctx: context this job is running on
+ * @state: state of the job
+ *
+ * Should be called when the codec is done encoding/decoding a frame.
+ * The src/dst buffers are returned to their queues and
+ * v4l2_m2m_job_finish() is called.
+ */
+void v4l2_m2m_codec_job_finish(struct v4l2_m2m_codec_ctx *ctx,
+                              enum vb2_buffer_state state)
+{
+       struct vb2_v4l2_buffer *src_buf, *dst_buf;
+
+       src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+       WARN_ON(!src_buf);
+       if (src_buf)
+               v4l2_m2m_buf_done(src_buf, state);
+
+       dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+       WARN_ON(!dst_buf);
+       if (dst_buf)
+               v4l2_m2m_buf_done(dst_buf, state);
+
+       v4l2_m2m_job_finish(ctx->codec->m2m_dev, ctx->fh.m2m_ctx);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_job_finish);
+
+/**
+ * v4l2_m2m_codec_request_validate() - Validate a media request
+ * @req: the media request to validate
+ *
+ * This function makes sure there's at least one src buffer attached to the
+ * request and checks that all per-request+mandatory controls have been set.
+ * This helper can be used to implement media_device_ops->req_validate() op.
+ */
+int v4l2_m2m_codec_request_validate(struct media_request *req)
+{
+       const struct v4l2_m2m_codec_ctrls *ctrls;
+       struct v4l2_m2m_codec_ctx *ctx;
+       struct v4l2_ctrl_handler *hdl;
+       struct vb2_buffer *vb;
+       unsigned int count;
+       unsigned int i;
+
+       vb = vb2_request_get_buf(req, 0);
+       if (!vb)
+               return -ENOENT;
+
+       ctx = vb2_get_drv_priv(vb->vb2_queue);
+       if (!ctx)
+               return -EINVAL;
+
+       count = vb2_request_buffer_cnt(req);
+       if (!count)
+               return -ENOENT;
+       else if (count > 1)
+               return -EINVAL;
+
+       hdl = v4l2_ctrl_request_hdl_find(req, &ctx->ctrl_hdl);
+       if (!hdl)
+               return -ENOENT;
+
+       ctrls = ctx->coded_fmt_desc->ctrls;
+       for (i = 0; ctrls && i < ctrls->num_ctrls; i++) {
+               u32 id = ctrls->ctrls[i].cfg.id;
+               struct v4l2_ctrl *ctrl;
+
+               if (!ctrls->ctrls[i].per_request || !ctrls->ctrls[i].mandatory)
+                       continue;
+
+               ctrl = v4l2_ctrl_request_hdl_ctrl_find(hdl, id);
+               if (!ctrl)
+                       return -ENOENT;
+       }
+
+       v4l2_ctrl_request_hdl_put(hdl);
+
+       return vb2_request_validate(req);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_request_validate);
+
+/**
+ * v4l2_m2m_codec_find_coded_fmt_desc() - Search for a supported coded format
+ *                                       based on its 4CC
+ * @codec: codec to search the coded format on
+ * @fourcc: the 4CC representing this format
+ *
+ * Return: A coded format desc if a matching format was found, NULL otherwise.
+ */
+const struct v4l2_m2m_codec_coded_fmt_desc *
+v4l2_m2m_codec_find_coded_fmt_desc(struct v4l2_m2m_codec *codec, u32 fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < codec->caps->num_coded_fmts; i++) {
+               if (codec->caps->coded_fmts[i].fourcc == fourcc)
+                       return &codec->caps->coded_fmts[i];
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_find_coded_fmt_desc);
+
+/**
+ * v4l2_m2m_codec_enum_framesizes() - Enumerate frame sizes helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @fsize: framesize object
+ *
+ * This helper can be used to implement ioctl_ops->vidioc_enum_framesizes().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_enum_framesizes(struct file *file, void *priv,
+                                  struct v4l2_frmsizeenum *fsize)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_m2m_codec *codec = vdev_to_v4l2_m2m_codec(vdev);
+       const struct v4l2_m2m_codec_coded_fmt_desc *fmt;
+
+       if (fsize->index != 0)
+               return -EINVAL;
+
+       fmt = v4l2_m2m_codec_find_coded_fmt_desc(codec, fsize->pixel_format);
+       if (!fmt)
+               return -EINVAL;
+
+       if (!fmt->frmsize)
+               return -EINVAL;
+
+       fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+       fsize->stepwise = *fmt->frmsize;
+       return 0;
+
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_enum_framesizes);
+
+static int v4l2_m2m_codec_enum_coded_fmt(struct file *file, void *priv,
+                                        struct v4l2_fmtdesc *f)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_m2m_codec *codec = vdev_to_v4l2_m2m_codec(vdev);
+
+       if (f->index >= codec->caps->num_coded_fmts)
+               return -EINVAL;
+
+       f->pixelformat = codec->caps->coded_fmts[f->index].fourcc;
+       return 0;
+}
+
+static int v4l2_m2m_codec_enum_decoded_fmt(struct file *file, void *priv,
+                                          struct v4l2_fmtdesc *f)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_m2m_codec *codec = vdev_to_v4l2_m2m_codec(vdev);
+
+       if (f->index >= codec->caps->num_decoded_fmts)
+               return -EINVAL;
+
+       f->pixelformat = codec->caps->decoded_fmts[f->index].fourcc;
+       return 0;
+}
+
+/**
+ * v4l2_m2m_codec_enum_output_fmt() - Enumerate output formats helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format descriptor
+ *
+ * This helper can be used to implement ioctl_ops->vidioc_enum_fmt_vid_out().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_enum_output_fmt(struct file *file, void *priv,
+                                  struct v4l2_fmtdesc *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+
+       if (ctx->codec->type == V4L2_M2M_DECODER)
+               return v4l2_m2m_codec_enum_coded_fmt(file, priv, f);
+
+       return v4l2_m2m_codec_enum_decoded_fmt(file, priv, f);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_enum_output_fmt);
+
+/**
+ * v4l2_m2m_codec_enum_output_fmt() - Enumerate capture formats helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format descriptor
+ *
+ * This helper can be used to implement ioctl_ops->vidioc_enum_fmt_vid_cap().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_enum_capture_fmt(struct file *file, void *priv,
+                                   struct v4l2_fmtdesc *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+
+       if (ctx->codec->type == V4L2_M2M_DECODER)
+               return v4l2_m2m_codec_enum_decoded_fmt(file, priv, f);
+
+       return v4l2_m2m_codec_enum_coded_fmt(file, priv, f);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_enum_capture_fmt);
+
+/**
+ * v4l2_m2m_codec_g_output_fmt() - Get output format helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format object
+ *
+ * This helper can be used to implement
+ * ioctl_ops->vidioc_g_fmt_vid_out[_mplane]().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_g_output_fmt(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+
+       if (ctx->codec->type == V4L2_M2M_DECODER)
+               *f = ctx->coded_fmt;
+       else
+               *f = ctx->decoded_fmt;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_g_output_fmt);
+
+/**
+ * v4l2_m2m_codec_g_capture_fmt() - Get capture format helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format object
+ *
+ * This helper can be used to implement
+ * ioctl_ops->vidioc_g_fmt_vid_cap[_mplane]().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_g_capture_fmt(struct file *file, void *priv,
+                                struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+
+       if (ctx->codec->type == V4L2_M2M_DECODER)
+               *f = ctx->decoded_fmt;
+       else
+               *f = ctx->coded_fmt;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_g_capture_fmt);
+
+static void
+v4l2_m2m_codec_apply_frmsize_constraints(struct v4l2_format *f,
+                               const struct v4l2_frmsize_stepwise *frmsize)
+{
+       u32 *width, *height;
+
+       if (!frmsize)
+               return;
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+               width = &f->fmt.pix.width;
+                       height = &f->fmt.pix.height;
+       } else {
+               width = &f->fmt.pix_mp.width;
+                       height = &f->fmt.pix_mp.height;
+       }
+
+       v4l2_apply_frmsize_constraints(width, height, frmsize);
+}
+
+static int v4l2_m2m_codec_try_coded_fmt(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+       const struct v4l2_m2m_codec_coded_fmt_desc *desc;
+       struct v4l2_m2m_codec *codec = ctx->codec;
+       u32 fourcc;
+       int ret;
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type))
+               fourcc = f->fmt.pix.pixelformat;
+       else
+               fourcc = f->fmt.pix_mp.pixelformat;
+
+       desc = v4l2_m2m_codec_find_coded_fmt_desc(codec, fourcc);
+       if (!desc)
+               return -EINVAL;
+
+       v4l2_m2m_codec_apply_frmsize_constraints(f, desc->frmsize);
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+               f->fmt.pix.field = V4L2_FIELD_NONE;
+       } else {
+               f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+               /* All coded formats are considered single planar for now. */
+               f->fmt.pix_mp.num_planes = 1;
+       }
+
+       if (desc->ops->adjust_fmt) {
+               ret = desc->ops->adjust_fmt(ctx, f);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int v4l2_m2m_codec_try_decoded_fmt(struct file *file, void *priv,
+                                         struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+       const struct v4l2_m2m_codec_coded_fmt_desc *coded_desc;
+       struct v4l2_m2m_codec *codec = ctx->codec;
+       unsigned int i;
+       u32 fourcc;
+
+       /*
+        * The codec context should point to a coded format desc, if the format
+        * on the coded end has not been set yet, it should point to the
+        * default value.
+        */
+       coded_desc = ctx->coded_fmt_desc;
+       if (WARN_ON(!coded_desc))
+               return -EINVAL;
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type))
+               fourcc = f->fmt.pix.pixelformat;
+       else
+               fourcc = f->fmt.pix_mp.pixelformat;
+
+       for (i = 0; i < codec->caps->num_decoded_fmts; i++) {
+               if (codec->caps->decoded_fmts[i].fourcc == fourcc)
+                       break;
+       }
+
+       if (i == codec->caps->num_decoded_fmts)
+               return -EINVAL;
+
+       /* Always apply the frmsize constraint of the coded end. */
+       v4l2_m2m_codec_apply_frmsize_constraints(f, coded_desc->frmsize);
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+               v4l2_fill_pixfmt(&f->fmt.pix, fourcc, f->fmt.pix.width,
+                                f->fmt.pix.height);
+               f->fmt.pix.field = V4L2_FIELD_NONE;
+       } else {
+               v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, fourcc, f->fmt.pix_mp.width,
+                                   f->fmt.pix_mp.height);
+               f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+       }
+
+       return 0;
+}
+
+/**
+ * v4l2_m2m_codec_try_output_fmt() - Try output format helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format object
+ *
+ * This helper can be used to implement
+ * ioctl_ops->vidioc_try_fmt_vid_out[_mplane]().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_try_output_fmt(struct file *file, void *priv,
+                                 struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+       
+       if (ctx->codec->type == V4L2_M2M_DECODER)
+               return v4l2_m2m_codec_try_coded_fmt(file, priv, f);
+
+       return v4l2_m2m_codec_try_decoded_fmt(file, priv, f);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_try_output_fmt);
+
+/**
+ * v4l2_m2m_codec_try_capture_fmt() - Try capture format helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format object
+ *
+ * This helper can be used to implement
+ * ioctl_ops->vidioc_try_fmt_vid_cap[_mplane]().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_try_capture_fmt(struct file *file, void *priv,
+                                  struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+       
+       if (ctx->codec->type == V4L2_M2M_DECODER)
+               return v4l2_m2m_codec_try_decoded_fmt(file, priv, f);
+
+       return v4l2_m2m_codec_try_coded_fmt(file, priv, f);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_try_capture_fmt);
+
+static int v4l2_m2m_codec_s_fmt(struct file *file, void *priv,
+                               struct v4l2_format *f,
+                               int (*try_fmt)(struct file *, void *,
+                                              struct v4l2_format *))
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+       struct vb2_queue *vq;
+       int ret;
+
+       if (!try_fmt)
+               return -EINVAL;
+
+       vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+       if (vb2_is_busy(vq))
+               return -EBUSY;
+
+       ret = try_fmt(file, priv, f);
+       if (ret)
+               return ret;
+
+       if (V4L2_TYPE_IS_OUTPUT(f->type) == 
+           (ctx->codec->type == V4L2_M2M_DECODER)) {
+               struct v4l2_m2m_ctx *m2m_ctx = v4l2_m2m_codec_get_m2m_ctx(ctx);
+               const struct v4l2_m2m_codec_coded_fmt_desc *desc;
+               u32 fourcc;
+
+               if (!V4L2_TYPE_IS_MULTIPLANAR(f->type))
+                       fourcc = f->fmt.pix.pixelformat;
+               else
+                       fourcc = f->fmt.pix_mp.pixelformat;
+
+               desc = v4l2_m2m_codec_find_coded_fmt_desc(ctx->codec, fourcc);
+               if (!desc)
+                       return -EINVAL;
+
+               ctx->coded_fmt_desc = desc;
+               m2m_ctx->out_q_ctx.q.requires_requests = 
desc->requires_requests;
+       }
+
+       return 0;
+}
+
+/**
+ * v4l2_m2m_codec_s_output_fmt() - Set output format helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format object
+ *
+ * This helper can be used to implement
+ * ioctl_ops->vidioc_s_fmt_vid_out[_mplane]().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_s_output_fmt(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+       struct video_device *vfd = video_devdata(file);
+       const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
+       struct v4l2_format *cap_fmt;
+       int ret;
+
+       ret = v4l2_m2m_codec_s_fmt(file, priv, f,
+                                  V4L2_TYPE_IS_MULTIPLANAR(f->type) ?
+                                  ops->vidioc_try_fmt_vid_out_mplane :
+                                  ops->vidioc_try_fmt_vid_out);
+       if (ret)
+               return ret;
+
+       if (ctx->codec->type == V4L2_M2M_DECODER) {
+               ctx->coded_fmt = *f;
+               cap_fmt = &ctx->decoded_fmt;
+       } else {
+               ctx->decoded_fmt = *f;
+               cap_fmt = &ctx->coded_fmt;
+       }
+
+       /* Propagate colorspace information to capture. */
+       if (V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+               cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+               cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+               cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+               cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+       } else {
+               cap_fmt->fmt.pix.colorspace = f->fmt.pix.colorspace;
+               cap_fmt->fmt.pix.xfer_func = f->fmt.pix.xfer_func;
+               cap_fmt->fmt.pix.ycbcr_enc = f->fmt.pix.ycbcr_enc;
+               cap_fmt->fmt.pix.quantization = f->fmt.pix.quantization;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_s_output_fmt);
+
+/**
+ * v4l2_m2m_codec_s_capture_fmt() - Set capture format helper
+ * @file: opened file descriptor
+ * @priv: private data
+ * @f: format object
+ *
+ * This helper can be used to implement
+ * ioctl_ops->vidioc_s_fmt_vid_cap[_mplane]().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_s_capture_fmt(struct file *file, void *priv,
+                                struct v4l2_format *f)
+{
+       struct v4l2_m2m_codec_ctx *ctx = fh_to_v4l2_m2m_codec_ctx(priv);
+       struct video_device *vfd = video_devdata(file);
+       const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
+       int ret;
+
+       ret = v4l2_m2m_codec_s_fmt(file, priv, f,
+                                  V4L2_TYPE_IS_MULTIPLANAR(f->type) ?
+                                  ops->vidioc_try_fmt_vid_cap_mplane:
+                                  ops->vidioc_try_fmt_vid_cap);
+       if (ret)
+               return ret;
+
+       if (ctx->codec->type == V4L2_M2M_DECODER)
+               ctx->decoded_fmt = *f;
+       else
+               ctx->coded_fmt = *f;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_s_capture_fmt);
+
+/**
+ * v4l2_m2m_codec_queue_setup() - Queue setup helper
+ * @vq: the VB2 queue to setup
+ * @num_buffers: the number of buffers to allocate
+ * @num_planes: the number of planes per image
+ * @sizes: the plane sizes
+ * @alloc_devs: the device to use to allocate each plane
+ *
+ * This helper can be used to implement vb2_ops->queue_setup().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+                              unsigned int *num_planes, unsigned int sizes[],
+                              struct device *alloc_devs[])
+{
+       struct v4l2_m2m_codec_ctx *ctx = vb2_get_drv_priv(vq);
+       struct v4l2_format *f;
+       int i;
+
+       if (V4L2_TYPE_IS_OUTPUT(vq->type) == 
+           (ctx->codec->type == V4L2_M2M_DECODER))
+               f = &ctx->coded_fmt;
+       else
+               f = &ctx->decoded_fmt;
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+               if (*num_planes) {
+                       if (*num_planes != 1)
+                               return -EINVAL;
+
+                       if (sizes[0] < f->fmt.pix.sizeimage)
+                               return -EINVAL;
+               } else {
+                       sizes[0] = f->fmt.pix.sizeimage;
+               }
+
+               return 0;
+       }
+
+       if (*num_planes) {
+               if (*num_planes != f->fmt.pix_mp.num_planes)
+                       return -EINVAL;
+
+               for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+                       if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage)
+                               return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       *num_planes = f->fmt.pix_mp.num_planes;
+       for (i = 0; i < f->fmt.pix_mp.num_planes; i++)
+               sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_queue_setup);
+
+/**
+ * v4l2_m2m_codec_queue_cleanup() - Queue cleanup helper
+ * @vq: the VB2 queue to cleanup
+ * @state: the sate to assigned to released bufs
+ *
+ * This helper can be used in the vb2_ops->stop_streaming() implementation
+ * to release buffers (and associated requests) bound to a queue.
+ */
+void v4l2_m2m_codec_queue_cleanup(struct vb2_queue *vq, u32 state)
+{
+       struct v4l2_m2m_codec_ctx *ctx = vb2_get_drv_priv(vq);
+
+       while (true) {
+               struct vb2_v4l2_buffer *vbuf;
+
+               if (V4L2_TYPE_IS_OUTPUT(vq->type))
+                       vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+               else
+                       vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+               if (!vbuf)
+                       break;
+
+               v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
+                                          &ctx->ctrl_hdl);
+               v4l2_m2m_buf_done(vbuf, state);
+       }
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_queue_cleanup);
+
+/**
+ * v4l2_m2m_codec_buf_out_validate() - Validate output buffer helper
+ * @vb: the VB2 buffer to validate
+ *
+ * This helper can be used to implement vb2_ops->buf_out_validate().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_buf_out_validate(struct vb2_buffer *vb)
+{
+       struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+       vbuf->field = V4L2_FIELD_NONE;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_buf_out_validate);
+
+/**
+ * v4l2_m2m_codec_buf_prepare() - Prepare buffer helper
+ * @vb: the VB2 buffer to prepare
+ *
+ * This helper can be used to implement vb2_ops->buf_prepare().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct v4l2_m2m_codec_ctx *ctx = vb2_get_drv_priv(vq);
+       struct v4l2_format *f;
+       int i;
+
+       if (V4L2_TYPE_IS_OUTPUT(vq->type) ==
+           (ctx->codec->type == V4L2_M2M_DECODER))
+               f = &ctx->coded_fmt;
+       else
+               f = &ctx->decoded_fmt;
+
+       if (!V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+               if (vb2_plane_size(vb, 0) < f->fmt.pix.sizeimage)
+                       return -EINVAL;
+
+               return 0;
+       }
+
+       for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) {
+               if (vb2_plane_size(vb, i) < 
f->fmt.pix_mp.plane_fmt[i].sizeimage)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_buf_prepare);
+
+/**
+ * v4l2_m2m_codec_buf_queue() - Queue buffer helper
+ * @vb: the VB2 buffer to queue
+ *
+ * This helper can be used to implement vb2_ops->buf_queue().
+ */
+void v4l2_m2m_codec_buf_queue(struct vb2_buffer *vb)
+{
+       struct v4l2_m2m_codec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+       v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_buf_queue);
+
+/**
+ * v4l2_m2m_codec_buf_queue() - Complete buffer request helper
+ * @vb: the VB2 buffer to complete request on
+ *
+ * This helper can be used to implement vb2_ops->buf_request_complete().
+ */
+void v4l2_m2m_codec_buf_request_complete(struct vb2_buffer *vb)
+{
+       struct v4l2_m2m_codec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+       v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_buf_request_complete);
+
+/**
+ * v4l2_m2m_codec_start_streaming() - Start streaming helper
+ * @q: the VB2 queue to start streaming on
+ *
+ * This helper can be used to implement vb2_ops->start_streaming().
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct v4l2_m2m_codec_ctx *ctx = vb2_get_drv_priv(q);
+       const struct v4l2_m2m_codec_coded_fmt_desc *desc;
+       struct v4l2_m2m_codec *codec = ctx->codec;
+       int ret;
+
+       if ((codec->type == V4L2_M2M_DECODER) != V4L2_TYPE_IS_OUTPUT(q->type))
+               return 0;
+
+       desc = ctx->coded_fmt_desc;
+       if (WARN_ON(!desc))
+               return -EINVAL;
+
+       if (desc->ops->start) {
+               ret = desc->ops->start(ctx);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_start_streaming);
+
+/**
+ * v4l2_m2m_codec_stop_streaming() - Stop streaming helper
+ * @q: the VB2 queue to stop streaming on
+ *
+ * This helper can be used to implement vb2_ops->stop_streaming().
+ */
+void v4l2_m2m_codec_stop_streaming(struct vb2_queue *q)
+{
+       struct v4l2_m2m_codec_ctx *ctx = vb2_get_drv_priv(q);
+       const struct v4l2_m2m_codec_coded_fmt_desc *desc;
+       struct v4l2_m2m_codec *codec = ctx->codec;
+
+       if ((codec->type == V4L2_M2M_DECODER) == V4L2_TYPE_IS_OUTPUT(q->type)) {
+               desc = ctx->coded_fmt_desc;
+               if (WARN_ON(!desc))
+                       return;
+
+               if (desc->ops->stop)
+                       desc->ops->stop(ctx);
+       }
+
+       v4l2_m2m_codec_queue_cleanup(q, VB2_BUF_STATE_ERROR);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_stop_streaming);
+
+/**
+ * v4l2_m2m_codec_device_run() - Device run helper
+ * @ctx: the codec context this run happens on
+ *
+ * This helper can be used to implement v4l2_m2m_ops->device_run(). It just
+ * calls the ->run() method of the selected coded format.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int v4l2_m2m_codec_device_run(struct v4l2_m2m_codec_ctx *ctx)
+{
+       const struct v4l2_m2m_codec_coded_fmt_desc *desc;
+
+       desc = ctx->coded_fmt_desc;
+       if (WARN_ON(!desc))
+               return -EINVAL;
+
+       desc->ops->run(ctx);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_codec_device_run);
diff --git a/include/media/v4l2-mem2mem-codec.h 
b/include/media/v4l2-mem2mem-codec.h
new file mode 100644
index 000000000000..2c3e21d53f9e
--- /dev/null
+++ b/include/media/v4l2-mem2mem-codec.h
@@ -0,0 +1,317 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Memory-to-memory codec framework for Video for Linux 2.
+ *
+ * Helper functions for codec devices that use memory buffers for both source
+ * and destination.
+ *
+ * Copyright (c) 2019 Collabora Ltd.
+ *
+ * Author:
+ *     Boris Brezillon <boris.brezil...@collabora.com>
+ */
+
+#ifndef _MEDIA_V4L2_MEM2MEM_CODEC_H
+#define _MEDIA_V4L2_MEM2MEM_CODEC_H
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mem2mem.h>
+
+struct v4l2_m2m_codec_ctx;
+
+/**
+ * struct v4l2_m2m_codec_ctrl_desc - Codec control description
+ * @per_request: set to true if the control is expected to be set everytime a
+ *              decoding/encoding request is queued
+ * @mandatory: set to true if the control is mandatory
+ * @cfg: control configuration
+ */
+struct v4l2_m2m_codec_ctrl_desc {
+       u32 per_request : 1;
+       u32 mandatory : 1;
+       struct v4l2_ctrl_config cfg;
+};
+
+/**
+ * struct v4l2_m2m_codec_ctrls - Codec controls
+ * @ctrls: array of control descriptions
+ * @num_ctrls: size of the ctrls array
+ *
+ * Structure used to declare codec specific controls.
+ */
+struct v4l2_m2m_codec_ctrls {
+       const struct v4l2_m2m_codec_ctrl_desc *ctrls;
+       unsigned int num_ctrls;
+};
+
+#define V4L2_M2M_CODEC_CTRLS(_name, ...)                                       
        \
+       struct v4l2_m2m_codec_ctrls _name = {                                   
        \
+               .ctrls = (const struct 
v4l2_m2m_codec_ctrl_desc[]){__VA_ARGS__},        \
+               .num_ctrls = sizeof((struct 
v4l2_m2m_codec_ctrl_desc[]){__VA_ARGS__}) / \
+                            sizeof(struct v4l2_m2m_codec_ctrl_desc),           
        \
+       }
+
+/**
+ * struct v4l2_m2m_codec_decoded_fmt_desc - Decoded format description
+ * @fourcc: the 4CC code of the decoded format
+ * @priv: driver private data to associate to this decoded format
+ *
+ * Structure used to describe decoded formats.
+ */
+struct v4l2_m2m_codec_decoded_fmt_desc {
+       u32 fourcc;
+       const void *priv;
+};
+
+/**
+ * struct v4l2_m2m_codec_coded_fmt_ops - Coded format methods
+ * @adjust_fmt: adjust a coded format before passing it back to userspace.
+ *             Particularly useful when one wants to tweak any of the
+ *             params set by the core (sizeimage, width, height, ...)
+ * @start: called when vb2_ops->start_streaming() is called. Any coded-format
+ *        specific context initialization should happen here
+ * @stop: called when vb2_ops->stop_streaming() is called. Any coded-format
+ *       specific context cleanup should happen here
+ * @run: called when v4l2_m2m_ops->device_run() is called. This method should
+ *      issue the encoding/decoding request
+ */
+struct v4l2_m2m_codec_coded_fmt_ops {
+       int (*adjust_fmt)(struct v4l2_m2m_codec_ctx *ctx,
+                         struct v4l2_format *f);
+       int (*start)(struct v4l2_m2m_codec_ctx *ctx);
+       void (*stop)(struct v4l2_m2m_codec_ctx *ctx);
+       int (*run)(struct v4l2_m2m_codec_ctx *ctx);
+};
+
+/**
+ * struct v4l2_m2m_codec_coded_fmt_desc - Coded format description
+ * @fourcc: 4CC code describing this coded format
+ * @requires_request: set to true if the codec requires a media request object
+ *                   to process encoding/decoding requests
+ * @frmsize: frame size constraint. Can be NULL if the codec does not have any
+ *          alignment/min/max size constraints for this coded format
+ * @ctrls: controls attached to this coded format
+ * @ops: coded format ops
+ * @priv: driver private data
+ */
+struct v4l2_m2m_codec_coded_fmt_desc {
+       u32 fourcc;
+       u32 requires_requests : 1;
+       const struct v4l2_frmsize_stepwise *frmsize;
+       const struct v4l2_m2m_codec_ctrls *ctrls;
+       const struct v4l2_m2m_codec_coded_fmt_ops *ops;
+       const void *priv;
+};
+
+#define V4L2_M2M_CODEC_CODED_FMTS(_fmt_array)          \
+       .num_coded_fmts = ARRAY_SIZE(_fmt_array),       \
+       .coded_fmts = _fmt_array
+
+#define V4L2_M2M_CODEC_DECODED_FMTS(_fmt_array)                \
+       .num_decoded_fmts = ARRAY_SIZE(_fmt_array),     \
+       .decoded_fmts = _fmt_array
+
+/**
+ * struct v4l2_m2m_codec_caps - Codec capabilities
+ * @coded_fmts: array of supported coded formats
+ * @num_coded_fmts: size of the coded_fmts array
+ * @decoded_fmts: array of supported decoded formats
+ * @num_decoded_fmts: size of the decoded_fmts array
+ *
+ * This structure is describing the formats supported by the codec.
+ */
+struct v4l2_m2m_codec_caps {
+       const struct v4l2_m2m_codec_coded_fmt_desc *coded_fmts;
+       unsigned int num_coded_fmts;
+       const struct v4l2_m2m_codec_decoded_fmt_desc *decoded_fmts;
+       unsigned int num_decoded_fmts;
+};
+
+/**
+ * enum v4l2_m2m_codec_type - Codec type
+ * @V4L2_M2M_ENCODER: encoder
+ * @V4L2_M2M_DECODER: decoder
+ */
+enum v4l2_m2m_codec_type {
+       V4L2_M2M_ENCODER,
+       V4L2_M2M_DECODER,
+};
+
+/**
+ * struct v4l2_m2m_codec_ops - Codec methods
+ * @queue_init: called by the v4l2_m2m_codec_queue_init() helper to let the
+ *             driver initialize the src/dst queues
+ */
+struct v4l2_m2m_codec_ops {
+       int (*queue_init)(struct v4l2_m2m_codec_ctx *ctx,
+                         struct vb2_queue *src_vq,
+                         struct vb2_queue *dst_vq);
+};
+
+/**
+ * struct v4l2_m2m_codec - Codec object
+ * @vdev: video device exposed by the codec
+ * @type: type of codec
+ * @m2m_dev: m2m device this codec is attached to
+ * @caps: codec capabilities
+ * @ops: codec operations
+ */
+struct v4l2_m2m_codec {
+       struct video_device vdev;
+       enum v4l2_m2m_codec_type type;
+       struct v4l2_m2m_dev *m2m_dev;
+       const struct v4l2_m2m_codec_caps *caps;
+       const struct v4l2_m2m_codec_ops *ops;
+};
+
+static inline struct v4l2_m2m_codec *
+vdev_to_v4l2_m2m_codec(struct video_device *vdev)
+{
+       return container_of(vdev, struct v4l2_m2m_codec, vdev);
+}
+
+static inline struct video_device *
+v4l2_m2m_codec_to_vdev(struct v4l2_m2m_codec *codec)
+{
+       return &codec->vdev;
+}
+
+static inline enum v4l2_m2m_codec_type
+v4l2_m2m_codec_get_type(const struct v4l2_m2m_codec *codec)
+{
+       return codec->type;
+}
+
+/**
+ * struct v4l2_m2m_codec_ctx - Codec context
+ * @fh: file handle
+ * @coded_fmt: current coded format
+ * @decoded_fmt: current decoded format
+ * @coded_fmt_desc: current coded format desc
+ * @decoded_fmt_desc: current decoded format desc
+ * @ctrl_hdl: control handler
+ * @codec: the codec that has created this context
+ */
+struct v4l2_m2m_codec_ctx {
+       struct v4l2_fh fh;
+       struct v4l2_format coded_fmt;
+       struct v4l2_format decoded_fmt;
+       const struct v4l2_m2m_codec_coded_fmt_desc *coded_fmt_desc;
+       const struct v4l2_m2m_codec_decoded_fmt_desc *decoded_fmt_desc;
+       struct v4l2_ctrl_handler ctrl_hdl;
+       struct v4l2_m2m_codec *codec;
+};
+
+static inline struct v4l2_m2m_codec_ctx *
+fh_to_v4l2_m2m_codec_ctx(struct v4l2_fh *fh)
+{
+       return container_of(fh, struct v4l2_m2m_codec_ctx, fh);
+}
+
+static inline struct v4l2_m2m_codec_ctx *
+file_to_v4l2_m2m_codec_ctx(struct file *file)
+{
+       return fh_to_v4l2_m2m_codec_ctx(file->private_data);
+}
+
+static inline struct v4l2_m2m_ctx *
+v4l2_m2m_codec_get_m2m_ctx(struct v4l2_m2m_codec_ctx *ctx)
+{
+       return ctx->fh.m2m_ctx;
+}
+
+static inline struct v4l2_ctrl_handler *
+v4l2_m2m_codec_get_ctrl_handler(struct v4l2_m2m_codec_ctx *ctx)
+{
+       return &ctx->ctrl_hdl;
+}
+
+struct v4l2_m2m_codec_run {
+       struct {
+               struct vb2_v4l2_buffer *src;
+               struct vb2_v4l2_buffer *dst;
+       } bufs;
+};
+
+int v4l2_m2m_codec_init(struct v4l2_m2m_codec *codec,
+                       enum v4l2_m2m_codec_type type,
+                       struct v4l2_m2m_dev *m2m_dev,
+                       struct v4l2_device *v4l2_dev,
+                       const struct v4l2_m2m_codec_caps *caps,
+                       const struct v4l2_m2m_codec_ops *ops,
+                       const struct v4l2_file_operations *vdev_fops,
+                       const struct v4l2_ioctl_ops *vdev_ioctl_ops,
+                       struct mutex *lock, const char *name, void *drvdata);
+int v4l2_m2m_codec_ctx_init(struct v4l2_m2m_codec_ctx *ctx, struct file *file,
+                           struct v4l2_m2m_codec *codec);
+void v4l2_m2m_codec_ctx_cleanup(struct v4l2_m2m_codec_ctx *ctx);
+void v4l2_m2m_codec_run_preamble(struct v4l2_m2m_codec_ctx *ctx,
+                                struct v4l2_m2m_codec_run *run);
+void v4l2_m2m_codec_run_postamble(struct v4l2_m2m_codec_ctx *ctx,
+                                 struct v4l2_m2m_codec_run *run);
+void v4l2_m2m_codec_job_finish(struct v4l2_m2m_codec_ctx *ctx,
+                              enum vb2_buffer_state state);
+
+static inline const struct v4l2_format *
+v4l2_m2m_codec_get_coded_fmt(struct v4l2_m2m_codec_ctx *ctx)
+{
+       return &ctx->coded_fmt;
+}
+
+static inline const struct v4l2_m2m_codec_coded_fmt_desc *
+v4l2_m2m_codec_get_coded_fmt_desc(struct v4l2_m2m_codec_ctx *ctx)
+{
+       return ctx->coded_fmt_desc;
+}
+
+static inline const struct v4l2_format *
+v4l2_m2m_codec_get_decoded_fmt(struct v4l2_m2m_codec_ctx *ctx)
+{
+       return &ctx->decoded_fmt;
+}
+
+static inline const struct v4l2_m2m_codec_decoded_fmt_desc *
+v4l2_m2m_codec_get_decoded_fmt_desc(struct v4l2_m2m_codec_ctx *ctx)
+{
+       return ctx->decoded_fmt_desc;
+}
+
+void v4l2_m2m_codec_reset_decoded_fmt(struct v4l2_m2m_codec_ctx *ctx);
+const struct v4l2_m2m_codec_coded_fmt_desc *
+v4l2_m2m_codec_find_coded_fmt_desc(struct v4l2_m2m_codec *codec, u32 fourcc);
+int v4l2_m2m_codec_enum_framesizes(struct file *file, void *priv,
+                                  struct v4l2_frmsizeenum *fsize);
+int v4l2_m2m_codec_enum_output_fmt(struct file *file, void *priv,
+                                  struct v4l2_fmtdesc *f);
+int v4l2_m2m_codec_enum_capture_fmt(struct file *file, void *priv,
+                                   struct v4l2_fmtdesc *f);
+int v4l2_m2m_codec_g_output_fmt(struct file *file, void *priv,
+                               struct v4l2_format *f);
+int v4l2_m2m_codec_g_capture_fmt(struct file *file, void *priv,
+                                struct v4l2_format *f);
+int v4l2_m2m_codec_try_output_fmt(struct file *file, void *priv,
+                                 struct v4l2_format *f);
+int v4l2_m2m_codec_try_capture_fmt(struct file *file, void *priv,
+                                  struct v4l2_format *f);
+int v4l2_m2m_codec_s_output_fmt(struct file *file, void *priv,
+                               struct v4l2_format *f);
+int v4l2_m2m_codec_s_capture_fmt(struct file *file, void *priv,
+                                struct v4l2_format *f);
+
+int v4l2_m2m_codec_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+                              unsigned int *num_planes, unsigned int sizes[],
+                              struct device *alloc_devs[]);
+void v4l2_m2m_codec_queue_cleanup(struct vb2_queue *vq, u32 state);
+int v4l2_m2m_codec_buf_out_validate(struct vb2_buffer *vb);
+int v4l2_m2m_codec_buf_prepare(struct vb2_buffer *vb);
+void v4l2_m2m_codec_buf_queue(struct vb2_buffer *vb);
+void v4l2_m2m_codec_buf_request_complete(struct vb2_buffer *vb);
+int v4l2_m2m_codec_start_streaming(struct vb2_queue *q, unsigned int count);
+void v4l2_m2m_codec_stop_streaming(struct vb2_queue *q);
+
+int v4l2_m2m_codec_request_validate(struct media_request *req);
+int v4l2_m2m_codec_device_run(struct v4l2_m2m_codec_ctx *ctx);
+
+#endif /* _MEDIA_V4L2_MEM2MEM_CODEC_H */
-- 
2.21.0

Reply via email to