libdrm全解析二十三 —— 源码全解析(20)

接前一篇文章: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函数。

猜你喜欢

转载自blog.csdn.net/phmatthaus/article/details/132496242