xref: /OK3568_Linux_fs/external/recovery/minui/graphics_fbdev.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 {
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 gr_surface fbdev_init(minui_backend* backend)
83 {
84     int fd;
85     void *bits;
86 
87     struct fb_fix_screeninfo fi;
88 
89     fd = open("/dev/graphics/fb0", O_RDWR);
90     if (fd < 0) {
91         perror("cannot open fb0");
92         return NULL;
93     }
94 
95     if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
96         perror("failed to get fb0 info");
97         close(fd);
98         return NULL;
99     }
100 
101     if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
102         perror("failed to get fb0 info");
103         close(fd);
104         return NULL;
105     }
106 
107     // We print this out for informational purposes only, but
108     // throughout we assume that the framebuffer device uses an RGBX
109     // pixel format.  This is the case for every development device I
110     // have access to.  For some of those devices (eg, hammerhead aka
111     // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
112     // different format (XBGR) but actually produces the correct
113     // results on the display when you write RGBX.
114     //
115     // If you have a device that actually *needs* another pixel format
116     // (ie, BGRX, or 565), patches welcome...
117 
118     printf("fb0 reports (possibly inaccurate):\n"
119            "  vi.bits_per_pixel = %d\n"
120            "  vi.red.offset   = %3d   .length = %3d\n"
121            "  vi.green.offset = %3d   .length = %3d\n"
122            "  vi.blue.offset  = %3d   .length = %3d\n",
123            vi.bits_per_pixel,
124            vi.red.offset, vi.red.length,
125            vi.green.offset, vi.green.length,
126            vi.blue.offset, vi.blue.length);
127 
128     bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
129     if (bits == MAP_FAILED) {
130         perror("failed to mmap framebuffer");
131         close(fd);
132         return NULL;
133     }
134 
135     memset(bits, 0, fi.smem_len);
136 
137     gr_framebuffer[0].width = vi.xres;
138     gr_framebuffer[0].height = vi.yres;
139     gr_framebuffer[0].row_bytes = fi.line_length;
140     gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
141     gr_framebuffer[0].data = bits;
142     memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
143 
144     /* check if we can use double buffering */
145     if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
146         double_buffered = true;
147 
148         memcpy(gr_framebuffer + 1, gr_framebuffer, sizeof(GRSurface));
149         gr_framebuffer[1].data = gr_framebuffer[0].data +
150                                  gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
151 
152         gr_draw = gr_framebuffer + 1;
153 
154     } else {
155         double_buffered = false;
156 
157         // Without double-buffering, we allocate RAM for a buffer to
158         // draw in, and then "flipping" the buffer consists of a
159         // memcpy from the buffer we allocated to the framebuffer.
160 
161         gr_draw = (GRSurface*) malloc(sizeof(GRSurface));
162         memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
163         gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
164         if (!gr_draw->data) {
165             perror("failed to allocate in-memory surface");
166             return NULL;
167         }
168     }
169 
170     memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
171     fb_fd = fd;
172     set_displayed_framebuffer(0);
173 
174     printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
175 
176     fbdev_blank(backend, true);
177     fbdev_blank(backend, false);
178 
179     return gr_draw;
180 }
181 
fbdev_flip(minui_backend * backend __unused)182 static gr_surface fbdev_flip(minui_backend* backend __unused)
183 {
184     if (double_buffered) {
185         // Change gr_draw to point to the buffer currently displayed,
186         // then flip the driver so we're displaying the other buffer
187         // instead.
188         gr_draw = gr_framebuffer + displayed_buffer;
189         set_displayed_framebuffer(1 - displayed_buffer);
190     } else {
191         // Copy from the in-memory surface to the framebuffer.
192 
193 #if defined(RECOVERY_BGRA)
194         unsigned int idx;
195         unsigned char* ucfb_vaddr = (unsigned char*)gr_framebuffer[0].data;
196         unsigned char* ucbuffer_vaddr = (unsigned char*)gr_draw->data;
197         for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); idx += 4) {
198             ucfb_vaddr[idx    ] = ucbuffer_vaddr[idx + 2];
199             ucfb_vaddr[idx + 1] = ucbuffer_vaddr[idx + 1];
200             ucfb_vaddr[idx + 2] = ucbuffer_vaddr[idx    ];
201             ucfb_vaddr[idx + 3] = ucbuffer_vaddr[idx + 3];
202         }
203 #else
204         memcpy(gr_framebuffer[0].data, gr_draw->data,
205                gr_draw->height * gr_draw->row_bytes);
206 #endif
207     }
208     return gr_draw;
209 }
210 
fbdev_exit(minui_backend * backend __unused)211 static void fbdev_exit(minui_backend* backend __unused)
212 {
213     close(fb_fd);
214     fb_fd = -1;
215 
216     if (!double_buffered && gr_draw) {
217         free(gr_draw->data);
218         free(gr_draw);
219     }
220     gr_draw = NULL;
221 }
222