Subject: [PATCH 02/14] media: uapi: Add H.264 stateless encode support

From: Paul Kocialkowski <[email-masked]>
Date: Fri, 22 May 2026 12:16:41 +0200

This introduces the stateless H.264 encode params control that allows
configuring an H.264 encode run.

Note that the current uAPI does not support explicit passing of
references and works following the sliding window decoded reference
picture marking process. This may change in the future.

Signed-off-by: Paul Kocialkowski <[email-masked]>
---
 drivers/media/v4l2-core/v4l2-ctrls-core.c | 62 +++++++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ctrls-defs.c |  4 ++
 include/media/v4l2-ctrls.h                |  2 +
 include/uapi/linux/v4l2-controls.h        | 33 ++++++++++++
 include/uapi/linux/videodev2.h            |  1 +
 5 files changed, 102 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 85d07ef44f62..48217811b0f1 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -382,6 +382,9 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
 	case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS:
 		pr_cont("H264_PRED_WEIGHTS");
 		break;
+	case V4L2_CTRL_TYPE_H264_ENCODE_PARAMS:
+		pr_cont("H264_ENCODE_PARAMS");
+		break;
 	case V4L2_CTRL_TYPE_FWHT_PARAMS:
 		pr_cont("FWHT_PARAMS");
 		break;
@@ -880,6 +883,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 	struct v4l2_ctrl_h264_pred_weights *p_h264_pred_weights;
 	struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
 	struct v4l2_ctrl_h264_decode_params *p_h264_dec_params;
+	struct v4l2_ctrl_h264_encode_params *p_h264_enc_params;
 	struct v4l2_ctrl_hevc_sps *p_hevc_sps;
 	struct v4l2_ctrl_hevc_pps *p_hevc_pps;
 	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
@@ -1102,6 +1106,61 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 		zero_reserved(*p_h264_dec_params);
 		break;
 
+	case V4L2_CTRL_TYPE_H264_ENCODE_PARAMS:
+		p_h264_enc_params = p;
+
+		if (p_h264_enc_params->slice_type != V4L2_H264_SLICE_TYPE_B)
+			p_h264_enc_params->flags &=
+				~V4L2_H264_ENCODE_FLAG_DIRECT_SPATIAL_MV_PRED;
+		if (!(p_h264_enc_params->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC) ||
+		    !p_h264_enc_params->nal_ref_idc)
+			p_h264_enc_params->flags &=
+				~V4L2_H264_ENCODE_FLAG_LONG_TERM_REFERENCE;
+
+		if (p_h264_enc_params->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC &&
+		    p_h264_enc_params->slice_type != V4L2_H264_SLICE_TYPE_I)
+			return -EINVAL;
+		if (p_h264_enc_params->colour_plane_id > 2)
+			return -EINVAL;
+		if (p_h264_enc_params->cabac_init_idc > 2)
+			return -EINVAL;
+		if (p_h264_enc_params->disable_deblocking_filter_idc > 2)
+			return -EINVAL;
+		if (p_h264_enc_params->slice_alpha_c0_offset_div2 < -6 ||
+		    p_h264_enc_params->slice_alpha_c0_offset_div2 > 6)
+			return -EINVAL;
+		if (p_h264_enc_params->slice_beta_offset_div2 < -6 ||
+		    p_h264_enc_params->slice_beta_offset_div2 > 6)
+			return -EINVAL;
+
+		if (p_h264_enc_params->slice_type == V4L2_H264_SLICE_TYPE_I ||
+		    p_h264_enc_params->slice_type == V4L2_H264_SLICE_TYPE_SI)
+			p_h264_enc_params->num_ref_idx_l0_active_minus1 = 0;
+		if (p_h264_enc_params->slice_type != V4L2_H264_SLICE_TYPE_B)
+			p_h264_enc_params->num_ref_idx_l1_active_minus1 = 0;
+
+		if (p_h264_enc_params->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC) {
+			p_h264_enc_params->frame_num = 0;
+			p_h264_enc_params->pic_order_cnt_lsb = 0;
+			p_h264_enc_params->delta_pic_order_cnt_bottom = 0;
+			p_h264_enc_params->delta_pic_order_cnt0 = 0;
+			p_h264_enc_params->delta_pic_order_cnt1 = 0;
+		} else {
+			p_h264_enc_params->idr_pic_id = 0;
+		}
+
+		if (p_h264_enc_params->num_ref_idx_l0_active_minus1 >
+		    (V4L2_H264_REF_LIST_LEN - 1))
+			return -EINVAL;
+		if (p_h264_enc_params->num_ref_idx_l1_active_minus1 >
+		    (V4L2_H264_REF_LIST_LEN - 1))
+			return -EINVAL;
+		memset(&p_h264_enc_params->reserved0, 0,
+		       sizeof(p_h264_enc_params->reserved0));
+		memset(&p_h264_enc_params->reserved1, 0,
+		       sizeof(p_h264_enc_params->reserved1));
+		break;
+
 	case V4L2_CTRL_TYPE_VP8_FRAME:
 		p_vp8_frame = p;
 
@@ -1913,6 +1972,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS:
 		elem_size = sizeof(struct v4l2_ctrl_h264_pred_weights);
 		break;
+	case V4L2_CTRL_TYPE_H264_ENCODE_PARAMS:
+		elem_size = sizeof(struct v4l2_ctrl_h264_encode_params);
+		break;
 	case V4L2_CTRL_TYPE_VP8_FRAME:
 		elem_size = sizeof(struct v4l2_ctrl_vp8_frame);
 		break;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
