Camera Support - linux-surface/linux-surface GitHub Wiki

Running the cameras on Surface is a work in progress. Follow https://github.com/linux-surface/linux-surface/issues/91 for the ongoing discussions.

Please note that where your camera is marked as working (✅), the image quality is not expected to be perfect yet. The algorithms are still undergoing development, and need some camera specific tuning.

This page will detail the steps required to be able to get the camera working on your device, with the current latest support.

If you have successfully enabled your camera on a device not yet in this table, please update it:

Device ISP Front Back IR
Surface Go IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Go 2 IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Go 3 IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Pro 4 IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Pro 5 IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Pro 6 IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Pro 7 IPU4 OV5693 🚫 OV8865 🚫 OV7251 🚫
Surface Pro 7+ IPU6 OV5693 🚫 OV8865 🚫 OV7251 🚫
Surface Pro 8 IPU6 OV5693 🚫 OV13858 🚫 VD55G0 🚫
Surface Pro 9 (Intel) IPU6 OV5693 🚫 OV13858 🚫 VD55G0 (?) 🚫
Surface Book 1 IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Book 2 13" IPU3 OV5693 ✅ OV8865 ✅ OV7251 🚫
Surface Book 3 IPU4 OV5693 🚫 OV8865 🚫 OV7251 🚫
Surface Laptop 1 IPU3 OV9734 🚫 N/A OV7251 🚫
Surface Laptop 2 IPU3 OV9734 🚫 N/A OV7251 🚫
Surface Laptop 3 (Intel) IPU4 OV9734 🚫 N/A OV7251 🚫
Surface Laptop 3 (AMD) (USB/UVC) OV5693 ✅ N/A OV7251 ✅
Surface Laptop 4 (USB/UVC) N/A ?
Surface Laptop 5 (USB/UVC) ? N/A ?
Surface Laptop Studio (USB/UVC) ? N/A ?

The Surface Pro 7, Surface Laptop 3 (Intel) and the Surface Book 3 use an IPU4 which can not be supported at this time

Kernel Support

Kernel patches and drivers are required to be built to support your device. The best way to obtain these for a Surface device is to use one of the binary releases provided by this repository. Alternatively, you can also build a kernel yourself and apply the cameras.patch found in this repository under patches/<version>/cameras.patch (e.g. patches/v5.12/cameras.patch).

Supporting the Camera on the Surface range requires extending the Intel IPU3 driver to support the Windows based ACPI definitions. This is being handled by an extension to the IPU3-CIO2 driver called the CIO2-Bridge.

IPU3 Firmware

The IPU3 used by the Surface requires some firmware to support the devices which is loaded at runtime.

On Ubuntu-based systems, this firmware is provided by the 'linux-firmware' package.

According to this response, for Debian, the ipu3 firmware is provided by firmware-misc-nonfree from the nonfree repo. The .bin file is named irci_irci_ecr-master_20161208_0213_20170112_1500.bin and must be renamed and moved to /lib/firmware/intel/ipu3-fw.bin.

The file:

/lib/firmware/intel/ipu3-fw.bin

is required to be present at kernel boot time to support loading of the IPU3 IMGU device.

libcamera Support

To test the cameras on the Surface devices we need libcamera to provide the userspace support required to operate the cameras.

Install libcamera from repositories

Some flavours of Linux have repositories containing versions of libcamera, which you can install without building libcamera from source.

Fedora

It has been reported that, on Fedora 36, as long as you have linux-surface kernel 5.18 or newer installed, you should be able to install libcamera components via dnf:

sudo dnf install libcamera libcamera-tools libcamera-qcam libcamera-gstreamer libcamera-ipa pipewire-plugin-libcamera

If you're using an immutable variant of Fedora (Silverblue, Kinoite,Sericea, etc), you should use rpm-ostree:

rpm-ostree install libcamera libcamera-tools libcamera-qcam libcamera-gstreamer libcamera-ipa pipewire-plugin-libcamera

Arch Linux

Arch now ships a stable libcamera package (pacman -S libcamera). You may also want to install the following packages:

  • libcamera-tools for cam and qcam
  • gst-plugin-libcamera for the gstreamer libcamera plugin (required to use applications that don't directly support libcamera)

Build libcamera from the latest git source

Both clang and gcc are supported compilers.

First install the required dependencies (e.g. on Debian/Ubuntu, a '\' denotes the line is continued)

$ sudo apt install \
    build-essential meson ninja-build pkg-config libgnutls28-dev openssl \
    python3-pip python3-yaml python3-ply python3-jinja2 \
    qtbase5-dev libqt5core5a libqt5gui5 libqt5widgets5 qttools5-dev-tools \
    libtiff-dev libevent-dev libyaml-dev \
    gstreamer1.0-tools libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

And now obtain and build libcamera:

$ sudo apt install git
$ git clone https://git.libcamera.org/libcamera/libcamera.git
$ cd libcamera

Configure libcamera minimally for the Surface

$ meson build -Dpipelines=uvcvideo,vimc,ipu3 -Dipas=vimc,ipu3 -Dprefix=/usr -Dgstreamer=enabled -Dv4l2=true -Dbuildtype=release
$ ninja -C build
$ sudo ninja -C build install

Troubleshooting

Running on a fresh system, I needed to run this, and it doesn't hurt to be sure (but shouldn't be needed):

$ ldconfig

If your distro doesn't provide meson>=0.55, you can use the following methods to install the latest meson for building:

From PyPI
$ sudo pip3 install -U meson

Usually you shouldn't use sudo pip because it might conflict with your distro's package manager. However, it is a documented method to install meson. And if you have different versions installed for user and system-wide, or only install meson for user, sudo ninja -C build install might not work.

From MPR

MPR is like AUR for Debian/Ubuntu. To use it, you need to install makedeb.

After installing makedeb, pull meson's PKGBUILD from MBR:

$ git clone https://mpr.hunterwittenborn.com/meson.git
$ cd meson
$ cat PKGBUILD  # Have a look at the PKGBUILD

If your apt version is lower than 1.9, makedeb cannot install the dependencies for you, and you'll need to install the packages in makedepends array manually. Then build and install the package:

$ makedeb -si

Alternatively, use the method above to install MPR helper tap, and install meson through tap:

$ tap install meson

Ensure your user account has permissions

Make sure your user is part of the 'video' group. (you only need to do this once on the machine)

$ sudo usermod -aG video $USER
$ newgrp video

Test with 'cam'

Now you can run cam --list and you should see the sensor listed

$ cam --list
Available cameras:
1: Internal front camera (\*SB*.PCI0.LNK1)

and running qcam should (all being well) get you a view of the video capture.

$ qcam

Applications

Cheese

Cheese uses Gstreamer to read from cameras, and should work as long as the gstreamer libcamerasrc plugin is correctly built and installed.

Firefox

Since version 116, Firefox added experimental support to pipewire camera. Navigate to about:config, enable media.webrtc.camera.allow-pipewire and restart Firefox. Make sure the plugin pipewire-plugin-libcamera is installed in your distribution.

GStreamer Loopback Device for other Applications

Right now only applications using libcamera (directly or indirectly, e.g. via the gstreamer plugin) are supported. It is, however, possible to use gstreamer to create a loopback device. To do this, you have to install v4l2loopback-dkms (if you're using Secure Boot you have to also follow this guide: https://github.com/umlaeute/v4l2loopback/wiki/Secure-Boot) and run

sudo modprobe v4l2loopback video_nr=42 card_label="virtualcam" exclusive_caps=1

to set up the kernel module and

gst-launch-1.0 libcamerasrc camera-name='\\\_SB_.PCI0.I2C2.CAMF' ! \
    video/x-raw,width=1280,height=720,framerate=30/1,format=NV12 \
    ! videoconvert ! video/x-raw,format=YUY2 ! queue ! \
    v4l2sink device=/dev/video42

to create the loopback device. Note that you may need to change camera-name, v4l2sink device, and/or other parameters for your setup.

If issues occur, you maybe need to remove queue ! from the command:

gst-launch-1.0 libcamerasrc camera-name='\\\_SB_.PCI0.I2C2.CAMF' ! \
    video/x-raw,width=1280,height=720,framerate=30/1,format=NV12 \
    ! videoconvert ! video/x-raw,format=YUY2 ! \
    v4l2sink device=/dev/video42

This guide also explains how to automatically load v4l2loopback on startup and how to create a terminal command to start the cameras quicker: https://neilzone.co.uk/2021/08/working-front-and-rear-cameras-on-debian-11-on-a-surface-pro-6-surfacebook-2-and-surface-go/

Development Information

Module IDs and Sensor Mappings

Device ISP Front Sensor Front Module Rear Sensor Rear Module IR Sensor IR Module
SGo1 IPU3 OV5693 YHCU OV8865 YHCT OV7251 YHCV
SGo2/3 IPU3 OV5693 YHCU OV8865 YHCT OV7251 YHCV
SB1 IPU3 OV5693 MSHW0070 OV8865 MSHW0071 OV7251 MSHW0072
SB2 13" IPU3 OV5693 MSHW0140 OV8865 MSHW0141 OV7251 MSHW0142
SB2 15" IPU3 OV5693 MSHW0150 OV8865 MSHW0151 OV7251 MSHW0152
SB3 13" IPU4 OV5693 MSHW0210 OV8865 MSHW0211 OV7251 MSHW0212
SB3 15" IPU4 OV5693 MSHW0200 OV8865 MSHW0201 OV7251 MSHW0202
SL1 IPU3 OV9734 MSHW0073 - - OV7251 MSHW0074
SL2 IPU3 OV9734 MSHW0073 - - OV7251 MSHW0074
SP4 IPU3 OV5693 MSHW0070 OV8865 MSHW0071 OV7251 MSHW0072
SP5 IPU3 OV5693 MSHW0120 OV8865 MSHW0121 OV7251 MSHW0122
SP6 IPU3 OV5693 MSHW0120 OV8865 MSHW0121 OV7251 MSHW0122
SP7 IPU4 OV5693 MSHW0190 OV8865 MSHW0191 OV7251 MSHW0192

Note: SL3 and SLGo1 use generic USB cameras and work out of the box.

ACPI Sensor IDs

Sensor ID ACPI ID
AR0330 APTA0330
IMX135 INT3471
OV2680 OVTI2680
OV2740 INT3474
OV5648 OVTI5648
OV5693 INT33BE
OV7251 INT347E
OV8835 OV8835, OVTI8835
OV8865 INT347A
OV9734 OVTI9734

Reverse engineering tips

For some of the work to build camera support, reverse engineering the settings made by the Windows drivers for either the camera devices or their supporting ICs is necessary. One way to do this is to talk to the i2c devices directly and read the registers to discover the settings. A mandatory first step is informing Windows that direct user access to those devices is allowed.

To perform that operation you will first need the microsoft asl compiler tool. This lets you extract and insert modified DSDT tables in Windows.

Once you've got that tool built, you need to edit the DSDT table to tell Windows to expose the I2C device for the PMIC. Run asl.exe /tab=DSDT to get a file named DSDT.asl in the current directory. You then need to make edits following this guide to add a section to the DSDT that creates an MSFT8000 dummy device and adds an I2cSerialBus entry describing the I2C Bus that the target device sits on, for example:

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)

    Name(_CRS, ResourceTemplate()
    {
        I2CSerialBus(0xFFFF, , 0, , "\\_SB.PCI0.I2C2", , ,) // in this case, we want to grant access to the I2C2 bus
    })

    Name(_DSD, Package()
    {
        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
        Package()
        {
            Package(2) { "bus-I2C-I2C2", Package() { 0 }}, // The 0 here is the index of the related resource in _CRS
        }
    })
}

You also need to increase the version number of the table in the `DefinitionBlock() or the edits will be ignored

Once that's done, compile the edited DSDT.asl using asl.exe DSDT.ASL. If it throws compiler errors you'll have to fix them. If you can't get it working using this tool you can actually boot Linux and try using iasl, which is Intel's equivalent and seems to work a lot better. You'd have to transfer the compiled file back to Windows somehow.

Once the file's compiled, you insert it using asl.exe /loadtable -v DSDT.AML. Pay attention to the output of the command as you may need to enable test signing mode (bcdedit /set testsigning on) and disable secure boot. Finally, you need to reboot.

To talk to the I2C devices themselves, use the I2cTestTool. Build that and run I2cTestTool <<addr>> <<busname>> to connect to the device. For example to connect to the device at 0x4d on I2C bus 2 the command would be I2cTestTool 0x4d I2C2. You can then perform read and write operations over I2C. To read a register value you use the writeread command, sending the address of the register followed by the number of registers you want to read, for example writeread {30 0a} 2 to read 0x300a and 0x300b, which on Omnivision sensors is usually the chip's ID.

I2C Workaround

The I2C workaround should not be required any more. This is fixed in the linux-surface kernels, and the fix should be heading upstream to other distribution kernels. This section is kept for reference only.

On a Surface Go 2 a workaround is required to enable the front camera support: The kernel needs to have the following command line parameter added to the boot arguments.

acpi_enforce_resources=lax

Where to put this is quite platform specific, however continuing the assumption that an Ubuntu or Debian based system is in use, we can infer that the system uses grub.

On the Surface Go device itself, edit the file "/etc/default/grub" and update the GRUB_CMDLINE_LINUX_DEFAULT entry to add the workaround:

 -GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
 +GRUB_CMDLINE_LINUX_DEFAULT="quiet splash acpi_enforce_resources=lax"
⚠️ **GitHub.com Fallback** ⚠️