前言
本文阅读总结自David Rheinsberg的drm-howto代码,代码的编排非常用心。注释也写的相当详尽,是很好的DRM应用程序入门参考资料
modeset.c
1、打开/dev/dri/card0节点
检查支持DRM_CAP_DUMB_BUFFER:drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb)
2、查找可用的display设备,即connector+crtc+framebuffer组合
获取drmModeRes,该结构包含所有需要的信息:drmModeGetResources(fd);
获取drmModeConnector:conn = drmModeGetConnector(fd, res->connectors[i])
检查该Connector是否没有使用,即没有panel连接上
直接使用第一个mode(分辨率刷新率):memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode))
查找一个CRTC,在获得CRTC之前需要先获取Encoders:enc = drmModeGetEncoder(fd, conn->encoder_id)
crtc = enc->crtc_id
3、创建一个framebuffer
创建一个dumb buffer:drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq)
创建一个framebuffer实例:drmModeAddFB(fd, dev->width, dev->height, 24, 32, dev->stride,dev->handle, &dev->fb);
准备dumb buffer映射:drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq)
进行实际的mmap内存映射:dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, mreq.offset);
将framebuffer置0黑色:memset(dev->map, 0, dev->size);
4、准备好以上对象后,就可以编程使CRTC连接到framebuffer和connector的组合:drmModeSetCrtc(fd, iter->crtc, iter->fb, 0, 0,
&iter->conn, 1, &iter->mode);
5、后面需要修改屏幕显示内容时,只需要修改framebuffer的内容即可
modeset-double-buffered.c
1、如果只有一个framebuffer,当我们向front buffer写入更新画面写入一半时,这个时候如果CTRC开始扫描输出下一帧,可能就会看到flickering
2、double-buffered就是申请两块framebuffer,front buffer用于当前的显示buffer,每次写入都更新back-buffer,然后调用drmModeSetCrtc(fd, iter->crtc, buf->fb, 0, 0, &iter->conn, 1, &iter->mode)交换front buffer和back-buffer
modeset-vsync.c
1、vertical-blank是CRTC扫描显示一帧framebuffer和下一帧画面之间的时间
2、drmModePageFlip():调度一个buffer-flip为下一个vblank事件,然后通知我们。它需要一个CRTC-id, fb-id和data-pointer做参数然后调度page-flip。这是完全异步的并且立即返回
3、drmHandleEvent():当page-flip发生时,DRM-fd就会变成可读的,然后我们就可以调用drmHandleEvent(),这个接口会读所有的vblank/page-flip 事件,然后调用modeset_page_flip_event()回调函数
4、modeset_page_flip_event():回调函数,函数里面重新更新了framebuffer,然后调用drmModePageFlip()调度一个page-flip为下次vsync
5、drmModePageFlip(fd, dev->crtc, buf->fb, DRM_MODE_PAGE_FLIP_EVENT, dev);
如果调用drmModePageFlip之后马上又调用一遍,将会返回EBUSY如果page-flip还没有发生。所有总是会传递DRM_MODE_PAGE_FLIP_EVENT,当page-flip发生了能够得到通知以便可以开始渲染下一帧
modeset-atomic.c
1、Plane:硬件图层,用于合成和叠加图像。三种类型:primary, cursor and overlay
一个Plane用法的例子:想象一个静态的电脑桌面,只有鼠标光标在移动,这个时候就不需要每次移动光标时都计算整个画面,我们可以只更新cursor plane然后硬件会自动将其叠加到primary plane
2、设计到多图层时会有同步问题,KMS API不是原子的,所有你要更新primary plane和overlay planes用不同的IOCTLS,这会导致tearing和blocking等问题。atomic API可以解决这种问题
3、drmModeAtomicCommit():所有的plane可以在一个IOCTL里更新,这可以是异步的,也可以是完全阻塞的
4、Property:由3部分组成:name、id、value。id是该property在DRM框架中全局唯一的标识符。操作property:通过name来获取property,通过id来操作property,通过value来修改property的值,完成这些操作的接口,就是atomic接口
5、drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1):被DRM_MODE_PROP_ATOMIC修饰过的property,只有应用程序支持Atomic操作是才可见,因此需要设置DRM_CLIENT_CAP_ATOMIC这个flag,告诉DRM驱动该应用程序支持Atomic操作
6、Property操作步骤:1.drmModeGetProperty()来获取property的相关信息 2.drmModeAtomicAddProperty()来修改property的值 3.drmModeAtomicCommit()发起真正的修改请求
7、获取connector属性:props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
property_crtc_id = get_property_id(fd, props, “CRTC_ID”);
8、获取crtc属性:props = drmModeObjectGetProperties(fd, crtc_id, DRM_MODE_OBJECT_CRTC);
property_active = get_property_id(fd, props, “ACTIVE”);
property_mode_id = get_property_id(fd, props, “MODE_ID”);
9、开始modesetting:req = drmModeAtomicAlloc();
drmModeAtomicAddProperty(req, crtc_id, property_active, 1);
drmModeAtomicAddProperty(req, crtc_id, property_mode_id, blob_id);
drmModeAtomicAddProperty(req, conn_id, property_crtc_id, crtc_id);
drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
以上代码并没有对fb_id的操作,所以作用只是初始CRTC、ENCODER、CONNECTOR硬件以及建立硬件链路关系,并不会显示framebuffer的内容