1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2013-2015 - Tobias Jakobi
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <unistd.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <fcntl.h>
21 #include <poll.h>
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdint.h>
26
27 #include <drm_fourcc.h>
28 #include <libdrm/exynos_drmif.h>
29 #include <exynos/exynos_fimg2d.h>
30
31 #include <retro_inline.h>
32 #include <retro_assert.h>
33 #include <string/stdstring.h>
34
35 #ifdef HAVE_CONFIG_H
36 #include "../../config.h"
37 #endif
38
39 #ifdef HAVE_MENU
40 #include "../../menu/menu_driver.h"
41 #endif
42
43 #include "../common/drm_common.h"
44 #include "../font_driver.h"
45 #include "../../configuration.h"
46 #include "../../retroarch.h"
47
48 /* TODO: Honor these properties: vsync, menu rotation, menu alpha, aspect ratio change */
49
50 /* Set to '1' to enable debug logging code. */
51 #define EXYNOS_GFX_DEBUG_LOG 0
52
53 /* Set to '1' to enable debug perf code. */
54 #define EXYNOS_GFX_DEBUG_PERF 0
55
56 extern void *memcpy_neon(void *dst, const void *src, size_t n);
57
58 /* We use two GEM buffers (main and aux) to handle 'data' from the frontend. */
59 enum exynos_buffer_type
60 {
61 EXYNOS_BUFFER_MAIN = 0,
62 EXYNOS_BUFFER_AUX,
63 EXYNOS_BUFFER_COUNT
64 };
65
66 /* We have to handle three types of 'data' from the frontend, each abstracted by a *
67 * G2D image object. The image objects are then backed by some storage buffer. *
68 * (1) the core framebuffer (backed by main buffer) *
69 * (2) the menu buffer (backed by aux buffer) *
70 * (3) the font rendering buffer (backed by aux buffer) */
71 enum exynos_image_type
72 {
73 EXYNOS_IMAGE_FRAME = 0,
74 EXYNOS_IMAGE_FRONT,
75 EXYNOS_IMAGE_MENU,
76 EXYNOS_IMAGE_COUNT
77 };
78
79 static const struct exynos_config_default
80 {
81 unsigned width, height;
82 enum exynos_buffer_type buf_type;
83 unsigned g2d_color_mode;
84 unsigned bpp; /* bytes per pixel */
85 } defaults[EXYNOS_IMAGE_COUNT] = {
86 {1024, 640, EXYNOS_BUFFER_MAIN, G2D_COLOR_FMT_RGB565 | G2D_ORDER_AXRGB, 2}, /* frame */
87 {720, 368, EXYNOS_BUFFER_AUX, G2D_COLOR_FMT_ARGB4444 | G2D_ORDER_AXRGB, 2}, /* font */
88 {400, 240, EXYNOS_BUFFER_AUX, G2D_COLOR_FMT_ARGB4444 | G2D_ORDER_RGBAX, 2} /* menu */
89 };
90
91 struct exynos_data;
92
93 #if (EXYNOS_GFX_DEBUG_PERF == 1)
94 struct exynos_perf
95 {
96 unsigned memcpy_calls;
97 unsigned g2d_calls;
98
99 unsigned long long memcpy_time;
100 unsigned long long g2d_time;
101
102 struct timespec tspec;
103 };
104 #endif
105
106 struct exynos_page
107 {
108 struct exynos_bo *bo;
109 uint32_t buf_id;
110
111 struct exynos_data *base;
112
113 bool used; /* Set if page is currently used. */
114 bool clear; /* Set if page has to be cleared. */
115 };
116
117 struct exynos_data
118 {
119 char drmname[32];
120 struct exynos_device *device;
121
122 /* G2D is used for scaling to framebuffer dimensions. */
123 struct g2d_context *g2d;
124 struct g2d_image *dst;
125 struct g2d_image *src[EXYNOS_IMAGE_COUNT];
126
127 struct exynos_bo *buf[EXYNOS_BUFFER_COUNT];
128
129 struct exynos_page *pages;
130 unsigned num_pages;
131
132 /* currently displayed page */
133 struct exynos_page *cur_page;
134
135 unsigned pageflip_pending;
136
137 /* framebuffer dimensions */
138 unsigned width, height;
139
140 /* framebuffer aspect ratio */
141 float aspect;
142
143 /* parameters for blitting core fb to screen */
144 unsigned blit_params[6];
145
146 /* bytes per pixel */
147 unsigned bpp;
148
149 /* framebuffer parameters */
150 unsigned pitch, size;
151
152 bool sync;
153
154 #if (EXYNOS_GFX_DEBUG_PERF == 1)
155 struct exynos_perf perf;
156 #endif
157 };
158
align_common(unsigned i,unsigned j)159 static INLINE unsigned align_common(unsigned i, unsigned j)
160 {
161 return (i + j - 1) & ~(j - 1);
162 }
163
164 /* Find the index of a compatible DRM device. */
exynos_get_device_index(void)165 static int exynos_get_device_index(void)
166 {
167 drmVersionPtr ver;
168 char buf[32] = {0};
169 int index = 0;
170 bool found = false;
171
172 while (!found)
173 {
174 int fd;
175
176 snprintf(buf, sizeof(buf), "/dev/dri/card%d", index);
177
178 fd = open(buf, O_RDWR);
179 if (fd < 0) break;
180
181 ver = drmGetVersion(fd);
182
183 if (string_is_equal(ver->name, "exynos"))
184 found = true;
185 else
186 ++index;
187
188 drmFreeVersion(ver);
189 close(fd);
190 }
191
192 if (!found)
193 return -1;
194 return index;
195 }
196
197 /* The main pageflip handler, which the DRM executes
198 * when it flips to the page.
199 *
200 * Decreases the pending pageflip count and
201 * updates the current page.
202 */
exynos_page_flip_handler(int fd,unsigned frame,unsigned sec,unsigned usec,void * data)203 static void exynos_page_flip_handler(int fd, unsigned frame, unsigned sec,
204 unsigned usec, void *data)
205 {
206 struct exynos_page *page = data;
207
208 if (page->base->cur_page)
209 page->base->cur_page->used = false;
210
211 page->base->pageflip_pending--;
212 page->base->cur_page = page;
213 }
214
exynos_get_free_page(struct exynos_page * p,unsigned cnt)215 static struct exynos_page *exynos_get_free_page(
216 struct exynos_page *p, unsigned cnt)
217 {
218 unsigned i;
219
220 for (i = 0; i < cnt; ++i)
221 {
222 if (!p[i].used)
223 return &p[i];
224 }
225
226 return NULL;
227 }
228
229 /* Count the number of used pages. */
exynos_pages_used(struct exynos_page * p,unsigned cnt)230 static unsigned exynos_pages_used(struct exynos_page *p, unsigned cnt)
231 {
232 unsigned i;
233 unsigned count = 0;
234
235 for (i = 0; i < cnt; ++i)
236 {
237 if (p[i].used)
238 ++count;
239 }
240
241 return count;
242 }
243
exynos_clean_up_pages(struct exynos_page * p,unsigned cnt)244 static void exynos_clean_up_pages(struct exynos_page *p, unsigned cnt)
245 {
246 unsigned i;
247
248 for (i = 0; i < cnt; ++i)
249 {
250 if (p[i].bo)
251 {
252 if (p[i].buf_id != 0)
253 drmModeRmFB(p[i].buf_id, p[i].bo->handle);
254
255 exynos_bo_destroy(p[i].bo);
256 }
257 }
258 }
259
260 #if (EXYNOS_GFX_DEBUG_LOG == 1)
exynos_buffer_name(enum exynos_buffer_type type)261 static const char *exynos_buffer_name(enum exynos_buffer_type type)
262 {
263 switch (type)
264 {
265 case EXYNOS_BUFFER_MAIN:
266 return "main";
267 case EXYNOS_BUFFER_AUX:
268 return "aux";
269 default:
270 retro_assert(false);
271 break;
272 }
273
274 return NULL;
275 }
276 #endif
277
278 /* Create a GEM buffer with userspace mapping.
279 * Buffer is cleared after creation. */
exynos_create_mapped_buffer(struct exynos_device * dev,unsigned size)280 static struct exynos_bo *exynos_create_mapped_buffer(
281 struct exynos_device *dev, unsigned size)
282 {
283 const unsigned flags = 0;
284 struct exynos_bo *buf = exynos_bo_create(dev, size, flags);
285
286 if (!buf)
287 {
288 RARCH_ERR("[video_exynos]: failed to create temp buffer object\n");
289 return NULL;
290 }
291
292 if (!exynos_bo_map(buf))
293 {
294 RARCH_ERR("[video_exynos]: failed to map temp buffer object\n");
295 exynos_bo_destroy(buf);
296 return NULL;
297 }
298
299 memset(buf->vaddr, 0, size);
300
301 return buf;
302 }
303
exynos_realloc_buffer(struct exynos_data * pdata,enum exynos_buffer_type type,unsigned size)304 static int exynos_realloc_buffer(struct exynos_data *pdata,
305 enum exynos_buffer_type type, unsigned size)
306 {
307 struct exynos_bo *buf = pdata->buf[type];
308
309 if (!buf)
310 return -1;
311
312 if (size > buf->size)
313 {
314 unsigned i;
315
316 #if (EXYNOS_GFX_DEBUG_LOG == 1)
317 RARCH_LOG("[video_exynos]: reallocating %s buffer (%u -> %u bytes)\n",
318 exynos_buffer_name(type), buf->size, size);
319 #endif
320
321 exynos_bo_destroy(buf);
322 buf = exynos_create_mapped_buffer(pdata->device, size);
323
324 if (!buf)
325 {
326 RARCH_ERR("[video_exynos]: reallocation failed\n");
327 return -1;
328 }
329
330 pdata->buf[type] = buf;
331
332 /* Map new GEM buffer to the G2D images backed by it. */
333 for (i = 0; i < EXYNOS_IMAGE_COUNT; ++i)
334 {
335 if (defaults[i].buf_type == type)
336 pdata->src[i]->bo[0] = buf->handle;
337 }
338 }
339
340 return 0;
341 }
342
343 /* Clear a buffer associated to a G2D image by doing a (fast) solid fill. */
exynos_clear_buffer(struct g2d_context * g2d,struct g2d_image * img)344 static int exynos_clear_buffer(struct g2d_context *g2d, struct g2d_image *img)
345 {
346 int ret = g2d_solid_fill(g2d, img, 0, 0, img->width, img->height);
347
348 if (ret == 0)
349 ret = g2d_exec(g2d);
350
351 if (ret != 0)
352 RARCH_ERR("[video_exynos]: failed to clear buffer using G2D\n");
353
354 return ret;
355 }
356
357 /* Put a font glyph at a position in the buffer that is backing the G2D font image object. */
exynos_put_glyph_rgba4444(struct exynos_data * pdata,const uint8_t * __restrict__ src,uint16_t color,unsigned g_width,unsigned g_height,unsigned g_pitch,unsigned dst_x,unsigned dst_y)358 static void exynos_put_glyph_rgba4444(struct exynos_data *pdata,
359 const uint8_t *__restrict__ src,
360 uint16_t color, unsigned g_width, unsigned g_height,
361 unsigned g_pitch, unsigned dst_x, unsigned dst_y)
362 {
363 unsigned x, y;
364 const enum exynos_image_type buf_type = defaults[EXYNOS_IMAGE_FONT].buf_type;
365 const unsigned buf_width = pdata->src[EXYNOS_IMAGE_FONT]->width;
366 uint16_t *__restrict__ dst = (uint16_t*)pdata->buf[buf_type]->vaddr +
367 dst_y * buf_width + dst_x;
368
369 for (y = 0; y < g_height; ++y, src += g_pitch, dst += buf_width)
370 {
371 for (x = 0; x < g_width; ++x)
372 {
373 const uint16_t blend = src[x];
374 dst[x] = color | ((blend << 8) & 0xf000);
375 }
376 }
377 }
378
379 #if (EXYNOS_GFX_DEBUG_PERF == 1)
exynos_perf_init(struct exynos_perf * p)380 static void exynos_perf_init(struct exynos_perf *p)
381 {
382 p->memcpy_calls = 0;
383 p->g2d_calls = 0;
384
385 p->memcpy_time = 0;
386 p->g2d_time = 0;
387
388 memset(&p->tspec, 0, sizeof(struct timespec));
389 }
390
exynos_perf_finish(struct exynos_perf * p)391 static void exynos_perf_finish(struct exynos_perf *p)
392 {
393 RARCH_LOG("[video_exynos]: debug: total memcpy calls: %u\n", p->memcpy_calls);
394 RARCH_LOG("[video_exynos]: debug: total g2d calls: %u\n", p->g2d_calls);
395
396 RARCH_LOG("[video_exynos]: debug: total memcpy time: %f seconds\n",
397 (double)p->memcpy_time / 1000000.0);
398 RARCH_LOG("[video_exynos]: debug: total g2d time: %f seconds\n",
399 (double)p->g2d_time / 1000000.0);
400
401 RARCH_LOG("[video_exynos]: debug: average time per memcpy call: %f microseconds\n",
402 (double)p->memcpy_time / (double)p->memcpy_calls);
403 RARCH_LOG("[video_exynos]: debug: average time per g2d call: %f microseconds\n",
404 (double)p->g2d_time / (double)p->g2d_calls);
405 }
406
exynos_perf_memcpy(struct exynos_perf * p,bool start)407 static void exynos_perf_memcpy(struct exynos_perf *p, bool start)
408 {
409 if (start)
410 clock_gettime(CLOCK_MONOTONIC, &p->tspec);
411 else
412 {
413 struct timespec new = { 0 };
414 clock_gettime(CLOCK_MONOTONIC, &new);
415
416 p->memcpy_time += (new.tv_sec - p->tspec.tv_sec) * 1000000;
417 p->memcpy_time += (new.tv_nsec - p->tspec.tv_nsec) / 1000;
418 ++p->memcpy_calls;
419 }
420 }
421
exynos_perf_g2d(struct exynos_perf * p,bool start)422 static void exynos_perf_g2d(struct exynos_perf *p, bool start)
423 {
424 if (start)
425 clock_gettime(CLOCK_MONOTONIC, &p->tspec);
426 else
427 {
428 struct timespec new = { 0 };
429 clock_gettime(CLOCK_MONOTONIC, &new);
430
431 p->g2d_time += (new.tv_sec - p->tspec.tv_sec) * 1000000;
432 p->g2d_time += (new.tv_nsec - p->tspec.tv_nsec) / 1000;
433 ++p->g2d_calls;
434 }
435 }
436 #endif
437
exynos_g2d_init(struct exynos_data * pdata)438 static int exynos_g2d_init(struct exynos_data *pdata)
439 {
440 unsigned i;
441 struct g2d_image *dst = NULL;
442 struct g2d_context *g2d = g2d_init(g_drm_fd);
443 if (!g2d)
444 return -1;
445
446 dst = calloc(1, sizeof(struct g2d_image));
447 if (!dst)
448 goto fail;
449
450 dst->buf_type = G2D_IMGBUF_GEM;
451 dst->color_mode = (pdata->bpp == 2) ? G2D_COLOR_FMT_RGB565 | G2D_ORDER_AXRGB :
452 G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
453 dst->width = pdata->width;
454 dst->height = pdata->height;
455 dst->stride = pdata->pitch;
456 dst->color = 0xff000000; /* Clear color for solid fill operation. */
457
458 for (i = 0; i < EXYNOS_IMAGE_COUNT; ++i)
459 {
460 const enum exynos_buffer_type buf_type = defaults[i].buf_type;
461 const unsigned buf_size = defaults[i].width * defaults[i].height * defaults[i].bpp;
462 struct g2d_image *src = (struct g2d_image*)calloc(1, sizeof(struct g2d_image));
463 if (!src)
464 break;
465
466 src->width = defaults[i].width;
467 src->height = defaults[i].height;
468 src->stride = defaults[i].width * defaults[i].bpp;
469
470 src->color_mode = defaults[i].g2d_color_mode;
471
472 /* Associate GEM buffer storage with G2D image. */
473 src->buf_type = G2D_IMGBUF_GEM;
474 src->bo[0] = pdata->buf[buf_type]->handle;
475
476 src->repeat_mode = G2D_REPEAT_MODE_PAD; /* Pad creates no border artifacts. */
477
478 /* Make sure that the storage buffer is large enough. If the code is working *
479 * properly, then this is just a NOP. Still put it here as an insurance. */
480 exynos_realloc_buffer(pdata, buf_type, buf_size);
481
482 pdata->src[i] = src;
483 }
484
485 if (i != EXYNOS_IMAGE_COUNT)
486 {
487 while (i-- > 0)
488 {
489 free(pdata->src[i]);
490 pdata->src[i] = NULL;
491 }
492 goto fail_src;
493 }
494
495 pdata->dst = dst;
496 pdata->g2d = g2d;
497
498 return 0;
499
500 fail_src:
501 free(dst);
502
503 fail:
504 g2d_fini(g2d);
505
506 return -1;
507 }
508
exynos_g2d_free(struct exynos_data * pdata)509 static void exynos_g2d_free(struct exynos_data *pdata)
510 {
511 unsigned i;
512
513 free(pdata->dst);
514
515 for (i = 0; i < EXYNOS_IMAGE_COUNT; ++i)
516 {
517 free(pdata->src[i]);
518 pdata->src[i] = NULL;
519 }
520
521 g2d_fini(pdata->g2d);
522 }
523
exynos_open(struct exynos_data * pdata)524 static int exynos_open(struct exynos_data *pdata)
525 {
526 unsigned i;
527 int fd = -1;
528 char buf[32] = {0};
529 int devidx = exynos_get_device_index();
530
531 if (pdata)
532 g_drm_fd = -1;
533
534 if (devidx != -1)
535 snprintf(buf, sizeof(buf), "/dev/dri/card%d", devidx);
536 else
537 {
538 RARCH_ERR("[video_exynos]: no compatible DRM device found\n");
539 return -1;
540 }
541
542 fd = open(buf, O_RDWR);
543
544 if (fd < 0)
545 {
546 RARCH_ERR("[video_exynos]: can't open DRM device\n");
547 return -1;
548 }
549
550 if (!drm_get_resources(fd))
551 goto fail;
552
553 if (!drm_get_decoder(fd))
554 goto fail;
555
556 if (!drm_get_encoder(fd))
557 goto fail;
558
559 /* Setup the flip handler. */
560 g_drm_fds.fd = fd;
561 g_drm_fds.events = POLLIN;
562 g_drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
563 g_drm_evctx.page_flip_handler = exynos_page_flip_handler;
564
565 strncpy(pdata->drmname, buf, sizeof(buf));
566 g_drm_fd = fd;
567
568 RARCH_LOG("[video_exynos]: using DRM device \"%s\" with connector id %u.\n",
569 pdata->drmname, g_drm_connector->connector_id);
570
571 return 0;
572
573 fail:
574 drm_free();
575 close(g_drm_fd);
576
577 return -1;
578 }
579
580 /* Counterpart to exynos_open. */
exynos_close(struct exynos_data * pdata)581 static void exynos_close(struct exynos_data *pdata)
582 {
583 memset(pdata->drmname, 0, sizeof(char) * 32);
584
585 drm_free();
586 close(g_drm_fd);
587 g_drm_fd = -1;
588 }
589
exynos_init(struct exynos_data * pdata,unsigned bpp)590 static int exynos_init(struct exynos_data *pdata, unsigned bpp)
591 {
592 unsigned i;
593 settings_t *settings = config_get_ptr();
594 unsigned video_fullscreen_x = settings->uints.video_fullscreen_x;
595 unsigned video_fullscreen_y = settings->uints.video_fullscreen_y;
596
597 if ( video_fullscreen_x != 0 &&
598 video_fullscreen_y != 0)
599 {
600 for (i = 0; i < g_drm_connector->count_modes; i++)
601 {
602 if (g_drm_connector->modes[i].hdisplay == video_fullscreen_x &&
603 g_drm_connector->modes[i].vdisplay == video_fullscreen_y)
604 {
605 g_drm_mode = &g_drm_connector->modes[i];
606 break;
607 }
608 }
609
610 if (!g_drm_mode)
611 {
612 RARCH_ERR(
613 "[video_exynos]: requested resolution (%ux%u) not available\n",
614 video_fullscreen_x,
615 video_fullscreen_y);
616 goto fail;
617 }
618
619 }
620 else
621 {
622 /* Select first mode, which is the native one. */
623 g_drm_mode = &g_drm_connector->modes[0];
624 }
625
626 if (g_drm_mode->hdisplay == 0 || g_drm_mode->vdisplay == 0)
627 {
628 RARCH_ERR("[video_exynos]: failed to select sane resolution\n");
629 goto fail;
630 }
631
632 drm_setup(g_drm_fd);
633
634 pdata->width = g_drm_mode->hdisplay;
635 pdata->height = g_drm_mode->vdisplay;
636
637 pdata->aspect = (float)g_drm_mode->hdisplay / (float)g_drm_mode->vdisplay;
638
639 /* Always use triple buffering to reduce chance of tearing. */
640 pdata->num_pages = 3;
641
642 pdata->bpp = bpp;
643 pdata->pitch = bpp * pdata->width;
644 pdata->size = pdata->pitch * pdata->height;
645
646 RARCH_LOG("[video_exynos]: selected %ux%u resolution with %u bpp\n",
647 pdata->width, pdata->height, pdata->bpp);
648
649 return 0;
650
651 fail:
652 drm_restore_crtc();
653
654 g_drm_mode = NULL;
655
656 return -1;
657 }
658
659 /* Counterpart to exynos_init. */
exynos_deinit(struct exynos_data * pdata)660 static void exynos_deinit(struct exynos_data *pdata)
661 {
662 drm_restore_crtc();
663
664 g_drm_mode = NULL;
665 pdata->width = 0;
666 pdata->height = 0;
667 pdata->num_pages = 0;
668 pdata->bpp = 0;
669 pdata->pitch = 0;
670 pdata->size = 0;
671 }
672
exynos_alloc(struct exynos_data * pdata)673 static int exynos_alloc(struct exynos_data *pdata)
674 {
675 struct exynos_bo *bo;
676 struct exynos_page *pages;
677 unsigned i;
678 uint32_t pixel_format;
679 const unsigned flags = 0;
680 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
681 struct exynos_device *device = exynos_device_create(g_drm_fd);
682
683 if (!device)
684 {
685 RARCH_ERR("[video_exynos]: failed to create device from fd\n");
686 return -1;
687 }
688
689 pages = (struct exynos_page*)calloc(pdata->num_pages,
690 sizeof(struct exynos_page));
691
692 if (!pages)
693 {
694 RARCH_ERR("[video_exynos]: failed to allocate pages\n");
695 goto fail_alloc;
696 }
697
698 for (i = 0; i < EXYNOS_BUFFER_COUNT; ++i)
699 {
700 const unsigned buffer_size = defaults[i].width * defaults[i].height * defaults[i].bpp;
701
702 bo = exynos_create_mapped_buffer(device, buffer_size);
703 if (!bo)
704 break;
705
706 pdata->buf[i] = bo;
707 }
708
709 if (i != EXYNOS_BUFFER_COUNT)
710 {
711 while (i-- > 0)
712 {
713 exynos_bo_destroy(pdata->buf[i]);
714 pdata->buf[i] = NULL;
715 }
716
717 goto fail;
718 }
719
720 for (i = 0; i < pdata->num_pages; ++i)
721 {
722 bo = exynos_bo_create(device, pdata->size, flags);
723 if (!bo)
724 {
725 RARCH_ERR("[video_exynos]: failed to create buffer object\n");
726 goto fail;
727 }
728
729 /* Don't map the BO, since we don't access it through userspace. */
730
731 pages[i].bo = bo;
732 pages[i].base = pdata;
733
734 pages[i].used = false;
735 pages[i].clear = true;
736 }
737
738 pixel_format = (pdata->bpp == 2) ? DRM_FORMAT_RGB565 : DRM_FORMAT_XRGB8888;
739 pitches[0] = pdata->pitch;
740 offsets[0] = 0;
741
742 for (i = 0; i < pdata->num_pages; ++i)
743 {
744 handles[0] = pages[i].bo->handle;
745
746 if (drmModeAddFB2(g_drm_fd, pdata->width, pdata->height,
747 pixel_format, handles, pitches, offsets,
748 &pages[i].buf_id, flags))
749 {
750 RARCH_ERR("[video_exynos]: failed to add bo %u to fb\n", i);
751 goto fail;
752 }
753 }
754
755 /* Setup CRTC: display the last allocated page. */
756 if (drmModeSetCrtc(g_drm_fd, g_crtc_id,
757 pages[pdata->num_pages - 1].buf_id,
758 0, 0, &g_drm_connector_id, 1, g_drm_mode))
759 {
760 RARCH_ERR("[video_exynos]: initial CRTC setup failed.\n");
761 goto fail;
762 }
763
764 pdata->pages = pages;
765 pdata->device = device;
766
767 return 0;
768
769 fail:
770 exynos_clean_up_pages(pages, pdata->num_pages);
771
772 fail_alloc:
773 exynos_device_destroy(device);
774
775 return -1;
776 }
777
778 /* Counterpart to exynos_alloc. */
exynos_free(struct exynos_data * pdata)779 static void exynos_free(struct exynos_data *pdata)
780 {
781 unsigned i;
782
783 /* Disable the CRTC. */
784 if (drmModeSetCrtc(g_drm_fd, g_crtc_id, 0,
785 0, 0, NULL, 0, NULL))
786 RARCH_WARN("[video_exynos]: failed to disable the CRTC.\n");
787
788 exynos_clean_up_pages(pdata->pages, pdata->num_pages);
789
790 free(pdata->pages);
791 pdata->pages = NULL;
792
793 for (i = 0; i < EXYNOS_BUFFER_COUNT; ++i)
794 {
795 exynos_bo_destroy(pdata->buf[i]);
796 pdata->buf[i] = NULL;
797 }
798
799 exynos_device_destroy(pdata->device);
800 pdata->device = NULL;
801 }
802
803 #if (EXYNOS_GFX_DEBUG_LOG == 1)
exynos_alloc_status(struct exynos_data * pdata)804 static void exynos_alloc_status(struct exynos_data *pdata)
805 {
806 unsigned i;
807 struct exynos_page *pages = pdata->pages;
808
809 RARCH_LOG("[video_exynos]: Allocated %u pages with %u bytes each (pitch = %u bytes)\n",
810 pdata->num_pages, pdata->size, pdata->pitch);
811
812 for (i = 0; i < pdata->num_pages; ++i)
813 {
814 RARCH_LOG("[video_exynos]: page %u: BO at %p, buffer id = %u\n",
815 i, pages[i].bo, pages[i].buf_id);
816 }
817 }
818 #endif
819
820 /* Find a free page, clear it if necessary, and return the page. If *
821 * no free page is available when called, wait for a page flip. */
exynos_free_page(struct exynos_data * pdata)822 static struct exynos_page *exynos_free_page(struct exynos_data *pdata)
823 {
824 struct exynos_page *page = NULL;
825 struct g2d_image *dst = pdata->dst;
826
827 /* Wait until a free page is available. */
828 while (!page)
829 {
830 page = exynos_get_free_page(pdata->pages, pdata->num_pages);
831
832 if (!page)
833 drm_wait_flip(-1);
834 }
835
836 dst->bo[0] = page->bo->handle;
837
838 if (page->clear)
839 {
840 if (exynos_clear_buffer(pdata->g2d, dst) == 0)
841 page->clear = false;
842 }
843
844 page->used = true;
845 return page;
846 }
847
exynos_setup_scale(struct exynos_data * pdata,unsigned width,unsigned height,unsigned src_bpp)848 static void exynos_setup_scale(struct exynos_data *pdata,
849 unsigned width, unsigned height, unsigned src_bpp)
850 {
851 unsigned i;
852 unsigned w, h;
853 struct g2d_image *src = pdata->src[EXYNOS_IMAGE_FRAME];
854 const float aspect = (float)width / (float)height;
855
856 src->width = width;
857 src->height = height;
858 src->color_mode = (src_bpp == 2) ?
859 G2D_COLOR_FMT_RGB565 | G2D_ORDER_AXRGB:
860 G2D_COLOR_FMT_XRGB8888 | G2D_ORDER_AXRGB;
861
862 if (fabsf(pdata->aspect - aspect) < 0.0001f)
863 {
864 w = pdata->width;
865 h = pdata->height;
866 }
867 else
868 {
869 if (pdata->aspect > aspect)
870 {
871 w = (float)pdata->width * aspect / pdata->aspect;
872 h = pdata->height;
873 }
874 else
875 {
876 w = pdata->width;
877 h = (float)pdata->height * pdata->aspect / aspect;
878 }
879 }
880
881 pdata->blit_params[0] = (pdata->width - w) / 2;
882 pdata->blit_params[1] = (pdata->height - h) / 2;
883 pdata->blit_params[2] = w;
884 pdata->blit_params[3] = h;
885 pdata->blit_params[4] = width;
886 pdata->blit_params[5] = height;
887
888 for (i = 0; i < pdata->num_pages; ++i)
889 pdata->pages[i].clear = true;
890 }
891
exynos_set_fake_blit(struct exynos_data * pdata)892 static void exynos_set_fake_blit(struct exynos_data *pdata)
893 {
894 unsigned i;
895
896 pdata->blit_params[0] = 0;
897 pdata->blit_params[1] = 0;
898 pdata->blit_params[2] = pdata->width;
899 pdata->blit_params[3] = pdata->height;
900
901 for (i = 0; i < pdata->num_pages; ++i)
902 pdata->pages[i].clear = true;
903 }
904
exynos_blit_frame(struct exynos_data * pdata,const void * frame,unsigned src_pitch)905 static int exynos_blit_frame(
906 struct exynos_data *pdata,
907 const void *frame,
908 unsigned src_pitch)
909 {
910 const enum exynos_buffer_type
911 buf_type = defaults[EXYNOS_IMAGE_FRAME].buf_type;
912 const unsigned size = src_pitch * pdata->blit_params[5];
913 struct g2d_image *src = pdata->src[EXYNOS_IMAGE_FRAME];
914
915 if (exynos_realloc_buffer(pdata, buf_type, size) != 0)
916 return -1;
917
918 #if (EXYNOS_GFX_DEBUG_PERF == 1)
919 exynos_perf_memcpy(&pdata->perf, true);
920 #endif
921
922 /* HACK: Without IOMMU the G2D only works properly between GEM buffers. */
923 memcpy_neon(pdata->buf[buf_type]->vaddr, frame, size);
924 src->stride = src_pitch;
925
926 #if (EXYNOS_GFX_DEBUG_PERF == 1)
927 exynos_perf_memcpy(&pdata->perf, false);
928 #endif
929
930 #if (EXYNOS_GFX_DEBUG_PERF == 1)
931 exynos_perf_g2d(&pdata->perf, true);
932 #endif
933
934 if (g2d_copy_with_scale(pdata->g2d, src, pdata->dst, 0, 0,
935 pdata->blit_params[4], pdata->blit_params[5],
936 pdata->blit_params[0], pdata->blit_params[1],
937 pdata->blit_params[2], pdata->blit_params[3], 0) ||
938 g2d_exec(pdata->g2d))
939 {
940 RARCH_ERR("[video_exynos]: failed to blit frame.\n");
941 return -1;
942 }
943
944 #if (EXYNOS_GFX_DEBUG_PERF == 1)
945 exynos_perf_g2d(&pdata->perf, false);
946 #endif
947
948 return 0;
949 }
950
exynos_blend_menu(struct exynos_data * pdata,unsigned rotation)951 static int exynos_blend_menu(struct exynos_data *pdata,
952 unsigned rotation)
953 {
954 struct g2d_image *src = pdata->src[EXYNOS_IMAGE_MENU];
955
956 #if (EXYNOS_GFX_DEBUG_PERF == 1)
957 exynos_perf_g2d(&pdata->perf, true);
958 #endif
959
960 if (g2d_scale_and_blend(pdata->g2d, src, pdata->dst, 0, 0,
961 src->width, src->height, pdata->blit_params[0],
962 pdata->blit_params[1], pdata->blit_params[2],
963 pdata->blit_params[3], G2D_OP_INTERPOLATE) ||
964 g2d_exec(pdata->g2d))
965 {
966 RARCH_ERR("[video_exynos]: failed to blend menu.\n");
967 return -1;
968 }
969
970 #if (EXYNOS_GFX_DEBUG_PERF == 1)
971 exynos_perf_g2d(&pdata->perf, false);
972 #endif
973
974 return 0;
975 }
976
exynos_blend_font(struct exynos_data * pdata)977 static int exynos_blend_font(struct exynos_data *pdata)
978 {
979 struct g2d_image *src = pdata->src[EXYNOS_IMAGE_FONT];
980
981 #if (EXYNOS_GFX_DEBUG_PERF == 1)
982 exynos_perf_g2d(&pdata->perf, true);
983 #endif
984
985 if (g2d_scale_and_blend(pdata->g2d, src, pdata->dst, 0, 0, src->width,
986 src->height, 0, 0, pdata->width, pdata->height,
987 G2D_OP_INTERPOLATE) ||
988 g2d_exec(pdata->g2d))
989 {
990 RARCH_ERR("[video_exynos]: failed to blend font\n");
991 return -1;
992 }
993
994 #if (EXYNOS_GFX_DEBUG_PERF == 1)
995 exynos_perf_g2d(&pdata->perf, false);
996 #endif
997
998 return 0;
999 }
1000
exynos_flip(struct exynos_data * pdata,struct exynos_page * page)1001 static int exynos_flip(struct exynos_data *pdata, struct exynos_page *page)
1002 {
1003 /* We don't queue multiple page flips. */
1004 if (pdata->pageflip_pending > 0)
1005 drm_wait_flip(-1);
1006
1007 /* Issue a page flip at the next vblank interval. */
1008 if (drmModePageFlip(g_drm_fd, g_crtc_id, page->buf_id,
1009 DRM_MODE_PAGE_FLIP_EVENT, page) != 0)
1010 {
1011 RARCH_ERR("[video_exynos]: failed to issue page flip\n");
1012 return -1;
1013 }
1014 else
1015 {
1016 pdata->pageflip_pending++;
1017 }
1018
1019 /* On startup no frame is displayed. We therefore wait for the initial flip to finish. */
1020 if (!pdata->cur_page)
1021 drm_wait_flip(-1);
1022
1023 return 0;
1024 }
1025
1026 struct exynos_video
1027 {
1028 struct exynos_data *data;
1029
1030 void *font;
1031 const font_renderer_driver_t *font_driver;
1032 uint16_t font_color; /* ARGB4444 */
1033
1034 unsigned bytes_per_pixel;
1035
1036 /* current dimensions of the core fb */
1037 unsigned width;
1038 unsigned height;
1039
1040 /* menu data */
1041 unsigned menu_rotation;
1042 bool menu_active;
1043
1044 bool aspect_changed;
1045 };
1046
exynos_init_font(struct exynos_video * vid)1047 static int exynos_init_font(struct exynos_video *vid)
1048 {
1049 struct exynos_data *pdata = vid->data;
1050 struct g2d_image *src = pdata->src[EXYNOS_IMAGE_FONT];
1051 const unsigned buf_height = defaults[EXYNOS_IMAGE_FONT].height;
1052 const unsigned buf_width = align_common(pdata->aspect * (float)buf_height, 16);
1053 const unsigned buf_bpp = defaults[EXYNOS_IMAGE_FONT].bpp;
1054 settings_t *settings = config_get_ptr();
1055 bool video_font_enable = settings->bools.video_font_enable;
1056 const char *font_path = settings->video.font_path;
1057 float video_font_size = settings->floats.video_font_size;
1058 float video_msg_color_r = settings->floats.video_msg_color_r;
1059 float video_msg_color_g = settings->floats.video_msg_color_g;
1060 float video_msg_color_b = settings->floats.video_msg_color_b;
1061
1062 if (!video_font_enable)
1063 return 0;
1064
1065 if (font_renderer_create_default(&vid->font_driver, &vid->font,
1066 *font_path ? font_path : NULL,
1067 video_font_size))
1068 {
1069 const int r = video_msg_color_r * 15;
1070 const int g = video_msg_color_g * 15;
1071 const int b = video_msg_color_b * 15;
1072
1073 vid->font_color = ((b < 0 ? 0 : (b > 15 ? 15 : b)) << 0) |
1074 ((g < 0 ? 0 : (g > 15 ? 15 : g)) << 4) |
1075 ((r < 0 ? 0 : (r > 15 ? 15 : r)) << 8);
1076 }
1077 else
1078 {
1079 RARCH_ERR("[video_exynos]: creating font renderer failed\n");
1080 return -1;
1081 }
1082
1083 /* The font buffer color type is ARGB4444. */
1084 if (exynos_realloc_buffer(pdata, defaults[EXYNOS_IMAGE_FONT].buf_type,
1085 buf_width * buf_height * buf_bpp) != 0)
1086 {
1087 vid->font_driver->free(vid->font);
1088 return -1;
1089 }
1090
1091 src->width = buf_width;
1092 src->height = buf_height;
1093 src->stride = buf_width * buf_bpp;
1094
1095 return 0;
1096 }
1097
exynos_render_msg(struct exynos_video * vid,const char * msg)1098 static int exynos_render_msg(struct exynos_video *vid,
1099 const char *msg)
1100 {
1101 const struct font_atlas *atlas;
1102 struct exynos_data *pdata = vid->data;
1103 struct g2d_image *dst = pdata->src[EXYNOS_IMAGE_FONT];
1104 settings_t *settings = config_get_ptr();
1105 int msg_base_x = settings->floats.video_msg_pos_x * dst->width;
1106 int msg_base_y = (1.0f - settings->floats.video_msg_pos_y) * dst->height;
1107
1108 if (!vid->font || !vid->font_driver)
1109 return -1;
1110
1111 if (exynos_clear_buffer(pdata->g2d, dst) != 0)
1112 return -1;
1113
1114 atlas = vid->font_driver->get_atlas(vid->font);
1115
1116 for (; *msg; ++msg)
1117 {
1118 int base_x, base_y;
1119 int glyph_width, glyph_height;
1120 const uint8_t *src = NULL;
1121 const struct font_glyph *glyph = vid->font_driver->get_glyph(vid->font, (uint8_t)*msg);
1122 if (!glyph)
1123 continue;
1124
1125 base_x = msg_base_x + glyph->draw_offset_x;
1126 base_y = msg_base_y + glyph->draw_offset_y;
1127
1128 const int max_width = dst->width - base_x;
1129 const int max_height = dst->height - base_y;
1130
1131 glyph_width = glyph->width;
1132 glyph_height = glyph->height;
1133
1134 src = atlas->buffer + glyph->atlas_offset_x + glyph->atlas_offset_y * atlas->width;
1135
1136 if (base_x < 0)
1137 {
1138 src -= base_x;
1139 glyph_width += base_x;
1140 base_x = 0;
1141 }
1142
1143 if (base_y < 0)
1144 {
1145 src -= base_y * (int)atlas->width;
1146 glyph_height += base_y;
1147 base_y = 0;
1148 }
1149
1150 if (max_width <= 0 || max_height <= 0)
1151 continue;
1152
1153 if (glyph_width > max_width)
1154 glyph_width = max_width;
1155 if (glyph_height > max_height)
1156 glyph_height = max_height;
1157
1158 exynos_put_glyph_rgba4444(pdata, src, vid->font_color,
1159 glyph_width, glyph_height,
1160 atlas->width, base_x, base_y);
1161
1162 msg_base_x += glyph->advance_x;
1163 msg_base_y += glyph->advance_y;
1164 }
1165
1166 return exynos_blend_font(pdata);
1167 }
1168
exynos_gfx_init(const video_info_t * video,input_driver_t ** input,void ** input_data)1169 static void *exynos_gfx_init(const video_info_t *video,
1170 input_driver_t **input, void **input_data)
1171 {
1172 struct exynos_video *vid;
1173 const unsigned fb_bpp = 4; /* Use XRGB8888 framebuffer. */
1174
1175 vid = calloc(1, sizeof(struct exynos_video));
1176 if (!vid)
1177 return NULL;
1178
1179 vid->data = calloc(1, sizeof(struct exynos_data));
1180 if (!vid->data)
1181 goto fail_data;
1182
1183 vid->bytes_per_pixel = video->rgb32 ? 4 : 2;
1184
1185 if (exynos_open(vid->data) != 0)
1186 {
1187 RARCH_ERR("[video_exynos]: opening device failed\n");
1188 goto fail;
1189 }
1190
1191 if (exynos_init(vid->data, fb_bpp) != 0)
1192 {
1193 RARCH_ERR("[video_exynos]: initialization failed\n");
1194 goto fail_init;
1195 }
1196
1197 if (exynos_alloc(vid->data) != 0)
1198 {
1199 RARCH_ERR("[video_exynos]: allocation failed\n");
1200 goto fail_alloc;
1201 }
1202
1203 if (exynos_g2d_init(vid->data) != 0)
1204 {
1205 RARCH_ERR("[video_exynos]: G2D initialization failed\n");
1206 goto fail_g2d;
1207 }
1208
1209 #if (EXYNOS_GFX_DEBUG_LOG == 1)
1210 exynos_alloc_status(vid->data);
1211 #endif
1212
1213 #if (EXYNOS_GFX_DEBUG_PERF == 1)
1214 exynos_perf_init(&vid->data->perf);
1215 #endif
1216
1217 if (input && input_data)
1218 *input = NULL;
1219
1220 if (exynos_init_font(vid) != 0)
1221 {
1222 RARCH_ERR("[video_exynos]: font initialization failed\n");
1223 goto fail_font;
1224 }
1225
1226 return vid;
1227
1228 fail_font:
1229 exynos_g2d_free(vid->data);
1230
1231 fail_g2d:
1232 exynos_free(vid->data);
1233
1234 fail_alloc:
1235 exynos_deinit(vid->data);
1236
1237 fail_init:
1238 exynos_close(vid->data);
1239
1240 fail:
1241 free(vid->data);
1242
1243 fail_data:
1244 free(vid);
1245 return NULL;
1246 }
1247
exynos_gfx_free(void * data)1248 static void exynos_gfx_free(void *data)
1249 {
1250 struct exynos_video *vid = data;
1251 struct exynos_data *pdata;
1252
1253 if (!vid)
1254 return;
1255
1256 pdata = vid->data;
1257
1258 exynos_g2d_free(pdata);
1259
1260 /* Flush pages: One page remains, the one being displayed at this moment. */
1261 while (exynos_pages_used(pdata->pages, pdata->num_pages) > 1)
1262 drm_wait_flip(-1);
1263
1264 exynos_free(pdata);
1265 exynos_deinit(pdata);
1266 exynos_close(pdata);
1267
1268 #if (EXYNOS_GFX_DEBUG_PERF == 1)
1269 exynos_perf_finish(&pdata->perf);
1270 #endif
1271
1272 free(pdata);
1273
1274 if (vid->font && vid->font_driver)
1275 vid->font_driver->free(vid->font);
1276
1277 free(vid);
1278 }
1279
exynos_gfx_frame(void * data,const void * frame,unsigned width,unsigned height,uint64_t frame_count,unsigned pitch,const char * msg,video_frame_info_t * video_info)1280 static bool exynos_gfx_frame(void *data, const void *frame, unsigned width,
1281 unsigned height, uint64_t frame_count, unsigned pitch, const char *msg,
1282 video_frame_info_t *video_info)
1283 {
1284 struct exynos_video *vid = data;
1285 struct exynos_page *page = NULL;
1286 #ifdef HAVE_MENU
1287 bool menu_is_alive = video_info->menu_is_alive;
1288
1289 /* Check if neither menu nor core framebuffer is to be displayed. */
1290 if (!vid->menu_active && !frame)
1291 return true;
1292 #endif
1293
1294 if (frame)
1295 {
1296 if (width != vid->width || height != vid->height)
1297 {
1298 /* Sanity check on new dimension parameters. */
1299 if (width == 0 || height == 0)
1300 return true;
1301
1302 RARCH_LOG("[video_exynos]: resolution changed by core: %ux%u -> %ux%u\n",
1303 vid->width, vid->height, width, height);
1304 exynos_setup_scale(vid->data, width, height, vid->bytes_per_pixel);
1305
1306 vid->width = width;
1307 vid->height = height;
1308 }
1309
1310 page = exynos_free_page(vid->data);
1311
1312 if (exynos_blit_frame(vid->data, frame, pitch) != 0)
1313 goto fail;
1314 }
1315
1316 /* If at this point the dimension parameters are still zero, setup some *
1317 * fake blit parameters so that menu and font rendering work properly. */
1318 if (vid->width == 0 || vid->height == 0)
1319 exynos_set_fake_blit(vid->data);
1320
1321 if (!page)
1322 page = exynos_free_page(vid->data);
1323
1324 #ifdef HAVE_MENU
1325 if (vid->menu_active)
1326 {
1327 if (exynos_blend_menu(vid->data, vid->menu_rotation) != 0)
1328 goto fail;
1329 menu_driver_frame(menu_is_alive, video_info);
1330 }
1331 else
1332 #endif
1333 if (video_info->statistics_show)
1334 {
1335 struct font_params *osd_params = video_info ?
1336 (struct font_params*)&video_info->osd_stat_params : NULL;
1337
1338 if (osd_params)
1339 font_driver_render_msg(vid, video_info->stat_text,
1340 (const struct font_params*)&video_info->osd_stat_params, NULL);
1341 }
1342
1343 if (msg)
1344 {
1345 if (exynos_render_msg(vid, msg) != 0)
1346 goto fail;
1347
1348 /* Font is blitted to the entire screen, so issue clear afterwards. */
1349 page->clear = true;
1350 }
1351
1352 if (exynos_flip(vid->data, page) != 0)
1353 goto fail;
1354
1355 return true;
1356
1357 fail:
1358 /* Since we didn't manage to issue a pageflip to this page, set *
1359 * it to 'unused' again, and hope that it works next time. */
1360 page->used = false;
1361
1362 return false;
1363 }
1364
exynos_gfx_set_nonblock_state(void * data,bool state,bool adaptive_vsync_enabled,unsigned swap_interval)1365 static void exynos_gfx_set_nonblock_state(void *data, bool state,
1366 bool adaptive_vsync_enabled, unsigned swap_interval)
1367 {
1368 struct exynos_video *vid = data;
1369 if (vid && vid->data)
1370 vid->data->sync = !state;
1371 }
1372
exynos_gfx_alive(void * data)1373 static bool exynos_gfx_alive(void *data)
1374 {
1375 (void)data;
1376 return true; /* always alive */
1377 }
1378
exynos_gfx_focus(void * data)1379 static bool exynos_gfx_focus(void *data)
1380 {
1381 (void)data;
1382 return true; /* drm device always has focus */
1383 }
1384
exynos_gfx_suppress_screensaver(void * data,bool enable)1385 static bool exynos_gfx_suppress_screensaver(void *data, bool enable)
1386 {
1387 (void)data;
1388 (void)enable;
1389
1390 return false;
1391 }
1392
exynos_gfx_set_rotation(void * data,unsigned rotation)1393 static void exynos_gfx_set_rotation(void *data, unsigned rotation)
1394 {
1395 struct exynos_video *vid = (struct exynos_video*)data;
1396 if (vid)
1397 vid->menu_rotation = rotation;
1398 }
1399
exynos_gfx_viewport_info(void * data,struct video_viewport * vp)1400 static void exynos_gfx_viewport_info(void *data, struct video_viewport *vp)
1401 {
1402 struct exynos_video *vid = (struct exynos_video*)data;
1403
1404 if (!vid)
1405 return;
1406
1407 vp->x = vp->y = 0;
1408
1409 vp->width = vp->full_width = vid->width;
1410 vp->height = vp->full_height = vid->height;
1411 }
1412
exynos_set_aspect_ratio(void * data,unsigned aspect_ratio_idx)1413 static void exynos_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
1414 {
1415 struct exynos_video *vid = (struct exynos_video*)data;
1416
1417 if (!vid)
1418 return;
1419
1420 vid->aspect_changed = true;
1421 }
1422
exynos_apply_state_changes(void * data)1423 static void exynos_apply_state_changes(void *data)
1424 {
1425 (void)data;
1426 }
1427
exynos_set_texture_frame(void * data,const void * frame,bool rgb32,unsigned width,unsigned height,float alpha)1428 static void exynos_set_texture_frame(void *data, const void *frame, bool rgb32,
1429 unsigned width, unsigned height, float alpha)
1430 {
1431 const enum exynos_buffer_type buf_type = defaults[EXYNOS_IMAGE_MENU].buf_type;
1432 struct exynos_video *vid = data;
1433 struct exynos_data *pdata = vid->data;
1434 struct g2d_image *src = pdata->src[EXYNOS_IMAGE_MENU];
1435 const unsigned size = width * height * (rgb32 ? 4 : 2);
1436
1437 if (exynos_realloc_buffer(pdata, buf_type, size) != 0)
1438 return;
1439
1440 src->width = width;
1441 src->height = height;
1442 src->stride = width * (rgb32 ? 4 : 2);
1443 src->color_mode = rgb32 ? G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_RGBAX :
1444 G2D_COLOR_FMT_ARGB4444 | G2D_ORDER_RGBAX;
1445
1446 src->component_alpha = (unsigned char)(255.0f * alpha);
1447
1448 #if (EXYNOS_GFX_DEBUG_PERF == 1)
1449 exynos_perf_memcpy(&pdata->perf, true);
1450 #endif
1451
1452 memcpy_neon(pdata->buf[buf_type]->vaddr, frame, size);
1453
1454 #if (EXYNOS_GFX_DEBUG_PERF == 1)
1455 exynos_perf_memcpy(&pdata->perf, false);
1456 #endif
1457 }
1458
exynos_set_texture_enable(void * data,bool state,bool full_screen)1459 static void exynos_set_texture_enable(void *data, bool state, bool full_screen)
1460 {
1461 struct exynos_video *vid = data;
1462 if (vid)
1463 vid->menu_active = state;
1464 }
1465
exynos_set_osd_msg(void * data,const char * msg,const struct font_params * params)1466 static void exynos_set_osd_msg(void *data, const char *msg,
1467 const struct font_params *params)
1468 {
1469 (void)data;
1470 (void)msg;
1471 (void)params;
1472 }
1473
exynos_show_mouse(void * data,bool state)1474 static void exynos_show_mouse(void *data, bool state)
1475 {
1476 (void)data;
1477 (void)state;
1478 }
1479
1480 static const video_poke_interface_t exynos_poke_interface = {
1481 NULL, /* get_flags */
1482 NULL,
1483 NULL,
1484 NULL, /* set_video_mode */
1485 drm_get_refresh_rate,
1486 NULL, /* set_filtering */
1487 NULL, /* get_video_output_size */
1488 NULL, /* get_video_output_prev */
1489 NULL, /* get_video_output_next */
1490 NULL, /* get_current_framebuffer */
1491 NULL, /* get_proc_address */
1492 exynos_set_aspect_ratio,
1493 exynos_apply_state_changes,
1494 exynos_set_texture_frame,
1495 exynos_set_texture_enable,
1496 exynos_set_osd_msg,
1497 exynos_show_mouse,
1498 NULL, /* grab_mouse_toggle */
1499 NULL, /* get_current_shader */
1500 NULL, /* get_current_software_framebuffer */
1501 NULL /* get_hw_render_interface */
1502 };
1503
exynos_gfx_get_poke_interface(void * data,const video_poke_interface_t ** iface)1504 static void exynos_gfx_get_poke_interface(void *data,
1505 const video_poke_interface_t **iface)
1506 {
1507 (void)data;
1508 *iface = &exynos_poke_interface;
1509 }
1510
exynos_gfx_set_shader(void * data,enum rarch_shader_type type,const char * path)1511 static bool exynos_gfx_set_shader(void *data,
1512 enum rarch_shader_type type, const char *path)
1513 {
1514 (void)data;
1515 (void)type;
1516 (void)path;
1517
1518 return false;
1519 }
1520
1521 video_driver_t video_exynos = {
1522 exynos_gfx_init,
1523 exynos_gfx_frame,
1524 exynos_gfx_set_nonblock_state,
1525 exynos_gfx_alive,
1526 exynos_gfx_focus,
1527 exynos_gfx_suppress_screensaver,
1528 NULL, /* has_windowed */
1529 exynos_gfx_set_shader,
1530 exynos_gfx_free,
1531 "exynos",
1532 NULL, /* set_viewport */
1533 exynos_gfx_set_rotation,
1534 exynos_gfx_viewport_info,
1535 NULL, /* read_viewport */
1536 NULL, /* read_frame_raw */
1537
1538 #ifdef HAVE_OVERLAY
1539 NULL, /* overlay_interface */
1540 #endif
1541 #ifdef HAVE_VIDEO_LAYOUT
1542 NULL,
1543 #endif
1544 exynos_gfx_get_poke_interface
1545 };
1546