分配
/* DRM fd 只用于缓冲分配/取物理地址,不需要 SET_MASTER(避免抢 fb0 显示) */
int drm_fd = open("dev/dri/card0");
if(drm_fd < 0){
printf("打开drm设备失败\n");
}
//创建drm_mode_create_dumb向内核申请显存
struct drm_mode_create_dumb create;
memset(&create, 0, sizeof(create)); //清空物理内存
//设置要分配的内存大小,长 宽 像素密度 RGB565的bpp是16,NV12是12 alloc_h高度如果是NV12那么高度要*1.5
create.width = w; create.height = h; create.bpp = bpp;
ioctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); //ioctl去分配
获取
struct drm_rockchip_gem_phys gp;
memset(&gp, 0, sizeof(gp));
gp.handle = create.handle; //拿到分配后的句柄
ioctl(drm_fd, DRM_ROCKCHIP_GEM_GET_PHYS, &gp);
// 拿到 32 位物理地址。无 IOMMU 的 RV1106 上,这是 RGA 硬件能直访内存的唯一途径
gp.phy_addr;
如何分配的概念
DRM、CMA和DMA的联系,本质上是DRM框架通过GEM内存管理器,根据硬件是否具备IOMMU,来选择使用CMA或SHMEM作为底层内存分配器,以满足不同硬件对内存的连续性需求。
关键角色:硬件显示控制器 (VOP) 与 IOMMU
理解三者的关系,首先要明白瑞芯微的VOP(Video Output Processor),也就是显示控制器。
- VOP的任务:VOP需要从内存中读取图像数据,然后转换成屏幕信号。
- 关键变量:IOMMU:VOP读取内存时,是否支持IOMMU(输入输出内存管理单元) 是关键。
- 有IOMMU:VOP可以通过IOMMU的页表,访问物理上不连续的内存。
- 无IOMMU:VOP只能直接访问物理地址连续的内存。
联系一:DRM GEM 是“调度中心”
瑞芯微的DRM驱动使用GEM (Graphics Execution Manager) 来管理所有图形内存的分配和生命周期。
- 当用户应用(如Wayland)通过
ioctl请求分配显存时,DRM驱动会调用rockchip_gem_create_object()等函数进行处理。 - 此时,DRM GEM扮演“决策者”的角色,根据当前平台的具体情况,决定将内存分配任务交给谁。
联系二:GEM 的两种分配策略:CMA vs SHMEM
瑞芯微的GEM驱动会根据有无IOMMU,选择两种不同的底层分配策略:
- CMA (Contiguous Memory Allocator) 策略:无IOMMU的选择
- 适用场景:当VOP没有IOMMU支持时(例如RK3506平台),GEM会使用CMA来分配内存。
- 工作原理:CMA从系统启动时就预留一大块物理连续的内存池。GEM通过调用CMA的API,从这个池子里申请内存。
- 硬件需求:这种方式分配的内存,VOP无需IOMMU就能直接通过物理地址访问。
- SHMEM 策略:有IOMMU的选择
- 适用场景:当VOP具备IOMMU支持时(例如RK3588等现代平台),GEM会优先使用基于SHMEM的内存分配方式。
- 工作原理:SHMEM通过标准的内存页分配机制,获取的是物理上可能不连续的内存页。
- 硬件需求:VOP需要通过IOMMU的地址映射功能,才能访问这些不连续的内存。
注意:内核中的
DRM_GEM_CMA_HELPER和DRM_GEM_DMA_HELPER等术语,提供了实现这些策略的标准化接口。
联系三:CMA 是 DMA 框架的一部分
你需要知道,CMA本身就是Linux内核DMA(Direct Memory Access)框架的一部分。
- 很多硬件(如摄像头ISP、视频编解码器VPU、2D图形加速器RGA)都需要物理连续的内存才能工作。
- 因此,DRM GEM在需要连续内存时,实际上是借用了内核DMA框架下的CMA分配器来完成任务。所以,DRM与DMA的联系,通过CMA这个桥梁得以建立。