3. SA8155 DRM驱动初始化:驱动入口函数、platform_driver注册、设备树匹配、组件绑定流程
SA8155 DRM驱动的初始化是整个显示子系统的启动起点。本章深入剖析驱动从内核入口到组件绑定完成的完整路径,涵盖 module_init、platform_driver_register、设备树匹配表 以及 component framework 绑定流程。理解这一过程是掌握后续CRTC、Encoder、Connector初始化的前提。
核心要点: SA8155 DRM驱动采用
component 框架管理多个显示硬件模块(DPU、DSI、HDMI TX等)的绑定顺序,确保所有子设备就绪后才触发DRM设备的最终注册。
3.1 驱动入口函数与module_init
SA8155 DRM驱动遵循标准Linux内核驱动模型,使用 module_platform_driver 宏简化注册过程。入口函数定义在 msm_drv.c 中:
/* msm_drv.c */
static struct platform_driver msm_drm_platform_driver = {
.probe = msm_drm_probe,
.remove = msm_drm_remove,
.shutdown = msm_drm_shutdown,
.driver = {
.name = "msm_drm",
.of_match_table = msm_drm_dt_match,
.pm = &msm_drm_pm_ops,
},
};
static int __init msm_drm_init(void)
{
DBG("msm_drm_init");
return platform_driver_register(&msm_drm_platform_driver);
}
module_init(msm_drm_init);
| 入口组件 | 作用 | 所在文件 |
|---|---|---|
module_init(msm_drm_init) |
内核启动时调用,注册platform驱动 | msm_drv.c |
platform_driver_register |
将驱动注册到platform总线 | msm_drv.c |
msm_drm_probe |
设备树匹配成功后调用,启动组件绑定 | msm_drv.c |
msm_drm_dt_match |
设备树兼容性匹配表 | msm_drv.c |
驱动入口流程图: 从
module_init 到 platform_driver_register,再到设备树匹配和 probe 的完整调用链。
3.2 platform_driver注册与设备树匹配
设备树匹配表 msm_drm_dt_match 定义了驱动支持的硬件兼容性字符串。SA8155平台对应的匹配条目如下:
/* msm_drv.c */
static const struct of_device_id msm_drm_dt_match[] = {
{ .compatible = "qcom,sa8155-dpu", .data = &sa8155_dpu_cfg },
{ .compatible = "qcom,sa8155-dsi", .data = &sa8155_dsi_cfg },
{ .compatible = "qcom,sa8155-hdmi", .data = &sa8155_hdmi_cfg },
{ .compatible = "qcom,sa8155-mdss", .data = &sa8155_mdss_cfg },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, msm_drm_dt_match);
设备树节点示例(sa8155.dtsi):
/* sa8155.dtsi */
mdss: mdss@ae00000 {
compatible = "qcom,sa8155-mdss";
reg = <0x0ae00000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
dpu: dpu@ae01000 {
compatible = "qcom,sa8155-dpu";
reg = <0x0ae01000 0x80000>;
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc MDSS_AHB_CLK>,
<&gcc MDSS_AXI_CLK>;
};
dsi0: dsi@ae94000 {
compatible = "qcom,sa8155-dsi";
reg = <0x0ae94000 0x400>;
clocks = <&gcc MDSS_BYTE0_CLK>;
};
};
| 设备树节点 | compatible | 对应驱动模块 | 功能 |
|---|---|---|---|
| mdss | qcom,sa8155-mdss | MDSS主控制器 | 显示子系统顶层管理 |
| dpu | qcom,sa8155-dpu | DPU CRTC | 显示处理单元,图层合成 |
| dsi0 | qcom,sa8155-dsi | DSI Encoder | MIPI DSI接口输出 |
| hdmi | qcom,sa8155-hdmi | HDMI Encoder | HDMI接口输出 |
匹配优先级: 设备树匹配按照
of_match_table 顺序进行。SA8155平台中,mdss 节点作为顶层容器先被匹配,随后其子节点(dpu、dsi、hdmi)由各自的platform驱动独立匹配。DRM master驱动只绑定 mdss 节点。
3.3 组件绑定流程:component framework
SA8155 DRM驱动使用 component 框架实现多设备绑定。核心思想是:DRM master驱动等待所有子组件(DPU、DSI、HDMI等)注册完成后,才调用 bind 回调完成DRM设备初始化。
组件绑定流程分为三个角色:
- Master(主控):
msm_drm_probe中调用component_master_add_with_match注册master。 - Component(组件): 各子设备(dpu、dsi、hdmi)的probe函数中调用
component_add注册自身。 - Match(匹配): 通过
component_match_add添加匹配规则,将子设备与master关联。
/* msm_drv.c - 组件绑定核心代码 */
static int msm_drm_probe(struct platform_device *pdev)
{
struct component_match *match = NULL;
int ret;
/* 添加子组件匹配:DPU、DSI、HDMI */
component_match_add(&pdev->dev, &match, compare_of, dpu_node);
component_match_add(&pdev->dev, &match, compare_of, dsi_node);
component_match_add(&pdev->dev, &match, compare_of, hdmi_node);
/* 注册master,等待所有组件就绪 */
ret = component_master_add_with_match(&pdev->dev, &msm_drm_comp_ops, match);
if (ret)
dev_err(&pdev->dev, "failed to add component master\n");
return ret;
}
/* master bind回调:所有组件就绪后调用 */
static int msm_drm_bind(struct device *dev)
{
struct drm_device *ddev;
int ret;
ddev = drm_dev_alloc(&msm_drm_driver, dev);
if (IS_ERR(ddev))
return PTR_ERR(ddev);
/* 初始化各个子模块:kms、crtc、encoder、connector */
ret = msm_drm_kms_init(ddev);
if (ret)
goto err_kms;
ret = drm_dev_register(ddev, 0);
if (ret)
goto err_register;
return 0;
err_register:
msm_drm_kms_fini(ddev);
err_kms:
drm_dev_put(ddev);
return ret;
}
3.4 组件绑定关键数据结构
组件绑定过程中,msm_drm_comp_ops 定义了master的bind/unbind回调:
/* msm_drv.c */
static const struct component_master_ops msm_drm_comp_ops = {
.bind = msm_drm_bind,
.unbind = msm_drm_unbind,
};
子组件通过 component_add 注册,并提供 component_ops:
/* dpu_kms.c */
static const struct component_ops dpu_component_ops = {
.bind = dpu_bind,
.unbind = dpu_unbind,
};
static int dpu_kms_probe(struct platform_device *pdev)
{
/* 初始化DPU硬件资源 */
return component_add(&pdev->dev, &dpu_component_ops);
}
| 数据结构/函数 | 作用 | 调用时机 |
|---|---|---|
component_master_ops |
定义master的bind/unbind回调 | 所有组件就绪后调用bind |
component_ops |
定义子组件的bind/unbind回调 | 组件注册时提供 |
component_match_add |
添加组件匹配规则 | master probe时 |
component_master_add_with_match |
注册master并关联匹配规则 | master probe时 |
component_add |
子组件注册到component框架 | 子设备probe时 |
绑定顺序保证: component框架确保
msm_drm_bind 在所有子组件的 bind 回调执行完毕后才被调用。这意味着DPU、DSI、HDMI的硬件初始化在DRM设备注册之前完成,避免了竞态条件。
3.5 初始化流程总结
SA8155 DRM驱动初始化完整流程可归纳为以下步骤:
- module_init:调用
platform_driver_register注册platform驱动。 - 设备树匹配:内核根据
of_match_table匹配mdss节点,调用msm_drm_probe。 - 组件匹配注册:在probe中通过
component_match_add添加DPU、DSI、HDMI子组件匹配。 - 子设备probe:各子设备(dpu、dsi、hdmi)的platform驱动独立probe,调用
component_add注册自身。 - master bind:所有子组件注册完成后,component框架调用
msm_drm_bind。 - DRM设备注册:在bind中完成KMS初始化、CRTC/Encoder/Connector创建,最终调用
drm_dev_register。
调试建议: 如果DRM设备注册失败,首先检查
/sys/kernel/debug/component 下的组件注册状态。使用 echo 0x1f > /sys/module/drm/parameters/debug 可开启DRM核心调试日志,观察bind/unbind调用顺序。