跳转至

Camera

本章节介绍V853平台 Camera 模块的开发。

V853支持并口CSI、MIPI,使用VIN camera驱动框架。

Camera通路框架

VIN_data_stream

  • VIN支持灵活配置单/双路输入双ISP多通路输出的规格

  • 引入media框架实现pipeline管理

  • 将libisp移植到用户空间解决GPL问题

  • 将统计buffer独立为v4l2 subdev

  • 将的scaler(vipp)模块独立为v4l2 subdev

  • 将video buffer修改为mplane方式,使用户层取图更方便

  • 采用v4l2-event实现事件管理

  • 采用v4l2-controls新特性

VIN框架

框架简介

VIN是全志基于linux 内核v4l2 框架实现自己Soc 的camera 驱动框架。

  • vin.c是驱动的主要功能实现,包括注册/注销、参数读取、与v4l2上层接口、与各device的下层接口、中断处理、buffer申请切换等;

  • 使用过程中可简单的看成是vin模块+ device模块 +af driver + flash控制模块的方式;

  • modules/sensor文件夹里面是各个sensor的器件层实现,一般包括上下电、初始化,各分辨率切换,yuv sensor包括绝大部分的v4l2定义的ioctrl命令的实现;而raw sensor的话大部分ioctrl命令在vin层调用isp库实现,少数如曝光/增益调节会透过vin层到实际器件层;

  • modules/actuator文件夹内是各种vcm的驱动;

  • modules/flash文件夹内是闪光灯控制接口实现;

  • vin-csivin-mipi为对csi接口和mipi接口的控制文件;

  • vin-isp文件夹为isp的库操作文件;

  • vin-video文件夹内主要是video设备操作文件。

VIN_data_stream

源码结构(linux4.9)

驱动路径位于linux-4.9/drivers/media/platform/sunxi-vin

sunxi-vin:
    │  vin.c                            ;v4l2驱动实现主体(包含视频接口和ISP部分)
    │  vin.h                        ;v4l2驱动头文件
    │  top_reg.c                        ;vin对各v4l2 subdev管理接口实现主体
    │  top_reg.h                        ;管理接口头文件
    │  top_reg_i.h                       ;vin模块接口层部分结构体
    ├── modules
    │   ├── actuator            ;vcm driver
    │   │   ├── actuator.c
    │   │   ├── actuator.h
    │   │   ├── dw9714_act.c
    │   │   ├── Makefile
    │   ├── flash            ;闪光灯 driver
    │   │   ├── flash.c
    │   │   └── flash.h
    │   └── sensor                ;sensor driver
    │       ├── ar0144_mipi.c
    │       ├── camera_cfg.h        ;camera ioctl扩展命令头文件
    │       ├── camera.h        ;camera公用结构体头文件
    │       ├── Makefile
    │       ├── gc2053_mipi.c
    │       ├── ov2775_mipi.c
    │       ├── ov5640.c
    │       ├── sensor-compat-ioctl32.c
    │       ├── sensor_helper.c        ;sensor公用操作接口函数文件
    │       ├── sensor_helper.h
    ├── platform                ;平台相关的配置接口
    ├── utility
    │   ├── bsp_common.c
    │   ├── bsp_common.h
    │   ├── cfg_op.c
    │   ├── cfg_op.h
    │   ├── config.c
    │   ├── config.h
    │   ├── sensor_info.c
    │   ├── sensor_info.h
    │   ├── vin_io.h
    │   ├── vin_os.c
    │   ├── vin_os.h
    │   ├── vin_supply.c
    │   └── vin_supply.h
    ├── vin-cci
    │   ├── sunxi_cci.c
    │   └── sunxi_cci.h
    ├── vin-csi
    │   ├── parser_reg.c
    │   ├── parser_reg.h
    │   ├── parser_reg_i.h
    │   ├── sunxi_csi.c
    │   └── sunxi_csi.h
    ├── vin-isp
    │   ├── sunxi_isp.c
    │   └── sunxi_isp.h
    ├── vin-mipi
    │   ├── sunxi_mipi.c
    │   └── sunxi_mipi.h
    ├── vin-stat
    │   ├── vin_h3a.c
    │   ├── vin_h3a.h
    │   ├── vin_ispstat.c
    │   └── vin_ispstat.h
    ├── vin_test
    ├── vin-video
    │   ├── vin_core.c
    │   ├── vin_core.h
    │   ├── vin_video.c
    │   └── vin_video.h
    └── vin-vipp
        ├── sunxi_scaler.c
        ├── sunxi_scaler.h
        ├── vipp_reg.c
        ├── vipp_reg.h
        └── vipp_reg_i.h

GC2063双摄模组

V853开发板配套了GC2063双摄像头模组,其复用GC2053的驱动,驱动代码路径为:

tina/lichee/linux-4.9/drivers/media/platform/sunxi-vin/modules/sensor/gc2053_mipi.c

此处以GC2063双摄模组为例,介绍V853 Tina系统下的摄像头模块相关配置文件。

Tina配置

Tina 中主要是修改平台的modules.mk配置,modules.mk主要完成两个方面:

  1. 拷贝相关的ko模块到小机rootfs中
  2. rootfs启动时,按顺序自动加载相关的ko模块。

modules.mk文件路径:

tina/target/allwinner/v853-vision/modules.mk

驱动加载配置

 define KernelPackage/vin-v4l2
   SUBMENU:=$(VIDEO_MENU)
   TITLE:=Video input support (staging)
   DEPENDS:=
   FILES:=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-core.ko
   FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-dma-contig.ko
   FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-memops.ko
   FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-v4l2.ko
   FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/vin_io.ko
   FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/modules/sensor/gc2053_mipi.ko
 #  FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/modules/sensor_power/sensor_power.ko
   FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/vin_v4l2.ko
   FILES+=$(LINUX_DIR)/drivers/input/sensor/da380/da380.ko
   AUTOLOAD:=$(call AutoProbe,videobuf2-core videobuf2-dma-contig videobuf2-memops videobuf2-v4l2 vin_io gc2053_mipi vin_v4l2 da380.ko)
 endef

 define KernelPackage/vin-v4l2/description
  Kernel modules for video input support
 endef

 $(eval $(call KernelPackage,vin-v4l2))

