DRM驱动(十)之page_flip

上一篇了解了下驱动如何上报给vsyncworker vsync;《DRM驱动之drm_vblank》

接下来聊一下另一种与vsync相关的刷图方式。

drm刷图的接口比较多,比如drmModeSetCrtcdrmModeSetPlanedrmModeAtomicCommit等;

最近在看drm中驱动的flip done事件,想到有个接口drmModePageFlip是用到事件回调的形式刷新。

防止刷新撕裂的防止有很多种,比如阻塞,非阻塞(需要用fence来同步),还有就是可以用drmModePageFlipdrmHandleEvent来防止撕裂。

今天我们来通过这个接口,看看drm驱动是如何用事件来防止撕裂的。(主要看事件处理流程,图像的刷新简单过一下)

应用程序

void page_flip_handler(...)
{
    drmModePageFlip(fd, crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, data);
    ...
}

int main(void)
{
    drmEventContext ev = {};

    ev.version = DRM_EVENT_CONTEXT_VERSION;
    ev.page_flip_handler = page_flip_handler;
    ...

    drmModePageFlip(fd, crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, data);
    
    while (1) {
        drmHandleEvent(&ev);
    }
}

drm_public int drmHandleEvent(int fd, drmEventContextPtr evctx)
{
    char buffer[1024];
    int len, i;
    struct drm_event *e;
    struct drm_event_vblank *vblank;
    struct drm_event_crtc_sequence *seq;
    void *user_data;
 
      len = read(fd, buffer, sizeof buffer);
    if (len == 0)
        return 0;
    if (len < (int)sizeof *e)
        return -1;

         i = 0;
    while (i < len) {
        e = (struct drm_event *)(buffer + i);
        switch (e->type) {
        case DRM_EVENT_VBLANK:
                    ... ...
                    break;
          case DRM_EVENT_FLIP_COMPLETE:
            vblank = (struct drm_event_vblank *) e;
            user_data = U642VOID (vblank->user_data);
            evctx->page_flip_handler(fd,
                        vblank->sequence,
                        vblank->tv_sec,
                        vblank->tv_usec,
                        user_data);
            break;
        case DRM_EVENT_CRTC_SEQUENCE:
                        ... ...
                        break;
            default:
            break;
        }
        i += e->length;
    }
    return 0;
}

为了便于分析,我把所有的实现都贴在上面了,没有单拎出来。

drm驱动流程

我们先来看第一个drmModePageFlip内核具体做了什么

drmModePageFlip会推一张图,也会创建drm_pending_vblank_event

这里commit调用的是drm_atomic_nonblocking_commit,此函数会创建workqueue进行刷图处理,因此ioctl不会等待hw配置完成。

应用就可以接着执行下面的内容

    while (1) {
        drmHandleEvent(&ev);
    }

drmHandleEventread(fd, buffer, sizeof buffer);

从drm中读出event事件,当前如果没有事件进程就会休眠。

第一次read肯定是没有事件的,file_priv->event_list是空的。事件是哪里来的呢?我们只要找到谁向file_priv->event_list放事件就可以找到是事件是哪里来的了。

事件发送

我已经找到了,还是看下rockchip实现的中断服务程序

static irqreturn_t vop_isr(int irq, void *data)
{
    ... ...
    if (active_irqs & FS_INTR) {
        drm_crtc_handle_vblank(crtc);
        vop_handle_vblank(vop);
        active_irqs &= ~FS_INTR;
        ret = IRQ_HANDLED;
    }
    ... ...
}

上一篇我们讲过这里会更新vblank count,还有vop_handle_vblank函数,我们来看下

static void vop_handle_vblank(struct vop *vop)
{
... ...

    spin_lock(&drm->event_lock);
    if (vop->event) {
        drm_crtc_send_vblank_event(crtc, vop->event);
        drm_crtc_vblank_put(crtc);
        vop->event = NULL;
    }
 ... ...
}

如果vop里的drm_pending_vblank_event不为空,就会执行drm_crtc_send_vblank_event

分析下drm_crtc_send_vblank_event

drm_crtc_send_vblank_event里的东西不多,其中有两点比较有意思的地方:

  1. e->file_priv为空的话,直接释放e;
  2. 就是我们要找的,把drm_pending_event添加到file_priv->event_list, 并唤醒event_wait;

不知道大家注意没,e->file_priv是在这里赋值的,也就是说不调用drm_mode_page_flip_ioctl,drm_pending_event有可能永远不会放到e->file_priv(没有追,只是猜测)

总结:

app使用drmModePageFlip刷新图像,drm driver会创建一个drm_pending_vblank_event,并且调用drm_atomic_nonblocking_commit把修改配置到hw;

app使用drmHandleEvent一直读取事件,读不到会休眠。display模块vsync中断到来之后调用中断处理函数发送drm_crtc_send_vblank_event,将drm_pending_event添加到file_priv->event_list, 并唤醒event_wait。

app使用drmHandleEvent读到事件,判断事件类型,调用event中的page_flip_handler回调,在page_flip_handler刷新下一帧图像。

  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值