Engineering

Fixing Suspend/Resume on Meteor Lake: One Kernel Parameter, One Year Too Late

11 min read
Angel Sulev

Correction, 2026-09-22. When I first published this I named i915.enable_psr2_sel_fetch=0 as the fix. It was not. My panel advertises PSR1 only, so selective fetch never engaged — the parameter changed the timing of the freeze, which is exactly why I believed it for months. The parameter that actually ended the freezes is i915.enable_psr=0. The URL keeps the old name so existing links still work; everything below is the corrected version, and I have left the wrong turn in at the bottom because it is the instructive part.

I held the power button down more than 300 times on this machine.

ThinkPad P1 Gen7, Intel Core Ultra 9 185H (Meteor Lake), kernel 6.x through 7.x. Two symptoms, not one. Every suspend ended in a freeze — power LED on, machine dead, hard shutdown. And separately: leave the screen static for about five minutes and the whole machine would lock up with no suspend involved at all. Over a year of real work on a laptop that could neither sleep nor sit still.

The second symptom is the one that solves the case, and I ignored it for months because I had decided this was a suspend bug.


Symptoms — so you know you’re in the right place

  • Freeze on resume from suspend — power LED on, unresponsive, hard shutdown required.
  • Idle freeze with no suspend — a static screen for ~5 minutes and the machine locks. This is the tell. A pure suspend bug does not freeze you while you are sitting there working.
  • It looks like a dead keyboard — input dies while the machine seems otherwise alive. It is not a keyboard bug, and chasing it as one is a trap I will come back to.
  • No logs from the failed attempt — journalctl -b -1 is empty or truncated; the freeze lands before the journal flushes.
  • Persists across kernel versions — 6.x and 7.x both affected.
  • Works fine on Fedora, Ubuntu and Kali out of the box. That is the whole clue.

Hardware that fits: any Meteor Lake laptop (Intel Core Ultra 100 or 200 series) on the i915 driver, especially with an eDP OLED panel. P1 Gen7 is confirmed.


Root cause: forced PSR wedges the eDP PHY on exit

Panel Self Refresh lets the panel hold its own image so the display engine can stop scanning out. It is a real power win when it works. On this machine it does not.

The internal panel (Samsung SDC OLED) advertises PSR1 only — no PSR2, no Panel Replay. My cmdline forced i915.enable_psr=2, but the panel dictates the level: the kernel negotiates down and runs PSR1 regardless. So I had configured one thing and was running another, which is how a whole year of tuning the wrong knob stayed plausible.

PSR auto-enters on a static frontbuffer — that is the idle freeze — and on s2idle, which is the resume freeze. The failure is on PSR exit: the driver has to relock the Meteor Lake C10 PHY, and it wedges. Eight instrumented cycles all failed at the identical point:

Failed to bring PHY A to idle
[CONNECTOR:eDP-1] commit wait timed out
vblank wait timed out   (in i915_drm_resume)
pixel_rate expected 572010 found 65082

Why it looks like a keyboard bug. The stuck DRM worker holds the DRM modeset lock while waiting for a vblank that never arrives. Every GPU and compositor task queues behind that lock, and input delivery is one of them. The serio interrupts still tick in hardware; userspace simply never gets to process them. One mechanism, two triggers, total lockup.


The fix

Minimal — add these to your kernel cmdline

i915.enable_psr=0 i915.enable_dc=1 i915.enable_fbc=1 mem_sleep_default=s2idle
  • i915.enable_psr=0 — the fix. The panel is PSR1-only, so there is nothing to tune: on is what wedges the PHY.
  • i915.enable_dc=1 — DC5/DC6 display power states. enable_dc=0 breaks the display on this stack; pin it on.
  • i915.enable_fbc=1 — pinned for determinism across kernel updates, not because it fixes anything.
  • mem_sleep_default=s2idle — Meteor Lake has no S3. Never set deep.

With NVIDIA dGPU (RTX series)

i915.enable_psr=0
i915.enable_dc=1 i915.enable_fbc=1 i915.enable_guc=3
i915.force_probe=7d55 xe.force_probe=!7d55
nvidia_drm.modeset=1 nvidia_drm.fbdev=1
nvidia.NVreg_DynamicPowerManagement=0x02
nvidia.NVreg_PreserveVideoMemoryAllocations=0
mem_sleep_default=s2idle

