linux-imx/drivers/mxc/hantro_v4l2/vsi-v4l2-enc.c
Ming Qian 166bdb2afd LF-12804: mxc: vpu: hantro_v4l2: filter out unapplicable ctrl
some ctrl is only applicable for certain format, if the format is not
supported by vpu hardware, just filter out it.

Signed-off-by: Ming Qian <ming.qian@nxp.com>
Reviewed-by: TaoJiang <tao.jiang_2@nxp.com>
2024-07-02 10:07:57 +09:00

1658 lines
45 KiB
C

/*
* VSI V4L2 encoder entry.
*
* Copyright (c) 2019, VeriSilicon Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License version 2 for more details.
*
* You may obtain a copy of the GNU General Public License
* Version 2 at the following locations:
* https://opensource.org/licenses/gpl-2.0.php
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/videodev2.h>
#include <linux/v4l2-dv-timings.h>
#include <linux/platform_device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-vmalloc.h>
#include <linux/delay.h>
#include <linux/version.h>
#include "vsi-v4l2-priv.h"
static int vsi_enc_querycap(
struct file *file,
void *priv,
struct v4l2_capability *cap)
{
struct vsi_v4l2_dev_info *hwinfo;
v4l2_klog(LOGLVL_CONFIG, "%s", __func__);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
hwinfo = vsiv4l2_get_hwinfo();
if (hwinfo->encformat == 0)
return -ENODEV;
strlcpy(cap->driver, "vsi_v4l2", sizeof("vsi_v4l2"));
strlcpy(cap->card, "vsi_v4l2enc", sizeof("vsi_v4l2enc"));
strlcpy(cap->bus_info, "platform:vsi_v4l2enc", sizeof("platform:vsi_v4l2enc"));
cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int vsi_enc_reqbufs(
struct file *filp,
void *priv,
struct v4l2_requestbuffers *p)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
int ret;
struct vb2_queue *q;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(p->type, ctx->flag))
return -EINVAL;
if (binputqueue(p->type))
q = &ctx->input_que;
else
q = &ctx->output_que;
ret = vb2_reqbufs(q, p);
if (!binputqueue(p->type) && p->count == 0)
set_bit(CTX_FLAG_ENC_FLUSHBUF, &ctx->flag);
v4l2_klog(LOGLVL_BRIEF, "%llx:%s:%d ask for %d buffer, got %d:%d:%d",
ctx->ctxid, __func__, p->type, p->count, q->num_buffers, ret, ctx->status);
return ret;
}
static int vsi_enc_create_bufs(struct file *filp, void *priv,
struct v4l2_create_buffers *create)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
int ret;
struct vb2_queue *q;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(create->format.type, ctx->flag))
return -EINVAL;
if (binputqueue(create->format.type))
q = &ctx->input_que;
else
q = &ctx->output_que;
ret = vb2_create_bufs(q, create);
if (!binputqueue(create->format.type) && create->count == 0)
set_bit(CTX_FLAG_ENC_FLUSHBUF, &ctx->flag);
v4l2_klog(LOGLVL_BRIEF, "%llx:%s:%d create for %d buffer, got %d:%d:%d\n",
ctx->ctxid, __func__, create->format.type, create->count,
q->num_buffers, ret, ctx->status);
return ret;
}
static int vsi_enc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
v4l2_klog(LOGLVL_CONFIG, "%s", __func__);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(parm->type, ctx->flag))
return -EINVAL;
if (!binputqueue(parm->type))
return -EINVAL;
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
memset(parm->parm.output.reserved, 0, sizeof(parm->parm.output.reserved));
if (!parm->parm.output.timeperframe.denominator)
parm->parm.output.timeperframe.denominator = ctx->mediacfg.outputparam.timeperframe.denominator;
else
ctx->mediacfg.outputparam.timeperframe.denominator = parm->parm.output.timeperframe.denominator;
if (!parm->parm.output.timeperframe.numerator)
parm->parm.output.timeperframe.numerator = ctx->mediacfg.outputparam.timeperframe.numerator;
else
ctx->mediacfg.outputparam.timeperframe.numerator = parm->parm.output.timeperframe.numerator;
ctx->mediacfg.encparams.general.inputRateNumer = parm->parm.output.timeperframe.denominator;
ctx->mediacfg.encparams.general.inputRateDenom = parm->parm.output.timeperframe.numerator;
ctx->mediacfg.encparams.general.outputRateNumer = parm->parm.output.timeperframe.denominator;
ctx->mediacfg.encparams.general.outputRateDenom = parm->parm.output.timeperframe.numerator;
parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
mutex_unlock(&ctx->ctxlock);
v4l2_klog(LOGLVL_BRIEF, "%llx:%s set fps number %d,denom %d\n",
ctx->ctxid, __func__, ctx->mediacfg.encparams.general.inputRateNumer, ctx->mediacfg.encparams.general.inputRateDenom);
return 0;
}
static int vsi_enc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
v4l2_klog(LOGLVL_CONFIG, "%s", __func__);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(parm->type, ctx->flag))
return -EINVAL;
if (!binputqueue(parm->type))
return -EINVAL;
parm->parm.output = ctx->mediacfg.outputparam;
return 0;
}
static int vsi_enc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
v4l2_klog(LOGLVL_CONFIG, "%s:%d", __func__, f->type);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(f->type, ctx->flag))
return -EINVAL;
return vsiv4l2_getfmt(ctx, f);
}
static int vsi_enc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
int ret;
v4l2_klog(LOGLVL_CONFIG, "%s fmt:%x, res:%dx%d\n", __func__,
f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width,
f->fmt.pix_mp.height);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(f->type, ctx->flag))
return -EINVAL;
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
ret = vsiv4l2_setfmt(ctx, f);
set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
mutex_unlock(&ctx->ctxlock);
return ret;
}
static int vsi_enc_querybuf(
struct file *filp,
void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
struct vb2_queue *q;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(buf->type, ctx->flag))
return -EINVAL;
if (binputqueue(buf->type))
q = &ctx->input_que;
else
q = &ctx->output_que;
v4l2_klog(LOGLVL_FLOW, "%s:%lx:%d:%d", __func__, ctx->flag, buf->type, buf->index);
ret = vb2_querybuf(q, buf);
if (buf->memory == V4L2_MEMORY_MMAP) {
if (ret == 0 && q == &ctx->output_que)
buf->m.planes[0].m.mem_offset += OUTF_BASE;
}
return ret;
}
static int vsi_enc_trystartenc(struct vsi_v4l2_ctx *ctx)
{
int ret = 0;
v4l2_klog(LOGLVL_BRIEF, "%s:streaming:%d:%d, queued buf:%d:%d",
__func__, ctx->input_que.streaming, ctx->output_que.streaming,
ctx->input_que.queued_count, ctx->output_que.queued_count);
if (vb2_is_streaming(&ctx->input_que) && vb2_is_streaming(&ctx->output_que)) {
if ((ctx->status == VSI_STATUS_INIT ||
ctx->status == ENC_STATUS_STOPPED ||
ctx->status == ENC_STATUS_EOS) &&
ctx->input_que.queued_count >= ctx->input_que.min_buffers_needed &&
ctx->output_que.queued_count >= ctx->output_que.min_buffers_needed) {
ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMON, NULL);
if (ret == 0) {
ctx->status = ENC_STATUS_ENCODING;
if (test_and_clear_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag)) {
ret |= vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_CMD_STOP, NULL);
ctx->status = ENC_STATUS_DRAINING;
}
}
}
}
return ret;
}
static int vsi_enc_qbuf(struct file *filp, void *priv, struct v4l2_buffer *buf)
{
int ret;
//struct vb2_queue *vq = vb->vb2_queue;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
struct video_device *vdev = ctx->dev->venc;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(buf->type, ctx->flag))
return -EINVAL;
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
if (!binputqueue(buf->type))
ret = vb2_qbuf(&ctx->output_que, vdev->v4l2_dev->mdev, buf);
else {
if (test_and_clear_bit(CTX_FLAG_FORCEIDR_BIT, &ctx->flag))
ctx->srcvbufflag[buf->index] |= FORCE_IDR;
ret = vb2_qbuf(&ctx->input_que, vdev->v4l2_dev->mdev, buf);
}
v4l2_klog(LOGLVL_FLOW, "%llx:%s:%d:%d:%d, %d:%d, %d:%d",
ctx->ctxid, __func__, buf->type, buf->index, buf->bytesused,
buf->m.planes[0].bytesused, buf->m.planes[0].length,
buf->m.planes[1].bytesused, buf->m.planes[1].length);
if (ret == 0 && ctx->status != ENC_STATUS_ENCODING && ctx->status != ENC_STATUS_EOS)
ret = vsi_enc_trystartenc(ctx);
mutex_unlock(&ctx->ctxlock);
return ret;
}
static int vsi_enc_streamon(struct file *filp, void *priv, enum v4l2_buf_type type)
{
int ret = 0;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
v4l2_klog(LOGLVL_BRIEF, "%s:%d", __func__, type);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(type, ctx->flag))
return -EINVAL;
if (ctx->status == ENC_STATUS_ENCODING)
return 0;
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
if (!binputqueue(type)) {
ret = vb2_streamon(&ctx->output_que, type);
printbufinfo(&ctx->output_que);
} else {
ret = vb2_streamon(&ctx->input_que, type);
printbufinfo(&ctx->input_que);
}
if (ret == 0) {
if (ctx->status == ENC_STATUS_EOS) {
//to avoid no queued buf when streamon
ctx->status = ENC_STATUS_STOPPED;
}
ret = vsi_enc_trystartenc(ctx);
}
mutex_unlock(&ctx->ctxlock);
return ret;
}
static int vsi_enc_streamoff(
struct file *file,
void *priv,
enum v4l2_buf_type type)
{
int i, ret;
u32 binput = binputqueue(type);
struct vsi_v4l2_ctx *ctx = fh_to_ctx(priv);
struct vb2_queue *q;
v4l2_klog(LOGLVL_BRIEF, "%s:%d", __func__, type);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(type, ctx->flag))
return -EINVAL;
if (ctx->status == VSI_STATUS_INIT)
return 0;
if (binput)
q = &ctx->input_que;
else
q = &ctx->output_que;
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
if (binput)
vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMOFF_OUTPUT, &binput);
else
vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMOFF_CAPTURE, &binput);
mutex_unlock(&ctx->ctxlock);
if (binput)
ret = wait_event_interruptible(ctx->capoffdone_queue, vsi_checkctx_outputoffdone(ctx));
else
ret = wait_event_interruptible(ctx->capoffdone_queue, vsi_checkctx_capoffdone(ctx));
if (ret != 0)
v4l2_klog(LOGLVL_WARNING, "%llx binput:%d, enc wait strmoff done fail\n",
ctx->ctxid, binput);
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
ctx->status = ENC_STATUS_STOPPED;
if (binput) {
clear_bit(CTX_FLAG_FORCEIDR_BIT, &ctx->flag);
for (i = 0; i < VIDEO_MAX_FRAME; i++)
ctx->srcvbufflag[i] = 0;
}
return_all_buffers(q, VB2_BUF_STATE_DONE, 1);
ret = vb2_streamoff(q, type);
mutex_unlock(&ctx->ctxlock);
return ret;
}
static int vsi_enc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret = 0;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
struct vb2_queue *q;
struct vb2_buffer *vb;
struct vsi_vpu_buf *vsibuf;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(p->type, ctx->flag))
return -EINVAL;
if (binputqueue(p->type))
q = &ctx->input_que;
else
q = &ctx->output_que;
if (ctx->status == ENC_STATUS_STOPPED ||
ctx->status == ENC_STATUS_EOS) {
p->bytesused = 0;
return -EPIPE;
}
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
ret = vb2_dqbuf(q, p, file->f_flags & O_NONBLOCK);
if (ret == 0) {
vb = q->bufs[p->index];
vsibuf = vb_to_vsibuf(vb);
list_del(&vsibuf->list);
p->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
if (!binputqueue(p->type)) {
if (ctx->vbufflag[p->index] & FRAMETYPE_I)
p->flags |= V4L2_BUF_FLAG_KEYFRAME;
else if (ctx->vbufflag[p->index] & FRAMETYPE_P)
p->flags |= V4L2_BUF_FLAG_PFRAME;
else if (ctx->vbufflag[p->index] & FRAMETYPE_B)
p->flags |= V4L2_BUF_FLAG_BFRAME;
}
}
if (!binputqueue(p->type)) {
if (ret == 0) {
if (ctx->vbufflag[p->index] & LAST_BUFFER_FLAG) {
vsi_v4l2_sendeos(ctx);
if (ctx->status == ENC_STATUS_DRAINING)
ctx->status = ENC_STATUS_EOS;
v4l2_klog(LOGLVL_BRIEF, "dqbuf get eos flag");
}
}
}
mutex_unlock(&ctx->ctxlock);
v4l2_klog(LOGLVL_FLOW, "%s:%d:%d:%d:%x:%d", __func__, p->type, p->index, ret, p->flags, ctx->status);
return ret;
}
static int vsi_enc_prepare_buf(
struct file *file,
void *priv,
struct v4l2_buffer *p)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
struct vb2_queue *q;
struct video_device *vdev = ctx->dev->venc;
v4l2_klog(LOGLVL_FLOW, "%s:%d", __func__, p->type);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(p->type, ctx->flag))
return -EINVAL;
if (binputqueue(p->type))
q = &ctx->input_que;
else
q = &ctx->output_que;
return vb2_prepare_buf(q, vdev->v4l2_dev->mdev, p);
}
static int vsi_enc_expbuf(
struct file *file,
void *priv,
struct v4l2_exportbuffer *p)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
struct vb2_queue *q;
v4l2_klog(LOGLVL_FLOW, "%s:%d", __func__, p->type);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(p->type, ctx->flag))
return -EINVAL;
if (binputqueue(p->type))
q = &ctx->input_que;
else
q = &ctx->output_que;
return vb2_expbuf(q, p);
}
static int vsi_enc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
vsiv4l2_verifyfmt(ctx, f, 1);
return 0;
}
static int vsi_enc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
struct vsi_video_fmt *pfmt;
int braw = brawfmt(ctx->flag, f->type);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (!isvalidtype(f->type, ctx->flag))
return -EINVAL;
pfmt = vsi_enum_encformat(f->index, braw);
if (pfmt == NULL)
return -EINVAL;
if (pfmt->name && strlen(pfmt->name))
strlcpy(f->description, pfmt->name, strlen(pfmt->name) + 1);
f->pixelformat = pfmt->fourcc;
f->flags = pfmt->flag;
v4l2_klog(LOGLVL_CONFIG, "%s:%d:%d:%x", __func__, f->index, f->type, pfmt->fourcc);
return 0;
}
static int vsi_enc_valid_crop(struct vsi_v4l2_ctx *ctx)
{
struct v4l2_daemon_enc_general_cmd *general = &ctx->mediacfg.encparams.general;
struct v4l2_frmsizeenum fsize;
vsi_enum_encfsize(&fsize, ctx->mediacfg.outfmt_fourcc);
general->horOffsetSrc = ALIGN(general->horOffsetSrc, fsize.stepwise.step_width);
general->verOffsetSrc = ALIGN(general->verOffsetSrc, fsize.stepwise.step_height);
general->width = ALIGN(general->width, fsize.stepwise.step_width);
general->height = ALIGN(general->height, fsize.stepwise.step_height);
general->width = min(general->width, ctx->mediacfg.width_src - general->horOffsetSrc);
general->width = max_t(u32, general->width, fsize.stepwise.min_width);
general->height = min(general->height, ctx->mediacfg.height_src - general->verOffsetSrc);
general->height = max_t(u32, general->height, fsize.stepwise.min_height);
return 0;
}
static int vsi_enc_set_selection(struct file *file, void *prv, struct v4l2_selection *s)
{
int ret = 0;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
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;
ret = vsiv4l2_verifycrop(s);
if (!ret) {
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
pcfg->encparams.general.horOffsetSrc = s->r.left;
pcfg->encparams.general.verOffsetSrc = s->r.top;
pcfg->encparams.general.width = s->r.width;
pcfg->encparams.general.height = s->r.height;
vsi_enc_valid_crop(ctx);
set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
mutex_unlock(&ctx->ctxlock);
}
v4l2_klog(LOGLVL_CONFIG, "%llx:%s:%d,%d,%d,%d",
ctx->ctxid, __func__, s->r.left, s->r.top, s->r.width, s->r.height);
return ret;
}
static int vsi_enc_get_selection(struct file *file, void *prv, struct v4l2_selection *s)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
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:
s->r.left = pcfg->encparams.general.horOffsetSrc;
s->r.top = pcfg->encparams.general.verOffsetSrc;
s->r.width = pcfg->encparams.general.width;
s->r.height = pcfg->encparams.general.height;
break;
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
s->r.left = 0;
s->r.top = 0;
s->r.width = pcfg->width_src;
s->r.height = pcfg->height_src;
break;
default:
return -EINVAL;
}
v4l2_klog(LOGLVL_CONFIG, "%llx:%s:%d,%d,%d,%d",
ctx->ctxid, __func__, s->r.left, s->r.top, s->r.width, s->r.height);
return 0;
}
static int vsi_enc_subscribe_event(
struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
if (!vsi_v4l2_daemonalive())
return -ENODEV;
v4l2_klog(LOGLVL_CONFIG, "%s:%d", __func__, sub->type);
switch (sub->type) {
case V4L2_EVENT_CTRL:
return v4l2_ctrl_subscribe_event(fh, sub);
case V4L2_EVENT_SKIP:
return v4l2_event_subscribe(fh, sub, 16, NULL);
case V4L2_EVENT_EOS:
case V4L2_EVENT_CODEC_ERROR:
case V4L2_EVENT_INVALID_OPTION:
return v4l2_event_subscribe(fh, sub, 0, NULL);
default:
return -EINVAL;
}
}
static int vsi_enc_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd)
{
switch (cmd->cmd) {
case V4L2_ENC_CMD_STOP:
case V4L2_ENC_CMD_START:
case V4L2_ENC_CMD_PAUSE:
case V4L2_ENC_CMD_RESUME:
cmd->flags = 0;
return 0;
default:
return -EINVAL;
}
}
static int vsi_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
int ret = 0;
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (mutex_lock_interruptible(&ctx->ctxlock))
return -EBUSY;
v4l2_klog(LOGLVL_BRIEF, "%s:%d:%d", __func__, ctx->status, cmd->cmd);
switch (cmd->cmd) {
case V4L2_ENC_CMD_STOP:
set_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag);
if (ctx->status == ENC_STATUS_ENCODING) {
ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_CMD_STOP, cmd);
if (ret == 0) {
ctx->status = ENC_STATUS_DRAINING;
clear_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag);
}
}
break;
case V4L2_ENC_CMD_START:
if (ctx->status == ENC_STATUS_STOPPED ||
ctx->status == ENC_STATUS_EOS) {
ret = vb2_streamon(&ctx->input_que, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
break;
ret = vb2_streamon(&ctx->output_que, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
break;
ret = vsi_enc_trystartenc(ctx);
}
break;
case V4L2_ENC_CMD_PAUSE:
case V4L2_ENC_CMD_RESUME:
default:
ret = -EINVAL;
break;
}
mutex_unlock(&ctx->ctxlock);
return ret;
}
static int vsi_enc_encoder_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
struct v4l2_format fmt;
v4l2_klog(LOGLVL_CONFIG, "%s:%x", __func__, fsize->pixel_format);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
if (fsize->index != 0) //only stepwise
return -EINVAL;
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt.fmt.pix_mp.pixelformat = fsize->pixel_format;
if (vsi_find_format(ctx, &fmt) != NULL)
vsi_enum_encfsize(fsize, ctx->mediacfg.outfmt_fourcc);
else {
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt.fmt.pix_mp.pixelformat = fsize->pixel_format;
if (vsi_find_format(ctx, &fmt) == NULL)
return -EINVAL;
vsi_enum_encfsize(fsize, fsize->pixel_format);
}
return 0;
}
/* ioctl handler */
/* take VIDIOC_S_INPUT for example, ioctl goes to V4l2-ioctl.c.: v4l_s_input() -> V4l2-dev.c: v4l2_ioctl_ops.vidioc_s_input() */
/* ioctl cmd could be disabled by v4l2_disable_ioctl() */
static const struct v4l2_ioctl_ops vsi_enc_ioctl = {
.vidioc_querycap = vsi_enc_querycap,
.vidioc_reqbufs = vsi_enc_reqbufs,
.vidioc_create_bufs = vsi_enc_create_bufs,
.vidioc_prepare_buf = vsi_enc_prepare_buf,
//create_buf can be provided now since we don't know buf type in param
.vidioc_querybuf = vsi_enc_querybuf,
.vidioc_qbuf = vsi_enc_qbuf,
.vidioc_dqbuf = vsi_enc_dqbuf,
.vidioc_streamon = vsi_enc_streamon,
.vidioc_streamoff = vsi_enc_streamoff,
.vidioc_s_parm = vsi_enc_s_parm,
.vidioc_g_parm = vsi_enc_g_parm,
//.vidioc_g_fmt_vid_cap = vsi_enc_g_fmt,
.vidioc_g_fmt_vid_cap_mplane = vsi_enc_g_fmt,
//.vidioc_s_fmt_vid_cap = vsi_enc_s_fmt,
.vidioc_s_fmt_vid_cap_mplane = vsi_enc_s_fmt,
.vidioc_expbuf = vsi_enc_expbuf, //this is used to export MMAP ptr as prime fd to user space app
//.vidioc_g_fmt_vid_out = vsi_enc_g_fmt,
.vidioc_g_fmt_vid_out_mplane = vsi_enc_g_fmt,
//.vidioc_s_fmt_vid_out = vsi_enc_s_fmt,
.vidioc_s_fmt_vid_out_mplane = vsi_enc_s_fmt,
//.vidioc_try_fmt_vid_cap = vsi_enc_try_fmt,
.vidioc_try_fmt_vid_cap_mplane = vsi_enc_try_fmt,
//.vidioc_try_fmt_vid_out = vsi_enc_try_fmt,
.vidioc_try_fmt_vid_out_mplane = vsi_enc_try_fmt,
.vidioc_enum_fmt_vid_cap = vsi_enc_enum_fmt,
.vidioc_enum_fmt_vid_out = vsi_enc_enum_fmt,
//.vidioc_g_fmt_vid_out = vsi_enc_g_fmt,
.vidioc_g_fmt_vid_out_mplane = vsi_enc_g_fmt,
.vidioc_s_selection = vsi_enc_set_selection, //VIDIOC_S_SELECTION, VIDIOC_S_CROP
.vidioc_g_selection = vsi_enc_get_selection, //VIDIOC_G_SELECTION, VIDIOC_G_CROP
.vidioc_subscribe_event = vsi_enc_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_try_encoder_cmd = vsi_enc_try_encoder_cmd,
//fixme: encoder cmd stop will make streamoff not coming from ffmpeg. Maybe this is the right way to get finished, check later
.vidioc_encoder_cmd = vsi_enc_encoder_cmd,
.vidioc_enum_framesizes = vsi_enc_encoder_enum_framesizes,
};
/*setup buffer information before real allocation*/
static int vsi_enc_queue_setup(
struct vb2_queue *vq,
unsigned int *nbuffers,
unsigned int *nplanes,
unsigned int sizes[],
struct device *alloc_devs[])
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
int i, ret;
v4l2_klog(LOGLVL_CONFIG, "%llx:%s:%d,%d,%d\n", ctx->ctxid, __func__, *nbuffers, *nplanes, sizes[0]);
ret = vsiv4l2_buffer_config(ctx, vq, nbuffers, nplanes, sizes);
if (ret == 0) {
for (i = 0; i < *nplanes; i++)
alloc_devs[i] = ctx->dev->dev;
}
return ret;
}
static void vsi_enc_buf_queue(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
struct vsi_vpu_buf *vsibuf;
int ret;
v4l2_klog(LOGLVL_FLOW, "%s:%d:%d", __func__, vb->type, vb->index);
vsibuf = vb_to_vsibuf(vb);
if (!binputqueue(vq->type)) {
list_add_tail(&vsibuf->list, &ctx->output_list);
} else {
list_add_tail(&vsibuf->list, &ctx->input_list);
ctx->performance.input_buf_num++;
}
ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_BUF_RDY, vb);
}
static int vsi_enc_buf_init(struct vb2_buffer *vb)
{
return 0;
}
static int vsi_enc_buf_prepare(struct vb2_buffer *vb)
{
/*any valid init operation on buffer vb*/
/*gspca and rockchip both check buffer size here*/
//like vb2_set_plane_payload(vb, 0, 1920*1080);
return 0;
}
static int vsi_enc_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(q->drv_priv);
struct vb2_queue *vq_peer;
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
ctx->out_sequence = 0;
vq_peer = &ctx->output_que;
} else {
ctx->cap_sequence = 0;
vq_peer = &ctx->input_que;
}
if (vb2_is_streaming(vq_peer))
ctx->performance.ts_start = ktime_get_raw();
return 0;
}
static void vsi_enc_stop_streaming(struct vb2_queue *q)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(q->drv_priv);
if (V4L2_TYPE_IS_OUTPUT(q->type))
vsi_v4l2_reset_performance(ctx);
}
static void vsi_enc_buf_finish(struct vb2_buffer *vb)
{
struct vb2_queue *vq = vb->vb2_queue;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
struct vsi_vpu_buf *vsibuf;
struct v4l2_ctrl *ctrl;
if (V4L2_TYPE_IS_OUTPUT(vb->type))
return;
vsibuf = vb_to_vsibuf(vb);
ctrl = v4l2_ctrl_find(ctx->fh.ctrl_handler, V4L2_CID_MPEG_VIDEO_AVERAGE_QP);
if (ctrl)
v4l2_ctrl_s_ctrl(ctrl, vsibuf->average_qp);
}
static void vsi_enc_buf_cleanup(struct vb2_buffer *vb)
{
}
static void vsi_enc_buf_wait_finish(struct vb2_queue *vq)
{
vb2_ops_wait_finish(vq);
}
static void vsi_enc_buf_wait_prepare(struct vb2_queue *vq)
{
vb2_ops_wait_prepare(vq);
}
static struct vb2_ops vsi_enc_qops = {
.queue_setup = vsi_enc_queue_setup,
.wait_prepare = vsi_enc_buf_wait_prepare, /*these two are just mutex protection for done_que*/
.wait_finish = vsi_enc_buf_wait_finish,
.buf_init = vsi_enc_buf_init,
.buf_prepare = vsi_enc_buf_prepare,
.buf_finish = vsi_enc_buf_finish,
.buf_cleanup = vsi_enc_buf_cleanup,
.start_streaming = vsi_enc_start_streaming,
.stop_streaming = vsi_enc_stop_streaming,
.buf_queue = vsi_enc_buf_queue,
//fill_user_buffer
//int (*buf_out_validate)(struct vb2_buffer *vb);
//void (*buf_request_complete)(struct vb2_buffer *vb);
};
static int vsi_v4l2_enc_s_ctrl(struct v4l2_ctrl *ctrl)
{
int ret;
struct vsi_v4l2_ctx *ctx = ctrl_to_ctx(ctrl);
v4l2_klog(LOGLVL_CONFIG, "%s:%x=%d", __func__, ctrl->id, ctrl->val);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.intraPicRate = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
ret = vsi_set_profile(ctx, ctrl->id, ctrl->val);
return ret;
case V4L2_CID_MPEG_VIDEO_BITRATE:
ctx->mediacfg.encparams.general.bitPerSecond = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
ret = vsi_get_Level(ctx, 0, 1, ctrl->val);
if (ret >= 0)
ctx->mediacfg.encparams.specific.enc_h26x_cmd.avclevel = ret;
else
return ret;
break;
case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
ret = vsi_get_Level(ctx, 1, 1, ctrl->val);
if (ret >= 0)
ctx->mediacfg.encparams.specific.enc_h26x_cmd.hevclevel = ret;
else
return ret;
break;
case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMax_vpx = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMax_h26x = ctrl->val;
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMaxI = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMin_vpx = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMin_h26x = ctrl->val;
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMinI = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_B_FRAMES:
if (ctrl->val != 0)
return -EINVAL;
/*in fact nothing to do*/
break;
case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.bFrameQpDelta = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
ctx->mediacfg.encparams.specific.enc_h26x_cmd.hrdConformance = 0;
else
ctx->mediacfg.encparams.specific.enc_h26x_cmd.hrdConformance = 1;
break;
case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
set_bit(CTX_FLAG_FORCEIDR_BIT, &ctx->flag);
break;
case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
break;
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
ctx->mediacfg.multislice_mode = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.sliceSize = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.picRc = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.ctbRc = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpHdrI_h26x = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpHdrI_vpx = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpHdrP_h26x = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.cpbSize = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.chromaQpOffset = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpHdrP_vpx = ctrl->val;
break;
case V4L2_CID_ROTATE:
switch (ctrl->val) {
case 90:
ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_90L;
break;
case 180:
ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_180R;
break;
case 270:
ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_90R;
break;
case 0:
default:
ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_0;
break;
}
break;
case V4L2_CID_ROI:
if (ctrl->p_new.p)
vsiv4l2_setROI(ctx, ctrl->p_new.p);
break;
case V4L2_CID_IPCM:
if (ctrl->p_new.p)
vsiv4l2_setIPCM(ctx, ctrl->p_new.p);
break;
case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER:
ctx->mediacfg.encparams.specific.enc_h26x_cmd.idrHdr = ctrl->val;
break;
default:
return 0;
}
set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
return 0;
}
static int vsi_v4l2_enc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsi_v4l2_ctx *ctx = ctrl_to_ctx(ctrl);
v4l2_klog(LOGLVL_CONFIG, "%s:%x", __func__, ctrl->id);
if (!vsi_v4l2_daemonalive())
return -ENODEV;
switch (ctrl->id) {
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
ctrl->val = ctx->mediacfg.minbuf_4capture; //these two may come from resoultion change
break;
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
ctrl->val = ctx->mediacfg.minbuf_4output;
break;
case V4L2_CID_ROI_COUNT:
ctrl->val = vsiv4l2_getROIcount();
break;
case V4L2_CID_IPCM_COUNT:
ctrl->val = vsiv4l2_getIPCMcount();
break;
default:
return -EINVAL;
}
return 0;
}
/********* for ext ctrl *************/
static bool vsi_enc_ctrl_equal(const struct v4l2_ctrl *ctrl,
union v4l2_ctrl_ptr ptr1,
union v4l2_ctrl_ptr ptr2)
{
//always update now, fix it later
return 0;
}
static void vsi_enc_ctrl_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
union v4l2_ctrl_ptr ptr)
{
void *p = ptr.p + from_idx * ctrl->elem_size;
memset(p, 0, (ctrl->elems - from_idx) * ctrl->elem_size);
}
static void vsi_enc_ctrl_log(const struct v4l2_ctrl *ctrl)
{
//do nothing now
}
static int vsi_enc_ctrl_validate(const struct v4l2_ctrl *ctrl,
union v4l2_ctrl_ptr ptr)
{
//always true
return 0;
}
static const struct v4l2_ctrl_type_ops vsi_enc_type_ops = {
.equal = vsi_enc_ctrl_equal,
.init = vsi_enc_ctrl_init,
.log = vsi_enc_ctrl_log,
.validate = vsi_enc_ctrl_validate,
};
/********* for ext ctrl *************/
static const struct v4l2_ctrl_ops vsi_encctrl_ops = {
.s_ctrl = vsi_v4l2_enc_s_ctrl,
.g_volatile_ctrl = vsi_v4l2_enc_g_volatile_ctrl,
};
static struct v4l2_ctrl_config vsi_v4l2_encctrl_defs[] = {
{
.ops = &vsi_encctrl_ops,
.id = V4L2_CID_ROI_COUNT,
.name = "get max ROI region number",
.type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
.min = 0,
.max = V4L2_MAX_ROI_REGIONS,
.step = 1,
.def = 0,
},
{
.ops = &vsi_encctrl_ops,
.type_ops = &vsi_enc_type_ops,
.id = V4L2_CID_ROI,
.name = "vsi priv v4l2 roi params set",
.type = VSI_V4L2_CMPTYPE_ROI,
.min = 0,
.max = V4L2_MAX_ROI_REGIONS,
.step = 1,
.def = 0,
.elem_size = sizeof(struct v4l2_enc_roi_params),
},
{
.ops = &vsi_encctrl_ops,
.id = V4L2_CID_IPCM_COUNT,
.name = "get max IPCM region number",
.type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
.min = 0,
.max = V4L2_MAX_IPCM_REGIONS,
.step = 1,
.def = 0,
},
{
.ops = &vsi_encctrl_ops,
.type_ops = &vsi_enc_type_ops,
.id = V4L2_CID_IPCM,
.name = "vsi priv v4l2 ipcm params set",
.type = VSI_V4L2_CMPTYPE_IPCM,
.min = 0,
.max = V4L2_MAX_IPCM_REGIONS,
.step = 1,
.def = 0,
.elem_size = sizeof(struct v4l2_enc_ipcm_params),
},
/* kernel defined controls */
{
.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 1,
.max = MAX_INTRA_PIC_RATE,
.step = 1,
.def = DEFAULT_INTRA_PIC_RATE,
},
{
.id = V4L2_CID_MPEG_VIDEO_BITRATE,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 10000,
.max = 288000000,
.step = 1,
.def = 2097152,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
.max = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
.def = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
},
{
.id = V4L2_CID_MPEG_VIDEO_VP8_PROFILE,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_VP8_PROFILE_0,
.max = V4L2_MPEG_VIDEO_VP8_PROFILE_3,
.def = V4L2_MPEG_VIDEO_VP8_PROFILE_0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
.max = V4L2_MPEG_VIDEO_VP9_PROFILE_3,
.def = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_2,
.def = V4L2_MPEG_VIDEO_H264_LEVEL_5_0,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1,
.def = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 51,
.step = 1,
.def = 51,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 51,
.step = 1,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 51,
.step = 1,
.def = 51,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 51,
.step = 1,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
.max = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
.def = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
},
{
.id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 0,
.step = 1,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -1,
.max = 51,
.step = 1,
.def = DEFAULT_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
.max = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
.def = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
},
{
.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_VOLATILE, //volatile contains read
.min = 1,
.max = MAX_MIN_BUFFERS_FOR_CAPTURE,
.step = 1,
.def = 1,
},
{
.id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
.type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_VOLATILE,
.min = 1,
.max = MAX_MIN_BUFFERS_FOR_OUTPUT,
.step = 1,
.def = 1,
},
{
.id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
.type = V4L2_CTRL_TYPE_BUTTON,
.min = 0,
.max = 0,
.step = 0,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -1,
.max = 51,
.step = 1,
.def = DEFAULT_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -1,
.max = 51,
.step = 1,
.def = DEFAULT_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 288000000,
.step = 1,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -12,
.max = 12,
.step = 1,
.def = DEFAULT_CHROMA_QP_INDEX_OFFSET,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -1,
.max = 51,
.step = 1,
.def = DEFAULT_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -1,
.max = 51,
.step = 1,
.def = DEFAULT_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
.type = V4L2_CTRL_TYPE_MENU,
.min = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
.max = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB,
.def = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 1,
.max = 120, //1920 div 16
.step = 1,
.def = 1,
},
{
.id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.min = 0,
.max = 1,
.step = 1,
.def = 1,
},
{
.id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.min = 0,
.max = 1,
.step = 1,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -1,
.max = 127,
.step = 1,
.def = DEFAULT_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -1,
.max = 127,
.step = 1,
.def = DEFAULT_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 127,
.step = 1,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 127,
.step = 1,
.def = 127,
},
{
.id = V4L2_CID_ROTATE,
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 270,
.step = 90,
.def = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.min = 0,
.max = 1,
.step = 1,
.def = 1,
},
};
static int vsi_setup_enc_ctrls(struct v4l2_ctrl_handler *handler)
{
struct vsi_v4l2_ctx *ctx = container_of(handler, struct vsi_v4l2_ctx, ctrlhdl);
int i, ctrl_num = ARRAY_SIZE(vsi_v4l2_encctrl_defs);
struct v4l2_ctrl *ctrl = NULL;
v4l2_ctrl_handler_init(handler, ctrl_num);
if (handler->error)
return handler->error;
for (i = 0; i < ctrl_num; i++) {
if (!vsi_v4l2_ctrl_is_applicable(ctx, vsi_v4l2_encctrl_defs[i].id)) {
v4l2_klog(LOGLVL_CONFIG, "ctrl %d is not applicable for vsienc\n",
vsi_v4l2_encctrl_defs[i].id);
continue;
}
vsi_v4l2_update_ctrlcfg(&vsi_v4l2_encctrl_defs[i]);
if (is_vsi_ctrl(vsi_v4l2_encctrl_defs[i].id))
ctrl = v4l2_ctrl_new_custom(handler, &vsi_v4l2_encctrl_defs[i], NULL);
else {
if (vsi_v4l2_encctrl_defs[i].type == V4L2_CTRL_TYPE_MENU) {
ctrl = v4l2_ctrl_new_std_menu(handler, &vsi_encctrl_ops,
vsi_v4l2_encctrl_defs[i].id,
vsi_v4l2_encctrl_defs[i].max,
0,
vsi_v4l2_encctrl_defs[i].def);
} else {
ctrl = v4l2_ctrl_new_std(handler,
&vsi_encctrl_ops,
vsi_v4l2_encctrl_defs[i].id,
vsi_v4l2_encctrl_defs[i].min,
vsi_v4l2_encctrl_defs[i].max,
vsi_v4l2_encctrl_defs[i].step,
vsi_v4l2_encctrl_defs[i].def);
}
}
if (ctrl && (vsi_v4l2_encctrl_defs[i].flags & V4L2_CTRL_FLAG_VOLATILE))
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
if (handler->error) {
v4l2_klog(LOGLVL_ERROR, "fail to set ctrl %d:%d", i, handler->error);
break;
}
}
v4l2_ctrl_new_std(handler, NULL, V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 127, 1, 0);
v4l2_ctrl_handler_setup(handler);
return handler->error;
}
static int v4l2_enc_open(struct file *filp)
{
//struct video_device *vdev = video_devdata(filp);
struct vsi_v4l2_device *dev = video_drvdata(filp);
struct vsi_v4l2_ctx *ctx = NULL;
struct vb2_queue *q;
int ret = 0;
struct v4l2_fh *vfh;
pid_t pid;
/* Allocate memory for context */
//fh->video_devdata = struct video_device, struct video_device->video_drvdata = struct vsi_v4l2_device
if (vsi_v4l2_addinstance(&pid) < 0)
return -EBUSY;
ctx = vsi_create_ctx();
if (ctx == NULL) {
vsi_v4l2_quitinstance();
return -ENOMEM;
}
v4l2_fh_init(&ctx->fh, video_devdata(filp));
filp->private_data = &ctx->fh;
v4l2_fh_add(&ctx->fh);
ctx->dev = dev;
mutex_init(&ctx->ctxlock);
ctx->flag = CTX_FLAG_ENC;
set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
set_bit(CTX_FLAG_ENC_FLUSHBUF, &ctx->flag);
ctx->frameidx = 0;
q = &ctx->input_que;
q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
q->min_buffers_needed = MIN_FRAME_4ENC;
q->drv_priv = &ctx->fh;
q->lock = &ctx->ctxlock;
q->buf_struct_size = sizeof(struct vsi_vpu_buf); //used to alloc mem control structures in reqbuf
q->ops = &vsi_enc_qops; /*it might be used to identify input and output */
q->mem_ops = &vb2_dma_contig_memops;
q->memory = VB2_MEMORY_UNKNOWN;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
INIT_LIST_HEAD(&ctx->input_list);
ret = vb2_queue_init(q);
/*q->buf_ops = &v4l2_buf_ops is set here*/
if (ret)
goto err_enc_dec_exit;
q = &ctx->output_que;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
q->min_buffers_needed = 1;
q->drv_priv = &ctx->fh;
q->lock = &ctx->ctxlock;
q->buf_struct_size = sizeof(struct vsi_vpu_buf);
q->ops = &vsi_enc_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->memory = VB2_MEMORY_UNKNOWN;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
INIT_LIST_HEAD(&ctx->output_list);
ret = vb2_queue_init(q);
if (ret) {
vb2_queue_release(&ctx->input_que);
goto err_enc_dec_exit;
}
vsiv4l2_initcfg(ctx);
vsi_setup_enc_ctrls(&ctx->ctrlhdl);
vfh = (struct v4l2_fh *)filp->private_data;
vfh->ctrl_handler = &ctx->ctrlhdl;
atomic_set(&ctx->srcframen, 0);
atomic_set(&ctx->dstframen, 0);
ctx->status = VSI_STATUS_INIT;
ctx->tgid = current->tgid;
ctx->pid = current->pid;
vsi_v4l2_create_dbgfs_file(ctx);
return 0;
err_enc_dec_exit:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
vsi_remove_ctx(ctx);
kfree(ctx);
vsi_v4l2_quitinstance();
return ret;
}
static int v4l2_enc_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
int ret;
v4l2_klog(LOGLVL_FLOW, "%s", __func__);
if (offset < OUTF_BASE) {
ret = vb2_mmap(&ctx->input_que, vma);
} else {
vma->vm_pgoff -= (OUTF_BASE >> PAGE_SHIFT);
ret = vb2_mmap(&ctx->output_que, vma);
}
return ret;
}
static __poll_t vsi_enc_poll(struct file *file, poll_table *wait)
{
__poll_t ret = 0;
struct v4l2_fh *fh = file->private_data;
struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
int dstn = atomic_read(&ctx->dstframen);
int srcn = atomic_read(&ctx->srcframen);
/*
* poll_wait() MUST be called on the first invocation on all the
* potential queues of interest, even if we are not interested in their
* events during this first call. Failure to do so will result in
* queue's events to be ignored because the poll_table won't be capable
* of adding new wait queues thereafter.
*/
poll_wait(file, &ctx->input_que.done_wq, wait);
poll_wait(file, &ctx->output_que.done_wq, wait);
poll_wait(file, &fh->wait, wait);
if (!vsi_v4l2_daemonalive())
ret |= POLLERR;
if (v4l2_event_pending(&ctx->fh)) {
v4l2_klog(LOGLVL_BRIEF, "%s event", __func__);
ret |= POLLPRI;
}
ret |= vb2_poll(&ctx->output_que, file, wait);
ret |= vb2_poll(&ctx->input_que, file, wait);
/*recheck for poll hang*/
if (ret == 0) {
if (dstn != atomic_read(&ctx->dstframen))
ret |= vb2_poll(&ctx->output_que, file, wait);
if (srcn != atomic_read(&ctx->srcframen))
ret |= vb2_poll(&ctx->input_que, file, wait);
}
if (ctx->error < 0)
ret |= POLLERR;
v4l2_klog(LOGLVL_VERBOSE, "%s %x", __func__, ret);
return ret;
}
static const struct v4l2_file_operations v4l2_enc_fops = {
.owner = THIS_MODULE,
.open = v4l2_enc_open,
.release = vsi_v4l2_release,
.unlocked_ioctl = video_ioctl2,
.mmap = v4l2_enc_mmap,
.poll = vsi_enc_poll,
};
struct video_device *vsi_v4l2_probe_enc(struct platform_device *pdev, struct vsi_v4l2_device *vpu)
{
struct video_device *venc;
int ret = 0;
v4l2_klog(LOGLVL_BRIEF, "%s", __func__);
/*init video device0, encoder */
venc = video_device_alloc();
if (!venc) {
v4l2_err(&vpu->v4l2_dev, "Failed to allocate enc device\n");
goto err;
}
venc->fops = &v4l2_enc_fops;
venc->ioctl_ops = &vsi_enc_ioctl;
venc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
venc->release = video_device_release;
venc->lock = &vpu->lock;
venc->v4l2_dev = &vpu->v4l2_dev;
venc->vfl_dir = VFL_DIR_M2M;
venc->vfl_type = VSI_DEVTYPE;
venc->queue = NULL;
video_set_drvdata(venc, vpu);
ret = video_register_device(venc, VSI_DEVTYPE, 0);
if (ret) {
v4l2_err(&vpu->v4l2_dev, "Failed to register enc device\n");
video_device_release(venc);
goto err;
}
return venc;
err:
return NULL;
}
void vsi_v4l2_release_enc(struct video_device *venc)
{
video_unregister_device(venc);
}