This document details the hardware and software architecture of the Surface Pro 5 IR Camera pipeline, specifically outlining the initialization, streaming, and hardware control phases necessary to achieve a stable, properly exposed video output.
The Surface Pro 5 uses an OmniVision OV7251 IR sensor connected via MIPI CSI-2 to an Intel IPU3 (Image Processing Unit 3). In mainline Linux kernels (via the linux-surface project), the IPU3 driver manages the sensor but lacks the vendor-specific algorithms to internally turn on the sensor's IR privacy LED or drive the ISP for usable user-facing video.
To overcome this, our pipeline acts as a bridge, manually managing sensor timing, hardware illumination (I2C injection), and software-based unpacking (IPU3 native 10-bit packed to standard 8-bit BGRA).
The pipeline consists of three discrete layers:
- The Kernel Data Path (V4L2):
ov7251: The physical subdevice node (/dev/v4l-subdevX).ipu3-csi2: The MIPI receiver.ipu3-cio2: The DMA engine outputting raw frames to/dev/video14.v4l2loopback: The virtual sink presenting the final video at/dev/video20.
- The Hardware Injector (
scanner):- A Rust daemon that bypasses the kernel's V4L2 abstraction to write directly to the physical I2C bus (Bus 3, Address
0x60). It forces the IR LED on and sets "Super-Boost" exposure/gain.
- A Rust daemon that bypasses the kernel's V4L2 abstraction to write directly to the physical I2C bus (Bus 3, Address
- The Software ISP (
surface-IR-rust):- A high-performance Rust engine that consumes raw IPU3 stream data via standard input (
stdin), unpacks the custom 10-bit format into 8-bit grayscale, rotates the image physically, converts it to 32-bit BGRA, and streams it to the virtual loopback device.
- A high-performance Rust engine that consumes raw IPU3 stream data via standard input (
The successful operation of the camera is governed by a strict sequence of events. If any condition fails or happens out of order, the stream will freeze, or the LED will remain off.
Before any stream begins, the physical topology must be linked and configured using media-ctl and v4l2-ctl.
- Action: Link
ov7251->ipu3-csi2->ipu3-cio2. - Action: Set the physical capture format on the subdevice to
SGRBG10_1X10(Bayer 10-bit) at 640x480. - Condition: The
vertical_blankingregister on the OV7251 must be normalized (e.g., to 1244). If this is not set correctly or fluctuates wildly, the IPU3 CSI-2 receiver loses sync (DPHY error), causing frame drops and a highly stuttering video feed.
When a consumer (like v4l2-ctl --stream-mmap or the Rust ISP via stdin) requests the stream to start, the IPU3 kernel driver takes temporary, absolute control of the sensor.
- Action: The IPU3 driver powers up the sensor, negotiates the MIPI CSI-2 physical link, and internally resets the OV7251 registers to their default state to prepare for frame acquisition.
- Critical Condition: Because of this reset, any manual hardware hacks (like turning on the IR LED) applied before this phase are wiped out.
To circumvent the driver reset, our scanner daemon employs a delayed, one-shot injection strategy.
- Action: The capture script (
run_capture.sh) launchesv4l2-ctl(which triggers Phase 2) and thescannerdaemon simultaneously in the background. - Action: The
scannerimmediately halts and waits for exactly 2.5 seconds. This provides the IPU3 driver enough time to finish its reset routine and stabilize the stream. - Action: After 2.5 seconds, the
scannerwrites the0x3bxxhardware registers (Strobe PWM Sync, Pulse Width) directly via I2C (i2ctransfer). - Condition: The injection must execute exactly once. If the daemon attempts to re-inject or continuously poll the I2C bus while the stream is active, the bus contention causes the IPU3's MIPI receiver to lose sync (
DPHY synchronization error), freezing the stream for ~1 second intervals.
Once the stream is flowing and illuminated, the Rust ISP processes the data.
- Action: Raw binary blobs (Intel IPU3 10-bit packed format) are piped into the ISP via
stdin. Each 640x480 frame arrives as exactly 399,360 bytes. - Action: The ISP unpacks every 5 bytes into 4 pixels of 10-bit data, immediately downshifting them to 8-bit grayscale for speed.
- Action: The 8-bit raw buffer is rotated 90 degrees clockwise to correct the sensor's physical 90-degree mounting orientation on the Surface Pro 5.
- Action: The 8-bit grayscale is expanded to 32-bit BGRA (Blue, Green, Red, Alpha) space.
- Action: The final BGRA buffer is written to
/dev/video20(thev4l2loopbacksink). - Condition: For video applications (
ffplay,fswebcam, Howdy) to read the stream without artifacts, they must be explicitly instructed to acquire the stream inbgr0(or compatible 32-bit format) at exactly 640x480.
If diagnosing future issues, verify these three pillars of stability:
- V-Blank Normalization: If the video is stuttering rapidly right out of the gate, the V-Blank timing is wrong. Fix it via
v4l2-ctl --set-ctrl=vertical_blanking=.... - I2C Silence During Streaming: If the video stutters periodically (e.g., every 2 seconds) and the kernel log (
dmesg) showsDPHYerrors, a secondary process is writing to the sensor's I2C bus concurrently. No process other than the IPU3 driver should touch the I2C bus while streaming. - Post-Initialization Injection: If the IR LED stays dark, the injection happened too early and was overwritten by the IPU3 sequence. Ensure the minimum 2-second delay.