# K230 Linux Camera Channel Count and Sensor Configuration Guide

This document describes the default camera configurations for each board in the current K230 Linux SDK, how to configure single-, dual-, or triple-camera configurations through the device tree, and how to enable the chip MCLK. For instructions on adding a new Sensor driver, see the
[K230 Linux SENSOR Porting Guide](sensor_adaptation_doc.md).

## Current Linux Board-Level Sensor Configuration

### Basic Concepts

The K230 provides up to three physical MIPI CSI controllers: CSI0, CSI1, and CSI2. In the device tree,
`mipi.N` indicates logical camera number N, which is not necessarily connected to the physical CSI with the same number.

The following numbers are easily confused and should be distinguished before reading the configuration:

| Name | Example | Meaning |
| --- | --- | --- |
| Logical camera number | slot0、slot1、slot2 | Determines the ISP port and output grouping for each channel |
| Physical CSI `id` | 0、1、2 | Corresponds to the CSI0, CSI1, and CSI2 controllers |
| Chip MCLK `mclk_id` | 0、1、2 | Corresponds to schematic MCLK1, MCLK2, and MCLK3, numbered starting from 0 |
| VVCAM output sequence | `video_index=0`～`8` | Internal driver sequence number, also the final segment of the entity name |
| Linux video device number | `/dev/video1`～`/dev/video9` | Device nodes dynamically assigned by the kernel and cannot be fixed by the DTS |

The two types of configuration in the device tree have different responsibilities:

- The node number N of `mipi.N` determines `/dev/vvcam-mipi.N` and ISP port N; the
  `id`, `reg`, `interrupts`, and `resets` in the node determine which physical CSI this logical port is connected to.
- The DTS label `&v4l2isp` points to the runtime `v4l2-isp` node. The
  `devN-sensor-name`, `devN-sensor-mode`, and `devN-sensor-i2c-bus` fields describe the Sensor driver name used by ISP port N, the mode index of that driver, and the Linux I2C adapter number. `devN-sensor-mclk-id` and `devN-sensor-use-chip-clk`
  describe whether this channel uses the chip MCLK and which MCLK it uses. They do not determine the physical CSI mapping.
- A channel is counted as a camera channel only when `mipi.N` is enabled and the corresponding `devN-sensor-*` fields are complete.

The data path can be understood as follows:

```text
Physical CSI (determined by resources such as id)
    → Logical mipi.N / ISP port N
    → Group N of MP, SP1, and SP2 entities
    → Dynamically assigned /dev/videoX

Sensor control information devN-sensor-*
    → Sensor driver, mode index, /dev/i2c-X, and chip MCLK
```

In the current MCM configuration, each Sensor registers three VVCAM outputs: MP, SP1, and SP2:

| Logical Camera | MIPI Node | VVCAM `video_index` | Entity Name | Current Board Device Node |
| --- | --- | --- | --- | --- |
| Channel 1 (slot0) | `mipi.0` | 0、1、2 | `vvcam-video.0.0`～`vvcam-video.0.2` | `/dev/video1`～`/dev/video3` |
| Channel 2 (slot1) | `mipi.1` | 3、4、5 | `vvcam-video.0.3`～`vvcam-video.0.5` | `/dev/video4`～`/dev/video6` |
| Channel 3 (slot2) | `mipi.2` | 6、7、8 | `vvcam-video.0.6`～`vvcam-video.0.8` | `/dev/video7`～`/dev/video9` |

The above “three outputs per channel” assumes that `vvcam_video mcm_mask=1` is loaded. The SDK startup script
`S31canaan_isp` loads the module in this manner. If the module is loaded manually without this parameter, the legacy pipeline
layout is used, and a single channel may register four outputs including RAW, which does not apply to the multi-channel
mapping table in this document.

The `/dev/video1`～`/dev/video9` in the table are measured results from the current board and are not fixed rules.
The code calls `video_register_device(..., -1)` to have the kernel automatically assign device numbers; on the current system,
`/dev/video0` is occupied by `mvx`, so VVCAM starts registering from `/dev/video1`.
If the registration order of other drivers changes, the device numbers may also change.

