blob: 978a3bb107d50d301f0cf9a765c2f30cfcb0b68f [file] [log] [blame]
Ethan Yonkera59da092015-10-13 19:35:05 -05001/*
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#include <errno.h>
22
23#include <fcntl.h>
24#include <stdio.h>
25
26#include <sys/cdefs.h>
27#include <sys/ioctl.h>
28#include <sys/mman.h>
29#include <sys/types.h>
30
31#include <linux/fb.h>
32#include <linux/kd.h>
33
34#ifdef MSM_BSP
35#include <linux/msm_mdp.h>
36#include <linux/msm_ion.h>
37#endif
38
39#include "minui.h"
40#include "graphics.h"
41
42#define MDP_V4_0 400
43#define MAX_DISPLAY_DIM 2048
44
45static GRSurface* overlay_init(minui_backend*);
46static GRSurface* overlay_flip(minui_backend*);
47static void overlay_blank(minui_backend*, bool);
48static void overlay_exit(minui_backend*);
49
50static GRSurface gr_framebuffer[2];
51static bool double_buffered;
52static GRSurface* gr_draw = NULL;
53static int displayed_buffer;
54
55static fb_var_screeninfo vi;
56static int fb_fd = -1;
57static bool isMDP5 = false;
58static int leftSplit = 0;
59static int rightSplit = 0;
60#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1))
61
62static size_t frame_size = 0;
63
64#ifdef MSM_BSP
65typedef struct {
66 unsigned char *mem_buf;
67 int size;
68 int ion_fd;
69 int mem_fd;
70 struct ion_handle_data handle_data;
71} memInfo;
72
73//Left and right overlay id
74static int overlayL_id = MSMFB_NEW_REQUEST;
75static int overlayR_id = MSMFB_NEW_REQUEST;
76
77static memInfo mem_info;
78#
79
80static int map_mdp_pixel_format()
81{
82 int format = MDP_RGB_565;
83#if defined(RECOVERY_BGRA)
84 format = MDP_BGRA_8888;
Kra1o577568592015-10-14 18:09:54 +020085#elif defined(RECOVERY_RGBA)
86 format = MDP_RGBA_8888;
Ethan Yonkera59da092015-10-13 19:35:05 -050087#elif defined(RECOVERY_RGBX)
88 format = MDP_RGBA_8888;
89#endif
90 return format;
91}
92#endif // MSM_BSP
93
94static minui_backend my_backend = {
95 .init = overlay_init,
96 .flip = overlay_flip,
97 .blank = overlay_blank,
98 .exit = overlay_exit,
99};
100
101bool target_has_overlay(char *version)
102{
103 int ret;
104 int mdp_version;
105 bool overlay_supported = false;
106
107 if (strlen(version) >= 8) {
108 if(!strncmp(version, "msmfb", strlen("msmfb"))) {
109 char str_ver[4];
110 memcpy(str_ver, version + strlen("msmfb"), 3);
111 str_ver[3] = '\0';
112 mdp_version = atoi(str_ver);
113 if (mdp_version >= MDP_V4_0) {
114 overlay_supported = true;
115 }
116 } else if (!strncmp(version, "mdssfb", strlen("mdssfb"))) {
117 overlay_supported = true;
118 isMDP5 = true;
119 }
120 }
121
122 return overlay_supported;
123}
124
125minui_backend* open_overlay() {
126 fb_fix_screeninfo fi;
127 int fd;
128
129 fd = open("/dev/graphics/fb0", O_RDWR);
130 if (fd < 0) {
131 perror("open_overlay cannot open fb0");
132 return NULL;
133 }
134
135 if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
136 perror("failed to get fb0 info");
137 close(fd);
138 return NULL;
139 }
140
141 if (target_has_overlay(fi.id)) {
142#ifdef MSM_BSP
143 close(fd);
144 return &my_backend;
145#else
146 printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id);
147#endif
148 }
149 close(fd);
150 return NULL;
151}
152
153static void overlay_blank(minui_backend* backend __unused, bool blank)
154{
155#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
156 int fd;
157 char brightness[4];
158 snprintf(brightness, 4, "%03d", TW_MAX_BRIGHTNESS/2);
159
160 fd = open(TW_BRIGHTNESS_PATH, O_RDWR);
161 if (fd < 0) {
162 perror("cannot open LCD backlight");
163 return;
164 }
165 write(fd, blank ? "000" : brightness, 3);
166 close(fd);
167#else
168 int ret;
169
170 ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
171 if (ret < 0)
172 perror("ioctl(): blank");
173#endif
174}
175
176static void set_displayed_framebuffer(unsigned n)
177{
178 if (n > 1 || !double_buffered) return;
179
180 vi.yres_virtual = gr_framebuffer[0].height * 2;
181 vi.yoffset = n * gr_framebuffer[0].height;
182 vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
183 if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
184 perror("active fb swap failed");
185 }
186 displayed_buffer = n;
187}
188
189#ifdef MSM_BSP
190void setDisplaySplit(void) {
191 char split[64] = {0};
192 if (!isMDP5)
193 return;
194 FILE* fp = fopen("/sys/class/graphics/fb0/msm_fb_split", "r");
195 if (fp) {
196 //Format "left right" space as delimiter
197 if(fread(split, sizeof(char), 64, fp)) {
198 leftSplit = atoi(split);
199 printf("Left Split=%d\n",leftSplit);
200 char *rght = strpbrk(split, " ");
201 if (rght)
202 rightSplit = atoi(rght + 1);
203 printf("Right Split=%d\n", rightSplit);
204 }
205 } else {
206 printf("Failed to open mdss_fb_split node\n");
207 }
208 if (fp)
209 fclose(fp);
210}
211
212int getLeftSplit(void) {
213 //Default even split for all displays with high res
214 int lSplit = vi.xres / 2;
215
216 //Override if split published by driver
217 if (leftSplit)
218 lSplit = leftSplit;
219
220 return lSplit;
221}
222
223int getRightSplit(void) {
224 return rightSplit;
225}
226
227int free_ion_mem(void) {
228 int ret = 0;
229
230 if (mem_info.mem_buf)
231 munmap(mem_info.mem_buf, mem_info.size);
232
233 if (mem_info.ion_fd >= 0) {
234 ret = ioctl(mem_info.ion_fd, ION_IOC_FREE, &mem_info.handle_data);
235 if (ret < 0)
236 perror("free_mem failed ");
237 }
238
239 if (mem_info.mem_fd >= 0)
240 close(mem_info.mem_fd);
241 if (mem_info.ion_fd >= 0)
242 close(mem_info.ion_fd);
243
244 memset(&mem_info, 0, sizeof(mem_info));
245 mem_info.mem_fd = -1;
246 mem_info.ion_fd = -1;
247 return 0;
248}
249
250int alloc_ion_mem(unsigned int size)
251{
252 int result;
253 struct ion_fd_data fd_data;
254 struct ion_allocation_data ionAllocData;
255
256 mem_info.ion_fd = open("/dev/ion", O_RDWR|O_DSYNC);
257 if (mem_info.ion_fd < 0) {
258 perror("ERROR: Can't open ion ");
259 return -errno;
260 }
261
262 ionAllocData.flags = 0;
263 ionAllocData.len = size;
264 ionAllocData.align = sysconf(_SC_PAGESIZE);
265#ifdef NEW_ION_HEAP
266 ionAllocData.heap_id_mask =
267#else
268 ionAllocData.heap_mask =
269#endif
270 ION_HEAP(ION_IOMMU_HEAP_ID) |
271 ION_HEAP(ION_SYSTEM_CONTIG_HEAP_ID);
272
273 result = ioctl(mem_info.ion_fd, ION_IOC_ALLOC, &ionAllocData);
274 if(result){
275 perror("ION_IOC_ALLOC Failed ");
276 close(mem_info.ion_fd);
277 return result;
278 }
279
280 fd_data.handle = ionAllocData.handle;
281 mem_info.handle_data.handle = ionAllocData.handle;
282 result = ioctl(mem_info.ion_fd, ION_IOC_MAP, &fd_data);
283 if (result) {
284 perror("ION_IOC_MAP Failed ");
285 free_ion_mem();
286 return result;
287 }
288 mem_info.mem_buf = (unsigned char *)mmap(NULL, size, PROT_READ |
289 PROT_WRITE, MAP_SHARED, fd_data.fd, 0);
290 mem_info.mem_fd = fd_data.fd;
291
292 if (!mem_info.mem_buf) {
293 perror("ERROR: mem_buf MAP_FAILED ");
294 free_ion_mem();
295 return -ENOMEM;
296 }
297
298 return 0;
299}
300
301bool isDisplaySplit(void) {
302 if (vi.xres > MAX_DISPLAY_DIM)
303 return true;
304 //check if right split is set by driver
305 if (getRightSplit())
306 return true;
307
308 return false;
309}
310
311int allocate_overlay(int fd, GRSurface gr_fb[])
312{
313 int ret = 0;
314
315 if (!isDisplaySplit()) {
316 // Check if overlay is already allocated
317 if (MSMFB_NEW_REQUEST == overlayL_id) {
318 struct mdp_overlay overlayL;
319
320 memset(&overlayL, 0 , sizeof (struct mdp_overlay));
321
322 /* Fill Overlay Data */
323 overlayL.src.width = ALIGN(gr_fb[0].width, 32);
324 overlayL.src.height = gr_fb[0].height;
325 overlayL.src.format = map_mdp_pixel_format();
326 overlayL.src_rect.w = gr_fb[0].width;
327 overlayL.src_rect.h = gr_fb[0].height;
328 overlayL.dst_rect.w = gr_fb[0].width;
329 overlayL.dst_rect.h = gr_fb[0].height;
330 overlayL.alpha = 0xFF;
331 overlayL.transp_mask = MDP_TRANSP_NOP;
332 overlayL.id = MSMFB_NEW_REQUEST;
333 ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
334 if (ret < 0) {
335 perror("Overlay Set Failed");
336 return ret;
337 }
338 overlayL_id = overlayL.id;
339 }
340 } else {
341 float xres = vi.xres;
342 int lSplit = getLeftSplit();
343 float lSplitRatio = lSplit / xres;
344 float lCropWidth = gr_fb[0].width * lSplitRatio;
345 int lWidth = lSplit;
346 int rWidth = gr_fb[0].width - lSplit;
347 int height = gr_fb[0].height;
348
349 if (MSMFB_NEW_REQUEST == overlayL_id) {
350
351 struct mdp_overlay overlayL;
352
353 memset(&overlayL, 0 , sizeof (struct mdp_overlay));
354
355 /* Fill OverlayL Data */
356 overlayL.src.width = ALIGN(gr_fb[0].width, 32);
357 overlayL.src.height = gr_fb[0].height;
358 overlayL.src.format = map_mdp_pixel_format();
359 overlayL.src_rect.x = 0;
360 overlayL.src_rect.y = 0;
361 overlayL.src_rect.w = lCropWidth;
362 overlayL.src_rect.h = gr_fb[0].height;
363 overlayL.dst_rect.x = 0;
364 overlayL.dst_rect.y = 0;
365 overlayL.dst_rect.w = lWidth;
366 overlayL.dst_rect.h = height;
367 overlayL.alpha = 0xFF;
368 overlayL.transp_mask = MDP_TRANSP_NOP;
369 overlayL.id = MSMFB_NEW_REQUEST;
370 ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
371 if (ret < 0) {
372 perror("OverlayL Set Failed");
373 return ret;
374 }
375 overlayL_id = overlayL.id;
376 }
377 if (MSMFB_NEW_REQUEST == overlayR_id) {
378 struct mdp_overlay overlayR;
379
380 memset(&overlayR, 0 , sizeof (struct mdp_overlay));
381
382 /* Fill OverlayR Data */
383 overlayR.src.width = ALIGN(gr_fb[0].width, 32);
384 overlayR.src.height = gr_fb[0].height;
385 overlayR.src.format = map_mdp_pixel_format();
386 overlayR.src_rect.x = lCropWidth;
387 overlayR.src_rect.y = 0;
388 overlayR.src_rect.w = gr_fb[0].width - lCropWidth;
389 overlayR.src_rect.h = gr_fb[0].height;
390 overlayR.dst_rect.x = 0;
391 overlayR.dst_rect.y = 0;
392 overlayR.dst_rect.w = rWidth;
393 overlayR.dst_rect.h = height;
394 overlayR.alpha = 0xFF;
395 overlayR.flags = MDSS_MDP_RIGHT_MIXER;
396 overlayR.transp_mask = MDP_TRANSP_NOP;
397 overlayR.id = MSMFB_NEW_REQUEST;
398 ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayR);
399 if (ret < 0) {
400 perror("OverlayR Set Failed");
401 return ret;
402 }
403 overlayR_id = overlayR.id;
404 }
405
406 }
407 return 0;
408}
409
410int overlay_display_frame(int fd, void* data, size_t size)
411{
412 int ret = 0;
413 struct msmfb_overlay_data ovdataL, ovdataR;
414 struct mdp_display_commit ext_commit;
415
416 if (!isDisplaySplit()) {
417 if (overlayL_id == MSMFB_NEW_REQUEST) {
418 perror("display_frame failed, no overlay\n");
419 return -EINVAL;
420 }
421
422 memcpy(mem_info.mem_buf, data, size);
423
424 memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data));
425
426 ovdataL.id = overlayL_id;
427 ovdataL.data.flags = 0;
428 ovdataL.data.offset = 0;
429 ovdataL.data.memory_id = mem_info.mem_fd;
430 ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL);
431 if (ret < 0) {
432 perror("overlay_display_frame failed, overlay play Failed\n");
433 return ret;
434 }
435 } else {
436
437 if (overlayL_id == MSMFB_NEW_REQUEST) {
438 perror("display_frame failed, no overlayL \n");
439 return -EINVAL;
440 }
441
442 memcpy(mem_info.mem_buf, data, size);
443
444 memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data));
445
446 ovdataL.id = overlayL_id;
447 ovdataL.data.flags = 0;
448 ovdataL.data.offset = 0;
449 ovdataL.data.memory_id = mem_info.mem_fd;
450 ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL);
451 if (ret < 0) {
452 perror("overlay_display_frame failed, overlayL play Failed\n");
453 return ret;
454 }
455
456 if (overlayR_id == MSMFB_NEW_REQUEST) {
457 perror("display_frame failed, no overlayR \n");
458 return -EINVAL;
459 }
460 memset(&ovdataR, 0, sizeof(struct msmfb_overlay_data));
461
462 ovdataR.id = overlayR_id;
463 ovdataR.data.flags = 0;
464 ovdataR.data.offset = 0;
465 ovdataR.data.memory_id = mem_info.mem_fd;
466 ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataR);
467 if (ret < 0) {
468 perror("overlay_display_frame failed, overlayR play Failed\n");
469 return ret;
470 }
471 }
472 memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
473 ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
474 ext_commit.wait_for_finish = 1;
475 ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
476 if (ret < 0) {
477 perror("overlay_display_frame failed, overlay commit Failed\n!");
478 }
479
480 return ret;
481}
482
483static GRSurface* overlay_flip(minui_backend* backend __unused) {
484 if (double_buffered) {
485#if defined(RECOVERY_BGRA)
486 // In case of BGRA, do some byte swapping
487 unsigned int idx;
488 unsigned char tmp;
489 unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data;
490 for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes);
491 idx += 4) {
492 tmp = ucfb_vaddr[idx];
493 ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2];
494 ucfb_vaddr[idx + 2] = tmp;
495 }
496#endif
497 // Change gr_draw to point to the buffer currently displayed,
498 // then flip the driver so we're displaying the other buffer
499 // instead.
500 gr_draw = gr_framebuffer + displayed_buffer;
501 set_displayed_framebuffer(1-displayed_buffer);
502 overlay_display_frame(fb_fd, gr_draw->data, frame_size);
503 } else {
504 // Copy from the in-memory surface to the framebuffer.
505 overlay_display_frame(fb_fd, gr_draw->data, frame_size);
506 }
507 return gr_draw;
508}
509
510int free_overlay(int fd)
511{
512 int ret = 0;
513 struct mdp_display_commit ext_commit;
514
515 if (!isDisplaySplit()) {
516 if (overlayL_id != MSMFB_NEW_REQUEST) {
517 ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id);
518 if (ret) {
519 perror("Overlay Unset Failed");
520 overlayL_id = MSMFB_NEW_REQUEST;
521 return ret;
522 }
523 }
524 } else {
525
526 if (overlayL_id != MSMFB_NEW_REQUEST) {
527 ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id);
528 if (ret) {
529 perror("OverlayL Unset Failed");
530 overlayL_id = MSMFB_NEW_REQUEST;
531 return ret;
532 }
533 }
534
535 if (overlayR_id != MSMFB_NEW_REQUEST) {
536 ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayR_id);
537 if (ret) {
538 perror("OverlayR Unset Failed");
539 overlayR_id = MSMFB_NEW_REQUEST;
540 return ret;
541 }
542 }
543 }
544 memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
545 ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
546 ext_commit.wait_for_finish = 1;
547 ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
548 if (ret < 0) {
549 perror("ERROR: Clear MSMFB_DISPLAY_COMMIT failed!");
550 overlayL_id = MSMFB_NEW_REQUEST;
551 overlayR_id = MSMFB_NEW_REQUEST;
552 return ret;
553 }
554 overlayL_id = MSMFB_NEW_REQUEST;
555 overlayR_id = MSMFB_NEW_REQUEST;
556
557 return 0;
558}
559
560static GRSurface* overlay_init(minui_backend* backend) {
561 int fd = open("/dev/graphics/fb0", O_RDWR);
562 if (fd == -1) {
563 perror("cannot open fb0");
564 return NULL;
565 }
566
567 fb_fix_screeninfo fi;
568 if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
569 perror("failed to get fb0 info");
570 close(fd);
571 return NULL;
572 }
573
574 if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
575 perror("failed to get fb0 info");
576 close(fd);
577 return NULL;
578 }
579
580 // We print this out for informational purposes only, but
581 // throughout we assume that the framebuffer device uses an RGBX
582 // pixel format. This is the case for every development device I
583 // have access to. For some of those devices (eg, hammerhead aka
584 // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
585 // different format (XBGR) but actually produces the correct
586 // results on the display when you write RGBX.
587 //
588 // If you have a device that actually *needs* another pixel format
589 // (ie, BGRX, or 565), patches welcome...
590
591 printf("fb0 reports (possibly inaccurate):\n"
592 " vi.bits_per_pixel = %d\n"
593 " vi.red.offset = %3d .length = %3d\n"
594 " vi.green.offset = %3d .length = %3d\n"
595 " vi.blue.offset = %3d .length = %3d\n",
596 vi.bits_per_pixel,
597 vi.red.offset, vi.red.length,
598 vi.green.offset, vi.green.length,
599 vi.blue.offset, vi.blue.length);
600
601 void* bits = malloc(vi.xres_virtual * vi.yres * (vi.bits_per_pixel / 8));
602 if (bits == NULL) {
603 perror("failed to malloc framebuffer");
604 close(fd);
605 return NULL;
606 }
607
608 memset(bits, 0, fi.smem_len);
609
610 gr_framebuffer[0].width = vi.xres;
611 gr_framebuffer[0].height = vi.yres;
612 gr_framebuffer[0].row_bytes = fi.line_length;
613 gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
614 gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
615 memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
616
617 /* check if we can use double buffering */
618 if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
619 double_buffered = true;
620 printf("double buffered.\n");
621 memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface));
622 gr_framebuffer[1].data = gr_framebuffer[0].data +
623 gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
624
625 gr_draw = gr_framebuffer+1;
626
627 } else {
628 double_buffered = false;
629 printf("single buffered.\n");
630 // Without double-buffering, we allocate RAM for a buffer to
631 // draw in, and then "flipping" the buffer consists of a
632 // memcpy from the buffer we allocated to the framebuffer.
633
634 gr_draw = (GRSurface*) malloc(sizeof(GRSurface));
635 if (gr_draw == NULL) {
636 printf("Failed to malloc gr_draw for single buffering.\n");
637 return NULL;
638 } else {
639 memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
640 gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
641 if (!gr_draw->data) {
642 perror("failed to allocate in-memory surface");
643 return NULL;
644 }
645 }
646 }
647
648 memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
649 fb_fd = fd;
650 set_displayed_framebuffer(0);
651
652 frame_size = fi.line_length * vi.yres;
653
654 printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
655
656 overlay_blank(backend, true);
657 overlay_blank(backend, false);
658
659 if (!alloc_ion_mem(fi.line_length * vi.yres))
660 allocate_overlay(fb_fd, gr_framebuffer);
661
662 return gr_draw;
663}
664
665static void overlay_exit(minui_backend* backend __unused) {
666 free_overlay(fb_fd);
667 free_ion_mem();
668
669 close(fb_fd);
670 fb_fd = -1;
671
672 if (!double_buffered && gr_draw) {
673 free(gr_draw->data);
674 free(gr_draw);
675 }
676 gr_draw = NULL;
677 if (gr_framebuffer[0].data) {
678 free(gr_framebuffer[0].data);
679 gr_framebuffer[0].data = NULL;
680 }
681}
682#else // MSM_BSP
683static GRSurface* overlay_flip(minui_backend* backend __unused) {
684 return NULL;
685}
686
687static GRSurface* overlay_init(minui_backend* backend __unused) {
688 return NULL;
689}
690
691static void overlay_exit(minui_backend* backend __unused) {
692 return;
693}
694#endif // MSM_BSP