drm中如何找crtc,vblank page flip实现

/* retrieve resources */

drmModeRes *res;

res = drmModeGetResources(fd);

通过drmModeGetResources能获取到有限个connector和crtc。而某个connector是否能与某个crtc一起协同工作,需要encoder的支持。每个connector都有有限个可供使用的encoder列表,而encoder需要与crtc适配上才能工作。

每个连接器都有一个可供使用的编码器的有限列表,可以通过conn->count_encoders来遍历每个连接器对应的编码器。每个编码器只能与有限的crtc列表一起工作,crtc只有有限个:res->count_crtcs,与连接器一样的少 res->count_connectors。

 

/*
 * modeset_find_crtc(fd, res, conn, dev): This small helper tries to find a
 * suitable CRTC for the given connector. We have actually have to introduce one
 * more DRM object to make this more clear: Encoders.
 * Encoders help the CRTC to convert data from a framebuffer into the right
 * format that can be used for the chosen connector. We do not have to
 * understand any more of these conversions to make use of it. However, you must
 * know that each connector has a limited list of encoders that it can use. And
 * each encoder can only work with a limited list of CRTCs. So what we do is
 * trying each encoder that is available and looking for a CRTC that this
 * encoder can work with. If we find the first working combination, we are happy
 * and write it into the @dev structure.
 * But before iterating all available encoders, we first try the currently
 * active encoder+crtc on a connector to avoid a full modeset.
 *
 * However, before we can use a CRTC we must make sure that no other device,
 * that we setup previously, is already using this CRTC. Remember, we can only
 * drive one connector per CRTC! So we simply iterate through the "modeset_list"
 * of previously setup devices and check that this CRTC wasn't used before.
 * Otherwise, we continue with the next CRTC/Encoder combination.
 */

static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn,
                 struct modeset_dev *dev)
{
    drmModeEncoder *enc;
    unsigned int i, j;
    int32_t crtc;
    struct modeset_dev *iter;

    /* first try the currently conected encoder+crtc */
    if (conn->encoder_id)
        enc = drmModeGetEncoder(fd, conn->encoder_id);
    else
        enc = NULL;

    if (enc) {
        if (enc->crtc_id) {
            crtc = enc->crtc_id;
            for (iter = modeset_list; iter; iter = iter->next) {
                if (iter->crtc == crtc) {
                    crtc = -1;
                    break;
                }
            }

            if (crtc >= 0) {
                drmModeFreeEncoder(enc);
                dev->crtc = crtc;
                return 0;
            }
        }

        drmModeFreeEncoder(enc);
    }

    /* If the connector is not currently bound to an encoder or if the
     * encoder+crtc is already used by another connector (actually unlikely
     * but lets be safe), iterate all other available encoders to find a
     * matching CRTC. */
    for (i = 0; i < conn->count_encoders; ++i) {
        enc = drmModeGetEncoder(fd, conn->encoders[i]);
        if (!enc) {
            fprintf(stderr, "cannot retrieve encoder %u:%u (%d): %m\n",
                i, conn->encoders[i], errno);
            continue;
        }

        /* iterate all global CRTCs */
        for (j = 0; j < res->count_crtcs; ++j) {
            /* check whether this CRTC works with the encoder */
            if (!(enc->possible_crtcs & (1 << j)))
                continue;

            /* check that no other device already uses this CRTC */
            crtc = res->crtcs[j];
            for (iter = modeset_list; iter; iter = iter->next) {
                if (iter->crtc == crtc) {
                    crtc = -1;
                    break;
                }
            }

            /* we have found a CRTC, so save it and return */
            if (crtc >= 0) {
                drmModeFreeEncoder(enc);
                dev->crtc = crtc;
                return 0;
            }
        }

        drmModeFreeEncoder(enc);
    }

    fprintf(stderr, "cannot find suitable CRTC for connector %u\n",
        conn->connector_id);
    return -ENOENT;
}

vblank:垂直空白是时间段:当显示控制器暂停扫描帧缓冲区时。之后垂直空白结束后,帧缓冲区再次逐行扫描并接着是一个垂直空白。


调用者调用drmModePageFlip函数为下一个vblank安排缓冲区翻转,当下一个vblank时间段到了后,内核会使用上次drmModePageFlip传入的buf->fb以完成下一次的缓冲区扫描操作.
当内核完成页面翻转工作后,DRM fd将变得可读.此时调用者可以再次调用drmModePageFlip函数为下一个vblank安排缓冲区翻转。

 

vblank_handler 函数用于处理显示垂直同步信号 (VBlank) 的中断处理程序,在 Linux 内核中使用 DRM 子系统时比较常见。下面是一个 vblank_handler 函数的使用示例: ```c #include <stdio.h> #include <stdbool.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <xf86drm.h> #include <xf86drmMode.h> static volatile bool vblank_occurred = false; static void vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { vblank_occurred = true; } int main(int argc, char *argv[]) { int fd = open("/dev/dri/card0", O_RDWR); drmEventContext evctx = { .version = DRM_EVENT_CONTEXT_VERSION, .vblank_handler = vblank_handler, }; drmModeRes *resources = drmModeGetResources(fd); drmModeConnector *connector = drmModeGetConnector(fd, resources->connectors[0]); drmModeCrtc *crtc = drmModeGetCrtc(fd, connector->crtc_id); drmModeModeInfo mode = connector->modes[0]; uint32_t fb_id, handle; drmModeCreateDumb create_arg = { .width = mode.hdisplay, .height = mode.vdisplay, .bpp = 32, }; drmModeAddFB add_arg = { .width = mode.hdisplay, .height = mode.vdisplay, .pitch = create_arg.width * 4, .depth = 24, .bpp = 32, }; drmModeAtomicReq *req; int ret; ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg); if (ret < 0) { fprintf(stderr, "Failed to create dumb buffer: %s\n", strerror(errno)); return 1; } fb_id = add_arg.fb_id = create_arg.handle; ret = drmModeAddFB(fd, add_arg.width, add_arg.height, add_arg.depth, add_arg.bpp, add_arg.pitch, create_arg.handle, &fb_id); if (ret < 0) { fprintf(stderr, "Failed to add framebuffer: %s\n", strerror(errno)); goto err_fb; } ret = drmModeSetCrtc(fd, crtc->crtc_id, fb_id, 0, 0, &connector->connector_id, 1, &mode); if (ret < 0) { fprintf(stderr, "Failed to set CRTC: %s\n", strerror(errno)); goto err_fb; } req = drmModeAtomicAlloc(); drmModeAtomicAddProperty(req, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, DRM_MODE_PROP_ACTIVE, 1); drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); while (true) { while (!vblank_occurred) drmHandleEvent(fd, &evctx); vblank_occurred = false; /* Draw to buffer here */ drmModePageFlip(fd, crtc->crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, &vblank_occurred); } drmModeAtomicFree(req); drmModeFreeCrtc(crtc); drmModeFreeConnector(connector); drmModeFreeResources(resources); drmModeRmFB(fd, fb_id); drmModeRmDumb(fd, &create_arg); close(fd); return 0; err_fb: drmModeRmFB(fd, fb_id); drmModeRmDumb(fd, &create_arg); close(fd); return 1; } ``` 在上述示例中,我们首先打开 /dev/dri/card0 设备文件,然后通过 drmModeGetResources 和 drmModeGetConnector 函数获取显示器的资源信息和连接器信息。接着,我们创建一个基于 VBlank 中断的事件处理程序 vblank_handler,并将其与 drmEventContext 结构体关联起来。在主循环中,我们使用 drmHandleEvent 函数监听事件,当 VBlank 中断发生时,vblank_handler 函数将会被调用。在 vblank_handler 函数中,设置 vblank_occurred 标志并退出函数。在主循环中,我们可以检查 vblank_occurred 标志是否为真,如果为真,则执行一些绘制操作,并使用 drmModePageFlip 函数请求显示器进行页面翻转。通过这种方式,我们可以使用 vblank_handler 函数来实现基于 VBlank 中断的显示管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunxiaopengsun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值