Applications should locate the corresponding device through the entity name instead of assuming that a particular channel will always be
`/dev/video1`, `/dev/video4`, or `/dev/video7`:

```shell
for d in /sys/class/video4linux/video*; do
    echo "$(basename "$d"): $(cat "$d/name")"
done

# View the complete media graph; the current board's VVCAM media device is media0
media-ctl -d /dev/media0 -p
```

The stable identifiers of the three MP channels are `vvcam-video.0.0`, `vvcam-video.0.3`, and `vvcam-video.0.6`,
respectively.

### Chip-Side CSI Resources

| Physical CSI | id | reg | IRQ |
| --- | ---: | --- | ---: |
| CSI0 | 0 | `0x90009800` | 115 |
| CSI1 | 1 | `0x9000a000` | 118 |
| CSI2 | 2 | `0x9000a800` | 121 |

The board-level device tree can map any physical CSI to logical `mipi.0`. For example, some board models map
physical CSI2 to `mipi.0`, so it still belongs to Channel 1, and the MP entity is still
`vvcam-video.0.0`.

The SoC baseline `k230.dtsi` enables `mipi.0`→CSI0 by default, while `mipi.1` and
`mipi.2` are disabled. When the board-level DTS maps CSI2 to slot0, it overrides the `id`, register, interrupt, and reset resources of `mipi0`, rather than enabling the native `mipi2`.

### Current Measured Board Configuration

The runtime device tree, `/proc/vsi/isp_subdev0`, and media topology were read on the currently connected board,
yielding the following configuration:

| Logical Camera | Physical CSI | Sensor | mode | Linux I2C Adapter | MP Entity | Current Device Node |
| --- | --- | --- | ---: | ---: | --- | --- |
| Channel 1 (slot0) | CSI2 | GC2093 | 0 | 4 | `vvcam-video.0.0` | `/dev/video1` |
| Channel 2 (slot1) | CSI0 | GC2093 | 0 | 0 | `vvcam-video.0.3` | `/dev/video4` |
| Channel 3 (slot2) | CSI1 | GC2093 | 0 | 1 | `vvcam-video.0.6` | `/dev/video7` |

The following was also confirmed at runtime:

- `mipi.0`, `mipi.1`, and `mipi.2` are all `"okay"`, and their physical `id` values are 2, 0, and 1, respectively.
- `/dev/vvcam-mipi.0`～`.2` all exist.
- The entities of the 9 VVCAM outputs are `vvcam-video.0.0`～
  `vvcam-video.0.8`, which map to `/dev/video1`～`/dev/video9`, respectively, in this case.
- `/dev/video0` is `mvx`, and `/dev/video10` is `canaan-non-ai-2d`, indicating that
  N in `/dev/videoN` is a dynamic system device number and does not equal the VVCAM `video_index`.

The following commands can be used to verify the configuration again on the target board:

```shell
cat /proc/vsi/isp_subdev0
media-ctl -d /dev/media0 -p
ls /dev/vvcam-mipi.*
```

### Default SDK Configurations for Each Board Model

The default Sensor for the current SDK is primarily GC2093, with default mode 0 corresponding to
1920×1080@30fps. The values described here are the SDK DTS defaults and do not represent the current board state if changes were made after flashing:

| defconfig / Board Model | Default Channel Count | Logical Slot to Physical CSI | DTS Bus → Actual I2C | Description |
| --- | ---: | --- | --- | --- |
| `k230_canmv_01studio_defconfig` | 1 | slot0→CSI2 | 4→i2c4 | slot1→CSI0 and slot2→CSI1 are preconfigured but disabled by default; CSI2 uses chip MCLK3 / IO63 (`mclk_id` 2) |
| `k230_canmv_01studio_emmc_defconfig` | 1 | slot0→CSI2 | 0→i2c4 (alias) | Three channels are not preconfigured; CSI2 reset=gpio1_30, and CSI2 uses chip MCLK3 / IO63 |
| `k230_canmv_lckfb_defconfig` | 1 | slot0→CSI2 | 4→i2c4 | slot1→CSI0 and slot2→CSI1 are preconfigured but disabled by default; CSI2 uses chip MCLK3 / IO63 |
| `k230_canmv_defconfig` | 1 | slot0→CSI0 | 0→i2c0 | CanMV 1.0/1.1; CSI0 uses chip MCLK1 / IO13 (`mclk_id` 0) |
| `k230_canmv_v3_defconfig` | 1 | slot0→CSI2 | 0→i2c4 (alias) | CSI2 uses chip MCLK1 / IO13 (`mclk_id` 0) |
| `k230_canmv_dongshanpi_defconfig` | 1 | slot0→CSI2 | 0→i2c4 (alias) | CSI2 uses chip MCLK1 / IO13 (`mclk_id` 0) |
| `k230_canmv_gt6700_defconfig` | 1 | slot0→CSI2 | 0→i2c4 (alias) | CSI2 uses chip MCLK1 / IO13 (`mclk_id` 0) |
| `k230d_canmv_defconfig` | 1 | slot0→CSI2 | 0→i2c4 (alias) | |
| `k230d_canmv_ilp32_defconfig` | DTS 1, VVCAM not enabled | slot0→CSI2 | 0→i2c4 (alias) | `BR2_PACKAGE_VVCAM` is not enabled by default |
| `BPI-CanMV-K230D-Zero_defconfig` | DTS 1, but the configuration is inconsistent | slot0→CSI2 | 0→i2c0 (no alias) | The defconfig uses IMX335, while the DTS still uses GC2093; the I2C connection also needs verification |
| `BPI-CanMV-K230D-Zero_ilp32_defconfig` | DTS 1, VVCAM not enabled | slot0→CSI2 | 0→i2c0 (no alias) | The Sensor configuration is inconsistent, and VVCAM is not enabled by default |
| `k230_evb_defconfig` | 1 | slot0→CSI0 | 0→i2c0 | Inherits the SoC baseline device tree |
| `k230d_canmv_junroc_ai_cam_defconfig` | 1 | slot0→CSI1 | 0→i2c4 (alias) | Special mapping |

Special attention is required for the following:

1. `k230_canmv_01studio_defconfig` and `k230_canmv_lckfb_defconfig` already include
   three-channel GC2093 configurations, but only `mipi.0` is `"okay"` by default, so only one channel is registered.
2. The default Sensor for the `BPI-CanMV-K230D-Zero` defconfig is IMX335, while the baseline
   DTS may still use GC2093. The DTS and Buildroot configuration should be unified before use.
3. Some ILP32 configurations do not enable `BR2_PACKAGE_VVCAM`; modifying only the DTS cannot make the camera work.

### Sensor mode and I2C Bus

`devN-sensor-mode` is the index in the `modes[]` array of the corresponding Sensor driver, not a generic
resolution enumeration. The commonly used GC2093 modes are as follows:

| mode | Resolution and Frame Rate | MIPI lane |
| ---: | --- | ---: |
| 0 | 1920×1080@30fps | 2 |
| 1 | 1920×1080@60fps | 2 |
| 2 | 1280×960@90fps | 2 |
| 3 | 1280×720@90fps | 2 |

After changing the Sensor, the `modes[]` for that Sensor in
`buildroot-overlay/package/vvcam/src/<sensor>.c` must be checked, and the meaning of the GC2093 modes must not be reused.

`devN-sensor-i2c-bus` indicates the Linux adapter number, namely N in `/dev/i2c-N`.
This number is affected by the board-level `aliases`; there are two configuration methods in the SDK.

Legacy single-camera boards often map Linux adapter 0 to hardware i2c4:

```dts
aliases {
    i2c0 = &i2c4;
};
```

In this case, `devN-sensor-i2c-bus = <0>` actually accesses hardware i2c4.

The multi-camera configurations for 01studio SD and lckfb use the actual numbers:

```dts
aliases {
    i2c0 = &i2c0;
    i2c1 = &i2c1;
    i2c4 = &i2c4;
};
```

In this case, the preconfigured bus numbers 4, 0, and 1 represent hardware i2c4, i2c0, and i2c1, respectively. Before modifying the configuration,
the board's `aliases` must be checked first, followed by entering the bus number; at the same time, ensure that the actual I2C controller node is
`"okay"`.

## Enabling Chip MCLK

The sensor main clock can come from the onboard crystal oscillator or be output by the K230 chip MCLK. Currently,
the VVCAM userspace library `libvvcam.so` enables MCLK during Sensor probe / `set_mode`
and disables it during `deinit()` (pipeline destruction). The DTS only determines “which channel to enable and whether it is driven by the chip,”
while the frequency is provided by the corresponding Sensor driver’s `modes[].mclk` and is not configured in the DTS.

### Hardware Numbering and Linux `mclk_id`

The chip provides three MCLK channels. The schematic and U-Boot use names numbered from 1, while the Linux DTS
`devN-sensor-mclk-id` is numbered from 0. The two differ by 1:

| Schematic / U-Boot | DTS `mclk_id` | Typical Pin | pinmux Macro |
| --- | ---: | --- | --- |
| MCLK1 | 0 | IO13 | `K230_IO13_M_CLK1` (alt1) |
| MCLK2 | 1 | IO62 | `K230_IO62_M_CLK2` (alt1) |
| MCLK3 | 2 | IO63 | `K230_IO63_M_CLK3` (alt1) |

`mclk_id` is not equal to the logical slot and is not necessarily equal to the physical CSI `id`. For example, CanMV-v3,
gt6700, and Dongshan Pi map physical CSI2 to `mipi.0`, but the CAM2 clock goes through MCLK1 on IO13, so `dev0-sensor-mclk-id = <0>` must be used. The CSI2 clock on 01studio
goes through MCLK3 on IO63, so `<2>` must be used.

The same pin serves different purposes on different board models. For example, IO62 on 01studio is the CSI2 reset GPIO,
not MCLK2; the default pins in the table above cannot simply be copied blindly.

### DTS Properties

Fill in the following for each channel under `&v4l2isp`:

| Property | Value | Default |
| --- | --- | --- |
| `devN-sensor-use-chip-clk` | `1`: the chip drives the MCLK for this channel; `0`: use the onboard crystal oscillator and have software disable the corresponding channel | Defaults to `1` |
| `devN-sensor-mclk-id` | `0` / `1` / `2`, corresponding to MCLK1 / MCLK2 / MCLK3 | Defaults to the physical CSI `id` for that channel |

The default `mclk_id = csi_idx` is only coincidentally correct when “CSI0 uses MCLK1, CSI1 uses MCLK2, and CSI2 uses
MCLK3.” If the board connects CSI2 to MCLK1 and `devN-sensor-mclk-id = <0>` is not explicitly specified,
the driver will enable MCLK3 according to CSI `id=2`.

These two properties are not used to determine the number of camera channels. Even if they are omitted, the channel will still be registered as long as `mipi.N` and
`devN-sensor-name/mode/i2c-bus` are complete. However, when the Sensor has no clock,
I2C chip ID reads may fail (`Remote I/O`), or images may not be produced.

### Configuration Steps

Enabling chip MCLK requires three things to be completed simultaneously: enable the switch, select the correct channel, and mux the pin to
the MCLK function.

1. Set `devN-sensor-use-chip-clk = <1>` under `&v4l2isp`.
2. Set `devN-sensor-mclk-id` according to the schematic (`0` for MCLK1, `1` for MCLK2,
   and `2` for MCLK3).
3. Under `&iomux`, mux the actual clock pin to the corresponding MCLK, and reference
   that pinctrl on the corresponding `&mipiN`.

Configuration for CSI2 + MCLK1 / IO13 on boards such as CanMV-v3 / gt6700 / Dongshan Pi:

```dts
&mipi0 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&cam2_mclk_pins>;
};

&v4l2isp {
    dev0-sensor-name = "gc2093";
    dev0-sensor-mode = <0>;
    dev0-sensor-i2c-bus = <0>; /* alias i2c0 = &i2c4 */
    dev0-sensor-mclk-id = <0>; /* CSI2 -> MCLK1 / IO13 */
    dev0-sensor-use-chip-clk = <1>;
};

&iomux {
    cam2_mclk_pins: cam2_mclk_pins {
        pins = K230_IO13;
        function = K230_IO13_M_CLK1;
    };
};
```

Configuration for CSI2 + MCLK3 / IO63 on boards such as 01studio:

```dts
&mipi0 {
    /* CSI2 resources omitted */
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&cam2_mclk_pins &cam2_rst_pins>;
};

&v4l2isp {
    dev0-sensor-name = "gc2093";
    dev0-sensor-mode = <0>;
    dev0-sensor-i2c-bus = <4>;
    dev0-sensor-mclk-id = <2>; /* CSI2 -> MCLK3 / IO63 */
    dev0-sensor-use-chip-clk = <1>;
};

&iomux {
    cam2_mclk_pins: cam2_mclk_pins {
        pins = K230_IO63;
        function = K230_IO63_M_CLK3;
    };
};
```

When the onboard crystal oscillator supplies the clock and chip output clock is not needed:

```dts
&v4l2isp {
    dev0-sensor-use-chip-clk = <0>;
};
```

In this case, software disables the corresponding `mclk_id` channel to prevent the chip clock and crystal oscillator from contending for the same pin. You should still
verify according to the schematic that the pin has not been muxed to MCLK.

The MCLK frequency is determined by the Sensor driver mode. For example, GC2093 mode 0 uses
`VVCAM_PLL1_CLK_DIV4` and `div = 25`, at approximately 23.76 MHz. After changing the Sensor or mode,
check the corresponding `modes[].mclk` in `buildroot-overlay/package/vvcam/src/<sensor>.c`. Do not change the frequency in the DTS.

### On-Board Verification

```shell
cat /proc/vsi/isp_subdev0
```

Pay attention to the `mclk_id` and `use_chip_clk` for the channel. When probe fails, userspace also prints
`i2c-N csi-N mclk-N chip_clk-N`. If `mclk_id` does not match the schematic or there is no
corresponding pinmux, modify the DTS rather than the Sensor registers.

## Configuring Single-, Dual-, or Triple-Camera Configurations

### Configuration Rules

The driver counts cameras continuously starting from slot0. Each enabled slot must satisfy all of the following:

1. `status = "okay"` for `mipi.N`.
2. `devN-sensor-name` exists in the DTS `&v4l2isp` node.
3. The matching `devN-sensor-mode` and
   `devN-sensor-i2c-bus` are configured simultaneously.
4. The corresponding I2C controller has been enabled.

Items 1～4 determine whether that camera is counted. If the chip provides a
main clock, `devN-sensor-mclk-id`, `devN-sensor-use-chip-clk`, and the
corresponding MCLK pinmux must also be configured to produce images normally.
For details, see the preceding section. These two items do not affect camera count.

Slots must be configured continuously from 0; enabling only slot0 and slot2 is not allowed. After the first missing or
disabled slot is encountered, subsequent slots are not counted as cameras. You may preconfigure
`dev1/dev2-sensor-*` in advance, as long as `mipi.1/mipi.2` remain disabled, and the system will still register as a single-camera
configuration.

Under `mcm_mask=1`, the three output sequence numbers for logical slot N are fixed as `3N`, `3N+1`, and
`3N+2`, corresponding to MP, SP1, and SP2, respectively.

The `&mipi0`, `&mipi1`, and `&mipi2` in the DTS source files are node label references; after system startup, the
corresponding node names are `mipi.0`, `mipi.1`, and `mipi.2`. Likewise, the DTS label
`&v4l2isp` corresponds to the runtime `v4l2-isp` node.

When mapping physical CSI interfaces, the following fields must be modified as a complete set:

- `id`
- `reg`
- `interrupts`
- `resets`
- `reset-gpios`
- The corresponding `devN-sensor-i2c-bus`
- The corresponding `devN-sensor-mclk-id` and MCLK pinmux (follow the schematic; do not assume it is equal to the CSI `id`)

Different `mipi.N` nodes cannot use the same physical CSI `id`. The `reset-gpios` and I2C
connections are board-level properties and must be based on the schematic and the corresponding board-level DTS; they cannot simply be copied from another board model.

### Modification Locations and Compilation

After compiling the SDK once, the board-level DTS is located at:

```text
output/<defconfig>/build/linux-*/arch/riscv/boot/dts/canaan/
```

For example:

```text
output/k230_canmv_01studio_defconfig/build/linux-*/arch/riscv/boot/dts/canaan/k230-canmv-01studio.dts
output/k230_canmv_lckfb_defconfig/build/linux-*/arch/riscv/boot/dts/canaan/k230-canmv-lckfb-lcd.dts
```

Directly modifying the output directory is suitable for debugging. For a formal modification, the changes should also be synchronized to the SDK's board-level DTS
patch or overlay to prevent them from being lost when the output directory is cleaned.

After modification, recompile:

```shell
make linux-dirclean
make linux-rebuild
make
```

Then update the DTB or full image on the board. If the VVCAM driver or
`libvvcam.so` has also been modified, the corresponding kernel modules, such as `vvcam_mipi`, `vvcam_isp_subdev`,
and `vvcam_video`, as well as the user-space library, must also be updated.

### Single Camera

A single-camera configuration must use logical slot0. The following example uses the preconfigured
“slot0→physical CSI2” configuration on 01studio / lckfb:

```dts
&i2c4 {
    status = "okay";
};

&mipi0 {
    /* id/reg/irq/resets/reset-gpios use the board-level CSI2 configuration */
    status = "okay";
};

&mipi1 {
    status = "disabled";
};

&mipi2 {
    status = "disabled";
};

&v4l2isp {
    dev0-sensor-name = "gc2093";
    dev0-sensor-mode = <0>;
    dev0-sensor-i2c-bus = <4>;
    /* 01studio CSI2 uses MCLK3; fill in according to the schematic for other boards, see "Enabling Chip MCLK" */
    dev0-sensor-mclk-id = <2>;
    dev0-sensor-use-chip-clk = <1>;
};
```

If the board-level alias is `i2c0 = &i2c4`, the bus in the above example should be written as `<0>`. To use CSI0 for the first camera,
change the entire set of physical resources for `mipi0` to CSI0, and modify the reset GPIO and
I2C bus accordingly, rather than enabling `mipi1`.

A single-camera configuration registers 3 VVCAM outputs:

- Internal `video_index`: 0～2.
- Entities: `vvcam-video.0.0`～`vvcam-video.0.2`.
- Currently observed on the target board: `/dev/video1`～`/dev/video3`; other systems may differ.

### Dual Cameras

The following example uses the preconfigured order on 01studio / lckfb:

- slot0 → physical CSI2
- slot1 → physical CSI0

```dts
&i2c0 {
    status = "okay";
};

&i2c4 {
    status = "okay";
};

&mipi0 {
    /* Board-level CSI2 resources */
    status = "okay";
};

&mipi1 {
    /* Board-level CSI0 resources */
    status = "okay";
};

&mipi2 {
    status = "disabled";
};

&v4l2isp {
    dev0-sensor-name = "gc2093";
    dev0-sensor-mode = <0>;
    dev0-sensor-i2c-bus = <4>; /* CSI2 */

    dev1-sensor-name = "gc2093";
    dev1-sensor-mode = <0>;
    dev1-sensor-i2c-bus = <0>; /* CSI0 */
};
```

For 01studio and lckfb, which already have a three-camera configuration preconfigured, confirm that I2C is enabled and change
the `status` of `mipi1` from `"disabled"` to `"okay"`. Other board models also require the CSI0 physical resources, reset GPIO,
and `dev1-sensor-*` for `mipi1` to be added.

Modifying only `mipi1` without enabling the actual corresponding `i2c0` results in the second camera node being registered,
but the Sensor being inaccessible and unable to produce images.

A dual-camera configuration registers 6 VVCAM outputs:

- Internal `video_index`: 0～5.
- Entities: `vvcam-video.0.0`～`vvcam-video.0.5`.
- MP entities for the two cameras: `vvcam-video.0.0` and `vvcam-video.0.3`.
- According to the current target-board device registration order, these usually correspond to `/dev/video1`～`/dev/video6`,
  but applications should still locate them by entity name.

### Three Cameras

The preconfigured order on 01studio / lckfb is:

- slot0 → physical CSI2
- slot1 → physical CSI0
- slot2 → physical CSI1

This is the preconfigured remap order for these two board models, not a mandatory chip order. To use the native order of
slot0→CSI0, slot1→CSI1, and slot2→CSI2, the physical CSI resources, reset GPIO, and corresponding `devN-sensor-i2c-bus` for each
`mipi.N` must be replaced simultaneously.

```dts
&i2c0 {
    status = "okay";
};

&i2c1 {
    status = "okay";
};

&i2c4 {
    status = "okay";
};

&mipi0 {
    /* Board-level CSI2 resources */
    status = "okay";
};

&mipi1 {
    /* Board-level CSI0 resources */
    status = "okay";
};

&mipi2 {
    /* Board-level CSI1 resources */
    status = "okay";
};

&v4l2isp {
    dev0-sensor-name = "gc2093";
    dev0-sensor-mode = <0>;
    dev0-sensor-i2c-bus = <4>; /* CSI2 */

    dev1-sensor-name = "gc2093";
    dev1-sensor-mode = <0>;
    dev1-sensor-i2c-bus = <0>; /* CSI0 */

    dev2-sensor-name = "gc2093";
    dev2-sensor-mode = <0>;
    dev2-sensor-i2c-bus = <1>; /* CSI1 */
};
```

For board models with a preconfigured three-camera configuration, confirm I2C and change both `mipi1` and `mipi2` to
`"okay"`. For board models without a preconfigured setup, all physical resources for the two MIPI nodes and the
`dev1/dev2` configurations must be completed.

The preconfigured three-camera solution requires hardware i2c4, i2c0, and i2c1 to all be available; if `&i2c0` or `&i2c1`
is not set to `status = "okay"`, the corresponding camera cannot work normally.

A three-camera configuration registers 9 VVCAM outputs:

- Internal `video_index`: 0～8.
- Entities: `vvcam-video.0.0`～`vvcam-video.0.8`.
- MP entities for the three cameras: `vvcam-video.0.0`, `vvcam-video.0.3`, and
  `vvcam-video.0.6`.
- The actual device nodes on the current target board are `/dev/video1`～`/dev/video9`.

### Replacing the Sensor

When replacing a GC2093 on one channel with another Sensor, the following steps must be completed synchronously:

1. Change `devN-sensor-name` to a name supported by VVCAM.
2. Set `devN-sensor-mode` according to the `modes[]` in the Sensor driver.
3. Modify the I2C bus, reset GPIO, MCLK channel, lane count, and MIPI frequency according to the actual connections.
4. Confirm that Buildroot's `BR2_PACKAGE_VVCAM_DEF_SENSOR` and `libvvcam.so` include
   the Sensor, and prepare the corresponding ISP calibration file under `/etc/vvcam/`.
5. When multiple channels use the same model and have the same I2C address, use different I2C controllers where possible to avoid address conflicts.

### On-Board Verification

After startup, check the following in order:

```shell
# MIPI 逻辑设备数量
ls /dev/vvcam-mipi.*

# I2C adapter 是否存在
ls /dev/i2c-*

# DTS 中的 sensor、mode、I2C bus、mclk 是否被正确解析
cat /proc/vsi/isp_subdev0

# 物理 CSI 与逻辑 DVP/ISP port 的绑定
dmesg | grep -E 'bind SOURCE_CSI|misc_id|sensor_count'

# 根据 entity 名称查找动态分配的 /dev/video*
for d in /sys/class/video4linux/video*; do
    echo "$(basename "$d"): $(cat "$d/name")"
done
```

