ee4e78b100
Currently our only entry point for DRM devices is MetaKms*, but in order to run without being DRM master, we cannot use /dev/dri/card*, nor can we be either of the existing MetaKmsImplDevice implementation (legacy KMS, and atomic KMS), as they both depend on being DRM master. Thus to handle running without being DRM master (i.e. headless), add a "dummy" MetaKmsImplDevice implementation, that doesn't do any mode setting at all, and that switches to operate on the render node, instead of the card node itself. This means we still use the same GBM code paths as the regular native backend paths, except we never make use of any CRTC backed onscreen framebuffers. Eventually, this "dummy" MetaKmsImplDevice will be replaced separating "KMS" device objects from "render" device objects, but that will require more significant changes. It will, however, be necessary for e.g. going from being headless, only having access to a render node, to turning into a real session, with a seat, being DRM master, and having access to a card node. This is currently not hooked up, but will be in a later commit. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1698>
596 lines
16 KiB
C
596 lines
16 KiB
C
/*
|
|
* Copyright (C) 2019 Red Hat
|
|
* Copyright (C) 2019 DisplayLink (UK) Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/native/meta-kms-device-private.h"
|
|
#include "backends/native/meta-kms-device.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <xf86drm.h>
|
|
|
|
#include "backends/native/meta-backend-native.h"
|
|
#include "backends/native/meta-kms-impl-device-atomic.h"
|
|
#include "backends/native/meta-kms-impl-device-dummy.h"
|
|
#include "backends/native/meta-kms-impl-device-simple.h"
|
|
#include "backends/native/meta-kms-impl-device.h"
|
|
#include "backends/native/meta-kms-impl.h"
|
|
#include "backends/native/meta-kms-plane.h"
|
|
#include "backends/native/meta-kms-private.h"
|
|
|
|
struct _MetaKmsDevice
|
|
{
|
|
GObject parent;
|
|
|
|
MetaKms *kms;
|
|
|
|
MetaKmsImplDevice *impl_device;
|
|
|
|
MetaKmsDeviceFlag flags;
|
|
char *path;
|
|
char *driver_name;
|
|
char *driver_description;
|
|
|
|
GList *crtcs;
|
|
GList *connectors;
|
|
GList *planes;
|
|
|
|
MetaKmsDeviceCaps caps;
|
|
|
|
GList *fallback_modes;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaKmsDevice, meta_kms_device, G_TYPE_OBJECT);
|
|
|
|
MetaKms *
|
|
meta_kms_device_get_kms (MetaKmsDevice *device)
|
|
{
|
|
return device->kms;
|
|
}
|
|
|
|
MetaKmsImplDevice *
|
|
meta_kms_device_get_impl_device (MetaKmsDevice *device)
|
|
{
|
|
return device->impl_device;
|
|
}
|
|
|
|
int
|
|
meta_kms_device_leak_fd (MetaKmsDevice *device)
|
|
{
|
|
return meta_kms_impl_device_leak_fd (device->impl_device);
|
|
}
|
|
|
|
const char *
|
|
meta_kms_device_get_path (MetaKmsDevice *device)
|
|
{
|
|
return device->path;
|
|
}
|
|
|
|
const char *
|
|
meta_kms_device_get_driver_name (MetaKmsDevice *device)
|
|
{
|
|
return device->driver_name;
|
|
}
|
|
|
|
const char *
|
|
meta_kms_device_get_driver_description (MetaKmsDevice *device)
|
|
{
|
|
return device->driver_description;
|
|
}
|
|
|
|
MetaKmsDeviceFlag
|
|
meta_kms_device_get_flags (MetaKmsDevice *device)
|
|
{
|
|
return device->flags;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_device_get_cursor_size (MetaKmsDevice *device,
|
|
uint64_t *out_cursor_width,
|
|
uint64_t *out_cursor_height)
|
|
{
|
|
if (device->caps.has_cursor_size)
|
|
{
|
|
*out_cursor_width = device->caps.cursor_width;
|
|
*out_cursor_height = device->caps.cursor_height;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
GList *
|
|
meta_kms_device_get_connectors (MetaKmsDevice *device)
|
|
{
|
|
return device->connectors;
|
|
}
|
|
|
|
GList *
|
|
meta_kms_device_get_crtcs (MetaKmsDevice *device)
|
|
{
|
|
return device->crtcs;
|
|
}
|
|
|
|
GList *
|
|
meta_kms_device_get_planes (MetaKmsDevice *device)
|
|
{
|
|
return device->planes;
|
|
}
|
|
|
|
static MetaKmsPlane *
|
|
get_plane_with_type_for (MetaKmsDevice *device,
|
|
MetaKmsCrtc *crtc,
|
|
MetaKmsPlaneType type)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = meta_kms_device_get_planes (device); l; l = l->next)
|
|
{
|
|
MetaKmsPlane *plane = l->data;
|
|
|
|
if (meta_kms_plane_get_plane_type (plane) != type)
|
|
continue;
|
|
|
|
if (meta_kms_plane_is_usable_with (plane, crtc))
|
|
return plane;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
MetaKmsPlane *
|
|
meta_kms_device_get_primary_plane_for (MetaKmsDevice *device,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
return get_plane_with_type_for (device, crtc, META_KMS_PLANE_TYPE_PRIMARY);
|
|
}
|
|
|
|
MetaKmsPlane *
|
|
meta_kms_device_get_cursor_plane_for (MetaKmsDevice *device,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
return get_plane_with_type_for (device, crtc, META_KMS_PLANE_TYPE_CURSOR);
|
|
}
|
|
|
|
GList *
|
|
meta_kms_device_get_fallback_modes (MetaKmsDevice *device)
|
|
{
|
|
return device->fallback_modes;
|
|
}
|
|
|
|
void
|
|
meta_kms_device_update_states_in_impl (MetaKmsDevice *device)
|
|
{
|
|
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
|
|
|
|
meta_assert_in_kms_impl (device->kms);
|
|
meta_assert_is_waiting_for_kms_impl_task (device->kms);
|
|
|
|
meta_kms_impl_device_update_states (impl_device);
|
|
|
|
g_list_free (device->crtcs);
|
|
device->crtcs = meta_kms_impl_device_copy_crtcs (impl_device);
|
|
|
|
g_list_free (device->connectors);
|
|
device->connectors = meta_kms_impl_device_copy_connectors (impl_device);
|
|
|
|
g_list_free (device->planes);
|
|
device->planes = meta_kms_impl_device_copy_planes (impl_device);
|
|
}
|
|
|
|
void
|
|
meta_kms_device_predict_states_in_impl (MetaKmsDevice *device,
|
|
MetaKmsUpdate *update)
|
|
{
|
|
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
|
|
|
|
meta_assert_in_kms_impl (device->kms);
|
|
|
|
meta_kms_impl_device_predict_states (impl_device, update);
|
|
}
|
|
|
|
void
|
|
meta_kms_device_add_fake_plane_in_impl (MetaKmsDevice *device,
|
|
MetaKmsPlaneType plane_type,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
MetaKmsImplDevice *impl_device = device->impl_device;
|
|
MetaKmsPlane *plane;
|
|
|
|
meta_assert_in_kms_impl (device->kms);
|
|
|
|
plane = meta_kms_impl_device_add_fake_plane (impl_device,
|
|
plane_type,
|
|
crtc);
|
|
device->planes = g_list_append (device->planes, plane);
|
|
}
|
|
|
|
typedef struct _CreateImplDeviceData
|
|
{
|
|
MetaKmsDevice *device;
|
|
int fd;
|
|
const char *path;
|
|
MetaKmsDeviceFlag flags;
|
|
|
|
MetaKmsImplDevice *out_impl_device;
|
|
GList *out_crtcs;
|
|
GList *out_connectors;
|
|
GList *out_planes;
|
|
MetaKmsDeviceCaps out_caps;
|
|
GList *out_fallback_modes;
|
|
char *out_driver_name;
|
|
char *out_driver_description;
|
|
char *out_path;
|
|
} CreateImplDeviceData;
|
|
|
|
static gboolean
|
|
is_atomic_allowed (const char *driver_name)
|
|
{
|
|
const char *atomic_driver_deny_list[] = {
|
|
"qxl",
|
|
"vmwgfx",
|
|
"vboxvideo",
|
|
"nvidia-drm",
|
|
NULL,
|
|
};
|
|
|
|
return !g_strv_contains (atomic_driver_deny_list, driver_name);
|
|
}
|
|
|
|
static gboolean
|
|
get_driver_info (int fd,
|
|
char **name,
|
|
char **description)
|
|
{
|
|
drmVersion *drm_version;
|
|
|
|
drm_version = drmGetVersion (fd);
|
|
if (!drm_version)
|
|
return FALSE;
|
|
|
|
*name = g_strndup (drm_version->name,
|
|
drm_version->name_len);
|
|
*description = g_strndup (drm_version->desc,
|
|
drm_version->desc_len);
|
|
drmFreeVersion (drm_version);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static MetaKmsImplDevice *
|
|
meta_create_kms_impl_device (MetaKmsDevice *device,
|
|
MetaKmsImpl *impl,
|
|
int fd,
|
|
const char *path,
|
|
MetaKmsDeviceFlag flags,
|
|
GError **error)
|
|
{
|
|
GType impl_device_type;
|
|
gboolean supports_atomic_mode_setting;
|
|
g_autofree char *driver_name = NULL;
|
|
g_autofree char *driver_description = NULL;
|
|
const char *atomic_kms_enable_env;
|
|
int impl_fd;
|
|
g_autofree char *impl_path = NULL;
|
|
MetaKmsImplDevice *impl_device;
|
|
|
|
meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl));
|
|
|
|
if (!get_driver_info (fd, &driver_name, &driver_description))
|
|
{
|
|
driver_name = g_strdup ("unknown");
|
|
driver_description = g_strdup ("Unknown");
|
|
}
|
|
|
|
atomic_kms_enable_env = getenv ("MUTTER_DEBUG_ENABLE_ATOMIC_KMS");
|
|
|
|
if (flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING)
|
|
{
|
|
g_autofree char *render_node_path = NULL;
|
|
|
|
render_node_path = drmGetRenderDeviceNameFromFd (fd);
|
|
if (!render_node_path)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Couldn't find render node device for '%s' (%s)",
|
|
path, driver_name);
|
|
return NULL;
|
|
}
|
|
|
|
impl_fd = open (render_node_path, O_RDWR | O_CLOEXEC, 0);
|
|
if (impl_fd == -1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
|
"Failed to open render node '%s': %s",
|
|
render_node_path, g_strerror (errno));
|
|
return NULL;
|
|
}
|
|
|
|
g_message ("Adding device '%s' (from '%s', %s) using no mode setting.",
|
|
render_node_path, path, driver_name);
|
|
|
|
impl_path = g_steal_pointer (&render_node_path);
|
|
impl_device_type = META_TYPE_KMS_IMPL_DEVICE_DUMMY;
|
|
}
|
|
else if (atomic_kms_enable_env)
|
|
{
|
|
if (g_strcmp0 (atomic_kms_enable_env, "1") == 0)
|
|
{
|
|
impl_device_type = META_TYPE_KMS_IMPL_DEVICE_ATOMIC;
|
|
if (drmSetClientCap (fd, DRM_CLIENT_CAP_ATOMIC, 1) != 0)
|
|
{
|
|
g_error ("Failed to force atomic mode setting on '%s' (%s).",
|
|
path, driver_name);
|
|
}
|
|
}
|
|
else if (g_strcmp0 (atomic_kms_enable_env, "0") == 0)
|
|
{
|
|
impl_device_type = META_TYPE_KMS_IMPL_DEVICE_SIMPLE;
|
|
}
|
|
else
|
|
{
|
|
g_error ("Invalid value '%s' for MUTTER_DEBUG_ENABLE_ATOMIC_KMS, "
|
|
"bailing.",
|
|
atomic_kms_enable_env);
|
|
}
|
|
|
|
impl_fd = dup (fd);
|
|
impl_path = g_strdup (path);
|
|
|
|
g_message ("Mode setting implementation for '%s' (%s) forced (%s).",
|
|
path, driver_name,
|
|
impl_device_type == META_TYPE_KMS_IMPL_DEVICE_ATOMIC ?
|
|
"atomic" : "non-atomic");
|
|
}
|
|
else if (!is_atomic_allowed (driver_name))
|
|
{
|
|
g_message ("Adding device '%s' (%s) using non-atomic mode setting"
|
|
" (using atomic mode setting not allowed).",
|
|
path, driver_name);
|
|
impl_fd = dup (fd);
|
|
impl_path = g_strdup (path);
|
|
impl_device_type = META_TYPE_KMS_IMPL_DEVICE_SIMPLE;
|
|
}
|
|
else
|
|
{
|
|
int ret;
|
|
|
|
ret = drmSetClientCap (fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
if (ret == 0)
|
|
supports_atomic_mode_setting = TRUE;
|
|
else
|
|
supports_atomic_mode_setting = FALSE;
|
|
|
|
if (supports_atomic_mode_setting)
|
|
{
|
|
g_message ("Adding device '%s' (%s) using atomic mode setting.",
|
|
path, driver_name);
|
|
impl_device_type = META_TYPE_KMS_IMPL_DEVICE_ATOMIC;
|
|
}
|
|
else
|
|
{
|
|
g_message ("Adding device '%s' (%s) using non-atomic mode setting.",
|
|
path, driver_name);
|
|
impl_device_type = META_TYPE_KMS_IMPL_DEVICE_SIMPLE;
|
|
}
|
|
|
|
impl_fd = dup (fd);
|
|
impl_path = g_strdup (path);
|
|
}
|
|
|
|
impl_device = g_initable_new (impl_device_type, NULL, error,
|
|
"device", device,
|
|
"impl", impl,
|
|
"fd", impl_fd,
|
|
"path", impl_path,
|
|
"flags", flags,
|
|
"driver-name", driver_name,
|
|
"driver-description", driver_description,
|
|
NULL);
|
|
if (!impl_device)
|
|
{
|
|
close (impl_fd);
|
|
return NULL;
|
|
}
|
|
|
|
close (fd);
|
|
|
|
return impl_device;
|
|
}
|
|
|
|
static gpointer
|
|
create_impl_device_in_impl (MetaKmsImpl *impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
CreateImplDeviceData *data = user_data;
|
|
MetaKmsImplDevice *impl_device;
|
|
|
|
impl_device = meta_create_kms_impl_device (data->device,
|
|
impl,
|
|
data->fd,
|
|
data->path,
|
|
data->flags,
|
|
error);
|
|
if (!impl_device)
|
|
return FALSE;
|
|
|
|
meta_kms_impl_add_impl_device (impl, impl_device);
|
|
|
|
data->out_impl_device = impl_device;
|
|
data->out_crtcs = meta_kms_impl_device_copy_crtcs (impl_device);
|
|
data->out_connectors = meta_kms_impl_device_copy_connectors (impl_device);
|
|
data->out_planes = meta_kms_impl_device_copy_planes (impl_device);
|
|
data->out_caps = *meta_kms_impl_device_get_caps (impl_device);
|
|
data->out_fallback_modes =
|
|
meta_kms_impl_device_copy_fallback_modes (impl_device);
|
|
data->out_driver_name =
|
|
g_strdup (meta_kms_impl_device_get_driver_name (impl_device));
|
|
data->out_driver_description =
|
|
g_strdup (meta_kms_impl_device_get_driver_description (impl_device));
|
|
data->out_path = g_strdup (meta_kms_impl_device_get_path (impl_device));
|
|
|
|
return GINT_TO_POINTER (TRUE);
|
|
}
|
|
|
|
MetaKmsDevice *
|
|
meta_kms_device_new (MetaKms *kms,
|
|
const char *path,
|
|
MetaKmsDeviceFlag flags,
|
|
GError **error)
|
|
{
|
|
MetaBackend *backend = meta_kms_get_backend (kms);
|
|
MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
|
|
MetaLauncher *launcher = meta_backend_native_get_launcher (backend_native);
|
|
MetaKmsDevice *device;
|
|
CreateImplDeviceData data;
|
|
int fd;
|
|
|
|
if (flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING)
|
|
{
|
|
fd = open (path, O_RDWR | O_CLOEXEC, 0);
|
|
if (fd == -1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
|
"Failed to open DRM device: %s", g_strerror (errno));
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fd = meta_launcher_open_restricted (launcher, path, error);
|
|
if (fd == -1)
|
|
return NULL;
|
|
}
|
|
|
|
device = g_object_new (META_TYPE_KMS_DEVICE, NULL);
|
|
device->kms = kms;
|
|
|
|
data = (CreateImplDeviceData) {
|
|
.device = device,
|
|
.fd = fd,
|
|
.path = path,
|
|
.flags = flags,
|
|
};
|
|
if (!meta_kms_run_impl_task_sync (kms, create_impl_device_in_impl, &data,
|
|
error))
|
|
{
|
|
if (flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING)
|
|
close (fd);
|
|
else
|
|
meta_launcher_close_restricted (launcher, fd);
|
|
g_object_unref (device);
|
|
return NULL;
|
|
}
|
|
|
|
device->impl_device = data.out_impl_device;
|
|
device->flags = flags;
|
|
device->path = g_strdup (path);
|
|
device->crtcs = data.out_crtcs;
|
|
device->connectors = data.out_connectors;
|
|
device->planes = data.out_planes;
|
|
device->caps = data.out_caps;
|
|
device->fallback_modes = data.out_fallback_modes;
|
|
device->driver_name = data.out_driver_name;
|
|
device->driver_description = data.out_driver_description;
|
|
free (device->path);
|
|
device->path = data.out_path;
|
|
|
|
return device;
|
|
}
|
|
|
|
typedef struct _FreeImplDeviceData
|
|
{
|
|
MetaKmsImplDevice *impl_device;
|
|
|
|
int out_fd;
|
|
} FreeImplDeviceData;
|
|
|
|
static gpointer
|
|
free_impl_device_in_impl (MetaKmsImpl *impl,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
FreeImplDeviceData *data = user_data;
|
|
MetaKmsImplDevice *impl_device = data->impl_device;
|
|
int fd;
|
|
|
|
fd = meta_kms_impl_device_close (impl_device);
|
|
g_object_unref (impl_device);
|
|
|
|
data->out_fd = fd;
|
|
|
|
return GINT_TO_POINTER (TRUE);
|
|
}
|
|
|
|
static void
|
|
meta_kms_device_finalize (GObject *object)
|
|
{
|
|
MetaKmsDevice *device = META_KMS_DEVICE (object);
|
|
MetaBackend *backend = meta_kms_get_backend (device->kms);
|
|
MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend);
|
|
MetaLauncher *launcher = meta_backend_native_get_launcher (backend_native);
|
|
|
|
g_free (device->path);
|
|
g_list_free (device->crtcs);
|
|
g_list_free (device->connectors);
|
|
g_list_free (device->planes);
|
|
|
|
if (device->impl_device)
|
|
{
|
|
FreeImplDeviceData data;
|
|
GError *error = NULL;
|
|
|
|
data = (FreeImplDeviceData) {
|
|
.impl_device = device->impl_device,
|
|
};
|
|
if (!meta_kms_run_impl_task_sync (device->kms, free_impl_device_in_impl, &data,
|
|
&error))
|
|
{
|
|
g_warning ("Failed to close KMS impl device: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
else
|
|
{
|
|
if (device->flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING)
|
|
close (data.out_fd);
|
|
else
|
|
meta_launcher_close_restricted (launcher, data.out_fd);
|
|
}
|
|
}
|
|
G_OBJECT_CLASS (meta_kms_device_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_kms_device_init (MetaKmsDevice *device)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_kms_device_class_init (MetaKmsDeviceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_kms_device_finalize;
|
|
}
|