root/drivers/media/platform/amphion/venc.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2020-2021 NXP
 */

#include <linux/init.h>
#include <linux/interconnect.h>
#include <linux/ioctl.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/ktime.h>
#include <linux/rational.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#include "vpu.h"
#include "vpu_defs.h"
#include "vpu_core.h"
#include "vpu_helpers.h"
#include "vpu_v4l2.h"
#include "vpu_cmds.h"
#include "vpu_rpc.h"

#define VENC_OUTPUT_ENABLE      BIT(0)
#define VENC_CAPTURE_ENABLE     BIT(1)
#define VENC_ENABLE_MASK        (VENC_OUTPUT_ENABLE | VENC_CAPTURE_ENABLE)
#define VENC_MAX_BUF_CNT        8
#define VENC_MIN_BUFFER_OUT     6
#define VENC_MIN_BUFFER_CAP     6

struct venc_t {
        struct vpu_encode_params params;
        u32 request_key_frame;
        u32 input_ready;
        u32 cpb_size;
        bool bitrate_change;

        struct vpu_buffer enc[VENC_MAX_BUF_CNT];
        struct vpu_buffer ref[VENC_MAX_BUF_CNT];
        struct vpu_buffer act[VENC_MAX_BUF_CNT];
        struct list_head frames;
        u32 frame_count;
        u32 encode_count;
        u32 ready_count;
        u32 enable;
        u32 stopped;
        u32 memory_resource_configured;

        u32 skipped_count;
        u32 skipped_bytes;

        wait_queue_head_t wq;
};

struct venc_frame_t {
        struct list_head list;
        struct vpu_enc_pic_info info;
        u32 bytesused;
        s64 timestamp;
};

static const struct vpu_format venc_formats[] = {
        {
                .pixfmt = V4L2_PIX_FMT_NV12M,
                .mem_planes = 2,
                .comp_planes = 2,
                .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
                .sibling = V4L2_PIX_FMT_NV12,
        },
        {
                .pixfmt = V4L2_PIX_FMT_NV12,
                .mem_planes = 1,
                .comp_planes = 2,
                .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
                .sibling = V4L2_PIX_FMT_NV12M,
        },
        {
                .pixfmt = V4L2_PIX_FMT_H264,
                .mem_planes = 1,
                .comp_planes = 1,
                .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
                .flags = V4L2_FMT_FLAG_COMPRESSED
        },
        {0, 0, 0, 0},
};

static int venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
        strscpy(cap->driver, "amphion-vpu", sizeof(cap->driver));
        strscpy(cap->card, "amphion vpu encoder", sizeof(cap->card));
        strscpy(cap->bus_info, "platform: amphion-vpu", sizeof(cap->bus_info));

        return 0;
}

static int venc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
        struct vpu_inst *inst = to_inst(file);
        const struct vpu_format *fmt;

        memset(f->reserved, 0, sizeof(f->reserved));
        fmt = vpu_helper_enum_format(inst, f->type, f->index);
        if (!fmt)
                return -EINVAL;

        f->pixelformat = fmt->pixfmt;
        f->flags = fmt->flags;

        return 0;
}

static int venc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize)
{
        struct vpu_inst *inst = to_inst(file);
        const struct vpu_core_resources *res;

        if (!fsize || fsize->index)
                return -EINVAL;

        if (!vpu_helper_find_format(inst, 0, fsize->pixel_format))
                return -EINVAL;

        res = vpu_get_resource(inst);
        if (!res)
                return -EINVAL;
        fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
        fsize->stepwise.max_width = res->max_width;
        fsize->stepwise.max_height = res->max_height;
        fsize->stepwise.min_width = res->min_width;
        fsize->stepwise.min_height = res->min_height;
        fsize->stepwise.step_width = res->step_width;
        fsize->stepwise.step_height = res->step_height;

        return 0;
}

static int venc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival)
{
        struct vpu_inst *inst = to_inst(file);
        const struct vpu_core_resources *res;

        if (!fival || fival->index)
                return -EINVAL;

        if (!vpu_helper_find_format(inst, 0, fival->pixel_format))
                return -EINVAL;

        if (!fival->width || !fival->height)
                return -EINVAL;

        res = vpu_get_resource(inst);
        if (!res)
                return -EINVAL;
        if (fival->width < res->min_width || fival->width > res->max_width ||
            fival->height < res->min_height || fival->height > res->max_height)
                return -EINVAL;

        fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
        fival->stepwise.min.numerator = 1;
        fival->stepwise.min.denominator = USHRT_MAX;
        fival->stepwise.max.numerator = USHRT_MAX;
        fival->stepwise.max.denominator = 1;
        fival->stepwise.step.numerator = 1;
        fival->stepwise.step.denominator = 1;

        return 0;
}

static int venc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
{
        struct vpu_inst *inst = to_inst(file);
        struct venc_t *venc = inst->priv;
        struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
        struct vpu_format *cur_fmt;
        int i;

        cur_fmt = vpu_get_format(inst, f->type);

        pixmp->pixelformat = cur_fmt->pixfmt;
        pixmp->num_planes = cur_fmt->mem_planes;
        pixmp->width = cur_fmt->width;
        pixmp->height = cur_fmt->height;
        pixmp->field = cur_fmt->field;
        pixmp->flags = cur_fmt->flags;
        for (i = 0; i < pixmp->num_planes; i++) {
                pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i];
                pixmp->plane_fmt[i].sizeimage = vpu_get_fmt_plane_size(cur_fmt, i);
        }

        f->fmt.pix_mp.colorspace = venc->params.color.primaries;
        f->fmt.pix_mp.xfer_func = venc->params.color.transfer;
        f->fmt.pix_mp.ycbcr_enc = venc->params.color.matrix;
        f->fmt.pix_mp.quantization = venc->params.color.full_range;

        return 0;
}

static int venc_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
{
        struct vpu_inst *inst = to_inst(file);
        struct vpu_format fmt;

        vpu_try_fmt_common(inst, f, &fmt);

        return 0;
}

static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
{
        struct vpu_inst *inst = to_inst(file);
        struct vpu_format fmt;
        struct vpu_format *cur_fmt;
        struct vb2_queue *q;
        struct venc_t *venc = inst->priv;
        struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;

        q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
        if (vb2_is_busy(q))
                return -EBUSY;

        if (vpu_try_fmt_common(inst, f, &fmt))
                return -EINVAL;

        cur_fmt = vpu_get_format(inst, f->type);

        memcpy(cur_fmt, &fmt, sizeof(*cur_fmt));

        if (V4L2_TYPE_IS_OUTPUT(f->type)) {
                venc->params.input_format = cur_fmt->pixfmt;
                venc->params.src_stride = cur_fmt->bytesperline[0];
                venc->params.src_width = cur_fmt->width;
                venc->params.src_height = cur_fmt->height;
                venc->params.crop.left = 0;
                venc->params.crop.top = 0;
                venc->params.crop.width = cur_fmt->width;
                venc->params.crop.height = cur_fmt->height;
        } else {
                venc->params.codec_format = cur_fmt->pixfmt;
                venc->params.out_width = cur_fmt->width;
                venc->params.out_height = cur_fmt->height;
        }

        if (V4L2_TYPE_IS_OUTPUT(f->type)) {
                venc->params.color.primaries = pix_mp->colorspace;
                venc->params.color.transfer = pix_mp->xfer_func;
                venc->params.color.matrix = pix_mp->ycbcr_enc;
                venc->params.color.full_range = pix_mp->quantization;
        }

        pix_mp->colorspace = venc->params.color.primaries;
        pix_mp->xfer_func = venc->params.color.transfer;
        pix_mp->ycbcr_enc = venc->params.color.matrix;
        pix_mp->quantization = venc->params.color.full_range;

        return 0;
}

static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
{
        struct vpu_inst *inst = to_inst(file);
        struct venc_t *venc = inst->priv;
        struct v4l2_fract *timeperframe;

        if (!parm)
                return -EINVAL;

        if (!V4L2_TYPE_IS_OUTPUT(parm->type))
                return -EINVAL;

        if (!vpu_helper_check_type(inst, parm->type))
                return -EINVAL;

        timeperframe = &parm->parm.capture.timeperframe;
        parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
        parm->parm.capture.readbuffers = 0;
        timeperframe->numerator = venc->params.frame_rate.numerator;
        timeperframe->denominator = venc->params.frame_rate.denominator;

        return 0;
}

static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
{
        struct vpu_inst *inst = to_inst(file);
        struct venc_t *venc = inst->priv;
        struct v4l2_fract *timeperframe;
        unsigned long n, d;

        if (!parm)
                return -EINVAL;

        if (!V4L2_TYPE_IS_OUTPUT(parm->type))
                return -EINVAL;

        if (!vpu_helper_check_type(inst, parm->type))
                return -EINVAL;

        timeperframe = &parm->parm.capture.timeperframe;
        if (!timeperframe->numerator)
                timeperframe->numerator = venc->params.frame_rate.numerator;
        if (!timeperframe->denominator)
                timeperframe->denominator = venc->params.frame_rate.denominator;

        venc->params.frame_rate.numerator = timeperframe->numerator;
        venc->params.frame_rate.denominator = timeperframe->denominator;

        rational_best_approximation(venc->params.frame_rate.numerator,
                                    venc->params.frame_rate.denominator,
                                    venc->params.frame_rate.numerator,
                                    venc->params.frame_rate.denominator,
                                    &n, &d);
        venc->params.frame_rate.numerator = n;
        venc->params.frame_rate.denominator = d;

        parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
        memset(parm->parm.capture.reserved, 0, sizeof(parm->parm.capture.reserved));

        return 0;
}

