ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MTK平台DRM显示驱动初始化流程解析:从component bind到KMS调试实战

MTK平台DRM显示驱动初始化流程解析:从component bind到KMS调试实战 MTK 平台显示驱动调试时遇到的一次“卡死”往往不是死机而是 DRM 设备根本没注册成功。你说不上哪里错了dmesg 也没有 panic就是/dev/dri/card0不存在fbcon 只有个光标闪系统像坏了又没全坏。我把mtk_drm_drv.c的初始化流程完整过了一遍又一遍之后才意识到这类问题大部分不是某一行代码写错而是对整个从 platform driver 到 component bind 的初始化链路缺乏全局概念。这篇文章就围绕这条链路来写从 KMS 模块如何从一个空壳变成可用的显示框架每个阶段谁在等谁、谁必须先完成按我实际调试的顺序讲。适合正在调 MTK 显示驱动、或者刚把 Android 显示栈切到标准 DRM/KMS 的 BSP 工程师。你要是急着找具体报错可以直接跳到第 5 节调试部分要是想把流程理顺建议从头看因为很多问题不走到 component 那一步你是根本看不出原因的。1. 从显示链路到DRMMTK的模块化硬件该怎么理解1.1 MTK 显示管线的基本形态MTK 的显示管线大致是GPU/CPU 把 framebuffer 写到内存内存经过显示控制器再到输出接口。中间会有多个 IP 依次参与典型路径是 OVLOverlay- RDMARead DMA- COLOR - AAL - GAMMA - DITHER - 输出接口。输出接口按平台不同可能是 DSI、DPI、HDMI 或者 DisplayPort。MT8173 上还有 main path 和 ext path 双通路的说法目的就是让两个屏能同时输出或者把合成和显示切片开。这样的硬件结构天然就是多模块的。OVL、RDMA、DSI、HDMI 这些 IP 不完全是一个地址空间里的一坨寄存器而是各自有独立时钟、独立中断、独立设备树节点。把它们全部揉进一个平台驱动里当然可以但后续每个 IP 的更新都会互相影响尤其像 HDMI 和 DSI 这种接口差异巨大的模块硬凑在一起维护成本很高。所以 MTK 选择了更模块化的组织方式这也是后面 component 框架出现的基础。我第一次看 mtk_drm_drv.c 的时候其实很懵一个显示主驱动居然不直接初始化显示而是像包工头一样到处点卯把活儿派给各个模块。后来才理解这套结构是照着硬件模块化形态来的每个 IP 的驱动独立 probe、独立管理自己的时钟和电源主 DRM 驱动只负责把它们“组织”起来。1.2 DRM 初始化流程解决的核心问题DRMDirect Rendering Manager管的是显存、同步、以及 KMS 那套显示模式管理。KMSKernel Mode Setting负责的就是我们常说的 CRTC、Encoder、Connector、Plane 这些对象它们的创建过程是整个 init 流程的核心。对 BSP 工程师来说最直观的一个指标是开机后/dev/dri/card0什么时候出现fbcon 什么时候接管控制台以及 weston 或 X 能不能打开这张卡。这三个时间点完全取决于 KMS 对象初始化是否流畅。如果 crtc 的 pipe 或 possible_crtcs 没配对modetest 会看到诡异的结果如果 connector 的 status 不对即使 card0 出现用户空间也拿不到可用的 display mode。所以理解初始化流程不是看热闹是排查黑屏的第一块敲门砖。另外要注意一个点DRM 设备的注册成功不等于显示通路一定可用。KMS 初始化只是把内核对象建好真正的上电、时序、数据流是后面 atomic commit 之后才发生的。很多调试时“看起来注册了却黑屏”的案子问题都不在 init而在后续的 enable 流程。但如果你连 init 顺序都不清楚就分不清问题是哪个阶段埋下的。2. 入口分析platform driver 与设备树是怎么对上号的2.1 驱动入口代码位置与匹配表MTK DRM 主驱动的入口在drivers/gpu/drm/mediatek/mtk_drm_drv.c。这是一个标准的platform_driver核心是下面的 of_match_tablestatic const struct of_device_id mtk_drm_of_ids[] { { .compatible mediatek,mt8173-drm, .data mt8173_drm_driver_data }, { .compatible mediatek,mt2701-drm, .data mt2701_drm_driver_data }, { .compatible mediatek,mt8167-drm, .data mt8167_drm_driver_data }, { .compatible mediatek,mt8183-drm, .data mt8183_drm_driver_data }, { .compatible mediatek,mt8195-drm, .data mt8195_drm_driver_data }, { } };设备树上必须有一个 compatible 为mediatek,mtXXXX-drm的节点这个驱动才会参与 probe。每个平台的driver_data里记录了这个平台有几条显示通路、main_path 和 ext_path 分别由哪些模块组成、屏蔽了哪些功能。这个 data 很关键后面组件遍历时就是拿它来判断哪些子节点需要纳入 KMS 初始化。很多人在设备树里改了子节点比如加了一个 DSI panel却发现主驱动根本没反应第一反应是去看 DSI 驱动。其实第一步应该确认主节点 compatible 是否匹配以及 driver_data 里是否声明了对应的输出接口。毕竟平台驱动 match 不上后面全白搭。2.2 probe 函数的第一批动作DMA、锁、组件列表mtk_drm_probe的第一步动作顺序很有意思它不是在创建 KMS 对象而是在“准备容器”。我按 mainline 近几个 5.x/6.x 版本的实际代码逻辑梳理一下static int mtk_drm_probe(struct platform_device *pdev) { struct device *dev pdev-dev; struct component_match *match NULL; int ret; ret dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34)); if (ret) return ret; mutex_init(private-commit.mutex); INIT_WORK(private-commit.work, mtk_atomic_work); mtk_drm_of_get_drm_components(dev, match); if (!match) return -EINVAL; return component_master_add_with_match(dev, mtk_drm_component_ops, match); }第一步是dma_set_mask_and_coherent。MTK 显示控制器的地址范围一般支持 34 位或 32 位地址不同平台有差异。这一步失败的话驱动直接返回后面什么都不用谈。实际调试中很少遇到但在 IOMMU 或 smmu 配置改过以后可能会把 DMA 掩码撑爆表现为 probe 一直失败还找不到原因。第二步是初始化一个 mutex 和一个 workqueue这里是给后面的 atomic commit 用的。mtk_atomic_work是异步提交的核心因为 MTK 的 commit 流程不是纯软件状态的更新它要等硬件 vblank所以必须丢到 workqueue 里去等不能占着主流程不放。第三步是mtk_drm_of_get_drm_components这步会把设备树里所有和显示相关的子节点收集到一个component_match链表里。这个函数会遍历主节点下的各个子节点OVL0、RDMA0、DSI0、DPI0 等根据driver_data判断哪些模块属于当前平台并且用component_match_add把这些节点和对应的 compare 回调注册进 match。第四步才是真正的关键component_master_add_with_match。调用完这一步probe 就结束了主驱动没有立刻创建任何 DRM 对象。这是初学者最容易困惑的地方也是整个 MTK DRM 驱动架构的“非典型”之处。它把一个本来可以一口气干完的初始化流程拆成了“等所有人到齐再开始开会”的模式。2.3 deferred probe 在里面扮演的角色这里顺便说一下 EPROBE_DEFER。主驱动的 probe 不会主动等子模块而是通过component_master_add_with_match注册一个“master”等系统里其他 component 都加进来。子模块驱动比如 mtk_dsi、mtk_disp_ovl在它们自己的 probe 里会调用component_add把自己注册成一个 component。如果某个子模块的 probe 因为资源没就绪返回了-EPROBE_DEFER内核会把它放进延迟列表等依赖资源出现后再重新 probe。也就是说子模块全部 probe 成功是 master 的 bind 被调用的隐含前提。很多“主驱动 probe 了但 drm 设备没出现”的怪现象本质就是某个子模块卡在 EPROBE_DEFERmaster 侧却在傻等 component。调试时想确认谁是罪魁祸首可以用dmesg | grep -i probe配合cat /sys/kernel/debug/devices_deferred看有哪些设备处于 deferred 状态。这个信息比猜主驱动 log 快得多。3. KMS 对象初始化顺序drm_device、crtc、plane、encoder、connector 谁先谁后3.1 先有 DRM Device才有 KMS 对象当所有 component 都到位后内核会调用mtk_drm_bind。这个函数才是真正创建 DRM 设备的地方static int mtk_drm_bind(struct device *dev) { struct mtk_drm_private *private dev_get_drvdata(dev); struct drm_device *drm_dev; int ret; drm_dev drm_dev_alloc(mtk_drm_driver, dev); if (IS_ERR(drm_dev)) return PTR_ERR(drm_dev); private-drm drm_dev; drm_dev-dev_private private; drm_mode_config_init(drm_dev); drm_dev-mode_config.min_width 1; drm_dev-mode_config.min_height 1; ... }drm_dev_alloc之后drm_mode_config_init会初始化 mode_config 这个核心结构体里面存了之后创建的 crtc、encoder、connector、plane 链表以及各种回调。MTK 在这里会设置 mode_config 的 funcs 和 helper_private像atomic_commit这类核心操作都注册在 funcs 里。注意这里有个细节drm_mode_config_init在新内核里已经被drmm_mode_config_init取代了但思想一样都是给 mode_config 分配内存并初始化链表。你如果看到老代码按新接口替换即可不影响流程理解。等到drm_dev_register成功系统里才会出现/dev/dri/card0。所以你看 dmesg 的时候如果 bind 内部某一步失败了用户空间就完全感知不到 DRM 的存在表现就是 fbcon 无法注册、xrandr 看不到输出。我在实际项目中遇到过 crtc 初始化失败导致 bind 提前 return 的情况dmesg 里只有一条[drm:mtk_drm_bind] *ERROR* Failed to initialize crtc其他什么提示都没有。3.2 plane 和 crtc先建平面再挂管道KMS 对象创建的先后顺序不是随便定的。MTK 在mtk_drm_kms_init里会遍历所有 component对每个 component 判断它是 CRTC 模块还是 Encoder 模块分别调用对应的创建函数。创建顺序里最核心的一点是先创建 plane再创建 crtc。因为drm_crtc_init_with_planes这个接口需要传入已经创建好的 primary plane 和 cursor plane 指针。MTK 的每个 CRTC 会挂多个 plane包括主平面primary和叠加平面overlay这些 plane 由mtk_drm_plane_create创建底层对应 OVL/OVL0 等硬件通道。ret mtk_drm_plane_create(drm_dev, comp, 0, 0); if (ret) return ret; ret mtk_drm_crtc_create(drm_dev, comp);plane 创建时要做几件事设置支持的 pixel format通常是 ARGB8888、XRGB8888、AFBC 格式等初始化 plane 的类型和可能的 crtc 掩码注册drm_plane_funcs和 helper 回调。如果 plane 的 possible_crtcs 设置不对modetest 里就会看不到 plane 对应的 crtc或者 kmscube 无法把画面合成到目标 crtc 上。这个错误非常隐蔽init 不会报错逻辑上却完全不可用。crtc 创建后MTK 的 crtc 结构体里保存了这个管线的 vblank 相关定时器、gamma、ctm 等资源。之后还要把该 crtc 可能连接到的 encoder 的 possible_crtcs 位掩码更新进去。这个位掩码决定了路由关系一个连接在 crtc0 上的 encoder初始化时如果 possible_crtcs 里没置 bit 0userspace 无论怎么设置 mode都会被 atomic check 拒绝。3.3 encoder 和 connector连接关系在初始化时怎么确立encoder 由主 DRM 驱动统一创建调用路径是mtk_drm_encoder_create。这个函数会根据 component 的硬件类型决定创建什么 encoder设置drm_encoder_funcs、drm_encoder_helper_funcs以及刚才说的 possible_crtcs。因为 MTK 在一个 SoC 上可能有 A 通路输出到 DSI、B 通路输出到 HDMI所以各 encoder 能连接的 crtc 是通过设备树和 driver_data 共同决定的。connector 则不是在 encoder 创建时一并在主驱动里创建的。它由各个接口驱动自己创建比如 DSI 驱动在mtk_dsi_probe成功后会创建自己的 connectorHDMI 驱动也会通过mtk_hdmi注册 connector。这个设计是有原因的connector 和实际物理输出强相关它需要知道 panel 或者 monitor 的 EDID、热插拔状态这些逻辑接口驱动最清楚没必要全部塞到主驱动里。panel 的接入是通过devm_of_drm_get_panel(dev, np, 0, panel)这类接口完成的。DSI 驱动在 probe 时从设备树的端口里找到 panel 节点拿到struct drm_panel *然后传给 connector。这个 panel 指针最终会在atomic_enable时调用panel-prepare()和panel-enable()来真正点亮屏幕。所以你在 init 阶段看到的“connector 出现”只是内核对象层面已经注册panel 真正初始化命令的下发要等第一次 modeset。很多同学在 init 阶段找不到 panel log 就以为面板没接上其实不是是你还没触发 atomic commit。3.4 一个容易忽略的时序点DSI 与 panel 的依赖DSI 这种接口在初始化时有个特殊的依赖链MIPI DSI host 必须先被 attach 到 DSI controller然后 connector 才能正常工作。具体来说mipi_dsi_attach和drm_bridge_attach的顺序很重要。如果把 bridge attach 放在 DSI 还没初始化完成之前probe 会返回错误或者 reject。panel 本身还有个 prepare/enable 的状态机。按 DRM 标准流程prepare是上电和发初始化命令enable是开始真正的视频流传输。这两个动作不是 init 阶段调用的而是在atomic_enable阶段由 encoder helper 调用。不搞清楚这个时点你会误以为初始化阶段就应当看到 panel 的日志输出结果排查半天发现流程压根没走到那里。4. component 机制MTK 为什么非要用这套组合拳4.1 多个子驱动协作时component 要解决什么问题先想一个基本问题如果 MTK 的显示相关节点全部作为独立 platform device 注册内核怎么保证它们都 probe 完再初始化主 DRM答案是没法保证。platform bus 上的设备 probe 顺序是注册顺序决定的而注册顺序在设备树解析和设备初始化阶段有不确定性。你不能假设 OVL 一定比 DSI 先 probe。内核为此提供了 component 机制核心在drivers/base/component.c。它把参与协作的驱动分成两类master 和 component。每个子驱动在 probe 成功时调用component_add报告“我准备好了”主驱动调用component_master_add_with_match注册自己在所有 component 就绪后要执行的 bind 回调。谁先谁后不再依赖 probe 顺序而是由框架统一调度。MTK 选这套机制不是说它比其他方式更好而是它的硬件结构本来就是多个独立 IP每个 IP 的驱动需要单独测试、单独维护、单独编译。你想想如果把 OVL、RDMA、DSI、HDMI 全部塞进一个 platform driver每次调试 HDMI 都要重新编译整个显示栈代码合并冲突也够喝一壶的。4.2 从 component_add 到 master bind 的完整时间线整个时间线是这样的主 DRM 设备 probe调用mtk_drm_of_get_drm_components把设备树中需要协作的节点都挂到一个 match 上然后调用component_master_add_with_match。各个子模块驱动mtk_dsi、mtk_disp_ovl、mtk_disp_rdma、mtk_mipi_tx 等陆续 probe各自在 probe 末尾调用component_add。component 框架每收到一个 add就用 master 注册时提供的 compare 回调去比对设备节点。mtk_drm_of_compare的实现就是看当前 component 的 of_node 和 master 期望的节点是否一致。当所有 match 里的节点都有对应 component 注册后框架调用 master 的 bind 回调即mtk_drm_bind。bind 里会遍历组件列表逐个创建对应的 KMS 对象。如果某个组件初始化失败整个 bind 返回错误DRM 设备不注册。卸载时按相反方向调用 unbind先释放 KMS 对象再释放组件顺序不能乱。注意第 4 步有个坑如果 match 里有三个节点其中两个 probe 成功了第三个因为设备树里没写 compatible 或者驱动没编译进去而一直不 probe那么 master 永远等不到 bind。内核不会为这件事报错因为它认为“组件还没齐等一等很正常”。排查的时候最容易在这里被卡住日志没有任何异常系统也没有 panic就是card0不出现。实操时我习惯在mtk_drm_bind里临时加一行DRM_DEV_INFO(dev, bind with %d components\n, ...)然后在每个 component_add 附近也加日志把时间线拉出来看。虽然不是最优方案但在快速定位“谁没来”时非常有效。4.3 和高通这类集中式驱动的差异很多人拿 MTK 和高通对比问为什么高通的 DRM 驱动看起来调用链更集中。大致上高通 msm/drm 的驱动也是把一个 display pipeline 的各个模块组织在一个大驱动目录里通过 platform device 和 dsi/panel 子驱动配合但集中度更高主驱动承担了更多初始化职责没有像 MTK 这样把 OVL、RDMA、COLOR、GAMMA 都拆成完全独立的 component。这不是谁好谁坏的问题而是两个厂商的硬件设计哲学和软件维护方式不同。MTK 这种拆分方式在模块复用上很有优势比如一个 DISP_PWM 模块可以在多个平台之间复用只要把设备树节点改一改就行。但代价是初始化流程的复杂度成倍增加对刚接触这个平台的人来说理解成本确实更高。调试思路上也要跟着变在 MTK 平台上看到一个显示初始化问题不能只盯主驱动先问“哪些组件还没到位”、“driver_data 里有没有把这些模块列进去”、“设备树节点和驱动 compatible 是否对得上”。这套思路理顺了比死磕某一个函数的返回值有用得多。5. 初始化结果怎么验证日志、modetest、debugfs 三个层次5.1 从 dmesg 判断初始化走到了哪一步看初始化进度第一步永远是 dmesg。MTK 驱动里贯穿了DRM_DEV_INFO和DRM_DEV_ERROR这类输出正常流程会看到类似这样的序列mediatek-drm 14000000.drm: bound 1400b000.ovl (ops mtk_disp_ovl_component_ops) mediatek-drm 14000000.drm: bound 1400c000.rdma (ops mtk_disp_rdma_component_ops) mediatek-drm 14000000.drm: bound 14020000.dsi (ops mtk_dsi_component_ops) [drm] Initialized mediatek 1.0.0 20150513 for 14000000.drm on minor 0 [drm] fb0: mediatekdrmfb frame buffer device如果只看到 component 逐个 bound却没有Initialized mediatek说明drm_dev_register之前的某个环节失败了重点查 KMS 对象创建。如果连 bound 都没有问题一定出在 component 等待环节。另一种常见情况系统里有几个[*ERROR*]之后依然注册成功。比如某个 ctm 或 gamma 初始化失败不会致命但 encoder 创建失败或者 crtc init 失败就会让mtk_drm_bind直接 return。所以看日志不要只扫 ERROR要连着上下文看是否继续走下去了。5.2 modetest 与 sysfs 节点把显示对象拉出来看看内核这层没问题之后用modetest验证是最快的。mtk DRM 注册成功后执行modetest -M mediatek -c modetest -M mediatek -p modetest -M mediatek -e-c列出 connector会显示每个 connector 的 id、类型DSI/HDMI/eDP 等、status 和可用 mode-p列出 plane能看到每个 plane 对应的 crtc 和格式-e列出 encoder。这个工具输出的信息对应 KMS 对象的实际状态能直接看出初始化时 possible_crtcs 配没配对。sysfs 侧看/sys/class/drm/下面的节点比如card0-DSI-1cat /sys/class/drm/card0-DSI-1/status会返回 connected/disconnected/unknown。如果这里返回 unknown说明 connector 没有从 panel 或 bridge 拿到有效状态很可能get_modes没有被调用或者 panel 的 driver 没正确注册。/sys/kernel/debug/dri/0/state是 atomic state 的 dump能看到每个 crtc 的 enable 状态、每个 plane 的 fb id 和 src/dst 坐标、每个 connector 的 mode。这个文件在显示异常但卡又正常工作的场景下特别有用能直接看出用户空间到底有没有把需要的 plane attach 到目标 crtc。需要内核开启CONFIG_DRM_DEBUG。cat /proc/fb也可以辅助判断 fbcon 有没有注册成功。MTK 主显示通路初始化完成后会注册一个 fbdev 兼容层如果这个节点不存在大概率是 bind 后的 fbdev 初始化失败或主 CRTC 没分配成功。5.3 常见初始化失败现场与反向定位我整理了几类常见现场和对应的排查方向现场大概率原因重点排查位置/dev/dri/card0不存在component 未绑齐或 KMS init 失败device link、deferred probe 列表、bind 日志card0 出现但 modetest 无 connectorDSI/DPI/HDMI 子驱动未创建 connector子驱动 probe 是否成功、panel 指针是否有效connector status unknownpanel 驱动未注册或 get_modes 未实现panel compatible、devm_of_drm_get_panel 返回值modetest 设置 mode 失败possible_crtcs 位掩码或 mode 时序不对encoder 与 crtc 的 possible_crtcs、panel timingcard0 出现但 fbcon 黑屏crtc/plane 的格式或通路分配错误atomic_check 返回、state dump、RGB 格式支持这些方向里possible_crtcs位掩码是 MTK 初始化里最容易被搞错的一个点。它是在 encoder 创建时通过drm_encoder_init的possible_crtcs参数设置的如果该 encoder 连接的是第 2 条通路上的 crtc而代码写成了第 1 条userspace 设置 mode 时会直接收到EINVAL但内核自身完全正常运行。查这种问题没有捷径就是把 state 里的 crtc 和 encoder 路由一条条对一遍。6. 我在 MTK 平台调显示时踩过的几个坑6.1 component 没等齐并不报错只是“静默卡住”我印象最深的一次问题是把 HDMI 相关驱动从内核配置里删掉后整个显示系统全挂了。当时只改了 defconfig少了CONFIG_DRM_MEDIATEK_HDMI结果/dev/dri/card0直接消失fbcon 也不出来dmesg 干干净净。一开始完全没往 HDMI 上想因为主屏走的是 DSI感觉 HDMI 删了不影响主屏才对。实际原因是主 DRM 节点的component_match里包含了 HDMI 对应的设备树节点而匹配这个节点的驱动被编译选项裁掉后永远不会有 component 注册。master 一直等KMS 初始化永远不会执行。这个问题的排查方式还是靠devices_deferred和对比 driver_data 里声明了哪些组件、实际系统里加载了哪些驱动。后来我养成了习惯改 MTK DRM 相关配置之前先把mtk_drm_of_get_drm_components要挂哪些节点列出来确认不是“绑死”的。这个函数不是把所有子节点全塞进 match 的它根据driver_data里的路径配置过滤所以哪些模块必须存在在 dtsi 里就能预判。6.2 时钟和 regulator 的启动顺序会直接决定 panel 是否点亮另一个高频坑是 panel 的 power sequence。很多第三方 panel 驱动把 power supply 的获取写在 probe 里但真正上电是在prepare回调里。如果 hardware 要求“先开 regulator再拉 reset再发初始化命令”而驱动里写成了先拉 reset那面板就点不亮而且不报错只是黑屏。MTK DSI 初始化时还要注意 MIPI D-PHY 的时钟通常由mtk_mipi_tx这个 PHY 驱动管理。如果 DSI 的 bit rate 和 PHY 的参考时钟计算不一致面板可能能亮但是花屏或者无响应。这个计算不是单纯看 pixel clock要考虑 lane num、bpp、burst mode 等参数。我有一次就是调一个 1080P 竖屏转横屏的需求改了 timing 之后忘记重新算 DSI clock结果屏幕一直闪后来才发现是物理层时钟不够。6.3 竖屏改横屏时真正要动的不是“显示驱动”说到竖屏改横屏这也是 MTK 平台常见需求曾让我绕了一段弯路。改横屏表面上是在“显示驱动”层面做修改实际上如果你走的是标准 DRM/KMS竖屏改横屏通常不是改驱动初始化而是改 mode 和旋转属性。在 MTK 平台做这种改动有几个层次。如果只是想让 fbcon 和早期 logo 横过来通常要调整 kernel command line 的 fbcon 旋转参数或者改 panel 的初始 mode 的 xres/yres让显示驱动的 mode 列表里第一个有效的 mode 就是横屏分辨率。如果在 DRM 用户空间应用层做weston 或者 kmscube 会读取 connector 的 mode再用 plane 的 rotation 属性做 90 度旋转。初始化流程在这里面的作用是保证 connector 的 mode 列表正确、plane 支持 rotation 属性。你如果为了改横屏去动 OVL 初始化的代码方向上就错了。我后来遇到旋转需求的习惯是先确认 panel 的 native mode 的分辨率方向再查 kernel 的fbconrotate:参数和用户的合成器旋转支持最后才考虑要不要把 mode 本身改掉。千万不要一上来就动 CRTC 初始化或 plane 初始化那是把一个应用层逻辑问题强行塞进内核以后每个项目都要重新解一遍这个难题。6.4 用临时打印和 state dump 对抗“没头绪”的状态最后分享一个实用技巧。当你在 MTK 平台调显示但“没有任何报错”时别急着翻源码先在几个关键点打上临时日志mtk_drm_probe完成、mtk_drm_bind开始、每个 component bind 成功、drm_dev_register完成。再配合/sys/kernel/debug/dri/0/state看运行期状态基本能覆盖 90% 的初始化问题。另一个技巧是用modetest -s connector_id:mode做单点验证。比如 DSI connector 的 id 是 80modetest -M mediatek -s 80:1080x192060如果能正常设置模式说明从 crtc 到 encoder 再到 connector 的 KMS 管线是通的剩下问题大概率在用户空间或 panel 驱动。如果失败则顺着atomic_check的日志往下查。我在实际调试中把“熟悉初始化顺序”当作第一优先级来处理。因为在这个平台上大多数诡异的显示问题都不是“驱动写错”而是“初始化链路上的某个环节没有按预期就位”。只要你能快速判断当前卡在哪一环再复杂的黑屏也能拆成一个个可以验证的小问题逐步缩小范围最终找到根因。这个过程本身就是每个 BSP 工程师调显示驱动最值钱的功力。
返回列表