One morning I opened my HP ZBook Ultra G1a and the trackpad was dead. Keyboard fine. Touchscreen fine. External mouse fine. Trackpad: nothing. Twelve hours earlier it had worked. I hadn’t changed anything. Welcome to bleeding-edge silicon on Linux.
This is the story of chasing the bug to ground. Spoiler: the root cause is that the in-development AMD ISP4 camera driver stack on Strix Halo wedges the AMD I²C subsystem during boot, which prevents the Synaptics trackpad from probing. The camera driver isn’t even upstream yet – so the modules that are breaking things aren’t providing any benefit. Blacklist them and the trackpad comes back.
I want to be clear up front: I used Claude Code heavily for this entire analysis. Not to write prose – to do the systems work. It pulled dmesg slices, correlated sysfs state across multiple buses, diffed initramfs contents between kernel versions, reasoned about probe-ordering races, and kept me from heading down at least one wrong rabbit hole for too long. A few years ago this kind of debugging was a long evening of man pages and mailing list archaeology. With a good agent, disciplined by skills and security rules, it turned into a focused afternoon. More on that at the end.
The System
- Platform: HP ZBook Ultra G1a 14" Mobile Workstation
- CPU: AMD Ryzen AI Max+ PRO 395 (“Strix Halo”)
- OS: Ubuntu 24.04 LTS
- Kernel: 6.17.0-1017-oem (
linux-oem-24.04d)
TL;DR – The Fix
If you’re here because Google dropped you into this post and you just want your trackpad back, stop reading and run this:
sudo tee /etc/modprobe.d/blacklist-amd-isp.conf <<'EOF'
blacklist i2c_designware_amdisp
blacklist amd_isp4
blacklist amd_capture
blacklist pinctrl_amdisp
EOF
sudo update-initramfs -u -k $(uname -r)
sudo reboot
The rest of this post is the diagnostic path. The wrong turns are instructive – so is the fact that Claude caught some of them before I did.
First Wrong Turn: Chasing the ELAN
First instinct, obviously, was to look at what libinput saw:
sudo libinput list-devices
There was a device called ELAN2513:00 04F3:4356 with Size: 308x191mm and Capabilities: touch. I assumed this was the trackpad. It wasn’t. It was the touchscreen. The 308x191mm dimensions are the 14" display’s diagonal, not a trackpad surface.
I didn’t catch that right away. I went off trying to convince the system to treat the ELAN as a touchpad – custom udev hwdb rules, unbind/rebind, all the usual stuff:
# What I tried -- this was wrong
sudo tee /etc/udev/hwdb.d/61-elan-zbook-g1a.hwdb <<'EOF'
evdev:name:ELAN2513:00 04F3:4356:*
ID_INPUT_TOUCHSCREEN=0
ID_INPUT_TOUCHPAD=1
ID_INPUT_MOUSE=1
EOF
sudo systemd-hwdb update
sudo udevadm trigger --subsystem-match=input --action=change
Do not do this. This disables the touchscreen without giving you a trackpad, because the ELAN really is a touchscreen. The firmware correctly reports INPUT_PROP_DIRECT. Forcing it to be classified as a touchpad is fighting the hardware.
I confirmed this empirically: touching the screen did nothing (because I’d disabled it) while the cursor still moved (driven by the external mouse). To back out the bad rule:
sudo rm /etc/udev/hwdb.d/61-elan-zbook-g1a.hwdb
sudo systemd-hwdb update
sudo udevadm trigger --subsystem-match=input --action=change
Lesson: check what a device actually is before trying to make it behave like something it isn’t. Claude had actually flagged that the ELAN dimensions matched the display, not a trackpad, but I pushed past it because the device tree seemed to fit my theory. My mistake, not the agent’s.
Finding the Actual Trackpad
The correct move is to stop looking at libinput (which only sees devices with drivers attached) and enumerate every I²C device on the system:
ls /sys/bus/i2c/devices/
Buried in the list:
i2c-ELAN2513:00 # touchscreen
i2c-SYNA3133:00 # *** the actual trackpad ***
SYNA3133 is Synaptics. That’s the real trackpad. And notice – it doesn’t appear in libinput output at all, because no driver is bound to it.
Cross-check with HID:
ls /sys/bus/hid/devices/
0003:045E:0823.0002 0018:04F3:4356.0001 0020:1022:0001.0005
0003:045E:0823.0003 0020:1022:0001.0004
Three HID devices: Microsoft mouse (045E), the ELAN touchscreen (04F3:4356), and the AMD sensor hub (1022). No Synaptics. The trackpad has a sysfs presence on the I²C bus but no HID driver has attached.
The smoking gun is in its sysfs directory:
cat /sys/bus/i2c/devices/i2c-SYNA3133:00/waiting_for_supplier
0
A value of 0 in waiting_for_supplier means “yes, still waiting for supplier.” The Synaptics device is blocked waiting for its parent – the I²C bus controller – to come up. It never does.
I did not know what waiting_for_supplier meant when I first saw it. I just knew it looked wrong – a file named that, containing 0, on a device with no driver attached, felt like it was trying to tell me something. So I asked Claude. It explained the semantics (counterintuitively, 0 means “still waiting”) and what that implied: the device is fine, its driver would bind, but the upstream supplier – the I²C bus controller – never came up. That reframed the whole problem from “the trackpad isn’t working” to “the bus controller isn’t working,” which is where the rest of the debugging had to go.
I could have eventually found that in a Stack Exchange answer or a kernel mailing list thread. It would have taken me a lot longer. Noticing that the file looked weird was mine. Turning “that looks weird” into “here is what it means and here is the next thing to check” was the agent’s, and it happened in seconds.
The Real Root Cause: A Wedged I²C Controller
The AMD Ryzen AI Max+ has four I²C controllers on the SoC, exposed as ACPI devices AMDI0010:00 through AMDI0010:03. Each drives a different subset of peripherals:
ls -l /sys/bus/i2c/devices/ | grep AMDI0010
AMDI0010:00→ drives the SYNA3133 trackpadAMDI0010:01→ audio codecAMDI0010:02→ ELAN2513 touchscreenAMDI0010:03→ power-sequencing ICs
Search dmesg:
sudo dmesg | grep -E "AMDI0010|i2c_designware"
[ 1.775670] i2c_designware AMDI0010:00: controller timed out
[ 1.800913] i2c_designware AMDI0010:00: timeout in disabling adapter
[ 1.821699] i2c_designware AMDI0010:00: timeout waiting for bus ready
Every boot: AMDI0010:00 – the controller the trackpad sits behind – times out 1.7 seconds into boot and never recovers. No communication on that bus means no trackpad driver. No trackpad driver means no pointer events. Dead trackpad.
Confirm the bus is genuinely dead and not just slow by looking at interrupt counts:
grep "AMDI0010" /proc/interrupts
3: 0 0 8 0 ... IO-APIC 3-edge AMDI0010:00
4: 0 0 0 15843 ... IO-APIC 4-edge AMDI0010:01
6: 0 0 0 0 50837 ... IO-APIC 6-edge AMDI0010:02
10: 0 0 0 0 0 ... IO-APIC 10-edge AMDI0010:03
AMDI0010:00 got 8 interrupts (probably from its reset sequence), then zero. The other controllers are firing thousands. That bus is not delivering I/O.
What Didn’t Work
A lot of things didn’t work, and if you’re searching for this bug you’ll probably try them too. Listing them here so you don’t waste the time.
BIOS update via fwupd:
fwupdmgr refresh --force
fwupdmgr get-updates
Already on the latest release from HP. No change.
Full EC drain – shutdown, unplug AC, hold power button 30 seconds, replug, boot. No change.
Driver unbind/rebind – my hope was the controller just needed a kick:
echo "AMDI0010:00" | sudo tee /sys/bus/platform/drivers/i2c_designware/unbind
sleep 2
echo "AMDI0010:00" | sudo tee /sys/bus/platform/drivers/i2c_designware/bind
sudo dmesg | tail
Fresh timeout on every rebind. Once wedged, it stays wedged for the rest of the session.
Unloading amd_pmf – the AMD Platform Management Framework was throwing amd_pmf_invoke_cmd hogged CPU warnings in dmesg and I suspected it was contending for the bus:
sudo modprobe -r amd_pmf
# retry the rebind
No change. Ruled out.
Stripping the kernel command line to remove custom parameters as a variable:
sudoedit /etc/default/grub
# Set: GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
sudo update-grub
sudo reboot
Same timeout. Not a cmdline issue.
Booting the previous OEM kernel via GRUB’s advanced-options menu:
sudo awk -F"'" '/menuentry |submenu /{print $2}' /boot/grub/grub.cfg
sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 6.17.0-1012-oem"
sudo reboot
Same behavior on the older kernel. Not a kernel regression.
Intel LPSS blacklist – a CachyOS user had posted this as a workaround for a similar-looking bug. I tried it. Didn’t help. A post-mortem diff of initramfs contents proved the Intel LPSS modules weren’t even loading on my system. Red herring.
Claude was actually useful here too: each failed attempt, it helped me form the next hypothesis and immediately suggested the verification step. When the Intel LPSS theory didn’t pan out, it was the one that said “let’s diff the initramfs to prove the modules weren’t there to begin with” rather than letting me keep pulling on that thread.
The Breakthrough
Looking at dmesg again, more carefully this time, a second pattern surfaced:
sudo dmesg | grep -iE "amd_isp|i2c_designware"
[ 45.123593] amd_isp_i2c_designware: timeout while trying to abort current transfer
[ 45.148993] amd_isp_i2c_designware: timeout in disabling adapter
[ 1174.974611] amd_isp_i2c_designware: timeout while trying to abort current transfer
A second AMD I²C controller – this one owned by the ISP (Image Signal Processor) – was also timing out. The ISP is the chunk of silicon that processes frames from the built-in camera sensor. Its internal I²C bus is driven by i2c_designware_amdisp.
Two AMD I²C controllers wedged at the same time on the same SoC isn’t a coincidence. It’s a strong signal that something in the ISP driver stack is interfering with the shared I²C plumbing – probably via shared pinctrl, ACPI power sequencing, or a probe-time race in the i2c-designware core code that both instances share.
This is where Claude really earned its keep. It knew that the ISP4 camera stack is brand new, still in upstream review (as of April 2026, the v7 patchset was posted in December 2025 and hasn’t landed), and known to be incomplete. More importantly, it synthesized that context with the dmesg evidence and proposed the right hypothesis: two buses from the same driver family timing out around the same time means the in-development driver is wedging the shared subsystem. If I’d been poking at this alone with man pages, I’d have figured it out eventually – but the connection between “ISP4 isn’t upstream yet” and “AMDI0010:00 is also wedged” isn’t one I would have made quickly.
The Fix
Since the ISP4 camera stack doesn’t actually work on Linux yet, blacklisting it is lossless:
sudo tee /etc/modprobe.d/blacklist-amd-isp.conf <<'EOF'
# HP ZBook Ultra G1a: amd_isp_i2c_designware wedges the AMD I2C subsystem,
# preventing the SYNA3133 trackpad on AMDI0010:00 from probing.
# The ISP4 camera driver isn't upstream as of April 2026, so these modules
# are not providing any functionality -- blacklisting is lossless.
blacklist i2c_designware_amdisp
blacklist amd_isp4
blacklist amd_capture
blacklist pinctrl_amdisp
EOF
sudo update-initramfs -u -k $(uname -r)
sudo reboot
After reboot:
cat /sys/bus/i2c/devices/i2c-SYNA3133:00/waiting_for_supplier 2>&1
sudo dmesg | grep -iE "AMDI0010|SYNA3133|i2c_designware"
ls /sys/bus/hid/devices/
sudo libinput list-devices | grep -iE "syna|touch" -A 3
Expected state:
waiting_for_supplierreports “No such file or directory” – the file is removed once the device binds- A new Synaptics HID device is present
libinput list-devicesshows a proper touchpad entry with real values for tap-to-click, scroll methods, etc. – not then/aplaceholders that mean “device isn’t classified as a touchpad”
Trackpad back. Clean boot.
What the Four Blacklisted Modules Do
Worth knowing exactly what you’re turning off.
i2c_designware_amdisp– the AMD ISP-internal I²C controller driver. The ISP has its own private I²C bus (separate from the fourAMDI0010:*controllers) that it uses to configure and read the image sensor. This is the driver that was timing out in dmesg and almost certainly the direct cause of the cascade.amd_isp4– the userspace-facing camera pipeline driver for Strix Halo’s ISP4. This is what would expose the built-in webcam as a/dev/video*device. Not upstream as of April 2026; the v7 patch series missed the 6.19 merge window.amd_capture– the frame-capture glue between the ISP and V4L2. Takes frames the ISP has processed and delivers them to userspace video buffers. Useless without the rest of the stack.pinctrl_amdisp– the pin-controller driver for the GPIO pins used by the ISP (sensor reset, power-enable, strobe signals). Multiplexes those pins for ISP use instead of generic GPIO.
The four form a tower: amd_capture sits on top of amd_isp4, which needs i2c_designware_amdisp and pinctrl_amdisp underneath. Blacklisting any one of them takes the stack down; blacklisting all four is belt-and-suspenders to prevent autoloading from any dependency path.
Cost of the workaround: the built-in 5MP webcam doesn’t work. Since the ISP4 driver isn’t upstream, the camera wasn’t working before the fix either. No functional regression. A USB webcam (e.g., Logitech C270) is a fully functional substitute.
When to Remove the Blacklist
Once the ISP4 driver lands upstream and Canonical ships an OEM kernel that includes the working version:
sudo rm /etc/modprobe.d/blacklist-amd-isp.conf
sudo update-initramfs -u -k $(uname -r)
sudo reboot
Test both trackpad and webcam. If both work, you’re done. If the trackpad breaks again, restore the blacklist – the upstream driver still has the I²C-subsystem interaction issue and you’re back to waiting.
References to watch:
- Phoronix ISP4 coverage: phoronix.com/?s=AMD+ISP4
- linux-media mailing list patch threads for ISP4
- The
linux-oem-6.xsource package changelog (apt-get source linux-oem-24.04dand readdebian/changelog)
Why This Broke “Overnight”
I originally wrote this section claiming the bug was a probe-ordering race. That was wrong, and the evidence was sitting right in front of me: I rebooted this machine tens of times during the debug session, on two different kernels, and it wedged the exact same way every single time. A real race would have resolved to the good side at least occasionally. Deterministic failure on every boot is not a race. It’s a deterministic bug.
Calling it a race was me reaching for an explanation that fit the “worked yesterday, broken today” narrative without squaring it against the reboot evidence. Claude didn’t catch that either. Worth flagging – an agent will happily elaborate on a theory you seed it with. Checking the theory against all of the evidence, not just the evidence that prompted it, is still on you.
So what actually changed overnight? My first guess was that an unattended-upgrades run had regenerated initramfs with different module contents. I ruled that out with a diff:
sudo lsinitramfs /boot/initrd.img-6.17.0-1012-oem | grep -iE "isp|amdisp" | sort > /tmp/old-initrd
sudo lsinitramfs /boot/initrd.img-6.17.0-1017-oem | grep -iE "isp|amdisp" | sort > /tmp/new-initrd
diff /tmp/old-initrd /tmp/new-initrd
Result: same ISP modules in both initramfs images. “New modules got pulled in” was wrong.
Given the deterministic behavior, the honest set of candidate explanations is:
- I was already broken and didn’t notice. I use an external mouse most of the day when I’m at the desk. It’s entirely plausible that the trackpad had been dead for some number of boots before I tried to use it and noticed. This fits the evidence best: the ISP driver deterministically wedges
AMDI0010:00on this hardware, it always has (on both 6.17.0-1012-oem and 6.17.0-1017-oem), and I only discovered the state when I reached for the trackpad without the mouse plugged in. - A firmware or persistent-state change flipped a deterministic switch. Something at a layer below the kernel (EC state, ACPI tables read from firmware, EFI variables) might have transitioned into a state where the ISP driver now deterministically wedges the bus, when previously it deterministically didn’t. An EC drain didn’t restore the good state, which argues against this – but doesn’t rule out firmware-level state that survives an EC drain.
- An update changed something I didn’t diff. I diffed initramfs contents but not, say, firmware blobs, microcode, or the list of modules autoloaded by udev outside initramfs. A change there could have shifted the bug into a state where the ISP driver now always loads and always wedges.
Of the three, (1) is the one that actually fits the evidence I have. I probably didn’t notice the trackpad was dead for some number of boots because I was using the mouse. That is, as they say, the boring answer – and the right one.
Either way, the blacklist is the correct fix: if the ISP driver deterministically wedges the bus every time it loads, don’t load it.
Diagnostic Steps, In Order
Consolidated for future-me and anyone else hitting this on Strix Halo hardware.
1. Check what’s actually on the I²C bus:
ls /sys/bus/i2c/devices/ | grep -iE "syna|elan|goodix|alps"
2. Check if the trackpad is stuck waiting for its supplier:
cat /sys/bus/i2c/devices/i2c-SYNA3133:00/waiting_for_supplier
Value of 0 means yes, still waiting – the bus controller never came up.
3. Find the bus controller and check its timeouts:
ls -l /sys/bus/i2c/devices/i2c-SYNA3133:00 | grep -o 'AMDI0010:[0-9][0-9]'
sudo dmesg | grep "AMDI0010"
4. Check interrupt activity per controller:
grep "AMDI0010" /proc/interrupts
A controller with far fewer interrupts than its siblings is wedged.
5. Look for ISP I²C timeouts – the fingerprint of this specific bug:
sudo dmesg | grep -iE "amd_isp|i2c_designware_amdisp"
If you see amd_isp_i2c_designware: timeout lines, you’ve got the same bug.
6. Apply the blacklist, regenerate initramfs, reboot – see TL;DR at the top.
7. Verify the fix:
ls /sys/bus/hid/devices/
sudo libinput list-devices | grep -iE "syna|touchpad" -A 5
You should see a new Synaptics HID device and libinput should classify it as a proper pointer with real configuration values.
A Word About Using Claude Code for Kernel Debugging
I want to close on this because it’s the part I think people underestimate.
Linux kernel debugging has always had a steep competence floor. You need to know where to look – /sys/bus/*/devices, /proc/interrupts, the right dmesg filters, the relationship between ACPI device IDs, driver bind files, udev, initramfs. That’s not hard if you’ve been doing it for twenty years. It’s pretty hard if you haven’t. And even if you have, the cognitive load of holding all four I²C controllers, their drivers, their ACPI parents, and their downstream consumers in your head at once while also reading dmesg is real.
What Claude Code gave me in this session was:
- Pattern recognition across layers. It correlated symptoms on the I²C bus, the HID bus, and the dmesg timeline in a way I could have done but would have taken meaningfully longer to do.
- A memory of state. Every time I tried a new hypothesis – rebind, modprobe, blacklist – it remembered what we’d already ruled out. I didn’t have to keep my own mental stack of “what have I tried already.”
- Domain context I didn’t have. I didn’t know the ISP4 driver was still out of tree. It did. That single piece of context is what flipped the debugging from “chase the I²C timeout” to “who else is on this bus that shouldn’t be?”
- A check on my own bad theories. When I wanted to keep pushing on the Intel LPSS workaround or the ELAN-as-touchpad theory, it pushed back with evidence. I still had to decide, but I got to decide with better information.
Honestly, it also wrote most of this post. That’s worth being direct about. I had a messy debug session full of dead ends, half-formed theories, and commands I’d half-forgotten I’d run. Organizing that into a coherent narrative – with the wrong turns in the right order and the working path cleanly separated from the red herrings – would have been a significant piece of work from my own notes. The prompt/reply history from the debug session was its own kind of primary source, and Claude had it all. Turning that into a readable post was a much easier task for the agent than for me after the fact. I directed it, pushed back on claims that didn’t fit the evidence (see the race-condition correction above – that was a real back-and-forth), and wrote the final voice. But the structural work of “take six hours of flailing and produce a clean write-up” is largely the agent’s.
What it did not do was run anything destructive. The skills and security rules I have loaded (see my dot-agents repo) gate that behavior. Every sudo command in this post was my call, not the agent’s.
The workflow is what makes this work. The model is table stakes. If you’re doing hard systems debugging on Linux and you’re still doing it without a coding agent tuned for your style of work, you’re making the job harder than it has to be. The agent isn’t going to replace the expertise – you still have to know what waiting_for_supplier means when it says 0 – but it will dramatically compress the time between hypothesis and evidence.
Takeaways
- Don’t assume the device you’re looking at is the device you think it is. I burned hours on the ELAN touchscreen thinking it was the trackpad. Enumerating I²C devices earlier would have surfaced SYNA3133 immediately.
/sys/bus/i2c/devices/<device>/waiting_for_supplieris the fastest way to diagnose “driver never attached” problems on I²C devices. If it’s there and says0, the driver isn’t coming up because its bus controller didn’t. That tells you exactly where to look next.- Interrupt counts in
/proc/interruptstell you whether hardware is actually responsive. A device delivering zero interrupts while its siblings are firing thousands is dead, not slow. - New hardware, new bugs. Strix Halo is bleeding-edge silicon and its Linux enablement is incomplete. If something doesn’t work on this hardware, the answer is frequently “a specific in-development driver is interfering” rather than “configuration is wrong.”
- “Worked yesterday, broken today” is not always what it looks like. My first theory was a probe-ordering race. The actual explanation was almost certainly that the trackpad had been broken for some number of boots before I noticed, because I use an external mouse most of the time. Before you invent a clever theory, check whether the simplest one – “I just didn’t notice” – fits the evidence. A real race would resolve the good way sometimes across tens of reboots. Mine never did.
- Verify agent-generated hypotheses against all the evidence, not just the evidence that prompted them. Claude helped me enormously on this bug, but it also happily elaborated on my race-condition theory without pushing back on the fact that tens of reboots had all failed identically. That’s still on me to catch.
- Use the agent. Seriously. The difference between a tuned Claude Code setup and no agent at all, on a problem like this, is hours. Possibly a day. I’m not going back.
If this saved you from spending a weekend on the same bug, drop me a note. And if the ISP4 driver lands upstream before I notice, please tell me – I’d like my webcam back.