2 * Mesa 3-D graphics library
5 * Copyright (C) 2010 LunarG Inc.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
26 * Chia-I Wu <olv@lunarg.com>
29 #include "util/u_memory.h"
30 #include "util/u_inlines.h"
33 #include "native_drm.h"
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
)
40 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
42 if (!resource_surface_add_resources(drmsurf
->rsurf
, attachment_mask
))
45 resource_surface_get_resources(drmsurf
->rsurf
, textures
, attachment_mask
);
48 *seq_num
= drmsurf
->sequence_number
;
50 *width
= drmsurf
->width
;
52 *height
= drmsurf
->height
;
58 * Add textures as DRM framebuffers.
61 drm_surface_init_framebuffers(struct native_surface
*nsurf
, boolean need_back
)
63 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
64 struct drm_display
*drmdpy
= drmsurf
->drmdpy
;
65 int num_framebuffers
= (need_back
) ? 2 : 1;
68 for (i
= 0; i
< num_framebuffers
; i
++) {
69 struct drm_framebuffer
*fb
;
70 enum native_attachment natt
;
71 struct winsys_handle whandle
;
75 fb
= &drmsurf
->front_fb
;
76 natt
= NATIVE_ATTACHMENT_FRONT_LEFT
;
79 fb
= &drmsurf
->back_fb
;
80 natt
= NATIVE_ATTACHMENT_BACK_LEFT
;
84 /* make sure the texture has been allocated */
85 resource_surface_add_resources(drmsurf
->rsurf
, 1 << natt
);
87 resource_surface_get_single_resource(drmsurf
->rsurf
, natt
);
92 /* already initialized */
96 /* TODO detect the real value */
97 fb
->is_passive
= TRUE
;
99 memset(&whandle
, 0, sizeof(whandle
));
100 whandle
.type
= DRM_API_HANDLE_TYPE_KMS
;
102 if (!drmdpy
->base
.screen
->resource_get_handle(drmdpy
->base
.screen
,
103 fb
->texture
, &whandle
))
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
,
120 drm_surface_flush_frontbuffer(struct native_surface
*nsurf
)
122 #ifdef DRM_MODE_FEATURE_DIRTYFB
123 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
124 struct drm_display
*drmdpy
= drmsurf
->drmdpy
;
126 if (drmsurf
->front_fb
.is_passive
)
127 drmModeDirtyFB(drmdpy
->fd
, drmsurf
->front_fb
.buffer_id
, NULL
, 0);
134 drm_surface_swap_buffers(struct native_surface
*nsurf
)
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
;
142 if (!drmsurf
->back_fb
.buffer_id
) {
143 if (!drm_surface_init_framebuffers(&drmsurf
->base
, TRUE
))
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
);
155 /* swap the buffers */
156 tmp_fb
= drmsurf
->front_fb
;
157 drmsurf
->front_fb
= drmsurf
->back_fb
;
158 drmsurf
->back_fb
= tmp_fb
;
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
);
171 drm_surface_present(struct native_surface
*nsurf
,
172 enum native_attachment natt
,
178 if (preserve
|| swap_interval
)
182 case NATIVE_ATTACHMENT_FRONT_LEFT
:
183 ret
= drm_surface_flush_frontbuffer(nsurf
);
185 case NATIVE_ATTACHMENT_BACK_LEFT
:
186 ret
= drm_surface_swap_buffers(nsurf
);
197 drm_surface_wait(struct native_surface
*nsurf
)
203 drm_surface_destroy(struct native_surface
*nsurf
)
205 struct drm_surface
*drmsurf
= drm_surface(nsurf
);
207 if (drmsurf
->current_crtc
.crtc
)
208 drmModeFreeCrtc(drmsurf
->current_crtc
.crtc
);
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
);
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
);
218 resource_surface_destroy(drmsurf
->rsurf
);
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
)
227 struct drm_display
*drmdpy
= drm_display(ndpy
);
228 struct drm_config
*drmconf
= drm_config(nconf
);
229 struct drm_surface
*drmsurf
;
231 drmsurf
= CALLOC_STRUCT(drm_surface
);
235 drmsurf
->drmdpy
= drmdpy
;
236 drmsurf
->color_format
= drmconf
->base
.color_format
;
237 drmsurf
->width
= width
;
238 drmsurf
->height
= height
;
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
|
246 if (!drmsurf
->rsurf
) {
251 resource_surface_set_size(drmsurf
->rsurf
, drmsurf
->width
, drmsurf
->height
);
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
;
264 * Choose a CRTC that supports all given connectors.
267 drm_display_choose_crtc(struct native_display
*ndpy
,
268 uint32_t *connectors
, int num_connectors
)
270 struct drm_display
*drmdpy
= drm_display(ndpy
);
273 for (idx
= 0; idx
< drmdpy
->resources
->count_crtcs
; idx
++) {
274 boolean found_crtc
= TRUE
;
277 for (i
= 0; i
< num_connectors
; i
++) {
278 drmModeConnectorPtr connector
;
279 int encoder_idx
= -1;
281 connector
= drmModeGetConnector(drmdpy
->fd
, connectors
[i
]);
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
)) {
295 drmModeFreeEncoder(encoder
);
298 drmModeFreeConnector(connector
);
299 if (encoder_idx
< 0) {
309 if (idx
>= drmdpy
->resources
->count_crtcs
) {
310 _eglLog(_EGL_WARNING
,
311 "failed to find a CRTC that supports the given %d connectors",
316 return drmdpy
->resources
->crtcs
[idx
];
320 * Remember the original CRTC status and set the CRTC
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
)
328 struct drm_display
*drmdpy
= drm_display(ndpy
);
329 struct drm_crtc
*drmcrtc
= &drmdpy
->saved_crtcs
[crtc_idx
];
334 crtc_id
= drmcrtc
->crtc
->crtc_id
;
340 * Choose the CRTC once. It could be more dynamic, but let's keep it
343 crtc_id
= drm_display_choose_crtc(&drmdpy
->base
,
344 connectors
, num_connectors
);
346 /* save the original CRTC status */
347 drmcrtc
->crtc
= drmModeGetCrtc(drmdpy
->fd
, crtc_id
);
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
;
356 encoder
= drmModeGetEncoder(drmdpy
->fd
, connector
->encoder_id
);
358 if (encoder
->crtc_id
== crtc_id
) {
359 drmcrtc
->connectors
[count
++] = connector
->connector_id
;
360 if (count
>= Elements(drmcrtc
->connectors
))
363 drmModeFreeEncoder(encoder
);
367 drmcrtc
->num_connectors
= count
;
370 err
= drmModeSetCrtc(drmdpy
->fd
, crtc_id
, buffer_id
, x
, y
,
371 connectors
, num_connectors
, mode
);
373 drmModeFreeCrtc(drmcrtc
->crtc
);
374 drmcrtc
->crtc
= NULL
;
375 drmcrtc
->num_connectors
= 0;
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
)
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];
394 drmModeModeInfo mode_tmp
, *mode
;
397 if (num_nconns
> Elements(connector_ids
)) {
398 _eglLog(_EGL_WARNING
, "too many connectors (%d)", num_nconns
);
399 num_nconns
= Elements(connector_ids
);
403 if (!drm_surface_init_framebuffers(&drmsurf
->base
, FALSE
))
406 buffer_id
= drmsurf
->front_fb
.buffer_id
;
407 /* the mode argument of drmModeSetCrtc is not constified */
408 mode_tmp
= drmmode
->mode
;
412 /* disable the CRTC */
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
;
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
);
430 if (drmdpy
->shown_surfaces
[crtc_idx
])
431 drmdpy
->shown_surfaces
[crtc_idx
]->is_shown
= FALSE
;
432 drmdpy
->shown_surfaces
[crtc_idx
] = drmsurf
;
434 /* remember the settings for buffer swapping */
436 uint32_t crtc_id
= drmdpy
->saved_crtcs
[crtc_idx
].crtc
->crtc_id
;
437 struct drm_crtc
*drmcrtc
= &drmsurf
->current_crtc
;
440 drmModeFreeCrtc(drmcrtc
->crtc
);
441 drmcrtc
->crtc
= drmModeGetCrtc(drmdpy
->fd
, crtc_id
);
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
;
448 drmsurf
->is_shown
= TRUE
;
454 static const struct native_mode
**
455 drm_display_get_modes(struct native_display
*ndpy
,
456 const struct native_connector
*nconn
,
459 struct drm_display
*drmdpy
= drm_display(ndpy
);
460 struct drm_connector
*drmconn
= drm_connector(nconn
);
461 const struct native_mode
**nmodes_return
;
464 /* delete old data */
465 if (drmconn
->connector
) {
466 drmModeFreeConnector(drmconn
->connector
);
467 FREE(drmconn
->drm_modes
);
469 drmconn
->connector
= NULL
;
470 drmconn
->drm_modes
= NULL
;
471 drmconn
->num_modes
= 0;
475 drmconn
->connector
= drmModeGetConnector(drmdpy
->fd
, drmconn
->connector_id
);
476 if (!drmconn
->connector
)
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
;
488 for (i
= 0; i
< count
; i
++) {
489 struct drm_mode
*drmmode
= &drmconn
->drm_modes
[i
];
490 drmModeModeInfoPtr mode
= &drmconn
->connector
->modes
[i
];
492 drmmode
->mode
= *mode
;
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;
503 nmodes_return
= MALLOC(count
* sizeof(*nmodes_return
));
505 for (i
= 0; i
< count
; i
++)
506 nmodes_return
[i
] = &drmconn
->drm_modes
[i
].base
;
511 return nmodes_return
;
514 static const struct native_connector
**
515 drm_display_get_connectors(struct native_display
*ndpy
, int *num_connectors
,
518 struct drm_display
*drmdpy
= drm_display(ndpy
);
519 const struct native_connector
**connectors
;
522 if (!drmdpy
->connectors
) {
524 CALLOC(drmdpy
->resources
->count_connectors
, sizeof(*drmdpy
->connectors
));
525 if (!drmdpy
->connectors
)
528 for (i
= 0; i
< drmdpy
->resources
->count_connectors
; i
++) {
529 struct drm_connector
*drmconn
= &drmdpy
->connectors
[i
];
531 drmconn
->connector_id
= drmdpy
->resources
->connectors
[i
];
532 /* drmconn->connector is allocated when the modes are asked */
535 drmdpy
->num_connectors
= drmdpy
->resources
->count_connectors
;
538 connectors
= MALLOC(drmdpy
->num_connectors
* sizeof(*connectors
));
540 for (i
= 0; i
< drmdpy
->num_connectors
; i
++)
541 connectors
[i
] = &drmdpy
->connectors
[i
].base
;
543 *num_connectors
= drmdpy
->num_connectors
;
547 *num_crtc
= drmdpy
->resources
->count_crtcs
;
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
)
557 struct drm_surface
*drmsurf
;
559 drmsurf
= drm_display_create_surface(ndpy
, nconf
, width
, height
);
560 return &drmsurf
->base
;
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
571 drm_display_fini_modeset(struct native_display
*ndpy
)
573 struct drm_display
*drmdpy
= drm_display(ndpy
);
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
);
584 FREE(drmdpy
->connectors
);
587 if (drmdpy
->shown_surfaces
) {
588 FREE(drmdpy
->shown_surfaces
);
589 drmdpy
->shown_surfaces
= NULL
;
592 if (drmdpy
->saved_crtcs
) {
593 for (i
= 0; i
< drmdpy
->resources
->count_crtcs
; i
++) {
594 struct drm_crtc
*drmcrtc
= &drmdpy
->saved_crtcs
[i
];
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
);
603 drmModeFreeCrtc(drmcrtc
->crtc
);
606 FREE(drmdpy
->saved_crtcs
);
609 if (drmdpy
->resources
) {
610 drmModeFreeResources(drmdpy
->resources
);
611 drmdpy
->resources
= NULL
;
614 drmdpy
->base
.modeset
= NULL
;
618 drm_display_init_modeset(struct native_display
*ndpy
)
620 struct drm_display
*drmdpy
= drm_display(ndpy
);
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.");
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
);
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
);
643 drmdpy
->base
.modeset
= &drm_display_modeset
;