GMSL2 multi-camera sync on Jetson Orin: FSYNC setup guide
Synchronizing multiple GMSL2 cameras on Jetson requires hardware-level frame sync, software timestamp comparison introduces too much jitter for stereo or multi-view applications. The FSYNC signal travels through the GMSL2 control channel from Jetson to each camera, triggering simultaneous exposure across all sensors. This post covers the full synchronization setup, from DTS configuration to verifying sync is actually working.
Key Insights
- Hardware frame sync via FSYNC is the only reliable way to synchronize GMSL2 cameras, software approaches drift within seconds
- The FSYNC signal travels from Jetson GPIO → MAX9296A → GMSL2 control channel → MAX9295A → camera sensor trigger input
- Virtual channel assignment is mandatory for multi-camera setups, two cameras on the same VC corrupt both streams
- The MAX96724 quad-deserializer simplifies 4-camera designs by aggregating all streams into a single MIPI output
- Frame sync failures look like intermittent frame drops or tearing, not a complete pipeline failure, harder to diagnose
Why frame sync matters for multi-camera
If you are building a stereo, surround-view, or array camera system, unsynchronized frames are not a cosmetic problem, they cause real failures. Stereo depth estimation requires both cameras to capture the same moment in time. Surround-view stitching requires frames to align spatially. Even 10 ms of offset between cameras introduces motion artifacts that make the output unusable for most applications.
Free-running cameras, even cameras with identical settings, drift relative to each other over time because their internal oscillators have tolerance. Two cameras nominally running at 30 fps may be 16.7 ms apart (half a frame period) within a minute of start-up.
Hardware frame sync solves this by driving all cameras from a common trigger. Every camera captures on the same edge. Frame-to-frame timing error is in the microsecond range, which is acceptable for virtually any multi-camera application.
The FSYNC signal path in GMSL2
GMSL2 provides a control channel that runs bidirectionally alongside the video data. This control channel carries I2C/UART traffic and GPIO signals between the deserializer and serializer sides.
The FSYNC path for a dual-camera GMSL2 setup on Jetson Orin:
Jetson GPIO (configured as PWM or GPIO output)
→ MAX9296A FSYNC pin
→ GMSL2 control channel (both links simultaneously)
→ MAX9295A FSYNC pin (link 0)
→ MAX9295A FSYNC pin (link 1)
→ Camera sensor TRIGGER/SYNC input
This means a single Jetson GPIO triggers all cameras simultaneously. No separate wire per camera. No cable routing changes for additional cameras.
The FSYNC GPIO is configured in the DTS and the deserializer driver forwards it automatically once the link is locked.
/* Configure FSYNC GPIO in DTS */
max9296@48 {
/* ... */
fsync-gpios = <&tegra_aon_gpio TEGRA234_AON_GPIO(BB, 1) GPIO_ACTIVE_HIGH>;
fsync-mode = "GMSL2";
fsync-period = <3300000>; /* 33ms = ~30fps FSYNC period in nanoseconds */
};
Virtual channel assignment for multi-camera
The MAX9296A deserializer aggregates multiple camera streams onto a single MIPI output using MIPI CSI-2 virtual channels. Each camera gets a unique VC number.
Camera 0 → MAX9295A link 0 → MAX9296A → MIPI VC0 → NVCSI → V4L2 device 0
Camera 1 → MAX9295A link 1 → MAX9296A → MIPI VC1 → NVCSI → V4L2 device 1
The VC assignment is configured in the MAX9296A deserializer registers and must also be reflected in the DTS via vc_id:
/* Camera 0 — Virtual Channel 0 */
imx390@1a {
sensor_modes {
mode0 {
vc_id = "0";
/* ... */
};
};
};
/* Camera 1 — Virtual Channel 1 */
imx390@1b {
sensor_modes {
mode0 {
vc_id = "1";
/* ... */
};
};
};
If both cameras are on VC0, the NVCSI interleaves their data and both streams are corrupt. The failure looks like two unusable but active video streams, not a complete pipeline failure.
4-camera topology with MAX96724
For 4-camera designs on Jetson Orin, the MAX96724 quad-deserializer is the common solution. It supports 4 GMSL2 input ports and outputs 4 virtual channels over a single MIPI CSI-2 link.
Camera 0 → MAX9295A → MAX96724 port 0 → \
Camera 1 → MAX9295A → MAX96724 port 1 → MIPI (VC0-VC3) → Jetson NVCSI
Camera 2 → MAX9295A → MAX96724 port 2 → /
Camera 3 → MAX9295A → MAX96724 port 3 → /
The MAX96724 requires a different kernel driver and DTS structure than the MAX9296A. NVIDIA’s reference drivers in L4T R36 include MAX96724 support for specific carrier board configurations.
GStreamer pipeline for synchronized multi-camera capture
Once hardware sync is working, reading all streams in GStreamer uses nvarguscamerasrc per sensor fed into nvstreammux. The NVCSI hardware timestamps each frame at the moment of capture using a shared system clock, frames triggered on the same FSYNC edge arrive at the muxer with matching timestamps, so no software alignment is needed.
gst-launch-1.0 \
nvstreammux name=mux batch-size=2 width=1920 height=1080 \
batched-push-timeout=4000000 live-source=1 ! \
nvinfer config-file-path=config_infer.txt ! \
nvmultistreamtiler rows=1 columns=2 width=3840 height=1080 ! \
nvvidconv ! nv3dsink \
nvarguscamerasrc sensor-id=0 ! \
'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=30/1' ! \
nvvidconv ! mux.sink_0 \
nvarguscamerasrc sensor-id=1 ! \
'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=30/1' ! \
nvvidconv ! mux.sink_1
live-source=1 is important, without it, nvstreammux may block waiting for a full batch from a slow source, which introduces artificial latency. With live sources, it pushes partial batches after batched-push-timeout microseconds.
For stereo depth applications, you typically want to read frames in C++ via appsink and process them as a pair. The key is setting max-buffers=1 and drop=false on both sinks and using a mutex to collect the matching pair before dispatching to the stereo algorithm.
How to verify frame sync is working
The most direct verification is timestamp comparison across streams:
# Run both in parallel, capture 60 frames each
v4l2-ctl --stream-mmap -d /dev/video0 --stream-count=60 \
--stream-to=/dev/null -v 2>&1 | grep "ts" > /tmp/ts0.txt &
v4l2-ctl --stream-mmap -d /dev/video1 --stream-count=60 \
--stream-to=/dev/null -v 2>&1 | grep "ts" > /tmp/ts1.txt
# Compare corresponding timestamps
paste /tmp/ts0.txt /tmp/ts1.txt
With hardware sync, every row should show timestamps within 1ms of each other. With free-running cameras, the delta grows over time as oscillator drift accumulates.
A second verification: probe the FSYNC pin on the serializer board with an oscilloscope while the pipeline is running. You should see a clean square wave at the configured frame rate (e.g., 30Hz = 33ms period). If the pin is static, the DTS GPIO configuration is wrong or the deserializer driver isn’t forwarding the signal.
# Confirm FSYNC GPIO direction and state from software
gpioinfo | grep -A2 "BB 1"
gpioget <chip> <line> # should toggle at frame rate if correctly configured
Diagnosing sync failures
Cameras appear to capture but timestamps diverge over time
Hardware FSYNC is not reaching the sensors. Check that the GMSL2 link is locked (dmesg | grep "link locked"), then verify the FSYNC GPIO is configured as output in the DTS. Use gpioset to manually pulse the GPIO and confirm it toggles.
One camera captures correctly, the other shows corrupted frames
Virtual channel conflict. Both cameras are sharing a VC number. Check the vc_id values in the DTS, they must be unique per camera. Also verify the deserializer register configuration matches the DTS.
All cameras drop frames after a few seconds of sync operation
FSYNC period mismatch between the configured period and what the sensor expects. If the sensor expects 30fps but FSYNC is toggling at 25Hz, it will eventually lose lock. Verify fsync-period in nanoseconds matches the target frame rate exactly.
nvarguscamerasrc fails to open sensor after adding FSYNC config
A DTS error in the FSYNC node is preventing driver probe. Check dmesg | grep max9296 for probe errors and compare the GPIO specifier against your carrier board schematic.
For complex GMSL2 bring-up problems, multi-camera with unusual serializer configurations, custom carrier boards, or mixed sensor arrays, the GMSL camera driver development service covers what a fixed-bid engagement typically involves. For the broader GMSL2 pipeline setup, see GMSL2 camera driver on Linux: V4L2, MAX9296 kernel driver, device tree.
The MIPI CSI-2 virtual channel specification is in the MIPI Alliance CSI-2 standard. The MAX9296A FSYNC configuration registers are documented in the Analog Devices MAX9296A datasheet.
Relevant Services
NVIDIA Jetson Expert Support
Stuck on a Jetson bring-up?
We've debugged this failure mode before. BSP, device tree, camera pipelines, OTA, most blockers clear in the first session. No long retainers. No guessing.
Frequently Asked Questions
What is the difference between hardware frame sync and software frame sync for GMSL2 cameras?
Hardware frame sync uses a dedicated FSYNC signal from the Jetson GPIO (or an external sync source) routed to each serializer via the GMSL2 control channel. All cameras trigger on the same hardware edge, so frames are captured within microseconds of each other. Software frame sync relies on the cameras running at the same nominal frame rate and the host timestamping frames as they arrive, frame capture offsets can be 10-50ms, which is unacceptable for stereo or surround-view applications.
How does the FSYNC signal reach GMSL2 cameras over coaxial cable?
The MAX9296A deserializer has a FSYNC input pin. When the host drives this pin, the deserializer forwards the signal to each connected serializer through the GMSL2 control channel. Each MAX9295A serializer then drives the FSYNC pin to the camera sensor. This is the primary mechanism for hardware frame sync in GMSL2 multi-camera systems, no separate physical wire to each camera is needed.
What is a virtual channel in GMSL2 and why does it matter for multi-camera?
MIPI CSI-2 supports 4 virtual channels (VC0–VC3) on a single physical link. In a GMSL2 multi-camera setup, each camera's data stream is assigned to a different virtual channel by the deserializer before being output over the MIPI link to Jetson. The Jetson NVCSI demultiplexes the virtual channels into separate capture streams. If two cameras are on the same virtual channel, their data is interleaved and both streams are corrupt.
Can I run 4 GMSL2 cameras on a single Jetson Orin NX?
Yes, with the right hardware. Jetson Orin NX supports 4 NVCSI ports and up to 8 MIPI lanes total. A quad-deserializer (MAX96724 or similar) connects to one or two NVCSI ports and outputs all 4 camera streams over MIPI using 4 virtual channels. Each camera uses 2 MIPI lanes (2-lane per camera × 4 cameras = 8 lanes total). Orin NX can handle this, but Orin Nano (fewer lanes) may require a different topology.
My multi-camera GMSL2 setup shows frame tearing on alternating cameras. What causes this?
Frame tearing between cameras almost always means hardware frame sync is not working and the cameras are free-running at slightly different effective frame rates. Even cameras with nominally identical frame rate settings drift relative to each other because crystal oscillators have tolerance. The fix is enabling hardware FSYNC so all cameras trigger from the same edge. Verify the FSYNC signal is reaching each serializer and that each sensor's trigger input is configured for external sync mode.
How do I verify GMSL2 hardware frame sync is actually working?
Capture timestamps from two camera streams simultaneously using v4l2-ctl and compare them. With hardware sync, the delta between corresponding frames should be under 1ms consistently. With free-running cameras, the delta will vary by tens of milliseconds and drift over time. You can also probe the FSYNC pin on the serializer board with an oscilloscope to confirm the trigger pulse is arriving.
What happens if the FSYNC GPIO is not configured correctly in the device tree?
The cameras will free-run. The FSYNC signal never reaches the serializer, so each camera triggers independently from its internal oscillator. There is no error message, the pipeline starts and frames are captured, but timestamps between cameras will diverge. Check that fsync-gpios is set correctly in the deserializer DTS node and that the GPIO direction is output. Use gpioset to manually toggle the GPIO and confirm the pin responds before debugging the driver.
Can I mix different camera sensor models in a GMSL2 synchronized array?
Yes, with care. All sensors in the array must support external trigger mode and must be configurable to the same frame rate. The FSYNC timing (pulse width, period) must be compatible with every sensor's trigger input specification. The most common problem with mixed sensors is that they have different trigger-to-first-line delays, meaning the actual exposure timing differs even when the trigger edge is shared. For stereo applications, identical sensors are strongly preferred.
What is GMSL2 link lock and why must it be established before FSYNC works?
GMSL2 link lock is the state where the serializer and deserializer have negotiated and established a stable high-speed serial link. The GMSL2 control channel, which carries I2C, GPIO, and the FSYNC signal, only operates over a locked link. If the link is not locked, the deserializer cannot forward the FSYNC pulse to the serializer, and the camera receives no trigger. Always confirm link lock via i2c register read or dmesg before debugging FSYNC issues.
How do I read synchronized GMSL2 frames in GStreamer with correct timestamps?
Use nvarguscamerasrc for each camera, one per sensor-id, and feed all streams into nvstreammux. Set live-source=1 on nvstreammux so it does not block waiting for slow streams. The NVCSI hardware timestamps each frame at capture time using a shared system clock, so frames captured on the same FSYNC edge will have timestamps within microseconds of each other, no software timestamp alignment is needed.
Written by
Aarón AnguloCo-Founder & CEO · ProventusNova
Obsessed with client outcomes. Aarón ensures every engagement delivers real results, on time, on scope, no exceptions.
Connect on LinkedInRelated Articles
Jetson multi-camera sync: CSI hardware trigger setup
Synchronize multiple CSI cameras on Jetson with hardware trigger sync. FRSYNC GPIO, DTS config, sensor trigger mode, GStreamer pipeline, and verification.
GMSL2 camera bring-up on Jetson Orin: MAX9295/MAX9296 setup
Step-by-step GMSL2 camera bring-up on Jetson Orin with MAX9295A and MAX9296A. Link lock, I2C tunnel, device tree, and first frame verification.
Choosing a GMSL2 carrier board for Jetson Orin: what to check
How to evaluate a GMSL2 carrier board for Jetson Orin before you buy. Port count, deserializer chipset, BSP support, and the questions most vendors don't.
Custom GMSL2 camera driver for Jetson: V4L2 subdev
Write a custom GMSL2 sensor driver for Jetson Orin. tegra-camera-platform ops, sensor mode table, register tables, SerDes integration, and troubleshooting.