Skip to content

media: Surface Pro 5 (IPU3) camera fixes: OV8865, OV5693, OV7251 IR illuminator - #181

Open
rylos wants to merge 10 commits into
linux-surface:v7.2-surface-develfrom
rylos:sp5-ipu3-cameras
Open

rylos wants to merge 10 commits into
linux-surface:v7.2-surface-develfrom
rylos:sp5-ipu3-cameras

Conversation

@rylos

@rylos rylos commented Oct 23, 2026

Copy link
Copy Markdown

Fixes for the three cameras of the Surface Pro 5 (1796, Kaby Lake / IPU3), tested on v7.2.12 with the current 7.2 patch set. Previously posted in linux-surface/linux-surface#2233. Most of them are generic or apply to the other IPU3 Surfaces with the same sensors (Book 2, Pro 6, Go).

OV8865 (rear)

  • OV8865_VTS_H() keeps bits 11:8 only, so any frame length above 4095 lines wraps around and long exposures give black frames. Generic.
  • VBLANK reset to the mode default on mode change (exposure range derived from it).
  • Analogue gain step 1 instead of 128 (the AGC was limited to integer 1x..16x).
  • SP5 only: 800x600 defaults to 30 fps; libcamera's IPU3 pipeline never changes VBLANK, so at 90 fps 640x480 was black indoors.

The "program the mode on stream start" fix I had for the OV8865 is dropped: Jurison Murati's patches already on this branch cover it.

OV5693 (front)

OV7251 (IR)

  • The illuminator hangs off the sensor's STROBE pad, which ACPI doesn't describe and the mode tables leave disabled. Enabled on stream start with an alternate-frame pattern (lit/unlit frames), pulse length following the exposure. On by DMI for the SP5, override with ov7251.surface_ir_strobe / surface_ir_pattern.

Orientation (SP5)

  • The SSDB reports 180° for front and rear, but both need a vertical flip only. ipu-bridge reports 0° for INT33BE/INT347A on SKU 1796/1807, and both drivers invert VFLIP by DMI.

Verification: 11 libcamera configurations (320x240 → full resolution, front and rear), 0 CSI-2 errors, correct frame rates, neutral colours; IR frames alternate between mean level 16 and 42; face login with howdy working.

rylos and others added 10 commits October 23, 2026 14:35
Same issue as ov8865: the mode registers are only written on runtime PM
resume, so a format change on a sensor that stays powered never reaches
the chip. Program the mode before the controls on every stream start.

Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
The SSDB of the Surface Pro 5 (SKU 1796, and 1807 for the LTE model)
reports a 180 degree rotation for both the front OV5693 and the rear
OV8865. Measured on the device, both sensors need a vertical flip only,
without mirroring, which is not a rotation: applying the reported 180
degrees leaves both images mirrored.

- ipu-bridge: add a DMI list of sensors that are not rotated despite
  what the SSDB says, and report 0 degrees for them.
- ov8865: on these machines invert the VFLIP control, so the default
  value gives an upright image and userspace (libcamera resets the
  flip controls) keeps working.

The front sensor gets the same treatment in a separate patch.

Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
On the Surface Pro 5 the IR flood LED is driven by the OV7251 STROBE
pad, not by a GPIO or the TPS68470 LED outputs, and ACPI does not
describe it. The mode tables leave the pad disabled (0x3005 = 0x00,
0x3016..0x301c = 0xf0), so the LED never lights and the IR camera,
which sits behind an IR-pass filter, only sees black.

Enable the STROBE output on stream start on the Surface Pro 5 (SKU 1796
and 1807), with an alternate-frame pattern (0x3b81 = 0xaa) so userspace
gets lit and unlit frames. The module parameter surface_ir_strobe
overrides the DMI match (-1 auto, 0 off, 1 on).

Verified on a Surface Pro 5 (1796): with the strobe enabled the mean
level of consecutive 640x480 frames alternates between 16 and 42, with
it disabled it stays at 16.

Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
The driver's 1296x972 binned mode has never streamed on IPU6 machines
(Surface Pro 7+/8/9, Go 4): the receiver reports "Transfer FIFO
overflow" or "Frame sync error" and no frame ever completes. Because of
that, every IPU6 setup has had to read the sensor at full 2592x1944 and
downscale, throwing away two stops of light and pinning the AGC at its
maximum analogue gain (127 vs ~15) - the familiar "working but unusably
noisy" IPU6 front camera. Fix the mode by porting what the vendor
(Windows) driver ov5693.sys programs differently, extracted from its
mode tables:

- Per-mode MIPI PLL (0x30b3: 0x70 binned / 0x83 full) and six per-mode
  analogue readout registers. Mainline writes only the full-resolution
  values, once, globally; the binned readout then overflows the
  receiver's FIFO.

