Your sensor answers on I2C, the driver loads, and the kernel log even prints the chip-found line — yet /dev is still empty of video nodes. That gap is a familiar wall in embedded camera bring-up, and it sits one layer below the wiring and address faults. This page is for engineers wiring a raw parallel sensor to a processor's CSI controller. You will map the devicetree chain that creates the node, run shell checks that show exactly where the chain stalls, size the pixel clock the bus must carry, and close with a rule for when to stop tuning the graph and take a module instead.
Why does the sensor probe cleanly but no video node appear?
The probe and the node are two separate events. The I2C probe proves the sensor answers register reads. The /dev/video node registers only after the async notifier has bound the sensor endpoint to the CSI endpoint and the complete callback has run.
The kernel registers the sensor driver as an asynchronous sub-device and the capture driver as a notifier. It matches them through the endpoint graph in the devicetree and creates the video device node only in the notifier's complete step, once every endpoint has matched. A log that ends at "probe end" therefore says nothing about the capture side. The CSI driver may have nothing to bind to, or the endpoints may fail to match — both failures look identical from /dev: nothing appears, nothing errors.
Module load order muddies this further. The sensor can probe long before the capture driver registers its notifier, so a quiet log for a few seconds after boot is normal. The fault to hunt is a graph that never completes, not a driver that failed to load.
What does the chip-found line on the I2C bus actually prove?
The chip-found line proves four things only: the sensor rails are powered, the I2C bus runs, the address is right — 0x21 in the device tree, which the kernel logs as 0x42 because it prints the 7-bit address shifted left by one — and a driver matched the compatible string.
The arithmetic in the log confirms which sensor you are talking to: 0x21 × 2 = 0x42, and the 21a0000.i2c label is the I2C controller's base address. That is the whole proof. The line says nothing about the eight parallel data lines, the hsync, vsync and pclk routing, the pin multiplexing, or whether the CSI controller is enabled — those all live in the capture path the probe never touches.
Treat the probe as the boundary between two fault families. If the chip-found line is missing entirely, you are on the control side; the camera I2C communication failure guide works that ladder. With the line present and no node, everything below applies.
Which devicetree links must exist before the node is created?
Two endpoint nodes must exist and point at each other before anything binds. The CSI node needs status = okay, a port and an endpoint with a remote-endpoint phandle. The sensor node needs a port whose endpoint carries the matching remote-endpoint pointing back.
The minimal graph for this class of sensor looks like this:
&csi {
status = "okay";
port {
csi_from_ov7670: endpoint {
remote-endpoint = <&ov7670_to_csi>;
};
};
};
camera@21 {
compatible = "ovti,ov7670";
reg = <0x21>;
/* clocks, reset and power GPIOs as wired */
port {
ov7670_to_csi: endpoint {
remote-endpoint = <&csi_from_ov7670>;
bus-width = <8>;
hsync-active = <1>;
vsync-active = <1>;
data-active = <1>;
pclk-sample = <1>;
};
};
};
Three details trip this up in practice. A phandle pointing at a label that does not exist builds without complaint and binds nothing at boot — the same failure class as the camera driver device tree mismatch case. The compatible string must match an entry in the sensor driver's match table, or the sensor never probes at all.
For the graph shape, diff your file against the capture driver's own binding example in the kernel source tree for your vendor kernel. The graph is a fixed shape: one port on each side, one endpoint on each side, and the two remote-endpoint phandles naming each other's labels. Most vendor kernels ship a reference devicetree overlay for their CSI controller; that file is the authoritative pattern.
Which endpoint properties decide whether the sensor binds to the CSI?
The bind compares the properties on the two endpoints. bus-width must equal the number of data lines actually wired, 8 for this sensor class. hsync-active, vsync-active, data-active and pclk-sample must match the sensor's output polarity; one disagreement leaves the graph open and the node uncreated.
Each property states one hardware fact:
-
bus-width = <8>— the number of parallel data lines actually wired between sensor and CSI. Count them on the schematic; the value must equal that count, and the sensor driver must produce a bus format that fits it. -
hsync-active = <1>andvsync-active = <1>— 1 marks the sync pulses active high, 0 active low, as the sensor's timing registers drive them. -
data-active = <1>— the level the data lines hold outside active video. -
pclk-sample = <1>— sample pixel data on the rising edge of pclk; 0 for the falling edge.
A wrong value can fail the match outright, or bind cleanly and produce torn frames once capture starts — either way the graph is not healthy. The master clock belongs to the same check: clock-names = "xclk" with assigned-clock-rates = <25000000> sets the 25 MHz the sensor needs before it streams, and the successful probe already proved that clock toggles, since register reads answered.
How do you test the media graph from the shell?
Work from the outside in with four checks: list the video4linux class and /dev for nodes, grep the kernel log for the sensor and CSI driver lines, print the graph with media-ctl -p on /dev/media0 if it exists, and check node permissions last.
-
ls /dev/video*andls /sys/class/video4linux/— an empty result means the video device never registered, so the fault is upstream in the graph. - Grep the kernel log for the sensor name and the CSI driver name: you want the sensor's probe line, the capture driver's registration and the notifier's bind messages as endpoints match.
- If /dev/media0 exists, run
media-ctl -p -d /dev/media0to print every entity and link. A capture entity with no link from the sensor sub-device shows exactly which hop is missing. - When nodes exist but reads fail, the graph is done and the fault is access control — the Linux camera device permissions guide covers that step.
On a 4.9 vendor kernel for this processor class, the capture test program mx6s_v4l2_capture.c from the imx-test package exercises the CSI path directly, which beats debugging through your own application first. Once the node exists but frames fail, the ladder changes: the MIPI CSI camera no images guide picks up from there.
How much pixel clock must the parallel bus carry?
Size the bus before blaming the graph: 640 × 480 active pixels at 30 frames per second is 9.2 Mpx/s of active data, and once typical VGA blanking of 1.4 to 1.8 times active is included the parallel bus must carry a pixel clock of roughly 13 to 17 MHz.
Worked example at this sensor's default VGA timing:
- Active pixels per frame: 640 × 480 = 307,200.
- Pixel rate at 30 fps: 307,200 × 30 = 9,216,000, about 9.2 Mpx/s.
- Data rate in 8-bit YUV, 2 bytes per pixel: 9,216,000 × 2 = 18,432,000 B/s, about 18.4 MB/s arriving at the capture buffer.
- Pixel clock with blanking at 1.4× active (a typical VGA figure — treat 1.4 to 1.8 as the working band): 9,216,000 × 1.4 ≈ 12.9 MHz, and at 1.8× about 16.6 MHz.
Against that demand, assigned-clock-rates = <25000000> supplies a 25 MHz master clock: headroom of 25 / 12.9 ≈ 1.9× in the tight case and 25 / 16.6 ≈ 1.5× in the loose one, so 30 fps VGA fits with room to spare. Cut the clock to 12 MHz and the ceiling drops to 12,000,000 / (307,200 × 1.4) ≈ 28 fps — 30 no longer fits, and you see either a lower stable rate or corrupted frames. Set the xclk at 24 to 25 MHz for 30 fps VGA, and scale the arithmetic linearly if your driver negotiates a different byte count per pixel.
Which causes should you rule out first, in what order?
Rule causes out in this order — disabled CSI node, dangling remote-endpoint phandle, bus-width mismatch, wrong polarity properties, capture driver missing from the kernel config — because the first two cause most failures and each one has a single-command test.
| Order | Cause | Test | Fix |
|---|---|---|---|
| 1 | CSI node disabled or missing | no capture-driver line in the kernel log while the sensor probes | set status = "okay" on the CSI node and rebuild the DTB |
| 2 | Dangling remote-endpoint phandle |
media-ctl -p shows a capture entity with no sensor link |
add the missing endpoint and point both phandles at each other |
| 3 | bus-width disagrees with the wiring | count the data lines on the schematic against the endpoint value | set bus-width to the wired count, 8 for this sensor class |
| 4 | Polarity properties wrong for the sensor output | torn or shifted frames once capture runs | correct hsync-active, vsync-active, data-active and pclk-sample
|
| 5 | Capture driver absent from the kernel config | no CSI or capture line anywhere in the log, module or built-in | enable the capture driver alongside the sensor driver and rebuild |
Rows 1 and 2 close most no-node cases; rows 3 and 4 usually surface only after the node exists, as garbage frames. Prevention is structural: keep the sensor node, the CSI node and their pinctrl groups in one devicetree include so a rebuild cannot update one and strand the others.
When is a self-enumerating camera module the better fix?
A self-enumerating module removes the entire graph from the problem: a GigE or USB3 board-level camera needs no devicetree endpoint work, so bring-up is an SDK install on a standard port instead of a kernel and DTB rebuild loop.
The decision turns on the deliverable. If the product is a disposable 8-bit sensor inside your own housing, finish the graph — the order above closes it in an afternoon of rebuild loops. If the deliverable is a working inspection image on a schedule, a module deletes the ladder, and the same logic covers housed area scan cameras on those interfaces.
Two catalog examples of the module path:
- Sensor format
- 1/2.9"
- Lens mount
- M12
- Interface
- GigE
- Resolution
- Up to 3 MP
The MV-CB016-10GM-M-W pairs a 1/2.9-inch global-shutter sensor with an M12 mount on GigE, a fit for fixed-mount embedded inspection where the host already has an Ethernet port.
- Sensor format
- 1/1.8"
- Lens mount
- C-mount
- Interface
- USB3
- Resolution
- 5 MP
The LS-BCM-0003 is a 3.1 MP board-level camera with a 1/1.8-inch global-shutter IMX252 sensor on USB3, rated 120 fps and fitted with a C-mount.
Before ordering, run the module through the board camera evaluation checklist, confirm the host side with the camera module interface compatibility guide, and let the board level camera selection guide for embedded vision machines narrow the field by mount, interface and resolution. Then start from the board-level cameras category, the camera finder or the camera configurator to pair camera, lens and cable in one pass; the systems topic page collects the rest of the bring-up ladder.
Questions
Does the capture driver have to be built as a loadable module?
No. Built-in and module builds both work; what matters is that both drivers are present and that the sensor's compatible string matches an entry in the driver's match table. A module build only shifts when binding happens, since the notifier waits for the module to load. If the sensor probes but the log shows no capture-driver line at all, the driver is missing from the kernel config, not the devicetree.
Why does /dev/media0 exist while /dev/video0 is missing?
On most capture stacks the media controller node registers with the pipeline itself, while the video node registers only in the notifier's complete callback, after every endpoint in the graph has bound. Media node present and video node missing means the capture driver is alive and the graph is incomplete. The usual causes are a dangling remote-endpoint phandle or endpoint properties the two drivers cannot agree on.
Can two parallel sensors share one CSI port?
No. One CSI port node has exactly one endpoint, so it binds one sensor; a second parallel camera needs a second CSI port on the controller or an external multiplexer between the sensors and the CSI. Sensors can share the I2C bus only when their addresses differ — two identical sensors need separate buses or an address strap change, which is its own bring-up problem.
The node finally exists but frames come out black or torn — is that the same fault?
No, it is the next fault. A registered node proves the graph bound; bad pixels point at polarity and packing. Work through pclk-sample first — a wrong edge usually tears every line — then hsync-active and vsync-active, which shift or duplicate frames, and finally the bus format each driver reports. The graph did its job; the capture timing is now the suspect.

