blob: 5d759f6d4028f19cb7f6ca4ac5aa0873ef2d49ef [file] [log] [blame]
Ethan Yonkerfbb43532015-12-28 21:54:50 +01001/*
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#include <pixelflinger/pixelflinger.h>
42
43#define MDP_V4_0 400
44#define MAX_DISPLAY_DIM 2048
45
46static GRSurface* overlay_init(minui_backend*);
47static GRSurface* overlay_flip(minui_backend*);
48static void overlay_blank(minui_backend*, bool);
49static void overlay_exit(minui_backend*);
50
51static GRSurface gr_framebuffer;
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
79static int map_mdp_pixel_format()
80{
81 if (gr_framebuffer.format == GGL_PIXEL_FORMAT_RGB_565)
82 return MDP_RGB_565;
83 else if (gr_framebuffer.format == GGL_PIXEL_FORMAT_BGRA_8888)
84 return MDP_BGRA_8888;
85 else if (gr_framebuffer.format == GGL_PIXEL_FORMAT_RGBA_8888)
86 return MDP_RGBA_8888;
87 else if (gr_framebuffer.format == GGL_PIXEL_FORMAT_RGBX_8888)
88 return MDP_RGBA_8888;
89 printf("No known pixel format for map_mdp_pixel_format, defaulting to MDP_RGB_565.\n");
90 return MDP_RGB_565;
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
176#ifdef MSM_BSP
177void setDisplaySplit(void) {
178 char split[64] = {0};
179 if (!isMDP5)
180 return;
181 FILE* fp = fopen("/sys/class/graphics/fb0/msm_fb_split", "r");
182 if (fp) {
183 //Format "left right" space as delimiter
184 if(fread(split, sizeof(char), 64, fp)) {
185 leftSplit = atoi(split);
186 printf("Left Split=%d\n",leftSplit);
187 char *rght = strpbrk(split, " ");
188 if (rght)
189 rightSplit = atoi(rght + 1);
190 printf("Right Split=%d\n", rightSplit);
191 }
192 } else {
193 printf("Failed to open mdss_fb_split node\n");
194 }
195 if (fp)
196 fclose(fp);
197}
198
199int getLeftSplit(void) {
200 //Default even split for all displays with high res
201 int lSplit = vi.xres / 2;
202
203 //Override if split published by driver
204 if (leftSplit)
205 lSplit = leftSplit;
206
207 return lSplit;
208}
209
210int getRightSplit(void) {
211 return rightSplit;
212}
213
214int free_ion_mem(void) {
215 int ret = 0;
216
217 if (mem_info.mem_buf)
218 munmap(mem_info.mem_buf, mem_info.size);
219
220 if (mem_info.ion_fd >= 0) {
221 ret = ioctl(mem_info.ion_fd, ION_IOC_FREE, &mem_info.handle_data);
222 if (ret < 0)
223 perror("free_mem failed ");
224 }
225
226 if (mem_info.mem_fd >= 0)
227 close(mem_info.mem_fd);
228 if (mem_info.ion_fd >= 0)
229 close(mem_info.ion_fd);
230
231 memset(&mem_info, 0, sizeof(mem_info));
232 mem_info.mem_fd = -1;
233 mem_info.ion_fd = -1;
234 return 0;
235}
236
237int alloc_ion_mem(unsigned int size)
238{
239 int result;
240 struct ion_fd_data fd_data;
241 struct ion_allocation_data ionAllocData;
242
243 mem_info.ion_fd = open("/dev/ion", O_RDWR|O_DSYNC);
244 if (mem_info.ion_fd < 0) {
245 perror("ERROR: Can't open ion ");
246 return -errno;
247 }
248
249 ionAllocData.flags = 0;
250 ionAllocData.len = size;
251 ionAllocData.align = sysconf(_SC_PAGESIZE);
252#ifdef NEW_ION_HEAP
253 ionAllocData.heap_id_mask =
254#else
255 ionAllocData.heap_mask =
256#endif
257 ION_HEAP(ION_IOMMU_HEAP_ID) |
258 ION_HEAP(ION_SYSTEM_CONTIG_HEAP_ID);
259
260 result = ioctl(mem_info.ion_fd, ION_IOC_ALLOC, &ionAllocData);
261 if(result){
262 perror("ION_IOC_ALLOC Failed ");
263 close(mem_info.ion_fd);
264 return result;
265 }
266
267 fd_data.handle = ionAllocData.handle;
268 mem_info.handle_data.handle = ionAllocData.handle;
269 result = ioctl(mem_info.ion_fd, ION_IOC_MAP, &fd_data);
270 if (result) {
271 perror("ION_IOC_MAP Failed ");
272 free_ion_mem();
273 return result;
274 }
275 mem_info.mem_buf = (unsigned char *)mmap(NULL, size, PROT_READ |
276 PROT_WRITE, MAP_SHARED, fd_data.fd, 0);
277 mem_info.mem_fd = fd_data.fd;
278
279 if (!mem_info.mem_buf) {
280 perror("ERROR: mem_buf MAP_FAILED ");
281 free_ion_mem();
282 return -ENOMEM;
283 }
284
285 return 0;
286}
287
288bool isDisplaySplit(void) {
289 if (vi.xres > MAX_DISPLAY_DIM)
290 return true;
291 //check if right split is set by driver
292 if (getRightSplit())
293 return true;
294
295 return false;
296}
297
298int allocate_overlay(int fd, GRSurface gr_fb)
299{
300 int ret = 0;
301
302 if (!isDisplaySplit()) {
303 // Check if overlay is already allocated
304 if (MSMFB_NEW_REQUEST == overlayL_id) {
305 struct mdp_overlay overlayL;
306
307 memset(&overlayL, 0 , sizeof (struct mdp_overlay));
308
309 /* Fill Overlay Data */
310 overlayL.src.width = ALIGN(gr_fb.width, 32);
311 overlayL.src.height = gr_fb.height;
312 overlayL.src.format = map_mdp_pixel_format();
313 overlayL.src_rect.w = gr_fb.width;
314 overlayL.src_rect.h = gr_fb.height;
315 overlayL.dst_rect.w = gr_fb.width;
316 overlayL.dst_rect.h = gr_fb.height;
317 overlayL.alpha = 0xFF;
xadro7509bc52016-01-30 21:30:12 +0100318#ifdef BOARD_HAS_FLIPPED_SCREEN
319 overlayL.flags = MDP_ROT_180;
320#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100321 overlayL.transp_mask = MDP_TRANSP_NOP;
322 overlayL.id = MSMFB_NEW_REQUEST;
323 ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
324 if (ret < 0) {
325 perror("Overlay Set Failed");
326 return ret;
327 }
328 overlayL_id = overlayL.id;
329 }
330 } else {
331 float xres = vi.xres;
332 int lSplit = getLeftSplit();
333 float lSplitRatio = lSplit / xres;
334 float lCropWidth = gr_fb.width * lSplitRatio;
335 int lWidth = lSplit;
336 int rWidth = gr_fb.width - lSplit;
337 int height = gr_fb.height;
338
339 if (MSMFB_NEW_REQUEST == overlayL_id) {
340
341 struct mdp_overlay overlayL;
342
343 memset(&overlayL, 0 , sizeof (struct mdp_overlay));
344
345 /* Fill OverlayL Data */
346 overlayL.src.width = ALIGN(gr_fb.width, 32);
347 overlayL.src.height = gr_fb.height;
348 overlayL.src.format = map_mdp_pixel_format();
349 overlayL.src_rect.x = 0;
350 overlayL.src_rect.y = 0;
351 overlayL.src_rect.w = lCropWidth;
352 overlayL.src_rect.h = gr_fb.height;
353 overlayL.dst_rect.x = 0;
354 overlayL.dst_rect.y = 0;
355 overlayL.dst_rect.w = lWidth;
356 overlayL.dst_rect.h = height;
357 overlayL.alpha = 0xFF;
xadro7509bc52016-01-30 21:30:12 +0100358#ifdef BOARD_HAS_FLIPPED_SCREEN
359 overlayL.flags = MDP_ROT_180;
360#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100361 overlayL.transp_mask = MDP_TRANSP_NOP;
362 overlayL.id = MSMFB_NEW_REQUEST;
363 ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
364 if (ret < 0) {
365 perror("OverlayL Set Failed");
366 return ret;
367 }
368 overlayL_id = overlayL.id;
369 }
370 if (MSMFB_NEW_REQUEST == overlayR_id) {
371 struct mdp_overlay overlayR;
372
373 memset(&overlayR, 0 , sizeof (struct mdp_overlay));
374
375 /* Fill OverlayR Data */
376 overlayR.src.width = ALIGN(gr_fb.width, 32);
377 overlayR.src.height = gr_fb.height;
378 overlayR.src.format = map_mdp_pixel_format();
379 overlayR.src_rect.x = lCropWidth;
380 overlayR.src_rect.y = 0;
381 overlayR.src_rect.w = gr_fb.width - lCropWidth;
382 overlayR.src_rect.h = gr_fb.height;
383 overlayR.dst_rect.x = 0;
384 overlayR.dst_rect.y = 0;
385 overlayR.dst_rect.w = rWidth;
386 overlayR.dst_rect.h = height;
387 overlayR.alpha = 0xFF;
xadro7509bc52016-01-30 21:30:12 +0100388#ifdef BOARD_HAS_FLIPPED_SCREEN
389 overlayR.flags = MDSS_MDP_RIGHT_MIXER | MDP_ROT_180;
390#else
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100391 overlayR.flags = MDSS_MDP_RIGHT_MIXER;
xadro7509bc52016-01-30 21:30:12 +0100392#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100393 overlayR.transp_mask = MDP_TRANSP_NOP;
394 overlayR.id = MSMFB_NEW_REQUEST;
395 ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayR);
396 if (ret < 0) {
397 perror("OverlayR Set Failed");
398 return ret;
399 }
400 overlayR_id = overlayR.id;
401 }
402
403 }
404 return 0;
405}
406
407int overlay_display_frame(int fd, void* data, size_t size)
408{
409 int ret = 0;
410 struct msmfb_overlay_data ovdataL, ovdataR;
411 struct mdp_display_commit ext_commit;
412
413 if (!isDisplaySplit()) {
414 if (overlayL_id == MSMFB_NEW_REQUEST) {
415 perror("display_frame failed, no overlay\n");
416 return -EINVAL;
417 }
418
419 memcpy(mem_info.mem_buf, data, size);
420
421 memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data));
422
423 ovdataL.id = overlayL_id;
424 ovdataL.data.flags = 0;
425 ovdataL.data.offset = 0;
426 ovdataL.data.memory_id = mem_info.mem_fd;
427 ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL);
428 if (ret < 0) {
429 perror("overlay_display_frame failed, overlay play Failed\n");
430 printf("%i, %i, %i, %i\n", ret, fb_fd, fd, errno);
431 return ret;
432 }
433 } else {
434
435 if (overlayL_id == MSMFB_NEW_REQUEST) {
436 perror("display_frame failed, no overlayL \n");
437 return -EINVAL;
438 }
439
440 memcpy(mem_info.mem_buf, data, size);
441
442 memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data));
443
444 ovdataL.id = overlayL_id;
445 ovdataL.data.flags = 0;
446 ovdataL.data.offset = 0;
447 ovdataL.data.memory_id = mem_info.mem_fd;
448 ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL);
449 if (ret < 0) {
450 perror("overlay_display_frame failed, overlayL play Failed\n");
451 return ret;
452 }
453
454 if (overlayR_id == MSMFB_NEW_REQUEST) {
455 perror("display_frame failed, no overlayR \n");
456 return -EINVAL;
457 }
458 memset(&ovdataR, 0, sizeof(struct msmfb_overlay_data));
459
460 ovdataR.id = overlayR_id;
461 ovdataR.data.flags = 0;
462 ovdataR.data.offset = 0;
463 ovdataR.data.memory_id = mem_info.mem_fd;
464 ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataR);
465 if (ret < 0) {
466 perror("overlay_display_frame failed, overlayR play Failed\n");
467 return ret;
468 }
469 }
470 memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
471 ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
472 ext_commit.wait_for_finish = 1;
473 ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
474 if (ret < 0) {
475 perror("overlay_display_frame failed, overlay commit Failed\n!");
476 }
477
478 return ret;
479}
480
481static GRSurface* overlay_flip(minui_backend* backend __unused) {
482#if defined(RECOVERY_BGRA)
483 // In case of BGRA, do some byte swapping
484 unsigned int idx;
485 unsigned char tmp;
486 unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data;
487 for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes);
488 idx += 4) {
489 tmp = ucfb_vaddr[idx];
490 ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2];
491 ucfb_vaddr[idx + 2] = tmp;
492 }
493#endif
494 // Copy from the in-memory surface to the framebuffer.
495 overlay_display_frame(fb_fd, gr_draw->data, frame_size);
496 return gr_draw;
497}
498
499int free_overlay(int fd)
500{
501 int ret = 0;
502 struct mdp_display_commit ext_commit;
503
504 if (!isDisplaySplit()) {
505 if (overlayL_id != MSMFB_NEW_REQUEST) {
506 ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id);
507 if (ret) {
508 perror("Overlay Unset Failed");
509 overlayL_id = MSMFB_NEW_REQUEST;
510 return ret;
511 }
512 }
513 } else {
514
515 if (overlayL_id != MSMFB_NEW_REQUEST) {
516 ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id);
517 if (ret) {
518 perror("OverlayL Unset Failed");
519 }
520 }
521
522 if (overlayR_id != MSMFB_NEW_REQUEST) {
523 ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayR_id);
524 if (ret) {
525 perror("OverlayR Unset Failed");
526 overlayR_id = MSMFB_NEW_REQUEST;
527 return ret;
528 }
529 }
530 }
531 memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
532 ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
533 ext_commit.wait_for_finish = 1;
534 ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
535 if (ret < 0) {
536 perror("ERROR: Clear MSMFB_DISPLAY_COMMIT failed!");
537 overlayL_id = MSMFB_NEW_REQUEST;
538 overlayR_id = MSMFB_NEW_REQUEST;
539 return ret;
540 }
541 overlayL_id = MSMFB_NEW_REQUEST;
542 overlayR_id = MSMFB_NEW_REQUEST;
543
544 return 0;
545}
546
547static GRSurface* overlay_init(minui_backend* backend) {
548 int fd = open("/dev/graphics/fb0", O_RDWR);
549 if (fd == -1) {
550 perror("cannot open fb0");
551 return NULL;
552 }
553
554 fb_fix_screeninfo fi;
555 if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
556 perror("failed to get fb0 info");
557 close(fd);
558 return NULL;
559 }
560
561 if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
562 perror("failed to get fb0 info");
563 close(fd);
564 return NULL;
565 }
566
567 // We print this out for informational purposes only, but
568 // throughout we assume that the framebuffer device uses an RGBX
569 // pixel format. This is the case for every development device I
570 // have access to. For some of those devices (eg, hammerhead aka
571 // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
572 // different format (XBGR) but actually produces the correct
573 // results on the display when you write RGBX.
574 //
575 // If you have a device that actually *needs* another pixel format
576 // (ie, BGRX, or 565), patches welcome...
577
578 printf("fb0 reports (possibly inaccurate):\n"
579 " vi.bits_per_pixel = %d\n"
580 " vi.red.offset = %3d .length = %3d\n"
581 " vi.green.offset = %3d .length = %3d\n"
582 " vi.blue.offset = %3d .length = %3d\n",
583 vi.bits_per_pixel,
584 vi.red.offset, vi.red.length,
585 vi.green.offset, vi.green.length,
586 vi.blue.offset, vi.blue.length);
587
588 gr_framebuffer.width = vi.xres;
589 gr_framebuffer.height = vi.yres;
590 gr_framebuffer.row_bytes = fi.line_length;
591 gr_framebuffer.pixel_bytes = vi.bits_per_pixel / 8;
592 //gr_framebuffer.data = reinterpret_cast<uint8_t*>(bits);
593 if (vi.bits_per_pixel == 16) {
594 printf("setting GGL_PIXEL_FORMAT_RGB_565\n");
595 gr_framebuffer.format = GGL_PIXEL_FORMAT_RGB_565;
Ziyan4fea38a2016-01-28 01:19:35 +0100596 } else if (vi.red.offset == 8 || vi.red.offset == 16) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100597 printf("setting GGL_PIXEL_FORMAT_BGRA_8888\n");
598 gr_framebuffer.format = GGL_PIXEL_FORMAT_BGRA_8888;
599 } else if (vi.red.offset == 0) {
600 printf("setting GGL_PIXEL_FORMAT_RGBA_8888\n");
601 gr_framebuffer.format = GGL_PIXEL_FORMAT_RGBA_8888;
602 } else if (vi.red.offset == 24) {
603 printf("setting GGL_PIXEL_FORMAT_RGBX_8888\n");
604 gr_framebuffer.format = GGL_PIXEL_FORMAT_RGBX_8888;
605 } else {
606 if (vi.red.length == 8) {
607 printf("No valid pixel format detected, trying GGL_PIXEL_FORMAT_RGBX_8888\n");
608 gr_framebuffer.format = GGL_PIXEL_FORMAT_RGBX_8888;
609 } else {
610 printf("No valid pixel format detected, trying GGL_PIXEL_FORMAT_RGB_565\n");
611 gr_framebuffer.format = GGL_PIXEL_FORMAT_RGB_565;
612 }
613 }
614
615 frame_size = fi.line_length * vi.yres;
616
617 gr_framebuffer.data = reinterpret_cast<uint8_t*>(calloc(frame_size, 1));
618 if (gr_framebuffer.data == NULL) {
619 perror("failed to calloc framebuffer");
620 close(fd);
621 return NULL;
622 }
623
624 gr_draw = &gr_framebuffer;
625 fb_fd = fd;
626
627 printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
628
629 overlay_blank(backend, true);
630 overlay_blank(backend, false);
631
632 if (!alloc_ion_mem(frame_size))
633 allocate_overlay(fb_fd, gr_framebuffer);
634
635 return gr_draw;
636}
637
638static void overlay_exit(minui_backend* backend __unused) {
639 free_overlay(fb_fd);
640 free_ion_mem();
641
642 close(fb_fd);
643 fb_fd = -1;
644
645 if (gr_draw) {
646 free(gr_draw->data);
647 free(gr_draw);
648 }
649 gr_draw = NULL;
650}
651#else // MSM_BSP
652static GRSurface* overlay_flip(minui_backend* backend __unused) {
653 return NULL;
654}
655
656static GRSurface* overlay_init(minui_backend* backend __unused) {
657 return NULL;
658}
659
660static void overlay_exit(minui_backend* backend __unused) {
661 return;
662}
663#endif // MSM_BSP