static int venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
{
        struct vpu_inst *inst = to_inst(file);
        struct venc_t *venc = inst->priv;

        if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
                return -EINVAL;

        switch (s->target) {
        case V4L2_SEL_TGT_CROP_DEFAULT:
        case V4L2_SEL_TGT_CROP_BOUNDS:
                s->r.left = 0;
                s->r.top = 0;
                s->r.width = inst->out_format.width;
                s->r.height = inst->out_format.height;
                break;
        case V4L2_SEL_TGT_CROP:
                s->r = venc->params.crop;
                break;
        default:
                return -EINVAL;
        }

        return 0;
}

static int venc_valid_crop(struct venc_t *venc, const struct vpu_core_resources *res)
{
        struct v4l2_rect *rect = NULL;
        u32 min_width;
        u32 min_height;
        u32 src_width;
        u32 src_height;

        rect = &venc->params.crop;
        min_width = res->min_width;
        min_height = res->min_height;
        src_width = venc->params.src_width;
        src_height = venc->params.src_height;

        if (rect->width == 0 || rect->height == 0)
                return -EINVAL;
        if (rect->left > src_width - min_width || rect->top > src_height - min_height)
                return -EINVAL;

        rect->width = min(rect->width, src_width - rect->left);
        rect->width = max_t(u32, rect->width, min_width);

        rect->height = min(rect->height, src_height - rect->top);
        rect->height = max_t(u32, rect->height, min_height);

        return 0;
}

static int venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
{
        struct vpu_inst *inst = to_inst(file);
        const struct vpu_core_resources *res;
        struct venc_t *venc = inst->priv;

        res = vpu_get_resource(inst);
        if (!res)
                return -EINVAL;

        if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
                return -EINVAL;
        if (s->target != V4L2_SEL_TGT_CROP)
                return -EINVAL;

        venc->params.crop.left = ALIGN(s->r.left, res->step_width);
        venc->params.crop.top = ALIGN(s->r.top, res->step_height);
        venc->params.crop.width = ALIGN(s->r.width, res->step_width);
        venc->params.crop.height = ALIGN(s->r.height, res->step_height);
        if (venc_valid_crop(venc, res)) {
                venc->params.crop.left = 0;
                venc->params.crop.top = 0;
                venc->params.crop.width = venc->params.src_width;
                venc->params.crop.height = venc->params.src_height;
        }

        inst->crop = venc->params.crop;

        return 0;
}

static int venc_drain(struct vpu_inst *inst)
{
        struct venc_t *venc = inst->priv;
        int ret;

        if (!inst->fh.m2m_ctx)
                return 0;

        if (inst->state != VPU_CODEC_STATE_DRAIN)
                return 0;

        if (!vpu_is_source_empty(inst))
                return 0;

        if (!venc->input_ready)
                return 0;

        venc->input_ready = false;
        vpu_trace(inst->dev, "[%d]\n", inst->id);
        ret = vpu_session_stop(inst);
        if (ret)
                return ret;
        inst->state = VPU_CODEC_STATE_STOP;
        wake_up_all(&venc->wq);

        return 0;
}

static int venc_request_eos(struct vpu_inst *inst)
{
        inst->state = VPU_CODEC_STATE_DRAIN;
        venc_drain(inst);

        return 0;
}

static int venc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd)
{
        struct vpu_inst *inst = to_inst(file);
        int ret;

        ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd);
        if (ret)
                return ret;

        vpu_inst_lock(inst);
        if (cmd->cmd == V4L2_ENC_CMD_STOP) {
                if (inst->state == VPU_CODEC_STATE_DEINIT)
                        vpu_set_last_buffer_dequeued(inst, true);
                else
                        venc_request_eos(inst);
        }
        vpu_inst_unlock(inst);

        return 0;
}

static int venc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
{
        switch (sub->type) {
        case V4L2_EVENT_EOS:
                return v4l2_event_subscribe(fh, sub, 0, NULL);
        case V4L2_EVENT_CTRL:
                return v4l2_ctrl_subscribe_event(fh, sub);
        default:
                return -EINVAL;
        }
}

static const struct v4l2_ioctl_ops venc_ioctl_ops = {
        .vidioc_querycap               = venc_querycap,
        .vidioc_enum_fmt_vid_cap       = venc_enum_fmt,
        .vidioc_enum_fmt_vid_out       = venc_enum_fmt,
        .vidioc_enum_framesizes        = venc_enum_framesizes,
        .vidioc_enum_frameintervals    = venc_enum_frameintervals,
        .vidioc_g_fmt_vid_cap_mplane   = venc_g_fmt,
        .vidioc_g_fmt_vid_out_mplane   = venc_g_fmt,
        .vidioc_try_fmt_vid_cap_mplane = venc_try_fmt,
        .vidioc_try_fmt_vid_out_mplane = venc_try_fmt,
        .vidioc_s_fmt_vid_cap_mplane   = venc_s_fmt,
        .vidioc_s_fmt_vid_out_mplane   = venc_s_fmt,
        .vidioc_g_parm                 = venc_g_parm,
        .vidioc_s_parm                 = venc_s_parm,
        .vidioc_g_selection            = venc_g_selection,
        .vidioc_s_selection            = venc_s_selection,
        .vidioc_try_encoder_cmd        = v4l2_m2m_ioctl_try_encoder_cmd,
        .vidioc_encoder_cmd            = venc_encoder_cmd,
        .vidioc_subscribe_event        = venc_subscribe_event,
        .vidioc_unsubscribe_event      = v4l2_event_unsubscribe,
        .vidioc_reqbufs                = v4l2_m2m_ioctl_reqbufs,
        .vidioc_querybuf               = v4l2_m2m_ioctl_querybuf,
        .vidioc_create_bufs            = v4l2_m2m_ioctl_create_bufs,
        .vidioc_prepare_buf            = v4l2_m2m_ioctl_prepare_buf,
        .vidioc_qbuf                   = v4l2_m2m_ioctl_qbuf,
        .vidioc_expbuf                 = v4l2_m2m_ioctl_expbuf,
        .vidioc_dqbuf                  = v4l2_m2m_ioctl_dqbuf,
        .vidioc_streamon               = v4l2_m2m_ioctl_streamon,
        .vidioc_streamoff              = v4l2_m2m_ioctl_streamoff,
};

