4. 显示管线初始化实战

i.MX 平台的 DRM 显示子系统并不是一个单一驱动,而是由 imx-drm 主驱动、多个 IP 子驱动(ipu-v3、ldb、parallel-display、dw-hdmi 等)以及通用 DRM 框架共同拼装而成。理解这条管线如何从设备树匹配一路走到用户空间看到 /dev/fb0 或 /dev/dri/card0,是排查“屏幕不亮”“分辨率不对”“fb 节点缺失”等问题的前提。本节沿着内核启动的真实顺序,把 probe、component master 绑定、CRTC/encoder/connector 注册、fbdev 兼容层这几件事串成一条可跟踪的链路。

一、设备树先描述“谁和谁连在一起”

在 i.MX6Q 上,典型显示通路是 IPU 的 DI 输出 → LDB 桥 → LVDS 面板,或者 IPU DI → parallel-display → RGB 屏。设备树里用 ports/endpoint 和 remote-endpoint 把这些节点连成图。例如:

&ipu1_di0 {
    status = "okay";
    port {
        ipu1_di0_lvds: endpoint {
            remote-endpoint = &ldb_in;
        };
    };
};

&ldb {
    status = "okay";
    ports {
        port@0 { ldb_in: endpoint { remote-endpoint = &ipu1_di0_lvds; }; };
        port@1 { ldb_out: endpoint { remote-endpoint = &panel_in; }; };
    };
};

这些 endpoint 不只是描述连接,它们决定了 component 框架的匹配关系:每个子驱动在 probe 时把自己的 component 注册进全局列表,主驱动则根据设备树里的 ports 关系判断“谁依赖谁”。如果 endpoint 写错或漏写,后面就会出现“component master 一直不 complete”的经典现象。

二、probe 顺序与 component 绑定

内核启动时,各平台驱动按设备树匹配依次 probe。ipu-v3 驱动先注册 IPU 的 CRTC 相关资源,LDB、parallel-display、dw-hdmi 等 encoder/bridge 驱动随后注册自己的 component。每个子驱动调用 component_add(),把 struct component_ops 挂到全局 component 列表上。

imx-drm 主驱动在 probe 时调用 component_master_add_with_match(),并提供一个 match 函数。这个 match 函数会遍历设备树中主驱动节点下的所有 remote-endpoint,判断每个子 component 是否已经就绪。只有当所有被引用的 component 都注册完成后,component 框架才会回调主驱动的 bind()。这就是“master 绑定”的本质:不是主驱动主动去抓子驱动,而是框架在所有依赖到齐后统一触发。

调试技巧:如果屏幕不亮且 dmesg 里没有 imx-drm 的 bind 日志,优先检查 component master 是否 complete。常见原因是某个子驱动 probe 失败(如 LDB 时钟未使能、HDMI DDC 引脚冲突),导致 match 永远不满足。

三、CRTC、encoder、connector 的注册

主驱动 bind 回调里,imx-drm 会创建 DRM device,并依次注册显示对象。以 IPU + LDB + LVDS 面板为例:

  • CRTC:由 ipu-v3 提供,对应 IPU 的 DI 通道。它负责扫描时序生成、framebuffer 读取和 vblank 中断。
  • Encoder:LDB 作为 encoder,把 IPU 的并行 RGB 信号转换成 LVDS 差分信号。它实现 atomic_enable/disable 和 mode_set。
  • Connector:由 panel 或 bridge 提供,负责读取 EDID(HDMI 场景)或固定 mode(LVDS 场景),并报告连接状态。

注册完成后,DRM 框架会把这些对象挂到 drm_mode_config 上,并创建 /dev/dri/card0。此时用户空间可以通过 libdrm 或 modetest 枚举资源。一个典型的 modetest 输出会显示 CRTC、encoder、connector 的 ID 和可用 mode 列表。

四、fbdev 兼容层:为什么还有 /dev/fb0

很多老应用和 Qt/EGLFS 配置仍依赖 framebuffer 接口。imx-drm 通过 drm_fbdev_generic_setup() 或早期的 imx_drm_fbdev 创建 fbdev 兼容层。它把 DRM 的 primary plane 包装成一个 fb_info,并注册 /dev/fb0。当应用对 fb0 做 mmap 并写像素时,fbdev 层通过 drm_framebuffer 把数据映射到 GEM buffer,再由 CRTC 扫描输出。

需要注意的是,fbdev 兼容层通常只支持一个固定分辨率,且不参与 atomic modeset。如果同时使用 DRM/KMS 应用和 fbdev 应用,可能出现资源冲突。建议在最终产品中明确选择一种显示接口。

i.MX DRM 显示管线初始化链路 设备树层 ipu1_di0 ldb panel / bridge endpoint endpoint probe 与 component 注册 ipu-v3 probe component_add ldb probe component_add panel/bridge probe component_add imx-drm master component_master_add 所有 component 就绪 → master bind() drm_dev_alloc / drm_mode_config_init DRM 对象注册 CRTC (IPU DI) Encoder (LDB) Connector (panel) /dev/dri/card0 → modetest / libdrm /dev/fb0 → fbdev 兼容层

五、从 probe 到 modeset 的实操跟踪

实际调试时,可以按以下顺序观察内核日志和 sysfs 节点:

  1. 启动后执行 dmesg | grep -E "imx-drm|ipu|ldb|component",确认各子驱动 probe 成功,并看到 imx-drm display-subsystem: bound 之类的绑定日志。
  2. 检查 /sys/class/drm/ 下是否出现 card0-LVDS-1 等 connector 目录,status 文件应显示 connected。
  3. 用 modetest -M imx-drm 枚举资源,确认 CRTC、encoder、connector 的 ID 和可用 mode。
  4. 执行 modetest -M imx-drm -s <connector_id>@<crtc_id>:1024x600 触发 modeset,观察屏幕是否点亮。
  5. 若使用 fbdev,检查 /dev/fb0 是否存在,并用 fbset 查看当前分辨率。

一个常见的失败场景是:LDB 的 status 在设备树里是 disabled,导致 component 不注册,master 永远不 bind,最终 /dev/dri/card0 不出现。此时 dmesg 里通常只有各子驱动的 probe 日志,没有 imx-drm 的绑定日志。修正设备树后重新编译 dtb 即可。

六、关键检查点速查

阶段观察对象正常表现异常线索
设备树endpoint / remote-endpoint连接关系完整master match 失败
子驱动 probedmesg 中 ipu/ldb/panel 日志各自 probe 成功时钟、引脚、电源报错
component 绑定imx-drm bind 日志master bind 触发无 bind 日志,卡在 match
DRM 注册/dev/dri/card0节点存在节点缺失
connector 状态/sys/class/drm/card0-*/statusconnecteddisconnected / unknown
modeset屏幕输出按指定分辨率点亮黑屏、花屏、时序错误
fbdev/dev/fb0节点存在且可 mmap节点缺失或分辨率固定错误

把这条链路走通之后,后续的 plane 配置、atomic commit、DMA-BUF 导入导出才有稳定的基础。建议在板子上保留一份可用的 dmesg 基线日志,每次修改设备树或驱动后对比差异,能快速定位是哪个 component 没有就绪。