mutter/src/backends/native/meta-kms-impl-device.c
Daniel van Vugt 48c9b638f3 kms/impl-device: Inhibit deadline timer on vc4 (Raspberry Pi)
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>
2023-09-15 13:30:52 +08:00

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);
}