SA8155 DRM/KMS显示驱动开发与CRTC集成调试 - 第3节
3.1 KMS 驱动入口与 probe 函数
KMS 驱动的初始化始于 probe 函数。当内核发现设备树中匹配的显示控制器节点时,会调用驱动注册的 probe 回调。对于 SA8155 平台,probe 函数负责完成以下核心任务:
- 分配 DRM 设备:调用
drm_dev_alloc()创建struct drm_device实例。 - 设置私有数据:将平台设备私有数据与 DRM 设备关联。
- 初始化 component 框架:为后续子组件(如 DSI、HDMI TX)的绑定做准备。
- 解析设备树:读取
reg、interrupts、clocks、ports等关键属性。 - 注册 DRM 驱动:调用
drm_dev_register()使设备对外可见。
关键点:SA8155 的显示控制器在设备树中通常以
display-subsystem 为父节点,其子节点包括 mdss、dp、dsi 等。probe 函数必须确保所有子组件就绪后,才能完成模式设置的初始化。
3.2 component 框架:解耦与同步
SA8155 显示子系统由多个硬件模块组成:MDP(显示处理)、DP(显示端口)、DSI(显示串行接口)等。这些模块的初始化顺序和依赖关系复杂,Linux 内核使用 component 框架 来管理它们的绑定与解绑。
3.2.1 核心数据结构
| 结构体 | 作用 | 关键字段 |
|---|---|---|
struct component | 表示一个子组件 | ops、dev、master |
struct component_master | 表示主控设备(显示控制器) | ops、match、dev |
struct component_ops | 子组件的操作回调 | bind、unbind |
3.2.2 工作流程
- 主控注册:probe 函数中调用
component_master_add_with_match(),传入匹配表。 - 子组件注册:每个子模块(如 DSI 控制器)的 probe 中调用
component_add()。 - 匹配与绑定:当所有子组件都注册后,主控的
bind回调被触发,此时可以安全地初始化 CRTC、Encoder、Connector。 - 解绑:任一子组件移除时,主控的
unbind被调用,释放资源。
设计意图:component 框架解决了“所有依赖设备都准备好后再初始化”的问题,避免了竞态条件和空指针访问。在 SA8155 上,MDP 作为 master,DSI 和 DP 作为 component。
3.3 设备树解析与匹配
设备树(Device Tree)是描述硬件拓扑和配置的关键手段。KMS 驱动通过解析设备树节点来获取显示控制器的寄存器地址、中断号、时钟频率、端口连接关系等信息。
3.3.1 关键设备树节点
display-subsystem {
compatible = "qcom,sdm845-display", "simple-bus";
mdss: mdss@ae00000 {
compatible = "qcom,sdm845-mdss";
reg = <0x0ae00000 0x1000>;
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_DISP_AHB_CLK>,
<&gcc GCC_DISP_AXI_CLK>;
mdp: mdp@ae01000 {
compatible = "qcom,sdm845-mdp5";
reg = <0x0ae01000 0x84000>;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
};
dsi0: dsi@ae94000 {
compatible = "qcom,sdm845-dsi-ctrl";
reg = <0x0ae94000 0x400>;
clocks = <&mdss_dsi0_clk>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dsi0_in: endpoint {
remote-endpoint = <&mdp_intf0_out>;
};
};
};
};
};
};
3.3.2 解析流程
| 步骤 | API 函数 | 获取内容 |
|---|---|---|
| 1. 获取父节点 | of_parse_phandle() | display-subsystem 句柄 |
| 2. 读取寄存器地址 | platform_get_resource() | reg 属性 → struct resource |
| 3. 映射 I/O 内存 | devm_ioremap_resource() | 虚拟地址空间 |
| 4. 获取中断 | platform_get_irq() | 中断号 |
| 5. 解析时钟 | devm_clk_get() | 时钟句柄 |
| 6. 解析端口 | of_graph_get_endpoint_by_regs() | 端口连接关系 |
常见陷阱:设备树中
reg 地址范围必须与硬件手册严格一致,否则 ioremap 会失败。另外,clocks 属性中的时钟名称需要与驱动中 clk_get 的字符串匹配,否则时钟框架无法正确使能。
3.3.3 匹配机制
驱动与设备树的匹配通过 of_match_table 实现。在 platform_driver 中定义:
static const struct of_device_id msm_mdp_of_match[] = {
{ .compatible = "qcom,sdm845-mdp5" },
{ .compatible = "qcom,sa8155-mdp5" },
{ }
};
static struct platform_driver mdp5_driver = {
.probe = mdp5_probe,
.remove = mdp5_remove,
.driver = {
.name = "msm_mdp5",
.of_match_table = msm_mdp_of_match,
},
};
当内核遍历设备树节点时,会比对 compatible 字符串。匹配成功后调用 probe 函数,并将 struct platform_device 传入,其中包含了设备树解析后的资源信息。
3.4 初始化流程总结
将 probe、component 框架、设备树解析三个环节串联起来,KMS 驱动的完整初始化流程如下:
- 平台驱动注册:
module_platform_driver()注册 MDP 驱动。 - 设备树匹配:内核根据
compatible找到对应节点,调用mdp5_probe()。 - DRM 设备创建:
drm_dev_alloc()分配 DRM 核心结构。 - 资源获取:通过设备树 API 获取寄存器、中断、时钟。
- component 主控注册:
component_master_add_with_match()等待子组件。 - 子组件绑定:DSI、DP 等驱动 probe 后调用
component_add()。 - bind 回调:所有组件就绪后,初始化 CRTC、Encoder、Connector。
- DRM 设备注册:
drm_dev_register()对外暴露显示能力。
性能考量:在 SA8155 上,MDP 的 probe 函数执行时间直接影响开机启动速度。建议将耗时的固件加载、时钟初始化等操作放在 bind 回调中,避免阻塞 probe 路径。