iter25 α-25: inject synthetic SPS before cap_pool_init to seed image_fmt

Root cause for Bug 5 (HEVC libva = all-zero CAPTURE) and Bug 4 (H.264
libva = keyframe partial), localized via iter17→iter24 kernel-printk
chain:

  rkvdec_s_ctrl() for HEVC_SPS / H264_SPS calls get_image_fmt() and,
  if the resolved image_fmt differs from cached ctx->image_fmt (default
  RKVDEC_IMG_FMT_ANY at open), tries to reset the CAPTURE format.
  Format reset returns -EBUSY when vb2_is_busy(CAPTURE_queue) — any
  CAPTURE buffer allocated blocks the change.

  libva (iter5b-β) pre-allocates 24 CAPTURE buffers at CreateContext
  via cap_pool_init, BEFORE any per-frame S_EXT_CTRLS. First per-frame
  HEVC_SPS therefore fails with -EBUSY in try_or_set_cluster, breaks
  v4l2_ctrl_request_setup's outer loop, leaves all 5 staged HEVC
  compound controls at zero in ctx->ctrl_hdl. rkvdec_hevc_run reads
  zero (iter20 dmesg: sps[0..16]=00..00), hardware sees w=0 h=0,
  CAPTURE comes out all-zero (Bug 5).

Fix: BEFORE cap_pool_init, inject one S_EXT_CTRLS (no request, no
which) with a synthetic SPS containing the profile's known chroma +
bit_depth. CAPTURE queue is still empty at this point → vb2_is_busy
returns false → rkvdec_s_ctrl succeeds, ctx->image_fmt is updated to
the profile's image_fmt. From then on, per-frame SPS submissions with
matching chroma + bit_depth see image_fmt_changed=false → skip reset
→ commit succeeds.

VP9 / MPEG-2 / VP8 paths are not affected: VP9's rkvdec coded_fmt_desc
has no get_image_fmt op; MPEG-2 + VP8 route to hantro.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 10:00:08 +00:00
parent e109306fd4
commit db0b7f9892
+88
View File
@@ -195,6 +195,94 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id,
goto error;
}
/*
* iter25 α-25: synthetic-SPS injection to pre-seed ctx->image_fmt
* before CAPTURE buffer allocation.
*
* Root cause (iter17→iter24 kernel-printk chain): rkvdec_s_ctrl for
* HEVC_SPS / H264_SPS calls get_image_fmt() and, if the resolved
* image_fmt differs from the cached ctx->image_fmt (default
* RKVDEC_IMG_FMT_ANY), tries to reset the CAPTURE format. The reset
* returns -EBUSY when vb2_is_busy(CAPTURE_queue) — i.e. any CAPTURE
* buffer is allocated.
*
* libva (iter5b-β CAPTURE pool) pre-allocates 24 CAPTURE buffers
* via cap_pool_init below — before any per-frame S_EXT_CTRLS
* arrives. So the first real HEVC_SPS at decode time fails with
* -EBUSY in try_or_set_cluster, breaks v4l2_ctrl_request_setup's
* outer loop, and leaves ctx->ctrl_hdl[SPS..DECODE_PARAMS] at all-
* zero contents. rkvdec_hevc_run reads zero, hardware sees w=0
* h=0, decoded CAPTURE is all-zero (Bug 5 + Bug 4).
*
* Fix: while CAPTURE is still empty (before cap_pool_init), inject
* a synthetic SPS containing the profile's chroma + bit_depth so
* rkvdec_s_ctrl resolves image_fmt and updates ctx->image_fmt
* before vb2_is_busy can return true. From then on, per-frame
* SPS submissions with matching profile parameters see
* image_fmt_changed=false → skip reset → commit succeeds.
*
* Gated by config->profile: only HEVC and H.264 paths set
* get_image_fmt in their rkvdec coded_fmt_desc->ops; VP9 / MPEG-2 /
* VP8 are unaffected (rkvdec_s_ctrl returns 0 immediately when
* get_image_fmt is NULL, or those codecs are routed to hantro).
*
* Failure is best-effort: if the kernel returns -EBUSY/-EINVAL here
* (e.g. driver doesn't expose the control on this DT path), we fall
* through and may still hit the original bug for that codec — but
* the device-init DECODE_MODE + START_CODE block below ALSO uses
* void-cast best-effort, so this is consistent with prior pattern.
*/
{
switch (config_object->profile) {
case VAProfileHEVCMain: {
struct v4l2_ctrl_hevc_sps dummy_sps;
struct v4l2_ext_control dummy_ctrl;
memset(&dummy_sps, 0, sizeof(dummy_sps));
dummy_sps.chroma_format_idc = 1; /* 4:2:0 */
dummy_sps.bit_depth_luma_minus8 = 0; /* 8-bit */
dummy_sps.bit_depth_chroma_minus8 = 0;
dummy_sps.pic_width_in_luma_samples = picture_width;
dummy_sps.pic_height_in_luma_samples = picture_height;
dummy_ctrl.id = V4L2_CID_STATELESS_HEVC_SPS;
dummy_ctrl.ptr = &dummy_sps;
dummy_ctrl.size = sizeof(dummy_sps);
(void)v4l2_set_controls(driver_data->video_fd, -1,
&dummy_ctrl, 1);
break;
}
case VAProfileH264Main:
case VAProfileH264High:
case VAProfileH264ConstrainedBaseline:
case VAProfileH264MultiviewHigh:
case VAProfileH264StereoHigh: {
struct v4l2_ctrl_h264_sps dummy_sps;
struct v4l2_ext_control dummy_ctrl;
memset(&dummy_sps, 0, sizeof(dummy_sps));
dummy_sps.chroma_format_idc = 1; /* 4:2:0 */
dummy_sps.bit_depth_luma_minus8 = 0;
dummy_sps.bit_depth_chroma_minus8 = 0;
dummy_sps.pic_width_in_mbs_minus1 =
(picture_width + 15) / 16 - 1;
dummy_sps.pic_height_in_map_units_minus1 =
(picture_height + 15) / 16 - 1;
dummy_sps.profile_idc = 100; /* High */
dummy_sps.level_idc = 41;
dummy_ctrl.id = V4L2_CID_STATELESS_H264_SPS;
dummy_ctrl.ptr = &dummy_sps;
dummy_ctrl.size = sizeof(dummy_sps);
(void)v4l2_set_controls(driver_data->video_fd, -1,
&dummy_ctrl, 1);
break;
}
default:
break;
}
}
destination_planes_count = video_format->planes_count;
/*