631 lines
14 KiB
C
631 lines
14 KiB
C
/*
|
|
* Copyright (C) 2022-2023 ArtInChip Technology Co., Ltd.
|
|
* Authors: ZeQuan Liang <zequan.liang@artinchip.com>
|
|
*/
|
|
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <linux/fb.h>
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/dma-heap.h>
|
|
|
|
#include "artinchip/sample_base.h"
|
|
#include "video/artinchip_fb.h"
|
|
#include "mpp_ge.h"
|
|
|
|
#define BYTE_ALIGN(x, byte) (((x) + ((byte) - 1))&(~((byte) - 1)))
|
|
|
|
#define FB_DEV "/dev/fb0"
|
|
#define DMA_HEAP_DEV "/dev/dma_heap/reserved"
|
|
|
|
#define ALPHA_SRC_PATH DATADIR"/alpha_src.bmp"
|
|
#define ALPHA_DST_PATH DATADIR"/alpha_dst.bmp"
|
|
#define ALPHA_BACK_GROUND_PATH DATADIR"/alpha_back_ground.bmp"
|
|
|
|
#define PICTURE_SRC 0
|
|
#define PICTURE_DST 1
|
|
#define PICTURE_BACK_GROUND 2
|
|
#define PICTURE_NUM 3
|
|
#define ALPHA_BACK_GROUND_NUM 14
|
|
|
|
/* bmp header format */
|
|
#pragma pack(push, 1)
|
|
struct bmp_header {
|
|
unsigned short type;
|
|
unsigned int size;
|
|
unsigned short reserved1;
|
|
unsigned short reserved2;
|
|
unsigned int offset;
|
|
|
|
unsigned int dib_size;
|
|
int width;
|
|
int height;
|
|
unsigned short planes;
|
|
unsigned short bit_count;
|
|
unsigned int compression;
|
|
unsigned int size_image;
|
|
int x_meter;
|
|
int y_meter;
|
|
unsigned int clr_used;
|
|
unsigned int clr_important;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
/* alpha test structure */
|
|
struct alpha_data {
|
|
int dma_fd;
|
|
char *bmp_path;
|
|
int bmp_fd;
|
|
int bmp_reverse;
|
|
unsigned short file_type;
|
|
unsigned int bmp_stride;
|
|
unsigned int bmp_width;
|
|
unsigned int bmp_height;
|
|
unsigned int height;
|
|
unsigned int stride;
|
|
|
|
unsigned int src_alpha_mode;
|
|
unsigned int src_global_alpha;
|
|
unsigned int dst_alpha_mode;
|
|
unsigned int dst_global_alpha;
|
|
};
|
|
|
|
/* screen information structure */
|
|
struct g_screen {
|
|
int screen_w;
|
|
int screen_h;
|
|
int fb_fd;
|
|
int fb_len;
|
|
int fb_stride;
|
|
unsigned int fb_format;
|
|
unsigned int fb_phy;
|
|
unsigned char *fb_buf;
|
|
};
|
|
|
|
static struct alpha_data alpha[PICTURE_NUM] ={0};
|
|
static struct g_screen screen = {0};
|
|
|
|
static void usage(char *app)
|
|
{
|
|
printf("Usage: %s [Options], built on %s %s\n\n", app, __DATE__, __TIME__);
|
|
printf(
|
|
"\t-s, --src_alpha_mode, Select src_alpha_mode (default 0), "
|
|
"0: pixel alpha mode, 1: global alpha mode, 2: mixded alpha mode"
|
|
"\n"
|
|
"\t-g, --src_alpha_global, Set src_alpha_global value (default 0), "
|
|
"source global alpha value (0~255)"
|
|
"\n\n"
|
|
"\t-d, --dst_alpha_mode, Select dst_alpha_mode (default 0), "
|
|
"0: pixel alpha mode, 1: global alpha mode, 2: mixded alpha mode"
|
|
"\n"
|
|
"\t-p, --dst_alpha_global, Select dst_alpha_global value (default 0), "
|
|
"destination global alpha value (0~255)"
|
|
"\n"
|
|
|
|
"\t-u, --usage\n");
|
|
}
|
|
|
|
static int fb_open(void)
|
|
{
|
|
struct fb_fix_screeninfo fix;
|
|
struct fb_var_screeninfo var;
|
|
struct aicfb_layer_data layer;
|
|
|
|
screen.fb_fd = open(FB_DEV, O_RDWR);
|
|
if (screen.fb_fd == -1) {
|
|
ERR("open %s", FB_DEV);
|
|
return -1;
|
|
}
|
|
|
|
if (ioctl(screen.fb_fd, FBIOGET_FSCREENINFO, &fix) < 0) {
|
|
ERR("ioctl FBIOGET_FSCREENINFO");
|
|
close(screen.fb_fd);
|
|
return -1;
|
|
}
|
|
|
|
if (ioctl(screen.fb_fd, FBIOGET_VSCREENINFO, &var) < 0) {
|
|
ERR("ioctl FBIOGET_VSCREENINFO");
|
|
close(screen.fb_fd);
|
|
return -1;
|
|
}
|
|
|
|
if(ioctl(screen.fb_fd, AICFB_GET_FB_LAYER_CONFIG, &layer) < 0) {
|
|
ERR("ioctl FBIOGET_VSCREENINFO");
|
|
close(screen.fb_fd);
|
|
return -1;
|
|
}
|
|
|
|
screen.screen_w = var.xres;
|
|
screen.screen_h = var.yres;
|
|
screen.fb_len = fix.smem_len;
|
|
screen.fb_phy = fix.smem_start;
|
|
screen.fb_stride = layer.buf.stride[0];
|
|
screen.fb_format = layer.buf.format;
|
|
|
|
screen.fb_buf = mmap(NULL, screen.fb_len,
|
|
PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
|
|
screen.fb_fd, 0);
|
|
|
|
if (screen.fb_buf == MAP_FAILED) {
|
|
ERR("mmap framebuffer");
|
|
close(screen.fb_fd);
|
|
|
|
screen.fb_fd = -1;
|
|
screen.fb_buf = NULL;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fb_close(void)
|
|
{
|
|
if (!screen.fb_buf)
|
|
munmap(screen.fb_buf, screen.fb_len);
|
|
|
|
if (screen.fb_fd > 0)
|
|
close(screen.fb_fd);
|
|
}
|
|
|
|
static int bmp_open(void)
|
|
{
|
|
int i = 0;
|
|
struct bmp_header bmp_header = {0};
|
|
|
|
/* set bmp file path */
|
|
alpha[0].bmp_path = ALPHA_SRC_PATH;
|
|
alpha[1].bmp_path = ALPHA_DST_PATH;
|
|
alpha[2].bmp_path = ALPHA_BACK_GROUND_PATH;
|
|
|
|
for(i = 0; i < PICTURE_NUM; i++) {
|
|
alpha[i].bmp_fd = open(alpha[i].bmp_path, O_RDWR);
|
|
if (alpha[i].bmp_fd < 0) {
|
|
ERR("Failed to open %s, errno: %d[%s]\n",
|
|
alpha[i].bmp_path, errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (read(alpha[i].bmp_fd, &bmp_header, sizeof(struct bmp_header)) !=
|
|
sizeof(struct bmp_header)) {
|
|
ERR("read bmp file error \n");
|
|
return -1;
|
|
}
|
|
|
|
alpha[i].file_type = bmp_header.type;
|
|
alpha[i].bmp_width = bmp_header.width;
|
|
alpha[i].bmp_height = abs(bmp_header.height);
|
|
|
|
if (bmp_header.height < 0)
|
|
alpha[i].bmp_reverse = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bmp_close(void)
|
|
{
|
|
int i = 0;
|
|
|
|
for(i = 0; i < PICTURE_NUM; i++) {
|
|
if (alpha[i].bmp_fd > 0)
|
|
close(alpha[i].bmp_fd);
|
|
}
|
|
}
|
|
|
|
static void set_alpha(int src_alpha_mode, int dst_alpha_mode,
|
|
int src_alpha_global, int dst_alpha_global)
|
|
{
|
|
int i = 0;
|
|
|
|
alpha[0].src_alpha_mode = src_alpha_mode;
|
|
alpha[0].src_global_alpha = src_alpha_global;
|
|
|
|
alpha[1].dst_alpha_mode = dst_alpha_mode;
|
|
alpha[1].dst_global_alpha = dst_alpha_global;
|
|
|
|
/* Set stride, height, bmp_stride, when bmp file format is argb8888 */
|
|
for(i = 0; i < PICTURE_NUM; i++) {
|
|
alpha[i].stride = BYTE_ALIGN((alpha[i].bmp_width * 4), 8);
|
|
alpha[i].height = alpha[i].bmp_height;
|
|
alpha[i].bmp_stride = BYTE_ALIGN((alpha[i].bmp_width * 4), 4);
|
|
}
|
|
}
|
|
|
|
static int dmabuf_heap_open()
|
|
{
|
|
int fd = 0;
|
|
|
|
fd = open(DMA_HEAP_DEV, O_RDWR);
|
|
if (fd < 0) {
|
|
ERR("Failed to open %s, errno: %d[%s]\n",
|
|
DMA_HEAP_DEV, errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
static void dmabuf_heap_close(int heap_fd)
|
|
{
|
|
if (heap_fd > 0)
|
|
close(heap_fd);
|
|
}
|
|
|
|
static int dmabuf_request(int heap_fd)
|
|
{
|
|
int i = 0;
|
|
struct dma_heap_allocation_data data = {0};
|
|
|
|
for(i = 0; i < PICTURE_NUM; i++) {
|
|
data.fd = 0;
|
|
data.len = alpha[i].stride * alpha[i].height;
|
|
data.fd_flags = O_RDWR | O_CLOEXEC;
|
|
data.heap_flags = 0;
|
|
|
|
if (ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data) < 0) {
|
|
ERR("ioctl() failed! errno: %d[%s]\n", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
alpha[i].dma_fd = data.fd;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dmabuf_release()
|
|
{
|
|
int i = 0;
|
|
|
|
for(i = 0; i < PICTURE_NUM; i++) {
|
|
if (alpha[i].dma_fd > 0)
|
|
close (alpha[i].dma_fd);
|
|
}
|
|
}
|
|
|
|
static int draw_bmp_dmabuf(int pic_num)
|
|
{
|
|
int i = 0;
|
|
int ret = 0;
|
|
int len = 0;
|
|
unsigned char *buf = NULL;
|
|
unsigned char *dmabuf = NULL;
|
|
|
|
if (alpha[pic_num].file_type != 0x4D42) {
|
|
ERR("not a Bmp file\n");
|
|
return -1;
|
|
}
|
|
|
|
len = alpha[pic_num].stride * alpha[pic_num].height;
|
|
dmabuf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, alpha[pic_num].dma_fd, 0);
|
|
if (dmabuf == MAP_FAILED) {
|
|
ERR("mmap() failed! errno: %d[%s]\n", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/* read from bottom to top */
|
|
if (alpha[i].bmp_reverse == 1) {
|
|
buf = dmabuf;
|
|
for (i = 0; i < alpha[pic_num].bmp_height; i++) {
|
|
ret = read(alpha[pic_num].bmp_fd, buf,alpha[pic_num].bmp_stride);
|
|
if (ret != alpha[pic_num].bmp_stride) {
|
|
ERR("read(%d) . errno: %d[%s]\n", alpha[pic_num].bmp_stride,
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
buf += alpha[pic_num].stride;
|
|
}
|
|
/* read from top to bottom */
|
|
} else {
|
|
for (i = alpha[pic_num].bmp_height -1; i >= 0; i--) {
|
|
buf = dmabuf + (i * alpha[pic_num].stride);
|
|
ret = read(alpha[pic_num].bmp_fd, buf,alpha[pic_num].bmp_stride);
|
|
if (ret != alpha[pic_num].bmp_stride) {
|
|
ERR("read(%d) . errno: %d[%s]\n", alpha[pic_num].bmp_stride,
|
|
errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
munmap(dmabuf, len);
|
|
lseek(alpha[pic_num].bmp_fd, sizeof(struct bmp_header), SEEK_SET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ge_add_pic_dmabuf(struct mpp_ge *ge)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < PICTURE_NUM; i++)
|
|
{
|
|
if (alpha[i].dma_fd > 0)
|
|
mpp_ge_add_dmabuf(ge, alpha[i].dma_fd);
|
|
}
|
|
}
|
|
|
|
static void ge_rm_pic_dmabuf(struct mpp_ge *ge)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < PICTURE_NUM; i++)
|
|
{
|
|
if (alpha[i].dma_fd > 0)
|
|
mpp_ge_rm_dmabuf(ge, alpha[i].dma_fd);
|
|
}
|
|
}
|
|
|
|
static int set_display_pos(int *pos_x, int *pos_y, int alpha_rules)
|
|
{
|
|
static int pos[][2] = {
|
|
{450, 420}, {640, 420}, {70, 60}, {450, 60}, {640, 60},
|
|
{830, 60}, {70, 240}, {260, 240}, {450, 240}, {640, 240},
|
|
{830, 240}, {260, 420}, {70, 420}, {260, 60}, {0, 0}
|
|
};
|
|
|
|
if (alpha_rules > ALPHA_BACK_GROUND_NUM || alpha_rules < 0) {
|
|
ERR("set_display_pos error, alpha_rules = %d\n", alpha_rules);
|
|
return -1;
|
|
}
|
|
|
|
*pos_x = pos[alpha_rules][0];
|
|
*pos_y = pos[alpha_rules][1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ge_display_set(struct ge_bitblt *blt, int pic_num, int alpha_rules)
|
|
{
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
memset(blt, 0, sizeof(struct ge_bitblt));
|
|
|
|
/* src layer settings */
|
|
blt->src_buf.buf_type = MPP_DMA_BUF_FD;
|
|
blt->src_buf.fd[0] = alpha[pic_num].dma_fd;
|
|
blt->src_buf.stride[0] = alpha[pic_num].stride;
|
|
blt->src_buf.size.width = alpha[pic_num].bmp_width;
|
|
blt->src_buf.size.height = alpha[pic_num].bmp_height;
|
|
blt->src_buf.format = MPP_FMT_ARGB_8888;
|
|
blt->src_buf.crop_en = 0;
|
|
|
|
/* dst layer settings */
|
|
if (set_display_pos(&x, &y, alpha_rules) < 0)
|
|
ERR("ge_display_pos error.\n");
|
|
|
|
blt->dst_buf.buf_type = MPP_PHY_ADDR;
|
|
blt->dst_buf.stride[0] = screen.fb_stride;
|
|
blt->dst_buf.phy_addr[0] = screen.fb_phy;
|
|
blt->dst_buf.size.width = screen.screen_w;
|
|
blt->dst_buf.size.height = screen.screen_h;
|
|
blt->dst_buf.format = screen.fb_format;
|
|
blt->dst_buf.crop_en = 1;
|
|
blt->dst_buf.crop.x = x;
|
|
blt->dst_buf.crop.y = y;
|
|
blt->dst_buf.crop.width = alpha[pic_num].bmp_width;
|
|
blt->dst_buf.crop.height = alpha[pic_num].bmp_height;
|
|
}
|
|
|
|
static void ge_porter_duff_set(struct ge_bitblt *blt, int src_num, int dst_num, int alpha_rule)
|
|
{
|
|
memset(blt, 0, sizeof(struct ge_bitblt));
|
|
|
|
/* src layer settings */
|
|
blt->src_buf.buf_type = MPP_DMA_BUF_FD;
|
|
blt->src_buf.fd[0] = alpha[src_num].dma_fd;
|
|
blt->src_buf.stride[0] = alpha[src_num].stride;
|
|
blt->src_buf.size.width = alpha[src_num].bmp_width;
|
|
blt->src_buf.size.height = alpha[src_num].bmp_height;
|
|
blt->src_buf.format = MPP_FMT_ARGB_8888;
|
|
blt->ctrl.src_alpha_mode = alpha[src_num].src_alpha_mode;
|
|
blt->ctrl.src_global_alpha = alpha[src_num].src_global_alpha;
|
|
|
|
/* dst layer settings */
|
|
blt->dst_buf.buf_type = MPP_DMA_BUF_FD;
|
|
blt->dst_buf.fd[0] = alpha[dst_num].dma_fd;
|
|
blt->dst_buf.stride[0] = alpha[dst_num].stride;
|
|
blt->dst_buf.size.width = alpha[dst_num].bmp_width;
|
|
blt->dst_buf.size.height = alpha[dst_num].bmp_height;
|
|
blt->dst_buf.format = MPP_FMT_ARGB_8888;
|
|
blt->ctrl.dst_alpha_mode = alpha[dst_num].dst_alpha_mode;
|
|
blt->ctrl.dst_global_alpha = alpha[dst_num].dst_global_alpha;
|
|
|
|
blt->ctrl.alpha_en = 1;
|
|
blt->ctrl.alpha_rules = alpha_rule;
|
|
}
|
|
|
|
static int run_ge_bitblt(struct mpp_ge *ge, struct ge_bitblt *blt)
|
|
{
|
|
int ret = -1;
|
|
|
|
ret = mpp_ge_bitblt(ge, blt);
|
|
if (ret) {
|
|
ERR("mpp_ge_bitblt task failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = mpp_ge_emit(ge);
|
|
if (ret) {
|
|
ERR("mpp_ge_emit task failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = mpp_ge_sync(ge);
|
|
if (ret) {
|
|
ERR("mpp_ge_sync task failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ge_porter_duff_test(struct mpp_ge *ge, struct ge_bitblt *blt)
|
|
{
|
|
int i = 0;
|
|
int ret = -1;
|
|
|
|
for (i = 0; i < PICTURE_NUM; i++)
|
|
{
|
|
/* draw all images */
|
|
ret = draw_bmp_dmabuf(i);
|
|
if (ret < 0) {
|
|
ERR("draw_bmp_dmabuf error\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* display background */
|
|
ge_display_set(blt, PICTURE_BACK_GROUND, ALPHA_BACK_GROUND_NUM);
|
|
ret = run_ge_bitblt(ge, blt);
|
|
if (ret < 0) {
|
|
ERR("run_ge_bitblt error\n");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < ALPHA_BACK_GROUND_NUM; i++)
|
|
{
|
|
/* set according to different porter duff rules */
|
|
ge_porter_duff_set(blt, PICTURE_SRC, PICTURE_DST, i);
|
|
ret = run_ge_bitblt(ge, blt);
|
|
if (ret < 0) {
|
|
ERR("run_ge_bitblt error\n");
|
|
return -1;
|
|
}
|
|
|
|
ge_display_set(blt, PICTURE_DST, i);
|
|
ret = run_ge_bitblt(ge, blt);
|
|
if (ret < 0) {
|
|
ERR("run_ge_bitblt error\n");
|
|
return -1;
|
|
}
|
|
|
|
/* redraw dst image */
|
|
ret = draw_bmp_dmabuf(PICTURE_DST);
|
|
if (ret < 0) {
|
|
ERR("draw_bmp_dmabuf error\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int ret = -1;
|
|
int heap_fd = -1;
|
|
int src_alpha_mode = 0;
|
|
int dst_alpha_mode = 0;
|
|
int src_alpha_global = 0;
|
|
int dst_alpha_global = 0;
|
|
struct mpp_ge *ge = NULL;
|
|
struct ge_bitblt blt = {0};
|
|
|
|
const char sopts[] = "us:d:g:p:";
|
|
const struct option lopts[] = {
|
|
{"usage", no_argument, NULL, 'u'},
|
|
{"src_alpha_mode" , required_argument, NULL, 's'},
|
|
{"dst_alpha_mode", required_argument, NULL, 'd'},
|
|
{"src_alpha_global", required_argument, NULL, 'g'},
|
|
{"dst_alpha_global", required_argument, NULL, 'p'},
|
|
};
|
|
|
|
while ((ret = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
|
|
switch (ret) {
|
|
case 's':
|
|
src_alpha_mode = str2int(optarg);
|
|
if ((src_alpha_mode > 3) || (src_alpha_mode < 0)) {
|
|
printf("src_alpha_mode invalid, please input against\n");
|
|
goto EXIT;
|
|
}
|
|
break;
|
|
case 'd':
|
|
dst_alpha_mode = str2int(optarg);
|
|
if ((dst_alpha_mode > 3) || (dst_alpha_mode < 0)) {
|
|
printf("dst_alpha_mode invalid, please input against\n");
|
|
goto EXIT;
|
|
}
|
|
break;
|
|
case 'g':
|
|
src_alpha_global = str2int(optarg);
|
|
if ((src_alpha_global > 255) || (src_alpha_global < 0)) {
|
|
printf("src_alpha_global invalid, please input against\n");
|
|
goto EXIT;
|
|
}
|
|
break;
|
|
case 'p':
|
|
dst_alpha_global = str2int(optarg);
|
|
if ((dst_alpha_global > 255) || (dst_alpha_global < 0)) {
|
|
printf("dst_alpha_global invalid, please input against\n");
|
|
goto EXIT;
|
|
}
|
|
break;
|
|
case 'u':
|
|
usage(argv[0]);
|
|
goto EXIT;
|
|
default:
|
|
ERR("Invalid parameter: %#x\n", ret);
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
ge = mpp_ge_open();
|
|
if (!ge) {
|
|
ERR("mpp_ge_open error\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
ret = fb_open();
|
|
if (ret < 0) {
|
|
ERR("fb_open error\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
ret = bmp_open();
|
|
if (ret < 0) {
|
|
ERR("bmp_open error\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
heap_fd = dmabuf_heap_open();
|
|
if (heap_fd < 0) {
|
|
ERR("dmabuf_heap_open error\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
set_alpha(src_alpha_mode, dst_alpha_mode, src_alpha_global, dst_alpha_global);
|
|
|
|
ret = dmabuf_request(heap_fd);
|
|
if (ret < 0) {
|
|
ERR("dmabuf_request error\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
ge_add_pic_dmabuf(ge);
|
|
|
|
ret = ge_porter_duff_test(ge, &blt);
|
|
if (ret < 0) {
|
|
ERR("ge_porter_duff_test error\n");
|
|
goto EXIT;
|
|
}
|
|
|
|
EXIT:
|
|
ge_rm_pic_dmabuf(ge);
|
|
|
|
dmabuf_release();
|
|
|
|
dmabuf_heap_close(heap_fd);
|
|
|
|
bmp_close();
|
|
|
|
if (ge)
|
|
mpp_ge_close(ge);
|
|
|
|
fb_close();
|
|
|
|
return 0;
|
|
}
|