- Readout geometry with spare pixels: cropping exactly the output size
  starves the sensor's internal ISP and every frame fails CSI-2 framing
  ("Frame sync error"). The vendor reads the whole pixel array, bins it
  to 1312x978, and places the 1296x972 output via the ISP window
  offsets. The parity of the Y offset selects the vertical Bayer phase
  (even = the advertised BGGR, odd = GRBG); it is exposed as a module
  parameter (binned_y_offset, default 2) because libcamera's software
  ISP wants the advertised phase while pipelines built around the
  Windows tuning (Intel's IPU6 HAL/PSYS) expect the vendor's odd
  offset.

- Mode-dependent vertical-flip bits: with binning, setting both FORMAT1
  flip bits corrupts the CSI-2 framing and the ISP bit alone does not
  flip; only the sensor-side bit both streams and flips. Re-apply the
  vflip control when the mode is configured, since the correct bits
  depend on the selected mode.

- Cap the frame rate at 30 fps: the auto-picked 60 fps VTS for the
  binned mode exceeds what the slower binned MIPI PLL can carry (the
  vendor runs this mode at VTS 1984 / 30 fps).

- Program MIPI_CTRL00 (0x4800) = 0x2d before stream-on. The 0x00
  power-on default is fine on IPU3 but leaves the IPU6 D-PHY unable to
  lock: the sensor streams yet no CSI-2 data arrives.

Also document the black-level pedestal next to the BLC init values: the
sensor keeps ~16 LSB (10-bit) at every analogue gain, while OEM .aiqb
calibrations state 64.9 because Windows' ISYS delivers RAW10 aligned to
12 bits.

Verified on a Surface Pro 7+ (IPU6, Tiger Lake): 1296x972 at 30 fps,
analogue gain in normal indoor light drops from 127 (maximum) to ~15,
both through libcamera's software ISP and through Intel's IPU6 HAL on
top of the hardware ISP. Reference setup and measurements:
https://github.com/dmanresa-saes/surface-ipu6-cameras

Signed-off-by: D. Manresa <d.manresa@electronica-submarina.com>
Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
The front OV5693 of the Surface Pro 5 is mounted vertically mirrored:
the upright, unmirrored image needs the sensor vertical flip and no
horizontal flip. The firmware reports a 180 degree rotation, which makes
userspace enable both flips and leaves the image mirrored.

ipu-bridge now reports 0 degrees for this sensor; invert the VFLIP
control on the Surface Pro 5 (SKU 1796/1807) so that its default value
gives the upright image.

In the full 2592x1944 readout the vertical flip also moves the Bayer
phase by one column (BGGR -> GBRG), which gives a magenta image through
libcamera. The crop start only moves in steps of two columns, so shift
the output window by one column and widen the crop accordingly while
the flip is active.

Verified on a Surface Pro 5 (1796): upright and unmirrored in the full
and in the binned 1296x972 mode, BGGR phase in both, natural colours
through libcamera.

Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
…rface Pro 5

set_fmt() updates the VBLANK range and default for the new mode but keeps
the current value when it is still in range. A value left over from the
previous mode then keeps that mode's timing: after the full-size mode
(VBLANK 22) the 800x600 mode runs faster than its nominal 90 fps, with
the exposure capped accordingly. Set VBLANK to the mode default on every
active format change, and derive the exposure range from it.

On the Surface Pro 5 the rear camera is used through libcamera's IPU3
pipeline, which does not change the frame length. With the 800x600 mode,
which libcamera picks for 640x480 and smaller requests, the exposure is
limited to ~11 ms and indoor images are black even at maximum analogue
gain. On this machine (SKU 1796/1807) default that mode to 30 fps by
tripling its frame length; higher frame rates remain available through
VBLANK.

Verified on a Surface Pro 5 (1796): 800x600 at 30.6 fps with the full
exposure range usable.

Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
With the readout and MIPI PLL values of the 60 fps binned mode, the IPU3
CIO2 of the Surface Pro 5 loses several frames per stream ("frame sync
error", payloads of 778 or 831 lines instead of 972). The 30 fps binned
values (MIPI PLL multiplier 0x7d, analogue 0x3620-0x3622, 0x3708,
0x3709, 0x371f) stream cleanly: 90 of 90 frames, no errors, through
libcamera at 640x480.

Use them by default; the module parameter binned_regs selects the old
set (0) or the 30 fps values with the full-mode PLL (2). Also set 0x4307
to 0x30 and add a blc_target parameter for the black level target
(0x4009) for further tuning.

Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
Signed-off-by: Marco Ziliani <rylos78@gmail.com>
Patchset: cameras
@CmdrShepardsPie

Copy link
Copy Markdown

I tested this PR's head (76d2ae0) as-is on a Surface Pro 9 (IPU6, Alder Lake-P 8086:465d): linux-surface's Fedora 7.2.12-1 package plus these 10 commits, with the front ov5693 through libcamera's soft ISP (Snapshot and cam).

Works on IPU6:

  • Mode switch (ac26433): the first stream after a switch got 30/30 frames, 18 of 18 times full→binned and 18 of 18 binned→full. Without it, that stream got no frames in 5 of 5 tries in my earlier test.
  • Binned 1296x972 with binned_regs 0, 1 (default) and 2: 3 × 300/300 frames each at 28.64 fps, no frame-sync errors or FIFO overflows, correct colours.
  • Full 2592x1944: 3 × 300/300 frames at 28.64 fps.
  • The unconditional MIPI_CTRL00 0x2d write caused no errors on IPU6.

Regression: full-resolution colours (1ee078c). On this unit the sensor runs with horizontal_flip=1 and vertical_flip=1 (rotation 180). Full-resolution images come out purple and green in Snapshot. Raw BG10 frames from the ISYS node show the delivered Bayer order doesn't match what the subdev reports (SBGGR10) when hflip is on:

Mode hflip/vflip Delivered
full 0/0 BGGR
full 1/0 GBRG
full 0/1 BGGR
full 1/1 GBRG
binned 0/0 and 1/1 BGGR

So in full mode the horizontal-flip column offset is one column off on this sensor. The row handling is right, and binned mode is right with both flips. Stock 7.2.11 and 7.3 give correct full-resolution colours on this unit. I'm happy to test a revision, and I'll add a Tested-by once full mode is fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants