site logo

Patching Intel GPU Driver for Better Linux Battery Life

Update Sep 25 ‘22: Binary build of patched linux-zen kernel is now available! See here for more info.

After previously wiping out Windows 10 on my laptop (an ASUS Flip 13 OLED UX363ea), I felt better. Not because it finally became the Linux master race(tm), but rather that it stopped causing third degree burns every time it booted up.

The battery optimizations I installed for Arch Linux, well, worked. It did helped reduce power consumption by a few hours. Hoever, my laptop’s Intel processor was unable to reach the elusive pc10 C-state with screen turned on. That means the CPU is still consuming electricity even though it would be better off turned off completely.

This, is not ideal.

But why does C-State matter?

C-states help CPU efficiency by progressively turning off more parts of the CPU with deeper C-states. Almost all modern x86 processors support it.

With Intel CPU’s amazing but elusive pc10 state, the whole CPU is practically turned off. We would want this if we need to minimize a computer’s power consumption.

You can check C-state residency of your device by utilizing

~$ sudo powertop

Searching for pc10 C-State

I was able to achieve pc10 state by turning screen completely off, while only pc8 could be reached otherwise. So most likely the system display is blocking pc10 on my system.

Blog post from Intel seems to support my guess. You need to either turn off the screen or have a panel self-refresh(PSR) screen to reach pc10. The problem is, my laptop’s screen does support PSR, and PSR is automatically enabled if possible.

Just in case, I checked its PSR status.

~$ sudo cat /sys/kernel/debug/dri/0/i915_edp_psr_status

Sink support: yes [0x01]
PSR mode: disabled
PSR sink not reliable: no

So it supports PSR, but Linux’s Intel GPU driver disabled it. For seemingly no reason. It even stated that the PSR sink is reliable. (PSR2 is not supported unfortunately)

Fixing panel self-refresh

So my hardware supports it. This is good news, and we just need to fix the software somehow. Since this clearly has something to do with i915, might as well enable drm logging in the kernel.

To enable drm logging, add

drm.debug=0x1ff log_buf_len=32M

to your kernel’s command-line parameters.

Then, I checked the kernel log:

~$ sudo dmesg | grep PSR
[    1.273207] i915 0000:00:02.0: [drm:intel_psr_init_dpcd [i915]] eDP panel supports PSR version 1
[    1.471109] i915 0000:00:02.0: [drm:intel_dp_initial_fastset_check [i915]] Forcing full modeset to compute PSR state
[    1.471892] i915 0000:00:02.0: [drm:intel_dp_compute_config [i915]] PSR condition failed: PSR setup time (330 us) too long

This error is caused by this file.

Apparently PSR is disabled due to a failed condition check. However, I know PSR worked on Windows. So, might just as well disable this condition check.

Patching Arch Linux’s kernel

I quickly made a patch for linux 5.19.9. (Only tested on Arch Linux and Fedora)

To build a patched kernel, get linux-zen’s build files,

~$ asp export linux-zen

, paste the patch file, and replace PKGBUILD with the file in that gist.

Then, build the kernel:

~$ makepkg -s --skippgpcheck

Installing the kernel with:

~$ sudo pacman -U linux-zen-psrfix-5.19.9.zen1-1-x86_64.pkg.tar.zst linux-zen-psrfix-headers-5.19.9.zen1-1-x86_64.pkg.tar.zst

Enabling the patch

The option to force enable PSR is hidden behind a module parameter. To enable it, add psr_ignore_setup_time=1 to i915’s parameters.

~$ echo "options i915 enable_psr=1 psr_ignore_setup_time=1" > /etc/modprobe.d/i915.conf

Regenerate your initramfs, and reboot:

sudo mkinitcpio -P

Did it work?

Using the same command, we can check if the patch worked:

~$ sudo cat /sys/kernel/debug/dri/0/i915_edp_psr_status

Sink support: yes [0x01]
PSR mode: PSR1 enabled
Source PSR ctl: enabled [0x81f00ee9]
Source PSR status: SRDENT [0x40010000]
Busy frontbuffer bits: 0x00000000

And it worked! Arbitarily removing that condition check enables PSR as expected.

Battery life after patching

Now, powertop reports a battery drain of 2.9 Watt with screens on. Wow. That’s 20 hours of battery life, a nearly 25% improvement from its previous best record of 16 hours. Although panel self-refresh only kicks in when the screem is displaying static images, this will still help a lot.

But wouldn’t that cause issues? That’s why the condition check was there in the first place, no?

Fortunately for me, there doesn’t seem to be any issues after applying the patch. The laptop still works, there are no tearing or stability issues. It could be that my screen reported erraneous latency. Even better, the CPU is able to reach pc10 with display on now!

Aftermath

Binary build of patched linux-zen kernel is now available! If your laptop is facing the same problem, feel free to try it out.

Make sure to have another kernel in case this one fails!

The installation files can be downloaded in CI/CD tab.