wlt: fix shl_hook API changes
[platform/upstream/kmscon.git] / src / uterm_drm3d_video.c
1 /*
2  * uterm - Linux User-Space Terminal
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * DRM Video backend
28  */
29
30 #define EGL_EGLEXT_PROTOTYPES
31 #define GL_GLEXT_PROTOTYPES
32
33 #include <EGL/egl.h>
34 #include <EGL/eglext.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <gbm.h>
38 #include <GLES2/gl2.h>
39 #include <GLES2/gl2ext.h>
40 #include <inttypes.h>
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <xf86drm.h>
46 #include <xf86drmMode.h>
47 #include "eloop.h"
48 #include "log.h"
49 #include "static_gl.h"
50 #include "uterm_drm_shared_internal.h"
51 #include "uterm_drm3d_internal.h"
52 #include "uterm_video.h"
53 #include "uterm_video_internal.h"
54
55 #define LOG_SUBSYSTEM "uterm_drm3d_video"
56
57 static int display_init(struct uterm_display *disp)
58 {
59         struct uterm_drm3d_display *d3d;
60         int ret;
61
62         d3d = malloc(sizeof(*d3d));
63         if (!d3d)
64                 return -ENOMEM;
65         memset(d3d, 0, sizeof(*d3d));
66
67         ret = uterm_drm_display_init(disp, d3d);
68         if (ret) {
69                 free(d3d);
70                 return ret;
71         }
72
73         return 0;
74 }
75
76 static void display_destroy(struct uterm_display *disp)
77 {
78         free(uterm_drm_display_get_data(disp));
79         uterm_drm_display_destroy(disp);
80 }
81
82 static void bo_destroy_event(struct gbm_bo *bo, void *data)
83 {
84         struct uterm_drm3d_rb *rb = data;
85         struct uterm_drm_video *vdrm;
86
87         if (!rb)
88                 return;
89
90         vdrm = rb->disp->video->data;
91         drmModeRmFB(vdrm->fd, rb->fb);
92         free(rb);
93 }
94
95 static struct uterm_drm3d_rb *bo_to_rb(struct uterm_display *disp,
96                                        struct gbm_bo *bo)
97 {
98         struct uterm_drm3d_rb *rb = gbm_bo_get_user_data(bo);
99         struct uterm_video *video = disp->video;
100         struct uterm_drm_video *vdrm = video->data;
101         int ret;
102         unsigned int stride, handle, width, height;;
103
104         if (rb)
105                 return rb;
106
107         rb = malloc(sizeof(*rb));
108         if (!rb) {
109                 log_error("cannot allocate memory for render buffer (%d): %m",
110                           errno);
111                 return NULL;
112         }
113         rb->disp = disp;
114         rb->bo = bo;
115
116 #ifdef BUILD_HAVE_GBM_BO_GET_PITCH
117         stride = gbm_bo_get_pitch(rb->bo);
118 #else
119         stride = gbm_bo_get_stride(rb->bo);
120 #endif
121         handle = gbm_bo_get_handle(rb->bo).u32;
122         width = gbm_bo_get_width(rb->bo);
123         height = gbm_bo_get_height(rb->bo);
124
125         ret = drmModeAddFB(vdrm->fd, width, height, 24, 32, stride,
126                            handle, &rb->fb);
127         if (ret) {
128                 log_err("cannot add drm-fb (%d): %m", errno);
129                 free(rb);
130                 return NULL;
131         }
132
133         gbm_bo_set_user_data(bo, rb, bo_destroy_event);
134         return rb;
135 }
136
137 static int display_activate(struct uterm_display *disp,
138                             struct uterm_mode *mode)
139 {
140         struct uterm_video *video = disp->video;
141         struct uterm_drm_video *vdrm;
142         struct uterm_drm3d_video *v3d;
143         struct uterm_drm_display *ddrm = disp->data;
144         struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
145         int ret;
146         struct gbm_bo *bo;
147         drmModeModeInfo *minfo;
148
149         if (!mode)
150                 return -EINVAL;
151
152         vdrm = video->data;
153         v3d = uterm_drm_video_get_data(video);
154         minfo = uterm_drm_mode_get_info(mode);
155         log_info("activating display %p to %ux%u", disp,
156                  minfo->hdisplay, minfo->vdisplay);
157
158         ret = uterm_drm_display_activate(disp, vdrm->fd);
159         if (ret)
160                 return ret;
161
162         d3d->current = NULL;
163         d3d->next = NULL;
164         disp->current_mode = mode;
165
166         d3d->gbm = gbm_surface_create(v3d->gbm, minfo->hdisplay,
167                                       minfo->vdisplay, GBM_FORMAT_XRGB8888,
168                                       GBM_BO_USE_SCANOUT |
169                                       GBM_BO_USE_RENDERING);
170         if (!d3d->gbm) {
171                 log_error("cannot create gbm surface (%d): %m", errno);
172                 ret = -EFAULT;
173                 goto err_saved;
174         }
175
176         d3d->surface = eglCreateWindowSurface(v3d->disp, v3d->conf,
177                                               (EGLNativeWindowType)d3d->gbm,
178                                               NULL);
179         if (d3d->surface == EGL_NO_SURFACE) {
180                 log_error("cannot create EGL window surface");
181                 ret = -EFAULT;
182                 goto err_gbm;
183         }
184
185         if (!eglMakeCurrent(v3d->disp, d3d->surface, d3d->surface,
186                             v3d->ctx)) {
187                 log_error("cannot activate EGL context");
188                 ret = -EFAULT;
189                 goto err_surface;
190         }
191
192         glClearColor(0, 0, 0, 0);
193         glClear(GL_COLOR_BUFFER_BIT);
194         if (!eglSwapBuffers(v3d->disp, d3d->surface)) {
195                 log_error("cannot swap buffers");
196                 ret = -EFAULT;
197                 goto err_noctx;
198         }
199
200         bo = gbm_surface_lock_front_buffer(d3d->gbm);
201         if (!bo) {
202                 log_error("cannot lock front buffer during creation");
203                 ret = -EFAULT;
204                 goto err_noctx;
205         }
206
207         d3d->current = bo_to_rb(disp, bo);
208         if (!d3d->current) {
209                 log_error("cannot lock front buffer");
210                 ret = -EFAULT;
211                 goto err_bo;
212         }
213
214         ret = drmModeSetCrtc(vdrm->fd, ddrm->crtc_id, d3d->current->fb,
215                              0, 0, &ddrm->conn_id, 1, minfo);
216         if (ret) {
217                 log_err("cannot set drm-crtc");
218                 ret = -EFAULT;
219                 goto err_bo;
220         }
221
222         disp->flags |= DISPLAY_ONLINE;
223         return 0;
224
225 err_bo:
226         gbm_surface_release_buffer(d3d->gbm, bo);
227 err_noctx:
228         eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
229                        v3d->ctx);
230 err_surface:
231         eglDestroySurface(v3d->disp, d3d->surface);
232 err_gbm:
233         gbm_surface_destroy(d3d->gbm);
234 err_saved:
235         disp->current_mode = NULL;
236         uterm_drm_display_deactivate(disp, vdrm->fd);
237         return ret;
238 }
239
240 static void display_deactivate(struct uterm_display *disp)
241 {
242         struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
243         struct uterm_video *video = disp->video;
244         struct uterm_drm_video *vdrm;
245         struct uterm_drm3d_video *v3d;
246
247         log_info("deactivating display %p", disp);
248
249         vdrm = video->data;
250         v3d = uterm_drm_video_get_data(video);
251         uterm_drm_display_deactivate(disp, vdrm->fd);
252
253         eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
254                        v3d->ctx);
255         eglDestroySurface(v3d->disp, d3d->surface);
256
257         if (d3d->current) {
258                 gbm_surface_release_buffer(d3d->gbm,
259                                            d3d->current->bo);
260                 d3d->current = NULL;
261         }
262         if (d3d->next) {
263                 gbm_surface_release_buffer(d3d->gbm,
264                                            d3d->next->bo);
265                 d3d->next = NULL;
266         }
267
268         gbm_surface_destroy(d3d->gbm);
269         disp->current_mode = NULL;
270 }
271
272 int uterm_drm3d_display_use(struct uterm_display *disp, bool *opengl)
273 {
274         struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
275         struct uterm_drm3d_video *v3d;
276
277         v3d = uterm_drm_video_get_data(disp->video);
278         if (!eglMakeCurrent(v3d->disp, d3d->surface,
279                             d3d->surface, v3d->ctx)) {
280                 log_error("cannot activate EGL context");
281                 return -EFAULT;
282         }
283
284         if (opengl)
285                 *opengl = true;
286
287         /* TODO: lets find a way how to retrieve the current front buffer */
288         return 0;
289 }
290
291 static int display_swap(struct uterm_display *disp, bool immediate)
292 {
293         int ret;
294         struct gbm_bo *bo;
295         struct uterm_drm3d_rb *rb;
296         struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
297         struct uterm_video *video = disp->video;
298         struct uterm_drm3d_video *v3d = uterm_drm_video_get_data(video);
299
300         if (!gbm_surface_has_free_buffers(d3d->gbm))
301                 return -EBUSY;
302
303         if (!eglSwapBuffers(v3d->disp, d3d->surface)) {
304                 log_error("cannot swap EGL buffers (%d): %m", errno);
305                 return -EFAULT;
306         }
307
308         bo = gbm_surface_lock_front_buffer(d3d->gbm);
309         if (!bo) {
310                 log_error("cannot lock front buffer");
311                 return -EFAULT;
312         }
313
314         rb = bo_to_rb(disp, bo);
315         if (!rb) {
316                 log_error("cannot lock front gbm buffer (%d): %m", errno);
317                 gbm_surface_release_buffer(d3d->gbm, bo);
318                 return -EFAULT;
319         }
320
321         ret = uterm_drm_display_swap(disp, rb->fb, immediate);
322         if (ret) {
323                 gbm_surface_release_buffer(d3d->gbm, bo);
324                 return ret;
325         }
326
327         if (d3d->next) {
328                 gbm_surface_release_buffer(d3d->gbm, d3d->next->bo);
329                 d3d->next = NULL;
330         }
331
332         if (immediate) {
333                 if (d3d->current)
334                         gbm_surface_release_buffer(d3d->gbm, d3d->current->bo);
335                 d3d->current = rb;
336         } else {
337                 d3d->next = rb;
338         }
339
340         return 0;
341 }
342
343 static const struct display_ops drm_display_ops = {
344         .init = display_init,
345         .destroy = display_destroy,
346         .activate = display_activate,
347         .deactivate = display_deactivate,
348         .set_dpms = uterm_drm_display_set_dpms,
349         .use = uterm_drm3d_display_use,
350         .get_buffers = NULL,
351         .swap = display_swap,
352         .blit = uterm_drm3d_display_blit,
353         .fake_blendv = uterm_drm3d_display_fake_blendv,
354         .fill = uterm_drm3d_display_fill,
355 };
356
357 static void show_displays(struct uterm_video *video)
358 {
359         int ret;
360         struct uterm_display *iter;
361         struct shl_dlist *i;
362
363         if (!video_is_awake(video))
364                 return;
365
366         shl_dlist_for_each(i, &video->displays) {
367                 iter = shl_dlist_entry(i, struct uterm_display, list);
368
369                 if (!display_is_online(iter))
370                         continue;
371                 if (iter->dpms != UTERM_DPMS_ON)
372                         continue;
373
374                 ret = uterm_drm3d_display_use(iter, NULL);
375                 if (ret)
376                         continue;
377
378                 glClearColor(0, 0, 0, 1);
379                 glClear(GL_COLOR_BUFFER_BIT);
380                 display_swap(iter, true);
381         }
382 }
383
384 static void page_flip_handler(struct uterm_display *disp)
385 {
386         struct uterm_drm3d_display *d3d = uterm_drm_display_get_data(disp);
387
388         if (d3d->next) {
389                 if (d3d->current)
390                         gbm_surface_release_buffer(d3d->gbm,
391                                                    d3d->current->bo);
392                 d3d->current = d3d->next;
393                 d3d->next = NULL;
394         }
395 }
396
397 static int video_init(struct uterm_video *video, const char *node)
398 {
399         static const EGLint conf_att[] = {
400                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
401                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
402                 EGL_RED_SIZE, 1,
403                 EGL_GREEN_SIZE, 1,
404                 EGL_BLUE_SIZE, 1,
405                 EGL_ALPHA_SIZE, 0,
406                 EGL_NONE,
407         };
408         static const EGLint ctx_att[] = {
409                 EGL_CONTEXT_CLIENT_VERSION, 2,
410                 EGL_NONE
411         };
412         const char *ext;
413         int ret;
414         EGLint major, minor, n;
415         EGLenum api;
416         EGLBoolean b;
417         struct uterm_drm_video *vdrm;
418         struct uterm_drm3d_video *v3d;
419
420         v3d = malloc(sizeof(*v3d));
421         if (!v3d)
422                 return -ENOMEM;
423         memset(v3d, 0, sizeof(*v3d));
424
425         ret = uterm_drm_video_init(video, node, page_flip_handler, v3d);
426         if (ret)
427                 goto err_free;
428         vdrm = video->data;
429
430         log_debug("initialize 3D layer on %p", video);
431
432         v3d->gbm = gbm_create_device(vdrm->fd);
433         if (!v3d->gbm) {
434                 log_err("cannot create gbm device for %s (permission denied)",
435                         node);
436                 ret = -EFAULT;
437                 goto err_video;
438         }
439
440         v3d->disp = eglGetDisplay((EGLNativeDisplayType) v3d->gbm);
441         if (v3d->disp == EGL_NO_DISPLAY) {
442                 log_err("cannot retrieve egl display for %s", node);
443                 ret = -EFAULT;
444                 goto err_gbm;
445         }
446
447         b = eglInitialize(v3d->disp, &major, &minor);
448         if (!b) {
449                 log_err("cannot init egl display for %s", node);
450                 ret = -EFAULT;
451                 goto err_gbm;
452         }
453
454         log_debug("EGL Init %d.%d", major, minor);
455         log_debug("EGL Version %s", eglQueryString(v3d->disp, EGL_VERSION));
456         log_debug("EGL Vendor %s", eglQueryString(v3d->disp, EGL_VENDOR));
457         ext = eglQueryString(v3d->disp, EGL_EXTENSIONS);
458         log_debug("EGL Extensions %s", ext);
459
460         if (!ext || !strstr(ext, "EGL_KHR_surfaceless_context")) {
461                 log_err("surfaceless opengl not supported");
462                 ret = -EFAULT;
463                 goto err_disp;
464         }
465
466         api = EGL_OPENGL_ES_API;
467         if (!eglBindAPI(api)) {
468                 log_err("cannot bind opengl-es api");
469                 ret = -EFAULT;
470                 goto err_disp;
471         }
472
473         b = eglChooseConfig(v3d->disp, conf_att, &v3d->conf, 1, &n);
474         if (!b || n != 1) {
475                 log_error("cannot find a proper EGL framebuffer configuration");
476                 ret = -EFAULT;
477                 goto err_disp;
478         }
479
480         v3d->ctx = eglCreateContext(v3d->disp, v3d->conf, EGL_NO_CONTEXT,
481                                     ctx_att);
482         if (v3d->ctx == EGL_NO_CONTEXT) {
483                 log_error("cannot create egl context");
484                 ret = -EFAULT;
485                 goto err_disp;
486         }
487
488         if (!eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
489                             v3d->ctx)) {
490                 log_error("cannot activate surfaceless EGL context");
491                 ret = -EFAULT;
492                 goto err_ctx;
493         }
494
495         ext = (const char*)glGetString(GL_EXTENSIONS);
496         if (ext && strstr((const char*)ext, "GL_EXT_unpack_subimage"))
497                 v3d->supports_rowlen = true;
498         else
499                 log_warning("your GL implementation does not support GL_EXT_unpack_subimage, rendering may be slower than usual");
500
501         return 0;
502
503 err_ctx:
504         eglDestroyContext(v3d->disp, v3d->ctx);
505 err_disp:
506         eglTerminate(v3d->disp);
507 err_gbm:
508         gbm_device_destroy(v3d->gbm);
509 err_video:
510         uterm_drm_video_destroy(video);
511 err_free:
512         free(v3d);
513         return ret;
514 }
515
516 static void video_destroy(struct uterm_video *video)
517 {
518         struct uterm_drm3d_video *v3d = uterm_drm_video_get_data(video);
519
520         log_info("free drm video device %p", video);
521
522         if (!eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
523                             v3d->ctx))
524                 log_error("cannot activate GL context during destruction");
525         uterm_drm3d_deinit_shaders(video);
526
527         eglMakeCurrent(v3d->disp, EGL_NO_SURFACE, EGL_NO_SURFACE,
528                        EGL_NO_CONTEXT);
529         eglDestroyContext(v3d->disp, v3d->ctx);
530         eglTerminate(v3d->disp);
531         gbm_device_destroy(v3d->gbm);
532         free(v3d);
533         uterm_drm_video_destroy(video);
534 }
535
536 static int video_poll(struct uterm_video *video)
537 {
538         return uterm_drm_video_poll(video, &drm_display_ops);
539 }
540
541 static void video_sleep(struct uterm_video *video)
542 {
543         show_displays(video);
544         uterm_drm_video_sleep(video);
545 }
546
547 static int video_wake_up(struct uterm_video *video)
548 {
549         int ret;
550
551         ret = uterm_drm_video_wake_up(video, &drm_display_ops);
552         if (ret)
553                 return ret;
554
555         show_displays(video);
556         return 0;
557 }
558
559 static const struct video_ops drm_video_ops = {
560         .init = video_init,
561         .destroy = video_destroy,
562         .segfault = NULL, /* TODO: reset all saved CRTCs on segfault */
563         .poll = video_poll,
564         .sleep = video_sleep,
565         .wake_up = video_wake_up,
566 };
567
568 static const struct uterm_video_module drm3d_module = {
569         .ops = &drm_video_ops,
570 };
571
572 const struct uterm_video_module *UTERM_VIDEO_DRM3D = &drm3d_module;