RK3568 MIPI CSI摄像头GC8034适配工作流程

电子说

1.3w人已加入

描述

本文以RK3568外接GC8034为例,首先介绍MIPI CSI摄像头的适配方法,然后介绍cmos sensor驱动的一些细节与cmos sensor驱动的工作流程。

硬件准备

首先介绍一下硬件。主板为风火轮科技的YY3568开发板,主控RK3568。

它支持一个MIPI CSI接口。此接口为4LANE,可以拆分为2个2LANE的。摄像头模组为TSC8034-HYX5,此模组主控为GC8034。此外,模组上面有一颗DW9714,这是一颗VCM,用于控制镜头伸缩。模组连接到一个转接板,然后转接板通过排线连接到YY3568开发板MIPI CSI接口。

 

目前市面上大多数的cmos image sensor一般会包含控制接口(i2cspi等)用于寄存器读写,数据接口(csi/bt656等)用于传原始图像(raw image)数据。

软件准备

本文所有的源码分析基于rk的4.19版本kernel。

Android和Debian用的内核源码是一样的。

V4L2框架简介

V4L2(Video for linux2)为linux中关于video设备的内核驱动。目前RK平台全部使用V4L2框架来操作摄像头设备。V4L2框架的组成大致如下图所示

RK3568

V4L2里面有v4l2-subdev和v4l2_device,以及videobuf2-core三类设备。

v4l2-subdev指的是硬件上面的接口,包括sensor,以及sensor的接口如mipicsi或者bt656等。对于用户层来说,其控制节点为/dev/v4l-subdevX。

v4l2_device指的是那种能够向用户层传递数据的设备,在rk平台上,这个设备可以是ISP(ImageSignal Processing unit),也可以是CIF(Camera Interface)。ISP具备图像处理功能,缩放以及压缩功能。如果需要对图像进行预处理,则需要用到ISP。其控制节点为 /dev/videoX

videobuf2-core用于分配和处理视频帧缓冲区,比如对mmap等操作提供支持。

Kernel部分修改

首先要配置链路。由于从GC8034获取的图像需要进行前处理才能被用户层使用,因此需要使用ISP,将链路设置为GC8034-> MIPI 接口->ISP。

首先配置GC8034。板上摄像头接口的定义如下

RK3568

此处可以看出,其复位脚使用的是GPIO3_B5,电源使能脚用的是GPIO4_B5,然后使用I2C4与GC8034和DW9714通信,另外摄像头的时钟要由主控提供,设备树配置如下。

 

&i2c4 {
  status = "okay";


  dw9714: dw9714@c {
        compatible = "dongwoon,dw9714";
        status = "okay";
        reg = <0x0c>;
        rockchip,camera-module-index = <0>;
        rockchip,vcm-start-current = <10>;
        rockchip,vcm-rated-current = <85>;
        rockchip,vcm-step-mode = <5>;
        rockchip,camera-module-facing = "back";
    };




  gc8034: gc8034@37 {
    compatible = "galaxycore,gc8034";
    reg = <0x37>;
    clocks = <&cru CLK_CIF_OUT>;
    clock-names = "xvclk";
    power-domains = <&power RK3568_PD_VI>;
    //sensor mclk pinctl设置。如果sensor的工作时钟由主控提供,则此处必须配置
    pinctrl-names = "default";
    pinctrl-0 = <&cif_clk>;
    // reset管脚分配及有效电平
    reset-gpios = <&gpio3 RK_PB5 GPIO_ACTIVE_LOW>;
    // powerdown管脚分配及有效电平
    pwdn-gpios = <&gpio4 RK_PB5 GPIO_ACTIVE_LOW>;
    rockchip,grf = <&grf>;
    // 模组编号,该编号不要重复
    rockchip,camera-module-index = <0>;
    // 模组朝向,有"back"和"front"
    rockchip,camera-module-facing = "back";
    rockchip,camera-module-name = "RK-CMK-8M-2-v1";
    rockchip,camera-module-lens-name = "CK8401";
    lens-focus = <&dw9714>;
    port {
      gc8034_out: endpoint {
        // csi2 dphy端的port名
        remote-endpoint = <&mipi_in_ucam0>;
        // csi2 dphy lane数,1lane为 <1>, 4lane为 <1 2 3 4>
        // rk3568支持1*4lane和2*2lane两种模式
        data-lanes = <1 2 3 4>;
      };
    };
  };
};

 

然后需要配置CSI链路。RK3568的CSI有两种工作模式,1*4LANE和2*2LANE。如果是前者,则需要使能csi2_dphy0,同时禁用csi2_dphy1/csi2_dphy2。后者相反。CSI的输入端为GC8034,4LANE连接,输出端为ISP,因此配置如下。

 

&csi2_dphy0 {
  //csi2_dphy0不与csi2_dphy1/csi2_dphy2同时使用,互斥
  status = "okay";
  ports {
    #address-cells = <1>;
    #size-cells = <0>;
    port@0 {
      reg = <0>;
      #address-cells = <1>;
      #size-cells = <0>;


      mipi_in_ucam0: endpoint@2 {
        reg = <2>;
        remote-endpoint = <&gc8034_out>;
        data-lanes = <1 2 3 4>;
      };
    };
    port@1 {
      reg = <1>;
      #address-cells = <1>;
      #size-cells = <0>;


      csidphy_out: endpoint@0 {
        reg = <0>;
        remote-endpoint = <&isp0_in>;
      };
    };
  };
};

 

然后ISP只需要配置输入端,为CSI

 

&rkisp_vir0 {
  status = "okay";
  port {
    #address-cells = <1>;
    #size-cells = <0>;


    isp0_in: endpoint@0 {
      reg = <0>;
      remote-endpoint = <&csidphy_out>;
    };
  };
};

 

配置完这个链路之后,编译内核,然后将内核烧录到板上,即可使用摄像头。

内核日志如下

RK3568

如果看到这个log,说明gc8034和csi2-dphy的链路已通。

GC8034驱动介绍

下面介绍一下GC8034的驱动。在kernel里面,由于有V4L2这个框架的存在,因此多数cmos image sensor的驱动的框架流程都差不多,只是在寄存器操作上存在差别。

首先是驱动注册方面,GC8034支持通过I2C进行寄存器配置,而它的CSI接口只能用于raw image数据的发送,并不能进行控制。因此,它是一个I2C device。驱动的开始便是注册了一个I2C device。如下

RK3568

在设备树上面的设备描述和驱动匹配时,便会执行probe函数。下面看下probe函数。

 

static int gc8034_probe(struct i2c_client *client,
       const struct i2c_device_id *id)
{
  struct device *dev = &client->dev;
  struct device_node *node = dev->of_node;
  struct gc8034 *gc8034;
  struct v4l2_subdev *sd;
  char facing[2];
  int ret;
...


  gc8034 = devm_kzalloc(dev, sizeof(*gc8034), GFP_KERNEL);
  if (!gc8034)
    return -ENOMEM;
  // 这些数据是为了 sensor 的 ioctl 获取模组状态
  // RK平台的一些上层应用(比如android的camera应用) 需要获取这些信息,以便实现前后摄像头切换,3A参数匹配等
  ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
    &gc8034->module_index);
  ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
    &gc8034->module_facing);
  ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
    &gc8034->module_name);
  ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
    &gc8034->len_name);
...
  gc8034->client = client;


  gc8034->xvclk = devm_clk_get(dev, "xvclk");
...
  // 电源 复位等 gpio 获取
...


  ret = gc8034_configure_regulators(gc8034);
...


  // 这个函数是获取与之匹配的接口,也就是CSI的设备树配置,主要是lane数
  ret = gc8034_parse_of(gc8034);
...


  // pinctrl资源获取
...


  mutex_init(&gc8034->mutex);


  // 最重要的函数 v4l2_i2c_subdev_init 此函数注册了一个v4l2 subdev,也就是将此摄像头注册到v4l2框架里面了
  // 其中 gc8034_subdev_ops 里面的就是gc8034响应上层控制的回调函数
  sd = &gc8034->subdev;
  v4l2_i2c_subdev_init(sd, client, &gc8034_subdev_ops);
  ret = gc8034_initialize_controls(gc8034);
...
  // 上电
  ret = __gc8034_power_on(gc8034);
...
  // 识别一下i2c上面是不是有gc8034存在,不存在的话会将上面的操作全部注销掉
  ret = gc8034_check_sensor_id(gc8034, client);
...
  // 读取gc8034 otp寄存器
  gc8034_otp_enable(gc8034);
  gc8034_otp_read(gc8034);
  gc8034_otp_disable(gc8034);


#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
  sd->internal_ops = &gc8034_internal_ops;
  sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
         V4L2_SUBDEV_FL_HAS_EVENTS;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
  gc8034->pad.flags = MEDIA_PAD_FL_SOURCE;
  sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
  ret = media_entity_pads_init(&sd->entity, 1, &gc8034->pad);
  if (ret < 0)
    goto err_power_off;
#endif


...
  // 此v4l2设备采用异步注册
  ret = v4l2_async_register_subdev_sensor_common(sd);
...
  return 0;


...
}

 

整个probe函数很长,这里删掉了那些错误处理和打印的部分,并为剩下的操作提供了注释。它的操作流程就是先从设备树上获取信息,然后申请gpio等资源,注册v4l2设备,然后尝试读取一下gc8034的id,如果gc8034存在,则读取其otp寄存器。在kernel启动的过程中,这些信息就是操作otp寄存器打印出来的

RK3568

一般来说,模组出厂的时候要进行校准,因为各个厂家基于同一个sensor芯片设计的模组存在硬件上面的差异,校准的内容包括AF(自动对焦校准)、AWB(白平衡校准)、LSC(镜头阴影校准),以及模组的出厂年月日厂商名等。前者一般需要被上层获取,以便上层配置校准参数。

然后就是v4l2框架里面几个比较重要的结构体。如下

 

static const struct v4l2_subdev_core_ops gc8034_core_ops = {
  .s_power = gc8034_s_power,
  .ioctl = gc8034_ioctl,
#ifdef CONFIG_COMPAT
  .compat_ioctl32 = gc8034_compat_ioctl32,
#endif
};


static const struct v4l2_subdev_video_ops gc8034_video_ops = {
  .s_stream = gc8034_s_stream,
  .g_frame_interval = gc8034_g_frame_interval,
  .g_mbus_config = gc8034_g_mbus_config,
};


static const struct v4l2_subdev_pad_ops gc8034_pad_ops = {
  .enum_mbus_code = gc8034_enum_mbus_code,
  .enum_frame_size = gc8034_enum_frame_sizes,
  .enum_frame_interval = gc8034_enum_frame_interval,
  .get_fmt = gc8034_get_fmt,
  .set_fmt = gc8034_set_fmt,
};


static const struct v4l2_subdev_ops gc8034_subdev_ops = {
  .core  = &gc8034_core_ops,
  .video  = &gc8034_video_ops,
  .pad  = &gc8034_pad_ops,
};

 

其中,

v4l2_subdev_core_ops主要是通用初始化的实现,gc8034中实现了上下电操作,也就是gc8034_s_power函数,还有一个ioctl操作,也就是gc8034_ioctl和gc8034_compat_ioctl32。

RK3568

这个s_power函数主要借助Linux的电源管理框架(pm)来实现gc8034电源使能脚的拉高拉低,此驱动是实现了dev_pm_ops里面的suspend和resume函数的。

另外还有一个write_array函数,这个函数主要用于初始化大多数的寄存器。大多数sensor的原厂会提供几种分辨率下寄存器的配置表。将这个函数放在这里,意思就是每一次使能gc8034的电源之后都重新初始化所有寄存器。

RK3568

而ioctl函数一般是实现平台私有的一些ioctl命令(通用的v4l2 ioctl命令通过其它的ops实现)。比如这里RKMODULE_GET_MODULE_INFO就是获取设备树配置的朝向和名称等信息,RKMODULE_AWB_CFG就是获取上文所述的otp寄存器里面的值。

v4l2_subdev_video_ops主要是对视频流进行控制,其中s_stream是必须实现的,用于控制视频流的开启和关闭。g_frame_interval是用于获取当前帧率,g_mbus_config则是用于获取lane数的,对于gc8034只能是2lane和4lane。

v4l2_subdev_pad_ops主要是对视频格式进行控制。enum_mbus_codeenum_frame_size

enum_frame_interval三个成员是获取当前sensor支持的格式,分辨率以及帧率。get_fmt set_fmt两个成员则是用于获取和设置当前sensor的格式分辨率和帧率。

对于rk平台来说,要实现一个sensor驱动,实现上面的api就足够了。如果是其它平台,则要看是否需要私有的ioctl。通用的v4l2驱动是不需要私有ioctl的。

总结

本文以RK3568外接GC8034为例,首先介绍MIPI CSI摄像头的适配方法,然后介绍cmos sensor驱动的一些细节与cmos sensor驱动的工作流程。参考GC8034的驱动,可以在通用平台上实现sensor的适配。






审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分