From: Gaggery Tsai Date: Thu, 17 Apr 2026 10:30:00 -0700 Subject: [PATCH] drm/i915/psr: exit Panel Replay during updates to work around ALPM lag Panel Replay ALPM AUX-Less wake/sleep cycling cannot resume selective updates at frame rate on the LGD OLED panel in the Dell XPS 14 DA14260. The panel's internal timing engine appears to need a per-frame timing beacon to resume quickly after ALPM sleep; without it, the source commits at 120 Hz but visible frames land around 15 Hz. Work around by temporarily exiting Panel Replay in frontbuffer_flush() before each frame update. Re-entry into PR (with ALPM) is deferred by 50 ms via dc3co_work so that during continuous motion the link stays on (cursor/video smooth), and PR re-engages after 50 ms of no further updates (idle power savings preserved). The delay was chosen empirically: | Delay | Idle (10x avg) | Cursor motion (3x avg) | |--------|----------------|------------------------| | 50 ms | 3268 mW | 8749 mW | | 100 ms | 3436 mW | 9088 mW | | 200 ms | 3484 mW | 9104 mW | | stock | 3384 mW | ~15 Hz visible lag | | PSR2 | 4060 mW | 9191 mW | 50 ms gives the best idle and cursor-motion power while remaining subjectively identical in smoothness to longer delays. Repurpose the existing dc3co_work delayed_work as the re-entry timer: each frontbuffer_flush cancels+rearms it to 50 ms, and the work handler calls intel_psr_activate() when it fires. Signed-off-by: Gaggery Tsai --- diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c index 03c4978..0743142 100644 --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -97,6 +97,7 @@ static void frontbuffer_flush(struct intel_display *display, might_sleep(); intel_td_flush(display); intel_drrs_flush(display, frontbuffer_bits); + intel_psr_panel_replay_exit(display); intel_psr_flush(display, frontbuffer_bits, origin); intel_fbc_flush(display, frontbuffer_bits, origin); } diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index cbf8cd4..9d69056 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1200,6 +1200,8 @@ static void tgl_psr2_disable_dc3co(struct intel_dp *intel_dp) psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp)); } +static void intel_psr_activate(struct intel_dp *intel_dp); + static void tgl_dc3co_disable_work(struct work_struct *work) { struct intel_dp *intel_dp = @@ -1210,6 +1212,13 @@ static void tgl_dc3co_disable_work(struct work_struct *work) if (delayed_work_pending(&intel_dp->psr.dc3co_work)) goto unlock; + /* Panel Replay ALPM cursor-lag workaround re-activation path. */ + if (intel_dp->psr.panel_replay_enabled && + intel_dp->psr.enabled && !intel_dp->psr.active) { + intel_psr_activate(intel_dp); + goto unlock; + } + tgl_psr2_disable_dc3co(intel_dp); unlock: mutex_unlock(&intel_dp->psr.lock); @@ -4626,3 +4635,30 @@ bool intel_psr_use_trans_push(const struct intel_crtc_state *crtc_state) return HAS_PSR_TRANS_PUSH_FRAME_CHANGE(display) && crtc_state->has_panel_replay; } + +/* + * intel_psr_panel_replay_exit - exit Panel Replay during frontbuffer activity + * + * Exits PR on frontbuffer flush and arms dc3co_work with a 50 ms + * delay. Each subsequent flush cancels and rearms the timer. When it + * finally fires, tgl_dc3co_disable_work re-activates PR. + */ +void intel_psr_panel_replay_exit(struct intel_display *display) +{ + struct intel_encoder *encoder; + + for_each_intel_encoder_with_psr(display->drm, encoder) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + mutex_lock(&intel_dp->psr.lock); + if (intel_dp->psr.panel_replay_enabled && + intel_dp->psr.sel_update_enabled) { + if (intel_dp->psr.active) + intel_psr_exit(intel_dp); + mod_delayed_work(display->wq.unordered, + &intel_dp->psr.dc3co_work, + msecs_to_jiffies(50)); + } + mutex_unlock(&intel_dp->psr.lock); + } +} diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h index 394b641..485d880 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.h +++ b/drivers/gpu/drm/i915/display/intel_psr.h @@ -40,6 +40,7 @@ int intel_psr_debug_set(struct intel_dp *intel_dp, u64 value); void intel_psr_invalidate(struct intel_display *display, unsigned frontbuffer_bits, enum fb_op_origin origin); +void intel_psr_panel_replay_exit(struct intel_display *display); void intel_psr_flush(struct intel_display *display, unsigned frontbuffer_bits, enum fb_op_origin origin);