RV1126平台GC2093 MIPI摄像头驱动适配与V4L2调试 1. 摄像头链路的整体构成与适配路线选择1.1 一条图像数据从镜头到应用要经过哪几道手接手一块 RV1126 的板子模组焊上去、系统能起来但/dev/video下面空空如也这是我在做 GC2093 适配时的起点。RV1126 的摄像头链路不像单片机那样接个并口就能读数它是一条从 Sensor 到应用层、跨越内核与用户态的完整通路GC2093 通过 MIPI CSI-2 差分线把 RAW 数据送出来进到 SoC 的 MIPI D-PHY再交给 CSI 控制器csi_dphy / mipi csi host然后汇入 VICAP 采集单元接着进 ISP 做黑电平校正、去马赛克、白平衡、Gamma、降噪这一系列处理最后以 NV12 或 NV16 的 YUV 格式落到 DDR 里由 V4L2 把 buffer 交给上层。这条链路在软件上被 V4L2 的 media controller 框架描述成一张拓扑图Sensor 是一个子设备D-PHY 是一个子设备CSI 是一个子设备ISP 又是一个子设备每个子设备有自己的 pad 和 endpoint中间用remote-endpoint串起来。你要做的“添加摄像头”本质上就是让这张拓扑图里多出一个节点并且把它的 pad 正确连到下一级。理解这一点非常关键因为后面所有的设备树编写、驱动 probe、media-ctl -p输出异常都是在围绕这张拓扑图做文章。很多人第一次调不通不是驱动写错了而是拓扑根本没连上——media-ctl打印出来中间断了一截数据自然是流不过去的。1.2 为什么 GC2093 属于“标准 sensor 驱动”这一档GC2093 是一颗 200 万像素的 CMOS 图像传感器1080P 输出MIPI 接口支持 1 lane 或 2 lane 输出I2C 控制接口7 位从地址通常为 0x37。它的寄存器结构、上电时序、曝光增益控制方式都属于这一类 sensor 的通用范式上电、给 MCLK、释放复位、读 Chip ID 确认在线、下载初始化寄存器表、配置输出模式、启动流。这个“通用范式”决定了它的驱动不需要从零发明而是在现有 sensor 驱动模板上做替换和调整。RV1126 的 SDK 里通常已经带了 GC2053、OV13850、OS04A10 之类的驱动随便挑一个接口形式最接近的作为模板把寄存器表、分辨率定义、时钟配置换掉主体框架可以原样保留。这是我最推荐的起步方式比你对着框架文档从头写一份要快得多也少踩框架版本差异的坑。需要提醒的是单纯靠“照着模板抄”能跑通基础出图但跑不稳。GC2093 的上电时序、VTS 与帧率的关系、曝光寄存器的位宽与行时间换算这些细节必须对着规格书逐项核对否则你会遇到“能出图但亮度跳变”“低照度下闪黑”“帧率对不上”这类磨人的问题。1.3 三条可选路线与实际取舍面对“把 GC2093 接上 RV1126”这件事实际有三条路可以走难度和维护成本差别很大。第一条是完整接入 ISP 链路走 rkisp 驱动 rkaiq 3A 库Sensor 只输出 RAW Bayer后面所有画质处理由 ISP 完成。这条路的优点是画质上限高、支持自动曝光自动白平衡、能上各种降噪和宽动态缺点是依赖 IQ 调优文件GC2093 如果没有现成的 IQ 文件要么找厂商要要么拿相近型号的文件先顶着画质会打折扣。第二条是绕过 ISP走 VICAP 的直接采集通道把 Sensor 配成输出 YUV 或者干脆在 Sensor 内部做简化处理直接搬到内存。这条路调试快适合先验证硬件链路是否通、MIPI 是否有信号但画质基本没法看只能做原型验证。第三条是保守路线先确认硬件没问题再按第一条走完整链路。我的实际做法是二、一结合先走第二条路把“电气层通不通”这件事确认掉再切回第一条路做完整 ISP 接入。理由很实际——如果 MIPI 差分线焊反了、MCLK 没出来、供电少一路你在 ISP 链路上排查会非常痛苦日志里全是子设备绑定失败看不出来到底是哪一层的问题。先用最简单的直采通道跑通把硬件嫌疑排除掉后面调 ISP 就只剩下软件配置的事难度下降一个量级。路线数据路径调试难度画质上限适用阶段ISP 完整链路Sensor → DPHY → CSI → ISP → DDR高高正式产品VICAP 直采Sensor → DPHY → CSI → DDR中低硬件验证内部 YUV 输出Sensor 内部处理 → 直出低很低点亮确认2. 上手之前必须确认的硬件信息与资料清单2.1 五个必须逐项核对的硬件项这部分是我吃过亏的地方。软件调了三天没结果最后发现是模组供电少接了一路这种事听起来离谱但在打样阶段发生的概率远比想象中高。所以动手写代码之前先拿万用表和示波器把下面五项确认一遍。供电。GC2093 一般需要三路模拟电源 AVDD典型 2.8V、IO 电源 DOVDD典型 1.8V、数字核心电源 DVDD典型 1.2V。三路的电压值、上电顺序、每路的去耦电容是否贴了都要对着模组规格书和原理图核对。上电顺序搞反有的批次的芯片会直接锁死表现就是 I2C 读 Chip ID 一直返回 0xFF 或者超时。MCLK。GC2093 的典型主时钟是 27MHz。RV1126 侧由 CRU 的CLK_MIPI_CAMARAOUT_M*系列时钟提供具体用 M0 还是 M1、M2、M3取决于你的模组接在哪一组引脚上。示波器探一下确认频率对、幅度够、波形干净。MCLK 没出来是最常见的“读不到 ID”原因之一。复位与掉电引脚。RESET 一般是低有效PWDN 一般高有效也有反的看规格书。这两个脚在设备树里要配对成 GPIO并且要注意默认电平。如果 PWDN 默认被拉高芯片一直在掉电状态你写什么寄存器都没反应。MIPI 差分对。确认 lane 数量和走线。GC2093 常见配置是 2 lane。差分对的正负有没有接反这个在软件上表现为“能出图但花屏、错位、颜色完全不对”。有些平台支持在 D-PHY 里做 lane swap 或 polarity swap能救回来但如果是 SoC 侧不支持交换的通道就只能改板。I2C 地址。7 位地址 0x37 是默认值但要注意有些模组上会把地址选择脚拉高变成 0x3F 之类。用i2cdetect -y 1扫一遍能看到设备再往下走比什么都实在。2.2 软件环境与源码结构要先摸清楚RV1126 的 SDK 一般是 Linux 4.19 内核摄像头相关代码集中在几个位置kernel/drivers/media/i2c/放 sensor 驱动kernel/drivers/media/platform/rockchip/下面有 isp、cif、csi 的控制器驱动设备树在kernel/arch/arm/boot/dts/下IQ 调优文件一般放在根文件系统的/etc/iqfiles/或者/oem/etc/iqfiles/。上手第一件事是确认内核版本和已有的 sensor 驱动清单ls drivers/media/i2c/ | grep -i gc看有没有现成的grep -r gc2053 arch/arm/boot/dts/看设备树里有没有可以参考的完整例子。这一步花了十分钟后面能省掉几小时。另外确认一下编译方式make ARCHarm rv1126_defconfig之后进menuconfig能不能找到CONFIG_VIDEO_GC2093。如果 Kconfig 里没有这一项说明驱动文件还没放进去或者 Makefile 没加先补这两处。2.3 一份必须提前拿到的资料清单写驱动之前我通常会向模组供应商要这几样东西缺一样都会在后面某个环节卡住规格书Datasheet至少要包含寄存器描述、上电时序图、MIPI 时序参数、曝光增益计算公式。初始化寄存器表这是最关键的一份通常是.h或者.txt格式对应 1080P30fps、1080P60fps 等几个模式。镜头与模组的光学参数包括镜头焦距、光圈、CRA主光线角这个影响 IQ 调优但早期可以不管。如果有的话再要一份同平台已经调通的参考驱动哪怕是别的 SoC 平台的参考价值也很大。注意初始化寄存器表一定要问清楚对应的是哪个输出模式、多少 lane、MCLK 多少。我遇到过给的寄存器表是按 27MHz MCLK 写的但板子上给的是 24MHz结果出图帧率始终差 10%排查了很久才发现是时钟源不匹配导致 PLL 分频算错。3. GC2093 驱动接入的核心细节解析3.1 Sensor 驱动的骨架与必须实现的回调RV1126 上的 sensor 驱动本质是一个 V4L2 子设备驱动核心结构是struct v4l2_subdev通过v4l2_i2c_subdev_init注册。这里面必须实现几组回调缺一个都跑不起来。v4l2_subdev_core_ops里的s_power或者在新框架里的 runtime PM 回调负责上电下电。v4l2_subdev_video_ops里的s_stream负责启动和停止数据流这里是写“stream on”寄存器的位置。v4l2_subdev_pad_ops里的enum_mbus_code、get_fmt、set_fmt、enum_frame_size、get_selection、set_selection负责格式协商和裁剪。还有控制相关的v4l2_ctrl_ops用来实现曝光、增益、帧率这些可调参数。驱动内部一般会维护一份supported_modes[]数组每个元素描述一个分辨率模式宽高、最大帧率、寄存器表指针、寄存器表长度、每行的像素时钟、HTS/VTS 值、MIPI 数据率。set_fmt被调用时驱动从数组里挑一个最接近的模式把对应的寄存器表准备好s_stream(1)被调用时把这张表通过 I2C 批量写下去。这里有个容易忽略的点寄存器表里通常不包含 stream on 的那一条需要单独在s_stream里写。反过来s_stream(0)时也不能只写一条 stream off 就完事有些模组需要重新走一遍下电上电才能正常重启否则第二次开流会黑屏。static int gc2093_s_stream(struct v4l2_subdev *sd, int on) { struct i2c_client *client v4l2_get_subdevdata(sd); struct gc2093 *gc2093 to_gc2093(sd); int ret 0; mutex_lock(gc2093-lock); on !!on; if (on gc2093-streaming) goto unlock; if (on) { ret gc2093_write_regs(client, gc2093-cur_mode-reg_list, gc2093-cur_mode-reg_list_size); if (ret) goto unlock; ret gc2093_write_reg(client, GC2093_REG_CTRL_MODE, 0x01); } else { ret gc2093_write_reg(client, GC2093_REG_CTRL_MODE, 0x00); } gc2093-streaming on; unlock: mutex_unlock(gc2093-lock); return ret; }3.2 模式定义与寄存器表的组织方式supported_modes[]的写法直接决定了后面能不能灵活切换分辨率。我习惯把常用模式都列上哪怕当前产品只用 1080P多列的几档在调试和客户演示时很有用。static const struct gc2093_mode supported_modes[] { { .width 1920, .height 1080, .max_fps { .numerator 10000, .denominator 300000, }, .exp_def 0x04B0, .hts_def 0x0898, .vts_def 0x046C, .reg_list gc2093_1080p30_linear_regs, .reg_list_size ARRAY_SIZE(gc2093_1080p30_linear_regs), }, };hts_def和vts_def这两个值是从寄存器表里反推出来的不能随便填。它们决定了曝光时间的换算基准一行的时间等于 HTS 除以像素时钟一帧的时间等于 VTS 乘以行时间。曝光寄存器写入的数值单位就是“行数”所以曝光时间 曝光寄存器值 × 行时间。如果 HTS/VTS 填错你会发现设置 10ms 曝光实际出来是 7ms 或者 14ms画面亮度怎么调都不对。帧率同理实际帧率 像素时钟 / (HTS × VTS)。想改帧率最省事的办法是改 VTS因为 HTS 牵扯到整行读出时序动它容易引起图像异常。3.3 上电时序与电源管理的实操细节上电时序这块规格书里通常会给一张时序图标注每一路电源之间、电源到复位释放之间的最小间隔。常见要求是各路电源间隔 0 到几毫秒全部稳定后至少等 1ms 再释放复位复位释放后再等若干毫秒才能开始 I2C 通信。代码里的做法一般是在power_on回调里用regulator_bulk_enable把几路 regulator 依次使能usleep_range等待再拉 GPIO 释放复位和 PWDN。这里有个细节regulator 的使能顺序由 regulator 驱动自己保证不了需要你在设备树里用regulator-boot-on或者在代码里按顺序一个个 enable。批量使能如果顺序不对某些批次的模组会出现上电电流冲击导致复位不干净。static int __gc2093_power_on(struct gc2093 *gc2093) { int ret; struct device *dev gc2093-client-dev; ret clk_prepare_enable(gc2093-xvclk); if (ret 0) { dev_err(dev, Failed to enable xvclk\n); return ret; } if (gc2093-reset_gpio) gpiod_set_value_cansleep(gc2093-reset_gpio, 0); ret regulator_bulk_enable(GC2093_NUM_SUPPLIES, gc2093-supplies); if (ret 0) { dev_err(dev, Failed to enable regulators\n); goto disable_clk; } usleep_range(1000, 2000); if (gc2093-pwdn_gpio) gpiod_set_value_cansleep(gc2093-pwdn_gpio, 0); if (gc2093-reset_gpio) gpiod_set_value_cansleep(gc2093-reset_gpio, 1); usleep_range(500, 1000); return 0; disable_clk: clk_disable_unprepare(gc2093-xvclk); return ret; }注意最后的usleep_range值不要图省事写成固定 10000过长的等待会让开流时间变长影响快速抓拍的响应过短则可能模组还没准备好第一帧是黑的或者花的。500 到 1000 微秒这个量级是我在几个批次模组上验证下来比较稳的。3.4 链路频率的推算过程link-frequencies这个设备树属性经常被随手填一个值了事但它是 D-PHY 配置的依据填错会导致采样错误、花屏或者干脆收不到数据。这里把推算过程走一遍。假设选 1080P30fps、RAW10、2 lane 的配置。总像素数 1920 × 1080 2,073,600。每秒 30 帧像素率就是 62,208,000 pixel/s。RAW10 每像素 10 bit总数据率 622,080,000 bit/s。分摊到 2 条 lane每条 311,040,000 bit/s。这还没完MIPI 传输有协议开销包括包头、包尾、长短包之间的间隔实际需要的线速率要在此基础上留余量一般按 1.15 到 1.2 倍算。311M × 1.15 ≈ 358M。取个整配 400Mbps 左右比较合适留出裕量也不会让 D-PHY 工作在过高的功耗点上。对应到设备树就是link-frequencies /bits/ 64 400000000;。这个值要和 sensor 驱动里v4l2_ctrl设置的 pixel rate 保持一致两个地方对不上V4L2 校验会报错。经验上先把频率往低了配去试如果出图正常再往上调。频率配高了不一定出问题但配低了必然花屏而花屏的表现和 lane 接反、时序不对非常像容易误判。4. 完整实操从内核配置到出第一张图4.1 内核选项与编译配置驱动文件放到位之后两处必须改drivers/media/i2c/Kconfig里加一项drivers/media/i2c/Makefile里加一行。config VIDEO_GC2093 tristate GalaxyCore GC2093 sensor support depends on I2C VIDEO_V4L2 VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the GalaxyCore GC2093 camera sensor.obj-$(CONFIG_VIDEO_GC2093) gc2093.o然后make ARCHarm menuconfig在Device Drivers → Multimedia support → V4L2 sub-device userspace API和Camera sensor devices下面找到 GC2093 勾上。编译完ls drivers/media/i2c/gc2093.o有产物就说明编进去了。有个经常被忽略的点RV1126 的 DTS 里如果引用了rockchip,camera-module-name这些属性内核还需要CONFIG_VIDEO_ROCKCHIP_CAMERA_MODULE之类的选项支持。不用的话可以不写写了但没开可能不影响驱动加载但会影响上层框架对模组的识别。4.2 设备树三层节点的写法设备树是三段式的I2C 下面挂 sensorDPHY 下面挂 sensor 的输出ISP 下面挂 DPHY 的输出。三段要互相remote-endpoint指到对方指错了拓扑就断。i2c1 { status okay; clock-frequency 400000; gc2093: gc209337 { compatible galaxycore,gc2093; status okay; reg 0x37; clocks cru CLK_MIPI_CAMARAOUT_M1; clock-names xvclk; clock-frequency 27000000; power-domains power RV1126_PD_VI; pinctrl-names default; pinctrl-0 mipim1_camera1_clk; reset-gpios gpio1 RK_PB0 GPIO_ACTIVE_LOW; pwdn-gpios gpio1 RK_PB1 GPIO_ACTIVE_HIGH; rockchip,camera-module-index 0; rockchip,camera-module-facing back; rockchip,camera-module-name GC2093; rockchip,camera-module-lens-name default; port { gc2093_out: endpoint { remote-endpoint mipi_in_gc2093; >csi_dphy1 { status okay; ports { port0 { reg 0; mipi_in_gc2093: endpoint { remote-endpoint gc2093_out; >i2cdetect -y 1在 0x37 位置看到37就说明上电和通信正常。看不到的话往下就别看了回去查供电、MCLK、复位。再确认驱动 probe 成功dmesg | grep -i gc2093正常会打印类似gc2093 1-0037: Detected GC2093 sensor, id: 0x2093这样的日志。如果打印了 probe 但 Chip ID 读出来是 0x0000 或者 0xFFFF那基本可以确定是电源或者时钟问题。然后看 media 拓扑media-ctl -d /dev/media0 -p输出里应该能看到一条完整的链路从 gc2093 到 csi_dphy 到 rkisp。中间任何一处显示(not connected)就回去查remote-endpoint是不是写反了或者写错了。再看视频节点v4l2-ctl --list-devices v4l2-ctl -d /dev/video0 --list-formats-ext格式列表里应该能看到1920x1080以及对应的 Bayer 格式比如RG10、BG10、BA10之类。如果只列出了 640x480 或者格式为空说明supported_modes没被正确解析。4.4 抓图验证与 ISP 链路的打通拓扑通了不代表能出图。要真正抓一帧得先让 ISP 的 3A 服务跑起来否则 ISP 通道不会启动。RV1126 上一般是rkaiq_3A_server或者集成在rkipc里具体用哪个看你的 SDK 提供了什么。rkaiq_3A_server sleep 2然后抓图v4l2-ctl -d /dev/video0 \ --set-fmt-videowidth1920,height1080,pixelformatNV12 \ --stream-mmap4 \ --stream-count10 \ --stream-to/tmp/cap.yuv/tmp/cap.yuv大小应该是 1920×1080×1.5×10 31,104,000 字节左右。大小对不上说明格式或者分辨率没生效。拿到裸数据之后转成图片看看ffmpeg -f rawvideo -pix_fmt nv12 -s 1920x1080 -i /tmp/cap.yuv -frames:v 1 /tmp/cap.png第一张图能出来只要不是全黑、全绿、大面积花屏硬件链路这一关就算过了。颜色偏、亮度暗这些属于 IQ 层面的事后面再调。4.5 出图之后要做的三件小事第一件把曝光和增益的控件跑一遍确认调节方向正确。用v4l2-ctl -l列出所有控件找exposure和analogue_gain两项分别加减试试观察图像亮度的变化幅度是否符合预期。如果曝光调大反而变暗说明寄存器映射写反了。第二件测实际帧率。v4l2-ctl --stream-mmap4 --stream-count300然后用time计时算出平均帧率。1080P30 配置下实测应该在 29 到 30 之间偏差超过 5% 就要回去核 HTS/VTS。第三件连续跑半小时以上观察有没有偶发黑帧、掉帧、dmesg里有没有 I2C 超时。这类间歇性问题在早期不暴露量产后才爆发代价极大。5. 常见问题排查实录5.1 探测阶段失败的三类原因I2C 扫不到设备来源就三块电、时钟、复位。先量电源三路都要量不能只量一路。再量 MCLK示波器看频率和幅度RV1126 输出的 MCLK 有些配置下默认是关的需要在设备树里把assigned-clocks和assigned-clock-rates写对。最后查复位和 PWDN 的 GPIO 极性用cat /sys/kernel/debug/gpio看引脚当前电平。I2C 能扫到但 Chip ID 读不对多半是寄存器地址位宽或者读写时序问题。GC2093 用 16 位寄存器地址、8 位数据如果模板驱动是 8 位地址的需要改regmap_config或者读写函数。也有可能是模组实际型号不是 GC2093供应商发错货这种事我遇到过不止一次。Probe 成功但没生成 video 节点去查v4l2_async_register_subdev_sensor有没有被调用以及 endpoint 是不是写得让框架找不到对端。dmesg里搜async关键字通常会有明确提示。5.2 出图阶段的典型异常与对策画面全绿或者大面积绿色块是 MIPI 数据没收到或者收到的是无效数据。原因可能是 lane 数量配错、lane 极性反了、link frequency 太低。先试把>