CAN, GPIO, RNG vhost-devices for virtio-loopback [v6]
[AGL/meta-agl-devel.git] / meta-egvirt / recipes-kernel / kernel-module-virtio-video / files / virtio_video_device.c
diff --git a/meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_device.c b/meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_device.c
deleted file mode 100644 (file)
index 2e1f90a..0000000
+++ /dev/null
@@ -1,1282 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* Driver for virtio video device.
- *
- * Copyright 2020 OpenSynergy GmbH.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/version.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-dma-sg.h>
-
-#include "virtio_video.h"
-
-enum video_stream_state virtio_video_state(struct virtio_video_stream *stream)
-{
-       return atomic_read(&stream->state);
-}
-
-void virtio_video_state_reset(struct virtio_video_stream *stream)
-{
-       atomic_set(&stream->state, STREAM_STATE_IDLE);
-}
-
-void virtio_video_state_update(struct virtio_video_stream *stream,
-                              enum video_stream_state new_state)
-{
-       enum video_stream_state prev_state;
-
-       do {
-           prev_state = atomic_read(&stream->state);
-           if (prev_state == STREAM_STATE_ERROR)
-                   return;
-       } while (atomic_cmpxchg(&stream->state, prev_state, new_state) !=
-                prev_state);
-}
-
-int virtio_video_pending_buf_list_empty(struct virtio_video_device *vvd)
-{
-       int ret = 0;
-
-       if (vvd->is_m2m_dev) {
-               v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
-               return -EPERM;
-       }
-
-       spin_lock(&vvd->pending_buf_list_lock);
-       if (list_empty(&vvd->pending_buf_list))
-               ret = 1;
-       spin_unlock(&vvd->pending_buf_list_lock);
-
-       return ret;
-}
-
-int virtio_video_pending_buf_list_pop(struct virtio_video_device *vvd,
-                                     struct virtio_video_buffer **virtio_vb)
-{
-       struct virtio_video_buffer *retbuf;
-
-       if (vvd->is_m2m_dev) {
-               v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
-               return -EPERM;
-       }
-
-       spin_lock(&vvd->pending_buf_list_lock);
-       if (list_empty(&vvd->pending_buf_list)) {
-               spin_unlock(&vvd->pending_buf_list_lock);
-               return -EAGAIN;
-       }
-
-       retbuf = list_first_entry(&vvd->pending_buf_list,
-                                 struct virtio_video_buffer, list);
-       spin_unlock(&vvd->pending_buf_list_lock);
-
-       *virtio_vb = retbuf;
-       return 0;
-}
-
-int virtio_video_pending_buf_list_add(struct virtio_video_device *vvd,
-                                     struct virtio_video_buffer *virtio_vb)
-{
-       if (vvd->is_m2m_dev) {
-               v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
-               return -EPERM;
-       }
-
-       spin_lock(&vvd->pending_buf_list_lock);
-       list_add_tail(&virtio_vb->list, &vvd->pending_buf_list);
-       spin_unlock(&vvd->pending_buf_list_lock);
-
-       return 0;
-}
-
-int virtio_video_pending_buf_list_del(struct virtio_video_device *vvd,
-                                     struct virtio_video_buffer *virtio_vb)
-{
-       struct virtio_video_buffer *vb, *vb_tmp;
-       int ret = -EINVAL;
-
-       if (vvd->is_m2m_dev) {
-               v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
-               return -EPERM;
-       }
-
-       spin_lock(&vvd->pending_buf_list_lock);
-       if (list_empty(&vvd->pending_buf_list)) {
-               spin_unlock(&vvd->pending_buf_list_lock);
-               return -EAGAIN;
-       }
-
-       list_for_each_entry_safe(vb, vb_tmp, &vvd->pending_buf_list, list) {
-               if (vb->resource_id == virtio_vb->resource_id) {
-                       list_del(&vb->list);
-                       ret = 0;
-                       break;
-               }
-       }
-       spin_unlock(&vvd->pending_buf_list_lock);
-
-       return ret;
-}
-
-int virtio_video_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
-                            unsigned int *num_planes, unsigned int sizes[],
-                            struct device *alloc_devs[])
-{
-       int i;
-       struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
-       struct video_format_info *p_info;
-
-       if (virtio_video_state(stream) == STREAM_STATE_ERROR)
-               return -EIO;
-
-       if (*num_planes)
-               return 0;
-
-       if (V4L2_TYPE_IS_OUTPUT(vq->type))
-               p_info = &stream->in_info;
-       else
-               p_info = &stream->out_info;
-
-       *num_planes = p_info->num_planes;
-
-       for (i = 0; i < p_info->num_planes; i++)
-               sizes[i] = p_info->plane_format[i].plane_size;
-
-       return 0;
-}
-
-static unsigned int
-build_virtio_video_sglist_contig(struct virtio_video_resource_sg_list *sgl,
-                                struct vb2_buffer *vb, unsigned int plane)
-{
-       sgl->entries[0].addr = cpu_to_le64(vb2_dma_contig_plane_dma_addr(vb, plane));
-       sgl->entries[0].length = cpu_to_le32(vb->planes[plane].length);
-
-       sgl->num_entries = 1;
-
-       return VIRTIO_VIDEO_RESOURCE_SG_SIZE(1);
-}
-
-static unsigned int
-build_virtio_video_sglist(struct virtio_video_resource_sg_list *sgl,
-                         struct vb2_buffer *vb, unsigned int plane,
-                         bool has_iommu)
-{
-       int i;
-       struct scatterlist *sg;
-       struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, plane);
-
-       for_each_sg(sgt->sgl, sg, sgt->nents, i) {
-               sgl->entries[i].addr = cpu_to_le64(has_iommu
-                                                       ? sg_dma_address(sg)
-                                                       : sg_phys(sg));
-               sgl->entries[i].length = cpu_to_le32(sg->length);
-       }
-
-       sgl->num_entries = sgt->nents;
-
-       return VIRTIO_VIDEO_RESOURCE_SG_SIZE(sgt->nents);
-}
-
-int virtio_video_buf_init(struct vb2_buffer *vb)
-{
-       int ret = 0;
-       void *buf;
-       size_t buf_size = 0;
-       struct virtio_video_resource_sg_list *sg_list;
-       unsigned int i, offset = 0, resource_id, nents = 0;
-       struct vb2_queue *vq = vb->vb2_queue;
-       enum v4l2_buf_type queue_type = vq->type;
-       struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
-       struct virtio_video_buffer *virtio_vb = to_virtio_vb(vb);
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-
-       if (vvd->supp_non_contig) {
-               for (i = 0; i < vb->num_planes; i++) {
-                       nents = vb2_dma_sg_plane_desc(vb, i)->nents;
-                       buf_size += VIRTIO_VIDEO_RESOURCE_SG_SIZE(nents);
-               }
-
-               buf = kcalloc(1, buf_size, GFP_KERNEL);
-               if (!buf)
-                       return -ENOMEM;
-
-               for (i = 0; i < vb->num_planes; i++) {
-                       sg_list = buf + offset;
-                       offset += build_virtio_video_sglist(sg_list, vb, i,
-                                                           vvd->has_iommu);
-               }
-       } else {
-               buf_size = vb->num_planes * VIRTIO_VIDEO_RESOURCE_SG_SIZE(nents);
-
-               buf = kcalloc(1, buf_size, GFP_KERNEL);
-               if (!buf)
-                       return -ENOMEM;
-
-               for (i = 0; i < vb->num_planes; i++) {
-                       sg_list = buf + offset;
-                       offset += build_virtio_video_sglist_contig(sg_list,
-                                                                  vb, i);
-               }
-       }
-
-       virtio_video_resource_id_get(vvd, &resource_id);
-
-       ret = virtio_video_cmd_resource_attach(vvd, stream->stream_id,
-                                              resource_id,
-                                              to_virtio_queue_type(queue_type),
-                                              buf, buf_size);
-       if (ret) {
-               virtio_video_resource_id_put(vvd, resource_id);
-               kfree(buf);
-               return ret;
-       }
-
-       virtio_vb->queued = false;
-       virtio_vb->resource_id = resource_id;
-
-       return 0;
-}
-
-void virtio_video_buf_cleanup(struct vb2_buffer *vb)
-{
-       struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
-       struct virtio_video_buffer *virtio_vb = to_virtio_vb(vb);
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-
-       virtio_video_resource_id_put(vvd, virtio_vb->resource_id);
-}
-
-void virtio_video_buf_queue(struct vb2_buffer *vb)
-{
-       int i, ret;
-       struct virtio_video_buffer *virtio_vb;
-       uint32_t data_size[VB2_MAX_PLANES] = {0};
-       struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-
-       for (i = 0; i < vb->num_planes; ++i)
-               data_size[i] = vb->planes[i].bytesused;
-
-       virtio_vb = to_virtio_vb(vb);
-
-       if (!vvd->is_m2m_dev)
-               virtio_video_pending_buf_list_add(vvd, virtio_vb);
-
-       ret = virtio_video_cmd_resource_queue(vvd, stream->stream_id,
-                                             virtio_vb, data_size,
-                                             vb->num_planes,
-                                             to_virtio_queue_type(vb->type));
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to queue buffer\n");
-               return;
-       }
-
-       virtio_vb->queued = true;
-}
-
-int virtio_video_qbuf(struct file *file, void *priv,
-                     struct v4l2_buffer *buf)
-{
-       struct virtio_video_stream *stream = file2stream(file);
-       struct virtio_video_device *vvd = video_drvdata(file);
-
-       if (virtio_video_state(stream) == STREAM_STATE_ERROR)
-               return -EIO;
-
-       if (vvd->is_m2m_dev)
-               return v4l2_m2m_ioctl_qbuf(file, priv, buf);
-
-       return vb2_ioctl_qbuf(file, priv, buf);
-}
-
-int virtio_video_dqbuf(struct file *file, void *priv,
-                      struct v4l2_buffer *buf)
-{
-       struct virtio_video_stream *stream = file2stream(file);
-       struct virtio_video_device *vvd = video_drvdata(file);
-
-       if (virtio_video_state(stream) == STREAM_STATE_ERROR)
-               return -EIO;
-
-       if (vvd->is_m2m_dev)
-               return v4l2_m2m_ioctl_dqbuf(file, priv, buf);
-
-       return vb2_ioctl_dqbuf(file, priv, buf);
-}
-
-int virtio_video_querycap(struct file *file, void *fh,
-                         struct v4l2_capability *cap)
-{
-       struct video_device *video_dev = video_devdata(file);
-       struct virtio_video_device *vvd = video_drvdata(file);
-
-       if (strscpy(cap->driver, DRIVER_NAME, sizeof(cap->driver)) < 0)
-               v4l2_err(&vvd->v4l2_dev, "failed to copy driver name\n");
-       if (strscpy(cap->card, video_dev->name, sizeof(cap->card)) < 0)
-               v4l2_err(&vvd->v4l2_dev, "failed to copy card name\n");
-
-       snprintf(cap->bus_info, sizeof(cap->bus_info), "virtio:%s",
-                video_dev->name);
-
-       cap->device_caps = video_dev->device_caps;
-       return 0;
-}
-
-int virtio_video_enum_framesizes(struct file *file, void *fh,
-                                struct v4l2_frmsizeenum *f)
-{
-       struct virtio_video_stream *stream = file2stream(file);
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-       struct video_format *fmt;
-       struct video_format_frame *frm;
-       struct virtio_video_format_frame *frame;
-       int idx = f->index;
-
-       fmt = virtio_video_find_video_format(&vvd->input_fmt_list,
-                                            f->pixel_format);
-       if (fmt == NULL)
-               fmt = virtio_video_find_video_format(&vvd->output_fmt_list,
-                                                    f->pixel_format);
-       if (fmt == NULL)
-               return -EINVAL;
-
-       if (idx >= fmt->desc.num_frames)
-               return -EINVAL;
-
-       frm = &fmt->frames[idx];
-       frame = &frm->frame;
-
-       if (frame->width.min == frame->width.max &&
-           frame->height.min == frame->height.max) {
-               f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-               f->discrete.width = frame->width.min;
-               f->discrete.height = frame->height.min;
-               return 0;
-       }
-
-       f->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
-       f->stepwise.min_width = frame->width.min;
-       f->stepwise.max_width = frame->width.max;
-       f->stepwise.min_height = frame->height.min;
-       f->stepwise.max_height = frame->height.max;
-       f->stepwise.step_width = frame->width.step;
-       f->stepwise.step_height = frame->height.step;
-       return 0;
-}
-
-static bool in_stepped_interval(struct virtio_video_format_range range,
-                               uint32_t point)
-{
-       if (point < range.min || point > range.max)
-               return false;
-
-       if (range.step == 0 && range.min == range.max && range.min == point)
-               return true;
-
-       if (range.step != 0 && (point - range.min) % range.step == 0)
-               return true;
-
-       return false;
-}
-
-int virtio_video_enum_framemintervals(struct file *file, void *fh,
-                                     struct v4l2_frmivalenum *f)
-{
-       struct virtio_video_stream *stream = file2stream(file);
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-       struct video_format *fmt;
-       struct video_format_frame *frm;
-       struct virtio_video_format_frame *frame;
-       struct virtio_video_format_range *frate;
-       int idx = f->index;
-       int f_idx;
-
-       fmt = virtio_video_find_video_format(&vvd->input_fmt_list,
-                                            f->pixel_format);
-       if (fmt == NULL)
-               fmt = virtio_video_find_video_format(&vvd->output_fmt_list,
-                                                    f->pixel_format);
-       if (fmt == NULL)
-               return -EINVAL;
-
-       for (f_idx = 0; f_idx <= fmt->desc.num_frames; f_idx++) {
-               frm = &fmt->frames[f_idx];
-               frame = &frm->frame;
-               if (in_stepped_interval(frame->width, f->width) &&
-                   in_stepped_interval(frame->height, f->height))
-                       break;
-       }
-
-       if (frame == NULL || f->index >= frame->num_rates)
-               return -EINVAL;
-
-       frate = &frm->frame_rates[idx];
-       if (frate->max == frate->min) {
-               f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
-               f->discrete.numerator = 1;
-               f->discrete.denominator = frate->max;
-       } else {
-               f->stepwise.min.numerator = 1;
-               f->stepwise.min.denominator = frate->max;
-               f->stepwise.max.numerator = 1;
-               f->stepwise.max.denominator = frate->min;
-               f->stepwise.step.numerator = 1;
-               f->stepwise.step.denominator = frate->step;
-               if (frate->step == 1)
-                       f->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
-               else
-                       f->type = V4L2_FRMIVAL_TYPE_STEPWISE;
-       }
-       return 0;
-}
-
-int virtio_video_stream_get_params(struct virtio_video_device *vvd,
-                                  struct virtio_video_stream *stream)
-{
-       int ret;
-
-       if (vvd->is_m2m_dev) {
-               ret = virtio_video_cmd_get_params(vvd, stream,
-                                               VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
-               if (ret) {
-                       v4l2_err(&vvd->v4l2_dev,
-                                "failed to get stream in params\n");
-                       goto err_get_parms;
-               }
-       }
-
-       ret = virtio_video_cmd_get_params(vvd, stream,
-                                         VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
-       if (ret)
-               v4l2_err(&vvd->v4l2_dev, "failed to get stream out params\n");
-
-err_get_parms:
-       return ret;
-}
-
-int virtio_video_stream_get_controls(struct virtio_video_device *vvd,
-                                    struct virtio_video_stream *stream)
-{
-       int ret;
-
-       ret = virtio_video_cmd_get_control(vvd, stream,
-                                          VIRTIO_VIDEO_CONTROL_PROFILE);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to get stream profile\n");
-               goto err_get_ctrl;
-       }
-
-       ret = virtio_video_cmd_get_control(vvd, stream,
-                                          VIRTIO_VIDEO_CONTROL_LEVEL);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to get stream level\n");
-               goto err_get_ctrl;
-       }
-
-       ret = virtio_video_cmd_get_control(vvd, stream,
-                                          VIRTIO_VIDEO_CONTROL_BITRATE);
-       if (ret)
-               v4l2_err(&vvd->v4l2_dev, "failed to get stream bitrate\n");
-
-err_get_ctrl:
-       return ret;
-}
-
-int virtio_video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
-       struct video_format_info *info;
-       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
-       struct virtio_video_stream *stream = file2stream(file);
-
-       if (!V4L2_TYPE_IS_OUTPUT(f->type))
-               info = &stream->out_info;
-       else
-               info = &stream->in_info;
-
-       virtio_video_format_from_info(info, pix_mp);
-
-       return 0;
-}
-
-int virtio_video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
-       int i, ret;
-       struct virtio_video_stream *stream = file2stream(file);
-       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-       struct video_format_info info;
-       struct video_format_info *p_info;
-       uint32_t queue;
-
-       ret = virtio_video_try_fmt(stream, f);
-       if (ret)
-               return ret;
-
-       if (V4L2_TYPE_IS_OUTPUT(f->type)) {
-               virtio_video_format_fill_default_info(&info, &stream->in_info);
-               queue = VIRTIO_VIDEO_QUEUE_TYPE_INPUT;
-       } else {
-               virtio_video_format_fill_default_info(&info, &stream->out_info);
-               queue = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT;
-       }
-
-       info.frame_width = pix_mp->width;
-       info.frame_height = pix_mp->height;
-       info.num_planes = pix_mp->num_planes;
-       info.fourcc_format = pix_mp->pixelformat;
-
-       for (i = 0; i < info.num_planes; i++) {
-               info.plane_format[i].stride =
-                                        pix_mp->plane_fmt[i].bytesperline;
-               info.plane_format[i].plane_size =
-                                        pix_mp->plane_fmt[i].sizeimage;
-       }
-
-       virtio_video_cmd_set_params(vvd, stream, &info, queue);
-       virtio_video_stream_get_params(vvd, stream);
-
-       if (V4L2_TYPE_IS_OUTPUT(f->type))
-               p_info = &stream->in_info;
-       else
-               p_info = &stream->out_info;
-
-       virtio_video_format_from_info(p_info, pix_mp);
-
-       return 0;
-}
-
-int virtio_video_g_selection(struct file *file, void *fh,
-                        struct v4l2_selection *sel)
-{
-       struct video_format_info *info;
-       struct virtio_video_stream *stream = file2stream(file);
-       struct virtio_video_device *vvd = video_drvdata(file);
-
-       switch (vvd->type) {
-       case VIRTIO_VIDEO_DEVICE_ENCODER:
-               if (!V4L2_TYPE_IS_OUTPUT(sel->type))
-                       return -EINVAL;
-               info = &stream->in_info;
-               break;
-       case VIRTIO_VIDEO_DEVICE_DECODER:
-       case VIRTIO_VIDEO_DEVICE_CAMERA:
-               if (V4L2_TYPE_IS_OUTPUT(sel->type))
-                       return -EINVAL;
-               info = &stream->out_info;
-               break;
-       default:
-               v4l2_err(&vvd->v4l2_dev, "unsupported device type\n");
-               return -EINVAL;
-       }
-
-       switch (sel->target) {
-       case V4L2_SEL_TGT_COMPOSE:
-       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
-       case V4L2_SEL_TGT_COMPOSE_PADDED:
-       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
-               sel->r.width = info->frame_width;
-               sel->r.height = info->frame_height;
-               break;
-       case V4L2_SEL_TGT_CROP_BOUNDS:
-       case V4L2_SEL_TGT_CROP_DEFAULT:
-       case V4L2_SEL_TGT_CROP:
-               sel->r.top = info->crop.top;
-               sel->r.left = info->crop.left;
-               sel->r.width = info->frame_width;
-               sel->r.height = info->frame_height;
-               break;
-       default:
-               v4l2_dbg(1, vvd->debug, &vvd->v4l2_dev,
-                        "unsupported/invalid selection target: %d\n",
-                        sel->target);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-int virtio_video_try_fmt(struct virtio_video_stream *stream,
-                        struct v4l2_format *f)
-{
-       int i, idx = 0;
-       struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-       struct video_format *fmt;
-       bool found = false;
-       struct video_format_frame *frm;
-       struct virtio_video_format_frame *frame;
-
-       if (virtio_video_state(stream) == STREAM_STATE_ERROR)
-               return -EIO;
-
-       if (V4L2_TYPE_IS_OUTPUT(f->type))
-               fmt = virtio_video_find_video_format(&vvd->input_fmt_list,
-                                                    pix_mp->pixelformat);
-       else
-               fmt = virtio_video_find_video_format(&vvd->output_fmt_list,
-                                                    pix_mp->pixelformat);
-
-       if (!fmt) {
-               if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
-                       virtio_video_format_from_info(&stream->out_info,
-                                                     pix_mp);
-               else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
-                       virtio_video_format_from_info(&stream->in_info,
-                                                     pix_mp);
-               else
-                       return -EINVAL;
-               return 0;
-       }
-
-       for (i = 0; i < fmt->desc.num_frames && !found; i++) {
-               frm = &fmt->frames[i];
-               frame = &frm->frame;
-               if (!within_range(frame->width.min, pix_mp->width,
-                                 frame->width.max))
-                       continue;
-
-               if (!within_range(frame->height.min, pix_mp->height,
-                                 frame->height.max))
-                       continue;
-               idx = i;
-               /*
-                * Try to find a more suitable frame size. Go with the current
-                * one otherwise.
-                */
-               if (needs_alignment(pix_mp->width, frame->width.step))
-                       continue;
-
-               if (needs_alignment(pix_mp->height, frame->height.step))
-                       continue;
-
-               stream->current_frame = frm;
-               found = true;
-       }
-
-       if (!found) {
-               frm = &fmt->frames[idx];
-               if (!frm)
-                       return -EINVAL;
-
-               frame = &frm->frame;
-               pix_mp->width = clamp(pix_mp->width, frame->width.min,
-                                     frame->width.max);
-               if (frame->width.step != 0)
-                       pix_mp->width = ALIGN(pix_mp->width, frame->width.step);
-
-               pix_mp->height = clamp(pix_mp->height, frame->height.min,
-                                      frame->height.max);
-               if (frame->height.step != 0)
-                       pix_mp->height = ALIGN(pix_mp->height,
-                                              frame->height.step);
-               stream->current_frame = frm;
-       }
-
-       return 0;
-}
-
-static int virtio_video_queue_free(struct virtio_video_device *vvd,
-                                  struct virtio_video_stream *stream,
-                                  enum v4l2_buf_type type)
-{
-       int ret;
-       enum virtio_video_queue_type queue_type = to_virtio_queue_type(type);
-
-       ret = virtio_video_cmd_queue_detach_resources(vvd, stream, queue_type);
-       if (ret) {
-               v4l2_warn(&vvd->v4l2_dev,
-                         "failed to destroy resources\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-int virtio_video_reqbufs(struct file *file, void *priv,
-                        struct v4l2_requestbuffers *rb)
-{
-       int ret;
-       struct virtio_video_stream *stream = file2stream(file);
-       struct v4l2_m2m_ctx *m2m_ctx = stream->fh.m2m_ctx;
-       struct virtio_video_device *vvd = video_drvdata(file);
-       struct video_device *vdev = video_devdata(file);
-       struct vb2_queue *vq;
-
-       if (vvd->is_m2m_dev)
-               vq = v4l2_m2m_get_vq(m2m_ctx, rb->type);
-       else
-               vq = vdev->queue;
-
-       if (rb->count == 0) {
-               ret = virtio_video_queue_free(vvd, stream, vq->type);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (vvd->is_m2m_dev)
-               return v4l2_m2m_reqbufs(file, m2m_ctx, rb);
-       else
-               return vb2_ioctl_reqbufs(file, priv, rb);
-}
-
-int virtio_video_subscribe_event(struct v4l2_fh *fh,
-                                const struct v4l2_event_subscription *sub)
-{
-       switch (sub->type) {
-       case V4L2_EVENT_SOURCE_CHANGE:
-               return v4l2_src_change_event_subscribe(fh, sub);
-       default:
-               return v4l2_ctrl_subscribe_event(fh, sub);
-       }
-}
-
-void virtio_video_queue_eos_event(struct virtio_video_stream *stream)
-{
-       static const struct v4l2_event eos_event = {
-               .type = V4L2_EVENT_EOS
-       };
-
-       v4l2_event_queue_fh(&stream->fh, &eos_event);
-}
-
-void virtio_video_queue_res_chg_event(struct virtio_video_stream *stream)
-{
-       static const struct v4l2_event ev_src_ch = {
-               .type = V4L2_EVENT_SOURCE_CHANGE,
-               .u.src_change.changes =
-                       V4L2_EVENT_SRC_CH_RESOLUTION,
-       };
-
-       v4l2_event_queue_fh(&stream->fh, &ev_src_ch);
-}
-
-void virtio_video_handle_error(struct virtio_video_stream *stream)
-{
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-
-       if (vvd->is_m2m_dev)
-               virtio_video_queue_release_buffers
-                       (stream, VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
-
-       virtio_video_queue_release_buffers
-               (stream, VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
-}
-
-int virtio_video_queue_release_buffers(struct virtio_video_stream *stream,
-                                      enum virtio_video_queue_type queue_type)
-{
-       int ret;
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-       struct vb2_v4l2_buffer *v4l2_vb;
-       struct virtio_video_buffer *vvb;
-
-       ret = virtio_video_cmd_queue_clear(vvd, stream, queue_type);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to clear queue\n");
-               return ret;
-       }
-
-       if (!vvd->is_m2m_dev) {
-               while (!virtio_video_pending_buf_list_pop(vvd, &vvb) && vvb) {
-                       v4l2_vb = &vvb->v4l2_m2m_vb.vb;
-                       v4l2_m2m_buf_done(v4l2_vb, VB2_BUF_STATE_ERROR);
-               }
-               return 0;
-       }
-
-       for (;;) {
-               if (queue_type == VIRTIO_VIDEO_QUEUE_TYPE_INPUT)
-                       v4l2_vb = v4l2_m2m_src_buf_remove(stream->fh.m2m_ctx);
-               else
-                       v4l2_vb = v4l2_m2m_dst_buf_remove(stream->fh.m2m_ctx);
-               if (!v4l2_vb)
-                       break;
-               v4l2_m2m_buf_done(v4l2_vb, VB2_BUF_STATE_ERROR);
-       }
-
-       return 0;
-}
-
-void virtio_video_buf_done(struct virtio_video_buffer *virtio_vb,
-                          uint32_t flags, uint64_t timestamp,
-                          uint32_t data_sizes[])
-{
-       int i;
-       enum vb2_buffer_state done_state = VB2_BUF_STATE_DONE;
-       struct vb2_v4l2_buffer *v4l2_vb = &virtio_vb->v4l2_m2m_vb.vb;
-       struct vb2_buffer *vb = &v4l2_vb->vb2_buf;
-       struct vb2_queue *vb2_queue = vb->vb2_queue;
-       struct virtio_video_stream *stream = vb2_get_drv_priv(vb2_queue);
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-       struct video_format_info *p_info;
-
-       virtio_vb->queued = false;
-
-       if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_ERR)
-               done_state = VB2_BUF_STATE_ERROR;
-
-       if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_KEY_FRAME)
-               v4l2_vb->flags |= V4L2_BUF_FLAG_KEYFRAME;
-
-       if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_BFRAME)
-               v4l2_vb->flags |= V4L2_BUF_FLAG_BFRAME;
-
-       if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_PFRAME)
-               v4l2_vb->flags |= V4L2_BUF_FLAG_PFRAME;
-
-       if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_EOS) {
-               v4l2_vb->flags |= V4L2_BUF_FLAG_LAST;
-               virtio_video_state_update(stream, STREAM_STATE_STOPPED);
-               virtio_video_queue_eos_event(stream);
-       }
-
-       if ((flags & VIRTIO_VIDEO_DEQUEUE_FLAG_ERR) ||
-           (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_EOS)) {
-               vb->planes[0].bytesused = 0;
-
-               if (!vvd->is_m2m_dev)
-                       virtio_video_pending_buf_list_del(vvd, virtio_vb);
-
-               v4l2_m2m_buf_done(v4l2_vb, done_state);
-               return;
-       }
-
-       if (!V4L2_TYPE_IS_OUTPUT(vb2_queue->type)) {
-               switch (vvd->type) {
-               case VIRTIO_VIDEO_DEVICE_ENCODER:
-                       for (i = 0; i < vb->num_planes; i++)
-                               vb->planes[i].bytesused =
-                                       le32_to_cpu(data_sizes[i]);
-                       break;
-               case VIRTIO_VIDEO_DEVICE_CAMERA:
-               case VIRTIO_VIDEO_DEVICE_DECODER:
-                       p_info = &stream->out_info;
-                       for (i = 0; i < p_info->num_planes; i++)
-                               vb->planes[i].bytesused =
-                                       p_info->plane_format[i].plane_size;
-                       break;
-               }
-
-               vb->timestamp = timestamp;
-       }
-
-       if (!vvd->is_m2m_dev)
-               virtio_video_pending_buf_list_del(vvd, virtio_vb);
-
-       v4l2_m2m_buf_done(v4l2_vb, done_state);
-}
-
-static int virtio_video_set_device_busy(struct virtio_video_device *vvd)
-{
-       struct video_device *vd = &vvd->video_dev;
-       int ret = 0;
-
-       /* Multiple open is allowed for m2m device */
-       if (vvd->is_m2m_dev)
-               return 0;
-
-       mutex_lock(vd->lock);
-
-       if (vvd->device_busy)
-               ret = -EBUSY;
-       else
-               vvd->device_busy = true;
-
-       mutex_unlock(vd->lock);
-
-       return ret;
-}
-
-static void virtio_video_clear_device_busy(struct virtio_video_device *vvd,
-                                          struct mutex *lock)
-{
-       /* Nothing to do for m2m device */
-       if (vvd->is_m2m_dev)
-               return;
-
-       if (lock)
-               mutex_lock(lock);
-
-       vvd->device_busy = false;
-
-       if (lock)
-               mutex_unlock(lock);
-}
-
-static int virtio_video_device_open(struct file *file)
-{
-       int ret;
-       uint32_t stream_id;
-       char name[TASK_COMM_LEN];
-       struct virtio_video_stream *stream;
-       struct video_format *default_fmt;
-       enum virtio_video_format format;
-       struct video_device *video_dev = video_devdata(file);
-       struct virtio_video_device *vvd = video_drvdata(file);
-
-       ret = virtio_video_set_device_busy(vvd);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "device already in use.\n");
-               return ret;
-       }
-
-       default_fmt = list_first_entry_or_null(vvd->ops->get_fmt_list(vvd),
-                                              struct video_format,
-                                              formats_list_entry);
-       if (!default_fmt) {
-               v4l2_err(&vvd->v4l2_dev, "device failed to start\n");
-               ret = -EIO;
-               goto err;
-       }
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       get_task_comm(name, current);
-       format = virtio_video_v4l2_format_to_virtio(default_fmt->desc.format);
-       virtio_video_stream_id_get(vvd, stream, &stream_id);
-       ret = virtio_video_cmd_stream_create(vvd, stream_id, format, name);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to create stream\n");
-               goto err_stream_create;
-       }
-
-       stream->video_dev = video_dev;
-       stream->stream_id = stream_id;
-
-       virtio_video_state_reset(stream);
-
-       ret = virtio_video_stream_get_params(vvd, stream);
-       if (ret)
-               goto err_stream_create;
-
-       if (format >= VIRTIO_VIDEO_FORMAT_CODED_MIN &&
-           format <= VIRTIO_VIDEO_FORMAT_CODED_MAX) {
-               ret = virtio_video_stream_get_controls(vvd, stream);
-               if (ret)
-                       goto err_stream_create;
-       }
-
-       mutex_init(&stream->vq_mutex);
-       v4l2_fh_init(&stream->fh, video_dev);
-       stream->fh.ctrl_handler = &stream->ctrl_handler;
-
-       if (vvd->is_m2m_dev) {
-               stream->fh.m2m_ctx = v4l2_m2m_ctx_init(vvd->m2m_dev, stream,
-                                                      vvd->ops->init_queues);
-               if (IS_ERR(stream->fh.m2m_ctx)) {
-                       ret = PTR_ERR(stream->fh.m2m_ctx);
-                       goto err_init_ctx;
-               }
-
-               v4l2_m2m_set_src_buffered(stream->fh.m2m_ctx, true);
-               v4l2_m2m_set_dst_buffered(stream->fh.m2m_ctx, true);
-       } else {
-               vvd->ops->init_queues(stream, NULL, &vvd->vb2_output_queue);
-               /* Video dev queue is required for vb2 ioctl wrappers */
-               video_dev->queue = &vvd->vb2_output_queue;
-       }
-
-       file->private_data = &stream->fh;
-       v4l2_fh_add(&stream->fh);
-
-       if (vvd->ops->init_ctrls) {
-               ret = vvd->ops->init_ctrls(stream);
-               if (ret) {
-                       v4l2_err(&vvd->v4l2_dev, "failed to init controls\n");
-                       goto err_init_ctrls;
-               }
-       }
-       return 0;
-
-err_init_ctrls:
-       v4l2_fh_del(&stream->fh);
-       mutex_lock(video_dev->lock);
-       if (vvd->is_m2m_dev)
-               v4l2_m2m_ctx_release(stream->fh.m2m_ctx);
-       mutex_unlock(video_dev->lock);
-err_init_ctx:
-       v4l2_fh_exit(&stream->fh);
-err_stream_create:
-       virtio_video_stream_id_put(vvd, stream_id);
-       kfree(stream);
-err:
-       virtio_video_clear_device_busy(vvd, video_dev->lock);
-       return ret;
-}
-
-static int virtio_video_device_release(struct file *file)
-{
-       struct virtio_video_stream *stream = file2stream(file);
-       struct video_device *video_dev = video_devdata(file);
-       struct virtio_video_device *vvd = video_drvdata(file);
-
-       mutex_lock(video_dev->lock);
-
-       v4l2_fh_del(&stream->fh);
-       if (vvd->is_m2m_dev) {
-               v4l2_m2m_ctx_release(stream->fh.m2m_ctx);
-       } else if (file->private_data == video_dev->queue->owner) {
-               vb2_queue_release(&vvd->vb2_output_queue);
-               video_dev->queue->owner = NULL;
-       }
-
-       v4l2_fh_exit(&stream->fh);
-
-       virtio_video_cmd_stream_destroy(vvd, stream->stream_id);
-       virtio_video_stream_id_put(vvd, stream->stream_id);
-
-       kfree(stream);
-
-       /* Mutex already locked here, passing NULL */
-       virtio_video_clear_device_busy(vvd, NULL);
-
-       mutex_unlock(video_dev->lock);
-
-       return 0;
-}
-
-static const struct v4l2_file_operations virtio_video_device_m2m_fops = {
-       .owner          = THIS_MODULE,
-       .open           = virtio_video_device_open,
-       .release        = virtio_video_device_release,
-       .poll           = v4l2_m2m_fop_poll,
-       .unlocked_ioctl = video_ioctl2,
-       .mmap           = v4l2_m2m_fop_mmap,
-};
-
-static const struct v4l2_file_operations virtio_video_device_fops = {
-       .owner          = THIS_MODULE,
-       .open           = virtio_video_device_open,
-       .release        = virtio_video_device_release,
-       .poll           = vb2_fop_poll,
-       .unlocked_ioctl = video_ioctl2,
-       .mmap           = vb2_fop_mmap,
-};
-
-static void virtio_video_device_run(void *priv)
-{
-
-}
-
-static void virtio_video_device_job_abort(void *priv)
-{
-       struct virtio_video_stream *stream = priv;
-       struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
-
-       v4l2_m2m_job_finish(vvd->m2m_dev, stream->fh.m2m_ctx);
-}
-
-static const struct v4l2_m2m_ops virtio_video_device_m2m_ops = {
-       .device_run     = virtio_video_device_run,
-       .job_abort      = virtio_video_device_job_abort,
-};
-
-static int virtio_video_device_register(struct virtio_video_device *vvd)
-{
-       int ret;
-       struct video_device *vd;
-
-       vd = &vvd->video_dev;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
-       ret = video_register_device(vd, VFL_TYPE_VIDEO, vvd->vid_dev_nr);
-#else
-       ret = video_register_device(vd, VFL_TYPE_GRABBER, vvd->vid_dev_nr);
-#endif
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to register video device\n");
-               return ret;
-       }
-
-       v4l2_info(&vvd->v4l2_dev, "Device '%s' registered as /dev/video%d\n",
-                 vd->name, vd->num);
-
-       return 0;
-}
-
-static void virtio_video_device_unregister(struct virtio_video_device *vvd)
-{
-       video_unregister_device(&vvd->video_dev);
-}
-
-static int
-virtio_video_query_capability(struct virtio_video_device *vvd,
-                             void *resp_buf,
-                             enum virtio_video_queue_type queue_type)
-{
-       int ret;
-       int resp_size = vvd->max_caps_len;
-
-       ret = virtio_video_cmd_query_capability(vvd, resp_buf, resp_size,
-                                               queue_type);
-       if (ret)
-               v4l2_err(&vvd->v4l2_dev, "failed to query capability\n");
-
-       return ret;
-}
-
-int virtio_video_device_init(struct virtio_video_device *vvd)
-{
-       int ret;
-       int vfl_dir;
-       u32 dev_caps;
-       struct video_device *vd;
-       struct v4l2_m2m_dev *m2m_dev;
-       const struct v4l2_file_operations *fops;
-       void *input_resp_buf, *output_resp_buf;
-
-       output_resp_buf = kzalloc(vvd->max_caps_len, GFP_KERNEL);
-       if (!output_resp_buf)
-               return -ENOMEM;
-
-       ret = virtio_video_query_capability(vvd, output_resp_buf,
-                                           VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to get output caps\n");
-               goto err_output_cap;
-       }
-
-       if (vvd->is_m2m_dev) {
-               input_resp_buf = kzalloc(vvd->max_caps_len, GFP_KERNEL);
-               if (!input_resp_buf) {
-                       ret = -ENOMEM;
-                       goto err_input_buf;
-               }
-
-               ret = virtio_video_query_capability(vvd, input_resp_buf,
-                                               VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
-               if (ret) {
-                       v4l2_err(&vvd->v4l2_dev, "failed to get input caps\n");
-                       goto err_input_cap;
-               }
-
-               m2m_dev = v4l2_m2m_init(&virtio_video_device_m2m_ops);
-               if (IS_ERR(m2m_dev)) {
-                       v4l2_err(&vvd->v4l2_dev, "failed to init m2m device\n");
-                       ret = PTR_ERR(m2m_dev);
-                       goto err_m2m_dev;
-               }
-               vfl_dir = VFL_DIR_M2M;
-               fops = &virtio_video_device_m2m_fops;
-               dev_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
-       } else {
-               input_resp_buf = NULL;
-               m2m_dev = NULL;
-               vfl_dir = VFL_DIR_RX;
-               fops = &virtio_video_device_fops;
-               dev_caps = V4L2_CAP_STREAMING;
-               if (vvd->is_mplane_cam)
-                       dev_caps |= V4L2_CAP_VIDEO_CAPTURE_MPLANE;
-               else
-                       dev_caps |= V4L2_CAP_VIDEO_CAPTURE;
-       }
-
-       vvd->m2m_dev = m2m_dev;
-       mutex_init(&vvd->video_dev_mutex);
-       vd = &vvd->video_dev;
-       vd->lock = &vvd->video_dev_mutex;
-       vd->v4l2_dev = &vvd->v4l2_dev;
-       vd->vfl_dir = vfl_dir;
-       vd->ioctl_ops = NULL;
-       vd->fops = fops;
-       vd->device_caps = dev_caps;
-       vd->release = video_device_release_empty;
-
-       /* Use the selection API instead */
-       v4l2_disable_ioctl(vd, VIDIOC_CROPCAP);
-       v4l2_disable_ioctl(vd, VIDIOC_G_CROP);
-
-       video_set_drvdata(vd, vvd);
-
-       INIT_LIST_HEAD(&vvd->input_fmt_list);
-       INIT_LIST_HEAD(&vvd->output_fmt_list);
-       INIT_LIST_HEAD(&vvd->controls_fmt_list);
-       INIT_LIST_HEAD(&vvd->pending_buf_list);
-
-       vvd->num_output_fmts = 0;
-       vvd->num_input_fmts = 0;
-
-       switch (vvd->type) {
-       case VIRTIO_VIDEO_DEVICE_CAMERA:
-               virtio_video_cam_init(vvd);
-               break;
-       case VIRTIO_VIDEO_DEVICE_ENCODER:
-               virtio_video_enc_init(vvd);
-               break;
-       case VIRTIO_VIDEO_DEVICE_DECODER:
-       default:
-               virtio_video_dec_init(vvd);
-               break;
-       }
-
-       ret = virtio_video_parse_virtio_capabilities(vvd, input_resp_buf,
-                                                    output_resp_buf);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to parse a function\n");
-               goto parse_cap_err;
-       }
-
-       ret = virtio_video_parse_virtio_control(vvd);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev, "failed to query controls\n");
-               goto parse_ctrl_err;
-       }
-
-       ret = virtio_video_device_register(vvd);
-       if (ret) {
-               v4l2_err(&vvd->v4l2_dev,
-                        "failed to init virtio video device\n");
-               goto register_err;
-       }
-
-       goto out_cleanup;
-
-register_err:
-       virtio_video_clean_control(vvd);
-parse_ctrl_err:
-       virtio_video_clean_capability(vvd);
-parse_cap_err:
-       if (vvd->is_m2m_dev)
-               v4l2_m2m_release(vvd->m2m_dev);
-err_m2m_dev:
-err_input_cap:
-out_cleanup:
-       if (vvd->is_m2m_dev)
-               kfree(input_resp_buf);
-err_input_buf:
-err_output_cap:
-       kfree(output_resp_buf);
-
-       return ret;
-}
-
-void virtio_video_device_deinit(struct virtio_video_device *vvd)
-{
-       vvd->commandq.ready = false;
-       vvd->eventq.ready = false;
-
-       virtio_video_device_unregister(vvd);
-       if (vvd->is_m2m_dev)
-               v4l2_m2m_release(vvd->m2m_dev);
-       virtio_video_clean_control(vvd);
-       virtio_video_clean_capability(vvd);
-}