4. VBlank原理

在DRM显示驱动开发中,VBlank(Vertical Blanking,垂直消隐期)是贯穿整个显示流水线的核心时间基准。无论是硬件光标的坐标更新、页面翻转(Page Flip)的提交时机,还是帧缓冲的同步释放,都依赖VBlank提供的精确时间窗口。理解VBlank的物理定义、中断产生路径以及它在刷新周期中的实际作用,是掌握显示同步机制的第一步。

一、垂直消隐期的物理定义

显示器并非瞬间完成一整帧画面的更新,而是按照从左到右、从上到下的顺序逐行扫描。以典型的1080p@60Hz时序为例,一帧的完整周期由以下部分组成:

  • 水平有效区(H Active):每行真正输出像素的区间,1920个像素时钟。
  • 水平消隐区(H Blank):行与行之间的回扫间隙,包含前肩(Front Porch)、同步脉冲(Sync Pulse)和后肩(Back Porch)。
  • 垂直有效区(V Active):1080行有效图像数据。
  • 垂直消隐区(V Blank):最后一行扫描完成后到下一帧第一行开始之间的空闲区间,同样由前肩、垂直同步脉冲和后肩构成。

垂直消隐期的存在有两个根本原因:一是CRT时代电子束需要时间从屏幕底部回扫到顶部;二是在现代LCD/OLED面板中,它提供了面板驱动IC重新锁存一行数据、准备下一帧的缓冲时间。V Blank期间不传输有效像素,但像素时钟仍在运行,因此它是一段“有节拍、无图像”的确定时间窗口。

关键参数:以1920×1080@60Hz为例,像素时钟约148.5MHz,水平总周期2200像素时钟,垂直总周期1125行。其中V Active为1080行,V Blank为45行,对应约0.75ms的消隐时间。这0.75ms就是驱动可以安全操作硬件光标或提交Flip的黄金窗口。

二、VBlank中断的产生机制

VBlank中断由显示控制器(Display Controller,如Intel i915、AMD DCN、Rockchip VOP等)在扫描进入垂直消隐区时产生。其典型信号链如下:

  1. 显示控制器内部的时序发生器(Timing Generator)持续计数当前扫描行号与像素位置。
  2. 当行计数器达到V Active结束值(如1080)时,控制器拉高VBlank状态位。
  3. 该状态位经过中断使能寄存器(如I915_DISPLAY_INTERRUPT_ENABLE)的门控,若对应Pipe的VBlank中断已使能,则向CPU发出IRQ。
  4. 内核中断处理程序读取中断身份寄存器,确认是哪个Pipe的VBlank,随后调用DRM注册的VBlank处理回调。
  5. DRM核心通过drm_crtc_handle_vblank()更新软件VBlank计数器,并唤醒等待队列上的任务。

在Linux DRM框架中,VBlank中断的注册与使能通常由CRTC的enable_vblank回调完成。以下是一个简化的驱动侧注册示意:

static int my_crtc_enable_vblank(struct drm_crtc *crtc)
{
    struct my_device *dev = to_my_device(crtc->dev);
    u32 pipe = drm_crtc_index(crtc);

    /* 使能对应Pipe的VBlank中断 */
    regmap_update_bits(dev->regs, INTR_ENABLE(pipe),
                       VBLANK_INT_EN, VBLANK_INT_EN);
    return 0;
}

static void my_crtc_disable_vblank(struct drm_crtc *crtc)
{
    struct my_device *dev = to_my_device(crtc->dev);
    u32 pipe = drm_crtc_index(crtc);

    regmap_update_bits(dev->regs, INTR_ENABLE(pipe),
                       VBLANK_INT_EN, 0);
}

中断服务程序(ISR)中需要读取状态寄存器并清除中断标志,否则会触发中断风暴。典型日志如下:

[  120.456789] my_disp 0000:00:02.0: vblank irq pipe=0 count=1024
[  120.473456] my_disp 0000:00:02.0: vblank irq pipe=0 count=1025
[  120.490123] my_disp 0000:00:02.0: vblank irq pipe=0 count=1026

三、VBlank在显示刷新中的作用

VBlank不仅仅是一个中断信号,它是整个显示同步体系的“节拍器”。其核心作用可归纳为以下四个方面:

作用说明典型API/机制
页面翻转同步在VBlank期间切换扫描out的帧缓冲地址,避免撕裂drm_atomic_commit + page_flip
硬件光标更新在消隐期写入光标坐标/图像寄存器,防止光标闪烁或残影drm_mode_cursor_ioctl
帧缓冲释放确认前一帧已扫描完成,安全释放其GEM对象drm_framebuffer_cleanup
时间戳与统计为应用提供精确的刷新时间基准drmWaitVBlank / vblank_event

以硬件光标为例,若在扫描过程中直接修改光标坐标寄存器,可能出现光标图像与坐标不同步,导致光标“撕裂”或短暂显示在错误位置。正确的做法是在VBlank中断到来后,通过工作队列或原子提交将新的坐标写入寄存器,确保下一帧扫描开始时新坐标已生效。

在DRM中,用户空间可以通过drmWaitVBlank或DRM_IOCTL_WAIT_VBLANK等待VBlank,实现与刷新率对齐的动画或视频播放。驱动侧则通过drm_crtc_send_vblank_event()向用户空间发送VBlank事件。

四、案例与常见误区

案例:双缓冲Page Flip的VBlank时序

假设应用提交了一个新的帧缓冲FB2,驱动在VBlank中断中完成地址切换。其时序如下:

  1. 应用调用drmModePageFlip,驱动将Flip请求挂入CRTC的pending队列。
  2. 当前帧FB1继续扫描,直到VBlank到来。
  3. VBlank中断触发,驱动在ISR或工作队列中写入扫描out地址为FB2。
  4. 下一帧从FB2开始扫描,同时驱动发送Page Flip完成事件。
  5. 应用收到事件后,可安全复用或释放FB1。

常见误区:

  • 误区一:VBlank中断等于帧结束。实际上VBlank是帧结束后的消隐期,此时最后一帧已扫描完,但下一帧尚未开始。
  • 误区二:在VBlank中断中做耗时操作。ISR应尽量短小,复杂操作应下放到工作队列或原子提交路径,否则会丢失后续VBlank。
  • 误区三:忽略多Pipe的VBlank独立性。多显示管道各自有独立的VBlank时序,驱动必须按Pipe分别使能和清除中断。
  • 误区四:未处理VBlank中断丢失。若中断被屏蔽或处理过慢,软件VBlank计数器会与硬件脱节,导致Page Flip超时。
调试VBlank问题时,应优先检查:中断使能寄存器是否置位、中断状态寄存器是否被正确清除、CRTC的VBlank回调是否注册、以及/sys/kernel/debug/dri/0/下的vblank计数是否递增。若计数停滞,通常意味着中断未到达或未被正确处理。

总结来说,VBlank是显示驱动中连接硬件时序与软件同步的桥梁。它定义了帧与帧之间的安全操作窗口,通过中断机制通知CPU,并在Page Flip、硬件光标、帧缓冲生命周期管理等场景中发挥不可替代的作用。掌握VBlank的物理定义、中断路径和同步语义,是后续实现无撕裂显示和流畅光标交互的基础。