785139e0eb36174703d510b1b43550fa4393c02d
[mesa.git] / src / egl / drivers / dri2 / platform_drm.c
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 #include "egl_dri2_fallbacks.h"
40 #include "loader.h"
41
42 static struct gbm_bo *
43 lock_front_buffer(struct gbm_surface *_surf)
44 {
45 struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf;
46 struct dri2_egl_surface *dri2_surf = surf->dri_private;
47 struct gbm_bo *bo;
48
49 if (dri2_surf->current == NULL) {
50 _eglError(EGL_BAD_SURFACE, "no front buffer");
51 return NULL;
52 }
53
54 bo = dri2_surf->current->bo;
55 dri2_surf->current->locked = 1;
56 dri2_surf->current = NULL;
57
58 return bo;
59 }
60
61 static void
62 release_buffer(struct gbm_surface *_surf, struct gbm_bo *bo)
63 {
64 struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf;
65 struct dri2_egl_surface *dri2_surf = surf->dri_private;
66 int i;
67
68 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
69 if (dri2_surf->color_buffers[i].bo == bo) {
70 dri2_surf->color_buffers[i].locked = 0;
71 }
72 }
73 }
74
75 static int
76 has_free_buffers(struct gbm_surface *_surf)
77 {
78 struct gbm_dri_surface *surf = (struct gbm_dri_surface *) _surf;
79 struct dri2_egl_surface *dri2_surf = surf->dri_private;
80 int i;
81
82 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
83 if (!dri2_surf->color_buffers[i].locked)
84 return 1;
85
86 return 0;
87 }
88
89 static _EGLSurface *
90 dri2_drm_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
91 _EGLConfig *conf, void *native_window,
92 const EGLint *attrib_list)
93 {
94 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
95 struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
96 struct dri2_egl_surface *dri2_surf;
97 struct gbm_surface *window = native_window;
98 struct gbm_dri_surface *surf;
99
100 (void) drv;
101
102 dri2_surf = calloc(1, sizeof *dri2_surf);
103 if (!dri2_surf) {
104 _eglError(EGL_BAD_ALLOC, "dri2_create_surface");
105 return NULL;
106 }
107
108 if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))
109 goto cleanup_surf;
110
111 switch (type) {
112 case EGL_WINDOW_BIT:
113 if (!window)
114 return NULL;
115 surf = gbm_dri_surface(window);
116 dri2_surf->gbm_surf = surf;
117 dri2_surf->base.Width = surf->base.width;
118 dri2_surf->base.Height = surf->base.height;
119 surf->dri_private = dri2_surf;
120 break;
121 default:
122 goto cleanup_surf;
123 }
124
125 dri2_surf->dri_drawable =
126 (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen,
127 dri2_conf->dri_double_config,
128 dri2_surf->gbm_surf);
129
130 if (dri2_surf->dri_drawable == NULL) {
131 _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable");
132 goto cleanup_surf;
133 }
134
135 return &dri2_surf->base;
136
137 cleanup_surf:
138 free(dri2_surf);
139
140 return NULL;
141 }
142
143 static _EGLSurface *
144 dri2_drm_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
145 _EGLConfig *conf, void *native_window,
146 const EGLint *attrib_list)
147 {
148 return dri2_drm_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
149 native_window, attrib_list);
150 }
151
152 static EGLBoolean
153 dri2_drm_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
154 {
155 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
156 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
157 int i;
158
159 if (!_eglPutSurface(surf))
160 return EGL_TRUE;
161
162 (*dri2_dpy->core->destroyDrawable)(dri2_surf->dri_drawable);
163
164 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
165 if (dri2_surf->color_buffers[i].bo)
166 gbm_bo_destroy(dri2_surf->color_buffers[i].bo);
167 }
168
169 for (i = 0; i < __DRI_BUFFER_COUNT; i++) {
170 if (dri2_surf->dri_buffers[i])
171 dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
172 dri2_surf->dri_buffers[i]);
173 }
174
175 free(surf);
176
177 return EGL_TRUE;
178 }
179
180 static int
181 get_back_bo(struct dri2_egl_surface *dri2_surf)
182 {
183 struct dri2_egl_display *dri2_dpy =
184 dri2_egl_display(dri2_surf->base.Resource.Display);
185 struct gbm_dri_surface *surf = dri2_surf->gbm_surf;
186 int i;
187
188 if (dri2_surf->back == NULL) {
189 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
190 if (!dri2_surf->color_buffers[i].locked) {
191 dri2_surf->back = &dri2_surf->color_buffers[i];
192 break;
193 }
194 }
195 }
196
197 if (dri2_surf->back == NULL)
198 return -1;
199 if (dri2_surf->back->bo == NULL)
200 dri2_surf->back->bo = gbm_bo_create(&dri2_dpy->gbm_dri->base.base,
201 surf->base.width, surf->base.height,
202 surf->base.format, surf->base.flags);
203 if (dri2_surf->back->bo == NULL)
204 return -1;
205
206 return 0;
207 }
208
209 static void
210 back_bo_to_dri_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
211 {
212 struct dri2_egl_display *dri2_dpy =
213 dri2_egl_display(dri2_surf->base.Resource.Display);
214 struct gbm_dri_bo *bo;
215 int name, pitch;
216
217 bo = (struct gbm_dri_bo *) dri2_surf->back->bo;
218
219 dri2_dpy->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_NAME, &name);
220 dri2_dpy->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch);
221
222 buffer->attachment = __DRI_BUFFER_BACK_LEFT;
223 buffer->name = name;
224 buffer->pitch = pitch;
225 buffer->cpp = 4;
226 buffer->flags = 0;
227 }
228
229 static int
230 get_aux_bo(struct dri2_egl_surface *dri2_surf,
231 unsigned int attachment, unsigned int format, __DRIbuffer *buffer)
232 {
233 struct dri2_egl_display *dri2_dpy =
234 dri2_egl_display(dri2_surf->base.Resource.Display);
235 __DRIbuffer *b = dri2_surf->dri_buffers[attachment];
236
237 if (b == NULL) {
238 b = dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
239 attachment, format,
240 dri2_surf->base.Width,
241 dri2_surf->base.Height);
242 dri2_surf->dri_buffers[attachment] = b;
243 }
244 if (b == NULL)
245 return -1;
246
247 memcpy(buffer, b, sizeof *buffer);
248
249 return 0;
250 }
251
252 static __DRIbuffer *
253 dri2_drm_get_buffers_with_format(__DRIdrawable *driDrawable,
254 int *width, int *height,
255 unsigned int *attachments, int count,
256 int *out_count, void *loaderPrivate)
257 {
258 struct dri2_egl_surface *dri2_surf = loaderPrivate;
259 int i, j;
260
261 dri2_surf->buffer_count = 0;
262 for (i = 0, j = 0; i < 2 * count; i += 2, j++) {
263 assert(attachments[i] < __DRI_BUFFER_COUNT);
264 assert(dri2_surf->buffer_count < 5);
265
266 switch (attachments[i]) {
267 case __DRI_BUFFER_BACK_LEFT:
268 if (get_back_bo(dri2_surf) < 0) {
269 _eglError(EGL_BAD_ALLOC, "failed to allocate color buffer");
270 return NULL;
271 }
272 back_bo_to_dri_buffer(dri2_surf, &dri2_surf->buffers[j]);
273 break;
274 default:
275 if (get_aux_bo(dri2_surf, attachments[i], attachments[i + 1],
276 &dri2_surf->buffers[j]) < 0) {
277 _eglError(EGL_BAD_ALLOC, "failed to allocate aux buffer");
278 return NULL;
279 }
280 break;
281 }
282 }
283
284 *out_count = j;
285 if (j == 0)
286 return NULL;
287
288 *width = dri2_surf->base.Width;
289 *height = dri2_surf->base.Height;
290
291 return dri2_surf->buffers;
292 }
293
294 static __DRIbuffer *
295 dri2_drm_get_buffers(__DRIdrawable * driDrawable,
296 int *width, int *height,
297 unsigned int *attachments, int count,
298 int *out_count, void *loaderPrivate)
299 {
300 unsigned int *attachments_with_format;
301 __DRIbuffer *buffer;
302 const unsigned int format = 32;
303 int i;
304
305 attachments_with_format = calloc(count * 2, sizeof(unsigned int));
306 if (!attachments_with_format) {
307 *out_count = 0;
308 return NULL;
309 }
310
311 for (i = 0; i < count; ++i) {
312 attachments_with_format[2*i] = attachments[i];
313 attachments_with_format[2*i + 1] = format;
314 }
315
316 buffer =
317 dri2_drm_get_buffers_with_format(driDrawable,
318 width, height,
319 attachments_with_format, count,
320 out_count, loaderPrivate);
321
322 free(attachments_with_format);
323
324 return buffer;
325 }
326
327 static int
328 dri2_drm_image_get_buffers(__DRIdrawable *driDrawable,
329 unsigned int format,
330 uint32_t *stamp,
331 void *loaderPrivate,
332 uint32_t buffer_mask,
333 struct __DRIimageList *buffers)
334 {
335 struct dri2_egl_surface *dri2_surf = loaderPrivate;
336 struct gbm_dri_bo *bo;
337
338 if (get_back_bo(dri2_surf) < 0)
339 return 0;
340
341 bo = (struct gbm_dri_bo *) dri2_surf->back->bo;
342 buffers->image_mask = __DRI_IMAGE_BUFFER_BACK;
343 buffers->back = bo->image;
344
345 return 1;
346 }
347
348 static void
349 dri2_drm_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
350 {
351 (void) driDrawable;
352 (void) loaderPrivate;
353 }
354
355 static EGLBoolean
356 dri2_drm_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
357 {
358 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
359 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
360 int i;
361
362 if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
363 if (dri2_surf->current)
364 _eglError(EGL_BAD_SURFACE, "dri2_swap_buffers");
365 for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
366 if (dri2_surf->color_buffers[i].age > 0)
367 dri2_surf->color_buffers[i].age++;
368 dri2_surf->current = dri2_surf->back;
369 dri2_surf->current->age = 1;
370 dri2_surf->back = NULL;
371 }
372
373 (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable);
374 (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable);
375
376 return EGL_TRUE;
377 }
378
379 static EGLint
380 dri2_drm_query_buffer_age(_EGLDriver *drv,
381 _EGLDisplay *disp, _EGLSurface *surface)
382 {
383 struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface);
384
385 if (get_back_bo(dri2_surf) < 0) {
386 _eglError(EGL_BAD_ALLOC, "dri2_query_buffer_age");
387 return 0;
388 }
389
390 return dri2_surf->back->age;
391 }
392
393 static _EGLImage *
394 dri2_drm_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
395 EGLClientBuffer buffer, const EGLint *attr_list)
396 {
397 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
398 struct gbm_dri_bo *dri_bo = gbm_dri_bo((struct gbm_bo *) buffer);
399 struct dri2_egl_image *dri2_img;
400
401 dri2_img = malloc(sizeof *dri2_img);
402 if (!dri2_img) {
403 _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr_pixmap");
404 return NULL;
405 }
406
407 if (!_eglInitImage(&dri2_img->base, disp)) {
408 free(dri2_img);
409 return NULL;
410 }
411
412 dri2_img->dri_image = dri2_dpy->image->dupImage(dri_bo->image, dri2_img);
413 if (dri2_img->dri_image == NULL) {
414 free(dri2_img);
415 _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr_pixmap");
416 return NULL;
417 }
418
419 return &dri2_img->base;
420 }
421
422 static _EGLImage *
423 dri2_drm_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp,
424 _EGLContext *ctx, EGLenum target,
425 EGLClientBuffer buffer, const EGLint *attr_list)
426 {
427 (void) drv;
428
429 switch (target) {
430 case EGL_NATIVE_PIXMAP_KHR:
431 return dri2_drm_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
432 default:
433 return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list);
434 }
435 }
436
437 static int
438 dri2_drm_authenticate(_EGLDisplay *disp, uint32_t id)
439 {
440 struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
441
442 return drmAuthMagic(dri2_dpy->fd, id);
443 }
444
445 static struct dri2_egl_display_vtbl dri2_drm_display_vtbl = {
446 .authenticate = dri2_drm_authenticate,
447 .create_window_surface = dri2_drm_create_window_surface,
448 .create_pixmap_surface = dri2_fallback_create_pixmap_surface,
449 .create_pbuffer_surface = dri2_fallback_create_pbuffer_surface,
450 .destroy_surface = dri2_drm_destroy_surface,
451 .create_image = dri2_drm_create_image_khr,
452 .swap_interval = dri2_fallback_swap_interval,
453 .swap_buffers = dri2_drm_swap_buffers,
454 .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,
455 .swap_buffers_region = dri2_fallback_swap_buffers_region,
456 .post_sub_buffer = dri2_fallback_post_sub_buffer,
457 .copy_buffers = dri2_fallback_copy_buffers,
458 .query_buffer_age = dri2_drm_query_buffer_age,
459 .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image,
460 };
461
462 EGLBoolean
463 dri2_initialize_drm(_EGLDriver *drv, _EGLDisplay *disp)
464 {
465 struct dri2_egl_display *dri2_dpy;
466 struct gbm_device *gbm;
467 int fd = -1;
468 int i;
469
470 loader_set_logger(_eglLog);
471
472 dri2_dpy = calloc(1, sizeof *dri2_dpy);
473 if (!dri2_dpy)
474 return _eglError(EGL_BAD_ALLOC, "eglInitialize");
475
476 disp->DriverData = (void *) dri2_dpy;
477
478 gbm = disp->PlatformDisplay;
479 if (gbm == NULL) {
480 fd = open("/dev/dri/card0", O_RDWR);
481 dri2_dpy->own_device = 1;
482 gbm = gbm_create_device(fd);
483 if (gbm == NULL)
484 return EGL_FALSE;
485 }
486
487 if (strcmp(gbm_device_get_backend_name(gbm), "drm") != 0) {
488 free(dri2_dpy);
489 return EGL_FALSE;
490 }
491
492 dri2_dpy->gbm_dri = gbm_dri_device(gbm);
493 if (dri2_dpy->gbm_dri->base.type != GBM_DRM_DRIVER_TYPE_DRI) {
494 free(dri2_dpy);
495 return EGL_FALSE;
496 }
497
498 if (fd < 0) {
499 fd = dup(gbm_device_get_fd(gbm));
500 if (fd < 0) {
501 free(dri2_dpy);
502 return EGL_FALSE;
503 }
504 }
505
506 dri2_dpy->fd = fd;
507 dri2_dpy->device_name = loader_get_device_name_for_fd(dri2_dpy->fd);
508 dri2_dpy->driver_name = dri2_dpy->gbm_dri->base.driver_name;
509
510 dri2_dpy->dri_screen = dri2_dpy->gbm_dri->screen;
511 dri2_dpy->core = dri2_dpy->gbm_dri->core;
512 dri2_dpy->dri2 = dri2_dpy->gbm_dri->dri2;
513 dri2_dpy->image = dri2_dpy->gbm_dri->image;
514 dri2_dpy->flush = dri2_dpy->gbm_dri->flush;
515 dri2_dpy->driver_configs = dri2_dpy->gbm_dri->driver_configs;
516
517 dri2_dpy->gbm_dri->lookup_image = dri2_lookup_egl_image;
518 dri2_dpy->gbm_dri->lookup_user_data = disp;
519
520 dri2_dpy->gbm_dri->get_buffers = dri2_drm_get_buffers;
521 dri2_dpy->gbm_dri->flush_front_buffer = dri2_drm_flush_front_buffer;
522 dri2_dpy->gbm_dri->get_buffers_with_format = dri2_drm_get_buffers_with_format;
523 dri2_dpy->gbm_dri->image_get_buffers = dri2_drm_image_get_buffers;
524
525 dri2_dpy->gbm_dri->base.base.surface_lock_front_buffer = lock_front_buffer;
526 dri2_dpy->gbm_dri->base.base.surface_release_buffer = release_buffer;
527 dri2_dpy->gbm_dri->base.base.surface_has_free_buffers = has_free_buffers;
528
529 dri2_setup_screen(disp);
530
531 for (i = 0; dri2_dpy->driver_configs[i]; i++) {
532 EGLint format, attr_list[3];
533 unsigned int mask;
534
535 dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
536 __DRI_ATTRIB_RED_MASK, &mask);
537 if (mask == 0x3ff00000)
538 format = GBM_FORMAT_XRGB2101010;
539 else if (mask == 0x00ff0000)
540 format = GBM_FORMAT_XRGB8888;
541 else if (mask == 0xf800)
542 format = GBM_FORMAT_RGB565;
543 else
544 continue;
545
546 attr_list[0] = EGL_NATIVE_VISUAL_ID;
547 attr_list[1] = format;
548 attr_list[2] = EGL_NONE;
549
550 dri2_add_config(disp, dri2_dpy->driver_configs[i],
551 i + 1, EGL_WINDOW_BIT, attr_list, NULL);
552 }
553
554 disp->Extensions.EXT_buffer_age = EGL_TRUE;
555
556 #ifdef HAVE_WAYLAND_PLATFORM
557 disp->Extensions.WL_bind_wayland_display = EGL_TRUE;
558 #endif
559
560 /* we're supporting EGL 1.4 */
561 disp->VersionMajor = 1;
562 disp->VersionMinor = 4;
563
564 /* Fill vtbl last to prevent accidentally calling virtual function during
565 * initialization.
566 */
567 dri2_dpy->vtbl = &dri2_drm_display_vtbl;
568
569 return EGL_TRUE;
570 }