Skip to content

Guide

The webcam on a Dell XPS 14 (Panther Lake) under Kubuntu 26.04

Published · Updated

The webcam on my Dell XPS 14 (DA14260, Intel Panther Lake) didn’t work on Kubuntu 26.04. No camera in the browser, and nothing in cam -l either. So this is how I got it going - one missing kernel driver, a few packages, and a small relay that works around a sensor bug.

A drawing: a laptop with its webcam glowing green, a camera window with a green tick and a Built-in Camera label on the screen, a green ribbon cable from the webcam to a blue IPU7 chip, and a crossed-out No sensor found tag

Before fixing anything I wanted to know what talks to what. On this laptop it’s a lot more parts than I expected 😅

How the camera is wired: the ov08x40 sensor sends pictures over two MIPI CSI-2 lanes through Intel’s CVS chip to the IPU7 in the Panther Lake chip; its settings go over an I2C bus from a Synaptics SVP7500 USB-IO bridge, which also switches power, reset and the privacy LED, and talks to the Panther Lake chip over USB

  • ov08x40 - the 8 MP sensor at the top of the screen. Sends raw pictures over two MIPI CSI-2 lanes
  • IPU7 - Intel’s camera block inside the Panther Lake chip. Its input part (ISYS) takes the raw frames and writes them to memory, and that’s it. It doesn’t make a colour picture, that’s left to software
  • Intel CVS chip - sits between the sensor and the IPU7. It can use the camera on its own (for presence detection), so it hangs on to the sensor until a driver asks for it
  • Synaptics SVP7500 - a USB-IO bridge. The sensor’s settings go over an I2C bus that belongs to this bridge, so they actually travel over USB. Its GPIO pins switch the sensor’s power, reset and privacy LED. And the CVS chip and the Windows Hello IR sensor are on the same I2C bus as well

An ov08x40 camera module: a small dark square block with a lens in the middle, joined by a short orange flex cable to a long connector strip

That’s an ov08x40 module like the one in the lid - the sensor and lens in the square block, and a flex cable to the connector, which carries the MIPI lanes, the I2C bus and power.

The Synaptics bridge shows up in lsusb as 06cb:0701 Synaptics, Inc. SVP7500, and the sensor sits under it:

Terminal window
readlink -f /sys/bus/i2c/devices/i2c-OVTI08F4:00
# /sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/usbio.usbio-i2c.1/i2c-13/i2c-OVTI08F4:00

Four separate problems, and each one was hiding behind the one before:

  1. No intel_cvs driver. Without it the CVS chip never lets go of the sensor, so the kernel never creates it
  2. No camera software - libcamera’s tools and its PipeWire and GStreamer plugins weren’t installed
  3. Brave and Edge opened the 32 raw IPU7 capture devices, which never give a picture
  4. PipeWire crashes below 2560×1440. The sensor’s smaller mode sends broken frames, and libcamera aborts on them
Part Notes
Dell XPS 14 DA14260 Or another Panther Lake laptop with an ov08x40 behind a CVS chip. Check ls /sys/bus/i2c/devices/ for i2c-OVTI08F4:00 and i2c-INTC10E1:00
Kubuntu 26.04 Ubuntu’s 7.0 kernel. Other Ubuntu 26.04 flavours should work the same, I guess
sudo For the packages

Grab them all in one go:

Terminal window
mkdir xps-14-webcam && cd xps-14-webcam
for f in setup-root.sh install.sh camera-relay.service camera-relay.sh camera-relay.desktop 51-camera-relay.conf ov08x40.yaml; do
curl -fsSO https://blog.jacq.cc/xps-14-webcam/$f
done

Seven files: setup-root.sh and install.sh set everything up, the other five are what they install. MIT licence (LICENSE).

Step 2: The CVS driver and the camera packages

Section titled “Step 2: The CVS driver and the camera packages”

The kernel loads the camera drivers in a chain, and each one waits for the one above it. On a fresh Kubuntu 26.04 the chain just stops at intel_cvs:

What has to load before the camera shows up: usbio, then int3472-discrete, then intel_cvs, which was missing, then ov08x40, then intel_ipu7_isys

