2 * Mesa 3-D graphics library
5 * Copyright (C) 2010 LunarG Inc.
6 * Copyright (C) 2011 VMware Inc. All rights reserved.
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
27 * Chia-I Wu <olv@lunarg.com>
28 * Thomas Hellstrom <thellstrom@vmware.com>
31 #include "util/u_memory.h"
32 #include "util/u_inlines.h"
35 #include "native_drm.h"
38 drm_surface_validate(struct native_surface
*nsurf
, uint attachment_mask
,
39 unsigned int *seq_num
, struct pipe_resource
**textures
,
40 int *width
, int *height
)
42 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
44 if (!resource_surface_add_resources(drmsurf
->rsurf
, attachment_mask
))
47 resource_surface_get_resources(drmsurf
->rsurf
, textures
, attachment_mask
);
50 *seq_num
= drmsurf
->sequence_number
;
52 *width
= drmsurf
->width
;
54 *height
= drmsurf
->height
;
60 * Add textures as DRM framebuffers.
63 drm_surface_init_framebuffers(struct native_surface
*nsurf
, boolean need_back
)
65 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
66 struct drm_display
*drmdpy
= drmsurf
->drmdpy
;
67 int num_framebuffers
= (need_back
) ? 2 : 1;
70 for (i
= 0; i
< num_framebuffers
; i
++) {
71 struct drm_framebuffer
*fb
;
72 enum native_attachment natt
;
73 struct winsys_handle whandle
;
77 fb
= &drmsurf
->front_fb
;
78 natt
= NATIVE_ATTACHMENT_FRONT_LEFT
;
81 fb
= &drmsurf
->back_fb
;
82 natt
= NATIVE_ATTACHMENT_BACK_LEFT
;
86 /* make sure the texture has been allocated */
87 resource_surface_add_resources(drmsurf
->rsurf
, 1 << natt
);
89 resource_surface_get_single_resource(drmsurf
->rsurf
, natt
);
94 /* already initialized */
98 /* TODO detect the real value */
99 fb
->is_passive
= TRUE
;
101 memset(&whandle
, 0, sizeof(whandle
));
102 whandle
.type
= DRM_API_HANDLE_TYPE_KMS
;
104 if (!drmdpy
->base
.screen
->resource_get_handle(drmdpy
->base
.screen
,
105 fb
->texture
, &whandle
))
108 block_bits
= util_format_get_blocksizebits(drmsurf
->color_format
);
109 err
= drmModeAddFB(drmdpy
->fd
, drmsurf
->width
, drmsurf
->height
,
110 block_bits
, block_bits
, whandle
.stride
, whandle
.handle
,
122 drm_surface_flush_frontbuffer(struct native_surface
*nsurf
)
124 #ifdef DRM_MODE_FEATURE_DIRTYFB
125 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
126 struct drm_display
*drmdpy
= drmsurf
->drmdpy
;
128 if (drmsurf
->front_fb
.is_passive
)
129 drmModeDirtyFB(drmdpy
->fd
, drmsurf
->front_fb
.buffer_id
, NULL
, 0);
136 drm_surface_copy_swap(struct native_surface
*nsurf
)
138 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
139 struct drm_display
*drmdpy
= drmsurf
->drmdpy
;
141 (void) resource_surface_throttle(drmsurf
->rsurf
);
142 if (!resource_surface_copy_swap(drmsurf
->rsurf
, &drmdpy
->base
))
145 (void) resource_surface_flush(drmsurf
->rsurf
, &drmdpy
->base
);
146 if (!drm_surface_flush_frontbuffer(nsurf
))
149 drmsurf
->sequence_number
++;
155 drm_surface_swap_buffers(struct native_surface
*nsurf
)
157 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
158 struct drm_crtc
*drmcrtc
= &drmsurf
->current_crtc
;
159 struct drm_display
*drmdpy
= drmsurf
->drmdpy
;
160 struct drm_framebuffer tmp_fb
;
163 if (!drmsurf
->have_pageflip
)
164 return drm_surface_copy_swap(nsurf
);
166 if (!drmsurf
->back_fb
.buffer_id
) {
167 if (!drm_surface_init_framebuffers(&drmsurf
->base
, TRUE
))
171 if (drmsurf
->is_shown
&& drmcrtc
->crtc
) {
172 err
= drmModePageFlip(drmdpy
->fd
, drmcrtc
->crtc
->crtc_id
,
173 drmsurf
->back_fb
.buffer_id
, 0, NULL
);
175 drmsurf
->have_pageflip
= FALSE
;
176 return drm_surface_copy_swap(nsurf
);
180 /* swap the buffers */
181 tmp_fb
= drmsurf
->front_fb
;
182 drmsurf
->front_fb
= drmsurf
->back_fb
;
183 drmsurf
->back_fb
= tmp_fb
;
185 resource_surface_swap_buffers(drmsurf
->rsurf
,
186 NATIVE_ATTACHMENT_FRONT_LEFT
, NATIVE_ATTACHMENT_BACK_LEFT
, FALSE
);
187 /* the front/back textures are swapped */
188 drmsurf
->sequence_number
++;
189 drmdpy
->event_handler
->invalid_surface(&drmdpy
->base
,
190 &drmsurf
->base
, drmsurf
->sequence_number
);
196 drm_surface_present(struct native_surface
*nsurf
,
197 const struct native_present_control
*ctrl
)
201 if (ctrl
->swap_interval
)
204 switch (ctrl
->natt
) {
205 case NATIVE_ATTACHMENT_FRONT_LEFT
:
206 ret
= drm_surface_flush_frontbuffer(nsurf
);
208 case NATIVE_ATTACHMENT_BACK_LEFT
:
210 ret
= drm_surface_copy_swap(nsurf
);
212 ret
= drm_surface_swap_buffers(nsurf
);
223 drm_surface_wait(struct native_surface
*nsurf
)
225 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
227 resource_surface_wait(drmsurf
->rsurf
);
231 drm_surface_destroy(struct native_surface
*nsurf
)
233 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
235 resource_surface_wait(drmsurf
->rsurf
);
236 if (drmsurf
->current_crtc
.crtc
)
237 drmModeFreeCrtc(drmsurf
->current_crtc
.crtc
);
239 if (drmsurf
->front_fb
.buffer_id
)
240 drmModeRmFB(drmsurf
->drmdpy
->fd
, drmsurf
->front_fb
.buffer_id
);
241 pipe_resource_reference(&drmsurf
->front_fb
.texture
, NULL
);
243 if (drmsurf
->back_fb
.buffer_id
)
244 drmModeRmFB(drmsurf
->drmdpy
->fd
, drmsurf
->back_fb
.buffer_id
);
245 pipe_resource_reference(&drmsurf
->back_fb
.texture
, NULL
);
247 resource_surface_destroy(drmsurf
->rsurf
);
251 static struct drm_surface
*
252 drm_display_create_surface(struct native_display
*ndpy
,
253 const struct native_config
*nconf
,
254 uint width
, uint height
)
256 struct drm_display
*drmdpy
= drm_display(ndpy
);
257 struct drm_config
*drmconf
= drm_config(nconf
);
258 struct drm_surface
*drmsurf
;
260 drmsurf
= CALLOC_STRUCT(drm_surface
);
264 drmsurf
->drmdpy
= drmdpy
;
265 drmsurf
->color_format
= drmconf
->base
.color_format
;
266 drmsurf
->width
= width
;
267 drmsurf
->height
= height
;
268 drmsurf
->have_pageflip
= TRUE
;
270 drmsurf
->rsurf
= resource_surface_create(drmdpy
->base
.screen
,
271 drmsurf
->color_format
,
272 PIPE_BIND_RENDER_TARGET
|
273 PIPE_BIND_SAMPLER_VIEW
|
274 PIPE_BIND_DISPLAY_TARGET
|
276 if (!drmsurf
->rsurf
) {
281 resource_surface_set_size(drmsurf
->rsurf
, drmsurf
->width
, drmsurf
->height
);
283 drmsurf
->base
.destroy
= drm_surface_destroy
;
284 drmsurf
->base
.present
= drm_surface_present
;
285 drmsurf
->base
.validate
= drm_surface_validate
;
286 drmsurf
->base
.wait
= drm_surface_wait
;
291 struct native_surface
*
292 drm_display_create_surface_from_resource(struct native_display
*ndpy
,
293 struct pipe_resource
*resource
)
295 struct drm_display
*drmdpy
= drm_display(ndpy
);
296 struct drm_surface
*drmsurf
;
297 enum native_attachment natt
= NATIVE_ATTACHMENT_FRONT_LEFT
;
299 drmsurf
= CALLOC_STRUCT(drm_surface
);
303 drmsurf
->drmdpy
= drmdpy
;
304 drmsurf
->color_format
= resource
->format
;
305 drmsurf
->width
= resource
->width0
;
306 drmsurf
->height
= resource
->height0
;
307 drmsurf
->have_pageflip
= FALSE
;
309 drmsurf
->rsurf
= resource_surface_create(drmdpy
->base
.screen
,
310 drmsurf
->color_format
,
311 PIPE_BIND_RENDER_TARGET
|
312 PIPE_BIND_SAMPLER_VIEW
|
313 PIPE_BIND_DISPLAY_TARGET
|
316 resource_surface_import_resource(drmsurf
->rsurf
, natt
, resource
);
318 drmsurf
->base
.destroy
= drm_surface_destroy
;
319 drmsurf
->base
.present
= drm_surface_present
;
320 drmsurf
->base
.validate
= drm_surface_validate
;
321 drmsurf
->base
.wait
= drm_surface_wait
;
323 return &drmsurf
->base
;
328 * Choose a CRTC that supports all given connectors.
331 drm_display_choose_crtc(struct native_display
*ndpy
,
332 uint32_t *connectors
, int num_connectors
)
334 struct drm_display
*drmdpy
= drm_display(ndpy
);
337 for (idx
= 0; idx
< drmdpy
->resources
->count_crtcs
; idx
++) {
338 boolean found_crtc
= TRUE
;
341 for (i
= 0; i
< num_connectors
; i
++) {
342 drmModeConnectorPtr connector
;
343 int encoder_idx
= -1;
345 connector
= drmModeGetConnector(drmdpy
->fd
, connectors
[i
]);
351 /* find an encoder the CRTC supports */
352 for (j
= 0; j
< connector
->count_encoders
; j
++) {
353 drmModeEncoderPtr encoder
=
354 drmModeGetEncoder(drmdpy
->fd
, connector
->encoders
[j
]);
355 if (encoder
->possible_crtcs
& (1 << idx
)) {
359 drmModeFreeEncoder(encoder
);
362 drmModeFreeConnector(connector
);
363 if (encoder_idx
< 0) {
373 if (idx
>= drmdpy
->resources
->count_crtcs
) {
374 _eglLog(_EGL_WARNING
,
375 "failed to find a CRTC that supports the given %d connectors",
380 return drmdpy
->resources
->crtcs
[idx
];
384 * Remember the original CRTC status and set the CRTC
387 drm_display_set_crtc(struct native_display
*ndpy
, int crtc_idx
,
388 uint32_t buffer_id
, uint32_t x
, uint32_t y
,
389 uint32_t *connectors
, int num_connectors
,
390 drmModeModeInfoPtr mode
)
392 struct drm_display
*drmdpy
= drm_display(ndpy
);
393 struct drm_crtc
*drmcrtc
= &drmdpy
->saved_crtcs
[crtc_idx
];
398 crtc_id
= drmcrtc
->crtc
->crtc_id
;
404 * Choose the CRTC once. It could be more dynamic, but let's keep it
407 crtc_id
= drm_display_choose_crtc(&drmdpy
->base
,
408 connectors
, num_connectors
);
410 /* save the original CRTC status */
411 drmcrtc
->crtc
= drmModeGetCrtc(drmdpy
->fd
, crtc_id
);
415 for (i
= 0; i
< drmdpy
->num_connectors
; i
++) {
416 struct drm_connector
*drmconn
= &drmdpy
->connectors
[i
];
417 drmModeConnectorPtr connector
= drmconn
->connector
;
418 drmModeEncoderPtr encoder
;
420 encoder
= drmModeGetEncoder(drmdpy
->fd
, connector
->encoder_id
);
422 if (encoder
->crtc_id
== crtc_id
) {
423 drmcrtc
->connectors
[count
++] = connector
->connector_id
;
424 if (count
>= Elements(drmcrtc
->connectors
))
427 drmModeFreeEncoder(encoder
);
431 drmcrtc
->num_connectors
= count
;
434 err
= drmModeSetCrtc(drmdpy
->fd
, crtc_id
, buffer_id
, x
, y
,
435 connectors
, num_connectors
, mode
);
437 drmModeFreeCrtc(drmcrtc
->crtc
);
438 drmcrtc
->crtc
= NULL
;
439 drmcrtc
->num_connectors
= 0;
448 drm_display_program(struct native_display
*ndpy
, int crtc_idx
,
449 struct native_surface
*nsurf
, uint x
, uint y
,
450 const struct native_connector
**nconns
, int num_nconns
,
451 const struct native_mode
*nmode
)
453 struct drm_display
*drmdpy
= drm_display(ndpy
);
454 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
455 const struct drm_mode
*drmmode
= drm_mode(nmode
);
456 uint32_t connector_ids
[32];
458 drmModeModeInfo mode_tmp
, *mode
;
461 if (num_nconns
> Elements(connector_ids
)) {
462 _eglLog(_EGL_WARNING
, "too many connectors (%d)", num_nconns
);
463 num_nconns
= Elements(connector_ids
);
467 if (!drm_surface_init_framebuffers(&drmsurf
->base
, FALSE
))
470 buffer_id
= drmsurf
->front_fb
.buffer_id
;
471 /* the mode argument of drmModeSetCrtc is not constified */
472 mode_tmp
= drmmode
->mode
;
476 /* disable the CRTC */
482 for (i
= 0; i
< num_nconns
; i
++) {
483 struct drm_connector
*drmconn
= drm_connector(nconns
[i
]);
484 connector_ids
[i
] = drmconn
->connector
->connector_id
;
487 if (!drm_display_set_crtc(&drmdpy
->base
, crtc_idx
, buffer_id
, x
, y
,
488 connector_ids
, num_nconns
, mode
)) {
489 _eglLog(_EGL_WARNING
, "failed to set CRTC %d", crtc_idx
);
494 if (drmdpy
->shown_surfaces
[crtc_idx
])
495 drmdpy
->shown_surfaces
[crtc_idx
]->is_shown
= FALSE
;
496 drmdpy
->shown_surfaces
[crtc_idx
] = drmsurf
;
498 /* remember the settings for buffer swapping */
500 uint32_t crtc_id
= drmdpy
->saved_crtcs
[crtc_idx
].crtc
->crtc_id
;
501 struct drm_crtc
*drmcrtc
= &drmsurf
->current_crtc
;
504 drmModeFreeCrtc(drmcrtc
->crtc
);
505 drmcrtc
->crtc
= drmModeGetCrtc(drmdpy
->fd
, crtc_id
);
507 assert(num_nconns
< Elements(drmcrtc
->connectors
));
508 memcpy(drmcrtc
->connectors
, connector_ids
,
509 sizeof(*connector_ids
) * num_nconns
);
510 drmcrtc
->num_connectors
= num_nconns
;
512 drmsurf
->is_shown
= TRUE
;
518 static const struct native_mode
**
519 drm_display_get_modes(struct native_display
*ndpy
,
520 const struct native_connector
*nconn
,
523 struct drm_display
*drmdpy
= drm_display(ndpy
);
524 struct drm_connector
*drmconn
= drm_connector(nconn
);
525 const struct native_mode
**nmodes_return
;
528 /* delete old data */
529 if (drmconn
->connector
) {
530 drmModeFreeConnector(drmconn
->connector
);
531 FREE(drmconn
->drm_modes
);
533 drmconn
->connector
= NULL
;
534 drmconn
->drm_modes
= NULL
;
535 drmconn
->num_modes
= 0;
539 drmconn
->connector
= drmModeGetConnector(drmdpy
->fd
, drmconn
->connector_id
);
540 if (!drmconn
->connector
)
543 count
= drmconn
->connector
->count_modes
;
544 drmconn
->drm_modes
= CALLOC(count
, sizeof(*drmconn
->drm_modes
));
545 if (!drmconn
->drm_modes
) {
546 drmModeFreeConnector(drmconn
->connector
);
547 drmconn
->connector
= NULL
;
552 for (i
= 0; i
< count
; i
++) {
553 struct drm_mode
*drmmode
= &drmconn
->drm_modes
[i
];
554 drmModeModeInfoPtr mode
= &drmconn
->connector
->modes
[i
];
556 drmmode
->mode
= *mode
;
558 drmmode
->base
.desc
= drmmode
->mode
.name
;
559 drmmode
->base
.width
= drmmode
->mode
.hdisplay
;
560 drmmode
->base
.height
= drmmode
->mode
.vdisplay
;
561 drmmode
->base
.refresh_rate
= drmmode
->mode
.vrefresh
;
562 /* not all kernels have vrefresh = refresh_rate * 1000 */
563 if (drmmode
->base
.refresh_rate
< 1000)
564 drmmode
->base
.refresh_rate
*= 1000;
567 nmodes_return
= MALLOC(count
* sizeof(*nmodes_return
));
569 for (i
= 0; i
< count
; i
++)
570 nmodes_return
[i
] = &drmconn
->drm_modes
[i
].base
;
575 return nmodes_return
;
578 static const struct native_connector
**
579 drm_display_get_connectors(struct native_display
*ndpy
, int *num_connectors
,
582 struct drm_display
*drmdpy
= drm_display(ndpy
);
583 const struct native_connector
**connectors
;
586 if (!drmdpy
->connectors
) {
588 CALLOC(drmdpy
->resources
->count_connectors
, sizeof(*drmdpy
->connectors
));
589 if (!drmdpy
->connectors
)
592 for (i
= 0; i
< drmdpy
->resources
->count_connectors
; i
++) {
593 struct drm_connector
*drmconn
= &drmdpy
->connectors
[i
];
595 drmconn
->connector_id
= drmdpy
->resources
->connectors
[i
];
596 /* drmconn->connector is allocated when the modes are asked */
599 drmdpy
->num_connectors
= drmdpy
->resources
->count_connectors
;
602 connectors
= MALLOC(drmdpy
->num_connectors
* sizeof(*connectors
));
604 for (i
= 0; i
< drmdpy
->num_connectors
; i
++)
605 connectors
[i
] = &drmdpy
->connectors
[i
].base
;
607 *num_connectors
= drmdpy
->num_connectors
;
611 *num_crtc
= drmdpy
->resources
->count_crtcs
;
616 static struct native_surface
*
617 drm_display_create_scanout_surface(struct native_display
*ndpy
,
618 const struct native_config
*nconf
,
619 uint width
, uint height
)
621 struct drm_surface
*drmsurf
;
623 drmsurf
= drm_display_create_surface(ndpy
, nconf
, width
, height
);
624 return &drmsurf
->base
;
627 static struct native_display_modeset drm_display_modeset
= {
628 .get_connectors
= drm_display_get_connectors
,
629 .get_modes
= drm_display_get_modes
,
630 .create_scanout_surface
= drm_display_create_scanout_surface
,
631 .program
= drm_display_program
635 drm_display_fini_modeset(struct native_display
*ndpy
)
637 struct drm_display
*drmdpy
= drm_display(ndpy
);
640 if (drmdpy
->connectors
) {
641 for (i
= 0; i
< drmdpy
->num_connectors
; i
++) {
642 struct drm_connector
*drmconn
= &drmdpy
->connectors
[i
];
643 if (drmconn
->connector
) {
644 drmModeFreeConnector(drmconn
->connector
);
645 FREE(drmconn
->drm_modes
);
648 FREE(drmdpy
->connectors
);
651 FREE(drmdpy
->shown_surfaces
);
652 drmdpy
->shown_surfaces
= NULL
;
654 if (drmdpy
->saved_crtcs
) {
655 for (i
= 0; i
< drmdpy
->resources
->count_crtcs
; i
++) {
656 struct drm_crtc
*drmcrtc
= &drmdpy
->saved_crtcs
[i
];
660 drmModeSetCrtc(drmdpy
->fd
, drmcrtc
->crtc
->crtc_id
,
661 drmcrtc
->crtc
->buffer_id
, drmcrtc
->crtc
->x
, drmcrtc
->crtc
->y
,
662 drmcrtc
->connectors
, drmcrtc
->num_connectors
,
663 &drmcrtc
->crtc
->mode
);
665 drmModeFreeCrtc(drmcrtc
->crtc
);
668 FREE(drmdpy
->saved_crtcs
);
671 if (drmdpy
->resources
) {
672 drmModeFreeResources(drmdpy
->resources
);
673 drmdpy
->resources
= NULL
;
676 drmdpy
->base
.modeset
= NULL
;
680 drm_display_init_modeset(struct native_display
*ndpy
)
682 struct drm_display
*drmdpy
= drm_display(ndpy
);
684 /* resources are fixed, unlike crtc, connector, or encoder */
685 drmdpy
->resources
= drmModeGetResources(drmdpy
->fd
);
686 if (!drmdpy
->resources
) {
687 _eglLog(_EGL_DEBUG
, "Failed to get KMS resources. Disable modeset.");
691 drmdpy
->saved_crtcs
=
692 CALLOC(drmdpy
->resources
->count_crtcs
, sizeof(*drmdpy
->saved_crtcs
));
693 if (!drmdpy
->saved_crtcs
) {
694 drm_display_fini_modeset(&drmdpy
->base
);
698 drmdpy
->shown_surfaces
=
699 CALLOC(drmdpy
->resources
->count_crtcs
, sizeof(*drmdpy
->shown_surfaces
));
700 if (!drmdpy
->shown_surfaces
) {
701 drm_display_fini_modeset(&drmdpy
->base
);
705 drmdpy
->base
.modeset
= &drm_display_modeset
;