diff --git a/src/backends/native/meta-kms-device.c b/src/backends/native/meta-kms-device.c index 8587c3065..b5b50d602 100644 --- a/src/backends/native/meta-kms-device.c +++ b/src/backends/native/meta-kms-device.c @@ -26,6 +26,7 @@ #include #include "backends/native/meta-backend-native.h" +#include "backends/native/meta-kms-impl-device-atomic.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" @@ -237,6 +238,20 @@ typedef struct _CreateImplDeviceData char *out_driver_description; } 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, @@ -264,9 +279,12 @@ meta_create_kms_impl_device (MetaKmsDevice *device, const char *path, GError **error) { + GType impl_device_type; + gboolean supports_atomic_mode_setting; int ret; g_autofree char *driver_name = NULL; g_autofree char *driver_description = NULL; + const char *atomic_kms_enable_env; meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl)); @@ -285,10 +303,64 @@ meta_create_kms_impl_device (MetaKmsDevice *device, driver_description = g_strdup ("Unknown"); } - g_message ("Adding device '%s' (%s) using non-atomic mode setting.", - path, driver_name); + atomic_kms_enable_env = getenv ("MUTTER_DEBUG_ENABLE_ATOMIC_KMS"); + 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); + } - return g_initable_new (META_TYPE_KMS_IMPL_DEVICE_SIMPLE, NULL, error, + 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_device_type = META_TYPE_KMS_IMPL_DEVICE_SIMPLE; + } + else + { + 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; + } + } + + return g_initable_new (impl_device_type, NULL, error, "device", device, "impl", impl, "fd", fd, diff --git a/src/backends/native/meta-kms-impl-device-atomic.c b/src/backends/native/meta-kms-impl-device-atomic.c new file mode 100644 index 000000000..a00c91a31 --- /dev/null +++ b/src/backends/native/meta-kms-impl-device-atomic.c @@ -0,0 +1,1035 @@ +/* + * Copyright (C) 2019-2020 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-atomic.h" + +#include "backends/native/meta-kms-connector-private.h" +#include "backends/native/meta-kms-crtc-private.h" +#include "backends/native/meta-kms-device-private.h" +#include "backends/native/meta-kms-mode-private.h" +#include "backends/native/meta-kms-plane-private.h" +#include "backends/native/meta-kms-private.h" +#include "backends/native/meta-kms-update-private.h" + +typedef gboolean (* MetaKmsAtomicProcessFunc) (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer entry_data, + gpointer user_data, + GError **error); + +struct _MetaKmsImplDeviceAtomic +{ + MetaKmsImplDevice parent; + + GHashTable *page_flip_datas; +}; + +G_DEFINE_TYPE (MetaKmsImplDeviceAtomic, meta_kms_impl_device_atomic, + META_TYPE_KMS_IMPL_DEVICE) + +static uint32_t +store_new_blob (MetaKmsImplDevice *impl_device, + GArray *blob_ids, + const void *data, + size_t size, + GError **error) +{ + int fd = meta_kms_impl_device_get_fd (impl_device); + uint32_t blob_id; + int ret; + + ret = drmModeCreatePropertyBlob (fd, data, size, &blob_id); + if (ret < 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModeCreatePropertyBlob: %s", g_strerror (-ret)); + return 0; + } + + g_array_append_val (blob_ids, blob_id); + + return blob_id; +} + +static void +release_blob_ids (MetaKmsImplDevice *impl_device, + GArray *blob_ids) +{ + int fd = meta_kms_impl_device_get_fd (impl_device); + unsigned int i; + + for (i = 0; i < blob_ids->len; i++) + { + uint32_t blob_id = g_array_index (blob_ids, uint32_t, i); + + drmModeDestroyPropertyBlob (fd, blob_id); + } +} + +static gboolean +add_connector_property (MetaKmsImplDevice *impl_device, + MetaKmsConnector *connector, + drmModeAtomicReq *req, + MetaKmsConnectorProp prop, + uint64_t value, + GError **error) +{ + int ret; + uint32_t prop_id; + + prop_id = meta_kms_connector_get_prop_id (connector, prop); + if (!prop_id) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Connector property '%s' not found", + meta_kms_connector_get_prop_name (connector, prop)); + return FALSE; + } + + meta_topic (META_DEBUG_KMS, + "[atomic] Setting connector %u (%s) property '%s' (%u) to %" + G_GUINT64_FORMAT, + meta_kms_connector_get_id (connector), + meta_kms_impl_device_get_path (impl_device), + meta_kms_connector_get_prop_name (connector, prop), + meta_kms_connector_get_prop_id (connector, prop), + value); + ret = drmModeAtomicAddProperty (req, + meta_kms_connector_get_id (connector), + prop_id, + value); + if (ret < 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModeAtomicAddProperty, connector: %u, prop id: %u: %s", + meta_kms_connector_get_id (connector), + prop_id, + g_strerror (-ret)); + return FALSE; + } + + return TRUE; +} + +static gboolean +process_connector_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer update_entry, + gpointer user_data, + GError **error) +{ + MetaKmsConnectorUpdate *connector_update = update_entry; + MetaKmsConnector *connector = connector_update->connector; + + if (connector_update->underscanning.has_update && + connector_update->underscanning.is_active) + { + meta_topic (META_DEBUG_KMS, + "[atomic] Setting underscanning on connector %u (%s) to " + "%" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, + meta_kms_connector_get_id (connector), + meta_kms_impl_device_get_path (impl_device), + connector_update->underscanning.hborder, + connector_update->underscanning.vborder); + + if (!add_connector_property (impl_device, + connector, req, + META_KMS_CONNECTOR_PROP_UNDERSCAN, + 1, + error)) + return FALSE; + if (!add_connector_property (impl_device, + connector, req, + META_KMS_CONNECTOR_PROP_UNDERSCAN_HBORDER, + connector_update->underscanning.hborder, + error)) + return FALSE; + if (!add_connector_property (impl_device, + connector, req, + META_KMS_CONNECTOR_PROP_UNDERSCAN_VBORDER, + connector_update->underscanning.vborder, + error)) + return FALSE; + } + else if (connector_update->underscanning.has_update) + { + meta_topic (META_DEBUG_KMS, + "[atomic] Unsetting underscanning on connector %u (%s)", + meta_kms_connector_get_id (connector), + meta_kms_impl_device_get_path (impl_device)); + + if (!add_connector_property (impl_device, + connector, req, + META_KMS_CONNECTOR_PROP_UNDERSCAN, + 0, + error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +add_crtc_property (MetaKmsImplDevice *impl_device, + MetaKmsCrtc *crtc, + drmModeAtomicReq *req, + MetaKmsCrtcProp prop, + uint64_t value, + GError **error) +{ + int ret; + uint32_t prop_id; + + prop_id = meta_kms_crtc_get_prop_id (crtc, prop); + if (!prop_id) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "CRTC property (%s) not found", + meta_kms_crtc_get_prop_name (crtc, prop)); + return FALSE; + } + + meta_topic (META_DEBUG_KMS, + "[atomic] Setting CRTC %u (%s) property '%s' (%u) to %" + G_GUINT64_FORMAT, + meta_kms_crtc_get_id (crtc), + meta_kms_impl_device_get_path (impl_device), + meta_kms_crtc_get_prop_name (crtc, prop), + meta_kms_crtc_get_prop_id (crtc, prop), + value); + ret = drmModeAtomicAddProperty (req, + meta_kms_crtc_get_id (crtc), + prop_id, + value); + if (ret < 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModeAtomicAddProperty, crtc: %u, prop: %s (%u): %s", + meta_kms_crtc_get_id (crtc), + meta_kms_crtc_get_prop_name (crtc, prop), + prop_id, + g_strerror (-ret)); + return FALSE; + } + + return TRUE; +} + +static gboolean +process_mode_set (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer update_entry, + gpointer user_data, + GError **error) +{ + MetaKmsModeSet *mode_set = update_entry; + MetaKmsCrtc *crtc = mode_set->crtc; + MetaKmsMode *mode; + + mode = (MetaKmsMode *) mode_set->mode; + if (mode) + { + uint32_t mode_id; + GList *l; + + mode_id = meta_kms_mode_ensure_blob_id (mode, error); + if (mode_id == 0) + return FALSE; + + meta_topic (META_DEBUG_KMS, + "[atomic] Setting mode of CRTC %u (%s) to %s", + meta_kms_crtc_get_id (crtc), + meta_kms_impl_device_get_path (impl_device), + meta_kms_mode_get_name (mode)); + + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_MODE_ID, + mode_id, + error)) + return FALSE; + + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_ACTIVE, + 1, + error)) + return FALSE; + + for (l = mode_set->connectors; l; l = l->next) + { + MetaKmsConnector *connector = l->data; + + if (!add_connector_property (impl_device, + connector, req, + META_KMS_CONNECTOR_PROP_CRTC_ID, + meta_kms_crtc_get_id (crtc), + error)) + return FALSE; + } + } + else + { + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_MODE_ID, + 0, + error)) + return FALSE; + + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_ACTIVE, + 0, + error)) + return FALSE; + + meta_topic (META_DEBUG_KMS, + "[atomic] Unsetting mode of (%u, %s)", + meta_kms_crtc_get_id (crtc), + meta_kms_impl_device_get_path (impl_device)); + } + + return TRUE; +} + +static gboolean +add_plane_property (MetaKmsImplDevice *impl_device, + MetaKmsPlane *plane, + drmModeAtomicReq *req, + MetaKmsCrtcProp prop, + uint64_t value, + GError **error) +{ + int ret; + uint32_t prop_id; + + prop_id = meta_kms_plane_get_prop_id (plane, prop); + if (!prop_id) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Plane property (%s) not found on %u", + meta_kms_plane_get_prop_name (plane, prop), + meta_kms_plane_get_id (plane)); + return FALSE; + } + + meta_topic (META_DEBUG_KMS, + "[atomic] Setting plane %u (%s) property '%s' (%u) to %" + G_GUINT64_FORMAT, + meta_kms_plane_get_id (plane), + meta_kms_impl_device_get_path (impl_device), + meta_kms_plane_get_prop_name (plane, prop), + meta_kms_plane_get_prop_id (plane, prop), + value); + ret = drmModeAtomicAddProperty (req, + meta_kms_plane_get_id (plane), + prop_id, + value); + if (ret < 0) + { + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModeAtomicAddProperty, plane: %u, prop: %s (%u): %s", + meta_kms_plane_get_id (plane), + meta_kms_plane_get_prop_name (plane, prop), + prop_id, + g_strerror (-ret)); + return FALSE; + } + + return TRUE; +} + +static const char * +get_plane_type_string (MetaKmsPlane *plane) +{ + switch (meta_kms_plane_get_plane_type (plane)) + { + case META_KMS_PLANE_TYPE_PRIMARY: + return "primary"; + case META_KMS_PLANE_TYPE_CURSOR: + return "cursor"; + case META_KMS_PLANE_TYPE_OVERLAY: + return "overlay"; + } + + g_assert_not_reached (); +} + +static gboolean +process_plane_assignment (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer update_entry, + gpointer user_data, + GError **error) +{ + MetaKmsPlaneAssignment *plane_assignment = update_entry; + MetaKmsPlane *plane = plane_assignment->plane; + MetaDrmBuffer *buffer; + + buffer = plane_assignment->buffer; + + meta_topic (META_DEBUG_KMS, + "[atomic] Assigning %s plane (%u, %s) to %u, " + "%hdx%hd+%hd+%hd -> %dx%d+%d+%d", + get_plane_type_string (plane), + meta_kms_plane_get_id (plane), + meta_kms_impl_device_get_path (impl_device), + buffer ? meta_drm_buffer_get_fb_id (buffer) : 0, + meta_fixed_16_to_int (plane_assignment->src_rect.width), + meta_fixed_16_to_int (plane_assignment->src_rect.height), + meta_fixed_16_to_int (plane_assignment->src_rect.x), + meta_fixed_16_to_int (plane_assignment->src_rect.y), + plane_assignment->dst_rect.width, + plane_assignment->dst_rect.height, + plane_assignment->dst_rect.x, + plane_assignment->dst_rect.y); + + if (buffer) + { + int i; + struct { + MetaKmsPlaneProp prop; + uint64_t value; + } props[] = { + { + .prop = META_KMS_PLANE_PROP_FB_ID, + .value = meta_drm_buffer_get_fb_id (buffer), + }, + { + .prop = META_KMS_PLANE_PROP_CRTC_ID, + .value = meta_kms_crtc_get_id (plane_assignment->crtc), + }, + { + .prop = META_KMS_PLANE_PROP_SRC_X, + .value = plane_assignment->src_rect.x, + }, + { + .prop = META_KMS_PLANE_PROP_SRC_Y, + .value = plane_assignment->src_rect.y, + }, + { + .prop = META_KMS_PLANE_PROP_SRC_W, + .value = plane_assignment->src_rect.width, + }, + { + .prop = META_KMS_PLANE_PROP_SRC_H, + .value = plane_assignment->src_rect.height, + }, + { + .prop = META_KMS_PLANE_PROP_CRTC_X, + .value = plane_assignment->dst_rect.x, + }, + { + .prop = META_KMS_PLANE_PROP_CRTC_Y, + .value = plane_assignment->dst_rect.y, + }, + { + .prop = META_KMS_PLANE_PROP_CRTC_W, + .value = plane_assignment->dst_rect.width, + }, + { + .prop = META_KMS_PLANE_PROP_CRTC_H, + .value = plane_assignment->dst_rect.height, + }, + }; + + for (i = 0; i < G_N_ELEMENTS (props); i++) + { + if (!add_plane_property (impl_device, + plane, req, + props[i].prop, + props[i].value, + error)) + return FALSE; + } + } + else + { + int i; + struct { + MetaKmsPlaneProp prop; + uint64_t value; + } props[] = { + { + .prop = META_KMS_PLANE_PROP_FB_ID, + .value = 0, + }, + { + .prop = META_KMS_PLANE_PROP_CRTC_ID, + .value = 0, + }, + }; + + for (i = 0; i < G_N_ELEMENTS (props); i++) + { + if (!add_plane_property (impl_device, + plane, req, + props[i].prop, + props[i].value, + error)) + return FALSE; + } + } + + if (plane_assignment->rotation) + { + meta_topic (META_DEBUG_KMS, + "[atomic] Setting plane (%u, %s) rotation to %" + G_GUINT64_FORMAT, + meta_kms_plane_get_id (plane), + meta_kms_impl_device_get_path (impl_device), + plane_assignment->rotation); + + if (!add_plane_property (impl_device, + plane, req, + META_KMS_PLANE_PROP_ROTATION, + plane_assignment->rotation, + error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +process_crtc_gamma (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer update_entry, + gpointer user_data, + GError **error) +{ + MetaKmsCrtcGamma *gamma = update_entry; + MetaKmsCrtc *crtc = gamma->crtc; + struct drm_color_lut drm_color_lut[gamma->size]; + int i; + uint32_t color_lut_blob_id; + + for (i = 0; i < gamma->size; i++) + { + drm_color_lut[i].red = gamma->red[i]; + drm_color_lut[i].green = gamma->green[i]; + drm_color_lut[i].blue = gamma->blue[i]; + } + + color_lut_blob_id = store_new_blob (impl_device, + blob_ids, + drm_color_lut, + sizeof drm_color_lut, + error); + if (!color_lut_blob_id) + return FALSE; + + meta_topic (META_DEBUG_KMS, + "[atomic] Setting CRTC (%u, %s) gamma, size: %d", + meta_kms_crtc_get_id (crtc), + meta_kms_impl_device_get_path (impl_device), + gamma->size); + + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_GAMMA_LUT, + color_lut_blob_id, + error)) + return FALSE; + + return TRUE; +} + +static gboolean +process_page_flip_listener (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer update_entry, + gpointer user_data, + GError **error) +{ + MetaKmsImplDeviceAtomic *impl_device_atomic = + META_KMS_IMPL_DEVICE_ATOMIC (impl_device); + MetaKmsPageFlipListener *listener = update_entry; + MetaKmsPageFlipData *page_flip_data; + uint32_t crtc_id; + gpointer listener_user_data; + GDestroyNotify listener_destroy_notify; + + crtc_id = meta_kms_crtc_get_id (listener->crtc); + page_flip_data = g_hash_table_lookup (impl_device_atomic->page_flip_datas, + GUINT_TO_POINTER (crtc_id)); + if (!page_flip_data) + { + page_flip_data = meta_kms_page_flip_data_new (impl_device, + listener->crtc); + g_hash_table_insert (impl_device_atomic->page_flip_datas, + GUINT_TO_POINTER (crtc_id), + page_flip_data); + + meta_topic (META_DEBUG_KMS, + "[atomic] Adding page flip data for (%u, %s): %p", + crtc_id, + meta_kms_impl_device_get_path (impl_device), + page_flip_data); + } + + listener_user_data = g_steal_pointer (&listener->user_data); + listener_destroy_notify = g_steal_pointer (&listener->destroy_notify); + meta_kms_page_flip_data_add_listener (page_flip_data, + listener->vtable, + listener->flags, + listener_user_data, + listener_destroy_notify); + + return TRUE; +} + +static gboolean +discard_page_flip_listener (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + gpointer update_entry, + gpointer user_data, + GError **error) +{ + MetaKmsPageFlipListener *listener = update_entry; + GError *commit_error = user_data; + MetaKmsPageFlipData *page_flip_data; + gpointer listener_user_data; + GDestroyNotify listener_destroy_notify; + + page_flip_data = meta_kms_page_flip_data_new (impl_device, + listener->crtc); + + meta_topic (META_DEBUG_KMS, + "[atomic] Creating transient page flip data for (%u, %s): %p", + meta_kms_crtc_get_id (listener->crtc), + meta_kms_impl_device_get_path (impl_device), + page_flip_data); + + listener_user_data = g_steal_pointer (&listener->user_data); + listener_destroy_notify = g_steal_pointer (&listener->destroy_notify); + meta_kms_page_flip_data_add_listener (page_flip_data, + listener->vtable, + listener->flags, + listener_user_data, + listener_destroy_notify); + + meta_kms_page_flip_data_discard_in_impl (page_flip_data, commit_error); + + return TRUE; +} + +static gboolean +process_entries (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update, + drmModeAtomicReq *req, + GArray *blob_ids, + GList *entries, + gpointer user_data, + MetaKmsAtomicProcessFunc func, + GError **error) +{ + GList *l; + + for (l = entries; l; l = l->next) + { + if (!func (impl_device, + update, + req, + blob_ids, + l->data, + user_data, + error)) + return FALSE; + } + + return TRUE; +} + +static void +atomic_page_flip_handler (int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + unsigned int crtc_id, + void *user_data) +{ + MetaKmsImplDeviceAtomic *impl_device_atomic = user_data; + MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_atomic); + MetaKmsPageFlipData *page_flip_data = NULL; + + g_hash_table_steal_extended (impl_device_atomic->page_flip_datas, + GUINT_TO_POINTER (crtc_id), + NULL, + (gpointer *) &page_flip_data); + + meta_topic (META_DEBUG_KMS, + "[atomic] Page flip callback for CRTC (%u, %s), data: %p", + crtc_id, meta_kms_impl_device_get_path (impl_device), + page_flip_data); + + if (!page_flip_data) + return; + g_return_if_fail (page_flip_data); + + meta_kms_page_flip_data_set_timings_in_impl (page_flip_data, + sequence, tv_sec, tv_usec); + meta_kms_impl_device_handle_page_flip_callback (impl_device, page_flip_data); +} + +static void +meta_kms_impl_device_atomic_setup_drm_event_context (MetaKmsImplDevice *impl, + drmEventContext *drm_event_context) +{ + drm_event_context->version = 3; + drm_event_context->page_flip_handler2 = atomic_page_flip_handler; +} + +static const char * +commit_flags_string (uint32_t commit_flags) +{ + static char static_commit_flags_string[255]; + const char *commit_flag_strings[4] = { NULL }; + int i = 0; + g_autofree char *commit_flags_string = NULL; + + if (commit_flags & DRM_MODE_ATOMIC_NONBLOCK) + commit_flag_strings[i++] = "ATOMIC_NONBLOCK"; + if (commit_flags & DRM_MODE_ATOMIC_ALLOW_MODESET) + commit_flag_strings[i++] = "ATOMIC_ALLOW_MODESET"; + if (commit_flags & DRM_MODE_PAGE_FLIP_EVENT) + commit_flag_strings[i++] = "PAGE_FLIP_EVENT"; + + commit_flags_string = g_strjoinv ("|", (char **) commit_flag_strings); + strncpy (static_commit_flags_string, commit_flags_string, + (sizeof static_commit_flags_string) - 1); + + return static_commit_flags_string; +} + +static gboolean +disable_connectors (MetaKmsImplDevice *impl_device, + drmModeAtomicReq *req, + GError **error) +{ + GList *l; + + for (l = meta_kms_impl_device_peek_connectors (impl_device); l; l = l->next) + { + MetaKmsConnector *connector = l->data; + + if (!add_connector_property (impl_device, + connector, req, + META_KMS_CONNECTOR_PROP_CRTC_ID, + 0, + error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +disable_planes (MetaKmsImplDevice *impl_device, + drmModeAtomicReq *req, + GError **error) +{ + GList *l; + + for (l = meta_kms_impl_device_peek_planes (impl_device); l; l = l->next) + { + MetaKmsPlane *plane = l->data; + + if (!add_plane_property (impl_device, + plane, req, + META_KMS_PLANE_PROP_CRTC_ID, + 0, + error)) + return FALSE; + + if (!add_plane_property (impl_device, + plane, req, + META_KMS_PLANE_PROP_FB_ID, + 0, + error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +disable_crtcs (MetaKmsImplDevice *impl_device, + drmModeAtomicReq *req, + GError **error) +{ + GList *l; + + for (l = meta_kms_impl_device_peek_crtcs (impl_device); l; l = l->next) + { + MetaKmsCrtc *crtc = l->data; + + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_ACTIVE, + 0, + error)) + return FALSE; + + if (!add_crtc_property (impl_device, + crtc, req, + META_KMS_CRTC_PROP_MODE_ID, + 0, + error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +disable_planes_and_connectors (MetaKmsImplDevice *impl_device, + drmModeAtomicReq *req, + GError **error) +{ + if (!disable_connectors (impl_device, req, error)) + return FALSE; + if (!disable_planes (impl_device, req, error)) + return FALSE; + + return TRUE; +} + +static gboolean +process_power_save (MetaKmsImplDevice *impl_device, + drmModeAtomicReq *req, + GError **error) +{ + if (!disable_connectors (impl_device, req, error)) + return FALSE; + if (!disable_planes (impl_device, req, error)) + return FALSE; + if (!disable_crtcs (impl_device, req, error)) + return FALSE; + + return TRUE; +} + +static MetaKmsFeedback * +meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device, + MetaKmsUpdate *update) +{ + GError *error = NULL; + GList *failed_planes = NULL; + drmModeAtomicReq *req; + GArray *blob_ids; + int fd; + uint32_t commit_flags = 0; + int ret; + + blob_ids = g_array_new (FALSE, TRUE, sizeof (uint32_t)); + + meta_topic (META_DEBUG_KMS, + "[atomic] Processing update %" G_GUINT64_FORMAT, + meta_kms_update_get_sequence_number (update)); + + req = drmModeAtomicAlloc (); + if (!req) + { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to create atomic transaction request: %s", + g_strerror (errno)); + goto err; + } + + if (meta_kms_update_get_mode_sets (update)) + { + if (!disable_planes_and_connectors (impl_device, req, &error)) + goto err; + } + + if (meta_kms_update_is_power_save (update)) + { + meta_topic (META_DEBUG_KMS, + "[atomic] Entering power save mode for %s", + meta_kms_impl_device_get_path (impl_device)); + + if (!process_power_save (impl_device, req, &error)) + goto err; + + commit_flags = DRM_MODE_ATOMIC_ALLOW_MODESET; + goto commit; + } + + if (!process_entries (impl_device, + update, + req, + blob_ids, + meta_kms_update_get_connector_updates (update), + NULL, + process_connector_update, + &error)) + goto err; + + if (!process_entries (impl_device, + update, + req, + blob_ids, + meta_kms_update_get_mode_sets (update), + NULL, + process_mode_set, + &error)) + goto err; + + if (!process_entries (impl_device, + update, + req, + blob_ids, + meta_kms_update_get_plane_assignments (update), + NULL, + process_plane_assignment, + &error)) + goto err; + + if (!process_entries (impl_device, + update, + req, + blob_ids, + meta_kms_update_get_crtc_gammas (update), + NULL, + process_crtc_gamma, + &error)) + goto err; + + if (meta_kms_update_get_mode_sets (update)) + commit_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + else + commit_flags |= DRM_MODE_ATOMIC_NONBLOCK; + + if (meta_kms_update_get_page_flip_listeners (update)) + commit_flags |= DRM_MODE_PAGE_FLIP_EVENT; + +commit: + meta_topic (META_DEBUG_KMS, + "[atomic] Committing update %" G_GUINT64_FORMAT ", flags: %s", + meta_kms_update_get_sequence_number (update), + commit_flags_string (commit_flags)); + + fd = meta_kms_impl_device_get_fd (impl_device); + ret = drmModeAtomicCommit (fd, req, commit_flags, impl_device); + drmModeAtomicFree (req); + if (ret < 0) + { + g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret), + "drmModeAtomicCommit: %s", g_strerror (-ret)); + goto err; + } + + process_entries (impl_device, + update, + req, + blob_ids, + meta_kms_update_get_page_flip_listeners (update), + NULL, + process_page_flip_listener, + NULL); + + release_blob_ids (impl_device, blob_ids); + + return meta_kms_feedback_new_passed (NULL); + +err: + meta_topic (META_DEBUG_KMS, "[atomic] KMS update failed: %s", error->message); + + process_entries (impl_device, + update, + req, + blob_ids, + meta_kms_update_get_page_flip_listeners (update), + error, + discard_page_flip_listener, + NULL); + + release_blob_ids (impl_device, blob_ids); + + return meta_kms_feedback_new_failed (failed_planes, error); +} + +static void +meta_kms_impl_device_atomic_handle_page_flip_callback (MetaKmsImplDevice *impl_device, + MetaKmsPageFlipData *page_flip_data) +{ + meta_kms_page_flip_data_flipped_in_impl (page_flip_data); +} + +static void +meta_kms_impl_device_atomic_discard_pending_page_flips (MetaKmsImplDevice *impl_device) +{ +} + +static void +meta_kms_impl_device_atomic_finalize (GObject *object) +{ + MetaKmsImplDeviceAtomic *impl_device_atomic = + META_KMS_IMPL_DEVICE_ATOMIC (object); + + g_hash_table_unref (impl_device_atomic->page_flip_datas); + + G_OBJECT_CLASS (meta_kms_impl_device_atomic_parent_class)->finalize (object); +} + +static void +meta_kms_impl_device_atomic_init (MetaKmsImplDeviceAtomic *impl_device_atomic) +{ + impl_device_atomic->page_flip_datas = + g_hash_table_new_full (NULL, NULL, + NULL, + (GDestroyNotify) meta_kms_page_flip_data_unref); +} + +static void +meta_kms_impl_device_atomic_class_init (MetaKmsImplDeviceAtomicClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MetaKmsImplDeviceClass *impl_device_class = + META_KMS_IMPL_DEVICE_CLASS (klass); + + object_class->finalize = meta_kms_impl_device_atomic_finalize; + + impl_device_class->setup_drm_event_context = + meta_kms_impl_device_atomic_setup_drm_event_context; + impl_device_class->process_update = + meta_kms_impl_device_atomic_process_update; + impl_device_class->handle_page_flip_callback = + meta_kms_impl_device_atomic_handle_page_flip_callback; + impl_device_class->discard_pending_page_flips = + meta_kms_impl_device_atomic_discard_pending_page_flips; +} diff --git a/src/backends/native/meta-kms-impl-device-atomic.h b/src/backends/native/meta-kms-impl-device-atomic.h new file mode 100644 index 000000000..74658797c --- /dev/null +++ b/src/backends/native/meta-kms-impl-device-atomic.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#ifndef META_KMS_IMPL_DEVICE_ATOMIC_H +#define META_KMS_IMPL_DEVICE_ATOMIC_H + +#include "backends/native/meta-kms-impl-device.h" + +#define META_TYPE_KMS_IMPL_DEVICE_ATOMIC (meta_kms_impl_device_atomic_get_type ()) +G_DECLARE_FINAL_TYPE (MetaKmsImplDeviceAtomic, meta_kms_impl_device_atomic, + META, KMS_IMPL_DEVICE_ATOMIC, MetaKmsImplDevice) + +#endif /* META_KMS_IMPL_DEVICE_ATOMIC_H */ diff --git a/src/backends/native/meta-kms-impl-device.c b/src/backends/native/meta-kms-impl-device.c index 8cc8a3b43..b2f1c3f11 100644 --- a/src/backends/native/meta-kms-impl-device.c +++ b/src/backends/native/meta-kms-impl-device.c @@ -129,6 +129,24 @@ meta_kms_impl_device_peek_connectors (MetaKmsImplDevice *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) { diff --git a/src/backends/native/meta-kms-impl-device.h b/src/backends/native/meta-kms-impl-device.h index 79db8a575..a3d31daeb 100644 --- a/src/backends/native/meta-kms-impl-device.h +++ b/src/backends/native/meta-kms-impl-device.h @@ -80,6 +80,10 @@ GList * meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device); GList * meta_kms_impl_device_peek_connectors (MetaKmsImplDevice *impl_device); +GList * meta_kms_impl_device_peek_crtcs (MetaKmsImplDevice *impl_device); + +GList * meta_kms_impl_device_peek_planes (MetaKmsImplDevice *impl_device); + const MetaKmsDeviceCaps * meta_kms_impl_device_get_caps (MetaKmsImplDevice *impl_device); GList * meta_kms_impl_device_copy_fallback_modes (MetaKmsImplDevice *impl_device); diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index 85ba1d998..0b0592591 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -3330,6 +3330,7 @@ meta_renderer_native_finish_frame (MetaRendererNative *renderer_native, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); break; case META_KMS_FEEDBACK_FAILED: + add_onscreen_frame_info (crtc); clutter_frame_set_result (frame, CLUTTER_FRAME_RESULT_PENDING_PRESENTED); diff --git a/src/meson.build b/src/meson.build index fb1d2ab56..8875fb2ef 100644 --- a/src/meson.build +++ b/src/meson.build @@ -679,6 +679,8 @@ if have_native_backend 'backends/native/meta-kms-device-private.h', 'backends/native/meta-kms-device.c', 'backends/native/meta-kms-device.h', + 'backends/native/meta-kms-impl-device-atomic.c', + 'backends/native/meta-kms-impl-device-atomic.h', 'backends/native/meta-kms-impl-device-simple.c', 'backends/native/meta-kms-impl-device-simple.h', 'backends/native/meta-kms-impl-device.c',