The expected results are shown below. The “Current Target-Board Device Nodes” column describes the mapping observed in this test and should not be used as
fixed device numbers in applications:

| Camera Count | MIPI Devices | VVCAM Entities | MP Entities | Current Target-Board Device Nodes |
| ---: | --- | --- | --- | --- |
| 1 | `/dev/vvcam-mipi.0` | `vvcam-video.0.0`～`vvcam-video.0.2` | `vvcam-video.0.0` | `/dev/video1`～`/dev/video3` |
| 2 | `/dev/vvcam-mipi.0`、`/dev/vvcam-mipi.1` | `vvcam-video.0.0`～`vvcam-video.0.5` | `vvcam-video.0.0`、`vvcam-video.0.3` | `/dev/video1`～`/dev/video6` |
| 3 | `/dev/vvcam-mipi.0`、`/dev/vvcam-mipi.1`、`/dev/vvcam-mipi.2` | `vvcam-video.0.0`～`vvcam-video.0.8` | `vvcam-video.0.0`、`vvcam-video.0.3`、`vvcam-video.0.6` | `/dev/video1`～`/dev/video9` |

If images are unavailable on one channel, first check that channel's I2C adapter, `reset-gpios`, chip
MCLK (`mclk_id`, `use_chip_clk`, and pinmux), `devN-sensor-mode`, and MIPI
lane configuration. If the number of `/dev/vvcam-mipi.*` devices or
`vvcam-video.*` entities does not meet expectations, check whether `mipi.N` is enabled continuously from 0
and whether the `devN-sensor-*` configurations are complete; do not determine the camera count based on the maximum `/dev/videoN` number.

### Correspondence Between Configuration and Code

The configuration rules in this document come from the following implementations (paths are relative to `buildroot-overlay/`):

| File | Actual Function |
| --- | --- |
| `package/vvcam/v4l2/common/vvcam_sensor_dt.h` | Counts channels continuously starting from slot0, based on “`devN-sensor-name` exists and `mipi.N` is enabled” |
| `package/vvcam/mipi/vvcam_mipi_driver.c` | Maps the `mipi.N` node index to DVP/ISP port N, and maps `id` to the physical CSI |
| `package/vvcam/v4l2/isp/vvcam_isp_driver.c` | Parses `devN-sensor-name`, mode, I2C bus, `mclk_id`, and `use_chip_clk` |
| `package/vvcam/include/vvcam_mclk.h` | Defines `mclk_id` 0～2 and the clock source/divider in mode |
| `package/vvcam/src/k230_mclk.c` | Controls the chip MCLK registers in user space |
| `package/vvcam/include/vvcam_sensor.h` | `vvcam_sensor_apply_mclk()`: enables the clock according to mode when `use_chip_clk=1`; otherwise disables it |
| `package/vvcam/v4l2/video/vvcam_pipeline_link.h` | Defines the `video_index` and ISP pad corresponding to MP, SP1, and SP2 for each channel |
| `package/vvcam/v4l2/video/vvcam_video_driver.c` | Registers VVCAM outputs according to `sensor_count × 3` in MCM mode |
| `package/vvcam/v4l2/video/vvcam_video_register.c` | Uses `video_register_device(..., -1)` to dynamically allocate `/dev/videoN` |
| `package/vvcam/src/gc2093.c` | Defines GC2093's `modes[]`, resolution, frame rate, lanes, and MIPI frequency |
| `board/canaan/k230-soc/rootfs_overlay/etc/init.d/S31canaan_isp` | Loads `vvcam_video` with `mcm_mask=1` and enables the multi-camera layout described in this document |

Therefore, when configuring the system, do not use the N in `/dev/videoN` to infer the logical port or physical CSI. The recommended procedure is:

1. First use `mipi.N` to determine which logical camera it is.
2. Then use `id` and the complete set of CSI resources to determine the physical interface.
3. Use the correspondingly numbered `devN-sensor-*` to configure the Sensor, I2C, and chip MCLK.
4. After startup, locate the actual `/dev/videoN` by the `vvcam-video.0.K` entity name.