S00mpp配置

V853平台在完成modules.mk配置后,还需要完成.ko挂载脚本S00mpp的配置,以便开机快速启动摄像头模块。

S00mpp配置路径:

target/allwinner/v853-vision/busybox-init-base-files/etc/init.d

脚本对摄像头驱动进行了提前加载,应用需要使用的时候即可快速配置并启动。

#!/bin/sh
#
# Load mpp modules....
#

MODULES_DIR="/lib/modules/`uname -r`"

start() {
    printf "Load mpp modules\n"
    insmod $MODULES_DIR/videobuf2-core.ko
    insmod $MODULES_DIR/videobuf2-memops.ko
    insmod $MODULES_DIR/videobuf2-dma-contig.ko
    insmod $MODULES_DIR/videobuf2-v4l2.ko
    insmod $MODULES_DIR/vin_io.ko
#   insmod $MODULES_DIR/sensor_power.ko
    insmod $MODULES_DIR/gc4663_mipi.ko
    insmod $MODULES_DIR/vin_v4l2.ko
    insmod $MODULES_DIR/sunxi_aio.ko
    insmod $MODULES_DIR/sunxi_eise.ko
#   insmod $MODULES_DIR/vipcore.ko
}

stop() {
    printf "Unload mpp modules\n"
#   rmmod $MODULES_DIR/vipcore.ko
    rmmod $MODULES_DIR/sunxi_eise.ko
    rmmod $MODULES_DIR/sunxi_aio.ko
    rmmod $MODULES_DIR/vin_v4l2.ko
    rmmod $MODULES_DIR/gc4663_mipi.ko
#   rmmod $MODULES_DIR/sensor_power.ko
    rmmod $MODULES_DIR/vin_io.ko
    rmmod $MODULES_DIR/videobuf2-v4l2.ko
    rmmod $MODULES_DIR/videobuf2-dma-contig.ko
    rmmod $MODULES_DIR/videobuf2-memops.ko
    rmmod $MODULES_DIR/videobuf2-core.ko
}

case "$1" in
    start)
    start
    ;;
    stop)
    stop
    ;;
    restart|reload)
    stop
    start
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
esac

exit $?

DTS配置

DTS配置文件路径:

tina/device/config/chips/v853/configs/vision

camera相关配置:

vind0:vind@0 {
    vind0_clk = <300000000>;
    status = "okay";

    csi2:csi@2 {
        pinctrl-names = "default","sleep";
        pinctrl-0 = <&ncsi_pins_a>;
        pinctrl-1 = <&ncsi_pins_b>;
        status = "disabled";
    };

    tdm0:tdm@0 {
        work_mode = <1>;
    };

    isp00:isp@0 {
        work_mode = <1>;
    };

    scaler00:scaler@0 {
        work_mode = <1>;
    };

    scaler10:scaler@4 {
        work_mode = <1>;
    };

    scaler20:scaler@8 {
        work_mode = <1>;
    };

    scaler30:scaler@12 {
        work_mode = <1>;
    };

    actuator0:actuator@0 {
        device_type = "actuator0";
        actuator0_name = "ad5820_act";
        actuator0_slave = <0x18>;
        actuator0_af_pwdn = <>;
        actuator0_afvdd = "afvcc-csi";
        actuator0_afvdd_vol = <2800000>;
        status = "disabled";
    };

    flash0:flash@0 {
        device_type = "flash0";
        flash0_type = <2>;
        flash0_en = <>;
        flash0_mode = <>;
        flash0_flvdd = "";
        flash0_flvdd_vol = <>;
        status = "disabled";
    };

    sensor0:sensor@0 {
        device_type = "sensor0";
        sensor0_mname = "gc2053_mipi";
        sensor0_twi_cci_id = <1>;
        sensor0_twi_addr = <0x6e>;
        sensor0_mclk_id = <0>;
        sensor0_pos = "rear";
        sensor0_isp_used = <1>;
        sensor0_fmt = <1>;
        sensor0_stby_mode = <0>;
        sensor0_vflip = <0>;
        sensor0_hflip = <0>;
        sensor0_iovdd-supply = <&reg_aldo2>;
        sensor0_iovdd_vol = <1800000>;
        sensor0_avdd-supply = <&reg_bldo2>;
        sensor0_avdd_vol = <2800000>;
        sensor0_dvdd-supply = <&reg_dldo2>;
        sensor0_dvdd_vol = <1200000>;
        sensor0_power_en = <>;
        sensor0_reset = <&pio PA 18 1 0 1 0>;
        sensor0_pwdn = <&pio PA 19 1 0 1 0>;
        sensor0_sm_hs = <>;
        sensor0_sm_vs = <>;
        flash_handle = <&flash0>;
        act_handle = <&actuator0>;
        status  = "okay";
    };

    sensor1:sensor@1 {
        device_type = "sensor1";
        sensor1_mname = "gc2053_mipi_2";
        sensor1_twi_cci_id = <0>;
        sensor1_twi_addr = <0x7f>;
        sensor1_mclk_id = <1>;
        sensor1_pos = "front";
        sensor1_isp_used = <1>;
        sensor1_fmt = <1>;
        sensor1_stby_mode = <0>;
        sensor1_vflip = <0>;
        sensor1_hflip = <0>;
        sensor1_iovdd-supply = <&reg_aldo2>;
        sensor1_iovdd_vol = <1800000>;
        sensor1_avdd-supply = <&reg_bldo2>;
        sensor1_avdd_vol = <2800000>;
        sensor1_dvdd-supply = <&reg_dldo2>;
        sensor1_dvdd_vol = <1200000>;
        sensor1_power_en = <>;
        sensor1_reset = <&pio PA 20 1 0 1 0>;
        sensor1_pwdn = <&pio PA 21 1 0 1 0>;
        sensor1_sm_hs = <>;
        sensor1_sm_vs = <>;
        flash_handle = <>;
        act_handle = <>;
        status  = "okay";
    };

    vinc00:vinc@0 {
        vinc0_csi_sel = <0>;
        vinc0_mipi_sel = <0>;
        vinc0_isp_sel = <0>;
        vinc0_isp_tx_ch = <0>;
        vinc0_tdm_rx_sel = <0>;
        vinc0_rear_sensor_sel = <0>;
        vinc0_front_sensor_sel = <0>;
        vinc0_sensor_list = <0>;
        work_mode = <0x1>;
        status = "okay";
    };

    vinc01:vinc@1 {
        vinc1_csi_sel = <1>;
        vinc1_mipi_sel = <1>;
        vinc1_isp_sel = <1>;
        vinc1_isp_tx_ch = <0>;
        vinc1_tdm_rx_sel = <1>;
        vinc1_rear_sensor_sel = <1>;
        vinc1_front_sensor_sel = <1>;
        vinc1_sensor_list = <0>;
        status = "okay";
    };

    vinc02:vinc@2 {
        vinc2_csi_sel = <2>;
        vinc2_mipi_sel = <0xff>;
        vinc2_isp_sel = <2>;
        vinc2_isp_tx_ch = <2>;
        vinc2_tdm_rx_sel = <2>;
        vinc2_rear_sensor_sel = <0>;
        vinc2_front_sensor_sel = <0>;
        vinc2_sensor_list = <0>;
        status = "disabled";
    };

    vinc03:vinc@3 {
        vinc3_csi_sel = <0>;
        vinc3_mipi_sel = <0xff>;
        vinc3_isp_sel = <0>;
        vinc3_isp_tx_ch = <0>;
        vinc3_tdm_rx_sel = <0>;
        vinc3_rear_sensor_sel = <1>;
        vinc3_front_sensor_sel = <1>;
        vinc3_sensor_list = <0>;
        status = "disabled";
    };

    vinc10:vinc@4 {
        vinc4_csi_sel = <0>;
        vinc4_mipi_sel = <0>;
        vinc4_isp_sel = <0>;
        vinc4_isp_tx_ch = <0>;
        vinc4_tdm_rx_sel = <0>;
        vinc4_rear_sensor_sel = <0>;
        vinc4_front_sensor_sel = <0>;
        vinc4_sensor_list = <0>;
        work_mode = <0x1>;
        status = "okay";
    };

    vinc11:vinc@5 {
        vinc5_csi_sel = <1>;
        vinc5_mipi_sel = <1>;
        vinc5_isp_sel = <1>;
        vinc5_isp_tx_ch = <0>;
        vinc5_tdm_rx_sel = <1>;
        vinc5_rear_sensor_sel = <1>;
        vinc5_front_sensor_sel = <1>;
        vinc5_sensor_list = <0>;
        status = "okay";
    };

    vinc12:vinc@6 {
        vinc6_csi_sel = <2>;
        vinc6_mipi_sel = <0xff>;
        vinc6_isp_sel = <0>;
        vinc6_isp_tx_ch = <0>;
        vinc6_tdm_rx_sel = <0>;
        vinc6_rear_sensor_sel = <0>;
        vinc6_front_sensor_sel = <0>;
        vinc6_sensor_list = <0>;
        status = "disabled";
    };

    vinc13:vinc@7 {
        vinc7_csi_sel = <2>;
        vinc7_mipi_sel = <0xff>;
        vinc7_isp_sel = <0>;
        vinc7_isp_tx_ch = <0>;
        vinc7_tdm_rx_sel = <0>;
        vinc7_rear_sensor_sel = <0>;
        vinc7_front_sensor_sel = <0>;
        vinc7_sensor_list = <0>;
        status = "disabled";
    };

    vinc20:vinc@8 {
        vinc8_csi_sel = <0>;
        vinc8_mipi_sel = <0x0>;
        vinc8_isp_sel = <0>;
        vinc8_isp_tx_ch = <0>;
        vinc8_tdm_rx_sel = <0>;
        vinc8_rear_sensor_sel = <0>;
        vinc8_front_sensor_sel = <0>;
        vinc8_sensor_list = <0>;
        work_mode = <0x1>;
        status = "okay";
    };

    vinc21:vinc@9 {
        vinc9_csi_sel = <2>;
        vinc9_mipi_sel = <0xff>;
        vinc9_isp_sel = <0>;
        vinc9_isp_tx_ch = <0>;
        vinc9_tdm_rx_sel = <0>;
        vinc9_rear_sensor_sel = <0>;
        vinc9_front_sensor_sel = <0>;
        vinc9_sensor_list = <0>;
        status = "disabled";
    };

    vinc22:vinc@10 {
        vinc10_csi_sel = <2>;
        vinc10_mipi_sel = <0xff>;
        vinc10_isp_sel = <0>;
        vinc10_isp_tx_ch = <0>;
        vinc10_tdm_rx_sel = <0>;
        vinc10_rear_sensor_sel = <0>;
        vinc10_front_sensor_sel = <0>;
        vinc10_sensor_list = <0>;
        status = "disabled";
    };

    vinc23:vinc@11 {
        vinc11_csi_sel = <2>;
        vinc11_mipi_sel = <0xff>;
        vinc11_isp_sel = <0>;
        vinc11_isp_tx_ch = <0>;
        vinc11_tdm_rx_sel = <0>;
        vinc11_rear_sensor_sel = <0>;
        vinc11_front_sensor_sel = <0>;
        vinc11_sensor_list = <0>;
        status = "disabled";
    };

    vinc30:vinc@12 {
        vinc12_csi_sel = <0>;
        vinc12_mipi_sel = <0x0>;
        vinc12_isp_sel = <0>;
        vinc12_isp_tx_ch = <0>;
        vinc12_tdm_rx_sel = <0>;
        vinc12_rear_sensor_sel = <0>;
        vinc12_front_sensor_sel = <0>;
        vinc12_sensor_list = <0>;
        work_mode = <0x1>;
        status = "okay";
    };

    vinc31:vinc@13 {
        vinc13_csi_sel = <2>;
        vinc13_mipi_sel = <0xff>;
        vinc13_isp_sel = <0>;
        vinc13_isp_tx_ch = <0>;
        vinc13_tdm_rx_sel = <0>;
        vinc13_rear_sensor_sel = <0>;
        vinc13_front_sensor_sel = <0>;
        vinc13_sensor_list = <0>;
        status = "disabled";
    };

    vinc32:vinc@14 {
        vinc14_csi_sel = <2>;
        vinc14_mipi_sel = <0xff>;
        vinc14_isp_sel = <0>;
        vinc14_isp_tx_ch = <0>;
        vinc14_tdm_rx_sel = <0>;
        vinc14_rear_sensor_sel = <0>;
        vinc14_front_sensor_sel = <0>;
        vinc14_sensor_list = <0>;
        status = "disabled";
    };

    vinc33:vinc@15 {
        vinc15_csi_sel = <2>;
        vinc15_mipi_sel = <0xff>;
        vinc15_isp_sel = <0>;
        vinc15_isp_tx_ch = <0>;
        vinc15_tdm_rx_sel = <0>;
        vinc15_rear_sensor_sel = <0>;
        vinc15_front_sensor_sel = <0>;
        vinc15_sensor_list = <0>;
        status = "disabled";
    };
};

修改该文件之后,需要重新编译固件再打包,才会更新到 dts。

使用双摄像头时,双摄分别使用到两个ISP,那么内核需要选上 SUPPORT_ISP_TDM 配置。

在命令行进入Tina根目录,执行命令进入配置主界面:

source build/envsetup.sh    (详见1)
lunch 方案编号                  (详见2)
make menuconfig                  (详见3)

详注:
    1.加载环境变量及tina提供的命令;
    2.输入编号,选择方案;
    3.进入配置主界面(对一个shell而言,前两个命令只需要执行一次)

make menuconfig配置路径:

Kernel modules
    └─>Video Support
                └─>kmod-sunxi-vfe(vfe框架的csi camera)                (详见1)
                └─>kmod-sunxi-vin(vin框架的csi camera)                (详见2)
                └─>kmod-sunxi-uvc(uvc camera)                      (详见3)

详注:
    1.平台使用vfe框架的csi camera选择该驱动;
    2.平台使用vin框架的csi camera选择该驱动;(该项与vfe框架,在同一个平台只会出现其中一个)
    3.usb camera选择该驱动;

在完成 sensor 驱动编写,modules.mk 和板级配置后,通过make menuconfig选上相应的驱动,camera即可正常使用。

双目GC2063配置

适配双目GC2063主要修改以下几处:

1、dts设备树 文件:device/config/chips/v853/configs/perf1/board.dts 修改:

  • (1)修改csi/isp频率

为节省带宽,可将csi/isp频率由原来默认的300MHz降至200MHz。

vind0:vind@0 {
-           vind0_clk = <300000000>;
+           vind0_clk = <200000000>;
            status = "okay";
  • (2)修改成离线模式

因双目场景下,ISP需分时复用,故只能支持离线模式,即work_mode改成1(离线)。

tdm0:tdm@0 {
-               work_mode = <0>;
+               work_mode = <1>;
            };

            isp00:isp@0 {
-               work_mode = <0>;
+               work_mode = <1>;
            };

            scaler00:scaler@0 {
-               work_mode = <0>;
+               work_mode = <1>;
            };

            scaler10:scaler@4 {
-               work_mode = <0>;
+               work_mode = <1>;
            };

            scaler20:scaler@8 {
-               work_mode = <0>;
+               work_mode = <1>;
            };

            scaler30:scaler@12 {
-               work_mode = <0>;
+               work_mode = <1>;
            };
  • (3)修改sensor配置

sensor0默认是gc2053,无需修改。但是需要修改sensor1的配置为gc2053。修改后,sensor0和sensor1的配置如下:

sensor0:sensor@0 {
                device_type = "sensor0";
                sensor0_mname = "gc2053_mipi";
                sensor0_twi_cci_id = <1>;
                sensor0_twi_addr = <0x6e>;
                sensor0_mclk_id = <0>;
                sensor0_pos = "rear";
                sensor0_isp_used = <1>;
                sensor0_fmt = <1>;
                sensor0_stby_mode = <0>;
                sensor0_vflip = <0>;
                sensor0_hflip = <0>;
                sensor0_iovdd-supply = <&reg_aldo2>;
                sensor0_iovdd_vol = <1800000>;
                sensor0_avdd-supply = <&reg_bldo2>;
                sensor0_avdd_vol = <2800000>;
                sensor0_dvdd-supply = <&reg_dldo2>;
                sensor0_dvdd_vol = <1200000>;
                sensor0_power_en = <>;
                sensor0_reset = <&pio PA 18 1 0 1 0>;
                sensor0_pwdn = <&pio PA 19 1 0 1 0>;
                sensor0_sm_hs = <>;
                sensor0_sm_vs = <>;
                flash_handle = <&flash0>;
                act_handle = <&actuator0>;
                status  = "okay";
            };

            sensor1:sensor@1 {
                device_type = "sensor1";
                sensor1_mname = "gc2053_mipi_2";
                sensor1_twi_cci_id = <0>;
                sensor1_twi_addr = <0x7e>;
                sensor1_mclk_id = <1>;
                sensor1_pos = "front";
                sensor1_isp_used = <1>;
                sensor1_fmt = <1>;
                sensor1_stby_mode = <0>;
                sensor1_vflip = <0>;
                sensor1_hflip = <0>;
                sensor1_iovdd-supply = <&reg_aldo2>;
                sensor1_iovdd_vol = <1800000>;
                sensor1_avdd-supply = <&reg_bldo2>;
                sensor1_avdd_vol = <2800000>;
                sensor1_dvdd-supply = <&reg_dldo2>;
                sensor1_dvdd_vol = <1200000>;
                sensor1_power_en = <>;
                sensor1_reset = <&pio PA 20 1 0 1 0>;
                sensor1_pwdn = <&pio PA 21 1 0 1 0>;
                sensor1_sm_hs = <>;
                sensor1_sm_vs = <>;
                flash_handle = <>;
                act_handle = <>;
                status  = "okay";
            };
  • (4)修改各video节点的配置

双目GC2053场景下,可支持的video节点分别为: sensor0:video 0/4/8/12 sensor1:video 1/5/9/13

故只需修改以上8个video节点的配置即可。修改后的配置如下:

vinc00:vinc@0 {
                vinc0_csi_sel = <0>;
                vinc0_mipi_sel = <0>;
                vinc0_isp_sel = <0>;
                vinc0_isp_tx_ch = <0>;
                vinc0_tdm_rx_sel = <0>;
                vinc0_rear_sensor_sel = <0>;
                vinc0_front_sensor_sel = <0>;
                vinc0_sensor_list = <0>;
                work_mode = <0x1>;
                status = "okay";
            };

            vinc01:vinc@1 {
                vinc1_csi_sel = <1>;
                vinc1_mipi_sel = <1>;
                vinc1_isp_sel = <1>;
                vinc1_isp_tx_ch = <0>;
                vinc1_tdm_rx_sel = <1>;
                vinc1_rear_sensor_sel = <1>;
                vinc1_front_sensor_sel = <1>;
                vinc1_sensor_list = <0>;
                status = "okay";
            };


            vinc10:vinc@4 {
                vinc4_csi_sel = <0>;
                vinc4_mipi_sel = <0>;
                vinc4_isp_sel = <0>;
                vinc4_isp_tx_ch = <0>;
                vinc4_tdm_rx_sel = <0>;
                vinc4_rear_sensor_sel = <0>;
                vinc4_front_sensor_sel = <0>;
                vinc4_sensor_list = <0>;
                work_mode = <0x1>;
                status = "okay";
            };

            vinc11:vinc@5 {
                vinc5_csi_sel = <1>;
                vinc5_mipi_sel = <1>;
                vinc5_isp_sel = <1>;
                vinc5_isp_tx_ch = <0>;
                vinc5_tdm_rx_sel = <1>;
                vinc5_rear_sensor_sel = <1>;
                vinc5_front_sensor_sel = <1>;
                vinc5_sensor_list = <0>;
                status = "okay";
            };


            vinc20:vinc@8 {
                vinc8_csi_sel = <0>;
                vinc8_mipi_sel = <0x0>;
                vinc8_isp_sel = <0>;
                vinc8_isp_tx_ch = <0>;
                vinc8_tdm_rx_sel = <0>;
                vinc8_rear_sensor_sel = <0>;
                vinc8_front_sensor_sel = <0>;
                vinc8_sensor_list = <0>;
                work_mode = <0x1>;
                status = "okay";
            };

            vinc21:vinc@9 {
                vinc9_csi_sel = <0>;
                vinc9_mipi_sel = <0x0>;
                vinc9_isp_sel = <0>;
                vinc9_isp_tx_ch = <0>;
                vinc9_tdm_rx_sel = <0>;
                vinc9_rear_sensor_sel = <0>;
                vinc9_front_sensor_sel = <0>;
                vinc9_sensor_list = <0>;
                work_mode = <0x1>;
                status = "okay";
            };


            vinc30:vinc@12 {
                vinc12_csi_sel = <0>;
                vinc12_mipi_sel = <0x0>;
                vinc12_isp_sel = <0>;
                vinc12_isp_tx_ch = <0>;
                vinc12_tdm_rx_sel = <0>;
                vinc12_rear_sensor_sel = <0>;
                vinc12_front_sensor_sel = <0>;
                vinc12_sensor_list = <0>;
                work_mode = <0x1>;
                status = "okay";
            };

            vinc31:vinc@13 {
                vinc13_csi_sel = <1>;
                vinc13_mipi_sel = <1>;
                vinc13_isp_sel = <1>;
                vinc13_isp_tx_ch = <0>;
                vinc13_tdm_rx_sel = <1>;
                vinc13_rear_sensor_sel = <1>;
                vinc13_front_sensor_sel = <1>;
                vinc13_sensor_list = <0>;
                status = "okay";
            };

2、内核配置

双mipi sensor场景下,ISP分时复用,需要使用TDM,故需打开TDM。 文件:device/config/chips/v853/configs/perf1/linux/config-4.9 修改:打开TDM配置

CONFIG_SUPPORT_ISP_TDM=y
CONFIG_TDM_LBC_EN=y

3、mpp middleware

首先,确保mpp配置中选中了gc2053 sensor。检查方法: make menuconfig,依次进入配置项:

Allwinner  --->
    eyesee-mpp  --->
        [*]   select sensor
        [*]     use sensor gc2053

然后,在ISP效果文件中,为第二个sensor添加效果文件配置。 位置:external/eyesee-mpp/middleware/sun8iw21/media/LIBRARY/libisp/isp_cfg/isp_ini_parse.c 修改:将原来的两组gc2053_mipi配置分别拷贝一份,然后修改名字为gc2053_mipi_2即可,其他参数不用修改。

struct isp_cfg_array cfg_arr[] = {
...

#ifdef SENSOR_GC2053
    {"gc2053_mipi",  "gc2053_mipi_isp600_20220511_164617_vlc4_day", 1920, 1088, 20, 0, 0, &gc2053_mipi_v853_isp_cfg},
    {"gc2053_mipi",  "gc2053_mipi_isp600_20220415_144837_ir", 1920, 1088, 20, 0, 1, &gc2053_mipi_ir_isp_cfg},
    {"gc2053_mipi_2",  "gc2053_mipi_isp600_20220511_164617_vlc4_day", 1920, 1088, 20, 0, 0, &gc2053_mipi_v853_isp_cfg},
    {"gc2053_mipi_2",  "gc2053_mipi_isp600_20220415_144837_ir", 1920, 1088, 20, 0, 1, &gc2053_mipi_ir_isp_cfg},
#endif

...
}

按以上步骤修改完成后,需重新编译mpp和固件,命令如下:

cleanmpp && mkmpp && mp

4、双目GC2053验证

支持验证双目GC2053的mpp sample有: sample_rtsp(支持双目采集+编码+RTSP)、sample_smartIPC_demo(支持双目采集+编码+RTSP+人形检测)、sample_smartPreview_demo(支持双目采集+预览) 如果不需要RTSP功能,注释掉代码中的宏 #define SUPPORT_RTSP_TEST 即可。

ISP效果调试

正在撰写,敬请期待……

USB Camera适配方法

make menuconfig下修改的选项和make kernel_menuconfig有点冲突。

故make menuconfig不需要做修改,只需要添加内核支持就可以了:

---->make kernel_menuconfig
---->Device Drivers
---->Multimedia support
---->Media USB Adapters

VIN_data_stream

在menuconfig中完成内核支持的添加后用指令cat /sys/devices/platform/soc/usbc0/usb_host,再插上USB摄像头就可以识别出来了

VIN_data_stream VIN_data_stream

常见问题

内核代码注意事项

驱动中一般禁止使用mdelay或者msleep实现延时,例如使用msleep实现10~20ms的延时,通常会因为系统调度而变成延时更长的时间,这种做法精度较差。所以如果需要使用ms级别延时,则使用usleep_range(a, b),比如原来mdelay(1)mdelay(10)可改为usleep_range(1000, 2000)usleep_range(10000, 12000)。如果是长达30ms或以上的延时可选择使用msleep();

中断过程中不能使用msleepusleep_range,除了特殊情况必须加延时之外,mdelay一般也不可使用。

如何进行Camera模块调试

Camera模块调试一般可以分为三步: (1)使用lsmod命令查看驱动是否加载,查看 /lib/modules/ 内核版本号 目录下是否存在相应的ko,如果没有,确认modules.mk是否修改正确,配置了开机自动加载。如果存在相应的.ko,可手动加载测试确认ko是否正常,手动加载成功,则确认内核的版本是否一致,导致开机时没有找到相应的ko从而没有加载;
(2)使用ls /dev/v\*查看是否有video0/1节点生成;
(3)在adb shell 中使用 cat /proc/kmsg 命令,或者是使用串口查看内核的打印信息,查看不能正常加载的原因。一般情况下驱动加载不成功的原因有:一是读取的sys_config.fex文件中的配置信息与加载的驱动不匹配,二是probe函数遇到某些错误没能正确的完成probe的时候返回。

移植一款 sensor 需要进行哪些操作

移植 camera sensor,主要进行以下操作:

(1)根据主板的原理图,确认与 sensor 模组的接口是否一致,一致才可以保证配置和数据的正常接收。
(2)根据产品的需求,让 sensor 模组厂提供产品所需的分辨率、帧率的寄存器配置,这一步需要注意,提供的配置需要是和模组匹配的。比如模组的 mipi 接口只引出 2lane,而提供的寄存器配置却是配置为 4lane 输出的,那么该配置在该模组无法正常使用,让模组厂提供该模组可以正常使用的正确配置。注意,该寄存器配置 SOC 原厂没有,需要 sensor 厂提供。
(3)拿到寄存器配置之后,按照Tina驱动模块配置等相关文档完成 sensor 驱动的编写。
(4)在完成驱动的编写之后,按照Tina系统软件开发指南等相关文档等完成 modules.mk 的修改。
(5)根据板子的原理图与模组的硬件连接,参照Tina系统软件开发指南等相关文档完成 sys_config.fex 或者 board.dts 的修改。
(6)完成上述操作之后,在menuconfig中进行camera相关配置,并配置camera demo进行调试测试,测试驱动移植是否正常。

如何开始camera模块调试

初次调试建议打开device中的DEV_DBG_EN为1,方便调试。

调试camera常见现象和功能检查

(1)insmod之后首先看内核打印,看加载有无错误打印,部分驱动在加载驱动进行上下电时候会进行i2c操作,如果此时报错的话就不需要再进入camera了,先检查是否io或电源配置不对。或者是在复用模组时候有可能是另外一个模组将i2c拉住了。

(2)如果i2c读写没有问题的话,一般就可以认为sensor控制是ok的,只需要根据sensor的配置填好H/VREF、PCLK的极性就能正常接收图像了。这个时候可以在进入camera应用之后用示波器测量sensor的各个信号,看h/vref、pclk极性、幅度是否正常(2.8V的vpp)。

(3)如果看到画面了,但是看起来是绿色和粉红色的,但是有轮廓,一般是YUYV的顺序设置反了,可检查yuyv那几个寄存器是否填写正确配置,其次,看是否是在配置的其他地方有填写同一个寄存器的地方导致将yuyv fmt的寄存器被改写。

(4) 如果画面颜色正常,但是看到有一些行是粉红或者绿色的,往往是sensor信号质量不好所致,通常在比较长的排线中出现这个情况。在信号质量不好并且yuyv顺序不对的时候也会看见整个画面的是绿色的花屏。

(5)当驱动能力不足的时候增强sensor的io驱动能力有可能解决这个问题。此时用示波器观察pclk和数据线可能会发现:pclk波形摆幅不够IOVDD的幅度,或者是data输出波形摆幅有时候能高电平达到IOVDD的幅度,有时候可能连一半都不够。

(6)如果是两个模组复用数据线的话,不排除是另外一个sensor在进入standby时候没有将其数据线设置成高阻,也会影响到当前模组的信号摆幅,允许的话可以剪断另一个模组来证实。

(7)当画面都正常之后检查前置摄像头垂直方向是否正确,水平方向是否是镜像,后置水平垂直是否正确,不对的话可以调节sys_config.fex中的hflip和vflip参数来解决,但如果屏幕上看到的画面与人眼看到的画面是成90度的话,只能是通过修改模组的方向来解决。

(8)之后可以检查不同分辨率之间的切换是否ok,是否有切换不成功的问题;以及拍照时候是否图形正常,亮度颜色是否和预览一致;双摄像头的话需要检查前后切换是否正常。

(9)如果上述都没有问题的话,可认为驱动无大问题,接下来可以进行其他功能(awb/exp bias/color effect等其他功能的测试)。

(10) 测试对焦功能,单次点触屏幕,可正确对上不同距离的物体;不点屏幕时候可以自动对焦对上画面中心物体,点下拍照后拍出来的画面能清晰。

(11)打开闪光灯功能,检查在单次对焦时候能打开灯,对完之后无论成功失败或者超时能够关闭,在点下拍照之后能打开,拍完之后能关闭。

(12)如果加载模块后,发现dev/videoX节点没有生成,请检查下面几点:

a. 模块加载的顺序
    一定要按照以下顺序加载模块
    insmod videobuf-core.ko
    insmod videobuf-dma-contig.ko
    ;如果有对应的vcm driver,在这里加载,如果没有,请省略。
    insmod actuator.ko
    insmod ad5820_act.ko
    ;以下是camera驱动和vfe驱动的加载,先安装一些公共资源。
    insmod vfe_os.ko
    insmod vfe_subdev.ko
    insmod cci.ko
    insmod ov5640.ko
    insmod gc0308.ko
    ;如果一个csi接两个camera,所有camera对应的ko都要在vfe_v4l2.ko之前加载。
    insmod vfe_v4l2.ko

b. sys_config.fex配置
    vip_used             = 1            ;确保used为1
    vip_dev_qty           = 2            ;确保csi接口上接的camera数量与ko加载情况相同
    vip_dev0_mname        = "ov5640"  ;确保camera型号与ko加载情况相同
    vip_dev0_twi_id        = 1            ;确保camera使用的i2c总线id与配置一样

    vip_dev1_mname         = "gc0308"  ;确保camera型号与ko加载情况相同
    vip_dev1_twi_id        = 1            ;确保camera使用的i2c总线id与配置一样

常见异常现象与处理方法

  • I2C通信出错
    I2C出现问题内核一般会伴随打印 cci_write_aX_dX error! slave = 0xXX, addr = 0xXX, value = 0xXX"

如果与此同时,内核出现打印chip found is not an target chip.,则说明在初始化camera前,读取camera的ID已经失败。

此时,一般是如下几点出现问题。

a. 最先考虑应该是更换一个camera模组试试。
b. 电源
    检查sys_config.fex
    vip_dev0_iovdd               = "axp22_eldo3"
    vip_dev0_iovdd_vol            = 2800000
    vip_dev0_avdd                = "axp22_dldo4"
    vip_dev0_avdd_vol            = 2800000
    vip_dev0_dvdd                = "axp22_eldo2"
    vip_dev0_dvdd_vol            = 1500000
    一定要与原理图设计保持一致。必要时,需要用万用表测量camera模组的各路电压是否正常。
c. reset和power down脚
    检查sys_config.fex配置
    vip_dev0_reset                = port:PH2<1><default><default><default>
    vip_dev0_pwdn                = port:PH1<1><default><default><default>
    是否与原理图设计保持一致。必要时,需要用示波器测量reset,pwdn脚,在camera加载时,是否有动作。
d. mclk
    检查sys_config.fex配置
    vip_csi_mck              = port:PE01<3><default><default><default>
    pin脚是否与原理图设计保持一致。必要时,在加载camera时,测量mclk,看是否有正确输出(一般是24MHz或27MHz)

如果已经能够正确通过camera的id读取,只是在使用过程当中,偶尔出现I2C的读写错误,此时需要从打印里面,将报错的地址和读写值,结合camera具体的spec来分析,到底是操作了camera哪些寄存器带来的问题。

还有可能出现如下信息:

[    5.556579] sunxi_i2c_do_xfer()1942 - [i2c1] incomplete xfer (status: 0x20, dev addr: 0x30)
[    5.566234] sunxi_i2c_do_xfer()1942 - [i2c1] incomplete xfer (status: 0x20, dev addr: 0x30)
[    5.575963] sunxi_i2c_do_xfer()1942 - [i2c1] incomplete xfer (status: 0x20, dev addr: 0x30)
[    5.585375] [VIN_DEV_I2C]sc031gs_mipi sensor read retry = 2
[    5.591666] [sensorname_mipi] error, chip found is not an target chip.

出现上述错误打印时,可按以下操作逐步debug。

(1)确认 sys_config.fex 中配置的 sensor I2C 地址是否正确(sensor datasheet 中标注,读地址为 0x6d,写地址为 0x6c,那么 sys_config.fex 配置 sensor I2C 地址为 0x6c);
(2)在完成以上操作之后,在 senor 上电函数中,将掉电操作屏蔽,保持 sensor 一直上电状态,方便 debug;
(3)确认 I2C 地址正确之后,测量 sensor 的各路电源电压是否正确且电压幅值达到 datasheet 标注的电压要求;
(4)测量 MCLK 的电压幅值与频率,是否正常;
(5)测量 senso r的 reset、pown 引脚电平配置是否正确,I2C 引脚 SCK、SDA 是否已经硬件上拉;
(6)确认 I2C 接口使用正确并使能(CCI / TWI);
(7)如果还是 I2C 出错,协调硬件同事使用逻辑分析仪等仪器进行debug;

  • I2C没有硬件上拉
    I2C没有硬件上拉时一般会异常报错:

twi_start()450 - [i2c2] START can't sendout! twi_start()450 - [i2c2] START can't sendout! twi_start()450 - [i2c2] START can't sendout! [VFE_DEV_I2C_ERR]cci_write_a16_d16 error! slave = 0x1e, addr = 0xa03e, value = 0x1

出现上述的问题是因为 SDA、SCK 没有拉上,导致在进行 I2C 通信时,发送开始信号失败,SDA、SCK 添加上拉即可。

  • I2C没有使能
    I2C没有使能时一般会异常报错:

[VFE]Sub device register "ov2775_mipi" i2c_addr = 0x6c start! [VFE_ERR]request i2c adapter failed! [VFE_ERR]vfe sensor register check error at input_num = 0

出现上述的错误,是因为使用 twi 进行 I2C 通信但没有使能 twi 导致的错误,此时需要确认 sys_config.fex 中,[twiX] 中的 twiX_used 是否已经设置为 1。

  • 图像全黑如何处理
    异常现象:

当 camerademo 成功采集到图像时,发现图像是全黑的

首先最起码整条数据通路已经正常,而发现图像时全黑的,注意以下几点:
(1)在编译 camerademo 之前,是根据平台正确的选上了 Enable vin isp support,选上之后,重新编译camerademo(建议 cd package/allwinner/camerademo 目录后执行mm -B编译);
(2)通过上述操作之后,执行新编译的camerademo可执行程序,运行过程应可看到类似[ISP]create isp0 server thread!信息,则正确运行isp,这时再查看新抓取的图像数据;
(3)执行运行camerademo只会抓取5张图像数据,由于isp计算合适的图像曝光需要一定的帧数,所以可能存在前面几张图像黑的情况,修改camerademo运行参数,抓取多几张图像数据查看(20张);
(4)如果是没有移植isp的环境,则可修改sensor驱动中寄存器组中的曝光参数配置,增加初始化时曝光时间,从而使初始输出的图像亮度较合适。

  • camerademo 采集的图像颜色异常
    异常现象:

运行 camerademo 采集图像之后,发现拍摄得到的轮廓正确但颜色不对,比如红蓝互换、画面整体偏红或偏蓝等颜色异常的情况

出现这样的问题,首先考虑是sensor驱动中配置的RAW数据RGB顺序错误导致的。在sensor驱动中有类似以下的配置:

static struct sensor_format_struct sensor_formats[] = {
    {
        .desc = "Raw RGB Bayer",
        .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
        .regs = sensor_fmt_raw,
        .regs_size = ARRAY_SIZE(sensor_fmt_raw),
        .bpp = 1
    },
};

以上配置表明sensor输出的图像数据是RAW10,RGB排列顺序是BGGR,出现颜色异常时,一般就是RGB的排列顺序配置错误导致的,RGB排列顺序一共有4种 (MEDIA_BUS_FMT_SBGGR10_1X10/MEDIA_BUS_FMT_SGBRG10_1X10/MEDIA_BUS_FMT_SGRBG10_1X10/MEDIA_BUS_FMT_SRGGB10_1X10),修改驱动中的mbus_code为上述的4种之一,确认哪一种颜色比较正常,则驱动配置正确。如果颜色还有细微的不够艳丽、准确等问题,需要进行isp效果调试,改善图像色彩。
上述是以10bit sensor为例进行介绍,其他的8bit、12bit、14bit类似,参考上述即可。

  • 画面大体轮廓正常,颜色出现大片绿色和紫红色

一般可能是csi采样到的yuyv顺序出现错位

确认camera输出的yuyv顺序的设置与camera的spec一致

若camera输出的yuyv顺序没有问题,则可能是由于走线问题,导致pclk采样data时发生错位,此时可以调整pclk的采样沿。具体做法如下:

在对应的camara驱动源码,如ov5640.c里面,找到宏定义#define CLK_POL。此宏定义可以有两个值V4L2_MBUS_PCLK_SAMPLE_RISINGV4L2_MBUS_PCLK_SAMPLE_FALLING。若原来是其中一个值,则修改成另外一个值,便可将PCLK的采样沿做反相。

  • 画面大体轮廓正常,但出现不规则的绿色紫色条纹

一般可能是pclk驱动能力不足,导致某个时刻采样data时发生错位。

解决办法:

  • 若pclk走线上有串联电阻,尝试将电阻阻值减小。

  • 增强pclk的驱动能力,需要设置camera的内部寄存器。

  • 画面看起来像油画效果,过渡渐变的地方有一圈一圈

一般是CSI的data线没有接好,或短路,或断路。

  • 常见报错: [VFE_WARN] Nobody is waiting on this video buffer

上层还回来所有的buffer,但是没有再来取buffer。

  • 常见报错 - [VFE_WARN] Only three buffer left for csi

上层占用了大部分buffer,没有还回,驱动部分只有三个buffer此时驱动不再进行buffer切换,直到有buffer还回为止。

sensor的硬件接口注意事项

(1)如果是使用并口的sensor模组,会使用到720p@30fps或更高速度的,必须在mclk/pclk/data/vsync/hsync上面串33ohm电阻,5M的sensor一律串电阻;

(2)使用Mipi模组时候PCB layout需要尽量保证clk/data的差分对等长,过孔数相等,特征阻抗100ohm;

(3)如果使用并口复用pin的模组时候,不建议reset脚的复用;

(4)并口模组的排线长度加上pcb板上走线长度不超过10cm, mipi模组排线长度加上pcb板上走线长度不超过20cm,超过此距离不保证能正常使用。

(5)主控并口数据线有D11~D0共12bit,并口的sensor输出一般为8/10bit,原理图连接需要做高位对齐。

Camera demo

Tina系统可以通过 SDK 中的 camerademo 包来验证 camera sensor(usb camera) 是否移植成功,如果可以正常捕获保存图像数据,则底层驱动、板子硬件正常。

camerademo配置

在命令行中进入Tina根目录,执行make menuconfig进入配置主界面,并按以下配置路径操作:

Allwinner
    └─>camerademo