intel_cvs isn’t in Ubuntu’s normal kernel modules - it’s in a separate signed package, linux-modules-vision-generic. Without it, the IPU7 driver knows about the sensor from ACPI (OVTI08F4:00) but never actually gets one, and cam -l says No sensor found for /dev/media0.

Terminal window
bash setup-root.sh

It asks for the sudo password and installs:

  • linux-modules-vision-generic: the CVS driver
  • libcamera-tools, libspa-0.2-libcamera, gstreamer1.0-libcamera: libcamera, its cam tool and its PipeWire and GStreamer plugins
  • gstreamer1.0-tools, gstreamer1.0-pipewire: gst-launch-1.0 and pipewiresink for the relay

It ends with 🟢 SUCCESS. If it says intel_cvs not loaded yet, reboot. Then have a look at the kernel log:

Terminal window
journalctl -k -b | grep -E 'ownership|bind ov08x40'
Intel CVS driver i2c-INTC10E1:00: cvs_common_probe:Transfer of ownership success
intel_ipu7_isys.isys intel_ipu7.isys.40: bind ov08x40 13-0036 nlanes is 2 port is 0

Then ask libcamera:

Terminal window
cam -l

Only the end of the output matters. Here’s mine, with the timestamps cut:

INFO Camera camera_manager.cpp:340 libcamera v0.7.0
ERROR V4L2 v4l2_subdevice.cpp:1201 'ov08x40 13-0036': Unable to get rectangle 2 on pad 0/0: Inappropriate ioctl for device
WARN CameraSensor camera_sensor_legacy.cpp:402 'ov08x40 13-0036': The PixelArraySize property has been defaulted to 3856x2176
...
WARN CameraSensor camera_sensor_legacy.cpp:427 'ov08x40 13-0036': The sensor kernel driver needs to be fixed
WARN CameraSensorProperties camera_sensor_properties.cpp:548 No static properties available for 'ov08x40'
WARN IPAProxy ipa_proxy.cpp:192 Configuration file 'ov08x40.yaml' not found for IPA module 'simple', falling back to '/usr/share/libcamera/ipa/simple/uncalibrated.yaml'
WARN IPASoft soft_simple.cpp:104 IPASoft: Failed to create camera sensor helper for ov08x40
INFO Camera camera_manager.cpp:223 Adding camera '\_SB_.LNK1' for pipeline handler simple
Available cameras:
1: Internal front camera (\_SB_.LNK1)

The ERROR and WARN lines look bad, but the camera works with all of them:

  • “Unable to get rectangle” - the ov08x40 driver doesn’t report its crop areas, so libcamera fills in the full 3856×2176 sensor size
  • “No static properties” and no sensor helper - libcamera has no entry for this sensor, so it guesses its timings and gain
  • “ov08x40.yaml not found” - cam doesn’t know about the tuning file from Step 5. Only the relay passes it in

At boot the log also shows a whole bunch of int3472-discrete … cannot find GPIO chip INTC10E2:00, deferring. They look like the problem, but they’re harmless: INTC10E2 is the USB-IO bridge’s GPIO chip, and it turns up a moment later.

ubuntu-drivers also suggests intel-ipu7-dkms. You don’t need it - it adds modules for Intel’s own camera stack, not libcamera.

Step 3: Why PipeWire’s own camera crashes

Section titled “Step 3: Why PipeWire’s own camera crashes”

With the sensor there, PipeWire shows a libcamera camera straight away. And it works at full size. But as soon as a browser asks for 640×480 or 1280×720, PipeWire crashes.

Why the relay always asks for 2560×1440: a request for 1280×720 picks the binned 1928×1088 mode, whose pixel values are broken, the histogram overflows and PipeWire crashes; a request for 2560×1440 keeps the sensor in its full 3856×2176 mode with clean values, and the relay scales that to 1280×720

For anything up to 1920×1080, libcamera switches the sensor to its binned 1928×1088 mode. In that mode the raw frames are broken: 2,121,824 of 2,123,776 pixel values were above 1023, the most a 10-bit pixel can hold. So pretty much every pixel. The full 3856×2176 mode is clean. The broken values overflow a 64-bin histogram in libcamera’s software ISP, and Ubuntu’s hardened build aborts on that.

I tested it with cam: 640×480, 1280×720, 1920×1080 and 1924×1088 crash. 2560×1440, 3840×2160 and 3848×2176 work. I think it’s a bug in the kernel’s ov08x40 driver for the binned mode. I haven’t found a report of it.

So the fix - never let anything ask for less than 2560×1440, and scale down afterwards.

The relay is just one GStreamer pipeline, run by a systemd user service. It reads 2560×1440 from libcamera, scales it to 1280×720, and offers that to PipeWire as a camera called “Built-in Camera”.

The relay, from sensor to browser: the sensor and IPU7 deliver raw frames, libcamera’s soft-ISP makes the colour picture on the GPU with statistics on the CPU, GStreamer scales to 1280×720 on the CPU, and PipeWire offers it as Built-in Camera to Brave and Edge

The main bit of camera-relay.service:

gst-launch-1.0 -e libcamerasrc saturation=1.3 \
! video/x-raw,width=2560,height=1440 \
! videoconvertscale n-threads=4 \
! video/x-raw,format=YUY2,width=1280,height=720,framerate=30/1 \
! pipewiresink mode=provide client-name=camera-relay \
'stream-properties="props,media.class=Video/Source,media.role=Camera,node.name=camera-relay,node.description=\"Built-in Camera\""'
  • libcamerasrc asks for 2560×1440, which keeps the sensor in its full mode
  • videoconvertscale scales on the CPU, with 4 threads
  • pipewiresink mode=provide makes the relay a PipeWire camera. It offers one fixed format, and that matters: Brave and Edge skip a PipeWire camera that only lists size ranges, with NotFoundError: Requested device not found. My first try used a v4l2loopback device, and it failed for exactly that reason

Install it:

Terminal window
bash install.sh

It installs the service, the colour tuning file (Step 5), the WirePlumber rules (Step 6), a camera-relay command in ~/.local/bin, and a KDE menu entry “Camera Relay (on/off)”. Then turn it on:

Terminal window
camera-relay start

The KDE application menu with Camera Relay (on/off) among the favourites, next to Edge, Brave and VS Code. Its tooltip reads: Turn the built-in camera on or off for browsers and video calls

Each start or stop pops up a notification:

Two KDE notifications from Camera relay: a green dot with Camera relay on, and a red dot with Camera relay off

camera-relay stop turns it off, and camera-relay toggle (or the menu entry) flips it. While it runs, the privacy LED is on and the relay uses about 56 % of one CPU core. So I turn it off after a call.

The first picture had a yellow-green cast. libcamera has no tuning for this sensor, so it falls back to uncalibrated.yaml, and on mine blue came out about 13 % low. ov08x40.yaml fixes that:

algorithms:
- BlackLevel:
blackLevel: 4096 # ov08x40: 64 at 10 bit, in libcamera's 16-bit scale
- Awb:
- Ccm:
ccms:
- ct: 6500
ccm: [ 1.0, 0, 0,
0, 0.92, 0,
0, 0, 1.28 ]
- Adjust:
- Agc:

The service passes it in with LIBCAMERA_SIMPLE_TUNING_FILE. The colour matrix cuts green a little and lifts blue. I tuned it by eye in office light, so other light might want other numbers. Edit ~/.config/camera-relay/ov08x40.yaml, then camera-relay stop and camera-relay start.

On webcamtests.com, the average colour of the picture went from a hue of 75° (yellow-green) and 15.75 % saturation to under 1 %, so a neutral grey. The picture and the result below are with the tuning file, and that run gave 0.52 %.

The camera pointed at a white painted block wall in an office, with a desk chair and a monitor at the bottom right. The wall comes out a neutral white

The webcamtests.com result panel: Built-in Camera, 1280×720 at 29 FPS, average colour a neutral grey, saturation 0.52 %

libcamerasrc takes saturation and contrast (1.0 is no change). The relay runs with saturation=1.3 for a bit more colour. The catch: the colour matrix has to be in the tuning file for saturation to do anything - with uncalibrated.yaml it had no effect. Contrast works the same way, and I’ve left it at 1.0 for now. To try other values, edit the ExecStart line in ~/.config/systemd/user/camera-relay.service:

gst-launch-1.0 -e libcamerasrc saturation=1.3 contrast=1.1 ! ...

