泰山派RK3566 Linux点亮MIPI OLED:从设备树到DRM驱动全解析 📅 发布时间:2026/9/4 3:59:33 👁 浏览次数: 如果你以为点亮一块 0.23 寸的小屏只是“把 MIPI 差分线对上、电源接好”那大概率会在泰山派上白屏很久。这块屏本身没有 MCU 帮你解析画面它是靠主控端 DSI 控制器把像素和寄存器命令“喂”进去的。真正难的不是 MIPI 协议本身而是怎么让 RK3566 的 Linux 显示子系统认识这块屏幕。只要驱动链路里有一环没打通屏幕就可能一直白着甚至系统启动到一半就因为 panel 驱动初始化失败而卡住。这篇文章会从协议、硬件、设备树、DRM panel 驱动到验证命令把整条链路拆开来讲帮你快速在泰山派上点亮一块国产 0.23 寸 MIPI OLED。很多人搜“MIPI OLED 驱动”会误以为是 STM32 那种“拉高 GPIO、SPI 写寄存器”的事。实际上到了泰山派这类Linux 平台它变成了一门涉及VOP 视频输出、MIPI DSI 控制器、DRM/KMS 面板驱动、设备树链路匹配的系统工程。下面我们进入正题。1. 为什么 0.23 寸的屏反而难点亮1.1 它和你平时用的 SSD1306 OLED 不一样如果你只玩过 0.96 寸的 I2C/SPI OLED会自然而然地认为 OLED 都是几根信号线、一个驱动库、直接写显存就行。但 0.23 寸 MIPI OLED 完全是另一个物种。这类小尺寸 OLED 模组在很多 AR/FPV/电子取景器方案里很常见屏幕内部是一个高密度像素阵列通常带有支持 MIPI DSI 输入的专用驱动 IC 或桥接芯片。主控需要通过 MIPI 接口发送初始化序列告诉驱动 IC 分辨率、扫描方向、内部电源时序、显示窗口等参数。初始化完成后还需要持续或者按帧发送 RGB 像素数据。如果把屏幕比作一个舞台MIPI 只是把“演员”送到舞台门口的通道。真正的“导演”是驱动 IC 里的寄存器配置。没有初始化序列你往 MIPI 总线上扔再多像素屏幕上也是白花花一片。1.2 Linux 下点亮屏幕的最小链路泰山派是基于 RK3566 方案的单板电脑。RK3566 自带 VOP、MIPI DSI、eDP、RGB/LVDS 等显示输出资源。要在 Linux 下把面板点亮至少需要经历应用程序/测试程序 ↓ 提交 Framebuffer Rockchip DRM VOP 层 ↓ 视频层合成 VOP 输出到 MIPI DSI Host ↓ D-PHY 差分信号 外部 OLED Panel这里的每一层都需要在设备树里告诉内核“谁和谁连接”否则 Linux DRM 子系统不知道你有一块屏。一个很容易犯的错是你觉得屏幕已经接好了然后去查/dev/fb0结果什么都没有。这是因为现代内核已经不认为屏幕是一块“裸的显存设备”而是把它交给 DRM/KMS 统一管理。屏幕在 KMS 里注册成功后才会出现/dev/dri/card0、连接器节点以及对应的fb设备。所以这篇文章的观点很明确泰山派点亮 0.23 寸 OLED本质是在 RK3566 Linux 平台上完成一次 DRM Panel 注册。2. MIPI 并不神秘但别把 DSI 和 CSI 搞混2.1 DSI 与 CSI-2 是两种方向搜索 MIPI 相关问题的时候你会发现大量内容混在一起有人问 MIPI 摄像头采集有人问 MIPI 屏幕点亮。它们都属于 MIPI 协议族但链路方向完全不同。术语全称作用对象数据方向典型应用MIPI DSIDisplay Serial Interface显示器主控 → 屏幕LCD/OLED 显示面板MIPI CSI-2Camera Serial Interface 2摄像头摄像头 → 主控图像采集0.23 寸 OLED 使用的是MIPI DSI数据方向是从泰山派到屏幕。而部分资料里提到的mipi csi、mipi csi-2是另一条通道通常用来接摄像头传感器。你要是把 CSI 的驱动框架套到屏幕上调试方向和逻辑都不对。2.2 DSI 物理层长什么样MIPI DSI 物理层采用差分信号每组 Lane 由一对线组成通常写作D0/D0-、D1/D1-、CLK/CLK-。在泰山派这类主控上常见 MIPI DSI 是 2 Lane 或者 4 Lane。0.23 寸 OLED 分辨率不高像素时钟很低所以 Lane 数量不一定是瓶颈。真正要先确认的是屏幕驱动 IC 要求的是2 Lane 还是 4 Lane接反了或设备树里配置错了都会不亮。除了数据 Lane屏幕通常还有几个控制信号RESET复位脚一般低有效。PWR_EN/VDD_EN面板电源控制。部分模组还带TE引脚用于命令模式下的撕裂效果同步。如果面板没有内置背光就还有背光控制OLED 自发光但部分模组内部为延长寿命会加保护功能。很多人在飞线调试时只关注 MIPI 的差分线却忘记给屏幕复位、忘记供电时序所以屏幕当然不会工作。MIPI DSI 还分为两种工作模式命令模式Command Mode主控把像素写入屏幕内部 RAM屏幕自行刷新。视频模式Video Mode主控持续向屏幕发送像素流类似 HDMI 的持续扫描。小尺寸 OLED 模组两种都可能用到具体要查屏的规格书和初始化代码。不要把 STM32 里操作带 RAM 屏的思维硬套进来某些屏幕就是在 Video Mode 下工作主控停发数据屏幕就会慢慢花屏或者白屏。2.3 为什么“通上电”不等于“屏幕亮了”有朋友搜索时问“OLED 屏连上电源就亮吗”。这个问题看似基础但很关键。不同 OLED 差异很大如果是一个已经内置驱动 IC 的成品模组上电后可能有默认扫描但通常显示的是空白或者随机点。如果是一个只给了 MIPI 输入接口的集成模组那必须由主控完成 DSI 初始化和显示使能。所以不要用“LCD 背光亮屏幕点亮”的惯性去判断。OLED 自发光没有背光的概念屏幕点亮的标志是显示出了预期图案而不是看到白底。3. 点亮泰山派 OLED 前需要准备的资料许多项目卡在硬件阶段不是不会写代码而是没找齐屏幕资料。拿到一块 0.23 寸国产 OLED 后建议先核对以下信息再去动内核。3.1 硬件接线图查清楚模组的引脚顺序。MIPI 差分对顺序接错是致命的轻则白屏重则烧坏驱动 IC。还要确认插座哪个是 1 脚哪个方向是正面。MIPI Lane 的极性是否在转接板上被交换过。复位脚是否需要板级外部上拉。面板电源电压是多少是 1.8V/3.3V 还是一组多电压。3.2 屏幕规格书Datasheet重点看这几项分辨率例如 320x240、640x400。像素格式RGB565、RGB666、RGB888。Lane 数1/2/4 Lane。刷新率范围。显示时序表Hactive、HFP、HBP、HSYNC、Vactive、VFP、VBP、VSYNC。这些参数会直接写进 DRMdrm_display_mode结构体里。任何一个不对画面上可能出现条纹、偏移、闪烁甚至完全白屏。3.3 厂商初始化序列这是所有资料里最核心的。很多国产屏厂商会提供一段寄存器初始化数组有些用文档有些给一份适配某一平台的内核补丁。你需要把它翻译成 Linuxmipi_dsi_dcs_write_buffer()能发送的字节数组。这里提醒一句不要自作聪明精简初始化序列。驱动 IC 的初始化顺序往往有严格依赖比如先开内部 LDO再配置扫描参数最后 Sleep Out。顺序错了后面发的命令可能不生效。4. 泰山派 Linux 驱动链路设备树、VOP 与 DSI Host4.1 先找到你的 DSI 节点RK3566 方案一般有多个显示输出控制器。泰山派板卡在设计时可能把某个 MIPI DSI Host 引到了排针或者 MIPI 连接器上。在设备树里你需要先确认使用的是dsi0还是dsi1。怎么判断看芯片原理图看屏幕信号接到了哪一组 DSI。看 SDK 默认设备树很多公版直接放开了dsi0板厂通常会修改这两个节点。查看内核启动日志启动时可能打印rockchip-mipi-dsi相关 probe 信息。不要凭感觉。配置错了dsi1代码写再多也是白发。4.2 设备树里的显示路由RK 平台设备树通常会有vp0、vp1等视频端口它们可以路由到不同的显示控制器。一个典型的路由过程是VOP0 VP0 ↓ dsi0_in_vp0 ↓ MIPI DSI Host (dsi0) ↓ dsi0_out_panel ↓ panel 节点如果只有 panel 节点而没有把remote-endpoint连接起来DRM 子系统可能根本没有检测到屏幕。4.3 Panel 节点挂在 DSI 下Panel 节点需要作为dsi0的子节点存在。compatible 要和驱动匹配。举个例子/* * 文件arch/arm64/boot/dts/rockchip/rk3566-taishan-pai.dts * 示例节点GPIO 编号请以底板原理图为准 */ dsi0 { status okay; panel0 { compatible example,0p23-mipi-oled; reg 0; reset-gpios gpio4 RK_PA2 GPIO_ACTIVE_LOW; enable-gpios gpio4 RK_PA3 GPIO_ACTIVE_HIGH; backlight backlight0; port { panel_in_dsi: endpoint { remote-endpoint dsi0_out_panel; }; }; }; };要让 VP 和 DSI 连接起来还需要在设备树的另一处配置dsi0_in_vp0 { remote-endpoint vp0_out_dsi0; };这段代码的语义是VP0 的输出连接到 DSI0 的输入。这个连接如果缺失系统探测不到 panel自然无法点亮。不同 SDK 的写法会有细微差异。有一些版本需要在video_phy或者 dsi 节点上额外配置phy、power-domains。如果编译 dts 时提示找不到某些属性说明你的 SDK 版本与这段示例不完全一致请以 SDK 里的rk356x.dtsi为准。5. 写一个 Linux DRM Panel 驱动如果没有现成内核驱动能匹配你的屏幕就需要自己写一个 panel driver。我建议不要一上来就写完整驱动先用简化的模块验证链路再逐步完善。5.1 Panel 驱动的典型结构Linux 中 DRM Panel 驱动的核心是struct drm_panel_funcs。它会在合适时机被 DSI Host 驱动调用。/* * 文件drivers/gpu/drm/panel/panel-example-oled.c * 这是一个教学化简版驱动骨架具体命令以屏幕规格书为准 */ #include linux/module.h #include linux/delay.h #include linux/of_graph.h #include linux/gpio/consumer.h #include drm/drm_mipi_dsi.h #include drm/drm_panel.h #include drm/drm_modes.h struct example_oled { struct device *dev; struct drm_panel panel; struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; struct gpio_desc *enable_gpio; }; static inline struct example_oled *to_example_oled(struct drm_panel *panel) { return container_of(panel, struct example_oled, panel); } static const struct drm_display_mode example_oled_mode { .clock 24000, /* kHz不是 Hz */ .hdisplay 320, .hsync_start 320 80, .hsync_end 320 80 4, .htotal 320 80 4 60, .vdisplay 240, .vsync_start 240 20, .vsync_end 240 20 2, .vtotal 240 20 2 10, .flags DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, };这段代码里所有时序数字都是占位符一定要用规格书里的真实时序替换。clock的单位是 kHz很多新手把 Hz 值直接填进去导致像素时钟差一千倍屏直接花掉。5.2 初始化命令发送将厂商初始化序列按屏幕内部地址页分段发送比一股脑发一整串数组更可靠。下面是一个示意static const u8 init_seq_page1[] { 0xFF, 0xAA, 0x55, 0x00, 0x00, 0x01, /* ... 厂商初始化序列 ... */ }; static const u8 init_seq_page2[] { 0x00, 0x10, 0x10, 0x20, /* ... 厂商初始化序列 ... */ };驱动里在prepare回调中执行面板上电、复位、发送初始化序列static int example_oled_prepare(struct drm_panel *panel) { struct example_oled *ctx to_example_oled(panel); if (ctx-enable_gpio) { gpiod_set_value_cansleep(ctx-enable_gpio, 1); msleep(20); } if (ctx-reset_gpio) { gpiod_set_value_cansleep(ctx-reset_gpio, 0); msleep(10); gpiod_set_value_cansleep(ctx-reset_gpio, 1); msleep(30); } mipi_dsi_dcs_write_buffer(ctx-dsi, init_seq_page1, ARRAY_SIZE(init_seq_page1)); msleep(5); mipi_dsi_dcs_write_buffer(ctx-dsi, init_seq_page2, ARRAY_SIZE(init_seq_page2)); msleep(20); return 0; } static int example_oled_enable(struct drm_panel *panel) { struct example_oled *ctx to_example_oled(panel); /* 关闭 Sleep 模式 */ mipi_dsi_dcs_exit_sleep_mode(ctx-dsi); msleep(120); /* 打开显示 */ mipi_dsi_dcs_set_display_on(ctx-dsi); return 0; }不要把Exit Sleep Mode和Set Display On放在同一个函数里猛发命令而不延时。部分驱动 IC 在 Sleep Out 之后需要等待内部时钟稳定通常要几十到上百毫秒。时序不符合要求最常见的结果就是屏幕亮起来但画面异常或者过一段时间自动熄灭。5.3 向 DRM 上报显示模式prepare和enable负责“让屏工作”但 DRM 还必须知道屏幕分辨率和时序。这部分在get_modes回调中完成。static int example_oled_get_modes(struct drm_panel *panel, struct drm_connector *connector) { struct drm_display_mode *mode; mode drm_mode_duplicate(connector-dev, example_oled_mode); if (!mode) return -ENOMEM; drm_mode_set_name(mode); drm_mode_probed_add(connector, mode); connector-display_info.width_mm 0; connector-display_info.height_mm 0; return 1; }5.4 probe 函数与 DSI 参数绑定在 probe 中需要拿到 reset gpio、fill modes、设置lanes、format、mode_flags然后把驱动 attach 到 DSI 总线上。static int example_oled_dsi_probe(struct mipi_dsi_device *dsi) { struct example_oled *ctx; struct device *dev dsi-dev; int ret; ctx devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; ctx-dev dev; ctx-dsi dsi; ctx-reset_gpio devm_gpiod_get_optional(dev, reset, GPIOD_OUT_LOW); ctx-enable_gpio devm_gpiod_get_optional(dev, enable, GPIOD_OUT_LOW); dsi-lanes 2; dsi-format MIPI_DSI_FMT_RGB888; dsi-mode_flags MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO; drm_panel_init(ctx-panel, dev, example_oled_panel_funcs, DRM_MODE_CONNECTOR_DSI); ret drm_panel_of_backlight(ctx-panel); if (ret) return ret; drm_panel_add(ctx-panel); ctx-panel.dev dev; ret mipi_dsi_attach(dsi); if (ret) return ret; return 0; }mode_flags会直接决定 DSI Host 工作模式。这里示例写的是MIPI_DSI_MODE_VIDEO如果你的屏幕是命令模式Command Mode却把它配成 Video Mode像素流格式不匹配屏幕初始化可能成功但无法正常显示。MIPI_DSI_FMT_RGB888需要和屏幕实际像素格式一致否则颜色通道会和主控发送的像素排列错位。6. 编译落板与功能验证6.1 编译内核或模块假设你已经把驱动文件放到了drivers/gpu/drm/panel/panel-example-oled.c并在drivers/gpu/drm/panel/Makefile里加入了编译规则。例如obj-$(CONFIG_DRM_PANEL_EXAMPLE_OLED) panel-example-oled.o在 Kconfig 中加入config DRM_PANEL_EXAMPLE_OLED tristate Example 0.23 inch MIPI OLED panel depends on DRM_MIPI_DSI help Say Y here if you want to support example 0.23 MIPI OLED.然后在泰山派 SDK 内核目录下打开配置make ARCHarm64 menuconfig进入Device Drivers - Graphics support - DRM Support for MIPI DSI panels打开对应选项保存后编译make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- -j$(nproc) Image dtbs如果你的 SDK 使用 build.sh 总控也可以直接调用 SDK 的编译脚本。编译成功后将boot.img、dtb或整个系统镜像烧写到泰山派。6.2 开机日志检查启动后先看内核日志dmesg | grep -E panel|dsi|vop|mipi正常情况能看到类似结构rockchip-mipi-dsi-dphy ... panel-example-oled: probe ok drm_panel_attach ...如果设备树里 compatible 没有匹配上日志中可能只有 DSI Host 初始化成功而 panel 完全没有出现。6.3 查看 DRM 设备节点执行ls /sys/class/drm/正常情况下会出现类似card0-DSI-1如果没有这个节点说明 DRM 连接器没有识别到 panel。这时不要再怀疑屏幕硬件先回头查设备树 link 和 panel driver 是否注册成功。6.4 用 modetest 手动点亮测试在泰山派系统里可以用 modetest 输出连接器信息modetest -M rockchip -p找到 DSI 连接器和可用的 mode。例如连接器 ID 是 122分辨率为 320x240那么手动测试命令可以写成modetest -M rockchip -s 122:320x240RG24这里的122是连接器编号不是固定值。每次启动可能变化必须以上一步modetest -p的输出结果为准。如果这一步能显示彩色测试画面说明从内核 DRM 到 MIPI DSI、再到屏幕的像素链路已经打通。后面需要做的是移植到更上层的显示应用或者配置启动 logo。7. 常见问题与排查思路问题现象可能原因排查方式解决方案开机白屏设备树链路没匹配panel 驱动未加载查看dmesg、/sys/class/drm/核对 compatible、remote-endpoint白屏但 dmesg 显示 panel probe 成功初始化序列未发送或发送失败检查 DSI 命令发送返回值确认命令格式、延时、I2C/GPIO 控制有背光但无图像未发Display On或屏幕处于 Sleep 状态确认 enable 回调是否调用在 enable 中加 Sleep Out 后延时花屏、条纹像素时钟或时序参数不对对照规格书检查 drm_display_mode修正 clock、HFP/HBP 等颜色不对RGB 格式配置不一致检查MIPI_DSI_FMT_RGB666/RGB888改为屏幕实际像素格式频繁闪烁供电不足或同步信号异常检查电源纹波、接线长度增强供电缩短差分线Ubuntu/桌面启动后屏不亮控制台未让 DRM 输出到默认连接器modetest -p查看连接器使用 modetest 或 Weston 配置输出启动后系统卡住或反复重启panel 初始化阻塞或 GPIO 冲突查看启动日志卡死位置检查复位 GPIO、延时是否过长识板时提示“rk3566 adb device”但屏不亮内核可能启动异常或未进入显示阶段先接串口看是否到用户态先跑通最小系统再调显示这里特别说一下“识别到 RK3566 但是是 adb 设备”的情况。这个现象其实不是屏幕驱动单独的问题而是整个系统可能卡在内核启动早期。你可以通过串口观察启动最后一条日志看看是卡在 DSI panel 初始化还是已经进入了用户态只是显示服务没有正常工作。先定位系统到了哪一步再决定是查内核还是查应用层。还有一种常见现象是“开机白屏几秒后有画面”。这通常是因为 panelprepare耗时很长内核显示 logo 时 panel 还没准备好。解决办法不是简单延后 logo而是让 logo 驱动等待 panel ready或者接受这种短暂白屏。对于消费级产品白屏的时长需要重点优化复位时序。8. 最佳实践与工程建议做泰山派 MIPI OLED 驱动真正成熟的项目往往不是功能跑通就结束而是要工程化落地。8.1 寄存器配置一定要版本化屏幕厂商修改初始化序列很常见。建议把初始化代码单独放到一个头文件或者设备树属性里不要攒在主驱动文件里。方便后续拿到新版本固件时快速替换也方便对比差异。8.2 控制 GPIO 命名的可读性设备树里使用 GPIO 时不要只写gpio4 RK_PA2。建议再定义pinctrl并且加清晰注释。如果板子迭代不同版本 GPIO 可能改掉日志里显示 GPIO 字符串比裸地址好排查得多。8.3 不在/dev/fb上钻牛角尖现代 DRM 平台中/dev/fb0可能不存在也可能默认没有映射到 DSI 屏。调屏优先用modetest、drm_info这组工具。它们能直接看见连接器、编码器、CRTC 的状态。/dev/fb只是 KMS 之上的兼容层别被它误导。8.4 热插拔与静电风险MIPI DSI 是高速差分接口支持热插拔的能力非常有限。调试过程中尽量不要带电插拔软排线也不要反复触摸差分引脚。一两次热插拔可能没坏长期下来很容易把某颗 PHY 或者驱动 IC 打坏。如果需要频繁测试优先用带 ESD 保护的转接板并在软件层设计好“断开检测”或者至少不让内核在信号不稳时反复 reset。8.5 命令模式与 TE 同步如果屏幕工作在命令模式又需要低延迟刷新通常要接 TE 引脚并在设备树和驱动里启用 TE 等待机制。否则高速刷新时可能出现撕裂。0.23 寸 OLED 常用于 FPV 或近眼显示撕裂虽然不像大屏那么显眼但在快速运动画面里较明显会影响体验。8.6 优先跑最小验证再叠加功能不要第一次就把开机 logo、桌面合成器、应用层渲染全部绑在一起。建议按以下顺序推进确认设备树 panel 节点能被内核识别。用 modetest 点一个纯色测试画面。再启用显示服务或开机 logo。最后做上层 UI 适配。这个顺序能大幅减少“屏幕不亮但不知道是驱动问题还是应用问题”的情况。9. 总结点亮屏幕只是第一步写到这里你应该已经掌握一条完整的链路从理解 0.23 寸国产 MIPI OLED 的驱动特征到看懂 MIPI DSI 与 CSI-2 的区别再到在泰山派 RK3566 Linux 上配置设备树、编写 DRM panel 驱动、用 modetest 验证画面。真正困难的不在“某个 GPIO 拉高拉低”而在于你是否把屏幕当成一个需要通过 DRM/KMS 注册的显示设备来对待。只要设备树路由正确、panel 驱动初始化序列完整、mode 时序和像素格式匹配剩下的点亮工作往往就是几分钟的事。点亮之后接着要关心的是刷新稳定性、功耗、闪烁、以及在不同 Linux 显示框架下如何复用。你可以继续深入研究 RK 平台的 VOP 路由、DSI PHY 配置、背光 PWM 联动也可以去调试更上层的 Weston 或 Android HWComposer。这一套基础打扎实后再回头接其他 MIPI 屏会发现所有流程都是相似的先找规格书、再做设备树匹配、然后写 panel 驱动、最后验证时序。希望这份实操记录能帮你在泰山派上少走弯路。建议把这份驱动移植过程存档下来下次接到新面板时能快速对照排错。