1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdbool.h> 18#include <stdlib.h> 19#include <unistd.h> 20 21#include <fcntl.h> 22#include <stdio.h> 23 24#include <sys/cdefs.h> 25#include <sys/ioctl.h> 26#include <sys/mman.h> 27#include <sys/types.h> 28 29#include <linux/fb.h> 30#include <linux/kd.h> 31 32#include "minui.h" 33#include "graphics.h" 34 35static gr_surface fbdev_init(minui_backend*); 36static gr_surface fbdev_flip(minui_backend*); 37static void fbdev_blank(minui_backend*, bool); 38static void fbdev_exit(minui_backend*); 39 40static GRSurface gr_framebuffer[2]; 41static bool double_buffered; 42static GRSurface* gr_draw = NULL; 43static int displayed_buffer; 44 45static struct fb_var_screeninfo vi; 46static int fb_fd = -1; 47 48static minui_backend my_backend = { 49 .init = fbdev_init, 50 .flip = fbdev_flip, 51 .blank = fbdev_blank, 52 .exit = fbdev_exit, 53}; 54 55minui_backend* open_fbdev() { 56 return &my_backend; 57} 58 59static void fbdev_blank(minui_backend* backend __unused, bool blank) 60{ 61 int ret; 62 63 ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); 64 if (ret < 0) 65 perror("ioctl(): blank"); 66} 67 68static void set_displayed_framebuffer(unsigned n) 69{ 70 if (n > 1 || !double_buffered) return; 71 72 vi.yres_virtual = gr_framebuffer[0].height * 2; 73 vi.yoffset = n * gr_framebuffer[0].height; 74 vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; 75 if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { 76 perror("active fb swap failed"); 77 } 78 displayed_buffer = n; 79} 80 81static gr_surface fbdev_init(minui_backend* backend) { 82 int fd; 83 void *bits; 84 85 struct fb_fix_screeninfo fi; 86 87 fd = open("/dev/graphics/fb0", O_RDWR); 88 if (fd < 0) { 89 perror("cannot open fb0"); 90 return NULL; 91 } 92 93 if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { 94 perror("failed to get fb0 info"); 95 close(fd); 96 return NULL; 97 } 98 99 if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { 100 perror("failed to get fb0 info"); 101 close(fd); 102 return NULL; 103 } 104 105 // We print this out for informational purposes only, but 106 // throughout we assume that the framebuffer device uses an RGBX 107 // pixel format. This is the case for every development device I 108 // have access to. For some of those devices (eg, hammerhead aka 109 // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a 110 // different format (XBGR) but actually produces the correct 111 // results on the display when you write RGBX. 112 // 113 // If you have a device that actually *needs* another pixel format 114 // (ie, BGRX, or 565), patches welcome... 115 116 printf("fb0 reports (possibly inaccurate):\n" 117 " vi.bits_per_pixel = %d\n" 118 " vi.red.offset = %3d .length = %3d\n" 119 " vi.green.offset = %3d .length = %3d\n" 120 " vi.blue.offset = %3d .length = %3d\n", 121 vi.bits_per_pixel, 122 vi.red.offset, vi.red.length, 123 vi.green.offset, vi.green.length, 124 vi.blue.offset, vi.blue.length); 125 126 bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 127 if (bits == MAP_FAILED) { 128 perror("failed to mmap framebuffer"); 129 close(fd); 130 return NULL; 131 } 132 133 memset(bits, 0, fi.smem_len); 134 135 gr_framebuffer[0].width = vi.xres; 136 gr_framebuffer[0].height = vi.yres; 137 gr_framebuffer[0].row_bytes = fi.line_length; 138 gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; 139 gr_framebuffer[0].data = bits; 140 memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); 141 142 /* check if we can use double buffering */ 143 if (vi.yres * fi.line_length * 2 <= fi.smem_len) { 144 double_buffered = true; 145 146 memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); 147 gr_framebuffer[1].data = gr_framebuffer[0].data + 148 gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; 149 150 gr_draw = gr_framebuffer+1; 151 152 } else { 153 double_buffered = false; 154 155 // Without double-buffering, we allocate RAM for a buffer to 156 // draw in, and then "flipping" the buffer consists of a 157 // memcpy from the buffer we allocated to the framebuffer. 158 159 gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); 160 memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); 161 gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); 162 if (!gr_draw->data) { 163 perror("failed to allocate in-memory surface"); 164 return NULL; 165 } 166 } 167 168 memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); 169 fb_fd = fd; 170 set_displayed_framebuffer(0); 171 172 printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); 173 174 fbdev_blank(backend, true); 175 fbdev_blank(backend, false); 176 177 return gr_draw; 178} 179 180static gr_surface fbdev_flip(minui_backend* backend __unused) { 181 if (double_buffered) { 182#if defined(RECOVERY_BGRA) 183 // In case of BGRA, do some byte swapping 184 unsigned int idx; 185 unsigned char tmp; 186 unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data; 187 for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); 188 idx += 4) { 189 tmp = ucfb_vaddr[idx]; 190 ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2]; 191 ucfb_vaddr[idx + 2] = tmp; 192 } 193#endif 194 // Change gr_draw to point to the buffer currently displayed, 195 // then flip the driver so we're displaying the other buffer 196 // instead. 197 gr_draw = gr_framebuffer + displayed_buffer; 198 set_displayed_framebuffer(1-displayed_buffer); 199 } else { 200 // Copy from the in-memory surface to the framebuffer. 201 202#if defined(RECOVERY_BGRA) 203 unsigned int idx; 204 unsigned char* ucfb_vaddr = (unsigned char*)gr_framebuffer[0].data; 205 unsigned char* ucbuffer_vaddr = (unsigned char*)gr_draw->data; 206 for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); idx += 4) { 207 ucfb_vaddr[idx ] = ucbuffer_vaddr[idx + 2]; 208 ucfb_vaddr[idx + 1] = ucbuffer_vaddr[idx + 1]; 209 ucfb_vaddr[idx + 2] = ucbuffer_vaddr[idx ]; 210 ucfb_vaddr[idx + 3] = ucbuffer_vaddr[idx + 3]; 211 } 212#else 213 memcpy(gr_framebuffer[0].data, gr_draw->data, 214 gr_draw->height * gr_draw->row_bytes); 215#endif 216 } 217 return gr_draw; 218} 219 220static void fbdev_exit(minui_backend* backend __unused) { 221 close(fb_fd); 222 fb_fd = -1; 223 224 if (!double_buffered && gr_draw) { 225 free(gr_draw->data); 226 free(gr_draw); 227 } 228 gr_draw = NULL; 229} 230