Skip to content

摄像头驱动

前言

摄像头有很多种,比如常见的USB摄像头,网络摄像头,lvds摄像头,mipi摄像头等。最常见的摄像头是usb摄像头,通常此款摄像头是免驱的,将摄像头插入USB接口即可使用。网络摄像头可以访问相应的ip地址即可获取画面。然而lvds、mipi摄像头则需要驱动,不可以将摄像头插入接口即可使用。

本篇文章主要介绍如何在rk3588上使用mipi摄像头。将从硬件设计和软件驱动两方面进行讲解。

MIPI摄像头硬件设计

电源

通常需要多组电压(如2.8V模拟电源、1.5V数字电源、1.2V核心电源)

信号

信号线包含MIPI CSI-2接口、I2C接口、时钟输入。

数据通常采用1~4对差分信号线进行传输,时钟通道使用1对差分时钟信号同步数据传输,I2C用于传感器配置。

摄像头驱动相关文件路径

在rk3588上添加摄像头驱动主要包含几步操作。

驱动文件:/rk3588/ELF2-linux-source/kernel/drivers/media/i2c.

设备树:rk3588/ELF2-linux-source/kernel/arch/arm64/boot/dts/rockchip

驱动Makefile:/zkb/rk3588/ELF2-linux-source/kernel/drivers/media/i2c

使用./build.sh kconfig修改内核配置。

设备树配置 elf2-3588-Camera.dtsi

c
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2022 Forlinx Co., Ltd.
 *

 * rkisp0 --> rkisp0_vir0/rkisp0_vir1/rkisp0_vir2/rkisp0_vir3
 * rkisp1 --> rkisp1_vir0/rkisp1_vir1/rkisp1_vir2/rkisp1_vir3

 * rkcif_mipi_lvds --> rkcif_mipi_lvds[0-4]_sditf

 * mipicamera0 --> csi2_dcphy0 --> mipi0_csi2 -->rkcif_mipi_lvds  --> rkcif_mipi_lvds_sditf  --> rkisp0_vir0
 * mipicamera1 --> csi2_dcphy1 --> mipi1_csi2 -->rkcif_mipi_lvds1 --> rkcif_mipi_lvds1_sditf --> rkisp0_vir1
	
 * csi2_dphy0 or (csi2_dphy1、csi2_dphy2)	dphy0
 * csi2_dphy3 or (csi2_dphy4、csi2_dphy5)	dphy1

 * mipicamera2 --> csi2_dphy1 -->  mipi2_csi2 -->rkcif_mipi_lvds2 -->rkcif_mipi_lvds2_sditf  --> rkisp0_vir2
 * mipicamera3 --> csi2_dphy2 -->  mipi3_csi2 -->rkcif_mipi_lvds3 -->rkcif_mipi_lvds3_sditf  --> rkisp1_vir0
 * mipicamera4 --> csi2_dphy4 -->  mipi4_csi2 -->rkcif_mipi_lvds4 -->rkcif_mipi_lvds4_sditf  --> rkisp1_vir1
 * mipicamera5 --> csi2_dphy5 -->  mipi5_csi2 -->rkcif_mipi_lvds5 -->rkcif_mipi_lvds5_sditf  --> rkisp1_vir2

 * mipicamera6 --> rkcif_dvp ---> rkcif_dvp_sditf
 */

/ {
    ext_cam_clk: external-camera-clock {
        compatible = "fixed-clock";
        clock-frequency = <24000000>;
        clock-output-names = "CLK_CAMERA_24MHZ";
        #clock-cells = <0>;
    };
};

&rkcif {
    status = "okay";
};

&rkcif_mmu {
    status = "okay";
};


//************************************************
//***  CAM1 OV13850 Configuration description  ***
//************************************************

&mipi_dcphy0 {
    status = "okay";
};

&rkisp0 {
    status = "okay";
};

&isp0_mmu {
    status = "okay";
};

&i2c3 {
    status = "okay";
    clock-frequency = <400000>;

   cam1_dw9763: cam1-dw9763@c {
        compatible = "dongwoon,dw9763";
        status = "okay";
        reg = <0x0c>;
        rockchip,vcm-max-current = <120>;
        rockchip,vcm-start-current = <20>;
        rockchip,vcm-rated-current = <90>;
        rockchip,vcm-step-mode = <3>;
        rockchip,vcm-t-src = <0x20>;
        rockchip,vcm-t-div = <1>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
    };

    cam1_ov13850: cam1-ov13850@10 {
        compatible = "ovti,ov13850";
        status = "okay";
        reg = <0x10>;

        clocks = <&ext_cam_clk>;
        clock-names = "xvclk";
	pinctrl-names = "default";
        pinctrl-0 = <&cam1_reset_gpio
                     &cam1_pwren_gpio>;
        pwdn-gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
        reset-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_HIGH>;

        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
        rockchip,camera-module-name = "ZC-OV13850R2A-V1";
        rockchip,camera-module-lens-name = "Largan-50064B31";
        lens-focus = <&cam1_dw9763>;
        port {
            cam1_ov13850_out: endpoint {
            remote-endpoint = <&mipi_in_0_ucam1>;
            data-lanes = <1 2 3 4>;
            };
        };
    };
};

