[PATCH v4 3/5] media: iris: Add ROI delta QP control support for HFI Gen2 encoders

From: Deepa Guthyappa Madivalara

Date: Tue Aug 11 2026 - 14:33:55 EST


Add ROI support to the Iris encoder driver for HFI Gen2 based targets.

- Add control infrastructure, ROI_PARAMS and MB_SIZE capability IDs to
platform_inst_fw_cap_type. Extend struct platform_inst_fw_cap with
p_array and elems fields to hold a pointer to the per-frame
delta QP array and its element count, set during s_ctrl.
- Register V4L2_CID_MPEG_VIDEO_ROI_MB_DELTA_QP as a dynamic S8
array control (max dims = 139264 entries, supporting resolution of
8192x4352 at 16x16 MB granularity).
Register V4L2_CID_MPEG_VIDEO_ROI_MB_SIZE as a read-only U8
control; g_volatile_ctrl returns 32 for HEVC and 16 for H.264.
- Define HFI_PROP_ROI_INFO and hook ROI_PARAMS to iris_set_roi_params().

Signed-off-by: Deepa Guthyappa Madivalara <deepa.madivalara@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/iris/iris_ctrls.c | 88 +++++++++++++++++++++-
drivers/media/platform/qcom/iris/iris_ctrls.h | 1 +
drivers/media/platform/qcom/iris/iris_hfi_gen2.c | 14 ++++
.../platform/qcom/iris/iris_hfi_gen2_defines.h | 1 +
.../platform/qcom/iris/iris_platform_common.h | 5 ++
5 files changed, 108 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index bf17d310eac081ffd3a4ad4842c2255ad798d4d8..de48b6e65d2ef6c7939455d28c08db88a8a0fda0 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -156,6 +156,10 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id)
return LAYER5_BITRATE_HEVC;
case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
return REQUEST_SYNC_FRAME;
+ case V4L2_CID_MPEG_VIDEO_ROI_MB_DELTA_QP:
+ return ROI_PARAMS;
+ case V4L2_CID_MPEG_VIDEO_ROI_MB_SIZE:
+ return MB_SIZE;
default:
return INST_FW_CAP_MAX;
}
@@ -301,6 +305,10 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR;
case REQUEST_SYNC_FRAME:
return V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
+ case ROI_PARAMS:
+ return V4L2_CID_MPEG_VIDEO_ROI_MB_DELTA_QP;
+ case MB_SIZE:
+ return V4L2_CID_MPEG_VIDEO_ROI_MB_SIZE;
default:
return 0;
}
@@ -327,6 +335,14 @@ static int iris_op_s_ctrl(struct v4l2_ctrl *ctrl)

inst->fw_caps[cap_id].value = ctrl->val;

+ if (inst->fw_caps[cap_id].flags & CAP_FLAG_CUSTOM) {
+ if (cap_id == ROI_PARAMS) {
+ inst->fw_caps[cap_id].p_array =
+ (const void *)ctrl->p_new.p;
+ inst->fw_caps[cap_id].elems = ctrl->new_elems;
+ }
+ }
+
if (vb2_is_streaming(q)) {
if (cap[cap_id].set)
cap[cap_id].set(inst, cap_id);
@@ -335,8 +351,52 @@ static int iris_op_s_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}

+static int iris_get_roi_mb_size(struct iris_inst *inst)
+{
+ return inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
+}
+
+static int iris_op_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct iris_inst *inst = container_of(ctrl->handler, struct iris_inst, ctrl_handler);
+ enum platform_inst_fw_cap_type cap_id;
+
+ cap_id = iris_get_cap_id(ctrl->id);
+ if (!iris_valid_cap_id(cap_id))
+ return -EINVAL;
+
+ if (cap_id == MB_SIZE)
+ ctrl->val = iris_get_roi_mb_size(inst);
+
+ return 0;
+}
+
static const struct v4l2_ctrl_ops iris_ctrl_ops = {
.s_ctrl = iris_op_s_ctrl,
+ .g_volatile_ctrl = iris_op_g_ctrl,
+};
+
+const struct v4l2_ctrl_config roi_mbqp_cfg = {
+ .ops = &iris_ctrl_ops,
+ .id = V4L2_CID_MPEG_VIDEO_ROI_MB_DELTA_QP,
+ .name = "Enc Mb ROI Delta QP",
+ .type = V4L2_CTRL_TYPE_S8,
+ .dims = {139264}, /* Max MBPF = 8192 * 4352 / 256 */
+ .min = -31,
+ .max = 30,
+ .def = 0,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config roi_mbqp_size = {
+ .ops = &iris_ctrl_ops,
+ .id = V4L2_CID_MPEG_VIDEO_ROI_MB_SIZE,
+ .name = "ROI Mb size",
+ .type = V4L2_CTRL_TYPE_U8,
+ .min = 16,
+ .max = 32,
+ .def = 16,
+ .step = 1,
};

int iris_ctrls_init(struct iris_inst *inst)
@@ -361,7 +421,7 @@ int iris_ctrls_init(struct iris_inst *inst)
return ret;

for (idx = 1; idx < INST_FW_CAP_MAX; idx++) {
- struct v4l2_ctrl *ctrl;
+ struct v4l2_ctrl *ctrl = NULL;

v4l2_id = iris_get_v4l2_id(cap[idx].cap_id);
if (!v4l2_id)
@@ -379,6 +439,13 @@ int iris_ctrls_init(struct iris_inst *inst)
cap[idx].max,
~(cap[idx].step_or_mask),
cap[idx].value);
+ } else if (cap[idx].flags & CAP_FLAG_CUSTOM) {
+ if (cap[idx].cap_id == ROI_PARAMS)
+ ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler,
+ &roi_mbqp_cfg, NULL);
+ if (cap[idx].cap_id == MB_SIZE)
+ ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler,
+ &roi_mbqp_size, NULL);
} else {
ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
&iris_ctrl_ops,
@@ -1540,3 +1607,22 @@ int iris_set_properties(struct iris_inst *inst, u32 plane)

return 0;
}
+
+int iris_set_roi_params(struct iris_inst *inst, u32 plane)
+{
+ struct iris_buffers *buffers = &inst->buffers[BUF_ROIMB_DELTAQP];
+ u32 metadata_header_bytes = 256;
+ u32 size = 0;
+ int ret = 0;
+
+ if (!inst->fw_caps[ROI_PARAMS].p_array)
+ return -EINVAL;
+
+ size = inst->fw_caps[ROI_PARAMS].elems * 2 + metadata_header_bytes;
+ buffers->size = ALIGN(size, 4096);
+ ret = iris_hfi_gen2_session_alloc_roi_metadata_buffer(inst);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h
index 5180d53d3c904cad460b2760913b475d0ff1bb55..4eb632c923e961de3679a8b049fa264d0ad88621 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.h
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.h
@@ -50,5 +50,6 @@ int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_typ
int iris_set_req_sync_frame(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_time_delta_based_rc(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_properties(struct iris_inst *inst, u32 plane);
+int iris_set_roi_params(struct iris_inst *inst, u32 plane);

#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
index 3b8fbefb8b93576962abd3850215f7b7fb364930..4d40d6733e5877a302ce63fb0b4a61b8f284e6dc 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
@@ -1782,6 +1782,20 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
.flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_req_sync_frame,
},
+ {
+ .cap_id = ROI_PARAMS,
+ .step_or_mask = 1,
+ .p_array = NULL,
+ .hfi_id = HFI_PROP_ROI_INFO,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_CUSTOM |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_roi_params,
+ },
+ {
+ .cap_id = MB_SIZE,
+ .step_or_mask = 1,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_CUSTOM,
+ },
};

static const u32 sm8550_vdec_input_config_params_default[] = {
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
index f43aea10090d8f1d6ae5c20547e5f6321b2ca203..08fc1036013de29e51747a6425ae7bb805ca3e0f 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -133,6 +133,7 @@ enum hfi_flip {
#define HFI_PROP_DEC_START_FROM_RAP_FRAME 0x03000169
#define HFI_PROP_NO_OUTPUT 0x0300016a
#define HFI_PROP_BUFFER_MARK 0x0300016c
+#define HFI_PROP_ROI_INFO 0x03000173
#define HFI_PROP_WORST_COMPRESSION_RATIO 0x03000174
#define HFI_PROP_WORST_COMPLEXITY_FACTOR 0x03000175
#define HFI_PROP_RAW_RESOLUTION 0x03000178
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index 9748095091461ba13443c63955a42906fa4f050c..9910eb5514b3ae0a6b5349adb071d0a56375102c 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -186,6 +186,8 @@ enum platform_inst_fw_cap_type {
LAYER5_BITRATE_HEVC,
REQUEST_SYNC_FRAME,
TIME_DELTA_BASED_RC,
+ ROI_PARAMS,
+ MB_SIZE,
INST_FW_CAP_MAX,
};

@@ -197,6 +199,7 @@ enum platform_inst_fw_cap_flags {
CAP_FLAG_CLIENT_SET = BIT(4),
CAP_FLAG_BITMASK = BIT(5),
CAP_FLAG_VOLATILE = BIT(6),
+ CAP_FLAG_CUSTOM = BIT(7),
};

struct platform_inst_fw_cap {
@@ -206,6 +209,8 @@ struct platform_inst_fw_cap {
s64 step_or_mask;
s64 value;
u32 hfi_id;
+ const void *p_array;
+ u32 elems;
enum platform_inst_fw_cap_flags flags;
int (*set)(struct iris_inst *inst,
enum platform_inst_fw_cap_type cap_id);

--
2.34.1