i915.force_probe=7d55 and xe.force_probe=!7d55 assign the Meteor Lake iGPU to i915 and block the xe driver from contending for it. On a dual-GPU system this is mandatory. 0x7d55 is the P1 Gen7 device ID — check yours with lspci -nn | grep VGA.

NVreg_DynamicPowerManagement=0x02 enables fine-grained D3 so the RTX power-gates when idle. NVreg_PreserveVideoMemoryAllocations depends on which driver you run. With the open kernel modules (nvidia-open), VRAM save and restore across suspend is handled automatically, so leave it at 0. Setting it to 1 is for the proprietary module, and NVIDIA’s power-management docs say it then requires the /proc/driver/nvidia/suspend mechanism and the nvidia-suspend/nvidia-resume services. Check which one you have with modinfo -F license nvidia: Dual MIT/GPL means open.

With QEMU/KVM GPU passthrough

i915.enable_psr=0 i915.enable_dc=1
intel_iommu=on iommu=pt
mem_sleep_default=s2idle

iommu=pt keeps IOMMU overhead low for host devices. Keep it only if you run VMs with GPU passthrough; CUDA on bare metal does not need it.

How to apply

GRUB — append to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub, then:

sudo grub-mkconfig -o /boot/grub/grub.cfg

systemd-boot — append to the options line in /boot/loader/entries/*.conf. No rebuild needed.


Prove it in 30 seconds, without rebooting

You do not have to take my word for it or edit a bootloader to find out. PSR toggles live through debugfs. The DRI card index is usually 1 for the iGPU — check /sys/kernel/debug/dri/*/:

echo 1 | sudo tee /sys/kernel/debug/dri/1/i915_edp_psr_debug      # disable PSR at runtime
sudo cat /sys/kernel/debug/dri/1/i915_edp_psr_status              # -> PSR mode: disabled

Then work normally, let the screen idle past five minutes, and run one sudo systemctl suspend. If both freezes are gone, PSR was your cause — make it permanent with i915.enable_psr=0. Undo with echo 0, or just reboot.

Toggle it while the screen is active rather than idle: that forces a clean PSR exit instead of provoking one from the state you are trying to avoid.


Testing after reboot

# Verify s2idle is selected
cat /sys/power/mem_sleep          # s2idle in brackets = selected

# Verify the parameter took
grep -o 'i915.enable_psr=[0-9]' /proc/cmdline

# Suspend
sudo systemctl suspend
# Wait 10 seconds. Press the power button once to wake.

# Sanity check
journalctl -b | grep -E 'PM: suspend|resume'

PM: suspend entry followed by PM: suspend exit a few seconds apart means suspend worked. If the machine is unresponsive after the power button, it froze. Before you hold the power button down, try the kernel’s own exit: Alt+SysRq, then s, u, o — sync, remount read-only, power off. On a ThinkPad without a SysRq key, hold Alt first, then press Fn+PrtSc, release those two, and tap each letter with Alt still held. Then read the previous boot:

journalctl -b -1 | grep -E 'PM: suspend|i915.*fail|PHY|flip_done|panic'

The PSR failure signature is Failed to bring PHY A to idle and flip_done timed out. If you see no i915 errors at all, you have a different problem — see the note at the end of this post.


It is the config, not the chip and not the kernel

The same hardware suspends fine on Fedora, Ubuntu and Kali with stock settings. Arch’s linux is built from the same mainline tree as Fedora’s, so the kernel version was never the variable — the config layer was. None of those distros force PSR. I did.

And a T480s (Whiskey Lake) froze the same way the moment it inherited the same cmdline. Different GPU, different PHY, same bug. Copy a configuration that is known to work before you tune anything blind.


Power tuning — what to do once suspend works

USB autosuspend — skip the global kill switch

I had usbcore.autosuspend=-1 in my cmdline, which disables USB autosuspend for every device on the machine. It works, and it costs battery for no reason. Enable autosuspend globally in TLP and exempt only the devices that must not sleep:

# /etc/tlp.conf
USB_AUTOSUSPEND=1
USB_DENYLIST="1050:0407 27c6:6594 04f2:b805"

Those IDs are a YubiKey (1050:0407), a Goodix fingerprint reader (27c6:6594) and a Chicony integrated camera (04f2:b805). Find yours with lsusb, and add Bluetooth dongles, smart card readers, anything that has to respond instantly.

Remove usbcore.autosuspend=-1 after confirming suspend works, regenerate the GRUB config, reboot, test again.

TLP on Meteor Lake

