2. fbdev基础回顾
在把显示驱动从 fbdev 迁移到 DRM KMS 之前,必须先回到 fbdev 的原始模型,理解它到底向用户空间暴露了什么、内核内部用什么结构承载这些能力。fbdev 的设计目标非常朴素:把一块可以被 CPU 直接读写、由显示控制器扫描输出的显存抽象成字符设备 /dev/fbX,让应用通过 mmap 拿到像素缓冲,通过 ioctl 查询和修改显示参数。它不负责 GPU 命令提交,也不负责多图层合成,只解决“帧缓冲”这一件事。
一、framebuffer 设备模型:从 /dev/fbX 到显存
fbdev 的设备模型可以概括为“一个字符设备 + 一段显存 + 一组显示参数”。内核为每个 framebuffer 注册一个 fb_info,并创建对应的字符设备节点 /dev/fb0、/dev/fb1。用户空间通过 open 打开设备,通过 ioctl 获取 fb_var_screeninfo 和 fb_fix_screeninfo,再通过 mmap 把显存映射到进程地址空间,之后直接写像素即可看到画面变化。
这里有两个关键结构:fb_var_screeninfo 描述“可变”的显示参数,如分辨率、虚拟分辨率、色深、像素时钟、时序;fb_fix_screeninfo 描述“固定”的硬件属性,如显存物理地址、显存长度、行跨度 line_length、加速器类型。应用通常先读 var 判断是否支持目标分辨率,再读 fix 计算 mmap 长度和行偏移。
二、fb_info 结构:fbdev 驱动的中枢
struct fb_info 是 fbdev 驱动的核心数据结构,它把设备、显存、操作集和显示参数全部聚合在一起。典型字段包括:
struct fb_var_screeninfo var:当前可变显示参数;struct fb_fix_screeninfo fix:固定硬件参数;struct fb_ops *fbops:驱动实现的操作集;struct device *device:关联的设备模型对象;struct fb_info *next:全局 fb 链表指针;char __iomem *screen_base:显存虚拟基地址;unsigned long screen_size:显存大小;struct fb_monspecs monspecs:显示器 EDID 信息;struct work_struct deferred_work:延迟工作,用于热插拔或模式变更。
驱动初始化时通常调用 framebuffer_alloc() 分配 fb_info,填充 var/fix/fbops,再调用 register_framebuffer() 注册。注销时调用 unregister_framebuffer() 和 framebuffer_release()。整个生命周期围绕 fb_info 展开,因此迁移到 DRM 时,第一步往往是把 fb_info 中的显示参数映射到 DRM 的 drm_display_mode、drm_framebuffer 和 drm_crtc 上。
三、fb_ops 操作集:驱动能力的入口
struct fb_ops 是 fbdev 驱动向内核注册的回调集合。用户空间的 ioctl、mmap、read/write 最终都会落到这些回调上。常见成员如下表所示:
| 回调 | 作用 | 迁移关注点 |
|---|---|---|
fb_open | 打开设备时调用 | 对应 DRM open,通常无需特殊处理 |
fb_release | 关闭设备时调用 | 对应 DRM release |
fb_check_var | 校验并调整 var 参数 | 对应 DRM mode_valid / atomic check |
fb_set_par | 应用 var 参数到硬件 | 对应 DRM crtc mode_set / atomic commit |
fb_setcolreg | 设置调色板寄存器 | 对应 DRM gamma / LUT |
fb_blank | 背光/电源管理 | 对应 DRM connector DPMS |
fb_pan_display | 平移显示起始地址 | 对应 DRM plane 位置更新 |
fb_fillrect | 填充矩形 | 对应 DRM 软件渲染或 GPU 加速 |
fb_copyarea | 区域拷贝 | 对应 DRM blit 或 GPU 加速 |
fb_imageblit | 图像位块传输 | 对应 DRM 软件渲染 |
fb_mmap | 映射显存到用户空间 | 对应 DRM dumb buffer mmap |
其中 fb_check_var 和 fb_set_par 是最关键的一对:前者做参数合法性检查,后者真正写寄存器。很多老驱动把时序计算、PLL 配置、DMA 地址设置都塞在 fb_set_par 里,迁移到 DRM 时需要把这些逻辑拆解到 atomic_check 和 atomic_commit 两个阶段。
四、ioctl 接口与典型驱动代码结构
fbdev 的 ioctl 接口定义在 linux/fb.h 中,常用命令包括:
FBIOGET_VSCREENINFO/FBIOPUT_VSCREENINFO:获取/设置可变参数;FBIOGET_FSCREENINFO:获取固定参数;FBIOGETCMAP/FBIOPUTCMAP:获取/设置调色板;FBIOBLANK:屏幕消隐;FBIOPAN_DISPLAY:平移显示;FBIO_WAITFORVSYNC:等待垂直同步。
一个典型的 fbdev 驱动代码结构通常包含以下步骤:
static int myfb_probe(struct platform_device *pdev)
{
struct fb_info *info;
struct myfb_par *par;
int ret;
info = framebuffer_alloc(sizeof(struct myfb_par), &pdev->dev);
if (!info)
return -ENOMEM;
par = info->par;
par->dev = &pdev->dev;
/* 1. 映射寄存器与显存 */
par->regs = devm_ioremap_resource(&pdev->dev, res);
par->screen_base = dma_alloc_coherent(...);
/* 2. 填充 fix */
info->fix.smem_start = par->screen_phys;
info->fix.smem_len = par->screen_size;
info->fix.line_length = width * bpp / 8;
info->fix.type = FB_TYPE_PACKED_PIXELS;
/* 3. 填充 var */
info->var.xres = 1920;
info->var.yres = 1080;
info->var.bits_per_pixel = 32;
/* 4. 绑定操作集 */
info->fbops = &myfb_ops;
/* 5. 注册 */
ret = register_framebuffer(info);
if (ret)
goto err_release;
return 0;
}
对应的 fb_ops 实现通常只填充必要回调,其余用 fb_sys_* 或 cfb_* 辅助函数补齐。例如:
static const struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = myfb_check_var,
.fb_set_par = myfb_set_par,
.fb_setcolreg = myfb_setcolreg,
.fb_blank = myfb_blank,
.fb_pan_display = myfb_pan_display,
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
.fb_mmap = myfb_mmap,
};
五、常见误区与迁移前的复盘要点
fb_set_par 可以直接平移到 DRM 的 mode_set。实际上 DRM 要求先做 atomic check,再提交 commit,且模式设置与 plane 更新是分离的。误区二:把 fb_info->screen_base 直接当作 DRM framebuffer 的映射地址。DRM 使用 GEM 对象管理缓冲,需要创建 dumb buffer 或导入 DMA-BUF。误区三:忽略 fb_pan_display 与 DRM plane 位置更新的语义差异,前者只改扫描起始地址,后者涉及 plane 的 src/dst 坐标和缩放。迁移前建议做一次“能力盘点”:列出当前 fbdev 驱动实现了哪些 fb_ops 回调、支持哪些像素格式、是否支持 pan display、是否支持 gamma、是否依赖 fb_info->par 私有数据。把这些能力映射到 DRM 的 CRTC、plane、connector、encoder 和 GEM 对象上,才能设计出合理的迁移路径。
理解这张图后,迁移工作就有了清晰的对照表:用户空间的 ioctl 语义要保留或替换,fb_info 中的显示参数要映射到 DRM 的 mode 和 framebuffer,fb_ops 中的硬件操作要拆解到 atomic check/commit 和 plane 更新中。fbdev 不是被“废弃”,而是被 DRM KMS 以更现代的方式重新组织。