Files
omarchy-pkgs/pkgbuilds/linux-ptl/0028-drm-i915-psr-exit-Panel-Replay-for-ALPM-lag.patch
T

119 lines
4.7 KiB
Diff

From: Gaggery Tsai <gaggery.tsai@intel.com>
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 <gaggery.tsai@intel.com>
---
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);