/* * 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, 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-impl-device.h" #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-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 "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 _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; } MetaKmsImplDevicePrivate; static void initable_iface_init (GInitableIface *iface); 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)) G_DEFINE_QUARK (-meta-kms-error-quark, meta_kms_error) 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 (MetaKmsImpl *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) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); g_autolist (MetaKmsConnector) connectors = NULL; gboolean added_connector = FALSE; 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); } 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 META_KMS_RESOURCE_CHANGE_NONE; 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; for (i = 0; i < prop->num_enum_values; i++) { if (prop->enum_values[i].valid && prop->enum_values[i].value == drm_value) { prop->value = i; return; } } prop->value = prop->default_value; return; } 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 (drm_value & (1 << prop->enum_values[i].value)) { result |= prop->enum_values[i].bitmask; drm_value &= ~(1 << prop->enum_values[i].value); } } if (drm_value != 0) result |= prop->default_value; prop->value = result; 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); } } 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_kms_register_fd_in_impl (meta_kms_impl_get_kms (priv->impl), meta_device_file_get_fd (device_file), kms_event_dispatch_in_impl, impl_device); } 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); 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 (crtc); } for (l = priv->connectors; l; l = l->next) { MetaKmsConnector *connector = META_KMS_CONNECTOR (l->data); if (connector_id > 0 && meta_kms_connector_get_id (connector) != connector_id) continue; changes |= meta_kms_connector_update_state (connector, drm_resources); } 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); 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, update); for (l = priv->connectors; l; l = l->next) { MetaKmsConnector *connector = l->data; changes |= meta_kms_connector_predict_state (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 emit_resources_changed_callback (MetaKms *kms, gpointer user_data) { MetaKmsResourceChanges changes = GPOINTER_TO_UINT (user_data); meta_kms_emit_resources_changed (kms, changes); } MetaKmsFeedback * meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update, MetaKmsUpdateFlag flags) { MetaKmsImplDevicePrivate *priv = meta_kms_impl_device_get_instance_private (impl_device); MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device); MetaKmsFeedback *feedback; g_autoptr (GError) error = NULL; MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE; if (!ensure_device_file (impl_device, &error)) return meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error)); meta_kms_impl_device_hold_fd (impl_device); feedback = klass->process_update (impl_device, update, flags); if (!(flags & META_KMS_UPDATE_FLAG_TEST_ONLY)) changes = meta_kms_impl_device_predict_states (impl_device, update); meta_kms_impl_device_unhold_fd (impl_device); if (changes != META_KMS_RESOURCE_CHANGE_NONE) { MetaKms *kms = meta_kms_device_get_kms (priv->device); meta_kms_queue_callback (kms, emit_resources_changed_callback, GUINT_TO_POINTER (changes), NULL); } return feedback; } void meta_kms_impl_device_disable (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 (!priv->device_file) return; meta_kms_impl_device_hold_fd (impl_device); klass->disable (impl_device); g_list_foreach (priv->crtcs, (GFunc) meta_kms_crtc_disable, NULL); g_list_foreach (priv->connectors, (GFunc) meta_kms_connector_disable, 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); drmModeFreeResources (drm_resources); return TRUE; } void meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *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); } 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 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"); } 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", "device", "MetaKmsDevice", META_TYPE_KMS_DEVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_IMPL] = g_param_spec_object ("impl", "impl", "MetaKmsImpl", META_TYPE_KMS_IMPL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_PATH] = g_param_spec_string ("path", "path", "Device path", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_FLAGS] = g_param_spec_flags ("flags", "flags", "KMS impl device flags", 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); }