If you bought an ASUS TUF Gaming A16 FA608WV (2024/2025, Ryzen AI 9 HX + RTX 4060 Laptop) and ran Linux on it, you probably hit every single one of these walls:
- Keyboard RGB backlight: completely dead —
asusctl,brightnessctl, every command returns success and nothing lights up - Screen brightness in hybrid GPU mode: writes succeed in
/sys/class/backlight/...but the panel doesn't dim - Sleep with the lid closed: laptop overheats in the backpack because the system never actually enters S3/S4
@sureshsaragadam wrote a deep dive on dev.to in January 2026 concluding that everything needed a kernel patch and we just had to wait. There's an open asusctl issue (#700) tracking the same problem.
It turns out every one of these is fixable from userspace today. This post documents the complete chain: how I went from "nothing works" to "everything works" on a clean Pop!_OS 24.04 install, including an upstream-quality asusctl patch and a workaround stack for the screen.
TL;DR
If you have a FA608WV, you can have:
- ✅ Keyboard RGB controllable from
asusctl,rog-control-center, and CLI scripts — color, brightness, modes - ✅ Screen brightness Fn keys working in hybrid GPU mode (no need for discrete-GPU mode)
- ✅ Real S4 hibernate (swapfile-backed),
HandleLidSwitch=hibernate, no more backpack-overheating
Skip to the sections that interest you.
Hardware context
Model ASUS TUF Gaming A16 FA608WV_FA608WV
CPU/iGPU AMD Ryzen AI 9 HX (Radeon 880M)
dGPU NVIDIA RTX 4060 (Laptop)
Distro Pop!_OS 24.04 LTS (single OS, no Windows dual-boot)
Kernel 7.0.11-76070011-generic
BIOS FA608WV.309 (2025-10-01)
asusctl 6.3.8 (patched, see below)
Part 1 — Keyboard RGB backlight via HID LampArray
The diagnosis
The keyboard on this SKU is NOT behind asus-wmi at all. The legacy /sys/class/leds/asus::kbd_backlight node exists and accepts writes, but the writes go nowhere — the actual backlight controller is an ITE5570 on the I2C-HID bus, vendor 0B05 product 19B6, speaking the Microsoft HID LampArray standard (HID Usage Tables 1.5, Usage Page 0x59).
$ ls /sys/bus/hid/devices/ | grep 0B05
0018:0B05:19B6.0002 # BUS 0018 = I2C-HID, not USB
$ cat /sys/bus/hid/devices/0018:0B05:19B6.0002/uevent
DRIVER=hid-generic
HID_ID=0018:00000B05:000019B6
HID_NAME=ITE5570:00 0B05:19B6
HID_PHYS=i2c-ITE5570:00
$ sudo xxd /sys/bus/hid/devices/0018:0B05:19B6.0002/report_descriptor | grep "05 59"
# match → LampArray Usage Page present
asusctl 6.3.x doesn't probe for HID LampArray devices and only walks USB-side parents. So nothing reaches the chip.
The LampArray protocol is well-defined:
| Report ID | Name | Direction | Purpose |
|---|---|---|---|
0x41 |
LampArrayAttributes | Feature IN | LampCount, bounding box, kind, min int. |
0x44 |
LampMultiUpdate | Feature OUT | Update up to 8 specific lamps |
0x45 |
LampRangeUpdate | Feature OUT | Update a contiguous range with one RGBI |
0x46 |
LampArrayControl | Feature OUT | Disable autonomous (vendor) mode |
Working sequence:
- Disable autonomous mode:
HIDIOCSFEATUREwith[0x46, 0x00] - Send
LampRangeUpdate:[0x45, 0x01, 0x00, 0x00, lamp_count-1, R, G, B, I]
On the FA608WV, LampArrayAttributes reports LampCount=1 (single-zone), Kind=Keyboard, bounding box ~310×144 mm.
Two ways to drive it from Linux
Option A: standalone Python script (fastest)
Doesn't require modifying asusctl. Stand-alone, ~70 lines of stdlib Python. Works immediately.
Save as /usr/local/bin/asus-tuf-kbd-led:
#!/usr/bin/env python3
"""asus-tuf-kbd-led — LampArray controller for ASUS TUF A16 FA608WV (0B05:19B6)."""
import fcntl, struct, sys, glob, json, pathlib
VID, PID = 0x0B05, 0x19B6
STATE_FILE = "/var/lib/asus-tuf-kbd-led/state.json"
def _IOC(d,t,n,s): return (d<<30)|(s<<16)|(t<<8)|n
def HIDIOCSFEATURE(l): return _IOC(3, ord('H'), 0x06, l)
def HIDIOCGFEATURE(l): return _IOC(3, ord('H'), 0x07, l)
def HIDIOCGRAWINFO(): return _IOC(2, ord('H'), 0x03, 8)
RID_ATTRS=0x41; RID_RANGE=0x45; RID_CTRL=0x46
def find_hidraw():
for path in sorted(glob.glob("/dev/hidraw*")):
try:
with open(path, "rb+") as fd:
buf = bytearray(8)
fcntl.ioctl(fd, HIDIOCGRAWINFO(), buf, True)
bus, v, p = struct.unpack("<IHH", bytes(buf))
if v == VID and p == PID: return path
except OSError: continue
return None
# ... (autonomous, range_update, state load/save, dispatch — full script in the gist link below)
udev rule for non-root access:
# /etc/udev/rules.d/99-asus-tuf-lamparray.rules
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", KERNELS=="0018:0B05:19B6.*", \
MODE="0660", GROUP="input", TAG+="uaccess"
systemd services for boot and resume:
# /etc/systemd/system/asus-tuf-kbd-led.service
[Unit]
Description=ASUS TUF kbd backlight (LampArray restore at boot)
After=multi-user.target
[Service]
Type=oneshot
ExecStartPre=/bin/sleep 1
ExecStart=/usr/local/bin/asus-tuf-kbd-led restore
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/asus-tuf-kbd-led-resume.service
[Unit]
Description=ASUS TUF kbd backlight (LampArray restore after resume)
After=hibernate.target suspend.target hybrid-sleep.target
[Service]
Type=oneshot
ExecStartPre=/bin/sleep 1
ExecStart=/usr/local/bin/asus-tuf-kbd-led restore
[Install]
WantedBy=hibernate.target suspend.target hybrid-sleep.target sleep.target
Usage:
asus-tuf-kbd-led on # restore last state
asus-tuf-kbd-led off
asus-tuf-kbd-led white
asus-tuf-kbd-led red|green|blue
asus-tuf-kbd-led set ff8800 # custom color (hex)
asus-tuf-kbd-led intensity 128 # 0-255 brightness
asus-tuf-kbd-led info
This works completely standalone and survives reboot, suspend, hibernate.
Option B: patched asusctl (upstream-quality fix)
For the proper solution that makes rog-control-center and asusctl themselves work, I patched the asusctl codebase to add I2C-HID LampArray support. The patch is open as PR to opengamingcollective/asusctl: https://github.com/OpenGamingCollective/asusctl/pull/147.
The patch:
- adds an I2C-HID fallback path to
aura_manager::init_hid_devices(existing USB path untouched) - adds
DeviceHandle::maybe_lamparray()factory - adds
Aura::is_lamparrayflag andlamparray_write_effect/lamparray_set_brightness/lamparray_set_aura_powermethods - routes the zbus methods through the LampArray branch when
is_lamparray=true - fixes a
HIDIOCGRAWINFOioctl size bug (was12, kernel struct is8bytes) - fixes
HID_IDparsing (was string compare on padded hex, now numeric) - fixes a
tokio::Mutex<AuraConfig>deadlock between zbus method and internal write - fixes a systemd
Type=dbustimeout by registering the bus name before discovery - uses
O_NONBLOCKon the hidraw open to avoid blocking inside the i2c-hid kernel driver
After the patched binary is installed, the test sequence is:
$ sudo install -m755 target/release/asusd /usr/bin/asusd
$ sudo systemctl restart asusd
$ busctl tree xyz.ljones.Asusd | grep aura
└─ /xyz/ljones/aura/lamparray_19b6
$ asusctl aura effect static -c ff0000 # keyboard turns red
$ asusctl aura effect static -c 00ff00 # green
$ asusctl aura effect static -c 0000ff # blue
And rog-control-center color picker on the keyboard tab now drives the hardware in real time.
Part 2 — Screen brightness in hybrid GPU mode
The diagnosis (corrected)
The internal panel is wired to the AMD iGPU, but in hybrid mode the kernel exposes three "backlight" devices, two of which are cosmetic:
| Device | Status on FA608WV |
|---|---|
amdgpu_bl0 |
created but writes are no-ops (cosmetic) |
nvidia_0 |
created by NVIDIA DRM but cosmetic in hybrid (NVIDIA isn't driving the panel) |
nvidia_wmi_ec_backlight |
the real one — driver talks to the Embedded Controller via NVIDIA WMI |
The trick is that nvidia_wmi_ec_backlight is the path that actually controls the panel even though it's named "nvidia" — in hybrid mode, the EC accepts the WMI command and adjusts the panel backlight regardless of which GPU is "active". The earlier dev.to deep dive concluded amdgpu_bl0 was the right one because the kernel registers it; in practice on this BIOS, it's cosmetic and only nvidia_wmi_ec_backlight works.
If you've already added acpi_backlight=native to your kernel cmdline or blacklisted nvidia_wmi_ec_backlight, undo both:
sudo rm -f /etc/modprobe.d/blacklist-nvidia-wmi-backlight.conf
sudo kernelstub -d 'acpi_backlight=native' # if you previously added it
sudo update-initramfs -u
sudo reboot
After reboot, only one backlight device should be present:
$ ls /sys/class/backlight/
nvidia_wmi_ec_backlight
$ brightnessctl --device=nvidia_wmi_ec_backlight set 30% # screen actually dims
$ brightnessctl --device=nvidia_wmi_ec_backlight set 100% # back to bright
Hook into COSMIC Fn keys
If you're on Pop!_OS 24.04 with COSMIC desktop, override the system actions to use the real device:
# In ~/.config/cosmic/com.system76.CosmicSettings.Shortcuts/v1/system_actions
# Replace the busctl-based BrightnessUp/Down with brightnessctl on the working device
Set them to:
BrightnessUp: "brightnessctl --device=nvidia_wmi_ec_backlight set +5%",
BrightnessDown: "brightnessctl --device=nvidia_wmi_ec_backlight set 5%-",
After logout/login, the laptop's Fn brightness keys work nat.
Pop!_OS already grants the video group write access to the brightness file, so no extra udev rule is needed.
Part 3 — Hibernate (S4) when closing the lid
Why default suspend overheats
The FA608WV firmware only advertises s2idle (/sys/power/mem_sleep shows [s2idle]), not S3 deep sleep. Closing the lid puts the system into Modern Standby, where the OS may keep waking the CPU to service spurious events. Inside a backpack, the laptop runs hot.
S4 (hibernate-to-disk) on the other hand truly powers off the machine. The catch: Pop!_OS 24.04 doesn't enable hibernation by default — there's no resume swap, the kernel cmdline has no resume=, and the COSMIC desktop masks hibernate.target and sleep.target to avoid double-handling with the compositor.
Enable real hibernation
- Create a swapfile sized to your RAM (mine: 36 GB for 32 GB):
sudo dd if=/dev/zero of=/swapfile bs=1M count=36864 status=progress
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
- Find the resume offset of the swapfile:
sudo filefrag -v /swapfile | awk '/^ *0:/ {gsub(/\.\./,"",$4); print $4; exit}'
- Add kernel cmdline parameters via kernelstub (Pop!_OS uses systemd-boot):
ROOT_UUID=$(findmnt -no UUID /)
OFFSET=<from step 2>
sudo kernelstub -a "resume=UUID=$ROOT_UUID"
sudo kernelstub -a "resume_offset=$OFFSET"
- Add resume configuration to initramfs:
echo "RESUME=UUID=$ROOT_UUID resume_offset=$OFFSET" | sudo tee /etc/initramfs-tools/conf.d/resume
sudo update-initramfs -u -k all
- Unmask the systemd sleep targets (COSMIC blocks them by default):
sudo systemctl unmask hibernate.target suspend.target hybrid-sleep.target suspend-then-hibernate.target sleep.target
- Disable zram (otherwise systemd refuses to hibernate — zram is RAM, can't survive shutdown):
sudo systemctl disable --now pop-default-settings-zram.service
sudo apt remove pop-default-settings-zram
-
Wire the lid close to hibernate:
In
/etc/systemd/logind.conf:
HandleLidSwitch=hibernate
HandleLidSwitchExternalPower=hibernate
HandleLidSwitchDocked=ignore
Then sudo systemctl restart systemd-logind (or just reboot).
- Test:
sudo systemctl hibernate
The laptop should power off completely. Power back on and the session is restored, including all apps and browser tabs.
Verification
journalctl -b -1 | grep -iE 'hibern|S4' should show:
ACPI: PM: Preparing to enter system sleep state S4
PM: hibernation: Creating image
PM: hibernation: Need to copy N pages
ACPI: PM: Waking up from system sleep state S4
And last -x will show a shutdown followed by a reboot covering the entire hibernated window.
The hibernation image takes a few seconds to write (depends on RAM in use), and resume is similar. Power consumption during hibernate is the same as a full shutdown.
What's not yet fixed (FA608WV)
-
per-key RGB: the LampArray on this SKU reports
LampCount=1, so it's hardware-limited to single zone. No software fix. -
asus-armourypower-limit attributes: the kernelasus_armourydriver tries to read attributes that this BIOS doesn't expose. Spammy errors in journal but functionally harmless. Upstream kernel cleanup.
References
- Microsoft HID Usage Tables 1.5 — LampArray Section
- Original FA608WV deep dive — dev.to / @sureshsaragadam (Jan 2026)
- asus-linux/asusctl issue #700 — FA608WV
- asus-linux/asusctl issue #578 — FA608WI (sibling)
- PR on OGC asusctl (this work): https://github.com/OpenGamingCollective/asusctl/pull/147
If your laptop is a sibling SKU (FA608WI, FA608WD, etc.), check ls /sys/bus/hid/devices/ | grep 0B05 and sudo xxd /sys/bus/hid/devices/<id>/report_descriptor | grep "05 59". If both produce output, the same approach should work — possibly with a different PID. Report back, I'll update the asusctl PID whitelist.
Top comments (0)