static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
{
        struct vpu_inst *inst = ctrl_to_inst(ctrl);
        struct venc_t *venc = inst->priv;
        int ret = 0;

        switch (ctrl->id) {
        case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
                venc->params.profile = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
                venc->params.level = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
                venc->params.rc_enable = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
                venc->params.rc_mode = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_BITRATE:
                if (ctrl->val != venc->params.bitrate)
                        venc->bitrate_change = true;
                venc->params.bitrate = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
                venc->params.bitrate_max = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
                venc->params.gop_length = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_B_FRAMES:
                venc->params.bframes = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
                venc->params.i_frame_qp = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
                venc->params.p_frame_qp = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
                venc->params.b_frame_qp = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
                venc->request_key_frame = 1;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
                venc->cpb_size = ctrl->val * 1024;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
                venc->params.sar.enable = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
                venc->params.sar.idc = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
                venc->params.sar.width = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
                venc->params.sar.height = ctrl->val;
                break;
        case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
                break;
        default:
                ret = -EINVAL;
                break;
        }

        return ret;
}

static const struct v4l2_ctrl_ops venc_ctrl_ops = {
        .s_ctrl = venc_op_s_ctrl,
        .g_volatile_ctrl = vpu_helper_g_volatile_ctrl,
};

static int venc_ctrl_init(struct vpu_inst *inst)
{
        struct v4l2_ctrl *ctrl;
        int ret;

        ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 20);
        if (ret)
                return ret;

        v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
                               V4L2_CID_MPEG_VIDEO_H264_PROFILE,
                               V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
                               ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
                                 (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
                                 (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)),
                               V4L2_MPEG_VIDEO_H264_PROFILE_HIGH);

        v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
                               V4L2_CID_MPEG_VIDEO_H264_LEVEL,
                               V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
                               0x0,
                               V4L2_MPEG_VIDEO_H264_LEVEL_4_0);

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1);

        v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
                               V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
                               V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
                               ~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
                                 (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)),
                               V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_BITRATE,
                          BITRATE_MIN,
                          BITRATE_MAX,
                          BITRATE_STEP,
                          BITRATE_DEFAULT);

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
                          BITRATE_MIN, BITRATE_MAX,
                          BITRATE_STEP,
                          BITRATE_DEFAULT_PEAK);

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 8000, 1, 30);

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 4, 1, 0);

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26);
        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28);
        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30);
        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 0, 0, 0, 0);
        ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                                 V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2);
        if (ctrl)
                ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
        ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                                 V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 2);
        if (ctrl)
                ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, 64, 10240, 1, 1024);

        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, 0, 1, 1, 1);
        v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
                               V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
                               V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
                               0x0,
                               V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1);
        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
                          0, USHRT_MAX, 1, 1);
        v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
                          V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
                          0, USHRT_MAX, 1, 1);
        v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
                               V4L2_CID_MPEG_VIDEO_HEADER_MODE,
                               V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
                               ~(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME),
                               V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);

        v4l2_ctrl_new_std(&inst->ctrl_handler, NULL,
                          V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, 0);

        if (inst->ctrl_handler.error) {
                ret = inst->ctrl_handler.error;
                v4l2_ctrl_handler_free(&inst->ctrl_handler);
                return ret;
        }

        ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler);
        if (ret) {
                dev_err(inst->dev, "[%d] setup ctrls fail, ret = %d\n", inst->id, ret);
                v4l2_ctrl_handler_free(&inst->ctrl_handler);
                return ret;
        }

        return 0;
}

static bool venc_check_ready(struct vpu_inst *inst, unsigned int type)
{
        struct venc_t *venc = inst->priv;

        if (V4L2_TYPE_IS_OUTPUT(type)) {
                if (vpu_helper_get_free_space(inst) < venc->cpb_size)
                        return false;
                return venc->input_ready;
        }

        if (list_empty(&venc->frames))
                return false;
        return true;
}

static u32 venc_get_enable_mask(u32 type)
{
        if (V4L2_TYPE_IS_OUTPUT(type))
                return VENC_OUTPUT_ENABLE;
        else
                return VENC_CAPTURE_ENABLE;
}

