Experimental Linux support for the Fairphone 6 wide camera · nondescriptpointer
Fairphone 6 ultra-wide working in GNOME Snapshot.
Background
The Fairphone 6 is an interesting target for mainline Linux and postmarketOS for a few reasons:
It has relatively modern hardware compared with most other supported postmarketOS devices.
Fairphone invests in mainline Linux support, with Luca Weiss driving much of the development.
Those efforts have already provided promising initial support for the hardware.
Fairphone deliberately allows alternative operating systems.
Fairphone aims to support its devices for a long time, so the phone could provide a stable base for future work.
There are still two main blockers to making the device generally usable: onboard audio and camera support . I am working on a project that needs QR scanning, so I used that as an opportunity to see whether I could get at least one camera working. Disclaimer: I heavily relied on LLM assistance to make this work.
Fairphone 6 cameras
The Fairphone 6 has three cameras:
Rear: Sony IMX896
Front: Samsung S5KKD1
Wide: OmniVision OV13B10
The Sony and Samsung sensors have no mainline Linux drivers, but the OmniVision OV13B10 has a mainline driver. The ultra-wide camera is sufficient for QR scanning.
A phone camera is not a single device. On Qualcomm SoCs, the capture path is a chain:
image sensor → CSI-2 D-PHY → CSIPHY → CSID → ISP (VFE/TFE) → memory<br>(I2C) (MIPI lanes) (decode) (demux) (write engine) (DMA)
There are also several supporting components, including a camera clock controller (camcc), the CCI (Qualcomm's dedicated camera I2C controller), power rails, and a VCM (voice-coil motor) for autofocus.
The downstream vendor Android kernel drives all of this with Qualcomm's substantial, proprietary CAMX/cam-kernel stack. On mainline, the equivalent is the much smaller qcom-camss driver, with libcamera providing userspace integration and image processing.
To get the OmniVision camera working, I had to teach qcom-camss about the SoC's specific blocks, describe the hardware in the device tree, enable the sensor driver, and configure libcamera to turn raw Bayer frames into images that applications could use.
Initial exploration
Before starting the implementation, I wanted to find out how much of the work was new and how much could be ported from hardware that mainline already supported. That would show whether the project was feasible without manufacturer support.
The initial research was encouraging:
The FP6's SoC is Qualcomm milos (SM7635). The downstream board is codenamed volcano, and postmarketOS already boots it with a mainline-based kernel fork.
The camera hardware blocks are TFE665 (a thin-front-end ISP), CSID665 , and CSIPHY v2.2.1 .
The camcc clock controller and CCI are already present in the kernel fork.
The three cameras are:
Main: Sony IMX896, with no mainline driver.
Ultra-wide: OmniVision OV13B10 , with a mainline driver that supports ACPI/x86 only.
Front: Samsung S5KKD1, with no mainline driver.
That made the target obvious: the OV13B10 ultra-wide . It is a 13 MP sensor with an existing mainline driver, and its wide field of view is suitable for QR scanning.
When I compared the downstream register headers (cam_tfe665.h, cam_csiphy_2_2_1_hwreg.h, and cam_tfe_bus.c) with mainline qcom-camss, I found that TFE665 is essentially the same IP as TFE530, which mainline already supports. The register layouts are identical. Only the base offsets of the internal blocks differ, because the write bus lives at a different address. Similarly, the CSID665 RDI registers match the supported CSID, and CSIPHY v2.2.1 belongs to the same three-phase PHY family.
This meant that I could port existing drivers instead of writing everything from scratch .
Step 1: Port the capture subsystem into the kernel
qcom-camss was not yet available on milos, so I had to connect the existing pieces.
TFE665 ISP driver
I added a new camss-vfe-665.c, modelled directly on the mainline TFE530 driver (camss-vfe-340.c). Because the register contents are identical, the driver logic is the same. The main difference is that milos places the ISP control block and write-bus block at different offsets. The bus register base moved from 0xa00 to 0x1800, and encapsulating that offset shift accounted for most of the work.
CSID665 and CSIPHY v2.2.1
CSID665 reused the existing gen-2 CSID operations because its RDI register offsets were identical.
CSIPHY v2.2.1 needed a new lane-configuration table and D-PHY tuning values for this revision. I transcribed the lane register sequence and the approximately 1.1 Gbps/lane ("500 Msps") data-rate and AFE settings from the downstream cam_csiphy_2_2_1_hwreg.h. The PHY's register window was at offset 0x1000.
Resources, compatible, and device tree
In camss.c, I described milos's capture complex: four CSIPHYs, three CSIDs, three TFEs, their clocks, interconnects, and power domains. I also registered a new...