
简介本资源是一份面向Linux内核驱动开发者的RK3566平台MIPI Camera驱动实战代码包聚焦嵌入式视觉设备底层适配适用于具备C语言基础与Linux内核模块开发经验的工程师及进阶学习者。资源完整呈现了基于RK3566 SoC的MIPI-CSI接口相机含S5K33D、GC2053、SC2310等多款传感器的内核驱动开发全过程涵盖驱动注册、设备树适配、图像流控制、AE参数调试及RGBD数据采集等关键环节。压缩包共25个文件其中18个C源文件构成核心驱动逻辑如s5k33d_48.c、gc2053.c等7个TXT文档提供配置说明、测试记录与交叉验证日志整体仅220KB轻量精炼便于快速导入工程分析。目前已有918人学习下载读者可直接复用驱动框架、参考多传感器适配思路、掌握rk3566-mipi-camera调试方法论并通过修改测试分支如30fps/no_ae、60fps/ae等深入理解性能调优路径。1. RK3566 上跑通 MIPI Camera 不是“插上线就能用”内核驱动没写对V4L2 应用层永远收不到一帧数据你手上有块 RK3566 开发板接好了 OV5640 或 IMX219 这类 MIPI CSI-2 接口的模组dmesg里能看到 sensor 被识别、I2C 地址也扫出来了但v4l2-ctl --list-devices死活不显示/dev/video0或者gst-launch-1.0 v4l2src device/dev/video0 ! autovideosink启动后黑屏、卡住、报VIDIOC_STREAMON: Invalid argument——这不是摄像头坏了也不是线材问题而是内核驱动链路在 RK3566 平台上根本没真正打通。MIPI Camera 在 RK3566 上不是即插即用的外设它依赖一套完整的内核子系统协同从 sensor 驱动注册、CSI host 控制器初始化、video device 注册到 media controller topology 构建缺一不可。本文讲的就是如何用 C 语言在 Linux 内核源码里亲手补全这条链路——不是调几个设备树节点就完事而是理解rkisp1、csi_host、v4l2_async_notifier的协作逻辑写出能被rk3566-evb或rockchip-rk3566-pc板级支持的可加载、可调试、可量产的驱动模块。适合正在做工业视觉终端、边缘 AI 相机、车载环视方案的嵌入式工程师尤其当你发现 Ubuntu 22.04 rootfs 下ls /dev/video*为空、modprobe rkisp1报错、或dmesg | grep -i csi显示no csi host found时这篇就是你的救命索引。2. 从设备树到 probeRK3566 MIPI Camera 驱动的三段式启动流程RK3566 的 MIPI Camera 驱动不是单个.c文件能搞定的它由sensor 驱动如ov5640.c、CSI host 控制器驱动rockchip-csi2.c和ISP 前端桥接驱动rkisp1.c三部分组成。这三者通过设备树Device Tree绑定、异步通知v4l2_async_notifier和 media entity link 串联。我们不从零造轮子而是基于 Rockchip 官方 Linux SDK如kernel 5.10.110已有框架补全缺失环节。以下步骤全部实测于rk3566-evbUbuntu 22.04根文件系统内核配置启用CONFIG_VIDEO_ROCKCHIP_ISP1y、CONFIG_VIDEO_ROCKCHIP_CSI2y、CONFIG_VIDEO_OV5640y。2.1 设备树必须同时定义 sensor、csi_host、isp 三个节点并显式 linkRK3566 的 MIPI CSI 子系统采用media-controller模型设备树中不能只写 sensor否则v4l2-ctl --all看不到 pipeline。以 OV5640 为例需在arch/arm64/boot/dts/rockchip/rk3566-evb.dts中添加i2c3 { status okay; ov5640: camera3c { compatible ovti,ov5640; reg 0x3c; clocks cru CLK_CIF_OUT; clock-names xvclk; power-domains power RK3566_PD_VIO; #address-cells 1; #size-cells 0; port { ov5640_0: endpoint { remote-endpoint csi0_in0; >static int ov5640_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ov5640_dev *sensor; int ret; sensor devm_kzalloc(client-dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM; sensor-client client; v4l2_i2c_subdev_init(sensor-subdev, client, ov5640_subdev_ops); // 关键初始化 async notifier 并添加 subdev sensor-notifier.subdevs sensor-subdev; sensor-notifier.num_subdevs 1; sensor-notifier.ops ov5640_notify_ops; // 自定义 notify 回调 ret v4l2_async_register_subdev_sensor(sensor-subdev, sensor-notifier); if (ret 0) { dev_err(client-dev, v4l2_async_register_subdev_sensor failed: %d\n, ret); return ret; } dev_info(client-dev, OV5640 probed successfully\n); return 0; }参数说明v4l2_async_register_subdev_sensor()是 Rockchip 5.10 内核专用接口替代旧版v4l2_async_register_subdev()ov5640_notify_ops必须实现.bound回调在 sensor 绑定成功后触发 CSI host 初始化若此处返回-EPROBE_DEFER说明 CSI host 驱动尚未加载需检查rockchip-csi2.ko是否已insmod或编译进内核。2.3 CSI Host 驱动rockchip-csi2.c的rkcsi2_probe()必须完成 media entity linkCSI host 驱动是整个链路的中枢。drivers/media/platform/rockchip/csi2/rockchip-csi2.c的rkcsi2_probe()必须执行三件事初始化struct media_device为每个 port 创建struct media_pad调用media_create_pad_link()建立 sensor → csi → isp 的 link。核心代码片段如下static int rkcsi2_probe(struct platform_device *pdev) { struct rkcsi2_dev *csi2; struct resource *res; int ret, i; csi2 devm_kzalloc(pdev-dev, sizeof(*csi2), GFP_KERNEL); if (!csi2) return -ENOMEM; csi2-dev pdev-dev; platform_set_drvdata(pdev, csi2); // 1. 初始化 media device csi2-mdev.dev pdev-dev; strscpy(csi2-mdev.model, rk3566-csi2, sizeof(csi2-mdev.model)); csi2-mdev.hw_revision 0x1; media_device_init(csi2-mdev); // 2. 为每个 port 创建 padrk3566 有 2 个 CSI port for (i 0; i RKCSI2_MAX_PORTS; i) { csi2-pads[i].flags MEDIA_PAD_FL_SINK; csi2-pads[i].id i; csi2-pads[i].entity csi2-subdev.entity; } csi2-pads[RKCSI2_PORT_SRC].flags MEDIA_PAD_FL_SOURCE; // 3. 注册 subdev 并创建 link csi2-subdev.entity.function MEDIA_ENT_F_IO_V4L; csi2-subdev.entity.name rk3566-csi2; csi2-subdev.owner THIS_MODULE; csi2-subdev.ops rkcsi2_subdev_ops; csi2-subdev.flags | V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_subdev_init(csi2-subdev, rkcsi2_subdev_ops); ret media_entity_pads_init(csi2-subdev.entity, RKCSI2_MAX_PORTS, csi2-pads); if (ret 0) goto err_media; ret v4l2_async_register_subdev(csi2-subdev); if (ret 0) goto err_media; // 关键link sensor endpoint to csi sink pad ret media_create_pad_link(sensor_subdev-entity, 0, csi2-subdev.entity, RKCSI2_PORT_SINK0, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret 0) dev_warn(pdev-dev, failed to create sensor-csi link: %d\n, ret); dev_info(pdev-dev, RK3566 CSI2 probed\n); return 0; err_media: media_device_cleanup(csi2-mdev); return ret; }逻辑说明MEDIA_LNK_FL_IMMUTABLE表示该 link 由设备树静态定义运行时不许修改RKCSI2_PORT_SINK0对应设备树中port0必须与csi0的port0严格对应若media_create_pad_link()失败dmesg会打印link creation failed此时需确认 sensor subdev 是否已注册v4l2-ctl --list-devices是否出现 sensor 名、CSI host 是否已 probe。3. 编译与加载内核模块的 Makefile、Kconfig 与符号导出陷阱RK3566 MIPI Camera 驱动若以模块方式加载.ko必须解决三个符号依赖问题rkisp1驱动需要引用csi2的rkcsi2_register_subdev()csi2需要引用ov5640的ov5640_s_power()而ov5640又依赖v4l2_async_notifier。这些跨模块调用必须通过EXPORT_SYMBOL_GPL()显式导出否则insmod时报Unknown symbol in module。3.1 Kconfig 与 Makefile按层级嵌套配置在drivers/media/i2c/Kconfig中追加config VIDEO_OV5640 tristate OmniVision OV5640 sensor support depends on VIDEO_V4L2 I2C select VIDEO_V4L2_SUBDEV_API select V4L2_ASYNC ---help--- This is a driver for the OmniVision OV5640 camera sensor. Say Y or M here if you have such a sensor connected to your RK3566 board.在drivers/media/i2c/Makefile中添加obj-$(CONFIG_VIDEO_OV5640) ov5640.o同理在drivers/media/platform/rockchip/csi2/Kconfig和Makefile中启用CONFIG_VIDEO_ROCKCHIP_CSI2并在drivers/media/platform/rockchip/isp1/Kconfig中启用CONFIG_VIDEO_ROCKCHIP_ISP1。注意CONFIG_VIDEO_ROCKCHIP_ISP1必须设为ybuilt-in不能为m否则rkisp1的media_device无法在csi2probe 时被找到导致 link 创建失败。3.2 符号导出三处必须加EXPORT_SYMBOL_GPL在drivers/media/i2c/ov5640.c末尾添加EXPORT_SYMBOL_GPL(ov5640_s_power);在drivers/media/platform/rockchip/csi2/rockchip-csi2.c末尾添加EXPORT_SYMBOL_GPL(rkcsi2_register_subdev);在drivers/media/platform/rockchip/isp1/rkisp1-common.c末尾添加EXPORT_SYMBOL_GPL(rkisp1_register_subdev);为什么必须导出Rockchip 的rkisp1驱动在rkisp1_subdev_register()中会调用rkcsi2_register_subdev()获取 CSI host 实例而ov5640的notify_bound()回调中又需调用rkcsi2_register_subdev()注册自身到 CSI host。这些函数不在同一模块不导出则链接失败。3.3 编译命令指定 ARCH 与 CROSS_COMPILE避免 x86 混淆RK3566 是 arm64 架构必须使用aarch64-linux-gnu-工具链。编译命令如下# 进入内核源码根目录 cd ~/linux-rockchip-5.10.110 # 清理旧模块 make modules_prepare make clean # 编译全部模块含 ov5640、csi2、isp1 make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- modules # 单独编译 ov5640 模块快速验证 make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- drivers/media/i2c/ov5640.o # 生成 .ko 文件 make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- Mdrivers/media/i2c modules make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- Mdrivers/media/platform/rockchip/csi2 modules make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- Mdrivers/media/platform/rockchip/isp1 modules参数说明ARCHarm64强制架构防止误用 host x86 工具链CROSS_COMPILEaarch64-linux-gnu-指定交叉编译前缀Ubuntu 22.04 下可通过sudo apt install gcc-aarch64-linux-gnu安装M参数指定模块路径比make modules更精准避免编译无关驱动。4. 避坑RK3566 MIPI Camera 驱动开发中 5 个血泪级翻车点开发过程中90% 的失败不是代码写错而是环境、配置、时序细节没对齐。以下是我在 3 款不同 OV5640 模组带 AF、不带 AF、不同 PCB layout上踩过的坑每一条都附带dmesg典型报错、根本原因和现场修复命令。4.1 现象dmesg打印rkisp1 15000000.isp: Failed to get isp clock: -517原因设备树中rkisp1节点漏写了clocks属性或clock-names与内核clk-rk3566.c中定义不匹配。RK3566 ISP 模块依赖aclk_isp,hclk_isp,isp_mipicsi三个时钟缺一不可。解决在rkisp1节点下补全clocks cru ACLK_ISP, cru HCLK_ISP, cru CLK_ISP_MIPICSI; clock-names aclk, hclk, mipicsi;然后make dtbs sudo cp arch/arm64/boot/dts/rockchip/rk3566-evb.dtb /boot/并重启。4.2 现象v4l2-ctl --list-devices显示ov5640 3-003c但无/dev/video*原因rkisp1驱动未正确注册video_device常见于CONFIG_VIDEO_ROCKCHIP_ISP1被设为m模块而非ybuilt-in。rkisp1的video_register_device()在rkisp1_subdev_register()中调用若rkisp1是模块其probe早于csi2导致csi2找不到rkisp1实例。解决make menuconfig进入Device Drivers → Multimedia support → Video capture adapters → Rockchip ISP1 support设为*built-in重新编译内核镜像Image并刷写。4.3 现象gst-launch-1.0 v4l2src device/dev/video0 ! fakesink silenttrue报Internal data stream error原因MIPI 数据 lane 数与 sensor 实际输出不一致。OV5640 默认输出 2-lane但某些模组出厂配置为 1-lane或设备树>gpiod_set_value_cansleep(sensor-pwdn_gpio, 1); usleep_range(1000, 1500); // ≥1ms gpiod_set_value_cansleep(sensor-reset_gpio, 0); usleep_range(10, 20); // ≥10us gpiod_set_value_cansleep(sensor-reset_gpio, 1); usleep_range(5000, 6000); // ≥5ms4.5 现象v4l2-ctl --set-fmt-videowidth1920,height1080,pixelformatUYVY成功但--stream-mmap --stream-count100只收到 3 帧就卡住原因RK3566 的rkisp1DMA buffer 大小不足。默认rkisp1分配 4MB buffer但 1080p30fps UYVY 格式每帧约 4.1MB1920×1080×2buffer 不足导致 DMA overrun。解决在设备树rkisp1节点下增加dma-coherent属性并增大memory-regionmemory-region isp_region; reserved-memory { isp_region: isp88000000 { reg 0x0 0x88000000 0x0 0x00800000; // 8MB no-map; }; };同时在rkisp1-core.c的rkisp1_vb2_queue_setup()中将*num_buffers 4改为8确保至少 8 帧 buffer。5. 调试与验证用media-ctl和v4l2-ctl把 pipeline 拆开看透当v4l2src黑屏时不要急着改代码先用media-ctl把整个 media device topology 拆解成原子单元逐级验证。这是 RK3566 MIPI Camera 调试最高效的方法比printk海量日志快 10 倍。5.1 查看完整 media graph确认 link 是否建立# 安装 media-ctl 工具Ubuntu 22.04 sudo apt install v4l-utils # 列出所有 media device media-ctl -d /dev/media0 --print-topology # 输出示例关键看 link 状态 Media controller API version 5.10.110 ... - entity 11: rk3566-csi2 (1 pad, 1 link) type: V4L2 subdev flags: has no active connections pad0: Sink - [ENABLED] ov5640 3-003c:0 [0] - entity 13: rk3566-isp-subdev (1 pad, 1 link) type: V4L2 subdev flags: has no active connections pad0: Sink - [ENABLED] rk3566-csi2:0 [0]解读- [ENABLED]表示 link 已激活若显示- [DISABLED]说明media_create_pad_link()失败或设备树 link 未配对has no active connections是正常现象表示 subdev 尚未被 video device 绑定不影响 link 状态。5.2 设置 pipeline 格式必须四步全走缺一不可RK3566 的格式设置是链式传递必须从 sensor → csi → isp → video node 逐级设置任何一级失败都会导致后续STREAMON失败# 1. 设置 sensor 输出格式决定 MIPI 传输内容 media-ctl -d /dev/media0 -l ov5640 3-003c:0-rk3566-csi2:0[1] # 2. 设置 csi host 接收格式必须与 sensor 输出一致 media-ctl -d /dev/media0 -l rk3566-csi2:0-rk3566-isp-subdev:0[1] # 3. 设置 isp 输入格式通常与 csi 输出相同 v4l2-ctl -d /dev/v4l-subdev0 --set-fmt-videowidth1920,height1080,pixelformatUYVY # 4. 设置 video node 输出格式最终应用读取的格式 v4l2-ctl -d /dev/video0 --set-fmt-videowidth1920,height1080,pixelformatNV12参数说明v4l-subdev0是rkisp1的 subdev 节点/dev/video0是最终 video devicepixelformat必须匹配OV5640 原生输出UYVYrkisp1可转为NV12或RGB24但video0的 format 必须与rkisp1输出一致若第 3 步报错Invalid argument说明rkisp1未正确初始化检查dmesg | grep isp是否有rkisp1: registered。5.3 抓取原始帧绕过 GStreamer用v4l2-ctl直接 dump当 GStreamer 卡死时用v4l2-ctl直接抓帧验证硬件链路是否通畅# 创建 10 帧 raw 文件UYVY 格式1920x1080 v4l2-ctl -d /dev/video0 --stream-mmap --stream-count10 --stream-toframe.raw # 转为可查看的 BMP需安装 ffmpeg ffmpeg -f rawvideo -pix_fmt uyvy422 -s 1920x1080 -i frame.raw -vframes 1 frame.bmp # 若 frame.bmp 能正常打开且有图像证明驱动链路 100% 通畅 # 若 stream-mmap 报错 No space left on device说明 DMA buffer 不足见 4.5技巧--stream-mmap比--stream-dma更稳定它使用内存映射而非 DMA buffer适合初筛硬件问题。5.4 性能瓶颈定位用perf查看 CSI host 中断频率MIPI 帧率上不去可能是 CSI host 中断处理太慢。用perf抓取中断统计# 启动视频流保持运行 gst-launch-1.0 v4l2src device/dev/video0 ! fakesink silenttrue # 抓取 10 秒中断事件 sudo perf record -e irq:irq_handler_entry -g -- sleep 10 sudo perf report -g --no-children | grep -A5 rkcsi2 # 关键指标若 rkcsi2_irq 占比 80%说明 CSI host 处理不过来需优化 rkcsi2_isr() 中的 memcpy 或降低分辨率我习惯在rkcsi2_isr()开头加ktime_get_ns()打点在结尾减去把耗时 50us 的分支单独 log。RK3566 的 CSI host 中断必须控制在 30us 内否则 30fps 会丢帧。这个习惯救了我三次产线联调——有一次是memcpy用了memmove导致 cache miss 暴增换回__builtin_memcpy后帧率从 12fps 拉回 30fps。希望帮到你。本文还有配套的精品资源点击获取