简介:本资源为瑞芯微RK平台ISP驱动的源码包,面向从事Linux内核驱动开发、嵌入式视觉与摄像头调试的工程师及学习者,可用于理解RK ISP在V4L2框架下的设备注册、平台驱动匹配与图像源子设备实现。包内共17个文件,以7个C源文件与8个头文件为核心,另含1个Makefile和1个Kconfig,整体约96KB,涵盖平台驱动、V4L2接口、图像源操作及寄存器定义等模块,便于按目录结构梳理驱动分层。资源从设备树匹配的of_device_id入手,展示了cif_isp11系列驱动在media/platform/rk-cif路径下的组织方式,可帮助读者掌握ISP驱动与V4L2子设备的对接思路、寄存器配置及平台适配方法。目前已有1750人学习下载,适合作为RK ISP驱动移植与二次开发的参考素材。
1. rkisp 驱动代码到底在管什么:从一颗摄像头的上电到出图
你插上一颗 MIPI 摄像头到 RK3588 板子上,dmesg里刷出一串rkisp相关日志,然后/dev/video0出现了。这中间 rkisp 驱动代码干了什么?很多人以为它就是个“摄像头驱动”,其实它管的是从 sensor 输出 raw 数据到 ISP 硬件出 YUV/RAW 图像的整条管线:包括 sensor 的 I2C 配置、MIPI CSI-2 接收、ISP 硬件寄存器配置、3A(AE/AWB/AF)统计数据的回读、以及最终通过 V4L2 框架把帧交给用户态。如果你正在调 RK 平台的摄像头,或者想改 ISP 效果、加自定义 3A 算法,绕不开这套代码。它适合做嵌入式视觉、车载、安防、工业相机方向的工程师,也适合想从 V4L2 层面理解 ISP 管线的人。下面按“代码在哪 → 怎么跑通 → 怎么改 → 坑在哪”的顺序拆。
2. 先找到代码:rkisp 驱动在 kernel 里的目录结构与编译入口
2.1 驱动代码在哪个目录:从 drivers/media 往下摸
RK 平台的 ISP 驱动代码主要落在内核源码的drivers/media/platform/rockchip/下面。不同 SDK 版本目录名会有差异,常见的有rkisp1、rkisp、isp这几种命名。你拿到一份 RK 的 Linux SDK 后,先确认内核版本,再按下面路径找:
# 进入内核源码根目录后执行 find drivers/media/platform/rockchip -maxdepth 2 -type d | sort # 典型输出(不同 SDK 会有出入): # drivers/media/platform/rockchip/isp # drivers/media/platform/rockchip/isp1 # drivers/media/platform/rockchip/cif # drivers/media/platform/rockchip/csiisp目录下通常能看到这些文件:dev.c(设备与 probe)、capture.c(视频节点与 vb2 队列)、isp_params.c(ISP 参数配置)、isp_stats.c(3A 统计数据)、hw.c(寄存器读写)、regs.c(寄存器定义)。cif是 CIF 接口驱动,csi是 MIPI CSI-2 接收端。三者配合才能让一颗 MIPI sensor 出图。
编译入口看Makefile和Kconfig:
cat drivers/media/platform/rockchip/isp/Makefile # 常见内容形如: # obj-$(CONFIG_VIDEO_ROCKCHIP_ISP) += video_rkisp.o # video_rkisp-objs := dev.o capture.o isp_params.o isp_stats.o hw.oKconfig里会定义CONFIG_VIDEO_ROCKCHIP_ISP这类开关。你要确保内核配置里打开了它,同时打开对应的 sensor 驱动和CONFIG_VIDEO_V4L2_SUBDEV_API。
提示:不同 RK SDK 的目录名和文件名差异较大,不要死记路径,用
grep -rn "rkisp" drivers/media/platform/rockchip/先定位。
2.2 编译进内核还是编成模块:两种方式的取舍
常见做法有两种:直接编进内核(=y)或编成模块(=m)。调试阶段我一般编成模块,方便insmod/rmmod反复验证,不用每次重启。
# 方式一:编成模块,在 SDK 根目录执行 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig # 路径:Device Drivers -> Multimedia support -> Rockchip ISP # 选 M,保存退出 # 编译内核模块 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules -j$(nproc) # 找到生成的 ko find drivers/media/platform/rockchip -name "*.ko"编成模块后,板子上加载顺序很关键:先加载 CSI/CIF 相关模块,再加载 ISP 模块,最后加载 sensor 驱动。顺序错了会出现 subdev 找不到、media拓扑不完整的问题。
# 板子上按顺序加载(模块名以实际生成为准) insmod csi2_dphy.ko insmod video_rkisp.ko insmod ov13850.ko # 确认 media 设备 ls /dev/media*参数说明:ARCH和CROSS_COMPILE必须和你的 SDK 一致,否则编出来的 ko 版本不匹配,insmod会报invalid module format。-j$(nproc)是并行编译,加快速度。加载后如果/dev/media0没出现,先看dmesg | tail -50,通常是依赖模块没加载或设备树节点没使能。
3. 跑通最小链路:设备树、media 拓扑与第一帧抓取
3.1 设备树里 rkisp 节点怎么配:端口、时钟、中断
rkisp 驱动 probe 依赖设备树节点。典型节点包含compatible、reg、interrupts、clocks、ports。ports里通过endpoint和 sensor、CSI 连接,形成 media 拓扑。
// 典型 rkisp 节点结构(简化,字段以实际 SoC 手册为准) rkisp: rkisp@ffa00000 { compatible = "rockchip,rkisp"; reg = <0x0 0xffa00000 0x0 0x10000>; interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru ACLK_ISP>, <&cru HCLK_ISP>; clock-names = "aclk", "hclk"; power-domains = <&power RK3588_PD_ISP>; ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; isp_in: endpoint { remote-endpoint = <&csi_out>; }; }; }; };关键点:remote-endpoint必须和上游 CSI 的 endpoint 对得上,否则 media 拓扑里两个 entity 连不起来,media-ctl -p会看到断链。clocks和power-domains配错会导致 probe 时寄存器读写失败,日志里出现failed to get clock或power domain相关报错。
3.2 用 media-ctl 和 v4l2-ctl 验证链路并抓一帧
驱动加载成功后,用media-ctl看拓扑,用v4l2-ctl抓帧。这是验证 rkisp 驱动是否正常工作的最快方式。
# 查看 media 拓扑 media-ctl -d /dev/media0 -p # 典型输出里能看到 sensor -> csi -> rkisp 的 entity 和 pad 连接 # 设置 sensor 输出格式(以 1920x1080 RAW10 为例) media-ctl -d /dev/media0 \ --set-v4l2 '"m00_b_ov13850 1-0010":0[fmt:SBGGR10_1X10/1920x1080]' # 设置 rkisp 输入 pad 格式 media-ctl -d /dev/media0 \ --set-v4l2 '"rkisp-isp-subdev":0[fmt:SBGGR10_1X10/1920x1080]' # 抓 10 帧保存 v4l2-ctl -d /dev/video0 \ --set-fmt-video=width=1920,height=1080,pixelformat=NV12 \ --stream-mmap --stream-count=10 --stream-to=frame.raw # 看文件大小是否合理:1920*1080*1.5*10 约 31MB ls -l frame.raw逻辑说明:media-ctl --set-v4l2是在配置 subdev 的 pad format,必须从 sensor 端往 ISP 端逐级设置,顺序反了会被驱动拒绝。v4l2-ctl --stream-mmap走的是 vb2 的 mmap 队列,--stream-count=10抓 10 帧后自动停。--stream-to把帧数据写到文件。
参数说明:pixelformat=NV12是 ISP 输出格式,如果 ISP 只做 raw 直通,这里要改成RG10之类。width/height必须和 sensor 输出一致,否则 ISP 会做缩放或直接报错。抓到的frame.raw可以用ffmpeg或python转成图片验证内容。
注意:如果
v4l2-ctl卡住不返回,先确认 sensor 的link_freq和>// 简化示意:isp_params 节点的 write 流程 static ssize_t rkisp_isp_params_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct rkisp_device *dev = video_drvdata(file); struct rkisp_isp_params_cfg *cfg; int ret; cfg = memdup_user(buf, count); // 从用户态拷贝参数 if (IS_ERR(cfg)) return PTR_ERR(cfg); ret = rkisp_set_isp_params(dev, cfg); // 写 ISP 寄存器 kfree(cfg); return ret ? ret : count; }逻辑说明:用户态把一帧的 ISP 参数(如 BLC、LSC、CCM、GAMMA)打包成结构体,通过
write()写到isp_params节点,驱动解析后写硬件寄存器。isp_stats节点则相反,驱动在每帧中断里把 ISP 硬件产生的统计 buffer 通过read()或 mmap 交给用户态。参数说明:
memdup_user是内核安全拷贝,不能用copy_from_user直接写栈上大结构体。count必须和驱动期望的结构体大小一致,否则返回-EINVAL。实际调试时,先确认rkaiq版本和驱动里定义的结构体版本匹配,版本错位是 3A 不生效的头号原因。4.2 加一个自定义 ISP 参数:从结构体到寄存器
假设你要在现有驱动里加一个自定义的 gamma 参数下发。步骤是:在
isp_params结构体里加字段 → 在rkisp_set_isp_params里解析 → 在hw.c里写对应寄存器。// 1. 在参数结构体里加字段(示意) struct rkisp_isp_params_cfg { u32 module_en_update; u32 module_ens; struct rkisp_isp_gamma_cfg gamma; /* 新增自定义字段 */ u32 custom_gamma_en; u32 custom_gamma_table[17]; }; // 2. 在参数下发函数里处理 static int rkisp_set_isp_params(struct rkisp_device *dev, struct rkisp_isp_params_cfg *cfg) { if (cfg->custom_gamma_en) { rkisp_hw_write(dev, ISP_GAMMA_BASE, cfg->custom_gamma_table[0]); /* 依次写其余表项 */ } return 0; }逻辑说明:ISP 参数下发本质是“用户态结构体 → 驱动解析 → 寄存器写入”三步。加字段时要注意结构体对齐,内核和用户态必须用同一份头文件,否则字段偏移错位,写进去的是垃圾值。
参数说明:
module_en_update是模块使能更新位,module_ens是使能值,这两个字段控制哪些 ISP 模块本帧生效。custom_gamma_table长度要和硬件 gamma 表深度一致,写多了越界,写少了硬件用默认值补齐。提示:改结构体后,用户态
rkaiq也要同步改,否则write()的count对不上,驱动直接返回错误。这是血泪经验,别问怎么知道的。5. 避坑与排查:rkisp 驱动调试中最容易翻车的 5 个点
5.1 现象:probe 成功但 /dev/video0 抓不到帧
原因:media 拓扑里 sensor→CSI→ISP 的 link 没使能,或者 sensor 没出流。驱动 probe 只说明设备注册了,不代表数据链路通了。
解决:
media-ctl -p看 link 状态,media-ctl --link '"sensor":0->"csi":0[1]'手动使能。再用v4l2-ctl --list-formats确认节点支持的格式。如果 sensor 没出流,检查 sensor 驱动的s_stream是否被调用。5.2 现象:抓到的帧全绿或全黑
原因:ISP 参数没下发,或者 3A 没跑。raw 数据直接出图就是绿的,因为 Bayer 格式没经过 demosaic。
解决:确认
isp_params节点有数据写入,rkaiq进程在跑。如果只做 raw 直通,把输出格式改成RG10而不是NV12。5.3 现象:insmod 报 invalid module format
原因:编译内核模块用的内核版本和板子上跑的内核版本不一致,或者
CROSS_COMPILE用错。解决:
uname -r看板子内核版本,modinfo xxx.ko看模块的 vermagic,两者必须一致。重新用 SDK 里对应的内核源码编译。5.4 现象:3A 统计 buffer 一直为空
原因:
isp_stats节点的 buffer 没排队,或者中断里没把统计 buffer 提交。常见是用户态没及时DQBUF,导致驱动侧 buffer 耗尽。解决:确认用户态 3A 库在持续
DQBUF/QBUF。驱动侧看isp_stats.c里中断处理是否调用了vb2_buffer_done。buffer 数量在reqbufs时设置,太少会丢帧。5.5 现象:MIPI 报错,dmesg 刷 csi2 错误
原因:
># 挂载 debugfs mount -t debugfs none /sys/kernel/debug # 打开 rkisp 驱动的动态调试 echo 'file drivers/media/platform/rockchip/isp/* +p' > /sys/kernel/debug/dynamic_debug/control # 然后抓帧,dmesg 里会打出寄存器读写和参数下发细节 v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=1 dmesg | tail -100如果你想追踪某一帧的 ISP 参数,可以在
rkisp_set_isp_params里加:dev_dbg(dev->dev, "params: en_update=0x%x ens=0x%x gamma_en=%d\n", cfg->module_en_update, cfg->module_ens, cfg->custom_gamma_en);这样每帧参数下发都能在 dmesg 里看到,配合
isp_stats的回读值,能判断 3A 是否收敛。常见做法是抓 30 帧,看 AE 的曝光值是否稳定,AWB 的 gain 是否收敛到合理范围。几个验证要点用表格列一下:
验证项 命令/方法 正常表现 media 拓扑 media-ctl -psensor→csi→isp 链路完整 格式支持 v4l2-ctl --list-formats有 NV12/RG10 等 抓帧 v4l2-ctl --stream-mmap文件大小符合分辨率 参数下发 dynamic debug 每帧有 params 日志 3A 统计 isp_statsread有非零统计值 寄存器 dev_dbg+io命令写入值可回读一致 最后说个我自己的习惯:每次改 rkisp 驱动,先
git diff看改了哪些文件,再单独编译模块,insmod后立刻dmesg | tail,确认没有新报错再抓帧。不要一次改多个地方,否则出问题根本不知道是哪处引起的。这套流程帮我省了很多后悔药。希望帮到你。本文还有配套的精品资源,点击获取