From: sashiko-bot@kernel.org
To: "Alan Stead" <alan.stead@intel.com>,
intel-gfx@lists.freedesktop.org, intel-xe@lists.freedesktop.org
Subject: Re: [RFC v1 2/3] drm/i915/dip: Send the HDMI 2.1 VTEM when VRR is enabled
Date: Fri, 30 Oct 2026 20:59:23 +0000 [thread overview]
Message-ID: <sashiko-outbox-191505@kernel.org> (raw)
In-Reply-To: <20261030203314.1085262-3-alan.stead@intel.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm/i915/dip: undeclared HDMI_EMP_DATA in intel_dip_write_hdmi_vtem()
- [Medium] drm/i915/dip: misordered and unsent VRR disable VTEM in intel_vrr_disable()
- [Medium] drm/i915/dip: unpopulated vrr.in_range check in intel_dip_write_hdmi_vtem()
--
commit a0eb4dbdb4fb231ddf6c7b2a3526d3d1a7f9707a
Author: Alan Stead <alan.stead@intel.com>
drm/i915/dip: Send the HDMI 2.1 VTEM when VRR is enabled
This commit adds intel_dip_write_hdmi_vtem() to program an HDMI 2.1 Video
Timing Extended Metadata (VTEM) packet when enabling or disabling VRR on
an HDMI transcoder via intel_vrr_enable() and intel_vrr_disable().
> diff --git a/drivers/gpu/drm/i915/display/intel_dip.c b/drivers/gpu/drm/i915/display/intel_dip.c
> index 831881e6c15ec..4748f6012337b 100644
> --- a/drivers/gpu/drm/i915/display/intel_dip.c
> +++ b/drivers/gpu/drm/i915/display/intel_dip.c
[ ... ]
> @@ -74,6 +77,78 @@ void intel_dip_write_emp_as_sdp_tl(const struct intel_crtc_state *crtc_state)
> EMP_AS_SDP_DB_TL(crtc_state->dip.emp_as_sdp_tl));
> }
>
> +/* VTEM: 7 byte EM Data Set header (PB0-PB6) + 4 byte data set (MD0-MD3) */
> +#define VTEM_DATA_SET_LENGTH 4
> +#define VTEM_EMP_SIZE (7 + VTEM_DATA_SET_LENGTH)
> +/* Payload bytes carried by one EMP data island packet */
> +#define HDMI_EMP_PACKET_BODY 28
> +
> +/*
> + * Tell an HDMI 2.1 sink whether VRR is active with a Class 0 (Gaming-VRR)
> + * Video Timing Extended Metadata (VTEM) packet. It is a periodic
> + * pseudo-static EM Data Set: the hardware sends it every frame until it is
> + * reprogrammed. On disable, a last VTEM with VRR_EN=0 and END set is sent,
> + * after which the hardware stops sending it.
> + */
> +void intel_dip_write_hdmi_vtem(const struct intel_crtc_state *crtc_state,
> + bool enable)
> +{
> + struct intel_display *display = to_intel_display(crtc_state);
> + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
> + const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode;
> + u8 vic = crtc_state->infoframes.avi.avi.video_code;
> + int base_rr = drm_mode_vrefresh(mode);
> + u8 pb[HDMI_EMP_PACKET_BODY] = {};
> + int i;
> +
> + if (DISPLAY_VER(display) < 20 ||
> + !intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) ||
> + !crtc_state->vrr.in_range)
> + return;
[Severity: Medium]
When intel_vrr_disable() calls intel_dip_write_hdmi_vtem(old_crtc_state,
false) to disable VRR from an inherited hardware state read out at driver
load, will this !crtc_state->vrr.in_range check cause an early return and
skip sending the terminating VTEM packet (VRR_EN=0 and END=1)?
During hardware state readout in intel_modeset_readout_hw_state() ->
intel_vrr_get_config(), old_crtc_state->vrr.enable is populated from
TRANS_VRR_CTL, but old_crtc_state->vrr.in_range is never set:
drivers/gpu/drm/i915/display/intel_vrr.c:intel_vrr_get_config() {
...
if (intel_vrr_always_use_vrr_tg(display))
crtc_state->vrr.enable = vrr_enable && !intel_vrr_is_fixed_rr(crtc_state);
else
crtc_state->vrr.enable = vrr_enable;
...
}
Since both callers of intel_dip_write_hdmi_vtem() — intel_vrr_enable() and
intel_vrr_disable() — already guard on crtc_state->vrr.enable (and
intel_vrr_compute_config() only sets vrr.enable when vrr.in_range is true),
is the !crtc_state->vrr.in_range check in intel_dip_write_hdmi_vtem()
needed?
> +
> + /* PB0-PB6: EM Data Set header (Sync = 0, DS_Type = pseudo-static) */
> + pb[0] = BIT(7) | /* NEW */
> + BIT(2) | /* VFR */
> + (enable ? 0 : BIT(6)); /* END */
> + pb[2] = 1; /* Organization_ID: HDMI */
> + pb[4] = 1; /* Data_Set_Tag: VTEM */
> + pb[6] = VTEM_DATA_SET_LENGTH; /* Data_Set_Length */
> +
> + /* PB7-PB10: MD0-MD3 (M_CONST = 0, FVA_Factor_M1 = 0, MD2 bit 2 reserved) */
> + if (enable) {
> + pb[7] = BIT(0); /* VRR_EN */
> + /* Base_Vfront and Base_Refresh_Rate are only sent without a VIC */
> + if (!vic) {
> + pb[8] = min(mode->crtc_vsync_start - mode->crtc_vdisplay, 255);
> + pb[9] = (base_rr >> 8) & 0x3;
> + pb[10] = base_rr & 0xff;
> + }
> + }
> +
> + drm_dbg_kms(display->drm,
> + "[TRANSCODER %s] VTEM: VRR_EN=%d VIC=%u Base_Vfront=%u Base_RR=%u\n",
> + transcoder_name(cpu_transcoder), enable, vic, pb[8],
> + pb[9] << 8 | pb[10]);
> +
> + /*
> + * HDMI_EMP_CTL first, then the payload dwords (PB0 in bits 7:0), then
> + * the header, whose write clock crosses the whole packet. HB0 must be
> + * set to the EMP packet type, the hardware doesn't fill it in.
> + */
> + intel_de_write(display, HDMI_EMP_CTL(display, cpu_transcoder),
> + HDMI_EMP_CTL_TYPE_VTEM);
> + for (i = 0; i < ARRAY_SIZE(pb); i += 4)
> + intel_de_write(display, HDMI_EMP_DATA(display, cpu_transcoder),
> + pb[i] | pb[i + 1] << 8 | pb[i + 2] << 16 |
> + pb[i + 3] << 24);
[Severity: High]
Does drivers/gpu/drm/i915/display/intel_dip.c fail to compile here because
HDMI_EMP_DATA is not defined?
In drivers/gpu/drm/i915/display/intel_dip_regs.h, the patch defines
HDMI_EMP_CTL and HDMI_EMP_HEADER with a comment noting that the payload is
HDMI_EMP_DATA, but neither intel_dip_regs.h nor intel_display_regs.h (nor
follow-up commit d2e831520b52) defines HDMI_EMP_DATA or _HDMI_EMP_DATA_A,
causing an implicit declaration build failure when CONFIG_DRM_I915 or
CONFIG_DRM_XE_DISPLAY is enabled.
In addition, the loop writes all 7 payload dwords to HDMI_EMP_DATA(display,
cpu_transcoder) without a dword index, whereas the transcoder Video DIP
Adaptive Sync / EMP packet buffer uses indexed data registers
ADL_TVIDEO_DIP_AS_SDP_DATA(display, trans, i) (0x60484 + i * 4) controlled
via VIDEO_DIP_ENABLE_AS_ADL in HSW_TVIDEO_DIP_CTL (0x60200). Should this
be programming the indexed Video DIP AS SDP / EMP buffer instead of
HDMI_EMP_CTL, HDMI_EMP_DATA, and HDMI_EMP_HEADER?
[ ... ]
> diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c
> index d43a06907d18a..142ba2db52fda 100644
> --- a/drivers/gpu/drm/i915/display/intel_vrr.c
> +++ b/drivers/gpu/drm/i915/display/intel_vrr.c
> @@ -1010,6 +1010,8 @@ void intel_vrr_enable(const struct intel_crtc_state *crtc_state)
>
> if (!intel_vrr_always_use_vrr_tg(display))
> intel_vrr_tg_enable(crtc_state);
> +
> + intel_dip_write_hdmi_vtem(crtc_state, true);
> }
>
> void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state)
> @@ -1024,6 +1026,8 @@ void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state)
>
> intel_vrr_disable_dc_balancing(old_crtc_state);
> intel_vrr_set_fixed_rr_timings(old_crtc_state, old_crtc_state->cpu_transcoder);
> +
> + intel_dip_write_hdmi_vtem(old_crtc_state, false);
> }
[Severity: Medium]
Is placing intel_dip_write_hdmi_vtem(old_crtc_state, false) at the end of
intel_vrr_disable() ordered and guarded as intended across the disable and
fastset paths?
1. On platforms where !intel_vrr_always_use_vrr_tg(display) (such as
display version 20), intel_vrr_tg_disable() clears VRR_CTL_VRR_ENABLE in
TRANS_VRR_CTL and waits for VRR_STATUS_VRR_EN_LIVE to clear at the next
vblank before intel_dip_write_hdmi_vtem(old_crtc_state, false) is called:
drivers/gpu/drm/i915/display/intel_vrr.c:intel_vrr_tg_disable() {
...
if (intel_de_wait_for_clear_ms(display,
TRANS_VRR_STATUS(display, cpu_transcoder),
VRR_STATUS_VRR_EN_LIVE, 1000))
drm_err(display->drm, "Timed out waiting for VRR live status to clear\n");
...
}
Because the VRR_EN=0, END=1 VTEM is only written after that vblank wait
completes, the vblank in which the VRR timing generator turns off will
still transmit the old VTEM with VRR_EN=1 and END=0.
2. On a full modeset in intel_commit_modeset_disables(),
intel_pre_plane_update() calls intel_vrr_disable(old_crtc_state) and then
intel_old_crtc_state_disables() -> hsw_crtc_disable() ->
intel_ddi_post_disable_hdmi_or_sst() -> intel_disable_transcoder() clears
TRANSCONF_ENABLE without waiting for another vblank after
intel_dip_write_hdmi_vtem(old_crtc_state, false), so the final VTEM with
VRR_EN=0 and END=1 is never transmitted before the transcoder stops.
3. In drivers/gpu/drm/i915/display/intel_display.c,
intel_crtc_vrr_disabling() also returns true during seamless fastsets where
VRR stays enabled (old_crtc_state->vrr.enable && new_crtc_state->vrr.enable)
if update_m_n, update_lrr, or vrr_params_changed() is set:
drivers/gpu/drm/i915/display/intel_display.c:intel_crtc_vrr_disabling() {
return is_disabling(vrr.enable, old_crtc_state, new_crtc_state) ||
(old_crtc_state->vrr.enable &&
(new_crtc_state->update_m_n || new_crtc_state->update_lrr ||
vrr_params_changed(old_crtc_state, new_crtc_state)));
}
In that case, intel_pre_update_crtc() -> intel_pre_plane_update() ->
intel_vrr_disable() programs a VTEM with VRR_EN=0 and END=1 before vblank
evasion even though VRR remains enabled in new_crtc_state. Should
intel_dip_write_hdmi_vtem(old_crtc_state, false) be programmed before the
vblank wait and avoided when VRR is only being reconfigured across a
fastset?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030203314.1085262-1-alan.stead@intel.com?part=2
next prev parent reply other threads:[~2026-10-30 20:59 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 20:32 [RFC v1 0/3] drm/i915/vrr: HDMI 2.1 VRR support Alan Stead
2026-10-30 20:32 ` [RFC v1 1/3] drm/i915/dip: Send HDMI EMPs early in V. Blank when VRR is in range Alan Stead
2026-10-30 20:46 ` sashiko-bot
2026-10-30 20:32 ` [RFC v1 2/3] drm/i915/dip: Send the HDMI 2.1 VTEM when VRR is enabled Alan Stead
2026-10-30 20:59 ` sashiko-bot [this message]
2026-10-30 20:32 ` [RFC v1 3/3] drm/i915/vrr: Allow VRR on HDMI 2.1 sinks Alan Stead
2026-10-30 20:58 ` sashiko-bot
2026-10-30 21:29 ` ✓ i915.CI.BAT: success for drm/i915/vrr: HDMI 2.1 VRR support Patchwork
2026-10-31 7:21 ` ✗ i915.CI.Full: failure " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-191505@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=alan.stead@intel.com \
--cc=intel-gfx@lists.freedesktop.org \
--cc=intel-xe@lists.freedesktop.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox