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

220 lines
8.0 KiB
Diff

From 9126525701cdf19e6c3d2caf8a6ece559f99a99e Mon Sep 17 00:00:00 2001
From: Gaggery Tsai <gaggery.tsai@intel.com>
Date: Mon, 10 Aug 2026 22:45:00 -0500
Subject: [PATCH] drm/i915/psr: restore DA14260 Panel Replay with ALPM lag
workaround
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.
Upstream avoids the visible lag by disabling Panel Replay for the exact
Dell 1028:0db9 and LGD 00:22:b9 match, which also removes the idle power
savings.
Replace only that exact disable entry with a hardware-scoped workaround.
Exit Panel Replay for relevant frontbuffer activity and re-enter it after
50 ms of idle time. Use a dedicated delayed work item integrated under
the existing PSR mutex and frontbuffer busy-bit tracking; block normal
immediate re-entry while it is armed, wait for sink idle, and cancel it
on invalidate, pause, and disable.
Other panels retain upstream behavior.
Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7521
Signed-off-by: Gaggery Tsai <gaggery.tsai@intel.com>
---
.../drm/i915/display/intel_display_types.h | 1 +
drivers/gpu/drm/i915/display/intel_psr.c | 65 ++++++++++++++++++-
drivers/gpu/drm/i915/display/intel_quirks.c | 9 +--
drivers/gpu/drm/i915/display/intel_quirks.h | 1 +
4 files changed, 71 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 2e6a857..51ae4ed 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -1779,6 +1779,7 @@ struct intel_psr {
u32 dc3co_exitline;
u32 dc3co_exit_delay;
struct delayed_work dc3co_work;
+ struct delayed_work panel_replay_reenable_work;
u8 entry_setup_frames;
u8 io_wake_lines;
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index 598fe76..0ca20f5 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -2445,6 +2445,7 @@ void intel_psr_disable(struct intel_dp *intel_dp,
mutex_unlock(&intel_dp->psr.lock);
cancel_work_sync(&intel_dp->psr.work);
cancel_delayed_work_sync(&intel_dp->psr.dc3co_work);
+ cancel_delayed_work_sync(&intel_dp->psr.panel_replay_reenable_work);
}
/**
@@ -2476,6 +2477,7 @@ void intel_psr_pause(struct intel_dp *intel_dp)
cancel_work_sync(&psr->work);
cancel_delayed_work_sync(&psr->dc3co_work);
+ cancel_delayed_work_sync(&psr->panel_replay_reenable_work);
}
/**
@@ -3504,6 +3506,17 @@ static void intel_psr_handle_irq(struct intel_dp *intel_dp)
drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
}
+#define PANEL_REPLAY_REENABLE_DELAY_MS 50
+
+static bool panel_replay_alpm_cursor_lag_workaround_enabled(struct intel_dp *intel_dp)
+{
+ return intel_dp_is_edp(intel_dp) &&
+ intel_dp->psr.panel_replay_enabled &&
+ intel_dp->psr.sel_update_enabled &&
+ intel_has_dpcd_quirk(intel_dp,
+ QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG);
+}
+
static void intel_psr_work(struct work_struct *work)
{
struct intel_dp *intel_dp =
@@ -3522,6 +3535,11 @@ static void intel_psr_work(struct work_struct *work)
if (intel_dp->psr.pause_counter)
goto unlock;
+ /* The dedicated delayed work owns re-entry while the workaround is armed. */
+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) &&
+ delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work))
+ goto unlock;
+
/*
* We have to make sure PSR is ready for re-enable
* otherwise it keeps disabled until next full enable/disable cycle.
@@ -3531,6 +3549,10 @@ static void intel_psr_work(struct work_struct *work)
if (!__psr_wait_for_idle_locked(intel_dp))
goto unlock;
+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) &&
+ delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work))
+ goto unlock;
+
/*
* The delayed work can race with an invalidate hence we need to
* recheck. Since psr_flush first clears this and then reschedules we
@@ -3544,6 +3566,33 @@ static void intel_psr_work(struct work_struct *work)
mutex_unlock(&intel_dp->psr.lock);
}
+static void panel_replay_reenable_work(struct work_struct *work)
+{
+ struct intel_dp *intel_dp =
+ container_of(work, typeof(*intel_dp),
+ psr.panel_replay_reenable_work.work);
+
+ mutex_lock(&intel_dp->psr.lock);
+
+ if (!intel_dp->psr.enabled ||
+ !panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) ||
+ intel_dp->psr.pause_counter ||
+ READ_ONCE(intel_dp->psr.irq_aux_error))
+ goto unlock;
+
+ if (!__psr_wait_for_idle_locked(intel_dp))
+ goto unlock;
+
+ /* Recheck for activity or a newer deadline after the unlocked wait. */
+ if (delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work) ||
+ intel_dp->psr.busy_frontbuffer_bits || intel_dp->psr.active)
+ goto unlock;
+
+ intel_psr_activate(intel_dp);
+unlock:
+ mutex_unlock(&intel_dp->psr.lock);
+}
+
static void intel_psr_configure_full_frame_update(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -3615,8 +3664,11 @@ void intel_psr_invalidate(struct intel_display *display,
INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe);
intel_dp->psr.busy_frontbuffer_bits |= pipe_frontbuffer_bits;
- if (pipe_frontbuffer_bits)
+ if (pipe_frontbuffer_bits) {
+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp))
+ cancel_delayed_work(&intel_dp->psr.panel_replay_reenable_work);
_psr_invalidate_handle(intel_dp);
+ }
mutex_unlock(&intel_dp->psr.lock);
}
@@ -3732,6 +3784,15 @@ void intel_psr_flush(struct intel_display *display,
if (intel_dp->psr.pause_counter)
goto unlock;
+ if (pipe_frontbuffer_bits &&
+ panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp)) {
+ intel_psr_exit(intel_dp);
+ mod_delayed_work(display->wq.unordered,
+ &intel_dp->psr.panel_replay_reenable_work,
+ msecs_to_jiffies(PANEL_REPLAY_REENABLE_DELAY_MS));
+ goto unlock;
+ }
+
if (origin == ORIGIN_FLIP ||
(origin == ORIGIN_CURSOR_UPDATE &&
!intel_dp->psr.psr2_sel_fetch_enabled)) {
@@ -3795,6 +3856,8 @@ void intel_psr_init(struct intel_dp *intel_dp)
INIT_WORK(&intel_dp->psr.work, intel_psr_work);
INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work);
+ INIT_DELAYED_WORK(&intel_dp->psr.panel_replay_reenable_work,
+ panel_replay_reenable_work);
mutex_init(&intel_dp->psr.lock);
}
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c
index 883f297..7c772a9 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.c
+++ b/drivers/gpu/drm/i915/display/intel_quirks.c
@@ -86,12 +86,13 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display *display)
drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n");
}
-static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp)
+static void quirk_panel_replay_alpm_cursor_lag(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
- intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY);
- drm_info(display->drm, "Applying disable Panel Replay quirk\n");
+ intel_set_dpcd_quirk(intel_dp, QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG);
+ drm_info(display->drm,
+ "Applying Panel Replay ALPM cursor lag workaround\n");
}
struct intel_quirk {
@@ -267,7 +268,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = {
.subsystem_vendor = 0x1028,
.subsystem_device = 0x0db9,
.sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
- .hook = quirk_disable_edp_panel_replay,
+ .hook = quirk_panel_replay_alpm_cursor_lag,
},
};
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h
index 83214eb..8b083c6 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.h
+++ b/drivers/gpu/drm/i915/display/intel_quirks.h
@@ -22,6 +22,7 @@ enum intel_quirk_id {
QUIRK_FW_SYNC_LEN,
QUIRK_EDP_LIMIT_RATE_HBR2,
QUIRK_DISABLE_EDP_PANEL_REPLAY,
+ QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG,
};
void intel_init_quirks(struct intel_display *display);
--
2.55.0