/
SDL_kmsdrmvideo.c
1861 lines (1512 loc) · 59.8 KB
1
2
/*
Simple DirectMedia Layer
3
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_KMSDRM
/* SDL internals */
#include "../SDL_sysvideo.h"
#include "SDL_syswm.h"
30
#include "../../events/SDL_events_c.h"
31
32
33
34
35
36
37
38
39
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_keyboard_c.h"
#ifdef SDL_INPUT_LINUXEV
#include "../../core/linux/SDL_evdev.h"
#endif
/* KMS/DRM declarations */
#include "SDL_kmsdrmvideo.h"
40
#include "SDL_kmsdrmevents.h"
41
42
43
#include "SDL_kmsdrmopengles.h"
#include "SDL_kmsdrmmouse.h"
#include "SDL_kmsdrmdyn.h"
44
45
46
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
47
#include <poll.h>
48
49
#define KMSDRM_DRI_PATH "/dev/dri/"
50
51
52
#define AMDGPU_COMPAT 1
53
static int
54
check_modesetting(int devindex)
55
{
56
57
SDL_bool available = SDL_FALSE;
char device[512];
58
unsigned int i;
59
60
61
int drm_fd;
SDL_snprintf(device, sizeof (device), "%scard%d", KMSDRM_DRI_PATH, devindex);
62
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "check_modesetting: probing \"%s\"", device);
63
64
drm_fd = open(device, O_RDWR | O_CLOEXEC);
65
66
67
if (drm_fd >= 0) {
if (SDL_KMSDRM_LoadSymbols()) {
drmModeRes *resources = KMSDRM_drmModeGetResources(drm_fd);
68
if (resources) {
69
70
71
72
73
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%scard%d connector, encoder and CRTC counts are: %d %d %d",
KMSDRM_DRI_PATH, devindex,
resources->count_connectors, resources->count_encoders, resources->count_crtcs);
if (resources->count_connectors > 0 && resources->count_encoders > 0 && resources->count_crtcs > 0) {
74
for (i = 0; i < resources->count_connectors; i++) {
75
76
77
78
79
80
81
82
83
84
85
drmModeConnector *conn = KMSDRM_drmModeGetConnector(drm_fd, resources->connectors[i]);
if (!conn) {
continue;
}
if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
available = SDL_TRUE;
}
KMSDRM_drmModeFreeConnector(conn);
86
87
88
if (available) {
break;
}
89
}
90
}
91
92
93
94
95
96
97
98
99
100
KMSDRM_drmModeFreeResources(resources);
}
SDL_KMSDRM_UnloadSymbols();
}
close(drm_fd);
}
return available;
}
101
static unsigned int get_dricount(void)
102
{
103
unsigned int devcount = 0;
104
105
106
107
struct dirent *res;
struct stat sb;
DIR *folder;
108
109
110
if (!(stat(KMSDRM_DRI_PATH, &sb) == 0 && S_ISDIR(sb.st_mode))) {
SDL_SetError("The path %s cannot be opened or is not available",
KMSDRM_DRI_PATH);
111
112
113
114
return 0;
}
if (access(KMSDRM_DRI_PATH, F_OK) == -1) {
115
116
SDL_SetError("The path %s cannot be opened",
KMSDRM_DRI_PATH);
117
118
119
120
121
122
return 0;
}
folder = opendir(KMSDRM_DRI_PATH);
if (folder) {
while ((res = readdir(folder))) {
123
size_t len = SDL_strlen(res->d_name);
124
if (len > 4 && SDL_strncmp(res->d_name, "card", 4) == 0) {
125
126
127
128
129
130
131
132
133
134
135
136
devcount++;
}
}
closedir(folder);
}
return devcount;
}
static int
get_driindex(void)
{
137
138
const unsigned int devcount = get_dricount();
unsigned int i;
139
140
for (i = 0; i < devcount; i++) {
141
if (check_modesetting(i)) {
142
143
144
145
146
147
148
return i;
}
}
return -ENOENT;
}
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**********************/
/* DUMB BUFFER Block. */
/**********************/
/* Create a dumb buffer, mmap the dumb buffer and fill it with pixels, */
/* then create a KMS framebuffer wrapping the dumb buffer. */
static dumb_buffer *KMSDRM_CreateDumbBuffer(_THIS)
{
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
struct drm_mode_create_dumb create;
struct drm_mode_map_dumb map;
struct drm_mode_destroy_dumb destroy;
164
dumb_buffer *ret = SDL_calloc(1, sizeof(*ret));
165
if (!ret) {
166
167
168
169
SDL_OutOfMemory();
return NULL;
}
170
171
172
173
174
175
176
177
178
179
180
181
/*
* The create ioctl uses the combination of depth and bpp to infer
* a format; 24/32 refers to DRM_FORMAT_XRGB8888 as defined in
* the drm_fourcc.h header. These arguments are the same as given
* to drmModeAddFB, which has since been superseded by
* drmModeAddFB2 as the latter takes an explicit format token.
*
* We only specify these arguments; the driver calculates the
* pitch (also known as stride or row length) and total buffer size
* for us, also returning us the GEM handle.
*/
create = (struct drm_mode_create_dumb) {
182
183
184
.width = dispdata->mode.hdisplay,
.height = dispdata->mode.vdisplay,
.bpp = 32,
185
186
187
};
if (KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)) {
188
189
SDL_SetError("failed to create dumb buffer\n");
goto err;
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
}
ret->gem_handles[0] = create.handle;
ret->format = DRM_FORMAT_XRGB8888;
ret->modifier = DRM_FORMAT_MOD_LINEAR;
ret->width = create.width;
ret->height = create.height;
ret->pitches[0] = create.pitch;
/*
* In order to map the buffer, we call an ioctl specific to the buffer
* type, which returns us a fake offset to use with the mmap syscall.
* mmap itself then works as you expect.
*
* Note this means it is not possible to map arbitrary offsets of
* buffers without specifically requesting it from the kernel.
*/
map = (struct drm_mode_map_dumb) {
.handle = ret->gem_handles[0],
};
210
211
212
213
214
215
216
if (KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map)) {
SDL_SetError("failed to get mmap offset for the dumb buffer.");
goto err_dumb;
}
ret->dumb.mem = mmap(NULL, create.size, PROT_WRITE, MAP_SHARED,
217
218
viddata->drm_fd, map.offset);
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
if (ret->dumb.mem == MAP_FAILED) {
SDL_SetError("failed to get mmap offset for the dumb buffer.");
goto err_dumb;
}
ret->dumb.size = create.size;
return ret;
err_dumb:
destroy = (struct drm_mode_destroy_dumb) { .handle = create.handle };
KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
err:
SDL_free(ret);
return NULL;
}
static void
KMSDRM_DestroyDumbBuffer(_THIS, dumb_buffer *buffer)
{
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
struct drm_mode_destroy_dumb destroy = {
.handle = buffer->gem_handles[0],
};
KMSDRM_drmModeRmFB(viddata->drm_fd, buffer->fb_id);
munmap(buffer->dumb.mem, buffer->dumb.size);
KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
free(buffer);
}
/* Using the CPU mapping, fill the dumb buffer with black pixels. */
static void
KMSDRM_FillDumbBuffer(dumb_buffer *buffer)
{
255
256
unsigned int x, y;
for (y = 0; y < buffer->height; y++) {
257
258
259
260
uint32_t *pix = (uint32_t *) ((uint8_t *) buffer->dumb.mem + (y * buffer->pitches[0]));
for (x = 0; x < buffer->width; x++) {
*pix++ = (0x00000000);
}
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
}
}
static dumb_buffer *KMSDRM_CreateBuffer(_THIS)
{
dumb_buffer *ret;
int err;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
ret = KMSDRM_CreateDumbBuffer(_this);
if (!ret)
return NULL;
/*
* Wrap our GEM buffer in a KMS framebuffer, so we can then attach it
* to a plane. Here's where we get out fb_id!
*/
err = KMSDRM_drmModeAddFB2(viddata->drm_fd, ret->width, ret->height,
ret->format, ret->gem_handles, ret->pitches,
ret->offsets, &ret->fb_id, 0);
if (err != 0 || ret->fb_id == 0) {
SDL_SetError("Failed AddFB2 on dumb buffer\n");
goto err;
}
return ret;
err:
KMSDRM_DestroyDumbBuffer(_this, ret);
return NULL;
}
/***************************/
/* DUMB BUFFER Block ends. */
/***************************/
299
300
301
302
303
304
/*********************************/
/* Atomic helper functions block */
/*********************************/
#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
305
int add_connector_property(drmModeAtomicReq *req, struct connector *connector,
306
const char *name, uint64_t value)
307
{
308
309
unsigned int i;
int prop_id = 0;
310
311
for (i = 0 ; i < connector->props->count_props ; i++) {
312
313
314
315
if (strcmp(connector->props_info[i]->name, name) == 0) {
prop_id = connector->props_info[i]->prop_id;
break;
}
316
}
317
318
if (prop_id < 0) {
319
320
SDL_SetError("no connector property: %s", name);
return -EINVAL;
321
}
322
323
return KMSDRM_drmModeAtomicAddProperty(req, connector->connector->connector_id, prop_id, value);
324
325
}
326
327
int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc,
const char *name, uint64_t value)
328
{
329
330
unsigned int i;
int prop_id = -1;
331
332
for (i = 0 ; i < crtc->props->count_props ; i++) {
333
334
335
336
if (strcmp(crtc->props_info[i]->name, name) == 0) {
prop_id = crtc->props_info[i]->prop_id;
break;
}
337
}
338
339
if (prop_id < 0) {
340
341
SDL_SetError("no crtc property: %s", name);
return -EINVAL;
342
}
343
344
return KMSDRM_drmModeAtomicAddProperty(req, crtc->crtc->crtc_id, prop_id, value);
345
346
}
347
int add_plane_property(drmModeAtomicReq *req, struct plane *plane,
348
const char *name, uint64_t value)
349
{
350
351
unsigned int i;
int prop_id = -1;
352
353
for (i = 0 ; i < plane->props->count_props ; i++) {
354
if (strcmp(plane->props_info[i]->name, name) == 0) {
355
356
prop_id = plane->props_info[i]->prop_id;
break;
357
}
358
}
359
360
if (prop_id < 0) {
361
362
SDL_SetError("no plane property: %s", name);
return -EINVAL;
363
}
364
365
return KMSDRM_drmModeAtomicAddProperty(req, plane->plane->plane_id, prop_id, value);
366
367
}
368
369
#if 0
370
371
372
373
374
375
376
377
378
379
380
381
382
void print_plane_info(_THIS, drmModePlanePtr plane)
{
char *plane_type;
drmModeRes *resources;
uint32_t type = 0;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
plane->plane_id, DRM_MODE_OBJECT_PLANE);
/* Search the plane props for the plane type. */
for (int j = 0; j < props->count_props; j++) {
383
drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[j]);
384
385
386
387
if ((strcmp(p->name, "type") == 0)) {
type = props->prop_values[j];
}
388
389
KMSDRM_drmModeFreeProperty(p);
390
391
392
393
}
switch (type) {
case DRM_PLANE_TYPE_OVERLAY:
394
395
plane_type = "overlay";
break;
396
397
case DRM_PLANE_TYPE_PRIMARY:
398
399
plane_type = "primary";
break;
400
401
case DRM_PLANE_TYPE_CURSOR:
402
403
plane_type = "cursor";
break;
404
405
406
}
407
408
/* Remember that to present a plane on screen, it has to be
connected to a CRTC so the CRTC scans it,
409
410
411
412
413
414
415
416
417
scales it, etc... and presents it on screen. */
/* Now we look for the CRTCs supported by the plane. */
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
if (!resources)
return;
printf("--PLANE ID: %d\nPLANE TYPE: %s\nCRTC READING THIS PLANE: %d\nCRTCS SUPPORTED BY THIS PLANE: ", plane->plane_id, plane_type, plane->crtc_id);
for (int i = 0; i < resources->count_crtcs; i++) {
418
419
if (plane->possible_crtcs & (1 << i)) {
uint32_t crtc_id = resources->crtcs[i];
420
printf ("%d", crtc_id);
421
422
break;
}
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
}
printf ("\n\n");
}
void get_planes_info(_THIS)
{
drmModePlaneResPtr plane_resources;
uint32_t i;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
if (!plane_resources) {
438
439
printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
return;
440
441
442
}
printf("--Number of planes found: %d-- \n", plane_resources->count_planes);
443
printf("--Usable CRTC that we have chosen: %d-- \n", dispdata->crtc->crtc->crtc_id);
444
445
446
447
448
449
/* Iterate on all the available planes. */
for (i = 0; (i < plane_resources->count_planes); i++) {
uint32_t plane_id = plane_resources->planes[i];
450
451
452
453
454
drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
if (!plane) {
printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno));
continue;
}
455
456
457
458
459
460
461
462
463
/* Print plane info. */
print_plane_info(_this, plane);
KMSDRM_drmModeFreePlane(plane);
}
KMSDRM_drmModeFreePlaneResources(plane_resources);
}
464
#endif
465
466
467
/* Get the plane_id of a plane that is of the specified plane type (primary,
overlay, cursor...) and can use the CRTC we have chosen previously. */
468
static int get_plane_id(_THIS, uint32_t plane_type)
469
{
470
471
drmModeRes *resources = NULL;
drmModePlaneResPtr plane_resources = NULL;
472
uint32_t i, j;
473
474
unsigned int crtc_index = 0;
int ret = -EINVAL;
475
int found = 0;
476
477
478
479
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
480
481
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
482
483
/* Get the crtc_index for the current CRTC.
It's needed to find out if a plane supports the CRTC. */
484
for (i = 0; i < resources->count_crtcs; i++) {
485
486
487
488
if (resources->crtcs[i] == dispdata->crtc->crtc->crtc_id) {
crtc_index = i;
break;
}
489
490
}
491
492
plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
if (!plane_resources) {
493
return SDL_SetError("drmModeGetPlaneResources failed.");
494
495
496
}
/* Iterate on all the available planes. */
497
for (i = 0; (i < plane_resources->count_planes) && !found; i++) {
498
499
uint32_t plane_id = plane_resources->planes[i];
500
501
502
503
504
drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
if (!plane) {
continue;
}
505
506
/* See if the current CRTC is available for this plane. */
507
if (plane->possible_crtcs & (1 << crtc_index)) {
508
509
510
511
drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(
viddata->drm_fd, plane_id, DRM_MODE_OBJECT_PLANE);
ret = plane_id;
512
513
514
/* Iterate on the plane props to find the type of the plane,
to see if it's of the type we want. */
515
for (j = 0; j < props->count_props; j++) {
516
517
518
drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd,
props->props[j]);
519
520
521
522
523
if ((strcmp(p->name, "type") == 0) && (props->prop_values[j] == plane_type)) {
/* found our plane, use that: */
found = 1;
}
524
525
KMSDRM_drmModeFreeProperty(p);
526
}
527
528
529
KMSDRM_drmModeFreeObjectProperties(props);
}
530
531
KMSDRM_drmModeFreePlane(plane);
532
533
534
}
KMSDRM_drmModeFreePlaneResources(plane_resources);
535
KMSDRM_drmModeFreeResources(resources);
536
537
538
539
return ret;
}
540
/* Setup a plane and it's props. */
541
542
543
int
setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
{
544
545
546
uint32_t plane_id;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
547
*plane = SDL_calloc(1, sizeof(**plane));
548
549
550
if (!(*plane)) {
return SDL_OutOfMemory();
}
551
552
553
554
555
556
557
558
559
560
561
562
563
/* Get plane ID. */
plane_id = get_plane_id(_this, plane_type);
if (!plane_id) {
goto cleanup;
}
/* Get the DRM plane itself. */
(*plane)->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
/* Get the DRM plane properties. */
if ((*plane)->plane) {
564
unsigned int i;
565
(*plane)->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
566
(*plane)->plane->plane_id, DRM_MODE_OBJECT_PLANE);
567
(*plane)->props_info = SDL_calloc((*plane)->props->count_props,
568
sizeof(*(*plane)->props_info));
569
570
571
572
573
574
if ( !((*plane)->props_info) ) {
SDL_OutOfMemory();
goto cleanup;
}
575
for (i = 0; i < (*plane)->props->count_props; i++) {
576
577
578
579
580
581
582
583
584
585
586
587
588
589
(*plane)->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
(*plane)->props->props[i]);
}
}
return 0;
cleanup:
SDL_free(*plane);
*plane = NULL;
return -1;
}
/* Free a plane and it's props. */
590
591
592
void
free_plane(struct plane **plane)
{
593
if (*plane) {
594
595
596
597
598
599
600
601
602
603
604
605
606
if ((*plane)->plane) {
KMSDRM_drmModeFreePlane((*plane)->plane);
(*plane)->plane = NULL;
}
if ((*plane)->props_info) {
SDL_free((*plane)->props_info);
(*plane)->props_info = NULL;
}
SDL_free(*plane);
*plane = NULL;
}
}
607
608
609
/**********************************************************************************/
/* The most important ATOMIC fn of the backend. */
/* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents */
610
611
/* over to a CONNECTOR->ENCODER system (several CONNECTORS can be connected */
/* to the same PLANE). */
612
613
614
615
616
617
618
619
620
621
622
623
624
/* Think of a plane as a "frame" sorrounding a picture, where the "picture" */
/* is the buffer, and we move the "frame" from a picture to another, */
/* and the one that has the "frame" is the one sent over to the screen */
/* via the CONNECTOR->ENCODER system. */
/* Think of a PLANE as being "in the middle", it's the CENTRAL part */
/* bewteen the CRTC and the BUFFER that is shown on screen. */
/* What we do here is connect a PLANE to a CRTC and a BUFFER. */
/* -ALWAYS set the CRTC_ID and FB_ID attribs of a plane at the same time, */
/* meaning IN THE SAME atomic request. */
/* -And NEVER destroy a GBM surface whose buffers are being read by a plane: */
/* first, move the plane away from those buffers and ONLY THEN destroy the */
/* buffers and/or the GBM surface containig them. */
/**********************************************************************************/
625
void
626
drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info)
627
628
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
629
630
631
632
/* Do we have a set of changes already in the making? If not, allocate a new one. */
if (!dispdata->atomic_req)
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
633
634
635
636
637
638
639
640
641
642
643
add_plane_property(dispdata->atomic_req, info->plane, "FB_ID", info->fb_id);
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_ID", info->crtc_id);
add_plane_property(dispdata->atomic_req, info->plane, "SRC_W", info->src_w << 16);
add_plane_property(dispdata->atomic_req, info->plane, "SRC_H", info->src_h << 16);
add_plane_property(dispdata->atomic_req, info->plane, "SRC_X", info->src_x);
add_plane_property(dispdata->atomic_req, info->plane, "SRC_Y", info->src_y);
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_W", info->crtc_w);
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_H", info->crtc_h);
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_X", info->crtc_x);
add_plane_property(dispdata->atomic_req, info->plane, "CRTC_Y", info->crtc_y);
644
645
646
647
648
649
650
}
int drm_atomic_commit(_THIS, SDL_bool blocking)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
int ret;
651
652
653
if (!blocking)
dispdata->atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK;
654
655
/* Never issue a new atomic commit if previous has not yet completed, or it will error. */
656
drm_atomic_waitpending(_this);
657
658
ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req, dispdata->atomic_flags, NULL);
659
if (ret) {
660
661
SDL_SetError("Atomic commit failed, returned %d.", ret);
/* Uncomment this for fast-debugging */
662
// printf("ATOMIC COMMIT FAILED: %d.\n", ret);
663
goto out;
664
}
665
666
if (dispdata->kms_in_fence_fd != -1) {
667
668
close(dispdata->kms_in_fence_fd);
dispdata->kms_in_fence_fd = -1;
669
670
671
}
out:
672
673
674
KMSDRM_drmModeAtomicFree(dispdata->atomic_req);
dispdata->atomic_req = NULL;
dispdata->atomic_flags = 0;
675
676
677
678
return ret;
}
679
void
680
drm_atomic_waitpending(_THIS)
681
682
683
684
685
686
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
/* Will return immediately if we have already destroyed the fence, because we NULL-ify it just after.
Also, will return immediately in double-buffer mode, because kms_fence will alsawys be NULL. */
if (dispdata->kms_fence) {
687
EGLint status;
688
689
690
691
692
do {
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display,
dispdata->kms_fence, 0, EGL_FOREVER_KHR);
} while (status != EGL_CONDITION_SATISFIED_KHR);
693
694
695
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
dispdata->kms_fence = NULL;
696
697
698
}
}
699
700
701
702
/***************************************/
/* End of Atomic helper functions block*/
/***************************************/
703
704
705
706
707
708
709
710
711
712
713
714
static int
KMSDRM_Available(void)
{
int ret = -ENOENT;
ret = get_driindex();
if (ret >= 0)
return 1;
return ret;
}
715
static void
716
KMSDRM_DeleteDevice(SDL_VideoDevice * device)
717
{
718
if (device->driverdata) {
719
720
721
SDL_free(device->driverdata);
device->driverdata = NULL;
}
722
723
SDL_free(device);
724
725
726
727
SDL_KMSDRM_UnloadSymbols();
}
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
static int
KMSDRM_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi)
{
int w, h;
uint32_t display_mm_width;
uint32_t display_mm_height;
SDL_DisplayData *dispdata;
dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); //viddata->devindex);
if (!dispdata) {
return SDL_SetError("No available displays");
}
display_mm_width = dispdata->connector->connector->mmWidth;
display_mm_height = dispdata->connector->connector->mmHeight;
w = dispdata->mode.hdisplay;
h = dispdata->mode.vdisplay;
*hdpi = display_mm_width ? (((float) w) * 25.4f / display_mm_width) : 0.0f;
*vdpi = display_mm_height ? (((float) h) * 25.4f / display_mm_height) : 0.0f;
*ddpi = SDL_ComputeDiagonalDPI(w, h, ((float) display_mm_width) / 25.4f,((float) display_mm_height) / 25.4f);
return 0;
}
760
static SDL_VideoDevice *
761
KMSDRM_CreateDevice(int devindex)
762
763
{
SDL_VideoDevice *device;
764
SDL_VideoData *viddata;
765
766
767
768
769
if (!KMSDRM_Available()) {
return NULL;
}
770
771
772
773
774
if (!devindex || (devindex > 99)) {
devindex = get_driindex();
}
if (devindex < 0) {
775
SDL_SetError("devindex (%d) must be between 0 and 99.", devindex);
776
777
778
779
780
781
782
783
return NULL;
}
if (!SDL_KMSDRM_LoadSymbols()) {
return NULL;
}
device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
784
if (!device) {
785
SDL_OutOfMemory();
786
return NULL;
787
788
}
789
790
viddata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
if (!viddata) {
791
792
793
SDL_OutOfMemory();
goto cleanup;
}
794
795
viddata->devindex = devindex;
viddata->drm_fd = -1;
796
797
device->driverdata = viddata;
798
799
/* Setup all functions that can be handled from this backend. */
800
801
802
device->VideoInit = KMSDRM_VideoInit;
device->VideoQuit = KMSDRM_VideoQuit;
device->GetDisplayModes = KMSDRM_GetDisplayModes;
803
device->SetDisplayMode = KMSDRM_SetDisplayMode;
804
device->GetDisplayDPI = KMSDRM_GetDisplayDPI;
805
806
device->CreateSDLWindow = KMSDRM_CreateWindow;
device->CreateSDLWindowFrom = KMSDRM_CreateWindowFrom;
807
808
809
810
device->SetWindowTitle = KMSDRM_SetWindowTitle;
device->SetWindowIcon = KMSDRM_SetWindowIcon;
device->SetWindowPosition = KMSDRM_SetWindowPosition;
device->SetWindowSize = KMSDRM_SetWindowSize;
811
device->SetWindowFullscreen = KMSDRM_SetWindowFullscreen;
812
813
814
815
816
817
818
819
820
device->ShowWindow = KMSDRM_ShowWindow;
device->HideWindow = KMSDRM_HideWindow;
device->RaiseWindow = KMSDRM_RaiseWindow;
device->MaximizeWindow = KMSDRM_MaximizeWindow;
device->MinimizeWindow = KMSDRM_MinimizeWindow;
device->RestoreWindow = KMSDRM_RestoreWindow;
device->SetWindowGrab = KMSDRM_SetWindowGrab;
device->DestroyWindow = KMSDRM_DestroyWindow;
device->GetWindowWMInfo = KMSDRM_GetWindowWMInfo;
821
#if SDL_VIDEO_OPENGL_EGL
822
device->GL_DefaultProfileConfig = KMSDRM_GLES_DefaultProfileConfig;
823
824
device->GL_LoadLibrary = KMSDRM_GLES_LoadLibrary;
device->GL_GetProcAddress = KMSDRM_GLES_GetProcAddress;
825
device->GL_UnloadLibrary = KMSDRM_GLES_UnloadLibrary;
826
827
828
829
device->GL_CreateContext = KMSDRM_GLES_CreateContext;
device->GL_MakeCurrent = KMSDRM_GLES_MakeCurrent;
device->GL_SetSwapInterval = KMSDRM_GLES_SetSwapInterval;
device->GL_GetSwapInterval = KMSDRM_GLES_GetSwapInterval;
830
device->GL_SwapWindow = KMSDRM_GLES_SwapWindow;
831
device->GL_DeleteContext = KMSDRM_GLES_DeleteContext;
832
#endif
833
device->PumpEvents = KMSDRM_PumpEvents;
834
device->free = KMSDRM_DeleteDevice;
835
836
837
838
return device;
cleanup:
839
if (device)
840
SDL_free(device);
841
842
if (viddata)
SDL_free(viddata);
843
844
845
846
847
848
return NULL;
}
VideoBootStrap KMSDRM_bootstrap = {
"KMSDRM",
"KMS/DRM Video Driver",
849
KMSDRM_CreateDevice
850
851
852
853
854
855
856
857
};
static void
KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data)
{
KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)data;
858
if (fb_info && fb_info->drm_fd >= 0 && fb_info->fb_id != 0) {
859
860
861
862
KMSDRM_drmModeRmFB(fb_info->drm_fd, fb_info->fb_id);
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Delete DRM FB %u", fb_info->fb_id);
}
863
SDL_free(fb_info);
864
865
866
867
868
}
KMSDRM_FBInfo *
KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
{
869
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
870
871
872
unsigned width, height;
uint32_t format, strides[4] = {0}, handles[4] = {0}, offsets[4] = {0};
const int num_planes = KMSDRM_gbm_bo_get_plane_count(bo);
873
unsigned int i;
874
int ret;
875
876
877
878
879
/* Check for an existing framebuffer */
KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
if (fb_info) {
880
881
882
return fb_info;
}
883
884
/* Create a structure that contains the info about framebuffer
that we need to use it. */
885
fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo));
886
if (!fb_info) {
887
888
889
SDL_OutOfMemory();
return NULL;
}
890
891
fb_info->drm_fd = viddata->drm_fd;
892
893
894
895
896
width = KMSDRM_gbm_bo_get_width(bo);
height = KMSDRM_gbm_bo_get_height(bo);
format = KMSDRM_gbm_bo_get_format(bo);
897
for (i = 0; i < num_planes; i++) {
898
899
900
strides[i] = KMSDRM_gbm_bo_get_stride_for_plane(bo, i);
handles[i] = KMSDRM_gbm_bo_get_handle(bo).u32;
offsets[i] = KMSDRM_gbm_bo_get_offset(bo, i);
901
902
903
904
905
906
}
/* Create framebuffer object for the buffer.
It's VERY important to note that fb_id is what we ise to set the FB_ID prop of a plane
when using the ATOMIC interface, and we get fb_id it here. */
ret = KMSDRM_drmModeAddFB2(viddata->drm_fd, width, height, format,
907
handles, strides, offsets, &fb_info->fb_id, 0);
908
909
910
911
if (ret) {
SDL_free(fb_info);
return NULL;
912
}
913
914
915
916
/* Set the userdata pointer. This pointer is used to store custom data that we need
to access in the future, so we store the fb_id here for later use, because fb_id is
what we need to set the FB_ID property of a plane when using the ATOMIC interface. */
917
KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback);
918
919
920
921
922
923
924
925
return fb_info;
}
/*****************************************************************************/
/* SDL Video and Display initialization/handling functions */
/* _this is a SDL_VideoDevice * */
/*****************************************************************************/
926
927
928
929
930
/* Destroy the window surfaces and buffers. Have the PRIMARY PLANE
disconnected from these buffers before doing so, or have the PRIMARY PLANE
reading the original FB where the TTY lives, before doing this, or CRTC will
be disconnected by the kernel. */
931
void
932
KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
933
{
934
SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
935
936
937
938
939
940
941
942
943
944
945
946
947
948
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
KMSDRM_PlaneInfo plane_info = {0};
#if SDL_VIDEO_OPENGL_EGL
EGLContext egl_context;
#endif
/********************************************************************/
/* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */
/* it's using. */
/********************************************************************/
plane_info.plane = dispdata->display_plane;
plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
949
plane_info.fb_id = dispdata->dumb_buffer->fb_id;
950
951
952
953
954
955
956
957
958
plane_info.src_w = dispdata->mode.hdisplay;
plane_info.src_h = dispdata->mode.vdisplay;
plane_info.crtc_w = dispdata->mode.hdisplay;
plane_info.crtc_h = dispdata->mode.vdisplay;
drm_atomic_set_plane_props(&plane_info);
/* Issue blocking atomic commit. */
if (drm_atomic_commit(_this, SDL_TRUE)) {
959
SDL_SetError("Failed to issue atomic commit on window destruction.");
960
961
962
963
964
965
966
}
/****************************************************************************/
/* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and */
/* GBM buffers now that the buffers are not being used by the PRIMARY PLANE */
/* anymore. */
/****************************************************************************/
967
968
969
970
971
/* Destroy the GBM surface and buffers. */
if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
windata->bo = NULL;
972
973
}
974
if (windata->next_bo) {
975
976
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
windata->next_bo = NULL;
977
978
}
979
980
/* Destroy the EGL surface. */
#if SDL_VIDEO_OPENGL_EGL
981
982
983
984
985
986
987
988
989
990
991
/***************************************************************************/
/* In this eglMakeCurrent() call, we disable the current EGL surface */
/* because we're going to destroy it, but DON'T disable the EGL context, */
/* because it won't be enabled again until the programs ask for a pageflip */
/* so we get to SwapWindow(). */
/* If we disable the context until then and a program tries to retrieve */
/* the context version info before calling for a pageflip, the program */
/* will get wrong info and we will be in trouble. */
/***************************************************************************/
egl_context = (EGLContext)SDL_GL_GetCurrentContext();
SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context);
992
993
994
995
996
997
998
999
if (windata->egl_surface != EGL_NO_SURFACE) {
SDL_EGL_DestroySurface(_this, windata->egl_surface);
windata->egl_surface = EGL_NO_SURFACE;
}
#endif
if (windata->gs) {
1000
KMSDRM_gbm_surface_destroy(windata->gs);