mutter/src/backends/native/meta-kms-impl-device-simple.c
Marco Trevisan (Treviño) 47d7bc7a13 backends/kms: Implement privacy screen handling and setting
Privacy screen events on connector are handled as notification events
that won't cause any monitors reconfiguration but will emit monitors
changed on DBus, so that the new value can be fetched.

We monitor the hardware state so that we can also handle the case of
devices with hw-switchers only.

In case a software state is available it means we can also support
changing the state, and if so expose the state as unlocked.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1952>
2022-01-25 07:31:19 +00:00

1788 lines
59 KiB
C

/*
* 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-simple.h"
#include "backends/native/meta-backend-native-private.h"
#include "backends/native/meta-drm-buffer-gbm.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"
#include "backends/native/meta-kms-utils.h"
typedef gboolean (* MetaKmsSimpleProcessFunc) (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
gpointer entry_data,
GError **error);
typedef struct _CachedModeSet
{
GList *connectors;
drmModeModeInfo *drm_mode;
int width;
int height;
int stride;
uint32_t format;
uint64_t modifier;
} CachedModeSet;
struct _MetaKmsImplDeviceSimple
{
MetaKmsImplDevice parent;
GSource *mode_set_fallback_feedback_source;
GList *mode_set_fallback_page_flip_datas;
GList *pending_page_flip_retries;
GSource *retry_page_flips_source;
GList *postponed_page_flip_datas;
GList *postponed_mode_set_fallback_datas;
GList *posted_page_flip_datas;
GHashTable *cached_mode_sets;
};
static GInitableIface *initable_parent_iface;
static void
initable_iface_init (GInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE (MetaKmsImplDeviceSimple, meta_kms_impl_device_simple,
META_TYPE_KMS_IMPL_DEVICE,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
initable_iface_init))
static void
flush_postponed_page_flip_datas (MetaKmsImplDeviceSimple *impl_device_simple);
static gboolean
get_connector_property (MetaKmsImplDevice *impl_device,
MetaKmsConnector *connector,
MetaKmsConnectorProp prop,
uint64_t *value,
GError **error)
{
uint32_t prop_id;
int fd;
drmModeConnector *drm_connector;
int i;
gboolean found;
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,
"Property (%s) not found on connector %u",
meta_kms_connector_get_prop_name (connector, prop),
meta_kms_connector_get_id (connector));
return FALSE;
}
fd = meta_kms_impl_device_get_fd (impl_device);
drm_connector = drmModeGetConnector (fd,
meta_kms_connector_get_id (connector));
if (!drm_connector)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
"Failed to get connector %u resources: %s",
meta_kms_connector_get_id (connector),
g_strerror (errno));
return FALSE;
}
found = FALSE;
for (i = 0; i < drm_connector->count_props; i++)
{
if (drm_connector->props[i] == prop_id)
{
*value = drm_connector->prop_values[i];
found = TRUE;
break;
}
}
drmModeFreeConnector (drm_connector);
if (!found)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Connector property %u not found", prop_id);
return FALSE;
}
return TRUE;
}
static gboolean
set_connector_property (MetaKmsImplDevice *impl_device,
MetaKmsConnector *connector,
MetaKmsConnectorProp prop,
uint64_t value,
GError **error)
{
uint32_t prop_id;
int fd;
int ret;
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,
"Property (%s) not found on connector %u",
meta_kms_connector_get_prop_name (connector, prop),
meta_kms_connector_get_id (connector));
return FALSE;
}
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeObjectSetProperty (fd,
meta_kms_connector_get_id (connector),
DRM_MODE_OBJECT_CONNECTOR,
prop_id,
value);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set connector %u property %u: %s",
meta_kms_connector_get_id (connector),
prop_id,
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static gboolean
process_power_save (MetaKmsImplDevice *impl_device,
GError **error)
{
GList *l;
for (l = meta_kms_impl_device_peek_connectors (impl_device); l; l = l->next)
{
MetaKmsConnector *connector = l->data;
meta_topic (META_DEBUG_KMS,
"[simple] Setting DPMS of connector %u (%s) to OFF",
meta_kms_connector_get_id (connector),
meta_kms_impl_device_get_path (impl_device));
if (!set_connector_property (impl_device,
connector,
META_KMS_CONNECTOR_PROP_DPMS,
DRM_MODE_DPMS_OFF,
error))
return FALSE;
}
return TRUE;
}
static gboolean
process_connector_update (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
gpointer update_entry,
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,
"[simple] 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 (!set_connector_property (impl_device,
connector,
META_KMS_CONNECTOR_PROP_UNDERSCAN,
1,
error))
return FALSE;
if (!set_connector_property (impl_device,
connector,
META_KMS_CONNECTOR_PROP_UNDERSCAN_HBORDER,
connector_update->underscanning.hborder,
error))
return FALSE;
if (!set_connector_property (impl_device,
connector,
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,
"[simple] Unsetting underscanning on connector %u (%s)",
meta_kms_connector_get_id (connector),
meta_kms_impl_device_get_path (impl_device));
if (!set_connector_property (impl_device,
connector,
META_KMS_CONNECTOR_PROP_UNDERSCAN,
0,
error))
return FALSE;
}
if (connector_update->privacy_screen.has_update)
{
meta_topic (META_DEBUG_KMS,
"[simple] Toggling privacy screen to %d on connector %u (%s)",
connector_update->privacy_screen.is_enabled,
meta_kms_connector_get_id (connector),
meta_kms_impl_device_get_path (impl_device));
if (!set_connector_property (impl_device,
connector,
META_KMS_CONNECTOR_PROP_PRIVACY_SCREEN_SW_STATE,
connector_update->privacy_screen.is_enabled,
error))
return FALSE;
}
return TRUE;
}
static CachedModeSet *
cached_mode_set_new (GList *connectors,
const drmModeModeInfo *drm_mode,
MetaDrmBuffer *buffer)
{
CachedModeSet *cached_mode_set;
cached_mode_set = g_new0 (CachedModeSet, 1);
*cached_mode_set = (CachedModeSet) {
.connectors = g_list_copy (connectors),
.drm_mode = g_memdup2 (drm_mode, sizeof *drm_mode),
.width = meta_drm_buffer_get_width (buffer),
.height = meta_drm_buffer_get_height (buffer),
.stride = meta_drm_buffer_get_stride (buffer),
.format = meta_drm_buffer_get_format (buffer),
.modifier = meta_drm_buffer_get_modifier (buffer),
};
return cached_mode_set;
}
static void
cached_mode_set_free (CachedModeSet *cached_mode_set)
{
g_list_free (cached_mode_set->connectors);
g_free (cached_mode_set->drm_mode);
g_free (cached_mode_set);
}
static void
fill_connector_ids_array (GList *connectors,
uint32_t **out_connectors,
int *out_n_connectors)
{
GList *l;
int i;
*out_n_connectors = g_list_length (connectors);
*out_connectors = g_new0 (uint32_t, *out_n_connectors);
i = 0;
for (l = connectors; l; l = l->next)
{
MetaKmsConnector *connector = l->data;
(*out_connectors)[i++] = meta_kms_connector_get_id (connector);
}
}
static gboolean
set_plane_rotation (MetaKmsImplDevice *impl_device,
MetaKmsPlane *plane,
uint64_t rotation,
GError **error)
{
int fd;
uint32_t rotation_prop_id;
int ret;
fd = meta_kms_impl_device_get_fd (impl_device);
rotation_prop_id = meta_kms_plane_get_prop_id (plane,
META_KMS_PLANE_PROP_ROTATION);
meta_topic (META_DEBUG_KMS,
"[simple] Setting plane %u (%s) rotation to %" G_GUINT64_FORMAT,
meta_kms_plane_get_id (plane),
meta_kms_impl_device_get_path (impl_device),
rotation);
ret = drmModeObjectSetProperty (fd,
meta_kms_plane_get_id (plane),
DRM_MODE_OBJECT_PLANE,
rotation_prop_id,
rotation);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to rotation property (%u) to %" G_GUINT64_FORMAT
" on plane %u: %s",
rotation_prop_id,
rotation,
meta_kms_plane_get_id (plane),
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static gboolean
process_mode_set (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
gpointer update_entry,
GError **error)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
MetaKmsModeSet *mode_set = update_entry;
MetaKmsCrtc *crtc = mode_set->crtc;
g_autofree uint32_t *connectors = NULL;
int n_connectors;
MetaKmsPlaneAssignment *plane_assignment;
MetaDrmBuffer *buffer;
drmModeModeInfo *drm_mode;
uint32_t x, y;
uint32_t fb_id;
int fd;
int ret;
crtc = mode_set->crtc;
if (mode_set->mode)
{
GList *l;
drm_mode = g_alloca (sizeof *drm_mode);
*drm_mode = *meta_kms_mode_get_drm_mode (mode_set->mode);
fill_connector_ids_array (mode_set->connectors,
&connectors,
&n_connectors);
plane_assignment = meta_kms_update_get_primary_plane_assignment (update,
crtc);
if (!plane_assignment)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing primary plane assignment for legacy mode set on CRTC %u",
meta_kms_crtc_get_id (crtc));
return FALSE;
}
x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
if (plane_assignment->rotation)
{
if (!set_plane_rotation (impl_device,
plane_assignment->plane,
plane_assignment->rotation,
error))
return FALSE;
}
buffer = plane_assignment->buffer;
if (!meta_drm_buffer_ensure_fb_id (buffer, error))
return FALSE;
fb_id = meta_drm_buffer_get_fb_id (buffer);
for (l = mode_set->connectors; l; l = l->next)
{
MetaKmsConnector *connector = l->data;
uint64_t dpms_value;
if (!get_connector_property (impl_device,
connector,
META_KMS_CONNECTOR_PROP_DPMS,
&dpms_value,
error))
return FALSE;
if (dpms_value != DRM_MODE_DPMS_ON)
{
meta_topic (META_DEBUG_KMS,
"[simple] Setting DPMS of connector %u (%s) to ON",
meta_kms_connector_get_id (connector),
meta_kms_impl_device_get_path (impl_device));
if (!set_connector_property (impl_device,
connector,
META_KMS_CONNECTOR_PROP_DPMS,
DRM_MODE_DPMS_ON,
error))
return FALSE;
}
}
meta_topic (META_DEBUG_KMS,
"[simple] Setting mode of CRTC %u (%s) to %s",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device),
drm_mode->name);
}
else
{
buffer = NULL;
drm_mode = NULL;
x = y = 0;
n_connectors = 0;
connectors = NULL;
fb_id = 0;
meta_topic (META_DEBUG_KMS,
"[simple] Unsetting mode of CRTC %u (%s)",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device));
}
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeSetCrtc (fd,
meta_kms_crtc_get_id (crtc),
fb_id,
x, y,
connectors, n_connectors,
drm_mode);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set mode %s on CRTC %u: %s",
drm_mode ? drm_mode->name : "off",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
if (drm_mode)
{
g_hash_table_replace (impl_device_simple->cached_mode_sets,
crtc,
cached_mode_set_new (mode_set->connectors,
drm_mode,
buffer));
}
else
{
g_hash_table_remove (impl_device_simple->cached_mode_sets, crtc);
}
return TRUE;
}
static gboolean
process_crtc_gamma (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
gpointer update_entry,
GError **error)
{
MetaKmsCrtcGamma *gamma = update_entry;
MetaKmsCrtc *crtc = gamma->crtc;
int fd;
int ret;
meta_topic (META_DEBUG_KMS,
"[simple] Setting CRTC %u (%s) gamma, size: %d",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device),
gamma->size);
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeCrtcSetGamma (fd, meta_kms_crtc_get_id (crtc),
gamma->size,
gamma->red,
gamma->green,
gamma->blue);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeCrtcSetGamma on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static gboolean
is_timestamp_earlier_than (uint64_t ts1,
uint64_t ts2)
{
if (ts1 == ts2)
return FALSE;
else
return ts2 - ts1 < UINT64_MAX / 2;
}
typedef struct _RetryPageFlipData
{
MetaKmsCrtc *crtc;
uint32_t fb_id;
MetaKmsPageFlipData *page_flip_data;
float refresh_rate;
uint64_t retry_time_us;
MetaKmsCustomPageFlip *custom_page_flip;
} RetryPageFlipData;
static void
retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data)
{
g_assert (!retry_page_flip_data->page_flip_data);
g_clear_pointer (&retry_page_flip_data->custom_page_flip,
meta_kms_custom_page_flip_free);
g_free (retry_page_flip_data);
}
static CachedModeSet *
get_cached_mode_set (MetaKmsImplDeviceSimple *impl_device_simple,
MetaKmsCrtc *crtc)
{
return g_hash_table_lookup (impl_device_simple->cached_mode_sets, crtc);
}
static float
get_cached_crtc_refresh_rate (MetaKmsImplDeviceSimple *impl_device_simple,
MetaKmsCrtc *crtc)
{
CachedModeSet *cached_mode_set;
cached_mode_set = g_hash_table_lookup (impl_device_simple->cached_mode_sets,
crtc);
g_assert (cached_mode_set);
return meta_calculate_drm_mode_refresh_rate (cached_mode_set->drm_mode);
}
#define meta_assert_in_kms_impl(kms) \
g_assert (meta_kms_in_impl_task (kms))
static gboolean
retry_page_flips (gpointer user_data)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (user_data);
MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_simple);
uint64_t now_us;
GList *l;
now_us = g_source_get_time (impl_device_simple->retry_page_flips_source);
l = impl_device_simple->pending_page_flip_retries;
while (l)
{
RetryPageFlipData *retry_page_flip_data = l->data;
MetaKmsCrtc *crtc = retry_page_flip_data->crtc;
GList *l_next = l->next;
int fd;
int ret;
MetaKmsPageFlipData *page_flip_data;
MetaKmsCustomPageFlip *custom_page_flip;
if (is_timestamp_earlier_than (now_us,
retry_page_flip_data->retry_time_us))
{
l = l_next;
continue;
}
custom_page_flip = retry_page_flip_data->custom_page_flip;
if (custom_page_flip)
{
meta_topic (META_DEBUG_KMS,
"[simple] Retrying custom page flip on CRTC %u (%s)",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device));
ret = custom_page_flip->func (custom_page_flip->user_data,
retry_page_flip_data->page_flip_data);
}
else
{
meta_topic (META_DEBUG_KMS,
"[simple] Retrying page flip on CRTC %u (%s) with %u",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device),
retry_page_flip_data->fb_id);
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
retry_page_flip_data->fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
retry_page_flip_data->page_flip_data);
}
if (ret == -EBUSY)
{
float refresh_rate;
meta_topic (META_DEBUG_KMS,
"[simple] Rescheduling page flip retry on CRTC %u (%s)",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device));
refresh_rate =
get_cached_crtc_refresh_rate (impl_device_simple, crtc);
retry_page_flip_data->retry_time_us +=
(uint64_t) (G_USEC_PER_SEC / refresh_rate);
l = l_next;
continue;
}
impl_device_simple->pending_page_flip_retries =
g_list_remove_link (impl_device_simple->pending_page_flip_retries, l);
page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
if (ret != 0)
{
g_autoptr (GError) error = NULL;
g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModePageFlip on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
if (!g_error_matches (error,
G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED))
g_critical ("Failed to page flip: %s", error->message);
meta_kms_page_flip_data_discard_in_impl (page_flip_data, error);
meta_kms_impl_device_unhold_fd (impl_device);
}
else
{
impl_device_simple->posted_page_flip_datas =
g_list_prepend (impl_device_simple->posted_page_flip_datas,
page_flip_data);
}
retry_page_flip_data_free (retry_page_flip_data);
l = l_next;
}
if (impl_device_simple->pending_page_flip_retries)
{
GList *l;
uint64_t earliest_retry_time_us = 0;
for (l = impl_device_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *retry_page_flip_data = l->data;
if (l == impl_device_simple->pending_page_flip_retries ||
is_timestamp_earlier_than (retry_page_flip_data->retry_time_us,
earliest_retry_time_us))
earliest_retry_time_us = retry_page_flip_data->retry_time_us;
}
g_source_set_ready_time (impl_device_simple->retry_page_flips_source,
earliest_retry_time_us);
return G_SOURCE_CONTINUE;
}
else
{
g_clear_pointer (&impl_device_simple->retry_page_flips_source,
g_source_unref);
flush_postponed_page_flip_datas (impl_device_simple);
return G_SOURCE_REMOVE;
}
}
static void
schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple,
MetaKmsCrtc *crtc,
uint32_t fb_id,
float refresh_rate,
MetaKmsPageFlipData *page_flip_data,
MetaKmsCustomPageFlip *custom_page_flip)
{
RetryPageFlipData *retry_page_flip_data;
uint64_t now_us;
uint64_t retry_time_us;
now_us = g_get_monotonic_time ();
retry_time_us = now_us + (uint64_t) (G_USEC_PER_SEC / refresh_rate);
retry_page_flip_data = g_new0 (RetryPageFlipData, 1);
*retry_page_flip_data = (RetryPageFlipData) {
.crtc = crtc,
.fb_id = fb_id,
.page_flip_data = page_flip_data,
.refresh_rate = refresh_rate,
.retry_time_us = retry_time_us,
.custom_page_flip = custom_page_flip,
};
if (!impl_device_simple->retry_page_flips_source)
{
MetaKmsImplDevice *impl_device =
META_KMS_IMPL_DEVICE (impl_device_simple);
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
MetaKms *kms = meta_kms_device_get_kms (device);
GSource *source;
source = meta_kms_add_source_in_impl (kms, retry_page_flips,
impl_device_simple, NULL);
g_source_set_ready_time (source, retry_time_us);
impl_device_simple->retry_page_flips_source = source;
}
else
{
GList *l;
for (l = impl_device_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *pending_retry_page_flip_data = l->data;
uint64_t pending_retry_time_us =
pending_retry_page_flip_data->retry_time_us;
if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us))
{
g_source_set_ready_time (impl_device_simple->retry_page_flips_source,
retry_time_us);
break;
}
}
}
impl_device_simple->pending_page_flip_retries =
g_list_append (impl_device_simple->pending_page_flip_retries,
retry_page_flip_data);
}
static void
dispatch_page_flip_datas (GList **page_flip_datas,
GFunc func,
gpointer user_data)
{
g_list_foreach (*page_flip_datas, func, user_data);
g_clear_pointer (page_flip_datas, g_list_free);
}
static gboolean
mode_set_fallback_feedback_idle (gpointer user_data)
{
MetaKmsImplDeviceSimple *impl_device_simple = user_data;
g_clear_pointer (&impl_device_simple->mode_set_fallback_feedback_source,
g_source_unref);
if (impl_device_simple->pending_page_flip_retries)
{
impl_device_simple->postponed_mode_set_fallback_datas =
g_steal_pointer (&impl_device_simple->mode_set_fallback_page_flip_datas);
}
else
{
dispatch_page_flip_datas (&impl_device_simple->mode_set_fallback_page_flip_datas,
(GFunc) meta_kms_page_flip_data_mode_set_fallback_in_impl,
NULL);
}
return G_SOURCE_REMOVE;
}
static gboolean
mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple,
MetaKmsUpdate *update,
MetaKmsPlaneAssignment *plane_assignment,
MetaKmsPageFlipData *page_flip_data,
GError **error)
{
MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_simple);
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
MetaKms *kms = meta_kms_device_get_kms (device);
MetaKmsCrtc *crtc = meta_kms_page_flip_data_get_crtc (page_flip_data);
CachedModeSet *cached_mode_set;
g_autofree uint32_t *connectors = NULL;
int n_connectors;
uint32_t fb_id;
uint32_t x, y;
int fd;
int ret;
cached_mode_set = g_hash_table_lookup (impl_device_simple->cached_mode_sets,
crtc);
if (!cached_mode_set)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing mode set for page flip fallback");
return FALSE;
}
if (!meta_drm_buffer_ensure_fb_id (plane_assignment->buffer, error))
return FALSE;
fill_connector_ids_array (cached_mode_set->connectors,
&connectors,
&n_connectors);
fb_id = meta_drm_buffer_get_fb_id (plane_assignment->buffer);
x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeSetCrtc (fd,
meta_kms_crtc_get_id (crtc),
fb_id,
x, y,
connectors, n_connectors,
cached_mode_set->drm_mode);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeSetCrtc mode '%s' on CRTC %u failed: %s",
cached_mode_set->drm_mode->name,
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
if (!impl_device_simple->mode_set_fallback_feedback_source)
{
GSource *source;
source = meta_kms_add_source_in_impl (kms,
mode_set_fallback_feedback_idle,
impl_device_simple,
NULL);
impl_device_simple->mode_set_fallback_feedback_source = source;
}
impl_device_simple->mode_set_fallback_page_flip_datas =
g_list_prepend (impl_device_simple->mode_set_fallback_page_flip_datas,
page_flip_data);
return TRUE;
}
static gboolean
symbolic_page_flip_idle (gpointer user_data)
{
MetaKmsPageFlipData *page_flip_data = user_data;
MetaKmsImplDevice *impl_device;
MetaKmsCrtc *crtc;
impl_device = meta_kms_page_flip_data_get_impl_device (page_flip_data);
crtc = meta_kms_page_flip_data_get_crtc (page_flip_data);
meta_topic (META_DEBUG_KMS,
"[simple] Handling symbolic page flip callback from %s, data: %p, CRTC: %u",
meta_kms_impl_device_get_path (impl_device),
page_flip_data,
meta_kms_crtc_get_id (crtc));
meta_kms_impl_device_handle_page_flip_callback (impl_device, page_flip_data);
return G_SOURCE_REMOVE;
}
static gboolean
dispatch_page_flip (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
MetaKmsPageFlipData *page_flip_data,
GError **error)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
MetaKmsCrtc *crtc;
MetaKmsPlaneAssignment *plane_assignment;
g_autoptr (MetaKmsCustomPageFlip) custom_page_flip = NULL;
int fd;
int ret;
crtc = meta_kms_page_flip_data_get_crtc (page_flip_data);
plane_assignment = meta_kms_update_get_primary_plane_assignment (update,
crtc);
custom_page_flip = meta_kms_update_take_custom_page_flip_func (update);
if (!plane_assignment && !custom_page_flip)
{
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
MetaKms *kms = meta_kms_device_get_kms (device);
GSource *source;
meta_kms_page_flip_data_make_symbolic (page_flip_data);
source = meta_kms_add_source_in_impl (kms,
symbolic_page_flip_idle,
page_flip_data,
NULL);
g_source_set_ready_time (source, 0);
g_source_unref (source);
return TRUE;
}
if (plane_assignment && plane_assignment->buffer &&
!meta_drm_buffer_ensure_fb_id (plane_assignment->buffer, error))
return FALSE;
fd = meta_kms_impl_device_get_fd (impl_device);
if (custom_page_flip)
{
meta_topic (META_DEBUG_KMS,
"[simple] Invoking custom page flip on CRTC %u (%s)",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device));
ret = custom_page_flip->func (custom_page_flip->user_data,
page_flip_data);
}
else
{
uint32_t fb_id;
fb_id = meta_drm_buffer_get_fb_id (plane_assignment->buffer);
meta_topic (META_DEBUG_KMS,
"[simple] Page flipping CRTC %u (%s) with %u, data: %p",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device),
fb_id,
page_flip_data);
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
page_flip_data);
}
if (ret == -EBUSY)
{
CachedModeSet *cached_mode_set;
meta_topic (META_DEBUG_KMS,
"[simple] Scheduling page flip retry on CRTC %u (%s)",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device));
cached_mode_set = get_cached_mode_set (impl_device_simple, crtc);
if (cached_mode_set)
{
uint32_t fb_id;
drmModeModeInfo *drm_mode;
float refresh_rate;
if (plane_assignment)
fb_id = meta_drm_buffer_get_fb_id (plane_assignment->buffer);
else
fb_id = 0;
drm_mode = cached_mode_set->drm_mode;
refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
meta_kms_impl_device_hold_fd (impl_device);
schedule_retry_page_flip (impl_device_simple,
crtc,
fb_id,
refresh_rate,
page_flip_data,
g_steal_pointer (&custom_page_flip));
return TRUE;
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Page flip of %u failed, and no mode set available",
meta_kms_crtc_get_id (crtc));
return FALSE;
}
}
else if (ret == -EINVAL)
{
meta_topic (META_DEBUG_KMS,
"[simple] Falling back to mode set on CRTC %u (%s)",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device));
return mode_set_fallback (impl_device_simple,
update,
plane_assignment,
page_flip_data,
error);
}
else if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModePageFlip on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
else
{
meta_kms_impl_device_hold_fd (impl_device);
impl_device_simple->posted_page_flip_datas =
g_list_prepend (impl_device_simple->posted_page_flip_datas,
page_flip_data);
return TRUE;
}
}
static GList *
generate_page_flip_datas (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update)
{
GList *listeners;
GList *page_flip_datas = NULL;
listeners = g_list_copy (meta_kms_update_get_page_flip_listeners (update));
while (listeners)
{
MetaKmsPageFlipListener *listener = listeners->data;
MetaKmsCrtc *crtc = listener->crtc;
MetaKmsPageFlipData *page_flip_data;
gpointer user_data;
GDestroyNotify destroy_notify;
GList *l;
page_flip_data = meta_kms_page_flip_data_new (impl_device, crtc);
page_flip_datas = g_list_append (page_flip_datas, page_flip_data);
user_data = g_steal_pointer (&listener->user_data);
destroy_notify = g_steal_pointer (&listener->destroy_notify);
meta_kms_page_flip_data_add_listener (page_flip_data,
listener->vtable,
listener->flags,
user_data,
destroy_notify);
listeners = g_list_delete_link (listeners, listeners);
l = listeners;
while (l)
{
MetaKmsPageFlipListener *other_listener = l->data;
GList *l_next = l->next;
if (other_listener->crtc == crtc)
{
gpointer other_user_data;
GDestroyNotify other_destroy_notify;
other_user_data = g_steal_pointer (&other_listener->user_data);
other_destroy_notify =
g_steal_pointer (&other_listener->destroy_notify);
meta_kms_page_flip_data_add_listener (page_flip_data,
other_listener->vtable,
other_listener->flags,
other_user_data,
other_destroy_notify);
listeners = g_list_delete_link (listeners, l);
}
l = l_next;
}
}
return page_flip_datas;
}
static gboolean
maybe_dispatch_page_flips (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
GList **failed_planes,
MetaKmsUpdateFlag flags,
GError **error)
{
g_autoptr (GList) page_flip_datas = NULL;
GList *l;
page_flip_datas = generate_page_flip_datas (impl_device, update);
while (page_flip_datas)
{
g_autoptr (GList) l = NULL;
MetaKmsPageFlipData *page_flip_data;
l = page_flip_datas;
page_flip_datas = g_list_remove_link (page_flip_datas, l);
page_flip_data = g_steal_pointer (&l->data);
if (!dispatch_page_flip (impl_device, update, page_flip_data, error))
{
if (!g_error_matches (*error,
G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED))
{
MetaKmsCrtc *crtc =
meta_kms_page_flip_data_get_crtc (page_flip_data);
MetaKmsPlaneAssignment *plane_assignment;
MetaKmsPlaneFeedback *plane_feedback;
plane_assignment =
meta_kms_update_get_primary_plane_assignment (update, crtc);
plane_feedback =
meta_kms_plane_feedback_new_take_error (plane_assignment->plane,
plane_assignment->crtc,
g_error_copy (*error));
*failed_planes = g_list_prepend (*failed_planes, plane_feedback);
}
if (!(flags & META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR))
meta_kms_page_flip_data_discard_in_impl (page_flip_data, *error);
goto err;
}
}
return TRUE;
err:
if (!(flags & META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR))
{
for (l = page_flip_datas; l; l = l->next)
{
MetaKmsPageFlipData *page_flip_data = l->data;
meta_kms_page_flip_data_discard_in_impl (page_flip_data, *error);
}
}
g_list_free (page_flip_datas);
return FALSE;
}
static gboolean
process_entries (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
GList *entries,
MetaKmsSimpleProcessFunc func,
GError **error)
{
GList *l;
for (l = entries; l; l = l->next)
{
if (!func (impl_device, update, l->data, error))
return FALSE;
}
return TRUE;
}
static gboolean
process_cursor_plane_assignment (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
MetaKmsPlaneAssignment *plane_assignment,
GError **error)
{
uint32_t crtc_id;
int fd;
crtc_id = meta_kms_crtc_get_id (plane_assignment->crtc),
fd = meta_kms_impl_device_get_fd (impl_device);
if (!(plane_assignment->flags & META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED))
{
int width, height;
int ret = -1;
uint32_t handle_u32;
width = plane_assignment->dst_rect.width;
height = plane_assignment->dst_rect.height;
if (plane_assignment->buffer)
{
MetaDrmBufferGbm *buffer_gbm =
META_DRM_BUFFER_GBM (plane_assignment->buffer);
struct gbm_bo *bo;
union gbm_bo_handle handle;
bo = meta_drm_buffer_gbm_get_bo (buffer_gbm);
handle = gbm_bo_get_handle (bo);
handle_u32 = handle.u32;
}
else
{
handle_u32 = 0;
}
meta_topic (META_DEBUG_KMS,
"[simple] Setting HW cursor of CRTC %u (%s) to %u "
"(size: %dx%d, hot: (%d, %d))",
crtc_id,
meta_kms_impl_device_get_path (impl_device),
handle_u32,
width, height,
plane_assignment->cursor_hotspot.x,
plane_assignment->cursor_hotspot.y);
if (plane_assignment->cursor_hotspot.is_valid)
{
ret = drmModeSetCursor2 (fd,
crtc_id,
handle_u32,
width, height,
plane_assignment->cursor_hotspot.x,
plane_assignment->cursor_hotspot.y);
}
if (ret != 0)
{
ret = drmModeSetCursor (fd, crtc_id,
handle_u32,
width, height);
}
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeSetCursor failed: %s", g_strerror (-ret));
return FALSE;
}
}
meta_topic (META_DEBUG_KMS,
"[simple] Moving HW cursor of CRTC %u (%s) to (%d, %d)",
crtc_id,
meta_kms_impl_device_get_path (impl_device),
plane_assignment->dst_rect.x,
plane_assignment->dst_rect.y);
drmModeMoveCursor (fd,
crtc_id,
plane_assignment->dst_rect.x,
plane_assignment->dst_rect.y);
return TRUE;
}
static gboolean
process_plane_assignment (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
MetaKmsPlaneAssignment *plane_assignment,
MetaKmsPlaneFeedback **plane_feedback)
{
MetaKmsPlane *plane;
MetaKmsPlaneType plane_type;
GError *error = NULL;
plane = plane_assignment->plane;
plane_type = meta_kms_plane_get_plane_type (plane);
switch (plane_type)
{
case META_KMS_PLANE_TYPE_PRIMARY:
/* Handled as part of the mode-set and page flip. */
return TRUE;
case META_KMS_PLANE_TYPE_CURSOR:
if (!process_cursor_plane_assignment (impl_device, update,
plane_assignment,
&error))
{
*plane_feedback =
meta_kms_plane_feedback_new_take_error (plane,
plane_assignment->crtc,
g_steal_pointer (&error));
return FALSE;
}
else
{
return TRUE;
}
case META_KMS_PLANE_TYPE_OVERLAY:
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
"Overlay planes cannot be assigned");
*plane_feedback =
meta_kms_plane_feedback_new_take_error (plane,
plane_assignment->crtc,
g_steal_pointer (&error));
return TRUE;
}
g_assert_not_reached ();
}
static gboolean
process_plane_assignments (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
GList **failed_planes,
GError **error)
{
GList *l;
for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next)
{
MetaKmsPlaneAssignment *plane_assignment = l->data;
MetaKmsPlaneFeedback *plane_feedback;
if (!process_plane_assignment (impl_device, update, plane_assignment,
&plane_feedback))
{
if (g_error_matches (plane_feedback->error,
G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED))
{
g_propagate_error (error,
g_steal_pointer (&plane_feedback->error));
meta_kms_plane_feedback_free (plane_feedback);
return FALSE;
}
*failed_planes = g_list_prepend (*failed_planes, plane_feedback);
if (plane_assignment->flags & META_KMS_ASSIGN_PLANE_FLAG_ALLOW_FAIL)
{
continue;
}
else
{
g_propagate_error (error, g_error_copy (plane_feedback->error));
return FALSE;
}
}
}
return TRUE;
}
static void
page_flip_handler (int fd,
unsigned int sequence,
unsigned int tv_sec,
unsigned int tv_usec,
void *user_data)
{
MetaKmsPageFlipData *page_flip_data = user_data;
MetaKmsImplDevice *impl_device;
MetaKmsImplDeviceSimple *impl_device_simple;
MetaKmsCrtc *crtc;
meta_kms_page_flip_data_set_timings_in_impl (page_flip_data,
sequence, tv_sec, tv_usec);
impl_device = meta_kms_page_flip_data_get_impl_device (page_flip_data);
impl_device_simple = META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
crtc = meta_kms_page_flip_data_get_crtc (page_flip_data);
meta_topic (META_DEBUG_KMS,
"[simple] Handling page flip callback from %s, data: %p, CRTC: %u",
meta_kms_impl_device_get_path (impl_device),
page_flip_data,
meta_kms_crtc_get_id (crtc));
meta_kms_impl_device_unhold_fd (impl_device);
meta_kms_impl_device_handle_page_flip_callback (impl_device, page_flip_data);
impl_device_simple->posted_page_flip_datas =
g_list_remove (impl_device_simple->posted_page_flip_datas,
page_flip_data);
}
static void
meta_kms_impl_device_simple_setup_drm_event_context (MetaKmsImplDevice *impl_device,
drmEventContext *drm_event_context)
{
drm_event_context->version = 2;
drm_event_context->page_flip_handler = page_flip_handler;
}
static MetaKmsFeedback *
perform_update_test (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
GList *failed_planes = NULL;
GList *l;
for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next)
{
MetaKmsPlaneAssignment *plane_assignment = l->data;
MetaKmsPlane *plane = plane_assignment->plane;
MetaKmsCrtc *crtc = plane_assignment->crtc;
MetaDrmBuffer *buffer = plane_assignment->buffer;
CachedModeSet *cached_mode_set;
if (!plane_assignment->crtc ||
!plane_assignment->buffer)
continue;
cached_mode_set = get_cached_mode_set (impl_device_simple,
plane_assignment->crtc);
if (!cached_mode_set)
{
MetaKmsPlaneFeedback *plane_feedback;
plane_feedback =
meta_kms_plane_feedback_new_failed (plane, crtc,
"No existing mode set");
failed_planes = g_list_append (failed_planes, plane_feedback);
continue;
}
if (meta_drm_buffer_get_width (buffer) != cached_mode_set->width ||
meta_drm_buffer_get_height (buffer) != cached_mode_set->height ||
meta_drm_buffer_get_stride (buffer) != cached_mode_set->stride ||
meta_drm_buffer_get_format (buffer) != cached_mode_set->format ||
meta_drm_buffer_get_modifier (buffer) != cached_mode_set->modifier)
{
MetaKmsPlaneFeedback *plane_feedback;
plane_feedback =
meta_kms_plane_feedback_new_failed (plane, crtc,
"Incompatible buffer");
failed_planes = g_list_append (failed_planes, plane_feedback);
continue;
}
}
if (failed_planes)
{
GError *error;
error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_FAILED,
"One or more buffers incompatible");
return meta_kms_feedback_new_failed (failed_planes, error);
}
else
{
return meta_kms_feedback_new_passed (NULL);
}
}
static MetaKmsFeedback *
meta_kms_impl_device_simple_process_update (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
MetaKmsUpdateFlag flags)
{
GError *error = NULL;
GList *failed_planes = NULL;
meta_topic (META_DEBUG_KMS,
"[simple] Processing update %" G_GUINT64_FORMAT,
meta_kms_update_get_sequence_number (update));
if (flags & META_KMS_UPDATE_FLAG_TEST_ONLY)
return perform_update_test (impl_device, update);
if (meta_kms_update_is_power_save (update))
{
if (!process_power_save (impl_device, &error))
goto err;
goto out;
}
if (!process_entries (impl_device,
update,
meta_kms_update_get_mode_sets (update),
process_mode_set,
&error))
goto err;
if (!process_entries (impl_device,
update,
meta_kms_update_get_connector_updates (update),
process_connector_update,
&error))
goto err;
if (!process_entries (impl_device,
update,
meta_kms_update_get_crtc_gammas (update),
process_crtc_gamma,
&error))
goto err;
if (!process_plane_assignments (impl_device, update, &failed_planes, &error))
goto err;
if (!maybe_dispatch_page_flips (impl_device, update, &failed_planes, flags,
&error))
goto err;
out:
return meta_kms_feedback_new_passed (failed_planes);
err:
return meta_kms_feedback_new_failed (failed_planes, error);
}
static void
flush_postponed_page_flip_datas (MetaKmsImplDeviceSimple *impl_device_simple)
{
dispatch_page_flip_datas (&impl_device_simple->postponed_page_flip_datas,
(GFunc) meta_kms_page_flip_data_flipped_in_impl,
NULL);
dispatch_page_flip_datas (&impl_device_simple->postponed_mode_set_fallback_datas,
(GFunc) meta_kms_page_flip_data_mode_set_fallback_in_impl,
NULL);
}
static void
meta_kms_impl_device_simple_handle_page_flip_callback (MetaKmsImplDevice *impl_device,
MetaKmsPageFlipData *page_flip_data)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
if (impl_device_simple->pending_page_flip_retries)
{
impl_device_simple->postponed_page_flip_datas =
g_list_append (impl_device_simple->postponed_page_flip_datas,
page_flip_data);
}
else
{
meta_kms_page_flip_data_flipped_in_impl (page_flip_data);
}
}
static void
dispose_page_flip_data (MetaKmsPageFlipData *page_flip_data,
MetaKmsImplDevice *impl_device)
{
meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
meta_kms_impl_device_unhold_fd (impl_device);
}
static void
meta_kms_impl_device_simple_discard_pending_page_flips (MetaKmsImplDevice *impl_device)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
GList *l;
if (!impl_device_simple->pending_page_flip_retries)
return;
for (l = impl_device_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *retry_page_flip_data = l->data;
MetaKmsPageFlipData *page_flip_data;
page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
meta_topic (META_DEBUG_KMS,
"[simple] Discarding page flip retry for CRTC %u (%s)",
meta_kms_crtc_get_id (
meta_kms_page_flip_data_get_crtc (page_flip_data)),
meta_kms_impl_device_get_path (
meta_kms_page_flip_data_get_impl_device (page_flip_data)));
dispose_page_flip_data (page_flip_data, impl_device);
retry_page_flip_data_free (retry_page_flip_data);
}
g_clear_pointer (&impl_device_simple->pending_page_flip_retries, g_list_free);
g_clear_pointer (&impl_device_simple->retry_page_flips_source,
g_source_destroy);
}
static void
meta_kms_impl_device_simple_prepare_shutdown (MetaKmsImplDevice *impl_device)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (impl_device);
g_list_foreach (impl_device_simple->posted_page_flip_datas,
(GFunc) dispose_page_flip_data,
impl_device);
g_clear_list (&impl_device_simple->posted_page_flip_datas, NULL);
}
static void
meta_kms_impl_device_simple_finalize (GObject *object)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (object);
MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_simple);
g_list_free_full (impl_device_simple->pending_page_flip_retries,
(GDestroyNotify) retry_page_flip_data_free);
dispatch_page_flip_datas (&impl_device_simple->postponed_page_flip_datas,
(GFunc) dispose_page_flip_data,
impl_device);
dispatch_page_flip_datas (&impl_device_simple->postponed_mode_set_fallback_datas,
(GFunc) dispose_page_flip_data,
impl_device);
g_assert (!impl_device_simple->posted_page_flip_datas);
g_clear_pointer (&impl_device_simple->mode_set_fallback_feedback_source,
g_source_destroy);
g_hash_table_destroy (impl_device_simple->cached_mode_sets);
G_OBJECT_CLASS (meta_kms_impl_device_simple_parent_class)->finalize (object);
}
static MetaDeviceFile *
meta_kms_impl_device_simple_open_device_file (MetaKmsImplDevice *impl_device,
const char *path,
GError **error)
{
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
MetaKms *kms = meta_kms_device_get_kms (device);
MetaBackend *backend = meta_kms_get_backend (kms);
MetaDevicePool *device_pool =
meta_backend_native_get_device_pool (META_BACKEND_NATIVE (backend));
g_autoptr (MetaDeviceFile) device_file = NULL;
device_file = meta_device_pool_open (device_pool, path,
META_DEVICE_FILE_FLAG_TAKE_CONTROL,
error);
if (!device_file)
return NULL;
if (!meta_device_file_has_tag (device_file,
META_DEVICE_FILE_TAG_KMS,
META_KMS_DEVICE_FILE_TAG_SIMPLE))
{
int fd = meta_device_file_get_fd (device_file);
g_warn_if_fail (!meta_device_file_has_tag (device_file,
META_DEVICE_FILE_TAG_KMS,
META_KMS_DEVICE_FILE_TAG_ATOMIC));
if (drmSetClientCap (fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0)
{
g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
"DRM_CLIENT_CAP_UNIVERSAL_PLANES not supported");
return NULL;
}
meta_device_file_tag (device_file,
META_DEVICE_FILE_TAG_KMS,
META_KMS_DEVICE_FILE_TAG_SIMPLE);
}
return g_steal_pointer (&device_file);
}
static gboolean
meta_kms_impl_device_simple_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
MetaKmsImplDeviceSimple *impl_device_simple =
META_KMS_IMPL_DEVICE_SIMPLE (initable);
MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_simple);
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
GList *l;
if (!initable_parent_iface->init (initable, cancellable, error))
return FALSE;
if (!meta_kms_impl_device_init_mode_setting (impl_device, error))
return FALSE;
impl_device_simple->cached_mode_sets =
g_hash_table_new_full (NULL,
NULL,
NULL,
(GDestroyNotify) cached_mode_set_free);
for (l = meta_kms_device_get_crtcs (device); l; l = l->next)
{
MetaKmsCrtc *crtc = l->data;
MetaKmsPlane *plane;
plane = meta_kms_device_get_cursor_plane_for (device, crtc);
if (plane)
continue;
meta_topic (META_DEBUG_KMS,
"[simple] Adding fake cursor plane for CRTC %u (%s)",
meta_kms_crtc_get_id (crtc),
meta_kms_impl_device_get_path (impl_device));
meta_kms_device_add_fake_plane_in_impl (device,
META_KMS_PLANE_TYPE_CURSOR,
crtc);
}
g_message ("Added device '%s' (%s) using non-atomic mode setting.",
meta_kms_impl_device_get_path (impl_device),
meta_kms_impl_device_get_driver_name (impl_device));
return TRUE;
}
static void
meta_kms_impl_device_simple_init (MetaKmsImplDeviceSimple *impl_device_simple)
{
}
static void
initable_iface_init (GInitableIface *iface)
{
initable_parent_iface = g_type_interface_peek_parent (iface);
iface->init = meta_kms_impl_device_simple_initable_init;
}
static void
meta_kms_impl_device_simple_class_init (MetaKmsImplDeviceSimpleClass *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_simple_finalize;
impl_device_class->open_device_file =
meta_kms_impl_device_simple_open_device_file;
impl_device_class->setup_drm_event_context =
meta_kms_impl_device_simple_setup_drm_event_context;
impl_device_class->process_update =
meta_kms_impl_device_simple_process_update;
impl_device_class->handle_page_flip_callback =
meta_kms_impl_device_simple_handle_page_flip_callback;
impl_device_class->discard_pending_page_flips =
meta_kms_impl_device_simple_discard_pending_page_flips;
impl_device_class->prepare_shutdown =
meta_kms_impl_device_simple_prepare_shutdown;
}