static void venc_set_enable(struct venc_t *venc, u32 type, int enable)
{
        u32 mask = venc_get_enable_mask(type);

        if (enable)
                venc->enable |= mask;
        else
                venc->enable &= ~mask;
}

static u32 venc_get_enable(struct venc_t *venc, u32 type)
{
        return venc->enable & venc_get_enable_mask(type);
}

static void venc_input_done(struct vpu_inst *inst)
{
        struct venc_t *venc = inst->priv;

        vpu_inst_lock(inst);
        venc->input_ready = true;
        vpu_process_output_buffer(inst);
        if (inst->state == VPU_CODEC_STATE_DRAIN)
                venc_drain(inst);
        vpu_inst_unlock(inst);
}

/*
 * It's hardware limitation, that there may be several bytes
 * redundant data at the beginning of frame.
 * For android platform, the redundant data may cause cts test fail
 * So driver will strip them
 */
static int venc_precheck_encoded_frame(struct vpu_inst *inst, struct venc_frame_t *frame)
{
        struct venc_t *venc;
        int skipped;

        if (!frame || !frame->bytesused)
                return -EINVAL;

        venc = inst->priv;
        skipped = vpu_helper_find_startcode(&inst->stream_buffer,
                                            inst->cap_format.pixfmt,
                                            frame->info.wptr - inst->stream_buffer.phys,
                                            frame->bytesused);
        if (skipped > 0) {
                frame->bytesused -= skipped;
                frame->info.wptr = vpu_helper_step_walk(&inst->stream_buffer,
                                                        frame->info.wptr, skipped);
                venc->skipped_bytes += skipped;
                venc->skipped_count++;
        }

        return 0;
}

static int venc_get_one_encoded_frame(struct vpu_inst *inst,
                                      struct venc_frame_t *frame,
                                      struct vb2_v4l2_buffer *vbuf)
{
        struct venc_t *venc = inst->priv;
        struct vb2_v4l2_buffer *src_buf;

        if (!vbuf)
                return -EAGAIN;

        src_buf = vpu_find_buf_by_sequence(inst, inst->out_format.type, frame->info.frame_id);
        if (src_buf) {
                v4l2_m2m_buf_copy_metadata(src_buf, vbuf);
                vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE);
                v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, src_buf);
                v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
        } else {
                vbuf->vb2_buf.timestamp = frame->info.timestamp;
        }
        if (!venc_get_enable(inst->priv, vbuf->vb2_buf.type)) {
                v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
                return 0;
        }
        if (frame->bytesused > vbuf->vb2_buf.planes[0].length) {
                v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
                return -ENOMEM;
        }

        venc_precheck_encoded_frame(inst, frame);

        if (frame->bytesused) {
                u32 rptr = frame->info.wptr;
                void *dst = vb2_plane_vaddr(&vbuf->vb2_buf, 0);

                vpu_helper_copy_from_stream_buffer(&inst->stream_buffer,
                                                   &rptr, frame->bytesused, dst);
                vpu_iface_update_stream_buffer(inst, rptr, 0);
        }
        vb2_set_plane_payload(&vbuf->vb2_buf, 0, frame->bytesused);
        vbuf->sequence = frame->info.frame_id;
        vbuf->field = inst->cap_format.field;
        vbuf->flags |= frame->info.pic_type;
        vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
        vpu_set_buffer_average_qp(vbuf, frame->info.average_qp);
        dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp);
        v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
        venc->ready_count++;

        if (vbuf->flags & V4L2_BUF_FLAG_KEYFRAME)
                dev_dbg(inst->dev, "[%d][%d]key frame\n", inst->id, frame->info.frame_id);

        return 0;
}

static int venc_get_encoded_frames(struct vpu_inst *inst)
{
        struct venc_t *venc;
        struct venc_frame_t *frame;
        struct venc_frame_t *tmp;

        if (!inst->fh.m2m_ctx)
                return 0;
        venc = inst->priv;
        list_for_each_entry_safe(frame, tmp, &venc->frames, list) {
                if (venc_get_one_encoded_frame(inst, frame,
                                               v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx)))
                        break;
                list_del_init(&frame->list);
                kfree(frame);
        }

        return 0;
}

static int venc_frame_encoded(struct vpu_inst *inst, void *arg)
{
        struct vpu_enc_pic_info *info = arg;
        struct venc_frame_t *frame;
        struct venc_t *venc;
        int ret = 0;

        if (!info)
                return -EINVAL;
        venc = inst->priv;
        frame = kzalloc_obj(*frame);
        if (!frame)
                return -ENOMEM;

        memcpy(&frame->info, info, sizeof(frame->info));
        frame->bytesused = info->frame_size;

        vpu_inst_lock(inst);
        list_add_tail(&frame->list, &venc->frames);
        venc->encode_count++;
        venc_get_encoded_frames(inst);
        vpu_inst_unlock(inst);

        return ret;
}

static void venc_set_last_buffer_dequeued(struct vpu_inst *inst)
{
        struct venc_t *venc = inst->priv;

        if (venc->stopped && list_empty(&venc->frames))
                vpu_set_last_buffer_dequeued(inst, true);
}

static void venc_stop_done(struct vpu_inst *inst)
{
        struct venc_t *venc = inst->priv;

        vpu_inst_lock(inst);
        venc->stopped = true;
        venc_set_last_buffer_dequeued(inst);
        vpu_inst_unlock(inst);

        wake_up_all(&venc->wq);
}

static void venc_event_notify(struct vpu_inst *inst, u32 event, void *data)
{
}

static void venc_release(struct vpu_inst *inst)
{
}

static void venc_cleanup(struct vpu_inst *inst)
{
        struct venc_t *venc;

        if (!inst)
                return;

        venc = inst->priv;
        kfree(venc);
        inst->priv = NULL;
        kfree(inst);
}

static int venc_start_session(struct vpu_inst *inst, u32 type)
{
        struct venc_t *venc = inst->priv;
        int stream_buffer_size;
        int ret;

        venc_set_enable(venc, type, 1);
        if ((venc->enable & VENC_ENABLE_MASK) != VENC_ENABLE_MASK)
                return 0;

        vpu_iface_init_instance(inst);
        stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core);
        if (stream_buffer_size > 0) {
                inst->stream_buffer.length = max_t(u32, stream_buffer_size, venc->cpb_size * 3);
                ret = vpu_alloc_dma(inst->core, &inst->stream_buffer);
                if (ret)
                        goto error;

                inst->use_stream_buffer = true;
                vpu_iface_config_stream_buffer(inst, &inst->stream_buffer);
        }

        ret = vpu_iface_set_encode_params(inst, &venc->params, 0);
        if (ret)
                goto error;

        venc->memory_resource_configured = false;
        ret = vpu_session_configure_codec(inst);
        if (ret)
                goto error;

        if (!venc->memory_resource_configured) {
                vb2_queue_error(v4l2_m2m_get_src_vq(inst->fh.m2m_ctx));
                vb2_queue_error(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx));
                ret = -ENOMEM;
                goto error;
        }

        inst->state = VPU_CODEC_STATE_CONFIGURED;
        /*vpu_iface_config_memory_resource*/

        /*config enc expert mode parameter*/
        ret = vpu_iface_set_encode_params(inst, &venc->params, 1);
        if (ret)
                goto error;

        ret = vpu_session_start(inst);
        if (ret)
                goto error;
        inst->state = VPU_CODEC_STATE_STARTED;

        venc->bitrate_change = false;
        venc->input_ready = true;
        venc->frame_count = 0;
        venc->encode_count = 0;
        venc->ready_count = 0;
        venc->stopped = false;
        vpu_process_output_buffer(inst);
        if (venc->frame_count == 0)
                dev_err(inst->dev, "[%d] there is no input when starting\n", inst->id);

        return 0;
error:
        venc_set_enable(venc, type, 0);
        inst->state = VPU_CODEC_STATE_DEINIT;

        vpu_free_dma(&inst->stream_buffer);
        return ret;
}

static void venc_cleanup_mem_resource(struct vpu_inst *inst)
{
        struct venc_t *venc;
        u32 i;

        venc = inst->priv;
        venc->memory_resource_configured = false;

        for (i = 0; i < ARRAY_SIZE(venc->enc); i++)
                vpu_free_dma(&venc->enc[i]);
        for (i = 0; i < ARRAY_SIZE(venc->ref); i++)
                vpu_free_dma(&venc->ref[i]);
}

static void venc_request_mem_resource(struct vpu_inst *inst,
                                      u32 enc_frame_size,
                                      u32 enc_frame_num,
                                      u32 ref_frame_size,
                                      u32 ref_frame_num,
                                      u32 act_frame_size,
                                      u32 act_frame_num)
{
        struct venc_t *venc;
        u32 i;
        int ret;

        venc = inst->priv;
        if (enc_frame_num > ARRAY_SIZE(venc->enc)) {
                dev_err(inst->dev, "[%d] enc num(%d) is out of range\n", inst->id, enc_frame_num);
                return;
        }
        if (ref_frame_num > ARRAY_SIZE(venc->ref)) {
                dev_err(inst->dev, "[%d] ref num(%d) is out of range\n", inst->id, ref_frame_num);
                return;
        }
        if (act_frame_num > ARRAY_SIZE(venc->act)) {
                dev_err(inst->dev, "[%d] act num(%d) is out of range\n", inst->id, act_frame_num);
                return;
        }

        for (i = 0; i < enc_frame_num; i++) {
                venc->enc[i].length = enc_frame_size;
                ret = vpu_alloc_dma(inst->core, &venc->enc[i]);
                if (ret) {
                        venc_cleanup_mem_resource(inst);
                        return;
                }
        }
        for (i = 0; i < ref_frame_num; i++) {
                venc->ref[i].length = ref_frame_size;
                ret = vpu_alloc_dma(inst->core, &venc->ref[i]);
                if (ret) {
                        venc_cleanup_mem_resource(inst);
                        return;
                }
        }
        if (act_frame_num != 1 || act_frame_size > inst->act.length) {
                venc_cleanup_mem_resource(inst);
                return;
        }
        venc->act[0].length = act_frame_size;
        venc->act[0].phys = inst->act.phys;
        venc->act[0].virt = inst->act.virt;

        for (i = 0; i < enc_frame_num; i++)
                vpu_iface_config_memory_resource(inst, MEM_RES_ENC, i, &venc->enc[i]);
        for (i = 0; i < ref_frame_num; i++)
                vpu_iface_config_memory_resource(inst, MEM_RES_REF, i, &venc->ref[i]);
        for (i = 0; i < act_frame_num; i++)
                vpu_iface_config_memory_resource(inst, MEM_RES_ACT, i, &venc->act[i]);
        venc->memory_resource_configured = true;
}

static void venc_cleanup_frames(struct venc_t *venc)
{
        struct venc_frame_t *frame;
        struct venc_frame_t *tmp;

        list_for_each_entry_safe(frame, tmp, &venc->frames, list) {
                list_del_init(&frame->list);
                kfree(frame);
        }
}

static int venc_stop_session(struct vpu_inst *inst, u32 type)
{
        struct venc_t *venc = inst->priv;

        venc_set_enable(venc, type, 0);
        if (venc->enable & VENC_ENABLE_MASK)
                return 0;

        if (inst->state == VPU_CODEC_STATE_DEINIT)
                return 0;

        if (inst->state != VPU_CODEC_STATE_STOP)
                venc_request_eos(inst);

        call_void_vop(inst, wait_prepare);
        if (!wait_event_timeout(venc->wq, venc->stopped, VPU_TIMEOUT)) {
                set_bit(inst->id, &inst->core->hang_mask);
                vpu_session_debug(inst);
        }
        call_void_vop(inst, wait_finish);

        inst->state = VPU_CODEC_STATE_DEINIT;
        venc_cleanup_frames(inst->priv);
        vpu_free_dma(&inst->stream_buffer);
        venc_cleanup_mem_resource(inst);

        return 0;
}

static int venc_process_output(struct vpu_inst *inst, struct vb2_buffer *vb)
{
        struct venc_t *venc = inst->priv;
        struct vb2_v4l2_buffer *vbuf;
        u32 flags;

        if (inst->state == VPU_CODEC_STATE_DEINIT)
                return -EINVAL;

        vbuf = to_vb2_v4l2_buffer(vb);
        if (inst->state == VPU_CODEC_STATE_STARTED)
                inst->state = VPU_CODEC_STATE_ACTIVE;

        flags = vbuf->flags;
        if (venc->request_key_frame) {
                vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME;
                venc->request_key_frame = 0;
        }
        if (venc->bitrate_change) {
                vpu_session_update_parameters(inst, &venc->params);
                venc->bitrate_change = false;
        }
        dev_dbg(inst->dev, "[%d][INPUT  TS]%32lld\n", inst->id, vb->timestamp);
        vpu_iface_input_frame(inst, vb);
        vbuf->flags = flags;
        venc->input_ready = false;
        venc->frame_count++;
        vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE);

        return 0;
}

static int venc_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb)
{
        struct venc_t *venc;
        struct venc_frame_t *frame = NULL;
        struct vb2_v4l2_buffer *vbuf;
        int ret;

        venc = inst->priv;
        if (list_empty(&venc->frames))
                return -EINVAL;

        frame = list_first_entry(&venc->frames, struct venc_frame_t, list);
        vbuf = to_vb2_v4l2_buffer(vb);
        v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
        ret = venc_get_one_encoded_frame(inst, frame, vbuf);
        if (ret)
                return ret;

        list_del_init(&frame->list);
        kfree(frame);
        return 0;
}

static void venc_on_queue_empty(struct vpu_inst *inst, u32 type)
{
        struct venc_t *venc = inst->priv;

        if (V4L2_TYPE_IS_OUTPUT(type))
                return;

        if (venc->stopped)
                venc_set_last_buffer_dequeued(inst);
}

