
1. 屏幕点亮这件事到底在折腾什么一块屏幕从“黑砖”到“出画面”中间隔着的不是一根线而是一整套显示子系统的协同工作。很多刚接触嵌入式显示驱动的朋友拿到一块新屏第一反应是“接上线不就行了”结果发现背光亮了但没图像或者图像花了、闪了、偏色了甚至内核直接崩了。这时候才意识到点亮屏幕远不是插拔那么简单。这篇内容围绕Panel 驱动移植展开核心是讲清楚一块 MIPI-DSI 屏幕从硬件上电到 DRM 框架出图的完整链路。涉及的关键词包括Panel驱动、DTS、DRM、MIPI-DSI也会结合 t113i 这类平台的实际场景聊聊 uboot 2018 环境下 DTS 的写法差异以及跨 DRM 录制时容易踩的坑。适合正在做显示驱动移植的嵌入式工程师、刚接手点亮任务的驱动新手以及想理清 DRM 显示通路的系统开发者参考。我不会只丢一堆代码让你抄而是把“为什么这么配”“这个参数从哪来”“不这么写会怎样”讲透。因为屏幕点不亮十有八九不是代码写错了而是某个时序参数、某个电源域、某个 DTS 节点没对上。把这些逻辑理顺换一块屏你也能自己推出来。2. 显示通路的整体设计与选型思路2.1 从 SoC 到屏幕信号到底走了哪条路一块 MIPI-DSI 屏的点亮本质上是 SoC 内部的显示控制器把图像数据打包成 DSI 协议格式通过 D-PHY 物理层发出去经过排线到达屏幕模组的驱动 IC驱动 IC 再把它转换成像素阵列能识别的行列驱动信号。这条链路里任何一个环节配置不对屏幕都不会出图。以 t113i 这类平台为例典型的通路是DRM 框架的 CRTC 负责扫描时序生成Encoder 把像素流编码成 DSI 信号Connector 负责和 Panel 对接最后通过 DSI Host 控制器发出。Panel 驱动在这一层的作用是告诉系统“我这块屏需要什么样的时序、什么格式的数据、怎么上电”。所以 Panel 驱动不是孤立的它是 DRM 框架里 Connector 和 Panel 之间的桥梁。为什么现在都用 DRM 而不是老的 FBDEV因为 DRM 对多图层、多显示通路、原子提交的支持更完整尤其是跨屏录制、多屏异显这类场景FBDEV 基本玩不转。跨 DRM 录制这个热词背后其实就是要在 DRM 框架下把显示通路的帧缓冲抓出来这要求你对 CRTC、Plane、Encoder 的关系非常清楚否则抓到的可能是空帧或者格式不对的数据。2.2 Panel 驱动为什么单独拆出来早期很多方案把屏幕初始化序列直接写在 DSI 控制器驱动里换一块屏就得改控制器代码维护起来很痛苦。后来 Linux 内核把 Panel 抽象成独立驱动通过drm_panel结构体和 DSI 控制器解耦。这样换屏只需要换 Panel 驱动和对应的 DTS 配置控制器驱动不用动。这个设计的好处很直接同一颗 SoC 可以配不同厂商、不同分辨率的屏只要 Panel 驱动实现了prepare、enable、disable、unprepare这几个回调DSI 控制器就能通过标准接口调用它。坏处是如果 Panel 驱动写得不对或者 DTS 里的时序和实际屏不匹配问题会变得很隐蔽因为控制器那边看起来“一切正常”但屏就是不亮。所以移植 Panel 驱动的第一步不是急着写代码而是先把这块屏的规格书翻出来把时序参数、上电时序、初始化序列这三样东西搞清楚。规格书里通常会有 Timing Table里面包含 HFP、HBP、HSA、VFP、VBP、VSA、像素时钟这些关键值。这些值不是随便填的填错了轻则花屏重则直接黑屏。2.3 DTS 在点亮流程里的角色DTS 在显示驱动里承担的是“硬件描述”的职责。它告诉内核这块屏接在哪个 DSI 控制器上、复位脚是哪个 GPIO、电源由哪路 regulator 供、背光怎么控制、时序参数是多少。内核启动时解析 DTS把这些信息注册成对应的设备Panel 驱动 probe 的时候就能拿到这些资源。uboot 2018 环境下的 DTS 和内核 DTS 有一些差异这个后面会细说。这里先建立一个概念DTS 不是“配置文件”它是硬件拓扑的描述。你写的每一个节点、每一个属性都应该能在原理图和规格书里找到对应。如果某个属性你不知道为什么要写那大概率是抄来的抄来的东西在换平台时最容易出问题。3. 核心细节解析与实操要点3.1 时序参数怎么从规格书里抠出来拿到一块屏的规格书翻到 Timing 那一页你会看到一堆缩写。别慌逐个拆解HSAHorizontal Sync Active行同步有效宽度单位是像素时钟周期。HBPHorizontal Back Porch行同步之后到有效数据之前的空闲区。HFPHorizontal Front Porch一行有效数据结束到下一个行同步之前的空闲区。VSA、VBP、VFP对应的场同步参数单位是行。Pixel Clock像素时钟频率决定刷新率。这些参数在 DRM 里对应的是struct drm_display_mode里的hsync_start、hsync_end、htotal、vsync_start、vsync_end、vtotal。换算关系是hsync_start hactive hfphsync_end hsync_start hsahtotal hsync_end hbpvsync_start vactive vfpvsync_end vsync_start vsavtotal vsync_end vbp规格书里有时给的是 HFP/HBP/HSA 的绝对值有时给的是 total 值你得自己反推。我踩过的坑是某块屏规格书写的是 HBP40但实际应该是 HBP40 加上 HSA 的一部分导致 htotal 算错屏幕右边出现一条黑边。后来用示波器抓 DSI 时钟才定位到。所以算完参数后最好用htotal * vtotal * 刷新率反算像素时钟和规格书标称值对比偏差超过 5% 就要重新检查。3.2 上电时序和 GPIO 控制屏幕的上电不是“给电就行”规格书里通常有一个 Power On Sequence 表格规定了 VDD、VDDIO、Reset、MIPI 信号之间的先后顺序和延时。比如常见的是先给 VDDIIO 电源上电。延时 10ms。给 VDD核心电源上电。延时 10ms。拉低 Reset保持 10ms。拉高 Reset延时 120ms。发送初始化序列。开始送图像数据。这些延时不是随便写的是驱动 IC 内部状态机需要的稳定时间。少一个延时可能十次有九次能亮但偶尔黑屏这种间歇性问题最难查。在 DTS 里这些通常通过panel-init-sequence或者驱动代码里的msleep来实现。如果平台支持尽量用 regulator 和 gpio 子系统来管理不要直接操作寄存器否则电源管理会乱。3.3 初始化序列的写法MIPI-DSI 屏的初始化序列是一串 DSI 短包或长包用来配置驱动 IC 的内部寄存器。规格书里一般会给一个 Initial Code 表格式类似0x39, 0x03, 0x00, 0x00, 0x00, 0xB9, 0xFF, 0x83, 0x94这串数据的含义是数据类型 0x39长包写延时 0x03长度 0x0000后面跟寄存器地址和数据。不同平台对初始化序列的解析方式不一样有的用panel-init-sequence属性有的在驱动里硬编码。我建议尽量放在 DTS 里这样换屏不用重新编译驱动。写初始化序列时要注意有些寄存器需要连续写多个值有些需要读回来校验。如果屏幕不亮可以先只发最基础的几个寄存器确认 DSI 通信正常再逐步加。另外初始化序列里的延时单位通常是毫秒但有些平台是微秒这个要对照文档确认。4. 实操过程与核心环节实现4.1 DTS 节点的完整写法下面是一个典型的 Panel DTS 节点结构基于 t113i 这类平台的常见实践dsi0 { status okay; panel0 { compatible vendor,panel-xxx; reg 0; reset-gpios pio 3 15 GPIO_ACTIVE_LOW; power-supply reg_lcd; backlight backlight; panel-init-sequence [ 39 00 04 B9 FF 83 94 39 00 02 BA 03 ... ]; display-timings { native-mode timing0; timing0: timing0 { clock-frequency 68000000; hactive 800; vactive 1280; hfront-porch 40; hback-porch 40; hsync-len 10; vfront-porch 20; vback-porch 20; vsync-len 4; }; }; }; };这里有几个关键点compatible要和 Panel 驱动里的of_device_id匹配否则 probe 不会执行reset-gpios的极性要和硬件一致写反了屏幕一直处于复位状态power-supply要指向正确的 regulator如果 regulator 没使能DSI 控制器会报 timeout。在 uboot 2018 下DTS 的解析方式和内核略有不同。uboot 的 DTS 主要用于初始化显示控制器和简单的 Panel 参数不支持完整的 DRM 框架。所以如果你在 uboot 阶段就要点亮屏幕DTS 里可能只需要配置 DSI 控制器和基本的时序初始化序列可能要在 uboot 的显示驱动里单独处理。这一点在移植时容易混淆建议先在内核里点亮的再考虑 uboot。4.2 驱动 probe 流程和调试手段Panel 驱动 probe 的时候会依次做这几件事从 DTS 里读取 GPIO、regulator、时序参数。申请 GPIO 和 regulator。注册drm_panel。等待 DSI 控制器调用prepare和enable。如果 probe 失败先看dmesg里有没有failed to get reset gpio或者regulator enable failed。如果 probe 成功但屏幕不亮就要看 DSI 控制器有没有发出信号。常用的调试手段有cat /sys/kernel/debug/dri/0/summary查看 CRTC、Encoder、Connector 的状态。cat /sys/kernel/debug/dri/0/DSI-1/status查看 DSI 链路状态。用示波器量 DSI 时钟和数据线确认有没有波形。如果平台支持读驱动 IC 的 ID 寄存器确认 DSI 通信正常。我遇到过一种情况probe 成功DSI 状态显示 connected但屏幕就是黑。后来发现是背光没使能。背光在 DRM 里是独立的子系统Panel 驱动只负责图像背光要单独配backlight节点。这个坑很典型因为背光不亮和图像不显示看起来都是“黑屏”但排查方向完全不同。4.3 跨 DRM 录制的实现思路跨 DRM 录制这个需求通常出现在需要把显示内容抓取出来做编码或者传输的场景。在 DRM 框架下帧缓冲是通过drm_framebuffer管理的你可以通过drmModeGetFB或者内核态的drm_framebuffer_lookup拿到。但要注意DRM 的帧缓冲可能是 GPU 渲染的结果格式可能是 ARGB8888 或者 NV12直接读出来的数据不一定是你想要的。实现上一般有两种路子一种是在应用层用 DRM 的 ioctl 抓取 CRTC 的当前帧另一种是在内核态通过drm_crtc的primaryplane 拿到 framebuffer。前者简单但性能一般后者效率高但需要改内核。如果要做跨屏录制还要考虑多个 CRTC 的同步问题否则录出来的画面可能不同步。这里有个细节DRM 的原子提交模式下帧缓冲的切换是异步的你抓到的可能是上一帧或者正在渲染的帧。解决办法是监听page_flip事件在 flip 完成后再抓。这个在跨 DRM 录制里很关键不然录出来的视频会有撕裂。5. 常见问题与排查技巧实录5.1 屏幕不亮的排查顺序屏幕不亮是最常见的问题排查要有顺序不要东一榔头西一棒子。我通常按这个顺序来电源量 VDD、VDDIO 有没有电电压对不对。复位量 Reset 脚电平确认上电后有没有拉高。背光背光有没有亮如果背光亮了但没图像说明电源和复位基本正常问题在 DSI 信号或初始化序列。DSI 信号用示波器量时钟和数据线有没有波形频率对不对。初始化序列如果 DSI 有波形但屏幕不亮大概率是初始化序列不对或者时序参数不匹配。DTS 配置检查 compatible、GPIO 极性、regulator 有没有配错。这个顺序的逻辑是从硬件到软件从简单到复杂。电源和复位是最容易量的先排除。DSI 信号需要示波器但能快速判断控制器有没有工作。初始化序列和时序参数是最容易出错的放在后面查。5.2 花屏、闪屏、偏色的原因花屏通常是时序参数不对尤其是 htotal 和 vtotal 算错导致数据错位。闪屏可能是背光 PWM 频率和刷新率冲突或者电源不稳。偏色一般是数据格式不对比如 RGB 顺序反了或者 DSI 的 lane 映射错了。我遇到过一次偏色红色和蓝色对调查了半天发现是 DSI 数据包的 RGB 顺序配置错了。在 DTS 里有一个dsi,format或者类似的属性用来指定像素格式。如果规格书要求 RGB888你配成了 BGR888颜色就会不对。这个细节在移植时很容易忽略因为屏幕能亮只是颜色不对看起来不像大问题。5.3 uboot 2018 DTS 的坑uboot 2018 的 DTS 解析和内核有差异主要体现在uboot 不支持display-timings里的所有属性有些要手动解析。uboot 的 GPIO 和 regulator 框架比较简单可能不支持GPIO_ACTIVE_LOW这种极性描述。uboot 的 DSI 驱动可能不完整初始化序列要在代码里写死。所以如果你在 uboot 阶段点亮屏幕不要直接抄内核的 DTS要先看 uboot 的显示驱动支持哪些属性。我一般建议先在 uboot 里只做最基本的初始化把屏幕点亮复杂的时序和初始化序列放到内核里做。这样分工明确排查也容易。5.4 常见问题速查表现象可能原因排查方法完全黑屏背光不亮电源未上电、regulator 未使能量 VDD/VDDIO 电压检查 DTS power-supply背光亮但无图像DSI 信号未发出、初始化序列错误示波器量 DSI 时钟检查初始化序列花屏时序参数错误、htotal/vtotal 算错重新计算时序对比规格书闪屏背光 PWM 频率冲突、电源不稳调整 PWM 频率检查电源纹波偏色像素格式错误、lane 映射错检查 DSI format 配置probe 失败compatible 不匹配、GPIO 申请失败看 dmesg检查 DTS 节点跨 DRM 录制撕裂未监听 page_flip在 flip 完成后抓帧这个表是我在实际项目中慢慢攒出来的每次遇到新问题就加一行。时间长了你会发现大部分问题都逃不出这几类关键是排查顺序要对。6. 一些实操心得和避坑建议移植 Panel 驱动这件事说难不难说简单也不简单。核心就三样时序、电源、初始化序列。但每块屏的规格书写法不一样有的参数藏在表格里有的要自己算有的甚至写错了。我现在的习惯是拿到规格书先自己算一遍时序然后用modetest或者平台自带的测试工具验证确认参数对了再写驱动。另外DTS 里的每一个属性都要有依据不要抄。抄来的配置在换平台时就是定时炸弹。尤其是 GPIO 极性和 regulator 电压抄错了轻则不亮重则烧屏。我见过有人把 1.8V 的 IO 电源配成 3.3V结果屏幕直接报废。最后说一个调试技巧如果屏幕不亮先别急着改代码用gpio命令或者regulator的 sysfs 接口手动把电源和复位拉一遍确认硬件没问题。硬件没问题再查软件这样能省很多时间。屏幕点亮这件事耐心比技术更重要因为很多时候问题就藏在一个你没注意到的延时或者极性里。