Cameras & sensorsTroubleshooting

Fix CSI clocks by correcting pix_clk_hz in the device tree

MIPI CSI camera works only with clocks pinned to max? The device tree's pix_clk_hz is too low — recompute it from lane rate, lanes and bits per pixel.

LumaSpec Vision9 min read Share on LinkedIn

On this page 9 sections
  1. 01The symptom and the clock evidence
  2. 02How pix_clk_hz sets the bandwidth budget
  3. 03Compute the value from your mode table
    1. The frame-timing cross-check
  4. 04Fix the device tree in seven steps
  5. 05Verify at default clocks
  6. 06When pinning clocks is not the answer
  7. 07Prevention checklist
  8. 08If you get to choose the interface
  9. 09Questions

Your sensor streams clean frames only while the VI, ISP and NVCSI clocks are pinned to their maximum rates; remove the pinning and the image collapses into noise. That pattern is not a failing camera and not a marginal board. It is the signature of a bandwidth budget computed from one wrong number: pix_clk_hz, the per-mode property in the sensor's device tree node. This guide is for engineers bringing up a MIPI CSI-2 sensor on a Jetson-class board whose clock tree lives under /sys/kernel/debug/bpmp. You will learn how the debugfs pinning masks the fault, how to compute pix_clk_hz from lane rate, lane count and bits per pixel, and how to correct the device tree so the camera runs clean at default clocks.

The symptom and the clock evidence

Reproduce the fault once with the numbers attached. At boot, before any pinning, an affected Jetson setup read these rates from the BPMP clock tree: vi at 832,000,000 Hz, isp at 1,011,200,000 Hz and nvcsi at 428,600,000 Hz, against an nvcsi max_rate of 642,900,000 Hz. The sensor in that case, an IMX567, delivered noise at the defaults. After writing 1 to each mrq_rate_locked under /sys/kernel/debug/bpmp/debug/clk/ and copying each max_rate into rate, the frames came back clean.

Then narrow it: pin only nvcsi with echo 1 > /sys/kernel/debug/bpmp/debug/clk/nvcsi/mrq_rate_locked and leave VI and ISP untouched. If the noise disappears there, one clock raised by 50% (642,900,000 ÷ 428,600,000 = 1.50) carried the whole stream. A stream that a single bandwidth clock rescues is short of bandwidth, not of compute — which convicts the numbers the driver feeds the clock framework rather than the board.

How pix_clk_hz sets the bandwidth budget

For every mode it exposes, the driver declares a pixel rate through the pix_clk_hz property of that mode's device tree node. The clock framework budgets wire throughput from it — pixel rate multiplied by bits per pixel — spreads the total across the data lanes and raises NVCSI and VI only enough to carry the budget plus a margin. Declare three quarters of the true rate and the clocks settle a quarter short of the wire: the deserializer drops bits mid-line and the frame arrives as noise, torn lines or short frames.

The debugfs pinning over-provisions every clock in one stroke, so it hides the shortfall completely. That is why the workaround looks like a fix on the bench: power and heat rise, and the first throttling event drops the clocks back onto the wrong budget.

pix_clk_hz (Hz) = per-lane rate (bit/s) × lanes ÷ bits per pixel budget (bit/s) = pix_clk_hz × bits per pixel wire (bit/s) = per-lane rate × lanes
Pixel format on the wire bits per pixel
RAW8 8
RAW10 10
RAW12 12
RAW14 14
YUV422, 8-bit 16
RGB888 24

Use the bit depth actually transmitted, not the sensor's internal depth: a 12-bit sensor streaming RAW10 makes the divisor 10. The lane-rate arithmetic is the same counting the MIPI CSI-2 frame rate estimation: lanes and data rate walkthrough applies to frame rates.

Compute the value from your mode table

The inputs come from the mode you actually stream: the number of data lanes, the pixel format and the per-lane rate stated in the sensor's mode table. Worked example with illustrative inputs — 4 data lanes, RAW10, 1.5 Gbit/s per lane:

pix_clk_hz = 1.5 Gbit/s × 4 lanes ÷ 10 bits per pixel = 600,000,000 Hz

The wire carries 1.5 Gbit/s × 4 = 6.0 Gbit/s. A declared value of 428,000,000 budgets only 4.28 Gbit/s — 28.7% short ((6.0 − 4.28) ÷ 6.0). That is exactly the gap a board idling nvcsi at 428.6 MHz cannot cover, and the same gap that pinning to 642.9 MHz, a 50% raise, hides. Take the lane rate from your own mode table; the ratio, not the absolute number, repeats from board to board.

The frame-timing cross-check

Multiplying frame geometry by frame rate gives a second estimate: pixels per line × lines per frame × fps, blanking included. For 1080p30 at CEA timing that is 2,200 × 1,125 × 30 = 74,250,000 Hz. The active-only shortcut, 1,920 × 1,080 × 30 = 62,208,000 Hz, sits 16% low. If the sensor oversamples internally — binning, remosaic, line repeat — the timing method drifts further down and stops describing the wire at all.

Decision: set pix_clk_hz from the lane rate. Keep the timing result as a cross-check only, and treat a mismatch beyond about 10% as evidence that the mode table does not describe what the sensor sends.

Fix the device tree in seven steps

  1. Confirm the mode you stream: data lanes, pixel format and per-lane rate, from the sensor's datasheet mode table or the vendor register script.
  2. Compute pix_clk_hz = per-lane rate × lanes ÷ bits per pixel for that mode.
  3. In the device tree source, find the sensor node's entry for that exact mode — resolution, frame rate and pixel phase all matching — and set pix_clk_hz to the computed integer in Hz, for example 600000000.
  4. Repeat for every mode entry you use. A 60 fps mode on the same lanes needs twice the value of the 30 fps mode; a mode you do not edit keeps its old, wrong number.
  5. Rebuild the DTB and load it. If the change never reaches the running kernel, the camera device tree overlay CSI mapping troubleshooting checklist covers the usual overlay causes.
  6. Reboot without touching debugfs, so the governor starts from the corrected budget.
  7. Stream, then read back cat /sys/kernel/debug/bpmp/debug/clk/nvcsi/rate: it should settle at or above what the stream needs, and the image should be clean with no pinning.

Verify at default clocks

The proving test is dull and decisive: stream at the defaults with untouched debugfs and get clean frames. Read the nvcsi rate back under load and record it; it should sit at or above the bandwidth the mode needs, not at the boot floor.

Add two more checks. Write 0 back to each mrq_rate_locked, or reboot, and confirm the stream survives both states — a fix that depends on debugfs is not a fix. Hold exposure fixed while you compare brightness: the same property feeds the exposure conversion, so the corrected value can shift auto-exposure briefly while the algorithm re-adapts. If short-frame errors survive the edit, the mode table is the suspect: see CSI camera short frame errors: match the mode to the sensor output.

When pinning clocks is not the answer

If frames stay corrupt with every clock at max_rate, bandwidth was never the problem and the pix_clk_hz edit will not save you. Work down this list:

Each of these has a clock-independent signature, which is what separates it from the budget fault.

Prevention checklist

  • Compute pix_clk_hz per mode at bring-up time, from lane rate × lanes ÷ bits per pixel — never from the active-pixel shortcut.
  • Recompute after any change to lanes, PLL setup or pixel format; the old value is void the moment a divider moves.
  • Treat pinning-to-max as a triage tool only: if it restores frames, stop pinning and fix the value that requested too little.
  • Sign off by streaming every mode in the table once with clocks at defaults.
  • Record the nvcsi readback under load in the bring-up notes, so the next board starts with a reference.
  • When a later project needs more frame rate, recompute again — the scaling behaviour with ROI and pixel clock is covered in how to increase camera frame rate with ROI and pixel clock.

If you get to choose the interface

When CSI is a requirement, the device tree is part of the design and pix_clk_hz is the number that decides whether the design streams. When it is a choice, let the link carry the timing instead of a property: the area scan cameras catalogue runs on GigE, USB3, 10GigE and CoaXPress, where the host controller negotiates bandwidth on the wire. The board-level cameras range suits builds that mount a small module beside the carrier, and the camera finder filters by interface, resolution and shutter. More CSI bring-up guides sit on the cameras topic page. To have a camera, lens and lighting combination matched for you, start at the camera step of the configurator, or reach the team through the contact page.

CamerasBoard-level cameras

Compact cameras to build in

120 models · from $179.00

Questions

What else uses pix_clk_hz besides the clock request?

The framework converts exposure times through the pixel rate: a requested exposure is turned into sensor line units using pix_clk_hz, so a value 25% low skews the effective exposure by the same proportion and auto-exposure lands brighter or darker than intended. Correcting it fixes brightness drift and the corrupted frames with one edit, which is why noise and exposure error usually arrive together.

Can I leave the clocks pinned to maximum in production?

It works on the bench and fails in the field. Pinning raises power draw and heat, and in a closed housing the first throttling event pulls the clocks back onto the wrong budget, returning the corruption exactly when the machine is busy. The debugfs writes also do not survive a reboot. Correct pix_clk_hz so the governor's own request carries the stream.

The datasheet gives PLL registers, not a lane rate — how do I get one?

CSI-2 clocks are double data rate, so the per-lane rate in bit/s is twice the MIPI byte clock, and the byte clock follows from the PLL divider chain in the mode's register script. Most sensor mode tables state the resulting bit rate per lane directly; prefer the stated number. A value 20-30% low produces exactly the pinning-to-stream symptom this article describes.

Does this apply to USB3 or GigE cameras?

No. pix_clk_hz, NVCSI and the BPMP clock tree belong to the MIPI CSI-2 receive pipeline on an embedded board. GigE and USB3 cameras negotiate bandwidth with the host controller and carry their own timing, so corruption there starts with link speed, cable quality and packet size, not with a device-tree property.

Why did the same sensor run clean on the older board without pinning?

The clock floor differed. The older board's governors idled the receive clocks high enough to carry the real stream by accident, so the under-declared pix_clk_hz never showed. A board that honours the low request and idles lower exposes the same error as noise. The value was wrong in both cases; only one board happened to mask it.