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