source "drivers/mxc/hantro_845/Kconfig"
source "drivers/mxc/hantro_845_h1/Kconfig"
source "drivers/mxc/hantro_vc8000e/Kconfig"
+source "drivers/mxc/hantro_v4l2/Kconfig"
source "drivers/mxc/vpu_malone/Kconfig"
source "drivers/mxc/vpu_windsor/Kconfig"
endif
obj-$(CONFIG_MXC_HANTRO_845) += hantro_845/
obj-$(CONFIG_MXC_HANTRO_845_H1) += hantro_845_h1/
obj-$(CONFIG_MXC_HANTRO_VC8000E) += hantro_vc8000e/
+obj-$(CONFIG_MXC_HANTRO_V4L2) += hantro_v4l2/
obj-$(CONFIG_MXC_VPU_MALONE) += vpu_malone/
obj-$(CONFIG_MXC_VPU_WINDSOR) += vpu_windsor/
obj-$(CONFIG_MXC_VPU) += vpu/
--- /dev/null
+#
+# Codec configuration
+#
+
+menu "MXC HANTRO(Video Processing Unit) V4L2 support"
+# depends on ARCH_FSL_IMX8MP
+ depends on ARCH_MXC
+
+config MXC_HANTRO_V4L2
+ tristate "Support for MXC HANTRO(Video Processing Unit) V4L2 driver"
+ depends on VIDEO_V4L2
+ default y
+ help
+ VPU hantro v4l2 device.
+
+endmenu
--- /dev/null
+#
+# Makefile for the VPU drivers.
+#
+
+EXTRA_CFLAGS += -I$(src)/include/ -DV4L2_DRIVER
+
+obj-$(CONFIG_MXC_HANTRO_V4L2) = vsiv4l2.o
+vsiv4l2-objs = vsi-v4l2.o vsi-v4l2daemon.o vsi-v4l2-config.o vsi-v4l2-dec.o vsi-v4l2-enc.o
--- /dev/null
+/*
+ * VSI v4l2 media config manager.
+ *
+ * Copyright (c) 2019, VeriSilicon Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ * https://opensource.org/licenses/gpl-2.0.php
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include "vsi-v4l2-priv.h"
+
+static s32 rpt_10bit = 1;
+module_param(rpt_10bit, int, 0444);
+
+static struct vsi_v4l2_dev_info vsi_v4l2_hwconfig = {0};
+static void calcPlanesize(struct vsi_v4l2_ctx *ctx, int pixelformat, int width, int height, int size[], int type, int planeno);
+static int vsiv4l2_verifyfmt(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt);
+
+/*copy from daemon header*/
+enum VCEncLevel {
+ VCENC_HEVC_LEVEL_1 = 30,
+ VCENC_HEVC_LEVEL_2 = 60,
+ VCENC_HEVC_LEVEL_2_1 = 63,
+ VCENC_HEVC_LEVEL_3 = 90,
+ VCENC_HEVC_LEVEL_3_1 = 93,
+ VCENC_HEVC_LEVEL_4 = 120,
+ VCENC_HEVC_LEVEL_4_1 = 123,
+ VCENC_HEVC_LEVEL_5 = 150,
+ VCENC_HEVC_LEVEL_5_1 = 153,
+ VCENC_HEVC_LEVEL_5_2 = 156,
+ VCENC_HEVC_LEVEL_6 = 180,
+ VCENC_HEVC_LEVEL_6_1 = 183,
+ VCENC_HEVC_LEVEL_6_2 = 186,
+
+ /* H264 Defination*/
+ VCENC_H264_LEVEL_1 = 10,
+ VCENC_H264_LEVEL_1_b = 99,
+ VCENC_H264_LEVEL_1_1 = 11,
+ VCENC_H264_LEVEL_1_2 = 12,
+ VCENC_H264_LEVEL_1_3 = 13,
+ VCENC_H264_LEVEL_2 = 20,
+ VCENC_H264_LEVEL_2_1 = 21,
+ VCENC_H264_LEVEL_2_2 = 22,
+ VCENC_H264_LEVEL_3 = 30,
+ VCENC_H264_LEVEL_3_1 = 31,
+ VCENC_H264_LEVEL_3_2 = 32,
+ VCENC_H264_LEVEL_4 = 40,
+ VCENC_H264_LEVEL_4_1 = 41,
+ VCENC_H264_LEVEL_4_2 = 42,
+ VCENC_H264_LEVEL_5 = 50,
+ VCENC_H264_LEVEL_5_1 = 51,
+ VCENC_H264_LEVEL_5_2 = 52,
+};
+
+static s32 leveltbl_hevc[][3] = {
+ {VCENC_HEVC_LEVEL_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_1, 35000},
+ {VCENC_HEVC_LEVEL_2, V4L2_MPEG_VIDEO_HEVC_LEVEL_2, 1500000},
+ {VCENC_HEVC_LEVEL_2_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1, 3000000},
+ {VCENC_HEVC_LEVEL_3, V4L2_MPEG_VIDEO_HEVC_LEVEL_3, 6000000},
+ {VCENC_HEVC_LEVEL_3_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1, 10000000},
+ {VCENC_HEVC_LEVEL_4, V4L2_MPEG_VIDEO_HEVC_LEVEL_4, 12000000},
+ {VCENC_HEVC_LEVEL_4_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1, 20000000},
+ {VCENC_HEVC_LEVEL_5, V4L2_MPEG_VIDEO_HEVC_LEVEL_5, 25000000},
+ {VCENC_HEVC_LEVEL_5_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 40000000},
+ {VCENC_HEVC_LEVEL_5_2, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2, 60000000},
+ {VCENC_HEVC_LEVEL_6, V4L2_MPEG_VIDEO_HEVC_LEVEL_6, 60000000},
+ {VCENC_HEVC_LEVEL_6_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1, 120000000},
+ {VCENC_HEVC_LEVEL_6_2, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, 240000000},
+};
+static s32 leveltbl_h264[][3] = {
+ {VCENC_H264_LEVEL_1, V4L2_MPEG_VIDEO_H264_LEVEL_1_0, 175000},
+ {VCENC_H264_LEVEL_1_b, V4L2_MPEG_VIDEO_H264_LEVEL_1B, 350000},
+ {VCENC_H264_LEVEL_1_1, V4L2_MPEG_VIDEO_H264_LEVEL_1_1, 500000},
+ {VCENC_H264_LEVEL_1_2, V4L2_MPEG_VIDEO_H264_LEVEL_1_2, 1000000},
+ {VCENC_H264_LEVEL_1_3, V4L2_MPEG_VIDEO_H264_LEVEL_1_3, 2000000},
+ {VCENC_H264_LEVEL_2, V4L2_MPEG_VIDEO_H264_LEVEL_2_0, 2000000},
+ {VCENC_H264_LEVEL_2_1, V4L2_MPEG_VIDEO_H264_LEVEL_2_1, 4000000},
+ {VCENC_H264_LEVEL_2_2, V4L2_MPEG_VIDEO_H264_LEVEL_2_2, 4000000},
+ {VCENC_H264_LEVEL_3, V4L2_MPEG_VIDEO_H264_LEVEL_3_0, 10000000},
+ {VCENC_H264_LEVEL_3_1, V4L2_MPEG_VIDEO_H264_LEVEL_3_1, 14000000},
+ {VCENC_H264_LEVEL_3_2, V4L2_MPEG_VIDEO_H264_LEVEL_3_2, 20000000},
+ {VCENC_H264_LEVEL_4, V4L2_MPEG_VIDEO_H264_LEVEL_4_0, 25000000},
+ {VCENC_H264_LEVEL_4_1, V4L2_MPEG_VIDEO_H264_LEVEL_4_1, 62500000},
+ {VCENC_H264_LEVEL_4_2, V4L2_MPEG_VIDEO_H264_LEVEL_4_2, 62500000},
+ {VCENC_H264_LEVEL_5, V4L2_MPEG_VIDEO_H264_LEVEL_5_0, 135000000},
+ {VCENC_H264_LEVEL_5_1, V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 240000000},
+ {VCENC_H264_LEVEL_5_2, V4L2_MPEG_VIDEO_H264_LEVEL_5_2, 240000000},
+};
+
+static const u8 colorprimaries[] = {
+ 0,
+ V4L2_COLORSPACE_REC709, /*Rec. ITU-R BT.709-6*/
+ 0,
+ 0,
+ V4L2_COLORSPACE_470_SYSTEM_M, /*Rec. ITU-R BT.470-6 System M*/
+ V4L2_COLORSPACE_470_SYSTEM_BG,/*Rec. ITU-R BT.470-6 System B, G*/
+ V4L2_COLORSPACE_SMPTE170M, /*SMPTE170M*/
+ V4L2_COLORSPACE_SMPTE240M, /*SMPTE240M*/
+ V4L2_COLORSPACE_GENERIC_FILM, /*Generic film*/
+ V4L2_COLORSPACE_BT2020, /*Rec. ITU-R BT.2020-2*/
+ V4L2_COLORSPACE_ST428 /*SMPTE ST 428-1*/
+};
+
+static const u8 colortransfers[] = {
+ 0,
+ V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.709-6*/
+ 0,
+ 0,
+ V4L2_XFER_FUNC_GAMMA22, /*Rec. ITU-R BT.470-6 System M*/
+ V4L2_XFER_FUNC_GAMMA28, /*Rec. ITU-R BT.470-6 System B, G*/
+ V4L2_XFER_FUNC_709, /*SMPTE170M*/
+ V4L2_XFER_FUNC_SMPTE240M,/*SMPTE240M*/
+ V4L2_XFER_FUNC_LINEAR, /*Linear transfer characteristics*/
+ 0,
+ 0,
+ V4L2_XFER_FUNC_XVYCC, /*IEC 61966-2-4*/
+ V4L2_XFER_FUNC_BT1361, /*Rec. ITU-R BT.1361-0 extended colour gamut*/
+ V4L2_XFER_FUNC_SRGB, /*IEC 61966-2-1 sRGB or sYCC*/
+ V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (10 bit system)*/
+ V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (12 bit system)*/
+ V4L2_XFER_FUNC_SMPTE2084,/*SMPTE ST 2084*/
+ V4L2_XFER_FUNC_ST428, /*SMPTE ST 428-1*/
+ V4L2_XFER_FUNC_HLG /*Rec. ITU-R BT.2100-0 hybrid log-gamma (HLG)*/
+};
+
+static const u8 colormatrixcoefs[] = {
+ 0,
+ V4L2_YCBCR_ENC_709, /*Rec. ITU-R BT.709-6*/
+ 0,
+ 0,
+ V4L2_YCBCR_ENC_BT470_6M, /*Title 47 Code of Federal Regulations*/
+ V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 625*/
+ V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 525*/
+ V4L2_YCBCR_ENC_SMPTE240M, /*SMPTE240M*/
+ 0,
+ V4L2_YCBCR_ENC_BT2020, /*Rec. ITU-R BT.2020-2*/
+ V4L2_YCBCR_ENC_BT2020_CONST_LUM /*Rec. ITU-R BT.2020-2 constant*/
+};
+
+static int enc_isRGBformat(u32 fourcc)
+{
+ switch (fourcc) {
+ case V4L2_PIX_FMT_RGBA32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_BGRX555:
+ case V4L2_PIX_FMT_RGBX32:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int enc_setvui(struct v4l2_format *v4l2fmt, struct v4l2_daemon_enc_params *encparams)
+{
+ u32 colorspace, quantization, transfer, matrixcoeff;
+ int i;
+
+ quantization = v4l2fmt->fmt.pix_mp.quantization;
+ colorspace = v4l2fmt->fmt.pix_mp.colorspace;
+ transfer = v4l2fmt->fmt.pix_mp.xfer_func;
+ matrixcoeff = v4l2fmt->fmt.pix_mp.ycbcr_enc;
+
+ encparams->specific.enc_h26x_cmd.vuiColorDescripPresentFlag = 1;
+ encparams->specific.enc_h26x_cmd.vuiVideoSignalTypePresentFlag = 1;
+ if (quantization == V4L2_QUANTIZATION_LIM_RANGE ||
+ quantization == V4L2_QUANTIZATION_DEFAULT)
+ encparams->specific.enc_h26x_cmd.videoRange = 0;
+ else
+ encparams->specific.enc_h26x_cmd.videoRange = 1;
+ encparams->specific.enc_h26x_cmd.vuiColorPrimaries = 0;
+ for (i = 0; i < ARRAY_SIZE(colorprimaries); i++) {
+ if (colorprimaries[i] == colorspace) {
+ encparams->specific.enc_h26x_cmd.vuiColorPrimaries = i;
+ break;
+ }
+ }
+ encparams->specific.enc_h26x_cmd.vuiTransferCharacteristics = 0;
+ for (i = 0; i < ARRAY_SIZE(colortransfers); i++) {
+ if (colortransfers[i] == transfer) {
+ encparams->specific.enc_h26x_cmd.vuiTransferCharacteristics = i;
+ break;
+ }
+ }
+ encparams->specific.enc_h26x_cmd.vuiMatrixCoefficients = 0;
+ for (i = 0; i < ARRAY_SIZE(colormatrixcoefs); i++) {
+ if (colormatrixcoefs[i] == matrixcoeff) {
+ encparams->specific.enc_h26x_cmd.vuiMatrixCoefficients = i;
+ break;
+ }
+ }
+ pr_debug("%s:%x:%d:%d:%d:%d", __func__, v4l2fmt->fmt.pix_mp.pixelformat,
+ colorspace, transfer, matrixcoeff, quantization);
+ if (binputqueue(v4l2fmt->type) && enc_isRGBformat(v4l2fmt->fmt.pix_mp.pixelformat)) {
+ if (encparams->specific.enc_h26x_cmd.videoRange == 0)
+ encparams->general.colorConversion = VCENC_RGBTOYUV_BT601_FULL_RANGE;
+ /*full/limit range is reversed here for ctrl sw reverse it. May turn back to right setting when it's fixed*/
+ switch (colorspace) {
+ case V4L2_COLORSPACE_REC709:
+ if (encparams->specific.enc_h26x_cmd.videoRange == 0)
+ encparams->general.colorConversion = VCENC_RGBTOYUV_BT709_FULL_RANGE;
+ else
+ encparams->general.colorConversion = VCENC_RGBTOYUV_BT709;
+ break;
+ case V4L2_COLORSPACE_JPEG:
+ if (encparams->specific.enc_h26x_cmd.videoRange == 0)
+ encparams->general.colorConversion = VCENC_RGBTOYUV_BT601_FULL_RANGE;
+ else
+ encparams->general.colorConversion = VCENC_RGBTOYUV_BT601;
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ encparams->general.colorConversion = VCENC_RGBTOYUV_BT2020;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+void dec_getvui(struct v4l2_format *v4l2fmt, struct v4l2_daemon_dec_info *decinfo)
+{
+ u32 colorspace, quantization, transfer, matrixcoeff;
+
+ colorspace = quantization = transfer = matrixcoeff = 0;
+ if (decinfo->colour_description_present_flag) {
+ quantization = (decinfo->video_range == 0 ?
+ V4L2_QUANTIZATION_LIM_RANGE :
+ V4L2_QUANTIZATION_FULL_RANGE);
+ if (decinfo->colour_primaries < ARRAY_SIZE(colorprimaries))
+ colorspace = colorprimaries[decinfo->colour_primaries];
+ if (decinfo->transfer_characteristics < ARRAY_SIZE(colortransfers))
+ transfer = colortransfers[decinfo->transfer_characteristics];
+ if (decinfo->matrix_coefficients < ARRAY_SIZE(colormatrixcoefs))
+ matrixcoeff = colormatrixcoefs[decinfo->matrix_coefficients];
+ }
+ v4l2fmt->fmt.pix.quantization = quantization;
+ v4l2fmt->fmt.pix.colorspace = colorspace;
+ v4l2fmt->fmt.pix.xfer_func = transfer;
+ v4l2fmt->fmt.pix.ycbcr_enc = matrixcoeff;
+ pr_debug("%s:%x:%d:%d:%d:%d", __func__, v4l2fmt->fmt.pix_mp.pixelformat,
+ colorspace, transfer, matrixcoeff, quantization);
+}
+
+void dec_updatevui(struct v4l2_daemon_dec_info *src, struct v4l2_daemon_dec_info *dst)
+{
+ pr_debug("%s:%d:%d:%d", __func__,
+ src->colour_primaries, src->transfer_characteristics, src->matrix_coefficients);
+ dst->colour_description_present_flag = 1;
+ dst->colour_primaries = src->colour_primaries;
+ dst->transfer_characteristics = src->transfer_characteristics;
+ dst->matrix_coefficients = src->matrix_coefficients;
+}
+
+int get_Level(struct vsi_v4l2_ctx *ctx, int mediatype, int dir, int level)
+{
+ int i, size;
+ int (*table)[3];
+
+ if (mediatype == 0) {
+ table = leveltbl_h264;
+ size = ARRAY_SIZE(leveltbl_h264);
+ } else {
+ table = leveltbl_hevc;
+ size = ARRAY_SIZE(leveltbl_hevc);
+ }
+ for (i = 0; i < size; i++) {
+ if (dir == 0 && level == table[i][0])
+ return table[i][1];
+ if (dir == 1 && level == table[i][1]) {
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.cpbSize = table[i][2];
+ return table[i][0];
+ }
+ }
+ if (dir == 0 && level == DEFAULTLEVEL)
+ return (mediatype == 0 ? V4L2_MPEG_VIDEO_H264_LEVEL_1_0 :
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_1);
+ return -EINVAL;
+}
+
+static struct vsi_video_fmt vsi_raw_fmt[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .enc_fmt = VCENC_YUV420_SEMIPLANAR,
+ .dec_fmt = VSI_V4L2_DECOUT_NV12,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .enc_fmt = VCENC_YUV420_PLANAR,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .enc_fmt = VCENC_YUV420_SEMIPLANAR_VU,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .enc_fmt = VCENC_YUV422_INTERLEAVED_YUYV,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .enc_fmt = VCENC_RGB565,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .name = "BGR16",
+ .fourcc = V4L2_PIX_FMT_BGR565,
+ .enc_fmt = VCENC_BGR565,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .enc_fmt = VCENC_RGB555,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGBA32,
+ .enc_fmt = VCENC_BGR888,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .enc_fmt = VCENC_RGB888,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .enc_fmt = VCENC_RGB888,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGBX32,
+ .enc_fmt = VCENC_BGR888,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = 0,
+ },
+ {
+ .name = "VSI DTRC",
+ .fourcc = V4L2_PIX_FMT_DTRC,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = VSI_V4L2_DECOUT_DTRC,
+ .flag = 0,
+ },
+ {
+ .name = "P010",
+ .fourcc = V4L2_PIX_FMT_P010,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = VSI_V4L2_DECOUT_P010,
+ .flag = 0,
+ },
+ {
+ .name = "NV12 10Bit",
+ .fourcc = V4L2_PIX_FMT_NV12X,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = VSI_V4L2_DECOUT_NV12_10BIT,
+ .flag = 0,
+ },
+};
+
+static struct vsi_video_fmt vsi_coded_fmt[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_HEVC,
+ .enc_fmt = V4L2_DAEMON_CODEC_ENC_HEVC,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_HEVC,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_H264,
+ .enc_fmt = V4L2_DAEMON_CODEC_ENC_H264,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_H264,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_JPEG,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VP8,
+ .enc_fmt = V4L2_DAEMON_CODEC_ENC_VP8,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_VP8,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VP9,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_VP9,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .name = "av1",
+ .fourcc = V4L2_PIX_FMT_AV1,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_MPEG2,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_MPEG2,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_MPEG4,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_H263,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_H263,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_VC1_G,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_VC1_L,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .name = "rv",
+ .fourcc = V4L2_PIX_FMT_RV,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_RV,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+ {
+ .name = "avs",
+ .fourcc = V4L2_PIX_FMT_AVS,
+ .enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+ .dec_fmt = V4L2_DAEMON_CODEC_DEC_AVS2,
+ .flag = (V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED),
+ },
+};
+
+void vsi_enum_encfsize(struct v4l2_frmsizeenum *f, u32 pixel_format)
+{
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_HEVC:
+ f->stepwise.min_width = 144;
+ f->stepwise.max_width = 1920;
+ f->stepwise.step_width = 2;
+ f->stepwise.min_height = 144;
+ f->stepwise.max_height = 1088;
+ f->stepwise.step_height = 2;
+ break;
+ case V4L2_PIX_FMT_H264:
+ if (vsi_v4l2_hwconfig.enc_isH1) {
+ f->stepwise.min_width = 144;
+ f->stepwise.max_width = 1920;
+ f->stepwise.step_width = 4;
+ f->stepwise.min_height = 96;
+ f->stepwise.max_height = 2944;
+ f->stepwise.step_height = 2;
+ } else {
+ f->stepwise.min_width = 132;
+ f->stepwise.max_width = 1920;
+ f->stepwise.step_width = 2;
+ f->stepwise.min_height = 128;
+ f->stepwise.max_height = 8192;
+ f->stepwise.step_height = 2;
+ }
+ break;
+ case V4L2_PIX_FMT_VP8:
+ f->stepwise.min_width = 144;
+ f->stepwise.max_width = 1920;
+ f->stepwise.step_width = 4;
+ f->stepwise.min_height = 96;
+ f->stepwise.max_height = 4080;
+ f->stepwise.step_height = 2;
+ break;
+ default:
+ f->stepwise.min_width = 132;
+ f->stepwise.max_width = 1920;
+ f->stepwise.min_height = 96;
+ f->stepwise.max_height = 1088;
+ break;
+ }
+ f->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+}
+
+int vsi_set_profile(struct vsi_v4l2_ctx *ctx, int type, int profile)
+{
+ static int h264list[] = {
+ VCENC_H264_BASE_PROFILE,
+ VCENC_H264_BASE_PROFILE,
+ VCENC_H264_MAIN_PROFILE,
+ VCENC_H264_BASE_PROFILE,
+ VCENC_H264_HIGH_PROFILE,
+ VCENC_H264_HIGH_10_PROFILE,
+ VCENC_H264_HIGH_10_PROFILE,
+ VCENC_H264_HIGH_10_PROFILE,
+ VCENC_H264_HIGH_10_PROFILE,
+ VCENC_H264_HIGH_10_PROFILE,
+ VCENC_H264_HIGH_10_PROFILE,
+ VCENC_H264_BASE_PROFILE,
+ VCENC_H264_BASE_PROFILE,
+ VCENC_H264_HIGH_PROFILE,
+ VCENC_H264_HIGH_PROFILE,
+ VCENC_H264_HIGH_PROFILE,
+ VCENC_H264_HIGH_PROFILE,
+ };
+ static int hevclist[] = {
+ VCENC_HEVC_MAIN_PROFILE,
+ VCENC_HEVC_MAIN_STILL_PICTURE_PROFILE,
+ VCENC_HEVC_MAIN_10_PROFILE,
+ };
+ static int vp9list[] = {
+ VCENC_VP9_MAIN_PROFILE,
+ VCENC_VP9_MSRGB_PROFILE,
+ VCENC_VP9_HIGH_PROFILE,
+ VCENC_VP9_HSRGB_PROFILE,
+ };
+
+ pr_debug("%s:%d:%d", __func__, type, profile);
+ switch (type) {
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ if (profile >= ARRAY_SIZE(h264list))
+ return -EINVAL;
+ ctx->mediacfg.profile_h264 = h264list[profile];
+ return 0;
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ if (profile >= ARRAY_SIZE(hevclist))
+ return -EINVAL;
+ ctx->mediacfg.profile_hevc = hevclist[profile];
+ return 0;
+ case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
+ ctx->mediacfg.profile_vp8 = profile;
+ return 0;
+ case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
+ if (profile >= ARRAY_SIZE(vp9list))
+ return -EINVAL;
+ ctx->mediacfg.profile_vp9 = vp9list[profile];
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+int vsi_get_profile(struct vsi_v4l2_ctx *ctx, int type)
+{
+ pr_debug("%s:%d", __func__, type);
+ switch (type) {
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ if (ctx->mediacfg.profile_h264 == VCENC_H264_BASE_PROFILE)
+ return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
+ else if (ctx->mediacfg.profile_h264 == VCENC_H264_MAIN_PROFILE)
+ return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN;
+ else if (ctx->mediacfg.profile_h264 == VCENC_H264_HIGH_PROFILE)
+ return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH;
+ else
+ return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10;
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ if (ctx->mediacfg.profile_hevc == VCENC_HEVC_MAIN_PROFILE)
+ return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN;
+ else if (ctx->mediacfg.profile_hevc == VCENC_HEVC_MAIN_STILL_PICTURE_PROFILE)
+ return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE;
+ else
+ return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10;
+ case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
+ return ctx->mediacfg.profile_vp8;
+ case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
+ if (ctx->mediacfg.profile_vp9 == VCENC_VP9_MAIN_PROFILE)
+ return V4L2_MPEG_VIDEO_VP9_PROFILE_0;
+ else if (ctx->mediacfg.profile_vp9 == VCENC_VP9_MSRGB_PROFILE)
+ return V4L2_MPEG_VIDEO_VP9_PROFILE_1;
+ else if (ctx->mediacfg.profile_vp9 == VCENC_VP9_HIGH_PROFILE)
+ return V4L2_MPEG_VIDEO_VP9_PROFILE_2;
+ else
+ return V4L2_MPEG_VIDEO_VP9_PROFILE_3;
+ default:
+ return -EINVAL;
+ }
+
+}
+
+struct vsi_video_fmt *vsi_find_format(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ u32 fourcc;
+ int i;
+ int braw = brawfmt(ctx->flag, fmt->type);
+ struct vsi_video_fmt *retfmt = NULL;
+
+ if (isencoder(ctx))
+ fourcc = fmt->fmt.pix_mp.pixelformat;
+ else
+ fourcc = fmt->fmt.pix.pixelformat;
+ if (braw) {
+ for (i = 0; i < ARRAY_SIZE(vsi_raw_fmt); i++) {
+ if (vsi_raw_fmt[i].fourcc == fourcc) {
+ retfmt = &vsi_raw_fmt[i];
+ if (isdecoder(ctx) && retfmt->dec_fmt == V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ retfmt = NULL;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(vsi_coded_fmt); i++) {
+ if (vsi_coded_fmt[i].fourcc == fourcc) {
+ retfmt = &vsi_coded_fmt[i];
+ if (isencoder(ctx) && retfmt->enc_fmt == V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ retfmt = NULL;
+ if (isdecoder(ctx) && retfmt->dec_fmt == V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ retfmt = NULL;
+ break;
+ }
+ }
+ }
+ return retfmt;
+}
+
+struct vsi_video_fmt *vsi_get_format_dec(int idx, int braw, struct vsi_v4l2_ctx *ctx)
+{
+ u32 inputformat = ctx->mediacfg.decparams.dec_info.io_buffer.inputFormat;
+ int i = 0, k = -1, outfmt;
+
+ pr_debug("%s:%d:%d:%x", __func__, idx, braw, inputformat);
+ if (braw == 1) {
+ for (; i < ARRAY_SIZE(vsi_raw_fmt); i++) {
+ outfmt = vsi_raw_fmt[i].dec_fmt;
+ if (outfmt == V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ continue;
+ if (outfmt == VSI_V4L2_DECOUT_DTRC &&
+ inputformat != V4L2_DAEMON_CODEC_DEC_HEVC &&
+ inputformat != V4L2_DAEMON_CODEC_DEC_VP9)
+ continue;
+ if (test_bit(CTX_FLAG_SRCCHANGED_BIT, &ctx->flag)) {
+ if ((outfmt == VSI_V4L2_DECOUT_NV12_10BIT ||
+ outfmt == VSI_V4L2_DECOUT_P010) &&
+ ctx->mediacfg.decparams.dec_info.dec_info.bit_depth < 10)
+ continue;
+ }
+ k++;
+
+ if (k == idx)
+ return &vsi_raw_fmt[i];
+ }
+ } else {
+ for (; i < ARRAY_SIZE(vsi_coded_fmt); i++) {
+ if (vsi_coded_fmt[i].dec_fmt != V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ k++;
+ if (k == idx)
+ return &vsi_coded_fmt[i];
+ }
+ }
+ return NULL;
+}
+
+struct vsi_video_fmt *vsi_get_format_enc(int idx, int braw)
+{
+ int i = 0, k = -1;
+
+ pr_debug("%s:%d:%d", __func__, idx, braw);
+ if (braw == 1) {
+ for (; i < ARRAY_SIZE(vsi_raw_fmt); i++) {
+ if (vsi_raw_fmt[i].enc_fmt != V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ k++;
+ if (k == idx)
+ return &vsi_raw_fmt[i];
+ }
+ } else {
+ for (; i < ARRAY_SIZE(vsi_coded_fmt); i++) {
+ if (vsi_coded_fmt[i].enc_fmt != V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ k++;
+ if (k == idx)
+ return &vsi_coded_fmt[i];
+ }
+ }
+ return NULL;
+}
+
+static void vsi_set_default_parameter_enc(
+ struct v4l2_daemon_enc_params *enc_params,
+ enum v4l2_daemon_codec_fmt fmt)
+{
+ /*general*/
+ enc_params->general.outputRateNumer = 30;
+ enc_params->general.outputRateDenom = 1;
+ enc_params->general.inputRateNumer = 30;
+ enc_params->general.inputRateDenom = 1;
+ enc_params->general.outputRateNumer = 30;
+ enc_params->general.outputRateDenom = 1;
+ enc_params->general.lastPic = 100;
+ enc_params->general.inputFormat = VCENC_FMT_INVALID;
+ enc_params->general.bitPerSecond = 1000000;
+ enc_params->general.colorConversion = -1;
+ enc_params->general.codecFormat = V4L2_DAEMON_CODEC_ENC_HEVC;
+
+ enc_params->specific.enc_h26x_cmd.byteStream = 1;
+ enc_params->specific.enc_h26x_cmd.profile = -1;
+ enc_params->specific.enc_h26x_cmd.tier = -1;
+ enc_params->specific.enc_h26x_cmd.avclevel = DEFAULTLEVEL;
+ enc_params->specific.enc_h26x_cmd.hevclevel = DEFAULTLEVEL;
+ enc_params->specific.enc_h26x_cmd.intraAreaTop = 1;
+ enc_params->specific.enc_h26x_cmd.intraAreaLeft = 1;
+ enc_params->specific.enc_h26x_cmd.intraAreaBottom = 1;
+ enc_params->specific.enc_h26x_cmd.intraAreaRight = -1;
+ enc_params->specific.enc_h26x_cmd.pcm_loop_filter_disabled_flag = 1;
+ /* Rate control parameters */
+ enc_params->specific.enc_h26x_cmd.hrdConformance = -1;
+ enc_params->specific.enc_h26x_cmd.cpbSize = 1000000;
+ enc_params->specific.enc_h26x_cmd.intraPicRate = DEFAULT_INTRA_PIC_RATE;
+ enc_params->specific.enc_h26x_cmd.qpHdr = DEFAULT_QP;
+ enc_params->specific.enc_h26x_cmd.qpHdrI = -1;
+ enc_params->specific.enc_h26x_cmd.qpHdrP = -1;
+ enc_params->specific.enc_h26x_cmd.qpMax = 51;
+ enc_params->specific.enc_h26x_cmd.qpMaxI = 51;
+ enc_params->specific.enc_h26x_cmd.bitVarRangeI = 10000;
+ enc_params->specific.enc_h26x_cmd.bitVarRangeP = 10000;
+ enc_params->specific.enc_h26x_cmd.bitVarRangeB = 10000;
+ enc_params->specific.enc_h26x_cmd.u32StaticSceneIbitPercent = 80;
+ enc_params->specific.enc_h26x_cmd.tolMovingBitRate = 2000;
+ enc_params->specific.enc_h26x_cmd.monitorFrames = -1;
+ enc_params->specific.enc_h26x_cmd.picRc = -1; //for VBR and CBR, 0 only for CQP
+ enc_params->specific.enc_h26x_cmd.ctbRc = -1;
+ enc_params->specific.enc_h26x_cmd.blockRCSize = -1;
+ enc_params->specific.enc_h26x_cmd.rcQpDeltaRange = -1;
+ enc_params->specific.enc_h26x_cmd.rcBaseMBComplexity = -1;
+ enc_params->specific.enc_h26x_cmd.picQpDeltaMin = -1;
+ enc_params->specific.enc_h26x_cmd.picQpDeltaMax = -1;
+ enc_params->specific.enc_h26x_cmd.ctbRcRowQpStep = -1;
+ enc_params->specific.enc_h26x_cmd.tolCtbRcInter = -1;
+ enc_params->specific.enc_h26x_cmd.tolCtbRcIntra = -1;
+ enc_params->specific.enc_h26x_cmd.bitrateWindow = 150;
+ enc_params->specific.enc_h26x_cmd.bFrameQpDelta = -1;
+ enc_params->specific.enc_h26x_cmd.enableSao = 1;
+ enc_params->specific.enc_h26x_cmd.tc_Offset = -2;
+ enc_params->specific.enc_h26x_cmd.beta_Offset = 5;
+ enc_params->specific.enc_h26x_cmd.ssim = 1;
+ enc_params->specific.enc_h26x_cmd.userData = NULL;
+ enc_params->specific.enc_h26x_cmd.gopSize = DEFAULT_GOP_SIZE;
+ enc_params->specific.enc_h26x_cmd.gopCfg = NULL;
+ enc_params->specific.enc_h26x_cmd.outReconFrame = 1;
+ enc_params->specific.enc_h26x_cmd.ltrInterval = -1;
+ enc_params->specific.enc_h26x_cmd.bitDepthLuma = 8;
+ enc_params->specific.enc_h26x_cmd.bitDepthChroma = 8;
+ enc_params->specific.enc_h26x_cmd.rdoLevel = 3;
+ enc_params->specific.enc_h26x_cmd.constCb = -1;
+ enc_params->specific.enc_h26x_cmd.constCr = -1;
+ /* HDR10 */
+ enc_params->specific.enc_h26x_cmd.hdr10_primary = 9;
+ enc_params->specific.enc_h26x_cmd.hdr10_matrix = 9;
+ enc_params->specific.enc_h26x_cmd.vui_timing_info_enable = 1;
+ enc_params->specific.enc_h26x_cmd.log2MaxPicOrderCntLsb = 16;
+ enc_params->specific.enc_h26x_cmd.log2MaxFrameNum = 12;
+ enc_params->specific.enc_h26x_cmd.cuInfoVersion = 2;
+ enc_params->specific.enc_h26x_cmd.parallelCoreNum = 1;
+}
+
+void vsiv4l2_initcfg(struct vsi_v4l2_ctx *ctxp)
+{
+ struct vsi_v4l2_mediacfg *ctx = &ctxp->mediacfg;
+
+ ctx->decparams.dec_info.io_buffer.inputFormat = V4L2_DAEMON_CODEC_DEC_HEVC;
+ ctx->decparams.dec_info.io_buffer.outBufFormat = VSI_V4L2_DECOUT_NV12;
+ ctx->decparams.dec_info.io_buffer.outputPixelDepth = 10;
+ ctx->decparams.dec_info.dec_info.bit_depth = 10;
+ vsi_set_default_parameter_enc(&ctx->encparams, V4L2_DAEMON_CODEC_ENC_H264);
+ ctx->infmt_fourcc = -1;
+ ctx->outfmt_fourcc = -1;
+
+ if (isencoder(ctxp))
+ ctx->srcplanes = 2; //default src format is NV12 now
+ else
+ ctx->srcplanes = 1;
+ ctx->dstplanes = 1;
+ ctx->profile_hevc = VCENC_HEVC_MAIN_PROFILE;
+ ctx->profile_h264 = VCENC_H264_BASE_PROFILE;
+ ctx->profile_vp9 = VCENC_VP9_MAIN_PROFILE;
+ ctx->encparams.specific.enc_h26x_cmd.gopSize = DEFAULT_GOP_SIZE;
+
+ ctx->encparams.general.inputFormat = VCENC_FMT_INVALID;
+ ctx->field = V4L2_FIELD_NONE;
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+ ctx->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ ctx->minbuf_4capture = 1;
+ ctx->minbuf_4output = 1;
+
+ ctx->capparam.capability = ctx->capparam.capturemode = V4L2_CAP_TIMEPERFRAME;
+ ctx->capparam.readbuffers = 1;
+ ctx->capparam.timeperframe.numerator = 1;
+ ctx->capparam.timeperframe.denominator = 25;
+
+ ctx->outputparam.capability = ctx->outputparam.outputmode = V4L2_CAP_TIMEPERFRAME;
+ ctx->outputparam.writebuffers = 1;
+ ctx->outputparam.timeperframe.numerator = 1;
+ ctx->outputparam.timeperframe.denominator = 25;
+
+ ctx->encparams.general.inputRateNumer = 1;
+ ctx->encparams.general.inputRateDenom = 25;
+
+ ctx->encparams.general.outputRateNumer = 1;
+ ctx->encparams.general.outputRateDenom = 25;
+
+ ctx->multislice_mode = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE; //0
+}
+
+static int get_fmtprofile(struct vsi_v4l2_mediacfg *pcfg)
+{
+ int codecFormat = pcfg->encparams.general.codecFormat;
+
+ switch (codecFormat) {
+ case V4L2_DAEMON_CODEC_ENC_HEVC:
+ return pcfg->profile_hevc;
+ case V4L2_DAEMON_CODEC_ENC_H264:
+ return pcfg->profile_h264;
+ case V4L2_DAEMON_CODEC_ENC_VP8:
+ return pcfg->profile_vp8;
+ case V4L2_DAEMON_CODEC_ENC_VP9:
+ return pcfg->profile_vp9;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void verifyPlanesize(unsigned int psize[], int braw, int pixelformat, int width, int height, int planeno, int pixelWidth)
+{
+ int totalsize = 0;
+ int basesize = width * height * pixelWidth / 8, extsize = 0, quadsize = 0;
+
+ if (braw) {
+ if (enc_isRGBformat(pixelformat)) {
+ extsize = 0;
+ quadsize = 0;
+ } else {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ extsize = basesize / 2;
+ quadsize = basesize / 4;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ extsize = 0;
+ quadsize = 0;
+ break;
+ default:
+ extsize = basesize;
+ quadsize = basesize / 2;
+ break;
+ }
+ }
+ if (planeno == 1) {
+ totalsize = basesize + extsize;
+ psize[0] = max_t(int, PAGE_ALIGN(totalsize), psize[0]);
+ } else if (planeno == 2) {
+ psize[0] = basesize;
+ psize[1] = extsize;
+ } else if (planeno == 3) {
+ psize[0] = basesize;
+ psize[1] = quadsize;
+ psize[2] = quadsize;
+ }
+ } else {
+ //for coded format we support 1 plane only
+ //except certain header the CR data can be any small
+ //so just make it page aligned.
+ psize[0] = max_t(int, PAGE_ALIGN(basesize), psize[0]);
+ }
+}
+
+static int config_planeno(int pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ return 2;
+ case V4L2_PIX_FMT_YUV420:
+ return 3;
+ default:
+ return 1;
+ }
+}
+
+static int vsiv4l2_enc_getalign(u32 srcfmt, u32 dstfmt, int width)
+{
+ int bytesperline = width;
+
+ switch (dstfmt) {
+ case V4L2_DAEMON_CODEC_ENC_HEVC:
+ bytesperline = ALIGN(bytesperline, 8);
+ break;
+ case V4L2_DAEMON_CODEC_ENC_H264:
+ case V4L2_DAEMON_CODEC_ENC_VP8:
+ default:
+ if (srcfmt == VCENC_YUV422_INTERLEAVED_YUYV && vsi_v4l2_hwconfig.enc_isH1)
+ bytesperline = ALIGN(bytesperline, 32);
+ else
+ bytesperline = ALIGN(bytesperline, 16);
+ break;
+ }
+ return bytesperline;
+}
+
+static int is_doublesizefmt(int fmt)
+{
+ if (fmt == V4L2_PIX_FMT_RGB565 || fmt == V4L2_PIX_FMT_RGB555 ||
+ fmt == V4L2_PIX_FMT_BGRX555 || fmt == V4L2_PIX_FMT_YUYV)
+ return 1;
+ return 0;
+}
+
+static int is_quadsizefmt(int fmt)
+{
+ if (fmt == V4L2_PIX_FMT_RGBA32
+ || fmt == V4L2_PIX_FMT_BGR32
+ || fmt == V4L2_PIX_FMT_ABGR32
+ || fmt == V4L2_PIX_FMT_RGBX32)
+ return 1;
+ return 0;
+}
+
+static int vsiv4l2_setfmt_enc(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+ int i;
+ struct vsi_video_fmt *targetfmt;
+ int userset_planeno;
+ unsigned int *psize;
+ int braw = brawfmt(ctx->flag, fmt->type);
+
+ targetfmt = vsi_find_format(ctx, fmt);
+ if (targetfmt == NULL)
+ return -EINVAL;
+ pr_debug("%s:%d:%x", __func__, fmt->type, fmt->fmt.pix_mp.pixelformat);
+ if (binputqueue(fmt->type))
+ psize = pcfg->sizeimagesrc;
+ else
+ psize = pcfg->sizeimagedst;
+
+ vsiv4l2_verifyfmt(ctx, fmt);
+ if (binputqueue(fmt->type)) {
+ pcfg->encparams.general.lumWidthSrc = fmt->fmt.pix_mp.width;
+ pcfg->encparams.general.lumHeightSrc = fmt->fmt.pix_mp.height;
+ pcfg->encparams.general.inputFormat = targetfmt->enc_fmt;
+ pcfg->infmt_fourcc = fmt->fmt.pix_mp.pixelformat;
+ } else {
+ pcfg->encparams.general.width = fmt->fmt.pix_mp.width;
+ pcfg->encparams.general.height = fmt->fmt.pix_mp.height;
+ pcfg->encparams.general.codecFormat = targetfmt->enc_fmt;
+ pcfg->outfmt_fourcc = fmt->fmt.pix_mp.pixelformat;
+ pcfg->encparams.specific.enc_h26x_cmd.profile = get_fmtprofile(pcfg);
+ }
+ userset_planeno = fmt->fmt.pix_mp.num_planes;
+ //force it
+ if (braw)
+ fmt->fmt.pix_mp.num_planes = config_planeno(fmt->fmt.pix_mp.pixelformat);
+ else
+ fmt->fmt.pix_mp.num_planes = 1;
+ pcfg->bytesperline = vsiv4l2_enc_getalign(pcfg->encparams.general.inputFormat, pcfg->encparams.general.codecFormat, fmt->fmt.pix_mp.plane_fmt[0].bytesperline);
+ if (pcfg->bytesperline == 0)
+ pcfg->bytesperline = vsiv4l2_enc_getalign(pcfg->encparams.general.inputFormat, pcfg->encparams.general.codecFormat, fmt->fmt.pix_mp.width);
+
+ if (fmt->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_YUV420) {
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = pcfg->bytesperline;
+ fmt->fmt.pix_mp.plane_fmt[1].bytesperline =
+ fmt->fmt.pix_mp.plane_fmt[2].bytesperline = pcfg->bytesperline/2;
+ } else {
+ for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++)
+ fmt->fmt.pix_mp.plane_fmt[i].bytesperline = pcfg->bytesperline;
+ }
+ if (is_doublesizefmt(fmt->fmt.pix_mp.pixelformat))
+ pcfg->encparams.general.lumWidthSrc = pcfg->bytesperline/2;
+ else if (is_quadsizefmt(fmt->fmt.pix_mp.pixelformat))
+ pcfg->encparams.general.lumWidthSrc = pcfg->bytesperline/4;
+ else
+ pcfg->encparams.general.lumWidthSrc = pcfg->bytesperline;
+
+ if (fmt->fmt.pix_mp.num_planes == userset_planeno) {
+ for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++)
+ psize[i] = fmt->fmt.pix_mp.plane_fmt[i].sizeimage;
+ verifyPlanesize(psize, braw, fmt->fmt.pix_mp.pixelformat, pcfg->bytesperline, fmt->fmt.pix_mp.height, userset_planeno, 8);
+ } else
+ calcPlanesize(ctx, fmt->fmt.pix_mp.pixelformat, pcfg->bytesperline, fmt->fmt.pix_mp.height, psize, fmt->type, fmt->fmt.pix_mp.num_planes);
+ for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++)
+ fmt->fmt.pix_mp.plane_fmt[i].sizeimage = psize[i];
+
+ if (binputqueue(fmt->type))
+ pcfg->srcplanes = fmt->fmt.pix_mp.num_planes;
+ else
+ pcfg->dstplanes = fmt->fmt.pix_mp.num_planes;
+ pcfg->field = fmt->fmt.pix_mp.field;
+ pcfg->colorspace = fmt->fmt.pix_mp.colorspace;
+ pcfg->flags = fmt->fmt.pix_mp.flags;
+ pcfg->quantization = fmt->fmt.pix_mp.quantization;
+ pcfg->xfer_func = fmt->fmt.pix_mp.xfer_func;
+ enc_setvui(fmt, &pcfg->encparams);
+
+ if (binputqueue(fmt->type)) {
+ pr_debug("%s:%d:%d:%d:%d:%d", __func__, fmt->type, pcfg->srcplanes, pcfg->bytesperline, pcfg->sizeimagesrc[0], pcfg->sizeimagesrc[1]);
+ pr_debug("%d:%d:%d:%d", fmt->fmt.pix_mp.num_planes, fmt->fmt.pix_mp.plane_fmt[0].bytesperline,
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage, fmt->fmt.pix_mp.plane_fmt[1].sizeimage);
+ } else {
+ pr_debug("%s:%d:%d:%d:%d", __func__, fmt->type, pcfg->dstplanes, pcfg->bytesperline, pcfg->sizeimagedst[0]);
+ pr_debug("%d:%d:%d", fmt->fmt.pix_mp.num_planes, fmt->fmt.pix_mp.plane_fmt[0].bytesperline,
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage);
+ }
+ return 0;
+}
+
+static void vsiv4l2_convertpixel2MB(
+ struct v4l2_rect *src,
+ int fmt,
+ s32 *left,
+ s32 *top,
+ s32 *right,
+ s32 *bottom)
+{
+ int align = (fmt == V4L2_DAEMON_CODEC_ENC_HEVC ? 64:16);
+ u32 r, b;
+
+ *left = src->left / align;
+ *top = src->top / align;
+ r = src->left + src->width;
+ b = src->top + src->height;
+ *right = (r + align - 1) / align;
+ *bottom = (b + align - 1) / align;
+}
+
+void convertROI(struct vsi_v4l2_ctx *ctx)
+{
+ struct v4l2_enc_roi_params *proi = &ctx->mediacfg.roiinfo;
+ int fmt = ctx->mediacfg.encparams.general.codecFormat;
+ int i, num;
+ struct v4l2_daemon_enc_h26x_cmd *penccfg = &ctx->mediacfg.encparams.specific.enc_h26x_cmd;
+
+ if (vsi_v4l2_hwconfig.encformat == 0)
+ return;
+ num = (vsi_v4l2_hwconfig.enc_isH1 ? VSI_V4L2_MAX_ROI_REGIONS_H1 : VSI_V4L2_MAX_ROI_REGIONS);
+ if (proi->num_roi_regions < num)
+ num = proi->num_roi_regions;
+
+ pr_debug("%s:%d", __func__, proi->num_roi_regions);
+ for (i = 0; i < num; i++) {
+ pr_debug("roi %d:%d:%d:%d:%d:%d", i, proi->roi_params[i].qp_delta,
+ proi->roi_params[i].rect.left, proi->roi_params[i].rect.top, proi->roi_params[i].rect.width, proi->roi_params[i].rect.height);
+ penccfg->roiAreaEnable[i] = proi->roi_params[i].enable;
+ vsiv4l2_convertpixel2MB(&proi->roi_params[i].rect, fmt, &penccfg->roiAreaLeft[i],
+ &penccfg->roiAreaTop[i], &penccfg->roiAreaRight[i], &penccfg->roiAreaBottom[i]);
+ penccfg->roiDeltaQp[i] = proi->roi_params[i].qp_delta;
+ penccfg->roiQp[i] = -1;
+ }
+ /*disable left ones*/
+ for (; i < VSI_V4L2_MAX_ROI_REGIONS; i++) {
+ penccfg->roiAreaEnable[i] = penccfg->roiAreaTop[i] = penccfg->roiAreaLeft[i] =
+ penccfg->roiAreaBottom[i] = penccfg->roiAreaRight[i] = 0;
+ }
+}
+
+void convertIPCM(struct vsi_v4l2_ctx *ctx)
+{
+ struct v4l2_enc_ipcm_params *ipcm = &ctx->mediacfg.ipcminfo;
+ int fmt = ctx->mediacfg.encparams.general.codecFormat;
+ int i, num;
+ struct v4l2_daemon_enc_h26x_cmd *penccfg = &ctx->mediacfg.encparams.specific.enc_h26x_cmd;
+
+ if (vsi_v4l2_hwconfig.encformat == 0 || vsi_v4l2_hwconfig.enc_isH1)
+ return;
+ num = (ipcm->num_ipcm_regions > VSI_V4L2_MAX_IPCM_REGIONS ?
+ VSI_V4L2_MAX_IPCM_REGIONS : ipcm->num_ipcm_regions);
+ pr_debug("%s:%d", __func__, ipcm->num_ipcm_regions);
+ for (i = 0; i < num; i++) {
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.pcm_loop_filter_disabled_flag = 0;
+ pr_debug("ipcm %d:%d:%d:%d:%d", i,
+ ipcm->ipcm_params[i].rect.left, ipcm->ipcm_params[i].rect.top,
+ ipcm->ipcm_params[i].rect.width, ipcm->ipcm_params[i].rect.height);
+ penccfg->ipcmAreaEnable[i] = ipcm->ipcm_params[i].enable;
+ vsiv4l2_convertpixel2MB(&ipcm->ipcm_params[i].rect, fmt, &penccfg->ipcmAreaLeft[i],
+ &penccfg->ipcmAreaTop[i], &penccfg->ipcmAreaRight[i], &penccfg->ipcmAreaBottom[i]);
+ }
+ /*disable left ones*/
+ for (; i < VSI_V4L2_MAX_IPCM_REGIONS; i++) {
+ penccfg->ipcmAreaEnable[i] = penccfg->ipcmAreaTop[i] = penccfg->ipcmAreaLeft[i] =
+ penccfg->ipcmAreaBottom[i] = penccfg->ipcmAreaRight[i] = 0;
+ }
+}
+
+int vsiv4l2_setROI(struct vsi_v4l2_ctx *ctx, void *params)
+{
+ struct v4l2_enc_roi_params *proi = (struct v4l2_enc_roi_params *)params;
+
+ ctx->mediacfg.roiinfo = *proi;
+ return 0;
+}
+
+int vsiv4l2_getROIcount(void)
+{
+ if (vsi_v4l2_hwconfig.encformat == 0)
+ return 0;
+ if (vsi_v4l2_hwconfig.enc_isH1)
+ return VSI_V4L2_MAX_ROI_REGIONS_H1;
+ return VSI_V4L2_MAX_ROI_REGIONS;
+}
+
+int vsiv4l2_setIPCM(struct vsi_v4l2_ctx *ctx, void *params)
+{
+ struct v4l2_enc_ipcm_params *ipcm = (struct v4l2_enc_ipcm_params *)params;
+
+ ctx->mediacfg.ipcminfo = *ipcm;
+ return 0;
+}
+
+int vsiv4l2_getIPCMcount(void)
+{
+ if (vsi_v4l2_hwconfig.encformat == 0 || vsi_v4l2_hwconfig.enc_isH1)
+ return 0;
+ return VSI_V4L2_MAX_IPCM_REGIONS;
+}
+
+static int vsiv4l2_decidepixeldepth(int pixelformat, int origdepth)
+{
+ switch (pixelformat) {
+ case VSI_V4L2_DECOUT_P010:
+ return 16;
+ case VSI_V4L2_DECOUT_NV12:
+ case VSI_V4L2_DECOUT_DTRC:
+ return 8;
+ default:
+ return origdepth;
+ }
+}
+
+static int vsiv4l2_setfmt_dec(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+ struct vsi_video_fmt *targetfmt;
+ unsigned int *psize;
+ int braw = brawfmt(ctx->flag, fmt->type);
+ int oldsize = 0;
+
+ targetfmt = vsi_find_format(ctx, fmt);
+ if (targetfmt == NULL)
+ return -EINVAL;
+ pr_debug("%s:%d", __func__, fmt->type);
+ if (binputqueue(fmt->type))
+ psize = pcfg->sizeimagesrc;
+ else {
+ psize = pcfg->sizeimagedst;
+ oldsize = psize[0];
+ }
+ if (binputqueue(fmt->type)) {
+ pcfg->decparams.dec_info.io_buffer.srcwidth = fmt->fmt.pix.width;
+ pcfg->decparams.dec_info.io_buffer.srcheight = fmt->fmt.pix.height;
+ pcfg->decparams.dec_info.io_buffer.inputFormat = targetfmt->dec_fmt;
+ } else {
+ //dtrc is only for HEVC and VP9
+ if ((targetfmt->dec_fmt == VSI_V4L2_DECOUT_DTRC) &&
+ pcfg->decparams.dec_info.io_buffer.inputFormat != V4L2_DAEMON_CODEC_DEC_VP9 &&
+ pcfg->decparams.dec_info.io_buffer.inputFormat != V4L2_DAEMON_CODEC_DEC_HEVC)
+ return -EINVAL;
+ fmt->fmt.pix.width = pcfg->decparams.dec_info.io_buffer.output_width;
+ fmt->fmt.pix.height = pcfg->decparams.dec_info.io_buffer.output_height;
+ pcfg->decparams.dec_info.io_buffer.outBufFormat = targetfmt->dec_fmt;
+ pcfg->decparams.dec_info.io_buffer.outputPixelDepth =
+ vsiv4l2_decidepixeldepth(targetfmt->dec_fmt, pcfg->decparams.dec_info.io_buffer.outputPixelDepth);
+ }
+ pcfg->bytesperline = fmt->fmt.pix.bytesperline; /* for padding, zero if unused */
+ pcfg->bytesperline = ALIGN(pcfg->bytesperline, 16);
+ if (pcfg->bytesperline == 0)
+ pcfg->bytesperline = ALIGN(fmt->fmt.pix.width, 16);
+ fmt->fmt.pix.bytesperline = pcfg->bytesperline;
+ psize[0] = fmt->fmt.pix.sizeimage;
+ verifyPlanesize(psize, braw, fmt->fmt.pix.pixelformat, pcfg->bytesperline, fmt->fmt.pix.height, 1,
+ !binputqueue(fmt->type) ? pcfg->decparams.dec_info.io_buffer.outputPixelDepth:8);
+ if (!binputqueue(fmt->type) && oldsize > psize[0])
+ psize[0] = oldsize;
+ fmt->fmt.pix.sizeimage = psize[0];
+ if (binputqueue(fmt->type))
+ pcfg->srcplanes = 1;
+ else
+ pcfg->dstplanes = 1;
+ pcfg->field = fmt->fmt.pix.field;
+ pcfg->colorspace = fmt->fmt.pix.colorspace;
+ pcfg->flags = fmt->fmt.pix.flags;
+ pcfg->quantization = fmt->fmt.pix.quantization;
+ pcfg->xfer_func = fmt->fmt.pix.xfer_func;
+ return 0;
+}
+
+
+int vsiv4l2_setfmt(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ if (isencoder(ctx))
+ return vsiv4l2_setfmt_enc(ctx, fmt);
+ else
+ return vsiv4l2_setfmt_dec(ctx, fmt);
+}
+
+static int vsiv4l2_verifyfmt(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+ struct v4l2_frmsizeenum fmsize;
+
+ /* verify and change format member to valid range */
+
+ if (!binputqueue(fmt->type))
+ fmsize.pixel_format = fmt->fmt.pix_mp.pixelformat;
+ else
+ fmsize.pixel_format = pcfg->outfmt_fourcc;
+ vsi_enum_encfsize(&fmsize, fmsize.pixel_format);
+
+ if (fmt->fmt.pix_mp.width < fmsize.stepwise.min_width)
+ fmt->fmt.pix_mp.width = fmsize.stepwise.min_width;
+ if (fmt->fmt.pix_mp.width > fmsize.stepwise.max_width)
+ fmt->fmt.pix_mp.width = fmsize.stepwise.max_width;
+ if (fmt->fmt.pix_mp.height < fmsize.stepwise.min_height)
+ fmt->fmt.pix_mp.height = fmsize.stepwise.min_height;
+ if (fmt->fmt.pix_mp.height > fmsize.stepwise.max_height)
+ fmt->fmt.pix_mp.height = fmsize.stepwise.max_height;
+
+ if (vsi_v4l2_hwconfig.enc_isH1) {
+ if (fmt->fmt.pix_mp.width & 0x3)
+ fmt->fmt.pix_mp.width = ALIGN(fmt->fmt.pix_mp.width, 4);
+ } else {
+ if (fmt->fmt.pix_mp.width & 0x1)
+ fmt->fmt.pix_mp.width = ALIGN(fmt->fmt.pix_mp.width, 2);
+ }
+ if (fmt->fmt.pix_mp.height & 0x1)
+ fmt->fmt.pix_mp.height = ALIGN(fmt->fmt.pix_mp.height, 2);
+ pr_debug("%s:%x:%d:%d", __func__, fmsize.pixel_format,
+ fmt->fmt.pix_mp.height, fmt->fmt.pix_mp.height);
+
+ return 0;
+}
+
+int vsiv4l2_verifycrop(struct v4l2_selection *s)
+{
+ int update = 0, align, mask;
+ struct v4l2_rect rect = s->r;
+ int right = rect.left + rect.width;
+ int bottom = rect.top + rect.height;
+
+ if (vsi_v4l2_hwconfig.enc_isH1) {
+ mask = 3;
+ align = 4;
+ } else {
+ mask = 1;
+ align = 2;
+ }
+ if ((rect.left & mask) || (right & mask) || (rect.top & 0x1) || (bottom & 0x1))
+ update = 1;
+ if (!update)
+ return 0;
+ if (s->flags == (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE))
+ return -ERANGE;
+ if (s->flags & V4L2_SEL_FLAG_LE) {
+ rect.left = ALIGN(rect.left, align);
+ if (right & mask)
+ right = ALIGN(right, align) - align;
+ rect.top = ALIGN(rect.top, 2);
+ if (bottom & 0x1)
+ bottom = ALIGN(bottom, 2) - 2;
+ rect.width = right - rect.left;
+ rect.height = bottom - rect.top;
+ s->r = rect;
+ return 0;
+ }
+ /*enlarge is previleged*/
+ if (rect.left & mask)
+ rect.left = ALIGN(rect.left, align) - align;
+ right = ALIGN(right, align);
+ if (rect.top & 2)
+ rect.top = ALIGN(rect.top, 2) - 2;
+ bottom = ALIGN(bottom, 2);
+ rect.width = right - rect.left;
+ rect.height = bottom - rect.top;
+ s->r = rect;
+ return 0;
+}
+
+static u32 find_local_dec_format(s32 fmt, int braw)
+{
+ int i;
+
+ if (braw) {
+ for (i = 0; i < ARRAY_SIZE(vsi_raw_fmt); i++) {
+ if (vsi_raw_fmt[i].dec_fmt == fmt)
+ return vsi_raw_fmt[i].fourcc;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(vsi_coded_fmt); i++) {
+ if (vsi_coded_fmt[i].dec_fmt == fmt)
+ return vsi_coded_fmt[i].fourcc;
+ }
+ }
+ return -1;
+}
+
+static int vsiv4l2_getfmt_enc(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+ int i;
+ int *psize = (binputqueue(fmt->type) ? pcfg->sizeimagesrc : pcfg->sizeimagedst);
+
+ if (binputqueue(fmt->type)) {
+ fmt->fmt.pix_mp.width = pcfg->encparams.general.lumWidthSrc;
+ fmt->fmt.pix_mp.height = pcfg->encparams.general.lumHeightSrc;
+ fmt->fmt.pix_mp.pixelformat = pcfg->infmt_fourcc;
+ } else {
+ fmt->fmt.pix_mp.width = pcfg->encparams.general.width;
+ fmt->fmt.pix_mp.height = pcfg->encparams.general.height;
+ fmt->fmt.pix_mp.pixelformat = pcfg->outfmt_fourcc;
+ }
+ fmt->fmt.pix_mp.field = pcfg->field;
+ if (binputqueue(fmt->type))
+ fmt->fmt.pix_mp.num_planes = pcfg->srcplanes;
+ else
+ fmt->fmt.pix_mp.num_planes = pcfg->dstplanes;
+ if (fmt->fmt.pix_mp.num_planes == 0)
+ fmt->fmt.pix_mp.num_planes = 1;
+ for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++)
+ fmt->fmt.pix_mp.plane_fmt[i].sizeimage = psize[i];
+ if (fmt->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_YUV420) {
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = pcfg->bytesperline;
+ fmt->fmt.pix_mp.plane_fmt[1].bytesperline =
+ fmt->fmt.pix_mp.plane_fmt[2].bytesperline = pcfg->bytesperline/2;
+ i = 3;
+ } else {
+ for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++)
+ fmt->fmt.pix_mp.plane_fmt[i].bytesperline = pcfg->bytesperline;
+ }
+
+ for (; i < VIDEO_MAX_PLANES; i++) {
+ fmt->fmt.pix_mp.plane_fmt[i].bytesperline = 0;
+ fmt->fmt.pix_mp.plane_fmt[i].sizeimage = 0;
+ }
+ pr_debug("%s:%d:%d:%d:%d", __func__, fmt->fmt.pix_mp.num_planes,
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage, fmt->fmt.pix_mp.plane_fmt[1].sizeimage, fmt->fmt.pix_mp.plane_fmt[0].bytesperline);
+ fmt->fmt.pix_mp.colorspace = pcfg->colorspace;
+ fmt->fmt.pix_mp.flags = pcfg->flags;
+ fmt->fmt.pix_mp.quantization = pcfg->quantization;
+ fmt->fmt.pix_mp.xfer_func = pcfg->xfer_func;
+ return 0;
+}
+
+static int vsiv4l2_getfmt_dec(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+ int braw = brawfmt(ctx->flag, fmt->type);
+ int *psize = (binputqueue(fmt->type) ? pcfg->sizeimagesrc : pcfg->sizeimagedst);
+
+ if (binputqueue(fmt->type)) {
+ fmt->fmt.pix.width = pcfg->decparams.dec_info.io_buffer.srcwidth;
+ fmt->fmt.pix.height = pcfg->decparams.dec_info.io_buffer.srcheight;
+ fmt->fmt.pix.pixelformat = find_local_dec_format(pcfg->decparams.dec_info.io_buffer.inputFormat, braw);
+ } else {
+ fmt->fmt.pix.width = pcfg->decparams.dec_info.io_buffer.output_width;
+ fmt->fmt.pix.height = pcfg->decparams.dec_info.io_buffer.output_height;
+ if (rpt_10bit && test_bit(CTX_FLAG_SRCCHANGED_BIT, &ctx->flag) &&
+ pcfg->decparams.dec_info.dec_info.bit_depth == 10)
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12X;
+ else
+ fmt->fmt.pix.pixelformat = find_local_dec_format(pcfg->decparams.dec_info.io_buffer.outBufFormat, braw);
+ }
+ fmt->fmt.pix.field = pcfg->field;
+ fmt->fmt.pix.bytesperline = pcfg->bytesperline;
+ fmt->fmt.pix.sizeimage = psize[0];
+ fmt->fmt.pix.colorspace = pcfg->colorspace;
+ fmt->fmt.pix.flags = pcfg->flags;
+ fmt->fmt.pix.quantization = pcfg->quantization;
+ fmt->fmt.pix.xfer_func = pcfg->xfer_func;
+ dec_getvui(fmt, &pcfg->decparams.dec_info.dec_info);
+ return 0;
+}
+
+
+
+int vsiv4l2_getfmt(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt)
+{
+ if (isencoder(ctx))
+ return vsiv4l2_getfmt_enc(ctx, fmt);
+ else
+ return vsiv4l2_getfmt_dec(ctx, fmt);
+}
+
+static void calcPlanesize(struct vsi_v4l2_ctx *ctx, int pixelformat, int width, int height, int size[], int type, int planeno)
+{
+ int i, basesize, extsize = 0, quadsize = 0;
+ int braw = brawfmt(ctx->flag, type);
+
+ for (i = 0; i < planeno; i++)
+ size[i] = 0;
+ basesize = width * height;
+ if (!braw)
+ size[0] = ALIGN(basesize + ENC_EXTRA_HEADER_SIZE, PAGE_SIZE);
+ else { /*raw formats*/
+ if (enc_isRGBformat(pixelformat)) {
+ extsize = 0;
+ quadsize = 0;
+ } else {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ extsize = basesize / 2;
+ quadsize = basesize / 4;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ extsize = 0;
+ quadsize = 0;
+ break;
+ default:
+ extsize = basesize;
+ quadsize = basesize / 2;
+ break;
+ }
+ }
+ if (planeno == 1) {
+ size[0] = basesize + extsize;
+ } else if (planeno == 2) {
+ size[0] = basesize;
+ size[1] = extsize;
+ } else if (planeno == 3) {
+ size[0] = basesize;
+ size[1] = quadsize;
+ size[2] = quadsize;
+ }
+ }
+ pr_debug("%s:%d:%d:%d", __func__, planeno, size[0], size[1]);
+}
+
+void vsiv4l2_buffer_config(
+ struct vsi_v4l2_ctx *ctx,
+ int type,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[]
+)
+{
+ struct v4l2_format fmt;
+ int i;
+ int *psize = (binputqueue(type) ? ctx->mediacfg.sizeimagesrc : ctx->mediacfg.sizeimagedst);
+
+ fmt.type = type;
+ vsiv4l2_getfmt(ctx, &fmt);
+ for (i = 0; i < VB2_MAX_PLANES; i++)
+ sizes[i] = 0;
+ if (isdecoder(ctx) && !binputqueue(type)) {
+ if (*nbuffers < ctx->mediacfg.minbuf_4capture)
+ *nbuffers = ctx->mediacfg.minbuf_4capture;
+ }
+ if (isencoder(ctx)) {
+ /*the upper limit is done in videobuf2-core*/
+ if (*nbuffers < ctx->mediacfg.encparams.specific.enc_h26x_cmd.gopSize)
+ *nbuffers = ctx->mediacfg.encparams.specific.enc_h26x_cmd.gopSize;
+ *nplanes = fmt.fmt.pix_mp.num_planes;
+ } else
+ *nplanes = 1;
+ for (i = 0; i < *nplanes; i++)
+ sizes[i] = psize[i];
+ //will this happen?
+ if (isdecoder(ctx) && binputqueue(type) &&
+ sizes[0] <= 0) {
+ sizes[0] = PAGE_SIZE;
+ }
+ pr_debug("%s:%d:%d:%d:%d", __func__, *nbuffers, *nplanes, sizes[0], sizes[1]);
+}
+
+
+void vsiv4l2_set_hwinfo(struct vsi_v4l2_dev_info *hwinfo)
+{
+ int i;
+
+ vsi_v4l2_hwconfig = *hwinfo;
+ pr_debug("%s:%lx:%lx", __func__, hwinfo->encformat, hwinfo->decformat);
+ for (i = 0; i < ARRAY_SIZE(vsi_coded_fmt); i++) {
+ if (((1 << i) & hwinfo->encformat) == 0)
+ vsi_coded_fmt[i].enc_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE;
+ if (((1 << i) & hwinfo->decformat) == 0)
+ vsi_coded_fmt[i].dec_fmt = V4L2_DAEMON_CODEC_UNKNOW_TYPE;
+ }
+ for (i = 0; i < ARRAY_SIZE(vsi_coded_fmt); i++)
+ pr_debug("%d:%d:%d", i, vsi_coded_fmt[i].enc_fmt, vsi_coded_fmt[i].dec_fmt);
+}
+
+struct vsi_v4l2_dev_info *vsiv4l2_get_hwinfo(void)
+{
+ return &vsi_v4l2_hwconfig;
+}
+
+void vsi_v4l2_update_ctrlcfg(struct v4l2_ctrl_config *cfg)
+{
+ switch (cfg->id) {
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ /*identiry 850 and 865 board, temp method*/
+ if (vsi_coded_fmt[DEC_HAS_VC1_G].dec_fmt == V4L2_DAEMON_CODEC_UNKNOW_TYPE)
+ cfg->max = (V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
+ else
+ cfg->max = (V4L2_MPEG_VIDEO_H264_LEVEL_5_2);
+ break;
+ default:
+ break;
+ }
+}
+
+
--- /dev/null
+/*
+ * VSI V4L2 decoder entry.
+ *
+ * Copyright (c) 2019, VeriSilicon Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ * https://opensource.org/licenses/gpl-2.0.php
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include "vsi-v4l2-priv.h"
+
+static int vsi_dec_querycap(
+ struct file *file,
+ void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vsi_v4l2_dev_info *hwinfo;
+
+ pr_debug("%s", __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ hwinfo = vsiv4l2_get_hwinfo();
+ if (hwinfo->decformat == 0)
+ return -ENODEV;
+ strlcpy(cap->driver, "vsi_v4l2", sizeof("vsi_v4l2"));
+ strlcpy(cap->card, "vsi_v4l2dec", sizeof("vsi_v4l2dec"));
+ strlcpy(cap->bus_info, "platform:vsi_v4l2dec", sizeof("platform:vsi_v4l2dec"));
+
+ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int vsi_dec_reqbufs(
+ struct file *filp,
+ void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ int ret;
+ struct vb2_queue *q;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ ret = vb2_reqbufs(q, p);
+ pr_debug("ctx %lx:%s:%d ask for %d buffer, got %d:%d", ctx->ctxid, __func__, p->type, p->count, q->num_buffers, ret);
+ if (ret == 0) {
+ print_queinfo(q);
+ if (p->count == 0 && binputqueue(p->type)) {
+ p->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP | V4L2_BUF_CAP_SUPPORTS_USERPTR | V4L2_BUF_CAP_SUPPORTS_DMABUF;
+ //ctx->status = VSI_STATUS_INIT;
+ }
+ }
+ return ret;
+}
+
+/*choose input source of index*/
+static int vsi_dec_s_input(struct file *file, void *priv, unsigned int index)
+{
+ pr_debug("%s", __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ return 0;
+}
+
+static int vsi_dec_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+
+ pr_debug("%s:%d:%d", __func__, parm->parm.output.timeperframe.numerator,
+ parm->parm.output.timeperframe.denominator);
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(parm->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(parm->type))
+ ctx->mediacfg.outputparam = parm->parm.output;
+ else
+ ctx->mediacfg.capparam = parm->parm.capture;
+ return 0;
+}
+
+static int vsi_dec_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+
+ pr_debug("%s:%d", __func__, parm->type);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(parm->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(parm->type))
+ parm->parm.output = ctx->mediacfg.outputparam;
+ else
+ parm->parm.capture = ctx->mediacfg.capparam;
+ return 0;
+}
+
+static int vsi_dec_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+
+ pr_debug("%lx:%s", ctx->ctxid, __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+
+ return vsiv4l2_getfmt(ctx, f);
+}
+
+static int vsi_dec_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+
+ pr_debug("%lx:%s:%d:%d:%x", ctx->ctxid, __func__,
+ f->fmt.pix_mp.width, f->fmt.pix_mp.height, f->fmt.pix_mp.pixelformat);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+
+ return vsiv4l2_setfmt(ctx, f);
+}
+
+static int vsi_dec_querybuf(
+ struct file *filp,
+ void *priv,
+ struct v4l2_buffer *buf)
+{
+ int ret;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ struct vb2_queue *q;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(buf->type, ctx->flag))
+ return -EINVAL;
+ if (binputqueue(buf->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ pr_debug("%s::%lx:%d:%d:%d", __func__, ctx->flag, buf->type, q->type, buf->index);
+ ret = vb2_querybuf(q, buf);
+ if (buf->memory == V4L2_MEMORY_MMAP) {
+ if (ret == 0 && q == &ctx->output_que)
+ buf->m.offset += OUTF_BASE;
+ }
+ return ret;
+}
+
+static int vsi_dec_qbuf(struct file *filp, void *priv, struct v4l2_buffer *buf)
+{
+ int ret;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ struct video_device *vdev = ctx->dev->vdec;
+
+ pr_debug("%lx:%s:%d:%d:%d", ctx->ctxid, __func__, buf->type, buf->index, buf->bytesused);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(buf->type, ctx->flag))
+ return -EINVAL;
+ if (binputqueue(buf->type) && buf->bytesused == 0 &&
+ ctx->status == DEC_STATUS_DECODING) {
+ if (test_and_clear_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag)) {
+ ctx->status = DEC_STATUS_DRAINING;
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_CMD_STOP, NULL);
+ } else
+ ret = 0;
+ return ret;
+ }
+ if (!binputqueue(buf->type)) {
+ ctx->outbuflen[buf->index] = buf->length;
+ ret = vb2_qbuf(&ctx->output_que, vdev->v4l2_dev->mdev, buf);
+ } else {
+ ctx->inbuflen[buf->index] = buf->length;
+ ctx->inbufbytes[buf->index] = buf->bytesused;
+ ret = vb2_qbuf(&ctx->input_que, vdev->v4l2_dev->mdev, buf);
+ }
+ if (ret == 0 && ctx->status == VSI_STATUS_INIT &&
+ ctx->input_que.queued_count >= ctx->input_que.min_buffers_needed
+ && ctx->input_que.streaming) {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMON_OUTPUT, NULL);
+ if (ret == 0)
+ ctx->status = DEC_STATUS_DECODING;
+ }
+ return ret;
+}
+
+static int vsi_dec_streamon(struct file *filp, void *priv, enum v4l2_buf_type type)
+{
+ int ret = 0;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(type, ctx->flag))
+ return -EINVAL;
+
+ if (!binputqueue(type)) {
+ ret = vb2_streamon(&ctx->output_que, type);
+ if (ret == 0) {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMON_CAPTURE, NULL);
+ if (ret == 0) {
+ if (ctx->status == DEC_STATUS_DRAINING)
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_CMD_STOP, NULL);
+ else
+ ctx->status = DEC_STATUS_DECODING;
+ }
+ }
+ printbufinfo(&ctx->output_que);
+ } else {
+ ret = vb2_streamon(&ctx->input_que, type);
+ if (ret == 0 && ctx->input_que.queued_count >= ctx->input_que.min_buffers_needed)
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMON_OUTPUT, NULL);
+ printbufinfo(&ctx->input_que);
+ }
+ pr_debug("%lx:%s:%d:%d:%d", ctx->ctxid, __func__, ctx->input_que.streaming, ctx->output_que.streaming, ret);
+
+ return ret;
+}
+
+static int vsi_checkctx_srcbuf(struct vsi_v4l2_ctx *ctx)
+{
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return 1;
+ if (ctx->queued_srcnum == 0 || ctx->error < 0)
+ ret = 1;
+ mutex_unlock(&ctx->ctxlock);
+ return ret;
+}
+
+static int vsi_dec_streamoff(
+ struct file *file,
+ void *priv,
+ enum v4l2_buf_type type)
+{
+ int ret;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(priv);
+ struct vb2_queue *q;
+ enum v4l2_buf_type otype;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (ctx->error < 0)
+ return -EFAULT;
+ if (!isvalidtype(type, ctx->flag))
+ return -EINVAL;
+
+ otype = (type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? V4L2_BUF_TYPE_VIDEO_OUTPUT :
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (binputqueue(type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+
+ if (q->streaming == 0 && ctx->status == VSI_STATUS_INIT)
+ return 0;
+
+ if (binputqueue(type)) {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMOFF_OUTPUT, NULL);
+ ctx->status = DEC_STATUS_SEEK;
+ } else {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMOFF_CAPTURE, NULL);
+ ctx->status = DEC_STATUS_STOPPED;
+ }
+ if (binputqueue(type)) {
+ if (!(test_bit(CTX_FLAG_ENDOFSTRM_BIT, &ctx->flag))) {
+ if (wait_event_interruptible(ctx->retbuf_queue,
+ vsi_checkctx_srcbuf(ctx) != 0))
+ return -ERESTARTSYS;
+ } else {
+ clear_bit(CTX_FLAG_ENDOFSTRM_BIT, &ctx->flag);
+ clear_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag);
+ }
+ } else {
+ ctx->buffed_capnum = 0;
+ ctx->buffed_cropcapnum = 0;
+ }
+ return_all_buffers(q, VB2_BUF_STATE_DONE, 1);
+ ret = vb2_streamoff(q, type);
+ pr_debug("%lx:%s:%d:%d", ctx->ctxid, __func__, type, ret);
+ return ret;
+}
+
+static int vsi_dec_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int ret = 0;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vb2_queue *q;
+ struct vb2_buffer *vb;
+ struct vsi_vpu_buf *vsibuf;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+
+ printbufinfo(q);
+ ret = vb2_dqbuf(q, p, file->f_flags & O_NONBLOCK);
+ if (ctx->status == DEC_STATUS_STOPPED) {
+ p->bytesused = 0;
+ return -EPIPE;
+ }
+ if (ret == 0) {
+ vb = q->bufs[p->index];
+ vsibuf = vb_to_vsibuf(vb);
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return -EBUSY;
+ list_del(&vsibuf->list);
+ if (!binputqueue(p->type)) {
+ ctx->buffed_capnum--;
+ ctx->buffed_cropcapnum--;
+ }
+ mutex_unlock(&ctx->ctxlock);
+ if (ctx->status != DEC_STATUS_ENDSTREAM &&
+ !(test_bit(CTX_FLAG_ENDOFSTRM_BIT, &ctx->flag)) &&
+ p->bytesused == 0)
+ return -EAGAIN;
+ }
+ if (!binputqueue(p->type)) {
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+
+ if (p->bytesused == 0 && (ctx->status == DEC_STATUS_ENDSTREAM || test_bit(CTX_FLAG_ENDOFSTRM_BIT, &ctx->flag))) {
+ p->flags |= V4L2_BUF_FLAG_LAST;
+ ctx->status = DEC_STATUS_STOPPED;
+ clear_bit(CTX_FLAG_ENDOFSTRM_BIT, &ctx->flag);
+ } else if (ctx->status == DEC_STATUS_RESCHANGE && ctx->buffed_capnum == 0) {
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+ pcfg->decparams.dec_info.dec_info = pcfg->decparams_bkup.dec_info.dec_info;
+ pcfg->decparams.dec_info.io_buffer.srcwidth = pcfg->decparams_bkup.io_buffer.srcwidth;
+ pcfg->decparams.dec_info.io_buffer.srcheight = pcfg->decparams_bkup.io_buffer.srcheight;
+ pcfg->decparams.dec_info.io_buffer.output_width = pcfg->decparams_bkup.io_buffer.output_width;
+ pcfg->decparams.dec_info.io_buffer.output_height = pcfg->decparams_bkup.io_buffer.output_height;
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ } else if (test_bit(CTX_FLAG_CROPCHANGE_BIT, &ctx->flag) && ctx->buffed_cropcapnum == 0) {
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_CROPCHANGE,
+ };
+ pcfg->decparams.dec_info.dec_info = pcfg->decparams_bkup.dec_info.dec_info;
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ clear_bit(CTX_FLAG_CROPCHANGE_BIT, &ctx->flag);
+ }
+ p->field = V4L2_FIELD_NONE;
+ }
+ pr_debug("%lx:%s:%d:%d:%x:%d", ctx->ctxid, __func__, p->type, p->index, p->flags, p->bytesused);
+ return ret;
+}
+
+static int vsi_dec_prepare_buf(
+ struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vb2_queue *q;
+ struct video_device *vdev = ctx->dev->vdec;
+
+ pr_debug("%s", __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ return vb2_prepare_buf(q, vdev->v4l2_dev->mdev, p);
+}
+
+static int vsi_dec_expbuf(
+ struct file *file,
+ void *priv,
+ struct v4l2_exportbuffer *p)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vb2_queue *q;
+
+ pr_debug("%s", __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ return vb2_expbuf(q, p);
+}
+
+static int vsi_dec_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+
+ pr_debug("%s", __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+
+ if (vsi_find_format(ctx, f) == NULL)
+ return -EINVAL;
+ dec_getvui(f, &ctx->mediacfg.decparams.dec_info.dec_info);
+ return 0;
+}
+
+static int vsi_dec_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vsi_video_fmt *pfmt;
+ int braw = brawfmt(ctx->flag, f->type);
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+
+ pfmt = vsi_get_format_dec(f->index, braw, ctx);
+ if (pfmt == NULL)
+ return -EINVAL;
+
+ if (pfmt->name && strlen(pfmt->name))
+ strlcpy(f->description, pfmt->name, strlen(pfmt->name) + 1);
+ f->pixelformat = pfmt->fourcc;
+ f->flags = pfmt->flag;
+ pr_debug("%s:%d:%d:%x", __func__, f->index, f->type, pfmt->fourcc);
+ return 0;
+}
+
+static int vsi_dec_set_selection(struct file *file, void *prv, struct v4l2_selection *s)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+
+ //NXP has no PP, so any crop like setting won't work for decoder
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ pcfg->decparams.dec_info.dec_info.visible_rect.left = s->r.left;
+ pcfg->decparams.dec_info.dec_info.visible_rect.top = s->r.top;
+ pcfg->decparams.dec_info.dec_info.visible_rect.width = s->r.width;
+ pcfg->decparams.dec_info.dec_info.visible_rect.height = s->r.height;
+
+ pr_debug("%lx:%s", ctx->ctxid, __func__);
+ return 0;
+}
+
+static int vsi_dec_get_selection(struct file *file, void *prv, struct v4l2_selection *s)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ s->r.left = pcfg->decparams.dec_info.dec_info.visible_rect.left;
+ s->r.top = pcfg->decparams.dec_info.dec_info.visible_rect.top;
+ s->r.width = pcfg->decparams.dec_info.dec_info.visible_rect.width;
+ s->r.height = pcfg->decparams.dec_info.dec_info.visible_rect.height;
+
+ pr_debug("%lx:%s: %d,%d,%d,%d", ctx->ctxid, __func__, s->r.left, s->r.top, s->r.width, s->r.height);
+
+ return 0;
+}
+
+static int vsi_dec_subscribe_event(
+ struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ int ret;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ ret = v4l2_event_subscribe(fh, sub, 0, NULL); //&v4l2_ctrl_sub_ev_ops);
+ pr_debug("%s:%d", __func__, ret);
+ return ret;
+}
+
+int vsi_dec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ //u32 flag = cmd->flags;
+ int ret = -EBUSY;
+
+ pr_debug("%lx:%s:%d", ctx->ctxid, __func__, cmd->cmd);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ switch (cmd->cmd) {
+ case V4L2_DEC_CMD_STOP:
+ set_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag);
+ if (ctx->status == DEC_STATUS_DECODING) {
+ ctx->status = DEC_STATUS_DRAINING;
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_CMD_STOP, NULL);
+ } else if ((ctx->status == VSI_STATUS_INIT && !test_bit(CTX_FLAG_DAEMONLIVE_BIT, &ctx->flag)) ||
+ ctx->status == DEC_STATUS_STOPPED) {
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_EOS,
+ };
+ clear_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag);
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ ret = 0;
+ }
+ break;
+ case V4L2_DEC_CMD_START:
+ if (ctx->status == DEC_STATUS_STOPPED) {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_CMD_START, cmd);
+ if (ret == 0)
+ ctx->status = DEC_STATUS_DECODING;
+ }
+ break;
+ case V4L2_DEC_CMD_RESET:
+ vsi_v4l2_reset_ctx(ctx);
+ break;
+ case V4L2_DEC_CMD_PAUSE:
+ case V4L2_DEC_CMD_RESUME:
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+static const struct v4l2_ioctl_ops vsi_dec_ioctl = {
+ .vidioc_querycap = vsi_dec_querycap,
+ .vidioc_reqbufs = vsi_dec_reqbufs,
+ .vidioc_prepare_buf = vsi_dec_prepare_buf,
+ //create_buf can be provided now since we don't know buf type in param
+ .vidioc_querybuf = vsi_dec_querybuf,
+ .vidioc_qbuf = vsi_dec_qbuf,
+ .vidioc_dqbuf = vsi_dec_dqbuf,
+ .vidioc_streamon = vsi_dec_streamon,
+ .vidioc_streamoff = vsi_dec_streamoff,
+ .vidioc_s_input = vsi_dec_s_input,
+ .vidioc_s_parm = vsi_dec_s_parm,
+ .vidioc_g_parm = vsi_dec_g_parm,
+ .vidioc_g_fmt_vid_cap = vsi_dec_g_fmt,
+ //.vidioc_g_fmt_vid_cap_mplane = vsi_dec_g_fmt,
+ .vidioc_s_fmt_vid_cap = vsi_dec_s_fmt,
+ //.vidioc_s_fmt_vid_cap_mplane = vsi_dec_s_fmt,
+ .vidioc_expbuf = vsi_dec_expbuf, //this is used to export MMAP ptr as prime fd to user space app
+
+ .vidioc_g_fmt_vid_out = vsi_dec_g_fmt,
+ //.vidioc_g_fmt_vid_out_mplane = vsi_dec_g_fmt,
+ .vidioc_s_fmt_vid_out = vsi_dec_s_fmt,
+ //.vidioc_s_fmt_vid_out_mplane = vsi_dec_s_fmt,
+ .vidioc_try_fmt_vid_cap = vsi_dec_try_fmt,
+ //.vidioc_try_fmt_vid_cap_mplane = vsi_dec_try_fmt,
+ .vidioc_try_fmt_vid_out = vsi_dec_try_fmt,
+ //.vidioc_try_fmt_vid_out_mplane = vsi_dec_try_fmt,
+
+ .vidioc_enum_fmt_vid_cap = vsi_dec_enum_fmt,
+ .vidioc_enum_fmt_vid_out = vsi_dec_enum_fmt,
+
+ .vidioc_g_fmt_vid_out = vsi_dec_g_fmt,
+ //.vidioc_g_fmt_vid_out_mplane = vsi_dec_g_fmt,
+
+ .vidioc_s_selection = vsi_dec_set_selection, //VIDIOC_S_SELECTION, VIDIOC_S_CROP
+ .vidioc_g_selection = vsi_dec_get_selection, //VIDIOC_G_SELECTION, VIDIOC_G_CROP
+
+ .vidioc_subscribe_event = vsi_dec_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ .vidioc_decoder_cmd = vsi_dec_decoder_cmd,
+};
+
+/*setup buffer information before real allocation*/
+static int vsi_dec_queue_setup(
+ struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ int i;
+
+ vsiv4l2_buffer_config(ctx, vq->type, nbuffers, nplanes, sizes);
+ pr_debug("%s:%d,%d,%d", __func__, *nbuffers, *nplanes, sizes[0]);
+
+ for (i = 0; i < *nplanes; i++)
+ alloc_devs[i] = ctx->dev->dev;
+ return 0;
+}
+
+static void vsi_dec_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct vsi_vpu_buf *vsibuf;
+ int ret;
+
+ pr_debug("%s", __func__);
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return;
+ vsibuf = vb_to_vsibuf(vb);
+ if (!binputqueue(vq->type))
+ list_add_tail(&vsibuf->list, &ctx->output_list);
+ else {
+ list_add_tail(&vsibuf->list, &ctx->input_list);
+ ctx->queued_srcnum++;
+ }
+ mutex_unlock(&ctx->ctxlock);
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_BUF_RDY, vb);
+ if (ret != 0)
+ ctx->error = ret;
+}
+
+static int vsi_dec_buf_init(struct vb2_buffer *vb)
+{
+ pr_debug("%s:%d", __func__, vb->index);
+ return 0;
+}
+
+static int vsi_dec_buf_prepare(struct vb2_buffer *vb)
+{
+ return 0;
+}
+
+static int vsi_dec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ pr_debug("%s:%d", __func__, q->type);
+
+ return 0;
+}
+static void vsi_dec_stop_streaming(struct vb2_queue *vq)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct list_head *plist;
+
+ if (binputqueue(vq->type))
+ plist = &ctx->input_list;
+ else
+ plist = &ctx->output_list;
+ pr_debug("%s:%d:%d", __func__, vq->type, list_empty(plist));
+ //return_all_buffers(vq, VB2_BUF_STATE_ERROR);
+}
+
+static void vsi_dec_buf_finish(struct vb2_buffer *vb)
+{
+ pr_debug("%s", __func__);
+}
+
+static void vsi_dec_buf_cleanup(struct vb2_buffer *vb)
+{
+ //struct vb2_queue *vq = vb->vb2_queue;
+ //struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ pr_debug("%s", __func__);
+ //ctx->dst_bufs[vb->index] = NULL;
+}
+
+static void vsi_dec_buf_wait_finish(struct vb2_queue *vq)
+{
+ vb2_ops_wait_finish(vq);
+ pr_debug("%s\n", __func__);
+}
+
+static void vsi_dec_buf_wait_prepare(struct vb2_queue *vq)
+{
+ int empty = list_empty(&vq->done_list);
+
+ pr_debug("%s:%d", __func__, empty);
+ vb2_ops_wait_prepare(vq);
+}
+
+static struct vb2_ops vsi_dec_qops = {
+ .queue_setup = vsi_dec_queue_setup,
+ .wait_prepare = vsi_dec_buf_wait_prepare, /*these two are just mutex protection for done_que*/
+ .wait_finish = vsi_dec_buf_wait_finish,
+ .buf_init = vsi_dec_buf_init,
+ .buf_prepare = vsi_dec_buf_prepare,
+ .buf_finish = vsi_dec_buf_finish,
+ .buf_cleanup = vsi_dec_buf_cleanup,
+ .start_streaming = vsi_dec_start_streaming,
+ .stop_streaming = vsi_dec_stop_streaming,
+ .buf_queue = vsi_dec_buf_queue,
+ //fill_user_buffer
+ //int (*buf_out_validate)(struct vb2_buffer *vb);
+ //void (*buf_request_complete)(struct vb2_buffer *vb);
+};
+
+static int v4l2_dec_open(struct file *filp)
+{
+ //struct video_device *vdev = video_devdata(filp);
+ struct vsi_v4l2_device *dev = video_drvdata(filp);
+ struct vsi_v4l2_ctx *ctx = NULL;
+ struct vb2_queue *q;
+ int ret = 0;
+ struct v4l2_fh *vfh;
+ pid_t pid;
+
+ /* Allocate memory for context */
+ //fh->video_devdata = struct video_device, struct video_device->video_drvdata = struct vsi_v4l2_device
+ if (vsi_v4l2_addinstance(&pid) < 0)
+ return -EBUSY;
+
+ ctx = vsi_create_ctx();
+ if (ctx == NULL) {
+ vsi_v4l2_quitinstance();
+ return -ENOMEM;
+ }
+
+ v4l2_fh_init(&ctx->fh, video_devdata(filp));
+ filp->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ ctx->dev = dev;
+ mutex_init(&ctx->ctxlock);
+ ctx->flag = CTX_FLAG_DEC;
+ set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+
+ ctx->frameidx = 0;
+ q = &ctx->input_que;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->min_buffers_needed = 1;
+ q->drv_priv = &ctx->fh;
+ q->lock = &ctx->ctxlock;
+ q->buf_struct_size = sizeof(struct vsi_vpu_buf); //used to alloc mem control structures in reqbuf
+ q->ops = &vsi_dec_qops; /*it might be used to identify input and output */
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->memory = VB2_MEMORY_UNKNOWN;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ INIT_LIST_HEAD(&ctx->input_list);
+ ret = vb2_queue_init(q);
+ /*q->buf_ops = &v4l2_buf_ops is set here*/
+ if (ret)
+ goto err_enc_dec_exit;
+
+ q = &ctx->output_que;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = &ctx->fh;
+ q->lock = &ctx->ctxlock;
+ q->buf_struct_size = sizeof(struct vsi_vpu_buf);
+ q->ops = &vsi_dec_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->memory = VB2_MEMORY_UNKNOWN;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ q->min_buffers_needed = 1;
+ INIT_LIST_HEAD(&ctx->output_list);
+ ret = vb2_queue_init(q);
+ if (ret) {
+ vb2_queue_release(&ctx->input_que);
+ goto err_enc_dec_exit;
+ }
+ vsiv4l2_initcfg(ctx);
+ vsi_setup_ctrls(&ctx->ctrlhdl);
+ vfh = (struct v4l2_fh *)filp->private_data;
+ vfh->ctrl_handler = &ctx->ctrlhdl;
+ atomic_set(&ctx->srcframen, 0);
+ atomic_set(&ctx->dstframen, 0);
+ ctx->status = VSI_STATUS_INIT;
+
+ //dev->vdev->queue = q;
+ //single queue is used for v4l2 default ops such as ioctl, read, write and poll
+ //If we wanna manage queue by ourselves, leave it null and don't use default v4l2 ioctl/read/write/poll interfaces.
+
+ return 0;
+
+err_enc_dec_exit:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ vsi_remove_ctx(ctx);
+ kfree(ctx);
+ vsi_v4l2_quitinstance();
+ return ret;
+}
+
+static int v4l2_dec_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int ret;
+
+ if (offset < OUTF_BASE) {
+ ret = vb2_mmap(&ctx->input_que, vma);
+ } else {
+ vma->vm_pgoff -= (OUTF_BASE >> PAGE_SHIFT);
+ offset -= OUTF_BASE;
+ ret = vb2_mmap(&ctx->output_que, vma);
+ }
+ return ret;
+}
+
+static __poll_t vsi_dec_poll(struct file *file, poll_table *wait)
+{
+ __poll_t res = 0;
+ __poll_t ret = 0;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ int dstn = atomic_read(&ctx->dstframen);
+ int srcn = atomic_read(&ctx->srcframen);
+
+ pr_debug("%s:%x:%d:%d", __func__, wait->_key,
+ ctx->output_que.streaming, ctx->input_que.streaming);
+ if (!vsi_v4l2_daemonalive())
+ return POLLERR;
+ if (ctx->status == DEC_STATUS_SEEK) {
+ ret = (POLLIN | POLLRDNORM);
+ if (ctx->error < 0)
+ ret |= POLLERR;
+ return ret;
+ }
+ if (vb2_is_streaming(&ctx->output_que))
+ res = vb2_poll(&ctx->output_que, file, wait);
+ res |= vb2_poll(&ctx->input_que, file, wait);
+
+ if (res & EPOLLERR)
+ ret |= POLLERR;
+ if (res & EPOLLPRI)
+ ret |= POLLPRI;
+ if (res & EPOLLIN)
+ ret |= POLLIN | POLLRDNORM;
+ if (res & EPOLLOUT)
+ ret |= POLLOUT | POLLWRNORM;
+ /*recheck for poll hang*/
+ if (ret == 0) {
+ if (dstn != atomic_read(&ctx->dstframen))
+ res = vb2_poll(&ctx->output_que, file, wait);
+ if (srcn != atomic_read(&ctx->srcframen))
+ res |= vb2_poll(&ctx->input_que, file, wait);
+ if (res & EPOLLERR)
+ ret |= POLLERR;
+ if (res & EPOLLPRI)
+ ret |= POLLPRI;
+ if (res & EPOLLIN)
+ ret |= POLLIN | POLLRDNORM;
+ if (res & EPOLLOUT)
+ ret |= POLLOUT | POLLWRNORM;
+ }
+ if (ctx->error < 0)
+ ret |= POLLERR;
+ pr_debug("poll out %x", ret);
+ return ret;
+}
+
+static const struct v4l2_file_operations v4l2_dec_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_dec_open,
+ .release = v4l2_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_dec_mmap,
+ .poll = vsi_dec_poll,
+};
+
+struct video_device *v4l2_probe_dec(struct platform_device *pdev, struct vsi_v4l2_device *vpu)
+{
+ struct video_device *vdec;
+ int ret = 0;
+
+ pr_debug("%s", __func__);
+
+ vdec = video_device_alloc();
+ if (!vdec) {
+ v4l2_err(&vpu->v4l2_dev, "Failed to allocate dec device\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ vdec->fops = &v4l2_dec_fops;
+ vdec->ioctl_ops = &vsi_dec_ioctl;
+ vdec->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ vdec->release = video_device_release;
+ vdec->lock = &vpu->lock;
+ vdec->v4l2_dev = &vpu->v4l2_dev;
+ vdec->vfl_dir = VFL_DIR_M2M;
+ vdec->vfl_type = VSI_DEVTYPE;
+ vpu->vdec = vdec;
+ vdec->queue = NULL;
+
+ video_set_drvdata(vdec, vpu);
+
+ ret = video_register_device(vdec, VSI_DEVTYPE, 0);
+ if (ret) {
+ v4l2_err(&vpu->v4l2_dev, "Failed to register dec device\n");
+ video_device_release(vdec);
+ goto err;
+ }
+ return vdec;
+
+err:
+ return NULL;
+}
+
+void v4l2_release_dec(struct video_device *vdec)
+{
+ video_unregister_device(vdec);
+}
+
--- /dev/null
+/*
+ * VSI V4L2 encoder entry.
+ *
+ * Copyright (c) 2019, VeriSilicon Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ * https://opensource.org/licenses/gpl-2.0.php
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include "vsi-v4l2-priv.h"
+
+static int vsi_enc_querycap(
+ struct file *file,
+ void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vsi_v4l2_dev_info *hwinfo;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ hwinfo = vsiv4l2_get_hwinfo();
+ if (hwinfo->encformat == 0)
+ return -ENODEV;
+ pr_debug("%s", __func__);
+ strlcpy(cap->driver, "vsi_v4l2", sizeof("vsi_v4l2"));
+ strlcpy(cap->card, "vsi_v4l2enc", sizeof("vsi_v4l2enc"));
+ strlcpy(cap->bus_info, "platform:vsi_v4l2enc", sizeof("platform:vsi_v4l2enc"));
+
+ cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int vsi_enc_reqbufs(
+ struct file *filp,
+ void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ int ret;
+ struct vb2_queue *q;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ ret = vb2_reqbufs(q, p);
+ pr_debug("%s:%d ask for %d buffer, got %d:%d:%d", __func__, p->type, p->count, q->num_buffers, ret, ctx->status);
+ return ret;
+}
+
+/*choose input source of index*/
+static int vsi_enc_s_input(struct file *file, void *priv, unsigned int index)
+{
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+
+ pr_debug("%s", __func__);
+ return 0;
+}
+
+static int vsi_enc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+
+ pr_debug("%s:%d:%d", __func__, parm->parm.output.timeperframe.numerator,
+ parm->parm.output.timeperframe.denominator);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(parm->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(parm->type)) {
+ ctx->mediacfg.encparams.general.inputRateNumer = parm->parm.output.timeperframe.denominator;
+ ctx->mediacfg.encparams.general.inputRateDenom = parm->parm.output.timeperframe.numerator;
+ ctx->mediacfg.outputparam = parm->parm.output;
+ } else {
+ ctx->mediacfg.encparams.general.outputRateNumer = parm->parm.capture.timeperframe.denominator;
+ ctx->mediacfg.encparams.general.outputRateDenom = parm->parm.capture.timeperframe.numerator;
+ ctx->mediacfg.capparam = parm->parm.capture;
+ }
+ set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+ return 0;
+}
+
+static int vsi_enc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+
+ pr_debug("%s:%d", __func__, parm->type);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(parm->type, ctx->flag))
+ return -EINVAL;
+ if (binputqueue(parm->type))
+ parm->parm.output = ctx->mediacfg.outputparam;
+ else
+ parm->parm.capture = ctx->mediacfg.capparam;
+ return 0;
+}
+
+static int vsi_enc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+
+ pr_debug("%s:%d", __func__, f->type);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+ return vsiv4l2_getfmt(ctx, f);
+}
+
+static int vsi_enc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ int ret;
+
+ pr_debug("%s:%d:%d:%x", __func__, f->fmt.pix_mp.width, f->fmt.pix_mp.height, f->fmt.pix_mp.pixelformat);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+ ret = vsiv4l2_setfmt(ctx, f);
+ set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+ return ret;
+}
+
+static int vsi_enc_querybuf(
+ struct file *filp,
+ void *priv,
+ struct v4l2_buffer *buf)
+{
+ int ret;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ struct vb2_queue *q;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(buf->type, ctx->flag))
+ return -EINVAL;
+ if (binputqueue(buf->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ pr_debug("%s::%ld:%d:%d:%d", __func__, ctx->flag, buf->type, q->type, buf->index);
+ ret = vb2_querybuf(q, buf);
+ if (buf->memory == V4L2_MEMORY_MMAP) {
+ if (ret == 0 && q == &ctx->output_que)
+ buf->m.planes[0].m.mem_offset += OUTF_BASE;
+ }
+
+ return ret;
+}
+
+static int vsi_enc_trystartenc(struct vsi_v4l2_ctx *ctx)
+{
+ int ret = 0;
+ struct vsi_queued_buf *buf, *node;
+ struct video_device *vdev = ctx->dev->venc;
+
+ if (ctx->input_que.streaming && ctx->output_que.streaming) {
+ if (!list_empty(&ctx->queued_list)) {
+ list_for_each_entry_safe(buf, node, &ctx->queued_list, list) {
+ if (!binputqueue(buf->qb.type))
+ ret = vb2_qbuf(&ctx->output_que, vdev->v4l2_dev->mdev, &buf->qb);
+ else
+ ret = vb2_qbuf(&ctx->input_que, vdev->v4l2_dev->mdev, &buf->qb);
+ list_del(&buf->list);
+ vfree(buf);
+ }
+ }
+ if ((ctx->status == VSI_STATUS_INIT ||
+ ctx->status == ENC_STATUS_STOPPED ||
+ ctx->status == ENC_STATUS_STOPPED_BYUSR ||
+ ctx->status == ENC_STATUS_RESET) &&
+ ctx->input_que.queued_count >= ctx->input_que.min_buffers_needed &&
+ ctx->output_que.queued_count >= ctx->output_que.min_buffers_needed) {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMON, NULL);
+ if (ret == 0)
+ ctx->status = ENC_STATUS_ENCODING;
+ }
+ }
+ return ret;
+}
+
+static int bufto_queuelist(struct vsi_v4l2_ctx *ctx, struct v4l2_buffer *buf)
+{
+ struct vsi_queued_buf *qb;
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+ int i, planeno = (binputqueue(buf->type) ? pcfg->srcplanes : pcfg->dstplanes);
+
+ qb = vmalloc(sizeof(struct vsi_queued_buf));
+ if (qb == NULL)
+ return -ENOMEM;
+ qb->qb = *buf;
+ for (i = 0; i < planeno; i++)
+ qb->planes[i] = buf->m.planes[i];
+ qb->qb.m.planes = qb->planes;
+ list_add_tail(&qb->list, &ctx->queued_list);
+ return 0;
+}
+
+static void clear_quelist(struct vsi_v4l2_ctx *ctx)
+{
+ struct vsi_queued_buf *buf, *node;
+
+ if (!list_empty(&ctx->queued_list)) {
+ list_for_each_entry_safe(buf, node, &ctx->queued_list, list) {
+ list_del(&buf->list);
+ vfree(buf);
+ }
+ }
+}
+
+static int vsi_enc_qbuf(struct file *filp, void *priv, struct v4l2_buffer *buf)
+{
+ int ret;
+ //struct vb2_queue *vq = vb->vb2_queue;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ struct video_device *vdev = ctx->dev->venc;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(buf->type, ctx->flag))
+ return -EINVAL;
+ if (ctx->status == ENC_STATUS_STOPPED_BYUSR)
+ return 0;
+ if ((ctx->status == ENC_STATUS_STOPPED ||
+ ctx->status == ENC_STATUS_RESET ||
+ ctx->status == ENC_STATUS_DRAINING) &&
+ binputqueue(buf->type))
+ return bufto_queuelist(ctx, buf);
+
+ if (!binputqueue(buf->type))
+ ret = vb2_qbuf(&ctx->output_que, vdev->v4l2_dev->mdev, buf);
+ else {
+ if (test_and_clear_bit(CTX_FLAG_FORCEIDR_BIT, &ctx->flag))
+ ctx->srcvbufflag[buf->index] |= FORCE_IDR;
+
+ ret = vb2_qbuf(&ctx->input_que, vdev->v4l2_dev->mdev, buf);
+ }
+ pr_debug("%s:%d:%d:%d, %d:%d, %d:%d",
+ __func__, buf->type, buf->index, buf->bytesused,
+ buf->m.planes[0].bytesused, buf->m.planes[0].length,
+ buf->m.planes[1].bytesused, buf->m.planes[1].length);
+ if (ret == 0 && ctx->status != ENC_STATUS_ENCODING &&
+ ctx->status != ENC_STATUS_STOPPED)
+ ret = vsi_enc_trystartenc(ctx);
+
+ return ret;
+}
+
+static int vsi_enc_streamon(struct file *filp, void *priv, enum v4l2_buf_type type)
+{
+ int ret = 0;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+
+ pr_debug("%s:%d:%d:%d", __func__,
+ ctx->input_que.streaming, ctx->output_que.streaming, ret);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(type, ctx->flag))
+ return -EINVAL;
+ if (ctx->status == ENC_STATUS_ENCODING)
+ return 0;
+
+ if (!binputqueue(type)) {
+ ret = vb2_streamon(&ctx->output_que, type);
+ printbufinfo(&ctx->output_que);
+ } else {
+ ret = vb2_streamon(&ctx->input_que, type);
+ printbufinfo(&ctx->input_que);
+ }
+
+ if (ret == 0) {
+ if (ctx->status == ENC_STATUS_STOPPED_BYUSR)
+ ctx->status = ENC_STATUS_STOPPED;
+ ret = vsi_enc_trystartenc(ctx);
+ }
+
+ return ret;
+}
+
+static int vsi_enc_streamoff(
+ struct file *file,
+ void *priv,
+ enum v4l2_buf_type type)
+{
+ int i, ret;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(priv);
+ struct vb2_queue *q;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(type, ctx->flag))
+ return -EINVAL;
+
+ if (ctx->status != ENC_STATUS_ENCODING &&
+ ctx->status != ENC_STATUS_DRAINING &&
+ ctx->status != ENC_STATUS_STOPPED &&
+ ctx->status != ENC_STATUS_STOPPED_BYUSR &&
+ ctx->status != ENC_STATUS_RESET)
+ return -EINVAL;
+
+ if (binputqueue(type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+
+ return_all_buffers(q, VB2_BUF_STATE_DONE, 1);
+ ret = vb2_streamoff(q, type);
+ pr_debug("%s:%d:%d", __func__, type, ret);
+ if (ret == 0) {
+ if (binputqueue(type)) {
+ ctx->status = ENC_STATUS_STOPPED_BYUSR;
+ clear_quelist(ctx);
+ clear_bit(CTX_FLAG_FORCEIDR_BIT, &ctx->flag);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++)
+ ctx->srcvbufflag[i] = 0;
+ } else
+ ctx->status = ENC_STATUS_RESET;
+ }
+ return ret;
+}
+
+static int vsi_enc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int ret = 0;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vb2_queue *q;
+ struct vb2_buffer *vb;
+ struct vsi_vpu_buf *vsibuf;
+ struct v4l2_event event = {.type = V4L2_EVENT_EOS};
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+
+ if (ctx->status == ENC_STATUS_STOPPED_BYUSR ||
+ ctx->status == ENC_STATUS_STOPPED) {
+ p->bytesused = 0;
+ return -EPIPE;
+ }
+ printbufinfo(q);
+ ret = vb2_dqbuf(q, p, file->f_flags & O_NONBLOCK);
+
+ if (ret == 0) {
+ vb = q->bufs[p->index];
+ vsibuf = vb_to_vsibuf(vb);
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return -EBUSY;
+ list_del(&vsibuf->list);
+ mutex_unlock(&ctx->ctxlock);
+ p->flags &= ~(V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
+ if (!binputqueue(p->type)) {
+ if (ctx->vbufflag[p->index] & FRAMETYPE_I)
+ p->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ else if (ctx->vbufflag[p->index] & FRAMETYPE_P)
+ p->flags |= V4L2_BUF_FLAG_PFRAME;
+ else if (ctx->vbufflag[p->index] & FRAMETYPE_B)
+ p->flags |= V4L2_BUF_FLAG_BFRAME;
+ }
+ }
+ if (!binputqueue(p->type)) {
+ if (ret == 0) {
+ if (ctx->vbufflag[p->index] & LAST_BUFFER_FLAG) {
+ p->flags |= V4L2_BUF_FLAG_LAST;
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ if (ctx->status == ENC_STATUS_DRAINING)
+ ctx->status = ENC_STATUS_STOPPED;
+ }
+ }
+ }
+ pr_debug("%s:%d:%d:%d:%x:%d", __func__, p->type, p->index, ret, p->flags, ctx->status);
+ return ret;
+}
+
+static int vsi_enc_prepare_buf(
+ struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vb2_queue *q;
+ struct video_device *vdev = ctx->dev->venc;
+
+ pr_debug("%s:%d", __func__, p->type);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ return vb2_prepare_buf(q, vdev->v4l2_dev->mdev, p);
+}
+
+static int vsi_enc_expbuf(
+ struct file *file,
+ void *priv,
+ struct v4l2_exportbuffer *p)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vb2_queue *q;
+
+ pr_debug("%s:%d", __func__, p->type);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(p->type, ctx->flag))
+ return -EINVAL;
+
+ if (binputqueue(p->type))
+ q = &ctx->input_que;
+ else
+ q = &ctx->output_que;
+ return vb2_expbuf(q, p);
+}
+
+static int vsi_enc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+
+ pr_debug("%s:%d", __func__, f->type);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+ if (vsi_find_format(ctx, f) == NULL)
+ return -EINVAL;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
+
+ return 0;
+}
+
+static int vsi_enc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vsi_video_fmt *pfmt;
+ int braw = brawfmt(ctx->flag, f->type);
+
+ pr_debug("%s:%d", __func__, f->type);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (!isvalidtype(f->type, ctx->flag))
+ return -EINVAL;
+
+ pfmt = vsi_get_format_enc(f->index, braw);
+ if (pfmt == NULL)
+ return -EINVAL;
+
+ if (pfmt->name && strlen(pfmt->name))
+ strlcpy(f->description, pfmt->name, strlen(pfmt->name) + 1);
+ f->pixelformat = pfmt->fourcc;
+ f->flags = pfmt->flag;
+ return 0;
+}
+
+static int vsi_enc_set_selection(struct file *file, void *prv, struct v4l2_selection *s)
+{
+ int ret = 0;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+
+ pr_debug("%s", __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+ if (s->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+ ret = vsiv4l2_verifycrop(s);
+ if (!ret) {
+ pcfg->encparams.general.horOffsetSrc = s->r.left;
+ pcfg->encparams.general.verOffsetSrc = s->r.top;
+ pcfg->encparams.general.width = s->r.width;
+ pcfg->encparams.general.height = s->r.height;
+ set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+ }
+ return ret;
+}
+
+static int vsi_enc_get_selection(struct file *file, void *prv, struct v4l2_selection *s)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+
+ pr_debug("%s", __func__);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = pcfg->encparams.general.horOffsetSrc;
+ s->r.top = pcfg->encparams.general.verOffsetSrc;
+ s->r.width = pcfg->encparams.general.width;
+ s->r.height = pcfg->encparams.general.height;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = pcfg->encparams.general.lumWidthSrc;
+ s->r.height = pcfg->encparams.general.lumHeightSrc;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vsi_enc_subscribe_event(
+ struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ int ret;
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ ret = v4l2_event_subscribe(fh, sub, 0, NULL); //&v4l2_ctrl_sub_ev_ops);
+ pr_debug("%s:%d", __func__, ret);
+ return ret;
+}
+
+static int vsi_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ //u32 flag = cmd->flags;
+ int ret = -EBUSY;
+
+ /// refer to https://linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/dev-encoder.html
+ pr_debug("%s:%d:%d", __func__, ctx->status, cmd->cmd);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ switch (cmd->cmd) {
+ case V4L2_ENC_CMD_STOP:
+ if (ctx->status == ENC_STATUS_ENCODING) {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_CMD_STOP, cmd);
+ if (ret == 0)
+ ctx->status = ENC_STATUS_DRAINING;
+ } else if (ctx->status != ENC_STATUS_DRAINING)
+ ret = 0;
+ break;
+ case V4L2_ENC_CMD_START:
+ if (ctx->status == ENC_STATUS_STOPPED ||
+ ctx->status == ENC_STATUS_STOPPED_BYUSR) {
+ vb2_streamon(&ctx->input_que, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ vb2_streamon(&ctx->input_que, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ ret = vsi_enc_trystartenc(ctx);
+ } else if (ctx->status == ENC_STATUS_ENCODING)
+ ret = 0;
+ break;
+ case V4L2_ENC_CMD_PAUSE:
+ case V4L2_ENC_CMD_RESUME:
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int vsi_enc_encoder_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ struct v4l2_format fmt;
+
+ pr_debug("%s:%x", __func__, fsize->pixel_format);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ if (fsize->index != 0) //only stepwise
+ return -EINVAL;
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ fmt.fmt.pix_mp.pixelformat = fsize->pixel_format;
+ if (vsi_find_format(ctx, &fmt) == NULL)
+ return -EINVAL;
+ vsi_enum_encfsize(fsize, ctx->mediacfg.outfmt_fourcc);
+ return 0;
+}
+
+/* ioctl handler */
+/* take VIDIOC_S_INPUT for example, ioctl goes to V4l2-ioctl.c.: v4l_s_input() -> V4l2-dev.c: v4l2_ioctl_ops.vidioc_s_input() */
+/* ioctl cmd could be disabled by v4l2_disable_ioctl() */
+static const struct v4l2_ioctl_ops vsi_enc_ioctl = {
+ .vidioc_querycap = vsi_enc_querycap,
+ .vidioc_reqbufs = vsi_enc_reqbufs,
+ .vidioc_prepare_buf = vsi_enc_prepare_buf,
+ //create_buf can be provided now since we don't know buf type in param
+ .vidioc_querybuf = vsi_enc_querybuf,
+ .vidioc_qbuf = vsi_enc_qbuf,
+ .vidioc_dqbuf = vsi_enc_dqbuf,
+ .vidioc_streamon = vsi_enc_streamon,
+ .vidioc_streamoff = vsi_enc_streamoff,
+ .vidioc_s_input = vsi_enc_s_input,
+ .vidioc_s_parm = vsi_enc_s_parm,
+ .vidioc_g_parm = vsi_enc_g_parm,
+ //.vidioc_g_fmt_vid_cap = vsi_enc_g_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = vsi_enc_g_fmt,
+ //.vidioc_s_fmt_vid_cap = vsi_enc_s_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vsi_enc_s_fmt,
+ .vidioc_expbuf = vsi_enc_expbuf, //this is used to export MMAP ptr as prime fd to user space app
+
+ //.vidioc_g_fmt_vid_out = vsi_enc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vsi_enc_g_fmt,
+ //.vidioc_s_fmt_vid_out = vsi_enc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vsi_enc_s_fmt,
+ //.vidioc_try_fmt_vid_cap = vsi_enc_try_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vsi_enc_try_fmt,
+ //.vidioc_try_fmt_vid_out = vsi_enc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vsi_enc_try_fmt,
+ .vidioc_enum_fmt_vid_cap = vsi_enc_enum_fmt,
+ .vidioc_enum_fmt_vid_out = vsi_enc_enum_fmt,
+
+ //.vidioc_g_fmt_vid_out = vsi_enc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vsi_enc_g_fmt,
+
+ .vidioc_s_selection = vsi_enc_set_selection, //VIDIOC_S_SELECTION, VIDIOC_S_CROP
+ .vidioc_g_selection = vsi_enc_get_selection, //VIDIOC_G_SELECTION, VIDIOC_G_CROP
+
+ .vidioc_subscribe_event = vsi_enc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ //fixme: encoder cmd stop will make streamoff not coming from ffmpeg. Maybe this is the right way to get finished, check later
+ .vidioc_encoder_cmd = vsi_enc_encoder_cmd,
+ .vidioc_enum_framesizes = vsi_enc_encoder_enum_framesizes,
+};
+
+/*setup buffer information before real allocation*/
+static int vsi_enc_queue_setup(
+ struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ int i;
+
+ vsiv4l2_buffer_config(ctx, vq->type, nbuffers, nplanes, sizes);
+ pr_debug("%s:%d,%d,%d", __func__, *nbuffers, *nplanes, sizes[0]);
+
+ for (i = 0; i < *nplanes; i++)
+ alloc_devs[i] = ctx->dev->dev;
+
+ return 0;
+}
+
+static void vsi_enc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct vsi_vpu_buf *vsibuf;
+ int ret;
+
+ pr_debug("%s", __func__);
+
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return;
+ vsibuf = vb_to_vsibuf(vb);
+ if (!binputqueue(vq->type))
+ list_add_tail(&vsibuf->list, &ctx->output_list);
+ else
+ list_add_tail(&vsibuf->list, &ctx->input_list);
+ mutex_unlock(&ctx->ctxlock);
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_BUF_RDY, vb);
+ if (ret != 0)
+ ctx->error = ret;
+}
+
+static int vsi_enc_buf_init(struct vb2_buffer *vb)
+{
+ pr_debug("%s:%d", __func__, vb->index);
+ return 0;
+}
+
+static int vsi_enc_buf_prepare(struct vb2_buffer *vb)
+{
+ /*any valid init operation on buffer vb*/
+ /*gspca and rockchip both check buffer size here*/
+ //like vb2_set_plane_payload(vb, 0, 1920*1080);
+ return 0;
+}
+
+static int vsi_enc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ pr_debug("%s:%d", __func__, q->type);
+
+ return 0;
+}
+static void vsi_enc_stop_streaming(struct vb2_queue *vq)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct list_head *plist;
+
+ if (binputqueue(vq->type))
+ plist = &ctx->input_list;
+ else
+ plist = &ctx->output_list;
+
+ pr_debug("%s:%d:%d", __func__, vq->type, list_empty(plist));
+ //return_all_buffers(vq, VB2_BUF_STATE_ERROR);
+}
+
+static void vsi_enc_buf_finish(struct vb2_buffer *vb)
+{
+ /*post process of buffer befre release*/
+ /*just for simulation*/
+ pr_debug("%s", __func__);
+}
+
+static void vsi_enc_buf_cleanup(struct vb2_buffer *vb)
+{
+ pr_debug("%s", __func__);
+}
+
+static void vsi_enc_buf_wait_finish(struct vb2_queue *vq)
+{
+ vb2_ops_wait_finish(vq);
+ pr_debug("%s\n", __func__);
+}
+
+static void vsi_enc_buf_wait_prepare(struct vb2_queue *vq)
+{
+ int empty = list_empty(&vq->done_list);
+
+ pr_debug("%s:%d", __func__, empty);
+ vb2_ops_wait_prepare(vq);
+}
+
+static struct vb2_ops vsi_enc_qops = {
+ .queue_setup = vsi_enc_queue_setup,
+ .wait_prepare = vsi_enc_buf_wait_prepare, /*these two are just mutex protection for done_que*/
+ .wait_finish = vsi_enc_buf_wait_finish,
+ .buf_init = vsi_enc_buf_init,
+ .buf_prepare = vsi_enc_buf_prepare,
+ .buf_finish = vsi_enc_buf_finish,
+ .buf_cleanup = vsi_enc_buf_cleanup,
+ .start_streaming = vsi_enc_start_streaming,
+ .stop_streaming = vsi_enc_stop_streaming,
+ .buf_queue = vsi_enc_buf_queue,
+ //fill_user_buffer
+ //int (*buf_out_validate)(struct vb2_buffer *vb);
+ //void (*buf_request_complete)(struct vb2_buffer *vb);
+};
+
+static int v4l2_enc_open(struct file *filp)
+{
+ //struct video_device *vdev = video_devdata(filp);
+ struct vsi_v4l2_device *dev = video_drvdata(filp);
+ struct vsi_v4l2_ctx *ctx = NULL;
+ struct vb2_queue *q;
+ int ret = 0;
+ struct v4l2_fh *vfh;
+ pid_t pid;
+
+ /* Allocate memory for context */
+ //fh->video_devdata = struct video_device, struct video_device->video_drvdata = struct vsi_v4l2_device
+ if (vsi_v4l2_addinstance(&pid) < 0)
+ return -EBUSY;
+
+ ctx = vsi_create_ctx();
+ if (ctx == NULL) {
+ vsi_v4l2_quitinstance();
+ return -ENOMEM;
+ }
+
+ v4l2_fh_init(&ctx->fh, video_devdata(filp));
+ filp->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ ctx->dev = dev;
+ mutex_init(&ctx->ctxlock);
+ ctx->flag = CTX_FLAG_ENC;
+ set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+
+ ctx->frameidx = 0;
+ q = &ctx->input_que;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->min_buffers_needed = MIN_FRAME_4ENC;
+ q->drv_priv = &ctx->fh;
+ q->lock = &ctx->ctxlock;
+ q->buf_struct_size = sizeof(struct vsi_vpu_buf); //used to alloc mem control structures in reqbuf
+ q->ops = &vsi_enc_qops; /*it might be used to identify input and output */
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->memory = VB2_MEMORY_UNKNOWN;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ INIT_LIST_HEAD(&ctx->input_list);
+ ret = vb2_queue_init(q);
+ /*q->buf_ops = &v4l2_buf_ops is set here*/
+ if (ret)
+ goto err_enc_dec_exit;
+
+ q = &ctx->output_que;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->min_buffers_needed = 1;
+ q->drv_priv = &ctx->fh;
+ q->lock = &ctx->ctxlock;
+ q->buf_struct_size = sizeof(struct vsi_vpu_buf);
+ q->ops = &vsi_enc_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->memory = VB2_MEMORY_UNKNOWN;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ INIT_LIST_HEAD(&ctx->output_list);
+ ret = vb2_queue_init(q);
+ if (ret) {
+ vb2_queue_release(&ctx->input_que);
+ goto err_enc_dec_exit;
+ }
+ INIT_LIST_HEAD(&ctx->queued_list);
+ vsiv4l2_initcfg(ctx);
+ vsi_setup_ctrls(&ctx->ctrlhdl);
+ vfh = (struct v4l2_fh *)filp->private_data;
+ vfh->ctrl_handler = &ctx->ctrlhdl;
+ atomic_set(&ctx->srcframen, 0);
+ atomic_set(&ctx->dstframen, 0);
+ ctx->status = VSI_STATUS_INIT;
+
+ return 0;
+
+err_enc_dec_exit:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ vsi_remove_ctx(ctx);
+ kfree(ctx);
+ vsi_v4l2_quitinstance();
+ return ret;
+}
+
+static int v4l2_enc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int ret;
+
+ if (offset < OUTF_BASE) {
+ ret = vb2_mmap(&ctx->input_que, vma);
+ } else {
+ vma->vm_pgoff -= (OUTF_BASE >> PAGE_SHIFT);
+ offset -= OUTF_BASE;
+ ret = vb2_mmap(&ctx->output_que, vma);
+ }
+ return ret;
+}
+
+static __poll_t vsi_enc_poll(struct file *file, poll_table *wait)
+{
+ __poll_t res;
+ __poll_t ret = 0;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(file->private_data);
+ int dstn = atomic_read(&ctx->dstframen);
+ int srcn = atomic_read(&ctx->srcframen);
+
+ pr_debug("%s:%x:%d:%d", __func__, wait->_key,
+ ctx->output_que.streaming, ctx->input_que.streaming);
+ if (!vsi_v4l2_daemonalive())
+ return POLLERR;
+
+ if (v4l2_event_pending(&ctx->fh)) {
+ pr_debug("poll event");
+ ret |= POLLPRI;
+ }
+
+ res = vb2_poll(&ctx->output_que, file, wait);
+ res |= vb2_poll(&ctx->input_que, file, wait);
+
+ if (res & EPOLLERR)
+ ret |= POLLERR;
+ if (res & EPOLLPRI)
+ ret |= POLLPRI;
+ if (res & EPOLLIN)
+ ret |= POLLIN | POLLRDNORM;
+ if (res & EPOLLOUT)
+ ret |= POLLOUT | POLLWRNORM;
+
+ /*recheck for poll hang*/
+ if (ret == 0) {
+ if (dstn != atomic_read(&ctx->dstframen))
+ res = vb2_poll(&ctx->output_que, file, wait);
+ if (srcn != atomic_read(&ctx->srcframen))
+ res |= vb2_poll(&ctx->input_que, file, wait);
+ if (res & EPOLLERR)
+ ret |= POLLERR;
+ if (res & EPOLLPRI)
+ ret |= POLLPRI;
+ if (res & EPOLLIN)
+ ret |= POLLIN | POLLRDNORM;
+ if (res & EPOLLOUT)
+ ret |= POLLOUT | POLLWRNORM;
+ }
+ if (ctx->error < 0)
+ ret |= POLLERR;
+
+ pr_debug("poll out %x", ret);
+ return ret;
+}
+
+static const struct v4l2_file_operations v4l2_enc_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_enc_open,
+ .release = v4l2_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_enc_mmap,
+ .poll = vsi_enc_poll,
+};
+
+struct video_device *v4l2_probe_enc(struct platform_device *pdev, struct vsi_v4l2_device *vpu)
+{
+ struct video_device *venc;
+ int ret = 0;
+
+ pr_debug("%s", __func__);
+
+ /*init video device0, encoder */
+ venc = video_device_alloc();
+ if (!venc) {
+ v4l2_err(&vpu->v4l2_dev, "Failed to allocate enc device\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ venc->fops = &v4l2_enc_fops;
+ venc->ioctl_ops = &vsi_enc_ioctl;
+ venc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ venc->release = video_device_release;
+ venc->lock = &vpu->lock;
+ venc->v4l2_dev = &vpu->v4l2_dev;
+ venc->vfl_dir = VFL_DIR_M2M;
+ venc->vfl_type = VSI_DEVTYPE;
+ venc->queue = NULL;
+
+ video_set_drvdata(venc, vpu);
+
+ ret = video_register_device(venc, VSI_DEVTYPE, 0);
+ if (ret) {
+ v4l2_err(&vpu->v4l2_dev, "Failed to register enc device\n");
+ video_device_release(venc);
+ goto err;
+ }
+
+ return venc;
+err:
+ return NULL;
+}
+
+void v4l2_release_enc(struct video_device *venc)
+{
+ video_unregister_device(venc);
+}
+
--- /dev/null
+/*
+ * VSI V4L2 kernel driver private header file.
+ *
+ * Copyright (c) 2019, VeriSilicon Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ * https://opensource.org/licenses/gpl-2.0.php
+ */
+
+#ifndef VSI_V4L2_PRIV_H
+#define VSI_V4L2_PRIV_H
+
+#include <linux/version.h>
+#include <linux/v4l2-controls.h>
+#include <linux/imx_vpu.h>
+#include "vsi-v4l2.h"
+
+#define CTX_SEQID_UPLIMT 0x7FFFFFFF
+#define CTX_ARRAY_ID(ctxid) (ctxid & 0xFFFFFFFF)
+#define CTX_SEQ_ID(ctxid) (ctxid >> 32)
+
+#define MIN_FRAME_4ENC 1
+
+#define MAX_MIN_BUFFERS_FOR_CAPTURE 16
+#define MAX_MIN_BUFFERS_FOR_OUTPUT 16
+
+#define ENC_EXTRA_HEADER_SIZE 1024
+
+#define DEFAULT_GOP_SIZE 1
+#define DEFAULT_INTRA_PIC_RATE 30
+#define DEFAULT_QP 30
+
+#if KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE
+#define VSI_DEVTYPE VFL_TYPE_GRABBER
+#else
+#define VSI_DEVTYPE VFL_TYPE_VIDEO
+#endif
+
+/* declarations */
+extern struct platform_device *gvsidev;
+extern struct idr inst_array;
+
+/*this table should be consistent with that in hevcencapi.h*/
+enum VCEncPictureType {
+ VCENC_FMT_INVALID = -1,
+ VCENC_YUV420_PLANAR = 0, /* YYYY... UUUU... VVVV... */
+ VCENC_YUV420_SEMIPLANAR = 1, /* YYYY... UVUVUV... */
+ VCENC_YUV420_SEMIPLANAR_VU = 2, /* YYYY... VUVUVU... */
+ VCENC_YUV422_INTERLEAVED_YUYV = 3, /* YUYVYUYV... */
+ VCENC_YUV422_INTERLEAVED_UYVY = 4, /* UYVYUYVY... */
+ VCENC_RGB565 = 5, /* 16-bit RGB 16bpp */
+ VCENC_BGR565 = 6, /* 16-bit RGB 16bpp */
+ VCENC_RGB555 = 7, /* 15-bit RGB 16bpp */
+ VCENC_BGR555 = 8, /* 15-bit RGB 16bpp */
+ VCENC_RGB888 = 11, /* 24-bit RGB 32bpp */
+ VCENC_BGR888 = 12, /* 24-bit BGR 32bpp */
+};
+
+enum VCEncPictureRotation {
+ VCENC_ROTATE_0 = 0,
+ VCENC_ROTATE_90R = 1, /* Rotate 90 degrees clockwise */
+ VCENC_ROTATE_90L = 2, /* Rotate 90 degrees counter-clockwise */
+ VCENC_ROTATE_180R = 3 /* Rotate 180 degrees clockwise */
+};
+
+enum VCEncProfile {
+ VCENC_HEVC_MAIN_PROFILE = 0,
+ VCENC_HEVC_MAIN_STILL_PICTURE_PROFILE = 1,
+ VCENC_HEVC_MAIN_10_PROFILE = 2,
+ /* H264 Defination*/
+ VCENC_H264_BASE_PROFILE = 9,
+ VCENC_H264_MAIN_PROFILE = 10,
+ VCENC_H264_HIGH_PROFILE = 11,
+ VCENC_H264_HIGH_10_PROFILE = 12,
+ /* AV1 Defination*/
+ VCENC_AV1_MAIN_PROFILE = 0, /*4:2:0 8/10 bit*/
+ VCENC_AV1_HIGH_PROFILE = 1,
+ VCENC_AV1_PROFESSIONAL_PROFILE = 2,
+
+ /*Vp9 Defination*/
+ VCENC_VP9_MAIN_PROFILE = 0, /*4:2:0 8 bit No SRGB*/
+ VCENC_VP9_MSRGB_PROFILE = 1, /*4:2:2 4:4:0 4:4:4 8 bit SRGB*/
+ VCENC_VP9_HIGH_PROFILE = 2, /*4:2:0 10/12 bit No SRGB*/
+ VCENC_VP9_HSRGB_PROFILE = 3, /*4:2:2 4:4:0 4:4:4 10 bit SRGB*/
+};
+
+#ifndef V4L2_MPEG_VIDEO_H264_LEVEL_5_2
+#define V4L2_MPEG_VIDEO_H264_LEVEL_5_2 16
+#endif
+/* Picture color space conversion (RGB input) for pre-processing, used for colorConversion below*/
+enum VCEncColorConversionType {
+ VCENC_RGBTOYUV_BT601 = 0, /* Color conversion of limited range[16,235] according to BT.601 */
+ VCENC_RGBTOYUV_BT709 = 1, /* Color conversion of limited range[16,235] according to BT.709 */
+ VCENC_RGBTOYUV_USER_DEFINED = 2, /* User defined color conversion */
+ VCENC_RGBTOYUV_BT2020 = 3, /* Color conversion according to BT.2020 */
+ VCENC_RGBTOYUV_BT601_FULL_RANGE = 4, /* Color conversion of full range[0,255] according to BT.601*/
+ VCENC_RGBTOYUV_BT601_LIMITED_RANGE = 5, /* Color conversion of limited range[0,219] according to BT.601*/
+ VCENC_RGBTOYUV_BT709_FULL_RANGE = 6 /* Color conversion of full range[0,255] according to BT.709*/
+};
+
+/****************************************************************/
+/* Extension of color macros copied from NXP ticket 464. In new kernel version they may be removed */
+
+#define V4L2_COLORSPACE_GENERIC_FILM (V4L2_COLORSPACE_DCI_P3+1)
+#define V4L2_COLORSPACE_ST428 (V4L2_COLORSPACE_DCI_P3+2)
+
+#define V4L2_XFER_FUNC_LINEAR (V4L2_XFER_FUNC_SMPTE2084+1)
+#define V4L2_XFER_FUNC_GAMMA22 (V4L2_XFER_FUNC_SMPTE2084+2)
+#define V4L2_XFER_FUNC_GAMMA28 (V4L2_XFER_FUNC_SMPTE2084+3)
+#define V4L2_XFER_FUNC_HLG (V4L2_XFER_FUNC_SMPTE2084+4)
+#define V4L2_XFER_FUNC_XVYCC (V4L2_XFER_FUNC_SMPTE2084+5)
+#define V4L2_XFER_FUNC_BT1361 (V4L2_XFER_FUNC_SMPTE2084+6)
+#define V4L2_XFER_FUNC_ST428 (V4L2_XFER_FUNC_SMPTE2084+7)
+
+#define V4L2_YCBCR_ENC_BT470_6M (V4L2_YCBCR_ENC_SMPTE240M+1)
+
+/****************************************************************/
+/************************ compound extension ctrl defines ***************************/
+#define VSI_V4L2_CMPTYPE_ROI (V4L2_CTRL_COMPOUND_TYPES + 100)
+#define VSI_V4L2_CMPTYPE_IPCM (V4L2_CTRL_COMPOUND_TYPES + 101)
+
+/*V4L2 status following spec*/
+enum CTX_STATUS {
+ VSI_STATUS_INIT = 0, /*init is a public state for dec and enc*/
+ ENC_STATUS_ENCODING,
+ ENC_STATUS_DRAINING,
+ ENC_STATUS_STOPPED,
+ ENC_STATUS_STOPPED_BYUSR,
+ ENC_STATUS_RESET,
+
+ DEC_STATUS_DECODING,
+ DEC_STATUS_DRAINING,
+ DEC_STATUS_STOPPED,
+ DEC_STATUS_SEEK,
+ DEC_STATUS_CAPSETUP,
+ DEC_STATUS_ENDSTREAM,
+ DEC_STATUS_RESCHANGE,
+};
+
+struct vsi_v4l2_mem_info_internal {
+ ulong size;
+ dma_addr_t busaddr;
+ void *vaddr;
+ struct page *pageaddr;
+};
+
+struct vsi_video_fmt {
+ char *name;
+ u32 fourcc; //V4L2 video format defines
+ s32 enc_fmt; //our own enc video format defines
+ s32 dec_fmt; //our own dec video format defines
+ u32 flag;
+};
+
+struct vsi_v4l2_mediacfg {
+
+ /*from ctrls setting*/
+ //unsigned int gopsize;
+ //move to encparams.specific.enc_h26x_cmd.gopSize
+ //unsigned int avcprofile;
+ //move to encparams.specific.enc_h26x_cmd.profile
+
+ /* from set format */
+ //unsigned int width;
+ //move to encparams.general.width
+ //unsigned int height;
+ //move to encparams.general.height
+ //unsigned int pixelformat;
+ //move to encparams.general.inputFormat
+ u32 field; /* enum v4l2_field */
+ u32 srcplanes;
+ u32 dstplanes;
+ u32 bytesperline; /* for padding, zero if unused */
+ u32 sizeimagesrc[VB2_MAX_PLANES]; /*this is for input plane size*/
+ u32 sizeimagedst[VB2_MAX_PLANES]; /*this is for output plane size*/
+ u32 colorspace; /* enum v4l2_colorspace */
+ u32 priv; /* depends on pixelformat */
+ u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */
+ u32 quantization; /* enum v4l2_quantization */
+ u32 xfer_func; /* enum v4l2_xfer_func */
+ u32 minbuf_4capture;
+ u32 minbuf_4output;
+ u32 multislice_mode;
+ u32 infmt_fourcc;
+ u32 outfmt_fourcc;
+ /*profiles for each format is put here instead of encparams to save some transfer data*/
+ s32 profile_h264;
+ s32 profile_hevc;
+ s32 profile_vp8;
+ s32 profile_vp9;
+
+ /*for vidioc_s/g_parm*/
+ struct v4l2_captureparm capparam;
+ struct v4l2_outputparm outputparam;
+
+ /*roi and ipcm temp info */
+ struct v4l2_enc_roi_params roiinfo;
+ struct v4l2_enc_ipcm_params ipcminfo;
+
+ /*internal storage*/
+ struct v4l2_daemon_enc_params encparams;
+ struct v4l2_daemon_dec_params decparams;
+ struct v4l2_daemon_dec_params decparams_bkup;
+};
+
+struct vsi_v4l2_device {
+ struct v4l2_device v4l2_dev;
+ struct platform_device *pdev;
+ struct device *dev;
+ struct video_device *venc;
+ struct video_device *vdec;
+ struct mutex lock;
+ struct mutex irqlock;
+};
+
+struct vsi_vpu_buf {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct vsi_queued_buf {
+ struct v4l2_buffer qb;
+ struct list_head list;
+ struct v4l2_plane planes[VB2_MAX_PLANES];
+};
+
+/*ctx flags*/
+#define CTX_FLAG_ENC (0 << 0)
+#define CTX_FLAG_DEC (1 << 0)
+
+enum {
+ CTX_FLAG_PRE_DRAINING_BIT = 1,
+ CTX_FLAG_ENDOFSTRM_BIT,
+ CTX_FLAG_CROPCHANGE_BIT,
+ CTX_FLAG_DAEMONLIVE_BIT,
+ CTX_FLAG_CONFIGUPDATE_BIT,
+ CTX_FLAG_FORCEIDR_BIT,
+ CTX_FLAG_SRCCHANGED_BIT,
+};
+
+struct vsi_v4l2_ctx {
+ struct v4l2_fh fh;
+ struct vsi_v4l2_device *dev;
+ ulong ctxid;
+ struct mutex ctxlock;
+
+ s32 status; /*hold current status*/
+ s32 error;
+ ulong flag;
+
+ struct vb2_queue input_que;
+ struct list_head input_list;
+ struct vb2_queue output_que;
+ struct list_head output_list;
+ u32 vbufflag[VIDEO_MAX_FRAME];
+ u32 srcvbufflag[VIDEO_MAX_FRAME];
+
+ u32 inbufbytes[VIDEO_MAX_FRAME];
+ u32 inbuflen[VIDEO_MAX_FRAME];
+ u32 outbuflen[VIDEO_MAX_FRAME];
+ u32 queued_srcnum;
+ u32 buffed_capnum;
+ u32 buffed_cropcapnum;
+
+ struct list_head queued_list;
+
+ struct vsi_v4l2_mediacfg mediacfg;
+
+ struct v4l2_ctrl_handler ctrlhdl;
+ wait_queue_head_t retbuf_queue;
+
+ uint64_t frameidx;
+
+ atomic_t srcframen;
+ atomic_t dstframen;
+};
+
+int vsi_setup_ctrls(struct v4l2_ctrl_handler *handler);
+int v4l2_release(struct file *filp);
+void vsi_remove_ctx(struct vsi_v4l2_ctx *ctx);
+struct vsi_v4l2_ctx *vsi_create_ctx(void);
+int vsi_v4l2_reset_ctx(struct vsi_v4l2_ctx *ctx);
+int vsi_v4l2_notify_reschange(struct vsi_v4l2_msg *pmsg);
+int vsi_v4l2_handle_warningmsg(struct vsi_v4l2_msg *pmsg);
+int vsi_v4l2_handle_cropchange(struct vsi_v4l2_msg *pmsg);
+int vsi_v4l2_bufferdone(struct vsi_v4l2_msg *pmsg);
+int vsi_v4l2_handleerror(unsigned long ctxtid, int error);
+int vsi_v4l2_handle_picconsumed(unsigned long ctxid);
+struct video_device *v4l2_probe_enc(
+ struct platform_device *pdev,
+ struct vsi_v4l2_device *vpu);
+void v4l2_release_enc(struct video_device *venc);
+struct video_device *v4l2_probe_dec(struct platform_device *pdev, struct vsi_v4l2_device *vpu);
+void v4l2_release_dec(struct video_device *vdec);
+
+u64 vsi_v4l2_getbandwidth(void);
+int vsiv4l2_initdaemon(void);
+void vsiv4l2_cleanupdaemon(void);
+int vsi_clear_daemonmsg(int instid);
+int vsiv4l2_execcmd(
+ struct vsi_v4l2_ctx *ctx,
+ enum v4l2_daemon_cmd_id id,
+ void *args);
+int vsi_v4l2_addinstance(pid_t *ppid);
+int vsi_v4l2_quitinstance(void);
+int vsi_v4l2_daemonalive(void);
+
+void dec_updatevui(struct v4l2_daemon_dec_info *src, struct v4l2_daemon_dec_info *dst);
+void dec_getvui(struct v4l2_format *v4l2fmt, struct v4l2_daemon_dec_info *decinfo);
+void vsi_enum_encfsize(struct v4l2_frmsizeenum *f, u32 pixel_format);
+void vsiv4l2_initcfg(struct vsi_v4l2_ctx *ctx);
+int get_Level(struct vsi_v4l2_ctx *ctx, int mediatype, int dir, int level);
+int vsiv4l2_setfmt(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt);
+int vsiv4l2_getfmt(struct vsi_v4l2_ctx *ctx, struct v4l2_format *fmt);
+void vsiv4l2_buffer_config(
+ struct vsi_v4l2_ctx *ctx,
+ int type,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[]
+);
+struct vsi_video_fmt *vsi_find_format(struct vsi_v4l2_ctx *ctx, struct v4l2_format *f);
+struct vsi_video_fmt *vsi_get_format_dec(int idx, int braw, struct vsi_v4l2_ctx *ctx);
+struct vsi_video_fmt *vsi_get_format_enc(int idx, int braw);
+int vsi_set_profile(struct vsi_v4l2_ctx *ctx, int type, int profile);
+int vsi_get_profile(struct vsi_v4l2_ctx *ctx, int type);
+void vsiv4l2_set_hwinfo(struct vsi_v4l2_dev_info *hwinfo);
+struct vsi_v4l2_dev_info *vsiv4l2_get_hwinfo(void);
+int vsiv4l2_setROI(struct vsi_v4l2_ctx *ctx, void *params);
+int vsiv4l2_setIPCM(struct vsi_v4l2_ctx *ctx, void *params);
+int vsiv4l2_getROIcount(void);
+int vsiv4l2_getIPCMcount(void);
+void convertROI(struct vsi_v4l2_ctx *ctx);
+void convertIPCM(struct vsi_v4l2_ctx *ctx);
+int vsiv4l2_verifycrop(struct v4l2_selection *s);
+void vsi_v4l2_update_ctrlcfg(struct v4l2_ctrl_config *cfg);
+
+static inline int isencoder(struct vsi_v4l2_ctx *ctx)
+{
+ return ((ctx->flag & CTX_FLAG_DEC) == 0);
+}
+
+static inline int isdecoder(struct vsi_v4l2_ctx *ctx)
+{
+ return ((ctx->flag & CTX_FLAG_DEC) != 0);
+}
+
+static inline int isvalidtype(int buftype, int ctxtype)
+{
+ if (((ctxtype & CTX_FLAG_DEC) == 0) &&
+ (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
+ buftype == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+ return 1;
+ if (((ctxtype & CTX_FLAG_DEC) != 0) &&
+ (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ buftype == V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return 1;
+ return 0;
+}
+
+static inline int binputqueue(int qtype)
+{
+ return V4L2_TYPE_IS_OUTPUT(qtype);
+}
+
+static inline int brawfmt(int ctxtype, int qtype)
+{
+ if ((((ctxtype & CTX_FLAG_DEC) == 0) && qtype == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ||
+ (((ctxtype & CTX_FLAG_DEC) != 0) && qtype == V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return 1;
+ return 0;
+}
+
+static inline struct vsi_vpu_buf *vb_to_vsibuf(struct vb2_buffer *vb)
+{
+ return container_of(to_vb2_v4l2_buffer(vb), struct vsi_vpu_buf, vb);
+}
+
+static inline struct vsi_v4l2_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct vsi_v4l2_ctx, fh);
+}
+
+static inline struct vsi_v4l2_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct vsi_v4l2_ctx, ctrlhdl);
+}
+
+static inline void printbufinfo(struct vb2_queue *vq)
+{
+ int i;
+ struct vb2_buffer *vb;
+ struct vsi_vpu_buf *buf, *node;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ pr_debug("#################################################");
+ pr_debug("que has %d vb2 buffers, que count = %d", vq->num_buffers, vq->queued_count);
+ for (i = 0; i < vq->num_buffers; i++) {
+ vb = vq->bufs[i];
+ pr_debug("vb2 buffer %d:num planes = %d", i, vb->num_planes);
+ }
+ pr_debug("input_list:");
+ if (!list_empty(&ctx->input_list)) {
+ list_for_each_entry_safe(buf, node, &ctx->input_list, list) {
+ pr_debug("list node %lx", (unsigned long)buf);
+ }
+ }
+ pr_debug("output_list:");
+ if (!list_empty(&ctx->output_list)) {
+ list_for_each_entry_safe(buf, node, &ctx->output_list, list) {
+ pr_debug("list node %lx", (unsigned long)buf);
+ }
+ }
+ pr_debug("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+}
+
+static inline void return_all_buffers(struct vb2_queue *vq, int status, int bRelbuf)
+{
+ int i;
+ struct vsi_vpu_buf *buf, *node;
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct list_head *plist;
+
+ pr_debug("%s", __func__);
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return;
+ if (binputqueue(vq->type))
+ plist = &ctx->input_list;
+ else
+ plist = &ctx->output_list;
+
+ for (i = 0; i < vq->num_buffers; ++i) {
+ if (vq->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(vq->bufs[i], status);
+ }
+ if (bRelbuf) {
+ list_for_each_entry_safe(buf, node, plist, list) {
+ for (i = 0; i < buf->vb.vb2_buf.num_planes; i++)
+ vb2_set_plane_payload(&buf->vb.vb2_buf, i, 0);
+ list_del(&buf->list);
+ }
+ }
+ mutex_unlock(&ctx->ctxlock);
+}
+
+static inline void print_queinfo(struct vb2_queue *q)
+{
+ int i, k;
+
+ pr_debug("got %d buffer", q->num_buffers);
+ for (i = 0; i < q->num_buffers; i++) {
+ struct vb2_buffer *buf = q->bufs[i];
+
+ pr_debug("buf %d%p has %d planes", i, buf, buf->num_planes);
+ for (k = 0; k < buf->num_planes; k++) {
+ int *data = vb2_plane_vaddr(buf, k);
+
+ pr_debug("plane %d = %lx, size = %x, offset = %x",
+ k, (unsigned long)data, buf->planes[k].length, buf->planes[k].m.offset);
+ }
+ }
+}
+
+#endif //VSI_V4L2_PRIV_H
+
--- /dev/null
+/*
+ * VSI V4L2 kernel driver main entrance.
+ *
+ * Copyright (c) 2019, VeriSilicon Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ * https://opensource.org/licenses/gpl-2.0.php
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include "vsi-v4l2-priv.h"
+
+#define DRIVER_NAME "vsiv4l2"
+
+struct platform_device *gvsidev;
+static s32 self;
+struct idr inst_array;
+static struct device *vsidaemondev;
+static struct mutex vsi_ctx_array_lock; //it only protect ctx between release from app and msg from daemon
+static ulong ctx_seqid;
+
+static void release_ctx(struct vsi_v4l2_ctx *ctx, int notifydaemon)
+{
+ int ret = 0;
+
+ if (notifydaemon == 1 && (ctx->status != VSI_STATUS_INIT || ctx->error < 0)) {
+ if (isdecoder(ctx))
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_DESTROY_DEC, NULL);
+ else
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMOFF, NULL);
+ }
+ /*vsi_vpu_buf obj is freed here, together with all buffer memory */
+ return_all_buffers(&ctx->input_que, VB2_BUF_STATE_DONE, 0);
+ return_all_buffers(&ctx->output_que, VB2_BUF_STATE_DONE, 0);
+ if (mutex_lock_interruptible(&vsi_ctx_array_lock))
+ return;
+ idr_remove(&inst_array, CTX_ARRAY_ID(ctx->ctxid));
+ mutex_unlock(&vsi_ctx_array_lock);
+ vb2_queue_release(&ctx->input_que);
+ vb2_queue_release(&ctx->output_que);
+ v4l2_ctrl_handler_free(&ctx->ctrlhdl);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ ctx = NULL;
+}
+
+void vsi_remove_ctx(struct vsi_v4l2_ctx *ctx)
+{
+ if (mutex_lock_interruptible(&vsi_ctx_array_lock))
+ return;
+ idr_remove(&inst_array, CTX_ARRAY_ID(ctx->ctxid));
+ mutex_unlock(&vsi_ctx_array_lock);
+}
+
+struct vsi_v4l2_ctx *vsi_create_ctx(void)
+{
+ struct vsi_v4l2_ctx *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+
+ if (!ctx)
+ return NULL;
+ if (mutex_lock_interruptible(&vsi_ctx_array_lock)) {
+ kfree(ctx);
+ return NULL;
+ }
+ ctx->ctxid = idr_alloc(&inst_array, (void *)ctx, 1, 0, GFP_KERNEL);
+ if (ctx->ctxid < 0) {
+ kfree(ctx);
+ ctx = NULL;
+ } else {
+ ctx_seqid++;
+ if (ctx_seqid >= CTX_SEQID_UPLIMT)
+ ctx_seqid = 1;
+ pr_debug("create ctx with %lx:%lx", ctx->ctxid, ctx_seqid);
+ ctx->ctxid |= (ctx_seqid << 32);
+ }
+ mutex_unlock(&vsi_ctx_array_lock);
+ init_waitqueue_head(&ctx->retbuf_queue);
+
+ return ctx;
+}
+
+static struct vsi_v4l2_ctx *find_ctx(unsigned long ctxid)
+{
+ unsigned long id = CTX_ARRAY_ID(ctxid);
+ unsigned long seq = CTX_SEQ_ID(ctxid);
+ struct vsi_v4l2_ctx *ctx;
+
+ if (mutex_lock_interruptible(&vsi_ctx_array_lock))
+ return NULL;
+ ctx = (struct vsi_v4l2_ctx *)idr_find(&inst_array, id);
+ mutex_unlock(&vsi_ctx_array_lock);
+ pr_debug("search for ctx %lx", ctxid);
+ if (ctx && (CTX_SEQ_ID(ctx->ctxid) == seq))
+ return ctx;
+ else
+ return NULL;
+}
+
+int vsi_v4l2_reset_ctx(struct vsi_v4l2_ctx *ctx)
+{
+ int ret = 0;
+
+ if (ctx->status != VSI_STATUS_INIT) {
+ if (isdecoder(ctx)) {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_DESTROY_DEC, NULL);
+ ctx->queued_srcnum = ctx->buffed_capnum = ctx->buffed_cropcapnum = 0;
+ wake_up_interruptible_all(&ctx->retbuf_queue);
+ ctx->flag = CTX_FLAG_DEC;
+ } else {
+ ret = vsiv4l2_execcmd(ctx, V4L2_DAEMON_VIDIOC_STREAMOFF, NULL);
+ ctx->flag = CTX_FLAG_ENC;
+ }
+ set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+ return_all_buffers(&ctx->input_que, VB2_BUF_STATE_DONE, 0);
+ return_all_buffers(&ctx->output_que, VB2_BUF_STATE_DONE, 0);
+ ctx->status = VSI_STATUS_INIT;
+ ctx->error = 0;
+ }
+ return ret;
+}
+
+int v4l2_release(struct file *filp)
+{
+ struct vsi_v4l2_ctx *ctx = fh_to_ctx(filp->private_data);
+
+ /*normal streaming end should fall here*/
+ vsi_clear_daemonmsg(CTX_ARRAY_ID(ctx->ctxid));
+ release_ctx(ctx, 1);
+ vsi_v4l2_quitinstance();
+ return 0;
+}
+
+//default ctrls are in v4l2-controls.h
+//VIDIOC_QUERYCTRL, VIDIOC_G_EXT_CTRLS/VIDIOC_S_EXT_CTRLS
+//for get and set print_control in v4l2-utils could be an example internal
+// ctrl usage is through v4l2_ctrl_find()/v4l2_ctrl_g_ctrl()/v4l2_ctrl_s_ctrl
+static int vsi_v4l2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret;
+ struct vsi_v4l2_ctx *ctx = ctrl_to_ctx(ctrl);
+
+ pr_debug("%s:%d=%d", __func__, ctrl->id, ctrl->val);
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.intraPicRate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ ret = vsi_set_profile(ctx, ctrl->id, ctrl->val);
+ return ret;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctx->mediacfg.encparams.general.bitPerSecond = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ ret = get_Level(ctx, 0, 1, ctrl->val);
+ if (ret >= 0)
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.avclevel = ret;
+ else
+ return ret;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ ret = get_Level(ctx, 1, 1, ctrl->val);
+ if (ret >= 0)
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.hevclevel = ret;
+ else
+ return ret;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMax = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpMin = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ if (ctrl->val != 0)
+ return -EINVAL;
+ /*in fact nothing to do*/
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.bFrameQpDelta = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.hrdConformance = 0;
+ else
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.hrdConformance = 1;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
+ set_bit(CTX_FLAG_FORCEIDR_BIT, &ctx->flag);
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ break;
+ case V4L2_CID_DIS_REORDER:
+ ctx->mediacfg.decparams.io_buffer.no_reordering_decoding = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ ctx->mediacfg.multislice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.sliceSize = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.picRc = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.ctbRc = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpHdrI = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:
+ ctx->mediacfg.encparams.specific.enc_h26x_cmd.qpHdrP = ctrl->val;
+ break;
+ case V4L2_CID_ROTATE:
+ switch (ctrl->val) {
+ case 90:
+ ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_90L;
+ break;
+ case 180:
+ ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_180R;
+ break;
+ case 270:
+ ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_90R;
+ break;
+ case 0:
+ default:
+ ctx->mediacfg.encparams.general.rotation = VCENC_ROTATE_0;
+ break;
+ }
+ break;
+ case V4L2_CID_ROI:
+ if (ctrl->p_new.p)
+ vsiv4l2_setROI(ctx, ctrl->p_new.p);
+ break;
+ case V4L2_CID_IPCM:
+ if (ctrl->p_new.p)
+ vsiv4l2_setIPCM(ctx, ctrl->p_new.p);
+ break;
+ default:
+ return 0;
+ }
+ set_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+ return 0;
+}
+
+static int vsi_v4l2_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vsi_v4l2_ctx *ctx = ctrl_to_ctx(ctrl);
+
+ if (!vsi_v4l2_daemonalive())
+ return -ENODEV;
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ ctrl->val = ctx->mediacfg.minbuf_4capture; //these two may come from resoultion change
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ ctrl->val = ctx->mediacfg.minbuf_4output;
+ break;
+ case V4L2_CID_ROI_COUNT:
+ ctrl->val = vsiv4l2_getROIcount();
+ break;
+ case V4L2_CID_IPCM_COUNT:
+ ctrl->val = vsiv4l2_getIPCMcount();
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+/********* for ext ctrl *************/
+static bool vsi_ctrl_equal(const struct v4l2_ctrl *ctrl, u32 idx,
+ union v4l2_ctrl_ptr ptr1,
+ union v4l2_ctrl_ptr ptr2)
+{
+ //always update now, fix it later
+ return 0;
+}
+
+static void vsi_ctrl_init(const struct v4l2_ctrl *ctrl, u32 idx,
+ union v4l2_ctrl_ptr ptr)
+{
+ void *p = ptr.p + idx * ctrl->elem_size;
+
+ pr_debug("%s:%d", __func__, idx);
+ memset(p, 0, ctrl->elem_size);
+}
+
+static void vsi_ctrl_log(const struct v4l2_ctrl *ctrl)
+{
+ //do nothing now
+}
+
+static int vsi_ctrl_validate(const struct v4l2_ctrl *ctrl, u32 idx,
+ union v4l2_ctrl_ptr ptr)
+{
+ //always true
+ return 0;
+}
+
+static const struct v4l2_ctrl_type_ops vsi_type_ops = {
+ .equal = vsi_ctrl_equal,
+ .init = vsi_ctrl_init,
+ .log = vsi_ctrl_log,
+ .validate = vsi_ctrl_validate,
+};
+/********* for ext ctrl *************/
+
+static const struct v4l2_ctrl_ops vsi_ctrl_ops = {
+ .s_ctrl = vsi_v4l2_s_ctrl,
+ .g_volatile_ctrl = vsi_v4l2_g_volatile_ctrl,
+};
+
+static struct v4l2_ctrl_config vsi_v4l2_ctrl_defs[] = {
+ {
+ .ops = &vsi_ctrl_ops,
+ .id = V4L2_CID_DIS_REORDER,
+ .name = "frame disable reoder ctrl",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+ },
+ {
+ .ops = &vsi_ctrl_ops,
+ .id = V4L2_CID_ROI_COUNT,
+ .name = "get max ROI region number",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+ .min = 0,
+ .max = V4L2_MAX_ROI_REGIONS,
+ .step = 1,
+ .def = 0,
+ },
+ {
+ .ops = &vsi_ctrl_ops,
+ .type_ops = &vsi_type_ops,
+ .id = V4L2_CID_ROI,
+ .name = "vsi priv v4l2 roi params set",
+ .type = VSI_V4L2_CMPTYPE_ROI,
+ .min = 0,
+ .max = V4L2_MAX_ROI_REGIONS,
+ .step = 1,
+ .def = 0,
+ .elem_size = sizeof(struct v4l2_enc_roi_params),
+ },
+ {
+ .ops = &vsi_ctrl_ops,
+ .id = V4L2_CID_IPCM_COUNT,
+ .name = "get max IPCM region number",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+ .min = 0,
+ .max = V4L2_MAX_IPCM_REGIONS,
+ .step = 1,
+ .def = 0,
+ },
+ {
+ .ops = &vsi_ctrl_ops,
+ .type_ops = &vsi_type_ops,
+ .id = V4L2_CID_IPCM,
+ .name = "vsi priv v4l2 ipcm params set",
+ .type = VSI_V4L2_CMPTYPE_IPCM,
+ .min = 0,
+ .max = V4L2_MAX_IPCM_REGIONS,
+ .step = 1,
+ .def = 0,
+ .elem_size = sizeof(struct v4l2_enc_ipcm_params),
+ },
+ /* kernel defined controls */
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 1,
+ .max = MAX_INTRA_PIC_RATE,
+ .step = 1,
+ .def = DEFAULT_INTRA_PIC_RATE,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 10000,
+ .max = 288000000,
+ .step = 1,
+ .def = 2097152,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ .def = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VP8_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_VP8_PROFILE_0,
+ .max = V4L2_MPEG_VIDEO_VP8_PROFILE_3,
+ .def = V4L2_MPEG_VIDEO_VP8_PROFILE_0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .max = V4L2_MPEG_VIDEO_VP9_PROFILE_3,
+ .def = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
+ .def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_2,
+ .def = V4L2_MPEG_VIDEO_H264_LEVEL_5_0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1,
+ .def = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = 51,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .max = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ .def = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 0,
+ .step = 1,
+ .def = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = DEFAULT_QP,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ .max = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+ .def = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ },
+ {
+ .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_VOLATILE, //volatile contains read
+ .min = 1,
+ .max = MAX_MIN_BUFFERS_FOR_CAPTURE,
+ .step = 1,
+ .def = 1,
+ },
+ {
+ .id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .min = 1,
+ .max = MAX_MIN_BUFFERS_FOR_OUTPUT,
+ .step = 1,
+ .def = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .min = 0,
+ .max = 0,
+ .step = 0,
+ .def = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = DEFAULT_QP,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = DEFAULT_QP,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = DEFAULT_QP,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = DEFAULT_QP,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ .max = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB,
+ .def = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 1,
+ .max = 120, //1920 div 16
+ .step = 1,
+ .def = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = DEFAULT_QP,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 51,
+ .step = 1,
+ .def = DEFAULT_QP,
+ },
+ {
+ .id = V4L2_CID_ROTATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 270,
+ .step = 90,
+ .def = 0,
+ },
+};
+
+#define is_vsi_ctrl(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_USER) && \
+ V4L2_CTRL_DRIVER_PRIV(x))
+
+int vsi_setup_ctrls(struct v4l2_ctrl_handler *handler)
+{
+ int i, ctrl_num = ARRAY_SIZE(vsi_v4l2_ctrl_defs);
+ struct v4l2_ctrl *ctrl = NULL;
+
+ v4l2_ctrl_handler_init(handler, ctrl_num);
+
+ if (handler->error)
+ return handler->error;
+
+ for (i = 0; i < ctrl_num; i++) {
+ vsi_v4l2_update_ctrlcfg(&vsi_v4l2_ctrl_defs[i]);
+ if (is_vsi_ctrl(vsi_v4l2_ctrl_defs[i].id))
+ ctrl = v4l2_ctrl_new_custom(handler, &vsi_v4l2_ctrl_defs[i], NULL);
+ else {
+ if (vsi_v4l2_ctrl_defs[i].type == V4L2_CTRL_TYPE_MENU) {
+ ctrl = v4l2_ctrl_new_std_menu(handler, &vsi_ctrl_ops,
+ vsi_v4l2_ctrl_defs[i].id,
+ vsi_v4l2_ctrl_defs[i].max,
+ 0,
+ vsi_v4l2_ctrl_defs[i].def);
+ } else {
+ ctrl = v4l2_ctrl_new_std(handler,
+ &vsi_ctrl_ops,
+ vsi_v4l2_ctrl_defs[i].id,
+ vsi_v4l2_ctrl_defs[i].min,
+ vsi_v4l2_ctrl_defs[i].max,
+ vsi_v4l2_ctrl_defs[i].step,
+ vsi_v4l2_ctrl_defs[i].def);
+ }
+ }
+ if (ctrl && (vsi_v4l2_ctrl_defs[i].flags & V4L2_CTRL_FLAG_VOLATILE))
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ if (handler->error) {
+ pr_err("fail to set ctrl %d:%d", i, handler->error);
+ break;
+ }
+ }
+
+ v4l2_ctrl_handler_setup(handler);
+ return handler->error;
+}
+
+/*orphan error msg from daemon write, should not call daemon back*/
+
+int vsi_v4l2_handle_picconsumed(unsigned long ctxid)
+{
+ struct vsi_v4l2_ctx *ctx;
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_SKIP,
+ };
+
+ pr_debug("%s:%lx got picconsumed event", __func__, ctxid);
+ ctx = find_ctx(ctxid);
+ if (ctx == NULL)
+ return -1;
+
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ return 0;
+}
+
+int vsi_v4l2_handleerror(unsigned long ctxid, int error)
+{
+ struct vsi_v4l2_ctx *ctx;
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_CODEC_ERROR,
+ };
+
+ pr_err("%s:%lx got error %d", __func__, ctxid, error);
+ ctx = find_ctx(ctxid);
+ if (ctx == NULL)
+ return -1;
+
+ ctx->error = (error > 0 ? -error:error);
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ wake_up_interruptible_all(&ctx->retbuf_queue);
+ wake_up_interruptible_all(&ctx->input_que.done_wq);
+ wake_up_interruptible_all(&ctx->output_que.done_wq);
+ wake_up_interruptible_all(&ctx->fh.wait);
+
+ return 0;
+}
+
+int vsi_v4l2_notify_reschange(struct vsi_v4l2_msg *pmsg)
+{
+ unsigned long ctxid = pmsg->inst_id;
+ struct vsi_v4l2_ctx *ctx;
+
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+ ctx = find_ctx(ctxid);
+ if (ctx == NULL)
+ return -ESRCH;
+
+ if (isdecoder(ctx)) {
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+
+ pr_debug("ctx %lx sending event res change:%d:%d", ctx->ctxid, ctx->status, list_empty(&ctx->output_que.done_list));
+ pcfg->minbuf_4output =
+ pcfg->minbuf_4capture = pmsg->params.dec_params.dec_info.dec_info.needed_dpb_nums;
+ pcfg->sizeimagedst[0] =
+ pmsg->params.dec_params.io_buffer.OutBufSize;
+ if (ctx->status == DEC_STATUS_DECODING && !list_empty(&ctx->output_que.done_list)) {
+ ctx->status = DEC_STATUS_RESCHANGE;
+ pcfg->decparams_bkup.dec_info = pmsg->params.dec_params.dec_info;
+ pcfg->decparams_bkup.io_buffer.srcwidth = pmsg->params.dec_params.io_buffer.srcwidth;
+ pcfg->decparams_bkup.io_buffer.srcheight = pmsg->params.dec_params.io_buffer.srcheight;
+ pcfg->decparams_bkup.io_buffer.output_width = pmsg->params.dec_params.io_buffer.output_width;
+ pcfg->decparams_bkup.io_buffer.output_height = pmsg->params.dec_params.io_buffer.output_height;
+ } else {
+ pcfg->decparams.dec_info.dec_info = pmsg->params.dec_params.dec_info.dec_info;
+ pcfg->decparams.dec_info.io_buffer.srcwidth = pmsg->params.dec_params.dec_info.io_buffer.srcwidth;
+ pcfg->decparams.dec_info.io_buffer.srcheight = pmsg->params.dec_params.dec_info.io_buffer.srcheight;
+ pcfg->decparams.dec_info.io_buffer.output_width = pmsg->params.dec_params.dec_info.io_buffer.output_width;
+ pcfg->decparams.dec_info.io_buffer.output_height = pmsg->params.dec_params.dec_info.io_buffer.output_height;
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ }
+ if (pmsg->params.dec_params.dec_info.dec_info.colour_description_present_flag)
+ dec_updatevui(&pmsg->params.dec_params.dec_info.dec_info, &pcfg->decparams.dec_info.dec_info);
+ set_bit(CTX_FLAG_SRCCHANGED_BIT, &ctx->flag);
+ }
+ return 0;
+}
+
+int vsi_v4l2_handle_warningmsg(struct vsi_v4l2_msg *pmsg)
+{
+ unsigned long ctxid = pmsg->inst_id;
+ struct vsi_v4l2_ctx *ctx;
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_INVALID_OPTION,
+ };
+
+ ctx = find_ctx(ctxid);
+ if (ctx == NULL)
+ return -ESRCH;
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ return 0;
+}
+
+int vsi_v4l2_handle_cropchange(struct vsi_v4l2_msg *pmsg)
+{
+ unsigned long ctxid = pmsg->inst_id;
+ struct vsi_v4l2_ctx *ctx;
+
+ ctx = find_ctx(ctxid);
+ if (ctx == NULL)
+ return -ESRCH;
+
+ if (isdecoder(ctx)) {
+ struct vsi_v4l2_mediacfg *pcfg = &ctx->mediacfg;
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_CROPCHANGE,
+ };
+
+ pr_debug("ctx %lx sending crop res change:%d:%d", ctx->ctxid, ctx->status, ctx->buffed_cropcapnum);
+ if (ctx->status == DEC_STATUS_DECODING && ctx->buffed_cropcapnum > 0) {
+ pcfg->decparams_bkup.dec_info.dec_info.frame_width = pmsg->params.dec_params.pic_info.pic_info.width;
+ pcfg->decparams_bkup.dec_info.dec_info.frame_height = pmsg->params.dec_params.pic_info.pic_info.height;
+ pcfg->decparams_bkup.dec_info.dec_info.visible_rect.left = pmsg->params.dec_params.pic_info.pic_info.crop_left;
+ pcfg->decparams_bkup.dec_info.dec_info.visible_rect.top = pmsg->params.dec_params.pic_info.pic_info.crop_top;
+ pcfg->decparams_bkup.dec_info.dec_info.visible_rect.width = pmsg->params.dec_params.pic_info.pic_info.crop_width;
+ pcfg->decparams_bkup.dec_info.dec_info.visible_rect.height = pmsg->params.dec_params.pic_info.pic_info.crop_height;
+ set_bit(CTX_FLAG_CROPCHANGE_BIT, &ctx->flag);
+ } else {
+ pcfg->decparams.dec_info.dec_info.frame_width = pmsg->params.dec_params.pic_info.pic_info.width;
+ pcfg->decparams.dec_info.dec_info.frame_height = pmsg->params.dec_params.pic_info.pic_info.height;
+ pcfg->decparams.dec_info.dec_info.visible_rect.left = pmsg->params.dec_params.pic_info.pic_info.crop_left;
+ pcfg->decparams.dec_info.dec_info.visible_rect.top = pmsg->params.dec_params.pic_info.pic_info.crop_top;
+ pcfg->decparams.dec_info.dec_info.visible_rect.width = pmsg->params.dec_params.pic_info.pic_info.crop_width;
+ pcfg->decparams.dec_info.dec_info.visible_rect.height = pmsg->params.dec_params.pic_info.pic_info.crop_height;
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ }
+ }
+ return 0;
+}
+
+int vsi_v4l2_bufferdone(struct vsi_v4l2_msg *pmsg)
+{
+ unsigned long ctxid = pmsg->inst_id;
+ int inbufidx, outbufidx, bytesused[4] = {0};
+ struct vsi_v4l2_ctx *ctx;
+ struct vb2_queue *vq = NULL;
+ struct vb2_buffer *vb;
+
+ ctx = find_ctx(ctxid);
+ if (ctx == NULL)
+ return -1;
+
+ if (isencoder(ctx)) {
+ inbufidx = pmsg->params.enc_params.io_buffer.inbufidx;
+ outbufidx = pmsg->params.enc_params.io_buffer.outbufidx;
+ bytesused[0] = pmsg->params.enc_params.io_buffer.bytesused;
+ } else {
+ inbufidx = pmsg->params.dec_params.io_buffer.inbufidx;
+ outbufidx = pmsg->params.dec_params.io_buffer.outbufidx;
+ bytesused[0] = pmsg->params.dec_params.io_buffer.bytesused;
+ }
+ pr_debug("ctx %lx:%s:%ld:%lx:%d:%d", ctx->ctxid, __func__,
+ pmsg->inst_id, ctx->flag, inbufidx, outbufidx);
+ //write comes over once, so avoid this problem.
+ if (inbufidx >= 0 && inbufidx < ctx->input_que.num_buffers) {
+ vq = &ctx->input_que;
+ vb = vq->bufs[inbufidx];
+ atomic_inc(&ctx->srcframen);
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return -1;
+ if (ctx->input_que.streaming && vb->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ ctx->queued_srcnum--;
+ mutex_unlock(&ctx->ctxlock);
+ if (ctx->queued_srcnum == 0)
+ wake_up_interruptible_all(&ctx->retbuf_queue);
+ }
+ if (outbufidx >= 0 && outbufidx < ctx->output_que.num_buffers) {
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return -EBUSY;
+ if (bytesused[0] > 0)
+ ctx->frameidx++;
+ mutex_unlock(&ctx->ctxlock);
+ vq = &ctx->output_que;
+ vb = vq->bufs[outbufidx];
+ atomic_inc(&ctx->dstframen);
+ if (vb->state == VB2_BUF_STATE_ACTIVE) {
+ vb->planes[0].bytesused = bytesused[0];
+ if (mutex_lock_interruptible(&ctx->ctxlock))
+ return -EBUSY;
+ if (isencoder(ctx)) {
+ vb->timestamp = pmsg->params.enc_params.io_buffer.timestamp;
+ ctx->vbufflag[outbufidx] = pmsg->param_type;
+ if (vb->planes[0].bytesused == 0 || (pmsg->param_type & LAST_BUFFER_FLAG))
+ ctx->vbufflag[outbufidx] |= LAST_BUFFER_FLAG;
+ pr_debug("enc output framed %d:%d size = %d,flag=%x, timestamp=%lld", outbufidx, (int)ctx->frameidx, vb->planes[0].bytesused, ctx->vbufflag[outbufidx], vb->timestamp);
+ } else {
+ if (bytesused[0] == 0) {
+ if ((ctx->status == DEC_STATUS_DRAINING) || test_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag)) {
+ ctx->status = DEC_STATUS_ENDSTREAM;
+ set_bit(CTX_FLAG_ENDOFSTRM_BIT, &ctx->flag);
+ clear_bit(CTX_FLAG_PRE_DRAINING_BIT, &ctx->flag);
+ }
+ } else
+ vb->timestamp = pmsg->params.dec_params.io_buffer.timestamp;
+ ctx->buffed_capnum++;
+ ctx->buffed_cropcapnum++;
+ pr_debug("dec output framed %d:%d size = %d", outbufidx, (int)ctx->frameidx, vb->planes[0].bytesused);
+ }
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ mutex_unlock(&ctx->ctxlock);
+ }
+ }
+ return 0;
+}
+
+static void vsi_daemonsdevice_release(struct device *dev)
+{
+}
+
+static int v4l2_probe(struct platform_device *pdev)
+{
+ struct vsi_v4l2_device *vpu = NULL;
+ struct video_device *venc, *vdec;
+ int ret = 0;
+
+ pr_debug("%s", __func__);
+ if (gvsidev != NULL)
+ return 0;
+ vpu = kzalloc(sizeof(*vpu), GFP_KERNEL);
+ if (!vpu)
+ return -ENOMEM;
+
+ vpu->dev = &pdev->dev;
+ vpu->pdev = pdev;
+ mutex_init(&vpu->lock);
+ mutex_init(&vpu->irqlock);
+
+ ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register v4l2 device\n");
+ kfree(vpu);
+ return ret;
+ }
+ platform_set_drvdata(pdev, vpu);
+
+ vpu->venc = NULL;
+ vpu->vdec = NULL;
+ venc = v4l2_probe_enc(pdev, vpu);
+ if (venc == NULL)
+ goto err;
+ vpu->venc = venc;
+
+ vdec = v4l2_probe_dec(pdev, vpu);
+ if (vdec == NULL)
+ goto err;
+ vpu->vdec = vdec;
+
+ ret = vsiv4l2_initdaemon();
+ if (ret < 0)
+ goto err;
+
+ vsidaemondev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ vsidaemondev->devt = MKDEV(VSI_DAEMON_DEVMAJOR, 0);
+ dev_set_name(vsidaemondev, "%s", VSI_DAEMON_FNAME);
+ vsidaemondev->release = vsi_daemonsdevice_release;
+ ret = device_register(vsidaemondev);
+ if (ret < 0) {
+ kfree(vsidaemondev);
+ vsidaemondev = NULL;
+ vsiv4l2_cleanupdaemon();
+ goto err;
+ }
+ idr_init(&inst_array);
+
+ gvsidev = pdev;
+ return 0;
+
+err:
+ if (vpu->venc) {
+ v4l2_release_enc(vpu->venc);
+ video_device_release(vpu->venc);
+ }
+ if (vpu->vdec) {
+ v4l2_release_dec(vpu->vdec);
+ video_device_release(vpu->vdec);
+ }
+ v4l2_device_unregister(&vpu->v4l2_dev);
+ kfree(vpu);
+
+ return ret;
+}
+
+static int v4l2_remove(struct platform_device *pdev)
+{
+ struct vsi_v4l2_device *vpu = platform_get_drvdata(pdev);
+
+ v4l2_release_dec(vpu->vdec);
+ v4l2_release_enc(vpu->venc);
+ v4l2_device_unregister(&vpu->v4l2_dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(vpu);
+
+ return 0;
+}
+
+static const struct platform_device_id v4l2_platform_ids[] = {
+ {
+ .name = DRIVER_NAME,
+ },
+ { },
+};
+
+static const struct of_device_id v4l2_of_match[] = {
+ { .compatible = "platform-vsiv4l2", },
+ {/* sentinel */}
+};
+
+static struct platform_driver v4l2_drm_platform_driver = {
+ .probe = v4l2_probe,
+ .remove = v4l2_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = v4l2_of_match,
+ },
+ .id_table = v4l2_platform_ids,
+};
+
+static const struct platform_device_info v4l2_platform_info = {
+ .name = DRIVER_NAME,
+ .id = -1,
+ .dma_mask = DMA_BIT_MASK(64),
+};
+
+static ssize_t BandWidth_show(struct device *kdev,
+ struct device_attribute *attr, char *buf)
+{
+ /*
+ * sys/bus/platform/drivers/vsiv4l2/xxxxx.vpu/BandWidth
+ * used to show bandwidth info to user space
+ */
+ u64 bandwidth;
+
+ bandwidth = vsi_v4l2_getbandwidth();
+ return snprintf(buf, PAGE_SIZE, "%lld\n", bandwidth);
+}
+
+static DEVICE_ATTR_RO(BandWidth);
+
+static struct attribute *vsi_v4l2_attrs[] = {
+ &dev_attr_BandWidth.attr,
+ NULL,
+};
+
+static const struct attribute_group vsi_v4l2_attr_group = {
+ .attrs = vsi_v4l2_attrs,
+};
+
+void __exit vsi_v4l2_cleanup(void)
+{
+ void *obj;
+ int id;
+
+ gvsidev = NULL;
+ idr_for_each_entry(&inst_array, obj, id) {
+ if (obj) {
+ release_ctx(obj, 0);
+ vsi_v4l2_quitinstance();
+ }
+ }
+
+ device_unregister(vsidaemondev);
+ kfree(vsidaemondev);
+ vsiv4l2_cleanupdaemon();
+ if (self)
+ platform_device_unregister(gvsidev);
+ platform_driver_unregister(&v4l2_drm_platform_driver);
+ gvsidev = NULL;
+ self = 0;
+ pr_debug("%s", __func__);
+}
+
+int __init vsi_v4l2_init(void)
+{
+ int result;
+
+ self = 0;
+ vsidaemondev = NULL;
+ mutex_init(&vsi_ctx_array_lock);
+ ctx_seqid = 0;
+ result = platform_driver_register(&v4l2_drm_platform_driver);
+ if (result < 0) {
+ platform_device_unregister(gvsidev);
+ gvsidev = NULL;
+ }
+
+ if (gvsidev == NULL) {
+ gvsidev = platform_device_register_full(&v4l2_platform_info);
+ if (gvsidev == NULL) {
+ pr_err("v4l2 create platform device fail");
+ platform_driver_unregister(&v4l2_drm_platform_driver);
+ return -1;
+ }
+ self = 1;
+ }
+ if (devm_device_add_group(&gvsidev->dev, &vsi_v4l2_attr_group))
+ pr_err("fail to create sysfs API");
+
+ pr_debug("v4l2 device created");
+
+ return result;
+}
+
+module_init(vsi_v4l2_init);
+module_exit(vsi_v4l2_cleanup);
+
+/* module description */
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Verisilicon");
+MODULE_DESCRIPTION("VSI v4l2 manager");
+
--- /dev/null
+/*
+ * public header file for vsi v4l2 driver and daemon.
+ *
+ * Copyright (c) 2019, VeriSilicon Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ * https://opensource.org/licenses/gpl-2.0.php
+ */
+
+#ifndef VSI_V4L2_H
+#define VSI_V4L2_H
+
+#define MAX_STREAMS 100
+#define MAX_GOP_SIZE 8
+#define MAX_INTRA_PIC_RATE 0x7fffffff
+#define NO_RESPONSE_SEQID 0xFFFFFFFE
+#define SEQID_UPLIMT 0x7FFFFFFE
+
+#define OUTF_BASE 0x3ffff000L
+#define VSI_DAEMON_FNAME "vsi_daemon_ctrl"
+#define VSI_DAEMON_PATH "/usr/bin/vsidaemon"
+#define VSI_DAEMON_DEVMAJOR 100
+
+/* some common defines between driver and daemon */
+#define DEFAULTLEVEL 0
+
+#define FRAMETYPE_I (1<<1)
+#define FRAMETYPE_P (1<<2)
+#define FRAMETYPE_B (1<<3)
+#define LAST_BUFFER_FLAG (1<<4)
+#define FORCE_IDR (1<<5)
+#define UPDATE_INFO (1<<6)
+
+#define VSI_V4L2_MAX_ROI_REGIONS 8
+#define VSI_V4L2_MAX_ROI_REGIONS_H1 2
+#define VSI_V4L2_MAX_IPCM_REGIONS 2
+
+/****************** communication with v4l2 driver. ***********/
+
+struct vsi_v4l2_dev_info {
+ s32 dec_corenum;
+ s32 enc_corenum;
+ s32 enc_isH1;
+ ulong decformat; //hw_dec_formats
+ ulong encformat; //hw_enc_formats
+};
+
+/* daemon ioctl id definitions */
+#define VSIV4L2_IOCTL_BASE 'd'
+#define VSI_IOCTL_CMD_BASE _IO(VSIV4L2_IOCTL_BASE, 0x44)
+
+/* user space daemno should use this ioctl to initial HW info to v4l2 driver */
+#define VSI_IOCTL_CMD_INITDEV _IOW(VSIV4L2_IOCTL_BASE, 45, struct vsi_v4l2_dev_info)
+/* end of daemon ioctl id definitions */
+
+/*these two enum have same sequence, identical to the table vsi_coded_fmt[] in vsi-v4l2-config.c */
+enum hw_enc_formats {
+ ENC_HAS_HEVC = 0,
+ ENC_HAS_H264,
+ ENC_HAS_JPEG,
+ ENC_HAS_VP8,
+ ENC_HAS_VP9,
+ ENC_HAS_AV1,
+ ENC_FORMATS_MAX,
+};
+
+enum hw_dec_formats {
+ DEC_HAS_HEVC = 0,
+ DEC_HAS_H264,
+ DEC_HAS_JPEG,
+ DEC_HAS_VP8,
+ DEC_HAS_VP9,
+ DEC_HAS_AV1,
+
+ DEC_HAS_MPEG2,
+ DEC_HAS_MPEG4,
+ DEC_HAS_H263,
+ DEC_HAS_VC1_G,
+ DEC_HAS_VC1_L,
+ DEC_HAS_RV,
+ DEC_HAS_AVS2,
+ DEC_HAS_CSC,
+ DEC_FORMATS_MAX
+};
+/*above two enum have same sequence, identical to the table vsi_coded_fmt[] in vsi-v4l2-config.c */
+
+enum v4l2_daemon_cmd_id {
+ /* every command should mark which kind of parameters is valid.
+ * For example, V4L2_DAEMON_VIDIOC_BUF_RDY can contains input or output buffers.
+ * also it can contains other parameters. */
+ V4L2_DAEMON_VIDIOC_STREAMON = 0,//for streamon and start
+ V4L2_DAEMON_VIDIOC_BUF_RDY,
+ V4L2_DAEMON_VIDIOC_CMD_STOP, //this is for flush.
+ V4L2_DAEMON_VIDIOC_STREAMOFF,//for encoder, 4 cmd is enough.
+
+ V4L2_DAEMON_VIDIOC_FAKE,//fake command.
+
+ /*Below is for decoder*/
+ V4L2_DAEMON_VIDIOC_S_EXT_CTRLS,
+ V4L2_DAEMON_VIDIOC_RESET_BITRATE,
+ V4L2_DAEMON_VIDIOC_CHANGE_RES,
+ V4L2_DAEMON_VIDIOC_G_FMT,
+ V4L2_DAEMON_VIDIOC_S_SELECTION,
+ V4L2_DAEMON_VIDIOC_S_FMT,
+ V4L2_DAEMON_VIDIOC_PACKET, // tell daemon a frame is ready.
+ V4L2_DAEMON_VIDIOC_STREAMON_CAPTURE,//for streamon and start
+ V4L2_DAEMON_VIDIOC_STREAMON_OUTPUT,
+ V4L2_DAEMON_VIDIOC_STREAMOFF_CAPTURE,
+ V4L2_DAEMON_VIDIOC_STREAMOFF_OUTPUT,
+ V4L2_DAEMON_VIDIOC_CMD_START,
+ V4L2_DAEMON_VIDIOC_FRAME,
+ V4L2_DAEMON_VIDIOC_DESTROY_DEC,
+
+ V4L2_DAEMON_VIDIOC_EXIT, //daemon should exit itself
+ V4L2_DAEMON_VIDIOC_PICCONSUMED,
+ V4L2_DAEMON_VIDIOC_CROPCHANGE,
+ V4L2_DAEMON_VIDIOC_WARNONOPTION,
+ V4L2_DAEMON_VIDIOC_TOTAL_AMOUNT,
+};
+
+enum v4l2_daemon_codec_fmt {
+ /*enc format, identical to VCEncVideoCodecFormat except name*/
+ V4L2_DAEMON_CODEC_ENC_HEVC = 0,
+ V4L2_DAEMON_CODEC_ENC_H264,
+ V4L2_DAEMON_CODEC_ENC_AV1,
+ V4L2_DAEMON_CODEC_ENC_VP8,
+ V4L2_DAEMON_CODEC_ENC_VP9,
+ V4L2_DAEMON_CODEC_ENC_MPEG2,
+ V4L2_DAEMON_CODEC_ENC_JPEG,
+
+ /*dec format*/
+ V4L2_DAEMON_CODEC_DEC_HEVC,
+ V4L2_DAEMON_CODEC_DEC_H264,
+ V4L2_DAEMON_CODEC_DEC_JPEG,
+ V4L2_DAEMON_CODEC_DEC_VP9,
+ V4L2_DAEMON_CODEC_DEC_MPEG2,
+ V4L2_DAEMON_CODEC_DEC_MPEG4,
+ V4L2_DAEMON_CODEC_DEC_VP8,
+ V4L2_DAEMON_CODEC_DEC_H263,
+ V4L2_DAEMON_CODEC_DEC_VC1_G,
+ V4L2_DAEMON_CODEC_DEC_VC1_L,
+ V4L2_DAEMON_CODEC_DEC_RV,
+ V4L2_DAEMON_CODEC_DEC_AVS2,
+ V4L2_DAEMON_CODEC_UNKNOW_TYPE,
+};
+
+enum vsi_v4l2dec_outputfmt {
+ VSI_V4L2_DECOUT_DEFAULT,
+ VSI_V4L2_DECOUT_NV12,
+ VSI_V4L2_DECOUT_DTRC,
+ VSI_V4L2_DECOUT_P010,
+ VSI_V4L2_DECOUT_NV12_10BIT,
+};
+
+enum vsi_v4l2dec_pixfmt {
+ VSI_V4L2_DEC_PIX_FMT_NV12,
+ VSI_V4L2_DEC_PIX_FMT_400,
+ VSI_V4L2_DEC_PIX_FMT_411SP,
+ VSI_V4L2_DEC_PIX_FMT_422SP,
+ VSI_V4L2_DEC_PIX_FMT_444SP,
+};
+
+struct v4l2_daemon_enc_buffers {
+ /*IO*/
+ s32 inbufidx; //from v4l2 driver, don't modify it
+ s32 outbufidx; //-1:invalid, other:valid.
+
+ dma_addr_t busLuma;
+ s32 busLumaSize;
+ dma_addr_t busChromaU;
+ s32 busChromaUSize;
+ dma_addr_t busChromaV;
+ s32 busChromaVSize;
+
+ dma_addr_t busLumaOrig;
+ dma_addr_t busChromaUOrig;
+ dma_addr_t busChromaVOrig;
+
+ dma_addr_t busOutBuf;
+ u32 outBufSize;
+
+ u32 bytesused; //valid bytes in buffer from user app.
+ s64 timestamp;
+};
+
+struct v4l2_daemon_enc_general_cmd {
+ s32 valid;//0:invalid, 1:valid.
+
+ /*frame property*/
+ s32 outputRateNumer; /* Output frame rate numerator */
+ s32 outputRateDenom; /* Output frame rate denominator */
+ s32 inputRateNumer; /* Input frame rate numerator */
+ s32 inputRateDenom; /* Input frame rate denominator */
+
+ s32 firstPic;
+ s32 lastPic;
+
+ s32 width; //encode width
+ s32 height; //encode height
+ s32 lumWidthSrc; //input width
+ s32 lumHeightSrc; //input height
+
+ s32 inputFormat; //input format
+ s32 bitPerSecond;
+
+ s32 rotation; //prep
+ s32 mirror;
+ s32 horOffsetSrc;
+ s32 verOffsetSrc;
+ s32 colorConversion;
+ s32 scaledWidth;
+ s32 scaledHeight;
+ s32 scaledOutputFormat;
+
+ s32 codecFormat;
+};
+
+struct v4l2_daemon_enc_h26x_cmd {
+ s32 valid;//0:invalid, 1:valid.
+ s32 byteStream; //byteStream
+
+ s32 enableCabac; /* [0,1] H.264 entropy coding mode, 0 for CAVLC, 1 for CABAC */
+ s32 cabacInitFlag; //cabacInitFlag
+
+ s32 profile; /*main profile or main still picture profile*/
+ s32 tier; /*main tier or high tier*/
+ s32 avclevel; /*h264 main profile level*/
+ s32 hevclevel; /*hevc main profile level*/
+
+ u32 strong_intra_smoothing_enabled_flag; // intra setup
+
+ s32 cirStart; //cir
+ s32 cirInterval;
+
+ s32 intraAreaEnable; //intra area
+ s32 intraAreaTop;
+ s32 intraAreaLeft;
+ s32 intraAreaBottom;
+ s32 intraAreaRight;
+
+ s32 pcm_loop_filter_disabled_flag;
+
+ s32 ipcmAreaEnable[VSI_V4L2_MAX_IPCM_REGIONS];
+ s32 ipcmAreaTop[VSI_V4L2_MAX_IPCM_REGIONS]; //ipcm area 1, 2
+ s32 ipcmAreaLeft[VSI_V4L2_MAX_IPCM_REGIONS];
+ s32 ipcmAreaBottom[VSI_V4L2_MAX_IPCM_REGIONS];
+ s32 ipcmAreaRight[VSI_V4L2_MAX_IPCM_REGIONS];
+
+ s32 ipcmMapEnable; //ipcm map
+ u8 *ipcmMapBuf;
+
+ s32 skipMapEnable; //skip map
+ s32 skipMapBlockUnit;
+ u8 *skipMapBuf;
+
+ s32 roiAreaEnable[VSI_V4L2_MAX_ROI_REGIONS]; //8 roi for H2, 2 roi for H1
+ s32 roiAreaTop[VSI_V4L2_MAX_ROI_REGIONS];
+ s32 roiAreaLeft[VSI_V4L2_MAX_ROI_REGIONS];
+ s32 roiAreaBottom[VSI_V4L2_MAX_ROI_REGIONS];
+ s32 roiAreaRight[VSI_V4L2_MAX_ROI_REGIONS];
+ s32 roiDeltaQp[VSI_V4L2_MAX_ROI_REGIONS]; //roiQp has higher priority than roiDeltaQp
+ s32 roiQp[VSI_V4L2_MAX_ROI_REGIONS]; //only H2 use it
+
+ u32 roiMapDeltaQpBlockUnit;//roimap cuctrl
+ u32 roiMapDeltaQpEnable;
+ u32 RoiCuCtrlVer;
+ u32 RoiQpDeltaVer;
+ u8 *roiMapDeltaQpBuf;
+ u8 *cuCtrlInfoBuf;
+
+ /* Rate control parameters */
+ s32 hrdConformance;
+ s32 cpbSize;
+ s32 intraPicRate; /* IDR interval */
+ s32 vbr; /* Variable Bit Rate Control by qpMin */
+ s32 qpHdr;
+ s32 qpHdrI; //for I frame QP
+ s32 qpHdrP; //for P frame PQ
+ s32 qpMin;
+ s32 qpMax;
+ s32 qpMinI;
+ s32 qpMaxI;
+ s32 bitVarRangeI;
+ s32 bitVarRangeP;
+ s32 bitVarRangeB;
+ u32 u32StaticSceneIbitPercent;
+ s32 tolMovingBitRate;/*tolerance of max Moving bit rate */
+ s32 monitorFrames;/*monitor frame length for moving bit rate*/
+ s32 picRc;
+ s32 ctbRc;
+ s32 blockRCSize;
+ u32 rcQpDeltaRange;
+ u32 rcBaseMBComplexity;
+ s32 picSkip;
+ s32 picQpDeltaMin;
+ s32 picQpDeltaMax;
+ s32 ctbRcRowQpStep;
+ s32 tolCtbRcInter;
+ s32 tolCtbRcIntra;
+ s32 bitrateWindow;
+ s32 intraQpDelta;
+ s32 fixedIntraQp;
+ s32 bFrameQpDelta;
+ s32 disableDeblocking;
+ s32 enableSao;
+ s32 tc_Offset;
+ s32 beta_Offset;
+ s32 chromaQpOffset;
+
+ s32 smoothPsnrInGOP; //smooth psnr
+ s32 sliceSize; //multi slice
+
+ s32 enableDeblockOverride; //deblock
+ s32 deblockOverride;
+
+ s32 enableScalingList; //scale list
+
+ u32 compressor; //rfc
+
+ s32 interlacedFrame;//pregress/interlace
+ s32 fieldOrder; //field order
+ s32 ssim;
+
+ s32 sei; //sei
+ s8 *userData;
+
+ u32 gopSize; //gop
+ s8 *gopCfg;
+ u32 gopLowdelay;
+ s32 outReconFrame;
+
+ u32 longTermGap; //longterm
+ u32 longTermGapOffset;
+ u32 ltrInterval;
+ s32 longTermQpDelta;
+
+ s32 gdrDuration; //gdr
+
+ s32 bitDepthLuma; //10 bit
+ s32 bitDepthChroma;
+
+ u32 enableOutputCuInfo; //cu info
+
+ u32 rdoLevel; //rdo
+
+ s32 constChromaEn;/* constant chroma control */
+ u32 constCb;
+ u32 constCr;
+
+ s32 skip_frame_enabled_flag; /*for skip frame encoding ctr*/
+ s32 skip_frame_poc;
+
+ /* HDR10 */
+ u32 hdr10_display_enable;
+ u32 hdr10_dx0;
+ u32 hdr10_dy0;
+ u32 hdr10_dx1;
+ u32 hdr10_dy1;
+ u32 hdr10_dx2;
+ u32 hdr10_dy2;
+ u32 hdr10_wx;
+ u32 hdr10_wy;
+ u32 hdr10_maxluma;
+ u32 hdr10_minluma;
+
+ u32 hdr10_lightlevel_enable;
+ u32 hdr10_maxlight;
+ u32 hdr10_avglight;
+
+ u32 hdr10_color_enable;
+ u32 hdr10_primary;
+ u32 hdr10_transfer;
+ u32 hdr10_matrix;
+
+ u32 RpsInSliceHeader;
+ u32 P010RefEnable;
+ u32 vui_timing_info_enable;
+
+ u32 picOrderCntType;
+ u32 log2MaxPicOrderCntLsb;
+ u32 log2MaxFrameNum;
+
+ u32 lookaheadDepth;
+ u32 halfDsInput;
+ u32 cuInfoVersion;
+ u32 parallelCoreNum;
+
+ u32 force_idr;
+
+ u32 vuiVideoSignalTypePresentFlag;//1
+ u32 vuiVideoFormat; //default 5
+ s32 videoRange;
+ u32 vuiColorDescripPresentFlag; //1 if elems below exist
+ u32 vuiColorPrimaries;
+ u32 vuiTransferCharacteristics;
+ u32 vuiMatrixCoefficients;
+};
+
+struct v4l2_daemon_enc_jpeg_cmd {
+ s32 valid;//0:invalid, 1:valid.
+ s32 restartInterval;
+ s32 frameType;
+ s32 partialCoding;
+ s32 codingMode;
+ s32 markerType;
+ s32 qLevel;
+ s32 unitsType;
+ s32 xdensity;
+ s32 ydensity;
+ s32 thumbnail;
+ s32 widthThumb;
+ s32 heightThumb;
+ s32 lumWidthSrcThumb;
+ s32 lumHeightSrcThumb;
+ s32 horOffsetSrcThumb;
+ s32 verOffsetSrcThumb;
+ s32 write;
+ s32 comLength;
+ s32 mirror;
+ s32 formatCustomizedType;
+ s32 constChromaEn;
+ u32 constCb;
+ u32 constCr;
+ s32 losslessEnable;
+ s32 predictMode;
+ s32 ptransValue;
+ u32 bitPerSecond;
+ u32 mjpeg;
+ s32 rcMode;
+ s32 picQpDeltaMin;
+ s32 picQpDeltaMax;
+ u32 qpmin;
+ u32 qpmax;
+ s32 fixedQP;
+ u32 exp_of_input_alignment;
+ u32 streamBufChain;
+ u32 streamMultiSegmentMode;
+ u32 streamMultiSegmentAmount;
+};
+
+struct v4l2_daemon_enc_params {
+ struct v4l2_daemon_enc_buffers io_buffer;
+ struct v4l2_daemon_enc_general_cmd general;
+ union {
+ struct v4l2_daemon_enc_h26x_cmd enc_h26x_cmd;
+ struct v4l2_daemon_enc_jpeg_cmd enc_jpeg_cmd;
+ } specific;
+};
+
+struct v4l2_daemon_dec_buffers {
+ /*IO*/
+ s32 inbufidx; //from v4l2 driver, don't modify it
+ s32 outbufidx; //-1:invalid, other:valid.
+
+ dma_addr_t busInBuf;
+ u32 inBufSize;
+ s32 inputFormat; //input format
+ s32 srcwidth; //encode width
+ s32 srcheight; //encode height
+//infer output
+ dma_addr_t busOutBuf; //for Y or YUV
+ s32 OutBufSize;
+ dma_addr_t busOutBufUV;
+ s32 OutUVBufSize;
+ s32 outBufFormat;
+ s32 output_width;
+ s32 output_height;
+ s32 output_wstride;
+ s32 output_hstride;
+ s32 outputPixelDepth;
+
+ u32 bytesused; //valid bytes in buffer from user app.
+ s64 timestamp;
+
+ s32 no_reordering_decoding;
+};
+
+//stub struct
+struct v4l2_daemon_dec_pp_cfg {
+ u32 x;
+ u32 y;
+ u32 width;
+ u32 height;
+};
+
+struct v4l2_vpu_hdr10_meta {
+ u32 redPrimary[2];
+ u32 greenPrimary[2];
+ u32 bluePrimary[2];
+ u32 whitePoint[2];
+ u32 maxMasteringLuminance;
+ u32 minMasteringLuminance;
+ u32 maxContentLightLevel;
+ u32 maxFrameAverageLightLevel;
+};
+
+struct v4l2_daemon_dec_info {
+ u32 frame_width;
+ u32 frame_height;
+ u32 bit_depth;
+ struct {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+ } visible_rect;
+ u32 needed_dpb_nums;
+ u32 dpb_buffer_size;
+ struct v4l2_daemon_dec_pp_cfg pp_params;
+ u32 colour_description_present_flag;
+ u32 matrix_coefficients;
+ u32 colour_primaries;
+ u32 transfer_characteristics;
+ u32 video_range;
+ enum vsi_v4l2dec_pixfmt src_pix_fmt;
+ struct v4l2_vpu_hdr10_meta vpu_hdr10_meta;
+};
+
+struct v4l2_daemon_pic_info {
+ u32 width;
+ u32 height;
+ u32 crop_left;
+ u32 crop_top;
+ u32 crop_width;
+ u32 crop_height;
+};
+
+struct v4l2_daemon_dec_resochange_params {
+ struct v4l2_daemon_dec_buffers io_buffer;
+ struct v4l2_daemon_dec_info dec_info;
+};
+
+struct v4l2_daemon_dec_pictureinfo_params {
+ //v4l2_daemon_dec_buffers io_buffer;
+ struct v4l2_daemon_pic_info pic_info;
+};
+
+struct v4l2_daemon_dec_params {
+ union {
+ struct v4l2_daemon_dec_buffers io_buffer;
+ struct v4l2_daemon_dec_resochange_params dec_info;
+ struct v4l2_daemon_dec_pictureinfo_params pic_info;
+ };
+// struct TBCfg general;
+};
+
+struct vsi_v4l2_msg_hdr {
+ s32 size;
+ s32 error;
+ ulong seq_id;
+ ulong inst_id;
+ enum v4l2_daemon_cmd_id cmd_id;
+ enum v4l2_daemon_codec_fmt codec_fmt;
+ s32 param_type;
+};
+
+struct vsi_v4l2_msg {
+ s32 size;
+ s32 error;
+ ulong seq_id;
+ ulong inst_id;
+ enum v4l2_daemon_cmd_id cmd_id;
+ enum v4l2_daemon_codec_fmt codec_fmt;
+ u32 param_type;
+ // above part must be identical to vsi_v4l2_msg_hdr
+
+ union {
+ struct v4l2_daemon_enc_params enc_params;
+ struct v4l2_daemon_dec_params dec_params;
+ } params;
+};
+
+#endif //#ifndef VSI_V4L2_H
+
+
--- /dev/null
+/*
+ * VSI v4l2 message pipe manager.
+ *
+ * Copyright (c) 2019, VeriSilicon Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * You may obtain a copy of the GNU General Public License
+ * Version 2 at the following locations:
+ * https://opensource.org/licenses/gpl-2.0.php
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/atomic.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include <linux/delay.h>
+#include "vsi-v4l2-priv.h"
+
+#define PIPE_DEVICE_NAME "vsiv4l2daemon"
+
+static s32 invoke_vsidaemon = 1;
+module_param(invoke_vsidaemon, int, 0444);
+
+static s32 loglevel;
+module_param(loglevel, int, 0444);
+
+static ulong g_seqid;
+static struct idr *cmdarray, *retarray;
+static atomic_t daemon_fn = ATOMIC_INIT(0);
+
+static DECLARE_WAIT_QUEUE_HEAD(cmd_queue);
+static struct mutex cmd_lock;
+static DECLARE_WAIT_QUEUE_HEAD(ret_queue);
+static struct mutex ret_lock;
+static s32 v4l2_fn;
+static struct mutex instance_lock;
+
+/************************* for bandwith calc ***************************/
+static u64 accubytes;
+static struct timespec64 lasttime;
+static u64 last_bandwidth;
+/********************************************************************/
+
+u64 vsi_v4l2_getbandwidth(void)
+{
+ struct timespec64 curtime;
+ u64 gap, ret;
+
+ if (v4l2_fn == 0)
+ return last_bandwidth;
+
+ ktime_get_real_ts64(&curtime);
+ gap = curtime.tv_sec - lasttime.tv_sec;
+ if (gap <= 0)
+ gap = 1;
+ ret = accubytes / gap;
+ lasttime = curtime;
+ accubytes = 0;
+ return ret;
+}
+
+int vsi_v4l2_daemonalive(void)
+{
+ return (atomic_read(&daemon_fn) > 0);
+}
+
+void vsiv4l2_cleanupdaemon(void)
+{
+ int id;
+ void *obj;
+
+ if (mutex_lock_interruptible(&ret_lock))
+ return;
+ idr_for_each_entry(cmdarray, obj, id) {
+ if (obj) {
+ idr_remove(cmdarray, id);
+ kfree(obj);
+ }
+ }
+ idr_destroy(cmdarray);
+ kfree(cmdarray);
+ idr_for_each_entry(retarray, obj, id) {
+ if (obj) {
+ idr_remove(retarray, id);
+ kfree(obj);
+ }
+ }
+ idr_destroy(retarray);
+ kfree(retarray);
+
+ mutex_unlock(&ret_lock);
+
+ unregister_chrdev(VSI_DAEMON_DEVMAJOR, PIPE_DEVICE_NAME);
+}
+
+int vsi_clear_daemonmsg(int instid)
+{
+ struct vsi_v4l2_msg *msg;
+ void *obj;
+ int id;
+
+ if (mutex_lock_interruptible(&cmd_lock))
+ return -EBUSY;
+ idr_for_each_entry(cmdarray, obj, id) {
+ if (obj) {
+ msg = (struct vsi_v4l2_msg *)obj;
+ if (msg->inst_id == instid) {
+ pr_debug("remove unused %d:%ld:%d from cmdarray", instid, msg->seq_id, msg->cmd_id);
+ idr_remove(cmdarray, id);
+ kfree(obj);
+ }
+ }
+ }
+ mutex_unlock(&cmd_lock);
+ if (mutex_lock_interruptible(&ret_lock))
+ return -EBUSY;
+ idr_for_each_entry(retarray, obj, id) {
+ if (obj) {
+ msg = (struct vsi_v4l2_msg *)obj;
+ if (msg->inst_id == instid) {
+ pr_debug("remove unused %d:%ld:%d from retarray", instid, msg->seq_id, msg->cmd_id);
+ idr_remove(retarray, id);
+ kfree(obj);
+ }
+ }
+ }
+ mutex_unlock(&ret_lock);
+ return 0;
+}
+
+static int getMsg(struct file *fh, char __user *buf, size_t size)
+{
+ int id, offset = 0;
+ struct vsi_v4l2_msg *obj;
+
+ if (mutex_lock_interruptible(&cmd_lock))
+ return -EBUSY;
+ idr_for_each_entry(cmdarray, obj, id) {
+ if (offset >= size)
+ break;
+ if (obj) {
+ if (copy_to_user((void __user *)buf + offset, (void *)obj, sizeof(struct vsi_v4l2_msg_hdr) + obj->size) != 0)
+ break;
+ pr_debug("send msg id = %d, size = %d", obj->cmd_id, obj->size);
+ offset += sizeof(struct vsi_v4l2_msg_hdr) + obj->size;
+ accubytes += sizeof(struct vsi_v4l2_msg_hdr) + obj->size;
+ idr_remove(cmdarray, id);
+ kfree(obj);
+ break;
+ }
+ }
+ mutex_unlock(&cmd_lock);
+ return offset;
+}
+
+static int getRet(unsigned long seqid, int *error, s32 *retflag)
+{
+ int match = 0, id;
+ struct vsi_v4l2_msg *obj;
+
+ if (atomic_read(&daemon_fn) <= 0)
+ return 1;
+ if (mutex_lock_interruptible(&ret_lock))
+ return -EBUSY;
+ idr_for_each_entry(retarray, obj, id) {
+ if (obj) {
+ if (obj->seq_id == seqid) {
+ *error = obj->error;
+ *retflag = obj->param_type;
+ idr_remove(retarray, id);
+ kfree(obj);
+ match = 1;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&ret_lock);
+
+ return match;
+}
+
+/* send msg from v4l2 driver to user space daemon */
+static int vsi_v4l2_sendcmd(
+ enum v4l2_daemon_cmd_id cmdid,
+ unsigned long instid,
+ int codecformat,
+ void *msgcontent,
+ s32 *retflag,
+ int msgsize,
+ u32 param_type)
+{
+ unsigned long mid;
+ int error = 0;
+ struct vsi_v4l2_msg *pmsg;
+ struct vsi_v4l2_msg_hdr *msghdr;
+
+ if (atomic_read(&daemon_fn) <= 0)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&cmd_lock))
+ return -EBUSY;
+
+ if (msgsize == 0) {
+ msghdr = kzalloc(sizeof(struct vsi_v4l2_msg_hdr), GFP_KERNEL);
+ msghdr->inst_id = instid;
+ msghdr->cmd_id = cmdid;
+ msghdr->codec_fmt = codecformat;
+ msghdr->param_type = param_type;
+ mid = msghdr->seq_id = g_seqid;
+ idr_alloc(cmdarray, (void *)msghdr, 1, 0, GFP_KERNEL);
+ } else {
+ pmsg = kzalloc(sizeof(struct vsi_v4l2_msg), GFP_KERNEL);
+ pmsg->inst_id = instid;
+ pmsg->cmd_id = cmdid;
+ pmsg->codec_fmt = codecformat;
+ pmsg->param_type = param_type;
+ mid = pmsg->seq_id = g_seqid;
+ pmsg->size = msgsize;
+ memcpy((void *)&pmsg->params, msgcontent, msgsize);
+ idr_alloc(cmdarray, (void *)pmsg, 1, 0, GFP_KERNEL);
+ }
+ g_seqid++;
+ if (g_seqid >= SEQID_UPLIMT)
+ g_seqid = 1;
+ mutex_unlock(&cmd_lock);
+ wake_up_interruptible_all(&cmd_queue);
+
+ if (cmdid != V4L2_DAEMON_VIDIOC_EXIT) {
+ if (wait_event_interruptible(ret_queue, getRet(mid, &error, retflag) != 0))
+ return -ERESTARTSYS;
+ }
+
+ return error;
+}
+
+/* ioctl handler from daemon dev */
+static long vsi_v4l2_daemon_ioctl(
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int error = 0;
+ struct vsi_v4l2_dev_info hwinfo;
+
+ switch (cmd) {
+ case VSI_IOCTL_CMD_INITDEV:
+ if (copy_from_user((void *)&hwinfo, (void __user *)arg, sizeof(hwinfo)) != 0) {
+ pr_err("%s fail to get data", __func__);
+ return -EINVAL;
+ }
+ vsiv4l2_set_hwinfo(&hwinfo);
+ //vsi_v4l2_initdevinfo((struct vsi_v4l2_dev_info *)arg);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return error;
+}
+
+static int getbusaddr(struct vsi_v4l2_ctx *ctx, dma_addr_t *busaddr, struct vb2_buffer *buf)
+{
+ void *baseaddr[4], *p[4];
+ struct vb2_queue *q;
+ int planeno, i;
+
+ if (binputqueue(buf->type)) {
+ q = &ctx->input_que;
+ planeno = ctx->mediacfg.srcplanes;
+ } else {
+ q = &ctx->output_que;
+ planeno = ctx->mediacfg.dstplanes;
+ }
+ for (i = 0; i < planeno; i++) {
+ baseaddr[i] = vb2_plane_vaddr(buf, i); //actually used for cmodel
+ p[i] = vb2_plane_cookie(buf, i);
+ if (p[i] != NULL)
+ busaddr[i] = *(dma_addr_t *)p[i];
+ else
+ busaddr[i] = virt_to_phys(baseaddr[i]);
+ }
+ pr_debug("%s:%d:%d:%lx:%lx:%lx", __func__, buf->type, planeno,
+ (unsigned long)busaddr[0], (unsigned long)busaddr[1], (unsigned long)busaddr[2]);
+ return planeno;
+}
+
+static u32 format_bufinfo_enc(struct vsi_v4l2_ctx *ctx, struct vsi_v4l2_msg *pmsg, struct vb2_buffer *buf, u32 update)
+{
+ u32 planeno, size;
+ struct v4l2_daemon_enc_buffers *encbufinfo;
+ dma_addr_t busaddr[4];
+
+ convertROI(ctx);
+ convertIPCM(ctx);
+ if (update) {
+ size = sizeof(struct v4l2_daemon_enc_params);
+ memcpy((void *)&pmsg->params.enc_params, (void *)&ctx->mediacfg.encparams, sizeof(ctx->mediacfg.encparams));
+ } else {
+ size = sizeof(struct v4l2_daemon_enc_buffers) + sizeof(struct v4l2_daemon_enc_general_cmd);
+ memcpy((void *)&pmsg->params.enc_params.io_buffer,
+ (void *)&ctx->mediacfg.encparams.io_buffer, sizeof(struct v4l2_daemon_enc_buffers));
+ memcpy((void *)&pmsg->params.enc_params.general,
+ (void *)&ctx->mediacfg.encparams.general, sizeof(struct v4l2_daemon_enc_general_cmd));
+ }
+ if (ctx->mediacfg.multislice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE)
+ pmsg->params.enc_params.specific.enc_h26x_cmd.sliceSize = 0;
+ if (binputqueue(buf->type)) {
+ //msg.params.enc_params.general.lumWidthSrc = ctx->mediacfg.bytesperline;
+ pmsg->params.enc_params.io_buffer.timestamp = buf->timestamp;
+ }
+ planeno = getbusaddr(ctx, busaddr, buf);
+ encbufinfo = &pmsg->params.enc_params.io_buffer;
+ if (binputqueue(buf->type)) {
+ encbufinfo->busLumaOrig = encbufinfo->busLuma = busaddr[0] + buf->planes[0].data_offset;
+ encbufinfo->busLumaSize = ctx->mediacfg.sizeimagesrc[0];
+ encbufinfo->busChromaUOrig = encbufinfo->busChromaU = 0;
+ encbufinfo->busChromaUSize = 0;
+ encbufinfo->busChromaVOrig = encbufinfo->busChromaV = 0;
+ encbufinfo->busChromaVSize = 0;
+ if (planeno == 2) {
+ encbufinfo->busChromaUOrig = encbufinfo->busChromaU = busaddr[1] + buf->planes[1].data_offset;
+ encbufinfo->busChromaUSize = ctx->mediacfg.sizeimagesrc[1];
+ } else if (planeno == 3) {
+ encbufinfo->busChromaUOrig = encbufinfo->busChromaU = busaddr[1] + buf->planes[1].data_offset;
+ encbufinfo->busChromaUSize = ctx->mediacfg.sizeimagesrc[1];
+ encbufinfo->busChromaVOrig = encbufinfo->busChromaV = busaddr[2] + buf->planes[2].data_offset;
+ encbufinfo->busChromaVSize = ctx->mediacfg.sizeimagesrc[2];
+ }
+ encbufinfo->busOutBuf = 0;
+ encbufinfo->outBufSize = 0;
+ encbufinfo->inbufidx = buf->index;
+ encbufinfo->outbufidx = -1;
+ if (ctx->srcvbufflag[buf->index] & FORCE_IDR) {
+ pmsg->params.enc_params.specific.enc_h26x_cmd.force_idr = 1;
+ ctx->srcvbufflag[buf->index] &= ~FORCE_IDR;
+ } else
+ pmsg->params.enc_params.specific.enc_h26x_cmd.force_idr = 0;
+ pr_debug("enc input: %llx:%d, %llx:%d", encbufinfo->busLuma, encbufinfo->busLumaSize,
+ encbufinfo->busChromaU, encbufinfo->busChromaUSize);
+ } else {
+ encbufinfo->busLumaOrig = encbufinfo->busLuma = 0;
+ encbufinfo->busChromaUOrig = encbufinfo->busChromaU = 0;
+ encbufinfo->busChromaVOrig = encbufinfo->busChromaV = 0;
+ encbufinfo->busOutBuf = busaddr[0] + buf->planes[0].data_offset;
+ encbufinfo->outBufSize = ctx->mediacfg.sizeimagedst[0];
+ encbufinfo->outbufidx = buf->index;
+ encbufinfo->inbufidx = -1;
+ pr_debug("enc output: %llx:%d", encbufinfo->busOutBuf, encbufinfo->outBufSize);
+ }
+ encbufinfo->bytesused = buf->planes[0].bytesused;
+ return size;
+}
+
+static void format_bufinfo_dec(struct vsi_v4l2_ctx *ctx, struct vsi_v4l2_msg *pmsg, struct vb2_buffer *buf)
+{
+ struct v4l2_daemon_dec_buffers *decbufinfo;
+ dma_addr_t busaddr[4];
+
+ memcpy((void *)&pmsg->params.dec_params.io_buffer, (void *)&ctx->mediacfg.decparams.io_buffer, sizeof(struct v4l2_daemon_dec_buffers));
+ if (binputqueue(buf->type))
+ pmsg->params.dec_params.dec_info.io_buffer.timestamp = buf->timestamp;
+ getbusaddr(ctx, busaddr, buf);
+ decbufinfo = &pmsg->params.dec_params.io_buffer;
+ if (!binputqueue(buf->type)) {
+ decbufinfo->inbufidx = -1;
+ decbufinfo->outbufidx = buf->index;
+ decbufinfo->busInBuf = 0;
+ decbufinfo->inBufSize = 0;
+ decbufinfo->busOutBuf = busaddr[0] + buf->planes[0].data_offset;
+ decbufinfo->OutBufSize = ctx->outbuflen[buf->index];//ctx->mediacfg.sizeimagedst[0];
+ decbufinfo->bytesused = buf->planes[0].bytesused;
+ pr_debug("dec output: %llx:%d, %llx:%d", decbufinfo->busOutBuf, decbufinfo->OutBufSize,
+ decbufinfo->busOutBufUV, decbufinfo->OutUVBufSize);
+ } else {
+ decbufinfo->inbufidx = buf->index;
+ decbufinfo->outbufidx = -1;
+ decbufinfo->busInBuf = busaddr[0] + buf->planes[0].data_offset;
+ decbufinfo->inBufSize = ctx->inbuflen[buf->index];//ctx->mediacfg.sizeimagesrc[0];
+ decbufinfo->bytesused = ctx->inbufbytes[buf->index];
+ decbufinfo->busOutBuf = 0;
+ decbufinfo->OutBufSize = 0;
+ pr_debug("dec input: %llx:%d", decbufinfo->busInBuf, decbufinfo->inBufSize);
+ }
+}
+
+int vsiv4l2_execcmd(struct vsi_v4l2_ctx *ctx, enum v4l2_daemon_cmd_id id, void *args)
+{
+ int ret = 0;
+ s32 retflag;
+ struct vsi_v4l2_msg msg;
+
+ if (atomic_read(&daemon_fn) <= 0)
+ return -ENODEV;
+ memset((void *)&msg, 0, sizeof(msg));
+ switch (id) {
+ case V4L2_DAEMON_VIDIOC_EXIT:
+ ret = vsi_v4l2_sendcmd(id, 0, 0, NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_STREAMOFF:
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid,
+ ctx->mediacfg.encparams.general.codecFormat, NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_DESTROY_DEC:
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid,
+ ctx->mediacfg.decparams.dec_info.io_buffer.outBufFormat, NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_CMD_STOP:
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid,
+ ctx->mediacfg.encparams.general.codecFormat, NULL, &retflag, 0, 0);
+ if (ret == 0) {
+ if ((retflag & LAST_BUFFER_FLAG) &&
+ ctx->status == ENC_STATUS_DRAINING)
+ ctx->status = ENC_STATUS_STOPPED;
+ }
+ break;
+ case V4L2_DAEMON_VIDIOC_STREAMON:
+ //memcpy((void *)&msg.params.enc_params,
+ // (void *)&ctx->mediacfg.encparams, sizeof(ctx->mediacfg.encparams));
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid,
+ ctx->mediacfg.encparams.general.codecFormat, NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_STREAMOFF_OUTPUT:
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid,
+ ctx->mediacfg.decparams.dec_info.io_buffer.inputFormat, NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_STREAMOFF_CAPTURE:
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid,
+ ctx->mediacfg.decparams.dec_info.io_buffer.outBufFormat, NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_STREAMON_OUTPUT:
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid, ctx->mediacfg.decparams.dec_info.io_buffer.inputFormat,
+ NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_STREAMON_CAPTURE:
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid, ctx->mediacfg.decparams.dec_info.io_buffer.outBufFormat,
+ NULL, &retflag, 0, 0);
+ break;
+ case V4L2_DAEMON_VIDIOC_BUF_RDY:
+ if (isencoder(ctx)) {
+ u32 size, update = test_and_clear_bit(CTX_FLAG_CONFIGUPDATE_BIT, &ctx->flag);
+
+ if (update)
+ update = UPDATE_INFO;
+ else
+ update = 0;
+ size = format_bufinfo_enc(ctx, &msg, args, update);
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid, ctx->mediacfg.encparams.general.codecFormat,
+ &msg.params, &retflag, size, update);
+ } else {
+ format_bufinfo_dec(ctx, &msg, args);
+ ret = vsi_v4l2_sendcmd(id, ctx->ctxid, ctx->mediacfg.decparams.dec_info.io_buffer.inputFormat,
+ &msg.params, &retflag, sizeof(struct v4l2_daemon_dec_buffers), 0);
+ }
+ break;
+ default:
+ return -1;
+ }
+ if (ctx)
+ set_bit(CTX_FLAG_DAEMONLIVE_BIT, &ctx->flag);
+ return ret;
+}
+
+int vsi_v4l2_addinstance(pid_t *ppid)
+{
+ int ret = 0;
+ char loglvl[20] = {0};
+ char *argv[] = {VSI_DAEMON_PATH, NULL};
+ char *env[] = {"LD_LIBRARY_PATH=/usr/lib",
+ "DAEMON_LOGPATH=/home/vsi/daemon.log",
+ loglvl,
+ NULL};
+
+ pr_debug("%s", __func__);
+ if (!invoke_vsidaemon && atomic_read(&daemon_fn) <= 0)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&instance_lock))
+ return -EBUSY;
+
+ if (v4l2_fn >= MAX_STREAMS)
+ ret = -EBUSY;
+ else {
+ v4l2_fn++;
+ if (v4l2_fn == 1 && invoke_vsidaemon) {
+ memcpy(loglvl, "HANTRO_LOG_LEVEL=00", 20);
+ loglvl[17] = loglevel/10 + 0x30;
+ loglvl[18] = loglevel%10 + 0x30;
+ ret = call_usermodehelper(argv[0], argv, env, UMH_WAIT_EXEC);
+ if (ret < 0)
+ v4l2_fn--;
+ else {
+ while (atomic_read(&daemon_fn) <= 0)
+ ndelay(10);
+ }
+ }
+ if (v4l2_fn == 1) {
+ /*reset bandwidth info*/
+ ktime_get_real_ts64(&lasttime);
+ accubytes = 0;
+ }
+ pr_debug("%s:%d", __func__, v4l2_fn);
+ }
+ mutex_unlock(&instance_lock);
+
+ return ret;
+}
+
+int vsi_v4l2_quitinstance(void)
+{
+ int ret = 0;
+
+ pr_debug("%s", __func__);
+ if (mutex_lock_interruptible(&instance_lock))
+ return -EBUSY;
+ v4l2_fn--;
+ if (invoke_vsidaemon && v4l2_fn == 0) {
+ struct timespec64 curtime;
+ u64 gap;
+
+ ktime_get_real_ts64(&curtime);
+ gap = curtime.tv_sec - lasttime.tv_sec;
+ if (gap <= 0)
+ gap = 1;
+ last_bandwidth = accubytes / gap;
+ ret = vsiv4l2_execcmd(NULL, V4L2_DAEMON_VIDIOC_EXIT, NULL);
+ while (atomic_read(&daemon_fn) > 0)
+ ndelay(10);
+ }
+ pr_debug("%s:%d", __func__, v4l2_fn);
+ mutex_unlock(&instance_lock);
+ return 0;
+}
+
+static ssize_t v4l2_msg_read(struct file *fh, char __user *buf, size_t size, loff_t *offest)
+{
+ int ret, r;
+
+ ret = wait_event_interruptible_timeout(cmd_queue, ((r = getMsg(fh, buf, size)) != 0), msecs_to_jiffies(100));
+ if (ret == -ERESTARTSYS)
+ return -EIO;
+ else if (ret == 0)
+ return 0;
+ return r;
+}
+
+static int vsi_handle_daemonmsg(struct vsi_v4l2_msg *pmsg)
+{
+ if (pmsg->error < 0)
+ return vsi_v4l2_handleerror(pmsg->inst_id, pmsg->error);
+
+ switch (pmsg->cmd_id) {
+ case V4L2_DAEMON_VIDIOC_BUF_RDY:
+ return vsi_v4l2_bufferdone(pmsg);
+ case V4L2_DAEMON_VIDIOC_CHANGE_RES:
+ return vsi_v4l2_notify_reschange(pmsg);
+ case V4L2_DAEMON_VIDIOC_PICCONSUMED:
+ return vsi_v4l2_handle_picconsumed(pmsg->inst_id);
+ case V4L2_DAEMON_VIDIOC_CROPCHANGE:
+ return vsi_v4l2_handle_cropchange(pmsg);
+ case V4L2_DAEMON_VIDIOC_WARNONOPTION:
+ return vsi_v4l2_handle_warningmsg(pmsg);
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t v4l2_msg_write(struct file *fh, const char __user *buf, size_t size, loff_t *offset)
+{
+ int ret = -1, msgsize;
+ struct vsi_v4l2_msg *pmsg;
+
+ if (v4l2_fn == 0)
+ return size;
+ if (size < sizeof(struct vsi_v4l2_msg_hdr))
+ return size;
+ if (!access_ok((void *) buf, size)) {
+ pr_err("input data unaccessable");
+ return size;
+ }
+ if (mutex_lock_interruptible(&ret_lock))
+ return size;
+
+ pmsg = kzalloc(sizeof(struct vsi_v4l2_msg), GFP_KERNEL);
+ if (copy_from_user((void *)pmsg,
+ (void __user *)buf, sizeof(struct vsi_v4l2_msg_hdr)) != 0) {
+ kfree(pmsg);
+ goto error;
+ }
+ msgsize = pmsg->size;
+ if (msgsize + sizeof(struct vsi_v4l2_msg_hdr) > size) {
+ kfree(pmsg);
+ goto error;
+ }
+ if (msgsize > 0) {
+ if (copy_from_user((void *)pmsg + sizeof(struct vsi_v4l2_msg_hdr),
+ (void __user *)buf + sizeof(struct vsi_v4l2_msg_hdr), msgsize) != 0) {
+ kfree(pmsg);
+ goto error;
+ }
+ }
+ pr_debug("get msg id = %d, flag = %x, seqid = %lx, err = %d",
+ pmsg->cmd_id, pmsg->param_type, pmsg->seq_id, pmsg->error);
+ accubytes += sizeof(struct vsi_v4l2_msg_hdr) + msgsize;
+ if (pmsg->seq_id == NO_RESPONSE_SEQID) {
+ ret = 0;
+ vsi_handle_daemonmsg(pmsg);
+ kfree(pmsg);
+ } else {
+ ret = idr_alloc(retarray, (void *)pmsg, 1, 0, GFP_KERNEL);
+ if (ret < 0)
+ kfree(pmsg);
+ }
+error:
+ mutex_unlock(&ret_lock);
+ if (ret >= 0)
+ wake_up_interruptible_all(&ret_queue);
+
+ return size;
+}
+
+static int v4l2_daemon_open(struct inode *inode, struct file *filp)
+{
+ /*we need single daemon. Each deamon uses 2 handles for ioctl and mmap*/
+ pr_debug("%s:%d", __func__, atomic_read(&daemon_fn));
+ if (atomic_read(&daemon_fn) >= 1)
+ return -EBUSY;
+ atomic_inc(&daemon_fn);
+ return 0;
+}
+
+static int v4l2_daemon_release(struct inode *inode, struct file *filp)
+{
+ atomic_dec(&daemon_fn);
+ pr_debug("%s:%d", __func__, atomic_read(&daemon_fn));
+ if (atomic_read(&daemon_fn) <= 0) {
+ struct vsi_v4l2_ctx *ctx;
+ int id;
+
+ idr_for_each_entry(&inst_array, ctx, id) {
+ if (ctx) {
+ ctx->error = -2;
+ wake_up_interruptible_all(&ctx->input_que.done_wq);
+ wake_up_interruptible_all(&ctx->output_que.done_wq);
+ wake_up_interruptible_all(&ctx->retbuf_queue);
+ wake_up_interruptible_all(&ctx->fh.wait);
+ }
+ }
+ wake_up_interruptible_all(&ret_queue);
+ }
+ return 0;
+}
+
+static int vsi_v4l2_mmap(
+ struct file *filp,
+ struct vm_area_struct *vma)
+{
+ size_t size = vma->vm_end - vma->vm_start;
+ phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
+
+ /* Does it even fit in phys_addr_t? */
+ if (offset >> PAGE_SHIFT != vma->vm_pgoff)
+ return -EINVAL;
+
+ /* It's illegal to wrap around the end of the physical address space. */
+ if (offset + (phys_addr_t)size - 1 < offset)
+ return -EINVAL;
+
+ //if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
+ // return -EINVAL;
+
+ if (!(vma->vm_flags & VM_MAYSHARE))
+ return -EPERM;
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot) ? -EAGAIN : 0;
+}
+
+
+static const struct file_operations daemon_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_daemon_open,
+ .release = v4l2_daemon_release,
+ .unlocked_ioctl = vsi_v4l2_daemon_ioctl,
+ .read = v4l2_msg_read,
+ .write = v4l2_msg_write,
+ .mmap = vsi_v4l2_mmap,
+};
+
+
+int vsiv4l2_initdaemon(void)
+{
+ int result;
+
+ cmdarray = NULL;
+ retarray = NULL;
+ v4l2_fn = 0;
+ accubytes = 0;
+ last_bandwidth = 0;
+ loglevel = 0;
+ accubytes = 0;
+ result = register_chrdev(VSI_DAEMON_DEVMAJOR, PIPE_DEVICE_NAME, &daemon_fops);
+ if (result < 0)
+ return result;
+
+ cmdarray = kzalloc(sizeof(struct idr), GFP_KERNEL);
+ if (cmdarray == NULL) {
+ unregister_chrdev(VSI_DAEMON_DEVMAJOR, PIPE_DEVICE_NAME);
+ return -ENOMEM;
+ }
+ idr_init(cmdarray);
+
+ retarray = kzalloc(sizeof(struct idr), GFP_KERNEL);
+ if (retarray == NULL) {
+ unregister_chrdev(VSI_DAEMON_DEVMAJOR, PIPE_DEVICE_NAME);
+ kfree(cmdarray);
+ return -ENOMEM;
+ }
+ idr_init(retarray);
+
+ mutex_init(&cmd_lock);
+ mutex_init(&ret_lock);
+ mutex_init(&instance_lock);
+ g_seqid = 1;
+ if (loglevel < 0)
+ loglevel = 0;
+ else if (loglevel > 10)
+ loglevel = 10;
+
+ return result;
+}
+
#ifndef _UAPI__LINUX_IMX_VPU_H
#define _UAPI__LINUX_IMX_VPU_H
+#include <linux/videodev2.h>
#include <linux/v4l2-controls.h>
/*imx v4l2 controls*/
/*imx v4l2 event*/
#define V4L2_EVENT_CODEC_ERROR (V4L2_EVENT_PRIVATE_START + 1)
-#define V4L2_EVENT_SKIP (V4L2_EVENT_PRIVATE_START + 2)
+#define V4L2_EVENT_SKIP (V4L2_EVENT_PRIVATE_START + 2)
+#define V4L2_EVENT_CROPCHANGE (V4L2_EVENT_PRIVATE_START + 3)
+#define V4L2_EVENT_INVALID_OPTION (V4L2_EVENT_PRIVATE_START + 4)
+
+/* imx v4l2 formats */
+/*raw formats*/
+#define V4L2_PIX_FMT_BGR565 v4l2_fourcc('B', 'G', 'R', 'P') /* 16 BGR-5-6-5 */
+#define V4L2_PIX_FMT_NV12X v4l2_fourcc('N', 'V', 'X', '2') /* Y/CbCr 4:2:0 for 10bit */
+#define V4L2_PIX_FMT_DTRC v4l2_fourcc('D', 'T', 'R', 'C') /* 8bit tile output */
+#define V4L2_PIX_FMT_P010 v4l2_fourcc('P', '0', '1', '0') /*ms p010, data stored in upper 10 bits of 16 */
+
+/*codec format*/
+#define V4L2_PIX_FMT_AV1 v4l2_fourcc('A', 'V', '1', '0') /* av1 */
+#define V4L2_PIX_FMT_RV v4l2_fourcc('R', 'V', '0', '0') /* rv */
+#define V4L2_PIX_FMT_AVS v4l2_fourcc('A', 'V', 'S', '0') /* avs */
+/*codec formats*/
+#endif //#ifndef _UAPI__LINUX_IMX_VPU_H
-#endif