Surface Go 2 rear camera detected but black on FydeOS v22.1 – IPU3 CSI-2 timeout

:pushpin:Question Description

**Have you searched the community for Have you searched the community for related issues?

Yes. I found previous Surface Go 2 camera reports, but my issue is not caused by the camera being disabled in UEFI.

Description

The front camera works on my Microsoft Surface Go 2, but switching to the rear camera results in a completely black screen.

After selecting the rear camera:

  • The Camera app shows a black image.
  • No frames are received.
  • I cannot switch back to the front camera.
  • Restarting the cros-camera service restores the front camera.
  • Selecting the rear camera again reproduces the problem.

The rear camera is enabled in Surface UEFI.

The same rear camera produced a visible image when I previously tested Mobian Linux, although camera support there was not fully polished. This suggests the rear camera hardware is functional.

Hardware and system information

Device: Microsoft Surface Go 2 LTE
Processor: Intel Core m3-8100Y
RAM: 8 GB
Storage: 128 GB
FydeOS product: FydeOS for You – Surface Go 2
FydeOS version: v22.1 stable
Build: 16503.20.22.7
Board: surface-go2-io
Kernel: 6.6.99-09011-gfdc62122de5f-dirty

Expected behaviour

The Camera app should switch between the working front OV5693 camera and rear OV8865 camera.

Actual behaviour

The rear camera is detected by the kernel and ChromeOS camera HAL, but opening it produces a black screen and no camera frames.

Hardware detection

The kernel detects all three Surface camera sensors:

ipu3-cio2 0000:00:14.3: Found supported sensor INT33BE:00
ipu3-cio2 0000:00:14.3: Found supported sensor INT347A:00
ipu3-cio2 0000:00:14.3: Found supported sensor INT347E:00
ipu3-cio2 0000:00:14.3: Connected 3 cameras

The required drivers are loaded:

ipu3_cio2
ipu3_imgu
ov5693
ov8865
intel_skl_int3472_discrete

The camera service also detects both normal cameras:

ProbeMediaController(): Probing sensor ‘ov5693 4-0036’
ProbeMediaController(): Probing sensor ‘ov8865 2-0010’

StartOnThread(): Camera module “libcamera camera HALv3 module” has 2 built-in camera(s)
StartOnThread(): SuperHAL started with 2 modules, 2 built-in cameras

Reproduction steps

  1. Boot FydeOS v22.1 on the Surface Go 2.
  2. Open the standard Camera app.
  3. Confirm that the front camera displays an image.
  4. Press the switch-camera button.
  5. The rear camera opens to a black screen.
  6. The application then cannot switch back to the front camera.

Relevant logs when rear camera is opened

OpenDeviceImpl(): CHROME, camera_id = 0, camera_module = libcamera camera HALv3 module, internal_camera_id = 0

ConfigureStreams(): id = 0, type = CAMERA3_STREAM_OUTPUT, size = 1600x1200, format = HAL_PIXEL_FORMAT_YCbCr_420_888

ConfigureStreams(): id = 1, type = CAMERA3_STREAM_OUTPUT, size = 1600x1200, format = HAL_PIXEL_FORMAT_BLOB

ipu3-cio2 0000:00:14.3: CSI-2 receiver port 0: escape mode ultra-low power state exit for clock lane

MonitorTimeoutOnThread(): No results for more than 2 s

MonitorTimeoutOnThread(): No requests for more than 2 s

Earlier attempts also produced repeated errors on CSI-2 receiver port 0:

multiple packet header errors detected
payload checksum (CRC) error
DPHY synchronization error

Android camera access also fails, including with a 640x480 preview, so this is not limited to the ChromeOS Camera application.

The following warning appears but may be unrelated because both cameras are still detected:

OnOptionsUpdated(): Cannot find feature profile as dict for device model “surface-go2”

Restarting the service temporarily restores the front camera:

restart cros-camera

Request

Please investigate the Surface Go 2 OV8865 rear-camera pipeline in the FydeOS v22.1 build.

The problem appears to be in the OV8865 / IPU3 CIO2 / libcamera pipeline because:

  • The rear sensor is detected.
  • The driver is loaded.
  • The camera HAL exposes it.
  • Opening it produces CSI-2 receiver errors.
  • No frames reach cros-camera.
  • The front camera works through the same camera service.
  • The rear camera previously produced an image under Mobian Linux.

I can provide the complete /var/log/messages output if required. issues?**

  • No similar issues found.

Please describe the problem you’ve encountered in detail:

  • Description:

:computer:System & Device Information

  • Hardware & System Configuration:

    • Processor:
    • Graphics Card(Please specify if there’s a discrete graphics card):
    • RAM:
    • FydeOS Version:
  • Relevant System Logs:

    • Open the Chromium browser and enter /var/log/messages to retrieve logs.
    • Log Details:

Note: If you’ve lost your FydeOS subscription, please refer to this FAQ for assistance.


The rear OV8865 probes and begins streaming, but CIO2 reports repeated CSI-2 packet-header, payload-CRC and D-PHY synchronisation errors, including at 640×480 from the Android camera stack. This suggests a sensor/CIO2 link configuration problem rather than an IPU3 tuning-file issue.
Linux 6.6 contains OV8865 PLL configurations for both 19.2MHz and 24MHz input clocks. Could you confirm which external clock rate the FydeOS build exposes to ov8865, which PLL table the driver selects, and whether the sensor link frequency and lane configuration match CIO2 receiver 0?
Separately, after the rear-camera stream times out, the camera HAL remains stuck and cannot recover or switch back to the front camera until restart. That appears to be an independent HAL recovery bug.

Thanks for the very detailed report — the symptom description is accurate and helpful.

Confirmed on our side: this is a real bug in our v22.1 Surface Go 2 build, not a configuration mistake or a UEFI issue. Your reasoning is right that the hardware is fine — the sensor probes, powers up and starts transmitting; the CSI-2 receiver just can’t decode what it sends.

Two separate problems here:

  1. The rear-camera CSI-2 errors. The DPHY-sync / packet-header / CRC / “ULPS exit for clock lane” combination points at a D-PHY timing mismatch — the receiver is timed for one bit rate while the sensor transmits at another. On this platform the sensor’s master clock is not programmed by the kernel; the INT3472 driver only gates it on and off and takes the frequency on trust from an ACPI table. If that table’s value doesn’t match the real oscillator, nothing in the kernel can detect it, and this is what you’d see. That’s my leading theory; a second possibility is the CIO2 capture mode we select on Surface boards.

  2. The stuck HAL. Independent and already confirmed from source: our camera pipeline has no timeout or recovery path, so when frames stop the session is never torn down and the front camera stays blocked. You found the right workaround — restart cros-camera. I’m fixing that separately, and it’ll land regardless of the rear-camera outcome.

There are two things you can run that would let me pick the right fix and both are read-only. In dev mode: Ctrl+Alt+Tshellsudo su.

(a) Please post the output of these — this is the most valuable thing:

dmesg | grep -iE 'ov8865|int3472|no external clock|clock rate'
cat /sys/kernel/debug/clk/clk_summary | grep -iE 'int3472|clk_name|rate'

The first tells me whether the driver found a clock at all; the second tells me the frequency it believes in and whether it’s actually enabled. If those two disagree with each other, that’s the bug.

(b) An A/B test that tells me whether the capture mode is involved:

stop cros-camera
cam -l                    # note the index of the ov8865 / back camera

dmesg -C
cam -c <N> -C30 -s role=raw,width=3264,height=2448 -s role=viewfinder,width=1600,height=1200
dmesg | grep ipu3-cio2

dmesg -C
cam -c <N> -C30 -s role=raw,width=1632,height=1224 -s role=viewfinder,width=640,height=480
dmesg | grep ipu3-cio2

start cros-camera

The second command takes a different path through our pipeline and puts the sensor in a lower-rate binned mode. If it captures frames cleanly while the first one errors, the fix is on our side and is small. If both error identically, it confirms the clock theory and I’ll go after that instead.

Your Mobian observation is genuinely useful, by the way — if you still have that install and can run dmesg | grep -i ov8865 there, the clock frequency it reports would settle this immediately.

I completed the requested A/B tests.

Rear camera is camera 1: Internal back camera (\_SB_.PCI0.LNK0).

Full-resolution mode configured as:

3264x2448-SBGGR10_IPU3 + 640x480-NV12

It produced no frames and remained stuck at Capture 30 frames until Ctrl+C. No ipu3-cio2 lines appeared afterward.

Binned mode configured as:

1632x1224-SBGGR10_IPU3 + 640x480-NV12

It also produced no frames and remained stuck until Ctrl+C. The only CIO2 line was:

ipu3-cio2 0000:00:14.3: CSI-2 receiver port 0: escape mode ultra-low power state exit for clock lane

After reboot, the OV8865 probes successfully, DW9719 is instantiated, and the TPS68470 clock rate is confirmed as 19200000.

