1 /*
2  * Copyright © 2011 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *    Kristian Høgsberg <krh@bitplanet.net>
26  */
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <xf86drm.h>
32 #include <dlfcn.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 
38 #include "egl_dri2.h"
39 
40 static struct gbm_bo *
lock_front_buffer(struct gbm_surface * _surf)41 lock_front_buffer(struct gbm_surface *_surf)
42 {
43    struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf;
44    struct dri2_egl_surface *dri2_surf = surf->dri_private;
45    struct gbm_bo *bo;
46 
47    if (dri2_surf->current == NULL) {
48       _eglError(EGL_BAD_SURFACE, "no front buffer");
49       return NULL;
50    }
51 
52    bo = dri2_surf->current->bo;
53    dri2_surf->current->locked = 1;
54    dri2_surf->current = NULL;
55 
56    return bo;
57 }
58 
59 static void
release_buffer(struct gbm_surface * _surf,struct gbm_bo * bo)60 release_buffer(struct gbm_surface *_surf, struct gbm_bo *bo)
61 {
62    struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf;
63    struct dri2_egl_surface *dri2_surf = surf->dri_private;
64    int i;
65 
66    for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
67       if (dri2_surf->color_buffers[i].bo == bo) {
68 	 dri2_surf->color_buffers[i].locked = 0;
69       }
70    }
71 }
72 
73 static int
has_free_buffers(struct gbm_surface * _surf)74 has_free_buffers(struct gbm_surface *_surf)
75 {
76    struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf;
77    struct dri2_egl_surface *dri2_surf = surf->dri_private;
78    int i;
79 
80    for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
81       if (!dri2_surf->color_buffers[i].locked)
82 	 return 1;
83 
84    return 0;
85 }
86 
87 static _EGLSurface *
dri2_create_surface(_EGLDriver * drv,_EGLDisplay * disp,EGLint type,_EGLConfig * conf,EGLNativeWindowType window,const EGLint * attrib_list)88 dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
89 		    _EGLConfig *conf, EGLNativeWindowType window,
90 		    const EGLint *attrib_list)
91 {
92    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
93    struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
94    struct dri2_egl_surface *dri2_surf;
95    struct gbm_dri_surface *surf;
96 
97    (void) drv;
98 
99    dri2_surf = malloc(sizeof *dri2_surf);
100    if (!dri2_surf) {
101       _eglError(EGL_BAD_ALLOC, "dri2_create_surface");
102       return NULL;
103    }
104 
105    memset(dri2_surf, 0, sizeof *dri2_surf);
106    if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))
107       goto cleanup_surf;
108 
109    switch (type) {
110    case EGL_WINDOW_BIT:
111       if (!window)
112          return NULL;
113       surf = gbm_dri_surface((struct gbm_surface *) window);
114       dri2_surf->gbm_surf = surf;
115       dri2_surf->base.Width =  surf->base.width;
116       dri2_surf->base.Height = surf->base.height;
117       surf->dri_private = dri2_surf;
118       break;
119    default:
120       goto cleanup_surf;
121    }
122 
123    dri2_surf->dri_drawable =
124       (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen,
125 					    dri2_conf->dri_double_config,
126 					    dri2_surf->gbm_surf);
127 
128    if (dri2_surf->dri_drawable == NULL) {
129       _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable");
130       goto cleanup_surf;
131    }
132 
133    return &dri2_surf->base;
134 
135  cleanup_surf:
136    free(dri2_surf);
137 
138    return NULL;
139 }
140 
141 static _EGLSurface *
dri2_create_window_surface(_EGLDriver * drv,_EGLDisplay * disp,_EGLConfig * conf,EGLNativeWindowType window,const EGLint * attrib_list)142 dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
143 			   _EGLConfig *conf, EGLNativeWindowType window,
144 			   const EGLint *attrib_list)
145 {
146    return dri2_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
147 			      window, attrib_list);
148 }
149 
150 static EGLBoolean
dri2_destroy_surface(_EGLDriver * drv,_EGLDisplay * disp,_EGLSurface * surf)151 dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
152 {
153    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
154    struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
155    int i;
156 
157    if (!_eglPutSurface(surf))
158       return EGL_TRUE;
159 
160    (*dri2_dpy->core->destroyDrawable)(dri2_surf->dri_drawable);
161 
162    for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
163       if (dri2_surf->color_buffers[i].bo)
164 	 gbm_bo_destroy(dri2_surf->color_buffers[i].bo);
165    }
166 
167    for (i = 0; i < __DRI_BUFFER_COUNT; i++) {
168       if (dri2_surf->dri_buffers[i])
169          dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
170                                        dri2_surf->dri_buffers[i]);
171    }
172 
173    free(surf);
174 
175    return EGL_TRUE;
176 }
177 
178 static int
get_back_bo(struct dri2_egl_surface * dri2_surf,__DRIbuffer * buffer)179 get_back_bo(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
180 {
181    struct dri2_egl_display *dri2_dpy =
182       dri2_egl_display(dri2_surf->base.Resource.Display);
183    struct gbm_dri_bo *bo;
184    struct gbm_dri_surface *surf = dri2_surf->gbm_surf;
185    int i, name, pitch;
186 
187    if (dri2_surf->back == NULL) {
188       for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
189 	 if (!dri2_surf->color_buffers[i].locked) {
190 	    dri2_surf->back = &dri2_surf->color_buffers[i];
191 	    break;
192 	 }
193       }
194    }
195 
196    if (dri2_surf->back == NULL)
197       return -1;
198    if (dri2_surf->back->bo == NULL)
199       dri2_surf->back->bo = gbm_bo_create(&dri2_dpy->gbm_dri->base.base,
200 					  surf->base.width, surf->base.height,
201 					  surf->base.format, surf->base.flags);
202    if (dri2_surf->back->bo == NULL)
203       return -1;
204 
205    bo = (struct gbm_dri_bo *) dri2_surf->back->bo;
206 
207    dri2_dpy->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_NAME, &name);
208    dri2_dpy->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch);
209 
210    buffer->attachment = __DRI_BUFFER_BACK_LEFT;
211    buffer->name = name;
212    buffer->pitch = pitch;
213    buffer->cpp = 4;
214    buffer->flags = 0;
215 
216    return 0;
217 }
218 
219 static int
get_aux_bo(struct dri2_egl_surface * dri2_surf,unsigned int attachment,unsigned int format,__DRIbuffer * buffer)220 get_aux_bo(struct dri2_egl_surface *dri2_surf,
221 	   unsigned int attachment, unsigned int format, __DRIbuffer *buffer)
222 {
223    struct dri2_egl_display *dri2_dpy =
224       dri2_egl_display(dri2_surf->base.Resource.Display);
225    __DRIbuffer *b = dri2_surf->dri_buffers[attachment];
226 
227    if (b == NULL) {
228       b = dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
229 					 attachment, format,
230 					 dri2_surf->base.Width,
231 					 dri2_surf->base.Height);
232       dri2_surf->dri_buffers[attachment] = b;
233    }
234    if (b == NULL)
235       return -1;
236 
237    memcpy(buffer, b, sizeof *buffer);
238 
239    return 0;
240 }
241 
242 static __DRIbuffer *
dri2_get_buffers_with_format(__DRIdrawable * driDrawable,int * width,int * height,unsigned int * attachments,int count,int * out_count,void * loaderPrivate)243 dri2_get_buffers_with_format(__DRIdrawable *driDrawable,
244 			     int *width, int *height,
245 			     unsigned int *attachments, int count,
246 			     int *out_count, void *loaderPrivate)
247 {
248    struct dri2_egl_surface *dri2_surf = loaderPrivate;
249    int i, j;
250 
251    dri2_surf->buffer_count = 0;
252    for (i = 0, j = 0; i < 2 * count; i += 2, j++) {
253       assert(attachments[i] < __DRI_BUFFER_COUNT);
254       assert(dri2_surf->buffer_count < 5);
255 
256       switch (attachments[i]) {
257       case __DRI_BUFFER_BACK_LEFT:
258 	 if (get_back_bo(dri2_surf, &dri2_surf->buffers[j]) < 0) {
259 	    _eglError(EGL_BAD_ALLOC, "failed to allocate color buffer");
260 	    return NULL;
261 	 }
262 	 break;
263       default:
264 	 if (get_aux_bo(dri2_surf, attachments[i], attachments[i + 1],
265 			&dri2_surf->buffers[j]) < 0) {
266 	    _eglError(EGL_BAD_ALLOC, "failed to allocate aux buffer");
267 	    return NULL;
268 	 }
269 	 break;
270       }
271    }
272 
273    *out_count = j;
274    if (j == 0)
275       return NULL;
276 
277    *width = dri2_surf->base.Width;
278    *height = dri2_surf->base.Height;
279 
280    return dri2_surf->buffers;
281 }
282 
283 static __DRIbuffer *
dri2_get_buffers(__DRIdrawable * driDrawable,int * width,int * height,unsigned int * attachments,int count,int * out_count,void * loaderPrivate)284 dri2_get_buffers(__DRIdrawable * driDrawable,
285 		 int *width, int *height,
286 		 unsigned int *attachments, int count,
287 		 int *out_count, void *loaderPrivate)
288 {
289    unsigned int *attachments_with_format;
290    __DRIbuffer *buffer;
291    const unsigned int format = 32;
292    int i;
293 
294    attachments_with_format = calloc(count * 2, sizeof(unsigned int));
295    if (!attachments_with_format) {
296       *out_count = 0;
297       return NULL;
298    }
299 
300    for (i = 0; i < count; ++i) {
301       attachments_with_format[2*i] = attachments[i];
302       attachments_with_format[2*i + 1] = format;
303    }
304 
305    buffer =
306       dri2_get_buffers_with_format(driDrawable,
307 				   width, height,
308 				   attachments_with_format, count,
309 				   out_count, loaderPrivate);
310 
311    free(attachments_with_format);
312 
313    return buffer;
314 }
315 
316 static void
dri2_flush_front_buffer(__DRIdrawable * driDrawable,void * loaderPrivate)317 dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
318 {
319    (void) driDrawable;
320    (void) loaderPrivate;
321 }
322 
323 static EGLBoolean
dri2_swap_buffers(_EGLDriver * drv,_EGLDisplay * disp,_EGLSurface * draw)324 dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
325 {
326    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
327    struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
328 
329    if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
330       if (dri2_surf->current)
331 	 _eglError(EGL_BAD_SURFACE, "dri2_swap_buffers");
332       dri2_surf->current = dri2_surf->back;
333       dri2_surf->back = NULL;
334    }
335 
336    (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable);
337    (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable);
338 
339    return EGL_TRUE;
340 }
341 
342 static _EGLImage *
dri2_create_image_khr_pixmap(_EGLDisplay * disp,_EGLContext * ctx,EGLClientBuffer buffer,const EGLint * attr_list)343 dri2_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
344 			     EGLClientBuffer buffer, const EGLint *attr_list)
345 {
346    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
347    struct gbm_dri_bo *dri_bo = gbm_dri_bo((struct gbm_bo *) buffer);
348    struct dri2_egl_image *dri2_img;
349 
350    dri2_img = malloc(sizeof *dri2_img);
351    if (!dri2_img) {
352       _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr_pixmap");
353       return NULL;
354    }
355 
356    if (!_eglInitImage(&dri2_img->base, disp)) {
357       free(dri2_img);
358       return NULL;
359    }
360 
361    dri2_img->dri_image = dri2_dpy->image->dupImage(dri_bo->image, dri2_img);
362    if (dri2_img->dri_image == NULL) {
363       free(dri2_img);
364       _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr_pixmap");
365       return NULL;
366    }
367 
368    return &dri2_img->base;
369 }
370 
371 static _EGLImage *
dri2_drm_create_image_khr(_EGLDriver * drv,_EGLDisplay * disp,_EGLContext * ctx,EGLenum target,EGLClientBuffer buffer,const EGLint * attr_list)372 dri2_drm_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp,
373                           _EGLContext *ctx, EGLenum target,
374                           EGLClientBuffer buffer, const EGLint *attr_list)
375 {
376    (void) drv;
377 
378    switch (target) {
379    case EGL_NATIVE_PIXMAP_KHR:
380       return dri2_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
381    default:
382       return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list);
383    }
384 }
385 
386 static int
dri2_drm_authenticate(_EGLDisplay * disp,uint32_t id)387 dri2_drm_authenticate(_EGLDisplay *disp, uint32_t id)
388 {
389    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
390 
391    return drmAuthMagic(dri2_dpy->fd, id);
392 }
393 
394 EGLBoolean
dri2_initialize_drm(_EGLDriver * drv,_EGLDisplay * disp)395 dri2_initialize_drm(_EGLDriver *drv, _EGLDisplay *disp)
396 {
397    struct dri2_egl_display *dri2_dpy;
398    struct gbm_device *gbm;
399    int fd = -1;
400    int i;
401 
402    dri2_dpy = malloc(sizeof *dri2_dpy);
403    if (!dri2_dpy)
404       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
405 
406    memset(dri2_dpy, 0, sizeof *dri2_dpy);
407 
408    disp->DriverData = (void *) dri2_dpy;
409 
410    gbm = disp->PlatformDisplay;
411    if (gbm == NULL) {
412       fd = open("/dev/dri/card0", O_RDWR);
413       dri2_dpy->own_device = 1;
414       gbm = gbm_create_device(fd);
415       if (gbm == NULL)
416          return EGL_FALSE;
417    }
418 
419    if (strcmp(gbm_device_get_backend_name(gbm), "drm") != 0) {
420       free(dri2_dpy);
421       return EGL_FALSE;
422    }
423 
424    dri2_dpy->gbm_dri = gbm_dri_device(gbm);
425    if (dri2_dpy->gbm_dri->base.type != GBM_DRM_DRIVER_TYPE_DRI) {
426       free(dri2_dpy);
427       return EGL_FALSE;
428    }
429 
430    if (fd < 0) {
431       fd = dup(gbm_device_get_fd(gbm));
432       if (fd < 0) {
433          free(dri2_dpy);
434          return EGL_FALSE;
435       }
436    }
437 
438    dri2_dpy->fd = fd;
439    dri2_dpy->device_name = dri2_get_device_name_for_fd(dri2_dpy->fd);
440    dri2_dpy->driver_name = dri2_dpy->gbm_dri->base.driver_name;
441 
442    dri2_dpy->dri_screen = dri2_dpy->gbm_dri->screen;
443    dri2_dpy->core = dri2_dpy->gbm_dri->core;
444    dri2_dpy->dri2 = dri2_dpy->gbm_dri->dri2;
445    dri2_dpy->image = dri2_dpy->gbm_dri->image;
446    dri2_dpy->flush = dri2_dpy->gbm_dri->flush;
447    dri2_dpy->driver_configs = dri2_dpy->gbm_dri->driver_configs;
448 
449    dri2_dpy->gbm_dri->lookup_image = dri2_lookup_egl_image;
450    dri2_dpy->gbm_dri->lookup_user_data = disp;
451 
452    dri2_dpy->gbm_dri->get_buffers = dri2_get_buffers;
453    dri2_dpy->gbm_dri->flush_front_buffer = dri2_flush_front_buffer;
454    dri2_dpy->gbm_dri->get_buffers_with_format = dri2_get_buffers_with_format;
455 
456    dri2_dpy->gbm_dri->base.base.surface_lock_front_buffer = lock_front_buffer;
457    dri2_dpy->gbm_dri->base.base.surface_release_buffer = release_buffer;
458    dri2_dpy->gbm_dri->base.base.surface_has_free_buffers = has_free_buffers;
459 
460    dri2_setup_screen(disp);
461 
462    for (i = 0; dri2_dpy->driver_configs[i]; i++)
463       dri2_add_config(disp, dri2_dpy->driver_configs[i],
464                       i + 1, 0, EGL_WINDOW_BIT, NULL, NULL);
465 
466    drv->API.CreateWindowSurface = dri2_create_window_surface;
467    drv->API.DestroySurface = dri2_destroy_surface;
468    drv->API.SwapBuffers = dri2_swap_buffers;
469    drv->API.CreateImageKHR = dri2_drm_create_image_khr;
470 
471 #ifdef HAVE_WAYLAND_PLATFORM
472    disp->Extensions.WL_bind_wayland_display = EGL_TRUE;
473 #endif
474    dri2_dpy->authenticate = dri2_drm_authenticate;
475 
476    /* we're supporting EGL 1.4 */
477    disp->VersionMajor = 1;
478    disp->VersionMinor = 4;
479 
480    return EGL_TRUE;
481 }
482