&csi2_dcphy0 {
    status = "okay";
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;
            mipi_in_0_ucam1: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&cam1_ov13850_out>;
                data-lanes = <1 2 3 4>;
            };
        };
        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;
            csidcphy0_out: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&mipi0_csi2_input>;
            };
        };
    };
};

&mipi0_csi2 {
    status = "okay";
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;
            mipi0_csi2_input: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&csidcphy0_out>;
            };
        };
        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;
            mipi0_csi2_output: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&cif_mipi_lvds0>;
            };
        };
    };
};

&rkcif_mipi_lvds {
    status = "okay";
    port {
        cif_mipi_lvds0: endpoint {
            remote-endpoint = <&mipi0_csi2_output>;
        };
    };
};

&rkcif_mipi_lvds_sditf {
    status = "okay";
    port {
        mipi_lvds_sditf: endpoint {
            remote-endpoint = <&isp0_vir0>;
        };
    };
};

&rkisp0_vir0 {
    status = "okay";
    port {
        #address-cells = <1>;
        #size-cells = <0>;
        isp0_vir0: endpoint@0 {
            reg = <0>;
            remote-endpoint = <&mipi_lvds_sditf>;
        };
    };
};
c
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2022 Forlinx Co., Ltd.
 *

 * rkisp0 --> rkisp0_vir0/rkisp0_vir1/rkisp0_vir2/rkisp0_vir3
 * rkisp1 --> rkisp1_vir0/rkisp1_vir1/rkisp1_vir2/rkisp1_vir3

 * rkcif_mipi_lvds --> rkcif_mipi_lvds[0-4]_sditf

 * mipicamera0 --> csi2_dcphy0 --> mipi0_csi2 -->rkcif_mipi_lvds  --> rkcif_mipi_lvds_sditf  --> rkisp0_vir0
 * mipicamera1 --> csi2_dcphy1 --> mipi1_csi2 -->rkcif_mipi_lvds1 --> rkcif_mipi_lvds1_sditf --> rkisp0_vir1
	
 * csi2_dphy0 or (csi2_dphy1、csi2_dphy2)	dphy0
 * csi2_dphy3 or (csi2_dphy4、csi2_dphy5)	dphy1

 * mipicamera2 --> csi2_dphy1 -->  mipi2_csi2 -->rkcif_mipi_lvds2 -->rkcif_mipi_lvds2_sditf  --> rkisp0_vir2
 * mipicamera3 --> csi2_dphy2 -->  mipi3_csi2 -->rkcif_mipi_lvds3 -->rkcif_mipi_lvds3_sditf  --> rkisp1_vir0
 * mipicamera4 --> csi2_dphy4 -->  mipi4_csi2 -->rkcif_mipi_lvds4 -->rkcif_mipi_lvds4_sditf  --> rkisp1_vir1
 * mipicamera5 --> csi2_dphy5 -->  mipi5_csi2 -->rkcif_mipi_lvds5 -->rkcif_mipi_lvds5_sditf  --> rkisp1_vir2

 * mipicamera6 --> rkcif_dvp ---> rkcif_dvp_sditf
 */

/ {
    ext_cam_clk: external-camera-clock {
        compatible = "fixed-clock";
        clock-frequency = <24000000>;
        clock-output-names = "CLK_CAMERA_24MHZ";
        #clock-cells = <0>;
    };
};

&rkcif {
    status = "okay";
};

&rkcif_mmu {
    status = "okay";
};


//************************************************
//***  CAM1 sc200ai Configuration description  ***
//************************************************

&mipi_dcphy0 {
    status = "okay";
};

&rkisp0 {
    status = "okay";
};

&isp0_mmu {
    status = "okay";
};

&i2c3 {
    status = "okay";
    clock-frequency = <400000>;

   cam1_dw9763: cam1-dw9763@c {
        compatible = "dongwoon,dw9763";
        status = "okay";
        reg = <0x0c>;
        rockchip,vcm-max-current = <120>;
        rockchip,vcm-start-current = <20>;
        rockchip,vcm-rated-current = <90>;
        rockchip,vcm-step-mode = <3>;
        rockchip,vcm-t-src = <0x20>;
        rockchip,vcm-t-div = <1>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
    };

    cam1_sc200ai: cam1-sc200ai@30 {
        compatible = "smartsens,sc200ai";
        status = "okay";
        reg = <0x30>;

        clocks = <&ext_cam_clk>;
        clock-names = "xvclk";
	pinctrl-names = "default";
        pinctrl-0 = <&cam1_reset_gpio
                     &cam1_pwren_gpio>;
        pwdn-gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
        reset-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_HIGH>;

        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
        rockchip,camera-module-name = "C7234A-400";
        rockchip,camera-module-lens-name = "30IRC-2MP-F20";
        rockchip,camera-hdr-mode = <5>;
        port {
            cam1_sc200ai_out: endpoint {
            remote-endpoint = <&mipi_in_0_ucam1>;
            data-lanes = <1 2>;
            };
        };
    };
};

&csi2_dcphy0 {
    status = "okay";
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;
            mipi_in_0_ucam1: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&cam1_sc200ai_out>;
                data-lanes = <1 2>;
            };
        };
        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;
            csidcphy0_out: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&mipi0_csi2_input>;
            };
        };
    };
};

&mipi0_csi2 {
    status = "okay";
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;
            mipi0_csi2_input: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&csidcphy0_out>;
            };
        };
        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;
            mipi0_csi2_output: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&cif_mipi_lvds0>;
            };
        };
    };
};

&rkcif_mipi_lvds {
    status = "okay";
    port {
        cif_mipi_lvds0: endpoint {
            remote-endpoint = <&mipi0_csi2_output>;
        };
    };
};

&rkcif_mipi_lvds_sditf {
    status = "okay";
    port {
        mipi_lvds_sditf: endpoint {
            remote-endpoint = <&isp0_vir0>;
        };
    };
};

&rkisp0_vir0 {
    status = "okay";
    port {
        #address-cells = <1>;
        #size-cells = <0>;
        isp0_vir0: endpoint@0 {
            reg = <0>;
            remote-endpoint = <&mipi_lvds_sditf>;
        };
    };
};

测试

检查驱动是否成功加载

shell
dmesg | grep -i camera

检查ov13850是否加载

shell
dmesg | grep -i ov13850

检查sc200ai是否加载

shell
dmesg | grep -i sc200ai

查找摄像头节点

grep '' /sys/class/video4linux/video*/name

查看设备

shell
v4l2-ctl --list-devices
shell
elf@elf2-desktop:~$ v4l2-ctl --list-devices
rk_hdmirx (fdee0000.hdmirx-controller):
        /dev/video20

rkisp-statistics (platform: rkisp):
        /dev/video18
        /dev/video19

rkcif-mipi-lvds (platform:rkcif):
        /dev/media0

rkcif (platform:rkcif-mipi-lvds):
        /dev/video0
        /dev/video1
        /dev/video2
        /dev/video3
        /dev/video4
        /dev/video5
        /dev/video6
        /dev/video7
        /dev/video8
        /dev/video9
        /dev/video10

rkisp_mainpath (platform:rkisp0-vir0):
        /dev/video11
        /dev/video12
        /dev/video13
        /dev/video14
        /dev/video15
        /dev/video16
        /dev/video17
        /dev/media1

检查/dev下的视频设备节点

shell
ls /dev/video*

查看拓扑结构

查看media节点

shell
ls /dev/media*

查看拓扑结构命令

shell
media-ctl –d /dev/mediaX –p

查看支持的格式

shell
v4l2-ctl -d /dev/video0 --list-formats-ext

输出的结果为:

shell
elf@elf2-desktop:~$ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Type: Video Capture Multiplanar

        [0]: 'RG10' (10-bit Bayer RGRG/GBGB)
                Size: Stepwise 64x64 - 2112x1568 with step 8/8
        [1]: 'BA10' (10-bit Bayer GRGR/BGBG)
                Size: Stepwise 64x64 - 2112x1568 with step 8/8
        [2]: 'GB10' (10-bit Bayer GBGB/RGRG)
                Size: Stepwise 64x64 - 2112x1568 with step 8/8
        [3]: 'BG10' (10-bit Bayer BGBG/GRGR)
                Size: Stepwise 64x64 - 2112x1568 with step 8/8
        [4]: 'Y10 ' (10-bit Greyscale)
                Size: Stepwise 64x64 - 2112x1568 with step 8/8

抓图

从main节点抓取raw图

v4l2-ctl -d /dev/video0 \
  --set-fmt-video=width=1920,height=1080,pixelformat=BG10 \
  --stream-mmap --stream-count=1 --stream-to=output.raw

结果

elf@elf2-desktop:~$ v4l2-ctl -d /dev/video0 \
  --set-fmt-video=width=1920,height=1080,pixelformat=BG10 \
  --stream-mmap --stream-count=1 --stream-to=output.raw
[ 1823.115580] rockchip-mipi-csi2 mipi0-csi2: stream on, src_sd: 00000000f4ff90c6, sd_name:rockchip-csi2-dphy0
[ 1823.115604] rockchip-mipi-csi2 mipi0-csi2: stream ON
<[ 1823.210107] rockchip-mipi-csi2 mipi0-csi2: stream off, src_sd: 00000000f4ff90c6, sd_name:rockchip-csi2-dphy0
[ 1823.210128] rockchip-mipi-csi2 mipi0-csi2: stream OFF

从isp节点拍摄一张图片

gst-launch-1.0 v4l2src device=/dev/video11 num-buffers=1 ! videoconvert ! jpegenc ! filesink location=capture.jpg

用GStreamer显示图像

gst-launch-1.0 v4l2src device=/dev/video11 ! autovideosink

问题排查

  1. 获取 I2C 访问权限
sudo i2cdetect -y 3