Jump to content

How to Create a GMSL2 Driver for NVIDIA Jetson

From RidgeRun Developer Wiki

Follow us on: YouTube Twitter LinkedIn Email Share this page

Share This Page

How to create a GMSL2 driver for NVIDIA Jetson

A GMSL2 camera driver for NVIDIA Jetson integrates the image sensor, GMSL2 serializer/deserializer, MIPI CSI-2 transport, Jetson NVCSI/VI capture path, device tree, and V4L2 camera interfaces. Bring up one camera and one mode first: prove remote I2C, then sensor streaming, SerDes video transport, CSI-2 reception, and V4L2 buffers before adding virtual channels, synchronization, or multiple cameras.

RidgeRun has experience with more than 50 NVIDIA Jetson camera drivers and complex serializer/deserializer camera architectures. See Software Services for NVIDIA Jetson, V4L2 drivers available for Jetson SoCs, and V4L2 driver for camera sensor or capture chip.


Architecture

Sensor -> MIPI CSI-2 -> GMSL2 Serializer -> GMSL2 link
       -> GMSL2 Deserializer -> MIPI CSI-2 -> Jetson NVCSI -> VI -> Application
Main responsibilities
Block Responsibility
Sensor Modes, exposure, gain, timing, stream control.
Serializer Remote control channel and CSI input transport.
Deserializer Link reception, video pipes, routing, virtual channels, CSI output.
NVCSI / VI CSI packet reception and frame capture.

The physical GMSL2 chain does not necessarily map one-to-one to independent V4L2 devices. Follow the ownership model of the exact Jetson Linux camera framework.

Define the Exact Target

Record the Jetson module, carrier, JetPack/L4T release, kernel, sensor, serializer, deserializer, I2C addressing scheme, resolution, frame rate, media-bus format, CSI lane count, CSI port, virtual channels, sensor clock, reset signals, and required capture API.

RidgeRun has worked with several GMSL2 configurations. Chips we have used before are:

Chip Type
MAX9295A GMSL2 CSI-2 Serializer
MAX9296A Quad GMSL2 CSI-2 Deserializer
MAX96712 Quad GMSL2 CSI-2 Deserializer
MAX96714 Single GMSL2/GMSL1 CSI-2 Deserializer
MAX96716A Dual GMSL2 CSI-2 Deserializer
MAX96717 GMSL2 CSI-2 Serializer
MAX96724 Quad GMSL2/GMSL1 CSI-2 Deserializer
MAX96792A Dual GMSL3/GMSL2 CSI-2 Deserializer
MAX96793 GMSL3/GMSL2 CSI-2 Serializer

Check Existing Drivers First

Inspect the NVIDIA BSP, upstream Linux, camera-module vendor sources, and RidgeRun resources before starting a new driver.

V4L2 drivers available for Jetson SoCs lists GMSL2 among tested technologies. GMSL serializer/deserializer Linux driver and MAX96705 MAX9286 GMSL SERDES Linux Drivers are useful historical references, but they target GMSL1-era devices and older Jetson releases; do not reuse their register programming as GMSL2 instructions.

Calculate Bandwidth

Start with:

active_payload = width x height x fps x bits_per_pixel

For 1920 x 1080 RAW12 at 30 fps:

746,496,000 bit/s

This is only active image data. Account for actual sensor timing, CSI-2 overhead, metadata, lane count, and every stream aggregated by the deserializer. Sensor pixel rate, CSI link frequency, and GMSL2 line rate are different quantities.

Bring Up the GMSL2 Control Channel

Do not begin at `/dev/video0`. Validate:

  1. Power rails, clocks, and resets.
  2. Local deserializer ID.
  3. Required GMSL2 link/control configuration.
  4. Remote serializer ID.
  5. I2C aliases or translation.
  6. Sensor ID.
  7. Repeatability after a cold boot.
Deserializer: PASS
Control link: PASS
Serializer:   PASS
Sensor:       PASS
Cold boot:    PASS

Identical camera modules commonly reuse native I2C addresses. Configure the SerDes-supported translation or isolation mechanism before probing all cameras.

A locked GMSL2 link does not prove that valid frames reach Jetson. Sensor streaming, deserializer video routing, CSI output, NVCSI, and VI must still be validated.

Implement the V4L2 Sensor Driver

A sensor subdevice typically provides register access, mode tables, media-bus formats, controls, power management, pads, and stream operations.

Typical controls include:

V4L2_CID_EXPOSURE
V4L2_CID_ANALOGUE_GAIN
V4L2_CID_VBLANK
V4L2_CID_HBLANK
V4L2_CID_PIXEL_RATE

Expose only real hardware modes and correct control ranges.

Keep responsibilities conceptually separated:

Sensor:
  mode / exposure / gain / timing / stream

SerDes:
  remote I2C / GMSL2 link / routing / VC / CSI output / diagnostics

The exact source-code boundary depends on the NVIDIA framework and release.

Configure the Jetson Device Tree

Verify sensor/SerDes nodes or framework properties, regulators, clocks, reset GPIOs, sensor modes, CSI lanes, port assignment, endpoints, pixel format, virtual channel, frame timing, and SerDes-specific timing properties.

An example of a complete device tree node showing a deserializer (MAX96724), a serializer (MAX96717) and a sensor (Sony IMX623) is as follows:

max96724@27 {
                csi-mode = "1x4a";
                #address-cells = <0x01>;
                #size-cells = <0x00>;
                status = "okay";
                compatible = "maxim,max96724";
                reg = <0x27>;
                des-id = <0x00>;
                link@0 {
                    reg = <0x00>;
                    #address-cells = <0x01>;
                    #size-cells = <0x00>;
                    status = "okay";
                    phandle = <0x25f>;
                    max96717@0x41 {
                        #address-cells = <0x01>;
                        #size-cells = <0x00>;
                        reg = <0x41>;
                        status = "okay";
                        compatible = "maxim,max96717-f";
                        des-id = <0x00>;
                        ser-id = <0x00>;
                        maxim,default-addr = <0x40>;
                        maxim,sensor-alias = <0x1b>;
                        maxim,sensor-addr = <0x1a>;
                        maxim,sensor-type = "imx623";
                        phandle = <0x260>;
                        imx623_a@1b {
                            compatible = "nvidia,imx623";
                            reg = <0x1b>;
                            devnode = "video0";
                            status = "okay";
                            physical_w = "3.680";
                            physical_h = "2.760";
                            sensor_model = "imx623";
                            use_sensor_mode_id = "false";
                            mode0 {
                                mclk_khz = "24000";
                                num_lanes = "2";
                                vc_id = "0";
                                tegra_sinterface = "serial_a";
                                phy_mode = "CPHY";
                                discontinuous_clk = "no";
                                dpcm_enable = "false";
                                cil_settletime = "30";
                                lane_polarity = "0";
                                mclk_multiplier = "12";
                                pix_clk_hz = "126000000";
                                serdes_pix_clk_hz = "368000000";
                                embedded_metadata_height = "0";
                                mode_type = "bayer";
                                pixel_phase = "rggb";
                                csi_pixel_bit_depth = "12";
                                dynamic_pixel_bit_depth = "12";
                                active_w = "1920";
                                active_h = "1536";
                                readout_orientation = "0";
                                line_length = "2400";
                                inherent_gain = "1";
                                gain_factor = "16";
                                min_gain_val = "16";
                                max_gain_val = "506";
                                step_gain_val = "1";
                                default_gain = "127";
                                exposure_factor = "1000000";
                                min_exp_time = "1220";
                                max_exp_time = "32990";
                                step_exp_time = "1";
                                default_exp_time = "11000";
                                min_hdr_ratio = "1";
                                max_hdr_ratio = "1";
                                framerate_factor = "1000000";
                                min_framerate = "30000000";
                                max_framerate = "30000000";
                                step_framerate = "1";
                                default_framerate = "30000000";
                            };
                            ports {
                                #address-cells = <0x01>;
                                #size-cells = <0x00>;
                                port@0 {
                                    reg = <0x00>;
                                    endpoint {
                                        port-index = <0x00>;
                                        bus-width = <0x02>;
                                        remote-endpoint = <0x103>;
                                        phandle = <0x10c>;
                                    };
                                };
                            };
                        };
                    };
                };

Multi-Camera Virtual Channels

When using multiple cameras with GMSL2 architectures. There are 2 alternatives:

  • Using 1 serializer per deserializer. This architecture is simpler, and isolate the cameras, as they all have their own communication and data physical channel. However, size and cost increases, since more deserializer chips are required in the compute board. Also, it is limited by the number of CSI ports in the SOC.
  • Using multiple serializers per deserializer. This is shown in the diagram below. This architecture takes advantage of the multi-camera features in the deserializer, using virtual channels to send up to 16 camera streams through a single MIPI connection. The number of cameras will be limited by the GMSL2 being used, some have 1 single serializer connection, while others have up to 4 COAX camera connections.
Camera A -> VC0 ---|
Camera B -> VC1 ---|
Camera C -> VC2 ------> Deserializer -> Jetson NVCSI -> VI
Camera D -> VC3 ---|


When using multiple cameras, Virtual-channel, CSI data-type, lane, and timing configuration must agree between the SerDes and Jetson receiver.

An example of how a NVIDIA Jetson VI configuration looks for a multi-camera system is as follows. In this example, there are 6 cameras, and only 4 MIPI ports are used, using dual virtual channel in 2 of the MIPI ports to enable all 6 simultaneously.

tegra-capture-vi {
        compatible = "nvidia,tegra-camrtc-capture-vi";
        nvidia,vi-devices = <0x1a4 0x1a6>;
        nvidia,vi-mapping-size = <0x06>;
        nvidia,vi-mapping = <0x00 0x00 0x01 0x00 0x02 0x01 0x03 0x01 0x04 0x00 0x05 0x01>;
        nvidia,vi-mapping-names = "csi-stream-id\0vi-unit-id";
        nvidia,vi-max-channels = <0x48>;
        num-channels = <0x06>;
        phandle = <0x39e>;
        ports {
            status = "okay";
            #address-cells = <0x01>;
            #size-cells = <0x00>;
            port@0 {
                reg = <0x00>;
                status = "okay";
                endpoint {
                    status = "okay";
                    port-index = <0x00>;
                    bus-width = <0x02>;
                    remote-endpoint = <0x1a7>;
                    phandle = <0x10d>;
                };
            };
            port@1 {
                reg = <0x01>;
                endpoint {
                    port-index = <0x01>;
                    bus-width = <0x02>;
                    remote-endpoint = <0x1a8>;
                    phandle = <0x10f>;
                };
            };
            port@2 {
                reg = <0x02>;
                endpoint {
                    port-index = <0x00>;
                    bus-width = <0x02>;
                    vc-id = <0x01>;
                    remote-endpoint = <0x1a9>;
                    phandle = <0x111>;
                };
            };
            port@3 {
                reg = <0x03>;
                endpoint {
                    port-index = <0x02>;
                    bus-width = <0x02>;
                    remote-endpoint = <0x1aa>;
                    phandle = <0x113>;
                };
            };
            port@4 {
                reg = <0x04>;
                endpoint {
                    port-index = <0x03>;
                    bus-width = <0x02>;
                    remote-endpoint = <0x1ab>;
                    phandle = <0x115>;
                };
            };
            port@5 {
                reg = <0x05>;
                endpoint {
                    port-index = <0x02>;
                    bus-width = <0x02>;
                    vc-id = <0x01>;
                    remote-endpoint = <0x1ac>;
                    phandle = <0x117>;
                };
            };
        };
    };

Build and Deploy

Build against the exact kernel source/configuration matching the deployed Jetson Linux image.

RidgeRun has multiple guides for different NVIDIA Jetson modules and different Jetpack versions. Find the one it works for you:

Validate the Pipeline

A simple mechanism to validate the capture is using V4L2-ctl tool, which is part of the v4l-utils package. An example command is:

v4l2-ctl -d /dev/video0 \
    --set-fmt-video=width=1920,height=1536,pixelformat=RG12 \
    --set-ctrl=bypass_mode=0,exposure=5000,gain=16 \
    --stream-mmap --stream-count=10 \
    --stream-to=/tmp/v4l2_capture.raw

For NVIDIA Jetson systems, another testing mechanism is using Argus API. Using the nvarguscamerasrc GStreamer Argus element, and example pipeline looks like:

gst-launch-1.0 -e \
    nvarguscamerasrc num-buffers=10 sensor-id=0 sensor-mode=0 \
        aelock=true awblock=true \
        exposuretimerange="5000000 5000000" \
        gainrange="1 1" \
        ispdigitalgainrange="1 1" \
    ! 'video/x-raw(memory:NVMM),format=NV12,width=1920,height=1536,framerate=30/1' \
    ! nvvidconv \
    ! 'video/x-raw,format=NV12,width=1920,height=1536,framerate=30/1' \
    ! filesink location=/tmp/argus_capture.nv12

Common Failures

Jetson GMSL2 troubleshooting
Symptom First checks
Sensor does not probe Power, clock, reset, remote I2C, aliases.
Link locks but no capture Sensor stream, video pipe, CSI data type, VC, NVCSI/VI.
Capture times out Lanes, format, timing, CSI receiver errors.
One camera works but multiple fail Address collisions, VC routing, bandwidth, shared state.

See How to Debug a GMSL2 Driver for Linux for a full layered diagnostic workflow.

Key Takeaways

  • Treat Jetson GMSL2 enablement as a complete camera-pipeline integration.
  • Prove remote I2C before video.
  • Validate sensor, SerDes, NVCSI, and VI separately.
  • Match driver and device tree to the exact Jetson Linux release.
  • Bring up one camera before multi-camera routing and synchronization.
  • RidgeRun provides custom V4L2 camera-driver development and Jetson camera integration.

Frequently Asked Questions

How do I create a GMSL2 driver for NVIDIA Jetson?
Define the exact BSP and hardware, establish the SerDes control channel, implement or adapt the sensor/V4L2 integration, configure device tree and CSI/VI topology, and validate one stream end-to-end before adding multiple cameras.
Why does GMSL2 lock but Jetson capture time out?
Link lock does not verify sensor streaming or downstream CSI. Check the video pipe, data type, virtual channel, lanes, NVCSI errors, and VI configuration.
Can RidgeRun develop a custom Jetson GMSL2 driver?
RidgeRun provides custom Linux V4L2 camera-driver services and experience with Jetson GMSL/GMSL2-style serializer/deserializer camera architectures.

Related Pages



For direct inquiries, please refer to the contact information available on our Contact page. Alternatively, you may complete and submit the form provided at the same link. We will respond to your request at our earliest opportunity.


Links to RidgeRun Resources and RidgeRun Artificial Intelligence Solutions can be found in the footer below.



Cookies help us deliver our services. By using our services, you agree to our use of cookies.