Linux DRM驱动HDR支持与VBlank同步调试工程 - 第4节
4.1 Encoder 的作用与实现
在 DRM/KMS 子系统中,Encoder(编码器)位于 CRTC 与 Connector 之间,负责将 CRTC 输出的像素数据流(通常为并行 RGB 或 LVDS 格式)转换为特定显示接口所需的串行信号格式。Encoder 的核心职责包括:
- 信号编码:将并行像素数据转换为 TMDS(HDMI/DVI)、DisplayPort 串行化数据、eDP 或 LVDS 差分信号。
- 链路训练:对于 DP/eDP,执行链路训练以协商通道数、速率和电压摆幅。
- 色彩格式转换:在 HDR 场景下,将 CRTC 输出的 RGB 或 YCbCr 数据转换为 HDMI 或 DP 所需的色彩格式(如 4:4:4、4:2:2、4:2:0)。
- 辅助通道管理:处理 DDC(I²C)用于 EDID 读取,以及 DP AUX 通道用于 DPCD 寄存器访问。
drm_encoder 结构体通过 possible_crtcs 位掩码声明其可连接的 CRTC。
4.1.1 Encoder 驱动实现要点
实现一个 Encoder 驱动需要填充 drm_encoder_helper_funcs 回调函数,核心接口如下表所示:
| 回调函数 | 触发时机 | 典型实现内容 |
|---|---|---|
atomic_mode_set |
模式设置时 | 配置 Encoder 的时钟、分频器、输出格式寄存器 |
atomic_enable |
Encoder 启用时 | 启动串行化器、PLL 锁定、链路训练 |
atomic_disable |
Encoder 禁用时 | 关闭输出、进入低功耗模式 |
atomic_check |
原子提交校验时 | 验证时钟频率、链路带宽是否满足模式需求 |
以下是一个简化的 Encoder 初始化代码示例,展示如何注册一个内置 HDMI Encoder:
static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = {
.atomic_mode_set = hdmi_encoder_mode_set,
.atomic_enable = hdmi_encoder_enable,
.atomic_disable = hdmi_encoder_disable,
.atomic_check = hdmi_encoder_atomic_check,
};
static int hdmi_encoder_init(struct drm_device *dev) {
struct drm_encoder *encoder;
int ret;
encoder = drm_encoder_alloc(dev, NULL, DRM_MODE_ENCODER_TMDS, 0);
if (IS_ERR(encoder))
return PTR_ERR(encoder);
encoder->possible_crtcs = drm_crtc_mask(&crtc); // 绑定到特定 CRTC
drm_encoder_helper_add(encoder, &hdmi_encoder_helper_funcs);
return 0;
}
atomic_mode_set 中是否正确设置了时钟源和分频器。使用 drm_info 打印 Encoder 的 encoder_type 和 possible_crtcs 确认绑定关系。
4.2 Connector 检测与 EDID 读取
Connector(连接器)代表物理显示接口(HDMI、DP、VGA 等),其核心职责是检测显示器是否连接、读取 EDID(Extended Display Identification Data)以获取显示器能力,并将这些信息传递给 DRM 核心以建立显示模式。Connector 的状态检测通常通过硬件 GPIO 引脚(热插拔检测,HPD)或轮询方式实现。
4.2.1 Connector 检测机制
Connector 检测分为两种模式:
- 热插拔检测(HPD):硬件中断驱动,当显示器插入或拔出时触发中断,驱动调用
drm_helper_hpd_irq_event()通知核心。 - 轮询检测:对于不支持 HPD 的接口(如 VGA),驱动在
connector->polled中设置DRM_CONNECTOR_POLL_CONNECT和DRM_CONNECTOR_POLL_DISCONNECT,核心定时调用detect回调。
Connector 的 detect 回调返回以下状态:
| 返回值 | 含义 | 典型处理 |
|---|---|---|
connector_status_connected |
显示器已连接 | 读取 EDID,建立模式列表 |
connector_status_disconnected |
显示器未连接 | 清空模式列表,禁用输出 |
connector_status_unknown |
无法确定状态 | 尝试读取 EDID 确认 |
connector_status_connected,因为桥接芯片自身始终在线。此时应通过 EDID 读取是否成功来辅助判断。
4.2.2 EDID 读取流程
EDID 是一块 128 字节(或扩展为 256 字节)的数据块,存储在显示器中,通过 DDC(I²C)或 DP AUX 通道读取。读取流程如下:
- 驱动调用
drm_get_edid()或drm_do_get_edid(),传入 connector 和 I²C 适配器。 - 核心通过 DDC 地址 0x50 读取 EDID 块,校验校验和。
- 如果读取成功,核心解析 EDID 并生成
drm_display_mode列表。 - 驱动将 EDID 数据存储在
connector->edid_blob_ptr中,供用户空间通过drmModeGetProperty访问。
以下是一个典型的 Connector get_modes 回调实现:
static int hdmi_connector_get_modes(struct drm_connector *connector) {
struct edid *edid;
int ret;
// 通过 DDC I²C 读取 EDID
edid = drm_get_edid(connector, &hdmi_i2c_adapter);
if (!edid) {
drm_err(connector->dev, "No EDID found on HDMI connector\n");
return 0;
}
// 将 EDID 解析为 drm_display_mode 并添加到 connector
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
return ret;
}
cat /sys/kernel/debug/dri/0/HDMI-A-1/edid > edid.bin 导出 EDID 原始数据,然后用 edid-decode 工具解析,验证显示器支持的 HDR 元数据(如 HDR static metadata 块)。
4.3 DP/HDMI 桥接
在实际嵌入式系统中,SoC 原生显示接口可能与目标显示器不匹配,此时需要外部桥接芯片进行协议转换。常见的桥接场景包括:
- DP 转 HDMI:将 SoC 的 DisplayPort 输出转换为 HDMI 信号,常用于电视或显示器。
- HDMI 转 DP:较少见,用于某些特殊适配器。
- LVDS 转 HDMI:将并行 LVDS 信号转换为 HDMI,用于老旧 SoC。
- eDP 转 DP:用于扩展坞或外接显示器。
4.3.1 桥接芯片驱动架构
在 DRM 驱动中,桥接芯片通常实现为 Bridge 驱动,使用 drm_bridge 框架。Bridge 位于 Encoder 与 Connector 之间,可以串联多个 Bridge 形成链。核心结构如下:
| 组件 | 角色 | 示例芯片 |
|---|---|---|
| SoC Encoder | 输出 DP 串行信号 | Rockchip RK3399 DP |
| Bridge (DP→HDMI) | 协议转换、电平转换 | IT66121, LT8619C |
| Connector | 物理 HDMI 接口 | HDMI Type A |
Bridge 驱动的核心回调包括:
attach:将 Bridge 连接到 Encoder 或上游 Bridge。detect:检测下游显示器状态(通过桥接芯片的 HPD 引脚)。get_edid:通过桥接芯片的 I²C 或 AUX 通道读取 EDID。mode_set:配置桥接芯片的输出时序和色彩格式。
mode_set 中,需要配置桥接芯片的 HDR_METADATA 寄存器,将 DRM 的 hdr_output_metadata 属性写入芯片。
4.3.2 桥接芯片调试要点
调试 DP/HDMI 桥接时,常见问题及排查方法如下:
- 无显示输出:检查桥接芯片的电源、复位时序,使用示波器测量 HPD 引脚电平。
- EDID 读取失败:确认 I²C 总线地址(通常为 0x50),检查桥接芯片是否处于透传模式。
- 色彩异常:验证
mode_set中是否设置了正确的色彩深度(8/10/12bit)和色彩空间(RGB/YCbCr)。 - HDR 不生效:使用
drm_info查看 Connector 的HDR_OUTPUT_METADATA属性是否已设置,并确认桥接芯片支持 HDR InfoFrame 注入。
// 示例:在 Bridge 的 mode_set 中配置 HDR 元数据
static void bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode) {
struct bridge_priv *priv = bridge_to_priv(bridge);
struct drm_connector *connector = bridge->connector;
struct drm_connector_state *state = connector->state;
// 配置基础时序
reg_write(priv, REG_VIDEO_TIMING, mode->clock);
// 如果启用了 HDR,写入 HDR 元数据
if (state->hdr_output_metadata) {
struct hdr_output_metadata *hdr = state->hdr_output_metadata;
reg_write(priv, REG_HDR_EOTF, hdr->hdmi_metadata_type1.eotf);
reg_write(priv, REG_HDR_LUMINANCE, hdr->hdmi_metadata_type1.max_luminance);
}
}