"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / winsys / cogl-winsys-egl-kms.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2011 Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see
20  * <http://www.gnu.org/licenses/>.
21  *
22  *
23  * Authors:
24  *   Rob Bradford <rob@linux.intel.com>
25  *   Kristian Høgsberg (from eglkms.c)
26  *   Benjamin Franzke (from eglkms.c)
27  *   Robert Bragg <robert@linux.intel.com>
28  *   Neil Roberts <neil@linux.intel.com>
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <EGL/egl.h>
36 #include <EGL/eglext.h>
37 #include <drm.h>
38 #include <xf86drmMode.h>
39 #include <gbm.h>
40 #include <glib.h>
41 #include <sys/fcntl.h>
42 #include <unistd.h>
43
44 #include "cogl-winsys-egl-kms-private.h"
45 #include "cogl-winsys-egl-private.h"
46 #include "cogl-renderer-private.h"
47 #include "cogl-framebuffer-private.h"
48 #include "cogl-onscreen-private.h"
49 #include "cogl-kms-renderer.h"
50
51 static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
52
53 typedef struct _CoglRendererKMS
54 {
55   int fd;
56   struct gbm_device *gbm;
57 } CoglRendererKMS;
58
59 typedef struct _CoglDisplayKMS
60 {
61   drmModeConnector *connector;
62   drmModeEncoder *encoder;
63   drmModeModeInfo mode;
64   drmModeCrtcPtr saved_crtc;
65   int width, height;
66 } CoglDisplayKMS;
67
68 typedef struct _CoglOnscreenKMS
69 {
70   uint32_t fb_id[2];
71   struct gbm_bo *bo[2];
72   unsigned int fb, color_rb[2], depth_rb;
73   EGLImageKHR image[2];
74   int current_frame;
75 } CoglOnscreenKMS;
76
77 static const char device_name[] = "/dev/dri/card0";
78
79 static void
80 _cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
81 {
82   CoglRendererEGL *egl_renderer = renderer->winsys;
83   CoglRendererKMS *kms_renderer = egl_renderer->platform;
84
85   eglTerminate (egl_renderer->edpy);
86
87   g_slice_free (CoglRendererKMS, kms_renderer);
88   g_slice_free (CoglRendererEGL, egl_renderer);
89 }
90
91 static gboolean
92 _cogl_winsys_renderer_connect (CoglRenderer *renderer,
93                                GError **error)
94 {
95   CoglRendererEGL *egl_renderer;
96   CoglRendererKMS *kms_renderer;
97
98   renderer->winsys = g_slice_new0 (CoglRendererEGL);
99   egl_renderer = renderer->winsys;
100
101   egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable;
102   egl_renderer->platform = g_slice_new0 (CoglRendererKMS);
103   kms_renderer = egl_renderer->platform;
104
105   kms_renderer->fd = open (device_name, O_RDWR);
106   if (kms_renderer->fd < 0)
107     {
108       /* Probably permissions error */
109       g_set_error (error, COGL_WINSYS_ERROR,
110                    COGL_WINSYS_ERROR_INIT,
111                    "Couldn't open %s", device_name);
112       return FALSE;
113     }
114
115   kms_renderer->gbm = gbm_create_device (kms_renderer->fd);
116   if (kms_renderer->gbm == NULL)
117     {
118       g_set_error (error, COGL_WINSYS_ERROR,
119                    COGL_WINSYS_ERROR_INIT,
120                    "Couldn't create gbm device");
121       goto close_fd;
122     }
123
124   egl_renderer->edpy = eglGetDisplay ((EGLNativeDisplayType)kms_renderer->gbm);
125   if (egl_renderer->edpy == EGL_NO_DISPLAY)
126     {
127       g_set_error (error, COGL_WINSYS_ERROR,
128                    COGL_WINSYS_ERROR_INIT,
129                    "Couldn't get eglDisplay");
130       goto destroy_gbm_device;
131     }
132
133   if (!_cogl_winsys_egl_renderer_connect_common (renderer, error))
134     goto egl_terminate;
135
136   return TRUE;
137
138 egl_terminate:
139   eglTerminate (egl_renderer->edpy);
140 destroy_gbm_device:
141   gbm_device_destroy (kms_renderer->gbm);
142 close_fd:
143   close (kms_renderer->fd);
144
145   _cogl_winsys_renderer_disconnect (renderer);
146
147   return FALSE;
148 }
149
150 static gboolean
151 _cogl_winsys_egl_display_setup (CoglDisplay *display,
152                                 GError **error)
153 {
154   CoglDisplayEGL *egl_display = display->winsys;
155   CoglDisplayKMS *kms_display;
156   CoglRendererEGL *egl_renderer = display->renderer->winsys;
157   CoglRendererKMS *kms_renderer = egl_renderer->platform;
158   CoglEGLWinsysFeature surfaceless_feature = 0;
159   const char *surfaceless_feature_name = "";
160   drmModeRes *resources;
161   drmModeConnector *connector;
162   drmModeEncoder *encoder;
163   int i;
164
165   kms_display = g_slice_new0 (CoglDisplayKMS);
166   egl_display->platform = kms_display;
167
168   switch (display->renderer->driver)
169     {
170     case COGL_DRIVER_GL:
171       surfaceless_feature = COGL_EGL_WINSYS_FEATURE_SURFACELESS_OPENGL;
172       surfaceless_feature_name = "opengl";
173       break;
174     case COGL_DRIVER_GLES1:
175       surfaceless_feature = COGL_EGL_WINSYS_FEATURE_SURFACELESS_GLES1;
176       surfaceless_feature_name = "gles1";
177       break;
178     case COGL_DRIVER_GLES2:
179       surfaceless_feature = COGL_EGL_WINSYS_FEATURE_SURFACELESS_GLES2;
180       surfaceless_feature_name = "gles2";
181       break;
182     case COGL_DRIVER_ANY:
183       g_return_val_if_reached (FALSE);
184     }
185
186   if (!(egl_renderer->private_features & surfaceless_feature))
187     {
188       g_set_error (error, COGL_WINSYS_ERROR,
189                    COGL_WINSYS_ERROR_INIT,
190                    "EGL_KHR_surfaceless_%s extension not available",
191                    surfaceless_feature_name);
192       return FALSE;
193     }
194
195   resources = drmModeGetResources (kms_renderer->fd);
196   if (!resources)
197     {
198       g_set_error (error, COGL_WINSYS_ERROR,
199                    COGL_WINSYS_ERROR_INIT,
200                    "drmModeGetResources failed");
201       return FALSE;
202     }
203
204   for (i = 0; i < resources->count_connectors; i++)
205     {
206       connector = drmModeGetConnector (kms_renderer->fd,
207                                        resources->connectors[i]);
208       if (connector == NULL)
209         continue;
210
211       if (connector->connection == DRM_MODE_CONNECTED &&
212           connector->count_modes > 0)
213         break;
214
215       drmModeFreeConnector(connector);
216     }
217
218   if (i == resources->count_connectors)
219     {
220       g_set_error (error, COGL_WINSYS_ERROR,
221                    COGL_WINSYS_ERROR_INIT,
222                    "No currently active connector found");
223       return FALSE;
224     }
225
226   for (i = 0; i < resources->count_encoders; i++)
227     {
228       encoder = drmModeGetEncoder (kms_renderer->fd, resources->encoders[i]);
229
230       if (encoder == NULL)
231         continue;
232
233       if (encoder->encoder_id == connector->encoder_id)
234         break;
235
236       drmModeFreeEncoder (encoder);
237     }
238
239   kms_display->saved_crtc = drmModeGetCrtc (kms_renderer->fd,
240                                             encoder->crtc_id);
241
242   kms_display->connector = connector;
243   kms_display->encoder = encoder;
244   kms_display->mode = connector->modes[0];
245   kms_display->width = kms_display->mode.hdisplay;
246   kms_display->height = kms_display->mode.vdisplay;
247
248   return TRUE;
249 }
250
251 static void
252 _cogl_winsys_egl_display_destroy (CoglDisplay *display)
253 {
254   CoglDisplayEGL *egl_display = display->winsys;
255
256   g_slice_free (CoglDisplayKMS, egl_display->platform);
257 }
258
259 static gboolean
260 _cogl_winsys_egl_try_create_context (CoglDisplay *display,
261                                      EGLint *attribs,
262                                      GError **error)
263 {
264   CoglRenderer *renderer = display->renderer;
265   CoglRendererEGL *egl_renderer = renderer->winsys;
266   CoglDisplayEGL *egl_display = display->winsys;
267
268   egl_display->egl_context = eglCreateContext (egl_renderer->edpy,
269                                                NULL,
270                                                EGL_NO_CONTEXT,
271                                                attribs);
272
273   if (egl_display->egl_context == NULL)
274     {
275       g_set_error (error, COGL_WINSYS_ERROR,
276                    COGL_WINSYS_ERROR_CREATE_CONTEXT,
277                    "Couldn't create EGL context");
278       return FALSE;
279     }
280
281   if (!eglMakeCurrent (egl_renderer->edpy,
282                        EGL_NO_SURFACE,
283                        EGL_NO_SURFACE,
284                        egl_display->egl_context))
285     {
286       g_set_error (error, COGL_WINSYS_ERROR,
287                    COGL_WINSYS_ERROR_CREATE_CONTEXT,
288                    "Failed to make context current");
289       return FALSE;
290     }
291
292   return TRUE;
293 }
294
295 static void
296 _cogl_winsys_egl_cleanup_context (CoglDisplay *display)
297 {
298   CoglDisplayEGL *egl_display = display->winsys;
299   CoglDisplayKMS *kms_display = egl_display->platform;
300   CoglRenderer *renderer = display->renderer;
301   CoglRendererEGL *egl_renderer = renderer->winsys;
302   CoglRendererKMS *kms_renderer = egl_renderer->platform;
303
304   /* Restore the saved CRTC - this failing should not propagate an error */
305   if (kms_display->saved_crtc)
306     {
307       int ret = drmModeSetCrtc (kms_renderer->fd,
308                                 kms_display->saved_crtc->crtc_id,
309                                 kms_display->saved_crtc->buffer_id,
310                                 kms_display->saved_crtc->x,
311                                 kms_display->saved_crtc->y,
312                                 &kms_display->connector->connector_id, 1,
313                                 &kms_display->saved_crtc->mode);
314       if (ret)
315         g_critical (G_STRLOC ": Error restoring saved CRTC");
316
317       drmModeFreeCrtc (kms_display->saved_crtc);
318     }
319 }
320
321 static void
322 _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
323 {
324   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
325   CoglDisplayEGL *egl_display = context->display->winsys;
326   CoglDisplayKMS *kms_display = egl_display->platform;
327   CoglRenderer *renderer = context->display->renderer;
328   CoglRendererEGL *egl_renderer = renderer->winsys;
329   CoglRendererKMS *kms_renderer = egl_renderer->platform;
330   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
331   CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
332
333   if (drmModeSetCrtc (kms_renderer->fd,
334                       kms_display->encoder->crtc_id,
335                       kms_onscreen->fb_id[kms_onscreen->current_frame],
336                       0, 0,
337                       &kms_display->connector->connector_id,
338                       1,
339                       &kms_display->mode) != 0)
340     {
341       g_error (G_STRLOC ": Setting CRTC failed");
342     }
343
344   /* Update frame that we're drawing to be the new one */
345   kms_onscreen->current_frame ^= 1;
346
347   context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
348   context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
349                                       GL_COLOR_ATTACHMENT0_EXT,
350                                       GL_RENDERBUFFER_EXT,
351                                       kms_onscreen->
352                                       color_rb[kms_onscreen->current_frame]);
353
354   if (context->glCheckFramebufferStatus (GL_FRAMEBUFFER_EXT) !=
355       GL_FRAMEBUFFER_COMPLETE)
356     {
357       g_error (G_STRLOC ": FBO not complete");
358     }
359 }
360
361 static gboolean
362 _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
363                             GError **error)
364 {
365   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
366   CoglContext *context = framebuffer->context;
367   CoglDisplay *display = context->display;
368   CoglDisplayEGL *egl_display = display->winsys;
369   CoglDisplayKMS *kms_display = egl_display->platform;
370   CoglRenderer *renderer = display->renderer;
371   CoglRendererEGL *egl_renderer = renderer->winsys;
372   CoglRendererKMS *kms_renderer = egl_renderer->platform;
373   CoglOnscreenEGL *egl_onscreen;
374   CoglOnscreenKMS *kms_onscreen;
375   int i;
376
377   _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
378
379   onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
380   egl_onscreen = onscreen->winsys;
381
382   kms_onscreen = g_slice_new0 (CoglOnscreenKMS);
383   egl_onscreen->platform = kms_onscreen;
384
385   context->glGenRenderbuffers (2, kms_onscreen->color_rb);
386
387   for (i = 0; i < 2; i++)
388     {
389       uint32_t handle, stride;
390
391       kms_onscreen->bo[i] =
392         gbm_bo_create (kms_renderer->gbm,
393                        kms_display->mode.hdisplay, kms_display->mode.vdisplay,
394                        GBM_BO_FORMAT_XRGB8888,
395                        GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
396       if (!kms_onscreen->bo[i])
397         {
398           g_set_error (error, COGL_WINSYS_ERROR,
399                        COGL_WINSYS_ERROR_CREATE_CONTEXT,
400                        "Failed to allocate buffer");
401           return FALSE;
402         }
403
404       kms_onscreen->image[i] =
405         _cogl_egl_create_image (context,
406                                 EGL_NATIVE_PIXMAP_KHR,
407                                 kms_onscreen->bo[i],
408                                 NULL);
409
410       if (kms_onscreen->image[i] == EGL_NO_IMAGE_KHR)
411         {
412           g_set_error (error, COGL_WINSYS_ERROR,
413                        COGL_WINSYS_ERROR_CREATE_CONTEXT,
414                        "Failed to create EGL image");
415           return FALSE;
416         }
417
418       context->glBindRenderbuffer (GL_RENDERBUFFER_EXT,
419                                    kms_onscreen->color_rb[i]);
420       context->glEGLImageTargetRenderbufferStorage (GL_RENDERBUFFER,
421                                                     kms_onscreen->image[i]);
422       context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, 0);
423
424       handle = gbm_bo_get_handle (kms_onscreen->bo[i]).u32;
425       stride = gbm_bo_get_pitch (kms_onscreen->bo[i]);
426
427       if (drmModeAddFB (kms_renderer->fd,
428                         kms_display->mode.hdisplay,
429                         kms_display->mode.vdisplay,
430                         24, 32,
431                         stride,
432                         handle,
433                         &kms_onscreen->fb_id[i]) != 0)
434         {
435           g_set_error (error, COGL_WINSYS_ERROR,
436                        COGL_WINSYS_ERROR_CREATE_CONTEXT,
437                        "Failed to create framebuffer from buffer");
438           return FALSE;
439         }
440     }
441
442   context->glGenFramebuffers (1, &kms_onscreen->fb);
443   context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
444
445   context->glGenRenderbuffers (1, &kms_onscreen->depth_rb);
446   context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, kms_onscreen->depth_rb);
447   context->glRenderbufferStorage (GL_RENDERBUFFER_EXT,
448                                   GL_DEPTH_COMPONENT16,
449                                   kms_display->mode.hdisplay,
450                                   kms_display->mode.vdisplay);
451   context->glBindRenderbuffer (GL_RENDERBUFFER_EXT, 0);
452
453   context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
454                                       GL_DEPTH_ATTACHMENT_EXT,
455                                       GL_RENDERBUFFER_EXT,
456                                       kms_onscreen->depth_rb);
457
458   kms_onscreen->current_frame = 0;
459   _cogl_winsys_onscreen_swap_buffers (onscreen);
460
461   _cogl_framebuffer_winsys_update_size (framebuffer,
462                                         kms_display->width,
463                                         kms_display->height);
464
465   return TRUE;
466 }
467
468 static void
469 _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
470 {
471   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
472   CoglContext *context = framebuffer->context;
473   CoglRenderer *renderer = context->display->renderer;
474   CoglRendererEGL *egl_renderer = renderer->winsys;
475   CoglRendererKMS *kms_renderer = egl_renderer->platform;
476   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
477   CoglOnscreenKMS *kms_onscreen;
478   int i;
479
480   /* If we never successfully allocated then there's nothing to do */
481   if (egl_onscreen == NULL)
482     return;
483
484   kms_onscreen = egl_onscreen->platform;
485
486   context->glBindFramebuffer (GL_FRAMEBUFFER_EXT, kms_onscreen->fb);
487   context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
488                                       GL_COLOR_ATTACHMENT0_EXT,
489                                       GL_RENDERBUFFER_EXT,
490                                       0);
491   context->glDeleteRenderbuffers(2, kms_onscreen->color_rb);
492   context->glFramebufferRenderbuffer (GL_FRAMEBUFFER_EXT,
493                                       GL_DEPTH_ATTACHMENT_EXT,
494                                       GL_RENDERBUFFER_EXT,
495                                       0);
496   context->glDeleteRenderbuffers(1, &kms_onscreen->depth_rb);
497
498   for (i = 0; i < 2; i++)
499     {
500       drmModeRmFB (kms_renderer->fd, kms_onscreen->fb_id[i]);
501       _cogl_egl_destroy_image (context, kms_onscreen->image[i]);
502       gbm_bo_destroy (kms_onscreen->bo[i]);
503     }
504
505   g_slice_free (CoglOnscreenKMS, kms_onscreen);
506   g_slice_free (CoglOnscreenEGL, onscreen->winsys);
507   onscreen->winsys = NULL;
508 }
509
510 static void
511 _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
512 {
513   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
514   CoglDisplayEGL *egl_display = context->display->winsys;
515   CoglRenderer *renderer = context->display->renderer;
516   CoglRendererEGL *egl_renderer = renderer->winsys;
517
518   eglMakeCurrent (egl_renderer->edpy,
519                   EGL_NO_SURFACE,
520                   EGL_NO_SURFACE,
521                   egl_display->egl_context);
522 }
523
524 static void
525 _cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen)
526 {
527   _cogl_winsys_onscreen_bind (onscreen);
528 }
529
530 static const CoglWinsysEGLVtable
531 _cogl_winsys_egl_vtable =
532   {
533     .display_setup = _cogl_winsys_egl_display_setup,
534     .display_destroy = _cogl_winsys_egl_display_destroy,
535     .try_create_context = _cogl_winsys_egl_try_create_context,
536     .cleanup_context = _cogl_winsys_egl_cleanup_context
537   };
538
539 const CoglWinsysVtable *
540 _cogl_winsys_egl_kms_get_vtable (void)
541 {
542   static gboolean vtable_inited = FALSE;
543   static CoglWinsysVtable vtable;
544
545   if (!vtable_inited)
546     {
547       /* The EGL_KMS winsys is a subclass of the EGL winsys so we
548          start by copying its vtable */
549
550       vtable = *_cogl_winsys_egl_get_vtable ();
551
552       vtable.id = COGL_WINSYS_ID_EGL_KMS;
553       vtable.name = "EGL_KMS";
554
555       vtable.renderer_connect = _cogl_winsys_renderer_connect;
556       vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect;
557
558       vtable.onscreen_init = _cogl_winsys_onscreen_init;
559       vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit;
560       vtable.onscreen_bind = _cogl_winsys_onscreen_bind;
561
562       /* The KMS winsys doesn't support swap region */
563       vtable.onscreen_swap_region = NULL;
564       vtable.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers;
565
566       vtable.onscreen_update_swap_throttled =
567         _cogl_winsys_onscreen_update_swap_throttled;
568
569       vtable_inited = TRUE;
570     }
571
572   return &vtable;
573 }
574
575 int
576 cogl_kms_renderer_get_kms_fd (CoglRenderer *renderer)
577 {
578   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), -1);
579
580   if (renderer->connected)
581     {
582       CoglRendererEGL *egl_renderer = renderer->winsys;
583       CoglRendererKMS *kms_renderer = egl_renderer->platform;
584       return kms_renderer->fd;
585     }
586   else
587     return -1;
588 }