A 2017 MacBook Pro is a genuinely nice piece of hardware. Good screen, good chassis…. of course not my favorite keyboard (butterfly), but it also happens to be a machine Apple has kind of stopped caring about, which makes it exactly the kind of laptop that deserves a second life…so I put Omarchy on it:

The install itself was unremarkable. Omarchy is Arch underneath with Hyprland on top, and it does the boring parts well. I had a desktop in under 5 minutes.
Then I started actually using it, and the Apple-ness began to surface.
None of what follows is Omarchy’s fault. Every single problem here is a 2017 Mac being a 2017 Mac on a kernel that owes it nothing. But if you’re doing this same project these are five walls I hit, perhaps they will help you 🙂.
1. No Wi-Fi, because NVRAM remembers
First boot, no wireless. The card is a Broadcom BCM4350, which is not uncommon ground on Linux brcmfmac handles it.

Except it didn’t. The hardware was there in lspci, the driver loaded, and nothing happened.
The fix is an NVRAM reset. Apple stashes hardware state in NVRAM that survives wiping the disk entirely, and some of it leaves the Wi-Fi card in a state Linux can’t talk to. Power off, then hold Cmd + Option + P + R through the startup chime.
That’s it. Wi-Fi came up on the next boot and has been fine since.
2. Scrolling backwards
Easiest problem of the batch. Omarchy ships with natural scrolling off – I’ve been on macOS long enough that reversed is correct and everything else feels broken.
Hyprland config lives in ~/.config/hypr/, and user overrides go in input.lua:
hl.config({ input = { natural_scroll = true, touchpad = { natural_scroll = true, }, },})
Worth noting why both are set. Run hyprctl devices on this machine and the trackpad shows up as apple-spi-touchpad under mice, not under touchpads. It talks over SPI rather than I²C, so the touchpad only setting might not catch it it (ask me how I know).
Hyprland hot reloads on save. hyprctl configerrors to confirm you didn’t make a typo.
3. Sound (buckle up)
No audio at all. This is a known issue on Apple Omarchy installs.

