1. 课程导入
在显示驱动开发中,画面能否“看起来正确”,往往不只取决于分辨率与刷新率,更取决于像素数据在进入面板之前经历了怎样的色彩处理。Gamma 校正与色彩管理,正是 DRM/KMS 显示管线里最容易被忽视、却直接影响观感与色彩一致性的关键环节。本课程围绕 drm_crtc 驱动中的 gamma 校正与色彩管理配置展开,从内核对象模型、硬件能力描述、属性配置到实际调试,建立一条可落地的工程路径。
一、学习目标
完成本课程后,你应当具备以下能力:
- 理解 DRM/KMS 中 CRTC、Plane、Encoder、Connector 的职责边界,明确 gamma 与色彩管理挂接在哪个对象上。
- 掌握
drm_crtc的 gamma 相关属性:GAMMA_LUT、GAMMA_LUT_SIZE、DEGAMMA_LUT、DEGAMMA_LUT_SIZE的语义与配置方式。 - 能够区分 gamma 校正、degamma、色彩空间转换(CSC)、色域映射与 HDR 色调映射在管线中的位置与作用。
- 能够阅读并修改驱动中的
gamma_set、atomic_begin/atomic_flush等回调,把用户态 LUT 正确写入硬件寄存器。 - 掌握使用
modetest、drm_info、内核日志与示波/色度计进行验证与排障的基本方法。
二、核心概念
Gamma 校正的本质,是在线性光强与编码像素值之间建立映射。显示设备(尤其是 LCD)的亮度响应通常近似幂函数,因此需要一条反向的幂曲线把编码值还原为线性光,或按创作意图调整中间调。驱动层面,这条曲线以查找表(LUT)形式存在,由用户态通过 DRM 属性下发。
色彩管理的范围更大,它关注“同一颜色在不同设备上看起来一致”。在 KMS 管线中,它通常包含 degamma(输入线性化)、色彩空间转换矩阵、gamma(输出编码)、以及可选的 3D LUT 与色调映射。理解这些模块的先后顺序,比记住某个寄存器偏移更重要。
三、整体框架
下图给出本课程的知识骨架:从用户态属性下发,到 DRM 原子提交,再到 CRTC 回调与硬件 LUT 写入,最后经面板呈现并由测量工具闭环验证。请把它当作后续每一章的“地图”。
四、典型场景与配置示例
假设一块支持 256 项 gamma LUT 的 CRTC,用户态希望加载一条标准 sRGB 近似曲线。典型流程是:查询属性、构造 LUT、原子提交。下面给出关键代码片段,帮助建立“属性—回调—寄存器”的对应关系。
/* 查询 CRTC 的 gamma 能力 */
drmModeObjectGetProperties(fd, crtc_id, DRM_MODE_OBJECT_CRTC, &props);
/* 读取 GAMMA_LUT_SIZE,确认硬件支持的表项数 */
uint32_t lut_size = get_prop_value(props, "GAMMA_LUT_SIZE");
/* 构造 256 项 LUT,每项为 drm_color_lut {red, green, blue} */
struct drm_color_lut lut[256];
for (int i = 0; i < 256; i++) {
double x = i / 255.0;
double y = pow(x, 2.2); /* 近似 sRGB gamma */
lut[i].red = (uint16_t)(y * 65535);
lut[i].green = (uint16_t)(y * 65535);
lut[i].blue = (uint16_t)(y * 65535);
}
/* 通过 atomic 提交 GAMMA_LUT blob 属性 */
drmModeCreatePropertyBlob(fd, lut, sizeof(lut), &blob_id);
drmModeAtomicAddProperty(req, crtc_id, prop_gamma_lut, blob_id);
drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
驱动侧则需要在 CRTC 的 atomic_begin 或 atomic_flush 阶段读取该 blob,并逐项写入硬件。若硬件 LUT 位宽为 10bit,而用户态给出 16bit,还需做右移或饱和处理,否则会出现截断导致的暗部断层。
五、常见误区与复盘
| 误区 | 现象 | 正确做法 |
|---|---|---|
| 把 gamma 当成“调亮度” | 中间调偏移、暗部死黑 | gamma 是曲线映射,亮度应交给背光或 plane 属性 |
| 忽略 degamma 与 gamma 的顺序 | 整体偏色、灰阶不单调 | 先线性化,再做矩阵/3D LUT,最后输出编码 |
| LUT 位宽不匹配直接强转 | 暗部断层、色带 | 按硬件位宽做缩放与饱和,必要时做插值 |
| 只在模式设置时写一次 LUT | 热插拔或恢复后失效 | 在 atomic_flush 中按状态重放,保证一致性 |
GAMMA_LUT 的语义可能不同:有的要求输入为线性值,有的要求已编码值。务必以对应驱动文档和实测为准,不要直接套用其他平台的 LUT 数据。