d3d1x: Use native_surface::present.
[mesa.git] / src / gallium / state_trackers / egl / drm / modeset.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 7.9
4 *
5 * Copyright (C) 2010 LunarG Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to 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 OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 * Chia-I Wu <olv@lunarg.com>
27 */
28
29 #include "util/u_memory.h"
30 #include "util/u_inlines.h"
31 #include "egllog.h"
32
33 #include "native_drm.h"
34
35 static boolean
36 drm_surface_validate(struct native_surface *nsurf, uint attachment_mask,
37 unsigned int *seq_num, struct pipe_resource **textures,
38 int *width, int *height)
39 {
40 struct drm_surface *drmsurf = drm_surface(nsurf);
41
42 if (!resource_surface_add_resources(drmsurf->rsurf, attachment_mask))
43 return FALSE;
44 if (textures)
45 resource_surface_get_resources(drmsurf->rsurf, textures, attachment_mask);
46
47 if (seq_num)
48 *seq_num = drmsurf->sequence_number;
49 if (width)
50 *width = drmsurf->width;
51 if (height)
52 *height = drmsurf->height;
53
54 return TRUE;
55 }
56
57 /**
58 * Add textures as DRM framebuffers.
59 */
60 static boolean
61 drm_surface_init_framebuffers(struct native_surface *nsurf, boolean need_back)
62 {
63 struct drm_surface *drmsurf = drm_surface(nsurf);
64 struct drm_display *drmdpy = drmsurf->drmdpy;
65 int num_framebuffers = (need_back) ? 2 : 1;
66 int i, err;
67
68 for (i = 0; i < num_framebuffers; i++) {
69 struct drm_framebuffer *fb;
70 enum native_attachment natt;
71 struct winsys_handle whandle;
72 uint block_bits;
73
74 if (i == 0) {
75 fb = &drmsurf->front_fb;
76 natt = NATIVE_ATTACHMENT_FRONT_LEFT;
77 }
78 else {
79 fb = &drmsurf->back_fb;
80 natt = NATIVE_ATTACHMENT_BACK_LEFT;
81 }
82
83 if (!fb->texture) {
84 /* make sure the texture has been allocated */
85 resource_surface_add_resources(drmsurf->rsurf, 1 << natt);
86 fb->texture =
87 resource_surface_get_single_resource(drmsurf->rsurf, natt);
88 if (!fb->texture)
89 return FALSE;
90 }
91
92 /* already initialized */
93 if (fb->buffer_id)
94 continue;
95
96 /* TODO detect the real value */
97 fb->is_passive = TRUE;
98
99 memset(&whandle, 0, sizeof(whandle));
100 whandle.type = DRM_API_HANDLE_TYPE_KMS;
101
102 if (!drmdpy->base.screen->resource_get_handle(drmdpy->base.screen,
103 fb->texture, &whandle))
104 return FALSE;
105
106 block_bits = util_format_get_blocksizebits(drmsurf->color_format);
107 err = drmModeAddFB(drmdpy->fd, drmsurf->width, drmsurf->height,
108 block_bits, block_bits, whandle.stride, whandle.handle,
109 &fb->buffer_id);
110 if (err) {
111 fb->buffer_id = 0;
112 return FALSE;
113 }
114 }
115
116 return TRUE;
117 }
118
119 static boolean
120 drm_surface_flush_frontbuffer(struct native_surface *nsurf)
121 {
122 #ifdef DRM_MODE_FEATURE_DIRTYFB
123 struct drm_surface *drmsurf = drm_surface(nsurf);
124 struct drm_display *drmdpy = drmsurf->drmdpy;
125
126 if (drmsurf->front_fb.is_passive)
127 drmModeDirtyFB(drmdpy->fd, drmsurf->front_fb.buffer_id, NULL, 0);
128 #endif
129
130 return TRUE;
131 }
132
133 static boolean
134 drm_surface_swap_buffers(struct native_surface *nsurf)
135 {
136 struct drm_surface *drmsurf = drm_surface(nsurf);
137 struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
138 struct drm_display *drmdpy = drmsurf->drmdpy;
139 struct drm_framebuffer tmp_fb;
140 int err;
141
142 if (!drmsurf->back_fb.buffer_id) {
143 if (!drm_surface_init_framebuffers(&drmsurf->base, TRUE))
144 return FALSE;
145 }
146
147 if (drmsurf->is_shown && drmcrtc->crtc) {
148 err = drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id,
149 drmsurf->back_fb.buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y,
150 drmcrtc->connectors, drmcrtc->num_connectors, &drmcrtc->crtc->mode);
151 if (err)
152 return FALSE;
153 }
154
155 /* swap the buffers */
156 tmp_fb = drmsurf->front_fb;
157 drmsurf->front_fb = drmsurf->back_fb;
158 drmsurf->back_fb = tmp_fb;
159
160 resource_surface_swap_buffers(drmsurf->rsurf,
161 NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, FALSE);
162 /* the front/back textures are swapped */
163 drmsurf->sequence_number++;
164 drmdpy->event_handler->invalid_surface(&drmdpy->base,
165 &drmsurf->base, drmsurf->sequence_number);
166
167 return TRUE;
168 }
169
170 static boolean
171 drm_surface_present(struct native_surface *nsurf,
172 enum native_attachment natt,
173 boolean preserve,
174 uint swap_interval)
175 {
176 boolean ret;
177
178 if (preserve || swap_interval)
179 return FALSE;
180
181 switch (natt) {
182 case NATIVE_ATTACHMENT_FRONT_LEFT:
183 ret = drm_surface_flush_frontbuffer(nsurf);
184 break;
185 case NATIVE_ATTACHMENT_BACK_LEFT:
186 ret = drm_surface_swap_buffers(nsurf);
187 break;
188 default:
189 ret = FALSE;
190 break;
191 }
192
193 return ret;
194 }
195
196 static void
197 drm_surface_wait(struct native_surface *nsurf)
198 {
199 /* no-op */
200 }
201
202 static void
203 drm_surface_destroy(struct native_surface *nsurf)
204 {
205 struct drm_surface *drmsurf = drm_surface(nsurf);
206
207 if (drmsurf->current_crtc.crtc)
208 drmModeFreeCrtc(drmsurf->current_crtc.crtc);
209
210 if (drmsurf->front_fb.buffer_id)
211 drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->front_fb.buffer_id);
212 pipe_resource_reference(&drmsurf->front_fb.texture, NULL);
213
214 if (drmsurf->back_fb.buffer_id)
215 drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->back_fb.buffer_id);
216 pipe_resource_reference(&drmsurf->back_fb.texture, NULL);
217
218 resource_surface_destroy(drmsurf->rsurf);
219 FREE(drmsurf);
220 }
221
222 static struct drm_surface *
223 drm_display_create_surface(struct native_display *ndpy,
224 const struct native_config *nconf,
225 uint width, uint height)
226 {
227 struct drm_display *drmdpy = drm_display(ndpy);
228 struct drm_config *drmconf = drm_config(nconf);
229 struct drm_surface *drmsurf;
230
231 drmsurf = CALLOC_STRUCT(drm_surface);
232 if (!drmsurf)
233 return NULL;
234
235 drmsurf->drmdpy = drmdpy;
236 drmsurf->color_format = drmconf->base.color_format;
237 drmsurf->width = width;
238 drmsurf->height = height;
239
240 drmsurf->rsurf = resource_surface_create(drmdpy->base.screen,
241 drmsurf->color_format,
242 PIPE_BIND_RENDER_TARGET |
243 PIPE_BIND_SAMPLER_VIEW |
244 PIPE_BIND_DISPLAY_TARGET |
245 PIPE_BIND_SCANOUT);
246 if (!drmsurf->rsurf) {
247 FREE(drmsurf);
248 return NULL;
249 }
250
251 resource_surface_set_size(drmsurf->rsurf, drmsurf->width, drmsurf->height);
252
253 drmsurf->base.destroy = drm_surface_destroy;
254 drmsurf->base.swap_buffers = drm_surface_swap_buffers;
255 drmsurf->base.flush_frontbuffer = drm_surface_flush_frontbuffer;
256 drmsurf->base.present = drm_surface_present;
257 drmsurf->base.validate = drm_surface_validate;
258 drmsurf->base.wait = drm_surface_wait;
259
260 return drmsurf;
261 }
262
263 /**
264 * Choose a CRTC that supports all given connectors.
265 */
266 static uint32_t
267 drm_display_choose_crtc(struct native_display *ndpy,
268 uint32_t *connectors, int num_connectors)
269 {
270 struct drm_display *drmdpy = drm_display(ndpy);
271 int idx;
272
273 for (idx = 0; idx < drmdpy->resources->count_crtcs; idx++) {
274 boolean found_crtc = TRUE;
275 int i, j;
276
277 for (i = 0; i < num_connectors; i++) {
278 drmModeConnectorPtr connector;
279 int encoder_idx = -1;
280
281 connector = drmModeGetConnector(drmdpy->fd, connectors[i]);
282 if (!connector) {
283 found_crtc = FALSE;
284 break;
285 }
286
287 /* find an encoder the CRTC supports */
288 for (j = 0; j < connector->count_encoders; j++) {
289 drmModeEncoderPtr encoder =
290 drmModeGetEncoder(drmdpy->fd, connector->encoders[j]);
291 if (encoder->possible_crtcs & (1 << idx)) {
292 encoder_idx = j;
293 break;
294 }
295 drmModeFreeEncoder(encoder);
296 }
297
298 drmModeFreeConnector(connector);
299 if (encoder_idx < 0) {
300 found_crtc = FALSE;
301 break;
302 }
303 }
304
305 if (found_crtc)
306 break;
307 }
308
309 if (idx >= drmdpy->resources->count_crtcs) {
310 _eglLog(_EGL_WARNING,
311 "failed to find a CRTC that supports the given %d connectors",
312 num_connectors);
313 return 0;
314 }
315
316 return drmdpy->resources->crtcs[idx];
317 }
318
319 /**
320 * Remember the original CRTC status and set the CRTC
321 */
322 static boolean
323 drm_display_set_crtc(struct native_display *ndpy, int crtc_idx,
324 uint32_t buffer_id, uint32_t x, uint32_t y,
325 uint32_t *connectors, int num_connectors,
326 drmModeModeInfoPtr mode)
327 {
328 struct drm_display *drmdpy = drm_display(ndpy);
329 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[crtc_idx];
330 uint32_t crtc_id;
331 int err;
332
333 if (drmcrtc->crtc) {
334 crtc_id = drmcrtc->crtc->crtc_id;
335 }
336 else {
337 int count = 0, i;
338
339 /*
340 * Choose the CRTC once. It could be more dynamic, but let's keep it
341 * simple for now.
342 */
343 crtc_id = drm_display_choose_crtc(&drmdpy->base,
344 connectors, num_connectors);
345
346 /* save the original CRTC status */
347 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
348 if (!drmcrtc->crtc)
349 return FALSE;
350
351 for (i = 0; i < drmdpy->num_connectors; i++) {
352 struct drm_connector *drmconn = &drmdpy->connectors[i];
353 drmModeConnectorPtr connector = drmconn->connector;
354 drmModeEncoderPtr encoder;
355
356 encoder = drmModeGetEncoder(drmdpy->fd, connector->encoder_id);
357 if (encoder) {
358 if (encoder->crtc_id == crtc_id) {
359 drmcrtc->connectors[count++] = connector->connector_id;
360 if (count >= Elements(drmcrtc->connectors))
361 break;
362 }
363 drmModeFreeEncoder(encoder);
364 }
365 }
366
367 drmcrtc->num_connectors = count;
368 }
369
370 err = drmModeSetCrtc(drmdpy->fd, crtc_id, buffer_id, x, y,
371 connectors, num_connectors, mode);
372 if (err) {
373 drmModeFreeCrtc(drmcrtc->crtc);
374 drmcrtc->crtc = NULL;
375 drmcrtc->num_connectors = 0;
376
377 return FALSE;
378 }
379
380 return TRUE;
381 }
382
383 static boolean
384 drm_display_program(struct native_display *ndpy, int crtc_idx,
385 struct native_surface *nsurf, uint x, uint y,
386 const struct native_connector **nconns, int num_nconns,
387 const struct native_mode *nmode)
388 {
389 struct drm_display *drmdpy = drm_display(ndpy);
390 struct drm_surface *drmsurf = drm_surface(nsurf);
391 const struct drm_mode *drmmode = drm_mode(nmode);
392 uint32_t connector_ids[32];
393 uint32_t buffer_id;
394 drmModeModeInfo mode_tmp, *mode;
395 int i;
396
397 if (num_nconns > Elements(connector_ids)) {
398 _eglLog(_EGL_WARNING, "too many connectors (%d)", num_nconns);
399 num_nconns = Elements(connector_ids);
400 }
401
402 if (drmsurf) {
403 if (!drm_surface_init_framebuffers(&drmsurf->base, FALSE))
404 return FALSE;
405
406 buffer_id = drmsurf->front_fb.buffer_id;
407 /* the mode argument of drmModeSetCrtc is not constified */
408 mode_tmp = drmmode->mode;
409 mode = &mode_tmp;
410 }
411 else {
412 /* disable the CRTC */
413 buffer_id = 0;
414 mode = NULL;
415 num_nconns = 0;
416 }
417
418 for (i = 0; i < num_nconns; i++) {
419 struct drm_connector *drmconn = drm_connector(nconns[i]);
420 connector_ids[i] = drmconn->connector->connector_id;
421 }
422
423 if (!drm_display_set_crtc(&drmdpy->base, crtc_idx, buffer_id, x, y,
424 connector_ids, num_nconns, mode)) {
425 _eglLog(_EGL_WARNING, "failed to set CRTC %d", crtc_idx);
426
427 return FALSE;
428 }
429
430 if (drmdpy->shown_surfaces[crtc_idx])
431 drmdpy->shown_surfaces[crtc_idx]->is_shown = FALSE;
432 drmdpy->shown_surfaces[crtc_idx] = drmsurf;
433
434 /* remember the settings for buffer swapping */
435 if (drmsurf) {
436 uint32_t crtc_id = drmdpy->saved_crtcs[crtc_idx].crtc->crtc_id;
437 struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
438
439 if (drmcrtc->crtc)
440 drmModeFreeCrtc(drmcrtc->crtc);
441 drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
442
443 assert(num_nconns < Elements(drmcrtc->connectors));
444 memcpy(drmcrtc->connectors, connector_ids,
445 sizeof(*connector_ids) * num_nconns);
446 drmcrtc->num_connectors = num_nconns;
447
448 drmsurf->is_shown = TRUE;
449 }
450
451 return TRUE;
452 }
453
454 static const struct native_mode **
455 drm_display_get_modes(struct native_display *ndpy,
456 const struct native_connector *nconn,
457 int *num_modes)
458 {
459 struct drm_display *drmdpy = drm_display(ndpy);
460 struct drm_connector *drmconn = drm_connector(nconn);
461 const struct native_mode **nmodes_return;
462 int count, i;
463
464 /* delete old data */
465 if (drmconn->connector) {
466 drmModeFreeConnector(drmconn->connector);
467 FREE(drmconn->drm_modes);
468
469 drmconn->connector = NULL;
470 drmconn->drm_modes = NULL;
471 drmconn->num_modes = 0;
472 }
473
474 /* detect again */
475 drmconn->connector = drmModeGetConnector(drmdpy->fd, drmconn->connector_id);
476 if (!drmconn->connector)
477 return NULL;
478
479 count = drmconn->connector->count_modes;
480 drmconn->drm_modes = CALLOC(count, sizeof(*drmconn->drm_modes));
481 if (!drmconn->drm_modes) {
482 drmModeFreeConnector(drmconn->connector);
483 drmconn->connector = NULL;
484
485 return NULL;
486 }
487
488 for (i = 0; i < count; i++) {
489 struct drm_mode *drmmode = &drmconn->drm_modes[i];
490 drmModeModeInfoPtr mode = &drmconn->connector->modes[i];
491
492 drmmode->mode = *mode;
493
494 drmmode->base.desc = drmmode->mode.name;
495 drmmode->base.width = drmmode->mode.hdisplay;
496 drmmode->base.height = drmmode->mode.vdisplay;
497 drmmode->base.refresh_rate = drmmode->mode.vrefresh;
498 /* not all kernels have vrefresh = refresh_rate * 1000 */
499 if (drmmode->base.refresh_rate < 1000)
500 drmmode->base.refresh_rate *= 1000;
501 }
502
503 nmodes_return = MALLOC(count * sizeof(*nmodes_return));
504 if (nmodes_return) {
505 for (i = 0; i < count; i++)
506 nmodes_return[i] = &drmconn->drm_modes[i].base;
507 if (num_modes)
508 *num_modes = count;
509 }
510
511 return nmodes_return;
512 }
513
514 static const struct native_connector **
515 drm_display_get_connectors(struct native_display *ndpy, int *num_connectors,
516 int *num_crtc)
517 {
518 struct drm_display *drmdpy = drm_display(ndpy);
519 const struct native_connector **connectors;
520 int i;
521
522 if (!drmdpy->connectors) {
523 drmdpy->connectors =
524 CALLOC(drmdpy->resources->count_connectors, sizeof(*drmdpy->connectors));
525 if (!drmdpy->connectors)
526 return NULL;
527
528 for (i = 0; i < drmdpy->resources->count_connectors; i++) {
529 struct drm_connector *drmconn = &drmdpy->connectors[i];
530
531 drmconn->connector_id = drmdpy->resources->connectors[i];
532 /* drmconn->connector is allocated when the modes are asked */
533 }
534
535 drmdpy->num_connectors = drmdpy->resources->count_connectors;
536 }
537
538 connectors = MALLOC(drmdpy->num_connectors * sizeof(*connectors));
539 if (connectors) {
540 for (i = 0; i < drmdpy->num_connectors; i++)
541 connectors[i] = &drmdpy->connectors[i].base;
542 if (num_connectors)
543 *num_connectors = drmdpy->num_connectors;
544 }
545
546 if (num_crtc)
547 *num_crtc = drmdpy->resources->count_crtcs;
548
549 return connectors;
550 }
551
552 static struct native_surface *
553 drm_display_create_scanout_surface(struct native_display *ndpy,
554 const struct native_config *nconf,
555 uint width, uint height)
556 {
557 struct drm_surface *drmsurf;
558
559 drmsurf = drm_display_create_surface(ndpy, nconf, width, height);
560 return &drmsurf->base;
561 }
562
563 static struct native_display_modeset drm_display_modeset = {
564 .get_connectors = drm_display_get_connectors,
565 .get_modes = drm_display_get_modes,
566 .create_scanout_surface = drm_display_create_scanout_surface,
567 .program = drm_display_program
568 };
569
570 void
571 drm_display_fini_modeset(struct native_display *ndpy)
572 {
573 struct drm_display *drmdpy = drm_display(ndpy);
574 int i;
575
576 if (drmdpy->connectors) {
577 for (i = 0; i < drmdpy->num_connectors; i++) {
578 struct drm_connector *drmconn = &drmdpy->connectors[i];
579 if (drmconn->connector) {
580 drmModeFreeConnector(drmconn->connector);
581 FREE(drmconn->drm_modes);
582 }
583 }
584 FREE(drmdpy->connectors);
585 }
586
587 if (drmdpy->shown_surfaces) {
588 FREE(drmdpy->shown_surfaces);
589 drmdpy->shown_surfaces = NULL;
590 }
591
592 if (drmdpy->saved_crtcs) {
593 for (i = 0; i < drmdpy->resources->count_crtcs; i++) {
594 struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[i];
595
596 if (drmcrtc->crtc) {
597 /* restore crtc */
598 drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id,
599 drmcrtc->crtc->buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y,
600 drmcrtc->connectors, drmcrtc->num_connectors,
601 &drmcrtc->crtc->mode);
602
603 drmModeFreeCrtc(drmcrtc->crtc);
604 }
605 }
606 FREE(drmdpy->saved_crtcs);
607 }
608
609 if (drmdpy->resources) {
610 drmModeFreeResources(drmdpy->resources);
611 drmdpy->resources = NULL;
612 }
613
614 drmdpy->base.modeset = NULL;
615 }
616
617 boolean
618 drm_display_init_modeset(struct native_display *ndpy)
619 {
620 struct drm_display *drmdpy = drm_display(ndpy);
621
622 /* resources are fixed, unlike crtc, connector, or encoder */
623 drmdpy->resources = drmModeGetResources(drmdpy->fd);
624 if (!drmdpy->resources) {
625 _eglLog(_EGL_DEBUG, "Failed to get KMS resources. Disable modeset.");
626 return FALSE;
627 }
628
629 drmdpy->saved_crtcs =
630 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->saved_crtcs));
631 if (!drmdpy->saved_crtcs) {
632 drm_display_fini_modeset(&drmdpy->base);
633 return FALSE;
634 }
635
636 drmdpy->shown_surfaces =
637 CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->shown_surfaces));
638 if (!drmdpy->shown_surfaces) {
639 drm_display_fini_modeset(&drmdpy->base);
640 return FALSE;
641 }
642
643 drmdpy->base.modeset = &drm_display_modeset;
644
645 return TRUE;
646 }