The codec is a Cirrus Logic CS8409. To see the actual problem, pull the kernel module apart and look at which machines it knows about:
$ strings snd-hda-codec-cs8409.ko | grep CS8409_CS8409_BULLSEYECS8409_WARLOCKCS8409_CYBORGCS8409_DOLPHINCS8409_ODIN
Those are all Dell machines. Every one of them.
There is no Apple entry in that table. My subsystem ID is 0x106b3300 and the driver has never heard of it, so it falls through to the generic parser. From the kernel log:
autoconfig for CS8409: line_outs=2 (0x24/0x25) type:speaker speaker_outs=0
Zero speaker outputs. Apple wires the speaker amplifiers off the codec’s I²C bus and Linux never initializes them. The codec accepts audio and throws it into a void.
That’s why everything looked healthy. Nothing in the userspace stack was wrong. The sound was going nowhere three layers below anything PipeWire can see.
The fix
An out of tree driver: davidjo/snd_hda_macbookpro. It patches the CS8409 driver with the Apple-specific amp initialization. Its patch set contains 0x106b3300 by name, which is how you know you’re in the right place.
The one part worth slowing down on is kernel headers. You need them matching your running kernel exactly. On a rolling distro that’s not a given – my repos had 7.2.4 while I was booted into 7.2.3, and a straight pacman -Sy linux-headers would have handed me headers for a kernel I wasn’t running.
The Arch archive solves this. Grab the exact version, verify it, install it:
# archive.archlinux.org/packages/l/linux-headers/pacman-key --verify linux-headers-<version>.pkg.tar.zst.sigsudo pacman -U linux-headers-<version>.pkg.tar.zst
Then build, and drop the module where it outranks the stock one:
sudo install -D -m 644 snd-hda-codec-cs8409.ko \ /lib/modules/$(uname -r)/updates/codecs/cirrus/snd-hda-codec-cs8409.kosudo depmod -a
Reboot, and the log finally admits what it’s doing:
snd_hda_intel: Primary patch_cs8409 NOT FOUND trying APPLE
Sound. Not perfect sound only the CS42L83 path gets exposed, and headphones are the least tested part of that driver upstream. But music comes out of the speakers which is the bar I set for success.
4. A DKMS module that was never doing anything
Installing those headers had a side effect: Arch’s DKMS hook woke up and immediately failed to build macbook12-spi-driver.
Which was confusing because my keyboard and trackpad worked fine.
They work because applespi is in the kernel now. That DKMS package is a leftover from when it wasn’t, and it had been sitting there as added: never built, never loaded, doing nothing except throwing an error on every kernel update.
sudo pacman -R macbook12-spi-driver-dkms
Keyboard and trackpad kept working, because they were never using it.
Check your dkms status when you inherit a machine or follow an old guide. Stale DKMS entries are noise that looks like signal, and one day you’ll waste an hour on that error while chasing something unrelated.
5. Close the lid, lose the laptop
This was the one that actually really bothered me.
Close the lid. Open it… surprise! Black screen, no backlight, no response. Hold power, reboot, lose whatever you were doing.
It looked exactly like the machine was shutting down on suspend. The journal seemed to agree every log ended at:
PM: suspend entry (deep)
Nothing after… then a cold power cycle.
My first instinct was the sleep mode. Intel Macs have a reputation for S3 trouble, so I switched to s2idle and tried again.
Identical failure. Which was annoying at the time, but it was information: if both sleep modes die the same way, the sleep mode isn’t the problem.
pm_test, which I didn’t know about and now love
The kernel has a debugging facility for precisely this, and it’s great. /sys/power/pm_test runs the suspend path down to a chosen depth, then automatically resumes about five seconds later: no real sleep, no gamble.
echo 1 | sudo tee /sys/power/pm_debug_messagesecho 1 | sudo tee /sys/power/pm_print_timesecho 0 | sudo tee /sys/power/pm_async # serialize, so timings name a deviceecho devices | sudo tee /sys/power/pm_testsystemctl suspend
Setting pm_async=0 is the part that makes this useful. Serialized device suspend means the timings point at one device instead of a blur.
Device phase passed in 366ms. So I went a level deeper to platform, and there it was:
PM: noirq resume of devices complete after 83499.626 msecs
The machine was never hanging. It was stalling? Long enough that I’d given up and held the power button every single time (I don’t think it was really ever coming back, I waited 5 mintutes once).
With per device timings on, the culprit wasn’t subtle:
| Device | noirq resume |
|---|---|
pcieport 0000:05:02.0 — Alpine Ridge TB3 bridge | 72.5 s |
pcieport 0000:04:00.0 — TB3 bridge | 5.8 s |
thunderbolt 0000:06:00.0 — TB3 NHI | 3.8 s |
Thunderbolt. An Intel JHL6540 controller whose PCIe bridges are never coming back 😆.
The fix is one kernel parameter
pcie_port_pm=off. On Omarchy that means /etc/default/limine:
KERNEL_CMDLINE[default]+=" pcie_port_pm=off"
Then sudo limine-update.
One gotcha: this setup boots a unified kernel image, so the cmdline gets baked into the EFI binary. It will not show up in limine.conf, and if that’s where you look to confirm your change, you’ll think it silently failed. Check the UKI itself:
sudo objcopy -O binary --only-section=.cmdline \ /boot/EFI/Linux/omarchy_linux.efi /dev/stdout
Results, same test, same sleep mode, one parameter different:
| noirq resume | |
|---|---|
| Before | 83,499 ms |
| After | 1,159 ms |
72× faster. Real lid cycles resume in about a second now.
The tradeoff is that PCIe ports no longer power-manage themselves, so idle draw goes up a little. Against a laptop that wouldn’t wake up….I’ll take it.
The one still on my list
The audio driver isn’t under DKMS yet.
Which means the next kernel update loads a module built for the wrong kernel, and my speakers go quiet again with no error message explaining why. I’ll have forgotten all of this by then. That’s genuinely the worst kind of bug one you already solved, with all the context gone.
So it’s written down in a file on that machine with the exact commands. Packaging it properly for DKMS is the actual answer and it’s next.
Was it worth it
Yes!
Wi-Fi took thirty seconds. Scrolling took one config block. The other three were real debugging, but they were tractable debugging, the kind where the system tells you what’s wrong if you know which question to ask. pm_test alone turned a problem I’d written off as “suspend is broken on this hardware” into a single wrong by default kernel parameter.
Worth saying honestly: I worked through most of this with Claude Code (comes out of the box in Omarchy like a bunch of other useful tools). I didn’t really use claude for writing the config but for the diagnostic stuff. Things like:
- Pulling the quirk table out of a compressed kernel module to prove the Apple entry didn’t exist.
- Knowing
pm_testwas the right tool and thatpm_async=0is what makes its output readable. - Catching that my repos had drifted a kernel version ahead of what I was booted into, before that turned a 30 minute troubleshooting session into a 3 hour one.
That’s the part that would’ve taken me a weekend of forum archaeology in the past. The fixes themselves were mostly one line each. Finding out which line was the whole job.
Old Apple hardware and Linux is still a negotiation. But it’s a negotiation you can win, and the machine on the other side of it is a good laptop again.
Aside: If you’re running Omarchy or plain Arch on an Intel Mac, I’d like to hear how it went! especially if you’ve got the CS8409 packaged properly for DKMS, because I’d rather steal your approach than invent one 😎.
Leave a Reply