做 RV1106 方案的第一个晚上,我盯着排线陷入沉思:sensor 供电正常、复位也拉完了,dmesg里死活不报 sensor 挂载,MIPI 时钟测出来却又是波形。后来翻了一整晚的资料,猜了无数种可能,最后发现根因既不在硬件上,也不在驱动里,而是设备树中 ISP 那段地址与 MIPI DPHY 的 lane 映射压根没对上。这个经历让我决定把 RV1106 的 ISP 配置、MIPI/LVDS 硬件连接和设备树优化整理成一篇实战笔记。文章面向正在折腾 RV1106 或者 RK 其他小算力 SoC 的嵌入式工程师,重点解决三件事:如何把 MIPI sensor 的数据真正送进 ISP,如何通过 MIPI DSI 转 LVDS 点亮工业屏,以及当画面不出、屏幕不亮、画质不对时,用什么思路一步步定位问题。
1. 先把 ISP 通路看明白:RV1106 上数据是怎么从 sensor 流到内存的
1.1 RV1106 的 ISP 只能吃 MIPI,不能直连 LVDS
RV1106 是瑞芯微面向 IPC 和智能视觉设备的 SoC,集成度很高:内置 ISP、H.264/H.265 编解码、以太网、USB,还带 MIPI CSI 和 MIPI DSI。很多人第一眼看到"支持 MIPI 和 LVDS"这句话,容易误以为它的接口控制器可以直接接 LVDS sensor 或者 LVDS 屏幕。这里必须先泼一盆冷水:RV1106 的摄像头输入通路是 MIPI CSI,显示器输出通路通常是 MIPI DSI,LVDS 并不是它的原生接口能力。
所以实际项目里会经常出现两类"翻译芯片":
- 摄像头侧:工业相机或车载 sensor 经常输出 LVDS,需要经过 FPGA 或者专用的 LVDS-to-MIPI bridge,转换成 MIPI CSI 信号后再进入 RV1106。
- 显示侧:工业屏、医疗屏、自助设备屏大多还是 LVDS 接口,需要 MIPI DSI 转 LVDS 的 bridge 芯片,比如 LT8912B、GM8775、TC358775 这类。
搞清楚这个之后,整个系统的数据流就很清晰了:sensor 输出 MIPI -> RV1106 内部 MIPI DPHY 接收 -> ISP 做坏点矫正、去马赛克、3A 等处理 -> DDR 中拿到 YUV/RGB 数据 -> 显示控制器从 DDR 取帧 -> MIPI DSI 发送给 LVDS bridge -> bridge 转成 LVDS 电平给屏幕。ISP 在中间起到的作用,不是简单给图像加一层滤镜,而是把 sensor 的 RAW Bayer 数据变成人眼看着正常、算法跑得动的 RGB/YUV 数据。所有后续编码、显示、分析,都依赖这一级输出质量。
1.2 ISP 的地址段、pipeline 与驱动节点关系
在 Linux 设备树里,RV1106 的 ISP 会以平台设备的形式存在,常见节点名类似isp: isp@xxxxxxxx。后面的@地址就是 ISP 寄存器基地址段,比如某份 SDK 里是reg = <0x00000000 0xff4a0000 0x00000000 0x10000>这样的布局。不同 SDK 和 BSP 版本地址段可能不一样,但这个节点在 dtsi 文件里一定存在。如果你在设备树里看到 ISP 节点被status = "disabled"或者填了一个错误的 reg 地址,驱动 probe 时就会报request_mem_region failed,整个 ISP 通路直接起不来。
ISP pipeline 在 Linux 里被表达成一条 media controller 拓扑。简单理解就是:一侧是 sensor subdev,中间是 MIPI DPHY,再接 ISP subdev,最后是rkisp_mainpath/rkisp_selfpathvideo 节点。每个节点之间通过remote-endpoint属性连接。这条链路只要有一个 endpoint 写错名字、写错>pwdn-gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>;
GPIO_ACTIVE_HIGH表示高电平有效,也就是pwdn拉高时进入 power down。GPIO_ACTIVE_LOW表示低电平复位。很多新人最容易在这里翻车:原理图上明明写了 RESET 是低有效,驱动逻辑里也按低电平复位写,但设备树里 active level 写反,导致复位引脚一直处于有效状态。
如果你不想猜时序,有一个办法:先看 dmesg 里 sensor 驱动的 probe 流程,再配合示波器量 GPIO 波形。我遇到过不止一次,驱动里reset_gpio请求的是高电平,但 DTS 里写GPIO_ACTIVE_HIGH,代码 set 1 时 pin 实际拉低,整颗 sensor 始终在复位里。这种问题不看波形基本找不到。
3. LVDS 显示输出:MIPI DSI 转 LVDS bridge 的硬件连接与初始化
3.1 为什么选 bridge 而不是 SOC 直出 LVDS
RV1106 内部并没有原生 LVDS 发送器,所以工业屏和车载屏常见的 LVDS 接口必须依赖外部 bridge。市面上常见的变通方法还有两条老路:一是用 SoC 的 RGB 并行接口通过 THC63LVDM83 这类芯片转为 LVDS,二是用 MIPI DSI 转 LVDS 的专用桥接芯片。优先推荐后者,因为 RV1106 的显示控制器本来就是按 MIPI DSI 设计的,让显示控制器输出 RGB 并行再去转,不仅多一次形式转换,还占用更多 IO。
选 bridge 时主要看三个参数:输入接口能否匹配 MIPI DSI 的 lane 数和时钟频率,输出 LVDS 是 6-bit 还是 8-bit,以及桥接芯片内部是否带寄存器配置脚和自动加载能力。LT8912B 这类芯片能支持 4-lane DSI 进、单/双口 LVDS 出,比较适合 1080P 以下的屏幕;GM8775 则有些型号支持 RGB 和 LVDS 双输出,方便做兼容设计。具体选哪颗,取决于你的屏规格和供应商能提供的软件支持,这里不绝对。
3.2 LT8912 和 GM8775 的连接要点
LVDS 接口本身也是差分对,但它的电气特性和 MIPI 不一样。LVDS 差分摆幅大约 350mV,共模电压约 1.2V,而 MIPI D-PHY 的 HS 差动摆幅只有 200mV 左右。所以 bridge 芯片存在的意义之一,就是电平转换。在连接时,不要拿 MIPI 的走线规则直接套 LVDS,LVDS 同样要求差分 100Ω 对,但端接一般做在接收端,SoC 无需额外再并联电阻,否则 PCIE/网络的经验用错了地方。
一个常见的连接要点是配置引脚。很多 LVDS bridge 芯片有多根配置脚,用来设置 I2C 地址、输出映射、6/8bit 模式、单/双通道,这些配置脚一般由硬件上拉/下拉电阻决定。不要以为在 dts 里写 I2C 寄存器配置就万事大吉,有些模式切换必须在芯片上电复位之前由 strap 脚决定。我踩过的坑是 GT---- 芯片的 output map 方向 strap 接错,导致屏幕能亮但颜色通道严重错位,蓝色变成了红色,一眼就能看出来不正常。
另外注意 HCSL 和 LVDS 的区别。HCSL 常用于 PCIe 时钟,共模电压低,不能用同一套匹配电路;LVDS 共模 1.2V,是真正的差分信号。如果 bridge 原理图里 MIPI 时钟输入与 LVDS 时钟输出画在相邻区域,画板时一定要分清,别把 HCSL 的偏置和端接套到 LVDS 上。
3.3 bridge 的复位与 I2C 初始化顺序
bridge 芯片的初始化顺序通常比 sensor 更讲究。典型的流程是:先给 bridge 供电,等电源稳定,拉高/拉低复位解除,再通过 I2C 写寄存器配置输入 lane、输出格式、pll 分频等。如果顺序不对,bridge 内部 PLL 可能锁定在错误频率,表现为屏幕有时能亮有时不亮,或者亮度闪烁。
设备树里一般会给 bridge 提供 enable-gpios 和 reset-gpios。驱动的 probe 流程里会按顺序操作它们。如果你发现屏幕点不亮,dmesg 里又没有任何报错,建议先量 bridge 的 I2C SCL 波形。如果 SCL 一直为低,可能是 bridge 忙;如果 I2C 有响应但配置失败,就要核对寄存器表是不是有 bank 切换。部分 bridge 的寄存器分多个 bank,直接按 datasheet 顺序写会串寄存器,这种问题看 dmesg 没用,必须逐个寄存器回读。
4. 设备树优化实战:sensor、ISP 与 MIPI 转 LVDS 的配置细节
4.1 sensor 节点配置:reg、时钟、GPIO 和 lane
以常见的 SC3336 这类 sensor 为例,RV1106 的 dts 中 sensor 节点通常挂在某个 I2C 总线下:
&i2c3 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c3_xfer>; sc3336: sc3336@30 { compatible = "smartsens,sc3336"; reg = <0x30>; clocks = <&cru CLK_MIPICAM0>; clock-names = "xvclk"; assigned-clocks = <&cru CLK_MIPICAM0>; assigned-clock-rates = <24000000>; pinctrl-names = "default"; pinctrl-0 = <&mipicam0_pinctrl>; reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "default"; rockchip,camera-module-lens-name = "default"; port { sc3336_out: endpoint { remote-endpoint = <&mipi_dphy0_ucam0>; >gpiod_set_value_cansleep(sensor->reset_gpio, 0); usleep_range(1000, 2000); gpiod_set_value_cansleep(sensor->pwdn_gpio, 0); msleep(20);但有些 SDK 的 bridge 驱动或 sensor 驱动支持reset-delay-ms、enable-delay-ms这类自定义属性。如果你用的驱动不支持,最直接的办法是改驱动里probe或初始化函数,给gpiod_set_value后加延时,不要妄想用 dts 的powerdown-gpios或者 regulator 的regulator-boot-on完全替代。
顺带提一个高级技巧:如果 sensor 的供电是由多个 regulator 控制的,且硬件要求 AVDD 比 DVDD 先上电,可以在 dts 中用regulator-boot-on保证上电顺序,但boot-on只保证 Linux 启动前 regulator 处于打开状态,不一定会按照你想要的顺序延迟。真正可靠的做法还是在 sensor 驱动里显式调用regulator_enable,并加udelay/msleep。
4.3 MIPI DSI 转 LVDS bridge 的 dts 写法
对于 DSI 转 LVDS 的 bridge,设备树通常这样组织:&dsi下面挂一个 panel/bridge 子节点,并在 DSI 输出端口和 bridge 输入端口之间建立remote-endpoint关系。
&dsi { status = "okay"; #address-cells = <1>; #size-cells = <0>; lvds_bridge@0 { compatible = "lontium,lt8912b"; reg = <0>; enable-gpios = <&gpio0 RK_PA6 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&lcd_lvds_rst_gpio>; dsi,lanes = <4>; dsi,format = "rgb888"; panel-timing { clock-frequency = <50000000>; hactive = <1024>; vactive = <768>; hfront-porch = <160>; hback-porch = <160>; hsync-len = <8>; vfront-porch = <23>; vback-porch = <23>; vsync-len = <4>; }; port { bridge_in: endpoint { remote-endpoint = <&dsi_out_panel>; }; }; }; };dsi,lanes控制 DSI 发送 lane 数,一般要和 bridge 输入能力一致。dsi,format通常是 rgb888,如果屏幕只支持 6-bit LVDS,那驱动会在输出端做裁位或抖动,但 dsi 侧仍然建议按 rgb888 传输。panel-timing里面的时序参数来自屏幕规格书,千万不要自己估算,尤其hfront-porch、hback-porch、hsync-len这几个参数,写错会引起屏幕抖动、偏移,甚至直接黑屏。
如果你接的是直接带 MIPI DSI 的屏而不是转 LVDS,常见 IC 如 ST7701S 也走同样的 dts 结构,只是 compatible 换成对应驱动。理解这套结构后,不管后面接 LVDS 桥还是直接 MIPI 屏,逻辑都一样。
4.4 用 media-ctl 验证 ISP pipeline 是否真正串起来
设备树改完之后,不要急着一路v4l2-ctl拉流。先做两件事:
- 确认 sensor subdev 被枚举出来:
media-ctl -p看设备列表。 - 确认 DPHY 和 ISP 的 link 已建立。
如果 sensor 出现在i2cdetect里但 media 拓扑没有它,多半是 sensor 驱动 probe 失败,dmesg 里找probe of 4-0030 failed with err。如果 sensor 在 media 拓扑里但 link 是断开的,可以手动建链:
media-ctl -d /dev/media0 -l "'m00_b_sc3336 4-0030:0'->'csi2dphy0':0[1]" media-ctl -d /dev/media0 -l "'csi2dphy0':0->'rkisp-isp0':0[1]"然后设置格式:
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=NV12 v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=1如果第一帧出不来,再往回检查。我自己的习惯是,把这一步当作硬性验收节点:只要--stream-mmap能拿到一帧,硬件和 dts 的基本链路就是通的,后面才进入画质调试阶段。
5. ISP 画质调优:坏点矫正、去马赛克和基础 tune 流程
5.1 坏点矫正:先解决最容易被误认为"噪声"的死点
CMOS sensor 生产过程中难免有坏点,表现为 RAW 域中固定位置的白点或黑点。这些坏点如果不去掉,会直接影响后面 ISP 的自动曝光和白平衡统计,因为统计模块会把坏点当成过亮或过暗的点,导致整体曝光偏移。更头疼的是,坏点在 scalar 缩放后不会消失,视频里会看到固定的亮点或暗点,用户一看就觉得 sensor 损坏。
ISP 的坏点矫正分为静态校正和动态校正两类。静态校正是把已知的坏点坐标写进一个坏点列表,ISP 在处理时直接跳过/插值这些位置。动态校正是实时检测图像中明显异常的孤立像素,在 Bayer 域做处理。RV1106 的 ISP 坏点矫正模块一般需要 tuning 工具导入坏点文件,特别是 sensor 出厂时提供的 defective pixel map。如果设备不提供,可以通过采集 RAW 图后手动生成列表。
调试时注意:坏点矫正不是越强越好。动态矫正阈值设太低了,会把正常的细小纹理也给抹平,图像看起来发糊;阈值设太高,坏点又清不干净。一般先用静态坏点表把已知坏点干掉,再配合动态矫正做补充,才不容易误伤边缘。
5.2 去马赛克参数与假彩抑制
sensor 输出的 RAW 数据是单通道 Bayer 格式,每个像素只有 R/G/B 中的一个分量。ISP 需要通过去马赛克(Demosaic)算法恢复出每个像素的 RGB 三通道。去马赛克的效果直接决定图像细节和边缘质量。
去马赛克的核心有两个:边缘方向检测和伪彩色抑制。边缘方向检测做得不好,会在文字边缘、栏杆等高频区域出现锯齿,专业点叫 "zipper effect"。伪彩色抑制不好,则会在白底黑字的边缘看到紫边或绿边。
tuning 时常用参数包括边缘检测阈值、水平/垂直方向的权重、周围像素搜索范围。如果图像整体偏软,可以增大边缘增益;如果出现明显假彩,则优先加大色差抑制强度,而不是去调 CCM。很多人拿到一块新 sensor,第一步就急着调饱和度、对比度和 gamma,这是误区。先保证去马赛克输出没有彩色锯齿,再谈色彩风格,否则后面所有色偏都可能是处理伪彩导致的,不是真实色彩问题。
5.3 一次完整的 ISP tune 实测流程
RV1106 的 ISP tuning 一般依赖 SDK 里的 rkaiq 或者独立 tuning 工具,流程大致如下:
- 在固定光源环境(标准 D65 灯箱)下挂灰卡和 24 色卡。
- 使用工具采集 RAW 图,同时记录 sensor 曝光和增益信息。
- 在 tuning 工具里加载 RAW,调整坏点矫正、去马赛克、白平衡、CCM、gamma 等模块参数。
- 导出生成的 tuning 文件,通常是一个 xml/json 或二进制 IQ 文件。
- 把 IQ 文件放到板子的
/etc/iqfiles或者vendor/etc/iqfiles路径下。 - 重启 rkaiq 或重跑一次 ISP pipeline,确认新参数加载。
调试中最重要的习惯是"每次只改一个模块":先关掉其他模块,确定坏点矫正是否生效;再开去马赛克,调整边缘;最后才做色彩。一次改三四个模块,画面变正常了也不知道是哪个参数起的作用,后面想继续优化就完全靠猜。另外建议把采集 RAW 的曝光控制在中间值,不要过曝或欠曝,否则画质参数会失真,到暗光环境就会出现颜色断层。
6. 实测踩坑:时钟波形、复位时序、LVDS 不亮和横竖屏问题
6.1 用示波器看 MIPI 时钟:无图时的第一个证据
当 sensor 启动失败、没有图像输出时,第一个要量的是 MIPI clock lane。在 sensor 处于 streaming 状态后,CLK 通道应该有稳定的差分时钟输出。如果 CLK 上没有波形,说明 sensor 根本没输出,问题大概率在 sensor 侧或者 MIPI 链路;如果 CLK 有波形,但 D0/D1 data lane 没有活动,则可能是数据 lane 配置不对,或者 sensor 仍处于空闲状态。
测量时要用差分探头,或者用两个单端探头分别测 P/N 再相减,不要直接拿普通探头夹一根线对地。MIPI HS 模式下的差动摆幅大约 200mV,如果你的探头地线太长,量出来的波形全是振铃,会误判为信号质量问题。测量点尽量选靠近 SoC 接收端。如果接收端波形幅度明显小于发送端,就有可能是走线阻抗不连续或 FPC 过长。
还有一个小技巧:看 sensor 驱动成功后是否设置了 MIPI 速率。clock-frequency在 sensor 驱动里通常由link-frequencies或clock-frequency属性决定,如果设得过低,图像会显得很糊;但如果设得过高,刚好超过 D-PHY 接收能力,画面会出现随机花屏,而不是完全不亮。这类问题靠示波器一眼就能看出来。
6.2 LVDS bridge 不工作的排查链路
LVDS bridge 点不亮屏幕时,不要直接怀疑屏幕。按下面这条链路逐级排查:
- 检查 bridge 供电:1.2V、1.8V、3.3V 是否都正常。
- 检查 I2C:用
i2cdetect -y <bus>看对应地址是否存在。 - 检查复位和使能 GPIO 电平:先看 dts 里的 active level 与原理图是否一致,再用万用表量。
- 检查 DSI 输入时钟:在 DSI 输出端量时钟是否翻转。
- 检查 LVDS 输出时钟和 data:如果有屏参测试信号,可以在 bridge 输出端量是否有 LVDS 时钟。
这个排查链路里,I2C 不通是最常见的。bridge 上电时间不够,I2C 访问就会失败。很多模块在 Linux 驱动加载时,bridge 刚上电几十毫秒,驱动就迫不及待去读寄存器,结果设备没准备好。解决方法是给 bridge 驱动加msleep(100)之类的初始化延时,或者用 regulator 的ramp-delay控制上电斜率。我在实际项目里见过某个 bridge 必须等 120ms 才能访问,Sdk 默认 50ms,导致 10 块板子里总有 2-3 块点不亮,就是时序边界的典型。
6.3 竖屏改横屏:改 dts 还是改驱动
很多人刚接触 DRM 显示时,以为把panel-timing里的hactive和vactive对调一下,就能把竖屏变横屏。这样做的结果通常是花屏或者黑屏,因为屏幕硬件就是竖屏,横竖只是逻辑方向问题,改 timing 并不会让像素扫描方向改变。
正确做法要分清场景:
- 如果只是为了 UI 横屏,可以在应用层旋转:Linux 下用
xrandr --rotate,Weston 桌面用output配置旋转。这种方式最快,但开机 logo 和内核阶段仍然是竖屏。 - 如果需要从 boot logo 开始就是横屏,需要在 DRM/panel 驱动里配置 panel 的 orientation 属性,让 DRM core 在 mode_set 时对帧缓冲做旋转。设备树里可以加
rotation = <90>或者rotation = <270>,具体属性名以内核版本为准。 - 如果是 LVDS bridge 负责扫描方向,那还要看 bridge 是否支持 output side mapping 翻转。很多 LVDS 转接芯片可以通过寄存器切换 LVDS 通道映射,但这是一条比较隐蔽的路,我建议优先走 DRM 旋转。
竖屏改横屏还容易忽略触摸屏坐标校准:就算图像横过来了,触摸坐标不跟着转,点击位置会完全错位。触摸驱动如果支持上报分辨率跟随显示旋转,就在驱动里同步改;如果不支持,app 层做坐标映射。
6.4 别忘了给 ISP 预留内存和 MMU
ISP 输出帧的 DMA buffer 需要足够大的内存。RV1106 这类设备通常通过 CMA 或 ion 分配连续物理内存。如果你在 dts 里给 ISP 改了status = "okay"但 reserved-memory 不够,拉流时会看到alloc buffer failed或fail to get vb2 queue buffer。
检查方法很简单:看cat /proc/vmallocinfo | grep rkisp、cat /proc/meminfo | grep Cma。RK ISP 驱动一般还依赖 MMU 模块,dtsi 里通常有isp_mmu或rkisp_mmu节点,也要保持status = "okay"。MMU 节点被关掉的典型现象是 probe 正常但 stream 时 page fault,dmesg 里出现类似IOMMU mapping error的信息。
另外,如果板子内存非常吃紧,可以把 ISP 输出分辨率降低,或者使用 NV12 半平面格式而不是 NV16/RGB888,能显著节省带宽和内存。对于很多监控类应用,1080P NV12 已经足够。
最后分享一个小习惯:每次拿到一块新 RV1106 板子,我都会先跑一遍 SDK 自带的默认 sensor demo,确认原始通路能出图,再开始改自己的设备树。这样可以把"硬件问题"和"配置问题"彻底隔离。第一次做 RV1106 ISP 适配时,我也曾在排线和寄存器之间来回折腾,后来发现很多问题都藏在最基本的时序和电平定义里。先用示波器把 MCLK、复位、I2C 波形全部测一遍,再用media-ctl、v4l2-ctl一步步验证链路,比反复改 dts 再编译烧写要节省太多时间。RV1106 的 ISP 调优门道不少,但路径是清晰的,按着硬件、设备树、驱动、tuning 的顺序走,基本不会翻车。