Getting a Samsung Galaxy Book4 Ultra (NP960XGL) fully working under CachyOS — webcam, fingerprint, battery limit, NPU, suspend.
Everything here was tested on one machine: 960XGL / NP960XGL-YG1BR, Core Ultra 9
185H, Intel Arc iGPU + RTX 4070 Max-Q, kernel linux-cachyos 7.1.6, KDE Plasma 6.7 on
Wayland, dual-booting Windows. Other Book4 variants and other Arch derivatives will
likely need adjustments — the reasoning is documented so you can adapt it, not just
the commands.
| Subsystem | State | Needs work? |
|---|---|---|
| Speakers (4× MAX98390) | ✅ Works | No — kernel ≥ 7.0 binds them natively |
| Microphones (DMIC) | ✅ Works | No |
| Wi-Fi / Bluetooth (AX211) | ✅ Works | No |
| Platform keys, keyboard backlight, perf profiles | ✅ Works | No — samsung-galaxybook |
| Suspend on lid close | ✅ Works | No |
| Battery charge limit | ✅ Works | Small config |
| Webcam (OV02C10 / IPU6) | ✅ Works | Yes — 3 separate fixes |
| Fingerprint (Egis ETU905A80-E) | ✅ Works | Yes — needs SDCP |
| NPU (Intel AI Boost) | ✅ Works | Userspace stack |
| Facial recognition | ❌ Impossible | No IR camera on this model |
The three things that actually cost effort are the webcam, the fingerprint reader, and knowing what not to touch.
Every line here cost an incident.
| Don't | Why |
|---|---|
Enable nvidia-suspend / nvidia-resume / nvidia-hibernate |
Freezes suspend. The freeze step goes from 1.86 s to 36.5 s and the machine hangs — hard power-off required. They ship disabled on CachyOS. Leave them alone. |
Install a third-party max98390-hda DKMS |
Kernel ≥ 7.0 already binds the amps. The external module conflicts. |
Apply the mic-fix with dsp_driver=3 |
DMICs already work with stock SOF. |
| Hot-reload the camera stack | modprobe -r intel_ipu6_isys intel_ipu6 ipu_bridge ov02c10 then reloading oopses the kernel — ipu_bridge owns the sensor's software nodes. Reboot instead. |
| Re-enable Secure Boot | Breaks boot: the CachyOS kernel isn't signed with Microsoft's key. It has nothing to do with the fingerprint reader. |
Run clear-storage --help (libfprint examples) |
It ignores --help and starts operating the sensor. |
Capture a baseline before changing anything, so you can diff later:
sudo ./scripts/recon.sh > baseline.txtThen follow the order in docs/runbook.md. Short version:
# 1. Battery limit (accepts any value, not just 80)
sudo cp fixes/battery/battery-charge-threshold.service /etc/systemd/system/
sudo systemctl enable --now battery-charge-threshold.service
# 2. Webcam — driver
sudo pacman -S --needed dkms
sudo mkdir -p /usr/src/ov02c10-1.0
sudo cp fixes/camera/{ov02c10.c,Makefile,dkms.conf} /usr/src/ov02c10-1.0/
sudo dkms add -m ov02c10 -v 1.0 && sudo dkms install -m ov02c10 -v 1.0 -k "$(uname -r)" --force
# 3. Webcam — userspace
sudo pacman -S --needed libcamera libcamera-ipa libcamera-tools pipewire-libcamera
sudo mkdir -p /etc/systemd/user/{pipewire,wireplumber}.service.d
sudo cp fixes/camera/10-libcamera-egl.conf /etc/systemd/user/pipewire.service.d/
sudo cp fixes/camera/10-libcamera-egl.conf /etc/systemd/user/wireplumber.service.d/
sudo rebootThis is the part nobody documents end to end, so here is the whole chain.
ov02c10 i2c-OVTI02C1:00: error -EINVAL: external clock 26000000 is not supported
The in-kernel driver only accepts 19.2 MHz. This board feeds 26 MHz.
fixes/camera/ has a DKMS module that accepts it and rescales
pixel_rate and vts by 26/19.2 — otherwise the sensor runs 35 % fast.
CachyOS detail: the kernel is built with clang/LLD, so
dkms.confmust passLLVM=1. Without it the module is useless. This is the main difference from the upstream fix this is based on.
cam0-stream0 seq: 032658 bytesused: 0
ERROR eGL egl.cpp:134 glFrameBufferTexture2D error 36054
ERROR Debayer debayer_egl.cpp:639 debayerGPU failed
libcamera's software ISP debayers through EGL, and glvnd hands it the NVIDIA EGL, where it fails. With the debayer broken the output buffers are empty — which looks like a capture failure but isn't.
The giveaway: direct V4L2 capture always worked, because it doesn't go through the software ISP:
v4l2-ctl -d /dev/video32 --set-fmt-video=width=1928,height=1092,pixelformat=BA10 \
--stream-mmap --stream-count=5 --stream-to=/tmp/frames.raw # 21 MBFix: force Mesa's EGL so the Intel iGPU does the debayer — 30 fps.
Scope it to PipeWire and WirePlumber (both!) via
fixes/camera/10-libcamera-egl.conf. Putting it
in /etc/environment would force Mesa system-wide and break NVIDIA EGL apps.
WARN IPASoft: Failed to create camera sensor helper for ov02c10
INFO IPASoft: Exposure 4-3144, gain 16-248 (1) <- reads the gain CODE as the gain
Without a CameraSensorHelper, libcamera thinks gain code 19 means 19× (it means
19/16 = 1.19×), decides it is already amplifying plenty, and stops. Exposure pegs at
maximum, gain stays at the floor.
fixes/camera/ov02c10-sensor-helper.patch
adds the helper. Parameters come from the kernel driver, not guesswork:
#define OV02C10_ANAL_GAIN_MIN 0x10 /* 16 -> 1.0x */
#define OV02C10_ANAL_GAIN_MAX 0xf8 /* 248 -> 15.5x */
cci_write(..., OV02C10_REG_ANALOG_GAIN, ctrl->val << 4, &ret); /* 1/16 steps */| analogue gain | mean luminance | |
|---|---|---|
| Without helper | 19 (1.19×) | ~90 |
| With helper | 81 (5.06×) | 125 |
Build libcamera at the same version your
libspa-libcamera.soexpects, or PipeWire won't load it. And the helper lives in the IPA module, notlibcamera.so— you needLIBCAMERA_IPA_MODULE_PATHtoo.
ACPI declares rotation 0, but the sensor is mounted 180°. Setting hflip/vflip by
hand does not stick — libcamera manages those from the declared rotation and resets
them. The DKMS patch forces props.rotation = 180, DMI-gated to this model.
The Egis/LighTuning ETU905A80-E is Match-on-Chip and requires SDCP. Arch's libfprint doesn't implement it:
strings /usr/lib/libfprint-2.so.2 | grep -c sdcp # 0Enrollment appears to succeed but nothing reaches the chip; on first verify, fprintd notices and deletes the orphan record.
Fix: build TenSeventy7/libfprint-egismoc-sdcp
into an isolated prefix and point only fprintd at it — see
fixes/fingerprint/. Don't overwrite the packaged libfprint;
pam_fprintd talks to the daemon over D-Bus, so nothing else needs the patched library.
The chip keeps one SDCP binding. Whichever OS boots last takes ownership and erases the other's enrollment. Measured directly from the chip:
after enrolling on Windows: ID 1 -> 1 print after enrolling on Linux: ID 1 + ID 2 -> 2 prints after booting Windows: ID 1 -> 1 print (Linux's is gone)Splitting fingers per OS does not help — it is the cryptographic binding, not storage slots (there are 10). Pick one OS.
Also note: fingerprint works at the lock screen, not at the boot login. Plasma
6.7's login manager ships no fingerprint PAM stack (kde-fingerprint exists,
plasmalogin-fingerprint does not).
The kernel side needs nothing — intel_vpu loads, firmware boots, /dev/accel/accel0
exists. Only userspace is missing. See docs/npu.md.
Measured with SqueezeNet:
| Device | Latency | Throughput | Package power | CPU busy |
|---|---|---|---|---|
| idle | — | — | 2.16 W | 0.1 % |
| NPU | 0.61 ms | 1639 fps | 7.70 W | 0.3 % |
| CPU | 1.54 ms | 643 fps | 78.89 W | 61 % |
Net of idle: +5.5 W vs +76.7 W — about 14× less energy. This is what makes NPU-accelerated webcam effects (background blur, auto-framing) practical on battery.
Trap:
intel-npu-driver-binships a udev rule that tightens/dev/accel/accel0from0666to0660 root:render. Installing the driver removes the access you had. Add yourself torender— the symptom otherwise looks like a driver mismatch (zeInit: ZE_RESULT_ERROR_UNINITIALIZED).
Closing the lid suspends correctly — KDE handles it, not logind. Measured:
| Device resume | 273 ms |
| Lid open → usable password prompt | ~1.9 s |
| Suspended for 31.8 min | one entry/exit pair — never woke by itself |
| Temperature | 55 °C → 39 °C (it cools down) |
| Power while suspended | < 1.49 W (upper bound), > 40 h from 80 % |
The power figure is an upper bound, not a measurement: this battery derives
charge_now from integer percent, so resolution is 1 % = 0.79 Wh. See
docs/suspend-resume.md for the method and the scripts.
Things that are structurally worse than Windows, with evidence — see docs/known-limits.md.
- Camera image quality. IPU6's hardware ISP (PSYS) has no device node on Linux. Intel upstreamed the kernel drivers but keeps the ISP interface and algorithms proprietary; PSYS was never merged. We use libcamera's software ISP.
- Audio. The MAX98390 amps expose zero mixer controls and there is no DSM
calibration firmware. Not an open-source limitation — the factory per-speaker
calibration simply isn't published.
easyeffectsrecovers part of it. - Facial recognition. No IR camera on this model (
_STA = 0forOVTI01AS), so it's unavailable on Windows too.
docs/ runbook (install order), hardware inventory, known limits,
suspend measurements, NPU, troubleshooting
fixes/ one directory per subsystem, each with a README explaining the why
scripts/ recon.sh (state dump), and the measurement tools used here
No state dumps from the original machine are included — they contain serials, UUIDs and
MAC addresses. Generate your own with scripts/recon.sh.
Built on top of, and cross-checked against:
- fjoelnr/CatchyOS-galaxy-book4-ultra
- Andycodeman/samsung-galaxy-book-linux-fixes — the OV02C10 26 MHz patch originates here
- TenSeventy7/libfprint-egismoc-sdcp
- joshuagrisham/samsung-galaxybook-extras — the platform driver, now upstream
Documentation and scripts: MIT (see LICENSE).
fixes/camera/ov02c10.c is a derivative of a Linux kernel driver and remains
GPL-2.0.