RGA (Raster Graphic Acceleration Unit)是一个独立的2D硬件加速器,可用于加速点/线绘制,执行图像缩放、旋转、格式转换等常见的2D图形操作。
本次demo将mipi摄像头,旋转90°并且缩放,YUYV转成RGBA,NV16/12转成RGBA。
1. 依赖的头文件
在SDK下,有文件夹:
/rv1106/media/rga/release_rga_rv1106_arm-rockchip830-linux-uclibcgnueabihf/include/rga
这个文件夹下有RGA所用到的头文件,共 18 个
2. 依赖的库
在SDK下,有文件夹:
/rv1106/media/rga/release_rga_rv1106_arm-rockchip830-linux-uclibcgnueabihf/lib
有两个文件:librga.a,librga.so,一个静态链接一个动态链接
3. 官方案例
在SDK,有文件夹:
/rv1106/media/rga/release_rga_rv1106_arm-rockchip830-linux-uclibcgnueabihf/rga_samples
这里面,各种案例可以借鉴。
4. 依赖到的DRM库
由于rv1106不能通过IOMMU实现连续物理内存分配。所以要通过DRM操作CMA拿到连续的物理内存。
/rv1106/media/libdrm
拿到交叉编译器对应的版本
- libdrm.so
- libdrm.so.2
5. 编写测试
流程
传感器(gc2145)
│ MIPI 信号
▼
rkcif 控制器 ──DMA(硬件直写,CPU 不参与)──► V4L2 缓冲3(内核物理内存,960KB)
│ DQBUF:所有权转移(数据原地不动)
▼
memcpy ①(CPU)
│ 960KB:V4L2 缓冲 → frame_buf(程序内存)
│ (因为缓冲要尽快 QBUF 还给内核)
▼
memcpy ②(CPU)
│ 960KB:frame_buf → CMA 连续内存(g_src.va)
│ (因为 RGA 硬件需要物理地址,只有
│ DRM 申请的 CMA 缓冲能拿到物理地址)
▼
RGA 处理(硬件,CPU 不参与)
│ imcvtcolor(YUYV→RGB565) sync=1 返回即完成
│ imresize(→BGRA 240×240) sync=1 返回即完成
│ imrotate(ROT_270) sync=1 返回即完成
▼
memcpy ③(CPU)
│ 230KB:CMA 缓冲(g_rot.va)→ canvas_buf
│ (RGA 输出是 CMA 内存,LVGL 画布是普通内存,
│ 必须拷;且 sync=1 保证硬件写完才拷)
▼
LVGL 渲染 → memcpy 到 /dev/fb0 显存 → 屏幕
优化(少一次cpu内存拷贝)
RGA 处理(硬件)
│ imcvtcolor / imresize / imrotate
│ sync=1 返回即完成
▼
RGA 输出缓冲 = canvas 缓冲(同一块 CMA 内存)
g_rot.va 直接传给 lv_canvas_set_buffer()
│
▼
LVGL 直接读 g_rot.va 渲染 → fb0 → 屏幕
实现
1.drm分配连续的物理内存
/* 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去分配
2.获取drm分配的物理地址
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;
3.把物理内存地址给到RGA
im_handle_param_t ip;
memset(&ip, 0, sizeof(ip));
//物理内存的大小,以及格式
ip.width = (uint32_t)width;
ip.height = (uint32_t)height;
ip.format = (uint32_t)format;//格式
//将phy_addr传入,注册返回句柄
rga_buffer_handle_t rga_handle = importbuffer_physicaladdr((uint64_t)phy_addr, &ip);
4.drm_mode_map_dump + mmap虚拟内存
用户态想拿这个物理内存的数据,还是要通过虚拟内存拿的,所以还是要通过mmap拿到对应的虚拟内存地址。
struct drm_mode_map_dumb map;
memset(&map, 0, sizeof(map));
map.handle = create.handle;
ioctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
//拿到对应的虚拟内存,用于给应用通过memcpy直接复制使用,应用不能直接拿物理内存数据
int viturlt_addr = mmap(NULL, create.size, PROT_READ | PROT_WRITE, MAP_SHARED,
g_drm_fd, map.offset);
5.使用
这一步是告诉RGA,物理内存地址,大小,然后要进行什么操作,然后写回到内存。
static pthread_mutex_t g_rga_mtx = PTHREAD_MUTEX_INITIALIZER;
// 1. 锁住 RGA
pthread_mutex_lock(&g_rga_mtx);
// 2. 准备输入数据(示例:将一张图像数据拷贝到映射的虚拟内存)
// 假设 input_data 是已经准备好的图像数据指针
memcpy(virt_addr, input_data, create.size);
// 3. 构造 RGA 输入/输出 buffer 分配内存
rga_buffer_t src = wrapbuffer_physicaladdr((void*)phy_addr, width, height, RK_FORMAT_YCbCr_420_SP);
rga_buffer_t dst = wrapbuffer_physicaladdr((void*)other_phy_addr, out_width, out_height, RK_FORMAT_RGB_565);
// 4. 执行 RGA 操作(例如缩放+格式转换)
im_rect src_rect = {0, 0, width, height};
im_rect dst_rect = {0, 0, out_width, out_height};
IM_STATUS ret = improcess(src, dst, {}, {}, src_rect, dst_rect, {}, IM_SYNC);
// 5. 操作完成后,可以从虚拟地址读取结果(如果 dst 也是映射的)
// 或者直接使用输出 buffer 的物理地址供其他硬件使用
// 6. 解锁
pthread_mutex_unlock(&g_rga_mtx);