diff mbox series

[v14,07/13] media: amphion: add v4l2 m2m vpu encoder stateful driver

Message ID 21224efcf6e54e021a3e0f594f2b503247f77f74.1638865027.git.ming.qian@nxp.com
State Superseded
Headers show
Series amphion video decoder/encoder driver | expand

Commit Message

Ming Qian Dec. 7, 2021, 8:24 a.m. UTC
This consists of video encoder implementation plus encoder controls.

Signed-off-by: Ming Qian <ming.qian@nxp.com>
Signed-off-by: Shijie Qin <shijie.qin@nxp.com>
Signed-off-by: Zhou Peng <eagle.zhou@nxp.com>
---
 drivers/media/platform/amphion/venc.c | 1354 +++++++++++++++++++++++++
 1 file changed, 1354 insertions(+)
 create mode 100644 drivers/media/platform/amphion/venc.c

Comments

Hans Verkuil Jan. 20, 2022, 3:17 p.m. UTC | #1
Hi Ming,

Some comments below:

On 12/7/21 09:24, Ming Qian wrote:
> This consists of video encoder implementation plus encoder controls.
> 
> Signed-off-by: Ming Qian <ming.qian@nxp.com>
> Signed-off-by: Shijie Qin <shijie.qin@nxp.com>
> Signed-off-by: Zhou Peng <eagle.zhou@nxp.com>
> ---
>  drivers/media/platform/amphion/venc.c | 1354 +++++++++++++++++++++++++
>  1 file changed, 1354 insertions(+)
>  create mode 100644 drivers/media/platform/amphion/venc.c
> 
> diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
> new file mode 100644
> index 000000000000..ca394504dbd8
> --- /dev/null
> +++ b/drivers/media/platform/amphion/venc.c
> @@ -0,0 +1,1354 @@
> +// 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 <media/videobuf2-vmalloc.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	(1 << 0)
> +#define VENC_CAPTURE_ENABLE	(1 << 1)
> +#define VENC_ENABLE_MASK	(VENC_OUTPUT_ENABLE | VENC_CAPTURE_ENABLE)
> +#define VENC_MAX_BUF_CNT	8
> +
> +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 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,
> +		.num_planes = 2,
> +		.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
> +	},
> +	{
> +		.pixfmt = V4L2_PIX_FMT_H264,
> +		.num_planes = 1,
> +		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> +	},
> +	{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->num_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 = cur_fmt->sizeimage[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);
> +
> +	vpu_try_fmt_common(inst, f);
> +
> +	return 0;
> +}
> +
> +static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct vpu_inst *inst = to_inst(file);
> +	const 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;
> +	int i;
> +
> +	if (!inst || !inst->fh.m2m_ctx)
> +		return -EINVAL;
> +	q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
> +	if (!q)
> +		return -EINVAL;
> +	if (vb2_is_streaming(q))

That should be vb2_is_busy(): once buffers have been allocated you can no longer
change the format. Same issue is present in the next patch.

Regards,

	Hans

> +		return -EBUSY;
> +
> +	fmt = vpu_try_fmt_common(inst, f);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	cur_fmt = vpu_get_format(inst, f->type);
> +
> +	cur_fmt->pixfmt = fmt->pixfmt;
> +	cur_fmt->num_planes = fmt->num_planes;
> +	cur_fmt->flags = fmt->flags;
> +	cur_fmt->width = pix_mp->width;
> +	cur_fmt->height = pix_mp->height;
> +	for (i = 0; i < fmt->num_planes; i++) {
> +		cur_fmt->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage;
> +		cur_fmt->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline;
> +	}
> +
> +	if (pix_mp->field != V4L2_FIELD_ANY)
> +		cur_fmt->field = pix_mp->field;
> +
> +	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)) {
> +		if (!vpu_color_check_primaries(pix_mp->colorspace)) {
> +			venc->params.color.primaries = pix_mp->colorspace;
> +			vpu_color_get_default(venc->params.color.primaries,
> +						&venc->params.color.transfer,
> +						&venc->params.color.matrix,
> +						&venc->params.color.full_range);
> +		}
> +		if (!vpu_color_check_transfers(pix_mp->xfer_func))
> +			venc->params.color.transfer = pix_mp->xfer_func;
> +		if (!vpu_color_check_matrix(pix_mp->ycbcr_enc))
> +			venc->params.color.matrix = pix_mp->ycbcr_enc;
> +		if (!vpu_color_check_full_range(pix_mp->quantization))
> +			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 = &parm->parm.capture.timeperframe;
> +
> +	if (!parm)
> +		return -EINVAL;
> +
> +	if (!vpu_helper_check_type(inst, parm->type))
> +		return -EINVAL;
> +
> +	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 = &parm->parm.capture.timeperframe;
> +	unsigned long n, d;
> +
> +	if (!parm)
> +		return -EINVAL;
> +
> +	if (!vpu_helper_check_type(inst, parm->type))
> +		return -EINVAL;
> +
> +	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 || !inst->fh.m2m_ctx)
> +		return -EINVAL;
> +	if (inst->state != VPU_CODEC_STATE_DRAIN)
> +		return 0;
> +
> +	if (v4l2_m2m_num_src_bufs_ready(inst->fh.m2m_ctx))
> +		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);
> +		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;
> +
> +	vpu_inst_lock(inst);
> +	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_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_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;
> +	}
> +	vpu_inst_unlock(inst);
> +
> +	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_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> +			V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
> +			V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
> +			0x0,
> +			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_GOP_SIZE, 0, (1 << 16) - 1, 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);
> +
> +	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 (!inst || !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;
> +
> +	if (!vbuf)
> +		return -EAGAIN;
> +
> +	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->vb2_buf.timestamp = frame->info.timestamp;
> +	vbuf->field = inst->cap_format.field;
> +	vbuf->flags |= frame->info.pic_type;
> +	vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
> +	dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, frame->info.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 || !inst->priv || !inst->fh.m2m_ctx)
> +		return -EINVAL;
> +
> +	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);
> +		vfree(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 (!inst || !info)
> +		return -EINVAL;
> +	venc = inst->priv;
> +	frame = vzalloc(sizeof(*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_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
> +{
> +	struct vb2_v4l2_buffer *vbuf;
> +
> +	if (!inst || !frame || !inst->fh.m2m_ctx)
> +		return;
> +
> +	vpu_inst_lock(inst);
> +	if (!venc_get_enable(inst->priv, frame->type))
> +		goto exit;
> +	vbuf = vpu_find_buf_by_sequence(inst, frame->type, frame->sequence);
> +	if (!vbuf) {
> +		dev_err(inst->dev, "[%d] can't find buf: type %d, sequence %d\n",
> +				inst->id, frame->type, frame->sequence);
> +		goto exit;
> +	}
> +
> +	vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
> +	if (V4L2_TYPE_IS_OUTPUT(frame->type))
> +		v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
> +	else
> +		v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
> +	v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
> +exit:
> +	vpu_inst_unlock(inst);
> +}
> +
> +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);
> +}
> +
> +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;
> +	if (venc)
> +		vfree(venc);
> +	inst->priv = NULL;
> +	vfree(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;
> +	ret = vpu_session_configure_codec(inst);
> +	if (ret)
> +		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;
> +
> +	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]);
> +	for (i = 0; i < ARRAY_SIZE(venc->act); i++)
> +		vpu_free_dma(&venc->act[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]);
> +}
> +
> +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);
> +		vfree(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_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_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;
> +
> +	if (!inst || !inst->fh.m2m_ctx)
> +		return -EINVAL;
> +
> +	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);
> +	vfree(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: mode = %d, bitrate = %d, qp = %d\n",
> +				venc->params.rc_mode,
> +				venc->params.bitrate,
> +				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,
> +	.buf_done = venc_buf_done,
> +	.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_max = BITRATE_MAX;
> +	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;
> +	f.fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
> +	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 = vzalloc(sizeof(*inst));
> +	if (!inst)
> +		return -ENOMEM;
> +
> +	venc = vzalloc(sizeof(*venc));
> +	if (!venc) {
> +		vfree(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;
> +
> +	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;
> +}
diff mbox series

Patch

diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
new file mode 100644
index 000000000000..ca394504dbd8
--- /dev/null
+++ b/drivers/media/platform/amphion/venc.c
@@ -0,0 +1,1354 @@ 
+// 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 <media/videobuf2-vmalloc.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	(1 << 0)
+#define VENC_CAPTURE_ENABLE	(1 << 1)
+#define VENC_ENABLE_MASK	(VENC_OUTPUT_ENABLE | VENC_CAPTURE_ENABLE)
+#define VENC_MAX_BUF_CNT	8
+
+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 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,
+		.num_planes = 2,
+		.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+	},
+	{
+		.pixfmt = V4L2_PIX_FMT_H264,
+		.num_planes = 1,
+		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+	},
+	{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->num_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 = cur_fmt->sizeimage[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);
+
+	vpu_try_fmt_common(inst, f);
+
+	return 0;
+}
+
+static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct vpu_inst *inst = to_inst(file);
+	const 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;
+	int i;
+
+	if (!inst || !inst->fh.m2m_ctx)
+		return -EINVAL;
+	q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
+	if (!q)
+		return -EINVAL;
+	if (vb2_is_streaming(q))
+		return -EBUSY;
+
+	fmt = vpu_try_fmt_common(inst, f);
+	if (!fmt)
+		return -EINVAL;
+
+	cur_fmt = vpu_get_format(inst, f->type);
+
+	cur_fmt->pixfmt = fmt->pixfmt;
+	cur_fmt->num_planes = fmt->num_planes;
+	cur_fmt->flags = fmt->flags;
+	cur_fmt->width = pix_mp->width;
+	cur_fmt->height = pix_mp->height;
+	for (i = 0; i < fmt->num_planes; i++) {
+		cur_fmt->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage;
+		cur_fmt->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline;
+	}
+
+	if (pix_mp->field != V4L2_FIELD_ANY)
+		cur_fmt->field = pix_mp->field;
+
+	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)) {
+		if (!vpu_color_check_primaries(pix_mp->colorspace)) {
+			venc->params.color.primaries = pix_mp->colorspace;
+			vpu_color_get_default(venc->params.color.primaries,
+						&venc->params.color.transfer,
+						&venc->params.color.matrix,
+						&venc->params.color.full_range);
+		}
+		if (!vpu_color_check_transfers(pix_mp->xfer_func))
+			venc->params.color.transfer = pix_mp->xfer_func;
+		if (!vpu_color_check_matrix(pix_mp->ycbcr_enc))
+			venc->params.color.matrix = pix_mp->ycbcr_enc;
+		if (!vpu_color_check_full_range(pix_mp->quantization))
+			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 = &parm->parm.capture.timeperframe;
+
+	if (!parm)
+		return -EINVAL;
+
+	if (!vpu_helper_check_type(inst, parm->type))
+		return -EINVAL;
+
+	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 = &parm->parm.capture.timeperframe;
+	unsigned long n, d;
+
+	if (!parm)
+		return -EINVAL;
+
+	if (!vpu_helper_check_type(inst, parm->type))
+		return -EINVAL;
+
+	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 || !inst->fh.m2m_ctx)
+		return -EINVAL;
+	if (inst->state != VPU_CODEC_STATE_DRAIN)
+		return 0;
+
+	if (v4l2_m2m_num_src_bufs_ready(inst->fh.m2m_ctx))
+		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);
+		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;
+
+	vpu_inst_lock(inst);
+	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_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_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;
+	}
+	vpu_inst_unlock(inst);
+
+	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_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+			V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+			V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+			0x0,
+			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_GOP_SIZE, 0, (1 << 16) - 1, 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);
+
+	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 (!inst || !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;
+
+	if (!vbuf)
+		return -EAGAIN;
+
+	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->vb2_buf.timestamp = frame->info.timestamp;
+	vbuf->field = inst->cap_format.field;
+	vbuf->flags |= frame->info.pic_type;
+	vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+	dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, frame->info.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 || !inst->priv || !inst->fh.m2m_ctx)
+		return -EINVAL;
+
+	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);
+		vfree(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 (!inst || !info)
+		return -EINVAL;
+	venc = inst->priv;
+	frame = vzalloc(sizeof(*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_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
+{
+	struct vb2_v4l2_buffer *vbuf;
+
+	if (!inst || !frame || !inst->fh.m2m_ctx)
+		return;
+
+	vpu_inst_lock(inst);
+	if (!venc_get_enable(inst->priv, frame->type))
+		goto exit;
+	vbuf = vpu_find_buf_by_sequence(inst, frame->type, frame->sequence);
+	if (!vbuf) {
+		dev_err(inst->dev, "[%d] can't find buf: type %d, sequence %d\n",
+				inst->id, frame->type, frame->sequence);
+		goto exit;
+	}
+
+	vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+	if (V4L2_TYPE_IS_OUTPUT(frame->type))
+		v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+	else
+		v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+	v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+exit:
+	vpu_inst_unlock(inst);
+}
+
+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);
+}
+
+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;
+	if (venc)
+		vfree(venc);
+	inst->priv = NULL;
+	vfree(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;
+	ret = vpu_session_configure_codec(inst);
+	if (ret)
+		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;
+
+	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]);
+	for (i = 0; i < ARRAY_SIZE(venc->act); i++)
+		vpu_free_dma(&venc->act[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]);
+}
+
+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);
+		vfree(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_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_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;
+
+	if (!inst || !inst->fh.m2m_ctx)
+		return -EINVAL;
+
+	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);
+	vfree(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: mode = %d, bitrate = %d, qp = %d\n",
+				venc->params.rc_mode,
+				venc->params.bitrate,
+				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,
+	.buf_done = venc_buf_done,
+	.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_max = BITRATE_MAX;
+	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;
+	f.fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
+	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 = vzalloc(sizeof(*inst));
+	if (!inst)
+		return -ENOMEM;
+
+	venc = vzalloc(sizeof(*venc));
+	if (!venc) {
+		vfree(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;
+
+	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;
+}