static int venc_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
{
        struct venc_t *venc = inst->priv;
        int num = -1;

        switch (i) {
        case 0:
                num = scnprintf(str, size, "profile = %d\n", venc->params.profile);
                break;
        case 1:
                num = scnprintf(str, size, "level = %d\n", venc->params.level);
                break;
        case 2:
                num = scnprintf(str, size, "fps = %d/%d\n",
                                venc->params.frame_rate.numerator,
                                venc->params.frame_rate.denominator);
                break;
        case 3:
                num = scnprintf(str, size, "%d x %d -> %d x %d\n",
                                venc->params.src_width,
                                venc->params.src_height,
                                venc->params.out_width,
                                venc->params.out_height);
                break;
        case 4:
                num = scnprintf(str, size, "(%d, %d)  %d x %d\n",
                                venc->params.crop.left,
                                venc->params.crop.top,
                                venc->params.crop.width,
                                venc->params.crop.height);
                break;
        case 5:
                num = scnprintf(str, size,
                                "enable = 0x%x, input = %d, encode = %d, ready = %d, stopped = %d\n",
                                venc->enable,
                                venc->frame_count, venc->encode_count,
                                venc->ready_count,
                                venc->stopped);
                break;
        case 6:
                num = scnprintf(str, size, "gop = %d\n", venc->params.gop_length);
                break;
        case 7:
                num = scnprintf(str, size, "bframes = %d\n", venc->params.bframes);
                break;
        case 8:
                num = scnprintf(str, size, "rc: %s, mode = %d, bitrate = %d(%d), qp = %d\n",
                                venc->params.rc_enable ? "enable" : "disable",
                                venc->params.rc_mode,
                                venc->params.bitrate,
                                venc->params.bitrate_max,
                                venc->params.i_frame_qp);
                break;
        case 9:
                num = scnprintf(str, size, "sar: enable = %d, idc = %d, %d x %d\n",
                                venc->params.sar.enable,
                                venc->params.sar.idc,
                                venc->params.sar.width,
                                venc->params.sar.height);

                break;
        case 10:
                num = scnprintf(str, size,
                                "colorspace: primaries = %d, transfer = %d, matrix = %d, full_range = %d\n",
                                venc->params.color.primaries,
                                venc->params.color.transfer,
                                venc->params.color.matrix,
                                venc->params.color.full_range);
                break;
        case 11:
                num = scnprintf(str, size, "skipped: count = %d, bytes = %d\n",
                                venc->skipped_count, venc->skipped_bytes);
                break;
        default:
                break;
        }

        return num;
}

static struct vpu_inst_ops venc_inst_ops = {
        .ctrl_init = venc_ctrl_init,
        .check_ready = venc_check_ready,
        .input_done = venc_input_done,
        .get_one_frame = venc_frame_encoded,
        .stop_done = venc_stop_done,
        .event_notify = venc_event_notify,
        .release = venc_release,
        .cleanup = venc_cleanup,
        .start = venc_start_session,
        .mem_request = venc_request_mem_resource,
        .stop = venc_stop_session,
        .process_output = venc_process_output,
        .process_capture = venc_process_capture,
        .on_queue_empty = venc_on_queue_empty,
        .get_debug_info = venc_get_debug_info,
        .wait_prepare = vpu_inst_unlock,
        .wait_finish = vpu_inst_lock,
};

static void venc_init(struct file *file)
{
        struct vpu_inst *inst = to_inst(file);
        struct venc_t *venc;
        struct v4l2_format f;
        struct v4l2_streamparm parm;

        venc = inst->priv;
        venc->params.qp_min = 1;
        venc->params.qp_max = 51;
        venc->params.qp_min_i = 1;
        venc->params.qp_max_i = 51;
        venc->params.bitrate_min = BITRATE_MIN;

        memset(&f, 0, sizeof(f));
        f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
        f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
        f.fmt.pix_mp.width = 1280;
        f.fmt.pix_mp.height = 720;
        f.fmt.pix_mp.field = V4L2_FIELD_NONE;
        venc_s_fmt(file, &inst->fh, &f);

        memset(&f, 0, sizeof(f));
        f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
        f.fmt.pix_mp.width = 1280;
        f.fmt.pix_mp.height = 720;
        f.fmt.pix_mp.field = V4L2_FIELD_NONE;
        venc_s_fmt(file, &inst->fh, &f);

        memset(&parm, 0, sizeof(parm));
        parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
        parm.parm.capture.timeperframe.numerator = 1;
        parm.parm.capture.timeperframe.denominator = 30;
        venc_s_parm(file, &inst->fh, &parm);
}

static int venc_open(struct file *file)
{
        struct vpu_inst *inst;
        struct venc_t *venc;
        int ret;

        inst = kzalloc_obj(*inst);
        if (!inst)
                return -ENOMEM;

        venc = kzalloc_obj(*venc);
        if (!venc) {
                kfree(inst);
                return -ENOMEM;
        }

        inst->ops = &venc_inst_ops;
        inst->formats = venc_formats;
        inst->type = VPU_CORE_TYPE_ENC;
        inst->priv = venc;
        INIT_LIST_HEAD(&venc->frames);
        init_waitqueue_head(&venc->wq);

        ret = vpu_v4l2_open(file, inst);
        if (ret)
                return ret;

        inst->min_buffer_out = VENC_MIN_BUFFER_OUT;
        inst->min_buffer_cap = VENC_MIN_BUFFER_CAP;
        venc_init(file);

        return 0;
}

static const struct v4l2_file_operations venc_fops = {
        .owner = THIS_MODULE,
        .open = venc_open,
        .release = vpu_v4l2_close,
        .unlocked_ioctl = video_ioctl2,
        .poll = v4l2_m2m_fop_poll,
        .mmap = v4l2_m2m_fop_mmap,
};

const struct v4l2_ioctl_ops *venc_get_ioctl_ops(void)
{
        return &venc_ioctl_ops;
}

const struct v4l2_file_operations *venc_get_fops(void)
{
        return &venc_fops;
}