简介:RK ISP11 驱动代码包聚焦 Rockchip Linux 内核图像信号处理(ISP)驱动,面向内核驱动开发者、嵌入式 Linux 工程师及有 V4L2 基础的学习者,用于理解 ISP 驱动中的设备树匹配机制与 platform 设备驱动框架。资源以 RAR 压缩包发布,共 17 个文件,其中 8 个 .h 头文件负责寄存器定义与接口声明,7 个 .c 源文件实现核心驱动逻辑,配合 Makefile 和 Kconfig 可快速纳入内核构建体系,整体仅 96KB,目录结构简洁,适合逐文件阅读。目前已有 1721 人浏览/学习,具备较好的社区参考价值。通过 cif_isp11_pltfrm.c、cif_isp11_v4l2.c 及 img_src 相关代码,可完整追踪从 of_device_id 设备树匹配、平台驱动 probe 到 V4L2 子设备注册的调用链路;cif_isp11_rv1108.c 则展示了具体 SoC 适配细节,对从事 ISP 驱动移植、pipeline 配置与内核模块调试的读者有直接帮助。
1. 先搞清楚rkisp是谁
1.1 从Sensor到一张照片,中间发生了什么
做嵌入式视觉开发的人,迟早都会碰到这个名字:rkisp。尤其是用瑞芯微平台做摄像头方案的时候,RKISP这三个字母基本绕不开。我第一次接触rkisp驱动,是在调试一款基于RV1126的IPC方案,当时sensor的RAW数据怎么都出不来图,对着内核日志和v4l2-ctl看了整整两天,最后发现是驱动里一个media link没enable。从那之后我才开始认真把这份驱动代码从头到尾捋了一遍。
先说清楚rkisp是什么。它就是Rockchip SoC内部那一块ISP(Image Signal Processor)硬件对应的Linux驱动代码,在标准内核里的路径一般是drivers/media/platform/rockchip/isp1/。ISP承担的工作,是把相机Sensor输出的Bayer格式原始数据,经过坏点校正、黑电平扣除、去马赛克、3A统计、降噪、锐化、色彩矫正等一系列处理,变成人眼看着正常、编码器也能直接消费的YUV或RGB图像。
没有这份驱动,Sensor就算正常出数据,你拿到的也只是一堆偏色、带噪点、完全没法看的RAW。而有了rkisp驱动,用户空间就可以通过标准的V4L2接口去操作它,配合rkaiq(瑞芯微的3A算法库)或者libcamera,完成从sensor到出图的完整pipeline。所以它解决的,本质上是“硬件能力怎么被软件程序正常使用”的问题。
1.2 驱动代码在kernel里的位置,以及它和CIF的关系
很多人第一次看rkisp代码时会蒙,因为瑞芯微的camera路径里不只有一个驱动,还有一个叫rkcif的东西(MIPI CSI Controller driver)。这两个经常同时出现,职责却完全不同:CIF负责从MIPI接口把sensor数据接到SoC内部,相当于“运输队”;ISP负责对数据做图像信号处理,相当于“加工厂”。rkisp驱动工作时,会通过media controller框架和sensor subdev、CIF的subdev连成一条链路,数据从sensor出来,经过CIF搬运,再进ISP处理,最后写到DDR里。
在drivers/media/platform/rockchip/isp1/目录下,主要文件大概这么几个:rkisp_dev.c管设备注册、中断、media device的初始化;rkisp_isp.c管ISP子设备的v4l2 subdev操作;rkisp_stream.c管视频流节点的VB2队列;rkisp_stats.c和rkisp_params.c分别管3A统计数据和参数下发。这几个文件的职责边界还算清晰,但代码之间耦合度不低,新手容易绕晕。
我在实际调试中感觉,这份驱动最大的特点是把“控制面”和“数据面”分得很清楚:控制面是V4L2 subdev,负责格式协商、链路管理;数据面是video node,负责buffer流。理解了这个,后面看代码就会顺很多。
2. 驱动代码架构拆解:几十个文件到底在干什么
2.1 V4L2不是“协议”,是“框架”
rkisp驱动是基于Linux内核的**V4L2(Video for Linux 2)**框架实现的,而且用的是media controller(MC)那一套,不是老的纯video device那套。这两者的区别,我打个比方:老V4L2就像一台独立DVD机,插上电源就能用;MC框架则像一套家庭影院,播放器、功放、音箱各自是一个独立设备,用HDMI线(media link)连起来,播放前得先确认整套链路都是通的。
对应到rkisp驱动里,Sensor是一个v4l2_subdev,ISP本身也是一个v4l2_subdev,而真正给用户空间出数据的节点则是video_device。这些subdev和video_device通过media graph组织成一个拓扑,用户空间要用media-ctl配置拓扑、用v4l2-ctl操作视频流。你可以理解为:media-ctl负责“接线”,v4l2-ctl负责“按下播放键”。
rkisp驱动之所以要这样做,是因为ISP处理链路涉及多个可配置模块,pipeline不是简单的一条直线;MC框架可以把内部节点暴露出来,方便调试和扩展。代价就是,很多不熟悉MC的人第一次看rkisp驱动时会觉得“入口在哪里都找不到”。
2.2 四个核心数据结构,理清驱动主线
rkisp驱动的核心数据结构不算多,但每个都很关键。我按理解程度排个序:
rkisp_device是全局主结构,一个ISP实例对应一个,贯穿probe到remove的全过程。它包含寄存器映射、中断号、media_device、v4l2_device以及各子设备指针。
rkisp_isp_subdev是ISP subdev的封装,里面包含sink pad和source pad,负责格式set/get、pipeline激活等控制面逻辑。
rkisp_stream是video节点这一侧的封装,每个stream对应一个VB2 queue,里面记录了当前格式、内存分布方式、帧缓冲状态等信息。驱动里通常有主路径(MainPath)、自路径(SelfPath)和RAW路径(RawPath)三类stream,分别对应不同输出场景。
rkisp_stats_vdev和rkisp_params_vdev是另外两个特殊节点:stats负责把ISP内部3A统计信息上报给用户空间算法库;params负责把算法算出来的ISP参数写回硬件寄存器。这两个节点是rkaiq能够工作的物理基础。
提示:不同内核版本里这些结构体的命名可能略有差异,但逻辑基本一致。看代码时抓住“一个主设备+一个ISP subdev+若干stream+stats/params节点”这条主线,就不会迷路。
2.3 用户空间看到的video节点到底有几个
rkisp驱动在成功注册后,用户空间会看到不止一个/dev/videoX节点。最常见的是:
- MP(Main Path)节点:输出ISP处理后的主路图像,通常分辨率最大,给编码器或抓拍用;
- SP(Self Path)节点:输出缩放后的图像,常用于预览或小分辨率视频;
- RAW节点:直接输出ISP接收到的RAW数据,主要用于调试和DNG保存;
- stats节点:输出3A统计buffer,一般给rkaiq读取;
- params节点:输入ISP参数,一般由rkaiq写入。
不同平台、不同内核版本,节点的数量和注册顺序会变,但不能只看video节点号就猜功能,正确做法是用v4l2-ctl --list-devices查看每个节点的名字和对应的media设备。
我第一次调试时,想当然以为/dev/video0一定是MP节点,结果抓了一整天数据都不对。后来才发现video编号顺序和平台注册顺序有关,和功能没有固定对应关系。这是个很小的坑,但确实能浪费半小时。
3. 关键流程一:驱动注册与media拓扑构造
3.1 probe阶段到底做了什么
rkisp驱动的入口是platform_driver的probe函数,也就是rkisp_plat_probe。整个注册流程可以拆成几步看:
第一步,从设备树获取硬件资源。主要是寄存器地址(platform_get_resource)和中断号(platform_get_irq),然后用devm_ioremap_resource映射寄存器,用devm_request_irq注册中断处理函数。
第二步,初始化media设备。调用media_device_init和v4l2_device_register,把media device和v4l2 device关联起来。这一步做完后,用户空间才能通过/dev/mediaX看到这个设备。
第三步,创建内部子设备。rkisp驱动内部会创建若干v4l2_subdev,包括ISP主subdev、stats subdev、params subdev等。每个subdev都有自己的pad(sink/source),并通过media_entity_pads_init初始化。之后再把它们用media_create_pad_link连起来,形成ISP内部的媒体拓扑。
第四步,注册video节点。调用video_register_device注册MP/SP/RAW等video设备,并把这些设备也挂到media graph里。
最后,注册v4l2异步subdev机制,等待外部sensor subdev出现。这一步很关键:rkisp驱动不是主动去找sensor,而是通过异步notifier监听sensor注册事件,一旦sensor出现并完成bound,整条链路才算是齐了。
3.2 media-ctl打印出来的拓扑怎么读
当你怀疑rkisp驱动是否正常工作时,最简单粗暴的方法就是用media-ctl把拓扑打出来看。命令是:
media-ctl -d /dev/media0 -p输出里会有一堆节点名,比如rockchip-mipi-dphy、rockchip-csi2-dphy、rkisp-isp-subdev、rkisp-main-path之类的。这堆名字看着乱,但其实就两个要点:看链路上有没有缺失的节点,看sensor和ISP之间有没有建立link。
正常拓扑里,从sensor的source pad出发,经过MIPI DPHY、CSI2,再到rkisp-isp-subdev的sink pad,再从rkisp-isp-subdev的source pad连到main-path或self-path的sink pad,这才是通的一条路。如果中间的link显示[ENABLED],说明链路已经激活;如果是[DISABLED],就需要用media-ctl去设置:
media-ctl -d /dev/media0 -l "'sensor-name 0-0036':0 -> 'rockchip-mipi-dphy':0 [1]"这里的[1]表示enable link。这个操作对很多新手来说像天书,但耐心对照拓扑图的node名、sink/source pad号,就能拼出来。我第一次拼这条命令也试了好几次,核心就是搞清楚每个pad的编号和方向。
3.3 等一个pipeline被激活
所有子设备注册完成、sensor bound之后,并不意味着硬件就开始干活了。rkisp驱动里还有一道关键门槛:media_pipeline_start。这个函数的作用,是把整条pipeline上的所有实体激活,让每个subdev进入工作状态。
同时,sensor侧也要进入streaming状态。也就是说,只有用户空间调用完stream on,并且所有底层subdev的s_stream(1)被依次调用之后,ISP硬件才会真的开始接收数据、处理数据、搬运数据。
很多出图失败的问题,根源就在这里——用户空间觉得API调用成功了,其实底层某个subdev根本没有进入streaming,导致数据流断了。这种情况查起来非常隐蔽,因为你用v4l2-ctl打开节点没问题,VIDIOC_STREAMON也返回成功,但就是没有数据。我排查过几次后养成了习惯:出图不对劲,第一件事先看dmesg里有没有subdev的s_stream报错。
4. 关键流程二:从STREAMON到第一帧图像
4.1 用户空间到底是怎么发起一帧的
在应用层,出图这件事看起来很简单:打开video节点、设置格式、申请buffer、入队、streamon,然后等buffer回来。
但实际上在rkisp驱动这边,VIDIOC_STREAMON触发后会连带做一串事情:先通过media graph找到整条pipeline上的子设备们,然后调用media_pipeline_start,接着往下游sensor的s_stream(1)方向逐级打通,最后才把ISP内部的DMA通道使能。
rkisp_stream.c里有一个rkisp_stream_start函数,它的职责是把当前stream对应的DMA地址配置到ISP寄存器、使能对应的输出路径、打开中断,然后等待数据。这里有个容易踩的坑:MP和SP的格式、分辨率必须和ISP subdev上协商好的format一致,否则驱动会直接报错config size mismatch之类的日志。所以格式协商不只是用户空间随便设一个格式那么简单,sensor端、ISP端、video节点三者的格式必须串起来匹配。
4.2 ISP中断处理的“现场”
ISP数据通路正常跑起来后,rkisp的IRQ handler会忙起来。每当一帧图像处理完成,ISP硬件会触发中断,驱动在中断里要做几件事:
- 读中断状态寄存器,清中断标志;
- 找到当前完成的是哪个stream(MP还是SP还是RAW);
- 把对应的VB2 buffer标记为done,从硬件队列里摘下来;
- 如果开了stats采集,还要把采集到的3A统计buffer上报到stats节点;
- 如果有params节点更新参数,驱动会在下一帧应用这些参数到ISP寄存器。
简单说,中断就是rdy、stat、param三类事件的交叉处理。rkisp_dev.c里的中断处理函数会把不同类型的事件分发到各自的处理流程。出图问题中很多是中断没触发或者中断号不对,这种情况直接看interrupt-names和设备树里是否匹配就能定位。
4.3 几类常见的启动失败现场
实际操作中,STREAMON之后不是每次都顺顺利利出图。我遇到的常见问题大概这几类:
- 打开节点返回
Device or resource busy:一般是pipeline已经被别的进程占用,MP/SP/RAW共享一套ISP硬件,不能同时被两个进程以冲突方式打开。 - streamon成功但dmesg报
buffer underrun或timeout:多半是ISP寄存器没配好,或者sensor根本没有输出数据。先确认sensor有没有正确配置,再看MIPI通道有没有数据。 - stats有数据但MP没数据:检查MP路径上的format是否设置正确,以及media link是否enable了MP这条路径。
- start失败提示
Invalid argument:大概率是buffer类型、像素格式、分辨率三者中有一个与驱动当前支持的配置不匹配。我一般先用v4l2-ctl --list-formats-ext确认当前分辨率是否在支持范围内。
5. 一些调试和移植的实操心得
5.1 我调试rkisp驱动时常用的三招
第一招,用好v4l2-ctl --list-devices和media-ctl -p这两个命令。每次拿到一个新的板子,第一件事不是急着跑demo,而是先把设备的media拓扑打印出来存放备份,确认节点数量和预期一致。等后面出问题,至少可以排除“节点注册阶段就失败”的可能性。
第二招,看dmesg要有耐心但也要有重点。rkisp驱动注册阶段、sensor bound阶段、streamon阶段都会打印大量日志,但很多平台的内核日志默认级别可能把INFO级别隐藏了。调试时建议临时加上dyndbg或者printk级别调整,把rkisp相关模块的日志打开,这样能看到rkisp_stream_start、s_stream等函数的实际调用情况。
第三招,抓raw图确认sensor原始数据是否已经进来。这部分很多人会忽略。先不看ISP输出效果,直接把RAW节点打开收一帧裸数据,用工具打开看颜色和亮暗分布。如果RAW图都正常,那问题基本在ISP配置;如果RAW图是花的或全黑,那要去查sensor上电、时钟、MIPI lane这些底层信号。这个判断思路能帮你把问题域缩小一大半。
5.2 移植到新sensor时要重点核对哪些内容
换一颗新sensor,或者在新板卡上bring up rkisp,最容易翻车的几个点,我每次都会对照检查:
一是设备树里的sensor节点,I2C地址、regulator供电、reset GPIO、MCLK频率都要和sensor手册一致。这里面MCLK尤其容易错,不同sensor标称的主时钟范围不一样,设错直接导致sensor输出异常或者干脆不工作。
二是CSI2 DPHY的配置。lane数、virtual channel、数据率能不能匹配上sensor的实际输出。rkisp驱动里对CSI2接收的参数校验比较严格,配置不匹配会直接在streamon阶段报错。
三是sensor driver的v4l2 subdev ops是否完整,特别是set_fmt和set_selection。很多时候sensor烧录了但还不支持特定的裁剪/缩放操作,导致ISP端的输入format和sensor输出format对不上,链路建不起来。
四是ISP端tuning和3A库的匹配。rkisp驱动的params节点和stats节点,最终是给rkaiq用的。如果sensor换了,但rkaiq里的sensor对应的iq tuning文件没换,画质会非常糟糕——这时候驱动没报错,但效果没法看。我第一次踩这坑时还以为ISP寄存器配置有bug,查来查去发现是tuning文件匹配错了。
rkisp这套驱动代码,如果你只看API和数据结构会很枯燥,但它实际上是整个瑞芯微视觉方案里承上启下的那个“腰”:没有它,sensor算法再强也白搭。花时间把probe流程、media拓扑、streamon路径、中断处理这几条线捋清楚,以后排查任何camera问题都会顺手很多。
本文还有配套的精品资源,点击获取