For a few years I’ve been having an issue with my laptop: the backlight is too bright.

A few months ago I became annoyed enough to actually fix it for myself.

After a bit of searching I found the problem in the linux kernel. It turns out that Linux parses the Intel VBT (Video Bios Table) to determine what the valid range of brightnesses are for a display.

The VBT is a giant binary blob with no official documentation, but Intel does provide a decoder aptly named intel_vbt_decode in “IGT GPU Tools”, a collection of scripts used in testing Linux’s Intel GPU drivers. Conveniently, Linux provides a copy of the VBT for you to parse in /sys/kernel/debug/dri/*/i915_vbt.

The first step was to patch the VBT, just as a file. I used intel_vbt_decode to find the values of the backlight info block, which ended up looking like this:

BDB block 43 (129 bytes, min 305 bytes) - LFP backlight info block:
        Entry size: 6
        Panel 0 (LFP2)
                Inverter type: PWM (2)
                Active low: no
                PWM freq: 200
                Minimum brightness: 6
                Level: 255
                Control type: 2
                Controller: 0
        Panel 2 (LFP1)
                Inverter type: PWM (2)
                Active low: no
                PWM freq: 200
                Minimum brightness: 6
                Level: 255
                Control type: 2
                Controller: 0

Using that and the igt-gpu-tools code I determined that there should be several instances of 0xc8 0x00 0x06 next to each other, referring to the PWM frequency and the minimum brightness. I replaced the 0x06 with 0x01 to get a very low but still on backlight.

Next I had to convince Linux to use it. With a bit of searching for the text “vbt” in the Linux DRM driver I found out that i915 will accept a modified vbt firmware when given the argument i915.vbt_firmware=<name>. That name gets passed to request_firmware, the function that gets a firmware file out of /lib/firmware (or equivalent if your distro sets it differently).

I added the kernel parameter i915.vbt_firmware=i915_vbt, then put a file named i915_vbt in the firmware directories of my root partition and initrd. This will vary somewhat by distro, but on NixOS it was:

boot.kernelParams = [ "i915.vbt_firmware=i915_vbt" ];
boot.initrd.extraFirmwarePaths = [ "i915_vbt" ];
hardware.firmware = [
  (pkgs.runCommand "i915-vbt" { } ''
    mkdir -p $out/lib/firmware
    cp ${./i915_vbt} $out/lib/firmware/i915_vbt
  '')
];

A reboot later and suddenly 0 brightness meant a little dimmer!

If I had thought about it more, I probably would have patched intel_vbt_decode.c to print out the offset of the minimum brightness values from the beginning of the file. Unfortunately I don’t tend to think through things enough when doing what I consider “simple projects”. This still only took a few hours though.