* [PATCH 06/14] media: h264: Add stateless encode reference management
From: Paul Kocialkowski @ 2026-05-22 10:16 UTC (permalink / raw)
To: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Nicolas Dufresne,
Benjamin Gaignard, Philipp Zabel, Mauro Carvalho Chehab,
Hans Verkuil, Marco Felsch, Michael Tretter, Paul Kocialkowski
In-Reply-To: <20260522101653.2565125-1-paulk@sys-base.io>
The H.264 stateless encode reference management implementation is
responsible for allocating and tracking reconstruction buffers that
need to be used by hardware as well as track references in the DPB
following the sliding window decoded reference picture marking process.
It is also responsible for building the L0 and L1 reference lists
from the DPB, using the common v4l2 h264 reflist builder.
Signed-off-by: Paul Kocialkowski <paulk@sys-base.io>
---
drivers/media/v4l2-core/v4l2-h264-enc.c | 418 ++++++++++++++++++++++++
include/media/v4l2-h264-enc.h | 33 ++
2 files changed, 451 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-h264-enc.c b/drivers/media/v4l2-core/v4l2-h264-enc.c
index 0b46922d1d7a..3b7bca117818 100644
--- a/drivers/media/v4l2-core/v4l2-h264-enc.c
+++ b/drivers/media/v4l2-core/v4l2-h264-enc.c
@@ -12,9 +12,90 @@
#include <media/v4l2-h264-enc-rbsp.h>
#include <media/videobuf2-v4l2.h>
+static int rec_buffer_alloc(struct v4l2_h264_enc *enc,
+ struct v4l2_h264_enc_rec_buffer *buffer)
+{
+ int ret;
+
+ ret = v4l2_h264_enc_op(enc, rec_buffer_alloc, buffer);
+ if (ret)
+ return ret;
+
+ buffer->allocated = true;
+ enc->ref.slots_count++;
+
+ return 0;
+}
+
+static void rec_buffer_free(struct v4l2_h264_enc *enc,
+ struct v4l2_h264_enc_rec_buffer *buffer)
+{
+ if (WARN_ON(!enc->ref.slots_count))
+ return;
+
+ v4l2_h264_enc_op(enc, rec_buffer_free, buffer);
+
+ buffer->allocated = false;
+ enc->ref.slots_count--;
+}
+
+static int rec_buffers_alloc(struct v4l2_h264_enc *enc,
+ unsigned int slots_count)
+{
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ unsigned int i;
+ int ret;
+
+ ret = rec_buffer_alloc(enc, &ref->buffer_current);
+ if (ret)
+ return ret;
+
+ if (!(enc->flags & (V4L2_H264_ENC_FLAG_INTER_PRED |
+ V4L2_H264_ENC_FLAG_INTER_BIPRED)))
+ return 0;
+
+ for (i = 0; i < slots_count; i++) {
+ ret = rec_buffer_alloc(enc, &ref->buffers[i]);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ while (i > 0) {
+ i--;
+ rec_buffer_free(enc, &ref->buffers[i]);
+ }
+
+ rec_buffer_free(enc, &ref->buffer_current);
+
+ return ret;
+}
+
+static void rec_buffers_free(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ unsigned int i;
+
+ rec_buffer_free(enc, &ref->buffer_current);
+
+ if (!(enc->flags & (V4L2_H264_ENC_FLAG_INTER_PRED |
+ V4L2_H264_ENC_FLAG_INTER_BIPRED)))
+ return;
+
+ for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
+ if (!ref->buffers[i].allocated)
+ continue;
+
+ rec_buffer_free(enc, &ref->buffers[i]);
+ }
+}
+
int v4l2_h264_enc_init(struct v4l2_h264_enc *enc)
{
struct v4l2_h264_enc_rbsp *rbsp = &enc->rbsp;
+ unsigned int slots_count = 0;
int ret;
if ((!enc->format && !enc->format_mplane) || !enc->timeperframe ||
@@ -25,6 +106,23 @@ int v4l2_h264_enc_init(struct v4l2_h264_enc *enc)
memset(&enc->state_next, 0, sizeof(enc->state_next));
enc->state_serial = 0;
+ memset(&enc->ref, 0, sizeof(enc->ref));
+
+ if (enc->flags & (V4L2_H264_ENC_FLAG_INTER_PRED |
+ V4L2_H264_ENC_FLAG_INTER_BIPRED)) {
+ if (!enc->ref_slots_count_init)
+ slots_count = V4L2_H264_NUM_DPB_ENTRIES / 2;
+ else if (WARN_ON(enc->ref_slots_count_init >
+ V4L2_H264_NUM_DPB_ENTRIES))
+ slots_count = V4L2_H264_NUM_DPB_ENTRIES;
+ else
+ slots_count = enc->ref_slots_count_init;
+ }
+
+ ret = rec_buffers_alloc(enc, slots_count);
+ if (ret)
+ return ret;
+
rbsp->ops = enc->rbsp_ops;
rbsp->private_data = enc->private_data;
@@ -34,6 +132,7 @@ EXPORT_SYMBOL_GPL(v4l2_h264_enc_init);
void v4l2_h264_enc_exit(struct v4l2_h264_enc *enc)
{
+ rec_buffers_free(enc);
}
EXPORT_SYMBOL_GPL(v4l2_h264_enc_exit);
@@ -665,6 +764,317 @@ static int rbsp_step(struct v4l2_h264_enc *enc,
return 0;
}
+static struct v4l2_h264_enc_rec_buffer *
+ref_step_buffers_slot_find(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_rec_buffer *buffer;
+ unsigned int i;
+
+ for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
+ buffer = &enc->ref.buffers[i];
+
+ if (buffer->allocated)
+ continue;
+
+ return buffer;
+ }
+
+ return NULL;
+}
+
+static int ref_step_buffers(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_sps *sps = &state->sps;
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ struct v4l2_h264_enc_rec_buffer *buffer;
+ unsigned int count;
+ unsigned int i;
+ int ret;
+
+ /*
+ * Only increase the number of slots. It avoids the cost of free
+ * (including on the first frame) and the cost of possible future
+ * allocations, at the expense of memory.
+ */
+ if (sps->max_num_ref_frames <= ref->slots_count)
+ return 0;
+
+ count = sps->max_num_ref_frames - ref->slots_count;
+
+ for (i = 0; i < count; i++) {
+ buffer = ref_step_buffers_slot_find(enc);
+ if (WARN_ON(!buffer))
+ return -ENOMEM;
+
+ ret = rec_buffer_alloc(enc, buffer);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ref_step_poc(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_sps *sps = &state->sps;
+ struct v4l2_ctrl_h264_pps *pps = &state->pps;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ unsigned int pic_order_cnt_lsb;
+ unsigned int pic_order_cnt_msb;
+ unsigned int prev_pic_order_cnt_msb;
+ unsigned int prev_pic_order_cnt_lsb;
+ unsigned int max_pic_order_cnt_lsb;
+ unsigned int delta_pic_order_cnt_bottom;
+ unsigned int top_field_order_cnt;
+ unsigned int bottom_field_order_cnt;
+
+ /* Only pic_order_cnt_type = 0 is currently supported. */
+ if (sps->pic_order_cnt_type)
+ return -EINVAL;
+
+ max_pic_order_cnt_lsb = BIT(sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
+ pic_order_cnt_lsb = encode->pic_order_cnt_lsb;
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC) {
+ prev_pic_order_cnt_msb = 0;
+ prev_pic_order_cnt_lsb = 0;
+ } else {
+ prev_pic_order_cnt_msb = ref->prev_pic_order_cnt_msb;
+ prev_pic_order_cnt_lsb = ref->prev_pic_order_cnt_lsb;
+ }
+
+ if ((pic_order_cnt_lsb < prev_pic_order_cnt_lsb) &&
+ ((prev_pic_order_cnt_lsb - pic_order_cnt_lsb) >=
+ (max_pic_order_cnt_lsb / 2)))
+ pic_order_cnt_msb = prev_pic_order_cnt_msb +
+ max_pic_order_cnt_lsb;
+ else if ((pic_order_cnt_lsb > prev_pic_order_cnt_lsb) &&
+ ((pic_order_cnt_lsb - prev_pic_order_cnt_lsb) >
+ (max_pic_order_cnt_lsb / 2)))
+ pic_order_cnt_msb = prev_pic_order_cnt_msb -
+ max_pic_order_cnt_lsb;
+ else
+ pic_order_cnt_msb = prev_pic_order_cnt_msb;
+
+ top_field_order_cnt = pic_order_cnt_msb + pic_order_cnt_lsb;
+
+ if (pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT)
+ delta_pic_order_cnt_bottom = encode->delta_pic_order_cnt_bottom;
+ else
+ delta_pic_order_cnt_bottom = 0;
+
+ if (!(encode->flags & V4L2_H264_ENCODE_FLAG_FIELD_PIC))
+ bottom_field_order_cnt = top_field_order_cnt +
+ delta_pic_order_cnt_bottom;
+ else
+ bottom_field_order_cnt = top_field_order_cnt;
+
+ ref->pic_order_cnt_msb = pic_order_cnt_msb;
+ ref->pic_order_cnt_lsb = pic_order_cnt_lsb;
+ ref->top_field_order_cnt = top_field_order_cnt;
+ ref->bottom_field_order_cnt = bottom_field_order_cnt;
+ ref->pic_order_cnt = min(top_field_order_cnt, bottom_field_order_cnt);
+
+ return 0;
+}
+
+static int ref_step(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_sps *sps = &state->sps;
+ struct v4l2_ctrl_h264_pps *pps = &state->pps;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ unsigned int l0_active_count_max;
+ unsigned int l1_active_count_max;
+ int ret;
+
+ ret = ref_step_buffers(enc);
+ if (ret)
+ return ret;
+
+ ret = ref_step_poc(enc);
+ if (ret)
+ return ret;
+
+ ref->l0_active_count = 0;
+ ref->l1_active_count = 0;
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_I) {
+ /* Flush the DPB on IDR pictures. */
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC)
+ memset(ref->dpb, 0, sizeof(ref->dpb));
+
+ return 0;
+ }
+
+ /* Generate reference lists. */
+
+ v4l2_h264_init_reflist_builder_gen(&ref->builder, sps, ref->dpb,
+ ref->pic_order_cnt,
+ encode->frame_num,
+ V4L2_H264_FRAME_REF);
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE) {
+ l0_active_count_max = encode->num_ref_idx_l0_active_minus1 + 1;
+ l1_active_count_max = encode->num_ref_idx_l1_active_minus1 + 1;
+ } else {
+ l0_active_count_max =
+ pps->num_ref_idx_l0_default_active_minus1 + 1;
+ l1_active_count_max =
+ pps->num_ref_idx_l1_default_active_minus1 + 1;
+ }
+
+ switch (encode->slice_type) {
+ case V4L2_H264_SLICE_TYPE_P:
+ v4l2_h264_build_p_ref_list(&ref->builder, ref->l0);
+
+ if (ref->builder.num_valid > l0_active_count_max)
+ ref->l0_active_count = l0_active_count_max;
+ else
+ ref->l0_active_count = ref->builder.num_valid;
+
+ WARN_ON(!ref->l0_active_count);
+
+ break;
+ case V4L2_H264_SLICE_TYPE_B:
+ v4l2_h264_build_b_ref_lists(&ref->builder, ref->l0, ref->l1);
+
+ if (ref->builder.num_valid > l0_active_count_max)
+ ref->l0_active_count = l0_active_count_max;
+ else
+ ref->l0_active_count = ref->builder.num_valid;
+
+ WARN_ON(!ref->l0_active_count);
+
+ if (ref->builder.num_valid > l1_active_count_max)
+ ref->l1_active_count = l1_active_count_max;
+ else
+ ref->l1_active_count = ref->builder.num_valid;
+
+ WARN_ON(!ref->l1_active_count);
+
+ break;
+ }
+
+ pr_debug("+ v4l2-h264-enc: ref");
+ pr_debug(" ref active l0: %u, l1: %u", ref->l0_active_count,
+ ref->l1_active_count);
+
+ return 0;
+}
+
+static int ref_complete_slot_find(struct v4l2_h264_enc *enc,
+ unsigned int *index)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_sps *sps = &state->sps;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ unsigned int max_frame_num = BIT(sps->log2_max_frame_num_minus4 + 4);
+ unsigned int frame_num_wrap_smallest = encode->frame_num;
+ unsigned int frame_num_wrap_smallest_index;
+ unsigned int frame_num_wrap;
+ struct v4l2_h264_enc_rec_buffer *buffer;
+ struct v4l2_h264_dpb_entry *dpb_entry;
+ unsigned int i;
+
+ for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
+ buffer = &ref->buffers[i];
+ dpb_entry = &ref->dpb[i];
+
+ if (!buffer->allocated)
+ continue;
+
+ /* Return an unused slot. */
+ if (!(dpb_entry->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) {
+ *index = i;
+ return 0;
+ }
+
+ /* Track the smallest FrameNumWrap value. */
+ if (dpb_entry->frame_num > encode->frame_num)
+ frame_num_wrap = dpb_entry->frame_num - max_frame_num;
+ else
+ frame_num_wrap = dpb_entry->frame_num;
+
+ if (frame_num_wrap < frame_num_wrap_smallest) {
+ frame_num_wrap_smallest = frame_num_wrap;
+ frame_num_wrap_smallest_index = i;
+ }
+ }
+
+ /* Clear the evicted DPB entry. */
+ dpb_entry = &ref->dpb[frame_num_wrap_smallest_index];
+ memset(dpb_entry, 0, sizeof(*dpb_entry));
+
+ *index = frame_num_wrap_smallest_index;
+
+ return 0;
+}
+
+static int ref_complete_swap(struct v4l2_h264_enc *enc, unsigned int index,
+ u64 ts)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ struct v4l2_h264_dpb_entry *dpb_entry = &ref->dpb[index];
+ struct v4l2_h264_enc_rec_buffer *buffer = &ref->buffers[index];
+ struct v4l2_h264_enc_rec_buffer *buffer_current =
+ &ref->buffer_current;
+
+ /* Set the DPB entry of the available slot. */
+ memset(dpb_entry, 0, sizeof(*dpb_entry));
+ dpb_entry->reference_ts = ts;
+ dpb_entry->pic_num = encode->frame_num;
+ dpb_entry->frame_num = encode->frame_num;
+ dpb_entry->fields = V4L2_H264_FRAME_REF;
+ dpb_entry->top_field_order_cnt = ref->top_field_order_cnt;
+ dpb_entry->bottom_field_order_cnt = ref->bottom_field_order_cnt;
+ dpb_entry->flags = V4L2_H264_DPB_ENTRY_FLAG_VALID |
+ V4L2_H264_DPB_ENTRY_FLAG_ACTIVE;
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_LONG_TERM_REFERENCE)
+ dpb_entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM;
+
+ /* Swap the current buffer with the available slot. */
+ swap(*buffer_current, *buffer);
+
+ return 0;
+}
+
+static int ref_complete(struct v4l2_h264_enc *enc,
+ struct vb2_v4l2_buffer *buffer)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_h264_enc_ref *ref = &enc->ref;
+ unsigned int index;
+ int ret;
+
+ if (!encode->nal_ref_idc)
+ return 0;
+
+ /* Keep the POC as last reference. */
+ ref->prev_pic_order_cnt_msb = ref->pic_order_cnt_msb;
+ ref->prev_pic_order_cnt_lsb = ref->pic_order_cnt_lsb;
+
+ ret = ref_complete_slot_find(enc, &index);
+ if (ret)
+ return ret;
+
+ /* Move our current picture to the DPB. */
+ ret = ref_complete_swap(enc, index, buffer->vb2_buf.timestamp);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
int v4l2_h264_enc_step(struct v4l2_h264_enc *enc,
struct vb2_v4l2_buffer *buffer)
{
@@ -682,6 +1092,10 @@ int v4l2_h264_enc_step(struct v4l2_h264_enc *enc,
if (ret)
return ret;
+ ret = ref_step(enc);
+ if (ret)
+ return ret;
+
ret = rbsp_step(enc, buffer);
if (ret)
return ret;
@@ -699,6 +1113,10 @@ int v4l2_h264_enc_complete(struct v4l2_h264_enc *enc,
if (ret)
return ret;
+ ret = ref_complete(enc, buffer);
+ if (ret)
+ return ret;
+
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_h264_enc_complete);
diff --git a/include/media/v4l2-h264-enc.h b/include/media/v4l2-h264-enc.h
index 3d6b97408707..817a9ca2f169 100644
--- a/include/media/v4l2-h264-enc.h
+++ b/include/media/v4l2-h264-enc.h
@@ -35,6 +35,33 @@
struct v4l2_h264_enc;
+struct v4l2_h264_enc_rec_buffer {
+ void *private_data;
+ bool allocated;
+};
+
+struct v4l2_h264_enc_ref {
+ struct v4l2_h264_enc_rec_buffer buffer_current;
+ struct v4l2_h264_enc_rec_buffer buffers[V4L2_H264_NUM_DPB_ENTRIES];
+ struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES];
+ unsigned int slots_count;
+
+ struct v4l2_h264_reference l0[V4L2_H264_REF_LIST_LEN];
+ unsigned int l0_active_count;
+ struct v4l2_h264_reference l1[V4L2_H264_REF_LIST_LEN];
+ unsigned int l1_active_count;
+
+ struct v4l2_h264_reflist_builder builder;
+
+ unsigned int prev_pic_order_cnt_msb;
+ unsigned int prev_pic_order_cnt_lsb;
+ unsigned int pic_order_cnt_msb;
+ unsigned int pic_order_cnt_lsb;
+ unsigned int top_field_order_cnt;
+ unsigned int bottom_field_order_cnt;
+ unsigned int pic_order_cnt;
+};
+
struct v4l2_h264_enc_state {
struct v4l2_ctrl_h264_sps sps;
struct v4l2_h264_sps_video sps_video;
@@ -52,6 +79,10 @@ struct v4l2_h264_enc_state {
struct v4l2_h264_enc_ops {
int (*state_constrain)(struct v4l2_h264_enc *enc,
struct v4l2_h264_enc_state *state);
+ int (*rec_buffer_alloc)(struct v4l2_h264_enc *enc,
+ struct v4l2_h264_enc_rec_buffer *rec_buffer);
+ int (*rec_buffer_free)(struct v4l2_h264_enc *enc,
+ struct v4l2_h264_enc_rec_buffer *rec_buffer);
};
struct v4l2_h264_enc {
@@ -63,11 +94,13 @@ struct v4l2_h264_enc {
struct v4l2_pix_format_mplane *format_mplane;
struct v4l2_fract *timeperframe;
struct v4l2_ctrl_handler *ctrl_handler;
+ unsigned int ref_slots_count_init;
struct v4l2_h264_enc_state state_active;
struct v4l2_h264_enc_state state_next;
unsigned int state_serial;
+ struct v4l2_h264_enc_ref ref;
struct v4l2_h264_enc_rbsp rbsp;
unsigned int rbsp_update;
--
2.53.0
^ permalink raw reply related
* [PATCH 05/14] media: h264: Add stateless encode rbsp
From: Paul Kocialkowski @ 2026-05-22 10:16 UTC (permalink / raw)
To: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Nicolas Dufresne,
Benjamin Gaignard, Philipp Zabel, Mauro Carvalho Chehab,
Hans Verkuil, Marco Felsch, Michael Tretter, Paul Kocialkowski
In-Reply-To: <20260522101653.2565125-1-paulk@sys-base.io>
The H.264 stateless encode rbsp implementation allows generating H.264
bitstream headers to accommodate hardware that does not do it (which is
rather common with stateless encoders).
It currently supports the following NAL unit types: AUD, SPS, PPS,
slice header, with or without start codes.
Dedicated low-level operations are supported in order to accommodate
hardware with dedicated hardware for bitstream storage. It is generally
better to use such mechanisms to ensure slice NAL unit continuation,
especially in unaligned cases.
It is deliberately not too tied to the H.264 stateless encode core so
that it can be reused by stateful drivers that may need to do the same.
Signed-off-by: Paul Kocialkowski <paulk@sys-base.io>
---
drivers/media/v4l2-core/Makefile | 2 +-
drivers/media/v4l2-core/v4l2-h264-enc-rbsp.c | 1173 ++++++++++++++++++
drivers/media/v4l2-core/v4l2-h264-enc.c | 153 +++
include/media/v4l2-h264-enc-rbsp.h | 72 ++
include/media/v4l2-h264-enc.h | 5 +
5 files changed, 1404 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/v4l2-core/v4l2-h264-enc-rbsp.c
create mode 100644 include/media/v4l2-h264-enc-rbsp.h
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index bd319e363c8e..aba9e310f2e5 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -29,7 +29,7 @@ obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
-obj-$(CONFIG_V4L2_H264_ENC) += v4l2-h264-enc.o
+obj-$(CONFIG_V4L2_H264_ENC) += v4l2-h264-enc.o v4l2-h264-enc-rbsp.o
obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o
diff --git a/drivers/media/v4l2-core/v4l2-h264-enc-rbsp.c b/drivers/media/v4l2-core/v4l2-h264-enc-rbsp.c
new file mode 100644
index 000000000000..a165f8114ce9
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-h264-enc-rbsp.c
@@ -0,0 +1,1173 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * V4L2 H.264 Encode RBSP
+ *
+ * Copyright (C) 2025-2026 Paul Kocialkowski <paulk@sys-base.io>
+ */
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/minmax.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/v4l2-controls.h>
+#include <media/v4l2-h264.h>
+#include <media/v4l2-h264-enc-rbsp.h>
+
+int v4l2_h264_enc_rbsp_init(struct v4l2_h264_enc_rbsp *rbsp, u8 *pointer,
+ unsigned int size)
+{
+ rbsp->pointer = pointer;
+ rbsp->size = size;
+
+ rbsp->bit_offset = 0;
+ rbsp->bits_count = 0;
+ rbsp->zero_count = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_init);
+
+unsigned int v4l2_h264_enc_rbsp_bits_count(struct v4l2_h264_enc_rbsp *rbsp)
+{
+ return rbsp->bits_count;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_bits_count);
+
+unsigned int v4l2_h264_enc_rbsp_bytes_count(struct v4l2_h264_enc_rbsp *rbsp)
+{
+ WARN_ON(rbsp->bits_count % 8);
+
+ return rbsp->bits_count / 8;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_bytes_count);
+
+static int v4l2_h264_enc_rbsp_bits_raw(struct v4l2_h264_enc_rbsp *rbsp,
+ u32 value, unsigned char bits_count)
+{
+ unsigned int bits_left = bits_count;
+ unsigned int bits_chunk;
+ unsigned int bit_start;
+ unsigned int bit_stop;
+ u32 value_extract;
+ int ret;
+
+ if (bits_count > 32)
+ return -EINVAL;
+ else if (!bits_count)
+ return 0;
+
+ value &= GENMASK(bits_count - 1, 0);
+
+ /* XXX: Has to manage rbsp->bit_offset and rbsp->bits_count. */
+ ret = v4l2_h264_enc_rbsp_op(rbsp, bits_raw, value, bits_count);
+ if (!ret)
+ return 0;
+ else if (ret != -EOPNOTSUPP)
+ return ret;
+
+ while (bits_left > 0) {
+ if (WARN_ON(rbsp->bit_offset >= 8))
+ return -1;
+
+ bits_chunk = min(8u - rbsp->bit_offset, bits_left);
+
+ bit_stop = bits_left - 1;
+ bit_start = bit_stop + 1 - bits_chunk;
+ value_extract = (value & GENMASK(bit_stop, bit_start)) >>
+ bit_start;
+
+ bit_stop = 7u - rbsp->bit_offset;
+ bit_start = bit_stop + 1 - bits_chunk;
+
+ *rbsp->pointer &= ~GENMASK(bit_stop, bit_start);
+ *rbsp->pointer |= value_extract << bit_start;
+
+ rbsp->bit_offset += bits_chunk;
+
+ if (rbsp->bit_offset == 8) {
+ rbsp->pointer++;
+ rbsp->bit_offset = 0;
+ }
+
+ rbsp->bits_count += bits_chunk;
+
+ if (rbsp->bits_count / 8 >= rbsp->size)
+ return -ENOMEM;
+
+ bits_left -= bits_chunk;
+ }
+
+ WARN_ON(bits_left);
+
+ return 0;
+}
+
+static int eptb_bits(struct v4l2_h264_enc_rbsp *rbsp, unsigned int *bits_left,
+ unsigned int zero_step)
+{
+ unsigned int zero_discard;
+ unsigned int zero_count;
+ unsigned int zero_chunk;
+ int ret;
+
+ if (!zero_step)
+ return 0;
+
+ /* Trailing zeros in a non-zero byte are discarded for EPTB. */
+ if (!rbsp->zero_count && rbsp->bit_offset)
+ zero_discard = min(8u - rbsp->bit_offset, zero_step);
+ else
+ zero_discard = 0;
+
+ /* Append discarded zeros before byte alignment. */
+ if (zero_discard) {
+ ret = v4l2_h264_enc_rbsp_bits_raw(rbsp, 0, zero_discard);
+ if (ret)
+ return ret;
+
+ *bits_left -= zero_discard;
+ zero_step -= zero_discard;
+ }
+
+ /* Count relevant zeros for EPTB (starting at byte alignment). */
+ zero_count = rbsp->zero_count + zero_step;
+
+ /* Append EPTB as many times as necessary. */
+ while (zero_count >= 22) {
+ zero_chunk = 22 - rbsp->zero_count;
+
+ ret = v4l2_h264_enc_rbsp_bits_raw(rbsp, 0, zero_chunk);
+ if (ret)
+ return ret;
+
+ /* Two high bits for 0x3 and 6 other bits for stolen zeros. */
+ ret = v4l2_h264_enc_rbsp_bits_raw(rbsp,
+ V4L2_H264_ENC_RBSP_EPTB << 6,
+ 8);
+ if (ret)
+ return ret;
+
+ /* We get 6 zeros after byte alignment. */
+ rbsp->zero_count = 6;
+
+ *bits_left -= zero_chunk;
+ zero_step -= zero_chunk;
+ zero_count = rbsp->zero_count + zero_step;
+ }
+
+ /* Append remaining zero bits. */
+ if (zero_step) {
+ ret = v4l2_h264_enc_rbsp_bits_raw(rbsp, 0, zero_step);
+ if (ret)
+ return ret;
+
+ *bits_left -= zero_step;
+ }
+
+ rbsp->zero_count = zero_count;
+
+ return ret;
+}
+
+static int v4l2_h264_enc_rbsp_bits(struct v4l2_h264_enc_rbsp *rbsp, u32 value,
+ unsigned int bits_count)
+{
+ unsigned int bits_left = bits_count;
+ unsigned int zero_head;
+ unsigned int zero_tail;
+ int ret;
+
+ if (bits_count > 32)
+ return -EINVAL;
+ else if (!bits_count)
+ return 0;
+
+ ret = v4l2_h264_enc_rbsp_op(rbsp, bits, value, bits_count);
+ if (!ret)
+ return 0;
+ else if (ret != -EOPNOTSUPP)
+ return ret;
+
+ value &= GENMASK(bits_count - 1, 0);
+
+ /* Count heading zeros. */
+ if (value)
+ zero_head = bits_count - __fls(value) - 1;
+ else
+ zero_head = bits_count;
+
+ /* Append heading zeros with EPTB. */
+ ret = eptb_bits(rbsp, &bits_left, zero_head);
+ if (ret)
+ return ret;
+
+ /* A zero value is entirely handled as heading zeros. */
+ if (!bits_left || WARN_ON(!value))
+ return 0;
+
+ /* Count trailing zeros. */
+ zero_tail = __ffs(value);
+
+ /* Append non-tail bits. */
+ ret = v4l2_h264_enc_rbsp_bits_raw(rbsp, value >> zero_tail,
+ bits_left - zero_tail);
+ if (ret)
+ return ret;
+
+ /* Reset zero count after appending non-zero bits. */
+ rbsp->zero_count = 0;
+
+ bits_left = zero_tail;
+ if (!bits_left)
+ return 0;
+
+ /* Append trailing zeros with EPTB. */
+ ret = eptb_bits(rbsp, &bits_left, zero_tail);
+ if (ret)
+ return ret;
+
+ WARN_ON(bits_left);
+
+ return 0;
+}
+
+static int v4l2_h264_enc_rbsp_ue(struct v4l2_h264_enc_rbsp *rbsp, u32 value)
+{
+ unsigned int bits_count;
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_op(rbsp, ue, value);
+ if (!ret)
+ return 0;
+ else if (ret != -EOPNOTSUPP)
+ return ret;
+
+ /*
+ * Exponential-Golomb coding of x stores the value of v + 1.
+ * This takes fls(v + 1) + 1 bits for the non-zero bits and fls(v + 1)
+ * heading zero bits.
+ */
+ value += 1;
+ bits_count = 2 * __fls(value) + 1;
+
+ return v4l2_h264_enc_rbsp_bits(rbsp, value, bits_count);
+}
+
+static int v4l2_h264_enc_rbsp_se(struct v4l2_h264_enc_rbsp *rbsp, s32 value)
+{
+ u32 value_ue;
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_op(rbsp, se, value);
+ if (!ret)
+ return 0;
+ else if (ret != -EOPNOTSUPP)
+ return ret;
+
+ /*
+ * The signed extension represents numbers in Exponential-Golomb
+ * with each positive value followed by its corresponding negative
+ * value in sequence order.
+ */
+
+ if (value > 0)
+ value_ue = 2 * value - 1;
+ else
+ value_ue = -2 * value;
+
+ return v4l2_h264_enc_rbsp_ue(rbsp, value_ue);
+}
+
+static int v4l2_h264_enc_rbsp_align(struct v4l2_h264_enc_rbsp *rbsp)
+{
+ unsigned int zero_count;
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_op(rbsp, align);
+ if (!ret)
+ return 0;
+ else if (ret != -EOPNOTSUPP)
+ return ret;
+
+ zero_count = 8 - rbsp->bit_offset;
+ if (!zero_count)
+ return 0;
+
+ return v4l2_h264_enc_rbsp_bits(rbsp, 0, zero_count);
+}
+
+static int v4l2_h264_enc_rbsp_u32(struct v4l2_h264_enc_rbsp *rbsp, u32 value)
+{
+ return v4l2_h264_enc_rbsp_bits(rbsp, value, 32);
+}
+
+static int v4l2_h264_enc_rbsp_u16(struct v4l2_h264_enc_rbsp *rbsp, u16 value)
+{
+ return v4l2_h264_enc_rbsp_bits(rbsp, value, 16);
+}
+
+static int v4l2_h264_enc_rbsp_u8(struct v4l2_h264_enc_rbsp *rbsp, u8 value)
+{
+ return v4l2_h264_enc_rbsp_bits(rbsp, value, 8);
+}
+
+static int v4l2_h264_enc_rbsp_bit(struct v4l2_h264_enc_rbsp *rbsp, u8 value)
+{
+ return v4l2_h264_enc_rbsp_bits(rbsp, value, 1);
+}
+
+static int v4l2_h264_enc_rbsp_flag(struct v4l2_h264_enc_rbsp *rbsp, u32 flags,
+ u32 flag)
+{
+ return v4l2_h264_enc_rbsp_bit(rbsp, (flags & flag) == flag);
+}
+
+int v4l2_h264_enc_rbsp_start_code(struct v4l2_h264_enc_rbsp *rbsp)
+{
+ /* Start code must be inserted at a byte-aligned position. */
+ if (WARN_ON(rbsp->bit_offset))
+ return -EINVAL;
+
+ return v4l2_h264_enc_rbsp_bits_raw(rbsp, V4L2_H264_START_CODE_ANNEX_B,
+ 32);
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_start_code);
+
+static int v4l2_h264_enc_rbsp_nalu_begin(struct v4l2_h264_enc_rbsp *rbsp,
+ u8 nal_ref_idc, u8 nal_unit_type)
+{
+ u8 forbidden_zero_bit = 0;
+ int ret;
+
+ /* NALU must be inserted at a byte-aligned position. */
+ if (WARN_ON(rbsp->bit_offset))
+ return -EINVAL;
+
+ /* NALU header bits must not be counted in EPTB. */
+
+ ret = v4l2_h264_enc_rbsp_bits_raw(rbsp, forbidden_zero_bit, 1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits_raw(rbsp, nal_ref_idc, 2);
+ if (ret)
+ return ret;
+
+ return v4l2_h264_enc_rbsp_bits_raw(rbsp, nal_unit_type, 5);
+}
+
+static int v4l2_h264_enc_rbsp_nalu_end(struct v4l2_h264_enc_rbsp *rbsp)
+{
+ u8 rbsp_stop_one_bit = 1;
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_bit(rbsp, rbsp_stop_one_bit);
+ if (ret)
+ return ret;
+
+ return v4l2_h264_enc_rbsp_align(rbsp);
+}
+
+int v4l2_h264_enc_rbsp_aud(struct v4l2_h264_enc_rbsp *rbsp, u8 primary_pic_type)
+{
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_nalu_begin(rbsp, 0, V4L2_H264_NALU_TYPE_AUD);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, primary_pic_type, 3);
+ if (ret)
+ return ret;
+
+ return v4l2_h264_enc_rbsp_nalu_end(rbsp);
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_aud);
+
+static int v4l2_h264_enc_rbsp_sps_hrd(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_h264_sps_video_hrd *hrd)
+{
+ unsigned int i;
+ int ret;
+
+ if (hrd->cpb_cnt_minus1 > 31)
+ return -EINVAL;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, hrd->cpb_cnt_minus1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, hrd->bit_rate_scale, 4);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, hrd->cpb_size_scale, 4);
+ if (ret)
+ return ret;
+
+ for (i = 0; i <= hrd->cpb_cnt_minus1; i++) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ hrd->bit_rate_value_minus1[i]);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ hrd->cpb_size_value_minus1[i]);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bit(rbsp, hrd->cbr_flag[i]);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp,
+ hrd->initial_cpb_removal_delay_length_minus1,
+ 5);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp,
+ hrd->cpb_removal_delay_length_minus1, 5);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, hrd->dpb_output_delay_length_minus1,
+ 5);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, hrd->time_offset_length, 5);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int v4l2_h264_enc_rbsp_sps_vui(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_h264_sps_video *sps_video)
+{
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_ASPECT_RATIO_INFO_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_ASPECT_RATIO_INFO_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_u8(rbsp, sps_video->aspect_ratio_idc);
+ if (ret)
+ return ret;
+
+ if (sps_video->aspect_ratio_idc ==
+ V4L2_H264_VUI_ASPECT_RATIO_IDC_EXTENDED) {
+ ret = v4l2_h264_enc_rbsp_u16(rbsp,
+ sps_video->sar_width);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_u16(rbsp,
+ sps_video->sar_height);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_OVERSCAN_INFO_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_OVERSCAN_INFO_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_OVERSCAN_APPROPRIATE);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_VIDEO_SIGNAL_TYPE_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_VIDEO_SIGNAL_TYPE_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, sps_video->video_format, 3);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_VIDEO_FULL_RANGE);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_COLOUR_DESCRIPTION_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_COLOUR_DESCRIPTION_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_u8(rbsp,
+ sps_video->colour_primaries);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_u8(rbsp,
+ sps_video->transfer_characteristics);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_u8(rbsp,
+ sps_video->matrix_coefficients);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_CHROMA_LOC_INFO_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_CHROMA_LOC_INFO_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->chroma_sample_loc_type_top_field);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->chroma_sample_loc_type_bottom_field);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_TIMING_INFO_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_TIMING_INFO_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_u32(rbsp,
+ sps_video->num_units_in_tick);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_u32(rbsp,
+ sps_video->time_scale);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_FIXED_FRAME_RATE);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_NAL_HRD_PARAMETERS_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_NAL_HRD_PARAMETERS_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_sps_hrd(rbsp, &sps_video->nal_hrd);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_VCL_HRD_PARAMETERS_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_VCL_HRD_PARAMETERS_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_sps_hrd(rbsp, &sps_video->vcl_hrd);
+ if (ret)
+ return ret;
+ }
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_NAL_HRD_PARAMETERS_PRESENT ||
+ sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_VCL_HRD_PARAMETERS_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_LOW_DELAY_HRD);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_PIC_STRUCT_PRESENT);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_BITSTREAM_RESTRICTION);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_BITSTREAM_RESTRICTION) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_MOTION_VECTORS_OVER_PIC_BOUNDARIES);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->max_bytes_per_pic_denom);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->max_bits_per_mb_denom);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->log2_max_mv_length_horizontal);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->log2_max_mv_length_vertical);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->max_num_reorder_frames);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->max_dec_frame_buffering);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int v4l2_h264_enc_rbsp_sps(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_ctrl_h264_sps *sps,
+ const struct v4l2_h264_sps_video *sps_video)
+{
+ u8 constraint_set_flags = 0;
+ u8 seq_scaling_matrix_present_flag = 0;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_nalu_begin(rbsp, 0, V4L2_H264_NALU_TYPE_SPS);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_u8(rbsp, sps->profile_idc);
+ if (ret)
+ return ret;
+
+ if (sps->constraint_set_flags & V4L2_H264_SPS_CONSTRAINT_SET0_FLAG)
+ constraint_set_flags |= BIT(7);
+ if (sps->constraint_set_flags & V4L2_H264_SPS_CONSTRAINT_SET1_FLAG)
+ constraint_set_flags |= BIT(6);
+ if (sps->constraint_set_flags & V4L2_H264_SPS_CONSTRAINT_SET2_FLAG)
+ constraint_set_flags |= BIT(5);
+ if (sps->constraint_set_flags & V4L2_H264_SPS_CONSTRAINT_SET3_FLAG)
+ constraint_set_flags |= BIT(4);
+ if (sps->constraint_set_flags & V4L2_H264_SPS_CONSTRAINT_SET4_FLAG)
+ constraint_set_flags |= BIT(3);
+ if (sps->constraint_set_flags & V4L2_H264_SPS_CONSTRAINT_SET5_FLAG)
+ constraint_set_flags |= BIT(2);
+
+ ret = v4l2_h264_enc_rbsp_u8(rbsp, constraint_set_flags);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_u8(rbsp, sps->level_idc);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->seq_parameter_set_id);
+ if (ret)
+ return ret;
+
+ if (V4L2_H264_SPS_HAS_CHROMA_FORMAT(sps)) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->chroma_format_idc);
+ if (ret)
+ return ret;
+
+ if (sps->chroma_format_idc == 3) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps->flags,
+ V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->bit_depth_luma_minus8);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->bit_depth_chroma_minus8);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps->flags,
+ V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
+ if (ret)
+ return ret;
+
+ /* Scaling matrix is not supported. */
+ ret = v4l2_h264_enc_rbsp_bit(rbsp,
+ seq_scaling_matrix_present_flag);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->log2_max_frame_num_minus4);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->pic_order_cnt_type);
+ if (ret)
+ return ret;
+
+ if (!sps->pic_order_cnt_type) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps->log2_max_pic_order_cnt_lsb_minus4);
+ if (ret)
+ return ret;
+ } else if (sps->pic_order_cnt_type == 1) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps->flags,
+ V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp, sps->offset_for_non_ref_pic);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp,
+ sps->offset_for_top_to_bottom_field);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps->num_ref_frames_in_pic_order_cnt_cycle);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) {
+ ret = v4l2_h264_enc_rbsp_se(rbsp,
+ sps->offset_for_ref_frame[i]);
+ if (ret)
+ return ret;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->max_num_ref_frames);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps->flags,
+ V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->pic_width_in_mbs_minus1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, sps->pic_height_in_map_units_minus1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps->flags,
+ V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
+ if (ret)
+ return ret;
+
+ if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps->flags,
+ V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps->flags,
+ V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_FRAME_CROPPING);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_FRAME_CROPPING) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->frame_crop_left_offset);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->frame_crop_right_offset);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->frame_crop_top_offset);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ sps_video->frame_crop_bottom_offset);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, sps_video->flags,
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_PARAMETERS_PRESENT);
+ if (ret)
+ return ret;
+
+ if (sps_video->flags & V4L2_H264_SPS_VIDEO_FLAG_VUI_PARAMETERS_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_sps_vui(rbsp, sps_video);
+ if (ret)
+ return ret;
+ }
+
+ return v4l2_h264_enc_rbsp_nalu_end(rbsp);
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_sps);
+
+int v4l2_h264_enc_rbsp_pps(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_ctrl_h264_pps *pps)
+{
+ u8 pic_scaling_matrix_present_flag = 0;
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_nalu_begin(rbsp, 0, V4L2_H264_NALU_TYPE_PPS);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, pps->pic_parameter_set_id);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, pps->seq_parameter_set_id);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, pps->flags,
+ V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, pps->flags,
+ V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, pps->num_slice_groups_minus1);
+ if (ret)
+ return ret;
+
+ /* Multiple slice groups are not supported. */
+ if (pps->num_slice_groups_minus1 > 1)
+ return -EINVAL;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ pps->num_ref_idx_l0_default_active_minus1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ pps->num_ref_idx_l1_default_active_minus1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, pps->flags,
+ V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, pps->weighted_bipred_idc, 2);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp, pps->pic_init_qp_minus26);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp, pps->pic_init_qs_minus26);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp, pps->chroma_qp_index_offset);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, pps->flags,
+ V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, pps->flags,
+ V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, pps->flags,
+ V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, pps->flags,
+ V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
+ if (ret)
+ return ret;
+
+ /* Scaling matrix is not supported. */
+ ret = v4l2_h264_enc_rbsp_bit(rbsp, pic_scaling_matrix_present_flag);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp, pps->second_chroma_qp_index_offset);
+ if (ret)
+ return ret;
+
+ return v4l2_h264_enc_rbsp_nalu_end(rbsp);
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_pps);
+
+int v4l2_h264_enc_rbsp_slice_header(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_ctrl_h264_sps *sps,
+ const struct v4l2_ctrl_h264_pps *pps,
+ const struct v4l2_ctrl_h264_encode_params *encode)
+{
+ u8 ref_pic_list_modification_flag_l0 = 0;
+ u8 ref_pic_list_modification_flag_l1 = 0;
+ u8 adaptive_ref_pic_marking_mode_flag = 0;
+ u32 first_mb_in_slice = 0;
+ u32 redundant_pic_cnt = 0;
+ u8 sp_for_switch_flag = 0;
+ s32 slice_qs_delta = 0;
+ u8 nal_unit_type;
+ int ret;
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC)
+ nal_unit_type = V4L2_H264_NALU_TYPE_SLICE_IDR;
+ else
+ nal_unit_type = V4L2_H264_NALU_TYPE_SLICE_NON_IDR;
+
+ ret = v4l2_h264_enc_rbsp_nalu_begin(rbsp, encode->nal_ref_idc,
+ nal_unit_type);
+ if (ret)
+ return ret;
+
+ /* Multiple slices are not supported. */
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, first_mb_in_slice);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, encode->slice_type);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, encode->pic_parameter_set_id);
+ if (ret)
+ return ret;
+
+ if (sps->flags & V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE) {
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, encode->colour_plane_id, 2);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, encode->frame_num,
+ sps->log2_max_frame_num_minus4 + 4);
+ if (ret)
+ return ret;
+
+ if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, encode->flags,
+ V4L2_H264_ENCODE_FLAG_FIELD_PIC);
+ if (ret)
+ return ret;
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_FIELD_PIC) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, encode->flags,
+ V4L2_H264_ENCODE_FLAG_BOTTOM_FIELD);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, encode->idr_pic_id);
+ if (ret)
+ return ret;
+ }
+
+ if (!sps->pic_order_cnt_type) {
+ ret = v4l2_h264_enc_rbsp_bits(rbsp, encode->pic_order_cnt_lsb,
+ sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
+ if (ret)
+ return ret;
+
+ if (pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT &&
+ !(encode->flags & V4L2_H264_ENCODE_FLAG_FIELD_PIC)) {
+ ret = v4l2_h264_enc_rbsp_se(rbsp,
+ encode->delta_pic_order_cnt_bottom);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (sps->pic_order_cnt_type == 1 &&
+ !(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO)) {
+ ret = v4l2_h264_enc_rbsp_se(rbsp,
+ encode->delta_pic_order_cnt0);
+ if (ret)
+ return ret;
+
+ if (pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT &&
+ !(encode->flags & V4L2_H264_ENCODE_FLAG_FIELD_PIC)) {
+ ret = v4l2_h264_enc_rbsp_se(rbsp,
+ encode->delta_pic_order_cnt1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) {
+ /* Redundant pictures are not supported. */
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, redundant_pic_cnt);
+ if (ret)
+ return ret;
+ }
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_B) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, encode->flags,
+ V4L2_H264_ENCODE_FLAG_DIRECT_SPATIAL_MV_PRED);
+ if (ret)
+ return ret;
+ }
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_P ||
+ encode->slice_type == V4L2_H264_SLICE_TYPE_SP ||
+ encode->slice_type == V4L2_H264_SLICE_TYPE_B) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, encode->flags,
+ V4L2_H264_ENCODE_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE);
+ if (ret)
+ return ret;
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ encode->num_ref_idx_l0_active_minus1);
+ if (ret)
+ return ret;
+ }
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE &&
+ encode->slice_type == V4L2_H264_SLICE_TYPE_B) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ encode->num_ref_idx_l1_active_minus1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (encode->slice_type != V4L2_H264_SLICE_TYPE_I &&
+ encode->slice_type != V4L2_H264_SLICE_TYPE_SI) {
+ /* Ref pic list modification is not supported. */
+ ret = v4l2_h264_enc_rbsp_bit(rbsp,
+ ref_pic_list_modification_flag_l0);
+ if (ret)
+ return ret;
+ }
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_B) {
+ /* Ref pic list modification is not supported. */
+ ret = v4l2_h264_enc_rbsp_bit(rbsp,
+ ref_pic_list_modification_flag_l1);
+ if (ret)
+ return ret;
+ }
+
+ /* Prediction weights are not supported. */
+ if (V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(pps, encode))
+ return -EINVAL;
+
+ if (encode->nal_ref_idc) {
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC) {
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, encode->flags,
+ V4L2_H264_ENCODE_FLAG_NO_OUTPUT_OF_PRIOR_PICS);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_flag(rbsp, encode->flags,
+ V4L2_H264_ENCODE_FLAG_LONG_TERM_REFERENCE);
+ if (ret)
+ return ret;
+ } else {
+ /* Adaptive ref pic marking mode is not supported. */
+ ret = v4l2_h264_enc_rbsp_bit(rbsp,
+ adaptive_ref_pic_marking_mode_flag);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE &&
+ encode->slice_type != V4L2_H264_SLICE_TYPE_I &&
+ encode->slice_type != V4L2_H264_SLICE_TYPE_SI) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp, encode->cabac_init_idc);
+ if (ret)
+ return ret;
+ }
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp, encode->slice_qp_delta);
+ if (ret)
+ return ret;
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_SP) {
+ /* Switching slices are not supported. */
+ ret = v4l2_h264_enc_rbsp_bit(rbsp, sp_for_switch_flag);
+ if (ret)
+ return ret;
+ }
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_SP ||
+ encode->slice_type == V4L2_H264_SLICE_TYPE_SI) {
+ ret = v4l2_h264_enc_rbsp_se(rbsp, slice_qs_delta);
+ if (ret)
+ return ret;
+ }
+
+ if (pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) {
+ ret = v4l2_h264_enc_rbsp_ue(rbsp,
+ encode->disable_deblocking_filter_idc);
+ if (ret)
+ return ret;
+
+ if (encode->disable_deblocking_filter_idc != 1) {
+ ret = v4l2_h264_enc_rbsp_se(rbsp,
+ encode->slice_alpha_c0_offset_div2);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_rbsp_se(rbsp,
+ encode->slice_beta_offset_div2);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* The slice NALU is not finished, the hardware has to write the rest! */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_rbsp_slice_header);
+
+MODULE_DESCRIPTION("V4L2 H.264 Encode RBSP");
+MODULE_AUTHOR("Paul Kocialkowski <paulk@sys-base.io>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-h264-enc.c b/drivers/media/v4l2-core/v4l2-h264-enc.c
index d4e1450691fb..0b46922d1d7a 100644
--- a/drivers/media/v4l2-core/v4l2-h264-enc.c
+++ b/drivers/media/v4l2-core/v4l2-h264-enc.c
@@ -9,10 +9,12 @@
#include <linux/v4l2-controls.h>
#include <media/v4l2-h264.h>
#include <media/v4l2-h264-enc.h>
+#include <media/v4l2-h264-enc-rbsp.h>
#include <media/videobuf2-v4l2.h>
int v4l2_h264_enc_init(struct v4l2_h264_enc *enc)
{
+ struct v4l2_h264_enc_rbsp *rbsp = &enc->rbsp;
int ret;
if ((!enc->format && !enc->format_mplane) || !enc->timeperframe ||
@@ -23,6 +25,9 @@ int v4l2_h264_enc_init(struct v4l2_h264_enc *enc)
memset(&enc->state_next, 0, sizeof(enc->state_next));
enc->state_serial = 0;
+ rbsp->ops = enc->rbsp_ops;
+ rbsp->private_data = enc->private_data;
+
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_h264_enc_init);
@@ -520,6 +525,146 @@ static int state_complete(struct v4l2_h264_enc *enc,
return 0;
}
+static int rbsp_update(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_next;
+ struct v4l2_h264_enc_state *state_active = &enc->state_active;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_ctrl_handler *handler = enc->ctrl_handler;
+ struct v4l2_ctrl *ctrl;
+
+ enc->rbsp_update = 0;
+
+ /* Start Code */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_STATELESS_H264_START_CODE);
+ if ((ctrl && ctrl->cur.val == V4L2_STATELESS_H264_START_CODE_ANNEX_B) ||
+ !ctrl)
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_START_CODE;
+
+ /* AUD */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_MPEG_VIDEO_AU_DELIMITER);
+ if (ctrl && ctrl->cur.val)
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_AUD;
+
+ /* SPS */
+
+ if (!enc->state_serial) {
+ if (memcmp(&state_active->sps, &state->sps,
+ sizeof(state_active->sps)) ||
+ memcmp(&state_active->sps_video, &state->sps_video,
+ sizeof(state_active->sps_video)))
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_SPS;
+ } else {
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_SPS;
+ }
+
+ /* PPS */
+
+ if (!enc->state_serial) {
+ if (memcmp(&state_active->pps, &state->pps,
+ sizeof(state_active->pps)))
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_PPS;
+ } else {
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_PPS;
+ }
+
+ /* IDR Prepend */
+
+ ctrl = v4l2_ctrl_find(handler,
+ V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR);
+ if (ctrl && ctrl->cur.val &&
+ encode->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC)
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_SPS |
+ V4L2_H264_ENC_RBSP_UPDATE_PPS;
+
+ /* Slice */
+
+ enc->rbsp_update |= V4L2_H264_ENC_RBSP_UPDATE_SLICE_HEADER;
+
+ return 0;
+}
+
+static int rbsp_step_unit(struct v4l2_h264_enc *enc,
+ unsigned int rbsp_update, unsigned int hw_flag)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_sps *sps = &state->sps;
+ struct v4l2_h264_sps_video *sps_video = &state->sps_video;
+ struct v4l2_ctrl_h264_pps *pps = &state->pps;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_h264_enc_rbsp *rbsp = &enc->rbsp;
+ u8 primary_pic_type;
+ int ret;
+
+ /* Return if no update is needed or if hardware generates the unit. */
+ if (!(enc->rbsp_update & rbsp_update) || enc->flags & hw_flag)
+ return 0;
+
+ if (enc->rbsp_update & V4L2_H264_ENC_RBSP_UPDATE_START_CODE) {
+ ret = v4l2_h264_enc_rbsp_start_code(rbsp);
+ if (ret)
+ return ret;
+ }
+
+ if (rbsp_update == V4L2_H264_ENC_RBSP_UPDATE_AUD) {
+ if (enc->flags & V4L2_H264_ENC_FLAG_INTER_BIPRED &&
+ enc->flags & V4L2_H264_ENC_FLAG_INTER_PRED)
+ primary_pic_type = V4L2_H264_PRIMARY_PIC_TYPE_IPB;
+ else if (enc->flags & V4L2_H264_ENC_FLAG_INTER_PRED)
+ primary_pic_type = V4L2_H264_PRIMARY_PIC_TYPE_IP;
+ else
+ primary_pic_type = V4L2_H264_PRIMARY_PIC_TYPE_I;
+ }
+
+ if (rbsp_update == V4L2_H264_ENC_RBSP_UPDATE_AUD)
+ return v4l2_h264_enc_rbsp_aud(rbsp, primary_pic_type);
+ else if (rbsp_update == V4L2_H264_ENC_RBSP_UPDATE_SPS)
+ return v4l2_h264_enc_rbsp_sps(rbsp, sps, sps_video);
+ else if (rbsp_update == V4L2_H264_ENC_RBSP_UPDATE_PPS)
+ return v4l2_h264_enc_rbsp_pps(rbsp, pps);
+ else if (rbsp_update == V4L2_H264_ENC_RBSP_UPDATE_SLICE_HEADER)
+ return v4l2_h264_enc_rbsp_slice_header(rbsp, sps, pps, encode);
+
+ return -EINVAL;
+}
+
+static int rbsp_step(struct v4l2_h264_enc *enc,
+ struct vb2_v4l2_buffer *buffer)
+{
+ struct v4l2_h264_enc_rbsp *rbsp = &enc->rbsp;
+ void *pointer = vb2_plane_vaddr(&buffer->vb2_buf, 0);
+ unsigned int size = vb2_plane_size(&buffer->vb2_buf, 0);
+ int ret;
+
+ ret = v4l2_h264_enc_rbsp_init(rbsp, pointer, size);
+ if (ret)
+ return ret;
+
+ ret = rbsp_step_unit(enc, V4L2_H264_ENC_RBSP_UPDATE_AUD,
+ V4L2_H264_ENC_FLAG_HW_AUD);
+ if (ret)
+ return ret;
+
+ ret = rbsp_step_unit(enc, V4L2_H264_ENC_RBSP_UPDATE_SPS,
+ V4L2_H264_ENC_FLAG_HW_SPS);
+ if (ret)
+ return ret;
+
+ ret = rbsp_step_unit(enc, V4L2_H264_ENC_RBSP_UPDATE_PPS,
+ V4L2_H264_ENC_FLAG_HW_PPS);
+ if (ret)
+ return ret;
+
+ ret = rbsp_step_unit(enc, V4L2_H264_ENC_RBSP_UPDATE_SLICE_HEADER,
+ V4L2_H264_ENC_FLAG_HW_SLICE_HEADER);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
int v4l2_h264_enc_step(struct v4l2_h264_enc *enc,
struct vb2_v4l2_buffer *buffer)
{
@@ -529,10 +674,18 @@ int v4l2_h264_enc_step(struct v4l2_h264_enc *enc,
if (ret)
return ret;
+ ret = rbsp_update(enc);
+ if (ret)
+ return ret;
+
ret = state_commit(enc);
if (ret)
return ret;
+ ret = rbsp_step(enc, buffer);
+ if (ret)
+ return ret;
+
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_h264_enc_step);
diff --git a/include/media/v4l2-h264-enc-rbsp.h b/include/media/v4l2-h264-enc-rbsp.h
new file mode 100644
index 000000000000..2ce9b4051436
--- /dev/null
+++ b/include/media/v4l2-h264-enc-rbsp.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * V4L2 H.264 Encode RBSP
+ *
+ * Copyright (C) 2025-2026 Paul Kocialkowski <paulk@sys-base.io>
+ */
+
+#ifndef _MEDIA_V4L2_H264_ENC_RBSP_H
+#define _MEDIA_V4L2_H264_ENC_RBSP_H
+
+#include <linux/v4l2-controls.h>
+#include <media/v4l2-h264.h>
+
+#define V4L2_H264_ENC_RBSP_EPTB 0x3
+
+#define V4L2_H264_ENC_RBSP_UPDATE_START_CODE 0x1
+#define V4L2_H264_ENC_RBSP_UPDATE_AUD 0x2
+#define V4L2_H264_ENC_RBSP_UPDATE_SPS 0x4
+#define V4L2_H264_ENC_RBSP_UPDATE_PPS 0x8
+#define V4L2_H264_ENC_RBSP_UPDATE_SLICE_HEADER 0x10
+
+#define v4l2_h264_enc_rbsp_op(r, o, a...) \
+ ({ \
+ int ret; \
+ if ((r)->ops && (r)->ops->o) \
+ ret = (r)->ops->o(r, ##a); \
+ else \
+ ret = -EOPNOTSUPP; \
+ ret; \
+ })
+
+struct v4l2_h264_enc_rbsp;
+
+struct v4l2_h264_enc_rbsp_ops {
+ int (*bits_raw)(struct v4l2_h264_enc_rbsp *rbsp, u32 value,
+ unsigned char bits_count);
+ int (*bits)(struct v4l2_h264_enc_rbsp *rbsp, u32 value,
+ unsigned char bits_count);
+ int (*ue)(struct v4l2_h264_enc_rbsp *rbsp, u32 value);
+ int (*se)(struct v4l2_h264_enc_rbsp *rbsp, s32 value);
+ int (*align)(struct v4l2_h264_enc_rbsp *rbsp);
+};
+
+struct v4l2_h264_enc_rbsp {
+ const struct v4l2_h264_enc_rbsp_ops *ops;
+ void *private_data;
+
+ u8 *pointer;
+ unsigned int size;
+ unsigned char bit_offset;
+ unsigned int bits_count;
+ unsigned int zero_count;
+};
+
+int v4l2_h264_enc_rbsp_init(struct v4l2_h264_enc_rbsp *rbsp, u8 *pointer,
+ unsigned int size);
+unsigned int v4l2_h264_enc_rbsp_bits_count(struct v4l2_h264_enc_rbsp *rbsp);
+unsigned int v4l2_h264_enc_rbsp_bytes_count(struct v4l2_h264_enc_rbsp *rbsp);
+int v4l2_h264_enc_rbsp_start_code(struct v4l2_h264_enc_rbsp *rbsp);
+int v4l2_h264_enc_rbsp_aud(struct v4l2_h264_enc_rbsp *rbsp,
+ u8 primary_pic_type);
+int v4l2_h264_enc_rbsp_sps(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_ctrl_h264_sps *sps,
+ const struct v4l2_h264_sps_video *sps_video);
+int v4l2_h264_enc_rbsp_pps(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_ctrl_h264_pps *pps);
+int v4l2_h264_enc_rbsp_slice_header(struct v4l2_h264_enc_rbsp *rbsp,
+ const struct v4l2_ctrl_h264_sps *sps,
+ const struct v4l2_ctrl_h264_pps *pps,
+ const struct v4l2_ctrl_h264_encode_params *encode);
+
+#endif
diff --git a/include/media/v4l2-h264-enc.h b/include/media/v4l2-h264-enc.h
index 2978a73baacd..3d6b97408707 100644
--- a/include/media/v4l2-h264-enc.h
+++ b/include/media/v4l2-h264-enc.h
@@ -10,6 +10,7 @@
#include <linux/v4l2-controls.h>
#include <linux/videodev2.h>
+#include <media/v4l2-h264-enc-rbsp.h>
#include <media/videobuf2-v4l2.h>
#define V4L2_H264_ENC_MB_UNIT 16
@@ -55,6 +56,7 @@ struct v4l2_h264_enc_ops {
struct v4l2_h264_enc {
const struct v4l2_h264_enc_ops *ops;
+ const struct v4l2_h264_enc_rbsp_ops *rbsp_ops;
void *private_data;
struct v4l2_pix_format *format;
@@ -66,6 +68,9 @@ struct v4l2_h264_enc {
struct v4l2_h264_enc_state state_next;
unsigned int state_serial;
+ struct v4l2_h264_enc_rbsp rbsp;
+ unsigned int rbsp_update;
+
unsigned int flags;
};
--
2.53.0
^ permalink raw reply related
* [PATCH 04/14] media: h264: Add stateless encode core
From: Paul Kocialkowski @ 2026-05-22 10:16 UTC (permalink / raw)
To: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Nicolas Dufresne,
Benjamin Gaignard, Philipp Zabel, Mauro Carvalho Chehab,
Hans Verkuil, Marco Felsch, Michael Tretter, Paul Kocialkowski
In-Reply-To: <20260522101653.2565125-1-paulk@sys-base.io>
The H.264 stateless encode core is the common implementation called by
drivers to coordinate stateless encoding. It provides all the relevant
configuration parameters to drivers, through a state mechanism.
States are built from controls (some of which are optional) with various
inter-control checks along the way. A state can be constrained by the
driver through a dedicated operation callback in order to disable
unsupported features that may have been requested by userspace.
The general philosophy is to adapt parameters provided by userspace to
reach a state that can be handled by the hardware and bail out if no
such accommodation can be made.
Adapted parameters are returned to userspace by updating the controls.
This is currently not attached to the inbound media request but should
be when support for it becomes available.
Signed-off-by: Paul Kocialkowski <paulk@sys-base.io>
---
drivers/media/v4l2-core/Kconfig | 4 +
drivers/media/v4l2-core/Makefile | 1 +
drivers/media/v4l2-core/v4l2-h264-enc.c | 555 ++++++++++++++++++++++++
include/media/v4l2-h264-enc.h | 79 ++++
4 files changed, 639 insertions(+)
create mode 100644 drivers/media/v4l2-core/v4l2-h264-enc.c
create mode 100644 include/media/v4l2-h264-enc.h
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 331b8e535e5b..121ab82a9631 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -44,6 +44,10 @@ config V4L2_JPEG_HELPER
config V4L2_H264
tristate
+# Used by drivers that need v4l2-h264-enc.ko
+config V4L2_H264_ENC
+ tristate
+
# Used by drivers that need v4l2-vp9.ko
config V4L2_VP9
tristate
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 2177b9d63a8f..bd319e363c8e 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
+obj-$(CONFIG_V4L2_H264_ENC) += v4l2-h264-enc.o
obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o
diff --git a/drivers/media/v4l2-core/v4l2-h264-enc.c b/drivers/media/v4l2-core/v4l2-h264-enc.c
new file mode 100644
index 000000000000..d4e1450691fb
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-h264-enc.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * V4L2 H.264 Encode Core
+ *
+ * Copyright (C) 2025-2026 Paul Kocialkowski <paulk@sys-base.io>
+ */
+
+#include <linux/module.h>
+#include <linux/v4l2-controls.h>
+#include <media/v4l2-h264.h>
+#include <media/v4l2-h264-enc.h>
+#include <media/videobuf2-v4l2.h>
+
+int v4l2_h264_enc_init(struct v4l2_h264_enc *enc)
+{
+ int ret;
+
+ if ((!enc->format && !enc->format_mplane) || !enc->timeperframe ||
+ !enc->ctrl_handler)
+ return -EINVAL;
+
+ memset(&enc->state_active, 0, sizeof(enc->state_active));
+ memset(&enc->state_next, 0, sizeof(enc->state_next));
+ enc->state_serial = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_init);
+
+void v4l2_h264_enc_exit(struct v4l2_h264_enc *enc)
+{
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_exit);
+
+static int state_prepare_params(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_next;
+ struct v4l2_ctrl_h264_sps *sps = &state->sps;
+ struct v4l2_h264_sps_video *sps_video = &state->sps_video;
+ struct v4l2_ctrl_h264_pps *pps = &state->pps;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+ struct v4l2_ctrl_handler *handler = enc->ctrl_handler;
+ struct v4l2_fract *timeperframe = enc->timeperframe;
+ unsigned int width, height;
+ unsigned int colorspace, quantization, xfer_func, ycbcr_enc;
+ struct v4l2_ctrl *ctrl;
+
+ /* Time per frame */
+
+ state->timeperframe = *timeperframe;
+
+ /* Format */
+
+ if (enc->format_mplane)
+ width = enc->format_mplane->width;
+ else
+ width = enc->format->width;
+
+ state->width_mbs = DIV_ROUND_UP(width, V4L2_H264_ENC_MB_UNIT);
+ state->width_aligned = ALIGN(width, V4L2_H264_ENC_MB_UNIT);
+
+ if (enc->format_mplane)
+ height = enc->format_mplane->height;
+ else
+ height = enc->format->height;
+
+ state->height_mbs = DIV_ROUND_UP(height, V4L2_H264_ENC_MB_UNIT);
+ state->height_aligned = ALIGN(height, V4L2_H264_ENC_MB_UNIT);
+
+ /* SPS */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_STATELESS_H264_SPS);
+ if (!ctrl)
+ return -EINVAL;
+
+ memcpy(sps, ctrl->p_cur.p_h264_sps, sizeof(*sps));
+
+ sps->pic_width_in_mbs_minus1 = state->width_mbs - 1;
+ sps->pic_height_in_map_units_minus1 = state->height_mbs - 1;
+
+ /*
+ * Only pic_order_cnt_type = 0 is currently supported.
+ * Error out since no pic_order_cnt_lsb was provided.
+ */
+ if (sps->pic_order_cnt_type)
+ return -EINVAL;
+
+ /* SPS Video */
+
+ if (width < state->width_aligned || height < state->height_aligned) {
+ sps_video->flags |= V4L2_H264_SPS_VIDEO_FLAG_FRAME_CROPPING;
+
+ sps_video->frame_crop_left_offset = 0;
+ sps_video->frame_crop_right_offset = (state->width_aligned -
+ width) / 2;
+ sps_video->frame_crop_top_offset = 0;
+ sps_video->frame_crop_bottom_offset = (state->height_aligned -
+ height) / 2;
+ }
+
+ if (timeperframe->numerator && timeperframe->denominator) {
+ sps_video->flags |=
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_PARAMETERS_PRESENT |
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_TIMING_INFO_PRESENT |
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_FIXED_FRAME_RATE;
+
+ /* Timing info is always provided as a field rate. */
+ sps_video->num_units_in_tick = timeperframe->numerator;
+ sps_video->time_scale = timeperframe->denominator * 2;
+ }
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE);
+ if (ctrl && ctrl->cur.val) {
+ sps_video->flags |=
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_ASPECT_RATIO_INFO_PRESENT;
+
+ ctrl = v4l2_ctrl_find(handler,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC);
+ if (!ctrl)
+ return -EINVAL;
+
+ if (ctrl->cur.val == V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED)
+ sps_video->aspect_ratio_idc =
+ V4L2_H264_VUI_ASPECT_RATIO_IDC_EXTENDED;
+ else
+ sps_video->aspect_ratio_idc = ctrl->cur.val;
+
+ if (sps_video->aspect_ratio_idc ==
+ V4L2_H264_VUI_ASPECT_RATIO_IDC_EXTENDED) {
+ ctrl = v4l2_ctrl_find(handler,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH);
+ if (!ctrl)
+ return -EINVAL;
+
+ sps_video->sar_width = ctrl->cur.val;
+
+ ctrl = v4l2_ctrl_find(handler,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT);
+ if (!ctrl)
+ return -EINVAL;
+
+ sps_video->sar_height = ctrl->cur.val;
+ }
+ }
+
+ if (enc->format_mplane) {
+ colorspace = enc->format_mplane->colorspace;
+ quantization = enc->format_mplane->quantization;
+ xfer_func = enc->format_mplane->xfer_func;
+ ycbcr_enc = enc->format_mplane->ycbcr_enc;
+ } else {
+ colorspace = enc->format->colorspace;
+ quantization = enc->format->quantization;
+ xfer_func = enc->format->xfer_func;
+ ycbcr_enc = enc->format->ycbcr_enc;
+ }
+
+ if (colorspace != V4L2_COLORSPACE_DEFAULT ||
+ quantization != V4L2_QUANTIZATION_DEFAULT ||
+ xfer_func != V4L2_XFER_FUNC_DEFAULT ||
+ ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT) {
+ sps_video->flags |=
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_VIDEO_SIGNAL_TYPE_PRESENT |
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_COLOUR_DESCRIPTION_PRESENT;
+
+ sps_video->video_format = V4L2_H264_VUI_VIDEO_UNSPECIFIED;
+
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_SMPTE170M;
+ break;
+ case V4L2_COLORSPACE_SMPTE240M:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_SMPTE240M;
+ break;
+ case V4L2_COLORSPACE_REC709:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_BT709;
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_BT470_SYSTEM_M;
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_BT470_SYSTEM_BG;
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_BT2020;
+ break;
+ case V4L2_COLORSPACE_JPEG:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_BT709;
+ break;
+ case V4L2_COLORSPACE_SRGB:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_BT709;
+ break;
+ default:
+ sps_video->colour_primaries =
+ V4L2_H264_VUI_COLOUR_UNSPECIFIED;
+ break;
+ }
+
+ if (quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ sps_video->flags |=
+ V4L2_H264_SPS_VIDEO_FLAG_VUI_VIDEO_FULL_RANGE;
+
+ switch (xfer_func) {
+ case V4L2_XFER_FUNC_709:
+ /*
+ * All of these are equivalent but the H.264
+ * specification allows being more specific.
+ */
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_SMPTE170M;
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_BT470_SYSTEM_M;
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_BT470_SYSTEM_BG;
+ break;
+ default:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_BT709;
+ break;
+ }
+ break;
+ case V4L2_XFER_FUNC_SRGB:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_SRGB;
+ break;
+ case V4L2_XFER_FUNC_SMPTE240M:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_SMPTE240M;
+ break;
+ case V4L2_XFER_FUNC_NONE:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_LINEAR;
+ break;
+ default:
+ sps_video->transfer_characteristics =
+ V4L2_H264_VUI_TRANSFER_UNSPECIFIED;
+ break;
+ }
+
+ switch (ycbcr_enc) {
+ case V4L2_YCBCR_ENC_601:
+ /*
+ * All of these are equivalent but the H.264
+ * specification allows being more specific.
+ */
+ switch (colorspace) {
+ case V4L2_COLORSPACE_SMPTE170M:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_SMPTE170M;
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_BT470_SYSTEM_M;
+ break;
+ default:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_BT470_SYSTEM_BG;
+ break;
+ }
+ break;
+ case V4L2_YCBCR_ENC_709:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_BT709;
+ break;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_SMPTE240M;
+ break;
+ case V4L2_YCBCR_ENC_BT2020:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_BT2020;
+ break;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_BT2020_CONST_LUM;
+ break;
+ default:
+ sps_video->matrix_coefficients =
+ V4L2_H264_VUI_MATRIX_UNSPECIFIED;
+ break;
+ }
+ }
+
+ /* PPS */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_STATELESS_H264_PPS);
+ if (!ctrl)
+ return -EINVAL;
+
+ memcpy(pps, ctrl->p_cur.p_h264_pps, sizeof(*pps));
+
+ /* Only single instances of PPS and SPS are supported. */
+ if (pps->seq_parameter_set_id != sps->seq_parameter_set_id)
+ pps->seq_parameter_set_id = sps->seq_parameter_set_id;
+
+ pps->flags &= ~V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT;
+
+ /* Slice groups are not supported. */
+ pps->num_slice_groups_minus1 = 0;
+
+ if (pps->num_ref_idx_l0_default_active_minus1 >
+ (sps->max_num_ref_frames - 1))
+ pps->num_ref_idx_l0_default_active_minus1 =
+ sps->max_num_ref_frames - 1;
+
+ if (pps->num_ref_idx_l1_default_active_minus1 >
+ (sps->max_num_ref_frames - 1))
+ pps->num_ref_idx_l1_default_active_minus1 =
+ sps->max_num_ref_frames - 1;
+
+ pps->flags &= ~V4L2_H264_PPS_FLAG_WEIGHTED_PRED;
+ pps->weighted_bipred_idc = 0;
+
+ /* Switching slices are not supported. */
+ pps->pic_init_qs_minus26 = 0;
+
+ /* Redundant pictures are not supported. */
+ pps->flags &= ~V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT;
+
+ /* Scaling matrix is not supported. */
+ pps->flags &= ~V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT;
+
+ /*
+ * RBSP always writes the optional second_chroma_qp_index_offset,
+ * which has to take the chroma_qp_index_offset value if unsupported.
+ */
+ if (!(enc->flags & V4L2_H264_ENC_FLAG_CHROMA_QP_CR_OFFSET))
+ pps->second_chroma_qp_index_offset =
+ pps->chroma_qp_index_offset;
+
+ /* Encode */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_STATELESS_H264_ENCODE_PARAMS);
+ if (!ctrl)
+ return -EINVAL;
+
+ memcpy(encode, ctrl->p_cur.p_h264_encode_params, sizeof(*encode));
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_SI)
+ encode->slice_type = V4L2_H264_SLICE_TYPE_I;
+ else if (encode->slice_type == V4L2_H264_SLICE_TYPE_SP)
+ encode->slice_type = V4L2_H264_SLICE_TYPE_P;
+
+ /* We cannot safely reuse parameters of a B frane for a P frame. */
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_B &&
+ !(enc->flags & V4L2_H264_ENC_FLAG_INTER_BIPRED))
+ return -EINVAL;
+
+ /* We can safely reuse parameters of a P frame for an I frame. */
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_P &&
+ !(enc->flags & V4L2_H264_ENC_FLAG_INTER_PRED))
+ encode->slice_type = V4L2_H264_SLICE_TYPE_I;
+
+ if (encode->slice_type != V4L2_H264_SLICE_TYPE_I &&
+ !sps->max_num_ref_frames)
+ return -EINVAL;
+
+ /* Ensure the stream starts with an I frame. */
+ if (!enc->state_serial && encode->slice_type != V4L2_H264_SLICE_TYPE_I)
+ return -EINVAL;
+
+ /* Only single instances of PPS and SPS are supported. */
+ if (encode->pic_parameter_set_id != pps->pic_parameter_set_id)
+ encode->pic_parameter_set_id = pps->pic_parameter_set_id;
+
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE) {
+ if (encode->num_ref_idx_l0_active_minus1 >
+ (sps->max_num_ref_frames - 1))
+ encode->num_ref_idx_l0_active_minus1 =
+ sps->max_num_ref_frames - 1;
+
+ if (encode->num_ref_idx_l1_active_minus1 >
+ (sps->max_num_ref_frames - 1))
+ encode->num_ref_idx_l1_active_minus1 =
+ sps->max_num_ref_frames - 1;
+ }
+
+ return 0;
+}
+
+static int state_prepare(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_next;
+ int ret;
+
+ memset(state, 0, sizeof(*state));
+
+ ret = state_prepare_params(enc);
+ if (ret)
+ return ret;
+
+ ret = v4l2_h264_enc_op(enc, state_constrain, state);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+
+ return 0;
+}
+
+static void state_debug(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_sps *sps = &state->sps;
+ struct v4l2_ctrl_h264_pps *pps = &state->pps;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+
+ pr_debug("+ v4l2-h264-enc: state");
+
+ pr_debug(" type: %c%s, ref: %s%s",
+ v4l2_h264_slice_type_char(encode->slice_type),
+ encode->flags & V4L2_H264_ENCODE_FLAG_IDR_PIC ? " (IDR)" : "",
+ encode->nal_ref_idc ? "marked" : "unmarked",
+ encode->flags & V4L2_H264_ENCODE_FLAG_LONG_TERM_REFERENCE ?
+ " (long-term)" : "");
+ pr_debug(" width: %u mbs, height: %u mbs",
+ sps->pic_width_in_mbs_minus1,
+ sps->pic_height_in_map_units_minus1);
+ pr_debug(" profile: %u, level: %u", sps->profile_idc,
+ sps->level_idc);
+
+ pr_debug(" entropy coding: %s",
+ pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE ? "cabac" :
+ "cavlc");
+
+ if (pps->flags & (V4L2_H264_PPS_FLAG_WEIGHTED_PRED |
+ V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED |
+ V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE |
+ V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) ||
+ encode->flags & V4L2_H264_ENCODE_FLAG_DIRECT_SPATIAL_MV_PRED)
+ pr_debug(" coding features:");
+
+ if (pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED)
+ pr_debug(" - weighted-pred");
+ if (pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED)
+ pr_debug(" - constrained-intra-pred");
+ if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE)
+ pr_debug(" - transform-8x8");
+ if (pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)
+ pr_debug(" - scaling-matrix");
+ if (encode->flags & V4L2_H264_ENCODE_FLAG_DIRECT_SPATIAL_MV_PRED)
+ pr_debug(" - direct-spatial-mv-pred");
+}
+
+static int state_commit(struct v4l2_h264_enc *enc)
+{
+ struct v4l2_h264_enc_state *state = &enc->state_next;
+ struct v4l2_ctrl_handler *handler = enc->ctrl_handler;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ /* The presence of required controls was checked already. */
+ /* TODO: Attach to media request. */
+
+ /* SPS */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_STATELESS_H264_SPS);
+ ret = v4l2_ctrl_s_ctrl_compound(ctrl, V4L2_CTRL_TYPE_H264_SPS,
+ &state->sps);
+ if (ret)
+ return ret;
+
+ /* PPS */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_STATELESS_H264_PPS);
+ ret = v4l2_ctrl_s_ctrl_compound(ctrl, V4L2_CTRL_TYPE_H264_PPS,
+ &state->pps);
+ if (ret)
+ return ret;
+
+ /* Encode */
+
+ ctrl = v4l2_ctrl_find(handler, V4L2_CID_STATELESS_H264_ENCODE_PARAMS);
+ ret = v4l2_ctrl_s_ctrl_compound(ctrl, V4L2_CTRL_TYPE_H264_ENCODE_PARAMS,
+ &state->encode);
+ if (ret)
+ return ret;
+
+ /* State */
+
+ memcpy(&enc->state_active, &enc->state_next, sizeof(enc->state_active));
+
+ state_debug(enc);
+
+ return 0;
+}
+
+static int state_complete(struct v4l2_h264_enc *enc,
+ struct vb2_v4l2_buffer *buffer)
+{
+
+ struct v4l2_h264_enc_state *state = &enc->state_active;
+ struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
+
+ if (encode->slice_type == V4L2_H264_SLICE_TYPE_I)
+ buffer->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ else if (encode->slice_type == V4L2_H264_SLICE_TYPE_P)
+ buffer->flags |= V4L2_BUF_FLAG_PFRAME;
+ else if (encode->slice_type == V4L2_H264_SLICE_TYPE_B)
+ buffer->flags |= V4L2_BUF_FLAG_BFRAME;
+
+ /*
+ * This is only incremented after a successful encode so we can still
+ * detect a stream start case after the first frame failed to encode.
+ */
+ enc->state_serial++;
+
+ return 0;
+}
+
+int v4l2_h264_enc_step(struct v4l2_h264_enc *enc,
+ struct vb2_v4l2_buffer *buffer)
+{
+ int ret;
+
+ ret = state_prepare(enc);
+ if (ret)
+ return ret;
+
+ ret = state_commit(enc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_step);
+
+int v4l2_h264_enc_complete(struct v4l2_h264_enc *enc,
+ struct vb2_v4l2_buffer *buffer)
+{
+ int ret;
+
+ ret = state_complete(enc, buffer);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_enc_complete);
+
+MODULE_DESCRIPTION("V4L2 H.264 Encode Core");
+MODULE_AUTHOR("Paul Kocialkowski <paulk@sys-base.io>");
+MODULE_LICENSE("GPL");
diff --git a/include/media/v4l2-h264-enc.h b/include/media/v4l2-h264-enc.h
new file mode 100644
index 000000000000..2978a73baacd
--- /dev/null
+++ b/include/media/v4l2-h264-enc.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * V4L2 H.264 Encode Core
+ *
+ * Copyright (C) 2025-2026 Paul Kocialkowski <paulk@sys-base.io>
+ */
+
+#ifndef _MEDIA_V4L2_H264_ENC_H
+#define _MEDIA_V4L2_H264_ENC_H
+
+#include <linux/v4l2-controls.h>
+#include <linux/videodev2.h>
+#include <media/videobuf2-v4l2.h>
+
+#define V4L2_H264_ENC_MB_UNIT 16
+
+#define V4L2_H264_ENC_FLAG_INTER_PRED 0x1
+#define V4L2_H264_ENC_FLAG_INTER_BIPRED 0x2
+#define V4L2_H264_ENC_FLAG_HW_AUD 0x4
+#define V4L2_H264_ENC_FLAG_HW_SPS 0x8
+#define V4L2_H264_ENC_FLAG_HW_PPS 0x10
+#define V4L2_H264_ENC_FLAG_HW_SLICE_HEADER 0x20
+#define V4L2_H264_ENC_FLAG_CHROMA_QP_CR_OFFSET 0x40
+
+#define v4l2_h264_enc_op(e, o, a...) \
+ ({ \
+ int ret; \
+ if ((e)->ops && (e)->ops->o) \
+ ret = (e)->ops->o(e, ##a); \
+ else \
+ ret = -EOPNOTSUPP; \
+ ret; \
+ })
+
+struct v4l2_h264_enc;
+
+struct v4l2_h264_enc_state {
+ struct v4l2_ctrl_h264_sps sps;
+ struct v4l2_h264_sps_video sps_video;
+ struct v4l2_ctrl_h264_pps pps;
+ struct v4l2_ctrl_h264_encode_params encode;
+
+ struct v4l2_fract timeperframe;
+
+ unsigned int width_mbs;
+ unsigned int width_aligned;
+ unsigned int height_mbs;
+ unsigned int height_aligned;
+};
+
+struct v4l2_h264_enc_ops {
+ int (*state_constrain)(struct v4l2_h264_enc *enc,
+ struct v4l2_h264_enc_state *state);
+};
+
+struct v4l2_h264_enc {
+ const struct v4l2_h264_enc_ops *ops;
+ void *private_data;
+
+ struct v4l2_pix_format *format;
+ struct v4l2_pix_format_mplane *format_mplane;
+ struct v4l2_fract *timeperframe;
+ struct v4l2_ctrl_handler *ctrl_handler;
+
+ struct v4l2_h264_enc_state state_active;
+ struct v4l2_h264_enc_state state_next;
+ unsigned int state_serial;
+
+ unsigned int flags;
+};
+
+int v4l2_h264_enc_init(struct v4l2_h264_enc *enc);
+void v4l2_h264_enc_exit(struct v4l2_h264_enc *enc);
+int v4l2_h264_enc_step(struct v4l2_h264_enc *enc,
+ struct vb2_v4l2_buffer *buffer);
+int v4l2_h264_enc_complete(struct v4l2_h264_enc *enc,
+ struct vb2_v4l2_buffer *buffer);
+
+#endif
--
2.53.0
^ permalink raw reply related
* [PATCH 03/14] media: h264: Add SPS video definitions
From: Paul Kocialkowski @ 2026-05-22 10:16 UTC (permalink / raw)
To: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Nicolas Dufresne,
Benjamin Gaignard, Philipp Zabel, Mauro Carvalho Chehab,
Hans Verkuil, Marco Felsch, Michael Tretter, Paul Kocialkowski
In-Reply-To: <20260522101653.2565125-1-paulk@sys-base.io>
These definitions are used to build the video part of the SPS,
including important fields such as frame cropping, timing info
(frame rate) and pixel coding.
Signed-off-by: Paul Kocialkowski <paulk@sys-base.io>
---
include/media/v4l2-h264.h | 115 ++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/include/media/v4l2-h264.h b/include/media/v4l2-h264.h
index 1833c0556963..3b00a1b67fe5 100644
--- a/include/media/v4l2-h264.h
+++ b/include/media/v4l2-h264.h
@@ -12,6 +12,121 @@
#include <media/v4l2-ctrls.h>
+#define V4L2_H264_START_CODE_ANNEX_B 0x00000001
+
+#define V4L2_H264_NALU_TYPE_SLICE_NON_IDR 1
+#define V4L2_H264_NALU_TYPE_SLICE_IDR 5
+#define V4L2_H264_NALU_TYPE_SPS 7
+#define V4L2_H264_NALU_TYPE_PPS 8
+#define V4L2_H264_NALU_TYPE_AUD 9
+
+#define V4L2_H264_PRIMARY_PIC_TYPE_I 0
+#define V4L2_H264_PRIMARY_PIC_TYPE_IP 1
+#define V4L2_H264_PRIMARY_PIC_TYPE_IPB 2
+
+#define V4L2_H264_VUI_ASPECT_RATIO_IDC_EXTENDED 255
+
+#define V4L2_H264_VUI_VIDEO_COMPONENT 0
+#define V4L2_H264_VUI_VIDEO_PAL 1
+#define V4L2_H264_VUI_VIDEO_NTSC 2
+#define V4L2_H264_VUI_VIDEO_SECAM 3
+#define V4L2_H264_VUI_VIDEO_MAC 4
+#define V4L2_H264_VUI_VIDEO_UNSPECIFIED 5
+
+#define V4L2_H264_VUI_COLOUR_BT709 1
+#define V4L2_H264_VUI_COLOUR_UNSPECIFIED 2
+#define V4L2_H264_VUI_COLOUR_BT470_SYSTEM_M 4
+#define V4L2_H264_VUI_COLOUR_BT470_SYSTEM_BG 5
+#define V4L2_H264_VUI_COLOUR_SMPTE170M 6
+#define V4L2_H264_VUI_COLOUR_SMPTE240M 7
+#define V4L2_H264_VUI_COLOUR_BT2020 9
+
+#define V4L2_H264_VUI_TRANSFER_BT709 1
+#define V4L2_H264_VUI_TRANSFER_UNSPECIFIED 2
+#define V4L2_H264_VUI_TRANSFER_BT470_SYSTEM_M 4
+#define V4L2_H264_VUI_TRANSFER_BT470_SYSTEM_BG 5
+#define V4L2_H264_VUI_TRANSFER_SMPTE170M 6
+#define V4L2_H264_VUI_TRANSFER_SMPTE240M 7
+#define V4L2_H264_VUI_TRANSFER_LINEAR 8
+#define V4L2_H264_VUI_TRANSFER_SRGB 13
+
+#define V4L2_H264_VUI_MATRIX_IDENTITY 0
+#define V4L2_H264_VUI_MATRIX_BT709 1
+#define V4L2_H264_VUI_MATRIX_UNSPECIFIED 2
+#define V4L2_H264_VUI_MATRIX_BT470_SYSTEM_M 4
+#define V4L2_H264_VUI_MATRIX_BT470_SYSTEM_BG 5
+#define V4L2_H264_VUI_MATRIX_SMPTE170M 6
+#define V4L2_H264_VUI_MATRIX_SMPTE240M 7
+#define V4L2_H264_VUI_MATRIX_BT2020 9
+#define V4L2_H264_VUI_MATRIX_BT2020_CONST_LUM 10
+
+#define V4L2_H264_SPS_VIDEO_FLAG_FRAME_CROPPING BIT(0)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_PARAMETERS_PRESENT BIT(1)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_ASPECT_RATIO_INFO_PRESENT BIT(2)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_OVERSCAN_INFO_PRESENT BIT(3)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_OVERSCAN_APPROPRIATE BIT(4)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_VIDEO_SIGNAL_TYPE_PRESENT BIT(5)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_VIDEO_FULL_RANGE BIT(6)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_COLOUR_DESCRIPTION_PRESENT BIT(7)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_CHROMA_LOC_INFO_PRESENT BIT(8)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_TIMING_INFO_PRESENT BIT(9)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_FIXED_FRAME_RATE BIT(10)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_NAL_HRD_PARAMETERS_PRESENT BIT(11)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_VCL_HRD_PARAMETERS_PRESENT BIT(12)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_LOW_DELAY_HRD BIT(13)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_PIC_STRUCT_PRESENT BIT(14)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_BITSTREAM_RESTRICTION BIT(15)
+#define V4L2_H264_SPS_VIDEO_FLAG_VUI_MOTION_VECTORS_OVER_PIC_BOUNDARIES BIT(16)
+
+struct v4l2_h264_sps_video_hrd {
+ u8 cpb_cnt_minus1;
+ u8 bit_rate_scale;
+ u8 cpb_size_scale;
+
+ u32 bit_rate_value_minus1[32];
+ u32 cpb_size_value_minus1[32];
+ u8 cbr_flag[32];
+
+ u8 initial_cpb_removal_delay_length_minus1;
+ u8 cpb_removal_delay_length_minus1;
+ u8 dpb_output_delay_length_minus1;
+ u8 time_offset_length;
+};
+
+struct v4l2_h264_sps_video {
+ u32 frame_crop_left_offset;
+ u32 frame_crop_right_offset;
+ u32 frame_crop_top_offset;
+ u32 frame_crop_bottom_offset;
+
+ u8 aspect_ratio_idc;
+ u16 sar_width;
+ u16 sar_height;
+
+ u8 video_format;
+ u8 colour_primaries;
+ u8 transfer_characteristics;
+ u8 matrix_coefficients;
+
+ u8 chroma_sample_loc_type_top_field;
+ u8 chroma_sample_loc_type_bottom_field;
+
+ u32 num_units_in_tick;
+ u32 time_scale;
+
+ struct v4l2_h264_sps_video_hrd nal_hrd;
+ struct v4l2_h264_sps_video_hrd vcl_hrd;
+
+ u32 max_bytes_per_pic_denom;
+ u32 max_bits_per_mb_denom;
+ u32 log2_max_mv_length_horizontal;
+ u32 log2_max_mv_length_vertical;
+ u32 max_num_reorder_frames;
+ u32 max_dec_frame_buffering;
+
+ u32 flags;
+};
+
/**
* struct v4l2_h264_reflist_builder - Reference list builder object
*
--
2.53.0
^ permalink raw reply related
* [PATCH 02/14] media: uapi: Add H.264 stateless encode support
From: Paul Kocialkowski @ 2026-05-22 10:16 UTC (permalink / raw)
To: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Nicolas Dufresne,
Benjamin Gaignard, Philipp Zabel, Mauro Carvalho Chehab,
Hans Verkuil, Marco Felsch, Michael Tretter, Paul Kocialkowski
In-Reply-To: <20260522101653.2565125-1-paulk@sys-base.io>
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 <paulk@sys-base.io>
---
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
^ permalink raw reply related
* [PATCH 01/14] media: h264: Add a more generic reflist builder init
From: Paul Kocialkowski @ 2026-05-22 10:16 UTC (permalink / raw)
To: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Nicolas Dufresne,
Benjamin Gaignard, Philipp Zabel, Mauro Carvalho Chehab,
Hans Verkuil, Marco Felsch, Michael Tretter, Paul Kocialkowski
In-Reply-To: <20260522101653.2565125-1-paulk@sys-base.io>
This should become part of a larger reflist builder rework.
Signed-off-by: Paul Kocialkowski <paulk@sys-base.io>
---
drivers/media/v4l2-core/v4l2-h264.c | 69 +++++++++++++++++++++++++++++
include/media/v4l2-h264.h | 7 +++
2 files changed, 76 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-h264.c b/drivers/media/v4l2-core/v4l2-h264.c
index c00197d095e7..4e5a177ca2c7 100644
--- a/drivers/media/v4l2-core/v4l2-h264.c
+++ b/drivers/media/v4l2-core/v4l2-h264.c
@@ -105,6 +105,75 @@ v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b,
}
EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder);
+void
+v4l2_h264_init_reflist_builder_gen(struct v4l2_h264_reflist_builder *b,
+ const struct v4l2_ctrl_h264_sps *sps,
+ const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES],
+ unsigned int pic_order_count, unsigned int frame_num, unsigned int fields)
+{
+ int cur_frame_num, max_frame_num;
+ unsigned int i;
+
+ max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4);
+ cur_frame_num = frame_num;
+
+ memset(b, 0, sizeof(*b));
+ b->cur_pic_order_count = pic_order_count;
+ b->cur_pic_fields = fields;
+
+ for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
+ if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
+ continue;
+
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+ b->refs[i].longterm = true;
+
+ /*
+ * Handle frame_num wraparound as described in section
+ * '8.2.4.1 Decoding process for picture numbers' of the spec.
+ * For long term references, frame_num is set to
+ * long_term_frame_idx which requires no wrapping.
+ */
+ if (!b->refs[i].longterm && dpb[i].frame_num > cur_frame_num)
+ b->refs[i].frame_num = (int)dpb[i].frame_num -
+ max_frame_num;
+ else
+ b->refs[i].frame_num = dpb[i].frame_num;
+
+ b->refs[i].top_field_order_cnt = dpb[i].top_field_order_cnt;
+ b->refs[i].bottom_field_order_cnt = dpb[i].bottom_field_order_cnt;
+
+ if (b->cur_pic_fields == V4L2_H264_FRAME_REF) {
+ u8 fields = V4L2_H264_FRAME_REF;
+
+ b->unordered_reflist[b->num_valid].index = i;
+ b->unordered_reflist[b->num_valid].fields = fields;
+ b->num_valid++;
+ continue;
+ }
+
+ if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) {
+ u8 fields = V4L2_H264_TOP_FIELD_REF;
+
+ b->unordered_reflist[b->num_valid].index = i;
+ b->unordered_reflist[b->num_valid].fields = fields;
+ b->num_valid++;
+ }
+
+ if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) {
+ u8 fields = V4L2_H264_BOTTOM_FIELD_REF;
+
+ b->unordered_reflist[b->num_valid].index = i;
+ b->unordered_reflist[b->num_valid].fields = fields;
+ b->num_valid++;
+ }
+ }
+
+ for (i = b->num_valid; i < ARRAY_SIZE(b->unordered_reflist); i++)
+ b->unordered_reflist[i].index = i;
+}
+EXPORT_SYMBOL_GPL(v4l2_h264_init_reflist_builder_gen);
+
static s32 v4l2_h264_get_poc(const struct v4l2_h264_reflist_builder *b,
const struct v4l2_h264_reference *ref)
{
diff --git a/include/media/v4l2-h264.h b/include/media/v4l2-h264.h
index 0d9eaa956123..1833c0556963 100644
--- a/include/media/v4l2-h264.h
+++ b/include/media/v4l2-h264.h
@@ -42,6 +42,7 @@ struct v4l2_h264_reflist_builder {
u8 cur_pic_fields;
struct v4l2_h264_reference unordered_reflist[V4L2_H264_REF_LIST_LEN];
+ /* FIXME: confusing with valid flag (active is checked). Proper terminology is "used" */
u8 num_valid;
};
@@ -51,6 +52,12 @@ v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b,
const struct v4l2_ctrl_h264_sps *sps,
const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]);
+void
+v4l2_h264_init_reflist_builder_gen(struct v4l2_h264_reflist_builder *b,
+ const struct v4l2_ctrl_h264_sps *sps,
+ const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES],
+ unsigned int pic_order_count, unsigned int frame_num, unsigned int fields);
+
/**
* v4l2_h264_build_b_ref_lists() - Build the B0/B1 reference lists
*
--
2.53.0
^ permalink raw reply related
* [PATCH 00/14] media: Add V4L2 H.264 stateless encode and VC8000E support
From: Paul Kocialkowski @ 2026-05-22 10:16 UTC (permalink / raw)
To: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Nicolas Dufresne,
Benjamin Gaignard, Philipp Zabel, Mauro Carvalho Chehab,
Hans Verkuil, Marco Felsch, Michael Tretter, Paul Kocialkowski
This series introduces support for the V4L2 H.264 stateless encode uAPI,
core and support in the hantro driver for the Verisilicon VC8000E.
While this is a first version that will likely need some level of rework,
it is already usable for most common use-cases and supports constant
bitrate rate-control.
A GStreamer tree can be used to test the series at:
https://github.com/paulkocialkowski/gstreamer/tree/v4l2codecs/h264enc
And an example pipeline would look like:
gst-launch-1.0 videotestsrc pattern=smpte num-buffers=25 ! video/x-raw,width=640,height=480 ! v4l2slh264enc rate-control=cbr bitrate=8000000 qp-min=8 qp-max=42 ! h264parse ! matroskamux ! filesink location=encode.mkv
Note that documentation for the new uAPI is intentionally left out of
this series since it has not yet received approval.
Marco Felsch (2):
media: hantro: use hantro_decoded_buffer only for dst_vq
arm64: dts: imx8mp: add VC8000E encoder node
Paul Kocialkowski (12):
media: h264: Add a more generic reflist builder init
media: uapi: Add H.264 stateless encode support
media: h264: Add SPS video definitions
media: h264: Add stateless encode core
media: h264: Add stateless encode rbsp
media: h264: Add stateless encode reference management
media: h264: Add stateless encode rate control
media: verisilicon: Report default pixel coding for non-JPEG and fix
JPEG case
media: verisilicon: Cancel job with runtime pm put/clk disable on
failure
media: verisilicon: Add common encoder parm and frameintervals ioctls
media: verisilicon: Add support for the VC8000E H.264 encoder
media: verilisicon: imx8m: Add support for the VC8000E on i.MX8MP
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 11 +
drivers/media/platform/verisilicon/Kconfig | 1 +
drivers/media/platform/verisilicon/Makefile | 2 +
drivers/media/platform/verisilicon/hantro.h | 17 +
.../media/platform/verisilicon/hantro_drv.c | 180 +-
.../media/platform/verisilicon/hantro_h264.c | 6 +-
.../media/platform/verisilicon/hantro_hw.h | 28 +
.../media/platform/verisilicon/hantro_v4l2.c | 123 +-
.../platform/verisilicon/hantro_vc8000e.c | 68 +
.../verisilicon/hantro_vc8000e_h264_enc.c | 883 +++++++
.../verisilicon/hantro_vc8000e_regs.h | 2129 +++++++++++++++++
.../media/platform/verisilicon/imx8m_vpu_hw.c | 113 +
drivers/media/v4l2-core/Kconfig | 4 +
drivers/media/v4l2-core/Makefile | 2 +
drivers/media/v4l2-core/v4l2-ctrls-core.c | 62 +
drivers/media/v4l2-core/v4l2-ctrls-defs.c | 4 +
drivers/media/v4l2-core/v4l2-h264-enc-rbsp.c | 1173 +++++++++
drivers/media/v4l2-core/v4l2-h264-enc-rc.c | 558 +++++
drivers/media/v4l2-core/v4l2-h264-enc.c | 1322 ++++++++++
drivers/media/v4l2-core/v4l2-h264.c | 69 +
include/media/v4l2-ctrls.h | 2 +
include/media/v4l2-h264-enc-rbsp.h | 72 +
include/media/v4l2-h264-enc-rc.h | 108 +
include/media/v4l2-h264-enc.h | 135 ++
include/media/v4l2-h264.h | 146 ++
include/uapi/linux/v4l2-controls.h | 33 +
include/uapi/linux/videodev2.h | 1 +
27 files changed, 7231 insertions(+), 21 deletions(-)
create mode 100644 drivers/media/platform/verisilicon/hantro_vc8000e.c
create mode 100644 drivers/media/platform/verisilicon/hantro_vc8000e_h264_enc.c
create mode 100644 drivers/media/platform/verisilicon/hantro_vc8000e_regs.h
create mode 100644 drivers/media/v4l2-core/v4l2-h264-enc-rbsp.c
create mode 100644 drivers/media/v4l2-core/v4l2-h264-enc-rc.c
create mode 100644 drivers/media/v4l2-core/v4l2-h264-enc.c
create mode 100644 include/media/v4l2-h264-enc-rbsp.h
create mode 100644 include/media/v4l2-h264-enc-rc.h
create mode 100644 include/media/v4l2-h264-enc.h
--
2.53.0
^ permalink raw reply
* Re: [PATCH] arm64: tlb: Flush walk cache when unsharing PMD tables
From: Catalin Marinas @ 2026-05-22 10:13 UTC (permalink / raw)
To: Zeng Heng
Cc: yezhenyu2, zhurui3, will, akpm, npiggin, aneesh.kumar, peterz,
linux-kernel, wangkefeng.wang, linux-arm-kernel, linux-mm,
linux-arch, David Hildenbrand, zengheng4
In-Reply-To: <b7098272-a348-d918-774a-d5d2645fc7f4@huaweicloud.com>
On Fri, May 22, 2026 at 01:32:07PM +0800, Zeng Heng wrote:
> On 2026/5/21 23:15, Catalin Marinas wrote:
> > On Thu, May 21, 2026 at 04:05:07PM +0100, Catalin Marinas wrote:
> > > On Thu, May 21, 2026 at 03:30:11PM +0800, Zeng Heng wrote:
> > > > From: Zeng Heng <zengheng4@huawei.com>
> > > >
> > > > When huge_pmd_unshare() is called to unshare a PMD table, the
> > > > tlb_unshare_pmd_ptdesc() function sets tlb->unshared_tables=true
> > > > but the aarch64 tlb_flush() only checked tlb->freed_tables to
> > > > determine whether to use TLBF_NONE (vae1is, invalidates walk
> > > > cache) or TLBF_NOWALKCACHE (vale1is, leaf-only).
> > > >
> > > > This caused the stale PMD page table entry to remain in the walk cache
> > > > after unshare, potentially leading to incorrect page table walks.
> > > >
> > > > Fix by including unshared_tables in the check, so that when
> > > > unsharing tables, TLBF_NONE is used and the walk cache is properly
> > > > invalidated.
> > > >
> > > > Here is the detailed distinction between vae1is and vale1is:
> > > >
> > > > | Instruction Combination | Actual Invalidation Scope |
> > > > | ------------------------ | --------------------------------------------------|
> > > > | `VAE1IS` + TTL=`0` | All entries at all levels (full invalidation) |
> > > > | `VAE1IS` + TTL=`2` (L2) | Non-leaf at Level 0/1 + leaf at Level 2 |
> > > > | `VALE1IS` + TTL=`0` | Leaf entries at all levels (non-leaf not cleared) |
> > > > | `VALE1IS` + TTL=`2` (L2) | Leaf entry at Level 2 only |
> > > >
> > > > Signed-off-by: Zeng Heng <zengheng4@huawei.com>
> > > The fix looks fine but does it need:
> > >
> > > Fixes: 8ce720d5bd91 ("mm/hugetlb: fix excessive IPI broadcasts when unsharing PMD tables using mmu_gather")
> > > Cc: <stable@vger.kernel.org>
> > >
> > > > ---
> > > > arch/arm64/include/asm/tlb.h | 3 ++-
> > > > 1 file changed, 2 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h
> > > > index 10869d7731b8..751bd57bc3ba 100644
> > > > --- a/arch/arm64/include/asm/tlb.h
> > > > +++ b/arch/arm64/include/asm/tlb.h
> > > > @@ -53,7 +53,8 @@ static inline int tlb_get_level(struct mmu_gather *tlb)
> > > > static inline void tlb_flush(struct mmu_gather *tlb)
> > > > {
> > > > struct vm_area_struct vma = TLB_FLUSH_VMA(tlb->mm, 0);
> > > > - tlbf_t flags = tlb->freed_tables ? TLBF_NONE : TLBF_NOWALKCACHE;
> > > > + tlbf_t flags = (tlb->freed_tables || tlb->unshared_tables) ?
> > > > + TLBF_NONE : TLBF_NOWALKCACHE;
> > > > unsigned long stride = tlb_get_unmap_size(tlb);
> > > > int tlb_level = tlb_get_level(tlb);
> > Do we need this as well?
>
> The proposed fix has been validated against the issue scenarios and
> works as expected.
>
> Per the ARM Architecture Reference Manual, whether only the last-level
> page table entry is invalidated is determined by the instruction used
> (vale1is for leaf entry only, vae1is for walk cache including leaf entry and
> non-leaf entry), rather than the TTL field. The TTL field merely specifies
> which level the leaf entry belongs to.
Ah, yes, you are right. The TTL is still 2 in this case for a huge pmd,
we just want the walk cache leading to it to be invalidated. So no need
for the additional tlb_get_level().
Thanks.
--
Catalin
^ permalink raw reply
* [PATCH v5 5/5] arm64: dts: qcom: glymur: Add GPU cooling
From: Akhil P Oommen @ 2026-05-22 10:12 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rob Clark, Sean Paul, Dmitry Baryshkov,
Abhinav Kumar, Jessica Zhang, Marijn Suijten, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Will Deacon, Robin Murphy, Joerg Roedel
Cc: linux-arm-msm, devicetree, linux-kernel, dri-devel, freedreno,
linux-arm-kernel, iommu, Akhil P Oommen,
Manaf Meethalavalappu Pallikunhi
In-Reply-To: <20260522-glymur-gpu-dt-v5-0-562c406b210c@oss.qualcomm.com>
From: Manaf Meethalavalappu Pallikunhi <manaf.pallikunhi@oss.qualcomm.com>
The GPU does not throttle its speed automatically when it
reaches high temperatures. Set up GPU cooling by throttling
the GPU speed when it reaches 95°C.
Signed-off-by: Manaf Meethalavalappu Pallikunhi <manaf.pallikunhi@oss.qualcomm.com>
Signed-off-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/glymur.dtsi | 240 +++++++++++++++++++++++++++--------
1 file changed, 184 insertions(+), 56 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
index 01a2e32e503b..e109fb5b35a4 100644
--- a/arch/arm64/boot/dts/qcom/glymur.dtsi
+++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
@@ -22,6 +22,7 @@
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include <dt-bindings/soc/qcom,rpmh-rsc.h>
#include <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/thermal/thermal.h>
#include "glymur-ipcc.h"
@@ -7149,13 +7150,22 @@ aoss-7-critical {
};
thermal_gpu_0_0: gpu-0-0-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 1>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu00_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu00_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-0-0-critical {
@@ -7164,16 +7174,26 @@ gpu-0-0-critical {
type = "critical";
};
};
+
};
thermal_gpu_0_1: gpu-0-1-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 2>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu01_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu01_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-0-1-critical {
@@ -7185,13 +7205,22 @@ gpu-0-1-critical {
};
thermal_gpu_0_2: gpu-0-2-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 3>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu02_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu02_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-0-2-critical {
@@ -7203,13 +7232,22 @@ gpu-0-2-critical {
};
thermal_gpu_1_0: gpu-1-0-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 4>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu10_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu10_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-1-0-critical {
@@ -7221,13 +7259,22 @@ gpu-1-0-critical {
};
thermal_gpu_1_1: gpu-1-1-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 5>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu11_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu11_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-1-1-critical {
@@ -7239,13 +7286,22 @@ gpu-1-1-critical {
};
thermal_gpu_1_2: gpu-1-2-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 6>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu12_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu12_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-1-2-critical {
@@ -7257,13 +7313,22 @@ gpu-1-2-critical {
};
thermal_gpu_2_0: gpu-2-0-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 7>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu20_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu20_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-2-0-critical {
@@ -7275,13 +7340,22 @@ gpu-2-0-critical {
};
thermal_gpu_2_1: gpu-2-1-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 8>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu21_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu21_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-2-1-critical {
@@ -7293,13 +7367,22 @@ gpu-2-1-critical {
};
thermal_gpu_2_2: gpu-2-2-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 9>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu22_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu22_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-2-2-critical {
@@ -7311,13 +7394,22 @@ gpu-2-2-critical {
};
thermal_gpu_3_0: gpu-3-0-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 10>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu30_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu30_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-3-0-critical {
@@ -7329,13 +7421,22 @@ gpu-3-0-critical {
};
thermal_gpu_3_1: gpu-3-1-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 11>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu31_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu31_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-3-1-critical {
@@ -7347,13 +7448,22 @@ gpu-3-1-critical {
};
thermal_gpu_3_2: gpu-3-2-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 12>;
+ cooling-maps {
+ map0 {
+ trip = <&gpu32_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpu32_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpu-3-2-critical {
@@ -7365,13 +7475,22 @@ gpu-3-2-critical {
};
thermal_gpuss_0: gpuss-0-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 13>;
+ cooling-maps {
+ map0 {
+ trip = <&gpuss0_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpuss0_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpuss-0-critical {
@@ -7383,13 +7502,22 @@ gpuss-0-critical {
};
thermal_gpuss_1: gpuss-1-thermal {
+ polling-delay-passive = <100>;
+
thermal-sensors = <&tsens7 14>;
+ cooling-maps {
+ map0 {
+ trip = <&gpuss1_alert0>;
+ cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+
trips {
- trip-point0 {
- temperature = <90000>;
- hysteresis = <5000>;
- type = "hot";
+ gpuss1_alert0: trip-point0 {
+ temperature = <95000>;
+ hysteresis = <1000>;
+ type = "passive";
};
gpuss-1-critical {
--
2.51.0
^ permalink raw reply related
* [PATCH v5 4/5] arm64: dts: qcom: Add GPU support for Glymur
From: Akhil P Oommen @ 2026-05-22 10:12 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rob Clark, Sean Paul, Dmitry Baryshkov,
Abhinav Kumar, Jessica Zhang, Marijn Suijten, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Will Deacon, Robin Murphy, Joerg Roedel
Cc: linux-arm-msm, devicetree, linux-kernel, dri-devel, freedreno,
linux-arm-kernel, iommu, Akhil P Oommen, Konrad Dybcio
In-Reply-To: <20260522-glymur-gpu-dt-v5-0-562c406b210c@oss.qualcomm.com>
The Adreno X2 series GPU present in Glymur SoC belongs to the A8x
family. It is a new HW IP with architectural improvements as well
as different set of hw configs like GMEM, num SPs, Caches sizes etc.
Add the GPU and GMU nodes to describe this hardware.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/glymur.dtsi | 183 +++++++++++++++++++++++++++++++++++
1 file changed, 183 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
index 5e76a0d53f01..01a2e32e503b 100644
--- a/arch/arm64/boot/dts/qcom/glymur.dtsi
+++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
@@ -3701,6 +3701,129 @@ hsc_noc: interconnect@2000000 {
#interconnect-cells = <2>;
};
+ gpu: gpu@3d00000 {
+ compatible = "qcom,adreno-44070001", "qcom,adreno";
+ reg = <0x0 0x03d00000 0x0 0x6c000>,
+ <0x0 0x03d9e000 0x0 0x2000>;
+ reg-names = "kgsl_3d0_reg_memory",
+ "cx_mem";
+
+ interrupts = <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH>;
+
+ iommus = <&adreno_smmu 0 0x0>,
+ <&adreno_smmu 1 0x0>;
+
+ operating-points-v2 = <&gpu_opp_table>;
+
+ qcom,gmu = <&gmu>;
+ #cooling-cells = <2>;
+
+ interconnects = <&hsc_noc MASTER_GFX3D QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "gfx-mem";
+
+ gpu_opp_table: opp-table {
+ compatible = "operating-points-v2-adreno",
+ "operating-points-v2";
+
+ opp-310000000 {
+ opp-hz = /bits/ 64 <310000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS_D1>;
+ opp-peak-kBps = <2136719>;
+ opp-supported-hw = <0xf>;
+ /* ACD is disabled */
+ };
+
+ opp-410000000 {
+ opp-hz = /bits/ 64 <410000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
+ opp-peak-kBps = <6074219>;
+ opp-supported-hw = <0xf>;
+ /* ACD is disabled */
+ };
+
+ opp-572000000 {
+ opp-hz = /bits/ 64 <572000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
+ opp-peak-kBps = <12449219>;
+ opp-supported-hw = <0xf>;
+ qcom,opp-acd-level = <0xe02d5ffd>;
+ };
+
+ opp-760000000 {
+ opp-hz = /bits/ 64 <760000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
+ opp-peak-kBps = <12449219>;
+ opp-supported-hw = <0xf>;
+ qcom,opp-acd-level = <0xc0285ffd>;
+ };
+
+ opp-820000000 {
+ opp-hz = /bits/ 64 <820000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS_L2>;
+ opp-peak-kBps = <16500000>;
+ opp-supported-hw = <0xf>;
+ qcom,opp-acd-level = <0xa82e5ffd>;
+ };
+
+ opp-915000000 {
+ opp-hz = /bits/ 64 <915000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_NOM>;
+ opp-peak-kBps = <16500000>;
+ opp-supported-hw = <0xf>;
+ qcom,opp-acd-level = <0x882d5ffd>;
+ };
+
+ opp-1070000000 {
+ opp-hz = /bits/ 64 <1070000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>;
+ opp-peak-kBps = <16500000>;
+ opp-supported-hw = <0xf>;
+ qcom,opp-acd-level = <0x882b5ffd>;
+ };
+
+ opp-1185000000 {
+ opp-hz = /bits/ 64 <1185000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_TURBO>;
+ opp-peak-kBps = <16500000>;
+ opp-supported-hw = <0xf>;
+ qcom,opp-acd-level = <0x882a5ffd>;
+ };
+
+ opp-1350000000 {
+ opp-hz = /bits/ 64 <1350000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>;
+ opp-peak-kBps = <18597657>;
+ opp-supported-hw = <0xf>;
+ qcom,opp-acd-level = <0x882a5ffd>;
+ };
+
+ opp-1550000000 {
+ opp-hz = /bits/ 64 <1550000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L3>;
+ opp-peak-kBps = <18597657>;
+ opp-supported-hw = <0x7>;
+ qcom,opp-acd-level = <0xa8295ffd>;
+ };
+
+ opp-1700000000 {
+ opp-hz = /bits/ 64 <1700000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L4>;
+ opp-peak-kBps = <18597657>;
+ opp-supported-hw = <0x7>;
+ qcom,opp-acd-level = <0x88295ffd>;
+ };
+
+ opp-1850000000 {
+ opp-hz = /bits/ 64 <1850000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L5>;
+ opp-peak-kBps = <18597657>;
+ opp-supported-hw = <0x3>;
+ qcom,opp-acd-level = <0x88285ffd>;
+ };
+ };
+ };
+
gxclkctl: clock-controller@3d64000 {
compatible = "qcom,glymur-gxclkctl";
reg = <0x0 0x03d64000 0x0 0x6000>;
@@ -3712,6 +3835,66 @@ gxclkctl: clock-controller@3d64000 {
#power-domain-cells = <1>;
};
+ gmu: gmu@3d6c000 {
+ compatible = "qcom,adreno-gmu-x285.1", "qcom,adreno-gmu";
+
+ reg = <0x0 0x03d6c000 0x0 0x32000>;
+ reg-names = "gmu";
+
+ interrupts = <GIC_SPI 304 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "hfi",
+ "gmu";
+
+ clocks = <&gpucc GPU_CC_AHB_CLK>,
+ <&gpucc GPU_CC_CX_GMU_CLK>,
+ <&gpucc GPU_CC_CXO_CLK>,
+ <&gcc GCC_GPU_GEMNOC_GFX_CLK>,
+ <&gpucc GPU_CC_HUB_CX_INT_CLK>,
+ <&gpucc GPU_CC_RSCC_HUB_AON_CLK>;
+ clock-names = "ahb",
+ "gmu",
+ "cxo",
+ "memnoc",
+ "hub",
+ "rscc";
+
+ power-domains = <&gpucc GPU_CC_CX_GDSC>,
+ <&gxclkctl GX_CLKCTL_GX_GDSC>;
+ power-domain-names = "cx",
+ "gx";
+
+ iommus = <&adreno_smmu 5 0x0>;
+
+ qcom,qmp = <&aoss_qmp>;
+
+ operating-points-v2 = <&gmu_opp_table>;
+
+ gmu_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-575000000 {
+ opp-hz = /bits/ 64 <575000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
+ };
+
+ opp-700000000 {
+ opp-hz = /bits/ 64 <700000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
+ };
+
+ opp-725000000 {
+ opp-hz = /bits/ 64 <725000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
+ };
+
+ opp-750000000 {
+ opp-hz = /bits/ 64 <750000000>;
+ opp-level = <RPMH_REGULATOR_LEVEL_NOM>;
+ };
+ };
+ };
+
gpucc: clock-controller@3d90000 {
compatible = "qcom,glymur-gpucc";
reg = <0x0 0x03d90000 0x0 0x9800>;
--
2.51.0
^ permalink raw reply related
* [PATCH v5 3/5] arm64: dts: qcom: glymur: Add GPU smmu node
From: Akhil P Oommen @ 2026-05-22 10:11 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rob Clark, Sean Paul, Dmitry Baryshkov,
Abhinav Kumar, Jessica Zhang, Marijn Suijten, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Will Deacon, Robin Murphy, Joerg Roedel
Cc: linux-arm-msm, devicetree, linux-kernel, dri-devel, freedreno,
linux-arm-kernel, iommu, Akhil P Oommen, Rajendra Nayak,
Konrad Dybcio, Dmitry Baryshkov
In-Reply-To: <20260522-glymur-gpu-dt-v5-0-562c406b210c@oss.qualcomm.com>
From: Rajendra Nayak <rajendra.nayak@oss.qualcomm.com>
Add the nodes to describe the GPU SMMU node.
Signed-off-by: Rajendra Nayak <rajendra.nayak@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/glymur.dtsi | 38 ++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
index ed9aac42fcbf..5e76a0d53f01 100644
--- a/arch/arm64/boot/dts/qcom/glymur.dtsi
+++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
@@ -3729,6 +3729,44 @@ gpucc: clock-controller@3d90000 {
#power-domain-cells = <1>;
};
+ adreno_smmu: iommu@3da0000 {
+ compatible = "qcom,glymur-smmu-500", "qcom,adreno-smmu",
+ "qcom,smmu-500", "arm,mmu-500";
+ reg = <0x0 0x03da0000 0x0 0x40000>;
+ #iommu-cells = <2>;
+ #global-interrupts = <1>;
+ interrupts = <GIC_SPI 674 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 678 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 679 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 680 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 681 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 682 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 683 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 684 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 685 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 686 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 687 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 688 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 476 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 574 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 575 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 576 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 577 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 660 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 662 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 665 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 666 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 667 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 669 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 670 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 700 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gpucc GPU_CC_GPU_SMMU_VOTE_CLK>;
+ clock-names = "hlos";
+ power-domains = <&gpucc GPU_CC_CX_GDSC>;
+ dma-coherent;
+ };
+
ipcc: mailbox@3e04000 {
compatible = "qcom,glymur-ipcc", "qcom,ipcc";
reg = <0x0 0x03e04000 0x0 0x1000>;
--
2.51.0
^ permalink raw reply related
* [PATCH v5 2/5] dt-bindings: display/msm: gpu: Document Adreno X2-185
From: Akhil P Oommen @ 2026-05-22 10:11 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rob Clark, Sean Paul, Dmitry Baryshkov,
Abhinav Kumar, Jessica Zhang, Marijn Suijten, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Will Deacon, Robin Murphy, Joerg Roedel
Cc: linux-arm-msm, devicetree, linux-kernel, dri-devel, freedreno,
linux-arm-kernel, iommu, Akhil P Oommen
In-Reply-To: <20260522-glymur-gpu-dt-v5-0-562c406b210c@oss.qualcomm.com>
Adreno X2-185 GPU found in Glymur chipsets belongs to the A8x family.
It features a new slice architecture with 4 slices, significantly higher
bandwidth throughput compared to mobile counterparts, raytracing support,
and the highest GPU Fmax seen so far on an Adreno GPU (1850 Mhz), among
other improvements. Update the dt bindings documentation to describe this
GPU.
Signed-off-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
---
Documentation/devicetree/bindings/display/msm/gpu.yaml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml
index 04b2328903ca..77caacd0fb3f 100644
--- a/Documentation/devicetree/bindings/display/msm/gpu.yaml
+++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml
@@ -411,6 +411,21 @@ allOf:
- clocks
- clock-names
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,adreno-44070001
+ then:
+ properties:
+ reg:
+ minItems: 2
+ maxItems: 2
+
+ reg-names:
+ minItems: 2
+ maxItems: 2
+
- if:
properties:
compatible:
@@ -434,6 +449,7 @@ allOf:
- qcom,adreno-43050a01
- qcom,adreno-43050c01
- qcom,adreno-43051401
+ - qcom,adreno-44070001
then: # Starting with A6xx, the clocks are usually defined in the GMU node
properties:
--
2.51.0
^ permalink raw reply related
* [PATCH v5 1/5] drm/msm/a8xx: Fix RSCC offset
From: Akhil P Oommen @ 2026-05-22 10:11 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rob Clark, Sean Paul, Dmitry Baryshkov,
Abhinav Kumar, Jessica Zhang, Marijn Suijten, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Will Deacon, Robin Murphy, Joerg Roedel
Cc: linux-arm-msm, devicetree, linux-kernel, dri-devel, freedreno,
linux-arm-kernel, iommu, Akhil P Oommen
In-Reply-To: <20260522-glymur-gpu-dt-v5-0-562c406b210c@oss.qualcomm.com>
In A8xx, the RSCC block is part of GPU's register space. Update the
virtual base address of rscc to point to the correct address.
Fixes: 50e8a557d8d3 ("drm/msm/a8xx: Add support for A8x GMU")
Signed-off-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index 1b44b9e21ad8..cab4c46c6cf2 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -2357,7 +2357,12 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
goto err_mmio;
}
} else if (adreno_is_a8xx(adreno_gpu)) {
- gmu->rscc = gmu->mmio + 0x19000;
+ /*
+ * On a8xx , RSCC lives at GPU base + 0x50000, which falls
+ * inside the GPU's kgsl_3d0_reg_memory range rather than the
+ * GMU's.
+ */
+ gmu->rscc = gpu->mmio + 0x50000;
} else {
gmu->rscc = gmu->mmio + 0x23000;
}
--
2.51.0
^ permalink raw reply related
* [PATCH v5 0/5] Devicetree support for Glymur GPU
From: Akhil P Oommen @ 2026-05-22 10:11 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Rob Clark, Sean Paul, Dmitry Baryshkov,
Abhinav Kumar, Jessica Zhang, Marijn Suijten, David Airlie,
Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Will Deacon, Robin Murphy, Joerg Roedel
Cc: linux-arm-msm, devicetree, linux-kernel, dri-devel, freedreno,
linux-arm-kernel, iommu, Akhil P Oommen, Rajendra Nayak,
Konrad Dybcio, Dmitry Baryshkov, Manaf Meethalavalappu Pallikunhi
This series adds the necessary Device Tree bits to enable GPU support
on the Glymur-based CRD devices. The Adreno X2-85 GPU present in Glymur
chipsets is based on the new Adreno A8x family of GPUs. It features a new
slice architecture with 4 slices, significantly higher bandwidth
throughput compared to mobile counterparts, raytracing support, and the
highest GPU Fmax seen so far on an Adreno GPU (1850 Mhz), among other
improvements.
This series includes patches that updates DT schema, add GPU SMMU &
GPU/GMU support. Keen-eyed readers may notice that the zap shader node
is missing. This is intentional: The Glymur-based laptop platforms
generally allow booting Linux at EL2 (yay!), which means the zap firmware
is not required here.
There is an update to the gxclkctl/drm drivers to properly support the IFPC
feature across all A8x GPUs. That series [1] is necessary to properly
support Glymur GPU:
[1] https://lore.kernel.org/lkml/20260427-gfx-clk-fixes-v2-0-797e54b3d464@oss.qualcomm.com/
Just FYI, on top of the linux-next, I had to pick below series [2] to boot
the device properly. But it is unrelated to GPU or this series:
[2] https://lore.kernel.org/all/20260331-qref_vote-v1-0-3fd7fbf87864@oss.qualcomm.com/
Signed-off-by: Akhil P Oommen <akhilpo@oss.qualcomm.com>
---
Changes in v5:
- Relax contraints for reg-names property (Krzysztof)
- Drop the smmu binding doc patch as it got picked up
- Link to v4: https://lore.kernel.org/r/20260513-glymur-gpu-dt-v4-0-f83832c3bc9a@oss.qualcomm.com
Changes in v4:
- Add a new patch for passive cooling support
- Link to v3: https://lore.kernel.org/r/20260512-glymur-gpu-dt-v3-0-84232dc21c03@oss.qualcomm.com
Changes in v3:
- Add a new patch to fix RSCC base vaddr in drm-msm
- Remove interconnect property from adreno smmu dt and the binding doc
- Add a contrait in GPU binding doc to limit the reg entries for Glymur
(Krzysztof)
- Link to v2: https://lore.kernel.org/r/20260501-glymur-gpu-dt-v2-0-2f128b5596bb@oss.qualcomm.com
Changes in v2:
- Keep GPU/GMU enabled by default and drop the enablement patch (Konrad)
- Drop zap shader node from DT
- A new patch to update GPU SMMU dt schema.
- Adjust reg range in dt nodes to avoid overlap.
- Removed cx_dbgc range as it is already stable across chipsets. This
region is now part of kgsl_3d0_reg_memory range.
- Link to v1: https://lore.kernel.org/r/20260405-glymur-gpu-dt-v1-0-2135eb11c562@oss.qualcomm.com
---
Akhil P Oommen (3):
drm/msm/a8xx: Fix RSCC offset
dt-bindings: display/msm: gpu: Document Adreno X2-185
arm64: dts: qcom: Add GPU support for Glymur
Manaf Meethalavalappu Pallikunhi (1):
arm64: dts: qcom: glymur: Add GPU cooling
Rajendra Nayak (1):
arm64: dts: qcom: glymur: Add GPU smmu node
.../devicetree/bindings/display/msm/gpu.yaml | 16 +
arch/arm64/boot/dts/qcom/glymur.dtsi | 461 ++++++++++++++++++---
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 7 +-
3 files changed, 427 insertions(+), 57 deletions(-)
---
base-commit: c9bd03db3e792a99e9789fde20e91898e3a29e8a
change-id: 20260226-glymur-gpu-dt-339e5092606b
prerequisite-message-id: <20260410-glymur_mmcc_dt_config_v2-v3-0-acce9d106e72@oss.qualcomm.com>
prerequisite-patch-id: f7ab29f2f0241b6536d3b0c0593f0baa0e435221
prerequisite-patch-id: 56c830b7718129323b006e492aed9822d7c30079
Best regards,
--
Akhil P Oommen <akhilpo@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v8 next 02/10] arm_mpam: Add intPARTID and reqPARTID support for Narrow-PARTID feature
From: Zeng Heng @ 2026-05-22 10:07 UTC (permalink / raw)
To: James Morse, ben.horgan, Dave.Martin, tan.shaopeng,
reinette.chatre, fenghuay, tglx, will, hpa, bp, babu.moger,
dave.hansen, mingo, tony.luck, gshan, catalin.marinas
Cc: linux-arm-kernel, x86, linux-kernel, wangkefeng.wang, zengheng4
In-Reply-To: <763d19a5-9b1a-4243-a7d7-9484c42c32c7@arm.com>
Hi James,
On 2026/5/15 1:06, James Morse wrote:
> Hi Zeng,
>
> On 13/04/2026 09:53, Zeng Heng wrote:
>> Introduce Narrow-PARTID (partid_nrw) feature support, which enables
>> many-to-one mapping of request PARTIDs (reqPARTID) to internal PARTIDs
>> (intPARTID). This expands monitoring capability by allowing a single
>> control group to track more task types through multiple reqPARTIDs
>> per intPARTID, bypassing the PMG limit in some extent.
>>
>> intPARTID: Internal PARTID used for control group configuration.
>> Configurations are synchronized to all reqPARTIDs mapped to the same
>> intPARTID. Count is indicated by MPAMF_PARTID_NRW_IDR.INTPARTID_MAX, or
>> defaults to PARTID count if Narrow-PARTID is unsupported.
>>
>> reqPARTID: Request PARTID used to expand monitoring groups. Enables
>> a single control group to monitor more task types by multiple reqPARTIDs
>> within one intPARTID, overcoming the PMG count limitation.
>>
>> For systems with homogeneous MSCs (all supporting Narrow-PARTID), the
>> driver exposes the full reqPARTID range directly. For heterogeneous
>> systems where some MSCs lack Narrow-PARTID support, the driver utilizes
>> PARTIDs beyond the intPARTID range as reqPARTIDs to expand monitoring
>> capacity.
>>
>> So, the numbers of control group and monitoring group are calculated as:
>>
>> n = min(intPARTID, PARTID) /* the number of control groups */
>> l = min(reqPARTID, PARTID) /* the number of monitoring groups */
>> m = l // n /* monitoring groups per control group */
>>
>> Where:
>>
>> intPARTID: intPARTIDs on Narrow-PARTID-capable MSCs
>> reqPARTID: reqPARTIDs on Narrow-PARTID-capable MSCs
>> PARTID: PARTIDs on non-Narrow-PARTID-capable MSCs
>>
>> Example: L3 cache (256 PARTIDs, without Narrow-PARTID feature) +
>> MATA (32 intPARTIDs, 256 reqPARTIDs):
>>
>> n = min( 32, 256) = 32 intPARTIDs
>> l = min(256, 256) = 256 reqPARTIDs
>> m = 256 / 32 = 8 reqPARTIDs per intPARTID
>>
>> Implementation notes:
>> * Handle mixed MSC systems (some support Narrow-PARTID, some don't) by
>> taking minimum number of intPARTIDs across all MSCs.
>> * resctrl_arch_get_num_closid() now returns the number of intPARTIDs
>> (was PARTID).
>
> What you're doing here is making intPARTID the fundamental unit in MPAM. I
> don't think we should do this as its not true in the architecture: narrowing
> is a single, optional feature. We have platforms that don't support narrowing
> at all - so having to think in terms of "what is the intPARTID limit on this
> platform that doesn't have the feature" is confusing.
> Narrowing doesn't affect the monitoring, so you can't just string-replace the
> driver.
>
> The resctrl glue code is going to have to know about narrowing as it must
> either duplicate the control values for aliasing PARTID, or remap them using
> narrowing.
>
> I'd prefer it if the mpam_devices code exposed an API to make use of narrowing
> that makes sense given the MPAM architecture. (e.g. narrowing is optional!)
> Whetever resctrl needs should then be built on top of that.
>
>
> Currently the MAX PARTID/PMG are dealt with separately as we need a global value
> at the end. It was done separately as it could be a patch on its own, to try and
> keep each patch reviewable.
> But it should probably have been dealt with the same way we deal with all MSC
> features - stash them in struct mpam_msc_ris at hw_probe time, and combine them
> up to the class level handling different values with __props_mismatch().
> The global state that includes the requestors can be created after that point.
>
> I think we should do this now to add intpartid_max to struct mpam_props so that the
> resctrl glue code can find the intpartid_max per class. I don't think it makes sense as
> a global property. (we should move partid_max and pmg_max at the same time)
>
> ... I think moving partid_max would just be cleanup. The resctrl glue code needs to
> know the maximum PARTID for monitoring, but I think this would always be the global
> PARTID max value.
>
I'll have a try to look into refactoring the existing code along the
lines you've suggested.
>
>> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
>> index 41b14344b16f..cf067bf5092e 100644
>> --- a/drivers/resctrl/mpam_devices.c
>> +++ b/drivers/resctrl/mpam_devices.c
>> @@ -63,6 +63,7 @@ static DEFINE_MUTEX(mpam_cpuhp_state_lock);
>> * Generating traffic outside this range will result in screaming interrupts.
>> */
>> u16 mpam_partid_max;
>> +u16 mpam_intpartid_max;
>> u8 mpam_pmg_max;
>> static bool partid_max_init, partid_max_published;
>> static DEFINE_SPINLOCK(partid_max_lock);
>
> The global properties are supposed to mean "if you generate any traffic outside
> this range, you'll get an out of range error causing mpam_disable()".
>
> This doesn't hold for intPARTID.
>
> In my hypothetical system from the cover letter: {
> 64 PARTID at the L3
> 64 PARTID and narrowing to 16 at the SLC
> 64 PARTID and narrowing to 32 at the memory-controller
>
> The resctrl glue code could ignore the SLC if it wanted to use 32 PARTID, and just
> duplicate the aliasing controls at L3. (and remove the non-aliasing controls)
> }
>
>
Thanks for the example.
Best regards,
Zeng Heng
^ permalink raw reply
* [PATCH] ARM: dts: aspeed: clemente: Remove IOB NIC TMP421 nodes
From: Mike Hsieh @ 2026-05-22 10:07 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joel Stanley,
Andrew Jeffery
Cc: devicetree, linux-arm-kernel, linux-aspeed, linux-kernel,
Cosmo Chou, Potin Lai, Mike Hsieh, Mike Hsieh
Remove the TMP421 sensor entry from the DTS, as it is no longer the
primary telemetry source.
Accessing the CX8 NIC via I2C while it is powered off causes voltage
leakage on the bus, leading to EEPROM corruption on shared I2C devices.
Removing this node prevents the BMC from initiating traffic to the NIC
during initialization, protecting the integrity of the shared bus.
Signed-off-by: Mike Hsieh <mike.quanta.115@gmail.com>
---
Remove the TMP421 sensor entry from the DTS, as it is no longer the
primary telemetry source.
Accessing the CX8 NIC via I2C while it is powered off causes voltage
leakage on the bus, leading to EEPROM corruption on shared I2C devices.
Removing this node prevents the BMC from initiating traffic to the NIC
during initialization, protecting the integrity of the shared bus.
---
.../boot/dts/aspeed/aspeed-bmc-facebook-clemente.dts | 20 --------------------
1 file changed, 20 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-clemente.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-clemente.dts
index 2aff21442f11..820d39a92974 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-clemente.dts
+++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-clemente.dts
@@ -443,11 +443,6 @@ i2c0mux2ch0: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
- // IOB0 NIC0 TEMP
- temperature-sensor@1f {
- compatible = "ti,tmp421";
- reg = <0x1f>;
- };
};
i2c0mux2ch1: i2c@1 {
@@ -466,11 +461,6 @@ i2c0mux2ch3: i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
- // IOB0 NIC1 TEMP
- temperature-sensor@1f {
- compatible = "ti,tmp421";
- reg = <0x1f>;
- };
};
};
@@ -637,11 +627,6 @@ i2c0mux5ch0: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
- // IOB1 NIC0 TEMP
- temperature-sensor@1f {
- compatible = "ti,tmp421";
- reg = <0x1f>;
- };
};
i2c0mux5ch1: i2c@1 {
@@ -666,11 +651,6 @@ i2c0mux5ch3: i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
- // IOB1 NIC1 TEMP
- temperature-sensor@1f {
- compatible = "ti,tmp421";
- reg = <0x1f>;
- };
};
};
};
---
base-commit: 6779b50faa562e6cca1aa6a4649a4d764c6c7e28
change-id: 20260522-clemente-dts-remove-iob-nic-tmp421-89221ba96dd7
Best regards,
--
Mike Hsieh <mike.quanta.115@gmail.com>
^ permalink raw reply related
* [PATCH 3/3] ASoC: rockchip: rockchip_pdm: Reorder clock enable sequence
From: phucduc.bui @ 2026-05-22 10:03 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, perex, tiwai, heiko, linux-arm-kernel, linux-kernel,
linux-sound, linux-rockchip, robh, krzk+dt, conor+dt, devicetree,
bui duc phuc
In-Reply-To: <20260522100318.73474-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
Enable the 'hclk' bus clock before the 'clk' controller
clock during runtime resume.
The bus clock provides the register access interface and
should be enabled before the controller clock.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/rockchip/rockchip_pdm.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c
index c69cdd6f2499..8f78f7bc1806 100644
--- a/sound/soc/rockchip/rockchip_pdm.c
+++ b/sound/soc/rockchip/rockchip_pdm.c
@@ -422,16 +422,16 @@ static int rockchip_pdm_runtime_resume(struct device *dev)
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(pdm->clk);
+ ret = clk_prepare_enable(pdm->hclk);
if (ret) {
- dev_err(pdm->dev, "clock enable failed %d\n", ret);
+ dev_err(pdm->dev, "hclock enable failed %d\n", ret);
return ret;
}
- ret = clk_prepare_enable(pdm->hclk);
+ ret = clk_prepare_enable(pdm->clk);
if (ret) {
- clk_disable_unprepare(pdm->clk);
- dev_err(pdm->dev, "hclock enable failed %d\n", ret);
+ clk_disable_unprepare(pdm->hclk);
+ dev_err(pdm->dev, "clock enable failed %d\n", ret);
return ret;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 2/3] ASoC: rockchip: spdif: Reorder clock enable sequence
From: phucduc.bui @ 2026-05-22 10:03 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, perex, tiwai, heiko, linux-arm-kernel, linux-kernel,
linux-sound, linux-rockchip, robh, krzk+dt, conor+dt, devicetree,
bui duc phuc
In-Reply-To: <20260522100318.73474-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
Enable the 'hclk' bus clock before the 'mclk' controller
clock during runtime resume.
The bus clock provides the register access interface and
should be enabled before the controller clock.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/rockchip/rockchip_spdif.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 581624f2682e..8de5b76cfe79 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -76,16 +76,16 @@ static int rk_spdif_runtime_resume(struct device *dev)
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(spdif->mclk);
+ ret = clk_prepare_enable(spdif->hclk);
if (ret) {
- dev_err(spdif->dev, "mclk clock enable failed %d\n", ret);
+ dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
return ret;
}
- ret = clk_prepare_enable(spdif->hclk);
+ ret = clk_prepare_enable(spdif->mclk);
if (ret) {
- clk_disable_unprepare(spdif->mclk);
- dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
+ clk_disable_unprepare(spdif->hclk);
+ dev_err(spdif->dev, "mclk clock enable failed %d\n", ret);
return ret;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 1/3] ASoC: dt-bindings: rockchip-spdif: Correct SPDIF clock descriptions
From: phucduc.bui @ 2026-05-22 10:03 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, perex, tiwai, heiko, linux-arm-kernel, linux-kernel,
linux-sound, linux-rockchip, robh, krzk+dt, conor+dt, devicetree,
bui duc phuc
In-Reply-To: <20260522100318.73474-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
The clock descriptions are currently swapped relative to the
clock names used by the driver.
Update the binding descriptions to match the actual clock
usage, where 'mclk' is the controller clock and 'hclk' is
the bus clock.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
Documentation/devicetree/bindings/sound/rockchip-spdif.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml b/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml
index 502907dd28b3..b174d7498029 100644
--- a/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml
+++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml
@@ -45,8 +45,8 @@ properties:
clocks:
items:
- - description: clock for SPDIF bus
- description: clock for SPDIF controller
+ - description: clock for SPDIF bus
clock-names:
items:
--
2.43.0
^ permalink raw reply related
* [PATCH 0/3] ASoC: rockchip: Reorder clock enable sequence
From: phucduc.bui @ 2026-05-22 10:03 UTC (permalink / raw)
To: broonie
Cc: lgirdwood, perex, tiwai, heiko, linux-arm-kernel, linux-kernel,
linux-sound, linux-rockchip, robh, krzk+dt, conor+dt, devicetree,
bui duc phuc
From: bui duc phuc <phucduc.bui@gmail.com>
Hi all,
This series reorders the runtime resume clock enable
sequence in the Rockchip SPDIF and PDM drivers to enable
the bus clock before the functional controller clock.
It also updates the SPDIF DT binding clock descriptions to
match the actual clock usage in the driver.
Best Regards,
Phuc
bui duc phuc (3):
ASoC: dt-bindings: rockchip-spdif: Correct SPDIF clock descriptions
ASoC: rockchip: spdif: Reorder clock enable sequence
ASoC: rockchip: rockchip_pdm: Reorder clock enable sequence
.../devicetree/bindings/sound/rockchip-spdif.yaml | 2 +-
sound/soc/rockchip/rockchip_pdm.c | 10 +++++-----
sound/soc/rockchip/rockchip_spdif.c | 10 +++++-----
3 files changed, 11 insertions(+), 11 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v14 09/44] arm64: RMI: Provide functions to delegate/undelegate ranges of memory
From: Marc Zyngier @ 2026-05-22 10:02 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: Steven Price, kvm, kvmarm, Catalin Marinas, Will Deacon,
James Morse, Oliver Upton, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <a81c3688-30f1-43e4-8d57-1d08a6e563af@arm.com>
On Thu, 21 May 2026 17:01:37 +0100,
Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>
> On 21/05/2026 14:59, Marc Zyngier wrote:
> > On Wed, 13 May 2026 14:17:17 +0100,
> > Steven Price <steven.price@arm.com> wrote:
> >>
> >> The RMM requires memory is 'delegated' to it so that it can be used
> >> either for a realm guest or for various tracking purposes within the RMM
> >> (e.g. for metadata or page tables). Memory that has been delegated
> >> cannot be accessed by the host (it will result in a Granule Protection
> >> Fault).
> >>
> >> Undelegation may fail if the memory is still in use by the RMM. This
> >> shouldn't happen (Linux should ensure it has destroyed the RMM objects
> >> before attempting to undelegate). In the event that it does happen this
> >> points to a programming bug and the only reasonable approach is for the
> >> physical pages to be leaked - it is up to the caller of
> >> rmi_undelegate_range() to handle this.
> >>
> >> Signed-off-by: Steven Price <steven.price@arm.com>
> >> ---
> >> v14:
> >> * Split into separate patch and moved out of KVM
> >> ---
> >> arch/arm64/include/asm/rmi_cmds.h | 13 +++++++++++
> >> arch/arm64/kernel/rmi.c | 36 +++++++++++++++++++++++++++++++
> >> 2 files changed, 49 insertions(+)
> >>
> >> diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
> >> index 9078a2920a7c..eb213c8e6f26 100644
> >> --- a/arch/arm64/include/asm/rmi_cmds.h
> >> +++ b/arch/arm64/include/asm/rmi_cmds.h
> >> @@ -33,6 +33,19 @@ struct rmi_sro_state {
> >> } while (RMI_RETURN_STATUS(res.a0) == RMI_BUSY || \
> >> RMI_RETURN_STATUS(res.a0) == RMI_BLOCKED)
> >> +int rmi_delegate_range(phys_addr_t phys, unsigned long size);
> >> +int rmi_undelegate_range(phys_addr_t phys, unsigned long size);
> >> +
> >> +static inline int rmi_delegate_page(phys_addr_t phys)
> >> +{
> >> + return rmi_delegate_range(phys, PAGE_SIZE);
> >> +}
> >> +
> >> +static inline int rmi_undelegate_page(phys_addr_t phys)
> >> +{
> >> + return rmi_undelegate_range(phys, PAGE_SIZE);
> >> +}
> >> +
> >> bool rmi_is_available(void);
> >> unsigned long rmi_sro_execute(struct rmi_sro_state *sro, gfp_t
> >> gfp);
> >> diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
> >> index 52a415e99500..08cef54acadb 100644
> >> --- a/arch/arm64/kernel/rmi.c
> >> +++ b/arch/arm64/kernel/rmi.c
> >> @@ -12,6 +12,42 @@ static bool arm64_rmi_is_available;
> >> unsigned long rmm_feat_reg0;
> >> unsigned long rmm_feat_reg1;
> >> +int rmi_delegate_range(phys_addr_t phys, unsigned long size)
> >> +{
> >> + unsigned long ret = 0;
> >> + unsigned long top = phys + size;
> >> + unsigned long out_top;
> >> +
> >> + while (phys < top) {
> >> + ret = rmi_granule_range_delegate(phys, top, &out_top);
> >> + if (ret == RMI_SUCCESS)
> >> + phys = out_top;
> >> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
> >> + return ret;
> >> + }
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +int rmi_undelegate_range(phys_addr_t phys, unsigned long size)
> >> +{
> >> + unsigned long ret = 0;
> >> + unsigned long top = phys + size;
> >> + unsigned long out_top;
> >> +
> >> + WARN_ON(size == 0);
> >
> > I find it odd to warn on size = 0. After all, free(NULL) is not an
> > error. But even then, you continue feeding this to the RMM.
> >
> > You also don't seem to be bothered with that on the delegation side...
> >
> >> +
> >> + while (phys < top) {
> >> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
> >> + if (ret == RMI_SUCCESS)
> >> + phys = out_top;
> >
> > and size==0 doesn't violate any of the failure conditions listed in
> > B4.5.18.2 (beta2). Will you end-up looping around forever?
>
> That is not true ? It triggers, top_bound error condition, for both.
>
>
> pre: UInt(top) <= UInt(base)
> post: result.status == RMI_ERROR_INPUT
News flash, I can't read. Ignore me.
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply
* Re: [PATCH v8 next 03/10] arm_mpam: Disable reqPARTID expansion when Narrow-PARTID is unavailable
From: Zeng Heng @ 2026-05-22 9:57 UTC (permalink / raw)
To: James Morse, ben.horgan, Dave.Martin, tan.shaopeng,
reinette.chatre, fenghuay, tglx, will, hpa, bp, babu.moger,
dave.hansen, mingo, tony.luck, gshan, catalin.marinas
Cc: linux-arm-kernel, x86, linux-kernel, wangkefeng.wang, zengheng4
In-Reply-To: <9efc30be-689b-4f42-bef0-d7d62b4392fa@arm.com>
Hi James,
On 2026/5/15 1:06, James Morse wrote:
> Hi Zeng,
>
> On 13/04/2026 09:53, Zeng Heng wrote:
>> MPAM supports heterogeneous systems where some type of MSCs may implement
>> Narrow-PARTID while others do not. However, when an MSC uses
>> percentage-based throttling (non-bitmap partition control) and lacks
>> Narrow-PARTID support, resctrl cannot correctly apply control group
>> configurations across multiple PARTIDs.
>>
>> To enable free assignment of multiple reqPARTIDs to resource control
>> groups, all MSCs used by resctrl must either: Implement Narrow-PARTID,
>> allowing explicit PARTID remapping, or only have stateless resource
>> controls (non-percentage-based), such that splitting a control group
>> across multiple PARTIDs does not affect behavior.
>
> I prefer Dave's terminology for this: aliasing and non-aliasing. It implies
> there are two controls, which stateless does not.
>
OK, count me as a fan of the terminology too.
>
>> The detection occurs at initialization time on the first call to
>> get_num_reqpartid() from update_rmid_limits(). This call is guaranteed
>> to occur after mpam_resctrl_pick_{mba,caches}() have set up the
>> resource classes, ensuring the necessary properties are available
>> for the Narrow-PARTID capability check.
>>
>> When an MSC with percentage-based control lacks Narrow-PARTID support,
>> get_num_reqpartid() falls back to returning the number of intPARTIDs,
>> effectively disabling the reqPARTID expansion for monitoring groups.
>
> No MSC has percentage based controls - that's an x86ism. The MSCs have
> fixed point fractions, bitmaps or a cost/weight.
>
Yes, thanks for head-up: it's x86ism.
>
> I think you're thinking about this the wrong way up - we should only enable
> this on a small number of platforms that don't have any controls we'd have to discard.
> (hopefully yours is such a platform!)
>
> I don't think this should be added to resctrl_arch_system_num_rmid_idx(). Please make
> this decision for resctrl at mpam_resctrl_setup() time.
>
>
>> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
>> index 5f4364c8101a..56859f354efa 100644
>> --- a/drivers/resctrl/mpam_resctrl.c
>> +++ b/drivers/resctrl/mpam_resctrl.c
>> @@ -257,9 +257,50 @@ u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored)
>> return mpam_intpartid_max + 1;
>> }
>>
>> +/*
>> + * Determine the effective number of PARTIDs available for resctrl.
>> + *
>> + * This function performs a one-time check to determine if Narrow-PARTID
>> + * can be used. It must be called after mpam_resctrl_pick_{mba,caches}()
>> + * have initialized the resource classes, as class properties are used
>> + * to detect Narrow-PARTID support.
>
>> + * The first call occurs in update_rmid_limits(), ensuring the
>> + * prerequisite initialization is complete.
>
> This is fragile to changes in the order resctrl makes these calls. We need these
> properties to be fixed before we call resctrl_init().
>
> (yes - I think CDP is fragile too!)
It makes sense to me.
>
>
>> + */
>> +static u32 get_num_reqpartid(void)
>> +{
>> + struct mpam_resctrl_res *res;
>> + struct mpam_props *cprops;
>> + static bool first = true;
>> + int rid;
>> +
>> + if (first) {
>> + for_each_mpam_resctrl_control(res, rid) {
>> + if (!res->class)
>> + continue;
>> +
>> + cprops = &res->class->props;
>> + if (mpam_has_feature(mpam_feat_partid_nrw, cprops))
>> + continue;
>
>
>> + if (mpam_has_feature(mpam_feat_mbw_max, cprops) ||
>> + mpam_has_feature(mpam_feat_mbw_min, cprops) ||
>> + mpam_has_feature(mpam_feat_cmax_cmax, cprops) ||
>> + mpam_has_feature(mpam_feat_cmax_cmin, cprops)) {
>
> Please make this a helper in mpam_internal.h with 'controls' and 'aliasing' in its name.
> (maybe has_aliasing_controls()).
>
> What about the priority for PRI and the proportional-stride?
>
> I don't think proportional-stride aliases properly: if I have groups with stride 1 and 2,
> I can't add a second '2' without decreasing the first groups stride from 1/3 to 1/5. If I
> halve the second groups, they each get half the bandwidth instead of sharing it.
>
> Can you check whether the priority for PRI aliases?
>
Proportional-stride is indeed a non-aliasing control, and this was an
oversight on my part (I haven't actually encountered this control option
before).
I'd argue that PRI is an aliasing control: the priority value defines a
scheduling class or identity. When multiple PARTIDs share the same
priority, it's akin to multiple users holding the same VIP tier. They
compete for resources within that same category, rather than each
receiving an independent, additive allocation.
Best regards,
Zeng Heng
^ permalink raw reply
* [PATCH] ASoC: sunxi: sun50i-dmic: Reorder clock enable sequence
From: phucduc.bui @ 2026-05-22 9:55 UTC (permalink / raw)
To: broonie
Cc: codekipper, jernej.skrabec, lgirdwood, linux-arm-kernel,
linux-kernel, linux-sound, linux-sunxi, nichen, perex, samuel,
tiwai, wens, bui duc phuc
From: bui duc phuc <phucduc.bui@gmail.com>
Enable the bus clock before the DMIC module clock during
runtime resume.
The bus clock provides the register access interface and
should be enabled before the module clock.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/sunxi/sun50i-dmic.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c
index eddfebe16616..7aff1afdf265 100644
--- a/sound/soc/sunxi/sun50i-dmic.c
+++ b/sound/soc/sunxi/sun50i-dmic.c
@@ -323,16 +323,16 @@ static int sun50i_dmic_runtime_suspend(struct device *dev)
static int sun50i_dmic_runtime_resume(struct device *dev)
{
- struct sun50i_dmic_dev *host = dev_get_drvdata(dev);
+ struct sun50i_dmic_dev *host = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(host->dmic_clk);
+ ret = clk_prepare_enable(host->bus_clk);
if (ret)
return ret;
- ret = clk_prepare_enable(host->bus_clk);
+ ret = clk_prepare_enable(host->dmic_clk);
if (ret) {
- clk_disable_unprepare(host->dmic_clk);
+ clk_disable_unprepare(host->bus_clk);
return ret;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v14 04/44] arm64: RMI: Add SMC definitions for calling the RMM
From: Marc Zyngier @ 2026-05-22 9:58 UTC (permalink / raw)
To: Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <3261b04f-1a0c-451d-8981-1e2bccc8a9ca@arm.com>
On Thu, 21 May 2026 16:33:09 +0100,
Steven Price <steven.price@arm.com> wrote:
>
> On 21/05/2026 13:40, Marc Zyngier wrote:
> > On Wed, 13 May 2026 14:17:12 +0100,
> > Steven Price <steven.price@arm.com> wrote:
> >>
> >> The RMM (Realm Management Monitor) provides functionality that can be
> >> accessed by SMC calls from the host.
> >>
> >> The SMC definitions are based on DEN0137[1] version 2.0-bet1
> >>
> >> [1] https://developer.arm.com/documentation/den0137/2-0bet1/
> >>
> >> Signed-off-by: Steven Price <steven.price@arm.com>
> >> ---
> >> Changes since v13:
> >> * Updated to RMM spec v2.0-bet1
> >> Changes since v12:
> >> * Updated to RMM spec v2.0-bet0
> >> Changes since v9:
> >> * Corrected size of 'ripas_value' in struct rec_exit. The spec states
> >> this is an 8-bit type with padding afterwards (rather than a u64).
> >> Changes since v8:
> >> * Added RMI_PERMITTED_GICV3_HCR_BITS to define which bits the RMM
> >> permits to be modified.
> >> Changes since v6:
> >> * Renamed REC_ENTER_xxx defines to include 'FLAG' to make it obvious
> >> these are flag values.
> >> Changes since v5:
> >> * Sorted the SMC #defines by value.
> >> * Renamed SMI_RxI_CALL to SMI_RMI_CALL since the macro is only used for
> >> RMI calls.
> >> * Renamed REC_GIC_NUM_LRS to REC_MAX_GIC_NUM_LRS since the actual
> >> number of available list registers could be lower.
> >> * Provided a define for the reserved fields of FeatureRegister0.
> >> * Fix inconsistent names for padding fields.
> >> Changes since v4:
> >> * Update to point to final released RMM spec.
> >> * Minor rearrangements.
> >> Changes since v3:
> >> * Update to match RMM spec v1.0-rel0-rc1.
> >> Changes since v2:
> >> * Fix specification link.
> >> * Rename rec_entry->rec_enter to match spec.
> >> * Fix size of pmu_ovf_status to match spec.
> >> ---
> >> arch/arm64/include/asm/rmi_smc.h | 448 +++++++++++++++++++++++++++++++
> >> 1 file changed, 448 insertions(+)
> >> create mode 100644 arch/arm64/include/asm/rmi_smc.h
> >>
> >> diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
> >> new file mode 100644
> >> index 000000000000..a09b7a631fef
> >> --- /dev/null
> >> +++ b/arch/arm64/include/asm/rmi_smc.h
> >> @@ -0,0 +1,448 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2023-2026 ARM Ltd.
> >> + *
> >> + * The values and structures in this file are from the Realm Management Monitor
> >> + * specification (DEN0137) version 2.0-bet1:
> >> + * https://developer.arm.com/documentation/den0137/2-0bet1/
> >
> > How long is this spec going to be available on the ARM web site, which
> > has a tendency of being reorganised every other week? And there is
> > already a beta2.
>
> Obviously I can't predict the next reorganisation - but at least it's a
> link that could be fed into archive.org or similar.
I found that the PDF spec was less susceptible to creative nonsense,
and people can download it for future reference, whereas ARM has
happily *deleted* specs from the website over time (try to find PSCI
0.1, for example...).
[...]
> >> +struct realm_params {
> >> + union { /* 0x0 */
> >> + struct {
> >> + u64 flags;
> >> + u64 s2sz;
> >> + u64 sve_vl;
> >> + u64 num_bps;
> >> + u64 num_wps;
> >> + u64 pmu_num_ctrs;
> >> + u64 hash_algo;
> >> + u64 num_aux_planes;
> >> + };
> >> + u8 padding0[0x400];
> >
> > SZ_1K? And similarly all over the shop?
>
> I'm a bit less sure that makes the code more readable - these structures
> are a bit of a pain because they are somewhat sparse. I've left a
> comment where the beginning of each union is, and personally I find it
> easier to see 0x0 + 0x400 == 0x400 rather than trying to work out what
> SZ_1K is in hex. This is particularly the case in terms of:
>
> > struct rec_params {
> > union { /* 0x0 */
> > u64 flags;
> > u8 padding0[0x100];
> > };
> > union { /* 0x100 */
> > u64 mpidr;
> > u8 padding1[0x100];
> > };
> > union { /* 0x200 */
> > u64 pc;
> > u8 padding2[0x100];
> > };
> > union { /* 0x300 */
> > u64 gprs[REC_CREATE_NR_GPRS];
> > u8 padding3[0xd00];
> > };
> > };
>
> Where 0xd00 doesn't even have a correspoding SZ_ define.
Indeed, but it is (SZ_4K - SZ_256 * 3). And a lot of these structures
seem to be designed to form a 4kB blob. I'm sure we can make use of
that information (BUILD_BUG_ON?).
>
> The RMM deals with this with macro magic:
>
> > struct rmi_rec_params {
> > /* Flags */
> > SET_MEMBER_RMI(unsigned long flags, 0, 0x100); /* Offset 0 */
> > /* MPIDR of the REC */
> > SET_MEMBER_RMI(unsigned long mpidr, 0x100, 0x200); /* 0x100 */
> > /* Program counter */
> > SET_MEMBER_RMI(unsigned long pc, 0x200, 0x300); /* 0x200 */
> > /* General-purpose registers */
> > SET_MEMBER_RMI(unsigned long gprs[REC_CREATE_NR_GPRS], 0x300, 0x1000); /* 0x300 */
> > };
>
> where the offsets are just directly encoded in the macro - but it's not
> an especially robust macro and I'm not convinced it's more readable.
I think this is just as horrible, but at least it seems to take the
boundaries of the structure into account.
>
> I'm happy to hear other suggestions on how to encode this neatly.
Honestly, I wouldn't mind having the structures described in a more
abstract way and then pre-processed to generate the include files. If
the architectural MRS wasn't so huge, I would have added it to the
kernel and used that directly for KVM.
>
> > I haven't checked the details of the encodings (life is too short),
> > but I wonder how much of this exists as an MRS and could be
> > automatically generated?
>
> Automatically generating this would be good - I'm not sure whether we
> have a (public) source available to generate from at the moment. I have
> tried to methodically work through the spec when updating this file, but
> as Gavin has already pointed out there was at least one mistake (in
> currently unused definitions) this time.
I'm slightly baffled that even the RMM is written this way. Given the
formalism used in the RMM spec, I was expecting that you'd have a
bunch of JSON at hand and able to generate any output from that. Doing
this stuff by hand is both incredibly dull work *and* extremely error
prone.
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply
* [PATCH v2 3/3] ASoC: sunxi: sun4i-spdif: Reorder clock enable sequence
From: phucduc.bui @ 2026-05-22 9:54 UTC (permalink / raw)
To: broonie
Cc: codekipper, jernej.skrabec, lgirdwood, linux-arm-kernel,
linux-kernel, linux-sound, linux-sunxi, nichen, perex, samuel,
tiwai, wens, bui duc phuc
In-Reply-To: <20260522095401.72915-1-phucduc.bui@gmail.com>
From: bui duc phuc <phucduc.bui@gmail.com>
Enable the APB bus clock before the SPDIF module clock
during runtime resume, as register accesses depend on the
bus clock being enabled first.
Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
---
sound/soc/sunxi/sun4i-spdif.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index f54eb14c9ed8..102db1a2afbb 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -643,15 +643,15 @@ static int sun4i_spdif_runtime_suspend(struct device *dev)
static int sun4i_spdif_runtime_resume(struct device *dev)
{
- struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
+ struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(host->spdif_clk);
+ ret = clk_prepare_enable(host->apb_clk);
if (ret)
return ret;
- ret = clk_prepare_enable(host->apb_clk);
+ ret = clk_prepare_enable(host->spdif_clk);
if (ret)
- clk_disable_unprepare(host->spdif_clk);
+ clk_disable_unprepare(host->apb_clk);
return ret;
}
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox