mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2025-10-07 10:09:34 +00:00
3654 lines
109 KiB
C
3654 lines
109 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2016 MediaTek Inc.
|
|
* Author: PC Chen <pc.chen@mediatek.com>
|
|
* Tiffany Lin <tiffany.lin@mediatek.com>
|
|
*/
|
|
|
|
#include <media/v4l2-event.h>
|
|
#include <media/v4l2-mem2mem.h>
|
|
#include <media/videobuf2-dma-contig.h>
|
|
#include <soc/mediatek/smi.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/semaphore.h>
|
|
#include <linux/module.h>
|
|
|
|
#include "mtk_heap.h"
|
|
#include "iommu_pseudo.h"
|
|
|
|
#include "mtk_vcodec_drv.h"
|
|
#include "mtk_vcodec_enc.h"
|
|
#include "mtk_vcodec_intr.h"
|
|
#include "mtk_vcodec_util.h"
|
|
#include "mtk_vcodec_enc_pm.h"
|
|
#include "mtk_vcodec_enc_pm_plat.h"
|
|
#include "venc_drv_if.h"
|
|
|
|
#define MTK_VENC_MIN_W 160U
|
|
#define MTK_VENC_MIN_H 128U
|
|
#define MTK_VENC_MAX_W 1920U
|
|
#define MTK_VENC_MAX_H 1088U
|
|
#define DFT_CFG_WIDTH MTK_VENC_MIN_W
|
|
#define DFT_CFG_HEIGHT MTK_VENC_MIN_H
|
|
|
|
static void mtk_venc_worker(struct work_struct *work);
|
|
struct mtk_video_fmt
|
|
mtk_venc_formats[MTK_MAX_ENC_CODECS_SUPPORT] = { {0} };
|
|
struct mtk_codec_framesizes
|
|
mtk_venc_framesizes[MTK_MAX_ENC_CODECS_SUPPORT] = { {0} };
|
|
static unsigned int default_out_fmt_idx;
|
|
static unsigned int default_cap_fmt_idx;
|
|
static struct vb2_mem_ops venc_dma_contig_memops;
|
|
static struct vb2_mem_ops venc_sec_dma_contig_memops;
|
|
|
|
inline unsigned int log2_enc(__u32 value)
|
|
{
|
|
unsigned int x = 0;
|
|
|
|
while (value > 1) {
|
|
value >>= 1;
|
|
x++;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
static int mtk_venc_sec_dc_map_dmabuf(void *mem_priv)
|
|
{
|
|
struct vb2_dc_buf *buf = mem_priv;
|
|
|
|
if (WARN_ON(!buf->db_attach)) {
|
|
mtk_v4l2_err("trying to pin a non attached buffer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (WARN_ON(buf->dma_addr)) {
|
|
mtk_v4l2_err("dmabuf buffer is already pinned\n");
|
|
return 0;
|
|
}
|
|
|
|
buf->dma_addr = dmabuf_to_secure_handle(buf->db_attach->dmabuf);
|
|
buf->dma_sgt = NULL;
|
|
buf->vaddr = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mtk_venc_sec_dc_unmap_dmabuf(void *mem_priv)
|
|
{
|
|
struct vb2_dc_buf *buf = mem_priv;
|
|
|
|
if (WARN_ON(!buf->db_attach)) {
|
|
mtk_v4l2_err("trying to unpin a not attached buffer\n");
|
|
return;
|
|
}
|
|
|
|
if (WARN_ON(!buf->dma_addr)) {
|
|
mtk_v4l2_err("dmabuf buffer is already unpinned\n");
|
|
return;
|
|
}
|
|
|
|
if (buf->vaddr) {
|
|
mtk_v4l2_err("dmabuf buffer vaddr not null\n");
|
|
buf->vaddr = NULL;
|
|
}
|
|
|
|
buf->dma_addr = 0;
|
|
buf->dma_sgt = NULL;
|
|
}
|
|
|
|
static bool mtk_venc_is_vcu(void)
|
|
{
|
|
if (VCU_FPTR(vcu_get_plat_device)) {
|
|
if (mtk_vcodec_vcp & (1 << MTK_INST_ENCODER))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void set_venc_vcp_data(struct mtk_vcodec_ctx *ctx, enum vcp_reserve_mem_id_t id)
|
|
{
|
|
struct venc_enc_param enc_prm;
|
|
|
|
memset(&enc_prm, 0, sizeof(enc_prm));
|
|
memset(enc_prm.set_vcp_buf, '\0', 1024);
|
|
|
|
if (id == VENC_SET_PROP_MEM_ID) {
|
|
|
|
sprintf(enc_prm.set_vcp_buf, "%s", mtk_venc_property);
|
|
mtk_v4l2_debug(3, "[%d] mtk_venc_property %s", ctx->id, enc_prm.set_vcp_buf);
|
|
mtk_v4l2_debug(3, "[%d] mtk_venc_property_prev %s",
|
|
ctx->id, mtk_venc_property_prev);
|
|
|
|
// set vcp log every time
|
|
if (/* strcmp(mtk_venc_property_prev, enc_prm.set_vcp_buf) != 0 && */
|
|
strlen(enc_prm.set_vcp_buf) != 0) {
|
|
|
|
if (venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_PROPERTY,
|
|
&enc_prm) != 0) {
|
|
mtk_v4l2_err("Error!! Cannot set venc property");
|
|
return;
|
|
}
|
|
strcpy(mtk_venc_property_prev, enc_prm.set_vcp_buf);
|
|
}
|
|
} else if (id == VENC_VCP_LOG_INFO_ID) {
|
|
|
|
sprintf(enc_prm.set_vcp_buf, "%s", mtk_venc_vcp_log);
|
|
mtk_v4l2_debug(3, "[%d] mtk_venc_vcp_log %s", ctx->id, enc_prm.set_vcp_buf);
|
|
mtk_v4l2_debug(3, "[%d] mtk_venc_vcp_log_prev %s", ctx->id, mtk_venc_vcp_log_prev);
|
|
|
|
// set vcp log every time
|
|
if (/* strcmp(mtk_venc_vcp_log_prev, enc_prm.set_vcp_buf) != 0 && */
|
|
strlen(enc_prm.set_vcp_buf) != 0) {
|
|
|
|
if (venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_VCP_LOG_INFO,
|
|
&enc_prm) != 0) {
|
|
mtk_v4l2_err("Error!! Cannot set venc vcp log info");
|
|
return;
|
|
}
|
|
strcpy(mtk_venc_vcp_log_prev, enc_prm.set_vcp_buf);
|
|
}
|
|
}
|
|
}
|
|
static void get_supported_format(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (mtk_venc_formats[0].fourcc == 0) {
|
|
if (venc_if_get_param(ctx,
|
|
GET_PARAM_VENC_CAP_SUPPORTED_FORMATS,
|
|
&mtk_venc_formats) != 0) {
|
|
mtk_v4l2_err("Error!! Cannot get supported format");
|
|
return;
|
|
}
|
|
for (i = 0; i < MTK_MAX_ENC_CODECS_SUPPORT; i++) {
|
|
if (mtk_venc_formats[i].fourcc != 0 &&
|
|
mtk_venc_formats[i].type == MTK_FMT_FRAME) {
|
|
default_out_fmt_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
for (i = 0; i < MTK_MAX_ENC_CODECS_SUPPORT; i++) {
|
|
if (mtk_venc_formats[i].fourcc != 0 &&
|
|
mtk_venc_formats[i].type == MTK_FMT_ENC) {
|
|
default_cap_fmt_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void get_supported_framesizes(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (mtk_venc_framesizes[0].fourcc == 0) {
|
|
if (venc_if_get_param(ctx, GET_PARAM_VENC_CAP_FRAME_SIZES,
|
|
&mtk_venc_framesizes) != 0) {
|
|
mtk_v4l2_err("[%d] Error!! Cannot get frame size",
|
|
ctx->id);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < MTK_MAX_ENC_CODECS_SUPPORT; i++) {
|
|
if (mtk_venc_framesizes[i].fourcc != 0) {
|
|
mtk_v4l2_debug(1,
|
|
"venc_fs[%d] fourcc %d s %d %d %d %d %d %d\n",
|
|
i, mtk_venc_framesizes[i].fourcc,
|
|
mtk_venc_framesizes[i].stepwise.min_width,
|
|
mtk_venc_framesizes[i].stepwise.max_width,
|
|
mtk_venc_framesizes[i].stepwise.step_width,
|
|
mtk_venc_framesizes[i].stepwise.min_height,
|
|
mtk_venc_framesizes[i].stepwise.max_height,
|
|
mtk_venc_framesizes[i].stepwise.step_height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void get_free_buffers(struct mtk_vcodec_ctx *ctx,
|
|
struct venc_done_result *pResult)
|
|
{
|
|
venc_if_get_param(ctx,
|
|
GET_PARAM_FREE_BUFFERS,
|
|
pResult);
|
|
}
|
|
|
|
void mtk_enc_put_buf(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
struct venc_done_result rResult;
|
|
struct venc_frm_buf *pfrm;
|
|
struct mtk_vcodec_mem *pbs;
|
|
struct mtk_video_enc_buf *bs_info, *frm_info;
|
|
struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2;
|
|
struct vb2_buffer *dst_buf;
|
|
|
|
mutex_lock(&ctx->buf_lock);
|
|
do {
|
|
dst_vb2_v4l2 = NULL;
|
|
src_vb2_v4l2 = NULL;
|
|
pfrm = NULL;
|
|
pbs = NULL;
|
|
|
|
memset(&rResult, 0, sizeof(rResult));
|
|
get_free_buffers(ctx, &rResult);
|
|
|
|
if (rResult.bs_va != 0 && virt_addr_valid(rResult.bs_va)) {
|
|
pbs = (struct mtk_vcodec_mem *)rResult.bs_va;
|
|
bs_info = container_of(pbs,
|
|
struct mtk_video_enc_buf, bs_buf);
|
|
dst_vb2_v4l2 = &bs_info->vb;
|
|
}
|
|
|
|
if (rResult.frm_va != 0 && virt_addr_valid(rResult.frm_va)) {
|
|
pfrm = (struct venc_frm_buf *)rResult.frm_va;
|
|
frm_info = container_of(pfrm,
|
|
struct mtk_video_enc_buf, frm_buf);
|
|
src_vb2_v4l2 = &frm_info->vb;
|
|
}
|
|
|
|
if (src_vb2_v4l2 != NULL && dst_vb2_v4l2 != NULL) {
|
|
if (rResult.is_key_frm)
|
|
dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME;
|
|
|
|
dst_vb2_v4l2->vb2_buf.timestamp =
|
|
src_vb2_v4l2->vb2_buf.timestamp;
|
|
dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode;
|
|
dst_vb2_v4l2->sequence = src_vb2_v4l2->sequence;
|
|
dst_buf = &dst_vb2_v4l2->vb2_buf;
|
|
dst_buf->planes[0].bytesused = rResult.bs_size;
|
|
v4l2_m2m_buf_done(src_vb2_v4l2, VB2_BUF_STATE_DONE);
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2, VB2_BUF_STATE_DONE);
|
|
|
|
mtk_v4l2_debug(1, "venc_if_encode bs size=%d",
|
|
rResult.bs_size);
|
|
} else if (src_vb2_v4l2 == NULL && dst_vb2_v4l2 != NULL) {
|
|
dst_buf = &dst_vb2_v4l2->vb2_buf;
|
|
dst_buf->planes[0].bytesused = rResult.bs_size;
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2,
|
|
VB2_BUF_STATE_DONE);
|
|
mtk_v4l2_debug(0, "[Warning] bs size=%d, frm NULL!!",
|
|
rResult.bs_size);
|
|
} else {
|
|
if (src_vb2_v4l2 == NULL)
|
|
mtk_v4l2_debug(1, "NULL enc src buffer\n");
|
|
|
|
if (dst_vb2_v4l2 == NULL)
|
|
mtk_v4l2_debug(1, "NULL enc dst buffer\n");
|
|
}
|
|
} while (rResult.bs_va != 0 || rResult.frm_va != 0);
|
|
mutex_unlock(&ctx->buf_lock);
|
|
}
|
|
|
|
static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f,
|
|
unsigned int t)
|
|
{
|
|
struct mtk_video_fmt *fmt;
|
|
unsigned int k;
|
|
|
|
mtk_v4l2_debug(3, "fourcc %d", f->fmt.pix_mp.pixelformat);
|
|
for (k = 0; k < MTK_MAX_ENC_CODECS_SUPPORT &&
|
|
mtk_venc_formats[k].fourcc != 0; k++) {
|
|
fmt = &mtk_venc_formats[k];
|
|
if (fmt->fourcc == f->fmt.pix.pixelformat && fmt->type == t)
|
|
return fmt;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int vidioc_venc_check_supported_profile_level(__u32 fourcc,
|
|
unsigned int pl, bool is_profile)
|
|
{
|
|
struct v4l2_format f;
|
|
int i = 0;
|
|
|
|
f.fmt.pix.pixelformat = fourcc;
|
|
if (mtk_venc_find_format(&f, MTK_FMT_ENC) == NULL)
|
|
return false;
|
|
|
|
for (i = 0; i < MTK_MAX_ENC_CODECS_SUPPORT; i++) {
|
|
if (mtk_venc_framesizes[i].fourcc == fourcc) {
|
|
if (is_profile) {
|
|
if (mtk_venc_framesizes[i].profile & (1 << pl))
|
|
return true;
|
|
else
|
|
return false;
|
|
} else {
|
|
if (mtk_venc_framesizes[i].level >= pl)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl);
|
|
struct mtk_enc_params *p = &ctx->enc_params;
|
|
struct vb2_queue *src_vq;
|
|
int ret = 0;
|
|
|
|
mtk_v4l2_debug(4, "[%d] id %d val %d array[0] %d array[1] %d",
|
|
ctx->id, ctrl->id, ctrl->val,
|
|
ctrl->p_new.p_u32[0], ctrl->p_new.p_u32[1]);
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d",
|
|
ctrl->val);
|
|
p->bitrate = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_BITRATE;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_SEC_ENCODE:
|
|
p->svp_mode = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_SEC_ENCODE;
|
|
mtk_v4l2_debug(0, "[%d] V4L2_CID_MPEG_MTK_SEC_ENCODE id %d val %d array[0] %d array[1] %d",
|
|
ctx->id, ctrl->id, ctrl->val,
|
|
ctrl->p_new.p_u32[0], ctrl->p_new.p_u32[1]);
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_B_FRAMES:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d",
|
|
ctrl->val);
|
|
p->num_b_frame = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d",
|
|
ctrl->val);
|
|
p->rc_frame = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d",
|
|
ctrl->val);
|
|
p->h264_max_qp = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d",
|
|
ctrl->val);
|
|
p->seq_hdr_mode = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d",
|
|
ctrl->val);
|
|
p->rc_mb = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d",
|
|
ctrl->val);
|
|
if (!vidioc_venc_check_supported_profile_level(
|
|
V4L2_PIX_FMT_H264, ctrl->val, 1))
|
|
return -EINVAL;
|
|
p->profile = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEVC_PROFILE val = %d",
|
|
ctrl->val);
|
|
if (!vidioc_venc_check_supported_profile_level(
|
|
V4L2_PIX_FMT_H265, ctrl->val, 1))
|
|
return -EINVAL;
|
|
p->profile = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE val = %d",
|
|
ctrl->val);
|
|
if (!vidioc_venc_check_supported_profile_level(
|
|
V4L2_PIX_FMT_MPEG4, ctrl->val, 1))
|
|
return -EINVAL;
|
|
p->profile = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d",
|
|
ctrl->val);
|
|
if (!vidioc_venc_check_supported_profile_level(
|
|
V4L2_PIX_FMT_H264, ctrl->val, 0))
|
|
return -EINVAL;
|
|
p->level = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEVC_LEVEL val = %d",
|
|
ctrl->val);
|
|
if (!vidioc_venc_check_supported_profile_level(
|
|
V4L2_PIX_FMT_H265, ctrl->val, 0))
|
|
return -EINVAL;
|
|
p->level = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEVC_TIER val = %d",
|
|
ctrl->val);
|
|
p->tier = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL val = %d",
|
|
ctrl->val);
|
|
if (!vidioc_venc_check_supported_profile_level(
|
|
V4L2_PIX_FMT_MPEG4, ctrl->val, 0))
|
|
return -EINVAL;
|
|
p->level = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d",
|
|
ctrl->val);
|
|
p->intra_period = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_INTRA_PERIOD;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d",
|
|
ctrl->val);
|
|
p->gop_size = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_GOP_SIZE;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
|
|
mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME");
|
|
p->force_intra = 1;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_FORCE_INTRA;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_SCENARIO:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_SCENARIO: %d",
|
|
ctrl->val);
|
|
p->scenario = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_SCENARIO;
|
|
if (p->scenario == 3 || p->scenario == 1) {
|
|
src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
|
|
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
|
|
if (ctx->enc_params.svp_mode && is_disable_map_sec() && mtk_venc_is_vcu())
|
|
src_vq->mem_ops = &venc_sec_dma_contig_memops;
|
|
else
|
|
src_vq->mem_ops = &venc_dma_contig_memops;
|
|
}
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_NONREFP:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_NONREFP: %d",
|
|
ctrl->val);
|
|
p->nonrefp = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_NONREFP;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_NONREFP_FREQ:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_NONREFP_FREQ: %d",
|
|
ctrl->val);
|
|
p->nonrefpfreq = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_NONREFPFREQ;
|
|
break;
|
|
|
|
case V4L2_CID_MPEG_MTK_ENCODE_DETECTED_FRAMERATE:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_DETECTED_FRAMERATE: %d",
|
|
ctrl->val);
|
|
p->detectframerate = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_DETECTED_FRAMERATE;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RFS_ON:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_RFS_ON: %d",
|
|
ctrl->val);
|
|
p->rfs = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_RFS_ON;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: %d",
|
|
ctrl->val);
|
|
p->prependheader = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_PREPEND_SPSPPS_TO_IDR;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_OPERATION_RATE:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_OPERATION_RATE: %d",
|
|
ctrl->val);
|
|
p->operationrate = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_OPERATION_RATE;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_VIDEO_BITRATE_MODE: %d",
|
|
ctrl->val);
|
|
p->bitratemode = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_BITRATE_MODE;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_ROI_ON:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_ROI_ON: %d",
|
|
ctrl->val);
|
|
p->roion = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_ROI_ON;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_GRID_SIZE:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_GRID_SIZE: %d",
|
|
ctrl->val);
|
|
p->heif_grid_size = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_GRID_SIZE;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_COLOR_DESC:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_COLOR_DESC: 0x%x",
|
|
ctrl->val);
|
|
memcpy(&p->color_desc, ctrl->p_new.p_u32,
|
|
sizeof(struct mtk_color_desc));
|
|
ctx->param_change |= MTK_ENCODE_PARAM_COLOR_DESC;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_MAX_WIDTH:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_MAX_WIDTH: %d",
|
|
ctrl->val);
|
|
p->max_w = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_MAX_HEIGHT:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_MAX_HEIGHT: %d",
|
|
ctrl->val);
|
|
p->max_h = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_I_FRAME_QP:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_RC_I_FRAME_QP val = %d",
|
|
ctrl->val);
|
|
p->i_qp = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_P_FRAME_QP:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_RC_P_FRAME_QP val = %d",
|
|
ctrl->val);
|
|
p->p_qp = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_B_FRAME_QP:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_RC_B_FRAME_QP val = %d",
|
|
ctrl->val);
|
|
p->b_qp = ctrl->val;
|
|
break;
|
|
case V4L2_CID_MPEG_VIDEO_ENABLE_TSVC:
|
|
mtk_v4l2_debug(0,
|
|
"V4L2_CID_MPEG_VIDEO_ENABLE_TSVC layer: %d, type: %d\n",
|
|
ctrl->p_new.p_u32[0], ctrl->p_new.p_u32[1]);
|
|
if (ctrl->p_new.p_u32[0] == 3)
|
|
p->tsvc = 1;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_TSVC;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_ENABLE_HIGHQUALITY:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_ENABLE_HIGHQUALITY: %d",
|
|
ctrl->val);
|
|
p->highquality = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_HIGHQUALITY;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_MAX_QP:
|
|
mtk_v4l2_debug(0, "V4L2_CID_MPEG_MTK_ENCODE_RC_MAX_QP");
|
|
p->max_qp = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_MAXQP;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_MIN_QP:
|
|
mtk_v4l2_debug(0, "V4L2_CID_MPEG_MTK_ENCODE_RC_MIN_QP");
|
|
p->min_qp = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_MINQP;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_I_P_QP_DELTA:
|
|
mtk_v4l2_debug(0, "V4L2_CID_MPEG_MTK_ENCODE_RC_I_P_QP_DELTA");
|
|
p->ip_qpdelta = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_IP_QPDELTA;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_FRAME_LEVEL_QP:
|
|
mtk_v4l2_debug(0, "V4L2_CID_MPEG_MTK_ENCODE_RC_FRAME_LEVEL_QP");
|
|
p->framelvl_qp = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_FRAMELVLQP;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_RC_QP_CONTROL_MODE:
|
|
mtk_v4l2_debug(0, "V4L2_CID_MPEG_MTK_ENCODE_RC_QP_CONTROL_MODE");
|
|
p->qp_control_mode = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_QP_CTRL_MODE;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_ENCODE_ENABLE_DUMMY_NAL:
|
|
mtk_v4l2_debug(2,
|
|
"V4L2_CID_MPEG_MTK_ENCODE_ENABLE_DUMMY_NAL: %d",
|
|
ctrl->val);
|
|
p->dummynal = ctrl->val;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_DUMMY_NAL;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_LOG:
|
|
mtk_vcodec_set_log(ctx->dev, ctrl->p_new.p_char, MTK_VCODEC_LOG_INDEX_LOG);
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_VCP_PROP:
|
|
mtk_vcodec_set_log(ctx->dev, ctrl->p_new.p_char, MTK_VCODEC_LOG_INDEX_PROP);
|
|
break;
|
|
default:
|
|
mtk_v4l2_debug(4, "ctrl-id=%d not support!", ctrl->id);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int vidioc_venc_g_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl);
|
|
int ret = 0;
|
|
int value = 0;
|
|
struct venc_resolution_change *reschange;
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_MPEG_MTK_ENCODE_ROI_RC_QP:
|
|
venc_if_get_param(ctx,
|
|
GET_PARAM_ROI_RC_QP,
|
|
&value);
|
|
ctrl->val = value;
|
|
break;
|
|
case V4L2_CID_MPEG_MTK_RESOLUTION_CHANGE:
|
|
reschange = (struct venc_resolution_change *)ctrl->p_new.p_u32;
|
|
venc_if_get_param(ctx,
|
|
GET_PARAM_RESOLUTION_CHANGE,
|
|
reschange);
|
|
break;
|
|
default:
|
|
mtk_v4l2_debug(4, "ctrl-id=%d not support!", ctrl->id);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = {
|
|
.s_ctrl = vidioc_venc_s_ctrl,
|
|
.g_volatile_ctrl = vidioc_venc_g_ctrl,
|
|
};
|
|
|
|
static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue)
|
|
{
|
|
struct mtk_video_fmt *fmt;
|
|
int i, j = 0;
|
|
|
|
for (i = 0; i < MTK_MAX_ENC_CODECS_SUPPORT &&
|
|
mtk_venc_formats[i].fourcc != 0; ++i) {
|
|
if (output_queue && mtk_venc_formats[i].type != MTK_FMT_FRAME)
|
|
continue;
|
|
if (!output_queue && mtk_venc_formats[i].type != MTK_FMT_ENC)
|
|
continue;
|
|
|
|
if (j == f->index) {
|
|
fmt = &mtk_venc_formats[i];
|
|
f->pixelformat = fmt->fourcc;
|
|
memset(f->reserved, 0, sizeof(f->reserved));
|
|
v4l_fill_mtk_fmtdesc(f);
|
|
return 0;
|
|
}
|
|
++j;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int vidioc_enum_framesizes(struct file *file, void *fh,
|
|
struct v4l2_frmsizeenum *fsize)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
if (fsize->index != 0)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < MTK_MAX_ENC_CODECS_SUPPORT &&
|
|
mtk_venc_framesizes[i].fourcc != 0; ++i) {
|
|
if (fsize->pixel_format != mtk_venc_framesizes[i].fourcc)
|
|
continue;
|
|
|
|
fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
|
|
fsize->stepwise = mtk_venc_framesizes[i].stepwise;
|
|
fsize->reserved[0] = mtk_venc_framesizes[i].profile;
|
|
fsize->reserved[1] = mtk_venc_framesizes[i].level;
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
|
|
struct v4l2_fmtdesc *f)
|
|
{
|
|
return vidioc_enum_fmt(f, false);
|
|
}
|
|
|
|
static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
|
|
struct v4l2_fmtdesc *f)
|
|
{
|
|
return vidioc_enum_fmt(f, true);
|
|
}
|
|
|
|
static int vidioc_venc_querycap(struct file *file, void *priv,
|
|
struct v4l2_capability *cap)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct mtk_vcodec_dev *dev = ctx->dev;
|
|
|
|
strlcpy(cap->driver, MTK_VCODEC_ENC_NAME, sizeof(cap->driver));
|
|
strlcpy(cap->bus_info, dev->platform, sizeof(cap->bus_info));
|
|
strlcpy(cap->card, dev->platform, sizeof(cap->card));
|
|
|
|
cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
|
|
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_venc_s_parm(struct file *file, void *priv,
|
|
struct v4l2_streamparm *a)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
|
return -EINVAL;
|
|
|
|
ctx->enc_params.framerate_num =
|
|
a->parm.output.timeperframe.denominator;
|
|
ctx->enc_params.framerate_denom =
|
|
a->parm.output.timeperframe.numerator;
|
|
ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE;
|
|
|
|
a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_venc_g_parm(struct file *file, void *priv,
|
|
struct v4l2_streamparm *a)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
|
return -EINVAL;
|
|
|
|
a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
|
|
a->parm.output.timeperframe.denominator =
|
|
ctx->enc_params.framerate_num;
|
|
a->parm.output.timeperframe.numerator =
|
|
ctx->enc_params.framerate_denom;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx,
|
|
enum v4l2_buf_type type)
|
|
{
|
|
if (ctx == NULL)
|
|
return NULL;
|
|
|
|
if (V4L2_TYPE_IS_OUTPUT(type))
|
|
return &ctx->q_data[MTK_Q_DATA_SRC];
|
|
|
|
return &ctx->q_data[MTK_Q_DATA_DST];
|
|
}
|
|
|
|
/* V4L2 specification suggests the driver corrects the format struct if any of
|
|
* the dimensions is unsupported
|
|
*/
|
|
static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt,
|
|
struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
|
|
int org_w, org_h, i;
|
|
int bitsPP = 8; /* bits per pixel */
|
|
__u32 bs_fourcc;
|
|
unsigned int step_width_in_pixel;
|
|
unsigned int step_height_in_pixel;
|
|
unsigned int saligned;
|
|
unsigned int imagePixels;
|
|
// for AFBC
|
|
unsigned int block_w = 16;
|
|
unsigned int block_h = 16;
|
|
unsigned int block_count;
|
|
|
|
struct mtk_codec_framesizes *spec_size_info = NULL;
|
|
|
|
pix_fmt_mp->field = V4L2_FIELD_NONE;
|
|
|
|
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
|
|
pix_fmt_mp->num_planes = 1;
|
|
pix_fmt_mp->plane_fmt[0].bytesperline = 0;
|
|
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
|
if (ctx->q_data[MTK_Q_DATA_DST].fmt != NULL) {
|
|
bs_fourcc =
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc;
|
|
} else {
|
|
bs_fourcc =
|
|
mtk_venc_formats[default_cap_fmt_idx].fourcc;
|
|
}
|
|
for (i = 0; i < MTK_MAX_ENC_CODECS_SUPPORT; i++) {
|
|
if (mtk_venc_framesizes[i].fourcc == bs_fourcc)
|
|
spec_size_info = &mtk_venc_framesizes[i];
|
|
}
|
|
if (!spec_size_info) {
|
|
mtk_v4l2_err("fail to get spec_size_info");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mtk_v4l2_debug(1,
|
|
"pix_fmt_mp->pixelformat %d bs fmt %d min_w %d min_h %d max_w %d max_h %d\n",
|
|
pix_fmt_mp->pixelformat, bs_fourcc,
|
|
spec_size_info->stepwise.min_width,
|
|
spec_size_info->stepwise.min_height,
|
|
spec_size_info->stepwise.max_width,
|
|
spec_size_info->stepwise.max_height);
|
|
|
|
if ((spec_size_info->stepwise.step_width &
|
|
(spec_size_info->stepwise.step_width - 1)) != 0)
|
|
mtk_v4l2_err("Unsupport stepwise.step_width not 2^ %d\n",
|
|
spec_size_info->stepwise.step_width);
|
|
if ((spec_size_info->stepwise.step_height &
|
|
(spec_size_info->stepwise.step_height - 1)) != 0)
|
|
mtk_v4l2_err("Unsupport stepwise.step_height not 2^ %d\n",
|
|
spec_size_info->stepwise.step_height);
|
|
|
|
if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_MT10 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_MT10S) {
|
|
step_width_in_pixel =
|
|
spec_size_info->stepwise.step_width * 4;
|
|
step_height_in_pixel =
|
|
spec_size_info->stepwise.step_height;
|
|
bitsPP = 10;
|
|
saligned = 6;
|
|
} else if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_P010M ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_P010S) {
|
|
step_width_in_pixel =
|
|
spec_size_info->stepwise.step_width / 2;
|
|
step_height_in_pixel =
|
|
spec_size_info->stepwise.step_height;
|
|
bitsPP = 16;
|
|
saligned = 6;
|
|
} else if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ABGR32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ARGB32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGB32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGR32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ARGB1010102 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ABGR1010102 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGBA1010102 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGRA1010102) {
|
|
step_width_in_pixel = 1;
|
|
step_height_in_pixel = 1;
|
|
bitsPP = 32;
|
|
saligned = 4;
|
|
} else if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGB24 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGR24) {
|
|
step_width_in_pixel = 1;
|
|
step_height_in_pixel = 1;
|
|
bitsPP = 24;
|
|
saligned = 4;
|
|
} else {
|
|
step_width_in_pixel =
|
|
spec_size_info->stepwise.step_width;
|
|
step_height_in_pixel =
|
|
spec_size_info->stepwise.step_height;
|
|
bitsPP = 8;
|
|
saligned = 6;
|
|
}
|
|
|
|
// Compute AFBC stream data size
|
|
if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGB32_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGR32_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGBA1010102_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGRA1010102_AFBC) {
|
|
step_width_in_pixel = 1;
|
|
step_height_in_pixel = 1;
|
|
block_w = 32;
|
|
block_h = 8;
|
|
bitsPP = 32;
|
|
saligned = 4;
|
|
} else if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_NV12_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_NV21_AFBC) {
|
|
step_width_in_pixel = 1;
|
|
step_height_in_pixel = 1;
|
|
block_w = 16;
|
|
block_h = 16;
|
|
bitsPP = 12;
|
|
saligned = 4;
|
|
} else if (pix_fmt_mp->pixelformat ==
|
|
V4L2_PIX_FMT_NV12_10B_AFBC) {
|
|
step_width_in_pixel = 1;
|
|
step_height_in_pixel = 1;
|
|
block_w = 16;
|
|
block_h = 16;
|
|
bitsPP = 16;
|
|
saligned = 4;
|
|
}
|
|
|
|
/* find next closer width stride align 16, height align 16,
|
|
* size align 64 rectangle without MBAFF encoder
|
|
* (with MBAFF height align should be 32)
|
|
* width height swappable
|
|
*/
|
|
|
|
if (pix_fmt_mp->height > pix_fmt_mp->width) {
|
|
pix_fmt_mp->height = clamp(pix_fmt_mp->height,
|
|
(spec_size_info->stepwise.min_height),
|
|
(spec_size_info->stepwise.max_width));
|
|
pix_fmt_mp->width = clamp(pix_fmt_mp->width,
|
|
(spec_size_info->stepwise.min_width),
|
|
(spec_size_info->stepwise.max_height));
|
|
org_w = pix_fmt_mp->width;
|
|
org_h = pix_fmt_mp->height;
|
|
v4l_bound_align_image(&pix_fmt_mp->width,
|
|
spec_size_info->stepwise.min_width,
|
|
spec_size_info->stepwise.max_height,
|
|
log2_enc(step_width_in_pixel),
|
|
&pix_fmt_mp->height,
|
|
spec_size_info->stepwise.min_height,
|
|
spec_size_info->stepwise.max_width,
|
|
log2_enc(step_height_in_pixel),
|
|
saligned);
|
|
|
|
if (pix_fmt_mp->width < org_w &&
|
|
(pix_fmt_mp->width +
|
|
step_width_in_pixel) <=
|
|
spec_size_info->stepwise.max_height)
|
|
pix_fmt_mp->width +=
|
|
step_width_in_pixel;
|
|
if (pix_fmt_mp->height < org_h &&
|
|
(pix_fmt_mp->height +
|
|
step_height_in_pixel) <=
|
|
spec_size_info->stepwise.max_width)
|
|
pix_fmt_mp->height +=
|
|
step_height_in_pixel;
|
|
} else {
|
|
pix_fmt_mp->height = clamp(pix_fmt_mp->height,
|
|
(spec_size_info->stepwise.min_height),
|
|
(spec_size_info->stepwise.max_height));
|
|
pix_fmt_mp->width = clamp(pix_fmt_mp->width,
|
|
(spec_size_info->stepwise.min_width),
|
|
(spec_size_info->stepwise.max_width));
|
|
org_w = pix_fmt_mp->width;
|
|
org_h = pix_fmt_mp->height;
|
|
v4l_bound_align_image(&pix_fmt_mp->width,
|
|
spec_size_info->stepwise.min_width,
|
|
spec_size_info->stepwise.max_width,
|
|
log2_enc(step_width_in_pixel),
|
|
&pix_fmt_mp->height,
|
|
spec_size_info->stepwise.min_height,
|
|
spec_size_info->stepwise.max_height,
|
|
log2_enc(step_height_in_pixel),
|
|
saligned);
|
|
|
|
if (pix_fmt_mp->width < org_w &&
|
|
(pix_fmt_mp->width +
|
|
step_width_in_pixel) <=
|
|
spec_size_info->stepwise.max_width)
|
|
pix_fmt_mp->width +=
|
|
step_width_in_pixel;
|
|
if (pix_fmt_mp->height < org_h &&
|
|
(pix_fmt_mp->height +
|
|
step_height_in_pixel) <=
|
|
spec_size_info->stepwise.max_height)
|
|
pix_fmt_mp->height +=
|
|
step_height_in_pixel;
|
|
}
|
|
|
|
pix_fmt_mp->num_planes = fmt->num_planes;
|
|
imagePixels = pix_fmt_mp->width * pix_fmt_mp->height;
|
|
|
|
if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ABGR32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ARGB32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGB32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGR32 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ARGB1010102 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_ABGR1010102 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGBA1010102 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGRA1010102 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGB24 ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGR24) {
|
|
pix_fmt_mp->plane_fmt[0].sizeimage =
|
|
imagePixels * bitsPP / 8;
|
|
pix_fmt_mp->plane_fmt[0].bytesperline =
|
|
pix_fmt_mp->width * bitsPP / 8;
|
|
pix_fmt_mp->num_planes = 1U;
|
|
} else if (pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGB32_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGR32_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_RGBA1010102_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_BGRA1010102_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_NV12_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_NV21_AFBC ||
|
|
pix_fmt_mp->pixelformat == V4L2_PIX_FMT_NV12_10B_AFBC) {
|
|
block_count =
|
|
((pix_fmt_mp->width + (block_w - 1))/block_w)
|
|
*((pix_fmt_mp->height + (block_h - 1))/block_h);
|
|
|
|
pix_fmt_mp->plane_fmt[0].sizeimage =
|
|
(block_count << 4) +
|
|
(block_count * block_w * block_h * bitsPP / 8);
|
|
mtk_v4l2_debug(0, "AFBC size:%d superblock(%dx%d) superblock_count(%d)\n",
|
|
pix_fmt_mp->plane_fmt[0].sizeimage,
|
|
block_w,
|
|
block_h,
|
|
block_count);
|
|
} else if (pix_fmt_mp->num_planes == 1U) {
|
|
pix_fmt_mp->plane_fmt[0].sizeimage =
|
|
(imagePixels * bitsPP / 8) +
|
|
(imagePixels * bitsPP / 8) / 2;
|
|
pix_fmt_mp->plane_fmt[0].bytesperline =
|
|
pix_fmt_mp->width * bitsPP / 8;
|
|
} else if (pix_fmt_mp->num_planes == 2U) {
|
|
pix_fmt_mp->plane_fmt[0].sizeimage =
|
|
imagePixels * bitsPP / 8;
|
|
pix_fmt_mp->plane_fmt[0].bytesperline =
|
|
pix_fmt_mp->width * bitsPP / 8;
|
|
pix_fmt_mp->plane_fmt[1].sizeimage =
|
|
(imagePixels * bitsPP / 8) / 2;
|
|
pix_fmt_mp->plane_fmt[1].bytesperline =
|
|
pix_fmt_mp->width * bitsPP / 8;
|
|
} else if (pix_fmt_mp->num_planes == 3U) {
|
|
pix_fmt_mp->plane_fmt[0].sizeimage =
|
|
imagePixels * bitsPP / 8;
|
|
pix_fmt_mp->plane_fmt[0].bytesperline =
|
|
pix_fmt_mp->width * bitsPP / 8;
|
|
pix_fmt_mp->plane_fmt[1].sizeimage =
|
|
(imagePixels * bitsPP / 8) / 4;
|
|
pix_fmt_mp->plane_fmt[1].bytesperline =
|
|
pix_fmt_mp->width * bitsPP / 8 / 2;
|
|
pix_fmt_mp->plane_fmt[2].sizeimage =
|
|
(imagePixels * bitsPP / 8) / 4;
|
|
pix_fmt_mp->plane_fmt[2].bytesperline =
|
|
pix_fmt_mp->width * bitsPP / 8 / 2;
|
|
} else
|
|
mtk_v4l2_err("Unsupport num planes = %d\n",
|
|
pix_fmt_mp->num_planes);
|
|
|
|
mtk_v4l2_debug(0,
|
|
"w/h (%d, %d) -> (%d,%d), sizeimage[%d,%d,%d]",
|
|
org_w, org_h,
|
|
pix_fmt_mp->width, pix_fmt_mp->height,
|
|
pix_fmt_mp->plane_fmt[0].sizeimage,
|
|
pix_fmt_mp->plane_fmt[1].sizeimage,
|
|
pix_fmt_mp->plane_fmt[2].sizeimage);
|
|
}
|
|
|
|
for (i = 0; i < pix_fmt_mp->num_planes; i++)
|
|
memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0,
|
|
sizeof(pix_fmt_mp->plane_fmt[0].reserved));
|
|
|
|
pix_fmt_mp->flags = 0;
|
|
memset(&pix_fmt_mp->reserved, 0x0,
|
|
sizeof(pix_fmt_mp->reserved));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx,
|
|
struct venc_enc_param *param)
|
|
{
|
|
struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC];
|
|
struct mtk_enc_params *enc_params = &ctx->enc_params;
|
|
|
|
switch (q_data_src->fmt->fourcc) {
|
|
case V4L2_PIX_FMT_YUV420M:
|
|
case V4L2_PIX_FMT_YUV420:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_I420;
|
|
break;
|
|
case V4L2_PIX_FMT_YVU420M:
|
|
case V4L2_PIX_FMT_YVU420:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_YV12;
|
|
break;
|
|
case V4L2_PIX_FMT_NV12M:
|
|
case V4L2_PIX_FMT_NV12:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_NV12;
|
|
break;
|
|
case V4L2_PIX_FMT_NV21M:
|
|
case V4L2_PIX_FMT_NV21:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_NV21;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB24:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_24bitRGB888;
|
|
break;
|
|
case V4L2_PIX_FMT_BGR24:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_24bitBGR888;
|
|
break;
|
|
case V4L2_PIX_FMT_ARGB32:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitARGB8888;
|
|
break;
|
|
case V4L2_PIX_FMT_ABGR32:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitBGRA8888;
|
|
break;
|
|
case V4L2_PIX_FMT_BGR32:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitABGR8888;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitRGBA8888;
|
|
break;
|
|
case V4L2_PIX_FMT_ARGB1010102:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitARGB1010102;
|
|
break;
|
|
case V4L2_PIX_FMT_ABGR1010102:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitABGR1010102;
|
|
break;
|
|
case V4L2_PIX_FMT_RGBA1010102:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitRGBA1010102;
|
|
break;
|
|
case V4L2_PIX_FMT_BGRA1010102:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitBGRA1010102;
|
|
break;
|
|
case V4L2_PIX_FMT_MT10:
|
|
case V4L2_PIX_FMT_MT10S:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_MT10;
|
|
break;
|
|
case V4L2_PIX_FMT_P010M:
|
|
case V4L2_PIX_FMT_P010S:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_P010;
|
|
break;
|
|
case V4L2_PIX_FMT_RGB32_AFBC:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitRGBA8888_AFBC;
|
|
break;
|
|
case V4L2_PIX_FMT_BGR32_AFBC:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitBGRA8888_AFBC;
|
|
break;
|
|
case V4L2_PIX_FMT_RGBA1010102_AFBC:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitRGBA1010102_AFBC;
|
|
break;
|
|
case V4L2_PIX_FMT_BGRA1010102_AFBC:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_32bitBGRA1010102_AFBC;
|
|
break;
|
|
case V4L2_PIX_FMT_NV12_AFBC:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_NV12_AFBC;
|
|
break;
|
|
case V4L2_PIX_FMT_NV21_AFBC:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_NV21_AFBC;
|
|
break;
|
|
case V4L2_PIX_FMT_NV12_10B_AFBC:
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_NV12_10B_AFBC;
|
|
break;
|
|
|
|
default:
|
|
mtk_v4l2_err("Unsupport fourcc =%d default use I420",
|
|
q_data_src->fmt->fourcc);
|
|
param->input_yuv_fmt = VENC_YUV_FORMAT_I420;
|
|
break;
|
|
}
|
|
param->profile = enc_params->profile;
|
|
param->level = enc_params->level;
|
|
param->tier = enc_params->tier;
|
|
|
|
/* Config visible resolution */
|
|
param->width = q_data_src->visible_width;
|
|
param->height = q_data_src->visible_height;
|
|
/* Config coded resolution */
|
|
param->buf_width = q_data_src->coded_width;
|
|
param->buf_height = q_data_src->coded_height;
|
|
param->frm_rate = enc_params->framerate_num /
|
|
enc_params->framerate_denom;
|
|
param->intra_period = enc_params->intra_period;
|
|
param->gop_size = enc_params->gop_size;
|
|
param->bitrate = enc_params->bitrate;
|
|
param->operationrate = enc_params->operationrate;
|
|
param->scenario = enc_params->scenario;
|
|
param->prependheader = enc_params->prependheader;
|
|
param->bitratemode = enc_params->bitratemode;
|
|
param->roion = enc_params->roion;
|
|
param->heif_grid_size = enc_params->heif_grid_size;
|
|
// will copy to vsi, pass after streamon
|
|
param->color_desc = &enc_params->color_desc;
|
|
param->max_w = enc_params->max_w;
|
|
param->max_h = enc_params->max_h;
|
|
param->num_b_frame = enc_params->num_b_frame;
|
|
param->slbc_ready = ctx->use_slbc;
|
|
param->slbc_addr = ctx->slbc_addr;
|
|
param->i_qp = enc_params->i_qp;
|
|
param->p_qp = enc_params->p_qp;
|
|
param->b_qp = enc_params->b_qp;
|
|
param->svp_mode = enc_params->svp_mode;
|
|
param->tsvc = enc_params->tsvc;
|
|
param->highquality = enc_params->highquality;
|
|
param->dummynal = enc_params->dummynal;
|
|
|
|
param->max_qp = enc_params->max_qp;
|
|
param->min_qp = enc_params->min_qp;
|
|
param->framelvl_qp = enc_params->framelvl_qp;
|
|
param->ip_qpdelta = enc_params->ip_qpdelta;
|
|
param->qp_control_mode = enc_params->qp_control_mode;
|
|
}
|
|
|
|
static int vidioc_venc_subscribe_evt(struct v4l2_fh *fh,
|
|
const struct v4l2_event_subscription *sub)
|
|
{
|
|
switch (sub->type) {
|
|
case V4L2_EVENT_EOS:
|
|
return v4l2_event_subscribe(fh, sub, 2, NULL);
|
|
case V4L2_EVENT_MTK_VENC_ERROR:
|
|
return v4l2_event_subscribe(fh, sub, 0, NULL);
|
|
default:
|
|
return v4l2_ctrl_subscribe_event(fh, sub);
|
|
}
|
|
}
|
|
|
|
static void mtk_vdec_queue_stop_enc_event(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
static const struct v4l2_event ev_eos = {
|
|
.type = V4L2_EVENT_EOS,
|
|
};
|
|
|
|
mtk_v4l2_debug(0, "[%d]", ctx->id);
|
|
v4l2_event_queue_fh(&ctx->fh, &ev_eos);
|
|
}
|
|
|
|
void mtk_venc_queue_error_event(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
static const struct v4l2_event ev_error = {
|
|
.type = V4L2_EVENT_MTK_VENC_ERROR,
|
|
};
|
|
|
|
mtk_v4l2_debug(0, "[%d]", ctx->id);
|
|
v4l2_event_queue_fh(&ctx->fh, &ev_error);
|
|
}
|
|
|
|
static int vidioc_venc_s_fmt_cap(struct file *file, void *priv,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct vb2_queue *vq;
|
|
struct mtk_q_data *q_data;
|
|
int i, ret;
|
|
struct mtk_video_fmt *fmt;
|
|
|
|
mtk_v4l2_debug(4, "[%d] type %d", ctx->id, f->type);
|
|
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
|
|
if (!vq) {
|
|
mtk_v4l2_err("fail to get vq");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (vb2_is_busy(vq)) {
|
|
mtk_v4l2_err("queue busy");
|
|
return -EBUSY;
|
|
}
|
|
|
|
q_data = mtk_venc_get_q_data(ctx, f->type);
|
|
if (!q_data) {
|
|
mtk_v4l2_err("fail to get q data");
|
|
return -EINVAL;
|
|
}
|
|
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_ENC);
|
|
if (!fmt) {
|
|
f->fmt.pix.pixelformat =
|
|
mtk_venc_formats[default_cap_fmt_idx].fourcc;
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_ENC);
|
|
}
|
|
if (fmt == NULL) {
|
|
mtk_v4l2_err("fail to get fmt");
|
|
return -EINVAL;
|
|
}
|
|
|
|
q_data->fmt = fmt;
|
|
ret = vidioc_try_fmt(f, q_data->fmt, ctx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
q_data->coded_width = f->fmt.pix_mp.width;
|
|
q_data->coded_height = f->fmt.pix_mp.height;
|
|
q_data->field = f->fmt.pix_mp.field;
|
|
|
|
for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
|
|
struct v4l2_plane_pix_format *plane_fmt;
|
|
|
|
plane_fmt = &f->fmt.pix_mp.plane_fmt[i];
|
|
q_data->bytesperline[i] = plane_fmt->bytesperline;
|
|
q_data->sizeimage[i] = plane_fmt->sizeimage;
|
|
}
|
|
|
|
if (ctx->state == MTK_STATE_FREE) {
|
|
ret = venc_if_init(ctx, q_data->fmt->fourcc);
|
|
if (ret) {
|
|
mtk_v4l2_err("venc_if_init failed=%d, codec type=%x",
|
|
ret, q_data->fmt->fourcc);
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
return -EBUSY;
|
|
}
|
|
ctx->state = MTK_STATE_INIT;
|
|
}
|
|
if (ctx->state == MTK_STATE_ABORT) {
|
|
ctx->state = MTK_STATE_INIT; // format change, trigger encode header
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_venc_s_fmt_out(struct file *file, void *priv,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct vb2_queue *vq;
|
|
struct mtk_q_data *q_data;
|
|
int ret, i;
|
|
struct mtk_video_fmt *fmt;
|
|
|
|
mtk_v4l2_debug(4, "[%d] type %d", ctx->id, f->type);
|
|
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
|
|
if (!vq) {
|
|
mtk_v4l2_err("fail to get vq");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (vb2_is_busy(vq)) {
|
|
mtk_v4l2_err("queue busy");
|
|
return -EBUSY;
|
|
}
|
|
|
|
q_data = mtk_venc_get_q_data(ctx, f->type);
|
|
if (!q_data) {
|
|
mtk_v4l2_err("fail to get q data");
|
|
return -EINVAL;
|
|
}
|
|
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_FRAME);
|
|
if (!fmt) {
|
|
f->fmt.pix.pixelformat =
|
|
mtk_venc_formats[default_out_fmt_idx].fourcc;
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_FRAME);
|
|
}
|
|
|
|
q_data->visible_width = f->fmt.pix_mp.width;
|
|
q_data->visible_height = f->fmt.pix_mp.height;
|
|
q_data->fmt = fmt;
|
|
ret = vidioc_try_fmt(f, q_data->fmt, ctx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
q_data->coded_width = f->fmt.pix_mp.width;
|
|
q_data->coded_height = f->fmt.pix_mp.height;
|
|
|
|
q_data->field = f->fmt.pix_mp.field;
|
|
ctx->colorspace = f->fmt.pix_mp.colorspace;
|
|
ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
|
|
ctx->quantization = f->fmt.pix_mp.quantization;
|
|
ctx->xfer_func = f->fmt.pix_mp.xfer_func;
|
|
|
|
for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
|
|
struct v4l2_plane_pix_format *plane_fmt;
|
|
|
|
plane_fmt = &f->fmt.pix_mp.plane_fmt[i];
|
|
q_data->bytesperline[i] = plane_fmt->bytesperline;
|
|
q_data->sizeimage[i] = plane_fmt->sizeimage;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_venc_g_fmt(struct file *file, void *priv,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct vb2_queue *vq;
|
|
struct mtk_q_data *q_data;
|
|
int i;
|
|
|
|
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
|
|
if (!vq)
|
|
return -EINVAL;
|
|
|
|
q_data = mtk_venc_get_q_data(ctx, f->type);
|
|
|
|
pix->width = q_data->coded_width;
|
|
pix->height = q_data->coded_height;
|
|
pix->pixelformat = q_data->fmt->fourcc;
|
|
pix->field = q_data->field;
|
|
pix->num_planes = q_data->fmt->num_planes;
|
|
for (i = 0; i < pix->num_planes; i++) {
|
|
pix->plane_fmt[i].bytesperline = q_data->bytesperline[i];
|
|
pix->plane_fmt[i].sizeimage = q_data->sizeimage[i];
|
|
memset(&(pix->plane_fmt[i].reserved[0]), 0x0,
|
|
sizeof(pix->plane_fmt[i].reserved));
|
|
}
|
|
|
|
pix->flags = 0;
|
|
pix->colorspace = ctx->colorspace;
|
|
pix->ycbcr_enc = ctx->ycbcr_enc;
|
|
pix->quantization = ctx->quantization;
|
|
pix->xfer_func = ctx->xfer_func;
|
|
mtk_v4l2_debug(4, "[%d] type %d", ctx->id, f->type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct mtk_video_fmt *fmt;
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_ENC);
|
|
if (!fmt) {
|
|
f->fmt.pix.pixelformat =
|
|
mtk_venc_formats[default_cap_fmt_idx].fourcc;
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_ENC);
|
|
}
|
|
if (fmt == NULL) {
|
|
mtk_v4l2_err("fail to get fmt");
|
|
return -EINVAL;
|
|
}
|
|
|
|
f->fmt.pix_mp.colorspace = ctx->colorspace;
|
|
f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc;
|
|
f->fmt.pix_mp.quantization = ctx->quantization;
|
|
f->fmt.pix_mp.xfer_func = ctx->xfer_func;
|
|
|
|
return vidioc_try_fmt(f, fmt, ctx);
|
|
}
|
|
|
|
static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct mtk_video_fmt *fmt;
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_FRAME);
|
|
if (!fmt) {
|
|
f->fmt.pix.pixelformat =
|
|
mtk_venc_formats[default_out_fmt_idx].fourcc;
|
|
fmt = mtk_venc_find_format(f, MTK_FMT_FRAME);
|
|
}
|
|
if (fmt == NULL) {
|
|
mtk_v4l2_err("fail to get fmt");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!f->fmt.pix_mp.colorspace) {
|
|
f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
|
|
f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
|
|
f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
|
|
f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
|
|
}
|
|
|
|
return vidioc_try_fmt(f, fmt, ctx);
|
|
}
|
|
|
|
static int vidioc_venc_g_selection(struct file *file, void *priv,
|
|
struct v4l2_selection *s)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct mtk_q_data *q_data;
|
|
|
|
if (!V4L2_TYPE_IS_OUTPUT(s->type))
|
|
return -EINVAL;
|
|
|
|
if (s->target != V4L2_SEL_TGT_COMPOSE &&
|
|
s->target != V4L2_SEL_TGT_CROP)
|
|
return -EINVAL;
|
|
|
|
q_data = mtk_venc_get_q_data(ctx, s->type);
|
|
if (!q_data)
|
|
return -EINVAL;
|
|
|
|
s->r.top = 0;
|
|
s->r.left = 0;
|
|
s->r.width = q_data->visible_width;
|
|
s->r.height = q_data->visible_height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_venc_s_selection(struct file *file, void *priv,
|
|
struct v4l2_selection *s)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct mtk_q_data *q_data;
|
|
|
|
|
|
if (!V4L2_TYPE_IS_OUTPUT(s->type))
|
|
return -EINVAL;
|
|
|
|
if (s->target != V4L2_SEL_TGT_COMPOSE &&
|
|
s->target != V4L2_SEL_TGT_CROP)
|
|
return -EINVAL;
|
|
|
|
q_data = mtk_venc_get_q_data(ctx, s->type);
|
|
if (!q_data)
|
|
return -EINVAL;
|
|
|
|
s->r.top = 0;
|
|
s->r.left = 0;
|
|
q_data->visible_width = s->r.width;
|
|
q_data->visible_height = s->r.height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_venc_qbuf(struct file *file, void *priv,
|
|
struct v4l2_buffer *buf)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct vb2_queue *vq;
|
|
struct vb2_buffer *vb;
|
|
struct mtk_video_enc_buf *mtkbuf;
|
|
struct vb2_v4l2_buffer *vb2_v4l2;
|
|
|
|
if (ctx->state == MTK_STATE_ABORT) {
|
|
mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error",
|
|
ctx->id);
|
|
return -EIO;
|
|
}
|
|
|
|
// Check if need to proceed cache operations
|
|
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, buf->type);
|
|
if (buf->index >= vq->num_buffers) {
|
|
mtk_v4l2_err("[%d] buffer index %d out of range %d",
|
|
ctx->id, buf->index, vq->num_buffers);
|
|
return -EINVAL;
|
|
}
|
|
vb = vq->bufs[buf->index];
|
|
vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
|
|
mtkbuf = container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
|
|
if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
|
if (buf->m.planes[0].bytesused == 0) {
|
|
mtkbuf->lastframe = EOS;
|
|
mtk_v4l2_debug(1, "[%d] index=%d Eos FB(%d,%d) vb=%p pts=%llu",
|
|
ctx->id, buf->index,
|
|
buf->bytesused,
|
|
buf->length, vb, vb->timestamp);
|
|
} else if (buf->flags & V4L2_BUF_FLAG_LAST) {
|
|
mtkbuf->lastframe = EOS_WITH_DATA;
|
|
mtk_v4l2_debug(1, "[%d] id=%d EarlyEos FB(%d,%d) vb=%p pts=%llu",
|
|
ctx->id, buf->index, buf->m.planes[0].bytesused,
|
|
buf->length, vb, vb->timestamp);
|
|
} else {
|
|
mtkbuf->lastframe = NON_EOS;
|
|
mtk_v4l2_debug(1, "[%d] id=%d getdata FB(%d,%d) vb=%p pts=%llu ",
|
|
ctx->id, buf->index,
|
|
buf->m.planes[0].bytesused,
|
|
buf->length, mtkbuf, vb->timestamp);
|
|
}
|
|
} else
|
|
mtk_v4l2_debug(1, "[%d] id=%d BS (%d) vb=%p",
|
|
ctx->id, buf->index,
|
|
buf->length, mtkbuf);
|
|
|
|
if (buf->flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN) {
|
|
mtk_v4l2_debug(4, "[%d] No need for Cache clean, buf->index:%d. mtkbuf:%p",
|
|
ctx->id, buf->index, mtkbuf);
|
|
mtkbuf->flags |= NO_CAHCE_CLEAN;
|
|
}
|
|
|
|
if (buf->flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE) {
|
|
mtk_v4l2_debug(4, "[%d] No need for Cache invalidate, buf->index:%d. mtkbuf:%p",
|
|
ctx->id, buf->index, mtkbuf);
|
|
mtkbuf->flags |= NO_CAHCE_INVALIDATE;
|
|
}
|
|
|
|
mtkbuf->frm_buf.has_qpmap = 0;
|
|
mtkbuf->frm_buf.has_meta = 0;
|
|
mtkbuf->frm_buf.qpmap_dma = 0;
|
|
mtkbuf->frm_buf.metabuffer_dma = 0;
|
|
|
|
if (buf->flags & V4L2_BUF_FLAG_HDR_META && buf->reserved != 0) {
|
|
mtkbuf->frm_buf.has_meta = 1;
|
|
mtkbuf->frm_buf.meta_dma = dma_buf_get(buf->reserved);
|
|
|
|
if (IS_ERR(mtkbuf->frm_buf.meta_dma)) {
|
|
mtk_v4l2_err("%s meta_dma is err 0x%p.\n", __func__,
|
|
mtkbuf->frm_buf.meta_dma);
|
|
|
|
mtk_venc_queue_error_event(ctx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mtkbuf->frm_buf.buf_att = dma_buf_attach(mtkbuf->frm_buf.meta_dma,
|
|
&ctx->dev->plat_dev->dev);
|
|
mtkbuf->frm_buf.sgt = dma_buf_map_attachment(mtkbuf->frm_buf.buf_att,
|
|
DMA_TO_DEVICE);
|
|
if (IS_ERR_OR_NULL(mtkbuf->frm_buf.sgt)) {
|
|
mtk_v4l2_err("dma_buf_map_attachment fail %d.\n",
|
|
mtkbuf->frm_buf.sgt);
|
|
dma_buf_detach(mtkbuf->frm_buf.meta_dma, mtkbuf->frm_buf.buf_att);
|
|
return -EINVAL;
|
|
}
|
|
mtkbuf->frm_buf.meta_addr = sg_dma_address(mtkbuf->frm_buf.sgt->sgl);
|
|
|
|
mtk_v4l2_debug(1, "[%d] Have HDR info meta fd, buf->index:%d. mtkbuf:%p, fd:%u",
|
|
ctx->id, buf->index, mtkbuf, buf->reserved);
|
|
}
|
|
|
|
if (buf->flags & V4L2_BUF_FLAG_QP_META &&
|
|
buf->reserved > 0 &&
|
|
buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
|
struct device *dev = NULL;
|
|
|
|
mtkbuf->frm_buf.qpmap_dma = dma_buf_get(buf->reserved);
|
|
if (IS_ERR(mtkbuf->frm_buf.qpmap_dma)) {
|
|
mtk_v4l2_err("%s qpmap_dma is err 0x%p.\n", __func__,
|
|
mtkbuf->frm_buf.qpmap_dma);
|
|
mtk_venc_queue_error_event(ctx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev = ctx->m2m_ctx->cap_q_ctx.q.dev;
|
|
/* use vcp & vcu compatible access device */
|
|
|
|
mtkbuf->frm_buf.qpmap_dma_att = dma_buf_attach(mtkbuf->frm_buf.qpmap_dma,
|
|
dev);
|
|
mtkbuf->frm_buf.qpmap_sgt = dma_buf_map_attachment(mtkbuf->frm_buf.qpmap_dma_att,
|
|
DMA_TO_DEVICE);
|
|
if (IS_ERR_OR_NULL(mtkbuf->frm_buf.qpmap_sgt)) {
|
|
mtk_v4l2_err("dma_buf_map_attachment fail %d.\n",
|
|
mtkbuf->frm_buf.qpmap_sgt);
|
|
dma_buf_detach(mtkbuf->frm_buf.qpmap_dma, mtkbuf->frm_buf.qpmap_dma_att);
|
|
return -EINVAL;
|
|
}
|
|
mtkbuf->frm_buf.qpmap_dma_addr = sg_dma_address(mtkbuf->frm_buf.qpmap_sgt->sgl);
|
|
mtkbuf->frm_buf.has_qpmap = 1;
|
|
mtk_v4l2_debug(1, "[%d] Have Qpmap fd, buf->index:%d, qpmap_dma:%p, fd:%u",
|
|
ctx->id, buf->index, mtkbuf->frm_buf.qpmap_dma, buf->reserved);
|
|
}
|
|
|
|
if (buf->flags & V4L2_BUF_FLAG_HAS_META &&
|
|
buf->reserved > 0 &&
|
|
buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
|
struct dma_buf_attachment *meta_buf_att;
|
|
struct sg_table *meta_sgt;
|
|
struct metadata_info *meta_info;
|
|
void *meta_va = NULL;
|
|
int index = 0;
|
|
struct meta_describe meta_desc;
|
|
struct device *dev = NULL;
|
|
|
|
mtkbuf->frm_buf.metabuffer_dma = dma_buf_get(buf->reserved);
|
|
if (IS_ERR(mtkbuf->frm_buf.metabuffer_dma)) {
|
|
mtk_v4l2_err("%s metabuffer_dma is err 0x%p.\n", __func__,
|
|
mtkbuf->frm_buf.metabuffer_dma);
|
|
mtk_venc_queue_error_event(ctx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev = ctx->m2m_ctx->cap_q_ctx.q.dev;
|
|
/* use vcp & vcu compatible access device */
|
|
|
|
meta_buf_att = dma_buf_attach(mtkbuf->frm_buf.metabuffer_dma,
|
|
dev);
|
|
meta_sgt = dma_buf_map_attachment(meta_buf_att, DMA_TO_DEVICE);
|
|
if (IS_ERR_OR_NULL(meta_sgt)) {
|
|
mtk_v4l2_err("dma_buf_map_attachment fail %d.\n", meta_sgt);
|
|
dma_buf_detach(mtkbuf->frm_buf.metabuffer_dma, meta_buf_att);
|
|
dma_buf_put(mtkbuf->frm_buf.metabuffer_dma);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mtkbuf->frm_buf.metabuffer_addr = sg_dma_address(meta_sgt->sgl);
|
|
//check required size before doing va mapping
|
|
if (mtkbuf->frm_buf.metabuffer_dma->size < sizeof(struct metadata_info)) {
|
|
mtk_v4l2_err("V4L2_BUF_FLAG_HAS_META dma size check failed");
|
|
dma_buf_unmap_attachment(meta_buf_att, meta_sgt, DMA_TO_DEVICE);
|
|
dma_buf_detach(mtkbuf->frm_buf.metabuffer_dma, meta_buf_att);
|
|
dma_buf_put(mtkbuf->frm_buf.metabuffer_dma);
|
|
return -EINVAL;
|
|
}
|
|
meta_va = dma_buf_vmap(mtkbuf->frm_buf.metabuffer_dma);
|
|
|
|
mtk_v4l2_debug(2, "V4L2_BUF_FLAG_HAS_META buf->reserved:%d dma_buf=%p, DMA=%lx",
|
|
buf->reserved, mtkbuf->frm_buf.metabuffer_dma,
|
|
(unsigned long)mtkbuf->frm_buf.metabuffer_addr);
|
|
|
|
if (meta_va) {
|
|
meta_info = (struct metadata_info *)meta_va;
|
|
} else {
|
|
mtk_v4l2_err("V4L2_BUF_FLAG_HAS_META meta_va is NULL");
|
|
dma_buf_unmap_attachment(meta_buf_att, meta_sgt, DMA_TO_DEVICE);
|
|
dma_buf_detach(mtkbuf->frm_buf.metabuffer_dma, meta_buf_att);
|
|
dma_buf_put(mtkbuf->frm_buf.metabuffer_dma);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (; index < MTK_MAX_METADATA_NUM; index++) {
|
|
memset(&meta_desc, 0, sizeof(meta_desc));
|
|
meta_desc.invalid = meta_info->metadata_dsc[index].invalid;
|
|
if (!meta_desc.invalid)
|
|
break;
|
|
|
|
meta_desc.fd_flag = meta_info->metadata_dsc[index].fd_flag;
|
|
meta_desc.type = meta_info->metadata_dsc[index].type;
|
|
meta_desc.size = meta_info->metadata_dsc[index].size;
|
|
meta_desc.value = meta_info->metadata_dsc[index].value;
|
|
|
|
mtk_v4l2_debug(1, "meta data info,index:%d fd_flag:%u type:%u size:%u val:%u)",
|
|
index, meta_desc.fd_flag, meta_desc.type,
|
|
meta_desc.size, meta_desc.value);
|
|
|
|
if (meta_desc.type == METADATA_QPMAP && !meta_desc.fd_flag) {
|
|
mtk_v4l2_err("qpmap should provide buffer fd");
|
|
continue;
|
|
} else if (meta_desc.type == METADATA_HDR && meta_desc.fd_flag) {
|
|
mtk_v4l2_err("hdr should not provide buffer fd");
|
|
continue;
|
|
}
|
|
|
|
if (meta_desc.fd_flag) {
|
|
if (meta_desc.type == METADATA_QPMAP) {
|
|
struct dma_buf_attachment *qpmap_buf_att;
|
|
struct sg_table *qpmap_meta_sgt;
|
|
|
|
mtkbuf->frm_buf.qpmap_dma = dma_buf_get(meta_desc.value);
|
|
|
|
if (IS_ERR(mtkbuf->frm_buf.qpmap_dma)) {
|
|
mtk_v4l2_err("%s qpmap_dma is err 0x%p.\n",
|
|
__func__, mtkbuf->frm_buf.qpmap_dma);
|
|
mtk_venc_queue_error_event(ctx);
|
|
continue;
|
|
}
|
|
|
|
qpmap_buf_att = dma_buf_attach(mtkbuf->frm_buf.qpmap_dma,
|
|
dev);
|
|
qpmap_meta_sgt = dma_buf_map_attachment(qpmap_buf_att,
|
|
DMA_TO_DEVICE);
|
|
if (IS_ERR_OR_NULL(qpmap_meta_sgt)) {
|
|
mtk_v4l2_err("dma_buf_map_attachment fail %d.\n",
|
|
qpmap_meta_sgt);
|
|
dma_buf_detach(mtkbuf->frm_buf.qpmap_dma,
|
|
qpmap_buf_att);
|
|
dma_buf_put(mtkbuf->frm_buf.qpmap_dma);
|
|
continue;
|
|
}
|
|
mtkbuf->frm_buf.qpmap_dma_addr =
|
|
sg_dma_address(qpmap_meta_sgt->sgl);
|
|
mtkbuf->frm_buf.has_qpmap = 1;
|
|
dma_buf_unmap_attachment(qpmap_buf_att,
|
|
qpmap_meta_sgt, DMA_TO_DEVICE);
|
|
dma_buf_detach(mtkbuf->frm_buf.qpmap_dma, qpmap_buf_att);
|
|
mtk_v4l2_debug(2, "[%d] Have Qpmap fd, buf->index:%d. qpmap_dma:%p, fd:%u",
|
|
ctx->id, buf->index,
|
|
mtkbuf->frm_buf.qpmap_dma, meta_desc.value);
|
|
}
|
|
} else {
|
|
if (meta_desc.type == METADATA_HDR) {
|
|
mtkbuf->frm_buf.has_meta = 1;
|
|
mtkbuf->frm_buf.meta_dma = mtkbuf->frm_buf.metabuffer_dma;
|
|
mtkbuf->frm_buf.meta_addr =
|
|
mtkbuf->frm_buf.metabuffer_addr + meta_desc.value;
|
|
//vpud use fd to get va and pa,we should add a
|
|
//offset to get real address of hdr
|
|
mtkbuf->frm_buf.meta_offset = meta_desc.value;
|
|
}
|
|
}
|
|
}
|
|
dma_buf_vunmap(mtkbuf->frm_buf.metabuffer_dma, meta_va);
|
|
dma_buf_unmap_attachment(meta_buf_att, meta_sgt, DMA_TO_DEVICE);
|
|
dma_buf_detach(mtkbuf->frm_buf.metabuffer_dma, meta_buf_att);
|
|
}
|
|
|
|
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
|
|
}
|
|
|
|
static int vidioc_venc_dqbuf(struct file *file, void *priv,
|
|
struct v4l2_buffer *buf)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
|
|
if (ctx->state == MTK_STATE_ABORT) {
|
|
mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error",
|
|
ctx->id);
|
|
return -EIO;
|
|
}
|
|
|
|
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
|
|
}
|
|
|
|
static int vidioc_try_encoder_cmd(struct file *file, void *priv,
|
|
struct v4l2_encoder_cmd *cmd)
|
|
{
|
|
switch (cmd->cmd) {
|
|
case V4L2_ENC_CMD_STOP:
|
|
case V4L2_ENC_CMD_START:
|
|
cmd->flags = 0; // don't support flags
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vidioc_encoder_cmd(struct file *file, void *priv,
|
|
struct v4l2_encoder_cmd *cmd)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
|
|
struct vb2_queue *src_vq, *dst_vq;
|
|
int ret;
|
|
|
|
ret = vidioc_try_encoder_cmd(file, priv, cmd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mtk_v4l2_debug(0, "[%d] encoder cmd= %u", ctx->id, cmd->cmd);
|
|
dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
|
|
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
|
|
switch (cmd->cmd) {
|
|
case V4L2_ENC_CMD_STOP:
|
|
src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
|
|
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
|
|
if (!vb2_is_streaming(src_vq)) {
|
|
mtk_v4l2_debug(1, "Output stream is off. No need to flush.");
|
|
return 0;
|
|
}
|
|
if (!vb2_is_streaming(dst_vq)) {
|
|
mtk_v4l2_debug(1, "Capture stream is off. No need to flush.");
|
|
return 0;
|
|
}
|
|
if (ctx->enc_flush_buf->lastframe == NON_EOS) {
|
|
ctx->enc_flush_buf->lastframe = EOS;
|
|
v4l2_m2m_buf_queue_check(ctx->m2m_ctx, &ctx->enc_flush_buf->vb);
|
|
v4l2_m2m_try_schedule(ctx->m2m_ctx);
|
|
} else {
|
|
mtk_v4l2_debug(1, "Stopping no need to queue cmd enc_flush_buf.");
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case V4L2_ENC_CMD_START:
|
|
vb2_clear_last_buffer_dequeued(dst_vq);
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = {
|
|
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
|
|
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
|
|
|
|
.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
|
|
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
|
|
.vidioc_qbuf = vidioc_venc_qbuf,
|
|
.vidioc_dqbuf = vidioc_venc_dqbuf,
|
|
|
|
.vidioc_querycap = vidioc_venc_querycap,
|
|
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap_mplane,
|
|
.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out_mplane,
|
|
.vidioc_enum_framesizes = vidioc_enum_framesizes,
|
|
|
|
.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
|
|
.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane,
|
|
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
|
|
|
|
.vidioc_s_parm = vidioc_venc_s_parm,
|
|
.vidioc_g_parm = vidioc_venc_g_parm,
|
|
.vidioc_s_fmt_vid_cap_mplane = vidioc_venc_s_fmt_cap,
|
|
.vidioc_s_fmt_vid_out_mplane = vidioc_venc_s_fmt_out,
|
|
|
|
.vidioc_g_fmt_vid_cap_mplane = vidioc_venc_g_fmt,
|
|
.vidioc_g_fmt_vid_out_mplane = vidioc_venc_g_fmt,
|
|
|
|
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
|
|
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
|
|
|
|
.vidioc_subscribe_event = vidioc_venc_subscribe_evt,
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
|
|
|
.vidioc_g_selection = vidioc_venc_g_selection,
|
|
.vidioc_s_selection = vidioc_venc_s_selection,
|
|
|
|
.vidioc_encoder_cmd = vidioc_encoder_cmd,
|
|
.vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
|
|
};
|
|
|
|
static int vb2ops_venc_queue_setup(struct vb2_queue *vq,
|
|
unsigned int *nbuffers,
|
|
unsigned int *nplanes,
|
|
unsigned int sizes[],
|
|
struct device *alloc_devs[])
|
|
{
|
|
struct mtk_vcodec_ctx *ctx;
|
|
struct mtk_q_data *q_data;
|
|
unsigned int i;
|
|
|
|
if (IS_ERR_OR_NULL(vq) || IS_ERR_OR_NULL(nbuffers) ||
|
|
IS_ERR_OR_NULL(nplanes) || IS_ERR_OR_NULL(alloc_devs)) {
|
|
mtk_v4l2_err("vq %p, nbuffers %p, nplanes %p, alloc_devs %p",
|
|
vq, nbuffers, nplanes, alloc_devs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctx = vb2_get_drv_priv(vq);
|
|
q_data = mtk_venc_get_q_data(ctx, vq->type);
|
|
if (q_data == NULL || (*nplanes) > MTK_VCODEC_MAX_PLANES) {
|
|
mtk_v4l2_err("vq->type=%d nplanes %d err", vq->type, *nplanes);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (*nplanes) {
|
|
for (i = 0; i < *nplanes; i++)
|
|
if (sizes[i] < q_data->sizeimage[i])
|
|
return -EINVAL;
|
|
} else {
|
|
*nplanes = q_data->fmt->num_planes;
|
|
for (i = 0; i < *nplanes; i++)
|
|
sizes[i] = q_data->sizeimage[i];
|
|
}
|
|
|
|
mtk_v4l2_debug(2, "[%d] nplanes %d sizeimage %d %d %d, state=%d",
|
|
ctx->id,
|
|
*nplanes,
|
|
q_data->sizeimage[0],
|
|
q_data->sizeimage[1],
|
|
q_data->sizeimage[2],
|
|
ctx->state);
|
|
|
|
if (ctx->enc_params.svp_mode && is_disable_map_sec() && mtk_venc_is_vcu()) {
|
|
vq->mem_ops = &venc_sec_dma_contig_memops;
|
|
mtk_v4l2_debug(1, "[%d] hook mem_ops.map_dmabuf for queue type %d",
|
|
ctx->id, vq->type);
|
|
}
|
|
|
|
if (ctx->state == MTK_STATE_ABORT) { // previously stream off with task not empty
|
|
ctx->state = MTK_STATE_FLUSH;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
struct mtk_q_data *q_data;
|
|
int i;
|
|
struct mtk_video_enc_buf *mtkbuf;
|
|
struct vb2_v4l2_buffer *vb2_v4l2;
|
|
|
|
if (vb->vb2_queue->memory != VB2_MEMORY_DMABUF)
|
|
return 0;
|
|
|
|
q_data = mtk_venc_get_q_data(ctx, vb->vb2_queue->type);
|
|
|
|
// Check if need to proceed cache operations
|
|
vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
|
|
mtkbuf = container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
|
|
for (i = 0; i < q_data->fmt->num_planes; i++) {
|
|
if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
|
|
mtk_v4l2_err("data will not fit into plane %d (%lu < %d)",
|
|
i,
|
|
vb2_plane_size(vb, i),
|
|
q_data->sizeimage[i]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// Check if need to proceed cache operations
|
|
vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
|
|
mtkbuf = container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
if (!(mtkbuf->flags & NO_CAHCE_CLEAN)) {
|
|
struct mtk_vcodec_mem src_mem;
|
|
struct dma_buf_attachment *buf_att;
|
|
struct sg_table *sgt;
|
|
|
|
buf_att = dma_buf_attach(vb->planes[i].dbuf,
|
|
&ctx->dev->plat_dev->dev);
|
|
buf_att->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC;
|
|
sgt = dma_buf_map_attachment(buf_att, DMA_TO_DEVICE);
|
|
if (IS_ERR_OR_NULL(sgt)) {
|
|
mtk_v4l2_err("dma_buf_map_attachment fail %d.\n", sgt);
|
|
dma_buf_detach(vb->planes[i].dbuf, buf_att);
|
|
return -EINVAL;
|
|
}
|
|
dma_sync_sg_for_device(&ctx->dev->plat_dev->dev,
|
|
sgt->sgl,
|
|
sgt->orig_nents,
|
|
DMA_TO_DEVICE);
|
|
dma_buf_unmap_attachment(buf_att, sgt, DMA_TO_DEVICE);
|
|
|
|
src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(vb, i);
|
|
src_mem.size = (size_t)(vb->planes[i].bytesused -
|
|
vb->planes[i].data_offset);
|
|
dma_buf_detach(vb->planes[i].dbuf, buf_att);
|
|
|
|
mtk_v4l2_debug(4, "[%d] Cache sync TD for %lx sz=%d dev %p ",
|
|
ctx->id,
|
|
(unsigned long)src_mem.dma_addr,
|
|
(unsigned int)src_mem.size,
|
|
&ctx->dev->plat_dev->dev);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vb2ops_venc_buf_finish(struct vb2_buffer *vb)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
struct mtk_video_enc_buf *mtkbuf;
|
|
struct vb2_v4l2_buffer *vb2_v4l2;
|
|
|
|
vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
|
|
mtkbuf = container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
|
|
if (vb2_v4l2->flags & V4L2_BUF_FLAG_LAST)
|
|
mtk_v4l2_debug(0, "[%d] type(%d) flags=%x idx=%d pts=%llu",
|
|
ctx->id, vb->vb2_queue->type, vb2_v4l2->flags,
|
|
vb->index, vb->timestamp);
|
|
|
|
if (vb->vb2_queue->memory == VB2_MEMORY_DMABUF &&
|
|
!(mtkbuf->flags & NO_CAHCE_INVALIDATE)) {
|
|
if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
|
|
struct mtk_vcodec_mem dst_mem;
|
|
struct dma_buf_attachment *buf_att;
|
|
struct sg_table *sgt;
|
|
|
|
buf_att = dma_buf_attach(vb->planes[0].dbuf,
|
|
&ctx->dev->plat_dev->dev);
|
|
buf_att->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC;
|
|
sgt = dma_buf_map_attachment(buf_att, DMA_FROM_DEVICE);
|
|
if (IS_ERR_OR_NULL(sgt)) {
|
|
mtk_v4l2_err("dma_buf_map_attachment fail %d.\n", sgt);
|
|
dma_buf_detach(vb->planes[0].dbuf, buf_att);
|
|
return;
|
|
}
|
|
mtk_dma_sync_sg_range(sgt, &ctx->dev->plat_dev->dev,
|
|
ROUND_N(vb->planes[0].bytesused, 64), DMA_FROM_DEVICE);
|
|
|
|
dma_buf_unmap_attachment(buf_att, sgt, DMA_FROM_DEVICE);
|
|
|
|
dst_mem.dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
|
|
dst_mem.size = (size_t)vb->planes[0].bytesused;
|
|
dma_buf_detach(vb->planes[0].dbuf, buf_att);
|
|
mtk_v4l2_debug(4,
|
|
"[%d] Cache sync FD for %lx sz=%d dev %p",
|
|
ctx->id,
|
|
(unsigned long)dst_mem.dma_addr,
|
|
(unsigned int)dst_mem.size,
|
|
&ctx->dev->plat_dev->dev);
|
|
}
|
|
}
|
|
|
|
if (mtkbuf->frm_buf.metabuffer_dma == 0 && mtkbuf->frm_buf.meta_dma != 0) {
|
|
mtk_v4l2_debug(4,
|
|
"dma_buf_put dma_buf=%p, DMA=%lx",
|
|
mtkbuf->frm_buf.meta_dma,
|
|
(unsigned long)mtkbuf->frm_buf.meta_addr);
|
|
dma_buf_unmap_attachment(mtkbuf->frm_buf.buf_att,
|
|
mtkbuf->frm_buf.sgt, DMA_TO_DEVICE);
|
|
dma_buf_detach(mtkbuf->frm_buf.meta_dma, mtkbuf->frm_buf.buf_att);
|
|
dma_buf_put(mtkbuf->frm_buf.meta_dma);
|
|
mtkbuf->frm_buf.meta_dma = 0;
|
|
}
|
|
|
|
if (mtkbuf->frm_buf.metabuffer_dma != 0) {
|
|
mtk_v4l2_debug(2,
|
|
"dma_buf_put dma_buf=%p, DMA=%lx",
|
|
mtkbuf->frm_buf.metabuffer_dma,
|
|
(unsigned long)mtkbuf->frm_buf.metabuffer_addr);
|
|
dma_buf_put(mtkbuf->frm_buf.metabuffer_dma);
|
|
mtkbuf->frm_buf.metabuffer_dma = 0;
|
|
}
|
|
|
|
if (mtkbuf->frm_buf.qpmap_dma != 0) {
|
|
mtk_v4l2_debug(2,
|
|
"dma_buf_put qpmap_dma=%p, DMA=%lx",
|
|
mtkbuf->frm_buf.qpmap_dma,
|
|
(unsigned long)mtkbuf->frm_buf.qpmap_dma_addr);
|
|
if (mtkbuf->frm_buf.qpmap_sgt != NULL) {
|
|
dma_buf_unmap_attachment(mtkbuf->frm_buf.qpmap_dma_att,
|
|
mtkbuf->frm_buf.qpmap_sgt, DMA_TO_DEVICE);
|
|
dma_buf_detach(mtkbuf->frm_buf.qpmap_dma, mtkbuf->frm_buf.qpmap_dma_att);
|
|
}
|
|
dma_buf_put(mtkbuf->frm_buf.qpmap_dma);
|
|
mtkbuf->frm_buf.qpmap_dma = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void vb2ops_venc_buf_queue(struct vb2_buffer *vb)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
struct vb2_v4l2_buffer *vb2_v4l2 =
|
|
container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
|
|
|
|
struct mtk_video_enc_buf *mtk_buf =
|
|
container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
|
|
if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) &&
|
|
(ctx->param_change != MTK_ENCODE_PARAM_NONE)) {
|
|
mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
ctx->param_change);
|
|
mtk_buf->param_change = ctx->param_change;
|
|
mtk_buf->enc_params = ctx->enc_params;
|
|
ctx->param_change = MTK_ENCODE_PARAM_NONE;
|
|
}
|
|
|
|
v4l2_m2m_buf_queue_check(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
|
|
}
|
|
|
|
static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q);
|
|
struct venc_enc_param param;
|
|
int ret;
|
|
int i;
|
|
|
|
mtk_v4l2_debug(4, "[%d] (%d) state=(%x)", ctx->id, q->type, ctx->state);
|
|
/* Once state turn into MTK_STATE_ABORT, we need stop_streaming
|
|
* to clear it
|
|
*/
|
|
if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) {
|
|
ret = -EIO;
|
|
goto err_set_param;
|
|
}
|
|
|
|
/* Do the initialization when both start_streaming have been called */
|
|
if (V4L2_TYPE_IS_OUTPUT(q->type)) {
|
|
if (!vb2_start_streaming_called(&ctx->m2m_ctx->cap_q_ctx.q))
|
|
return 0;
|
|
} else {
|
|
if (!vb2_start_streaming_called(&ctx->m2m_ctx->out_q_ctx.q))
|
|
return 0;
|
|
}
|
|
|
|
memset(¶m, 0, sizeof(param));
|
|
mtk_venc_set_param(ctx, ¶m);
|
|
ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, ¶m);
|
|
|
|
mtk_v4l2_debug(0,
|
|
"fmt 0x%x, P/L %d/%d, w/h %d/%d, buf %d/%d, fps/bps %d/%d(%d), gop %d, ip# %d opr %d async %d grid size %d/%d b#%d, slbc %d maxqp %d minqp %d",
|
|
param.input_yuv_fmt, param.profile,
|
|
param.level, param.width, param.height,
|
|
param.buf_width, param.buf_height,
|
|
param.frm_rate, param.bitrate, param.bitratemode,
|
|
param.gop_size, param.intra_period,
|
|
param.operationrate, ctx->async_mode,
|
|
(param.heif_grid_size>>16), param.heif_grid_size&0xffff,
|
|
param.num_b_frame, param.slbc_ready, param.max_qp, param.min_qp);
|
|
|
|
if (ret) {
|
|
mtk_v4l2_err("venc_if_set_param failed=%d", ret);
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
goto err_set_param;
|
|
}
|
|
ctx->param_change = MTK_ENCODE_PARAM_NONE;
|
|
|
|
if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264 ||
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H265 ||
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_HEIF ||
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_MPEG4) &&
|
|
(ctx->enc_params.seq_hdr_mode !=
|
|
V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)) {
|
|
ret = venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_PREPEND_HEADER,
|
|
NULL);
|
|
if (ret) {
|
|
mtk_v4l2_err("venc_if_set_param failed=%d", ret);
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
goto err_set_param;
|
|
}
|
|
ctx->state = MTK_STATE_HEADER;
|
|
} else if (ctx->state == MTK_STATE_FLUSH) {
|
|
mtk_v4l2_debug(1, "recover from flush");
|
|
ctx->state = MTK_STATE_HEADER; // flush and reset
|
|
} else {
|
|
ctx->state = MTK_STATE_INIT;
|
|
}
|
|
|
|
mutex_lock(&ctx->dev->enc_dvfs_mutex);
|
|
mtk_venc_dvfs_begin_inst(ctx);
|
|
mtk_venc_pmqos_begin_inst(ctx);
|
|
mutex_unlock(&ctx->dev->enc_dvfs_mutex);
|
|
|
|
return 0;
|
|
|
|
err_set_param:
|
|
for (i = 0; i < q->num_buffers; ++i) {
|
|
if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) {
|
|
mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED",
|
|
ctx->id, i, q->type,
|
|
(int)q->bufs[i]->state);
|
|
v4l2_m2m_buf_done(to_vb2_v4l2_buffer(q->bufs[i]),
|
|
VB2_BUF_STATE_QUEUED);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void vb2ops_venc_stop_streaming(struct vb2_queue *q)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q);
|
|
struct vb2_buffer *dst_buf;
|
|
struct vb2_v4l2_buffer *src_vb2_v4l2, *dst_vb2_v4l2;
|
|
struct mtk_video_enc_buf *srcbuf, *dstbuf;
|
|
struct vb2_queue *srcq, *dstq;
|
|
struct venc_done_result enc_result;
|
|
int i, ret;
|
|
|
|
mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type);
|
|
|
|
if (vb2_start_streaming_called(&ctx->m2m_ctx->cap_q_ctx.q) &&
|
|
vb2_start_streaming_called(&ctx->m2m_ctx->out_q_ctx.q)) {
|
|
ret = venc_if_encode(ctx,
|
|
VENC_START_OPT_ENCODE_FRAME_FINAL,
|
|
NULL, NULL, &enc_result);
|
|
if (!ctx->async_mode)
|
|
mtk_enc_put_buf(ctx);
|
|
if (ret) {
|
|
mtk_v4l2_err("venc_if_encode FINAL failed=%d", ret);
|
|
if (ret == -EIO) {
|
|
dstq = &ctx->m2m_ctx->cap_q_ctx.q;
|
|
srcq = &ctx->m2m_ctx->out_q_ctx.q;
|
|
for (i = 0; i < dstq->num_buffers; i++) {
|
|
dst_vb2_v4l2 = container_of(
|
|
dstq->bufs[i], struct vb2_v4l2_buffer, vb2_buf);
|
|
dstbuf = container_of(
|
|
dst_vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
if (dst_vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
|
|
v4l2_m2m_buf_done(&dstbuf->vb, VB2_BUF_STATE_ERROR);
|
|
}
|
|
|
|
for (i = 0; i < srcq->num_buffers; i++) {
|
|
src_vb2_v4l2 = container_of(
|
|
srcq->bufs[i], struct vb2_v4l2_buffer, vb2_buf);
|
|
srcbuf = container_of(
|
|
src_vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
if (src_vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
|
|
v4l2_m2m_buf_done(&srcbuf->vb, VB2_BUF_STATE_ERROR);
|
|
}
|
|
venc_check_release_lock(ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
|
|
while ((dst_vb2_v4l2 = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) {
|
|
dst_buf = &dst_vb2_v4l2->vb2_buf;
|
|
dst_buf->planes[0].bytesused = 0;
|
|
if (dst_vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2, VB2_BUF_STATE_ERROR);
|
|
}
|
|
} else {
|
|
while ((src_vb2_v4l2 = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) {
|
|
if (src_vb2_v4l2 != &ctx->enc_flush_buf->vb &&
|
|
src_vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
|
|
v4l2_m2m_buf_done(src_vb2_v4l2, VB2_BUF_STATE_ERROR);
|
|
}
|
|
ctx->enc_flush_buf->lastframe = NON_EOS;
|
|
mutex_lock(&ctx->dev->enc_dvfs_mutex);
|
|
mtk_venc_dvfs_end_inst(ctx);
|
|
mtk_venc_pmqos_end_inst(ctx);
|
|
mutex_unlock(&ctx->dev->enc_dvfs_mutex);
|
|
}
|
|
|
|
if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
|
|
vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q)) ||
|
|
(q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
|
|
vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q))) {
|
|
mtk_v4l2_debug(1, "[%d]-> q type %d out=%d cap=%d",
|
|
ctx->id, q->type,
|
|
vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q),
|
|
vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q));
|
|
return;
|
|
}
|
|
}
|
|
|
|
static const struct vb2_ops mtk_venc_vb2_ops = {
|
|
.queue_setup = vb2ops_venc_queue_setup,
|
|
.buf_prepare = vb2ops_venc_buf_prepare,
|
|
.buf_queue = vb2ops_venc_buf_queue,
|
|
.wait_prepare = vb2_ops_wait_prepare,
|
|
.wait_finish = vb2_ops_wait_finish,
|
|
.buf_finish = vb2ops_venc_buf_finish,
|
|
.start_streaming = vb2ops_venc_start_streaming,
|
|
.stop_streaming = vb2ops_venc_stop_streaming,
|
|
};
|
|
|
|
static int mtk_venc_encode_header(void *priv)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = priv;
|
|
int ret;
|
|
struct vb2_buffer *src_buf, *dst_buf;
|
|
struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2;
|
|
struct mtk_video_enc_buf *dst_buf_info;
|
|
struct mtk_vcodec_mem *bs_buf;
|
|
struct venc_done_result enc_result;
|
|
|
|
memset(&enc_result, 0, sizeof(enc_result));
|
|
dst_vb2_v4l2 = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
|
|
if (!dst_vb2_v4l2) {
|
|
mtk_v4l2_debug(1, "No dst buffer");
|
|
return -EINVAL;
|
|
}
|
|
dst_buf = &dst_vb2_v4l2->vb2_buf;
|
|
dst_buf_info = container_of(dst_vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
|
|
bs_buf = &dst_buf_info->bs_buf;
|
|
bs_buf->va = vb2_plane_vaddr(dst_buf, 0);
|
|
bs_buf->dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
|
|
bs_buf->size = (size_t)dst_buf->planes[0].length;
|
|
bs_buf->dmabuf = dst_buf->planes[0].dbuf;
|
|
bs_buf->index = dst_buf->index;
|
|
ctx->bs_list[bs_buf->index + 1] = (uintptr_t)bs_buf;
|
|
|
|
mtk_v4l2_debug(0,
|
|
"[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu state %d",
|
|
ctx->id,
|
|
dst_buf->index, bs_buf->va,
|
|
(u64)bs_buf->dma_addr,
|
|
bs_buf->size, ctx->state);
|
|
|
|
ret = venc_if_encode(ctx,
|
|
VENC_START_OPT_ENCODE_SEQUENCE_HEADER,
|
|
NULL, bs_buf, &enc_result);
|
|
|
|
get_free_buffers(ctx, &enc_result);
|
|
|
|
if (enc_result.bs_va == 0) {
|
|
dst_buf->planes[0].bytesused = 0;
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2,
|
|
VB2_BUF_STATE_ERROR);
|
|
mtk_v4l2_err("%s venc_if_encode failed=%d",
|
|
__func__, ret);
|
|
return -EINVAL;
|
|
}
|
|
src_vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
|
|
if (src_vb2_v4l2) {
|
|
src_buf = &src_vb2_v4l2->vb2_buf;
|
|
dst_vb2_v4l2->vb2_buf.timestamp =
|
|
src_vb2_v4l2->vb2_buf.timestamp;
|
|
dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode;
|
|
} else
|
|
mtk_v4l2_err("No timestamp for the header buffer.");
|
|
|
|
ctx->state = MTK_STATE_HEADER;
|
|
dst_buf->planes[0].bytesused = enc_result.bs_size;
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2, VB2_BUF_STATE_DONE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
struct venc_enc_param enc_prm;
|
|
struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
|
|
struct mtk_video_enc_buf *mtk_buf =
|
|
container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
|
|
int ret = 0;
|
|
|
|
memset(&enc_prm, 0, sizeof(enc_prm));
|
|
if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE)
|
|
return 0;
|
|
|
|
if (mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE) {
|
|
enc_prm.bitrate = mtk_buf->enc_params.bitrate;
|
|
mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
enc_prm.bitrate);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ADJUST_BITRATE,
|
|
&enc_prm);
|
|
}
|
|
if (mtk_buf->param_change & MTK_ENCODE_PARAM_SEC_ENCODE) {
|
|
enc_prm.svp_mode = mtk_buf->enc_params.svp_mode;
|
|
mtk_v4l2_debug(0, "[%d] change param svp=%d",
|
|
ctx->id,
|
|
enc_prm.svp_mode);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_SEC_MODE,
|
|
&enc_prm);
|
|
}
|
|
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMERATE) {
|
|
enc_prm.frm_rate = mtk_buf->enc_params.framerate_num /
|
|
mtk_buf->enc_params.framerate_denom;
|
|
mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
enc_prm.frm_rate);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ADJUST_FRAMERATE,
|
|
&enc_prm);
|
|
}
|
|
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_GOP_SIZE) {
|
|
enc_prm.gop_size = mtk_buf->enc_params.gop_size;
|
|
mtk_v4l2_debug(1, "change param intra period=%d",
|
|
enc_prm.gop_size);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_GOP_SIZE,
|
|
&enc_prm);
|
|
}
|
|
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) {
|
|
mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.force_intra);
|
|
if (mtk_buf->enc_params.force_intra)
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_FORCE_INTRA,
|
|
NULL);
|
|
}
|
|
|
|
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_SCENARIO) {
|
|
enc_prm.scenario = mtk_buf->enc_params.scenario;
|
|
if (mtk_buf->enc_params.scenario)
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_SCENARIO,
|
|
&enc_prm);
|
|
mtk_v4l2_debug(0, "[%d] idx=%d, change param scenario=%d async_mode=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.scenario,
|
|
ctx->async_mode);
|
|
}
|
|
|
|
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_NONREFP) {
|
|
enc_prm.nonrefp = mtk_buf->enc_params.nonrefp;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, change param nonref=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.nonrefp);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_NONREFP,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_NONREFPFREQ) {
|
|
enc_prm.nonrefpfreq = mtk_buf->enc_params.nonrefpfreq;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, change param nonrefpfreq=%d",
|
|
ctx->id, mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.nonrefpfreq);
|
|
ret |= venc_if_set_param(
|
|
ctx, VENC_SET_PARAM_NONREFPFREQ, &enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_DETECTED_FRAMERATE) {
|
|
enc_prm.detectframerate = mtk_buf->enc_params.detectframerate;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, change param detectfr=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.detectframerate);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_DETECTED_FRAMERATE,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_RFS_ON) {
|
|
enc_prm.rfs = mtk_buf->enc_params.rfs;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, change param rfs=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.rfs);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_RFS_ON,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_PREPEND_SPSPPS_TO_IDR) {
|
|
enc_prm.prependheader = mtk_buf->enc_params.prependheader;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, prepend spspps idr=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.prependheader);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_PREPEND_SPSPPS_TO_IDR,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_OPERATION_RATE) {
|
|
enc_prm.operationrate = mtk_buf->enc_params.operationrate;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, operationrate=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.operationrate);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_OPERATION_RATE,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE_MODE) {
|
|
enc_prm.bitratemode = mtk_buf->enc_params.bitratemode;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, bitratemode=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.bitratemode);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_BITRATE_MODE,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_ROI_ON) {
|
|
enc_prm.roion = mtk_buf->enc_params.roion;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, roion=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.roion);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ROI_ON,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_GRID_SIZE) {
|
|
enc_prm.heif_grid_size = mtk_buf->enc_params.heif_grid_size;
|
|
mtk_v4l2_err("[%d] idx=%d, heif_grid_size=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.heif_grid_size);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_HEIF_GRID_SIZE,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_COLOR_DESC) {
|
|
// avoid much copies
|
|
enc_prm.color_desc = &mtk_buf->enc_params.color_desc;
|
|
mtk_v4l2_err("[%d] idx=%d, color_primaries=%d range=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
enc_prm.color_desc->color_primaries,
|
|
enc_prm.color_desc->full_range);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_COLOR_DESC,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_TSVC) {
|
|
enc_prm.tsvc = mtk_buf->enc_params.tsvc;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, tsvc=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.tsvc);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_TSVC,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_HIGHQUALITY) {
|
|
enc_prm.highquality = mtk_buf->enc_params.highquality;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, enable highquality=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.highquality);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ENABLE_HIGHQUALITY,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_MAXQP) {
|
|
enc_prm.max_qp = mtk_buf->enc_params.max_qp;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, max_qp=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.max_qp);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ADJUST_MAX_QP,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_MINQP) {
|
|
enc_prm.min_qp = mtk_buf->enc_params.min_qp;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, min_qp=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.min_qp);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ADJUST_MIN_QP,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_IP_QPDELTA) {
|
|
enc_prm.ip_qpdelta = mtk_buf->enc_params.ip_qpdelta;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, ip_qpdelta=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.ip_qpdelta);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ADJUST_I_P_QP_DELTA,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMELVLQP) {
|
|
enc_prm.framelvl_qp = mtk_buf->enc_params.framelvl_qp;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, framelvl_qp=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.framelvl_qp);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ADJUST_FRAME_LEVEL_QP,
|
|
&enc_prm);
|
|
}
|
|
|
|
if (!ret &&
|
|
mtk_buf->param_change & MTK_ENCODE_PARAM_DUMMY_NAL) {
|
|
enc_prm.dummynal = mtk_buf->enc_params.dummynal;
|
|
mtk_v4l2_debug(1, "[%d] idx=%d, tsvc=%d",
|
|
ctx->id,
|
|
mtk_buf->vb.vb2_buf.index,
|
|
mtk_buf->enc_params.dummynal);
|
|
ret |= venc_if_set_param(ctx,
|
|
VENC_SET_PARAM_ENABLE_DUMMY_NAL,
|
|
&enc_prm);
|
|
}
|
|
|
|
mtk_buf->param_change = MTK_ENCODE_PARAM_NONE;
|
|
|
|
if (ret) {
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
mtk_v4l2_err("venc_if_set_param %d failed=%d",
|
|
mtk_buf->param_change, ret);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mtk_venc_check_queue_cnt(struct mtk_vcodec_ctx *ctx, struct vb2_queue *vq)
|
|
{
|
|
int done_list_cnt = 0;
|
|
int rdy_q_cnt = 0;
|
|
struct vb2_buffer *vb;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&vq->done_lock, flags);
|
|
list_for_each_entry(vb, &vq->done_list, queued_entry)
|
|
done_list_cnt++;
|
|
spin_unlock_irqrestore(&vq->done_lock, flags);
|
|
|
|
if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
|
|
rdy_q_cnt = ctx->m2m_ctx->out_q_ctx.num_rdy;
|
|
else
|
|
rdy_q_cnt = ctx->m2m_ctx->cap_q_ctx.num_rdy;
|
|
|
|
mtk_v4l2_debug(0,
|
|
"[%d] type %d queued_cnt %d done_cnt %d rdy_q_cnt %d tatal %d",
|
|
ctx->id, vq->type, vq->queued_count,
|
|
done_list_cnt, rdy_q_cnt, vq->num_buffers);
|
|
}
|
|
|
|
/*
|
|
* v4l2_m2m_streamoff() holds dev_mutex and waits mtk_venc_worker()
|
|
* to call v4l2_m2m_job_finish().
|
|
* If mtk_venc_worker() tries to acquire dev_mutex, it will deadlock.
|
|
* So this function must not try to acquire dev->dev_mutex.
|
|
* This means v4l2 ioctls and mtk_venc_worker() can run at the same time.
|
|
* mtk_venc_worker() should be carefully implemented to avoid bugs.
|
|
*/
|
|
static void mtk_venc_worker(struct work_struct *work)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx,
|
|
encode_work);
|
|
struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC];
|
|
struct vb2_buffer *src_buf, *dst_buf;
|
|
struct venc_frm_buf *pfrm_buf;
|
|
struct mtk_vcodec_mem *pbs_buf;
|
|
struct venc_done_result enc_result;
|
|
int ret, i, length;
|
|
struct vb2_v4l2_buffer *dst_vb2_v4l2, *src_vb2_v4l2, *pend_src_vb2_v4l2;
|
|
struct mtk_video_enc_buf *dst_buf_info, *src_buf_info;
|
|
|
|
mutex_lock(&ctx->worker_lock);
|
|
memset(&enc_result, 0, sizeof(enc_result));
|
|
if (ctx->state == MTK_STATE_ABORT) {
|
|
v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
|
|
mtk_v4l2_debug(1, " %d", ctx->state);
|
|
mutex_unlock(&ctx->worker_lock);
|
|
return;
|
|
}
|
|
|
|
/* check dst_buf, dst_buf may be removed in device_run
|
|
* to stored encdoe header so we need check dst_buf and
|
|
* call job_finish here to prevent recursion
|
|
*/
|
|
dst_vb2_v4l2 = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
|
|
if (!dst_vb2_v4l2) {
|
|
v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
|
|
mutex_unlock(&ctx->worker_lock);
|
|
return;
|
|
}
|
|
|
|
src_vb2_v4l2 = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
|
|
if (!src_vb2_v4l2) {
|
|
v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
|
|
mutex_unlock(&ctx->worker_lock);
|
|
return;
|
|
}
|
|
src_buf = &src_vb2_v4l2->vb2_buf;
|
|
dst_buf = &dst_vb2_v4l2->vb2_buf;
|
|
|
|
src_buf_info = container_of(src_vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
dst_buf_info = container_of(dst_vb2_v4l2, struct mtk_video_enc_buf, vb);
|
|
|
|
pbs_buf = &dst_buf_info->bs_buf;
|
|
pfrm_buf = &src_buf_info->frm_buf;
|
|
|
|
pbs_buf->va = vb2_plane_vaddr(dst_buf, 0);
|
|
pbs_buf->dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
|
|
pbs_buf->size = (size_t)dst_buf->planes[0].length;
|
|
pbs_buf->dmabuf = dst_buf->planes[0].dbuf;
|
|
pbs_buf->index = dst_buf->index;
|
|
ctx->bs_list[pbs_buf->index + 1] = (uintptr_t)pbs_buf;
|
|
|
|
if (src_buf_info->lastframe == EOS) {
|
|
src_buf_info->lastframe = NON_EOS;
|
|
if (ctx->oal_vcodec == 1) {
|
|
ret = venc_if_encode(ctx,
|
|
VENC_START_OPT_ENCODE_FRAME_FINAL,
|
|
NULL, pbs_buf, &enc_result);
|
|
|
|
pend_src_vb2_v4l2 =
|
|
to_vb2_v4l2_buffer(ctx->pend_src_buf);
|
|
dst_vb2_v4l2->flags |= pend_src_vb2_v4l2->flags;
|
|
dst_vb2_v4l2->vb2_buf.timestamp =
|
|
pend_src_vb2_v4l2->vb2_buf.timestamp;
|
|
dst_vb2_v4l2->timecode = pend_src_vb2_v4l2->timecode;
|
|
dst_vb2_v4l2->sequence = pend_src_vb2_v4l2->sequence;
|
|
dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST;
|
|
if (enc_result.is_key_frm)
|
|
dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME;
|
|
|
|
if (ret) {
|
|
dst_buf->planes[0].bytesused = 0;
|
|
v4l2_m2m_buf_done(pend_src_vb2_v4l2,
|
|
VB2_BUF_STATE_ERROR);
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2,
|
|
VB2_BUF_STATE_ERROR);
|
|
mtk_v4l2_err("last venc_if_encode failed=%d",
|
|
ret);
|
|
if (ret == -EIO) {
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
venc_check_release_lock(ctx);
|
|
}
|
|
} else {
|
|
dst_buf->planes[0].bytesused =
|
|
enc_result.bs_size;
|
|
v4l2_m2m_buf_done(pend_src_vb2_v4l2,
|
|
VB2_BUF_STATE_DONE);
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2,
|
|
VB2_BUF_STATE_DONE);
|
|
}
|
|
|
|
ctx->pend_src_buf = NULL;
|
|
} else {
|
|
ret = venc_if_encode(ctx,
|
|
VENC_START_OPT_ENCODE_FRAME_FINAL,
|
|
NULL, NULL, &enc_result);
|
|
dst_vb2_v4l2->vb2_buf.timestamp =
|
|
src_vb2_v4l2->vb2_buf.timestamp;
|
|
dst_vb2_v4l2->timecode = src_vb2_v4l2->timecode;
|
|
dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST;
|
|
dst_buf->planes[0].bytesused = 0;
|
|
|
|
if (ret) {
|
|
mtk_v4l2_err("last venc_if_encode failed=%d",
|
|
ret);
|
|
if (ret == -EIO) {
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
venc_check_release_lock(ctx);
|
|
}
|
|
} else if (!ctx->async_mode)
|
|
mtk_enc_put_buf(ctx);
|
|
|
|
mtk_venc_check_queue_cnt(ctx, src_buf->vb2_queue);
|
|
mtk_venc_check_queue_cnt(ctx, dst_buf->vb2_queue);
|
|
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2,
|
|
VB2_BUF_STATE_DONE);
|
|
}
|
|
mtk_vdec_queue_stop_enc_event(ctx);
|
|
|
|
if (src_buf->planes[0].bytesused == 0U) {
|
|
src_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST;
|
|
vb2_set_plane_payload(&src_buf_info->vb.vb2_buf, 0, 0);
|
|
v4l2_m2m_buf_done(src_vb2_v4l2,
|
|
VB2_BUF_STATE_DONE);
|
|
}
|
|
v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
|
|
mutex_unlock(&ctx->worker_lock);
|
|
return;
|
|
} else if (src_buf_info->lastframe == EOS_WITH_DATA) {
|
|
/*
|
|
* Getting early eos frame buffer, after encode this
|
|
* buffer, need to flush encoder. Use the flush_buf
|
|
* as normal EOS, and flush encoder.
|
|
*/
|
|
mtk_v4l2_debug(0, "[%d] EarlyEos: encode last frame %d",
|
|
ctx->id, src_buf->planes[0].bytesused);
|
|
if (ctx->enc_flush_buf->lastframe == NON_EOS) {
|
|
ctx->enc_flush_buf->lastframe = EOS;
|
|
src_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST;
|
|
dst_vb2_v4l2->flags |= V4L2_BUF_FLAG_LAST;
|
|
v4l2_m2m_buf_queue_check(ctx->m2m_ctx, &ctx->enc_flush_buf->vb);
|
|
} else {
|
|
mtk_v4l2_debug(1, "Stopping no need to queue enc_flush_buf.");
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < src_buf->num_planes ; i++) {
|
|
pfrm_buf->fb_addr[i].va = vb2_plane_vaddr(src_buf, i) +
|
|
(size_t)src_buf->planes[i].data_offset;
|
|
pfrm_buf->fb_addr[i].dma_addr =
|
|
vb2_dma_contig_plane_dma_addr(src_buf, i) +
|
|
(size_t)src_buf->planes[i].data_offset;
|
|
pfrm_buf->fb_addr[i].size =
|
|
(size_t)(src_buf->planes[i].bytesused-
|
|
src_buf->planes[i].data_offset);
|
|
pfrm_buf->fb_addr[i].dmabuf =
|
|
src_buf->planes[i].dbuf;
|
|
pfrm_buf->fb_addr[i].data_offset =
|
|
src_buf->planes[i].data_offset;
|
|
|
|
mtk_v4l2_debug(2, "fb_addr[%d].va %p, offset %d, dma_addr %p, size %d\n",
|
|
i, pfrm_buf->fb_addr[i].va,
|
|
src_buf->planes[i].data_offset,
|
|
(void *)pfrm_buf->fb_addr[i].dma_addr,
|
|
(int)pfrm_buf->fb_addr[i].size);
|
|
}
|
|
pfrm_buf->num_planes = src_buf->num_planes;
|
|
pfrm_buf->timestamp = src_vb2_v4l2->vb2_buf.timestamp;
|
|
pfrm_buf->index = src_buf->index;
|
|
ctx->fb_list[pfrm_buf->index + 1] = (uintptr_t)pfrm_buf;
|
|
length = q_data_src->coded_width * q_data_src->coded_height;
|
|
|
|
mtk_v4l2_debug(2,
|
|
"Framebuf %d VA=%p PA=%llx Size=0x%zx Offset=%d;VA=%p PA=0x%llx Size=0x%zx Offset=%d;VA=%p PA=0x%llx Size=%zu Offset=%d",
|
|
pfrm_buf->index,
|
|
pfrm_buf->fb_addr[0].va,
|
|
(u64)pfrm_buf->fb_addr[0].dma_addr,
|
|
pfrm_buf->fb_addr[0].size,
|
|
src_buf->planes[0].data_offset,
|
|
pfrm_buf->fb_addr[1].va,
|
|
(u64)pfrm_buf->fb_addr[1].dma_addr,
|
|
pfrm_buf->fb_addr[1].size,
|
|
src_buf->planes[1].data_offset,
|
|
pfrm_buf->fb_addr[2].va,
|
|
(u64)pfrm_buf->fb_addr[2].dma_addr,
|
|
pfrm_buf->fb_addr[2].size,
|
|
src_buf->planes[2].data_offset);
|
|
|
|
ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME,
|
|
pfrm_buf, pbs_buf, &enc_result);
|
|
if (ret) {
|
|
dst_buf->planes[0].bytesused = 0;
|
|
v4l2_m2m_buf_done(src_vb2_v4l2, VB2_BUF_STATE_ERROR);
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2, VB2_BUF_STATE_ERROR);
|
|
mtk_v4l2_err("venc_if_encode failed=%d", ret);
|
|
if (ret == -EIO) {
|
|
ctx->state = MTK_STATE_ABORT;
|
|
mtk_venc_queue_error_event(ctx);
|
|
venc_check_release_lock(ctx);
|
|
}
|
|
} else if (!ctx->async_mode)
|
|
mtk_enc_put_buf(ctx);
|
|
|
|
v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
|
|
|
|
mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>",
|
|
src_buf->index, dst_buf->index, ret,
|
|
enc_result.bs_size);
|
|
|
|
mutex_unlock(&ctx->worker_lock);
|
|
}
|
|
|
|
static void m2mops_venc_device_run(void *priv)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = priv;
|
|
|
|
mtk_venc_param_change(ctx);
|
|
|
|
if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264 ||
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H265 ||
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_HEIF ||
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_MPEG4 ||
|
|
ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H263) &&
|
|
(ctx->state != MTK_STATE_HEADER)) {
|
|
/* encode h264 sps/pps header */
|
|
mtk_venc_encode_header(ctx);
|
|
queue_work(ctx->dev->encode_workqueue, &ctx->encode_work);
|
|
return;
|
|
}
|
|
|
|
queue_work(ctx->dev->encode_workqueue, &ctx->encode_work);
|
|
}
|
|
|
|
static int m2mops_venc_job_ready(void *m2m_priv)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = m2m_priv;
|
|
|
|
if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) {
|
|
mtk_v4l2_debug(4, "[%d]Not ready: state=0x%x.",
|
|
ctx->id, ctx->state);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void m2mops_venc_job_abort(void *priv)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = priv;
|
|
|
|
mtk_v4l2_debug(4, "[%d]", ctx->id);
|
|
ctx->state = MTK_STATE_ABORT;
|
|
}
|
|
|
|
const struct v4l2_m2m_ops mtk_venc_m2m_ops = {
|
|
.device_run = m2mops_venc_device_run,
|
|
.job_ready = m2mops_venc_job_ready,
|
|
.job_abort = m2mops_venc_job_abort,
|
|
};
|
|
|
|
void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
struct mtk_q_data *q_data;
|
|
|
|
ctx->m2m_ctx->q_lock = &ctx->q_mutex;
|
|
ctx->fh.m2m_ctx = ctx->m2m_ctx;
|
|
ctx->fh.ctrl_handler = &ctx->ctrl_hdl;
|
|
INIT_WORK(&ctx->encode_work, mtk_venc_worker);
|
|
|
|
ctx->colorspace = V4L2_COLORSPACE_REC709;
|
|
ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
|
|
ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
|
|
ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
|
|
|
|
get_supported_format(ctx);
|
|
get_supported_framesizes(ctx);
|
|
|
|
if (mtk_vcodec_vcp & (1 << MTK_INST_ENCODER)) {
|
|
set_venc_vcp_data(ctx, VENC_VCP_LOG_INFO_ID);
|
|
set_venc_vcp_data(ctx, VENC_SET_PROP_MEM_ID);
|
|
}
|
|
|
|
q_data = &ctx->q_data[MTK_Q_DATA_SRC];
|
|
memset(q_data, 0, sizeof(struct mtk_q_data));
|
|
q_data->visible_width = DFT_CFG_WIDTH;
|
|
q_data->visible_height = DFT_CFG_HEIGHT;
|
|
q_data->coded_width = DFT_CFG_WIDTH;
|
|
q_data->coded_height = DFT_CFG_HEIGHT;
|
|
q_data->field = V4L2_FIELD_NONE;
|
|
|
|
q_data->fmt = &mtk_venc_formats[default_out_fmt_idx];
|
|
|
|
v4l_bound_align_image(&q_data->coded_width,
|
|
MTK_VENC_MIN_W,
|
|
MTK_VENC_MAX_W, 4,
|
|
&q_data->coded_height,
|
|
MTK_VENC_MIN_H,
|
|
MTK_VENC_MAX_H, 5, 6);
|
|
|
|
if (q_data->coded_width < DFT_CFG_WIDTH &&
|
|
(q_data->coded_width + 16) <= MTK_VENC_MAX_W)
|
|
q_data->coded_width += 16;
|
|
if (q_data->coded_height < DFT_CFG_HEIGHT &&
|
|
(q_data->coded_height + 32) <= MTK_VENC_MAX_H)
|
|
q_data->coded_height += 32;
|
|
|
|
q_data->sizeimage[0] =
|
|
q_data->coded_width * q_data->coded_height +
|
|
((ALIGN(q_data->coded_width, 16) * 2) * 16);
|
|
q_data->bytesperline[0] = q_data->coded_width;
|
|
q_data->sizeimage[1] =
|
|
(q_data->coded_width * q_data->coded_height) / 2 +
|
|
(ALIGN(q_data->coded_width, 16) * 16);
|
|
q_data->bytesperline[1] = q_data->coded_width;
|
|
|
|
q_data = &ctx->q_data[MTK_Q_DATA_DST];
|
|
memset(q_data, 0, sizeof(struct mtk_q_data));
|
|
q_data->coded_width = DFT_CFG_WIDTH;
|
|
q_data->coded_height = DFT_CFG_HEIGHT;
|
|
q_data->fmt = &mtk_venc_formats[default_cap_fmt_idx];
|
|
q_data->field = V4L2_FIELD_NONE;
|
|
ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] =
|
|
DFT_CFG_WIDTH * DFT_CFG_HEIGHT;
|
|
ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0;
|
|
|
|
}
|
|
|
|
void mtk_vcodec_enc_custom_ctrls_check(struct v4l2_ctrl_handler *hdl,
|
|
const struct v4l2_ctrl_config *cfg, void *priv)
|
|
{
|
|
v4l2_ctrl_new_custom(hdl, cfg, NULL);
|
|
|
|
if (hdl->error) {
|
|
mtk_v4l2_debug(0, "Adding control failed %s %x %d",
|
|
cfg->name, cfg->id, hdl->error);
|
|
} else {
|
|
mtk_v4l2_debug(4, "Adding control %s %x %d",
|
|
cfg->name, cfg->id, hdl->error);
|
|
}
|
|
}
|
|
|
|
int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
const struct v4l2_ctrl_ops *ops = &mtk_vcodec_enc_ctrl_ops;
|
|
struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl;
|
|
struct v4l2_ctrl_config cfg;
|
|
|
|
v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT);
|
|
ctx->enc_params.bitrate = 20000000;
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE,
|
|
0, 400000000, 1, ctx->enc_params.bitrate);
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES,
|
|
0, 3, 1, 0);
|
|
ctx->enc_params.rc_frame = 1;
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
|
|
0, 1, 1, ctx->enc_params.rc_frame);
|
|
ctx->enc_params.h264_max_qp = 51;
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
|
|
0, 51, 1, ctx->enc_params.h264_max_qp);
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
|
|
0, 65535, 1, 0);
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE,
|
|
0, 65535, 1, 0);
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
|
|
0, 1, 1, 0);
|
|
v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
|
|
0, 1, 1, 0);
|
|
v4l2_ctrl_new_std_menu(handler, ops,
|
|
V4L2_CID_MPEG_VIDEO_HEADER_MODE,
|
|
V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
|
|
0, V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE);
|
|
v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE,
|
|
V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
|
|
0, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
|
|
v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
|
|
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
|
|
0, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
|
|
v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
|
|
V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
|
|
0, V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE);
|
|
v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL,
|
|
V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
|
|
0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0);
|
|
v4l2_ctrl_new_std_menu(handler, ops,
|
|
V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
|
|
V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
|
|
0, V4L2_MPEG_VIDEO_HEVC_LEVEL_1);
|
|
v4l2_ctrl_new_std_menu(handler, ops,
|
|
V4L2_CID_MPEG_VIDEO_HEVC_TIER,
|
|
V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
|
|
0, V4L2_MPEG_VIDEO_HEVC_TIER_MAIN);
|
|
v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
|
|
V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
|
|
0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0);
|
|
if (handler->error)
|
|
mtk_v4l2_debug(0, "Adding control failed V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL %x %d",
|
|
V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, handler->error);
|
|
|
|
v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
|
|
V4L2_MPEG_VIDEO_BITRATE_MODE_CQ,
|
|
0, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
|
|
if (handler->error)
|
|
mtk_v4l2_debug(0, "Adding control failed V4L2_CID_MPEG_VIDEO_BITRATE_MODE %x %d",
|
|
V4L2_CID_MPEG_VIDEO_BITRATE_MODE, handler->error);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_SCENARIO;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode scenario";
|
|
cfg.min = 0;
|
|
cfg.max = 32;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_NONREFP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode nonrefp";
|
|
cfg.min = 0;
|
|
cfg.max = 32;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_NONREFP_FREQ;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode nonrefp";
|
|
cfg.min = 0;
|
|
cfg.max = 32;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_DETECTED_FRAMERATE;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode detect framerate";
|
|
cfg.min = 0;
|
|
cfg.max = 32;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RFS_ON;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode slice loss indication";
|
|
cfg.min = 0;
|
|
cfg.max = 1;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode slice loss indication";
|
|
cfg.min = 0;
|
|
cfg.max = 1;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_OPERATION_RATE;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode operation rate";
|
|
cfg.min = 0;
|
|
cfg.max = 2048;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_ROI_ON;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode roi switch";
|
|
cfg.min = 0;
|
|
cfg.max = 8;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_GRID_SIZE;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode heif grid size";
|
|
cfg.min = 0;
|
|
cfg.max = (3840<<16)+2176;
|
|
cfg.step = 16;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_COLOR_DESC;
|
|
cfg.type = V4L2_CTRL_TYPE_U32;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode color description for HDR";
|
|
cfg.min = 0x00000000;
|
|
cfg.max = 0xffffffff;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
cfg.dims[0] = (sizeof(struct mtk_color_desc)/sizeof(u32));
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_MAX_WIDTH;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode max width";
|
|
cfg.min = 0;
|
|
cfg.max = 3840;
|
|
cfg.step = 16;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_MAX_HEIGHT;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode max height";
|
|
cfg.min = 0;
|
|
cfg.max = 3840;
|
|
cfg.step = 16;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
ctx->enc_params.i_qp = 51;
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_I_FRAME_QP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "I-Frame QP Value";
|
|
cfg.min = 0;
|
|
cfg.max = 51;
|
|
cfg.step = 1;
|
|
cfg.def = ctx->enc_params.i_qp;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
ctx->enc_params.p_qp = 51;
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_P_FRAME_QP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "P-Frame QP Value";
|
|
cfg.min = 0;
|
|
cfg.max = 51;
|
|
cfg.step = 1;
|
|
cfg.def = ctx->enc_params.p_qp;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
ctx->enc_params.b_qp = 51;
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_B_FRAME_QP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "B-Frame QP Value";
|
|
cfg.min = 0;
|
|
cfg.max = 51;
|
|
cfg.step = 1;
|
|
cfg.def = ctx->enc_params.b_qp;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_SEC_ENCODE;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video Sec Encode path";
|
|
cfg.min = 0;
|
|
cfg.max = 2;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
/* g_volatile_ctrl */
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_ROI_RC_QP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_VOLATILE |
|
|
V4L2_CTRL_FLAG_READ_ONLY;
|
|
cfg.name = "Video encode roi rc qp";
|
|
cfg.min = 0;
|
|
cfg.max = 2048;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_RESOLUTION_CHANGE;
|
|
cfg.type = V4L2_CTRL_TYPE_U32;
|
|
cfg.flags = V4L2_CTRL_FLAG_VOLATILE |
|
|
V4L2_CTRL_FLAG_READ_ONLY;
|
|
cfg.name = "Video encode resolution change";
|
|
cfg.min = 0x00000000;
|
|
cfg.max = 0x00ffffff;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
cfg.dims[0] = sizeof(struct venc_resolution_change)/sizeof(u32);
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_VIDEO_ENABLE_TSVC;
|
|
cfg.type = V4L2_CTRL_TYPE_U32;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode tsvc";
|
|
cfg.min = 0;
|
|
cfg.max = 15;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.dims[0] = 2;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_ENABLE_HIGHQUALITY;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode enable highquality";
|
|
cfg.min = 0;
|
|
cfg.max = 1;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
ctx->enc_params.max_qp = -1;
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_MAX_QP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode max qp";
|
|
cfg.min = -1;
|
|
cfg.max = 51;
|
|
cfg.step = 1;
|
|
cfg.def = ctx->enc_params.max_qp;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
ctx->enc_params.min_qp = -1;
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_MIN_QP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode min qp";
|
|
cfg.min = -1;
|
|
cfg.max = 51;
|
|
cfg.step = 1;
|
|
cfg.def = ctx->enc_params.min_qp;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
ctx->enc_params.ip_qpdelta = -1;
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_I_P_QP_DELTA;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode ip qp delta";
|
|
cfg.min = -1;
|
|
cfg.max = 51;
|
|
cfg.step = 1;
|
|
cfg.def = ctx->enc_params.ip_qpdelta;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
ctx->enc_params.framelvl_qp = -1;
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_FRAME_LEVEL_QP;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode frame level qp";
|
|
cfg.min = -1;
|
|
cfg.max = 51;
|
|
cfg.step = 1;
|
|
cfg.def = ctx->enc_params.framelvl_qp;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_RC_QP_CONTROL_MODE;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode qp control mode";
|
|
cfg.min = 0;
|
|
cfg.max = 8;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_ENCODE_ENABLE_DUMMY_NAL;
|
|
cfg.type = V4L2_CTRL_TYPE_INTEGER;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video encode enable dummynal";
|
|
cfg.min = 0;
|
|
cfg.max = 1;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_LOG;
|
|
cfg.type = V4L2_CTRL_TYPE_STRING;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video Log";
|
|
cfg.min = 0;
|
|
cfg.max = 255;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
cfg.id = V4L2_CID_MPEG_MTK_VCP_PROP;
|
|
cfg.type = V4L2_CTRL_TYPE_STRING;
|
|
cfg.flags = V4L2_CTRL_FLAG_WRITE_ONLY;
|
|
cfg.name = "Video VCP Property";
|
|
cfg.min = 0;
|
|
cfg.max = 255;
|
|
cfg.step = 1;
|
|
cfg.def = 0;
|
|
cfg.ops = ops;
|
|
mtk_vcodec_enc_custom_ctrls_check(handler, &cfg, NULL);
|
|
|
|
if (handler->error) {
|
|
mtk_v4l2_err("Init control handler fail %d",
|
|
handler->error);
|
|
return handler->error;
|
|
}
|
|
|
|
v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *mtk_venc_dc_attach_dmabuf(struct device *dev, struct dma_buf *dbuf,
|
|
unsigned long size, enum dma_data_direction dma_dir)
|
|
{
|
|
struct vb2_dc_buf *buf;
|
|
struct dma_buf_attachment *dba;
|
|
|
|
if (dbuf->size < size)
|
|
return ERR_PTR(-EFAULT);
|
|
|
|
if (WARN_ON(!dev))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
|
|
if (!buf)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
buf->dev = dev;
|
|
/* create attachment for the dmabuf with the user device */
|
|
dba = dma_buf_attach(dbuf, buf->dev);
|
|
if (IS_ERR(dba)) {
|
|
pr_info("failed to attach dmabuf\n");
|
|
kfree(buf);
|
|
return dba;
|
|
}
|
|
|
|
/* always skip cache operations, we handle it manually */
|
|
dba->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC;
|
|
|
|
buf->dma_dir = dma_dir;
|
|
buf->size = size;
|
|
buf->db_attach = dba;
|
|
|
|
return buf;
|
|
}
|
|
|
|
int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq,
|
|
struct vb2_queue *dst_vq)
|
|
{
|
|
struct mtk_vcodec_ctx *ctx = priv;
|
|
int ret;
|
|
|
|
/* Note: VB2_USERPTR works with dma-contig because mt8173
|
|
* support iommu
|
|
* https://patchwork.kernel.org/patch/8335461/
|
|
* https://patchwork.kernel.org/patch/7596181/
|
|
*/
|
|
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
|
src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
|
|
src_vq->drv_priv = ctx;
|
|
src_vq->buf_struct_size = sizeof(struct mtk_video_enc_buf);
|
|
src_vq->ops = &mtk_venc_vb2_ops;
|
|
venc_dma_contig_memops = vb2_dma_contig_memops;
|
|
venc_dma_contig_memops.attach_dmabuf = mtk_venc_dc_attach_dmabuf;
|
|
if (is_disable_map_sec() && mtk_venc_is_vcu()) {
|
|
venc_sec_dma_contig_memops = venc_dma_contig_memops;
|
|
venc_sec_dma_contig_memops.map_dmabuf = mtk_venc_sec_dc_map_dmabuf;
|
|
venc_sec_dma_contig_memops.unmap_dmabuf = mtk_venc_sec_dc_unmap_dmabuf;
|
|
}
|
|
src_vq->mem_ops = &vb2_dma_contig_memops;
|
|
mtk_v4l2_debug(4, "src_vq use vb2_dma_contig_memops");
|
|
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
src_vq->lock = &ctx->q_mutex;
|
|
src_vq->allow_zero_bytesused = 1;
|
|
src_vq->dev = &ctx->dev->plat_dev->dev;
|
|
|
|
ret = vb2_queue_init(src_vq);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
|
dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
|
|
dst_vq->drv_priv = ctx;
|
|
dst_vq->buf_struct_size = sizeof(struct mtk_video_enc_buf);
|
|
dst_vq->ops = &mtk_venc_vb2_ops;
|
|
dst_vq->mem_ops = &venc_dma_contig_memops;
|
|
if (ctx->enc_params.svp_mode && is_disable_map_sec() && mtk_venc_is_vcu())
|
|
src_vq->mem_ops = &venc_sec_dma_contig_memops;
|
|
mtk_v4l2_debug(4, "dst_vq use venc_dma_contig_memops");
|
|
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
dst_vq->lock = &ctx->q_mutex;
|
|
dst_vq->allow_zero_bytesused = 1;
|
|
#if IS_ENABLED(CONFIG_MTK_TINYSYS_VCP_SUPPORT)
|
|
#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_V1)
|
|
dst_vq->dev = vcp_get_io_device(VCP_IOMMU_VENC_512MB2);
|
|
mtk_v4l2_debug(4, "use VCP_IOMMU_VENC_512MB2 domain");
|
|
#else
|
|
if (ctx->dev->enc_cnt & 1) {
|
|
dst_vq->dev = vcp_get_io_device(VCP_IOMMU_VDEC_512MB1);
|
|
mtk_v4l2_debug(4, "use VCP_IOMMU_VDEC_512MB1 domain");
|
|
} else {
|
|
dst_vq->dev = vcp_get_io_device(VCP_IOMMU_VENC_512MB2);
|
|
mtk_v4l2_debug(4, "use VCP_IOMMU_VENC_512MB2 domain");
|
|
}
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCU)
|
|
if (!dst_vq->dev)
|
|
dst_vq->dev = &ctx->dev->plat_dev->dev;
|
|
#endif
|
|
#else
|
|
dst_vq->dev = &ctx->dev->plat_dev->dev;
|
|
#endif
|
|
|
|
return vb2_queue_init(dst_vq);
|
|
}
|
|
|
|
void mtk_venc_unlock(struct mtk_vcodec_ctx *ctx, u32 hw_id)
|
|
{
|
|
|
|
if (hw_id >= MTK_VENC_HW_NUM)
|
|
return;
|
|
|
|
mtk_v4l2_debug(4, "ctx %p [%d] hw_id %d sem_cnt %d, lock: %d",
|
|
ctx, ctx->id, hw_id, ctx->dev->enc_sem[hw_id].count,
|
|
ctx->dev->enc_hw_locked[hw_id]);
|
|
|
|
|
|
if (hw_id < MTK_VENC_HW_NUM) {
|
|
ctx->core_locked[hw_id] = 0;
|
|
up(&ctx->dev->enc_sem[hw_id]);
|
|
}
|
|
|
|
ctx->dev->enc_hw_locked[hw_id] = VENC_LOCK_NONE;
|
|
}
|
|
|
|
int mtk_venc_lock(struct mtk_vcodec_ctx *ctx, u32 hw_id, bool sec)
|
|
{
|
|
int ret = -1;
|
|
enum venc_lock lock = VENC_LOCK_NONE;
|
|
|
|
if (hw_id >= MTK_VENC_HW_NUM)
|
|
return ret;
|
|
|
|
if (sec != 0)
|
|
lock = VENC_LOCK_SEC;
|
|
else
|
|
lock = VENC_LOCK_NORMAL;
|
|
|
|
mtk_v4l2_debug(4, "ctx %p [%d] hw_id %d sem_cnt %d, sec: %d, lock: %d",
|
|
ctx, ctx->id, hw_id, ctx->dev->enc_sem[hw_id].count, sec,
|
|
ctx->dev->enc_hw_locked[hw_id]);
|
|
|
|
|
|
|
|
|
|
if (lock != ctx->dev->enc_hw_locked[hw_id])
|
|
ret = down_trylock(&ctx->dev->enc_sem[hw_id]);
|
|
else
|
|
ret = 0;
|
|
|
|
if (ret == 0)
|
|
ctx->dev->enc_hw_locked[hw_id] = lock;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
void mtk_vcodec_enc_empty_queues(struct file *file, struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
struct vb2_buffer *dst_buf = NULL;
|
|
struct vb2_v4l2_buffer *src_vb2_v4l2, *dst_vb2_v4l2;
|
|
struct v4l2_fh *fh = file->private_data;
|
|
|
|
// error handle for release before stream-off
|
|
// stream off both queue mannually.
|
|
v4l2_m2m_streamoff(file, fh->m2m_ctx,
|
|
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
|
|
v4l2_m2m_streamoff(file, fh->m2m_ctx,
|
|
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
|
|
|
|
while ((src_vb2_v4l2 = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) {
|
|
if (src_vb2_v4l2 != &ctx->enc_flush_buf->vb &&
|
|
src_vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
|
|
v4l2_m2m_buf_done(src_vb2_v4l2, VB2_BUF_STATE_ERROR);
|
|
}
|
|
|
|
while ((dst_vb2_v4l2 = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) {
|
|
dst_buf = &dst_vb2_v4l2->vb2_buf;
|
|
dst_buf->planes[0].bytesused = 0;
|
|
if (dst_vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
|
|
v4l2_m2m_buf_done(dst_vb2_v4l2, VB2_BUF_STATE_ERROR);
|
|
}
|
|
|
|
ctx->state = MTK_STATE_FREE;
|
|
}
|
|
|
|
void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx)
|
|
{
|
|
int ret = venc_if_deinit(ctx);
|
|
|
|
if (ret)
|
|
mtk_v4l2_err("venc_if_deinit failed=%d", ret);
|
|
}
|
|
MODULE_LICENSE("GPL v2");
|
|
|