Skip to content

Surface Pro 6: OV5693 (front) and OV8865 (rear) cameras produce green output — ISP tuning files missing #2166

Open

Description

DISCLAIMER: I have no idea what's going on, and I'm back with another claude-written issue. Any help for my situation is appreciated!!

Summary

Both cameras on the Surface Pro 6 are detected and enumerated correctly by
libcamera, but produce a fully green image in all applications. The root cause
is the absence of sensor-specific ISP tuning files (ov5693.yaml and
ov8865.yaml) for the IPU3 pipeline. Without them, libcamera falls back to
uncalibrated.yaml, which cannot correctly configure white balance, black
level correction, or color correction for these sensors.

Note: the wiki currently marks both OV5693 and OV8865 as ✅ for Surface Pro 6,
which is misleading — the sensors are detected, but the output is unusable.

Environment

  • Device: Microsoft Surface Pro 6
  • OS: Linux Mint 22 (Ubuntu 24.04 base)
  • Kernel: 6.19.8-surface-3
  • libcamera: v0.7.1+26-9107effd (built from source)

Current behavior

for IPA module 'ipu3', falling back to '/usr/share/libcamera/ipa/ipu3/uncalibrated.yaml'
WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'ov8865.yaml' not found
for IPA module 'ipu3', falling back to '/usr/share/libcamera/ipa/ipu3/uncalibrated.yaml'

Both cameras enumerate correctly:

1: Internal back camera (SB.PCI0.I2C3.CAMR)   [OV8865, CSI-2 receiver 0]
2: Internal front camera (SB.PCI0.I2C2.CAMF)  [OV5693, CSI-2 receiver 1]

Frames are captured at ~30fps with correct byte counts (921600 bytes for
1280×720 NV12), but the image is entirely green in all applications (Zoom,
Discord, cam --capture, GStreamer pipeline to v4l2loopback).

