Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

x220-bl

Linux kernel backlight driver for the ThinkPad x220 FHD mod control board — a USB HID device at 10c4:83ce. It registers /sys/class/backlight/x220-bl so desktop environments (KDE/KWin, GNOME, etc.) can control the panel backlight natively through their usual brightness controls.

What it does

  • Forwards brightness to the control board over USB HID SET_REPORT / GET_REPORT (output report ID 0x06, feature report ID 0x04; 16 levels, 0–15).
  • Parents the backlight device under the correct DRM connector so Wayland/KWin discovers it automatically. It matches in priority order:
    1. A connected eDP connector (patched VBT → eDP-1)
    2. A connected connector named DP-3 (stock VBT → DP-3) ...and skips phantom connectors like LVDS-1 that may also report as connected on this hardware.
  • Auto-reclaims the USB interface after suspend/resume and hotplug, so the driver survives re-enumeration instead of losing the device to usbhid. See How auto-reclaim works.

Requirements

  • Linux kernel headers for the running kernel (linux-headers on Arch).
  • DKMS — optional, but recommended so the module rebuilds on kernel updates.
  • Rust toolchain — only needed to build the optional VBT patcher.

Installation

Arch Linux (recommended on Arch)

makepkg -si

This builds and installs the x220-bl-dkms package, which:

  • Installs the module source under /usr/src/x220-bl-dkms-1.0/.
  • Registers it with DKMS and builds/installs it for the running kernel.
  • Installs the modprobe quirk to /etc/modprobe.d/x220-bl.conf (see below).

After a kernel update, DKMS auto-rebuilds the module.

DKMS (manual, any distro)

sudo cp -r . /usr/src/x220-bl-1.0
sudo dkms add x220-bl/1.0
sudo dkms build x220-bl/1.0
sudo dkms install x220-bl/1.0

Then install the modprobe quirk manually (see below).

Manual (quick test, not persistent across reboots)

make
sudo insmod x220_bl.ko

You will also want the modprobe quirk below, or usbhid will grab the device.

Modprobe quirk (x220-bl.conf)

The FHD control board is not HID-compliant, so usbhid / hid-generic match it with higher priority than this driver and own interface 0 at boot. The quirk tells usbhid to ignore the device entirely so x220-bl owns it:

# Prevent usbhid from binding the (non-HID-compliant) AGAN FHD control board
# so the x220-bl driver owns it. HID_QUIRK_IGNORE = BIT(2) = 0x4.
options usbhid quirks=0x10c4:0x83ce:0x4

On Arch, makepkg -si installs this to /etc/modprobe.d/x220-bl.conf automatically (it is backed up by the package). On other distros, install it manually:

sudo cp x220-bl.conf /etc/modprobe.d/
sudo modprobe -r usbhid && sudo modprobe usbhid   # or just reboot

This is the first layer of auto-reclaim — see the next section.

Usage

# Set brightness (0-15)
echo 15 > /sys/class/backlight/x220-bl/brightness

# Read current brightness directly from the device
cat /sys/class/backlight/x220-bl/actual_brightness

# Max brightness
cat /sys/class/backlight/x220-bl/max_brightness

Note: the backlight core caches brightness, so cat brightness does not round-trip to the device. Read actual_brightness to verify a real device read.

To unload the module:

sudo rmmod x220_bl

How auto-reclaim works

Reclaiming the interface after suspend/resume (and hotplug) is done in two layers. Both are recommended; together they make the driver robust.

Layer 1 — modprobe quirk (x220-bl.conf, described above): prevents usbhid from binding the board at all, so x220-bl wins interface 0 cleanly on every (re-)enumeration.

Layer 2 — in-module notifier: the module registers on the USB notifier chain via usb_register_notify(). On USB_DEVICE_ADD for 10c4:83ce it defers into a dedicated ordered workqueue (claim/probe sleep, but the notifier runs in atomic context), resolves interface 0 via usb_ifnum_to_if(), and calls the reclaim helper which unbinds any current owner, claims the interface, and probes it. This covers resume re-enumeration and hotplug even when Layer 1 is absent.

At boot, the notifier only sees devices added after registration, so the module also does a synchronous boot-time claim via usb_for_each_dev() to pick up devices that were already present.

With Layer 1 active, the device is already ours by the time USB_DEVICE_ADD fires, so the reclaim helper is a no-op. Without Layer 1, the worker steals it back from usbhid. Either way, the driver ends up owning the interface.

Verify / troubleshoot

ls /sys/class/backlight/                 # should list x220-bl
readlink -f /sys/class/backlight/x220-bl  # should resolve under drm/card*/card*-eDP-1
                                         #   (or card*-DP-3 with the stock VBT)
cat /sys/class/drm/card*/status           # LVDS-1 should be gone or disconnected
dmesg | grep x220-bl                      # connector match + claim log lines

If the backlight shows up but reads look stale, remember to check actual_brightness, not brightness (see the note in Usage).

VBT patching

This is an optional, separate tool under vbt/ (Rust). It is not required to use the driver — with the stock VBT the panel appears as DP-3 and the driver matches that just fine. Patching the VBT only changes the connector name to eDP-1 so i915 stops creating a phantom LVDS-1 / intel_backlight.

The x220 FHD mod replaces the LVDS panel with an eDP panel wired through DP-D (DRM connector DP-3), but the stock VBT still describes an LVDS internal panel. This causes i915 to create both LVDS-1 + intel_backlight and DP-3, and some desktop environments prefer the phantom intel_backlight. The vbt/ tool patches the VBT so i915 treats DP-D as the internal panel:

  • Child device 0 (LVDS/LFP1) is replaced with the EFP3/DP-D child.
  • The replacement gets handle 0x0008 (LFP1) and type 0x78c6 (internal + eDP).
  • The old EFP3 slot is zeroed out.
  • Boot display type is set to eDP in BDB_DRIVER_FEATURES.
  • The VBT header checksum is recalculated.

Build the patcher

cd vbt
cargo build

Extract and patch the VBT

# Extract from the running kernel
cp /sys/kernel/debug/dri/*/i915_vbt /tmp/x220.vbt

# Patch it (writes x220-edp.vbt next to the input)
cargo run -- /tmp/x220.vbt

# Install the patched VBT
sudo cp x220-edp.vbt /lib/firmware/

Configure initramfs

If your root filesystem is encrypted, embed the VBT in the initramfs so i915 can load it before root is mounted. Edit /etc/mkinitcpio.conf:

FILES=(/lib/firmware/x220-edp.vbt)

Configure kernel parameters

Add to /etc/kernel/cmdline:

i915.vbt_firmware=x220-edp.vbt

For rEFInd, also update /boot/refind_linux.conf to match.

Rebuild and reboot

sudo mkinitcpio -P
sudo reboot

Verify

ls /sys/class/backlight/           # should show x220-bl
readlink /sys/class/backlight/x220-bl  # should be under drm/card*/card*-eDP-1/
cat /sys/class/drm/card*/status    # LVDS-1 should be gone or disconnected

License

GPL-2.0-only. See LICENSE.

About

Kernel driver for a.gan (or a.gain / 阿甘) x220 and x230 FHD mod

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages