G2D主要功能:
1)旋转:支持90、180、270旋转;
2)scale:放缩;
3)镜像反转:H / V;
4)透明叠加:实现两个rgb图片叠加;
5)格式转换:yuv转rgb等多种格式相互间转换;
6)矩形填充,等诸多功能;
G2D配置
源码目录
tina-v853-docker/kernel/linux-4.9/drivers/char/sunxi_g2d
make kernel_menuconfig 配置
Device Drivers > Character devices > sunxi g2d driver
Device Tree 设备树配置
sun8iw21p1.dtsi路径:
tina-v853-docker/kernel/linux-4.9/arch/arm/boot/dts/sun8iw21p1.dtsi
g2d: g2d@05410000 { compatible = "allwinner,sunxi-g2d"; reg = <0x0 0x05410000 0x0 0xbffff>; interrupts = ; clocks = <&clk_g2d>; iommus = <&mmu_aw 3 1>; status = "okay"; };
注:status 要设定为“okay” 状态。
重新编译内核
使用烧录工具PhoenixSuit 将编译打包好的img镜像烧录到开发板。
adb shell 打开控制终端查看设备节点G2D:
通过G2D设备节点进行操作
static int SampleG2d_G2dOpen(SAMPLE_G2D_CTX *p_g2d_ctx) { int ret = 0; p_g2d_ctx->mG2dFd = open("/dev/g2d", O_RDWR, 0); if (p_g2d_ctx->mG2dFd < 0) { aloge("fatal error! open /dev/g2d failed"); ret = -1; } return ret; }
G2D sample具体应用
G2D sample目录
进行rotation,scale,格式转换
具体实现:将 nv21 格式的1920x1080图转换成rgb888 格式并放缩为640x360 大小。具体用到两个功能,格式转换和放缩。
首先根据1920x1080 nv21 格式以及 640x360 rgb888 格式申请虚拟地址空间以及转换成物理地址(注意:g2d 转换是在物理地址中完成的)
1920x1080 nv21 格式空间大小(输入文件):
Y 占 19201080 = 2073600 字节
UV 占 19201080 / 2 = 1036800 字节
640x360 rgb888 格式空间大小(输出文件):
RGB 占 6403603 = 691200 字节
另外:虚拟地址转换成物理地址使用如下函数:
g2d_getPhyAddrByVirAddr()
申请虚拟空间并转换成物理空间完整函数如下:
static int PrepareFrmBuff(SAMPLE_G2D_CTX *p_g2d_ctx) { SampleG2dConfig *pConfig = NULL; unsigned int size = 0; pConfig = &p_g2d_ctx->mConfigPara; p_g2d_ctx->src_frm_info.frm_width = pConfig->mSrcWidth; p_g2d_ctx->src_frm_info.frm_height = pConfig->mSrcHeight; p_g2d_ctx->dst_frm_info.frm_width = pConfig->mDstWidth; p_g2d_ctx->dst_frm_info.frm_height = pConfig->mDstHeight; size = ALIGN(p_g2d_ctx->src_frm_info.frm_width, 16)*ALIGN(p_g2d_ctx->src_frm_info.frm_height, 16); if(pConfig->mPicFormat == MM_PIXEL_FORMAT_YVU_SEMIPLANAR_420 || pConfig->mPicFormat == MM_PIXEL_FORMAT_YUV_SEMIPLANAR_420) { p_g2d_ctx->src_frm_info.p_vir_addr[0] = (void *)g2d_allocMem(size); if(NULL == p_g2d_ctx->src_frm_info.p_vir_addr[0]) { aloge("malloc_src_frm_y_mem_failed"); return -1; } p_g2d_ctx->src_frm_info.p_vir_addr[1] = (void *)g2d_allocMem(size/2); if(NULL == p_g2d_ctx->src_frm_info.p_vir_addr[1]) { g2d_freeMem(p_g2d_ctx->src_frm_info.p_vir_addr[0]); aloge("malloc_src_frm_c_mem_failed"); return -1; } p_g2d_ctx->src_frm_info.p_phy_addr[0] = (void *)g2d_getPhyAddrByVirAddr(p_g2d_ctx->src_frm_info.p_vir_addr[0]); p_g2d_ctx->src_frm_info.p_phy_addr[1] = (void *)g2d_getPhyAddrByVirAddr(p_g2d_ctx->src_frm_info.p_vir_addr[1]); } if(pConfig->mDstPicFormat == MM_PIXEL_FORMAT_RGB_888) { size = p_g2d_ctx->dst_frm_info.frm_width * p_g2d_ctx->dst_frm_info.frm_height * 3; p_g2d_ctx->dst_frm_info.p_vir_addr[0] = (void *)g2d_allocMem(size); if(NULL == p_g2d_ctx->dst_frm_info.p_vir_addr[0]) { if(p_g2d_ctx->src_frm_info.p_vir_addr[0] != NULL) { g2d_freeMem(p_g2d_ctx->src_frm_info.p_vir_addr[0]); } if(p_g2d_ctx->src_frm_info.p_vir_addr[1] != NULL) { g2d_freeMem(p_g2d_ctx->src_frm_info.p_vir_addr[1]); } aloge("malloc_dst_frm_y_mem_failed"); return -1; } p_g2d_ctx->dst_frm_info.p_phy_addr[0] = (void *)g2d_getPhyAddrByVirAddr(p_g2d_ctx->dst_frm_info.p_vir_addr[0]); } return 0; }
通过fopen 传菜间两个文件句柄,fd_in fd_out 用来操作输入输出两个文件资源。
p_g2d_ctx->fd_in = fopen(p_g2d_ctx->mConfigPara.SrcFile,"r"); if(NULL == p_g2d_ctx->fd_in) { aloge("open src file failed"); ret = -1; goto _err2; } fseek(p_g2d_ctx->fd_in, 0, SEEK_SET); p_g2d_ctx->fd_out = fopen(p_g2d_ctx->mConfigPara.DstFile, "wb"); if (NULL == p_g2d_ctx->fd_out) { aloge("open out file failed"); ret = -1; goto _err2; } fseek(p_g2d_ctx->fd_out, 0, SEEK_SET);
读出 1920x1080 nv21 图资放入 虚拟空间
read_len = p_g2d_ctx->src_frm_info.frm_width * p_g2d_ctx->src_frm_info.frm_height; if(pConfig->mPicFormat == MM_PIXEL_FORMAT_YVU_SEMIPLANAR_420|| pConfig->mPicFormat == MM_PIXEL_FORMAT_YUV_SEMIPLANAR_420) { size1 = fread(p_g2d_ctx->src_frm_info.p_vir_addr[0] , 1, read_len, p_g2d_ctx->fd_in); if(size1 != read_len) { aloge("read_y_data_frm_src_file_invalid"); } size2 = fread(p_g2d_ctx->src_frm_info.p_vir_addr[1], 1, read_len /2, p_g2d_ctx->fd_in); if(size2 != read_len/2) { aloge("read_c_data_frm_src_file_invalid"); } fclose(p_g2d_ctx->fd_in); g2d_flushCache((void *)p_g2d_ctx->src_frm_info.p_vir_addr[0], read_len); g2d_flushCache((void *)p_g2d_ctx->src_frm_info.p_vir_addr[1], read_len/2); }
打开g2d 初始化,并开始转换
ret = SampleG2d_G2dOpen(p_g2d_ctx); if (ret < 0) { aloge("fatal error! open /dev/g2d fail!"); goto _err2; } ret = SampleG2d_G2dConvert(p_g2d_ctx); if (ret < 0) { aloge("fatal error! g2d convert fail!"); goto _close_g2d; } //具体转化函数: static int SampleG2d_G2dConvert_scale(SAMPLE_G2D_CTX *p_g2d_ctx) { int ret = 0; g2d_blt_h blit; g2d_fmt_enh eSrcFormat, eDstFormat; SampleG2dConfig *pConfig = NULL; pConfig = &p_g2d_ctx->mConfigPara; ret = convert_PIXEL_FORMAT_E_to_g2d_fmt_enh(pConfig->mPicFormat, &eSrcFormat); if(ret!=SUCCESS) { aloge("fatal error! src pixel format[0x%x] is invalid!", pConfig->mPicFormat); return -1; } ret = convert_PIXEL_FORMAT_E_to_g2d_fmt_enh(pConfig->mDstPicFormat, &eDstFormat); if(ret!=SUCCESS) { aloge("fatal error! dst pixel format[0x%x] is invalid!", pConfig->mPicFormat); return -1; } //config blit memset(&blit, 0, sizeof(g2d_blt_h)); if(0 != pConfig->mDstRotate) { aloge("fatal_err: rotation can't be performed when do scaling"); } blit.flag_h = G2D_BLT_NONE_H; // angle rotation used // switch(pConfig->mDstRotate) // { // case 0: // blit.flag_h = G2D_BLT_NONE_H; //G2D_ROT_0, G2D_BLT_NONE_H // break; // case 90: // blit.flag_h = G2D_ROT_90; // break; // case 180: // blit.flag_h = G2D_ROT_180; // break; // case 270: // blit.flag_h = G2D_ROT_270; // break; // default: // aloge("fatal error! rotation[%d] is invalid!", pConfig->mDstRotate); // blit.flag_h = G2D_BLT_NONE_H; // break; // } //blit.src_image_h.bbuff = 1; //blit.src_image_h.color = 0xff; blit.src_image_h.format = eSrcFormat; blit.src_image_h.laddr[0] = (unsigned int)p_g2d_ctx->src_frm_info.p_phy_addr[0]; blit.src_image_h.laddr[1] = (unsigned int)p_g2d_ctx->src_frm_info.p_phy_addr[1]; blit.src_image_h.laddr[2] = (unsigned int)p_g2d_ctx->src_frm_info.p_phy_addr[2]; //blit.src_image_h.haddr[] = blit.src_image_h.width = p_g2d_ctx->src_frm_info.frm_width; blit.src_image_h.height = p_g2d_ctx->src_frm_info.frm_height; blit.src_image_h.align[0] = 0; blit.src_image_h.align[1] = 0; blit.src_image_h.align[2] = 0; blit.src_image_h.clip_rect.x = pConfig->mSrcRectX; blit.src_image_h.clip_rect.y = pConfig->mSrcRectY; blit.src_image_h.clip_rect.w = pConfig->mSrcRectW; blit.src_image_h.clip_rect.h = pConfig->mSrcRectH; blit.src_image_h.gamut = G2D_BT601; blit.src_image_h.bpremul = 0; //blit.src_image_h.alpha = 0xff; blit.src_image_h.mode = G2D_PIXEL_ALPHA; //G2D_PIXEL_ALPHA, G2D_GLOBAL_ALPHA blit.src_image_h.fd = -1; blit.src_image_h.use_phy_addr = 1; //blit.dst_image_h.bbuff = 1; //blit.dst_image_h.color = 0xff; blit.dst_image_h.format = eDstFormat; blit.dst_image_h.laddr[0] = (unsigned int)p_g2d_ctx->dst_frm_info.p_phy_addr[0]; blit.dst_image_h.laddr[1] = (unsigned int)p_g2d_ctx->dst_frm_info.p_phy_addr[1]; blit.dst_image_h.laddr[2] = (unsigned int)p_g2d_ctx->dst_frm_info.p_phy_addr[2]; //blit.dst_image_h.haddr[] = blit.dst_image_h.width = p_g2d_ctx->dst_frm_info.frm_width; blit.dst_image_h.height = p_g2d_ctx->dst_frm_info.frm_height; blit.dst_image_h.align[0] = 0; blit.dst_image_h.align[1] = 0; blit.dst_image_h.align[2] = 0; blit.dst_image_h.clip_rect.x = pConfig->mDstRectX; blit.dst_image_h.clip_rect.y = pConfig->mDstRectY; blit.dst_image_h.clip_rect.w = pConfig->mDstRectW; blit.dst_image_h.clip_rect.h = pConfig->mDstRectH; blit.dst_image_h.gamut = G2D_BT601; blit.dst_image_h.bpremul = 0; //blit.dst_image_h.alpha = 0xff; blit.dst_image_h.mode = G2D_PIXEL_ALPHA; //G2D_PIXEL_ALPHA, G2D_GLOBAL_ALPHA blit.dst_image_h.fd = -1; blit.dst_image_h.use_phy_addr = 1; ret = ioctl(p_g2d_ctx->mG2dFd, G2D_CMD_BITBLT_H, (unsigned long)&blit); if(ret < 0) { aloge("fatal error! bit-block(image) transfer failed[%d]", ret); system("cd /sys/class/sunxi_dump;echo 0x14A8000,0x14A8100 > dump;cat dump"); } return ret; }
转化完成后将640x360 rgb888 图资通过fd_out句柄存储起来
if(pConfig->mDstPicFormat == MM_PIXEL_FORMAT_RGB_888) { out_len = p_g2d_ctx->dst_frm_info.frm_width * p_g2d_ctx->dst_frm_info.frm_height *3; g2d_flushCache((void *)p_g2d_ctx->dst_frm_info.p_vir_addr[0], out_len); fwrite(p_g2d_ctx->dst_frm_info.p_vir_addr[0], 1, out_len, p_g2d_ctx->fd_out); }
转化步骤总结
通过步骤3中的模块化分析,可以看出g2d 转化大概分为一下步骤:
为打开 iomen 初始化;
为src以及dst图资申请虚拟地址空间并转换成物理地址空间;
将src图资放入虚拟地址空间,然后自动映射到物理地址空间;
打开g2d 设备节点进行转换(最重要的一环,可以通过手册分析具体怎么转换的);
将转换好的dst图资保存起来;
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !