一、驱动概述
本文分析的是一个面向 Cn(寒武纪)SoC 的完整 Linux DRM 驱动示例。该驱动展示了现代 DRM 子系统的典型实现模式,包括:
- Component 框架进行多组件管理
- DRM Atomic 模式设置
- GEM DMA 内存管理
- VOP(视频输出处理器)显示流水线
- HDMI 显示接口
// cambr_drm_drv.h
#define DRIVER_NAME "cndrm"
#define DRIVER_DESC "Cn SoC DRM"
二、整体架构
graph TB
subgraph "DRM Framework"
DRM[DRM Driver]
GC[Gem Core]
AMC[Atomic Mode Setting]
end
subgraph "Component Master"
MB[cambr_drm_bind]
end
subgraph "Component Slaves"
VOP[cambr_vop]
HDMI[cambr_hdmi]
end
subgraph "Hardware"
HW[Video Output Processor]
ENC[HDMI Encoder]
PHY[HDMI PHY]
end
DRM --> GC
DRM --> AMC
MB --> VOP
MB --> HDMI
VOP --> HW
HDMI --> ENC
ENC --> PHY
显示数据流:
User Space (Framebuffer)
↓
Plane (Primary/Overlay) → OVL (Overlay Engine)
↓
RVDMA (Read Video DMA) → DPI (Display Pixel Interface)
↓
HDMI Encoder → HDMI PHY → Monitor
三、核心组件分析
3.1 DRM 驱动主模块 (cambr_drm_drv)
主模块负责:
- 注册 platform driver
- 初始化 DRM 核心
- 管理 Component 框架
// cambr_drm_drv.c
static struct drm_driver cambr_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
.name = DRIVER_NAME,
.fops = &cambr_drm_driver_fops,
DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_dma_dumb_create),
};
关键特性:
DRIVER_MODESET:支持模式设置DRIVER_GEM:使用 GEM 内存管理DRIVER_ATOMIC:支持 Atomic 模式设置
初始化流程:
static int cambr_drm_bind(struct device *dev)
{
// 1. 分配 DRM 设备
drm = drm_dev_alloc(&cambr_drm_driver, dev);
// 2. 初始化模式配置
drmm_mode_config_init(drm);
drm->mode_config.max_width = CN_MAX_FB_WIDTH; // 4096
drm->mode_config.max_height = CN_MAX_FB_HEIGHT; // 4096
// 3. 初始化 Vblank
drm_vblank_init(drm, MAX_CRTC); // MAX_CRTC = 1
// 4. 绑定子组件
component_bind_all(dev, drm);
// 5. 注册 DRM 设备
drm_dev_register(drm, 0);
// 6. 初始化 fbdev
drm_fbdev_generic_setup(drm, 32);
}
3.2 VOP 驱动 (cambr_vop_drv)
VOP(Video Output Processor)是显示硬件的核心,负责:
- Plane 管理
- Overlay 叠加
- DMA 传输
- DPI 接口输出
// cambr_vop_drv.c
static int cambr_vop_bind(struct device *dev, struct device *mdev, void *data)
{
// 1. 创建 Primary Plane
ret = cambr_plane_create(drm, &cndrm_vop->plane, DRM_PLANE_TYPE_PRIMARY);
// 2. 创建 CRTC
ret = cambr_crtc_init(drm, &cndrm_vop->crtc, &cndrm_vop->plane);
// 3. 注册中断处理
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, vop_irq_handler, ...);
}
3.3 HDMI 驱动 (cambr_hdmi_drv)
HDMI 驱动负责:
- Encoder 创建与配置
- Connector 检测与模式获取
// cambr_hdmi_drv.c
static int cambr_hdmi_bind(struct device *dev, struct device *mdev, void *data)
{
// 1. 初始化 Encoder
ret = drm_encoder_init(drm, &cndrm_hdmi->encoder,
DRM_MODE_ENCODER_TMDS, NULL);
// 2. 初始化 Connector
ret = drm_connector_init(drm, &cndrm_hdmi->connector,
DRM_MODE_CONNECTOR_HDMIA);
// 3. 关联 Encoder 与 Connector
drm_connector_attach_encoder(&cndrm_hdmi->connector,
&cndrm_hdmi->encoder);
}
四、CRTC 与 Plane 实现
4.1 Plane 原子操作
Plane 在 Atomic 模式下工作,核心函数:
// cambr_drm_crtc.c
static void cambr_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
// 1. 获取 GEM 对象
gem = drm_fb_dma_get_gem_obj(new_state->fb, 0);
layer_mode.addr = gem->dma_addr;
// 2. 配置 OVL 层
vop_ovl_config(cndrm_vop->regs, OVL_LAYER2, &layer_mode);
// 3. 配置 RVDMA
vop_rvdma_config(cndrm_vop->regs, RCHN_GRP2, &layer_mode);
// 4. 更新地址
vop_update_plane(cndrm_vop->regs, RCHN_GRP2, &layer_mode);
// 5. 启用 OVL 层
vop_ovl_enable(cndrm_vop->regs, OVL_LAYER2, OVL_ENABLE);
}
4.2 CRTC 模式设置
static const struct drm_crtc_helper_funcs cambr_crtc_helper_funcs = {
.mode_valid = cambr_crtc_mode_valid,
.mode_fixup = cambr_crtc_mode_fixup,
.mode_set_nofb = cambr_crtc_mode_set_nofb,
.atomic_flush = cambr_crtc_atomic_flush,
.atomic_enable = cambr_crtc_atomic_enable,
.atomic_disable = cambr_crtc_atomic_disable,
};
static void cambr_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
// 1. 开启 VBlank 中断
reg_clear(cndrm_vop->regs, VO_CMN_INTR_MASK,
RG_DISP_SOF_INTR_MASK);
// 2. 开启 DPI
reg_write(cndrm_vop->regs, DISP_DPI_EN, 0x00800005);
}
4.3 格式支持与 CSC
支持的格式:
const struct cndrm_format_info cndrm_format_infos[] = {
{ .fourcc = DRM_FORMAT_ARGB8888, .pixel_format = PIXEL_FORMAT_ARGB8888_PACKED },
{ .fourcc = DRM_FORMAT_XRGB8888, .pixel_format = PIXEL_FORMAT_XRGB8888_PACKED },
{ .fourcc = DRM_FORMAT_NV12, .csc_format = CSC_YUV420_RGB,
.csc_conv_type = CSC_BT709_RGB, .pixel_format = PIXEL_FORMAT_YUV420_SEMI_PLANAR },
{ .fourcc = DRM_FORMAT_NV21, .csc_format = CSC_YUV420_RGB,
.csc_conv_type = CSC_BT709_RGB, .pixel_format = PIXEL_FORMAT_YVU420_SEMI_PLANAR },
{ .fourcc = DRM_FORMAT_NV16, .csc_format = CSC_YUV422_RGB,
.csc_conv_type = CSC_BT709_RGB, .pixel_format = PIXEL_FORMAT_YUV422_SEMI_PLANAR },
{ .fourcc = DRM_FORMAT_NV61, .csc_format = CSC_YUV422_RGB,
.csc_conv_type = CSC_BT709_RGB, .pixel_format = PIXEL_FORMAT_YVU422_SEMI_PLANAR },
};
CSC (Color Space Conversion) 配置:
if (format->is_yuv == true) {
// 配置 YUV->RGB 转换
reg_write_field(cndrm_vop->regs, OVL_LAYER0_CSC_MAIN_CFG,
0x00, 0x4, layer_mode.csc_format);
reg_write_field(cndrm_vop->regs, OVL_LAYER0_CSC_MAIN_CFG,
0x04, 0x4, layer_mode.csc_conv_type);
// 启用 CSC
reg_clear(cndrm_vop->regs, OVL_LAYER0_CSC_MAIN_CFG, CSC_BYPASS);
} else {
// 绕过 CSC
reg_set(cndrm_vop->regs, OVL_LAYER0_CSC_MAIN_CFG, CSC_BYPASS);
}
五、Component 框架详解
该驱动使用 Linux Component 框架管理多设备:
// 1. 主设备定义
static const struct component_master_ops cambr_drm_ops = {
.bind = cambr_drm_bind,
.unbind = cambr_drm_unbind,
};
// 2. 子设备绑定
static const struct component_ops cambr_vop_ops = {
.bind = cambr_vop_bind,
.unbind = cambr_vop_unbind,
};
// 3. 设备树解析与组件匹配
for (i = 0; ; i++) {
np = of_parse_phandle(dev->of_node, "ports", i);
if (!np) break;
// 添加 ports 节点
component_match_add(dev, &match, compare_of, np);
// 遍历每个 port 的 endpoint
for_each_endpoint_of_node(np, ep) {
remote = of_graph_get_remote_port_parent(ep);
if (remote && of_device_is_available(remote)) {
// 添加远程设备
component_match_add(dev, &match, compare_of, remote);
}
}
}
// 4. 注册主设备
component_master_add_with_match(&pdev->dev, &cambr_drm_ops, match);
六、GEM 与 DMA-BUF 集成
使用 drm_gem_dma_helper 简化 GEM 实现:
// 驱动定义使用宏自动生成 GEM 操作
DEFINE_DRM_GEM_DMA_FOPS(cambr_drm_driver_fops);
static struct drm_driver cambr_drm_driver = {
// ...
DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_dma_dumb_create),
};
这会自动实现:
gem_create:创建 GEM 对象gem_mmap:内存映射dumb_create:创建 dumb bufferdumb_map_offset:获取 mmap offset
Framebuffer 映射:
static const struct drm_mode_config_funcs cambr_drm_mode_config_funcs = {
.fb_create = drm_gem_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
七、关键寄存器配置
7.1 时钟与复位初始化
void vosys_crg_init(const void __iomem *regs)
{
// 视频系统时钟使能
reg_write(regs, VOSYS_CRG_VOPROC0_VCLK_CLK_SEL, 0x00020002);
reg_write(regs, VOSYS_CRG_CLK_EN_0, 0x80008000);
reg_write(regs, VOSYS_CRG_CLK_EN_1, 0xFFC7FFC7);
reg_write(regs, VOSYS_CRG_CLK_EN_2, 0xFFEFFFEF);
reg_write(regs, VO_PROC_CRG_CLK_EN, 0xFFFFFFFF);
// 复位控制
reg_write(regs, VOSYS_CRG_RST_CTRL_0, 0xFFFFFFFF);
reg_write(regs, VOSYS_CRG_RST_CTRL_1, 0xFFFFFFFF);
reg_write(regs, VOSYS_CRG_RST_CTRL_2, 0xFFFFFF9F);
}
7.2 DPI 时序配置
void vop_dpi_config(void __iomem *regs)
{
// DPI 控制
reg_write(regs, DISP_DPI_CL_CTRL, 0x00);
// 时序参数
reg_write(regs, DISP_DPI_TIM_CFG0, 0x00580094); // Hsync/Vsync
reg_write(regs, DISP_DPI_TIM_CFG1, 0x0780002C); // Vbp/Vfp
reg_write(regs, DISP_DPI_TIM_CFG4, 0x00040024); // Vact
reg_write(regs, DISP_DPI_TIM_CFG5, 0x04380005); // Hact high
reg_write(regs, DISP_DPI_TIM_CFG6, 0x04380780); // Hact low
// 数据复用配置
reg_write(regs, DISP_DPI_DATA_MUX4, 0x3bcdeb38);
reg_write(regs, DISP_DPI_OUTSTAGE_CFG, 0x00002406);
}
八、总结
该 Cn DRM 驱动展示了现代 Linux DRM 子系统的完整实现模式:
| 特性 | 实现方式 |
|---|---|
| 多组件管理 | Component 框架 |
| 内存管理 | GEM DMA Helper |
| 模式设置 | Atomic Mode Setting |
| 显示引擎 | VOP (OVL + RVDMA + DPI) |
| 输出接口 | HDMI (TMDS) |
| 格式支持 | ARGB8888, XRGB8888, NV12/21, NV16/61 |
| 色彩空间 | BT.709 YUV→RGB |
这个驱动是学习 DRM 子系统的优秀范例,涵盖了从用户空间到底层硬件的完整数据通路。
感谢阅读!
