modesetting_drv之pageflip机制

DRM接口

        目前DRM主要推荐使用的是Atomic(原子的)接口。Property(属性)是 Atomic 操作必须依赖的基本元素。所谓 Property,其实就是把 legacy 接口传入的参数单独抽出来,做成一个个独立的全局属性。通过设置这些属性参数,即可完成对显示参数的设置。

        Property 的结构简单概括主要由3部分组成:name、id 和 value。其中 id 为该 property 在 DRM 框架中全局唯一的标识符。

        采用property机制的好处是:

  1. 减少上层应用接口的维护工作量。当开发者有新的功能需要添加时,无需增加新的函数名和IOCTL,只需在底层驱动中新增一个property,然后在自己的应用程序中获取/操作该property的值即可。
  2. 增强了参数设置的灵活性。一次IOCTL可以同时设置多个property,减少了user space与kernel space切换的次数,同时最大限度的满足了不同硬件对于参数设置的要求,提高了软件效率。

        DRM 中的 property 大多以功能进行划分,并且还定义了一组 Standard Properties,这些标准 properties 在任何平台上都会被创建。

        下表列出了应用程序开发中,常用的 property:

  • CRTC
namedescription
ACTIVECRTC当前的使能状态,一般用于CRTC上下电
MODE_IDCRTC当前所使用的display mode ID, 通过该ID可以找到具体的display mode配置
OUT_FENCE_PTR输出fence指针,指向当前正在显示的buffer所对应的fence id,该fence由DRM驱动创建,供上层应用程序使用,用来表示当前buffer CRTC是否还在占用
DEGAMMA_LUTde-gamma查找表参数
DEGAMMA_LUT_SIZEde-gamma查找表参数长度
CTMColor Transformation Matrix, 颜色矩阵转换参数,3*3的矩阵
GAMMA_LUTgamma查找表参数
GAMMA_LUT_SIZEgamma查找表参数长度
  • PLANE
namedescription
typeplane的类型,CURSOR、PRIMARY或者OVERLAY
FB_ID与当前plane绑定的framebuffer object ID
IN_FENCE_FD与当前plane相关的input fence fd,由buffer的生产者创建,供DRM底层驱动使用,用来标识当前传下来的buffer是否可以开始访问
CRTC_ID当前plane所关联的CRTC object ID,与CONNECTOR中的CRTC_ID属性是同一个property
SRC_X当前framebuffer crop区域的起始偏移x坐标
SRC_Y当前framebuffer crop区域的起始偏移y坐标

SRC_W

当前framebuffer crop区域的宽度
SRC_H当前framebuffer crop区域的高度
CRTC_X屏幕显示区域的起始偏移x坐标
CRTC_Y屏幕显示区域的起始偏移y坐标
CRTC_W屏幕显示区域的宽度
CRTC_H屏幕显示区域的高度
IN_FORMATS用于标识特殊的颜色存储格式,如AFBC、IFBC存储格式,该属性为只读
rotation当前图层的旋转角度
zposition当前图层在所有图层中的z轴顺序
alpha当前图层的global alpha(非pixel alpha),用于多层合成
pixel blend mode当前图层的合成方式,如Pre-multiplied/Coverage等
  • CONNECTOR

name

description
EDIDExtended Display Identification Data, 标识显示器的参数信息,是一种VESA标准数据格式
DPMSDisplay Power Management Signaling,用于控制显示器的电源状态,如休眠唤醒,也是一种VESA标准
link-status用于标识当前connector的连接状态,如Good/Bad
CRTC_ID当前connector所连接的CRTC object ID,与PLANE中CRTC_ID属性是同一个property
PATHDisplayPort专用的属性,主要用于Multi-Stream(MST),即多路显示应用场景
TILE用于标识当前connector是否应用于多屏拼接场景,如平时随处可见的多屏拼接显示的广告大屏幕

Property的类型如下所示:

Property 的类型分为如下几种:

  • enum
  • bitmask
  • range
  • signed range
  • object
  • blob

        以上类型中需要着重介绍的是 object 和 blob 类型,其它类型看名字就知道什么意思,所以就不做介绍了。

  • object

        Object 类型的 property,它的值用 drm_mode_object ID来表示。目前的 DRM 架构中仅用到 2 个 Object Property,它们分别是 "FB_ID" 和 "CRTC_ID" ,它们的 property 值分别表示 framebuffer object ID 和 crtc object ID。

  • blob

        Blob 类型的 property,它的值用 blob object ID 来表示。所谓 blob,说白了就是一个自定义长度的内存块,用来存放自定义的结构体数据。典型的 Blob Property,如 "MODE_ID" ,它的值为 blob object ID,drm 驱动可以根据该 ID 找到对应的 drm_property_blob 结构体,该结构体中存放着 modeinfo 的相关信息。

        下面介绍下如何操作这些property的atomic接口:

        操作 property 非常简单,通过 name 来获取 property,通过 id 来操作 property,通过 value 来修改 property 的值。而完成这些操作的应用接口,就是 libdrm 提供的 Atomic 接口。

        需要记住一点,在libdrm中,所有的操作都是以Object ID来进行访问的,因此要操作property,首先需要获取该property的Object ID。

        伪代码如下:

int main(void)
{
    ...
 drmSetClientCap(DRM_CLIENT_CAP_ATOMIC);

 drmModeObjectGetProperties(...);
 drmModeGetProperty(property_id)
 ...
 drmModeAtomicAlloc();
 drmModeAtomicAddProperty(..., property_id, property_value);
 drmModeAtomicCommit(...);
 drmModeAtomicFree();
    ...
}

        首先通过 drmModeGetProperty() 来获取 property 的相关信息;然后通过 drmModeAtomicAddProperty() 来修改 property 的值;最后通过 drmModeAtomicCommit() 来发起真正的修改请求。

modesetting_drv中关于pageflip接口的调用

    while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
        /* We may have failed because the event queue was full.  Flush it
         * and retry.  If there was nothing to flush, then we failed for
         * some other reason and should just return an error.
         */
        if (ms_flush_drm_events(screen) <= 0) {
            /* Aborting will also decrement flip_count and free(flip). */
            ms_drm_abort_seq(scrn, seq);
            return QUEUE_FLIP_DRM_FLUSH_FAILED;
        }

        /* We flushed some events, so try again. */
        xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
    }

    /* The page flip succeeded. */
    return QUEUE_FLIP_SUCCESS;
}

        其中,do_queue_flip_on_crtc()最终会向内核提交flip请求,ms_flush_drm_events()最终调用

drmHandleEvent()监听上一次flip complete消息。

drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
{
    modesettingPtr ms = modesettingPTR(crtc->scrn);
    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
    int ret;

    if (ms->atomic_modeset) {
        drmModeAtomicReq *req = drmModeAtomicAlloc();

        if (!req)
            return 1;

        ret = plane_add_props(req, crtc, fb_id, crtc->x, crtc->y);
        flags |= DRM_MODE_ATOMIC_NONBLOCK;
        if (ret == 0)
            ret = drmModeAtomicCommit(ms->fd, req, flags, data);
        drmModeAtomicFree(req);
        return ret;
    }

    return drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
                           fb_id, flags, data);
}

        按照上文所述,主要关注plane_add_props(),代码如下所示:

static int
plane_add_props(drmModeAtomicReq *req, xf86CrtcPtr crtc,
                uint32_t fb_id, int x, int y)
{
    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
    int ret = 0;

    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_FB_ID,
                          fb_id);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_ID,
                          fb_id ? drmmode_crtc->mode_crtc->crtc_id : 0);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_X, x << 16);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_Y, y << 16);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_W,
                          crtc->mode.HDisplay << 16);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_H,
                          crtc->mode.VDisplay << 16);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_X, 0);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_Y, 0);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_W,
                          crtc->mode.HDisplay);
    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_H,
                          crtc->mode.VDisplay);

    return ret;
}
static int
plane_add_prop(drmModeAtomicReq *req, drmmode_crtc_private_ptr drmmode_crtc,
               enum drmmode_plane_property prop, uint64_t val)
{
    drmmode_prop_info_ptr info = &drmmode_crtc->props_plane[prop];
    int ret;

    if (!info)
        return -1;

    ret = drmModeAtomicAddProperty(req, drmmode_crtc->plane_id,
                                   info->prop_id, val);
    return (ret <= 0) ? -1 : 0;
}

        下面主要关注fb_id的获取:

int
drmmode_bo_import(drmmode_ptr drmmode, drmmode_bo *bo,
                  uint32_t *fb_id)
{
#ifdef GBM_BO_WITH_MODIFIERS
    modesettingPtr ms = modesettingPTR(drmmode->scrn);
    if (bo->gbm && ms->kms_has_modifiers &&
        gbm_bo_get_modifier(bo->gbm) != DRM_FORMAT_MOD_INVALID) {
        int num_fds;

        num_fds = gbm_bo_get_plane_count(bo->gbm);
        if (num_fds > 0) {
            int i;
            uint32_t format;
            uint32_t handles[4];
            uint32_t strides[4];
            uint32_t offsets[4];
            uint64_t modifiers[4];

            memset(handles, 0, sizeof(handles));
            memset(strides, 0, sizeof(strides));
            memset(offsets, 0, sizeof(offsets));
            memset(modifiers, 0, sizeof(modifiers));

            format = gbm_bo_get_format(bo->gbm);
            format = get_opaque_format(format);
            for (i = 0; i < num_fds; i++) {
                handles[i] = gbm_bo_get_handle_for_plane(bo->gbm, i).u32;
                strides[i] = gbm_bo_get_stride_for_plane(bo->gbm, i);
                offsets[i] = gbm_bo_get_offset(bo->gbm, i);
                modifiers[i] = gbm_bo_get_modifier(bo->gbm);
            }

            return drmModeAddFB2WithModifiers(drmmode->fd, bo->width, bo->height,
                                              format, handles, strides,
                                              offsets, modifiers, fb_id,
                                              DRM_MODE_FB_MODIFIERS);
        }
    }
#endif
    return drmModeAddFB(drmmode->fd, bo->width, bo->height,
                        drmmode->scrn->depth, drmmode->kbpp,
                        drmmode_bo_get_pitch(bo),
                        drmmode_bo_get_handle(bo), fb_id);
}

        主要使用drmModeAddFB2WIthModifiers()或者drmModeAddFB()来为显存创建并绑定framebuffer object,并获取该framebuffer的唯一标识:fb_id。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值