Large update from the Surface Go 2 / OV8865 investigation. I think the userspace-side elimination work is now about as complete as I can make it.
Environment:
- Mobian
- linux-surface 6.18.7-surface-1
- libcamera 0.7.2
- rear OV8865 = CSI-2 receiver / CIO2 port 0
- front OV5693 = port 1
- OV7251 IR sensor = port 2
=== 1. A genuinely GOOD rear state was captured ===
Earlier in the same session the OV8865 genuinely worked.
I had coherent real rear-camera images and also captured the rear directly from CIO2 using:
1632x1224
ip3b
BPL 2112
SizeImage 2585088
20/20 direct RAW frames contained dense real data:
~95.5% non-zero bytes per frame
~87.2% compressed size
0 CIO2 payload warnings
So the exact direct-RAW harness is proven to work on this machine/kernel.
During that GOOD state:
HBLANK = 291
and I saved a full OV8865 register snapshot including:
0x0100
0x0300-0x0312
0x3018-0x3019
0x3020-0x3022
0x3031-0x3033
0x3106
0x3808-0x380f
0x3820-0x3821
0x4837
0x4850-0x4851
0x5000-0x5002
0x5e00
Example GOOD timing/config:
0x0100 = 01
0x0302 = 4b
0x3018 = 72
0x3808/09 = 0660 = 1632
0x380a/0b = 04c8 = 1224
0x380c/0d = 0783 = HTS 1923
0x380e/0f = 04de = VTS 1246
0x4837 = 16
0x4850 = 10
0x4851 = 32
0x5000 = 16
0x5001 = 01
0x5002 = 08
0x5e00 = 00
=== 2. Suspend gave me a reproducible GOOD → BAD transition ===
While the rear was confirmed GOOD, I closed the lid and suspended the machine.
After resume the rear immediately returned to the broken state.
A 1280x720 NV12 sample gave:
frame gzip ~1408 bytes
i.e. the familiar effectively-empty output.
I dumped exactly the same OV8865 register set before rebooting or changing anything.
GOOD vs BAD register diff:
diff exit status: 0
good-vs-bad.diff size: 0 bytes
Every measured OV8865 register was byte-for-byte identical between the proven GOOD state and the proven BAD post-suspend state.
HBLANK was also still 291 while completely broken.
So stale HBLANK=624 is not necessary for the fault and cannot be the root cause.
=== 3. BAD direct rear RAW reproduces the original low-level failure ===
While still in that post-suspend BAD state I captured the rear directly from CIO2 again.
20/20 frames:
non-zero bytes = 0
0.00%
Kernel payload warnings during the capture:
24 x “received 0”
I do not assume the 24 warnings correspond one-to-one with the 20 userspace buffers, but every reported mismatch said received=0 and every captured RAW frame was entirely 0x00.
At the same time CIO2 port 0 repeatedly reported:
DPHY synchronization error
single packet header error corrected
frame sync error
=== 4. Front camera remains healthy after the same suspend ===
Without rebooting, while the rear remained broken, I captured the front OV5693 directly through CIO2 port 1.
5 frames:
96.39% non-zero
96.04%
95.98%
96.39%
96.39%
So the same suspend/resume leaves port 1 carrying dense real RAW while port 0 is completely zero.
=== 5. CIO2 port 0 is actually reprogrammed correctly in the BAD state ===
I read the CIO2 MMIO registers while the broken rear stream was definitely still active and PCI runtime state was “active”.
BAD rear / port 0:
ENABLE 0x00000001
NOF_LANES 4
SP_IF_CONFIG 0x00000000
LP_IF_CONFIG 0x00000000
STATUS 0x00000000
DLANE_HS varied between 0x00 and 0x50 in different BAD runs
DLANE_LP 0x000000
CLK settle 1343
D0 settle 1316
D1 settle 1316
D2 settle 1316
D3 settle 1316
So the expected 4-lane configuration and 1343/1316 timing were restored correctly.
Working front / port 1 was:
ENABLE 1
NOF_LANES 2
STATUS 1
CLK settle 1368
D0/D1 settle 1322
Initially STATUS looked interesting, but see the IR control below: a working port can also have STATUS=0, so STATUS is not a GOOD/BAD discriminator.
=== 6. TPS68470 software bookkeeping AND actual hardware registers checked ===
In a proven BAD rear state, Linux reported:
CORE = enabled, 1.200000 V
ANA = enabled, 2.815200 V
VCM = enabled, 2.815200 V
VIO = 1.800600 V, one user
VSIO = enabled, 1.800600 V
tps68470-clk:
rate = 19200000
non-zero prepare/enable counts
Because clk_summary is only framework bookkeeping, I then read the TPS68470’s actual register map through:
/sys/kernel/debug/regmap/i2c-INT3472:05/registers
The real PMIC hardware registers in the BAD state match the driver’s 19.2 MHz clock profile exactly:
06 = 01
07 = 03
08 = 02
09 = 03
0a = aa
0b = 20
0c = 01
0d = d7
0e = 00
0f = 0a
10 = 05
Against the driver’s 19.2 MHz entry:
XTALDIV = 170 → aa
PLLDIV = 32 → 20
POSTDIV = 1
POSTDIV2 = 1
BUCKDIV = 2
BOOSTDIV = 3
PLLCTL also has PLL_EN set, and CLKCFG1/2 match the expected output configuration.
The regulator/power registers also contain the expected programmed values.
=== 7. Rear TPS68470 reset/powerdown state is correct ===
Actual PMIC register:
SGPO 0x22 = 0x05
gpiolib reports:
gpio-7 powerdown out hi ACTIVE LOW
gpio-9 reset out hi ACTIVE LOW
So both rear-camera powerdown and reset are deasserted while the rear is BAD.
The OV8865 is therefore not simply being held in reset or powerdown.
=== 8. Most useful control: the OV7251 IR camera WORKS ===
The OV7251 is the other sensor using the TPS68470 clock provider/PLL, so I enabled its normally-disabled CIO2 port-2 link and captured it directly.
640x480 Y10 / ip3y
10 frames
3,993,600 bytes total
non-zero bytes:
3,688,089 = 92.35%
compression:
45.76%
So this is dense real sensor data.
The only port-2 kernel message was:
“escape mode ultra-low power state exit for clock lane”
The IR path otherwise worked.
I also read CIO2 port 2 while the working IR stream was active:
ENABLE 0x00000001
NOF_LANES 1
STATUS 0x00000000
DLANE_HS 0x00
DLANE_LP 0x000000
CLK settle 1254
D0 settle 1294
This also proves that STATUS=0 and DLANE_HS=0 do not by themselves indicate a failed receiver, because port 2 was producing real data with those values.
=== 9. Current three-port comparison ===
Port 0:
OV8865 rear
BROKEN
all-zero RAW
DPHY / frame-sync / header errors
Port 1:
OV5693 front
WORKING
dense RAW
Port 2:
OV7251 IR
WORKING
92.35% non-zero RAW
So two other CIO2 ports work, including the OV7251 using the TPS68470 clock system, while only the rear OV8865 / port-0 path fails.
=== 10. Recovery is NOT understood ===
The earlier GOOD state was real, but I have not been able to reproduce the transition back to GOOD.
After returning to BAD I tried:
- normal reboot → BAD
- full shutdown/power-off, left off ~1 hour, then cold boot → BAD
- forced power-button reboot → BAD
- different camera application → BAD
- another suspend/resume → BAD
All continue to give the same ~1405-1408 byte gzip empty-frame result.
Therefore the earlier recovery after a hard reboot was coincidental with some other state change; hard reboot should NOT currently be considered a recovery mechanism.
Suspend is established as at least one reproducible GOOD → BAD trigger, but BAD → GOOD is currently unknown.
=== 11. One invalid experiment to explicitly disregard ===
I tried unbinding/rebinding only the OV8865 driver.
The driver reprobed, but its media link did not get reconstructed:
ov8865 2-0010
1 pad
0 link
0 routes
and streaming then failed with:
“Link has been severed”
So that experiment tells us NOTHING about whether a sensor-only reset would recover the failure. Please disregard it as a recovery result.
=== Current elimination map ===
While the rear is definitely BAD:
- measured OV8865 registers: correct
- GOOD vs BAD OV8865 register diff: byte-identical
- HBLANK: correct
- OV8865 is streaming according to 0x0100
- TPS68470 PLL/divider hardware registers: correct
- TPS68470 regulator hardware registers: correct
- rear reset/powerdown GPIO state: correct
- CIO2 port-0 lane count/timing/configuration: correct
- front / CIO2 port 1: working
- IR / CIO2 port 2: working
- IR uses the TPS68470 clock system and transmits real data
- rear / CIO2 port 0: zero payload + CSI-2/DPHY errors
This appears to leave a fairly narrow remaining boundary:
- hidden/internal OV8865 MIPI transmitter state not represented by the registers we’ve read;
- the physical OV8865 → CIO2 port-0 CSI-2 link;
- internal CIO2 port-0/DPHY state not visible in the registers we’ve inspected;
- or sequencing/timing between those components.
I think I’ve exhausted useful read-only userspace diagnostics at this point.
I have preserved the GOOD RAW capture/register baseline and the BAD sensor registers, CIO2 dumps, PMIC regmap, kernel logs, front control, IR control and GPIO state.
If you want another diagnostic, could you point me at the kernel-side state or instrumentation you think would discriminate between the OV8865 transmitter side and CIO2 port 0? I would rather follow a targeted test now than keep changing state speculatively.