I also attempted the requested media-ctl topology command, but this FydeOS v22.1 host image does not include media-ctl:

sudo: media-ctl: command not found

/dev/media0 and /dev/media1 are both present.

Therefore, both full-resolution and binned modes stall before delivering the first frame, and I cannot collect the requested media topology without a supplied media-ctl binary or an alternative command available in this build.

Thank you — the A/B test did exactly what I hoped and ruled out my main theory. Since both the full-resolution and binned modes fail the same way, the capture mode we select is not the problem, and I can stop looking there.

The 19200000 reading is also useful: it tells me the kernel and the camera PMIC agree on the sensor clock. If the firmware had asked for 24 MHz, the driver would have programmed 24 MHz and you’d have seen that number instead. So the clock is almost certainly fine too.

What your test did show is more interesting than either. Previously, through the Camera app, we saw a storm of corrupted-packet errors. Your cam runs show the opposite — the receiver is nearly silent, with just one “clock lane woke up” event and no data at all. That means the sensor powers on and brings up its MIPI clock, then never sends a frame. That’s a different and more tractable problem, so this round of testing genuinely moved things forward.

One more round and I think I’ll have it. Please run these and attach the two log files (they’ll be large — that’s expected):

stop cros-camera
cam -l                                  # confirm the indices; you reported rear = 1

# broken path
LIBCAMERA_LOG_LEVELS=*:DEBUG cam -c1 -C5 \
  -s role=raw,width=1632,height=1224 -s role=viewfinder,width=640,height=480 \
  > /tmp/cam-rear.log 2>&1
# let it sit ~10 s, then Ctrl+C

# working path, same code, for comparison
LIBCAMERA_LOG_LEVELS=*:DEBUG cam -c2 -C5 \
  -s role=viewfinder,width=640,height=480 \
  > /tmp/cam-front.log 2>&1

start cros-camera

The front-camera log matters as much as the rear one — the front sensor works through identical code, so diffing the two will show me exactly where the rear path diverges. These logs also contain the full media topology, so the missing media-ctl is no longer a blocker.

Also please post:

dmesg | grep -iE 'ov8865|dw9719|no external clock|clock rate|int3472'

I specifically want to know whether the line no external clock found, continuing... appears — that’s the last remaining way the clock could be wrong despite the correct rate reading.

cam-rear.log (225.5 KB)
cam-front.log (46.2 KB) > I completed the requested rear/front camera comparison and have attached both full debug logs.

  • cam-rear.log — 226 KB
  • cam-front.log — 47 KB

Both cam commands completed and returned to the prompt without needing Ctrl+C.

I also ran:

dmesg | grep -iE 'ov8865|dw9719|no external clock|clock rate|int3472'

It returned no output. In particular, there was no no external clock found, continuing... message.

The detected cameras were:

1: Internal back camera (\_SB_.PCI0.LNK0)

2: Internal front camera (_SB_.PCI0.LNK1)


































`cam -l` also reported that both `ov8865.yaml` and `ov5693.yaml` were missing.

These logs answered it — The software side of the rear camera is completely healthy. Buffers allocate, streaming starts, the pipeline runs end to end, and there isn’t a single error in 1,785 lines apart from a missing tuning file that the working front camera is also missing. The problem is one layer lower.

Comparing your two logs: the front camera reports 6 frame-start events over the capture. The rear camera reports 987 of them in 26 milliseconds. The receiver isn’t seeing frames — it’s seeing noise and mistaking it for the start of a frame, tens of thousands of times a second. That’s why the app reports 200 fps instead of 30, why the raw frame counter never advances past zero, and why the auto-exposure logic pushes gain to 100,000,000× and still measures nothing: every pixel arriving is black.

So the sensor is powered, configured and transmitting, but the receiver can’t lock onto the signal. Your original “black screen” and the packet/CRC errors you saw earlier are both symptoms of that single problem.

I produced a real image on this exact Surface Go 2 under Mobian using an earlier linux-surface kernel. It was extremely buggy and unreliable, but I definitely saw output from the rear camera.
That makes a completely failed camera module or disconnected MIPI link unlikely and suggests a configuration or regression difference between the earlier linux-surface camera stack and the FydeOS 6.6 build.
One distinction may still be useful: the current signature could mean either:
the OV8865 is genuinely streaming but CIO2 cannot lock onto its signal; or
the sensor never exits standby and CIO2 is interpreting floating-line noise as thousands of frame-start events.
Could we confirm the OV8865 mode-select register while a rear capture is running? I have seen this suggested:
i2ctransfer -y -f 2 w2@0x10 0x01 0x00 r1
but I do not want to force access while the kernel driver owns the sensor without first confirming that bus 2 and address 0x10 are correct for this build.
I can also attach a complete ACPI dump so you can inspect the INT347A SSDB data from this exact unit. Please let me know whether you would prefer:
acpidump -b
or particular tables/files from /sys/firmware/acpi/tables/.

