接前一篇文章:libdrm全解析二十二 —— 源码全解析(19)
本文参考以下博文:
DRM 驱动程序开发(VKMS)
特此致谢!
前一篇文章已提到,drmModeGetResources函数值得深入地进行讲解。本回就来深入讲解一下这个函数。为了便于理解,再次贴出函数代码,如下:
drm_public drmModeResPtr drmModeGetResources(int fd)
{
struct drm_mode_card_res res, counts;
drmModeResPtr r = 0;
retry:
memclear(res);
if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
return 0;
counts = res;
if (res.count_fbs) {
res.fb_id_ptr = VOID2U64(drmMalloc(res.count_fbs*sizeof(uint32_t)));
if (!res.fb_id_ptr)
goto err_allocs;
}
if (res.count_crtcs) {
res.crtc_id_ptr = VOID2U64(drmMalloc(res.count_crtcs*sizeof(uint32_t)));
if (!res.crtc_id_ptr)
goto err_allocs;
}
if (res.count_connectors) {
res.connector_id_ptr = VOID2U64(drmMalloc(res.count_connectors*sizeof(uint32_t)));
if (!res.connector_id_ptr)
goto err_allocs;
}
if (res.count_encoders) {
res.encoder_id_ptr = VOID2U64(drmMalloc(res.count_encoders*sizeof(uint32_t)));
if (!res.encoder_id_ptr)
goto err_allocs;
}
if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
goto err_allocs;
/* The number of available connectors and etc may have changed with a
* hotplug event in between the ioctls, in which case the field is
* silently ignored by the kernel.
*/
if (counts.count_fbs < res.count_fbs ||
counts.count_crtcs < res.count_crtcs ||
counts.count_connectors < res.count_connectors ||
counts.count_encoders < res.count_encoders)
{
drmFree(U642VOID(res.fb_id_ptr));
drmFree(U642VOID(res.crtc_id_ptr));
drmFree(U642VOID(res.connector_id_ptr));
drmFree(U642VOID(res.encoder_id_ptr));
goto retry;
}
/*
* return
*/
if (!(r = drmMalloc(sizeof(*r))))
goto err_allocs;
r->min_width = res.min_width;
r->max_width = res.max_width;
r->min_height = res.min_height;
r->max_height = res.max_height;
r->count_fbs = res.count_fbs;
r->count_crtcs = res.count_crtcs;
r->count_connectors = res.count_connectors;
r->count_encoders = res.count_encoders;
r->fbs = drmAllocCpy(U642VOID(res.fb_id_ptr), res.count_fbs, sizeof(uint32_t));
r->crtcs = drmAllocCpy(U642VOID(res.crtc_id_ptr), res.count_crtcs, sizeof(uint32_t));
r->connectors = drmAllocCpy(U642VOID(res.connector_id_ptr), res.count_connectors, sizeof(uint32_t));
r->encoders = drmAllocCpy(U642VOID(res.encoder_id_ptr), res.count_encoders, sizeof(uint32_t));
if ((res.count_fbs && !r->fbs) ||
(res.count_crtcs && !r->crtcs) ||
(res.count_connectors && !r->connectors) ||
(res.count_encoders && !r->encoders))
{
drmFree(r->fbs);
drmFree(r->crtcs);
drmFree(r->connectors);
drmFree(r->encoders);
drmFree(r);
r = 0;
}
err_allocs:
drmFree(U642VOID(res.fb_id_ptr));
drmFree(U642VOID(res.crtc_id_ptr));
drmFree(U642VOID(res.connector_id_ptr));
drmFree(U642VOID(res.encoder_id_ptr));
return r;
}
可以看出,drmModeGetResources函数较长,因此分段来进行解析。
先来看两个结构:struct drm_mode_card_res和drmModeResPtr。
struct drm_mode_card_res前文已经介绍过,为了便于理解,再次贴出源码(include/drm/drm_mode.h中):
struct drm_mode_card_res {
__u64 fb_id_ptr;
__u64 crtc_id_ptr;
__u64 connector_id_ptr;
__u64 encoder_id_ptr;
__u32 count_fbs;
__u32 count_crtcs;
__u32 count_connectors;
__u32 count_encoders;
__u32 min_width;
__u32 max_width;
__u32 min_height;
__u32 max_height;
};
drmModeResPtr在xf86drmMode.h中定义,代码如下:
typedef struct _drmModeRes {
int count_fbs;
uint32_t *fbs;
int count_crtcs;
uint32_t *crtcs;
int count_connectors;
uint32_t *connectors;
int count_encoders;
uint32_t *encoders;
uint32_t min_width, max_width;
uint32_t min_height, max_height;
} drmModeRes, *drmModeResPtr;
通过这两个结构,就可以看出其中有几个概念:fb、crtc、connector和encoder。实际上前边已经接触过很多次了,只是一直没有专门讲一下。为了后续内容的理解,要把这些概念捋清楚。在此讲一下这些概念以及它们之间的关系。
一切还得从头说起。在本系列开篇的时候,给出了以下架构图:
图中libdrm是我们本系列的主题,也是目前我们所熟悉的。与之对应的,是更为复杂和核心的概念:DRM。
DRM是Linux目前主流的图形显示框架。资深一些的BSP或驱动工程师应该知道,以前的显示架构为FrameBuffer(简称FB)。相比于FB架构,DRM更能适应当前日益更新的显示硬件。比如FB原生不支持多层合成、不支持VSYNC、不支持DMA-BUF、不支持异步更新、不支持fence机制等等,而这些功能DRM原生都支持。同时DRM可以统一管理GPU和Display驱动,使得软件架构更为统一,方便管理和维护。
DRM从模块上划分,可以简单分为三部分:libdrm、KMS和GEM:
- libdrm
libdrm的作用之前已经讲过,对底层接口进行封装,向上层提供通用的API接口,主要是对各种IOCTL接口进行封装。
- KMS
Kernel Mode Setting,内核模式设置(实际上还有个UMS,用户模式设置)。所谓Mode setting,其实说白了就两件事:更新画面和设置显示参数。更新画面包括:显示buffer的切换,多图层的合成方式,以及确定每个图层的显示位置;设置显示参数包括:设置分辨率、刷新率、电源状态(休眠唤醒)等。
- GEM
Graphic Execution Manager,图形执行管理器。主要负责显示buffer的分配和释放,也是GPU唯一用到DRM的地方。
DRM的KMS和GEM中包含了很多基本元素,大致如下:
KMS包含:CRTC、ENCODER、CONNECTOR、PLANE、FB等。
GEM包含:DUMP、PRIME、fence。
这些元素之间的关系及整体组成如下图所示:
再通过如下表格进行详细描述:
元素 | 说明 |
---|---|
CRTC | 对显示buffer进行扫描,并产生时序信号的硬件模块,通常指Display Controller |
ENCODER | 负责将CRTC输出的timing时序转换成外部设备所需要的信号的模块,如HDMI转换器或 DSI Controller |
CONNECTOR | 连接物理显示设备的连接器,如HDMI、DisplayPort、DSI总线,通常和Encoder驱动绑定 在一起 |
PLANE | 硬件图层,有的Display硬件支持多层合成显示,但所有的Display Controller至少要有1个 plane |
FB | Framebuffer,单个图层的显示内容,唯一一个和硬件无关的基本元素 |
VBLANK | 软件和硬件的同步机制,RGB时序中的垂直消影区,软件通常使用硬件VSYNC来实现 |
property | 任何你想设置的参数,都可以做成property,是DRM驱动中最灵活、最方便的 Mode setting机制 |
DUMB | 只支持连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景 |
PRIME | 连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存 复杂场景 |
fence | buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题 |
弄清楚了以上概念后,下一篇文章继续分析drmModeGetResources函数。