2017-08-02 19:22:48 +02:00
|
|
|
/*
|
|
|
|
Simple DirectMedia Layer
|
2020-01-17 05:49:25 +01:00
|
|
|
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
2020-08-25 16:30:23 +02:00
|
|
|
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
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"
|
2020-02-09 20:44:22 +01:00
|
|
|
#include "../../events/SDL_events_c.h"
|
2017-08-02 19:22:48 +02:00
|
|
|
#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"
|
2017-08-22 02:20:50 +02:00
|
|
|
#include "SDL_kmsdrmevents.h"
|
2017-08-02 19:22:48 +02:00
|
|
|
#include "SDL_kmsdrmopengles.h"
|
|
|
|
#include "SDL_kmsdrmmouse.h"
|
|
|
|
#include "SDL_kmsdrmdyn.h"
|
2020-12-18 22:53:51 +01:00
|
|
|
#include "SDL_kmsdrmvulkan.h"
|
2018-10-09 06:27:55 +02:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
2020-02-09 20:44:22 +01:00
|
|
|
#include <poll.h>
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-10-26 14:49:09 +01:00
|
|
|
/* for older KMSDRM headers... */
|
|
|
|
#ifndef DRM_FORMAT_MOD_VENDOR_NONE
|
|
|
|
#define DRM_FORMAT_MOD_VENDOR_NONE 0
|
|
|
|
#endif
|
|
|
|
#ifndef DRM_FORMAT_MOD_LINEAR
|
|
|
|
#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)
|
|
|
|
#endif
|
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
#define KMSDRM_DRI_PATH "/dev/dri/"
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-09-06 12:08:22 +02:00
|
|
|
#define AMDGPU_COMPAT 1
|
|
|
|
|
2020-12-18 15:50:02 +01:00
|
|
|
static int check_atomic_modesetting (int fd)
|
|
|
|
{
|
|
|
|
if (KMSDRM_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
|
|
|
|
return SDL_SetError("no atomic modesetting support.");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
static int
|
2020-07-20 03:46:41 +02:00
|
|
|
check_modesetting(int devindex)
|
2017-08-02 19:22:48 +02:00
|
|
|
{
|
2018-10-09 06:27:55 +02:00
|
|
|
SDL_bool available = SDL_FALSE;
|
|
|
|
char device[512];
|
2020-09-26 19:18:09 +02:00
|
|
|
unsigned int i;
|
2018-10-09 06:27:55 +02:00
|
|
|
int drm_fd;
|
|
|
|
|
|
|
|
SDL_snprintf(device, sizeof (device), "%scard%d", KMSDRM_DRI_PATH, devindex);
|
2020-07-20 03:48:17 +02:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "check_modesetting: probing \"%s\"", device);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
drm_fd = open(device, O_RDWR | O_CLOEXEC);
|
2017-08-02 19:22:48 +02:00
|
|
|
if (drm_fd >= 0) {
|
|
|
|
if (SDL_KMSDRM_LoadSymbols()) {
|
2020-12-18 15:50:02 +01:00
|
|
|
drmModeRes *resources = (check_atomic_modesetting(drm_fd) < 0) ? NULL : KMSDRM_drmModeGetResources(drm_fd);
|
2020-02-09 20:44:22 +01:00
|
|
|
if (resources) {
|
2018-12-01 22:31:56 +01:00
|
|
|
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) {
|
2020-09-26 19:18:09 +02:00
|
|
|
for (i = 0; i < resources->count_connectors; i++) {
|
2020-07-20 03:48:17 +02:00
|
|
|
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);
|
2020-07-20 19:09:07 +02:00
|
|
|
if (available) {
|
|
|
|
break;
|
|
|
|
}
|
2020-07-20 03:48:17 +02:00
|
|
|
}
|
2018-12-01 22:31:56 +01:00
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_drmModeFreeResources(resources);
|
|
|
|
}
|
|
|
|
SDL_KMSDRM_UnloadSymbols();
|
|
|
|
}
|
|
|
|
close(drm_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return available;
|
|
|
|
}
|
|
|
|
|
2020-08-24 12:51:20 +02:00
|
|
|
static unsigned int get_dricount(void)
|
2018-10-09 06:27:55 +02:00
|
|
|
{
|
2020-08-24 12:51:20 +02:00
|
|
|
unsigned int devcount = 0;
|
2018-10-09 06:27:55 +02:00
|
|
|
struct dirent *res;
|
|
|
|
struct stat sb;
|
|
|
|
DIR *folder;
|
|
|
|
|
2020-08-23 23:44:07 +02:00
|
|
|
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);
|
2018-10-09 06:27:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access(KMSDRM_DRI_PATH, F_OK) == -1) {
|
2020-08-23 23:44:07 +02:00
|
|
|
SDL_SetError("The path %s cannot be opened",
|
|
|
|
KMSDRM_DRI_PATH);
|
2018-10-09 06:27:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
folder = opendir(KMSDRM_DRI_PATH);
|
|
|
|
if (folder) {
|
|
|
|
while ((res = readdir(folder))) {
|
2020-08-24 12:51:20 +02:00
|
|
|
size_t len = SDL_strlen(res->d_name);
|
2019-06-19 15:40:50 +02:00
|
|
|
if (len > 4 && SDL_strncmp(res->d_name, "card", 4) == 0) {
|
2018-10-09 06:27:55 +02:00
|
|
|
devcount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(folder);
|
|
|
|
}
|
|
|
|
|
|
|
|
return devcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_driindex(void)
|
|
|
|
{
|
2020-08-24 12:51:20 +02:00
|
|
|
const unsigned int devcount = get_dricount();
|
|
|
|
unsigned int i;
|
2018-10-09 06:27:55 +02:00
|
|
|
|
|
|
|
for (i = 0; i < devcount; i++) {
|
2020-07-20 03:46:41 +02:00
|
|
|
if (check_modesetting(i)) {
|
2018-10-09 06:27:55 +02:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2020-09-07 22:54:15 +02:00
|
|
|
/**********************/
|
|
|
|
/* 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;
|
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
dumb_buffer *ret = SDL_calloc(1, sizeof(*ret));
|
2020-10-22 16:15:34 +02:00
|
|
|
if (!ret) {
|
2020-10-22 16:01:51 +02:00
|
|
|
SDL_OutOfMemory();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-09-07 22:54:15 +02:00
|
|
|
/*
|
|
|
|
* 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) {
|
2020-10-22 16:01:51 +02:00
|
|
|
.width = dispdata->mode.hdisplay,
|
|
|
|
.height = dispdata->mode.vdisplay,
|
|
|
|
.bpp = 32,
|
2020-09-07 22:54:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)) {
|
2020-10-22 16:01:51 +02:00
|
|
|
SDL_SetError("failed to create dumb buffer\n");
|
|
|
|
goto err;
|
2020-09-07 22:54:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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],
|
|
|
|
};
|
2020-10-22 16:15:34 +02:00
|
|
|
|
2020-09-07 22:54:15 +02:00
|
|
|
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,
|
2020-10-22 16:15:34 +02:00
|
|
|
viddata->drm_fd, map.offset);
|
|
|
|
|
2020-09-07 22:54:15 +02:00
|
|
|
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
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_DestroyDumbBuffer(_THIS, dumb_buffer **buffer)
|
2020-09-07 22:54:15 +02:00
|
|
|
{
|
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
|
|
|
|
struct drm_mode_destroy_dumb destroy = {
|
2020-12-18 22:53:51 +01:00
|
|
|
.handle = (*buffer)->gem_handles[0],
|
2020-09-07 22:54:15 +02:00
|
|
|
};
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_drmModeRmFB(viddata->drm_fd, (*buffer)->fb_id);
|
2020-09-07 22:54:15 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
munmap((*buffer)->dumb.mem, (*buffer)->dumb.size);
|
2020-09-07 22:54:15 +02:00
|
|
|
KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
|
2020-12-18 22:53:51 +01:00
|
|
|
free(*buffer);
|
|
|
|
*buffer = NULL;
|
2020-09-07 22:54:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Using the CPU mapping, fill the dumb buffer with black pixels. */
|
|
|
|
static void
|
|
|
|
KMSDRM_FillDumbBuffer(dumb_buffer *buffer)
|
|
|
|
{
|
2020-09-26 19:18:09 +02:00
|
|
|
unsigned int x, y;
|
|
|
|
for (y = 0; y < buffer->height; y++) {
|
2020-10-22 16:01:51 +02:00
|
|
|
uint32_t *pix = (uint32_t *) ((uint8_t *) buffer->dumb.mem + (y * buffer->pitches[0]));
|
|
|
|
for (x = 0; x < buffer->width; x++) {
|
|
|
|
*pix++ = (0x00000000);
|
|
|
|
}
|
2020-09-07 22:54:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_DestroyDumbBuffer(_this, &ret);
|
2020-09-07 22:54:15 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************/
|
|
|
|
/* DUMB BUFFER Block ends. */
|
|
|
|
/***************************/
|
|
|
|
|
2020-07-28 21:11:25 +02:00
|
|
|
/*********************************/
|
|
|
|
/* Atomic helper functions block */
|
|
|
|
/*********************************/
|
|
|
|
|
|
|
|
#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
|
|
|
|
|
2020-09-06 12:08:22 +02:00
|
|
|
int add_connector_property(drmModeAtomicReq *req, struct connector *connector,
|
2020-08-25 16:18:49 +02:00
|
|
|
const char *name, uint64_t value)
|
2020-07-28 21:11:25 +02:00
|
|
|
{
|
2020-08-23 23:44:07 +02:00
|
|
|
unsigned int i;
|
|
|
|
int prop_id = 0;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-23 23:44:07 +02:00
|
|
|
for (i = 0 ; i < connector->props->count_props ; i++) {
|
2020-10-22 16:01:51 +02:00
|
|
|
if (strcmp(connector->props_info[i]->name, name) == 0) {
|
|
|
|
prop_id = connector->props_info[i]->prop_id;
|
|
|
|
break;
|
|
|
|
}
|
2020-08-23 23:44:07 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-23 23:44:07 +02:00
|
|
|
if (prop_id < 0) {
|
2020-10-22 16:01:51 +02:00
|
|
|
SDL_SetError("no connector property: %s", name);
|
|
|
|
return -EINVAL;
|
2020-08-23 23:44:07 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-23 23:44:07 +02:00
|
|
|
return KMSDRM_drmModeAtomicAddProperty(req, connector->connector->connector_id, prop_id, value);
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
2020-08-25 16:18:49 +02:00
|
|
|
int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc,
|
|
|
|
const char *name, uint64_t value)
|
2020-07-28 21:11:25 +02:00
|
|
|
{
|
2020-08-23 23:44:07 +02:00
|
|
|
unsigned int i;
|
|
|
|
int prop_id = -1;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-23 23:44:07 +02:00
|
|
|
for (i = 0 ; i < crtc->props->count_props ; i++) {
|
2020-10-22 16:01:51 +02:00
|
|
|
if (strcmp(crtc->props_info[i]->name, name) == 0) {
|
|
|
|
prop_id = crtc->props_info[i]->prop_id;
|
|
|
|
break;
|
|
|
|
}
|
2020-08-23 23:44:07 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-23 23:44:07 +02:00
|
|
|
if (prop_id < 0) {
|
2020-10-22 16:01:51 +02:00
|
|
|
SDL_SetError("no crtc property: %s", name);
|
|
|
|
return -EINVAL;
|
2020-08-23 23:44:07 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-23 23:44:07 +02:00
|
|
|
return KMSDRM_drmModeAtomicAddProperty(req, crtc->crtc->crtc_id, prop_id, value);
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
2020-08-23 11:26:55 +02:00
|
|
|
int add_plane_property(drmModeAtomicReq *req, struct plane *plane,
|
2020-08-25 16:18:49 +02:00
|
|
|
const char *name, uint64_t value)
|
2020-07-28 21:11:25 +02:00
|
|
|
{
|
2020-08-08 14:27:55 +02:00
|
|
|
unsigned int i;
|
|
|
|
int prop_id = -1;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
for (i = 0 ; i < plane->props->count_props ; i++) {
|
2020-08-23 23:44:07 +02:00
|
|
|
if (strcmp(plane->props_info[i]->name, name) == 0) {
|
2020-10-22 16:01:51 +02:00
|
|
|
prop_id = plane->props_info[i]->prop_id;
|
|
|
|
break;
|
2020-08-23 23:44:07 +02:00
|
|
|
}
|
2020-08-08 14:27:55 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-08 14:27:55 +02:00
|
|
|
if (prop_id < 0) {
|
2020-08-23 23:44:07 +02:00
|
|
|
SDL_SetError("no plane property: %s", name);
|
|
|
|
return -EINVAL;
|
2020-08-08 14:27:55 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
return KMSDRM_drmModeAtomicAddProperty(req, plane->plane->plane_id, prop_id, value);
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
2020-08-05 15:28:51 +02:00
|
|
|
#if 0
|
|
|
|
|
2020-07-28 21:11:25 +02:00
|
|
|
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. */
|
2020-12-18 22:53:51 +01:00
|
|
|
for (int j = 0; j < props->count_props; j++) {
|
|
|
|
|
|
|
|
drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[j]);
|
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
if ((strcmp(p->name, "type") == 0)) {
|
2020-12-18 22:53:51 +01:00
|
|
|
type = props->prop_values[j];
|
2020-10-22 16:01:51 +02:00
|
|
|
}
|
2020-12-18 22:53:51 +01:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
KMSDRM_drmModeFreeProperty(p);
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case DRM_PLANE_TYPE_OVERLAY:
|
2020-10-22 16:01:51 +02:00
|
|
|
plane_type = "overlay";
|
|
|
|
break;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
case DRM_PLANE_TYPE_PRIMARY:
|
2020-10-22 16:01:51 +02:00
|
|
|
plane_type = "primary";
|
|
|
|
break;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
case DRM_PLANE_TYPE_CURSOR:
|
2020-10-22 16:01:51 +02:00
|
|
|
plane_type = "cursor";
|
|
|
|
break;
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
/* Remember that to present a plane on screen, it has to be
|
|
|
|
connected to a CRTC so the CRTC scans it,
|
2020-07-28 21:11:25 +02:00
|
|
|
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);
|
2020-12-18 22:53:51 +01:00
|
|
|
for (int i = 0; i < resources->count_crtcs; i++) {
|
2020-10-22 16:01:51 +02:00
|
|
|
if (plane->possible_crtcs & (1 << i)) {
|
|
|
|
uint32_t crtc_id = resources->crtcs[i];
|
2020-07-28 21:11:25 +02:00
|
|
|
printf ("%d", crtc_id);
|
2020-10-22 16:01:51 +02:00
|
|
|
break;
|
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-10-22 16:01:51 +02:00
|
|
|
printf("drmModeGetPlaneResources failed: %s\n", strerror(errno));
|
|
|
|
return;
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("--Number of planes found: %d-- \n", plane_resources->count_planes);
|
2020-08-17 18:35:04 +02:00
|
|
|
printf("--Usable CRTC that we have chosen: %d-- \n", dispdata->crtc->crtc->crtc_id);
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
/* Iterate on all the available planes. */
|
|
|
|
for (i = 0; (i < plane_resources->count_planes); i++) {
|
|
|
|
|
|
|
|
uint32_t plane_id = plane_resources->planes[i];
|
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
|
|
|
|
if (!plane) {
|
|
|
|
printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno));
|
|
|
|
continue;
|
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
/* Print plane info. */
|
|
|
|
print_plane_info(_this, plane);
|
|
|
|
KMSDRM_drmModeFreePlane(plane);
|
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_drmModeFreePlaneResources(plane_resources);
|
|
|
|
}
|
|
|
|
|
2020-08-05 15:28:51 +02:00
|
|
|
#endif
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
/* 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. */
|
2020-08-24 12:51:20 +02:00
|
|
|
static int get_plane_id(_THIS, uint32_t plane_type)
|
2020-07-28 21:11:25 +02:00
|
|
|
{
|
2020-08-23 02:58:57 +02:00
|
|
|
drmModeRes *resources = NULL;
|
|
|
|
drmModePlaneResPtr plane_resources = NULL;
|
2020-07-28 21:11:25 +02:00
|
|
|
uint32_t i, j;
|
2020-08-24 12:51:20 +02:00
|
|
|
unsigned int crtc_index = 0;
|
|
|
|
int ret = -EINVAL;
|
2020-08-17 18:35:04 +02:00
|
|
|
int found = 0;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
|
|
|
|
2020-08-23 02:58:57 +02:00
|
|
|
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
/* Get the crtc_index for the current CRTC.
|
|
|
|
It's needed to find out if a plane supports the CRTC. */
|
2020-08-05 18:55:22 +02:00
|
|
|
for (i = 0; i < resources->count_crtcs; i++) {
|
2020-10-22 16:01:51 +02:00
|
|
|
if (resources->crtcs[i] == dispdata->crtc->crtc->crtc_id) {
|
|
|
|
crtc_index = i;
|
|
|
|
break;
|
|
|
|
}
|
2020-08-05 18:55:22 +02:00
|
|
|
}
|
|
|
|
|
2020-07-28 21:11:25 +02:00
|
|
|
plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
|
|
|
|
if (!plane_resources) {
|
2020-10-22 16:01:51 +02:00
|
|
|
return SDL_SetError("drmModeGetPlaneResources failed.");
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Iterate on all the available planes. */
|
2020-08-17 18:35:04 +02:00
|
|
|
for (i = 0; (i < plane_resources->count_planes) && !found; i++) {
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
uint32_t plane_id = plane_resources->planes[i];
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
|
|
|
|
if (!plane) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
/* See if the current CRTC is available for this plane. */
|
2020-10-22 16:01:51 +02:00
|
|
|
if (plane->possible_crtcs & (1 << crtc_index)) {
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(
|
|
|
|
viddata->drm_fd, plane_id, DRM_MODE_OBJECT_PLANE);
|
|
|
|
ret = plane_id;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
/* Iterate on the plane props to find the type of the plane,
|
|
|
|
to see if it's of the type we want. */
|
2020-10-22 16:01:51 +02:00
|
|
|
for (j = 0; j < props->count_props; j++) {
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd,
|
|
|
|
props->props[j]);
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
if ((strcmp(p->name, "type") == 0) && (props->prop_values[j] == plane_type)) {
|
|
|
|
/* found our plane, use that: */
|
|
|
|
found = 1;
|
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-05 18:55:22 +02:00
|
|
|
KMSDRM_drmModeFreeProperty(p);
|
2020-10-22 16:01:51 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
KMSDRM_drmModeFreeObjectProperties(props);
|
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
KMSDRM_drmModeFreePlane(plane);
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_drmModeFreePlaneResources(plane_resources);
|
2020-08-23 02:58:57 +02:00
|
|
|
KMSDRM_drmModeFreeResources(resources);
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-09-07 22:54:15 +02:00
|
|
|
/* Setup a plane and it's props. */
|
2020-08-23 11:26:55 +02:00
|
|
|
int
|
|
|
|
setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
|
|
|
|
{
|
2020-08-23 02:58:57 +02:00
|
|
|
uint32_t plane_id;
|
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
2020-12-18 22:53:51 +01:00
|
|
|
int ret = 0;
|
2020-08-23 02:58:57 +02:00
|
|
|
|
2020-09-13 21:41:10 +02:00
|
|
|
*plane = SDL_calloc(1, sizeof(**plane));
|
2020-10-22 17:06:34 +02:00
|
|
|
if (!(*plane)) {
|
2020-12-18 22:53:51 +01:00
|
|
|
ret = SDL_OutOfMemory();
|
|
|
|
goto cleanup;
|
2020-10-22 17:06:34 +02:00
|
|
|
}
|
2020-08-23 02:58:57 +02:00
|
|
|
|
|
|
|
/* Get plane ID. */
|
|
|
|
plane_id = get_plane_id(_this, plane_type);
|
|
|
|
|
|
|
|
if (!plane_id) {
|
2020-12-18 22:53:51 +01:00
|
|
|
ret = SDL_SetError("Invalid Plane ID");
|
2020-08-23 02:58:57 +02:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the DRM plane itself. */
|
|
|
|
(*plane)->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
|
|
|
|
|
|
|
|
/* Get the DRM plane properties. */
|
|
|
|
if ((*plane)->plane) {
|
2020-09-26 19:18:09 +02:00
|
|
|
unsigned int i;
|
2020-08-23 02:58:57 +02:00
|
|
|
(*plane)->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
|
2020-10-22 16:01:51 +02:00
|
|
|
(*plane)->plane->plane_id, DRM_MODE_OBJECT_PLANE);
|
2020-08-23 02:58:57 +02:00
|
|
|
(*plane)->props_info = SDL_calloc((*plane)->props->count_props,
|
2020-09-13 21:41:10 +02:00
|
|
|
sizeof(*(*plane)->props_info));
|
2020-08-23 02:58:57 +02:00
|
|
|
|
2020-10-22 17:06:34 +02:00
|
|
|
if ( !((*plane)->props_info) ) {
|
2020-12-18 22:53:51 +01:00
|
|
|
ret = SDL_OutOfMemory();
|
2020-10-22 17:06:34 +02:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:18:09 +02:00
|
|
|
for (i = 0; i < (*plane)->props->count_props; i++) {
|
2020-08-23 02:58:57 +02:00
|
|
|
(*plane)->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
|
|
|
|
(*plane)->props->props[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2020-12-18 22:53:51 +01:00
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
if (*plane) {
|
|
|
|
SDL_free(*plane);
|
|
|
|
*plane = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
2020-08-23 02:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Free a plane and it's props. */
|
2020-08-23 11:26:55 +02:00
|
|
|
void
|
|
|
|
free_plane(struct plane **plane)
|
|
|
|
{
|
2020-09-08 02:42:32 +02:00
|
|
|
if (*plane) {
|
2020-08-23 02:58:57 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
/**********************************************************************************/
|
|
|
|
/* The most important ATOMIC fn of the backend. */
|
|
|
|
/* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents */
|
2020-09-06 12:08:22 +02:00
|
|
|
/* over to a CONNECTOR->ENCODER system (several CONNECTORS can be connected */
|
|
|
|
/* to the same PLANE). */
|
2020-08-17 18:35:04 +02:00
|
|
|
/* 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. */
|
|
|
|
/**********************************************************************************/
|
2020-09-12 15:58:47 +02:00
|
|
|
void
|
2020-08-23 23:44:07 +02:00
|
|
|
drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info)
|
2020-07-28 21:11:25 +02:00
|
|
|
{
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
2020-08-08 14:27:55 +02:00
|
|
|
|
|
|
|
/* 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();
|
2020-08-17 18:35:04 +02:00
|
|
|
|
2020-09-12 15:58:47 +02:00
|
|
|
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);
|
2020-08-08 14:27:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-08 14:27:55 +02:00
|
|
|
if (!blocking)
|
|
|
|
dispdata->atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-08 14:27:55 +02:00
|
|
|
/* Never issue a new atomic commit if previous has not yet completed, or it will error. */
|
2020-08-17 18:35:04 +02:00
|
|
|
drm_atomic_waitpending(_this);
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2020-08-08 14:27:55 +02:00
|
|
|
ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req, dispdata->atomic_flags, NULL);
|
2020-08-17 18:35:04 +02:00
|
|
|
if (ret) {
|
2020-08-23 23:44:07 +02:00
|
|
|
SDL_SetError("Atomic commit failed, returned %d.", ret);
|
|
|
|
/* Uncomment this for fast-debugging */
|
2020-12-18 22:53:51 +01:00
|
|
|
printf("ATOMIC COMMIT FAILED: %s.\n", strerror(errno));
|
2020-10-22 16:01:51 +02:00
|
|
|
goto out;
|
2020-08-17 18:35:04 +02:00
|
|
|
}
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
if (dispdata->kms_in_fence_fd != -1) {
|
2020-10-22 16:01:51 +02:00
|
|
|
close(dispdata->kms_in_fence_fd);
|
|
|
|
dispdata->kms_in_fence_fd = -1;
|
2020-07-28 21:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2020-08-08 14:27:55 +02:00
|
|
|
KMSDRM_drmModeAtomicFree(dispdata->atomic_req);
|
|
|
|
dispdata->atomic_req = NULL;
|
|
|
|
dispdata->atomic_flags = 0;
|
2020-07-28 21:11:25 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-08-07 11:53:04 +02:00
|
|
|
void
|
2020-08-17 18:35:04 +02:00
|
|
|
drm_atomic_waitpending(_THIS)
|
2020-08-07 11:53:04 +02:00
|
|
|
{
|
|
|
|
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) {
|
2020-10-22 16:01:51 +02:00
|
|
|
EGLint status;
|
2020-08-07 11:53:04 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
do {
|
|
|
|
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display,
|
|
|
|
dispdata->kms_fence, 0, EGL_FOREVER_KHR);
|
|
|
|
} while (status != EGL_CONDITION_SATISFIED_KHR);
|
2020-08-07 11:53:04 +02:00
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
|
|
|
|
dispdata->kms_fence = NULL;
|
2020-08-07 11:53:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 21:11:25 +02:00
|
|
|
/***************************************/
|
|
|
|
/* End of Atomic helper functions block*/
|
|
|
|
/***************************************/
|
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
static int
|
|
|
|
KMSDRM_Available(void)
|
|
|
|
{
|
|
|
|
int ret = -ENOENT;
|
|
|
|
|
|
|
|
ret = get_driindex();
|
|
|
|
if (ret >= 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
static void
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_DeleteDevice(SDL_VideoDevice * device)
|
2017-08-02 19:22:48 +02:00
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
if (device->driverdata) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_free(device->driverdata);
|
|
|
|
device->driverdata = NULL;
|
|
|
|
}
|
2017-08-02 22:51:14 +02:00
|
|
|
|
|
|
|
SDL_free(device);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_KMSDRM_UnloadSymbols();
|
|
|
|
}
|
|
|
|
|
2020-10-15 17:56:19 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
static SDL_VideoDevice *
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_CreateDevice(int devindex)
|
2017-08-02 19:22:48 +02:00
|
|
|
{
|
|
|
|
SDL_VideoDevice *device;
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-07-13 01:11:15 +02:00
|
|
|
if (!KMSDRM_Available()) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-09 06:27:55 +02:00
|
|
|
if (!devindex || (devindex > 99)) {
|
|
|
|
devindex = get_driindex();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (devindex < 0) {
|
2020-08-17 18:35:04 +02:00
|
|
|
SDL_SetError("devindex (%d) must be between 0 and 99.", devindex);
|
2017-08-02 19:22:48 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SDL_KMSDRM_LoadSymbols()) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!device) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_OutOfMemory();
|
2017-08-02 19:24:47 +02:00
|
|
|
return NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
viddata = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
|
|
|
|
if (!viddata) {
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_OutOfMemory();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
viddata->devindex = devindex;
|
|
|
|
viddata->drm_fd = -1;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
device->driverdata = viddata;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
Fixed bug 5140 - KMSDRM: Dynamic vsync toggle does not work
Manuel Alfayate Corchete
The KMSDRM backend was doing things wrong because of some small (but important) misconceptions on how KMS/DRM works: to implement a largely broken non-vsync refresh mechanism, the SwapWindow() function was issuing new pageflips before previous ones had completed, thus causing EBUSY returns, buffer mismanagement, etc... resulting in general breakage on vsync disabling from apps, that would not allow vsync to work again without KMSDRM video re-initialization.
To further clarify, on most DRM drivers async pageflips are NOT working nowadays, so all issued pageflips will complete on next VBLANK, NOT ASAP (calling drmModePageFlip() with the DRM_MODE_PAGE_FLIP_ASYNC flag will return error).
The old code was assuming that can just issue a synchronous (=on VBLANK) pageflip and then pass a 0 timeout to the pull() function so we do not wait for the pageflip event, thinking that this will lead to correct non-vsynced screen updates from the program: That is plain wrong.
Each pageflip has to be waite before issuing a new one, ALWAYS. And if we do not support ASYNC pageflips on the DRM driver level, then we are forced to wait for the next VBLANK. There is no way around it.
I have also added many comments on the KMSDRM code. This is needed for future reference for me or others who may need to look at this code: KMS/DRM terminology regarding what SYNC and ASYNC mean in pageflip terms, and where to do certain things and why, is not trivial. It is not desirable or possible to invest time on researching the same concepts every time there is need to dive into this code. So please leave all these comments in the patch.
2020-05-27 01:27:00 +02:00
|
|
|
/* Setup all functions that can be handled from this backend. */
|
2017-08-02 19:22:48 +02:00
|
|
|
device->VideoInit = KMSDRM_VideoInit;
|
|
|
|
device->VideoQuit = KMSDRM_VideoQuit;
|
|
|
|
device->GetDisplayModes = KMSDRM_GetDisplayModes;
|
2020-09-12 00:47:00 +02:00
|
|
|
device->SetDisplayMode = KMSDRM_SetDisplayMode;
|
2020-10-15 17:56:19 +02:00
|
|
|
device->GetDisplayDPI = KMSDRM_GetDisplayDPI;
|
2017-08-28 09:43:14 +02:00
|
|
|
device->CreateSDLWindow = KMSDRM_CreateWindow;
|
|
|
|
device->CreateSDLWindowFrom = KMSDRM_CreateWindowFrom;
|
2017-08-02 19:22:48 +02:00
|
|
|
device->SetWindowTitle = KMSDRM_SetWindowTitle;
|
|
|
|
device->SetWindowIcon = KMSDRM_SetWindowIcon;
|
|
|
|
device->SetWindowPosition = KMSDRM_SetWindowPosition;
|
|
|
|
device->SetWindowSize = KMSDRM_SetWindowSize;
|
2020-08-28 22:38:26 +02:00
|
|
|
device->SetWindowFullscreen = KMSDRM_SetWindowFullscreen;
|
2017-08-02 19:22:48 +02:00
|
|
|
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;
|
2017-08-04 23:00:47 +02:00
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
2020-09-10 21:04:35 +02:00
|
|
|
device->GL_DefaultProfileConfig = KMSDRM_GLES_DefaultProfileConfig;
|
2017-08-02 19:22:48 +02:00
|
|
|
device->GL_LoadLibrary = KMSDRM_GLES_LoadLibrary;
|
|
|
|
device->GL_GetProcAddress = KMSDRM_GLES_GetProcAddress;
|
2020-09-06 12:08:22 +02:00
|
|
|
device->GL_UnloadLibrary = KMSDRM_GLES_UnloadLibrary;
|
2017-08-02 19:22:48 +02:00
|
|
|
device->GL_CreateContext = KMSDRM_GLES_CreateContext;
|
|
|
|
device->GL_MakeCurrent = KMSDRM_GLES_MakeCurrent;
|
|
|
|
device->GL_SetSwapInterval = KMSDRM_GLES_SetSwapInterval;
|
|
|
|
device->GL_GetSwapInterval = KMSDRM_GLES_GetSwapInterval;
|
2020-09-10 21:07:23 +02:00
|
|
|
device->GL_SwapWindow = KMSDRM_GLES_SwapWindow;
|
2017-08-02 19:22:48 +02:00
|
|
|
device->GL_DeleteContext = KMSDRM_GLES_DeleteContext;
|
2017-08-04 23:00:47 +02:00
|
|
|
#endif
|
2017-08-02 19:22:48 +02:00
|
|
|
device->PumpEvents = KMSDRM_PumpEvents;
|
2020-02-09 20:44:22 +01:00
|
|
|
device->free = KMSDRM_DeleteDevice;
|
2020-12-18 22:53:51 +01:00
|
|
|
#if SDL_VIDEO_VULKAN
|
|
|
|
device->Vulkan_LoadLibrary = KMSDRM_Vulkan_LoadLibrary;
|
|
|
|
device->Vulkan_UnloadLibrary = KMSDRM_Vulkan_UnloadLibrary;
|
|
|
|
device->Vulkan_GetInstanceExtensions = KMSDRM_Vulkan_GetInstanceExtensions;
|
|
|
|
device->Vulkan_CreateSurface = KMSDRM_Vulkan_CreateSurface;
|
|
|
|
device->Vulkan_GetDrawableSize = KMSDRM_Vulkan_GetDrawableSize;
|
|
|
|
#endif
|
2017-08-02 19:22:48 +02:00
|
|
|
return device;
|
|
|
|
|
|
|
|
cleanup:
|
2020-02-09 20:44:22 +01:00
|
|
|
if (device)
|
2017-08-02 19:22:48 +02:00
|
|
|
SDL_free(device);
|
2020-02-09 20:44:22 +01:00
|
|
|
if (viddata)
|
|
|
|
SDL_free(viddata);
|
2017-08-02 19:22:48 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoBootStrap KMSDRM_bootstrap = {
|
|
|
|
"KMSDRM",
|
|
|
|
"KMS/DRM Video Driver",
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_CreateDevice
|
2017-08-02 19:22:48 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
KMSDRM_FBDestroyCallback(struct gbm_bo *bo, void *data)
|
|
|
|
{
|
|
|
|
KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)data;
|
|
|
|
|
2019-03-13 14:54:51 +01:00
|
|
|
if (fb_info && fb_info->drm_fd >= 0 && fb_info->fb_id != 0) {
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_drmModeRmFB(fb_info->drm_fd, fb_info->fb_id);
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Delete DRM FB %u", fb_info->fb_id);
|
|
|
|
}
|
|
|
|
|
2019-03-11 15:22:40 +01:00
|
|
|
SDL_free(fb_info);
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_FBInfo *
|
|
|
|
KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
2020-08-17 18:35:04 +02:00
|
|
|
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);
|
2020-09-26 19:18:09 +02:00
|
|
|
unsigned int i;
|
2020-02-15 01:17:17 +01:00
|
|
|
int ret;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
|
|
|
/* Check for an existing framebuffer */
|
|
|
|
KMSDRM_FBInfo *fb_info = (KMSDRM_FBInfo *)KMSDRM_gbm_bo_get_user_data(bo);
|
|
|
|
|
|
|
|
if (fb_info) {
|
2017-08-02 19:22:48 +02:00
|
|
|
return fb_info;
|
|
|
|
}
|
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
/* Create a structure that contains the info about framebuffer
|
|
|
|
that we need to use it. */
|
2017-08-02 19:22:48 +02:00
|
|
|
fb_info = (KMSDRM_FBInfo *)SDL_calloc(1, sizeof(KMSDRM_FBInfo));
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!fb_info) {
|
2017-08-05 22:10:36 +02:00
|
|
|
SDL_OutOfMemory();
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
fb_info->drm_fd = viddata->drm_fd;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
width = KMSDRM_gbm_bo_get_width(bo);
|
|
|
|
height = KMSDRM_gbm_bo_get_height(bo);
|
|
|
|
format = KMSDRM_gbm_bo_get_format(bo);
|
|
|
|
|
2020-09-26 19:18:09 +02:00
|
|
|
for (i = 0; i < num_planes; i++) {
|
2020-10-22 16:01:51 +02:00
|
|
|
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);
|
2020-08-17 18:35:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create framebuffer object for the buffer.
|
2020-12-18 22:53:51 +01:00
|
|
|
It's VERY important to note that fb_id is what we use to set the FB_ID prop
|
|
|
|
of a plane when using the ATOMIC interface, and we get the fb_id here. */
|
2020-08-17 18:35:04 +02:00
|
|
|
ret = KMSDRM_drmModeAddFB2(viddata->drm_fd, width, height, format,
|
2020-10-22 16:01:51 +02:00
|
|
|
handles, strides, offsets, &fb_info->fb_id, 0);
|
2020-08-17 18:35:04 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (ret) {
|
|
|
|
SDL_free(fb_info);
|
|
|
|
return NULL;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-08-17 18:35:04 +02:00
|
|
|
/* 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. */
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_gbm_bo_set_user_data(bo, fb_info, KMSDRM_FBDestroyCallback);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return fb_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* SDL Video and Display initialization/handling functions */
|
|
|
|
/* _this is a SDL_VideoDevice * */
|
|
|
|
/*****************************************************************************/
|
2020-08-17 18:35:04 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Deinitializes the dispdata members needed for KMSDRM operation that are
|
|
|
|
inoffeensive for VK compatibility. */
|
|
|
|
void KMSDRM_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) {
|
|
|
|
/* Free connector */
|
|
|
|
if (dispdata && dispdata->connector) {
|
|
|
|
if (dispdata->connector->connector) {
|
|
|
|
KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
|
|
|
|
dispdata->connector->connector = NULL;
|
|
|
|
}
|
|
|
|
if (dispdata->connector->props_info) {
|
|
|
|
SDL_free(dispdata->connector->props_info);
|
|
|
|
dispdata->connector->props_info = NULL;
|
|
|
|
}
|
|
|
|
SDL_free(dispdata->connector);
|
|
|
|
dispdata->connector = NULL;
|
|
|
|
}
|
2020-09-06 23:19:54 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Free CRTC */
|
|
|
|
if (dispdata && dispdata->crtc) {
|
|
|
|
if (dispdata->crtc->crtc) {
|
|
|
|
KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
|
|
|
|
dispdata->crtc->crtc = NULL;
|
|
|
|
}
|
|
|
|
if (dispdata->crtc->props_info) {
|
|
|
|
SDL_free(dispdata->crtc->props_info);
|
|
|
|
dispdata->crtc->props_info = NULL;
|
|
|
|
}
|
|
|
|
SDL_free(dispdata->crtc);
|
|
|
|
dispdata->crtc = NULL;
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 23:19:54 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Initializes the dispdata members needed for KMSDRM operation that are
|
|
|
|
inoffeensive for VK compatibility, except we must leave the drm_fd
|
|
|
|
closed when we get to the end of this function.
|
|
|
|
This is to be called early, in VideoInit(), because it gets us
|
|
|
|
the videomode information, which SDL needs immediately after VideoInit(). */
|
|
|
|
int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
|
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
2020-09-06 23:19:54 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
drmModeRes *resources = NULL;
|
|
|
|
drmModeEncoder *encoder = NULL;
|
|
|
|
drmModeConnector *connector = NULL;
|
|
|
|
drmModeCrtc *crtc = NULL;
|
2020-09-06 23:19:54 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
char devname[32];
|
|
|
|
int ret = 0;
|
|
|
|
unsigned i,j;
|
2020-09-06 23:19:54 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
dispdata->atomic_flags = 0;
|
|
|
|
dispdata->atomic_req = NULL;
|
|
|
|
dispdata->kms_fence = NULL;
|
|
|
|
dispdata->gpu_fence = NULL;
|
|
|
|
dispdata->kms_out_fence_fd = -1;
|
|
|
|
dispdata->dumb_buffer = NULL;
|
|
|
|
dispdata->modeset_pending = SDL_FALSE;
|
|
|
|
dispdata->gbm_init = SDL_FALSE;
|
|
|
|
|
|
|
|
dispdata->display_plane = NULL;
|
|
|
|
dispdata->cursor_plane = NULL;
|
|
|
|
|
|
|
|
dispdata->cursor_bo = NULL;
|
|
|
|
|
|
|
|
/* Open /dev/dri/cardNN */
|
|
|
|
SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), "/dev/dri/card%d", viddata->devindex);
|
|
|
|
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
|
|
|
|
viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
|
|
|
|
|
|
|
|
if (viddata->drm_fd < 0) {
|
|
|
|
ret = SDL_SetError("Could not open %s", devname);
|
|
|
|
goto cleanup;
|
2020-09-06 23:19:54 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
|
2020-08-07 11:53:04 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Try ATOMIC compatibility */
|
|
|
|
ret = check_atomic_modesetting(viddata->drm_fd);
|
|
|
|
if (ret) {
|
|
|
|
ret = SDL_SetError("not compatible with atomic modesetting");
|
|
|
|
goto cleanup;
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/********************************************/
|
|
|
|
/* Block for enabling ATOMIC compatibility. */
|
|
|
|
/********************************************/
|
|
|
|
|
|
|
|
/* Set ATOMIC compatibility */
|
|
|
|
/* TODO: We have just tried ATOMIC compatibility, haven't we? */
|
|
|
|
ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
|
|
if (ret) {
|
|
|
|
ret = SDL_SetError("no atomic modesetting support.");
|
|
|
|
goto cleanup;
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Set UNIVERSAL PLANES compatibility */
|
|
|
|
ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
|
|
|
if (ret) {
|
|
|
|
ret = SDL_SetError("no universal planes support.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/*******************************************/
|
|
|
|
/* Block for getting the ATOMIC resources. */
|
|
|
|
/*******************************************/
|
2020-09-06 12:08:22 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Get all of the available connectors / devices / crtcs */
|
|
|
|
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
|
|
|
|
if (!resources) {
|
|
|
|
ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
|
|
|
|
goto cleanup;
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Iterate on the available connectors to find a connected connector. */
|
|
|
|
for (i = 0; i < resources->count_connectors; i++) {
|
|
|
|
drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd,
|
|
|
|
resources->connectors[i]);
|
2020-08-31 19:17:17 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!conn) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-06 12:08:22 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
|
|
|
|
conn->connector_id, conn->count_modes);
|
|
|
|
connector = conn;
|
2020-09-12 04:52:56 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_drmModeFreeConnector(conn);
|
2020-08-31 19:17:17 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!connector) {
|
|
|
|
ret = SDL_SetError("No currently active connector found.");
|
|
|
|
goto cleanup;
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Try to find the connector's current encoder */
|
|
|
|
for (i = 0; i < resources->count_encoders; i++) {
|
|
|
|
encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!encoder) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (encoder->encoder_id == connector->encoder_id) {
|
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
|
|
|
break;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
|
encoder = NULL;
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!encoder) {
|
|
|
|
/* No encoder was connected, find the first supported one */
|
|
|
|
for (i = 0; i < resources->count_encoders; i++) {
|
|
|
|
encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!encoder) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
for (j = 0; j < connector->count_encoders; j++) {
|
|
|
|
if (connector->encoders[j] == encoder->encoder_id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 23:19:54 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (j != connector->count_encoders) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
|
encoder = NULL;
|
|
|
|
}
|
2020-09-06 12:08:22 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!encoder) {
|
|
|
|
ret = SDL_SetError("No connected encoder found.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-09-06 12:08:22 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
|
2020-09-06 12:08:22 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Try to find a CRTC connected to this encoder */
|
|
|
|
crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
2020-09-06 12:08:22 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* If no CRTC was connected to the encoder, find the first CRTC
|
|
|
|
that is supported by the encoder, and use that. */
|
|
|
|
if (!crtc) {
|
|
|
|
for (i = 0; i < resources->count_crtcs; i++) {
|
|
|
|
if (encoder->possible_crtcs & (1 << i)) {
|
|
|
|
encoder->crtc_id = resources->crtcs[i];
|
|
|
|
crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
|
|
|
|
break;
|
2020-09-06 12:08:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!crtc) {
|
|
|
|
ret = SDL_SetError("No CRTC found.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-09-06 12:08:22 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Figure out the default mode to be set. */
|
|
|
|
dispdata->mode = crtc->mode;
|
2020-08-28 23:04:00 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Find the connector's preferred mode, to be used in case the current mode
|
|
|
|
is not valid, or if restoring the current mode fails.
|
|
|
|
We can always count on the preferred mode! */
|
|
|
|
for (i = 0; i < connector->count_modes; i++) {
|
|
|
|
if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) {
|
|
|
|
dispdata->preferred_mode = connector->modes[i];
|
|
|
|
}
|
|
|
|
}
|
2020-09-12 04:52:56 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* If the current CRTC's mode isn't valid, select the preferred
|
|
|
|
mode of the connector. */
|
|
|
|
if (crtc->mode_valid == 0) {
|
|
|
|
dispdata->mode = dispdata->preferred_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0 ) {
|
|
|
|
ret = SDL_SetError("Couldn't get a valid connector videomode.");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get CRTC properties */
|
|
|
|
dispdata->crtc->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
|
|
|
|
crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
|
|
|
|
|
|
|
|
dispdata->crtc->props_info = SDL_calloc(dispdata->crtc->props->count_props,
|
|
|
|
sizeof(*dispdata->crtc->props_info));
|
2020-09-12 04:52:56 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!dispdata->crtc->props_info) {
|
|
|
|
ret = SDL_OutOfMemory();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-09-12 04:52:56 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
for (i = 0; i < dispdata->crtc->props->count_props; i++) {
|
|
|
|
dispdata->crtc->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
|
|
|
|
dispdata->crtc->props->props[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get connector properties */
|
|
|
|
dispdata->connector->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
|
|
|
|
connector->connector_id, DRM_MODE_OBJECT_CONNECTOR);
|
|
|
|
|
|
|
|
dispdata->connector->props_info = SDL_calloc(dispdata->connector->props->count_props,
|
|
|
|
sizeof(*dispdata->connector->props_info));
|
2020-09-12 04:52:56 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!dispdata->connector->props_info) {
|
|
|
|
ret = SDL_OutOfMemory();
|
|
|
|
goto cleanup;
|
2020-08-28 23:04:00 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
for (i = 0; i < dispdata->connector->props->count_props; i++) {
|
|
|
|
dispdata->connector->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
|
|
|
|
dispdata->connector->props->props[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store the connector and crtc for future use. This is all we keep from this function,
|
|
|
|
and these are just structs, inoffensive to VK. */
|
|
|
|
dispdata->connector->connector = connector;
|
|
|
|
dispdata->crtc->crtc = crtc;
|
|
|
|
|
|
|
|
/***********************************/
|
|
|
|
/* Block fpr Vulkan compatibility. */
|
|
|
|
/***********************************/
|
|
|
|
|
|
|
|
/* THIS IS FOR VULKAN! Leave the FD closed, so VK can work.
|
|
|
|
Will reopen this in CreateWindow, but only if requested a non-VK window. */
|
|
|
|
KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 0);
|
|
|
|
KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
|
|
|
|
close (viddata->drm_fd);
|
|
|
|
viddata->drm_fd = -1;
|
2020-09-06 12:08:22 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
cleanup:
|
|
|
|
// TODO Use it as a list to see if everything we use here is freed.
|
|
|
|
if (encoder)
|
|
|
|
KMSDRM_drmModeFreeEncoder(encoder);
|
|
|
|
if (resources)
|
|
|
|
KMSDRM_drmModeFreeResources(resources);
|
|
|
|
if (ret != 0) {
|
|
|
|
/* Error (complete) cleanup */
|
|
|
|
if (dispdata->connector->connector) {
|
|
|
|
KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
|
|
|
|
dispdata->connector->connector = NULL;
|
|
|
|
}
|
|
|
|
if (dispdata->crtc->crtc) {
|
|
|
|
KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
|
|
|
|
dispdata->crtc->crtc = NULL;
|
|
|
|
}
|
|
|
|
if (viddata->drm_fd >= 0) {
|
|
|
|
close(viddata->drm_fd);
|
|
|
|
viddata->drm_fd = -1;
|
|
|
|
}
|
|
|
|
SDL_free(dispdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2020-08-28 23:04:00 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Init the Vulkan-INCOMPATIBLE stuff:
|
|
|
|
Reopen FD, create gbm dev, create dumb buffer and setup display plane.
|
|
|
|
This is to be called late, in WindowCreate(), and ONLY if this is not
|
|
|
|
a Vulkan window.
|
|
|
|
We are doing this so late to allow Vulkan to work if we build a VK window.
|
|
|
|
These things are incompatible with Vulkan, which accesses the same resources
|
|
|
|
internally so they must be free when trying to build a Vulkan surface.
|
|
|
|
*/
|
2017-08-02 19:22:48 +02:00
|
|
|
int
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_GBMInit (_THIS, SDL_DisplayData *dispdata)
|
2017-08-02 19:22:48 +02:00
|
|
|
{
|
2020-12-18 22:53:51 +01:00
|
|
|
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
2017-08-02 19:22:48 +02:00
|
|
|
int ret = 0;
|
2020-12-18 22:53:51 +01:00
|
|
|
|
|
|
|
/* Reopen the FD! */
|
|
|
|
viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
|
|
|
|
KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
|
|
KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
|
|
|
|
|
|
|
/* Create aux dumb buffer. It's only useful to keep the PRIMARY PLANE occupied
|
|
|
|
when we destroy the GBM surface and it's KMS buffers, so not being able to
|
|
|
|
create it is not fatal. */
|
|
|
|
dispdata->dumb_buffer = KMSDRM_CreateBuffer(_this);
|
|
|
|
if (!dispdata->dumb_buffer) {
|
|
|
|
ret = SDL_SetError("can't create dumb buffer.");
|
|
|
|
} else {
|
|
|
|
/* Fill the dumb buffer with black pixels. */
|
|
|
|
KMSDRM_FillDumbBuffer(dispdata->dumb_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the GBM device. */
|
|
|
|
viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd);
|
|
|
|
if (!viddata->gbm_dev) {
|
|
|
|
ret = SDL_SetError("Couldn't create gbm device.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup the display plane. ONLY do this after dispdata has the right
|
|
|
|
crtc and connector, because these are used in this function. */
|
|
|
|
ret = setup_plane(_this, &(dispdata->display_plane), DRM_PLANE_TYPE_PRIMARY);
|
|
|
|
if (ret) {
|
|
|
|
ret = SDL_SetError("can't find suitable display plane.");
|
|
|
|
}
|
|
|
|
|
|
|
|
dispdata->gbm_init = SDL_TRUE;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deinit the Vulkan-incompatible KMSDRM stuff. */
|
|
|
|
void
|
|
|
|
KMSDRM_GBMDeinit (_THIS, SDL_DisplayData *dispdata)
|
|
|
|
{
|
|
|
|
KMSDRM_PlaneInfo plane_info = {0};
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
2020-12-18 22:53:51 +01:00
|
|
|
drmModeModeInfo mode;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
uint32_t blob_id;
|
2020-10-22 19:44:38 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/*****************************************************************/
|
|
|
|
/* */
|
|
|
|
/* BLOCK to safely destroy the DUMB BUFFER. */
|
|
|
|
/* */
|
|
|
|
/* We come from DestroyWindow(), where both DestroySurfaces() */
|
|
|
|
/* and this GBMDeinit() are called, one after another. */
|
|
|
|
/* So the GBM/EGL surfaces and buffers of the windows have */
|
|
|
|
/* already been destroyed already and, because of that, the */
|
|
|
|
/* PRIMARY PLANE is using the DUMB BUFFER. BUT the DUMB BUFFER */
|
|
|
|
/* we use to keep the PRIMARY PLANE occupied when we do */
|
|
|
|
/* DestroySurfaces calls is going to be destroyed one way or */
|
|
|
|
/* another when the program quits so, to prevent the the kernel */
|
|
|
|
/* from disabling the CRTC when it detects the deletion of a */
|
|
|
|
/* buffer that IS IN USE BY THE PRIMARY PLANE, we do one of */
|
|
|
|
/* these: */
|
|
|
|
/* */
|
|
|
|
/* -In AMDGPU, where manually disabling the CRTC and */
|
|
|
|
/* disconnecting the CONNECTOR from the CRTC is an */
|
|
|
|
/* unrecoverable situation, we point the PRIMARY PLANE to */
|
|
|
|
/* the original TTY buffer (not guaranteed to be there for us!) */
|
|
|
|
/* and then destroy the DUMB BUFFER). */
|
|
|
|
/* */
|
|
|
|
/* -In other drivers, we disconnect the CONNECTOR from the CRTC */
|
|
|
|
/* (remember: several connectors can read a CRTC), deactivate */
|
|
|
|
/* the CRTC, and set the PRIMARY PLANE props CRTC_ID and FB_ID */
|
|
|
|
/* to 0. Then we destroy the DUMB BUFFER. */
|
|
|
|
/* We can leave all like this if we are exiting the program: */
|
|
|
|
/* FBCON or whatever will reconfigure things as it needs. */
|
|
|
|
/* */
|
|
|
|
/*****************************************************************/
|
|
|
|
|
|
|
|
/* dispdata->crtc->crtc->mode is the original video mode that was
|
|
|
|
configured on the connector when we lauched the program,
|
|
|
|
dispdata->mode is the current video mode, which could be different,
|
|
|
|
and dispdata->preferred_mode is the native display mode. */
|
|
|
|
mode = dispdata->crtc->crtc->mode;
|
|
|
|
|
|
|
|
#if AMDGPU_COMPAT
|
|
|
|
/* This won't work if the console wasn't correctly restored when a prevous
|
|
|
|
program exited, because in that case the TTY buffer won't be in
|
|
|
|
crtc->crtc->buffer_id, so the following atomic commit will fail. */
|
|
|
|
plane_info.plane = dispdata->display_plane;
|
|
|
|
plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
|
|
|
|
plane_info.fb_id = dispdata->crtc->crtc->buffer_id;
|
|
|
|
plane_info.src_w = mode.hdisplay;
|
|
|
|
plane_info.src_h = mode.vdisplay;
|
|
|
|
plane_info.crtc_w = mode.hdisplay;
|
|
|
|
plane_info.crtc_h = mode.vdisplay;
|
|
|
|
|
|
|
|
drm_atomic_set_plane_props(&plane_info);
|
|
|
|
#else
|
|
|
|
add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0);
|
|
|
|
add_crtc_property(dispdata->atomic_req, dispdata->crtc , "ACTIVE", 0);
|
|
|
|
|
|
|
|
/* Since we initialize plane_info to all zeros,
|
|
|
|
ALL PRIMARY PLANE props are set to 0 with this,
|
|
|
|
including FB_ID and CRTC_ID.
|
|
|
|
Not all drivers like FB_ID and CRTC_ID to 0 yet. */
|
|
|
|
plane_info.plane = dispdata->display_plane;
|
|
|
|
drm_atomic_set_plane_props(&plane_info);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Set the props that restore the original video mode. */
|
|
|
|
dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
|
|
add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
|
|
|
|
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &mode, sizeof(mode), &blob_id);
|
|
|
|
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id);
|
|
|
|
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "ACTIVE", 1);
|
|
|
|
|
|
|
|
/* Issue blocking atomic commit. */
|
|
|
|
if (drm_atomic_commit(_this, SDL_TRUE)) {
|
|
|
|
SDL_SetError("Failed to issue atomic commit on video quitting.");
|
2020-10-22 17:06:34 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Destroy the DUMB buffer if it exists, now that it's not being
|
|
|
|
used anymore by the PRIMARY PLANE. */
|
|
|
|
if (dispdata->dumb_buffer) {
|
|
|
|
KMSDRM_DestroyDumbBuffer(_this, &dispdata->dumb_buffer);
|
2020-10-22 17:06:34 +02:00
|
|
|
}
|
2020-08-17 18:35:04 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/*************************************************/
|
|
|
|
/* BLOCK to safely destroy the dumb buffer ENDS. */
|
|
|
|
/*************************************************/
|
|
|
|
|
|
|
|
/* Free display plane */
|
|
|
|
free_plane(&dispdata->display_plane);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Free cursor plane (if still not freed) */
|
|
|
|
free_plane(&dispdata->cursor_plane);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(),
|
|
|
|
already called when we get here. */
|
|
|
|
if (viddata->gbm_dev) {
|
|
|
|
KMSDRM_gbm_device_destroy(viddata->gbm_dev);
|
|
|
|
viddata->gbm_dev = NULL;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Finally close DRM FD. May be reopen on next non-vulkan window creation. */
|
|
|
|
if (viddata->drm_fd >= 0) {
|
|
|
|
close(viddata->drm_fd);
|
|
|
|
viddata->drm_fd = -1;
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
dispdata->gbm_init = SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
void
|
|
|
|
KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
|
|
|
|
{
|
|
|
|
SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
|
|
|
|
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, by making it point to the dumb buffer. */
|
|
|
|
/********************************************************************/
|
|
|
|
|
|
|
|
plane_info.plane = dispdata->display_plane;
|
|
|
|
plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
|
|
|
|
plane_info.fb_id = dispdata->dumb_buffer->fb_id;
|
|
|
|
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)) {
|
|
|
|
SDL_SetError("Failed to issue atomic commit on surfaces destruction.");
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/****************************************************************************/
|
|
|
|
/* 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. */
|
|
|
|
/****************************************************************************/
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Destroy the GBM surface and buffers. */
|
|
|
|
if (windata->bo) {
|
|
|
|
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
|
|
|
windata->bo = NULL;
|
|
|
|
}
|
2020-10-22 17:55:45 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (windata->next_bo) {
|
|
|
|
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
|
|
|
|
windata->next_bo = NULL;
|
2020-10-22 17:55:45 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
/* Destroy the EGL surface. */
|
|
|
|
/* 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. */
|
|
|
|
/***************************************************************************/
|
|
|
|
|
|
|
|
#if SDL_VIDEO_OPENGL_EGL
|
|
|
|
egl_context = (EGLContext)SDL_GL_GetCurrentContext();
|
|
|
|
SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context);
|
|
|
|
|
|
|
|
if (windata->egl_surface != EGL_NO_SURFACE) {
|
|
|
|
SDL_EGL_DestroySurface(_this, windata->egl_surface);
|
|
|
|
windata->egl_surface = EGL_NO_SURFACE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (windata->gs) {
|
|
|
|
KMSDRM_gbm_surface_destroy(windata->gs);
|
|
|
|
windata->gs = NULL;
|
2020-10-22 17:55:45 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
}
|
2020-10-22 17:55:45 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
int
|
|
|
|
KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
|
uint32_t surface_fmt = GBM_FORMAT_ARGB8888;
|
|
|
|
uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
|
|
|
|
uint32_t width, height;
|
|
|
|
|
|
|
|
EGLContext egl_context;
|
|
|
|
|
|
|
|
/* Destroy the surfaces and buffers before creating the new ones. */
|
|
|
|
// TODO REENABLE THIS IF CGENIUS FAILS BECAUSE IT CREATES A NEW WINDOW
|
|
|
|
// W/O DESTRYING THE PREVIOUS ONE.
|
|
|
|
//KMSDRM_DestroySurfaces(_this, window);
|
|
|
|
|
|
|
|
if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
|
|
|
((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
|
|
|
|
|
|
|
|
width = dispdata->mode.hdisplay;
|
|
|
|
height = dispdata->mode.vdisplay;
|
|
|
|
} else {
|
|
|
|
width = window->w;
|
|
|
|
height = window->h;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
|
|
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags);
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!windata->gs) {
|
|
|
|
return SDL_SetError("Could not create GBM surface");
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
#if SDL_VIDEO_OPENGL_EGL //TODO Restore this lo LibraryLoad and Unload calls.
|
|
|
|
/* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
|
|
|
|
but we need an EGL surface NOW, or GL won't be able to render into any surface
|
|
|
|
and we won't see the first frame. */
|
|
|
|
SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
|
|
|
|
windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (windata->egl_surface == EGL_NO_SURFACE) {
|
|
|
|
return SDL_SetError("Could not create EGL window surface");
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Current context passing to EGL is now done here. If something fails,
|
|
|
|
go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */
|
|
|
|
/* TODO Errorcheck on this (may lead to delayed call again...) */
|
|
|
|
egl_context = (EGLContext)SDL_GL_GetCurrentContext();
|
|
|
|
SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KMSDRM_DestroyWindow(_THIS, SDL_Window *window)
|
|
|
|
{
|
|
|
|
SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
|
SDL_VideoData *viddata = windata->viddata;
|
|
|
|
SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
if (!windata) {
|
|
|
|
return;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!is_vulkan) {
|
2018-12-01 19:09:00 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_DestroySurfaces(_this, window);
|
2018-12-01 19:09:00 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_DeinitMouse(_this);
|
|
|
|
|
|
|
|
if (_this->egl_data) {
|
|
|
|
SDL_EGL_UnloadLibrary(_this);
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (dispdata->gbm_init) {
|
|
|
|
KMSDRM_GBMDeinit(_this, dispdata);
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
#if AMDGPU_COMPAT
|
|
|
|
/* Since vkDestroySurfaceKHR does not destroy the native surface (only the Vulkan one),
|
|
|
|
runnin Vulkan programs leave the last buffer connected to the primary plane,
|
|
|
|
at least on AMDGPU, and the TTY buffer isn't restored.
|
|
|
|
That's not a big problem, but running a GLES program after that will cause the
|
|
|
|
atomic commit we do for restoring the TTY buffer to fail, because the TTY buffer
|
|
|
|
isn't there when we launch the GLES program.
|
|
|
|
So what we do here is "hack" Vulkan and restore the TTY buffer here, as if
|
|
|
|
we were running a GLES program. We get here after the program's vkDestroySurfaceKHR
|
|
|
|
has been called, which allows us to think as if we were in the beginig
|
|
|
|
of a GLES program.
|
|
|
|
THIS IS DONE IN A SEPARATE BLOCK FOR POSSIBLE EASY FUTURE REMOVAL. DON'T OPTIMIZE. */
|
|
|
|
|
|
|
|
if (is_vulkan) {
|
|
|
|
KMSDRM_GBMInit(_this, dispdata);
|
|
|
|
KMSDRM_GBMDeinit(_this, dispdata);
|
|
|
|
}
|
|
|
|
#endif
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/********************************************/
|
|
|
|
/* Remove from the internal SDL window list */
|
|
|
|
/********************************************/
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
for (i = 0; i < viddata->num_windows; i++) {
|
|
|
|
if (viddata->windows[i] == window) {
|
|
|
|
viddata->num_windows--;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
for (j = i; j < viddata->num_windows; j++) {
|
|
|
|
viddata->windows[j] = viddata->windows[j + 1];
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
/* Free the window driverdata. Bye bye, surface and buffer pointers! */
|
|
|
|
/*********************************************************************/
|
|
|
|
window->driverdata = NULL;
|
|
|
|
SDL_free(windata);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Reconfigure the window scaling parameters and re-construct it's surfaces, */
|
|
|
|
/* without destroying the window itself. */
|
|
|
|
/* To be used by SetWindowSize() and SetWindowFullscreen(). */
|
|
|
|
/*****************************************************************************/
|
|
|
|
static int
|
|
|
|
KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
|
|
|
|
SDL_WindowData *windata = window->driverdata;
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
|
|
|
SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
|
|
|
|
float ratio;
|
|
|
|
|
|
|
|
if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
|
|
|
((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
|
|
|
|
|
|
|
|
windata->src_w = dispdata->mode.hdisplay;
|
|
|
|
windata->src_h = dispdata->mode.vdisplay;
|
|
|
|
windata->output_w = dispdata->mode.hdisplay;
|
|
|
|
windata->output_h = dispdata->mode.vdisplay;
|
|
|
|
windata->output_x = 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* Normal non-fullscreen windows are scaled using the CRTC,
|
|
|
|
so get output (CRTC) size and position, for AR correction. */
|
|
|
|
ratio = (float)window->w / (float)window->h;
|
|
|
|
windata->src_w = window->w;
|
|
|
|
windata->src_h = window->h;
|
|
|
|
windata->output_w = dispdata->mode.vdisplay * ratio;
|
|
|
|
windata->output_h = dispdata->mode.vdisplay;
|
|
|
|
windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!is_vulkan) {
|
|
|
|
if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-12-18 22:53:51 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
int
|
|
|
|
KMSDRM_VideoInit(_THIS)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2018-12-01 19:09:00 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
|
|
|
SDL_DisplayData *dispdata = NULL;
|
|
|
|
SDL_VideoDisplay display = {0};
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
|
|
|
|
|
|
|
|
viddata->video_init = SDL_FALSE;
|
|
|
|
|
|
|
|
dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
|
|
|
|
if (!dispdata) {
|
|
|
|
return SDL_OutOfMemory();
|
2018-12-01 19:09:00 +01:00
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Alloc memory for these. */
|
|
|
|
dispdata->display_plane = SDL_calloc(1, sizeof(*dispdata->display_plane));
|
|
|
|
dispdata->crtc = SDL_calloc(1, sizeof(*dispdata->crtc));
|
|
|
|
dispdata->connector = SDL_calloc(1, sizeof(*dispdata->connector));
|
|
|
|
if (!(dispdata->display_plane) || !(dispdata->crtc) || !(dispdata->connector)) {
|
|
|
|
ret = SDL_OutOfMemory();
|
2017-08-02 19:22:48 +02:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Setup the single display that's available.
|
|
|
|
There's no problem with it being still incomplete. */
|
|
|
|
display.driverdata = dispdata;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* Get KMSDRM resources info and store what we need. Getting and storing
|
|
|
|
this info isn't a problem for VK compatibility.
|
|
|
|
For VK-incompatible initializations we have KMSDRM_GBMInit(), which is
|
|
|
|
called on window creation, and only when we know it's not a VK window. */
|
|
|
|
if (KMSDRM_DisplayDataInit(_this, dispdata)) {
|
|
|
|
ret = SDL_SetError("error getting KMS/DRM information");
|
|
|
|
goto cleanup;
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
display.desktop_mode.w = dispdata->mode.hdisplay;
|
|
|
|
display.desktop_mode.h = dispdata->mode.vdisplay;
|
|
|
|
display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
|
|
|
|
display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
|
|
|
|
display.current_mode = display.desktop_mode;
|
2020-12-18 22:53:51 +01:00
|
|
|
|
|
|
|
/* Add the display only when it's ready, */
|
2020-10-09 01:42:20 +02:00
|
|
|
SDL_AddVideoDisplay(&display, SDL_FALSE);
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-08-05 15:28:51 +02:00
|
|
|
/* Use this if you ever need to see info on all available planes. */
|
|
|
|
#if 0
|
|
|
|
get_planes_info(_this);
|
|
|
|
#endif
|
2020-07-28 21:11:25 +02:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
#ifdef SDL_INPUT_LINUXEV
|
|
|
|
SDL_EVDEV_Init();
|
|
|
|
#endif
|
|
|
|
|
2020-10-22 19:44:38 +02:00
|
|
|
viddata->video_init = SDL_TRUE;
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
cleanup:
|
2020-08-17 18:35:04 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (ret) {
|
|
|
|
if (dispdata->display_plane) {
|
|
|
|
SDL_free(dispdata->display_plane);
|
|
|
|
}
|
|
|
|
if (dispdata->crtc) {
|
|
|
|
SDL_free(dispdata->crtc);
|
|
|
|
}
|
|
|
|
if (dispdata->connector) {
|
|
|
|
SDL_free(dispdata->connector);
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-08-17 18:35:04 +02:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
/* The driverdata pointers, like dispdata, viddata, windata, etc...
|
2020-08-18 01:52:15 +02:00
|
|
|
are freed by SDL internals, so not our job. */
|
2017-08-02 19:22:48 +02:00
|
|
|
void
|
|
|
|
KMSDRM_VideoQuit(_THIS)
|
|
|
|
{
|
2020-02-09 20:44:22 +01:00
|
|
|
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
2020-12-18 22:53:51 +01:00
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
2020-09-07 22:54:15 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
KMSDRM_DisplayDataDeinit(_this, dispdata);
|
2020-09-07 22:54:15 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
#ifdef SDL_INPUT_LINUXEV
|
|
|
|
SDL_EVDEV_Quit();
|
2020-09-07 22:54:15 +02:00
|
|
|
#endif
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Clear out the window list */
|
|
|
|
SDL_free(viddata->windows);
|
|
|
|
viddata->windows = NULL;
|
|
|
|
viddata->max_windows = 0;
|
|
|
|
viddata->num_windows = 0;
|
2020-10-22 19:44:38 +02:00
|
|
|
viddata->video_init = SDL_FALSE;
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-09-07 00:18:13 +02:00
|
|
|
#if 0
|
2017-08-02 19:22:48 +02:00
|
|
|
void
|
|
|
|
KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
|
|
|
|
{
|
2020-08-23 02:58:57 +02:00
|
|
|
/* Only one display mode available: the current one */
|
|
|
|
SDL_AddDisplayMode(display, &display->current_mode);
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-09-07 00:18:13 +02:00
|
|
|
#endif
|
|
|
|
|
2020-09-12 04:52:56 +02:00
|
|
|
/* We only change the video mode for FULLSCREEN windows
|
|
|
|
that are not FULLSCREEN_DESKTOP.
|
|
|
|
Normal non-fullscreen windows are scaled using the CRTC. */
|
2020-09-07 00:18:13 +02:00
|
|
|
void
|
|
|
|
KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
|
|
|
|
{
|
|
|
|
SDL_DisplayData *dispdata = display->driverdata;
|
|
|
|
drmModeConnector *conn = dispdata->connector->connector;
|
|
|
|
SDL_DisplayMode mode;
|
2020-09-26 19:18:09 +02:00
|
|
|
int i;
|
2020-09-07 00:18:13 +02:00
|
|
|
|
2020-09-26 19:18:09 +02:00
|
|
|
for (i = 0; i < conn->count_modes; i++) {
|
2020-09-07 01:33:04 +02:00
|
|
|
SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
|
|
|
|
|
2020-10-22 17:06:34 +02:00
|
|
|
if (!modedata) {
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
return;
|
2020-09-07 01:33:04 +02:00
|
|
|
}
|
2020-10-22 17:06:34 +02:00
|
|
|
|
|
|
|
modedata->mode_index = i;
|
2020-09-07 01:33:04 +02:00
|
|
|
|
2020-09-07 00:18:13 +02:00
|
|
|
mode.w = conn->modes[i].hdisplay;
|
|
|
|
mode.h = conn->modes[i].vdisplay;
|
|
|
|
mode.refresh_rate = conn->modes[i].vrefresh;
|
|
|
|
mode.format = SDL_PIXELFORMAT_ARGB8888;
|
2020-09-07 01:33:04 +02:00
|
|
|
mode.driverdata = modedata;
|
2020-09-07 00:18:13 +02:00
|
|
|
|
2020-09-07 01:33:04 +02:00
|
|
|
if (!SDL_AddDisplayMode(display, &mode)) {
|
|
|
|
SDL_free(modedata);
|
|
|
|
}
|
|
|
|
}
|
2020-09-07 00:18:13 +02:00
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-09-12 00:47:00 +02:00
|
|
|
int
|
|
|
|
KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
|
|
|
|
{
|
2020-09-12 04:52:56 +02:00
|
|
|
/* Set the dispdata->mode to the new mode and leave actual modesetting
|
|
|
|
pending to be done on SwapWindow(), to be included on next atomic
|
|
|
|
commit changeset. */
|
|
|
|
|
|
|
|
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
|
|
|
SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
|
|
|
|
SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
|
|
|
|
drmModeConnector *conn = dispdata->connector->connector;
|
2020-09-26 19:18:09 +02:00
|
|
|
int i;
|
2020-09-12 04:52:56 +02:00
|
|
|
|
|
|
|
if (!modedata) {
|
|
|
|
return SDL_SetError("Mode doesn't have an associated index");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Take note of the new mode. It will be used in SwapWindow to
|
|
|
|
set the props needed for mode setting. */
|
|
|
|
dispdata->mode = conn->modes[modedata->mode_index];
|
|
|
|
|
|
|
|
dispdata->modeset_pending = SDL_TRUE;
|
|
|
|
|
2020-09-26 19:18:09 +02:00
|
|
|
for (i = 0; i < viddata->num_windows; i++) {
|
2020-09-12 04:52:56 +02:00
|
|
|
SDL_Window *window = viddata->windows[i];
|
|
|
|
|
2020-10-22 16:01:51 +02:00
|
|
|
if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2020-09-12 04:52:56 +02:00
|
|
|
|
|
|
|
/* Tell app about the window resize */
|
|
|
|
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
|
|
|
|
}
|
2020-09-12 00:47:00 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
int
|
|
|
|
KMSDRM_CreateWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
2020-08-23 02:58:57 +02:00
|
|
|
SDL_WindowData *windata = NULL;
|
2020-12-18 22:53:51 +01:00
|
|
|
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
|
|
|
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
|
|
|
|
SDL_DisplayData *dispdata = display->driverdata;
|
|
|
|
SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
|
|
|
|
NativeDisplayType egl_display;
|
|
|
|
|
2020-08-28 22:38:26 +02:00
|
|
|
float ratio;
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if ( !(dispdata->gbm_init) && (!is_vulkan)) {
|
|
|
|
/* Reopen FD, create gbm dev, setup display plane, etc,.
|
|
|
|
but only when we come here for the first time,
|
|
|
|
and only if it's not a VK window. */
|
|
|
|
KMSDRM_GBMInit(_this, dispdata);
|
|
|
|
|
|
|
|
/* Manually load the EGL library. KMSDRM_EGL_LoadLibrary() has already
|
|
|
|
been called by SDL_CreateWindow() but we don't do anything there,
|
|
|
|
precisely to be able to load it here.
|
|
|
|
If we let SDL_CreateWindow() load the lib, it will be loaded
|
|
|
|
before we call KMSDRM_GBMInit(), causing GLES programs to fail. */
|
|
|
|
// TODO errorcheck the return value of this.
|
|
|
|
if (!_this->egl_data) {
|
|
|
|
egl_display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
|
|
|
|
SDL_EGL_LoadLibrary(_this, NULL, egl_display, EGL_PLATFORM_GBM_MESA);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Can't init mouse sooner because planes are not ready. */
|
|
|
|
// TODO Add a mouse_init bool and use it to avoid double intializations.
|
|
|
|
KMSDRM_InitMouse(_this);
|
2020-02-09 20:44:22 +01:00
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
|
|
|
/* Allocate window internal data */
|
2020-02-15 01:17:17 +01:00
|
|
|
windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
|
2020-10-22 17:06:34 +02:00
|
|
|
if (!windata) {
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
goto error;
|
|
|
|
}
|
2020-02-09 20:44:22 +01:00
|
|
|
|
2020-09-12 04:52:56 +02:00
|
|
|
if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
|
2020-10-22 17:06:34 +02:00
|
|
|
((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN))
|
|
|
|
{
|
2020-08-31 19:17:17 +02:00
|
|
|
windata->src_w = dispdata->mode.hdisplay;
|
|
|
|
windata->src_h = dispdata->mode.vdisplay;
|
2020-08-28 22:38:26 +02:00
|
|
|
windata->output_w = dispdata->mode.hdisplay;
|
|
|
|
windata->output_h = dispdata->mode.vdisplay;
|
|
|
|
windata->output_x = 0;
|
2020-09-12 04:52:56 +02:00
|
|
|
|
2020-08-28 22:38:26 +02:00
|
|
|
} else {
|
2020-09-12 04:52:56 +02:00
|
|
|
|
|
|
|
/* Normal non-fullscreen windows are scaled using the CRTC,
|
|
|
|
so get output (CRTC) size and position, for AR correction. */
|
2020-08-28 22:38:26 +02:00
|
|
|
ratio = (float)window->w / (float)window->h;
|
2020-08-31 19:17:17 +02:00
|
|
|
windata->src_w = window->w;
|
|
|
|
windata->src_h = window->h;
|
2020-08-28 22:38:26 +02:00
|
|
|
windata->output_w = dispdata->mode.vdisplay * ratio;
|
|
|
|
windata->output_h = dispdata->mode.vdisplay;
|
|
|
|
windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
|
2020-09-12 04:52:56 +02:00
|
|
|
|
2020-08-28 22:38:26 +02:00
|
|
|
}
|
2020-08-23 02:58:57 +02:00
|
|
|
|
|
|
|
/* Don't force fullscreen on all windows: it confuses programs that try
|
|
|
|
to set a window fullscreen after creating it as non-fullscreen (sm64ex) */
|
|
|
|
// window->flags |= SDL_WINDOW_FULLSCREEN;
|
2020-07-19 19:53:57 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Setup driver data for this window */
|
2020-06-19 19:37:14 +02:00
|
|
|
windata->viddata = viddata;
|
2020-02-09 20:44:22 +01:00
|
|
|
window->driverdata = windata;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-12-18 22:53:51 +01:00
|
|
|
if (!is_vulkan) {
|
|
|
|
if (KMSDRM_CreateSurfaces(_this, window)) {
|
|
|
|
goto error;
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
/* Add window to the internal list of tracked windows. Note, while it may
|
|
|
|
seem odd to support multiple fullscreen windows, some apps create an
|
|
|
|
extra window as a dummy surface when working with multiple contexts */
|
|
|
|
if (viddata->num_windows >= viddata->max_windows) {
|
2020-08-24 12:51:20 +02:00
|
|
|
unsigned int new_max_windows = viddata->max_windows + 1;
|
2020-02-09 20:44:22 +01:00
|
|
|
viddata->windows = (SDL_Window **)SDL_realloc(viddata->windows,
|
|
|
|
new_max_windows * sizeof(SDL_Window *));
|
|
|
|
viddata->max_windows = new_max_windows;
|
2017-10-21 13:20:57 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
if (!viddata->windows) {
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-02-09 20:44:22 +01:00
|
|
|
viddata->windows[viddata->num_windows++] = window;
|
2017-08-02 19:22:48 +02:00
|
|
|
|
2020-03-07 17:48:04 +01:00
|
|
|
/* Focus on the newly created window */
|
|
|
|
SDL_SetMouseFocus(window);
|
|
|
|
SDL_SetKeyboardFocus(window);
|
|
|
|
|
2020-09-08 15:30:45 +02:00
|
|
|
/***********************************************************/
|
|
|
|
/* Tell SDL that the mouse has entered the window using an */
|
|
|
|
/* artificial event: we have no windowing system to tell */
|
|
|
|
/* SDL that it has happened. This makes SDL set the */
|
|
|
|
/* SDL_WINDOW_MOUSE_FOCUS on this window, thus fixing */
|
|
|
|
/* Scummvm sticky-on-sides software cursor. */
|
|
|
|
/***********************************************************/
|
|
|
|
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_ENTER, 0, 0);
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
2020-02-09 20:44:22 +01:00
|
|
|
KMSDRM_DestroyWindow(_this, window);
|
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
KMSDRM_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowTitle(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowPosition(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
2020-08-28 23:04:00 +02:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
void
|
|
|
|
KMSDRM_SetWindowSize(_THIS, SDL_Window * window)
|
|
|
|
{
|
2020-09-06 12:08:22 +02:00
|
|
|
if (KMSDRM_ReconfigureWindow(_this, window)) {
|
|
|
|
SDL_SetError("Can't reconfigure window on SetWindowSize.");
|
|
|
|
}
|
2017-08-02 19:22:48 +02:00
|
|
|
}
|
2020-08-28 23:04:00 +02:00
|
|
|
|
2017-08-02 19:22:48 +02:00
|
|
|
void
|
2020-08-28 22:38:26 +02:00
|
|
|
KMSDRM_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
|
|
|
|
{
|
2020-09-06 12:08:22 +02:00
|
|
|
if (KMSDRM_ReconfigureWindow(_this, window)) {
|
|
|
|
SDL_SetError("Can't reconfigure window on SetWindowFullscreen.");
|
|
|
|
}
|
2020-08-28 22:38:26 +02:00
|
|
|
}
|
2020-08-31 19:17:17 +02:00
|
|
|
|
2020-08-28 22:38:26 +02:00
|
|
|
void
|
2017-08-02 19:22:48 +02:00
|
|
|
KMSDRM_ShowWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_HideWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_RaiseWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_MaximizeWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_MinimizeWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_RestoreWindow(_THIS, SDL_Window * window)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
KMSDRM_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* SDL Window Manager function */
|
|
|
|
/*****************************************************************************/
|
|
|
|
SDL_bool
|
|
|
|
KMSDRM_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info)
|
|
|
|
{
|
|
|
|
if (info->version.major <= SDL_MAJOR_VERSION) {
|
|
|
|
return SDL_TRUE;
|
|
|
|
} else {
|
|
|
|
SDL_SetError("application not compiled with SDL %d.%d\n",
|
2020-10-22 16:01:51 +02:00
|
|
|
SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
|
2017-08-02 19:22:48 +02:00
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Failed to get window manager information */
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* SDL_VIDEO_DRIVER_KMSDRM */
|
|
|
|
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|