Yes please, acpidump -b is the right form. One caveat so you know what to expect — the specific values I most want (lane count and clock rate) are filled in at runtime by firmware, so they’ll read as zero in a static dump. It’s still useful because your unit is the Core m3 LTE model and ours is the Pentium version, so the tables may differ. But the register reads above are what will actually settle it.

Neither acpidump nor the i2c-tools utilities are included in this FydeOS host image.

I copied the raw ACPI tables directly from:

/sys/firmware/acpi/tables

including its contents and dynamic tables, and have attached them as:

surface-go2-m3-lte-acpi-sysfs.tar.gz

Please confirm whether this raw sysfs archive is sufficient in place of acpidump -b.

I also checked for the register-reading tools:

i2ctransfer — not installed
i2cget      — not installed
i2cset      — not installed
i2cdetect   — not installed

I have not attempted to install anything into the FydeOS host system. Could you provide a compatible i2ctransfer binary or an alternative command available in this build so I can read OV8865 register 0x0100 while capture is active?
surface-go2-m3-lte-acpi-sysfs.tar.gz (64.0 KB)

Attached: fydeos-camera-debug.tar.gz (732 KB). It contains i2ctransfer, i2cget, i2cdetect, media-ctl, v4l2-ctl, yavta and acpidump.
fydeos-camera-debug.tar.gz (731.6 KB)

ov8865-capture.log (954 Bytes)
ov8865-probe-output.txt (6.4 KB)
I completed the supplied OV8865 register probe.

The supplied script initially could not locate the sensor because this build exposes it through the i2c-INT347A:00 ACPI path, so I adjusted only the bus-detection section; it resolved correctly to bus 2, address 0x10.

Results:

  • Correct chip ID: 00 88 65
  • Idle MODE SELECT 0x0100: 0x00
  • Capturing at both t+4s and t+7s: 0x01
  • MIPI configuration 0x3018: 0x72 — four lanes
  • PLL registers: 0x0300=0x02, 0x0302=0x4b — 19.2 MHz table
  • CIO2 still reports only: escape mode ultra-low power state exit for clock lane
  • No valid frames are delivered

Therefore the OV8865 genuinely exits standby with the expected 19.2 MHz PLL configuration, but CIO2 receiver 0 still cannot decode its CSI-2 stream.

I have attached the complete probe output and /tmp/ov8865-capture.log.

That’s the answer to the question you raised, and it rules out your second hypothesis cleanly: the sensor is genuinely streaming. Mode select goes 0x00 when idle and 0x01 during capture, exactly as it should; four lanes are programmed; the PLL is on the 19.2 MHz table. Everything the driver controls is correct. So the receiver is failing to decode a real signal, not misreading a dead bus.

Thanks also for fixing the bus detection — you were right, and I’ve folded your change into the attached version.

Your results pointed me somewhere specific. I went back through the power and clock wiring for both cameras and found that they don’t share a clock source. The front camera, which works, gets its clock from one controller; the rear camera gets its clock from a separate power-management chip (the TPS68470). That chip is the only piece of hardware unique to the camera that’s failing.

And there’s something questionable about how the kernel drives it. It calculates the sensor’s clock from a crystal frequency that is assumed, hard-coded, and never checked against the actual board. If that assumption is wrong for the Surface Go 2, the sensor would run at a slightly wrong speed — which the receiver cannot tolerate, and which would produce exactly what you’re seeing, while leaving the front camera untouched.

One more probe, and it’s read-only and quick — attached, tps68470-probe.sh:


sudo tar xzf fydeos-camera-debug.tar.gz -C /usr/local # overwrites the old copy

cd /usr/local/fydeos-camera-debug

sudo chmod +x *.sh bin/*

sudo ./tps68470-probe.sh

The two lines to watch during capture are CLKCFG1 and PLLCTL. If CLKCFG1 reads 0x00 or PLLCTL bit 0 is clear while capturing, the sensor is getting no clock at all and we have our answer. If they read 0x0a and 0xd1 with the expected dividers, the chip is doing its job, and the remaining suspects are the crystal itself and the physical lane wiring — neither of which software can read, so that’s where I’d move to a test kernel.

If you ran gnvs-camera-read.sh from the previous package, its output would be useful too.
fydeos-camera-debug.tar.gz (736.0 KB)

The TPS68470 probe had the same ACPI sysfs naming issue as the OV8865 probe.

I changed only the detection section to use:

/sys/bus/i2c/devices/i2c-INT3472:05

This resolved correctly to I²C bus 2, and the device is bound to the int3472-tps68470 driver.

The probe then identified:

TPS68470 at i2c bus 2, address 0x4c

However, every register read returned:

Error: Sending messages failed: Remote I/O error

This happened both while idle and during rear-camera capture, so I could not obtain valid CLKCFG1, PLLCTL or divider values.

I have attached the complete probe output. Does this FydeOS kernel require a different read method, such as accessing the driver’s regmap/debug interface rather than raw i2ctransfer?
tps68470-probe-output.txt (3.1 KB)

Yes — that’s exactly the right call, and the method you suggested works on this build.

The chip’s driver keeps a register interface exposed under debugfs, and on this kernel it’s configured without caching, so reading it gives the real hardware state rather than a copy of what the driver last wrote. That’s actually better evidence than the raw I2C read would have been.

Attached, tps68470-regmap.sh:


sudo tar xzf fydeos-camera-debug.tar.gz -C /usr/local # overwrites the old copy

cd /usr/local/fydeos-camera-debug

sudo chmod +x *.sh bin/*

sudo ./tps68470-regmap.sh

Same registers as before, same idle-vs-capturing comparison, plus a full dump and a listing of every I2C device on the system. Ignore the old tps68470-probe.sh.

The two values that matter, in the “while capturing” section:

  • CLKCFG1 — 0x0a means the clock outputs are being driven; 0x00 means they’re tri-stated and the sensor is getting no clock at all.

  • PLLCTL — bit 0 set (0xd1) means the PLL is running; 0xd0 means it isn’t.

REVID should read 0x21; if it does, we know the read path is trustworthy.

On your question about why the raw read failed — I don’t have a definitive answer yet. The sensor answered on the same bus, so the bus is fine and the PMIC specifically isn’t responding at the address I assumed. Either the address is different from what the firmware tables suggest, or something else owns it.
fydeos-camera-debug.tar.gz (737.4 KB)

tps68470-regmap-output.txt (4.4 KB)
The debugfs regmap probe completed successfully.

During capture:

  • REVID = 0x21
  • CLKCFG1 = 0x0a
  • PLLCTL = 0xd7 — bit 0 set
  • All clock dividers match the expected values:
    POSTDIV2=01, BOOSTDIV=03, BUCKDIV=02, PLLSWR=03, XTALDIV=aa, PLLDIV=20, POSTDIV=01, CLKCFG2=05

The values are identical while idle and capturing. This appears to confirm that the TPS68470 PLL is enabled and its clock outputs are being driven correctly.

I have attached the full regmap output. It looks like the next useful step is the test kernel you mentioned.

sudo tar xzf fydeos-camera-debug-modules.tar.gz -C /usr/local
cd /usr/local/fydeos-camera-debug-modules

sudo ./install-debug-modules.sh          # test 1
sudo reboot
# open Camera, switch to the rear camera, then:
dmesg | grep -i DEBUG

Then please run test 2 — it’s the decisive one:

sudo ./install-debug-modules.sh testpattern
sudo reboot

This makes the sensor generate colour bars internally, bypassing the lens and exposure entirely. If they come through, the data path works and the problem is much narrower than we think. If they don’t, the link genuinely can’t carry data, and tests 3 and 4 (lanes 2, plldiv 40) tell us why.
fydeos-camera-debug-modules.tar.gz (60 KB)

Test 2 completed.

I installed the sensor internal colour-bar test pattern, rebooted, opened the Camera app and switched to the rear camera. The rear camera remained completely black; no colour bars appeared.

The debug log confirms that the test pattern was enabled successfully:

ov8865 i2c-INT347A:00: DEBUG: forced test pattern 2 (ret 0)

It also shows the rear camera starting with:

lanes=4 link_freq=360000000 pixel_rate=288000000

I have attached camera-debug-test2.txt.

I have left the debug modules installed and am ready for the next test
camera-debug-test2.txt (1.9 KB)
camera-debug-test1.txt (1.9 KB)