关于sunxi图层设置DISP_LAYER_SET_CONFIG的一个问题
-
通过ioctl函数传递的图层参数,framebuffer地址(即下面的add[0])到底是物理地址还是虚拟地址?这个framebuffer由应用层提供,有没有什么特别要求?比如地址对齐要求、连续内存要求等等。另外哪里能找到说明文档或者参考代码?
/* 选择图层所需的通道以及本图层参数 */ config.channel = 0; config.layer_id = 0; config.info.enable = 1; config.info.mode = LAYER_MODE_BUFFER; config.info.fb.addr[0] = (unsigned long long)mem_in; //FB物理地址 config.info.fb.size[0].width = width; config.info.fb.size[0].height = height; config.info.fb.align[0] = 4; //4bytes align config.info.fb.format = DISP_FORMAT_ARGB_8888; /* 上层调用DE显示引擎所用的ioctl接口 */ arg[0] = 0; //screen_id 0表示选择通路0 arg[1] = (unsigned long)&config; arg[2] = 1;//表示设置图层数量为1 ret = ioctl(disphd, DISP_LAYER_SET_CONFIG, (void *)arg);
-
这是我网上搜到的一张图片,这段代码使用了android的ion库来创建framebuffer。sunxi图层创建必须要用ion库吗?
PS,这段代码是采用DISP_LAYER_SET_CONFIG2配置,所以是用ion memory,那么DISP_LAYER_SET_CONFIG配置呢,DISP_LAYER_SET_CONFIG配置的时候,应用程序该如何准备图层的framebuffer?有没有大神能解释一下
-
-
@xiaowenge 图片没挂,只是不支持外链。
-
另外全志的文档中提到sunxi_display2.h,我发现这个头文件不能直接用于应用程序开发。原因是它引用了很多只有内核中才用到的c语言类型,如s32等。请问全志的sunxi应用程序开发到底要使用什么样的开发环境呢?
-
@laocai tina的sdk有一个libuapi库是对这部分接口的封装,我在v851s上用DISP_LAYER_SET_CONFIG2会导致内核内存泄漏,你可以试试有没有同样的问题
-
@zheng1 之前我只用了tina linux内核,没有安装tina sdk。
等我把tina sdk安装了看看,全志要能提供更详细的使用说明就好了。 -
@laocai 找到了libuapi\src\ion_alloc.c中的代码
/* return virtual address: 0 failed */ void* sunxi_ion_alloc_palloc(int size) { aw_ion_allocation_info_t alloc_data; ion_fd_data_t fd_data; struct ion_handle_data handle_data; struct aw_ion_custom_info custom_data; sunxi_phys_data phys_data; struct user_iommu_param iommu_param; int rest_size = 0; unsigned long addr_phy = 0; unsigned long addr_vir = 0; buffer_node * alloc_buffer = NULL; int ret = 0; char *cache_flag; pthread_mutex_lock(&g_ion_mutex_alloc); if (g_ion_alloc_context == NULL) { loge("call ion_alloc_open\n"); goto ALLOC_OUT; } if (size <= 0) { loge("can not alloc size 0\n"); goto ALLOC_OUT; } memset(&alloc_data, 0, sizeof(alloc_data)); memset(&fd_data, 0, sizeof(fd_data)); alloc_data.aw_len = (size_t)size; alloc_data.aw_align = ION_ALLOC_ALIGN ; #if defined(CONF_KERNEL_IOMMU) alloc_data.aw_heap_id_mask = AW_ION_SYSTEM_HEAP_MASK | AW_ION_CARVEOUT_HEAP_MASK; #else alloc_data.aw_heap_id_mask = AW_ION_DMA_HEAP_MASK | AW_ION_CARVEOUT_HEAP_MASK; #endif cache_flag = getenv("MG_IONBUFFER"); if(cache_flag == NULL || strcmp(cache_flag, "1") != 0){ alloc_data.flags = AW_ION_CACHED_FLAG | AW_ION_CACHED_NEEDS_SYNC_FLAG; } #if CONF_KERNEL_VERSION_OLD ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_ALLOC, &alloc_data); if (ret) { loge("ION_IOC_ALLOC error\n"); goto ALLOC_OUT; } /* get dmabuf fd */ fd_data.handle = alloc_data.handle; ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_MAP, &fd_data); if (ret) { loge("ION_IOC_MAP err, ret %d, dmabuf fd 0x%08x\n", ret, (unsigned int)fd_data.aw_fd); goto ALLOC_OUT; } #else struct aw_ion_new_alloc_data data = { .len = alloc_data.aw_len, .heap_id_mask = alloc_data.aw_heap_id_mask, .flags = alloc_data.flags, }; ret = ioctl(g_ion_alloc_context->fd, AW_ION_IOC_NEW_ALLOC, &data); if (ret < 0) { loge("AW_ION_IOC_NEW_ALLOC error\n"); goto ALLOC_OUT; } fd_data.aw_fd = data.fd; #endif /* mmap to user */ addr_vir = (unsigned long)mmap(NULL, alloc_data.aw_len, \ PROT_READ|PROT_WRITE, MAP_SHARED, fd_data.aw_fd, 0); if ((unsigned long)MAP_FAILED == addr_vir) { loge("mmap err, ret %u\n", (unsigned int)addr_vir); addr_vir = 0; goto ALLOC_OUT; } /* get phy address */ #if defined(CONF_KERNEL_IOMMU) memset(&iommu_param, 0, sizeof(iommu_param)); iommu_param.fd = fd_data.aw_fd; ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_ENGINE_REQ, 0); if (ret) { loge("ENGINE_REQ err, ret %d\n", ret); addr_phy = 0; addr_vir = 0; goto ALLOC_OUT; } ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_GET_IOMMU_ADDR, &iommu_param); if (ret) { loge("GET_IOMMU_ADDR err, ret %d\n", ret); addr_phy = 0; addr_vir = 0; goto ALLOC_OUT; } addr_phy = iommu_param.iommu_addr; #else memset(&phys_data, 0, sizeof(phys_data)); phys_data.handle = alloc_data.handle; phys_data.size = size; custom_data.aw_cmd = ION_IOC_SUNXI_PHYS_ADDR; custom_data.aw_arg = (unsigned long)&phys_data; ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_CUSTOM, &custom_data); if (ret) { loge("ION_IOC_CUSTOM err, ret %d\n", ret); addr_phy = 0; addr_vir = 0; goto ALLOC_OUT; } addr_phy = phys_data.phys_addr; #endif alloc_buffer = (buffer_node *)malloc(sizeof(buffer_node)); if (alloc_buffer == NULL) { loge("malloc buffer node failed"); /* unmmap */ ret = munmap((void*)addr_vir, alloc_data.aw_len); if (ret) { loge("munmap err, ret %d\n", ret); } #if defined(CONF_KERNEL_IOMMU) ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_ENGINE_REL, 0); if (ret) logv("ENGINE_REL failed\n"); ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_FREE_IOMMU_ADDR, &iommu_param); if (ret) logv("FREE_IOMMU_ADDR err, ret %d\n", ret); #endif /* close dmabuf fd */ close(fd_data.aw_fd); /* free buffer */ handle_data.handle = alloc_data.handle; ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_FREE, &handle_data); if(ret) loge("ION_IOC_FREE err, ret %d\n", ret); addr_phy = 0; addr_vir = 0; /* value of MAP_FAILED is -1, should return 0*/ goto ALLOC_OUT; } alloc_buffer->phy = addr_phy; alloc_buffer->vir = addr_vir; alloc_buffer->user_virt = addr_vir; alloc_buffer->size = size; alloc_buffer->fd_data.handle = fd_data.handle; alloc_buffer->fd_data.aw_fd = fd_data.aw_fd; aw_mem_list_add_tail(&alloc_buffer->i_list, &g_ion_alloc_context->list); #if DEBUG_ION_REF==1 cdx_use_mem += size; int i = 0; for (i = 0; i < ION_BUF_LEN; i++) { if (ion_buf_nodes_test[i].addr == 0 && ion_buf_nodes_test[i].size == 0) { ion_buf_nodes_test[i].addr = addr_vir; ion_buf_nodes_test[i].size = size; break; } } if (i>= ION_BUF_LEN) { loge("error, ion buf len is large than [%d]", ION_BUF_LEN); } #endif ALLOC_OUT: pthread_mutex_unlock(&g_ion_mutex_alloc); return (void*)addr_vir; }
根据配置不同,libuapi使用ion设备或者cedar设备来分配内存。
-
@laocai 而选择ion设备还是cedar设备,是由编译选项决定的。如果要采用DISP_LAYER_SET_CONFIG2,必须编译的时候选择SUNXI_ALLOC_IOMMU,不知道我的理解是否正确?我找到的关于全志disp的讨论
choice prompt "Choose alloc method" default SUNXI_ALLOC_CMA help Select how to apply for memory. config SUNXI_ALLOC_CMA bool "sunxi_alloc_cma" config SUNXI_ALLOC_IOMMU bool "sunxi_alloc_iommu" endchoice choice prompt "Choose layer alloc method" default LUAPI_LAYER_ALLOC_CHN help Select how to apply for layer. config LUAPI_LAYER_ALLOC_CHN bool "luapi_layer_alloc_chn" config LUAPI_LAYER_ALLOC_LAY bool "luapi_layer_alloc_lay" endchoice choice prompt "Choose display rendering mode" default SUNXI_DISPLAY_DE help Choose display rendering mode, DE or GPU. config SUNXI_DISPLAY_DE bool "DE" config SUNXI_DISPLAY_GPU bool "GPU" help Define the macro SUNXI_DISPLAY_GPU, use GPU rendering, send the RGBA data after rendering to fb1 display, execute export FRAMEBUFFER=/dev/fb1 before executing the program, no longer support DispSetEnable, DispSetSrcRect, DispSetZorder and DispSetIoctl functions. Define the macro SUNXI_DISPLAY_GPU and SUNXI_DISPLAY_GPU_BUF, use GPU rendering, send the RGBA data after rendering to layer. No longer support DispSetSrcRect functions. Default use. endchoice endif endef define Build/Prepare $(INSTALL_DIR) $(PKG_BUILD_DIR)/ $(CP) ./src $(PKG_BUILD_DIR)/ endef define Build/Configure endef ifeq ($(CONFIG_SUNXI_DISPLAY_TWO),y) TARGET_CFLAGS+=-D__SUNXI_DISPLAY2__ TARGET_CFLAGS+=-D$(KERNEL_VERSION) endif ifeq ($(CONFIG_SUNXI_DISPLAY_ONE),y) TARGET_CFLAGS+=-D__SUNXI_DISPLAY__ TARGET_CFLAGS+=-D$(KERNEL_VERSION) endif ifeq ($(CONFIG_SUNXI_ALLOC_CMA),y) TARGET_CFLAGS+=-DCONF_KERNEL_CMA endif ifeq ($(CONFIG_SUNXI_ALLOC_IOMMU),y) TARGET_CFLAGS+=-DCONF_KERNEL_IOMMU endif
另外我还发现,linux 内核5.4版本的时候,libuapi库无法使用ion设备来分配framebuffer,原因就是代码里面根本没有实现alloc_data的赋值。
#if CONF_KERNEL_VERSION_OLD ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_ALLOC, &alloc_data); if (ret) { loge("ION_IOC_ALLOC error\n"); goto ALLOC_OUT; } /* get dmabuf fd */ fd_data.handle = alloc_data.handle; ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_MAP, &fd_data); if (ret) { loge("ION_IOC_MAP err, ret %d, dmabuf fd 0x%08x\n", ret, (unsigned int)fd_data.aw_fd); goto ALLOC_OUT; } #else struct aw_ion_new_alloc_data data = { .len = alloc_data.aw_len, .heap_id_mask = alloc_data.aw_heap_id_mask, .flags = alloc_data.flags, }; ret = ioctl(g_ion_alloc_context->fd, AW_ION_IOC_NEW_ALLOC, &data); if (ret < 0) { loge("AW_ION_IOC_NEW_ALLOC error\n"); goto ALLOC_OUT; } fd_data.aw_fd = data.fd; #endif
/* get phy address */ #if defined(CONF_KERNEL_IOMMU) memset(&iommu_param, 0, sizeof(iommu_param)); iommu_param.fd = fd_data.aw_fd; ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_ENGINE_REQ, 0); if (ret) { loge("ENGINE_REQ err, ret %d\n", ret); addr_phy = 0; addr_vir = 0; goto ALLOC_OUT; } ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_GET_IOMMU_ADDR, &iommu_param); if (ret) { loge("GET_IOMMU_ADDR err, ret %d\n", ret); addr_phy = 0; addr_vir = 0; goto ALLOC_OUT; } addr_phy = iommu_param.iommu_addr; #else memset(&phys_data, 0, sizeof(phys_data)); phys_data.handle = alloc_data.handle; phys_data.size = size; custom_data.aw_cmd = ION_IOC_SUNXI_PHYS_ADDR; custom_data.aw_arg = (unsigned long)&phys_data; ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_CUSTOM, &custom_data); if (ret) { loge("ION_IOC_CUSTOM err, ret %d\n", ret); addr_phy = 0; addr_vir = 0; goto ALLOC_OUT; } addr_phy = phys_data.phys_addr; #endif
-
@laocai 我找到了一个简单使用libuapi库的例子,在tina-package/blob/main/gui/littlevgl-8/lv_drivers/display/sunximem.c
/** * @file sunximem.c * */ /********************* * INCLUDES *********************/ #include "sunximem.h" #if USE_SUNXIFB_G2D #include <stdio.h> #include <ion_mem_alloc.h> /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STRUCTURES **********************/ /********************** * STATIC PROTOTYPES **********************/ static struct SunxiMemOpsS *memops; /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ bool sunxifb_mem_init() { if (memops) return true; memops = GetMemAdapterOpsS(); if (SunxiMemOpen(memops) < 0) { perror("Error: cannot open ion device"); return false; } return true; } void sunxifb_mem_deinit() { if (memops) { SunxiMemClose(memops); memops = NULL; } } void* sunxifb_mem_alloc(size_t size, char *label) { if (size == 0) { printf("size illegal."); return NULL; } void *alloc = SunxiMemPalloc(memops, size); if (alloc == NULL) { printf("couldn't allocate memory (%lu bytes).", (unsigned long) size); return NULL; } #ifdef LV_USE_SUNXIFB_DEBUG printf("%s: sunxifb_mem_alloc=%p size=%lu bytes\n", label, alloc, (unsigned long) size); #endif /* LV_USE_SUNXIFB_DEBUG */ return alloc; } void sunxifb_mem_free(void **data, char *label) { if (*data != NULL) { #ifdef LV_USE_SUNXIFB_DEBUG printf("%s: sunxifb_mem_free=%p\n", label, *data); #endif /* LV_USE_SUNXIFB_DEBUG */ SunxiMemPfree(memops, *data); *data = NULL; } else { printf("couldn't free memory.\n"); } } void* sunxifb_mem_get_phyaddr(void *data) { if (data != NULL) return SunxiMemGetPhysicAddressCpu(memops, data); else return NULL; } void sunxifb_mem_flush_cache(void *data, size_t size) { SunxiMemFlushCache(memops, data, size); } /********************** * STATIC FUNCTIONS **********************/ #endif
-
@laocai 如果要获得framebuffer的物理地址,就采用这个方法
void* sunxifb_mem_get_phyaddr(void *data) { if (data != NULL) return SunxiMemGetPhysicAddressCpu(memops, data); else return NULL; }
-
@laocai 另外关于sunxi_display2.h文件,我在tina sdk的package中发现了七个路径下有这个文件,前面三个文件的内容各不相同,和内核使用的sunxi_display2.h文件也不同。
allwinner/libAWIspApi/src/isp522/libisp/include/device/sunxi_display2.h
allwinner/tina_multimedia/libcedarx/demo/xplayerdemo/sunxi_display2.h
allwinner/tina_multimedia/libcedarx/demo/recoderdemo/sunxi_display2.h
allwinner/libuapi/src/sunxi_display_v2.h
testtools/tinatest/testcase/base/production/displaytester/common/sunxi_display_v2.h
allwinner/tinacvr/src/middle_ware/sunxi_display_v2.h
gui/minigui/multimedia-test/src/middle_ware/sunxi_display_v2.h比较了一下,和内核头文件最接近的是allwinner/libAWIspApi/src/isp522/libisp/include/device/sunxi_display2.h
allwinner/tinacvr/src/middle_ware/sunxi_display_v2.h和allwinner/libuapi/src/sunxi_display_v2.h比较接近,后者像是在前者基础上修改而来。
-
@zheng1 在 关于sunxi图层设置DISP_LAYER_SET_CONFIG的一个问题 中说:
@laocai tina的sdk有一个libuapi库是对这部分接口的封装,我在v851s上用DISP_LAYER_SET_CONFIG2会导致内核内存泄漏,你可以试试有没有同样的问题
我猜你遇到的问题可能和这段代码有关:
#if defined(CONF_KERNEL_IOMMU) ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_ENGINE_REL, 0); if (ret) logv("ENGINE_REL failed\n"); struct user_iommu_param iommu_param; memset(&iommu_param, 0, sizeof(iommu_param)); iommu_param.fd = tmp->fd_data.aw_fd; ret = ioctl(g_ion_alloc_context->cedar_fd, AW_MEM_FREE_IOMMU_ADDR, &iommu_param); if (ret) logv("FREE_IOMMU_ADDR err, ret %d\n", ret); #endif /*close dma buffer fd*/ close(tmp->fd_data.aw_fd); /* free buffer */ handle_data.handle = tmp->fd_data.handle; ret = ioctl(g_ion_alloc_context->fd, AW_MEM_ION_IOC_FREE, &handle_data); if (ret) logv("TON_IOC_FREE failed\n"); aw_mem_list_del(&tmp->i_list); free(tmp); flag = 1;
handle_data.handle = tmp->fd_data.handle;
内核版本为高版本(5.4)的时候,libuapi库在申请内存时根本没有初始化这个handle。然后释放内存的时候,却又用这个空的handle去调用释放的系统调用,内存根本不可能真正释放。换句话说,这个libuapi库只有在低版本内核上才有可能正常工作;高版本内核上,内存只能申请,无法释放。
-
@laocai 官方文档里也到处都是错误。
红线处正确的代码应该是这样的:
memset(&config, 0, sizeof(struct disp_layer_config)); config.channel = 0;//blending channel config.layer_id = 0;//layer index in the blending channel config.enable = 1; config.info.mode = LAYER_MODE_BUFFER;
-
@laocai 文档上的源代码根本就没编译验证过
-
@laocai 直接把头文件sunxi_display2.h 拷贝出来,然后把编译不过的地方改了,不用的删除掉,像s32就是 signed int , u32 就是 unsgined int
Copyright © 2023 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号