V4L2 Media Bus Binding Properties-Free Linux Device Drivers Course

PREV_LEC | NEXT_LEC

V4L2 Media Bus Binding Properties

A lecture from EmbeddedPathashala’s free Linux kernel development course — the exact device tree properties that shape a camera bus

In the last lecture of this free linux kernel development course, we introduced every media bus type V4L2 understands and their top-level data structures. That answered “which bus is it?” This lecture answers the next question: “how exactly is a specific bus configured?” We go property-by-property through the device tree bindings that fill in bus.parallel, bus.mipi_csi1, and bus.mipi_csi2, explain the bus-type selector property, cover the older CCP2/CSI-1 single-lane buses, and finish with the auto-detection algorithm the V4L2 core runs when no bus type is specified — all part of our free embedded systems course.

bus-type property hsync-active vsync-active data-lanes clock-lanes free linux device drivers course

What You Will Learn

  • How the bus-type device tree property selects or overrides bus auto-detection
  • Every binding property for parallel and BT.656 buses, and the exact flag each one sets
  • Every binding property for the MIPI CSI-2 bus
  • The older single-lane CCP2 and MIPI CSI-1 buses and their properties
  • Exactly how the V4L2 core guesses a bus type when none is specified

Prerequisites

  • The previous lecture in this free linux development course — V4L2 media bus types and the current v4l2_fwnode_endpoint/v4l2_mbus_config_* structures
  • Basic device tree syntax: nodes, properties, phandles

The bus-type Property: Choosing or Forcing a Bus

Before it fills in any bus-specific fields, v4l2_fwnode_endpoint_parse() looks at an optional bus-type integer property on the endpoint node. This single property decides which of the per-bus parsers runs next:

bus-type ValueMeaning
0Auto-detect — the core guesses parallel, BT.656, or CSI-2 D-PHY from whichever properties are present
1MIPI CSI-2 C-PHY
2MIPI CSI-1
3CCP2 (SMIA Compact Camera Port 2)

A CCP2 endpoint, for instance, simply carries bus-type = <3>; and nothing else needs to hint at the bus — the number says it explicitly.

There is also a driver-side safety net. A driver can pre-set vep.bus_type to the bus type it expects before calling v4l2_fwnode_endpoint_parse(). If the firmware node’s bus-type property does not match what the driver expected, parsing fails outright instead of silently misconfiguring hardware — unless the driver deliberately left bus_type as V4L2_MBUS_UNKNOWN, which re-enables auto-detection.

bus-type Resolution Flow

Driver sets vep.bus_type (or leaves V4L2_MBUS_UNKNOWN) → v4l2_fwnode_endpoint_parse(fwnode, &vep)
Core reads “bus-type” property from fwnode → compares against vep.bus_type if the driver pre-set one
Mismatch → parse fails with -EINVAL  |  Match or UNKNOWN → correct per-bus parser runs and fills vep.bus

Parallel and BT.656 Binding Properties

These two bus types share struct v4l2_mbus_config_parallel (flags, bus_width, data_shift) and are filled by the same internal parser. Every property below is optional — if none are present, the bus is treated as having a static, pre-agreed configuration and no flags are set.

Device Tree PropertyEffect on flags
hsync-active0 → V4L2_MBUS_HSYNC_ACTIVE_LOW, otherwise V4L2_MBUS_HSYNC_ACTIVE_HIGH
vsync-active0 → V4L2_MBUS_VSYNC_ACTIVE_LOW, otherwise V4L2_MBUS_VSYNC_ACTIVE_HIGH
field-even-active0 → V4L2_MBUS_FIELD_EVEN_LOW, otherwise V4L2_MBUS_FIELD_EVEN_HIGH
pclk-sample0 → V4L2_MBUS_PCLK_SAMPLE_FALLING, 1 → V4L2_MBUS_PCLK_SAMPLE_RISING
data-activeSets V4L2_MBUS_DATA_ACTIVE_HIGH or _LOW, mirroring HSYNC/VSYNC semantics
data-enable-activeSets V4L2_MBUS_DATA_ENABLE_HIGH or _LOW
slave-modeBoolean presence → V4L2_MBUS_SLAVE, absence → V4L2_MBUS_MASTER
bus-widthNumber of actively-used data lines, stored directly in bus_width
data-shiftLines to skip before the first active data line, stored in data_shift
sync-on-green-activeSets V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH or _LOW

The bus-width/data-shift pair is worth a concrete example: bus-width = <8>; data-shift = <2>; tells the driver that out of a wider physical bus, only 8 lines carry real data, and the active window starts 2 lines in — i.e., lines 9 down to 2 are the ones actually wired to the sensor’s output. The resulting bus type is either V4L2_MBUS_PARALLEL or V4L2_MBUS_BT656, decided by which of the two compatible strings or context the driver used.

MIPI CSI-2 Binding Properties

CSI-2 endpoints (D-PHY or C-PHY) fill bus.mipi_csi2 from these properties:

Device Tree PropertyEffect
data-lanesArray of physical lane indexes — the length determines num_data_lanes
clock-lanesPhysical lane index of the clock lane
lane-polaritiesOne entry per lane (clock lane first, then data lanes in data-lanes order) — 0 is normal, 1 is inverted
clock-noncontinuousBoolean presence → V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK, absence → V4L2_MBUS_CSI2_CONTINUOUS_CLOCK

A two-lane CSI-2 sensor endpoint typically looks like this:

port {
    ep_sensor_out: endpoint {
        remote-endpoint = <&ep_bridge_in>;
        clock-lanes = <0>;
        data-lanes = <1 2>;
        lane-polarities = <0 0 0>;
        clock-noncontinuous;
    };
};

Note the lane-polarities length: one entry for the clock lane plus one per data lane, three total for a two-data-lane bus. If the property is omitted entirely, every lane defaults to normal polarity.

CCP2 and MIPI CSI-1 — the Single-Lane Serial Buses

CCP2 and CSI-1 predate CSI-2 and only ever use a single data lane plus a single clock/strobe lane. Both share struct v4l2_mbus_config_mipi_csi1:

struct v4l2_mbus_config_mipi_csi1 {
    unsigned char clock_inv:1;
    unsigned char strobe:1;
    bool lane_polarity[2];
    unsigned char data_lane;
    unsigned char clock_lane;
};
Field / PropertyMeaning
clock_invPolarity of the clock/strobe signal — false is not inverted, true is inverted
strobeFalse means the second lane carries a clock; true means it carries a strobe signal instead
data_lane / clock_lanePhysical lane index for data and clock, taken from data-lanes / clock-lanes
lane_polarity[2]Exactly two entries — index 0 is the clock lane’s polarity, index 1 is the data lane’s polarity

Because CCP2 and CSI-1 look almost identical at the wire-property level, the kernel cannot tell them apart from properties alone — this is exactly why the explicit bus-type property (values 2 and 3) exists for these two buses.

Bus Guessing: What Auto-Detect Actually Checks

When bus-type is 0 or simply absent, the core runs a small, deterministic decision tree instead of failing:

  1. It first checks for any CSI-2-specific property (data-lanes, clock-lanes, clock-noncontinuous). If found, it parses the endpoint as CSI-2 D-PHY.
  2. If no CSI-2 properties are present, it falls back to the parallel/BT.656 parser, since CSI-2 and parallel buses deliberately share no property names.
  3. CCP2 and CSI-1 are never guessed. If your sensor uses either, you must set bus-type explicitly — otherwise the core will misidentify it as a parallel bus with an empty configuration.

Original Demo: Dumping Every Bus Property

Let’s extend the idea from the previous lecture into ep_busprops — a platform driver that prints every relevant flag and property, not just the bus type, so you can see auto-detection working in your own dmesg.

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mediabus.h>

static void ep_busprops_dump_parallel(struct device *dev,
                                       struct v4l2_mbus_config_parallel *p)
{
    dev_info(dev, "parallel: width=%u shift=%u hsync=%s vsync=%s pclk=%s mode=%s\n",
              p->bus_width, p->data_shift,
              (p->flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) ? "high" : "low",
              (p->flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) ? "high" : "low",
              (p->flags & V4L2_MBUS_PCLK_SAMPLE_RISING) ? "rising" : "falling",
              (p->flags & V4L2_MBUS_SLAVE) ? "slave" : "master");
}

static void ep_busprops_dump_csi2(struct device *dev,
                                   struct v4l2_mbus_config_mipi_csi2 *c)
{
    int i;

    dev_info(dev, "csi2: lanes=%u clock_lane=%u clock=%s\n",
              c->num_data_lanes, c->clock_lane,
              (c->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) ?
                  "non-continuous" : "continuous");

    for (i = 0; i < c->num_data_lanes; i++)
        dev_info(dev, "  data_lane[%d] = physical lane %u, polarity=%s\n",
                  i, c->data_lanes[i],
                  c->lane_polarities[i + 1] ? "inverted" : "normal");
}

static int ep_busprops_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct fwnode_handle *ep;
    struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_UNKNOWN };
    int ret;

    ep = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
    if (!ep)
        return -ENODEV;

    ret = v4l2_fwnode_endpoint_parse(ep, &vep);
    fwnode_handle_put(ep);
    if (ret)
        return ret;

    switch (vep.bus_type) {
    case V4L2_MBUS_PARALLEL:
    case V4L2_MBUS_BT656:
        ep_busprops_dump_parallel(dev, &vep.bus.parallel);
        break;
    case V4L2_MBUS_CSI2_DPHY:
    case V4L2_MBUS_CSI2_CPHY:
        ep_busprops_dump_csi2(dev, &vep.bus.mipi_csi2);
        break;
    default:
        dev_info(dev, "bus_type=%d (not dumped by this demo)\n", vep.bus_type);
    }

    return 0;
}

static const struct of_device_id ep_busprops_of_match[] = {
    { .compatible = "ep,busprops" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_busprops_of_match);

static struct platform_driver ep_busprops_driver = {
    .probe = ep_busprops_probe,
    .driver = {
        .name = "ep_busprops",
        .of_match_table = ep_busprops_of_match,
    },
};
module_platform_driver(ep_busprops_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("EmbeddedPathashala demo: dump V4L2 media bus binding properties");

Build, Load, and Expected Output

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_busprops.ko
dmesg | tail -n 6
[   20.114402] ep_busprops ep_busprops@0: csi2: lanes=2 clock_lane=0 clock=non-continuous
[   20.114455] ep_busprops ep_busprops@0:   data_lane[0] = physical lane 1, polarity=normal
[   20.114488] ep_busprops ep_busprops@0:   data_lane[1] = physical lane 2, polarity=normal
[   20.114520] ep_busprops: probe of ep_busprops@0 succeeded

Remove data-lanes/clock-lanes from the device tree endpoint and add bus-width/hsync-active instead — the exact same driver code will now print the parallel branch, proving the core’s auto-detection is doing the work, not your driver.

Common Mistakes and Troubleshooting

  • Assuming CCP2/CSI-1 auto-detects: it never does — always set bus-type explicitly for these two buses.
  • Wrong lane-polarities length: forgetting the leading clock-lane entry is one of the most common device tree bugs on CSI-2 sensors — the array must be num_data_lanes + 1 long.
  • Mixing up bus-width and data-shift: bus-width is a count, data-shift is an offset — swapping them silently reads the wrong physical pins.
  • Forgetting slave-mode is boolean: it’s presence-only, like clock-noncontinuous — don’t write slave-mode = <1>;, just write slave-mode;.

Best Practices

  • Prefer explicit bus-type for anything other than plain parallel/CSI-2 D-PHY sensors — it documents intent and avoids relying on guesswork.
  • When writing a new sensor binding, look at an already-upstreamed sensor of the same bus family for the expected property names rather than guessing.
  • Log parsed lane counts and polarities at probe time (as our demo does) — CSI-2 lane-mapping bugs are notoriously hard to debug from image corruption alone.

Summary and Key Takeaways

  • bus-type is the explicit override; when absent, the core guesses using a deterministic CSI-2-first, parallel-fallback algorithm — but never guesses CCP2 or CSI-1.
  • Parallel/BT.656 buses expose ten distinct binding properties, all optional, each mapping to a specific V4L2_MBUS_* flag.
  • CSI-2 lane mapping and polarity arrays must be sized and ordered exactly right, or capture will silently corrupt.
  • CCP2 and CSI-1 share one data structure and always need an explicit bus-type.

With bus types and now every binding property covered, you have the complete picture needed to read, debug, or write a device tree binding for practically any V4L2 camera sensor — a genuinely practical skill built entirely within this free linux kernel development course. Next, we return to the V4L2 async framework and look at the current connection and matching data structures that replaced the old async sub-device model.

Frequently Asked Questions

What happens if I set bus-type to a value that doesn’t match my driver’s expected bus?

If the driver pre-set vep.bus_type before calling v4l2_fwnode_endpoint_parse(), parsing fails immediately instead of silently misconfiguring the bus — a deliberate safety check added in kernel v5.0.

Why doesn’t auto-detect work for CCP2 and MIPI CSI-1?

CCP2 and CSI-1 use almost the same property set as each other, so there is no reliable way to tell them apart from properties alone — the explicit bus-type value (2 or 3) is required.

How long should the lane-polarities array be for a CSI-2 endpoint?

One entry for the clock lane plus one entry per data lane, in the same order as data-lanes — so a four-data-lane sensor needs five entries.

What is the difference between bus-width and data-shift on a parallel bus?

bus-width is how many data lines are actively used; data-shift is how many lines to skip before reaching the first active one, letting a driver describe a narrower bus sitting on a wider physical connector.

Is clock-noncontinuous a boolean or numeric property?

It’s a boolean presence property, just like slave-mode — you either include the property name with no value, or omit it entirely.

Does this lecture belong to a free course?

Yes — this is part of EmbeddedPathashala’s free linux kernel development course, alongside our free embedded systems course and free linux device drivers course material.

Continue the Free Linux Kernel Development Course

Next up: the V4L2 async framework’s current connection and matching data structures.

Browse All Lectures Join the Free Course
PREV_LEC | NEXT_LEC

Leave a Reply

Your email address will not be published. Required fields are marked *