119 lines
4.7 KiB
Diff
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);
|