Sitemap
Patrick McClory

Patrick McClory

Managing Principal at QuorumSystems.io

I fixed the touchscreen so I could turn it off

14 min readOct 28, 2026

--

A used $515 Dell Latitude 7455, Ubuntu 26.10 on a Snapdragon X Elite, three things that didn’t work out of the box, and one I still can’t fix.

Press enter or click to view image in full size
Photo by Dell on Unsplash

“I bought one for $515 on eBay and it’s in fantastic shape. wtf?”

That was me, looking at a Dell Latitude 7455 with a Snapdragon X Elite (X1E-80–100, 12 cores), 32 GB of RAM, a 512 GB NVMe drive and a 14-inch 2560x1600 touchscreen. Manufactured July 2025. The battery reported five charge cycles. The chassis looked like it had never left a desk.

The listing was $525 or best offer. My offer of $515 was accepted, and with shipping and tax it came to $566.75 delivered. The seller’s description was three lines, and the last one was a warning:

This unit has an ARM64 based PC please take this into consideration before bidding.

Noted. Dell still sells this exact configuration new. On Dell’s own configurator in October 2026, X Elite X1E-80–100, 32 GB, 512 GB and the QHD+ touch panel come to $2,418 before tax. That price comes with Dell’s basic onsite warranty. Mine came with ProSupport Plus, Dell’s top support tier: next-business-day onsite repair, accidental damage coverage and keep-your-drive, all transferred to me and running until July 2028. For a laptop that’s going to live on a couch, the accidental damage part is not a small detail.

I ordered it on October 3. A week later it was running my test suites. The day after that I wiped it and did the whole thing again from scratch, with encryption this time and a recorder running, to find out whether any of it was repeatable. Two weeks after that, I shipped an app to production from a campground on one charge.

I’ve installed Ubuntu on a lot of hardware. I expected ARM Linux on a Snapdragon laptop to be a weekend of misery and a working terminal at the end, if I was lucky. What I actually got was a machine that booted on the first try, then handed me three real problems: firmware that only existed in Windows, Wi-Fi that died on resume, and a touchscreen that Linux was looking for at the wrong address. The last one is the good story. A fourth, a screen whose brightness Linux can’t change, is still open.

These are field notes, not a guide that works on every 7455. Everything below is from one unit. Scripts, notes and how-tos live in the companion repo: ubuntu-latitude-7455.

Before you wipe Windows, copy the DriverStore

If you take one thing from this post, take this one.

The Qualcomm firmware Linux needs for the GPU, audio DSPs and video blocks on this laptop is model-specific, and the copy you’re entitled to is sitting in the Windows install you’re about to delete. Before wiping, I did two things in Windows:

  1. Updated the BIOS to 2.16.0 through Windows Update.
  2. Copied C:\Windows\System32\DriverStore\FileRepository to a separate USB stick with robocopy. 2,213 files, about 1.9 GiB. Source and destination counts matched.

That stick turned out to be the most important piece of hardware in the project. Don’t download these files from random sites, and don’t redistribute what you extract. Keep your own copy.

The install was the boring part

I used the Ubuntu 26.10 ARM64 Snapdragon Concept image and installed to the internal NVMe. It booted the first time on kernel 7.3.0-15-qcom-x1e. Screen, keyboard, touchpad, Wi-Fi and the NVMe all worked. GNOME came up. What I typed at that moment was short:

Fuuuuuuuck it booted.

And a few minutes later:

Like… it’s (mostly) just fucking working.

I opted into Ubuntu’s first-run telemetry on purpose. If Canonical wants data on X Elite laptops, they can have mine.

“Mostly” was doing some work in that sentence. The GPU wanted firmware, audio showed up as Dummy Output, and the touchscreen did nothing.

The firmware extractor wanted a partition I’d deleted

The GPU driver asked for qcom/x1e80100/dell/latitude-7455/qcdxkmsuc8380.mbn. Ubuntu ships qcom-firmware-extract for exactly this: point it at Windows drivers, it pulls the right files and builds a local Debian package. Version 20 has a -d flag for an explicit source directory, so I pointed it at the backup.

Failed to find windows partition

Correct. There was no Windows partition. That was the point of the -d flag.

The bug is one line. A guard near the end of the script fails if no Windows partition was discovered, even when you’ve handed it a search path. I patched a local copy, not the packaged script:

-if [ -z "$part" ]; then
+if [ -z "$part" ] && [ -z "$search_path" ]; then

The reinstall a week later hit exactly the same error with the same packaged version, so this isn’t a one-off. With the patch, extraction ran, built qcom-x1e-firmware-extracted-x1e80100-dell-latitude7455, and installed 11 files into /usr/lib/firmware/updates/qcom/x1e80100/dell/latitude-7455/. After regenerating the initramfs and rebooting, glxinfo -B reported direct rendering on an Adreno X1-85 through Mesa Freedreno 26.2.4, OpenGL 4.6. Vulkan through Turnip came later and rendered vkcube on the Adreno under Wayland for 900 frames without complaint.

Two gotchas from this stage:

  • This image uses Dracut, not update-initramfs. Most Ubuntu instructions you'll find assume the latter.
  • The extractor’s package doesn’t include qcvss8380.mbn, which the IRIS video driver asks for. It was in the DriverStore backup, so I copied it into the same directory by hand and checked the hash. IRIS encoder and decoder devices now show up. I have not proven hardware video decode under a real workload, and "the device enumerates" is not the same claim.

Speakers, a mic, and a camera that only needed its shutter opened

Once the firmware landed, Dummy Output turned into a real sound card with a HiFi profile. Speakers played, the internal mic recorded and played back, and wired headphones worked after I picked them manually the first time. After that, plug and unplug switched on their own. I didn’t go cranking amplifier gain to prove a point. Snapdragon laptop speakers have protection concerns, and a working speaker test is enough.

The camera was funnier. libcamera found the OV02E10 sensor and streamed frames at about 30 fps. Every frame was black. The investigation went fairly deep into sensor properties and capture formats before the actual cause became obvious: the physical privacy shutter was closed.

I slid it open. Video.

Then I noticed the little white light next to the lens wasn’t on. The camera was streaming and nothing on the laptop said so. The LED itself works: writing 1 to /sys/class/leds/white:camera-indicator/brightness lights it. The camera sensor's device tree node just never links to it (no leds / led-names = "privacy"), so nothing ever turns it on. I added that link to the same corrected device tree I built for the touchscreen (more on that below), and now the light follows the camera on and off. "The camera is on and nothing tells you" shouldn't ship on a laptop, so that one's filed as LP #2171357.

The picture is grainy and desaturated, and libcamera 0.7.0 complains about missing OV02E10 tuning and sensor helper support, so there’s real work left there. A Google Meet call in Firefox worked after some PipeWire portal and Snap camera fiddling. I haven’t isolated which of those changes was the one that mattered, so I’m not publishing it as a recipe.

It woke up and forgot how Wi-Fi worked

Deep suspend on 7.3.0-15 was the first thing that went properly sideways. The power button didn't wake it. Opening the lid eventually did, but Wi-Fi was dead until a reboot:

ath12k_wifi7_pci 0004:01:00.0: failed to resume core: -110

The Concept PPA (ubuntu-concept/x1e) already had 7.3.0-21 waiting. One apt upgrade later, with the old kernel kept as a fallback, I tried again. The power button woke it, Wi-Fi reconnected, and 3 of 3 pings came back. In my notes: "power button restored it too."

Since then it’s been suspended and resumed every day, lid and power button both, and Wi-Fi comes back on its own, about 17 seconds after the lid opens. That’s daily use, not a stress test, and I’m not going to name the upstream commit that fixed it until I’ve checked it’s in the build.

The touchscreen was at the wrong address

Here’s the part I’ll be telling people about.

I wanted the touchscreen working so I could turn it off. I don’t use touch on a laptop. I do want the choice, and a reliable way to turn it back on, and you can’t disable something Linux can’t find.

It worked in Windows. It worked in the BIOS setup screen. In Linux, nothing. No input device, no driver bound, a dummy regulator warning in the log that looked like a power problem and wasn't. The keyboard and touchpad threw the same warning and worked fine.

The device tree for this model declares the touchscreen on the I2C controller at a80000, address 0x09, with its HID descriptor at register 1. Linux creates an I2C client for that declaration whether or not anything is there. So 2-0009 existed in sysfs, which looked like discovery and wasn't. It was just the device tree describing what it expected.

A targeted reprobe of that client failed before it ever reached the descriptor: nothing at this address: -6. ENXIO. Nobody home.

So I asked the bus a smaller question. With the bus checked for bound or unexpected clients first, I did a one-byte read at each of the 117 non-reserved addresses from 0x03 to 0x77, on that one controller only. That's an active bus operation, and I don't recommend running it on every I2C bus you own. One address answered: 0x34. Every other address, including 0x09, returned ENXIO.

An answer isn’t an identity, so the next step was a bounded read of the HID-over-I2C descriptor at 0x34. Register 1, the one the device tree specifies, returned an invalid descriptor. Register 0 returned a valid descriptor:

Device tree says     This unit has
I2C address 0x09 0x34
HID descriptor reg 0x0001 0x0000
Vendor:Product (unknown) 1FD2:B011
Report descriptor never reached 755 bytes, Digitizer / Touch Screen

That’s a touchscreen. Same bus, same interrupt line, different address and descriptor register. My best guess is a panel or controller variant that the upstream device tree doesn’t describe yet. I don’t know the controller’s marketing name, and I don’t know how many 7455s ship with it.

The fix that didn’t fix it

The correction is three lines in one node. Everything else, including the interrupt and GPIO configuration, stays as it was:

-touchscreen@9 {
- reg = <0x09>;
- hid-descr-addr = <0x01>;
+touchscreen@34 {
+ reg = <0x34>;
+ hid-descr-addr = <0x00>;
/* interrupts, pinctrl and supplies unchanged */

I compiled a corrected DTB, dropped it in /boot under a new name, and added a separate GRUB entry in custom.cfg with a devicetree line pointing at it. The stock entry stayed untouched as the fallback. I picked the test entry by hand, it booted, and the live device tree still said touchscreen@9.

Get Patrick McClory’s stories in your inbox

Join Medium for free to get updates from this writer.

The DTB on disk was right. GRUB was configured to load it. I had selected the right entry. The running kernel had the wrong tree anyway.

The answer was one layer further down. Ubuntu’s ARM64 kernel image on this release is a PE binary wrapped in a boot stub called Stubble. Running objdump -h on the installed kernel showed 41 .dtbauto sections: device trees embedded in the kernel image itself. Stubble's documented default is to match the machine's compatible string against those embedded trees and replace whatever device tree it was handed with its own. My carefully corrected DTB got swapped for the stock one before the kernel ever saw it. I didn't trace that boot instruction by instruction, but the sections, the option string in the binary and the documentation all line up.

There’s an off switch on the kernel command line:

stubble.dtb_override=false

I added it to the test entry only and rebooted. This time the live tree said touchscreen@34, i2c_hid_of bound to 2-0034, and /proc/bus/input/devices listed hid-over-i2c 1FD2:B011. Then the real test, in my words at the time:

I can poke at the bright panel-ey thing and it highlights the things my sausage fingers touch.

Touch also survived a short deep suspend.

Where that leaves it

On the first install, persistence was shaky: one ordinary boot after I made the entry the default came up with the stock tree again, and I never pinned down why. On the clean reinstall I scripted the same three-line change, built the entry once, set it as the GRUB default, and it has come up corrected on every ordinary reboot since, through a desktop swap and the removal of GNOME. The stock Ubuntu entry is still in the menu as a fallback.

It’s pinned to 7.3.0-21. Every new kernel needs its own corrected DTB and a fresh check. Edge mapping and repeated resume with touch are still on the list.

The fix belongs upstream as a device-tree variant or some kind of detection, not as a GRUB entry on my laptop. It’s filed as LP #2171354, and the longer write-up for the Ubuntu Snapdragon folks is on Discourse.

One rule kept this honest: nothing counted until there was captured output from this machine. A controller name from an upstream description and an HID ID from someone else’s bug report both looked plausible early on. Neither was this laptop. That rule is the reason the table above exists.

Making it a dev machine

A laptop that boots is a demo. I needed one I could work on.

  • COSMIC is the desktop now. The first time, I installed Pop!_OS’s ARM64 packages as local debs to avoid adding another distro’s repo, which left updates unsolved. On the rebuild I added Pop’s repo properly, pinned low so it can only supply COSMIC and never replace an Ubuntu package. One catch: Pop’s signing key uses SHA-1 self-signatures, and apt on 26.10 rejects those. Rather than turn verification off, I pushed apt’s SHA-1 cutoff out to February 2027. That exception is system-wide and it expires, which is the point: it’s a dated reminder that this needs a real fix on Pop’s side. GNOME is gone now.
  • Chrome 155 installs from Google’s official ARM64 .deb. That still feels new. With Chrome's PipeWire camera flag on, a Google Meet call with camera, mic and screen share worked under COSMIC on the first try. YouTube plays AV1 in software (dav1d). The IRIS video block shows up as a V4L2 decoder, not VA-API, so Chrome doesn't use it.
  • VS Code 1.141.0 ARM64 installed fine, then every extension install failed signature verification. Running the bundled vsce-sign verifier directly gave the real error: No usable version of libssl was found. Ubuntu 26.10 ships OpenSSL 4, the verifier wants OpenSSL 3. Installing the packaged libssl3t64 fixed it with signature checking still on. Don't disable verification to get past this.
  • Playwright’s bundled browser install refuses Ubuntu 26.10 ARM64. Forcing the ubuntu24.04-arm64 build downloads a Chromium binary, but Ubuntu's AppArmor policy blocks its sandbox. Pointing the tests at the installed Chrome works, sandbox intact.
  • Docker 29.7.2 with Compose ran a 57-test integration suite against a real ephemeral ARM64 Postgres/pgvector container in about 16 seconds. A 55-page static site built clean. kubectl, Helm, Argo CD, Velero and friends all have ARM64 binaries and run.

That’s the point where I stopped thinking of it as an experiment.

Then I wiped it and did it again

A setup you can only do once isn’t a setup. It’s an accident you got lucky with. So the day after it became useful, I erased the drive and rebuilt it from a written runbook, with a small capture script timestamping every phase and recording every command.

This time I used the plain Ubuntu 26.10 daily image instead of the Concept image, with full-disk encryption and a passphrase. The daily image boots a generic kernel, so I added the Concept PPA myself to get 7.3.0-21-qcom-x1e. Everything else followed the notes.From wipe to finished dev machine took about six hours of wall-clock time, breaks and hardware testing included. The clock breaks the main stretches down like this: 15 minutes from BIOS check to first login, encrypted install included. About 65 minutes for firmware, kernel, a suspend test and the touchscreen fix. About 90 minutes for COSMIC, Chrome, VS Code, dev tooling and runtimes.

Every bug came back on cue: the extractor guard, the missing video firmware, the touchscreen at 0x34, Stubble swapping out my device tree, the VS Code OpenSSL error. That's the best evidence I have that these are real and not something I did to the first install.

Two new things turned up. sg and newgrp are gone from 26.10, so the classic "use the docker group without logging out" trick doesn't work; sudo -u $USER -g docker does the same job. And the Pop repo signing problem above, which the first install had dodged by using local debs.

Then I took it camping

The real test wasn’t a benchmark.

A couple of weeks after the rebuild I sat down in the middle of a campground with this laptop, unplugged, and built an entire app: Marked Words, buzzword bingo minus the card. Start to deployed in production took about four hours, with some AI help. When I closed the lid the battery was at about 45%.

The battery number isn’t what stuck with me. What stuck with me is that it never dragged. Plenty of laptops feel fine on the charger and go soft the moment you pull the plug. This one kept up with me the whole time, on battery, on a desktop environment I installed from someone else’s distro, on a kernel from a PPA.

And the screen looked beautiful outside. That’s lucky, because I still can’t change its brightness.

What still isn’t verified

I’d rather list this than have you find it the hard way.

  • Screen brightness, the one I actually want. Linux can’t change it: the backlight device reports a maximum brightness of 0. The kernel also logs Unknown panel LGD 0x0754 on every boot, and for a while I was sure that was the cause, because it would rhyme so nicely with the touchscreen story. Then I found LP #2159387, a Dell Inspiron with the same max_brightness 0 on a panel the driver does recognize. So probably not. The lead I want to try next is a power-rail change in the device tree that helped another Snapdragon laptop. Untested.
  • IRIS hardware video decode and encode under a real workload. The devices enumerate; Chrome doesn’t use them.
  • USB4, external displays, docks. I don’t have a USB-C monitor to test with.
  • Fingerprint reader. It works at the COSMIC login screen and lock screen. I turned it off anyway, because a fingerprint login leaves the keyring locked and asking for my password. That’s a Linux-wide annoyance, not a Snapdragon one.
  • Standby drain over long suspends.
  • Battery runtime. The campground session is one data point, not a measurement.
  • Battery health. Through both installs it reported 46.43 Wh full against a 54 Wh design, about 86%, at five cycles. Two weeks of real use later it reports 48.71 Wh, about 90%. That looks more like the gauge calibrating than the battery wearing, which is what I hoped. On battery it has also claimed to be charging at 0 W with nothing plugged in. Odd, not a diagnosis.
  • Audio after resume. On the first install, resume sometimes logged -61 errors from the WSA speaker codec. Once the speaker came back muted; once it vanished until a reboot. I haven't seen it on the rebuild.
  • Touch across kernel upgrades, plus edges.
  • Camera image quality.

If you want to do this

  1. Update the BIOS from Windows first.
  2. Copy C:\Windows\System32\DriverStore\FileRepository to separate media and verify the file count. Keep it.
  3. Install Ubuntu 26.10 ARM64. The Concept image comes with the Concept PPA set up; the daily image installs fine with encryption but you’ll add ppa:ubuntu-concept/x1e and linux-qcom-x1e yourself.
  4. Run qcom-firmware-extract -d against your backup. If version 20 complains about a missing Windows partition, the one-line guard above is why.
  5. Copy qcvss8380.mbn into the model firmware directory yourself if you want IRIS.
  6. Regenerate the initramfs (Dracut on this release) and reboot.
  7. Be on the newest Concept PPA kernel before trusting suspend.
  8. If your touchscreen is dead, check what actually answers on the bus before editing anything. If you test a corrected device tree, check the live tree after boot, and remember stubble.dtb_override=false.
  9. Install libssl3t64 before VS Code extensions.
  10. Write it down as you go. The second install took an afternoon because the first one left notes.

The details, scripts and the read-only collectors I used are in ubuntu-latitude-7455.

About turning the touchscreen off

The whole reason I chased the touchscreen was so I could turn it off. I said so at the top of that section. I meant it.

It’s still on.

I hate fingerprints on a laptop screen. I still do. But every so often I’m slumped on the couch, a dialog pops up, and I reach over and poke the button instead of hunting for the touchpad. It works every time. Apparently I spent an evening arguing with a boot stub for exactly that.

The off switch is still on the list. It’s just not near the top anymore.

--

--