Files
omarchy-pkgs/pkgbuilds/edge/linux-ptl/0028-drm-i915-psr-exit-Panel-Replay-for-ALPM-lag.patch
T
49214baae5 linux-ptl: [EXPERIMENTAL] enable Panel Replay Selective Update with cursor fix (#77)
* linux-ptl: add package with audio, wifi, and Panel Replay SU patches

Adds the linux-ptl Omarchy package for Panther Lake (PTL) hardware.
The stock Arch linux kernel on PTL is missing: SDCA audio class
driver backports, an iwlwifi generation-data fix, and Panel Replay
SU enablement + cursor-lag workaround. Ship them in this package
until they land upstream and Arch picks them up.

Patch sets
==========

Audio (SDCA backports on 6.19.10)
  0001-0005, 0007-0008, 0010, 0012-0014, 0017
  Cirrus CS35L57 amps and SDCA jack on Dell XPS OLED require the
  SDCA class driver plus a NULL-pointer fix (0017). Without these
  the sound card never registers. 0006/0009/0011/0015/0016 from the
  full upstream series are already in 6.19.10 stable and not needed.

WiFi
  0018-wifi-iwlwifi-mld-correctly-set-wifi-generation-data.patch
  Backport from wifi-next to fix wifi generation data reporting on
  Intel AX/BE radios in PTL.

Display (PSR2 + Panel Replay SU)
  0019-drm-i915-psr-accept-early-transport-for-psr2.patch
  0020-drm-i915-psr-fix-su-granularity-source-for-psr2.patch
  0022-drm-i915-psr-allow-psr-with-vrr-on-ptl.patch
    Enable PSR2 Early Transport and fix the SU-granularity DPCD
    source so PSR2 is not rejected at compute_config on OLED
    panels (LGD 2880x1800 etc). Also allow PSR with VRR on PTL.

  0026-drm-i915-psr-handle-Panel-Replay-full-line-SU-granul.patch
    PR SU can advertise X granularity = 0xFFFF (full-line).
    Without this the modulo check rejects PR SU.

  0027-drm-i915-trans-push-frame-change-for-panel-replay.patch
    Program TRANS_PUSH Frame Change path so PR SU cursor/plane
    updates actually trigger selective updates.

  0028-drm-i915-psr-exit-Panel-Replay-for-ALPM-lag.patch
    Workaround for the LGD OLED panel (Dell XPS 14 DA14260,
    sink OUI 00:22:b9). PR ALPM AUX-Less wake/sleep cycling on
    this panel cannot resume selective updates at frame rate,
    producing ~15 Hz visible motion from a 120 Hz source.

    The workaround exits PR in frontbuffer_flush() and uses the
    existing dc3co_work delayed_work as a 50 ms idle timer.
    During continuous motion the timer is re-armed by each flush,
    so the link stays awake and motion is smooth; after 50 ms of
    genuine idle, dc3co_work fires and PR re-activates with ALPM
    for power savings.

    Compared to a per-commit exit/re-enter approach (earlier
    revision of this PR), the delayed re-entry keeps video and
    animation playback smooth: per-commit cycling pays the panel's
    wake-to-display latency on every frame, which is visible as
    playback stutter. 50 ms was chosen empirically against 100 ms
    and 200 ms on the same device; 50 ms gives the best idle and
    cursor-motion power while remaining subjectively identical in
    smoothness to the longer delays.

    Power on Dell XPS 14 OLED (LGD 2880x1800 @ 120 Hz, battery,
    brightness 268/512, 10-run averaged idle):

      PR SU + delayed-re-entry WA  : 3268 mW  (smooth)
      PR SU stock (cursor laggy)   : 3384 mW
      PR SU + WA 100 ms            : 3436 mW
      PR SU + WA 200 ms            : 3484 mW
      PSR2                         : 4060 mW  (baseline)

config.x86_64 changes
=====================

Required for the kernel to boot cleanly on this hardware:

  CONFIG_PINCTRL_INTEL_PLATFORM=y   (was =m)
    Without this the PTL GPIO controller is a late-loaded module
    and early probe of CS35L57 audio amps (spk-id-gpios) and the
    I2C touchpad time out with EPROBE_DEFER / -110 ETIMEDOUT.

  CONFIG_VFAT_FS=y, CONFIG_FAT_FS=y, CONFIG_MSDOS_FS=y,
  CONFIG_NLS_ISO8859_1=y   (all were =m)
    Required for the ESP /boot to mount in the initramfs / UKI
    boot flow. Without these the box drops to emergency mode.

CONFIG_RUST is disabled in this package because Arch Rust 1.94.1
is too new for kernel 6.19.x Rust code.

* Backport DisplayID adaptive sync VRR parsing

* add vrr patch

* fix linux-ptl patch indentation

* linux-ptl: renumber DisplayID VRR patch

---------

Co-authored-by: Gaggery Tsai <gaggery.tsai@gmail.com>
Co-authored-by: Spencer Bull <Spencer.Bull@Dell.com>
2026-04-21 14:39:32 +02:00

117 lines
4.5 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>
---
drivers/gpu/drm/i915/display/intel_frontbuffer.c | 1 +
drivers/gpu/drm/i915/display/intel_psr.c | 33 ++++++++++++++++++
drivers/gpu/drm/i915/display/intel_psr.h | 1 +
3 files changed, 35 insertions(+)
--- 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);
}
--- 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);
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -1217,6 +1217,8 @@ static void tgl_psr2_enable_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 =
@@ -1227,6 +1229,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);
@@ -4593,2 +4593,29 @@ u8 intel_psr_needed_vblank_count_for_frontbuffer_flush(struct intel_display *dis
return psr_min_guardband;
}
+
+/*
+ * 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);
+ }
+}