mirror of
https://github.com/brl/mutter.git
synced 2025-02-21 23:44:10 +00:00

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