[PATCH 3/9] media: iris: add support for interlaced decoded content
From: Dmitry Baryshkov
Date: Thu Jul 09 2026 - 15:38:07 EST
The Gen1 firmware can report that a decoded stream is interlaced through
the progressive_only field of HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT. So far
the driver treated any interlaced content as unsupported and moved the
instance to the error state, rejecting otherwise decodable streams.
Stop rejecting interlaced content. Track the interlaced state on the
instance, report V4L2_FIELD_INTERLACED on the capture queue when the
content is interlaced, and skip allocation of the bin (scratch) buffer
in that case, matching the venus driver behaviour. Only genuinely
unsupported content (non-8-bit depth) is still rejected.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/iris/iris_ctrls.c | 8 +++++++-
drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c | 6 +++++-
drivers/media/platform/qcom/iris/iris_instance.h | 2 ++
drivers/media/platform/qcom/iris/iris_vb2.c | 2 --
drivers/media/platform/qcom/iris/iris_vdec.c | 2 ++
drivers/media/platform/qcom/iris/iris_vpu_buffer.c | 7 +++++++
6 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index 33a34573391a..c12fe724dfb8 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -517,7 +517,13 @@ int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id
u32 work_mode = STAGE_2;
if (inst->domain == DECODER) {
- if (iris_res_is_less_than(width, height, 1280, 720))
+ /*
+ * The two-stage pipeline cannot decode interlaced or MPEG2
+ * content, so fall back to a single stage. Low resolution
+ * content also uses a single stage.
+ */
+ if (inst->is_interlaced ||
+ iris_res_is_less_than(width, height, 1280, 720))
work_mode = STAGE_1;
}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
index 7ad6f0bb4677..cf2cf31d5b61 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
@@ -103,11 +103,15 @@ static void iris_hfi_gen1_read_changed_params(struct iris_inst *inst,
num_properties_changed--;
} while (num_properties_changed > 0);
+ /* pic_struct carries progressive_only: 0 means the content is interlaced */
+ inst->is_interlaced = !event.pic_struct;
+
pixmp_ip->width = event.width;
pixmp_ip->height = event.height;
pixmp_op->width = ALIGN(event.width, 128);
pixmp_op->height = ALIGN(event.height, 32);
+ pixmp_op->field = inst->is_interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
pixmp_op->plane_fmt[0].bytesperline = ALIGN(event.width, 128);
pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
@@ -165,7 +169,7 @@ static void iris_hfi_gen1_read_changed_params(struct iris_inst *inst,
dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
dst_q->min_reqbufs_allocation = inst->buffers[BUF_OUTPUT].min_count;
- if (event.bit_depth || !event.pic_struct) {
+ if (event.bit_depth) {
dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n",
event.bit_depth, event.pic_struct);
iris_inst_change_state(inst, IRIS_INST_ERROR);
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
index ffdbbd20901a..67466b4d0896 100644
--- a/drivers/media/platform/qcom/iris/iris_instance.h
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -53,6 +53,7 @@ struct iris_hfi_session_ops;
* @metadata_idx: index for metadata buffer
* @codec: codec type
* @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver
+ * @is_interlaced: a flag to indicate that decoded content is interlaced
* @last_buf_ns: start time of received input buffer for current one second FPS window
* @frame_counter: input buffer counter for current one second FPS window
* @frame_rate: frame rate of current instance
@@ -100,6 +101,7 @@ struct iris_inst {
u32 metadata_idx;
u32 codec;
bool last_buffer_dequeued;
+ bool is_interlaced;
u64 last_buf_ns;
u32 frame_counter;
u32 frame_rate;
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
index a2ea2d67f60d..28dc269dcf0b 100644
--- a/drivers/media/platform/qcom/iris/iris_vb2.c
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -260,8 +260,6 @@ int iris_vb2_buf_prepare(struct vb2_buffer *vb)
if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
if (vbuf->field == V4L2_FIELD_ANY)
vbuf->field = V4L2_FIELD_NONE;
- if (vbuf->field != V4L2_FIELD_NONE)
- return -EINVAL;
}
if (!(inst->sub_state & IRIS_INST_SUB_DRC)) {
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
index 9fee5f28097d..9169b1335b5c 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.c
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -251,6 +251,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
fmt = inst->fmt_src;
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
+ fmt->fmt.pix_mp.field = f->fmt.pix_mp.field;
inst->codec = fmt->fmt.pix_mp.pixelformat;
codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align);
@@ -290,6 +291,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
fmt = inst->fmt_dst;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
+ fmt->fmt.pix_mp.field = f->fmt.pix_mp.field;
fmt->fmt.pix_mp.num_planes = 1;
switch (f->fmt.pix_mp.pixelformat) {
case V4L2_PIX_FMT_P010:
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
index 00dbb091c54e..88f130d09a6e 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -937,6 +937,13 @@ static u32 iris_vpu_dec_bin_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
+ /*
+ * The bin (scratch) buffer is not used when decoding interlaced
+ * content.
+ */
+ if (inst->is_interlaced)
+ return 0;
+
if (inst->codec == V4L2_PIX_FMT_H264)
return hfi_buffer_bin_h264d(width, height, num_vpp_pipes);
else if (inst->codec == V4L2_PIX_FMT_HEVC)
--
2.47.3