index ad41f65374e2..4fa6dc7a3b00 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
@@ -1216,6 +1216,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_STATELESS_H264_PPS:			return "H264 Picture Parameter Set";
 	case V4L2_CID_STATELESS_H264_SCALING_MATRIX:		return "H264 Scaling Matrix";
 	case V4L2_CID_STATELESS_H264_PRED_WEIGHTS:		return "H264 Prediction Weight Table";
+	case V4L2_CID_STATELESS_H264_ENCODE_PARAMS:		return "H264 Encode Parameters";
 	case V4L2_CID_STATELESS_H264_SLICE_PARAMS:		return "H264 Slice Parameters";
 	case V4L2_CID_STATELESS_H264_DECODE_PARAMS:		return "H264 Decode Parameters";
 	case V4L2_CID_STATELESS_FWHT_PARAMS:			return "FWHT Stateless Parameters";
@@ -1555,6 +1556,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_STATELESS_H264_PRED_WEIGHTS:
 		*type = V4L2_CTRL_TYPE_H264_PRED_WEIGHTS;
 		break;
+	case V4L2_CID_STATELESS_H264_ENCODE_PARAMS:
+		*type = V4L2_CTRL_TYPE_H264_ENCODE_PARAMS;
+		break;
 	case V4L2_CID_STATELESS_VP8_FRAME:
 		*type = V4L2_CTRL_TYPE_VP8_FRAME;
 		break;
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 31fc1bee3797..54133cda7bc7 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -42,6 +42,7 @@ struct video_device;
  * @p_h264_scaling_matrix:	Pointer to a struct v4l2_ctrl_h264_scaling_matrix.
  * @p_h264_slice_params:	Pointer to a struct v4l2_ctrl_h264_slice_params.
  * @p_h264_decode_params:	Pointer to a struct v4l2_ctrl_h264_decode_params.
+ * @p_h264_encode_params:	Pointer to a struct v4l2_ctrl_h264_encode_params.
  * @p_h264_pred_weights:	Pointer to a struct v4l2_ctrl_h264_pred_weights.
  * @p_vp8_frame:		Pointer to a VP8 frame params structure.
  * @p_vp9_compressed_hdr_probs:	Pointer to a VP9 frame compressed header probs structure.
@@ -76,6 +77,7 @@ union v4l2_ctrl_ptr {
 	struct v4l2_ctrl_h264_scaling_matrix *p_h264_scaling_matrix;
 	struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
 	struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
+	struct v4l2_ctrl_h264_encode_params *p_h264_encode_params;
 	struct v4l2_ctrl_h264_pred_weights *p_h264_pred_weights;
 	struct v4l2_ctrl_vp8_frame *p_vp8_frame;
 	struct v4l2_ctrl_hevc_sps *p_hevc_sps;
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 2d30107e047e..9add059ca02a 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1568,6 +1568,7 @@ struct v4l2_h264_reference {
 
 #define V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED		0x01
 #define V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH			0x02
+#define V4L2_H264_SLICE_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE	0x04
 
 #define V4L2_CID_STATELESS_H264_SLICE_PARAMS	(V4L2_CID_CODEC_STATELESS_BASE + 6)
 /**
@@ -1707,6 +1708,38 @@ struct v4l2_ctrl_h264_decode_params {
 	__u32 flags;
 };
 
+#define V4L2_H264_ENCODE_FLAG_IDR_PIC				0x01
+#define V4L2_H264_ENCODE_FLAG_FIELD_PIC				0x02
+#define V4L2_H264_ENCODE_FLAG_BOTTOM_FIELD			0x04
+#define V4L2_H264_ENCODE_FLAG_DIRECT_SPATIAL_MV_PRED		0x08
+#define V4L2_H264_ENCODE_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE	0x10
+#define V4L2_H264_ENCODE_FLAG_NO_OUTPUT_OF_PRIOR_PICS		0x20
+#define V4L2_H264_ENCODE_FLAG_LONG_TERM_REFERENCE		0x40
+
+#define V4L2_CID_STATELESS_H264_ENCODE_PARAMS	(V4L2_CID_CODEC_STATELESS_BASE + 8)
+struct v4l2_ctrl_h264_encode_params {
+	__u8 nal_ref_idc;
+	__u8 slice_type;
+	__u8 pic_parameter_set_id;
+	__u8 colour_plane_id;
+	__u16 frame_num;
+	__u16 idr_pic_id;
+	__u16 pic_order_cnt_lsb;
+	__u8 reserved0[2];
+	__s32 delta_pic_order_cnt_bottom;
+	__s32 delta_pic_order_cnt0;
+	__s32 delta_pic_order_cnt1;
+	__u8 num_ref_idx_l0_active_minus1;
+	__u8 num_ref_idx_l1_active_minus1;
+	__u8 cabac_init_idc;
+	__s8 slice_qp_delta;
+	__u8 disable_deblocking_filter_idc;
+	__s8 slice_alpha_c0_offset_div2;
+	__s8 slice_beta_offset_div2;
+	__u8 reserved1[6];
+	__u32 flags; /* V4L2_H264_ENCODE_FLAG_ */
+};
+
 /* Stateless FWHT control, used by the vicodec driver */
 
 /* Current FWHT version */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index becd08fdbddb..1d5e27b65544 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1964,6 +1964,7 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_H264_SLICE_PARAMS    = 0x0203,
 	V4L2_CTRL_TYPE_H264_DECODE_PARAMS   = 0x0204,
 	V4L2_CTRL_TYPE_H264_PRED_WEIGHTS    = 0x0205,
+	V4L2_CTRL_TYPE_H264_ENCODE_PARAMS   = 0x0206,
 
 	V4L2_CTRL_TYPE_FWHT_PARAMS	    = 0x0220,
 
-- 
2.53.0