☰
RV1126平台GC2093 MIPI摄像头驱动适配与V4L2调试
2026/10/26 21:03:55 网站建设 项目流程

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 ARCH=arm rv1126_defconfig之后进menuconfig能不能找到CONFIG_VIDEO_GC2093。如果 Kconfig 里没有这一项,说明驱动文件还没放进去或者 Makefile 没加,先补这两处。

2.3 一份必须提前拿到的资料清单

写驱动之前,我通常会向模组供应商要这几样东西,缺一样都会在后面某个环节卡住:

规格书(Datasheet),至少要包含寄存器描述、上电时序图、MIPI 时序参数、曝光增益计算公式。初始化寄存器表,这是最关键的一份,通常是.h或者.txt格式,对应 1080P@30fps、1080P@60fps 等几个模式。镜头与模组的光学参数,包括镜头焦距、光圈、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 配置的依据,填错会导致采样错误、花屏或者干脆收不到数据。这里把推算过程走一遍。

假设选 1080P@30fps、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 ARCH=arm 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 下面挂 sensor,DPHY 下面挂 sensor 的输出,ISP 下面挂 DPHY 的输出。三段要互相remote-endpoint指到对方,指错了拓扑就断。

&i2c1 { status = "okay"; clock-frequency = <400000>; gc2093: gc2093@37 { 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 { port@0 { 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-video=width=1920,height=1080,pixelformat=NV12 \ --stream-mmap=4 \ --stream-count=10 \ --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-mmap=4 --stream-count=300然后用time计时,算出平均帧率。1080P@30 配置下,实测应该在 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 太低。先试把>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询