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.
| 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
- 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.
- Compute pix_clk_hz = per-lane rate × lanes ÷ bits per pixel for that mode.
- 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.
- 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.
- 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.
- Reboot without touching debugfs, so the governor starts from the corrected budget.
- 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:
- Green or shifted image content points at line width and format mismatches: see green frames from a CSI camera: pixel clock and padded line width.
- Corruption that started after a driver update suggests the loaded driver is not the one you edited: work through camera loads the wrong sensor driver: read dmesg, fix the overlay.
- Noise that ignores the clocks entirely and wanders with temperature can come from the sensor rails; camera sensor PMIC power sequencing: device tree rails and delays covers the rail checks.
- Failure in one mode only means that entry still carries its old value; recompute it separately.
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.
Compact cameras to build in
120 models · from $179.00Questions
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.