1. DRM driver 与 libdrm库
DRM是Linux内核层的显示驱动框架。它把显示功能封装成 open/close/ioctl 等标准接口,用户空间的程序调用这些接口,驱动设备,显示数据。
libdrm库封装了DRM driver提供的这些接口。通过libdrm库,程序可以间接调用DRM Driver。
2. 打开设备
DRM驱动会在/dev/dri下创建3个设备节点:
card0
controlD64
renderD128
使用drmOpen()打开设备。它根据指定的name和busid,在上述三个设备中选择匹配的节点。
// 打开DRM设备
int drmOpen(const char *name, const char *busid);
也可以使用open(),直接打开指定设备。
int fd;
fd = open ("/dev/dri/card0", O_RDWR, 0);
3. Capability
得到、设置capability。
// 得到指定的capability
int drmGetCap(int fd, uint64_t capability, uint64_t *value);
// 设置指定的capability
int drmSetClientCap(int fd, uint64_t capability, uint64_t value);
4. DRM组件
下图显示各个组件,以及组件之间的关联关系。在DRM的函数名中称作资源(Resource)。如drmModeGetResources()。
每种资源的结构都定义了一个成员,唯一标识这个资源。如drmModeCrtc的crtc_id、drmModeConnector的connector_id、drmModeEncoder的encoder_id、drmModePlane的plane_id。
显示设备的设置、状态,保存在若干结构、变量中,如下图。如drmModeModeInfo,保存设备大小,刷新率。drmModeConnection保存连接状态。
4. 得到资源
drmModeRes结构保存DRM设备的资源集。
drmModeRes的成员fbs、crtcs、connectors、encoders是变长数组,数组长度保存在对应的变量,如count_fbs中。数组中保存的是资源ID。
函数drmModeGetResources()用于得到资源集。
// 得到资源集
drmModeResPtr drmModeGetResources(int fd);
根据资源 ID 得到对应的资源,包括connector、encoder、crtc。
// 得到connector
drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id);
// 得到encoder
drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id);
// 得到crtc
drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId);
与其他资源不同,plane资源不在drmModeRes结构中,而是保存在drmModePlaneRes结构。
成员planes是变长数组,数组长度保存在count_planes中。这里保存是plane的资源ID。
drmModeGetPlaneResources()得到plane资源集。
// 得到plane资源集
drmModePlaneResPtr drmModeGetPlaneResources(int fd);
drmModeGetPlane() 根据Plane ID得到Plane。
// 根据plane ID得到Plane资源
drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id);
5. 资源Property
drmModeProperty结构保存属性。
- 成员prop_id是属性的唯一标识值,
- 成员name是属性名。
drmModeObjectProperties保存资源的属性集。
- 成员props是变长数组,保存属性的prop_id。数组长度保存在count_props中。
- 成员prop_values是与props等长的数组。保存与props数组对应的属性值。其中有标准属性,也可能有厂商自定义属性,比如Plane类型DRM_PLANE_TYPE。
drmModeObjectGetProperties()得到资源属性集。
drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd,
uint32_t object_id,
uint32_t object_type);
drmModeGetProperty()得到属性,drmModeObjectSetProperty()设置属性。
drmModePropertyPtr drmModeGetProperty(int fd, uint32_t property_id);
int drmModeObjectSetProperty(int fd, uint32_t object_id, uint32_t object_type,
uint32_t property_id, uint64_t value);
5. framebuffer ioctl操作
使用drmIoctl()创建、映射framebuffer。
// requests
#define DRM_IOCTL_MODE_CREATE_DUMB ... // 创建framebuffer
#define DRM_IOCTL_MODE_MAP_DUMB ... // 映射framebuffer
int drmIoctl(int fd, unsigned long request, void *arg)
使用Linux API函数 mmap(),将framebuffer映射到用户空间。
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
5. 加入framebuffer
加入framebuffer(不是提交显示!)。
int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
uint32_t pixel_format, // framebuffer图像格式
uint32_t bo_handles[4],
uint32_t pitches[4], uint32_t offsets[4],
uint32_t *buf_id, // framebuffer ID
uint32_t flags);
5. 提交DRM请求
drmModeAtomicReq保存DRM请求。
drmModeAtomicCommit()提交请求。比如,将framebuffer保存的图像提交显示。
int drmModeAtomicCommit(int fd,
drmModeAtomicReqPtr req,
uint32_t flags,
void *user_data);
5. 绑定framebuffer到Plane
将framebuffer绑定到plane。程序更新framebuffer,就是更新plane。
// 将framebuffer绑定到plane
int drmModeSetPlane(int fd,
uint32_t plane_id, // plane id
uint32_t crtc_id,
uint32_t fb_id, // frambuffer id
uint32_t flags,
int32_t crtc_x, int32_t crtc_y, uint32_t crtc_w, uint32_t crtc_h, // 目标区域RECT
uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); // 源区域RECT