• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 static gr_surface fbdev_init(minui_backend*);
36 static gr_surface fbdev_flip(minui_backend*);
37 static void fbdev_blank(minui_backend*, bool);
38 static void fbdev_exit(minui_backend*);
39 
40 static GRSurface gr_framebuffer[2];
41 static bool double_buffered;
42 static GRSurface* gr_draw = NULL;
43 static int displayed_buffer;
44 
45 static struct fb_var_screeninfo vi;
46 static int fb_fd = -1;
47 
48 static minui_backend my_backend = {
49     .init = fbdev_init,
50     .flip = fbdev_flip,
51     .blank = fbdev_blank,
52     .exit = fbdev_exit,
53 };
54 
open_fbdev()55 minui_backend* open_fbdev() {
56     return &my_backend;
57 }
58 
fbdev_blank(minui_backend * backend __unused,bool blank)59 static 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 
set_displayed_framebuffer(unsigned n)68 static 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 
fbdev_init(minui_backend * backend)81 static 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 
fbdev_flip(minui_backend * backend __unused)180 static 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 
fbdev_exit(minui_backend * backend __unused)220 static 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