MIPI OV13855 的整体获取图像流程:从设备树到用户态取帧

张开发
2026/4/13 7:40:18 15 分钟阅读

分享文章

MIPI OV13855 的整体获取图像流程:从设备树到用户态取帧
RK3588系列技术贴第一章 MIPI OV13855 的整体获取图像流程从设备树到用户态取帧文章目录RK3588系列技术贴前言一、易混淆点1.设备树概念2.驱动的概念3.平台设备platform devicevs外设设备4.热插拔vs非热插拔板载固定外设二、OV13855 的整体获取图像流程1.从 DTS 到 OV13855 出现设备对象是怎么来的1.1 DTS - DTB1.2 内核先创建 platform_device2. 以OV13855 为例详细分析 probe 流程核心内容2.1 probe 函数原型2.2 设备使能与通信2.3 确认模块存在2.4 设置驱动软件状态2.5 异步注册 v4l2_subdev3. 在用户态部分图像链路是怎么真正跑起来的总结前言最近在学习 RK 平台下 MIPI 摄像头的接入流程以 OV13855 为例把从设备树描述、内核匹配、驱动 probe到最后用户态通过 /dev/videoX 获取图像的全过程梳理了一遍。这篇文章主要记录当前阶段对整体链路的理解重点放在设备模型、probe 过程和图像获取流程上。需要说明的是文中会涉及U-Boot启动并 加载 DTB、内核启动后解析设备树**、外设 的probe 触发** 等内容但在本章这些启动阶段的细节暂时只做串联不会展开到太底层。我准备放到后续文章里单独分析。贴主目前还是一个初学者如果各位喜欢贴主的文章希望点一个关注和点赞也希望各位大佬对文章出现的错误进行不吝赐教感谢各位的支持和关注。各位观众如果有问题或想和贴主交流发在评论区我会及时回复。PS对于本节内容主要以概念为主强烈建议大家根据源码按照文中思路走一遍印象会更加深刻一、易混淆点首先在进入正题之前需要说几个易混的点也是我在学习过程中容易混淆的内容。1.设备树概念首先我们要知道开发板硬件和Linux内核之间并不是天生就融为一体的而**设备树就是 Linux 内核用来认识板级硬件的一种描述机制。**首先LInux内核本身是通用的他可以跑在不同的开发板上但不同的板子他的硬件连接可能是不同的。那我们需要针对不同的板子去写不同的Linux内核吗这肯定是不可能的所以我们需要一份“说明书”让Linux内核知道这块板子究竟配置了什么模块这个就是设备树。设备树描述的是硬件事实设备书中记录了一块硬件如果想要工作它需要什么。比如寄存器地址是什么需要哪个时钟或电源等一系列问题。同时对于挂载在其他控制器下的设备他也记录了这个设备的相关内容在那条总线地址是多少GPIO在哪等。2.驱动的概念现在内核已经知道这块板子上都有哪些设备了他现在可以直接用这些设备了吗答案是不可以的想要设备起作用还需要驱动具体去执行。驱动会在代码里声明我支持哪些设备***最常见的就是靠 compatible 匹配。***例如驱动会写staticconststructof_device_idov13855_of_match[]{{.compatibleovti,ov13855},{}};他表示如果设备树里有哪个节点写了 compatible “ovti,ov13855”那这个设备我能管。PS内核启动时会先解析设备树根据设备树中的节点创建对应的设备对象驱动加载后也会向内核声明自己支持哪些 compatible。内核将设备树节点中的 compatible 和驱动支持表进行匹配匹配成功后便调用驱动的 probe() 函数。进入 probe() 之后驱动会进一步获取 reset GPIO、时钟、regulator 等资源并执行上电、释放 reset、通过 I2C 读取芯片 ID 等初始化操作以确认设备是否真实存在且工作正常。对于 RK3588 这类平台即使设备树和驱动中都写好了 ov13855如果实际摄像头模组没有插入那么 compatible 仍然可能匹配成功但驱动在 probe 阶段真正访问硬件时会失败最终表现为 probe 失败。3.平台设备platform devicevs外设设备平台设备指的是SoC内部集成的硬件控制器它并非是需要“独立芯片插入”而在芯片内部原本就存在的模块简单理解为 板子上给你连接好的控制器。它们一般是 SoC 自带的硬件模块有固定的 寄存器地址、中断号、时钟、复位资源它们通常在设备树里体现为一个 platform device。例如I2C GPIO SPI UART等。外设设备一般是需要挂载在平台控制器上的设备它需要通过某种接口接入例如我们今天说的ov13855摄像头他们通过这些控制器与内核进行交互。此外判断一个设备是否是外设并不看它是否被焊接在板子上而是看它提供给驱动的资源类型和是否挂载在其他设备上。4.热插拔vs非热插拔板载固定外设同为外设设备这里还要做进一步区分。比如USB就是典型的热插拔外设而今天要说的OV13855就是非热插拔外设。那么它们的区别是什么最重要的区别就是在设备树中该设备的”说明书“是否有被提前写好。所以非热插拔的外设需要设备树提前写好连接关系ov13855需要挂载在哪里引脚占用是哪个哪个驱动可以识别这个compatible等。而热插拔的外设比如鼠标键盘或者PCIe等就不需要。本质上来说设备树没有识别功能他是程序员提前写好的硬件描述用来告诉内核板子上有哪些硬件怎么连接连接需要用到哪些资源。二、OV13855 的整体获取图像流程1.从 DTS 到 OV13855 出现设备对象是怎么来的1.1 DTS - DTB我们在文件中看到的设备树通常如以下代码表示i2c7{statusokay;ov1385536{compatibleovti,ov13855;reg0x36;reset-gpiosgpio3 RK_PC4 GPIO_ACTIVE_HIGH;pwdn-gpiosgpio3 RK_PC6 GPIO_ACTIVE_HIGH;clockscru CLK_MIPI_CAMARAOUT_M3;port{ov13855_out:endpoint{remote-endpointmipi_in_ucam1;data-lanes12;};};};};DTS 编译为 DTB 后启动阶段由 U-Boot 加载到内存再传给 Linux 内核。内核启动后OF 子系统会解析 DTB形成一棵设备树节点结构。(DTB 如何传给内核、内核早期如何展开设备树、各种节点何时被扫描等内容本文暂不深究关于 U-Boot 启动和设备树传递机制我会放到后续更新中单独分析。1.2 内核先创建 platform_device设备树解析后像SPI、I2C、GPIO等平台设备模块会首先被创建为”platform_device“然后由 platform 总线框架去匹配对应的platform_driver配对成功后驱动会注册出一条总线。而对于需要挂载在平台设备上的外设设备以I2C为例需要等待I2C 总线i2c_adapter注册好后内核再扫描总线的子节点根据这条总线下面的子节点去创建 OV13855 对应的 struct i2c_client。它表示为“某条 I2C 总线上地址为 0x36 的那个从设备实例”。接下来内核会拿它去匹配 OV13855 的 i2c_driver匹配成功后调用ov13855_probe(struct i2c_client *client)probe 本质上就是驱动里的一个函数但它并不是普通函数而是驱动正式接管某个设备实例的入口函数所以你可以看到内核匹配设备树和驱动设备树模块说明书驱动实施连接的行动函数。2. 以OV13855 为例详细分析 probe 流程核心内容2.1 probe 函数原型刚才我们说到probe 本质上就是驱动里的一个函数它是驱动正式接管某个设备实例的入口函数。典型的 sensor 驱动会有类似函数staticintov13855_probe(structi2c_client*client)//这里的 client 就是前面 I2C core 创建出来的那个具体设备对象。{...}probe 一开始通常会分配一个私有结构体用来保存这个 sensor 的所有状态structov13855{//1、资源句柄--从设备树中主动读取structi2c_client*client;structv4l2_subdevsd;structmedia_padpad;structclk*xvclk;structgpio_desc*reset_gpio;structgpio_desc*pwdn_gpio;structregulator*avdd;structregulator*dovdd;structregulator*dvdd;//2、驱动状态--驱动自己维护的软件状态structmutexmutex;bool streaming;conststructov13855_mode*cur_mode;};在驱动通过内核给出的资源接口主动读取设备树中的内容保存到自己维护的结构体中。2.2 设备使能与通信拿到资源后要先让硬件进入可通信状态最重要的是让“控制面”先通即让 I2C 通信能通、让驱动能读 chip id、让驱动能后续配置寄存器。此时probe会打开供电和输入时钟设置好GPIO状态最终实现设备可以通过 I2C 和 SoC 通信。此时需要注意一点这里的通信不是实现图像数据的传输仅为让 sensor 进入可通过 I2C 通信和配置的状态。2.3 确认模块存在对于ov13855而言上述内容你没有摄像头也可以实现但从下面开始必须你真的接入摄像头才可以将流程继续下去。简单来说就是I2C总线会发送一个时序信号给sensorsensor在收到信号后会回复储存在他寄存器里面的芯片ID这个ID会一层层返回驱动直到驱动读取并确认真实存在这样的设备ID。没设备就没ID就会连接失败详细过程如果有同学感兴趣可以自己梳理以下基本和上面的内容是对照的在此不去赘述。OV13855 驱动 调 i2c_transfer() 或 i2c_smbus_read…()I2C core 接收到这个请求把请求交给对应的 i2c_adapter也就是交给具体的 I2C 控制器驱动I2C 控制器驱动操作 SoC 的 I2C 硬件寄存器真正把时序发到线上sensor 收到 I2C 命令返回寄存器值数据再一层层返回到 OV13855 驱动2.4 设置驱动软件状态初始化时驱动会通过 I2C 这条控制通路把不同寄存器配置写入到 sensor 芯片内部这些寄存器虽然分别对应芯片内部不同功能块但在 Linux 驱动层面它们通常统一由同一个 sensor 驱动通过 I2C 写寄存器完成配置。配置完成后芯片内部对应的功能模块就会按照这些寄存器值工作。后续会通过用户层再重新配置所需要的参数但是如果用户不配置为了保证图像的正常处理驱动会自动配置默认参数进去。然后摄像头sensor会注册V4L2 controls把 sensor 的可调能力以标准 V4L2 控件的形式暴露出来也就是上面说的用户层面上调节参数内容。2.5 异步注册 v4l2_subdev这一步是 sensor 接入 V4L2 体系的关键。说了这么久其实ov13855芯片型号是整个摄像头的一个子设备它更像是视频管线中的“前端部件”。所以它不会直接变成 /dev/video0而是通常注册成v4l2_subdev。整个摄像头的子模块链路为OV13855 - DPHY - CSI - ISP - 开发板这些独立硬件模块通常都各自有自己的 probe他们同样会按照连接顺序注册好自己的拓扑接口 pad和连接顺序fwnode / endpoint而所有注册好的子模块连接后会成为一条多媒体链路那异步注册是什么意思简单来说虽然子设备的数据传输方向是有顺序的但是他们初始化是可以没顺序的。也就是说虽然数据反方向如上所示但是如果ISP先于DPHY初始化好也完全没问题他会等到所有子模块初始化结束统一组合成多媒体链路。那么如何进行匹配呢主要是通过remote-endpoint来决定匹配逻辑。一旦双方逻辑匹配就可以建立link连接OV13855 的 source pad连到 DPHY/CSI 的 sink pad也就是在 media graph 里画出一条真正的线。最后链接的效果为终端输入 media-ctl-p 终端输出 DTS/Device Tree 里的描述关系 OV13855 节点 └─ port └─ endpoint:ov13855_out └─ remote-endpointdphy_csi_inDPHY/CSI 节点 └─ port └─ endpoint:dphy_csi_in └─ remote-endpointov13855_outCSI/ISP 节点 └─ port └─ endpoint:csi_out └─ remote-endpointisp_in/* 框架细节 async notifier / matching 逻辑 CSI 侧 notifier 我在等 remote-endpoint ov13855_out 对应的 subdev OV13855 注册 subdev 我的 fwnode / endpoint ov13855_out 框架比对 matched! 触发 bound/bind */};3. 在用户态部分图像链路是怎么真正跑起来的具体伪代码流程为open(“/dev/videoX”)VIDIOC_S_FMTVIDIOC_REQBUFSVIDIOC_QBUFVIDIOC_STREAMON不再详细赘述。但要说明的是当用户态打开 /dev/videoX 并完成格式设置、缓冲区申请和 VIDIOC_STREAMON 后内核不会只启动最终 video 节点本身而是会沿着整条 pipeline 向上游逐层启动相关 subdev。在这个过程中内核会调用各模块的流控制操作最终调用到 sensor 的 s_stream(1) 回调。对于 OV13855 而言s_stream(1) 通常意味着驱动通过 I2C 写入 stream-on 相关寄存器使 sensor 真正开始以当前模式通过 MIPI CSI-2 输出图像数据。此后图像数据才会沿着 sensor - DPHY - CSI - ISP - video buffer 的链路流动并最终被用户态通过 DQBUF 获取。总结从 DTS 到 OV13855 设备对象出现并不是“设备树一写好驱动就直接工作”。真实流程是内核先解析 DTB先让 I2C 控制器这类 SoC 内部模块作为 platform_device 被匹配并 probeI2C 控制器驱动再向 I2C core 注册 adapter随后 I2C core 扫描设备树中挂在这条总线下的子节点为 OV13855 创建 i2c_client再与 OV13855 的 i2c_driver 匹配最终调用 ov13855_probe()。probe 中驱动会获取 GPIO、clock、regulator 等资源完成上电复位和芯片 ID 校验并将 sensor 注册为 v4l2_subdev初始化 media_entity 和 pad最后通过异步框架等待与 CSI/ISP 链路完成绑定。最后我会放一份MIPI摄像头实时采集画面通过RTMP推流到虚拟机的源代码供大家参考希望大家多多关注多多点赞谢谢各位观看已上传在我的资源处查看可免费下载

更多文章