Hi,
this series reverts the "consolidate DCN vblank/flip handling onto
vupdate_no_lock" change (c87e6635d2db) and its two follow-ups, which cause
frequent, short bursts of visual corruption on a gfx1201 multi-monitor
setup. The known follow-up fix (70de0a021658, "Atomize IRQ register
read/modify/write ops", which closes #5616) does NOT cover this failure
mode - the corruption there is not accompanied by any flip_done timeouts,
and it remains with the fix applied.
Symptom: brief (~0.1s) bursts of white-noise strips on ALL connected
displays simultaneously, nearly constant within minutes of normal Wayland
desktop use, in a mixed-timing dual-monitor configuration
(3840x2160@144 DP + 1920x1080@60 HDMI). Kernel log is completely quiet at
the events. This is visually distinct from #5616's freezes/flip_done
timeouts - I believe it is an unreported second failure mode of the same
commit.
Evidence (module-level test builds against distro 7.0.0-38 headers,
vermagic- and CRC-identical to stock, only amdgpu.ko/ttm.ko swapped):
without the commit (older kernel state) .................. clean
(>=10 min)
with the commit .......................................... glitchy
(~1/6s)
commit + follow-ups reverted ............................. clean (hours)
commit kept, only follow-ups reverted .................... glitchy
commit kept + 70de0a021658 applied ....................... glitchy
Also ruled out: DCC / explicit modifiers (reproduces with
WLR_EGL_NO_MODIFIERS=1 and radeonsi R600_DEBUG=nodcc), DPM clock
transitions (mclk sits pinned at its top level in this configuration and
per-clock-domain transition statistics are identical in glitch and
glitch-free windows), firmware age (linux-firmware HEAD), VRR (off),
display link and engine (TTY console at identical modes is clean), and
hardware (Windows clean for weeks at identical timings).
Full report and raw data:
https://gitlab.freedesktop.org/drm/amd/-/issues/6004
I understand a blanket revert may undo intended vblank-timing improvements,
so if you prefer a targeted change (e.g. falling back to the legacy vblank
IRQ source for multi-CRTC / mixed-timing configurations), I can turn that
around quickly and test it: my build+reproducibility loop is minutes, and
the glitch is near-constant when present, so results are decisive within a
5-minute test session.
Thanks,
Ava H
From 4a85e560043fb74aa2fc501a786c220b6f006b92 Mon Sep 17 00:00:00 2001
From: Ava H <[email protected]>
Date: Sun, 18 Oct 2026 11:23:54 -0500
Subject: [PATCH 3/3] drm/amd/display: Revert "consolidate DCN vblank/flip
handling onto vupdate_no_lock"
This reverts commit c87e6635d2db (Ubuntu stable backport 9ae6be4a6).
Regression: on gfx1201 (RX 9070 XT, DCN 4.0.1) with two monitors at
different timings (3840x2160@144 DP + 1920x1080@60 HDMI) under Wayland
(sway/wlroots), this commit causes brief bursts (~0.1s) of white-noise
strips on ALL displays simultaneously, nearly constant within minutes of
normal desktop use. No kernel log output at the events (no flip_done
timeouts, resets, or page faults).
Evidence (module-level test builds, vermagic/CRC-identical, only
amdgpu.ko/ttm.ko swapped):
- kernel without this commit (7.0.0-34-era DRM code): clean (>=10 min)
- kernel with this commit (7.0.0-38): glitchy (~1 per 6s of desktop use)
- with commit reverted (plus the 2 follow-ups): clean (hours of soak)
- with commit kept, only follow-ups reverted: glitchy
- with commit kept + backport of 70de0a021658 (Atomize IRQ register RMW
ops, Fixes: c87e6635d2db, closes #5616): still glitchy -> the known fix
covers only the flip_done-timeout failure mode, not this corruption mode
Ruled out: DCC/explicit modifiers (reproduces with WLR_EGL_NO_MODIFIERS=1
and radeonsi R600_DEBUG=nodcc), DPM clock transitions (mclk pinned at top
level in this config; per-domain transition statistics identical in glitch
and glitch-free windows), firmware age, VRR (disabled), display link/engine
(TTY console at identical modes is clean), hardware (Windows clean at
identical timings).
Until the failure mode inside the consolidation is understood, reverting
restores correctness for multi-monitor/mixed-timing users. Happy to test
targeted fixes quickly (build+test turnaround is minutes).
Reported-by: Ava H <[email protected]>
Tested-by: Ava H <[email protected]>
Signed-off-by: Ava H <[email protected]>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 347 ++++++++----------
.../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 70 ++--
2 files changed, 189 insertions(+), 228 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index a747eefea..f65132d54 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -572,25 +572,89 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
queue_work(system_wq, &offload_work->work);
}
+static void dm_vupdate_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+ struct drm_device *drm_dev;
+ struct drm_vblank_crtc *vblank;
+ ktime_t frame_duration_ns, previous_timestamp;
+ unsigned long flags;
+ int vrr_active;
+
+ acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
+
+ if (acrtc) {
+ vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
+ drm_dev = acrtc->base.dev;
+ vblank = drm_crtc_vblank_crtc(&acrtc->base);
+ previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
+ frame_duration_ns = vblank->time - previous_timestamp;
+
+ if (frame_duration_ns > 0) {
+ trace_amdgpu_refresh_rate_track(acrtc->base.index,
+ frame_duration_ns,
+ ktime_divns(NSEC_PER_SEC, frame_duration_ns));
+ atomic64_set(&irq_params->previous_timestamp, vblank->time);
+ }
+
+ drm_dbg_vbl(drm_dev,
+ "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
+ vrr_active);
+
+ /* Core vblank handling is done here after end of front-porch in
+ * vrr mode, as vblank timestamping will give valid results
+ * while now done after front-porch. This will also deliver
+ * page-flip completion events that have been queued to us
+ * if a pageflip happened inside front-porch.
+ */
+ if (vrr_active && acrtc->dm_irq_params.stream) {
+ bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
+ bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
+ bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
+ == VRR_STATE_ACTIVE_VARIABLE;
+
+ amdgpu_dm_crtc_handle_vblank(acrtc);
+
+ /* BTR processing for pre-DCE12 ASICs */
+ if (adev->family < AMDGPU_FAMILY_AI) {
+ spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
+ mod_freesync_handle_v_update(
+ adev->dm.freesync_module,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params);
+
+ if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
+ schedule_dc_vmin_vmax(adev,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params.adjust);
+ }
+ spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+ }
+ }
+ }
+}
+
/**
- * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling
- * @adev: amdgpu device
- * @acrtc: the CRTC to service
- *
- * Performs writeback completion, vblank event handling, CRC processing, VRR BTR
- * updates and pageflip completion delivery.
+ * dm_crtc_high_irq() - Handles CRTC interrupt
+ * @interrupt_params: used for determining the CRTC instance
*
- * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from
- * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank
- * from dm_crtc_high_irq().
+ * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
+ * event handler.
*/
-static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
- struct amdgpu_crtc *acrtc)
+static void dm_crtc_high_irq(void *interrupt_params)
{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
struct drm_writeback_job *job;
+ struct amdgpu_crtc *acrtc;
unsigned long flags;
int vrr_active;
- bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0;
+
+ acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
+ if (!acrtc)
+ return;
if (acrtc->wb_conn) {
spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
@@ -627,17 +691,12 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
vrr_active, acrtc->dm_irq_params.active_planes);
/**
- * Core vblank handling.
- *
- * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch
- * point, which is the correct place to timestamp both VRR and non-VRR
- * vblanks.
- *
- * On DCE this handler runs at the start of front-porch, where only
- * non-VRR timestamping is valid; VRR vblank is deferred to
- * dm_vupdate_high_irq() after end of front-porch.
+ * Core vblank handling at start of front-porch is only possible
+ * in non-vrr mode, as only there vblank timestamping will give
+ * valid results while done in front-porch. Otherwise defer it
+ * to dm_vupdate_high_irq after end of front-porch.
*/
- if (is_dcn || !vrr_active)
+ if (!vrr_active)
amdgpu_dm_crtc_handle_vblank(acrtc);
/**
@@ -670,16 +729,18 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
}
/*
- * Deliver pageflip completion events (DCN only).
- *
- * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
- * point. Deliver any pending pageflip completion event from here.
+ * If there aren't any active_planes then DCH HUBP may be clock-gated.
+ * In that case, pageflip completion interrupts won't fire and pageflip
+ * completion events won't get delivered. Prevent this by sending
+ * pending pageflip events from here if a flip is still pending.
*
- * NOTE: This can deliver an event for a flip that was armed but not yet
- * programmed into HW; that race is closed in a follow-up change by
- * checking the programmed flip status.
+ * If any planes are enabled, use dm_pflip_high_irq() instead, to
+ * avoid race conditions between flip programming and completion,
+ * which could cause too early flip completion events.
*/
- if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) {
+ if (adev->family >= AMDGPU_FAMILY_RV &&
+ acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc->dm_irq_params.active_planes == 0) {
if (acrtc->event) {
drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
acrtc->event = NULL;
@@ -691,104 +752,6 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
}
-static void dm_vupdate_high_irq(void *interrupt_params)
-{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
- struct drm_device *drm_dev;
- struct drm_vblank_crtc *vblank;
- ktime_t frame_duration_ns, previous_timestamp;
- unsigned long flags;
- int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
- if (!acrtc)
- return;
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
- drm_dev = acrtc->base.dev;
- vblank = drm_crtc_vblank_crtc(&acrtc->base);
- previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
- frame_duration_ns = vblank->time - previous_timestamp;
-
- if (frame_duration_ns > 0) {
- trace_amdgpu_refresh_rate_track(acrtc->base.index,
- frame_duration_ns,
- ktime_divns(NSEC_PER_SEC, frame_duration_ns));
- atomic64_set(&irq_params->previous_timestamp, vblank->time);
- }
-
- drm_dbg_vbl(drm_dev,
- "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
- vrr_active);
-
- /*
- * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
- * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are
- * not used. Run the full handler here.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
- dm_crtc_high_irq_handler(adev, acrtc);
- return;
- }
-
- /* DCE only below. */
-
- /* Core vblank handling is done here after end of front-porch in
- * vrr mode, as vblank timestamping will give valid results
- * while now done after front-porch. This will also deliver
- * page-flip completion events that have been queued to us
- * if a pageflip happened inside front-porch.
- */
- if (vrr_active && acrtc->dm_irq_params.stream) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
- == VRR_STATE_ACTIVE_VARIABLE;
-
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (adev->family < AMDGPU_FAMILY_AI) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- mod_freesync_handle_v_update(
- adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
- }
-}
-
-/**
- * dm_crtc_high_irq() - Handles CRTC interrupt
- * @interrupt_params: used for determining the CRTC instance
- *
- * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler.
- *
- * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is
- * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq().
- */
-static void dm_crtc_high_irq(void *interrupt_params)
-{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
- if (!acrtc)
- return;
-
- dm_crtc_high_irq_handler(adev, acrtc);
-}
-
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/**
* dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
@@ -3133,13 +3096,6 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
*/
if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
-
- } else if (acrtc && state->stream_status[i].plane_count != 0) {
- /* DCN only needs to toggle VUPDATE_NO_LOCK */
- rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, enable);
- if (rc)
- drm_warn(adev_to_drm(adev), "Failed to %sable vupdate interrupt\n",
- enable ? "en" : "dis");
}
}
@@ -4741,6 +4697,38 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
* for acknowledging and handling.
*/
+ /* Use VSTARTUP interrupt */
+ for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
+ i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
+ i++) {
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq);
+
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, i, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
+ int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
+ drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_crtc_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
/* Use otg vertical line interrupt */
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
@@ -4812,6 +4800,37 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
return -ENOMEM;
}
+ /* Use GRPH_PFLIP interrupt */
+ for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
+ i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1;
+ i++) {
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, i, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
+ int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
+ drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_pflip_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
/* HPD */
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
&adev->hpd_irq);
@@ -9381,22 +9400,14 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
drm_crtc_vblank_on_config(&acrtc->base,
&config);
- /*
- * Since pflip_high_irq is no longer registered for DCN, grab an
- * extra reference to vupdate irq instead to workaround this
- * issue:
- * https://gitlab.freedesktop.org/drm/amd/-/work_items/3936
- *
- * The callbacks to drm_vblank_on/off should really take care of
- * this though.
- */
+ /* Allow RX6xxx, RX7700, RX7800 GPUs to call amdgpu_irq_get.*/
switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
case IP_VERSION(3, 0, 0):
case IP_VERSION(3, 0, 2):
case IP_VERSION(3, 0, 3):
case IP_VERSION(3, 2, 0):
- if (amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type))
- drm_err(dev, "DM_IRQ: Cannot get vupdate irq!\n");
+ if (amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type))
+ drm_err(dev, "DM_IRQ: Cannot get pageflip irq!\n");
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
if (amdgpu_irq_get(adev, &adev->vline0_irq, irq_type))
drm_err(dev, "DM_IRQ: Cannot get vline0 irq!\n");
@@ -9414,8 +9425,8 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
if (amdgpu_irq_put(adev, &adev->vline0_irq, irq_type))
drm_err(dev, "DM_IRQ: Cannot put vline0 irq!\n");
#endif
- if (amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type))
- drm_err(dev, "DM_IRQ: Cannot put vupdate irq!\n");
+ if (amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type))
+ drm_err(dev, "DM_IRQ: Cannot put pageflip irq!\n");
}
drm_crtc_vblank_off(&acrtc->base);
@@ -9428,10 +9439,6 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
int irq_type =
amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
- /* GRPH_PFLIP is not used on DCN; nothing to reapply. */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0)
- return;
-
/**
* This reads the current state for the IRQ and force reapplies
* the setting to hardware.
@@ -9763,13 +9770,9 @@ static void update_stream_irq_parameters(
static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
- struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev);
bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
- /* Only DCE gates vupdate on VRR, keep it enabled for DCN */
- bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
-
if (!old_vrr_active && new_vrr_active) {
/* Transition VRR inactive -> active:
* While VRR is active, we must not disable vblank irq, as a
@@ -9779,8 +9782,7 @@ static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
* We also need vupdate irq for the actual core vblank handling
* at end of vblank.
*/
- if (vrr_gates_vupdate)
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
+ WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
__func__, new_state->base.crtc->base.id);
@@ -9788,8 +9790,7 @@ static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
/* Transition VRR active -> inactive:
* Allow vblank irq disable again for fixed refresh rate.
*/
- if (vrr_gates_vupdate)
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
+ WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
drm_crtc_vblank_put(new_state->base.crtc);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
__func__, new_state->base.crtc->base.id);
@@ -9964,7 +9965,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
bool cursor_update = false;
bool pflip_present = false;
- bool immediate_flip = false;
bool dirty_rects_changed = false;
bool updated_planes_and_streams = false;
struct {
@@ -10130,8 +10130,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
acrtc_state->update_type == UPDATE_TYPE_FAST &&
get_mem_type(old_plane_state->fb) == get_mem_type(fb);
- immediate_flip |= bundle->flip_addrs[planes_count].flip_immediate;
-
timestamp_ns = ktime_get_ns();
bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count];
@@ -10333,29 +10331,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
amdgpu_dm_commit_cursors(state);
- /*
- * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK.
- * However, an immediate (tearing / async) flip is latched by HW right
- * away and does not wait for the next vupdate, so deliver its
- * completion event here after programming.
- *
- * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so
- * this is DCN-only.
- */
- if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc_attach->event) {
- drm_crtc_accurate_vblank_count(&acrtc_attach->base);
- drm_crtc_send_vblank_event(&acrtc_attach->base,
- acrtc_attach->event);
- acrtc_attach->event = NULL;
- drm_crtc_vblank_put(&acrtc_attach->base);
- acrtc_attach->pflip_status = AMDGPU_FLIP_NONE;
- }
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
cleanup:
kfree(bundle);
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index ad4b724f8..7cfaf225f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -309,14 +309,7 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
drm_crtc_vblank_restore(crtc);
}
- /*
- * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
- * vblank and pageflip completion events, so enable it whenever vblank
- * is enabled. On DCE, vupdate is only needed in VRR mode.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
- rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable);
- } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
+ if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
if (enable) {
/* vblank irq on -> Only need vupdate irq in vrr mode */
if (amdgpu_dm_crtc_vrr_active(acrtc_state))
@@ -330,44 +323,37 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
if (rc)
return rc;
- /*
- * VLINE0 (crtc_irq) and GRPH_PFLIP (pageflip_irq) are only used on
- * DCE. On DCN, vblank and pageflip completion are delivered from
- * VUPDATE_NO_LOCK (enabled above), so don't touch them here.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
- /* crtc vblank or vstartup interrupt */
- if (enable) {
- rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
- } else {
- rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
- }
-
- if (rc)
- return rc;
+ /* crtc vblank or vstartup interrupt */
+ if (enable) {
+ rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
+ }
- /*
- * hubp surface flip interrupt
- *
- * We have no guarantee that the frontend index maps to the same
- * backend index - some even map to more than one.
- *
- * TODO: Use a different interrupt or check DC itself for the mapping.
- */
- if (enable) {
- rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
- } else {
- rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
- }
+ if (rc)
+ return rc;
- if (rc)
- return rc;
+ /*
+ * hubp surface flip interrupt
+ *
+ * We have no guarantee that the frontend index maps to the same
+ * backend index - some even map to more than one.
+ *
+ * TODO: Use a different interrupt or check DC itself for the mapping.
+ */
+ if (enable) {
+ rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
}
+ if (rc)
+ return rc;
+
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/* crtc vline0 interrupt, only available on DCN+ */
if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
--
2.53.0
From d7c6afdfb38772a8e7b8c3f5b5171f6c6cfcbcff Mon Sep 17 00:00:00 2001
From: Ava H <[email protected]>
Date: Sun, 18 Oct 2026 11:23:54 -0500
Subject: [PATCH 2/3] drm/amd/display: revert "Fix flip-done timeouts on mode1
reset"
This reverts commit 172795a98, part of the vupdate_no_lock vblank handling
series. See the final commit of the series for the regression evidence.
Signed-off-by: Ava H <[email protected]>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index ec7939cb0..ad4b724f8 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -315,19 +315,10 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
* is enabled. On DCE, vupdate is only needed in VRR mode.
*/
if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
- if (enable) {
- rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc);
- } else {
- rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc);
- }
+ rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable);
} else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
if (enable) {
- /* vblank irq on -> Only need vupdate irq in vrr mode
- * Not ref-counted since we need explicit enable/disable
- * for DCE VRR handling
- */
+ /* vblank irq on -> Only need vupdate irq in vrr mode */
if (amdgpu_dm_crtc_vrr_active(acrtc_state))
rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true);
} else {
--
2.53.0
From 8f5e1439b1e1b93dd9398d59b2f2d20a3ff868e1 Mon Sep 17 00:00:00 2001
From: Ava H <[email protected]>
Date: Sun, 18 Oct 2026 11:23:53 -0500
Subject: [PATCH 1/3] drm/amd/display: revert "Fix missing DCE check in
dm_gpureset_toggle_interrupts()"
This reverts commit 06586d3bf, part of the vupdate_no_lock vblank handling
series. Reverted as a unit with the consolidation it follows up on.
Reason: visual corruption regression, see the final commit of the series.
Signed-off-by: Ava H <[email protected]>
---
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 69a14bcf6..a747eefea 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -3105,8 +3105,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
acrtc = get_crtc_by_otg_inst(
adev, state->stream_status[i].primary_otg_inst);
- if (acrtc && state->stream_status[i].plane_count != 0 &&
- amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
+ if (acrtc && state->stream_status[i].plane_count != 0) {
irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
if (rc)
--
2.53.0