/* RetroArch - A frontend for libretro. * Copyright (C) 2015-2017 - Manuel Alfayate * Copyright (C) 2011-2017 - Daniel De Matteis * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "../../config.h" #endif #ifdef HAVE_MENU #include "../../menu/menu_driver.h" #endif #include "../font_driver.h" #include "../../retroarch.h" #include "../../verbosity.h" #define NUMPAGES 2 /* Lowlevel SunxiG2D functions block */ #define FBIOGET_LAYER_HDL_0 0x4700 #define FBIOGET_LAYER_HDL_1 0x4701 #define DISP_FORMAT_RGB565 0x5 #define DISP_FORMAT_ARGB8888 0xa #define DISP_MOD_INTERLEAVED 0x1 #define DISP_SEQ_ARGB 0x0 #define DISP_SEQ_P10 0xd #define DISP_BT601 = 0, #define DISP_LAYER_WORK_MODE_NORMAL 0 #define DISP_LAYER_WORK_MODE_SCALER 4 #define DISP_CMD_VERSION 0x00 /* for tracking the ioctls API/ABI */ #define SUNXI_DISP_VERSION_MAJOR 1 #define SUNXI_DISP_VERSION_MINOR 0 #define SUNXI_DISP_VERSION ((SUNXI_DISP_VERSION_MAJOR << 16) | SUNXI_DISP_VERSION_MINOR) #define SUNXI_DISP_VERSION_MAJOR_GET(x) (((x) >> 16) & 0x7FFF) #define SUNXI_DISP_VERSION_MINOR_GET(x) ((x) & 0xFFFF) typedef struct { __s32 x; __s32 y; __u32 width; __u32 height; } __disp_rect_t; typedef struct { __u32 width; __u32 height; } __disp_rectsz_t; typedef struct { __s32 x; __s32 y; } __disp_pos_t; typedef enum tag_DISP_CMD { /* ----layer---- */ DISP_CMD_LAYER_REQUEST = 0x40, DISP_CMD_LAYER_RELEASE = 0x41, DISP_CMD_LAYER_OPEN = 0x42, DISP_CMD_LAYER_CLOSE = 0x43, DISP_CMD_LAYER_SET_FB = 0x44, DISP_CMD_LAYER_GET_FB = 0x45, DISP_CMD_LAYER_SET_SRC_WINDOW = 0x46, DISP_CMD_LAYER_GET_SRC_WINDOW = 0x47, DISP_CMD_LAYER_SET_SCN_WINDOW = 0x48, DISP_CMD_LAYER_GET_SCN_WINDOW = 0x49, DISP_CMD_LAYER_SET_PARA = 0x4a, DISP_CMD_LAYER_GET_PARA = 0x4b } __disp_cmd_t; typedef struct { int fd_fb; int fd_disp; int fb_id; /* /dev/fb0 = 0, /dev/fb1 = 1 */ int xres, yres, bits_per_pixel; uint8_t *framebuffer_addr; /* mmapped address */ uintptr_t framebuffer_paddr; /* physical address */ uint32_t framebuffer_size; /* total size of the framebuffer */ int framebuffer_height;/* virtual vertical resolution */ uint32_t gfx_layer_size; /* the size of the primary layer */ /* Layers support */ int gfx_layer_id; int layer_id; int layer_has_scaler; int layer_buf_x, layer_buf_y, layer_buf_w, layer_buf_h; int layer_win_x, layer_win_y; int layer_scaler_is_enabled; int layer_format; } sunxi_disp_t; typedef struct { /* * the contents of the frame buffer address for rgb type only addr[0] * valid */ __u32 addr[3]; __disp_rectsz_t size; /* unit is pixel */ unsigned int format; unsigned int seq; unsigned int mode; /* * blue red color swap flag, FALSE:RGB; TRUE:BGR,only used in rgb format */ signed char br_swap; unsigned int cs_mode; /* color space */ signed char b_trd_src; /* if 3d source, used for scaler mode layer */ /* source 3d mode, used for scaler mode layer */ unsigned int trd_mode; __u32 trd_right_addr[3]; /* used when in frame packing 3d mode */ } __disp_fb_t; typedef struct { unsigned int mode; /* layer work mode */ signed char b_from_screen; /* * layer pipe,0/1,if in scaler mode, scaler0 must be pipe0, * scaler1 must be pipe1 */ __u8 pipe; /* * layer priority,can get layer prio,but never set layer prio. * From bottom to top, priority from low to high */ __u8 prio; signed char alpha_en; /* layer global alpha enable */ __u16 alpha_val; /* layer global alpha value */ signed char ck_enable; /* layer color key enable */ /* framebuffer source window,only care x,y if is not scaler mode */ __disp_rect_t src_win; __disp_rect_t scn_win; /* screen window */ __disp_fb_t fb; /* framebuffer */ signed char b_trd_out; /* if output 3d mode, used for scaler mode layer */ unsigned int out_trd_mode; /* output 3d mode, used for scaler mode layer */ } __disp_layer_info_t; /***************************************************************************** * Support for scaled layers * *****************************************************************************/ static int sunxi_layer_change_work_mode(sunxi_disp_t *ctx, int new_mode) { __disp_layer_info_t layer_info; uint32_t tmp[4]; if (ctx->layer_id < 0) return -1; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; tmp[2] = (uintptr_t)&layer_info; if (ioctl(ctx->fd_disp, DISP_CMD_LAYER_GET_PARA, tmp) < 0) return -1; layer_info.mode = new_mode; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; tmp[2] = (uintptr_t)&layer_info; return ioctl(ctx->fd_disp, DISP_CMD_LAYER_SET_PARA, tmp); } static int sunxi_layer_reserve(sunxi_disp_t *ctx) { __disp_layer_info_t layer_info; uint32_t tmp[4]; /* try to allocate a layer */ tmp[0] = ctx->fb_id; tmp[1] = DISP_LAYER_WORK_MODE_NORMAL; ctx->layer_id = ioctl(ctx->fd_disp, DISP_CMD_LAYER_REQUEST, &tmp); if (ctx->layer_id < 0) return -1; /* Initially set the layer configuration to something reasonable */ tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; tmp[2] = (uintptr_t)&layer_info; if (ioctl(ctx->fd_disp, DISP_CMD_LAYER_GET_PARA, tmp) < 0) return -1; /* the screen and overlay layers need to be in different pipes */ layer_info.pipe = 1; layer_info.alpha_en = 1; layer_info.alpha_val = 255; layer_info.fb.addr[0] = ctx->framebuffer_paddr; layer_info.fb.size.width = 1; layer_info.fb.size.height = 1; layer_info.fb.format = DISP_FORMAT_ARGB8888; layer_info.fb.seq = DISP_SEQ_ARGB; layer_info.fb.mode = DISP_MOD_INTERLEAVED; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; tmp[2] = (uintptr_t)&layer_info; if (ioctl(ctx->fd_disp, DISP_CMD_LAYER_SET_PARA, tmp) < 0) return -1; /* Now probe the scaler mode to see if there is a free scaler available */ if (sunxi_layer_change_work_mode(ctx, DISP_LAYER_WORK_MODE_SCALER) == 0) ctx->layer_has_scaler = 1; /* Revert back to normal mode */ sunxi_layer_change_work_mode(ctx, DISP_LAYER_WORK_MODE_NORMAL); ctx->layer_scaler_is_enabled = 0; ctx->layer_format = DISP_FORMAT_ARGB8888; return ctx->layer_id; } static int sunxi_layer_set_output_window(sunxi_disp_t *ctx, int x, int y, int w, int h) { __disp_rect_t win_rect = { x, y, w, h }; uint32_t tmp[4]; if (ctx->layer_id < 0 || w <= 0 || h <= 0) return -1; /* Save the new non-adjusted window position */ ctx->layer_win_x = x; ctx->layer_win_y = y; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; tmp[2] = (uintptr_t)&win_rect; return ioctl(ctx->fd_disp, DISP_CMD_LAYER_SET_SCN_WINDOW, &tmp); } static int sunxi_layer_show(sunxi_disp_t *ctx) { uint32_t tmp[4]; if (ctx->layer_id < 0) return -1; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; return ioctl(ctx->fd_disp, DISP_CMD_LAYER_OPEN, &tmp); } static int sunxi_layer_release(sunxi_disp_t *ctx) { uint32_t tmp[4]; if (ctx->layer_id < 0) return -1; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; ioctl(ctx->fd_disp, DISP_CMD_LAYER_RELEASE, &tmp); ctx->layer_id = -1; ctx->layer_has_scaler = 0; return 0; } static int sunxi_layer_set_rgb_input_buffer(sunxi_disp_t *ctx, int bpp, uint32_t offset_in_framebuffer, int width, int height, int stride) { __disp_fb_t fb; uint32_t tmp[4]; __disp_rect_t rect = { 0, 0, width, height }; memset(&fb, 0, sizeof(fb)); if (ctx->layer_id < 0) return -1; if (!ctx->layer_scaler_is_enabled) { if (sunxi_layer_change_work_mode(ctx, DISP_LAYER_WORK_MODE_SCALER) == 0) ctx->layer_scaler_is_enabled = 1; else return -1; } fb.addr[0] = ctx->framebuffer_paddr + offset_in_framebuffer; fb.size.height = height; if (bpp == 32) { fb.format = DISP_FORMAT_ARGB8888; fb.seq = DISP_SEQ_ARGB; fb.mode = DISP_MOD_INTERLEAVED; fb.size.width = stride; } else if (bpp == 16) { fb.format = DISP_FORMAT_RGB565; fb.seq = DISP_SEQ_P10; fb.mode = DISP_MOD_INTERLEAVED; fb.size.width = stride * 2; } else return -1; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; tmp[2] = (uintptr_t)&fb; if (ioctl(ctx->fd_disp, DISP_CMD_LAYER_SET_FB, &tmp) < 0) return -1; ctx->layer_buf_x = rect.x; ctx->layer_buf_y = rect.y; ctx->layer_buf_w = rect.width; ctx->layer_buf_h = rect.height; ctx->layer_format = fb.format; tmp[0] = ctx->fb_id; tmp[1] = ctx->layer_id; tmp[2] = (uintptr_t)▭ return ioctl(ctx->fd_disp, DISP_CMD_LAYER_SET_SRC_WINDOW, &tmp); } static sunxi_disp_t *sunxi_disp_init(const char *device) { int tmp, version; struct fb_var_screeninfo fb_var; struct fb_fix_screeninfo fb_fix; sunxi_disp_t *ctx = (sunxi_disp_t*)calloc(1, sizeof(sunxi_disp_t)); if (!ctx) return NULL; /* use /dev/fb0 by default */ if (!device) device = "/dev/fb0"; if (string_is_equal(device, "/dev/fb0")) ctx->fb_id = 0; else if (string_is_equal(device, "/dev/fb1")) ctx->fb_id = 1; else goto error; ctx->fd_disp = open("/dev/disp", O_RDWR); /* maybe it's even not a sunxi hardware */ if (ctx->fd_disp < 0) goto error; /* version check */ tmp = SUNXI_DISP_VERSION; version = ioctl(ctx->fd_disp, DISP_CMD_VERSION, &tmp); if (version < 0) { close(ctx->fd_disp); goto error; } ctx->fd_fb = open(device, O_RDWR); if (ctx->fd_fb < 0) { close(ctx->fd_disp); goto error; } if (ioctl(ctx->fd_fb, FBIOGET_VSCREENINFO, &fb_var) < 0 || ioctl(ctx->fd_fb, FBIOGET_FSCREENINFO, &fb_fix) < 0) { close(ctx->fd_fb); close(ctx->fd_disp); goto error; } ctx->xres = fb_var.xres; ctx->yres = fb_var.yres; ctx->bits_per_pixel = fb_var.bits_per_pixel; ctx->framebuffer_paddr = fb_fix.smem_start; ctx->framebuffer_size = fb_fix.smem_len; ctx->framebuffer_height = ctx->framebuffer_size / (ctx->xres * ctx->bits_per_pixel / 8); ctx->gfx_layer_size = ctx->xres * ctx->yres * fb_var.bits_per_pixel / 8; if (ctx->framebuffer_size < ctx->gfx_layer_size) { close(ctx->fd_fb); close(ctx->fd_disp); goto error; } /* mmap framebuffer memory */ ctx->framebuffer_addr = (uint8_t *)mmap(0, ctx->framebuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, ctx->fd_fb, 0); if (ctx->framebuffer_addr == MAP_FAILED) { close(ctx->fd_fb); close(ctx->fd_disp); goto error; } /* Get the id of the screen layer */ if (ioctl(ctx->fd_fb, ctx->fb_id == 0 ? FBIOGET_LAYER_HDL_0 : FBIOGET_LAYER_HDL_1, &ctx->gfx_layer_id)) { close(ctx->fd_fb); close(ctx->fd_disp); goto error; } if (sunxi_layer_reserve(ctx) < 0) { close(ctx->fd_fb); close(ctx->fd_disp); goto error; } return ctx; error: if (ctx) free(ctx); return NULL; } static int sunxi_disp_close(sunxi_disp_t *ctx) { if (ctx->fd_disp >= 0) { /* release layer */ sunxi_layer_release(ctx); /* close descriptors */ munmap(ctx->framebuffer_addr, ctx->framebuffer_size); close(ctx->fd_fb); close(ctx->fd_disp); ctx->fd_disp = -1; free(ctx); } return 0; } /* END of lowlevel SunxiG2D functions block */ void pixman_composite_src_0565_8888_asm_neon(int width, int height, uint32_t *dst, int dst_stride_pixels, uint16_t *src, int src_stride_pixels); void pixman_composite_src_8888_8888_asm_neon(int width, int height, uint32_t *dst, int dst_stride_pixels, uint16_t *src, int src_stride_pixels); /* Pointer to the blitting function. Will be asigned * when we find out what bpp the core uses. */ void (*pixman_blit) (int width, int height, uint32_t *dst, int dst_stride_pixels, uint16_t *src, int src_stride_pixels); struct sunxi_page { uint32_t *address; unsigned int offset; }; struct sunxi_video { void *font; const font_renderer_driver_t *font_driver; uint8_t font_rgb[4]; /* Sunxi framebuffer information struct */ sunxi_disp_t *sunxi_disp; /* current dimensions of the emulator fb */ unsigned int src_width; unsigned int src_height; unsigned int src_pitch; unsigned int src_bpp; unsigned int src_bytes_per_pixel; unsigned int src_pixels_per_line; unsigned int dst_pitch; unsigned int dst_pixels_per_line; unsigned int bytes_per_pixel; struct sunxi_page *pages; struct sunxi_page *nextPage; bool pageflip_pending; /* Keep the vsync while loop going. Set to false to exit. */ bool keep_vsync; /* Variables to restore screen on exit */ unsigned int screensize; char *screen_bck; /* For threading */ sthread_t *vsync_thread; scond_t *vsync_condition; slock_t *pending_mutex; /* menu data */ unsigned int menu_rotation; bool menu_active; unsigned int menu_width; unsigned int menu_height; unsigned int menu_pitch; float aspect_ratio; }; static void sunxi_blank_console(struct sunxi_video *_dispvars) { if (!_dispvars) return; /* Disable cursor blinking so it's not visible. */ system("setterm -cursor off"); /* Figure out the size of the screen in bytes. */ _dispvars->screensize = _dispvars->sunxi_disp->xres * _dispvars->sunxi_disp->yres * _dispvars->sunxi_disp->bits_per_pixel / 8; /* Backup screen contents. */ _dispvars->screen_bck = (char*)malloc(_dispvars->screensize * sizeof(char)); if (!_dispvars->screen_bck) return; memcpy((char*)_dispvars->screen_bck, (char*)_dispvars->sunxi_disp->framebuffer_addr, _dispvars->screensize); /* Blank screen. */ memset((char*)(_dispvars->sunxi_disp->framebuffer_addr), 0x00, _dispvars->screensize); } static void sunxi_restore_console(struct sunxi_video *_dispvars) { if (!_dispvars) return; system("setterm -cursor on"); memcpy((char*)_dispvars->sunxi_disp->framebuffer_addr, (char*)_dispvars->screen_bck, _dispvars->screensize); free(_dispvars->screen_bck); } static void sunxi_vsync_thread_func(void *data) { struct sunxi_video *_dispvars = (struct sunxi_video*)data; while (_dispvars->keep_vsync) { /* Wait for next vsync */ ioctl(_dispvars->sunxi_disp->fd_fb, FBIO_WAITFORVSYNC, 0); /* Changing the page to write must be done before the signaling * so we have the right page in nextPage when update_main continues */ if (_dispvars->nextPage == &_dispvars->pages[0]) _dispvars->nextPage = &_dispvars->pages[1]; else _dispvars->nextPage = &_dispvars->pages[0]; /* These two things must be isolated "atomically" to avoid getting * a false positive in the pending_mutex test in update_main. */ slock_lock(_dispvars->pending_mutex); _dispvars->pageflip_pending = false; scond_signal(_dispvars->vsync_condition); slock_unlock(_dispvars->pending_mutex); } } static void *sunxi_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data) { struct sunxi_video *_dispvars = (struct sunxi_video*) calloc(1, sizeof(struct sunxi_video)); if (!_dispvars) return NULL; _dispvars->src_bytes_per_pixel = video->rgb32 ? 4 : 2; _dispvars->sunxi_disp = sunxi_disp_init("/dev/fb0"); /* Blank text console and disable cursor blinking. */ sunxi_blank_console(_dispvars); _dispvars->pages = (struct sunxi_page*)calloc(NUMPAGES, sizeof (struct sunxi_page)); if (!_dispvars->pages) goto error; _dispvars->dst_pitch = _dispvars->sunxi_disp->xres * _dispvars->sunxi_disp->bits_per_pixel / 8; /* Considering 4 bytes per pixel since we will be in 32bpp on the CB/CB2/CT for hw scalers to work. */ _dispvars->dst_pixels_per_line = _dispvars->dst_pitch / 4; _dispvars->pageflip_pending = false; _dispvars->nextPage = &_dispvars->pages[0]; _dispvars->keep_vsync = true; _dispvars->menu_active = false; _dispvars->bytes_per_pixel = video->rgb32 ? 4 : 2; /* It's very important that we set aspect here because the * call seq when a core is loaded is gfx_init()->set_aspect()->gfx_frame() * and we don't want the main surface to be setup in set_aspect() * before we get to gfx_frame(). */ _dispvars->aspect_ratio = video_driver_get_aspect_ratio(); switch (_dispvars->bytes_per_pixel) { case 2: pixman_blit = pixman_composite_src_0565_8888_asm_neon; break; case 4: pixman_blit = pixman_composite_src_8888_8888_asm_neon; break; default: goto error; } _dispvars->pending_mutex = slock_new(); _dispvars->vsync_condition = scond_new(); if (input && input_data) *input = NULL; /* Launching vsync thread */ _dispvars->vsync_thread = sthread_create(sunxi_vsync_thread_func, _dispvars); return _dispvars; error: if (_dispvars) free(_dispvars); return NULL; } static void sunxi_gfx_free(void *data) { struct sunxi_video *_dispvars = (struct sunxi_video*)data; /* Stop the vsync thread and wait for it to join. */ /* When menu is active, vsync thread has already been stopped. */ if (!_dispvars->menu_active) { _dispvars->keep_vsync = false; sthread_join(_dispvars->vsync_thread); } slock_free(_dispvars->pending_mutex); scond_free(_dispvars->vsync_condition); free(_dispvars->pages); /* Restore text console contents and reactivate cursor blinking. */ sunxi_restore_console(_dispvars); sunxi_disp_close(_dispvars->sunxi_disp); free(_dispvars); } static void sunxi_update_main(const void *frame, struct sunxi_video *_dispvars) { slock_lock(_dispvars->pending_mutex); if (_dispvars->pageflip_pending) scond_wait(_dispvars->vsync_condition, _dispvars->pending_mutex); slock_unlock(_dispvars->pending_mutex); /* Frame blitting */ pixman_blit( _dispvars->src_width, _dispvars->src_height, _dispvars->nextPage->address, _dispvars->dst_pixels_per_line, (uint16_t*)frame, _dispvars->src_pixels_per_line ); /* Issue pageflip. Will flip on next vsync. */ sunxi_layer_set_rgb_input_buffer(_dispvars->sunxi_disp, _dispvars->sunxi_disp->bits_per_pixel, _dispvars->nextPage->offset, _dispvars->src_width, _dispvars->src_height, _dispvars->sunxi_disp->xres); slock_lock(_dispvars->pending_mutex); _dispvars->pageflip_pending = true; slock_unlock(_dispvars->pending_mutex); } static void sunxi_setup_scale (void *data, unsigned width, unsigned height, unsigned pitch) { int i; unsigned int xpos, visible_width; struct sunxi_video *_dispvars = (struct sunxi_video*)data; _dispvars->src_width = width; _dispvars->src_height = height; /* Total pitch, including things the * cores render between "visible" scanlines. */ _dispvars->src_pitch = pitch; /* Pixels per line */ _dispvars->src_pixels_per_line = _dispvars->src_pitch/_dispvars->bytes_per_pixel; /* Incremental offset that sums up on * each previous page offset. * Total offset of each page has to * be adjusted when internal resolution changes. */ for (i = 0; i < NUMPAGES; i++) { _dispvars->pages[i].offset = (_dispvars->sunxi_disp->yres + i * _dispvars->src_height) * _dispvars->sunxi_disp->xres * 4; _dispvars->pages[i].address = ((uint32_t*) _dispvars->sunxi_disp->framebuffer_addr + (_dispvars->sunxi_disp->yres + i * _dispvars->src_height) * _dispvars->dst_pitch/4); } visible_width = _dispvars->sunxi_disp->yres * _dispvars->aspect_ratio; xpos = (_dispvars->sunxi_disp->xres - visible_width) / 2; /* setup layer window */ sunxi_layer_set_output_window(_dispvars->sunxi_disp, xpos, 0, visible_width, _dispvars->sunxi_disp->yres); /* make the layer visible */ sunxi_layer_show(_dispvars->sunxi_disp); } static bool sunxi_gfx_frame(void *data, const void *frame, unsigned width, unsigned height, uint64_t frame_count, unsigned pitch, const char *msg, video_frame_info_t *video_info) { struct sunxi_video *_dispvars = (struct sunxi_video*)data; if (_dispvars->src_width != width || _dispvars->src_height != height) { /* Sanity check on new dimensions */ if (width == 0 || height == 0) return true; RARCH_LOG("video_sunxi: internal resolution changed by core: %ux%u -> %ux%u\n", _dispvars->src_width, _dispvars->src_height, width, height); sunxi_setup_scale(_dispvars, width, height, pitch); } #ifdef HAVE_MENU menu_driver_frame(video_info); #endif if (_dispvars->menu_active) { ioctl(_dispvars->sunxi_disp->fd_fb, FBIO_WAITFORVSYNC, 0); return true; } sunxi_update_main(frame, _dispvars); return true; } static void sunxi_gfx_set_nonblock_state(void *data, bool state) { struct sunxi_video *vid = data; (void)vid; (void)state; } static bool sunxi_gfx_alive(void *data) { (void)data; return true; /* always alive */ } static bool sunxi_gfx_focus(void *data) { (void)data; return true; /* fb device always has focus */ } static void sunxi_gfx_set_rotation(void *data, unsigned rotation) { (void)data; (void)rotation; } static bool sunxi_gfx_suppress_screensaver(void *data, bool enable) { (void)data; (void)enable; return false; } static void sunxi_gfx_viewport_info(void *data, struct video_viewport *vp) { struct sunxi_video *_dispvars = (struct sunxi_video*)data; if (!vp || !_dispvars) return; vp->x = vp->y = 0; vp->width = vp->full_width = _dispvars->src_width; vp->height = vp->full_height = _dispvars->src_height; } static bool sunxi_gfx_set_shader(void *data, enum rarch_shader_type type, const char *path) { (void)data; (void)type; (void)path; return false; } static void sunxi_set_texture_enable(void *data, bool state, bool full_screen) { struct sunxi_video *_dispvars = (struct sunxi_video*)data; /* If it wasn't active and starts being active... */ if (!_dispvars->menu_active && state) { /* Stop the vsync thread. */ _dispvars->keep_vsync = false; sthread_join(_dispvars->vsync_thread); } /* If it was active but now it isn't active anymore... */ if (_dispvars->menu_active && !state) { _dispvars->keep_vsync = true; _dispvars->vsync_thread = sthread_create(sunxi_vsync_thread_func, _dispvars); } _dispvars->menu_active = state; } static void sunxi_set_texture_frame(void *data, const void *frame, bool rgb32, unsigned width, unsigned height, float alpha) { struct sunxi_video *_dispvars = (struct sunxi_video*)data; if (_dispvars->menu_active) { unsigned int i, j; /* We have to go on a pixel format conversion adventure for now, until we can * convince RGUI to output in an 8888 format. */ unsigned int src_pitch = width * 2; unsigned int dst_pitch = _dispvars->sunxi_disp->xres * 4; unsigned int dst_width = _dispvars->sunxi_disp->xres; uint32_t line[dst_width]; /* Remember, memcpy() works with 8bits pointers for increments. */ char *dst_base_addr = (char*)(_dispvars->pages[0].address); for (i = 0; i < height; i++) { for (j = 0; j < src_pitch / 2; j++) { uint16_t src_pix = *((uint16_t*)frame + (src_pitch / 2 * i) + j); /* The hex AND is for keeping only the part we need for each component. */ uint32_t R = (src_pix << 8) & 0x00FF0000; uint32_t G = (src_pix << 4) & 0x0000FF00; uint32_t B = (src_pix << 0) & 0x000000FF; line[j] = (0 | R | G | B); } memcpy(dst_base_addr + (dst_pitch * i), (char*)line, dst_pitch); } /* Issue pageflip. Will flip on next vsync. */ sunxi_layer_set_rgb_input_buffer(_dispvars->sunxi_disp, _dispvars->sunxi_disp->bits_per_pixel, _dispvars->pages[0].offset, width, height, _dispvars->sunxi_disp->xres); } } static void sunxi_set_aspect_ratio (void *data, unsigned aspect_ratio_idx) { struct sunxi_video *_dispvars = (struct sunxi_video*)data; float new_aspect = aspectratio_lut[aspect_ratio_idx].value; if (new_aspect != _dispvars->aspect_ratio) { /* Here we set the new aspect ratio. */ _dispvars->aspect_ratio = new_aspect; sunxi_setup_scale(_dispvars, _dispvars->src_width, _dispvars->src_height, _dispvars->src_pitch); } } static const video_poke_interface_t sunxi_poke_interface = { NULL, NULL, NULL, /* set_video_mode */ NULL, /* set_filtering */ NULL, /* get_video_output_size */ NULL, /* get_video_output_prev */ NULL, /* get_video_output_next */ NULL, /* get_current_framebuffer */ NULL, /* get_proc_address */ sunxi_set_aspect_ratio, NULL, /* sunxi_apply_state_changes */ #ifdef HAVE_MENU sunxi_set_texture_frame, sunxi_set_texture_enable, #endif NULL, /* sunxi_set_osd_msg */ NULL /* sunxi_show_mouse */ }; static void sunxi_gfx_get_poke_interface(void *data, const video_poke_interface_t **iface) { (void)data; *iface = &sunxi_poke_interface; } video_driver_t video_sunxi = { sunxi_gfx_init, sunxi_gfx_frame, sunxi_gfx_set_nonblock_state, sunxi_gfx_alive, sunxi_gfx_focus, sunxi_gfx_suppress_screensaver, NULL, /* has_windowed */ sunxi_gfx_set_shader, sunxi_gfx_free, "sunxi", NULL, /* set_viewport */ sunxi_gfx_set_rotation, sunxi_gfx_viewport_info, NULL, /* read_viewport */ NULL, /* read_frame_raw */ #ifdef HAVE_OVERLAY NULL, /* overlay_interface */ #endif sunxi_gfx_get_poke_interface };