Commit c9a59dc2 authored by Detlev Casanova's avatar Detlev Casanova Committed by Hans Verkuil
Browse files

media: rkvdec: Add HEVC support for the VDPU381 variant



The VDPU381 supports HEVC decoding up to 7680x4320@30fps.
It could double that when using both decoder cores.

It support YUV420 (8 and 10 bits) as well as AFBC (not implemented
here)

The fluster score is 146/147 for JCT-VC-HEVC_V1, tested on ROCK 5B.
None of the other test suites works.

Tested-by: Diederik de Haas <didi.debian@cknow.org>  # Rock 5B
Reviewed-by: default avatarNicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: default avatarDetlev Casanova <detlev.casanova@collabora.com>
Signed-off-by: default avatarNicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: default avatarHans Verkuil <hverkuil+cisco@kernel.org>
parent fde24907
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -9,5 +9,6 @@ rockchip-vdec-y += \
		   rkvdec-hevc-common.o \
		   rkvdec-rcb.o \
		   rkvdec-vdpu381-h264.o \
		   rkvdec-vdpu381-hevc.o \
		   rkvdec-vdpu383-h264.o \
		   rkvdec-vp9.o
+335 −0
Original line number Diff line number Diff line
@@ -21,6 +21,125 @@
#include "rkvdec.h"
#include "rkvdec-hevc-common.h"

/* Store the Short term ref pic set calculated values */
struct calculated_rps_st_set {
	u8 num_delta_pocs;
	u8 num_negative_pics;
	u8 num_positive_pics;
	u8 used_by_curr_pic_s0[16];
	u8 used_by_curr_pic_s1[16];
	s32 delta_poc_s0[16];
	s32 delta_poc_s1[16];
};

void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
			   u16 width, u16 height, s32 pic_in_cts_width,
			   s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
{
	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
	int i;

	for (i = 0; i < pps->num_tile_columns_minus1 + 1; i++)
		column_width[i] = ((i + 1) * pic_in_cts_width) /
				  (pps->num_tile_columns_minus1 + 1) -
				  (i * pic_in_cts_width) /
				  (pps->num_tile_columns_minus1 + 1);

	for (i = 0; i < pps->num_tile_rows_minus1 + 1; i++)
		row_height[i] = ((i + 1) * pic_in_cts_height) /
				(pps->num_tile_rows_minus1 + 1) -
				(i * pic_in_cts_height) /
				(pps->num_tile_rows_minus1 + 1);
}

void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
			       u16 width, u16 height, s32 pic_in_cts_width,
			       s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
{
	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
	s32 sum = 0;
	int i;

	for (i = 0; i < pps->num_tile_columns_minus1; i++) {
		column_width[i] = pps->column_width_minus1[i] + 1;
		sum += column_width[i];
	}
	column_width[i] = pic_in_cts_width - sum;

	sum = 0;
	for (i = 0; i < pps->num_tile_rows_minus1; i++) {
		row_height[i] = pps->row_height_minus1[i] + 1;
		sum += row_height[i];
	}
	row_height[i] = pic_in_cts_height - sum;
}

static void set_ref_poc(struct rkvdec_rps_short_term_ref_set *set, int poc, int value, int flag)
{
	switch (poc) {
	case 0:
		set->delta_poc0 = value;
		set->used_flag0 = flag;
		break;
	case 1:
		set->delta_poc1 = value;
		set->used_flag1 = flag;
		break;
	case 2:
		set->delta_poc2 = value;
		set->used_flag2 = flag;
		break;
	case 3:
		set->delta_poc3 = value;
		set->used_flag3 = flag;
		break;
	case 4:
		set->delta_poc4 = value;
		set->used_flag4 = flag;
		break;
	case 5:
		set->delta_poc5 = value;
		set->used_flag5 = flag;
		break;
	case 6:
		set->delta_poc6 = value;
		set->used_flag6 = flag;
		break;
	case 7:
		set->delta_poc7 = value;
		set->used_flag7 = flag;
		break;
	case 8:
		set->delta_poc8 = value;
		set->used_flag8 = flag;
		break;
	case 9:
		set->delta_poc9 = value;
		set->used_flag9 = flag;
		break;
	case 10:
		set->delta_poc10 = value;
		set->used_flag10 = flag;
		break;
	case 11:
		set->delta_poc11 = value;
		set->used_flag11 = flag;
		break;
	case 12:
		set->delta_poc12 = value;
		set->used_flag12 = flag;
		break;
	case 13:
		set->delta_poc13 = value;
		set->used_flag13 = flag;
		break;
	case 14:
		set->delta_poc14 = value;
		set->used_flag14 = flag;
		break;
	}
}

/*
 * Flip one or more matrices along their main diagonal and flatten them
 * before writing it to the memory.
@@ -120,6 +239,211 @@ void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_hevc_run *run,
	       sizeof(struct v4l2_ctrl_hevc_scaling_matrix));
}

static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps)
{
	const struct v4l2_ctrl_hevc_sps *sps = run->sps;

	if (!run->ext_sps_lt_rps)
		return;

	for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) {
		rps->refs[i].lt_ref_pic_poc_lsb =
			run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps;
		rps->refs[i].used_by_curr_pic_lt_flag =
			!!(run->ext_sps_lt_rps[i].flags & V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT);
	}
}

static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
					   struct calculated_rps_st_set *calculated_rps_st_sets)
{
	const struct v4l2_ctrl_hevc_sps *sps = run->sps;

	for (int i = 0; i < sps->num_short_term_ref_pic_sets; i++) {
		int poc = 0;
		int j = 0;
		const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i];

		rps->short_term_ref_sets[i].num_negative = set->num_negative_pics;
		rps->short_term_ref_sets[i].num_positive = set->num_positive_pics;

		for (; j < set->num_negative_pics; j++) {
			set_ref_poc(&rps->short_term_ref_sets[i], j,
				    set->delta_poc_s0[j], set->used_by_curr_pic_s0[j]);
		}
		poc = j;

		for (j = 0; j < set->num_positive_pics; j++) {
			set_ref_poc(&rps->short_term_ref_sets[i], poc + j,
				    set->delta_poc_s1[j], set->used_by_curr_pic_s1[j]);
		}
	}
}

/*
 * Compute the short term ref pic set parameters based on its reference short term ref pic
 */
static void st_ref_pic_set_prediction(struct rkvdec_hevc_run *run, int idx,
				      struct calculated_rps_st_set *calculated_rps_st_sets)
{
	const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
	struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx];
	struct calculated_rps_st_set *ref_rps;
	u8 st_rps_idx = idx;
	u8 ref_rps_idx = 0;
	s16 delta_rps = 0;
	u8 use_delta_flag[16] = { 0 };
	u8 used_by_curr_pic_flag[16] = { 0 };
	int i, j;
	int dPoc;

	ref_rps_idx = st_rps_idx - (rps_data->delta_idx_minus1 + 1); /* 7-59 */
	delta_rps = (1 - 2 * rps_data->delta_rps_sign) *
		   (rps_data->abs_delta_rps_minus1 + 1); /* 7-60 */

	ref_rps = &calculated_rps_st_sets[ref_rps_idx];

	for (j = 0; j <= ref_rps->num_delta_pocs; j++) {
		used_by_curr_pic_flag[j] = !!(rps_data->used_by_curr_pic & (1 << j));
		use_delta_flag[j] = !!(rps_data->use_delta_flag & (1 << j));
	}

	/* 7-61: calculate num_negative_pics, delta_poc_s0 and used_by_curr_pic_s0 */
	i = 0;
	for (j = (ref_rps->num_positive_pics - 1); j >= 0; j--) {
		dPoc = ref_rps->delta_poc_s1[j] + delta_rps;
		if (dPoc < 0 && use_delta_flag[ref_rps->num_negative_pics + j]) {
			st_rps->delta_poc_s0[i] = dPoc;
			st_rps->used_by_curr_pic_s0[i++] =
				used_by_curr_pic_flag[ref_rps->num_negative_pics + j];
		}
	}
	if (delta_rps < 0 && use_delta_flag[ref_rps->num_delta_pocs]) {
		st_rps->delta_poc_s0[i] = delta_rps;
		st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs];
	}
	for (j = 0; j < ref_rps->num_negative_pics; j++) {
		dPoc = ref_rps->delta_poc_s0[j] + delta_rps;
		if (dPoc < 0 && use_delta_flag[j]) {
			st_rps->delta_poc_s0[i] = dPoc;
			st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[j];
		}
	}
	st_rps->num_negative_pics = i;

	/* 7-62: calculate num_positive_pics, delta_poc_s1 and used_by_curr_pic_s1 */
	i = 0;
	for (j = (ref_rps->num_negative_pics - 1); j >= 0; j--) {
		dPoc = ref_rps->delta_poc_s0[j] + delta_rps;
		if (dPoc > 0 && use_delta_flag[j]) {
			st_rps->delta_poc_s1[i] = dPoc;
			st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[j];
		}
	}
	if (delta_rps > 0 && use_delta_flag[ref_rps->num_delta_pocs]) {
		st_rps->delta_poc_s1[i] = delta_rps;
		st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs];
	}
	for (j = 0; j < ref_rps->num_positive_pics; j++) {
		dPoc = ref_rps->delta_poc_s1[j] + delta_rps;
		if (dPoc > 0 && use_delta_flag[ref_rps->num_negative_pics + j]) {
			st_rps->delta_poc_s1[i] = dPoc;
			st_rps->used_by_curr_pic_s1[i++] =
				used_by_curr_pic_flag[ref_rps->num_negative_pics + j];
		}
	}
	st_rps->num_positive_pics = i;

	st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics;
}

/*
 * Compute the short term ref pic set parameters based on the control's data.
 */
static void st_ref_pic_set_calculate(struct rkvdec_hevc_run *run, int idx,
				     struct calculated_rps_st_set *calculated_rps_st_sets)
{
	const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
	struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx];
	int j, i = 0;

	/* 7-63 */
	st_rps->num_negative_pics = rps_data->num_negative_pics;
	/* 7-64 */
	st_rps->num_positive_pics = rps_data->num_positive_pics;

	for (i = 0; i < st_rps->num_negative_pics; i++) {
		/* 7-65 */
		st_rps->used_by_curr_pic_s0[i] = !!(rps_data->used_by_curr_pic & (1 << i));

		if (i == 0) {
			/* 7-67 */
			st_rps->delta_poc_s0[i] = -(rps_data->delta_poc_s0_minus1[i] + 1);
		} else {
			/* 7-69 */
			st_rps->delta_poc_s0[i] =
				st_rps->delta_poc_s0[i - 1] -
				(rps_data->delta_poc_s0_minus1[i] + 1);
		}
	}

	for (j = 0; j < st_rps->num_positive_pics; j++) {
		/* 7-66 */
		st_rps->used_by_curr_pic_s1[j] = !!(rps_data->used_by_curr_pic & (1 << (i + j)));

		if (j == 0) {
			/* 7-68 */
			st_rps->delta_poc_s1[j] = rps_data->delta_poc_s1_minus1[j] + 1;
		} else {
			/* 7-70 */
			st_rps->delta_poc_s1[j] =
				st_rps->delta_poc_s1[j - 1] +
				(rps_data->delta_poc_s1_minus1[j] + 1);
		}
	}

	/* 7-71 */
	st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics;
}

static void rkvdec_hevc_prepare_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
					  struct v4l2_ctrl_hevc_ext_sps_st_rps *cache)
{
	int idx;

	if (!run->ext_sps_st_rps)
		return;

	if (!memcmp(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps)))
		return;

	struct calculated_rps_st_set *calculated_rps_st_sets =
		kzalloc(sizeof(struct calculated_rps_st_set) *
			run->sps->num_short_term_ref_pic_sets, GFP_KERNEL);

	for (idx = 0; idx < run->sps->num_short_term_ref_pic_sets; idx++) {
		const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];

		if (rps_data->flags & V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED)
			st_ref_pic_set_prediction(run, idx, calculated_rps_st_sets);
		else
			st_ref_pic_set_calculate(run, idx, calculated_rps_st_sets);
	}

	rkvdec_hevc_assemble_hw_st_rps(run, rps, calculated_rps_st_sets);

	kfree(calculated_rps_st_sets);

	memcpy(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps));
}

void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
				 struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache)
{
	rkvdec_hevc_prepare_hw_st_rps(run, rps, st_cache);
	rkvdec_hevc_assemble_hw_lt_rps(run, rps);
}

struct vb2_buffer *
get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run,
	    unsigned int dpb_idx)
@@ -201,5 +525,16 @@ void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx,
			      V4L2_CID_STATELESS_HEVC_SCALING_MATRIX);
	run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;

	if (ctx->has_sps_st_rps) {
		ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
				      V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS);
		run->ext_sps_st_rps = ctrl ? ctrl->p_cur.p : NULL;
	}
	if (ctx->has_sps_lt_rps) {
		ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
				      V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS);
		run->ext_sps_lt_rps = ctrl ? ctrl->p_cur.p : NULL;
	}

	rkvdec_run_preamble(ctx, &run->base);
}
+59 −0
Original line number Diff line number Diff line
@@ -16,9 +16,58 @@
 */

#include <media/v4l2-mem2mem.h>
#include <linux/types.h>

#include "rkvdec.h"

struct rkvdec_rps_refs {
	u16 lt_ref_pic_poc_lsb;
	u16 used_by_curr_pic_lt_flag	: 1;
	u16 reserved			: 15;
} __packed;

struct rkvdec_rps_short_term_ref_set {
	u32 num_negative	: 4;
	u32 num_positive	: 4;
	u32 delta_poc0		: 16;
	u32 used_flag0		: 1;
	u32 delta_poc1		: 16;
	u32 used_flag1		: 1;
	u32 delta_poc2		: 16;
	u32 used_flag2		: 1;
	u32 delta_poc3		: 16;
	u32 used_flag3		: 1;
	u32 delta_poc4		: 16;
	u32 used_flag4		: 1;
	u32 delta_poc5		: 16;
	u32 used_flag5		: 1;
	u32 delta_poc6		: 16;
	u32 used_flag6		: 1;
	u32 delta_poc7		: 16;
	u32 used_flag7		: 1;
	u32 delta_poc8		: 16;
	u32 used_flag8		: 1;
	u32 delta_poc9		: 16;
	u32 used_flag9		: 1;
	u32 delta_poc10		: 16;
	u32 used_flag10		: 1;
	u32 delta_poc11		: 16;
	u32 used_flag11		: 1;
	u32 delta_poc12		: 16;
	u32 used_flag12		: 1;
	u32 delta_poc13		: 16;
	u32 used_flag13		: 1;
	u32 delta_poc14		: 16;
	u32 used_flag14		: 1;
	u32 reserved_bits	: 25;
	u32 reserved[3];
} __packed;

struct rkvdec_rps {
	struct rkvdec_rps_refs refs[32];
	struct rkvdec_rps_short_term_ref_set short_term_ref_sets[64];
} __packed;

struct rkvdec_hevc_run {
	struct rkvdec_run base;
	const struct v4l2_ctrl_hevc_slice_params *slices_params;
@@ -26,6 +75,8 @@ struct rkvdec_hevc_run {
	const struct v4l2_ctrl_hevc_sps *sps;
	const struct v4l2_ctrl_hevc_pps *pps;
	const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix;
	const struct v4l2_ctrl_hevc_ext_sps_st_rps *ext_sps_st_rps;
	const struct v4l2_ctrl_hevc_ext_sps_lt_rps *ext_sps_lt_rps;
	int num_slices;
};

@@ -36,6 +87,14 @@ struct scaling_factor {
	u8 reserved[4];		/*16Bytes align*/
};

void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
			   u16 width, u16 height, s32 pic_in_cts_width,
			   s32 pic_in_cts_height, u16 *column_width, u16 *row_height);
void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
			       u16 width, u16 height, s32 pic_in_cts_width,
			       s32 pic_in_cts_height, u16 *column_width, u16 *row_height);
void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
				 struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache);
void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_hevc_run *run,
					  struct scaling_factor *scaling_factor,
					  struct v4l2_ctrl_hevc_scaling_matrix *cache);
+640 −0

File added.

Preview size limit exceeded, changes collapsed.

+82 −0
Original line number Diff line number Diff line
@@ -153,6 +153,16 @@ static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl)
	enum rkvdec_image_fmt image_fmt;
	struct vb2_queue *vq;

	if (ctrl->id == V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS) {
		ctx->has_sps_st_rps |= !!(ctrl->has_changed);
		return 0;
	}

	if (ctrl->id == V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS) {
		ctx->has_sps_lt_rps |= !!(ctrl->has_changed);
		return 0;
	}

	/* Check if this change requires a capture format reset */
	if (!desc->ops->get_image_fmt)
		return 0;
@@ -226,6 +236,62 @@ static const struct rkvdec_ctrls rkvdec_hevc_ctrls = {
	.num_ctrls = ARRAY_SIZE(rkvdec_hevc_ctrl_descs),
};

static const struct rkvdec_ctrl_desc vdpu38x_hevc_ctrl_descs[] = {
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
	},
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_SPS,
		.cfg.ops = &rkvdec_ctrl_ops,
	},
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_PPS,
	},
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
	},
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
		.cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
		.cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
		.cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
	},
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE,
		.cfg.min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
		.cfg.def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
		.cfg.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
	},
	{
		.cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
		.cfg.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
		.cfg.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
		.cfg.menu_skip_mask =
			BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
		.cfg.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
	},
	{
		.cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
		.cfg.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
		.cfg.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
	},
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS,
		.cfg.ops = &rkvdec_ctrl_ops,
		.cfg.dims = { 65 },
	},
	{
		.cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS,
		.cfg.ops = &rkvdec_ctrl_ops,
		.cfg.dims = { 65 },
	},
};

static const struct rkvdec_ctrls vdpu38x_hevc_ctrls = {
	.ctrls = vdpu38x_hevc_ctrl_descs,
	.num_ctrls = ARRAY_SIZE(vdpu38x_hevc_ctrl_descs),
};

static const struct rkvdec_decoded_fmt_desc rkvdec_hevc_decoded_fmts[] = {
	{
		.fourcc = V4L2_PIX_FMT_NV12,
@@ -445,6 +511,22 @@ static const struct rkvdec_coded_fmt_desc rk3288_coded_fmts[] = {
};

static const struct rkvdec_coded_fmt_desc vdpu381_coded_fmts[] = {
	{
		.fourcc = V4L2_PIX_FMT_HEVC_SLICE,
		.frmsize = {
			.min_width = 64,
			.max_width = 65472,
			.step_width = 64,
			.min_height = 64,
			.max_height = 65472,
			.step_height = 16,
		},
		.ctrls = &vdpu38x_hevc_ctrls,
		.ops = &rkvdec_vdpu381_hevc_fmt_ops,
		.num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts),
		.decoded_fmts = rkvdec_hevc_decoded_fmts,
		.subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
	},
	{
		.fourcc = V4L2_PIX_FMT_H264_SLICE,
		.frmsize = {
Loading