1. 龙芯LSDC DRM显示系统全链路解析
搞龙芯平台显示开发的朋友应该都遇到过这样的场景:硬件连接没问题,但HDMI就是点不亮。今天我就把整个显示链路从硬件到软件给你彻底讲透,这可能是目前最完整的龙芯LSDC DRM显示系统解析。
我在龙芯2K1000平台上折腾显示系统整整三个月,踩过的坑比吃过的饭还多。现在回头看,其实整个链路非常清晰,关键是要理解每个环节的依赖关系。下面我就用实际项目经验,带你走通这条显示链路。
2. 系统架构全景
2.1 硬件基础构成
龙芯的显示系统核心是LSDC(Loongson Display Controller),它需要几个关键硬件协同工作:
- 显示控制器:负责图形渲染和时序控制
- PLL时钟:提供像素时钟和HDMI TMDS时钟
- DDR控制器:管理显存访问
- HDMI PHY:物理层信号转换
这些硬件在板子上通过特定总线连接,任何一个环节出问题都会导致显示异常。比如我就遇到过PLL时钟配置错误导致HDMI输出花屏的情况。
2.2 软件栈全景
软件层面主要涉及以下组件:
code复制硬件描述(设备树)
↓
内核配置(Kconfig)
↓
驱动加载(LSDC/DRM/HDMI)
↓
用户空间接口(/dev/dri, /dev/fb0)
↓
图形栈(X11/Wayland/应用)
这个链条中,设备树是基石,它告诉内核硬件长什么样。没有正确的设备树描述,再好的驱动也跑不起来。
3. 设备树详解
3.1 设备树关键节点
设备树必须完整描述以下硬件信息:
dts复制lcdc: lcdc@1fe0c000 {
compatible = "loongson,ls2k1000-lcdc";
reg = <0x1fe0c000 0x1000>;
interrupts = <32>;
clocks = <&pix_clk>, <&hdmi_clk>;
clock-names = "pix", "tmds";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lcdc_out_hdmi: endpoint {
remote-endpoint = <&hdmi_in_lcdc>;
};
};
};
};
这个节点定义了:
- 寄存器基地址(0x1fe0c000)
- 中断号(32)
- 时钟依赖(pix_clk和hdmi_clk)
- 端口连接关系
3.2 常见设备树问题
在实际项目中,设备树最容易出问题的地方有:
- 寄存器地址错误:会导致驱动无法访问硬件
- 时钟定义不全:特别是HDMI的TMDS时钟必须正确定义
- 端口连接错误:endpoint必须与HDMI节点匹配
我曾经因为漏写了一个clock-names属性,导致驱动加载失败。调试这种问题最有效的方法是检查内核启动日志中的probe失败信息。
4. 内核配置要点
4.1 必须开启的配置项
要让DRM显示栈正常工作,内核需要开启以下关键选项:
code复制CONFIG_DRM=y
CONFIG_DRM_LOONGSON=y
CONFIG_DRM_KMS_HELPER=y
CONFIG_DRM_LOAD_EDID_FIRMWARE=y
CONFIG_FB=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FRAMEBUFFER_CONSOLE=y
特别注意:CONFIG_DRM_LOONGSON是龙芯专属选项,通用DRM驱动不包含龙芯特定的优化。
4.2 配置依赖关系
这些配置项之间存在严格的依赖:
- DRM_KMS_HELPER依赖DRM
- FB控制台依赖基础的FB支持
- EDID支持需要固件加载功能
配置时要特别注意menuconfig中的依赖提示,避免漏选关键选项。
5. 驱动加载流程
5.1 驱动初始化顺序
驱动加载遵循以下顺序:
- 平台驱动注册(of_platform)
- LSDC驱动probe
- DRM子系统初始化
- KMS接口注册
- HDMI驱动绑定
这个过程中最关键的环节是LSDC驱动probe,它会完成:
- 寄存器映射
- 时钟获取
- 中断注册
- 显存分配
5.2 关键驱动代码
驱动中最核心的是模式设置代码:
c复制static void lsdc_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode)
{
struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);
struct lsdc_device *ldev = crtc->dev->dev_private;
/* 计算并设置像素时钟 */
clk_set_rate(ldev->pixclk, mode->clock * 1000);
/* 配置显示时序 */
lsdc_set_timing(ldev, mode);
/* 使能显示控制器 */
lsdc_crtc_enable(crtc);
}
这段代码处理显示模式切换,任何错误都会导致显示异常。
6. HDMI输出实现
6.1 HDMI链路建立
HDMI输出需要完整的显示管线:
code复制DRM CRTC → DRM Encoder → DRM Connector → HDMI PHY
在驱动中,这对应以下初始化流程:
c复制/* 创建CRTC */
drm_crtc_init(dev, crtc, &lsdc_crtc_funcs);
/* 创建Encoder */
drm_encoder_init(dev, encoder, &lsdc_encoder_funcs);
/* 创建Connector */
drm_connector_init(dev, connector, &lsdc_connector_funcs);
/* 绑定各组件 */
drm_connector_attach_encoder(connector, encoder);
6.2 EDID处理
HDMI驱动需要读取显示器的EDID信息:
c复制static int lsdc_get_edid(struct drm_connector *connector)
{
struct i2c_adapter *adapter = ...;
struct edid *edid;
edid = drm_get_edid(connector, adapter);
if (!edid) {
DRM_ERROR("Failed to get EDID\n");
return -ENODEV;
}
drm_connector_update_edid_property(connector, edid);
kfree(edid);
return 0;
}
EDID获取失败会导致无法识别显示器的最佳分辨率。
7. 用户空间接口
7.1 DRM设备节点
驱动成功加载后会创建:
code复制/dev/dri/card0 - 主DRM设备节点
/dev/fb0 - 帧缓冲设备(兼容传统FB)
现代图形栈应该使用**/dev/dri/card0**,它支持KMS和原子模式设置。
7.2 测试工具使用
调试时常用的DRM工具:
bash复制# 查看DRM设备信息
modetest -M loongson
# 测试显示输出
modetest -M loongson -s 1280x720@60
modetest是调试显示问题的利器,可以绕过上层图形栈直接测试硬件功能。
8. 常见问题排查
8.1 HDMI无输出排查步骤
- 检查内核日志是否有驱动加载错误
- 确认设备树寄存器地址正确
- 使用示波器测量HDMI时钟信号
- 检查EDID是否成功读取
- 测试直接模式设置(modetest)
8.2 典型问题解决
问题:显示花屏
原因:显存地址不对齐或DMA配置错误
解决:检查设备树中的memory-region配置
问题:HDMI无信号
原因:PLL时钟未正确配置
解决:检查驱动中的时钟设置代码
问题:分辨率不支持
原因:EDID解析失败
解决:手动指定有效模式或检查HDMI连接
9. 性能优化技巧
9.1 显存管理优化
龙芯平台建议使用CMA(连续内存分配器)来管理显存:
dts复制reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x10000000>; // 256MB
linux,cma-default;
};
};
这样可以避免内存碎片导致的性能问题。
9.2 时钟配置优化
对于4K显示,需要特别注意PLL配置:
c复制/* 设置HDMI TMDS时钟 */
clk_set_rate(hdmi_clk, mode->clock * 1000 * 5);
TMDS时钟通常是像素时钟的5倍,这个系数必须正确设置。
10. 开发调试心得
经过几个月的开发调试,我总结了以下几点经验:
- 设备树先行:在写驱动代码前,先确保设备树描述完全正确
- 时钟是关键:显示问题80%与时钟配置有关
- 工具链要匹配:使用龙芯官方推荐的交叉编译工具链
- 日志要详细:在驱动关键路径添加调试打印
- 硬件别忽略:有时问题就是HDMI线接触不良
记住,显示系统是一个完整的链路,调试时要系统地检查每个环节。从我的经验来看,大多数问题都出在设备树和时钟配置这两个环节。