• 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 <string.h>
20 #include <unistd.h>
21 
22 #include <fcntl.h>
23 #include <stdio.h>
24 
25 #include <sys/cdefs.h>
26 #include <sys/ioctl.h>
27 #include <sys/mman.h>
28 #include <sys/types.h>
29 
30 #include <linux/fb.h>
31 #include <linux/kd.h>
32 
33 #include "minui.h"
34 #include "graphics.h"
35 
36 static GRSurface* fbdev_init(minui_backend*);
37 static GRSurface* fbdev_flip(minui_backend*);
38 static void fbdev_blank(minui_backend*, bool);
39 static void fbdev_exit(minui_backend*);
40 
41 static GRSurface gr_framebuffer[2];
42 static bool double_buffered;
43 static GRSurface* gr_draw = NULL;
44 static int displayed_buffer;
45 
46 static fb_var_screeninfo vi;
47 static int fb_fd = -1;
48 
49 static minui_backend my_backend = {
50     .init = fbdev_init,
51     .flip = fbdev_flip,
52     .blank = fbdev_blank,
53     .exit = fbdev_exit,
54 };
55 
open_fbdev()56 minui_backend* open_fbdev() {
57     return &my_backend;
58 }
59 
fbdev_blank(minui_backend * backend __unused,bool blank)60 static void fbdev_blank(minui_backend* backend __unused, bool blank)
61 {
62     int ret;
63 
64     ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
65     if (ret < 0)
66         perror("ioctl(): blank");
67 }
68 
set_displayed_framebuffer(unsigned n)69 static void set_displayed_framebuffer(unsigned n)
70 {
71     if (n > 1 || !double_buffered) return;
72 
73     vi.yres_virtual = gr_framebuffer[0].height * 2;
74     vi.yoffset = n * gr_framebuffer[0].height;
75     vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
76     if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
77         perror("active fb swap failed");
78     }
79     displayed_buffer = n;
80 }
81 
fbdev_init(minui_backend * backend)82 static GRSurface* fbdev_init(minui_backend* backend) {
83     int fd = open("/dev/graphics/fb0", O_RDWR);
84     if (fd == -1) {
85         perror("cannot open fb0");
86         return NULL;
87     }
88 
89     fb_fix_screeninfo fi;
90     if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
91         perror("failed to get fb0 info");
92         close(fd);
93         return NULL;
94     }
95 
96     if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
97         perror("failed to get fb0 info");
98         close(fd);
99         return NULL;
100     }
101 
102     // We print this out for informational purposes only, but
103     // throughout we assume that the framebuffer device uses an RGBX
104     // pixel format.  This is the case for every development device I
105     // have access to.  For some of those devices (eg, hammerhead aka
106     // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
107     // different format (XBGR) but actually produces the correct
108     // results on the display when you write RGBX.
109     //
110     // If you have a device that actually *needs* another pixel format
111     // (ie, BGRX, or 565), patches welcome...
112 
113     printf("fb0 reports (possibly inaccurate):\n"
114            "  vi.bits_per_pixel = %d\n"
115            "  vi.red.offset   = %3d   .length = %3d\n"
116            "  vi.green.offset = %3d   .length = %3d\n"
117            "  vi.blue.offset  = %3d   .length = %3d\n",
118            vi.bits_per_pixel,
119            vi.red.offset, vi.red.length,
120            vi.green.offset, vi.green.length,
121            vi.blue.offset, vi.blue.length);
122 
123     void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
124     if (bits == MAP_FAILED) {
125         perror("failed to mmap framebuffer");
126         close(fd);
127         return NULL;
128     }
129 
130     memset(bits, 0, fi.smem_len);
131 
132     gr_framebuffer[0].width = vi.xres;
133     gr_framebuffer[0].height = vi.yres;
134     gr_framebuffer[0].row_bytes = fi.line_length;
135     gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
136     gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
137     memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
138 
139     /* check if we can use double buffering */
140     if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
141         double_buffered = true;
142 
143         memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface));
144         gr_framebuffer[1].data = gr_framebuffer[0].data +
145             gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
146 
147         gr_draw = gr_framebuffer+1;
148 
149     } else {
150         double_buffered = false;
151 
152         // Without double-buffering, we allocate RAM for a buffer to
153         // draw in, and then "flipping" the buffer consists of a
154         // memcpy from the buffer we allocated to the framebuffer.
155 
156         gr_draw = (GRSurface*) malloc(sizeof(GRSurface));
157         memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
158         gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
159         if (!gr_draw->data) {
160             perror("failed to allocate in-memory surface");
161             return NULL;
162         }
163     }
164 
165     memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
166     fb_fd = fd;
167     set_displayed_framebuffer(0);
168 
169     printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
170 
171     fbdev_blank(backend, true);
172     fbdev_blank(backend, false);
173 
174     return gr_draw;
175 }
176 
fbdev_flip(minui_backend * backend __unused)177 static GRSurface* fbdev_flip(minui_backend* backend __unused) {
178     if (double_buffered) {
179         // Change gr_draw to point to the buffer currently displayed,
180         // then flip the driver so we're displaying the other buffer
181         // instead.
182         gr_draw = gr_framebuffer + displayed_buffer;
183         set_displayed_framebuffer(1-displayed_buffer);
184     } else {
185         // Copy from the in-memory surface to the framebuffer.
186         memcpy(gr_framebuffer[0].data, gr_draw->data,
187                gr_draw->height * gr_draw->row_bytes);
188     }
189     return gr_draw;
190 }
191 
fbdev_exit(minui_backend * backend __unused)192 static void fbdev_exit(minui_backend* backend __unused) {
193     close(fb_fd);
194     fb_fd = -1;
195 
196     if (!double_buffered && gr_draw) {
197         free(gr_draw->data);
198         free(gr_draw);
199     }
200     gr_draw = NULL;
201 }
202