What has been confirmed working

  • Kernel detects all three sensors (OV5693, OV8865, OV7251)
  • dw9719 VCM binds correctly (fixed in patches/6.19: cameras: add dw9719 i2c_device_id table fix #2123)
  • libcamera v0.7.1 enumerates both cameras
  • PipeWire detects both cameras natively via the rebuilt libspa-libcamera
    plugin
  • Frames are streamed at correct resolution and framerate
  • The green output is produced by cam itself, not by any downstream
    GStreamer or v4l2loopback conversion

Root cause

The IPU3 ISP requires sensor-specific tuning files to correctly process Bayer
output. The uncalibrated.yaml fallback does not configure color correction,
white balance, or black level correction, resulting in a green image. The
Surface Pro 6's specific hardware implementation of the OV5693 and OV8865 has
never been calibrated for libcamera.

This is also reported (as a side note) in #2045 for the Surface Pro 5, and
appears to affect all IPU3-based Surface devices.

Request

Creation of ov5693.yaml and ov8865.yaml ISP tuning files for the Surface
Pro 6 hardware, to be shipped with libcamera or the linux-surface project. If
anyone has guidance on the calibration process or existing tuning files from
similar hardware that could serve as a starting point, that would be very
helpful.

Related

Activity

  1. toor11 commented on Jun 9, 2026

    @toor11

    We root-caused the rear-camera (ov8865) green-screen on a Surface Book 2, and at least that part of this issue is kernel-side, not a tuning-file problem — uncalibrated.yaml is sufficient to get a live stream (the front ov5693 streams fine with it).

    What actually happens: the ov8865 driver writes its sensor registers only in the runtime-PM resume handler — set_fmt() just stores the requested mode. On IPU3 platforms, ipu_bridge gives the dw9719 VCM a DL_FLAG_PM_RUNTIME device link to the sensor, and on a stock desktop wireplumber holds the VCM subdev open, which pins the sensor runtime-active. After that, format changes never reach the hardware: the sensor keeps streaming the mode programmed at the last resume while ipu3-cio2 expects the newly negotiated one → frame sync error / payload length is X, received Y → the stream stalls after a single bogus frame, which apps display forever (green in YUV).

    Smoking gun: with the binned mode pinned, requesting full-res fails with payload length is 10340352, received 2585088 — exactly one 1632x1224 binned frame where a 3264x2448 frame was expected. Stop wireplumber (so the sensor can suspend/resume and reprogram) and both modes stream 10/10 clean.

    Fix (program the negotiated mode at stream-on): submitted upstream to linux-media — https://lore.kernel.org/linux-media/[email protected]/ — and to the surface kernel as #2169. With it, the SB2 rear camera streams 40/40 frames at 30 fps through libcamera where it previously froze on frame 1.

    Tuning-file quality (colors/AE being rough with uncalibrated.yaml) is still a real, but separate, topic.

  2. N6RFM commented on Aug 2, 2026

    @N6RFM

    Confirming this also affects the Microsoft Surface Go (1st gen) with ov5693 (front) and ov8865 (rear) sensors, kernel 6.19.8-surface-3, Linux Mint.

    After applying the dw9719 i2c_device_id fix from PR #2123, both cameras are now detected and enumerated correctly by libcamera:

    1: Internal back camera (\_SB_.PCI0.LNK0)   ← ov8865
    2: Internal front camera (\_SB_.PCI0.LNK1)  ← ov5693
    

    Capture works (cam -c 2 --capture=3 runs at ~30fps), and Cheese opens successfully — but all output is green. The existing ov5693.yaml shipped with libcamera 0.7.0 appears to contain only algorithm stubs (Af, Agc, Awb, BlackLevelCorrection, ToneMapping) with no actual calibration data, which matches the behavior described here.

    A properly calibrated tuning file for ov5693 and ov8865 on Surface Go hardware would be the fix. Happy to assist with testing if anyone is working on calibration.

  3. ma-schmitz commented on Aug 12, 2026

    @ma-schmitz

    Same device here (Surface Pro 6, kernel 6.19.8-surface-3) and the identical symptom, so I can say what it actually was — the diagnosis in this report points the wrong way, and I think that is worth correcting because it sends people looking for tuning files that will not help.

    The green output is not a tuning problem. It is an empty buffer. I dumped the NV12 frames and they are all zeros — Y=0, U=0, V=0 — which renders as exactly that green. A frame suffering from missing white balance or colour correction would still contain image data; these contain none.

    The real causes are two separate kernel bugs, both with patches already open:

    With both applied, front and rear both work here.

    Two further measurements, since the report attributes the problem to the IPA:

    The tuning files change nothing. /usr/share/libcamera/ipa/ipu3/uncalibrated.yaml contains only a list of algorithm names with no parameters whatsoever. Copying it to ov5693.yaml / ov8865.yaml silences the warning and has no other effect.

    A newer libcamera does not help either. You mention building v0.7.1 from source. I measured mean frame luminance over 300 frames with a static scene: Ubuntu's stock 0.2.0 gives stdev 2.5, a self-built 0.7.2 gives 6.3. Both are stable after roughly the first 15 frames. The AGC is not the culprit.

    What does remain on this device, unrelated to the above: the rear sensor sits at maximum exposure (33 ms) in indoor light because its analogue gain caps at 16x, so handheld motion blurs. That is a sensor limitation, not a bug.

  4. luqqas96 commented on Aug 21, 2026

    @luqqas96

    Same missing tuning files on a Surface Pro 7+, with a detail that I think strengthens
    the case rather than just adding a "me too".

    This machine is IPU6 (Tiger Lake, 8086:9a19), not IPU3, so libcamera uses the simple
    pipeline and its software ISP. The warning is the same fault through a different IPA
    module:

    WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'ov5693.yaml' not found for IPA module
         'simple', falling back to '/usr/share/libcamera/ipa/simple/uncalibrated.yaml'
    WARN IPAProxy ipa_proxy.cpp:196 Configuration file 'ov8865.yaml' not found for IPA module
         'simple', falling back to '/usr/share/libcamera/ipa/simple/uncalibrated.yaml'
    

    Your Pro 6 hits it as ipa/ipu3/uncalibrated.yaml. Same two sensors, two different
    pipelines, both uncalibrated — which places the gap at the sensor level, not in either
    ISP. Whoever generates ov5693.yaml and ov8865.yaml fixes both machines and both
    pipelines at once.

    Both cameras here do produce a usable picture rather than solid green — white balance and
    exposure are just visibly poor, left to the software ISP's guesswork. So the IPU3 green
    output is probably a second, separate problem stacked on top of the missing tuning files,
    worth keeping distinct when someone works on this.

    Agreed on the wiki point, and it applies to the Pro 7+ column too: "sensor enumerates" and
    "camera is usable" are very far apart on these machines.

  5. javon27 commented on Aug 26, 2026

    @javon27

    (Posting this on behalf of the user, via Claude — flagging that upfront since it's not their own typing.)

    Surface Pro 7+ here too, IPU6, simple pipeline, same two sensors. Can confirm and narrow down what luqqas96 described ("usable but visibly poor white balance") — it's specifically a red channel clipping to 255 on every frame, and I think I've found the actual root cause in libcamera 0.7.2's SoftISP.

    Root cause: src/ipa/simple/algorithms/awb.cpp's AWB process() subtracts an 8-bit-scale blackLevel from stats->sum_. But for 10-bit sensors (ov5693/ov8865 both are), the stats accumulated by swstats_cpu.cpp (statsBGGR10Line0) are at native 10-bit depth (0–1023), never normalized down to 8-bit before that subtraction. That's a ~4x under-subtraction of black level in the AWB gain computation, which computes a wildly oversized red gain → red channel saturates.

    It's not AWB-only, though — I disabled the Awb algorithm entirely in the tuning file (so no white-balance gain at all is applied) and red still clips to 255/100%. So Agc is likely affected the same way, since it consumes the same 10-bit-scale stats for its exposure/gain convergence — meaning the actual sensor exposure is probably being driven up by the same scale bug, saturating red at the raw level before any color algorithm runs.

    Two tuning-file-only workarounds I tried, for the record, since they're the obvious first things to reach for and neither works:

    • CCM override (e.g. ccm: [0.40,0,0, 0,1,0, 0,0,1] to scale down R) — parses and instantiates fine, but has zero measurable effect on actual output (verified via pixel-level analysis of raw captures, byte-identical with/without). Looks like the CCM the IPA computes never actually reaches the GPU/EGL debayer shader in this pipeline — possibly worth a separate look (DebayerEGL::configure() has a [[maybe_unused]] bool ccmEnabled that made me suspicious).
    • Inflating blackLevel manually to compensate the AWB's under-subtraction at the source — doesn't fix the red clipping either, and additionally crushes the other channels toward black (green channel dropped to 0 across the whole frame in my test). Net worse.

    So as of 0.7.2 this looks like it needs an actual source fix to awb.cpp (and possibly the shared stats-scaling path Agc also reads from), not something fixable from a tuning file. Happy to test a patch against this hardware if anyone's working on one.

  6. fildunsky commented on Aug 31, 2026

    @fildunsky

    Data point from a Surface Pro 8 (OV5693 + OV13858 on IPU6, simple
    pipeline handler with the CPU software ISP), since this thread has become
    the place where IPU6 colour quality is discussed.

    On the AWB black-level claim earlier in the thread. I checked the
    0.7.0 sources: commit 07240afa12 ("libcamera: simple: Fix black level
    offsets in AWB") is indeed an ancestor of the v0.7.0 tag, so that
    particular bug is fixed in any current release. However there is a
    second, separate unit bug in the same area which is still present in
    current upstream:

    SwIspStats::sum_ is accumulated in native sensor units — for a 10-bit
    sensor the raw 0..1023 values, see SWSTATS_ACCUMULATE_LINE_STATS where
    the div argument normalises only the luminance histogram and not the
    sums. But Awb::process() subtracts context.activeState.blc.level,
    which is 8-bit (blc.cpp shifts the tuning value right by 8). So a 10-bit
    sensor gets a quarter of the intended black-level subtraction, and the
    error grows as the scene gets darker.

    I fix it by normalising the sums in SwStatsCpu::finishFrame() by the
    same factor the histogram already uses, after accumulation so no precision
    is lost per pixel. Patch:
    https://github.com/fildunsky/linux-surface-pro8-cameras/blob/master/patches/libcamera/softisp-stats-sums-to-8bit.patch

    On tuning files for OV5693 on IPU6. They do help, a lot, but two
    things caught me out and may be useful to whoever writes theirs:

    1. libcamera 0.7's simple AWB is plain grey world and cannot be given
      fixed gains at all — it does not read AwbEnable or ColourGains, it
      only reports its own gains in metadata. Measured on a white ceiling here,
      grey world sat at R/G = 0.88 (visibly green). Wiring the factory
      grey-card ratios from the .aiqb into libipa's Bayesian AWB moved that to
      within 1–2% of the factory curve. That needs a backport, since upstream's
      d5d00b9c is written against a later libipa than 0.7.0 ships.

    2. The factory calibration appears to be in pedestal-included units.
      OV5693 here has a black level of 64/1023 (both the .aiqb and
      libcamera's own histogram auto-detection agree to within half a count).
      Applying it is very much worth it for rendering — the darkest percentile
      goes from 63 to 0, contrast rises about 40% and saturation nearly doubles,
      which is exactly the "washed out" complaint. But doing so also throws the
      colour badly green, identically with grey world and with the Bayesian AWB,
      which suggests the colour matrices were derived without the pedestal
      removed. Either the matrices need re-deriving, or the pedestal has to be
      applied after the matrix rather than before. I have not solved this yet
      and would be glad to hear if anyone has.

    Also worth stating plainly for this thread: simple/softisp upstream has
    no sharpening and no lens shading correction algorithms at all, so
    whatever the .aiqb holds for those cannot currently be used.

    Full notes: https://github.com/fildunsky/linux-surface-pro8-cameras

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions