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);