Then systemctl --user daemon-reload and restart the relay.

Manual exposure, gain and colour gains do nothing on this sensor. libcamerasrc accepts them and the picture just doesn’t change. libcamera logs Failed to create camera sensor helper for ov08x40 - it has no gain model for it.

The sensor is mounted upside down in the lid. libcamera knows that from ACPI and sets the sensor’s own flip bits, so the picture comes out upright with no videoflip in the pipeline.

Once, the picture showed upside down in Edge, which was weird. After a camera-relay stop and camera-relay start it was upright again, and I haven’t seen it since. I don’t know what caused it. If it happens to you, restart the relay before you add a videoflip method=rotate-180 - with the flip bits set as well, that would turn a good picture upside down.

Chromium-based browsers only look for PipeWire cameras with an option turned on. Without it they open the raw IPU7 devices and get nothing.

How a browser finds the camera: with the option off, Brave or Edge opens the 32 raw ipu7 devices and gets no picture; with the PipeWire camera option on, it asks the camera portal, KDE asks once, and PipeWire hands over the relay’s Built-in Camera. WirePlumber rules hide the raw devices and turn off PipeWire’s own libcamera camera

51-camera-relay.conf is a WirePlumber rule that turns off PipeWire’s own libcamera camera (the one that crashes) and hides the 32 raw ipu7 devices. install.sh puts it in ~/.config/wireplumber/wireplumber.conf.d/ and restarts WirePlumber.

Brave - paste this into the address bar, set “PipeWire Camera support” to “Enabled”, and click “Relaunch”:

brave://flags/#enable-webrtc-pipewire-camera

Edge has the same option, but edge://flags doesn’t show it. So install.sh turns it on from the command line instead. It writes a copy of Edge’s launcher to ~/.local/share/applications/microsoft-edge.desktop with this added to the Exec= line:

--enable-features=WebRtcPipeWireCamera

KDE uses that copy instead of the system launcher. It only kicks in when Edge starts fresh, so quit it first with “⋯ → Close Microsoft Edge”, then start it from the menu. Running microsoft-edge --enable-features=… while Edge is open doesn’t work - it just opens a new window in the running Edge, without the option.

To check it worked, open edge://version and look for WebRtcPipeWireCamera in the “Command-line” row.

The first time a site asks for the camera, KDE asks “Allow Apps to Use the Camera?” - click OK.

If PipeWire restarts or crashes while the browser’s open, restart the browser too. Otherwise it hangs on to a dead camera.

  • BIOS updates - someone with the same model on BIOS 1.12.0 gets no frames at all (omarchy #16792). Mine works on 1.12.0, so I’m not sure what’s different. Check the camera after a BIOS update
  • Suspend - on the XPS 16 the sensor is gone after a suspend (omarchy #10624). I haven’t seen it on mine yet. If it happens, reboot
  • Grain - in office light the auto-exposure runs the sensor near its longest exposure at about 10× gain, so the picture’s noisy. The binned mode would collect more light per pixel, but that’s the broken one
  • The GPU - the software ISP runs on the GPU. I’ve had one KDE graphics hang while the relay ran. KDE hangs on this laptop without the camera too, so I don’t know yet if it’s related
  • Apps that read /dev/video* directly (not through PipeWire) don’t see the relay

When a kernel or libcamera update fixes the binned mode, the relay can go - just delete the WirePlumber rule and PipeWire’s own camera comes back.

Terminal window
camera-relay stop
rm ~/.config/systemd/user/camera-relay.service \
~/.config/wireplumber/wireplumber.conf.d/51-camera-relay.conf \
~/.local/bin/camera-relay \
~/.local/share/applications/camera-relay.desktop \
~/.local/share/applications/microsoft-edge.desktop
systemctl --user daemon-reload
systemctl --user restart wireplumber

Change history

  • : First published.

This guide as plain Markdown, for LLMs and scripts: xps-14-panther-lake-webcam-linux.md. All posts: API and Markdown.

Text, photos and drawings: CC BY 4.0. Code: MIT. Share and adapt them, crediting HomeLab Series (blog.jacq.cc). Licence and terms of use ·Privacy policy (no cookies, no tracking)