CPU_DRIVER_OPMODE_ON_AC=active
CPU_DRIVER_OPMODE_ON_BAT=active
CPU_SCALING_GOVERNOR_ON_AC=powersave
CPU_SCALING_GOVERNOR_ON_BAT=powersave
CPU_BOOST_ON_AC=1
CPU_BOOST_ON_BAT=0
PLATFORM_PROFILE_ON_AC=balanced
PLATFORM_PROFILE_ON_BAT=low-power

active is the right mode for intel_pstate on MTL — do not switch to passive. The powersave governor under intel_pstate is not what the name suggests; it still boosts when needed, it just makes better efficiency decisions about when.

What disabling PSR actually costs

Measured on this machine with i915.enable_psr=0:

  • Idle draw: ~6 W whole-system (upower energy-rate)
  • Intel GPU RC6 residency: ~99% (intel_gpu_top) — the iGPU is parked almost the entire idle window
  • Battery: over 14 h idle to full discharge on a 90 Wh pack

So you forgo a small self-refresh saving and in exchange the machine resumes, and never locks up while you are looking at it. If you want that watt back, take it from the idle path with a black framebuffer blank — on OLED, black means the pixels are off — rather than by re-enabling the PSR that wedges the PHY. Keep DPMS panel power-off out of it; that drives the same PHY.


Known non-critical issues

NVIDIA “PRH failed to update thermal limit”

NVRM: GPU0 nvAssertFailedNoLog: Assertion failed: PRH failed to update thermal limit!

Appears twice per boot on my system. It is a bug in nvidia-open — the Platform Request Handler fails to sync with GSP firmware. Non-fatal, no crashes, no effect on suspend. Switch to the proprietary nvidia package if it bothers you; otherwise ignore it.

“Correcting number of heads (0x00)” — expected when no external display is connected. Not your config.

TLP vm.laptop_mode deprecation — cosmetic. Remove the line from your TLP config.


What I got wrong (and it is worth reading)

For months I was certain the culprit was PSR2 selective fetch. I set i915.enable_psr2_sel_fetch=0, kept enable_psr=2, and cited linux-surface #2158 as support. It shifted the timing of the failure, so I believed it. It was moot: the panel runs PSR1, so sel_fetch was never in the path at all. The freezes continued. Turning PSR off outright is what ended them.

A parameter that shifts symptoms is not a parameter that fixes a cause. Verify against a known-good system instead of reasoning forward from a plausible mechanism, which is what I did for a year.

Two more, in the same spirit:

  • Never add any i8042.* boot parameter. On the keyboard theory I tried i8042.reset i8042.nopnp. They run inside the s2idle resume path and hang the entire machine — strictly worse than the bug. It was never the keyboard; the keyboard was downstream of the modeset lock.
  • Do not blacklist mei / mei_me. It binds to i915 as mei_gsc_proxy, which makes it downstream of the GPU, not causal. Blacklisting it moved the failure around and fixed nothing.

A year and 300-odd hard shutdowns for a one-liner, and the one-liner was not even the one I published first. That is how it goes sometimes.


If this does not fix it

enable_psr=0 ends the PSR class of freeze. It does not end every resume bug on this platform, and I am still chasing one myself.

Match your logs before you match your symptoms. If a resume hang comes with the PHY A and flip_done errors above, you are in the bug this post is about. If your desktop and all your input devices die together while the kernel keeps running normally — SSH still answers, the disk still syncs — and dmesg shows zero *ERROR* lines from any driver, that is something else entirely. Mine turned out to live in the session layer. On every failed cycle I captured, logind never paused the iGPU’s DRM device at suspend, then sent a resume for it anyway. Xorg never acted on that resume and went on modesetting without DRM master, which fails. The kernel is not the variable: the LTS 6.18 kernel fails exactly like 7.2. It is tracked upstream at systemd#43352, and it is not fixed.

You do not need the power button for this one either. Alt+SysRq+k (SAK) kills everything attached to the active console, which means the frozen X session and nothing else. On my autologin setup a fresh session was up within a second; with a display manager you should get its login screen back instead. System services keep running. logind dies too, since it holds the console open, but it restarts itself within a second. Open windows are lost, because they die with X. Give v a few seconds first if you like; it costs nothing, but it did not bring my session back.

A clean dmesg is evidence, not silence. It took me an embarrassingly long time to treat it as such.


References


Share this post

AS

Angel Sulev

Cybersecurity + Agentic AI Expert

Senior specialist in cybersecurity and Agentic AI with 30+ years turning security into competitive advantage.

About me

Related Posts