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