camera 驱动调试

一、摄像头类型

Mipi: 当前主要使用的摄像头接口,速度快,抗干扰强。
Lvds:个别 摄像头  mipi 接口接收不了,所以使用 lvds 接口
Dvp: 相对 mipi 接口,传输的速率有限,一般 5M 或以下摄像头。

二、摄像头dts配置

ov426: ov426@36 {
                status = "okay";
                compatible = "ovti,ov426";
                reg = <0x36>;
                clocks = <&cru CLK_CIF_OUT>;
                clock-names = "xvclk";
                avdd-supply = <&vcc_avdd>;
                dovdd-supply = <&vcc_dovdd>;
                dvdd-supply = <&vcc_dvdd>;
                power-domains = <&power RV1126_PD_VI>;
                pwdn-gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>;
                //pwdn-gpios = <&gpio2 RK_PA6 GPIO_ACTIVE_HIGH>;
                reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;
                //rockchip,grf = <&grf>;
                pinctrl-names = "default";
                pinctrl-0 = <&cifm0_dvp_ctl>;
                rockchip,camera-module-index = <0>;
                rockchip,camera-module-facing = "back";
                rockchip,camera-module-name = "default";
                rockchip,camera-module-lens-name = "default";

                port {
                        cam_para_out1: endpoint {
                                remote-endpoint = <&cif_para_in>;
                                bus-width = <10>;
                                hsync-active = <1>;
                                vsync-active = <0>;
                        };
                };

        };

         调单个摄像头的时候,建议就按照这个来改,这是标准 i2c 设备,定义在 i2c 的节点下面,前面的一大串如果原理图没怎么变的话,都一样就行,当然 i2c 的地址要注意。如果摄像头 lane 数有变,在后面的 port 里面有 data-lane 这个参数,都配置成一 样的。

1.rockchip,camera-module-index:  这个定义多个摄像头时候做个区分
2.rockchip,camera-module-facing : 摄像头前后置标识
3.rockchip,camera-module-name :摄像头模块名
4.rockchip,camera-module-lens-name :摄像头镜头名
主要功能是通过关键字找对应的效果文件。比如当前 dts 找的 xml 文件为default_default.xml ,对于 yuv 摄像头不需要 xml ,可以不管这个配置。
5.Ir-cut 是红外滤光片,我这里没配置,就删掉了
6.Port 是根据摄像头在内部的级联关系 默认的关系是
sensor0->csi_dphy0->csi2->vicap->isp0->ispp0
sensor0->csi_dphy0->csi2->vicap->isp1->ispp1
sensor1->csi_dphy1->isp0->ispp0

  如果是DVP摄像头时

camera 驱动调试_第1张图片

 对应的数据链路

camera 驱动调试_第2张图片

三、驱动

1.摄像头寄存器

static const struct ov426_mode supported_modes[] = {
        {
                /* linear mode0 */
                .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
                .width = 400,
                .height = 400,
                .max_fps = {
                        .numerator = 10000,
                        .denominator = 300000,
                },
                .exp_def = 0x01a0,
                .hts_def = 0x0240,
                .vts_def = 0x01ce,
                .reg_list = ov426_linear10bit_400x400_regs,
                .hdr_mode = NO_HDR,
        },

        {
                /* linear mode1 */
                .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
                .width = 400,
                .height = 400,
                .max_fps = {
                        .numerator = 10000,
                        .denominator = 300000,
                },
                .exp_def = 0x01a0,
                .hts_def = 0x0240,
                .vts_def = 0x01ce,
                .reg_list = ov426_color_400x400_regs,
                .hdr_mode = NO_HDR,
        }

};
        摄像头可以有支持不同的格式,不同的分辨率以及帧率等,可以写在 supported_modes 下面, 400x400  分辨率,帧率是 30 帧,后面有对应的 曝光等描述。
        reg_list 里面是摄像头该 supported_modes 对应的寄存器。注意确定跟上面的参数匹配

2.摄像头 id

#define OV426_CHIP_ID                   0x694F
#define OV426_REG_CHIP_ID               0x300A
寄存器和 id ,如果读到的值不匹配,节点都注册不上。如果匹配成功,打印正常的ID值

2.probe函数

static int probe(struct i2c_client *client)
{
        struct device *dev = &client->dev;
        struct device_node *node = dev->of_node;
        struct ov426 *ov426;
        struct v4l2_subdev *sd;
        char facing[2];
        int ret;
        dev_info(dev, " driver version: %02x.%02x.%02x",
                 DRIVER_VERSION >> 16,
                 (DRIVER_VERSION & 0xff00) >> 8,
                 DRIVER_VERSION & 0x00ff);

        ov426 = devm_kzalloc(dev, sizeof(*ov426), GFP_KERNEL);
        printk("devm_kzalloc ok");
        if (!ov426)
                return -ENOMEM;

        ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
                                   &ov426->module_index);
        

        ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
                                       &ov426->module_facing);
        

        ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
                                       &ov426->module_name);
       

        ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
                                       &ov426->len_name);
        
        ov426->cfg_num = ARRAY_SIZE(supported_modes);

    
        ov426->cur_mode = &supported_modes[0];
        printk("Current mode: %d\n",mode);
        ov426->client = client;

        ov426->xvclk = devm_clk_get(dev, "xvclk");
        
        ov426->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
        
        ov426->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_HIGH);
        
         ret = ov426_configure_regulators(ov426);
        

        mutex_init(&ov426->mutex);

        sd = &ov426->subdev;
        v4l2_i2c_subdev_init(sd, client, &ov426_subdev_ops);
        ret = ov426_initialize_controls(ov426);
        
        ret = __ov426_power_on(ov426);

        
        ret = ov426_check_sensor_id(ov426, client);
       
        printk("check id ok");

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
        sd->internal_ops = &ov426_internal_ops;
        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
                     V4L2_SUBDEV_FL_HAS_EVENTS;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
        ov426->pad.flags = MEDIA_PAD_FL_SOURCE;
        sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
        ret = media_entity_pads_init(&sd->entity, 1, &ov426->pad);
        if (ret < 0)
                goto err_power_off;
#endif
        memset(facing, 0, sizeof(facing));
        if (strcmp(ov426->module_facing, "back") == 0)
                facing[0] = 'b';
        else
                facing[0] = 'f';

        snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
                 ov426->module_index, facing,
OV426_NAME, dev_name(sd->dev));
        printk("sd->name: %s\n", sd->name);

        ret = v4l2_async_register_subdev_sensor_common(sd);
        if (ret) {
                dev_err(dev, "v4l2 async register subdev failed\n");
                goto err_clean_entity;
        }
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
        pm_runtime_idle(dev);

 	return ret;

}

主要是进行设备的初始化设置和配置。

  1. 从设备节点读取属性信息,包括模块索引、模块朝向、模块名称和镜头名称等。
  2. 设置当前的工作模式。
  3. 获取设备的时钟和GPIO引脚,并进行错误检查和处理。
  4. 初始化互斥锁和v4l2子设备,并注册相关的控制项。
  5. 打开OV426传感器,并检查传感器的ID。
  6. 设置v4l2子设备的属性。
  7. 注册v4l2子设备,并启用设备的运行时电源管理功能。

四、调式

驱动编译好之后,烧入就可以尝试抓图了。

media-ctl 工具查看节点链路:media-ctl -p -d /dev/mediaX

camera 驱动调试_第3张图片

1.抓原图(RAW)

通过如下命令可以实现紧凑与非紧凑格式的切换,0表示非紧凑型,1表示紧凑型;

echo 0 > /sys/devices/platform/rkcif_dvp/compact_test
echo 0 > /sys/devices/platform/rkcif_dvp/align_test

抓图命令:

v4l2-ctl -d /dev/video0 --set-fmt-video=width=400,height=400,pixelformat=BG10 --stream-mmap=4 --stream-count=1 --stream-to=/data/BG10.raw --stream-skip=2

具体含义如下:

-d /dev/video0:指定要配置和采集的视频设备为/dev/video0。
--set-fmt-video=width=400,height=400,pixelformat=BG10:设置视频格式为宽度为400,高度为400,像素格式为BG10。
--stream-mmap=4:使用mmap内存映射方式进行视频采集,缓冲区数量为4。
--stream-count=1:采集一帧视频数据。
--stream-to=/data/BG10.raw:将采集到的视频数据保存为文件/data/BG10.raw。
--stream-skip=2:跳过前两个采集缓冲区。

 2.抓YUV图

经过 isp ispp 处理后,我们可以抓 yuv 图,找到 rkispp_m_bypass 对应的节点。
camera 驱动调试_第4张图片

抓图命令

v4l2-ctl -d /dev/video18 --set-fmt-video=width=400,height=400,pixelformat=NV12 --stream-mmap=4 --stream-count=1 --stream-to=/data/NV12.out --stream-skip=2

具体含义如下:

-d /dev/video18:指定要配置和采集的视频设备为/dev/video18。
--set-fmt-video=width=400,height=400,pixelformat=NV12:设置视频格式为宽度为400,高度为400,像素格式为NV12。
--stream-mmap=4:使用mmap内存映射方式进行视频采集,缓冲区数量为4。
--stream-count=1:采集一帧视频数据。
--stream-to=/data/NV12.out:将采集到的视频数据保存为文件/data/NV12.out。
--stream-skip=2:跳过前两个采集缓冲区。

你可能感兴趣的:(linux)