• Lv0
    粉丝1

积分31 / 贡献0

提问0答案被采纳0文章1

作者动态

    [经验分享] RK3399 mipi Camera在 OpenHarmony-v3.2-Release系统上 驱动适配实践 精华

    诚迈科技 显示全部楼层 发表于 2023-10-20 16:23:42
                                                    
    RK3399 mipi Camera在 OpenHarmony-v3.2-Release系统上驱动适配实践



    fakename.png

                                                    
    1. 概述

    RK3399 camera驱动适配主要围绕v4l2驱动框架中/dev/videox节点展开,参考下图可以清楚看到需要生成/dev/videox、/dev/v4l-subdevx (x表示0,1,2等)节点。



    fakename.png

                                                    
    图1:v4l2驱动框架图


    当应用层通过/dev/video来操作设备的时候,首先会来到v4l2的核心层,核心层通过注册video_device的回调函数,进而调用相应的操作函数,video_device可以直接操作硬件或者是通过v4l2_subdev来操作硬件。
    本案例中SOC为RK3399,sensor是OV8858,如下(图2:硬件连接图)一方面通过i2c对sensor初始化配置寄存器和摄像头参数的配置。另一方面,通过mipi接口用来传输图像的数据,数据传输路径为从sensor传输到SOC。
    fakename.png


    图2:硬件连接图


    光线经过sensor之后,sensor芯片经过ADC转换生成图像数据,在经过mipi总线进入SOC,进入SOC之后经过isp进行图像处理。由此可以看出,camera驱动有三部分组成,
    第一部分与sensor相关的,如i2c控制sensor的寄存器进行配置;
    第二部分和mipi相关的,需要mipi进行图像传输;
    第三部分是isp部分,SOC里面有isp图像处理模块,经过mipi传输的图像进入SOC之后需要在传入SOC的isp模块对图像进一步进行加工。
    以上是对RK3399 camera的概述,下面在驱动适配中将继续细化具体的操作。

    2.      驱动适配
    2.1.   DTS配置
    在dtsi中添加对应的i2c、sensor、isp、mipi相关配置
    a)  i2c1配置
    1. &i2c1 {
    2.         clock-frequency = <200000>;
    3.         i2c-scl-rising-time-ns = <150>;
    4.         i2c-scl-falling-time-ns = <30>;
    5.         status = "okay";
    6.         ov8858p0: ov8858@36 {
    7.        compatible = "ovti,ov8858";
    8.                 status = "okay";
    9.                 reg = <0x36>;
    10.                 clocks = <&cru SCLK_CIF_OUT>;
    11.                 clock-names = "xvclk";
    12.                
    13.                 reset-gpios = <&gpio2 27 GPIO_ACTIVE_HIGH>;
    14.             pwdn-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>;
    15.                 pinctrl-names = "rockchip,camera_default", "rockchip,camera_sleep";
    16.                 pinctrl-0 = <&cam0_default_pins &cif_clkout_a>;
    17.                 pinctrl-1 = <&cam0_default_pins>;

    18.         rockchip,camera-module-index = <0>;
    19.         rockchip,camera-module-facing = "front";
    20.         rockchip,camera-module-name = "CameraKing";
    21.         rockchip,camera-module-lens-name = "Largan-9569A2";

    22.                 port {
    23.                         ucam_out0a: endpoint {
    24.                                 remote-endpoint = <&mipi_in_ucam0a>;
    25.                                 data-lanes = <1 2 3 4>;
    26.                         };
    27.                 };
    28.         };
    29. }
    复制代码
                                          
    b)sensor 配置
    1. &pinctrl {
    2.         cam_pins {
    3.                 cif_clkout_a: cif-clkout-a {
    4.                                 rockchip,pins = <2 11 RK_FUNC_3 &pcfg_pull_none>;
    5.                 };

    6.                 cif_clkout_a_sleep: cif-clkout-a-sleep {
    7.                                 rockchip,pins = <2 11 RK_FUNC_GPIO &pcfg_pull_none>;
    8.                 };

    9.                 cam0_default_pins: cam0-default-pins {
    10.                                 rockchip,pins =
    11.                                                 <2 28 RK_FUNC_GPIO &pcfg_pull_down>,
    12.                                                 <2 27 RK_FUNC_GPIO &pcfg_pull_none>;
    13.                 };

    14.                 cam1_default_pins: cam1-default-pins {
    15.                                 rockchip,pins =
    16.                                                 <0 12 RK_FUNC_GPIO &pcfg_pull_down>,
    17.                                                 <0  8 RK_FUNC_GPIO &pcfg_pull_none>;
    18.                 };
    19.         };
    20. };
    复制代码
                                                   
    c)  mipi配置
    1. mipi_dphy_rx0: mipi-dphy-rx0 {
    2.         compatible = "rockchip,rk3399-mipi-dphy";
    3.         clocks = <&cru SCLK_mipiDPHY_REF>,
    4.                  <&cru SCLK_DPHY_RX0_CFG>,
    5.                  <&cru PCLK_VIO_GRF>;
    6.         clock-names = "dphy-ref", "dphy-cfg", "grf";
    7.         power-domains = <&power RK3399_PD_VIO>;
    8.         #phy-cells = <0>;
    9.         status = "disabled";
    10. };

    11. &mipi_dphy_rx0 {
    12.         status = "okay";

    13.         ports {
    14.                 #address-cells = <1>;
    15.                 #size-cells = <0>;

    16.                 port@0 {
    17.                         reg = <0>;
    18.                         #address-cells = <1>;
    19.                         #size-cells = <0>;
    20.                         mipi_in_ucam0a: endpoint@0 {
    21.                                 reg = <0>;
    22.                                 remote-endpoint = <&ucam_out0a>;
    23.                                 data-lanes = <1 2 3 4>;
    24.                         };
    25.                         
    26.                         mipi_in_ucam0b: endpoint@1 {
    27.                                 reg = <1>;
    28.                                 remote-endpoint = <&ucam_out0b>;
    29.                                 data-lanes = <1 2>;
    30.                         };
    31.                 };

    32.                 port@1 {
    33.                         reg = <1>;
    34.                         #address-cells = <1>;
    35.                         #size-cells = <0>;

    36.                         dphy_rx0_out: endpoint@0 {
    37.                                 reg = <0>;
    38.                                 remote-endpoint = <&isp0_mipi_in>;
    39.                         };
    40.                 };
    41.         };
    42. };
    复制代码
                                                   
    d)isp配置
    1. &rkisp1_0 {
    2.         status = "okay";

    3.         port {
    4.                 #address-cells = <1>;
    5.                 #size-cells = <0>;

    6.                 isp0_mipi_in: endpoint@0 {
    7.                         reg = <0>;
    8.                         remote-endpoint = <&dphy_rx0_out>;
    9.                 };
    10.         };
    11. };

    12. &isp0_mmu {
    13.         status = "okay";
    14. };
    复制代码
                                                   
    2.2. 模块代码使能
    a)  代码路径
    和硬件相关的驱动有3部分,分别为sensor相关,mipi相关,isp相关,需要找到正确的代码路径,并补齐缺失的代码,其中RK3399 SOC使用isp1类型。
    sensor相关: kernel/drivers/media/i2c/ov8858.c
    mipi相关:kernel/drivers/phy/rockchip/phy-rockchip-mipi-rx.c
    isp相关:kernel/drivers/media/platform/rockchip/isp1/rkisp1.c
    v4l2驱动框架代码路径:
    kernel/drivers/media/v4l2-core、kernel/drivers/media/common/videobuf2
    b)  使能宏配置
    1. CONFIG_VIDEO_DEV=y
    2. CONFIG_VIDEO_OV8858=y
    3. CONFIG_VIDEO_v4l2=y
    4. CONFIG_VIDEO_v4l2_I2C=y
    5. CONFIG_VIDEO_v4l2_SUBDEV_API=y
    6. CONFIG_VIDEOBUF2_v4l2=y
    7. CONFIG_v4l2_FWNODE=y
    8. CONFIG_v4l2_MEM2MEM_DEV=y
    9. CONFIG_MEDIA_CONTROLLER=y
    10. CONFIG_VIDEOMODE_HELPERS=y
    11. CONFIG_NO_GKI=y
    12. CONFIG_VIDEO_ROCKCHIP_RKisp1=y
    13. CONFIG_PHY_ROCKCHIP_mipi_RX=y
    复制代码
                                                   
    2.3.      调试
    在调试之前,需要理清数据传输路径、确定调试目标、整理可用的工具。上面第2步已经配置与使能了各模块的代码,它们在主板上会生成各个设备拓扑节点,并组成数据路径。
    a)  验证DTS配置是否生成
    fakename.png

    fakename.png

    fakename.png

                                                    
    问题1:camera sensor未成功挂接到i2c总线上
    现象:日志打印 ov8858 1-0036: Unexpected sensor id(000000), ret(-5),表明i2c1读取sensor id失败;打印 rk3x-i2c ff110000.i2c: timeout, ipd: 0x00, state: 2,表明i2c1通信超时。
    思路1:1、测量电源电压排查i2c1供电      2、测量系统启动过程中i2c1的波形   3、检查clock配置
    结果:1、供电正确,启动过程中没有波形   2、clock配置正确
    分析:无法直接排查启动过程中i2c1通信超时的原因,继续分析
    思路2:系统启动后,在i2c1下手动挂接1-0036
    结果:挂接成功
    分析:系统启动过程中有其他依赖的因素导致i2c1无法通信,这个依赖因素在启动过程中又被解决了。在日志中排查相关因素,比如电源。发现在ov8858注册失败后,vcc3v3_sys才被申请。显然,ov8858注册依赖i2c1通信, i2c1通信依赖电源vcc3v3_sys。
    解决方式:
    ov8858 驱动加载使用late_initcall接口延迟,使vcc3v3_sys在i2c1通信前被申请。
    b)  v4l2相关的节点是否生成
    通过上面可以知道不管是sensor、mipi还是isp,统一描述为struct v4l2_subdev对象,然后将这个对象struct v4l2_subdev通过不同函数注册到v4l2框架中。如下(图3: 设备注册函数)分别有不同的注册函数。
    fakename.png


    图3:设备注册函数


    注册成功且数据通路正确的话会出现以下节点。
    fakename.png
    fakename.png
    fakename.png


    问题2:没有生成/dev/v4l-subdevx节点
    思路1:根据图3,通过日志排查 v4l-subdevx相关的sensor、mipi、isp设备是否注册成功
    结果:sensor ov8858注册失败,
    思路1:根据图3,数据传输路径的设备节点是sensor->mipi->isp, 通过日志判断这三个设备是否注册成功,且注册顺序是否正确
    结果:通过打印发现sensor注册失败,失败代码在v4l2_async_notifier_try_complete 中,表明列表根节点v4l2_dev为空。
    fakename.png


    分析:sensor应该为v4l2_dev列表的根节点,不应走到这里。再排查三个设备节点的注册顺序,发现为isp->mipi->ov8858。
    思路2:调整设备加载的接口方法,问题1中已经将ov8858的加载修改成late_initcall,需要将mipi 和 isp加载顺序调整得更靠后。
    结果:顺序仍旧不正确
    思路3:通过日志对比,找出isp设备加载的调用入口
    结果:rkisp1_clr_unready_dev是启动isp的入口,调整后顺序正确,sensor、mipi、isp注册成功。且/dev/v4l-subdevx节点生成。
    c)  通过camera_demo操作/dev/video0 获取数据流,验证驱动适配是否成功
    相关camera_demo代码网上也有很多,可以参考:https://blog.csdn.net/ds1130071727/article/details/83544794
    3.      总结
    本文没有介绍的相机上层框架,如HDI接口、Pipeline模型等。本文重点讲述了在RK3399主板上与camera设备驱动相关的如ov8858、isp1、mipi-dpy的底层适配步骤以及总结了在适配过程中出现的重点问题。在OpenAtom OpenHarmony(简称“OpenHarmony”) V3.2Release上主要的适配工作也在此处,此后mipi camera可通过系统相机应用完成预览、拍照等交互。

    4.      参考链接
    图1.png
    图2.png
    图3.png

    ©著作权归作者所有,转载或内容合作请联系作者

    您尚未登录,无法参与评论,登录后可以:
    参与开源共建问题交流
    认同或收藏高质量问答
    获取积分成为开源共建先驱

    精彩评论1

    jiale

    沙发 发表于 2023-11-27 10:49:08

    请问这样就完成适配OHOS了吗?hcs文件需要修改吗?

    Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

    返回顶部