227 lines
8.3 KiB
Diff
227 lines
8.3 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 XPS 14/16 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 panels in the Dell XPS 14 DA14260
|
|
and XPS 16 DA16260. Upstream avoids the visible lag on the XPS 14 by
|
|
disabling Panel Replay for the exact Dell 1028:0db9 and LGD 00:22:b9
|
|
match, which also removes the idle power savings. The XPS 16 has the
|
|
same sink OUI under Dell subsystem 1028:0dba and exhibits the same lag.
|
|
|
|
Replace the XPS 14 disable entry and add an exact XPS 16 match for the
|
|
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 | 15 +++--
|
|
drivers/gpu/drm/i915/display/intel_quirks.h | 1 +
|
|
4 files changed, 77 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,13 @@ 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,
|
|
},
|
|
+ {
|
|
+ .subsystem_vendor = 0x1028,
|
|
+ .subsystem_device = 0x0dba,
|
|
+ .sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
|
|
+ .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
|