/* * Copyright (C) 2019 Red Hat * * 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, see . */ #include "config.h" #include "backends/native/meta-kms-impl-device.h" #include #include #include #include #include "backends/native/meta-backend-native.h" #include "backends/native/meta-device-pool.h" #include "backends/native/meta-kms-connector-private.h" #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-crtc-private.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-mode-private.h" #include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms-plane-private.h" #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-private.h" #include "backends/native/meta-thread-private.h" #include "meta-default-modes.h" #include "meta-private-enum-types.h" enum { PROP_0, PROP_DEVICE, PROP_IMPL, PROP_PATH, PROP_FLAGS, N_PROPS }; static GParamSpec *obj_props[N_PROPS]; typedef struct _CrtcDeadline { MetaKmsImplDevice *impl_device; MetaKmsCrtc *crtc; MetaKmsUpdate *pending_update; gboolean await_flush; gboolean pending_page_flip; struct { int timer_fd; GSource *source; gboolean armed; gboolean is_deadline_page_flip; int64_t expected_presentation_time_us; } deadline; } CrtcFrame; typedef struct _MetaKmsImplDevicePrivate { MetaKmsDevice *device; MetaKmsImpl *impl; int fd_hold_count; MetaDeviceFile *device_file; GSource *fd_source; char *path; MetaKmsDeviceFlag flags; gboolean has_latched_fd_hold; char *driver_name; char *driver_description; GList *crtcs; GList *connectors; GList *planes; MetaKmsDeviceCaps caps; GList *fallback_modes; GHashTable *crtc_frames; gboolean deadline_timer_inhibited; } MetaKmsImplDevicePrivate; static void initable_iface_init (GInitableIface *iface); static CrtcFrame * get_crtc_frame (MetaKmsImplDevice *impl_device, MetaKmsCrtc *latch_crtc); G_DEFINE_TYPE_WITH_CODE (MetaKmsImplDevice, meta_kms_impl_device, G_TYPE_OBJECT, G_ADD_PRIVATE (MetaKmsImplDevice) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) MetaKmsImpl * meta_kms_impl_device_get_impl (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->impl; } MetaKmsDevice * meta_kms_impl_device_get_device (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->device; } GList * meta_kms_impl_device_copy_connectors (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return g_list_copy (priv->connectors); } GList * meta_kms_impl_device_copy_crtcs (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return g_list_copy (priv->crtcs); } GList * meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return g_list_copy (priv->planes); } GList * meta_kms_impl_device_peek_connectors (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->connectors; } GList * meta_kms_impl_device_peek_crtcs (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->crtcs; } GList * meta_kms_impl_device_peek_planes (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->planes; } const MetaKmsDeviceCaps * meta_kms_impl_device_get_caps (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return &priv->caps; } GList * meta_kms_impl_device_copy_fallback_modes (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return g_list_copy (priv->fallback_modes); } const char * meta_kms_impl_device_get_driver_name (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->driver_name; } const char * meta_kms_impl_device_get_driver_description (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->driver_description; } const char * meta_kms_impl_device_get_path (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return priv->path; } gboolean meta_kms_impl_device_dispatch (MetaKmsImplDevice *impl_device, GError **error) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); int fd; drmEventContext drm_event_context; meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl)); drm_event_context = (drmEventContext) { 0 }; klass->setup_drm_event_context (impl_device, &drm_event_context); fd = meta_device_file_get_fd (priv->device_file); while (TRUE) { if (drmHandleEvent (fd, &drm_event_context) != 0) { struct pollfd pfd; int ret; if (errno != EAGAIN) { g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), strerror (errno)); return FALSE; } pfd.fd = fd; pfd.events = POLL_IN | POLL_ERR; do { ret = poll (&pfd, 1, -1); } while (ret == -1 && errno == EINTR); } else { break; } } return TRUE; } static gpointer kms_event_dispatch_in_impl (MetaThreadImpl *impl, gpointer user_data, GError **error) { MetaKmsImplDevice *impl_device = user_data; gboolean ret; ret = meta_kms_impl_device_dispatch (impl_device, error); return GINT_TO_POINTER (ret); } drmModePropertyPtr meta_kms_impl_device_find_property (MetaKmsImplDevice *impl_device, drmModeObjectProperties *props, const char *prop_name, int *out_idx) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); int fd; unsigned int i; meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl)); fd = meta_device_file_get_fd (priv->device_file); for (i = 0; i < props->count_props; i++) { drmModePropertyPtr prop; prop = drmModeGetProperty (fd, props->props[i]); if (!prop) continue; if (strcmp (prop->name, prop_name) == 0) { *out_idx = i; return prop; } drmModeFreeProperty (prop); } return NULL; } static void init_caps (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); int fd; uint64_t cursor_width, cursor_height; uint64_t prefer_shadow; uint64_t uses_monotonic_clock; uint64_t addfb2_modifiers; fd = meta_device_file_get_fd (priv->device_file); if (drmGetCap (fd, DRM_CAP_CURSOR_WIDTH, &cursor_width) == 0 && drmGetCap (fd, DRM_CAP_CURSOR_HEIGHT, &cursor_height) == 0) { priv->caps.has_cursor_size = TRUE; priv->caps.cursor_width = cursor_width; priv->caps.cursor_height = cursor_height; } if (drmGetCap (fd, DRM_CAP_DUMB_PREFER_SHADOW, &prefer_shadow) == 0) { if (prefer_shadow) g_message ("Device '%s' prefers shadow buffer", priv->path); priv->caps.prefers_shadow_buffer = prefer_shadow; } if (drmGetCap (fd, DRM_CAP_TIMESTAMP_MONOTONIC, &uses_monotonic_clock) == 0) { priv->caps.uses_monotonic_clock = uses_monotonic_clock; } if (drmGetCap (fd, DRM_CAP_ADDFB2_MODIFIERS, &addfb2_modifiers) == 0) { priv->caps.addfb2_modifiers = (addfb2_modifiers != 0); } } static void init_crtcs (MetaKmsImplDevice *impl_device, drmModeRes *drm_resources) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); int idx; int fd; fd = meta_device_file_get_fd (priv->device_file); for (idx = 0; idx < drm_resources->count_crtcs; idx++) { uint32_t crtc_id; drmModeCrtc *drm_crtc; MetaKmsCrtc *crtc; g_autoptr (GError) error = NULL; crtc_id = drm_resources->crtcs[idx]; drm_crtc = drmModeGetCrtc (fd, crtc_id); if (!drm_crtc) { g_warning ("Failed to get CRTC %u info on '%s': %s", crtc_id, priv->path, error->message); continue; } crtc = meta_kms_crtc_new (impl_device, drm_crtc, idx, &error); drmModeFreeCrtc (drm_crtc); if (!crtc) { g_warning ("Failed to create CRTC for %u on '%s': %s", crtc_id, priv->path, error->message); continue; } priv->crtcs = g_list_prepend (priv->crtcs, crtc); } priv->crtcs = g_list_reverse (priv->crtcs); } static MetaKmsConnector * find_existing_connector (MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); GList *l; for (l = priv->connectors; l; l = l->next) { MetaKmsConnector *connector = l->data; if (meta_kms_connector_is_same_as (connector, drm_connector)) return connector; } return NULL; } static MetaKmsResourceChanges update_connectors (MetaKmsImplDevice *impl_device, drmModeRes *drm_resources, uint32_t updated_connector_id) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); g_autolist (MetaKmsConnector) connectors = NULL; gboolean added_connector = FALSE; MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE; unsigned int i; int fd; fd = meta_device_file_get_fd (priv->device_file); for (i = 0; i < drm_resources->count_connectors; i++) { drmModeConnector *drm_connector; MetaKmsConnector *connector; drm_connector = drmModeGetConnector (fd, drm_resources->connectors[i]); if (!drm_connector) continue; connector = find_existing_connector (impl_device, drm_connector); if (connector) { connector = g_object_ref (connector); if (updated_connector_id == 0 || meta_kms_connector_get_id (connector) == updated_connector_id) { changes |= meta_kms_connector_update_state_in_impl (connector, drm_resources, drm_connector); } } else { connector = meta_kms_connector_new (impl_device, drm_connector, drm_resources); added_connector = TRUE; } drmModeFreeConnector (drm_connector); connectors = g_list_prepend (connectors, connector); } if (!added_connector && g_list_length (connectors) == g_list_length (priv->connectors)) return changes; g_list_free_full (priv->connectors, g_object_unref); priv->connectors = g_list_reverse (g_steal_pointer (&connectors)); return META_KMS_RESOURCE_CHANGE_FULL; } static MetaKmsPlaneType get_plane_type (MetaKmsImplDevice *impl_device, drmModeObjectProperties *props) { drmModePropertyPtr prop; int idx; prop = meta_kms_impl_device_find_property (impl_device, props, "type", &idx); if (!prop) return FALSE; drmModeFreeProperty (prop); switch (props->prop_values[idx]) { case DRM_PLANE_TYPE_PRIMARY: return META_KMS_PLANE_TYPE_PRIMARY; case DRM_PLANE_TYPE_CURSOR: return META_KMS_PLANE_TYPE_CURSOR; case DRM_PLANE_TYPE_OVERLAY: return META_KMS_PLANE_TYPE_OVERLAY; default: g_warning ("Unhandled plane type %" G_GUINT64_FORMAT, props->prop_values[idx]); return -1; } } MetaKmsPlane * meta_kms_impl_device_add_fake_plane (MetaKmsImplDevice *impl_device, MetaKmsPlaneType plane_type, MetaKmsCrtc *crtc) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsPlane *plane; plane = meta_kms_plane_new_fake (plane_type, crtc); priv->planes = g_list_append (priv->planes, plane); return plane; } uint64_t meta_kms_prop_convert_value (MetaKmsProp *prop, uint64_t value) { switch (prop->type) { case DRM_MODE_PROP_RANGE: case DRM_MODE_PROP_SIGNED_RANGE: case DRM_MODE_PROP_BLOB: case DRM_MODE_PROP_OBJECT: return value; case DRM_MODE_PROP_ENUM: g_assert (prop->enum_values[value].valid); return prop->enum_values[value].value; case DRM_MODE_PROP_BITMASK: { int i; uint64_t result = 0; for (i = 0; i < prop->num_enum_values; i++) { if (!prop->enum_values[i].valid) continue; if (value & prop->enum_values[i].bitmask) { result |= (1 << prop->enum_values[i].value); value &= ~(prop->enum_values[i].bitmask); } } g_assert (value == 0); return result; } default: g_assert_not_reached (); } return 0; } static void update_prop_value (MetaKmsProp *prop, uint64_t drm_value) { switch (prop->type) { case DRM_MODE_PROP_RANGE: case DRM_MODE_PROP_SIGNED_RANGE: case DRM_MODE_PROP_BLOB: case DRM_MODE_PROP_OBJECT: prop->value = drm_value; return; case DRM_MODE_PROP_ENUM: { int i; uint64_t result = prop->default_value; uint64_t supported = 0; for (i = 0; i < prop->num_enum_values; i++) { if (!prop->enum_values[i].valid) continue; if (prop->enum_values[i].value == drm_value) { result = i; } supported |= (1 << i); } prop->value = result; prop->supported_variants = supported; return; } case DRM_MODE_PROP_BITMASK: { int i; uint64_t result = 0; uint64_t supported = 0; for (i = 0; i < prop->num_enum_values; i++) { if (!prop->enum_values[i].valid) continue; if (drm_value & (1 << prop->enum_values[i].value)) { result |= prop->enum_values[i].bitmask; drm_value &= ~(1 << prop->enum_values[i].value); } supported |= prop->enum_values[i].bitmask; } if (drm_value != 0) result |= prop->default_value; prop->value = result; prop->supported_variants = supported; return; } default: g_assert_not_reached (); } } static void update_prop_enum_value(MetaKmsEnum *prop_enum, drmModePropertyRes *drm_prop) { int i; for (i = 0; i < drm_prop->count_enums; i++) { if (strcmp (prop_enum->name, drm_prop->enums[i].name) == 0) { prop_enum->value = drm_prop->enums[i].value; prop_enum->valid = TRUE; return; } } prop_enum->valid = FALSE; } static MetaKmsProp * find_prop (MetaKmsProp *props, int n_props, const char *name) { int i; for (i = 0; i < n_props; i++) { MetaKmsProp *prop = &props[i]; g_warn_if_fail (prop->name); if (g_strcmp0 (prop->name, name) == 0) return prop; } return NULL; } void meta_kms_impl_device_update_prop_table (MetaKmsImplDevice *impl_device, uint32_t *drm_props, uint64_t *drm_prop_values, int n_drm_props, MetaKmsProp *props, int n_props) { int fd; uint32_t i, j; fd = meta_kms_impl_device_get_fd (impl_device); for (i = 0; i < n_props; i++) { MetaKmsProp *prop = &props[i]; prop->prop_id = 0; prop->value = 0; for (j = 0; j < prop->num_enum_values; j++) { prop->enum_values[j].valid = FALSE; prop->enum_values[j].value = 0; } } for (i = 0; i < n_drm_props; i++) { uint32_t prop_id; uint64_t prop_value; drmModePropertyRes *drm_prop; MetaKmsProp *prop; prop_id = drm_props[i]; prop_value = drm_prop_values[i]; drm_prop = drmModeGetProperty (fd, prop_id); if (!drm_prop) continue; prop = find_prop (props, n_props, drm_prop->name); if (!prop) { drmModeFreeProperty (drm_prop); continue; } if (!(drm_prop->flags & prop->type)) { g_warning ("DRM property '%s' (%u) had unexpected flags (0x%x), " "ignoring", drm_prop->name, prop_id, drm_prop->flags); drmModeFreeProperty (drm_prop); continue; } prop->prop_id = prop_id; if (prop->type == DRM_MODE_PROP_BITMASK || prop->type == DRM_MODE_PROP_ENUM) { for (j = 0; j < prop->num_enum_values; j++) update_prop_enum_value (&prop->enum_values[j], drm_prop); } update_prop_value (prop, prop_value); if (prop->type == DRM_MODE_PROP_RANGE) { if (drm_prop->count_values == 2) { prop->range_min = drm_prop->values[0]; prop->range_max = drm_prop->values[1]; } else { g_warning ("DRM property '%s' is a range with %d values, ignoring", drm_prop->name, drm_prop->count_values); } } if (prop->type == DRM_MODE_PROP_SIGNED_RANGE) { if (drm_prop->count_values == 2) { prop->range_min_signed = (int64_t) drm_prop->values[0]; prop->range_max_signed = (int64_t) drm_prop->values[1]; } else { g_warning ("DRM property '%s' is a signed range with %d values, ignoring", drm_prop->name, drm_prop->count_values); } } drmModeFreeProperty (drm_prop); } } static void init_planes (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); int fd; drmModePlaneRes *drm_planes; unsigned int i; fd = meta_device_file_get_fd (priv->device_file); drm_planes = drmModeGetPlaneResources (fd); if (!drm_planes) return; for (i = 0; i < drm_planes->count_planes; i++) { drmModePlane *drm_plane; drmModeObjectProperties *props; drm_plane = drmModeGetPlane (fd, drm_planes->planes[i]); if (!drm_plane) continue; props = drmModeObjectGetProperties (fd, drm_plane->plane_id, DRM_MODE_OBJECT_PLANE); if (props) { MetaKmsPlaneType plane_type; plane_type = get_plane_type (impl_device, props); if (plane_type != -1) { MetaKmsPlane *plane; plane = meta_kms_plane_new (plane_type, impl_device, drm_plane, props); priv->planes = g_list_prepend (priv->planes, plane); } } g_clear_pointer (&props, drmModeFreeObjectProperties); drmModeFreePlane (drm_plane); } priv->planes = g_list_reverse (priv->planes); } static void init_fallback_modes (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); GList *modes = NULL; int i; for (i = 0; i < G_N_ELEMENTS (meta_default_landscape_drm_mode_infos); i++) { MetaKmsMode *mode; mode = meta_kms_mode_new (impl_device, &meta_default_landscape_drm_mode_infos[i], META_KMS_MODE_FLAG_FALLBACK_LANDSCAPE); modes = g_list_prepend (modes, mode); } for (i = 0; i < G_N_ELEMENTS (meta_default_portrait_drm_mode_infos); i++) { MetaKmsMode *mode; mode = meta_kms_mode_new (impl_device, &meta_default_portrait_drm_mode_infos[i], META_KMS_MODE_FLAG_FALLBACK_PORTRAIT); modes = g_list_prepend (modes, mode); } priv->fallback_modes = g_list_reverse (modes); } static MetaDeviceFile * meta_kms_impl_device_open_device_file (MetaKmsImplDevice *impl_device, const char *path, GError **error) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); return klass->open_device_file (impl_device, priv->path, error); } static gboolean ensure_device_file (MetaKmsImplDevice *impl_device, GError **error) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaDeviceFile *device_file; if (priv->device_file) return TRUE; device_file = meta_kms_impl_device_open_device_file (impl_device, priv->path, error); if (!device_file) return FALSE; priv->device_file = device_file; if (!(priv->flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING)) { priv->fd_source = meta_thread_impl_register_fd (META_THREAD_IMPL (priv->impl), meta_device_file_get_fd (device_file), kms_event_dispatch_in_impl, impl_device); g_source_set_priority (priv->fd_source, G_PRIORITY_HIGH); } return TRUE; } static void ensure_latched_fd_hold (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); if (!priv->has_latched_fd_hold) { meta_kms_impl_device_hold_fd (impl_device); priv->has_latched_fd_hold = TRUE; } } static void clear_latched_fd_hold (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); if (priv->has_latched_fd_hold) { meta_kms_impl_device_unhold_fd (impl_device); priv->has_latched_fd_hold = FALSE; } } MetaKmsResourceChanges meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device, uint32_t crtc_id, uint32_t connector_id) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); g_autoptr (GError) error = NULL; int fd; drmModeRes *drm_resources; MetaKmsResourceChanges changes; GList *l; meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl)); meta_topic (META_DEBUG_KMS, "Updating device state for %s", priv->path); if (!ensure_device_file (impl_device, &error)) { g_warning ("Failed to reopen '%s': %s", priv->path, error->message); goto err; } ensure_latched_fd_hold (impl_device); fd = meta_device_file_get_fd (priv->device_file); drm_resources = drmModeGetResources (fd); if (!drm_resources) { meta_topic (META_DEBUG_KMS, "Device '%s' didn't return any resources", priv->path); goto err; } changes = update_connectors (impl_device, drm_resources, connector_id); for (l = priv->crtcs; l; l = l->next) { MetaKmsCrtc *crtc = META_KMS_CRTC (l->data); if (crtc_id > 0 && meta_kms_crtc_get_id (crtc) != crtc_id) continue; changes |= meta_kms_crtc_update_state_in_impl (crtc); } drmModeFreeResources (drm_resources); return changes; err: g_clear_list (&priv->planes, g_object_unref); g_clear_list (&priv->crtcs, g_object_unref); g_clear_list (&priv->connectors, g_object_unref); g_clear_pointer (&priv->crtc_frames, g_hash_table_unref); return META_KMS_RESOURCE_CHANGE_FULL; } static MetaKmsResourceChanges meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE; GList *l; g_list_foreach (priv->crtcs, (GFunc) meta_kms_crtc_predict_state_in_impl, update); for (l = priv->connectors; l; l = l->next) { MetaKmsConnector *connector = l->data; changes |= meta_kms_connector_predict_state_in_impl (connector, update); } return changes; } void meta_kms_impl_device_notify_modes_set (MetaKmsImplDevice *impl_device) { clear_latched_fd_hold (impl_device); } int meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl)); return meta_device_file_get_fd (priv->device_file); } static void disarm_crtc_frame_deadline_timer (CrtcFrame *crtc_frame) { struct itimerspec its = {}; if (!crtc_frame->deadline.source) return; meta_topic (META_DEBUG_KMS, "Disarming deadline timer for crtc %u (%s)", meta_kms_crtc_get_id (crtc_frame->crtc), meta_kms_device_get_path (meta_kms_crtc_get_device (crtc_frame->crtc))); timerfd_settime (crtc_frame->deadline.timer_fd, TFD_TIMER_ABSTIME, &its, NULL); crtc_frame->deadline.armed = FALSE; } static void arm_crtc_frame_deadline_timer (CrtcFrame *crtc_frame, int64_t next_deadline_us, int64_t next_presentation_us) { struct itimerspec its = {}; int64_t tv_sec; int64_t tv_nsec; g_warn_if_fail (!crtc_frame->await_flush); if (!crtc_frame->deadline.source) return; meta_topic (META_DEBUG_KMS, "Arming deadline timer for crtc %u (%s): %ld", meta_kms_crtc_get_id (crtc_frame->crtc), meta_kms_device_get_path (meta_kms_crtc_get_device (crtc_frame->crtc)), next_deadline_us); tv_sec = us2s (next_deadline_us); tv_nsec = us2ns (next_deadline_us - s2us (tv_sec)); its.it_value.tv_sec = tv_sec; its.it_value.tv_nsec = tv_nsec; timerfd_settime (crtc_frame->deadline.timer_fd, TFD_TIMER_ABSTIME, &its, NULL); crtc_frame->deadline.expected_presentation_time_us = next_presentation_us; crtc_frame->deadline.armed = TRUE; } static void notify_crtc_frame_ready (CrtcFrame *crtc_frame) { MetaKmsCrtc *crtc = crtc_frame->crtc; crtc_frame->pending_page_flip = FALSE; crtc_frame->deadline.is_deadline_page_flip = FALSE; if (!crtc_frame->pending_update) return; if (crtc_frame->await_flush) return; meta_kms_impl_device_schedule_process (crtc_frame->impl_device, crtc); } static void crtc_page_flip_feedback_flipped (MetaKmsCrtc *crtc, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, gpointer user_data) { CrtcFrame *crtc_frame = user_data; if (crtc_frame->deadline.is_deadline_page_flip && meta_is_topic_enabled (META_DEBUG_KMS)) { struct timeval page_flip_timeval; int64_t presentation_time_us; page_flip_timeval = (struct timeval) { .tv_sec = tv_sec, .tv_usec = tv_usec, }; presentation_time_us = meta_timeval_to_microseconds (&page_flip_timeval); meta_topic (META_DEBUG_KMS, "Deadline page flip presentation time: %"G_GINT64_FORMAT" us, " "expected %"G_GINT64_FORMAT" us " "(diff: %"G_GINT64_FORMAT")", presentation_time_us, crtc_frame->deadline.expected_presentation_time_us, crtc_frame->deadline.expected_presentation_time_us - presentation_time_us); } notify_crtc_frame_ready (crtc_frame); } static void crtc_page_flip_feedback_ready (MetaKmsCrtc *crtc, gpointer user_data) { CrtcFrame *crtc_frame = user_data; notify_crtc_frame_ready (crtc_frame); } static void crtc_page_flip_feedback_mode_set_fallback (MetaKmsCrtc *crtc, gpointer user_data) { CrtcFrame *crtc_frame = user_data; crtc_frame->pending_page_flip = FALSE; } static void crtc_page_flip_feedback_discarded (MetaKmsCrtc *crtc, gpointer user_data, const GError *error) { CrtcFrame *crtc_frame = user_data; crtc_frame->pending_page_flip = FALSE; } static const MetaKmsPageFlipListenerVtable crtc_page_flip_listener_vtable = { .flipped = crtc_page_flip_feedback_flipped, .ready = crtc_page_flip_feedback_ready, .mode_set_fallback = crtc_page_flip_feedback_mode_set_fallback, .discarded = crtc_page_flip_feedback_discarded, }; static void emit_resources_changed_callback (MetaThread *thread, gpointer user_data) { MetaKmsResourceChanges changes = GPOINTER_TO_UINT (user_data); meta_kms_emit_resources_changed (META_KMS (thread), changes); } static void queue_result_feedback (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update, MetaKmsFeedback *feedback) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKms *kms = meta_kms_device_get_kms (priv->device); g_autoptr (GList) result_listeners = NULL; GList *l; result_listeners = meta_kms_update_take_result_listeners (update); for (l = result_listeners; l; l = l->next) { MetaKmsResultListener *listener = l->data; meta_kms_result_listener_set_feedback (listener, feedback); meta_kms_queue_result_callback (kms, listener); } } static MetaKmsFeedback * do_process (MetaKmsImplDevice *impl_device, MetaKmsCrtc *latch_crtc, MetaKmsUpdate *update, MetaKmsUpdateFlag flags) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKms *kms = meta_kms_device_get_kms (priv->device); MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); CrtcFrame *crtc_frame = NULL; MetaKmsFeedback *feedback; MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE; COGL_TRACE_BEGIN_SCOPED (MetaKmsImplDeviceProcess, "KMS device impl (processing)"); update = meta_kms_impl_filter_update (impl, latch_crtc, update, flags); if (!update || meta_kms_update_is_empty (update)) { GError *error; error = g_error_new (META_KMS_ERROR, META_KMS_ERROR_EMPTY_UPDATE, "Empty update"); feedback = meta_kms_feedback_new_failed (NULL, error); if (update) { queue_result_feedback (impl_device, update, feedback); meta_kms_update_free (update); } return feedback; } if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) { if (latch_crtc) { crtc_frame = get_crtc_frame (impl_device, latch_crtc); if (crtc_frame && crtc_frame->pending_update) { meta_kms_update_merge_from (crtc_frame->pending_update, update); meta_kms_update_free (update); update = g_steal_pointer (&crtc_frame->pending_update); } } if (crtc_frame) { GMainContext *thread_context = meta_thread_impl_get_main_context (thread_impl); meta_kms_update_add_page_flip_listener (update, crtc_frame->crtc, &crtc_page_flip_listener_vtable, META_KMS_PAGE_FLIP_LISTENER_FLAG_NONE, thread_context, crtc_frame, NULL); crtc_frame->pending_page_flip = TRUE; } } feedback = klass->process_update (impl_device, update, flags); if (meta_kms_feedback_get_result (feedback) != META_KMS_FEEDBACK_PASSED && crtc_frame) crtc_frame->pending_page_flip = FALSE; if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) changes = meta_kms_impl_device_predict_states (impl_device, update); queue_result_feedback (impl_device, update, feedback); meta_kms_update_free (update); if (changes != META_KMS_RESOURCE_CHANGE_NONE) { meta_kms_queue_callback (kms, NULL, emit_resources_changed_callback, GUINT_TO_POINTER (changes), NULL); } return feedback; } static gpointer crtc_frame_deadline_dispatch (MetaThreadImpl *thread_impl, gpointer user_data, GError **error) { CrtcFrame *crtc_frame = user_data; MetaKmsDevice *device = meta_kms_crtc_get_device (crtc_frame->crtc); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); g_autoptr (MetaKmsFeedback) feedback = NULL; uint64_t timer_value; ssize_t ret; ret = read (crtc_frame->deadline.timer_fd, &timer_value, sizeof (timer_value)); if (ret == -1) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Failed to read from timerfd: %s", g_strerror (errno)); return GINT_TO_POINTER (FALSE); } else if (ret != sizeof (timer_value)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to read from timerfd: unexpected size %zd", ret); return GINT_TO_POINTER (FALSE); } feedback = do_process (impl_device, crtc_frame->crtc, g_steal_pointer (&crtc_frame->pending_update), META_KMS_UPDATE_FLAG_NONE); if (meta_kms_feedback_did_pass (feedback)) crtc_frame->deadline.is_deadline_page_flip = TRUE; disarm_crtc_frame_deadline_timer (crtc_frame); return GINT_TO_POINTER (TRUE); } static void crtc_frame_free (CrtcFrame *crtc_frame) { g_clear_fd (&crtc_frame->deadline.timer_fd, NULL); g_clear_pointer (&crtc_frame->deadline.source, g_source_destroy); g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free); g_free (crtc_frame); } static CrtcFrame * get_crtc_frame (MetaKmsImplDevice *impl_device, MetaKmsCrtc *latch_crtc) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); return g_hash_table_lookup (priv->crtc_frames, latch_crtc); } static gboolean is_using_deadline_timer (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); if (priv->deadline_timer_inhibited) { return FALSE; } else { MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); return meta_thread_impl_is_realtime (thread_impl); } } static CrtcFrame * ensure_crtc_frame (MetaKmsImplDevice *impl_device, MetaKmsCrtc *latch_crtc) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device); MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl); CrtcFrame *crtc_frame; crtc_frame = get_crtc_frame (impl_device, latch_crtc); if (crtc_frame) return crtc_frame; crtc_frame = g_new0 (CrtcFrame, 1); crtc_frame->impl_device = impl_device; crtc_frame->crtc = latch_crtc; crtc_frame->deadline.timer_fd = -1; crtc_frame->await_flush = TRUE; if (is_using_deadline_timer (impl_device)) { int timer_fd; GSource *source; g_autofree char *name = NULL; timer_fd = timerfd_create (CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); source = meta_thread_impl_register_fd (thread_impl, timer_fd, crtc_frame_deadline_dispatch, crtc_frame); name = g_strdup_printf ("[mutter] KMS deadline clock (crtc: %u, %s)", meta_kms_crtc_get_id (latch_crtc), priv->path); g_source_set_name (source, name); g_source_set_priority (source, G_PRIORITY_HIGH + 1); g_source_set_can_recurse (source, FALSE); g_source_set_ready_time (source, -1); crtc_frame->deadline.timer_fd = timer_fd; crtc_frame->deadline.source = source; g_source_unref (source); } g_hash_table_insert (priv->crtc_frames, latch_crtc, crtc_frame); return crtc_frame; } static void queue_update (MetaKmsImplDevice *impl_device, CrtcFrame *crtc_frame, MetaKmsUpdate *update) { g_assert (update); if (crtc_frame->pending_update) { meta_kms_update_merge_from (crtc_frame->pending_update, update); meta_kms_update_free (update); } else { crtc_frame->pending_update = update; } } void meta_kms_impl_device_handle_update (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update, MetaKmsUpdateFlag flags) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); g_autoptr (GError) error = NULL; MetaKmsCrtc *latch_crtc; CrtcFrame *crtc_frame; MetaKmsFeedback *feedback; meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl)); latch_crtc = meta_kms_update_get_latch_crtc (update); if (!latch_crtc) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Only single-CRTC updates supported"); goto err; } if (!priv->crtc_frames) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Shutting down"); goto err; } if (!ensure_device_file (impl_device, &error)) goto err; meta_kms_update_realize (update, impl_device); crtc_frame = ensure_crtc_frame (impl_device, latch_crtc); crtc_frame->await_flush = FALSE; if (crtc_frame->pending_page_flip && !meta_kms_update_get_mode_sets (update)) { g_assert (latch_crtc); meta_topic (META_DEBUG_KMS, "Queuing update on CRTC %u (%s): pending page flip", meta_kms_crtc_get_id (latch_crtc), priv->path); queue_update (impl_device, crtc_frame, update); return; } if (crtc_frame->pending_update) { meta_kms_update_merge_from (crtc_frame->pending_update, update); meta_kms_update_free (update); update = g_steal_pointer (&crtc_frame->pending_update); disarm_crtc_frame_deadline_timer (crtc_frame); } meta_kms_device_handle_flush (priv->device, latch_crtc); feedback = do_process (impl_device, latch_crtc, update, flags); meta_kms_feedback_unref (feedback); return; err: feedback = meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error)); queue_result_feedback (impl_device, update, feedback); meta_kms_feedback_unref (feedback); meta_kms_update_free (update); } void meta_kms_impl_device_await_flush (MetaKmsImplDevice *impl_device, MetaKmsCrtc *crtc) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); CrtcFrame *crtc_frame; meta_topic (META_DEBUG_KMS, "Awaiting flush on CRTC %u (%s)", meta_kms_crtc_get_id (crtc), priv->path); crtc_frame = ensure_crtc_frame (impl_device, crtc); crtc_frame->await_flush = TRUE; if (crtc_frame->deadline.armed) disarm_crtc_frame_deadline_timer (crtc_frame); } static gboolean ensure_deadline_timer_armed (MetaKmsImplDevice *impl_device, CrtcFrame *crtc_frame, GError **error) { int64_t next_deadline_us; int64_t next_presentation_us; if (crtc_frame->deadline.armed) return TRUE; if (!meta_kms_crtc_determine_deadline (crtc_frame->crtc, &next_deadline_us, &next_presentation_us, error)) return FALSE; arm_crtc_frame_deadline_timer (crtc_frame, next_deadline_us, next_presentation_us); return TRUE; } void meta_kms_impl_device_schedule_process (MetaKmsImplDevice *impl_device, MetaKmsCrtc *crtc) { CrtcFrame *crtc_frame; g_autoptr (GError) error = NULL; MetaKmsImplDevicePrivate *priv; crtc_frame = ensure_crtc_frame (impl_device, crtc); if (crtc_frame->await_flush) return; if (!is_using_deadline_timer (impl_device)) goto needs_flush; if (crtc_frame->pending_page_flip) return; if (ensure_deadline_timer_armed (impl_device, crtc_frame, &error)) return; if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) g_warning ("Failed to determine deadline: %s", error->message); priv = meta_kms_impl_device_get_instance_private (impl_device); priv->deadline_timer_inhibited = TRUE; needs_flush: meta_kms_device_set_needs_flush (meta_kms_crtc_get_device (crtc), crtc); } static MetaKmsFeedback * process_mode_set_update (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update, MetaKmsUpdateFlag flags) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImpl *kms_impl = meta_kms_impl_device_get_impl (impl_device); MetaThreadImpl *thread_impl = META_THREAD_IMPL (kms_impl); MetaThread *thread = meta_thread_impl_get_thread (thread_impl); MetaKmsFeedback *feedback; CrtcFrame *crtc_frame; GList *l; GHashTableIter iter; for (l = meta_kms_update_get_mode_sets (update); l; l = l->next) { MetaKmsModeSet *mode_set = l->data; MetaKmsCrtc *crtc = mode_set->crtc; crtc_frame = get_crtc_frame (impl_device, crtc); if (!crtc_frame) continue; if (!crtc_frame->pending_update) continue; meta_kms_update_merge_from (crtc_frame->pending_update, update); meta_kms_update_free (update); update = g_steal_pointer (&crtc_frame->pending_update); } g_hash_table_iter_init (&iter, priv->crtc_frames); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &crtc_frame)) { crtc_frame->deadline.is_deadline_page_flip = FALSE; crtc_frame->await_flush = FALSE; crtc_frame->pending_page_flip = FALSE; g_clear_pointer (&crtc_frame->pending_update, meta_kms_update_free); disarm_crtc_frame_deadline_timer (crtc_frame); } meta_thread_inhibit_realtime_in_impl (thread); feedback = do_process (impl_device, NULL, update, flags); meta_thread_uninhibit_realtime_in_impl (thread); return feedback; } MetaKmsFeedback * meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update, MetaKmsUpdateFlag flags) { g_autoptr (GError) error = NULL; if (!ensure_device_file (impl_device, &error)) { MetaKmsFeedback *feedback = NULL; feedback = meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error)); queue_result_feedback (impl_device, update, feedback); meta_kms_update_free (update); return feedback; } meta_kms_update_realize (update, impl_device); if (flags & META_KMS_UPDATE_FLAG_TEST_ONLY) { return do_process (impl_device, meta_kms_update_get_latch_crtc (update), update, flags); } else if (flags & META_KMS_UPDATE_FLAG_MODE_SET) { return process_mode_set_update (impl_device, update, flags); } else { g_assert_not_reached (); } } void meta_kms_impl_device_disable (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImpl *kms_impl = meta_kms_impl_device_get_impl (impl_device); MetaThreadImpl *thread_impl = META_THREAD_IMPL (kms_impl); MetaThread *thread = meta_thread_impl_get_thread (thread_impl); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); if (!priv->device_file) return; meta_kms_impl_device_hold_fd (impl_device); meta_thread_inhibit_realtime_in_impl (thread); klass->disable (impl_device); meta_thread_uninhibit_realtime_in_impl (thread); g_list_foreach (priv->crtcs, (GFunc) meta_kms_crtc_disable_in_impl, NULL); g_list_foreach (priv->connectors, (GFunc) meta_kms_connector_disable_in_impl, NULL); meta_kms_impl_device_unhold_fd (impl_device); } void meta_kms_impl_device_handle_page_flip_callback (MetaKmsImplDevice *impl_device, MetaKmsPageFlipData *page_flip_data) { MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); klass->handle_page_flip_callback (impl_device, page_flip_data); } void meta_kms_impl_device_discard_pending_page_flips (MetaKmsImplDevice *impl_device) { MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); klass->discard_pending_page_flips (impl_device); } void meta_kms_impl_device_hold_fd (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKms *kms = meta_kms_device_get_kms (priv->device); meta_assert_in_kms_impl (kms); g_assert (priv->device_file); priv->fd_hold_count++; } static void clear_fd_source (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); if (!priv->fd_source) return; g_source_destroy (priv->fd_source); g_clear_pointer (&priv->fd_source, g_source_unref); } void meta_kms_impl_device_unhold_fd (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKms *kms = meta_kms_device_get_kms (priv->device); meta_assert_in_kms_impl (kms); g_return_if_fail (priv->fd_hold_count > 0); priv->fd_hold_count--; if (priv->fd_hold_count == 0) { g_clear_pointer (&priv->device_file, meta_device_file_release); clear_fd_source (impl_device); } } static void meta_kms_impl_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (object); MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); switch (prop_id) { case PROP_DEVICE: g_value_set_object (value, priv->device); break; case PROP_IMPL: g_value_set_object (value, priv->impl); break; case PROP_FLAGS: g_value_set_flags (value, priv->flags); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_kms_impl_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (object); MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); switch (prop_id) { case PROP_DEVICE: priv->device = g_value_get_object (value); break; case PROP_IMPL: priv->impl = g_value_get_object (value); break; case PROP_PATH: priv->path = g_value_dup_string (value); break; case PROP_FLAGS: priv->flags = g_value_get_flags (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_kms_impl_device_finalize (GObject *object) { MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (object); MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); meta_kms_impl_remove_impl_device (priv->impl, impl_device); g_list_free_full (priv->planes, g_object_unref); g_list_free_full (priv->crtcs, g_object_unref); g_list_free_full (priv->connectors, g_object_unref); g_list_free_full (priv->fallback_modes, (GDestroyNotify) meta_kms_mode_free); clear_latched_fd_hold (impl_device); g_warn_if_fail (!priv->device_file); g_free (priv->driver_name); g_free (priv->driver_description); g_free (priv->path); G_OBJECT_CLASS (meta_kms_impl_device_parent_class)->finalize (object); } gboolean meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device, GError **error) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); int fd; drmModeRes *drm_resources; fd = meta_device_file_get_fd (priv->device_file); drm_resources = drmModeGetResources (fd); if (!drm_resources) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Failed to activate universal planes: %s", g_strerror (errno)); return FALSE; } init_caps (impl_device); init_crtcs (impl_device, drm_resources); init_planes (impl_device); init_fallback_modes (impl_device); update_connectors (impl_device, drm_resources, 0); drmModeFreeResources (drm_resources); return TRUE; } void meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); if (klass->prepare_shutdown) klass->prepare_shutdown (impl_device); clear_fd_source (impl_device); g_clear_pointer (&priv->crtc_frames, g_hash_table_unref); } 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 void maybe_inhibit_deadline_timer (MetaKmsImplDevice *impl_device) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); static const char *deadline_timer_deny_list[] = { "vc4", }; int i; for (i = 0; i < G_N_ELEMENTS (deadline_timer_deny_list); i++) { if (g_strcmp0 (deadline_timer_deny_list[i], priv->driver_name) == 0) { priv->deadline_timer_inhibited = TRUE; break; } } } static gboolean meta_kms_impl_device_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (initable); MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); int fd; if (!ensure_device_file (impl_device, error)) return FALSE; ensure_latched_fd_hold (impl_device); g_clear_pointer (&priv->path, g_free); priv->path = g_strdup (meta_device_file_get_path (priv->device_file)); fd = meta_device_file_get_fd (priv->device_file); if (!get_driver_info (fd, &priv->driver_name, &priv->driver_description)) { priv->driver_name = g_strdup ("unknown"); priv->driver_description = g_strdup ("Unknown"); } maybe_inhibit_deadline_timer (impl_device); priv->crtc_frames = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) crtc_frame_free); return TRUE; } static void meta_kms_impl_device_init (MetaKmsImplDevice *impl_device) { } static void initable_iface_init (GInitableIface *iface) { iface->init = meta_kms_impl_device_initable_init; } static void meta_kms_impl_device_class_init (MetaKmsImplDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = meta_kms_impl_device_get_property; object_class->set_property = meta_kms_impl_device_set_property; object_class->finalize = meta_kms_impl_device_finalize; obj_props[PROP_DEVICE] = g_param_spec_object ("device", NULL, NULL, META_TYPE_KMS_DEVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_IMPL] = g_param_spec_object ("impl", NULL, NULL, META_TYPE_KMS_IMPL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_PATH] = g_param_spec_string ("path", NULL, NULL, NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_FLAGS] = g_param_spec_flags ("flags", NULL, NULL, META_TYPE_KMS_DEVICE_FLAG, META_KMS_DEVICE_FLAG_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); }