mirror of
https://github.com/brl/mutter.git
synced 2024-11-28 02:50:41 -05:00
46f81afb3b
The property enum type was wrong. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2319>
1276 lines
42 KiB
C
1276 lines
42 KiB
C
/*
|
|
* Copyright (C) 2019-2020 Red Hat
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/native/meta-kms-impl-device-atomic.h"
|
|
|
|
#include "backends/native/meta-backend-native-private.h"
|
|
#include "backends/native/meta-kms-connector-private.h"
|
|
#include "backends/native/meta-kms-crtc-private.h"
|
|
#include "backends/native/meta-kms-device-private.h"
|
|
#include "backends/native/meta-kms-mode-private.h"
|
|
#include "backends/native/meta-kms-plane-private.h"
|
|
#include "backends/native/meta-kms-private.h"
|
|
#include "backends/native/meta-kms-update-private.h"
|
|
|
|
typedef gboolean (* MetaKmsAtomicProcessFunc) (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
gpointer entry_data,
|
|
gpointer user_data,
|
|
GError **error);
|
|
|
|
struct _MetaKmsImplDeviceAtomic
|
|
{
|
|
MetaKmsImplDevice parent;
|
|
|
|
GHashTable *page_flip_datas;
|
|
};
|
|
|
|
static GInitableIface *initable_parent_iface;
|
|
|
|
static void
|
|
initable_iface_init (GInitableIface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (MetaKmsImplDeviceAtomic, meta_kms_impl_device_atomic,
|
|
META_TYPE_KMS_IMPL_DEVICE,
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
|
initable_iface_init))
|
|
|
|
static uint32_t
|
|
store_new_blob (MetaKmsImplDevice *impl_device,
|
|
GArray *blob_ids,
|
|
const void *data,
|
|
size_t size,
|
|
GError **error)
|
|
{
|
|
int fd = meta_kms_impl_device_get_fd (impl_device);
|
|
uint32_t blob_id;
|
|
int ret;
|
|
|
|
ret = drmModeCreatePropertyBlob (fd, data, size, &blob_id);
|
|
if (ret < 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
"drmModeCreatePropertyBlob: %s", g_strerror (-ret));
|
|
return 0;
|
|
}
|
|
|
|
g_array_append_val (blob_ids, blob_id);
|
|
|
|
return blob_id;
|
|
}
|
|
|
|
static void
|
|
release_blob_ids (MetaKmsImplDevice *impl_device,
|
|
GArray *blob_ids)
|
|
{
|
|
int fd = meta_kms_impl_device_get_fd (impl_device);
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < blob_ids->len; i++)
|
|
{
|
|
uint32_t blob_id = g_array_index (blob_ids, uint32_t, i);
|
|
|
|
drmModeDestroyPropertyBlob (fd, blob_id);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
add_connector_property (MetaKmsImplDevice *impl_device,
|
|
MetaKmsConnector *connector,
|
|
drmModeAtomicReq *req,
|
|
MetaKmsConnectorProp prop,
|
|
uint64_t value,
|
|
GError **error)
|
|
{
|
|
int ret;
|
|
uint32_t prop_id;
|
|
|
|
prop_id = meta_kms_connector_get_prop_id (connector, prop);
|
|
if (!prop_id)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"Connector property '%s' not found",
|
|
meta_kms_connector_get_prop_name (connector, prop));
|
|
return FALSE;
|
|
}
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting connector %u (%s) property '%s' (%u) to %"
|
|
G_GUINT64_FORMAT,
|
|
meta_kms_connector_get_id (connector),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
meta_kms_connector_get_prop_name (connector, prop),
|
|
meta_kms_connector_get_prop_id (connector, prop),
|
|
value);
|
|
ret = drmModeAtomicAddProperty (req,
|
|
meta_kms_connector_get_id (connector),
|
|
prop_id,
|
|
value);
|
|
if (ret < 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
"drmModeAtomicAddProperty, connector: %u, prop id: %u: %s",
|
|
meta_kms_connector_get_id (connector),
|
|
prop_id,
|
|
g_strerror (-ret));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
process_connector_update (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
gpointer update_entry,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update = update_entry;
|
|
MetaKmsConnector *connector = connector_update->connector;
|
|
|
|
if (connector_update->underscanning.has_update &&
|
|
connector_update->underscanning.is_active)
|
|
{
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting underscanning on connector %u (%s) to "
|
|
"%" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT,
|
|
meta_kms_connector_get_id (connector),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
connector_update->underscanning.hborder,
|
|
connector_update->underscanning.vborder);
|
|
|
|
if (!add_connector_property (impl_device,
|
|
connector, req,
|
|
META_KMS_CONNECTOR_PROP_UNDERSCAN,
|
|
1,
|
|
error))
|
|
return FALSE;
|
|
if (!add_connector_property (impl_device,
|
|
connector, req,
|
|
META_KMS_CONNECTOR_PROP_UNDERSCAN_HBORDER,
|
|
connector_update->underscanning.hborder,
|
|
error))
|
|
return FALSE;
|
|
if (!add_connector_property (impl_device,
|
|
connector, req,
|
|
META_KMS_CONNECTOR_PROP_UNDERSCAN_VBORDER,
|
|
connector_update->underscanning.vborder,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
else if (connector_update->underscanning.has_update)
|
|
{
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Unsetting underscanning on connector %u (%s)",
|
|
meta_kms_connector_get_id (connector),
|
|
meta_kms_impl_device_get_path (impl_device));
|
|
|
|
if (!add_connector_property (impl_device,
|
|
connector, req,
|
|
META_KMS_CONNECTOR_PROP_UNDERSCAN,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
if (connector_update->privacy_screen.has_update)
|
|
{
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Toggling privacy screen to %d on connector %u (%s)",
|
|
connector_update->privacy_screen.is_enabled,
|
|
meta_kms_connector_get_id (connector),
|
|
meta_kms_impl_device_get_path (impl_device));
|
|
|
|
if (!add_connector_property (impl_device,
|
|
connector, req,
|
|
META_KMS_CONNECTOR_PROP_PRIVACY_SCREEN_SW_STATE,
|
|
connector_update->privacy_screen.is_enabled,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
add_crtc_property (MetaKmsImplDevice *impl_device,
|
|
MetaKmsCrtc *crtc,
|
|
drmModeAtomicReq *req,
|
|
MetaKmsCrtcProp prop,
|
|
uint64_t value,
|
|
GError **error)
|
|
{
|
|
int ret;
|
|
uint32_t prop_id;
|
|
|
|
prop_id = meta_kms_crtc_get_prop_id (crtc, prop);
|
|
if (!prop_id)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"CRTC property (%s) not found",
|
|
meta_kms_crtc_get_prop_name (crtc, prop));
|
|
return FALSE;
|
|
}
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting CRTC %u (%s) property '%s' (%u) to %"
|
|
G_GUINT64_FORMAT,
|
|
meta_kms_crtc_get_id (crtc),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
meta_kms_crtc_get_prop_name (crtc, prop),
|
|
meta_kms_crtc_get_prop_id (crtc, prop),
|
|
value);
|
|
ret = drmModeAtomicAddProperty (req,
|
|
meta_kms_crtc_get_id (crtc),
|
|
prop_id,
|
|
value);
|
|
if (ret < 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
"drmModeAtomicAddProperty, crtc: %u, prop: %s (%u): %s",
|
|
meta_kms_crtc_get_id (crtc),
|
|
meta_kms_crtc_get_prop_name (crtc, prop),
|
|
prop_id,
|
|
g_strerror (-ret));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
process_mode_set (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
gpointer update_entry,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsModeSet *mode_set = update_entry;
|
|
MetaKmsCrtc *crtc = mode_set->crtc;
|
|
MetaKmsMode *mode;
|
|
|
|
mode = (MetaKmsMode *) mode_set->mode;
|
|
if (mode)
|
|
{
|
|
uint32_t mode_id;
|
|
GList *l;
|
|
|
|
mode_id = meta_kms_mode_create_blob_id (mode, error);
|
|
if (mode_id == 0)
|
|
return FALSE;
|
|
|
|
g_array_append_val (blob_ids, mode_id);
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting mode of CRTC %u (%s) to %s",
|
|
meta_kms_crtc_get_id (crtc),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
meta_kms_mode_get_name (mode));
|
|
|
|
if (!add_crtc_property (impl_device,
|
|
crtc, req,
|
|
META_KMS_CRTC_PROP_MODE_ID,
|
|
mode_id,
|
|
error))
|
|
return FALSE;
|
|
|
|
if (!add_crtc_property (impl_device,
|
|
crtc, req,
|
|
META_KMS_CRTC_PROP_ACTIVE,
|
|
1,
|
|
error))
|
|
return FALSE;
|
|
|
|
for (l = mode_set->connectors; l; l = l->next)
|
|
{
|
|
MetaKmsConnector *connector = l->data;
|
|
|
|
if (!add_connector_property (impl_device,
|
|
connector, req,
|
|
META_KMS_CONNECTOR_PROP_CRTC_ID,
|
|
meta_kms_crtc_get_id (crtc),
|
|
error))
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!add_crtc_property (impl_device,
|
|
crtc, req,
|
|
META_KMS_CRTC_PROP_MODE_ID,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
|
|
if (!add_crtc_property (impl_device,
|
|
crtc, req,
|
|
META_KMS_CRTC_PROP_ACTIVE,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Unsetting mode of (%u, %s)",
|
|
meta_kms_crtc_get_id (crtc),
|
|
meta_kms_impl_device_get_path (impl_device));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
add_plane_property (MetaKmsImplDevice *impl_device,
|
|
MetaKmsPlane *plane,
|
|
drmModeAtomicReq *req,
|
|
MetaKmsPlaneProp prop,
|
|
uint64_t value,
|
|
GError **error)
|
|
{
|
|
int ret;
|
|
uint32_t prop_id;
|
|
|
|
prop_id = meta_kms_plane_get_prop_id (plane, prop);
|
|
if (!prop_id)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"Plane property (%s) not found on %u",
|
|
meta_kms_plane_get_prop_name (plane, prop),
|
|
meta_kms_plane_get_id (plane));
|
|
return FALSE;
|
|
}
|
|
|
|
switch (meta_kms_plane_get_prop_internal_type (plane, prop))
|
|
{
|
|
case META_KMS_PROP_TYPE_RAW:
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting plane %u (%s) property '%s' (%u) to %"
|
|
G_GUINT64_FORMAT,
|
|
meta_kms_plane_get_id (plane),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
meta_kms_plane_get_prop_name (plane, prop),
|
|
meta_kms_plane_get_prop_id (plane, prop),
|
|
value);
|
|
break;
|
|
case META_KMS_PROP_TYPE_FIXED_16:
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting plane %u (%s) property '%s' (%u) to %.2f",
|
|
meta_kms_plane_get_id (plane),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
meta_kms_plane_get_prop_name (plane, prop),
|
|
meta_kms_plane_get_prop_id (plane, prop),
|
|
meta_fixed_16_to_double (value));
|
|
break;
|
|
}
|
|
ret = drmModeAtomicAddProperty (req,
|
|
meta_kms_plane_get_id (plane),
|
|
prop_id,
|
|
value);
|
|
if (ret < 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
"drmModeAtomicAddProperty, plane: %u, prop: %s (%u): %s",
|
|
meta_kms_plane_get_id (plane),
|
|
meta_kms_plane_get_prop_name (plane, prop),
|
|
prop_id,
|
|
g_strerror (-ret));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const char *
|
|
get_plane_type_string (MetaKmsPlane *plane)
|
|
{
|
|
switch (meta_kms_plane_get_plane_type (plane))
|
|
{
|
|
case META_KMS_PLANE_TYPE_PRIMARY:
|
|
return "primary";
|
|
case META_KMS_PLANE_TYPE_CURSOR:
|
|
return "cursor";
|
|
case META_KMS_PLANE_TYPE_OVERLAY:
|
|
return "overlay";
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
static gboolean
|
|
process_plane_assignment (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
gpointer update_entry,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsPlaneAssignment *plane_assignment = update_entry;
|
|
MetaKmsPlane *plane = plane_assignment->plane;
|
|
MetaDrmBuffer *buffer;
|
|
MetaKmsFbDamage *fb_damage;
|
|
uint32_t prop_id;
|
|
|
|
buffer = plane_assignment->buffer;
|
|
|
|
if (buffer && !meta_drm_buffer_ensure_fb_id (buffer, error))
|
|
return FALSE;
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Assigning %s plane (%u, %s) to %u, "
|
|
"%hdx%hd+%hd+%hd -> %dx%d+%d+%d",
|
|
get_plane_type_string (plane),
|
|
meta_kms_plane_get_id (plane),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
buffer ? meta_drm_buffer_get_fb_id (buffer) : 0,
|
|
meta_fixed_16_to_int (plane_assignment->src_rect.width),
|
|
meta_fixed_16_to_int (plane_assignment->src_rect.height),
|
|
meta_fixed_16_to_int (plane_assignment->src_rect.x),
|
|
meta_fixed_16_to_int (plane_assignment->src_rect.y),
|
|
plane_assignment->dst_rect.width,
|
|
plane_assignment->dst_rect.height,
|
|
plane_assignment->dst_rect.x,
|
|
plane_assignment->dst_rect.y);
|
|
|
|
if (buffer)
|
|
{
|
|
int i;
|
|
struct {
|
|
MetaKmsPlaneProp prop;
|
|
uint64_t value;
|
|
} props[] = {
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_FB_ID,
|
|
.value = meta_drm_buffer_get_fb_id (buffer),
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_CRTC_ID,
|
|
.value = meta_kms_crtc_get_id (plane_assignment->crtc),
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_SRC_X,
|
|
.value = plane_assignment->src_rect.x,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_SRC_Y,
|
|
.value = plane_assignment->src_rect.y,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_SRC_W,
|
|
.value = plane_assignment->src_rect.width,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_SRC_H,
|
|
.value = plane_assignment->src_rect.height,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_CRTC_X,
|
|
.value = plane_assignment->dst_rect.x,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_CRTC_Y,
|
|
.value = plane_assignment->dst_rect.y,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_CRTC_W,
|
|
.value = plane_assignment->dst_rect.width,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_CRTC_H,
|
|
.value = plane_assignment->dst_rect.height,
|
|
},
|
|
};
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (props); i++)
|
|
{
|
|
if (!add_plane_property (impl_device,
|
|
plane, req,
|
|
props[i].prop,
|
|
props[i].value,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
struct {
|
|
MetaKmsPlaneProp prop;
|
|
uint64_t value;
|
|
} props[] = {
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_FB_ID,
|
|
.value = 0,
|
|
},
|
|
{
|
|
.prop = META_KMS_PLANE_PROP_CRTC_ID,
|
|
.value = 0,
|
|
},
|
|
};
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (props); i++)
|
|
{
|
|
if (!add_plane_property (impl_device,
|
|
plane, req,
|
|
props[i].prop,
|
|
props[i].value,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (plane_assignment->rotation)
|
|
{
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting plane (%u, %s) rotation to %"
|
|
G_GUINT64_FORMAT,
|
|
meta_kms_plane_get_id (plane),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
plane_assignment->rotation);
|
|
|
|
if (!add_plane_property (impl_device,
|
|
plane, req,
|
|
META_KMS_PLANE_PROP_ROTATION,
|
|
plane_assignment->rotation,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
fb_damage = plane_assignment->fb_damage;
|
|
if (fb_damage &&
|
|
meta_kms_plane_get_prop_id (plane,
|
|
META_KMS_PLANE_PROP_FB_DAMAGE_CLIPS_ID))
|
|
{
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting %d damage clips on %u",
|
|
fb_damage->n_rects,
|
|
meta_kms_plane_get_id (plane));
|
|
|
|
prop_id = store_new_blob (impl_device,
|
|
blob_ids,
|
|
fb_damage->rects,
|
|
fb_damage->n_rects *
|
|
sizeof (struct drm_mode_rect),
|
|
error);
|
|
if (!prop_id)
|
|
return FALSE;
|
|
|
|
if (!add_plane_property (impl_device,
|
|
plane, req,
|
|
META_KMS_PLANE_PROP_FB_DAMAGE_CLIPS_ID,
|
|
prop_id,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
process_crtc_gamma (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
gpointer update_entry,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsCrtcGamma *gamma = update_entry;
|
|
MetaKmsCrtc *crtc = gamma->crtc;
|
|
struct drm_color_lut drm_color_lut[gamma->size];
|
|
int i;
|
|
uint32_t color_lut_blob_id;
|
|
|
|
for (i = 0; i < gamma->size; i++)
|
|
{
|
|
drm_color_lut[i].red = gamma->red[i];
|
|
drm_color_lut[i].green = gamma->green[i];
|
|
drm_color_lut[i].blue = gamma->blue[i];
|
|
}
|
|
|
|
color_lut_blob_id = store_new_blob (impl_device,
|
|
blob_ids,
|
|
drm_color_lut,
|
|
sizeof drm_color_lut,
|
|
error);
|
|
if (!color_lut_blob_id)
|
|
return FALSE;
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Setting CRTC (%u, %s) gamma, size: %d",
|
|
meta_kms_crtc_get_id (crtc),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
gamma->size);
|
|
|
|
if (!add_crtc_property (impl_device,
|
|
crtc, req,
|
|
META_KMS_CRTC_PROP_GAMMA_LUT,
|
|
color_lut_blob_id,
|
|
error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
process_page_flip_listener (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
gpointer update_entry,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsImplDeviceAtomic *impl_device_atomic =
|
|
META_KMS_IMPL_DEVICE_ATOMIC (impl_device);
|
|
MetaKmsPageFlipListener *listener = update_entry;
|
|
MetaKmsPageFlipData *page_flip_data;
|
|
uint32_t crtc_id;
|
|
gpointer listener_user_data;
|
|
GDestroyNotify listener_destroy_notify;
|
|
|
|
crtc_id = meta_kms_crtc_get_id (listener->crtc);
|
|
page_flip_data = g_hash_table_lookup (impl_device_atomic->page_flip_datas,
|
|
GUINT_TO_POINTER (crtc_id));
|
|
if (!page_flip_data)
|
|
{
|
|
page_flip_data = meta_kms_page_flip_data_new (impl_device,
|
|
listener->crtc);
|
|
g_hash_table_insert (impl_device_atomic->page_flip_datas,
|
|
GUINT_TO_POINTER (crtc_id),
|
|
page_flip_data);
|
|
|
|
meta_kms_impl_device_hold_fd (impl_device);
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Adding page flip data for (%u, %s): %p",
|
|
crtc_id,
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
page_flip_data);
|
|
}
|
|
|
|
listener_user_data = g_steal_pointer (&listener->user_data);
|
|
listener_destroy_notify = g_steal_pointer (&listener->destroy_notify);
|
|
meta_kms_page_flip_data_add_listener (page_flip_data,
|
|
listener->vtable,
|
|
listener->flags,
|
|
listener_user_data,
|
|
listener_destroy_notify);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
discard_page_flip_listener (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
gpointer update_entry,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaKmsPageFlipListener *listener = update_entry;
|
|
GError *commit_error = user_data;
|
|
MetaKmsPageFlipData *page_flip_data;
|
|
gpointer listener_user_data;
|
|
GDestroyNotify listener_destroy_notify;
|
|
|
|
page_flip_data = meta_kms_page_flip_data_new (impl_device,
|
|
listener->crtc);
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Creating transient page flip data for (%u, %s): %p",
|
|
meta_kms_crtc_get_id (listener->crtc),
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
page_flip_data);
|
|
|
|
listener_user_data = g_steal_pointer (&listener->user_data);
|
|
listener_destroy_notify = g_steal_pointer (&listener->destroy_notify);
|
|
meta_kms_page_flip_data_add_listener (page_flip_data,
|
|
listener->vtable,
|
|
listener->flags,
|
|
listener_user_data,
|
|
listener_destroy_notify);
|
|
|
|
meta_kms_page_flip_data_discard_in_impl (page_flip_data, commit_error);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
process_entries (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
drmModeAtomicReq *req,
|
|
GArray *blob_ids,
|
|
GList *entries,
|
|
gpointer user_data,
|
|
MetaKmsAtomicProcessFunc func,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = entries; l; l = l->next)
|
|
{
|
|
if (!func (impl_device,
|
|
update,
|
|
req,
|
|
blob_ids,
|
|
l->data,
|
|
user_data,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
atomic_page_flip_handler (int fd,
|
|
unsigned int sequence,
|
|
unsigned int tv_sec,
|
|
unsigned int tv_usec,
|
|
unsigned int crtc_id,
|
|
void *user_data)
|
|
{
|
|
MetaKmsImplDeviceAtomic *impl_device_atomic = user_data;
|
|
MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_atomic);
|
|
MetaKmsPageFlipData *page_flip_data = NULL;
|
|
|
|
g_hash_table_steal_extended (impl_device_atomic->page_flip_datas,
|
|
GUINT_TO_POINTER (crtc_id),
|
|
NULL,
|
|
(gpointer *) &page_flip_data);
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Page flip callback for CRTC (%u, %s), data: %p",
|
|
crtc_id, meta_kms_impl_device_get_path (impl_device),
|
|
page_flip_data);
|
|
|
|
if (!page_flip_data)
|
|
return;
|
|
|
|
meta_kms_impl_device_unhold_fd (impl_device);
|
|
|
|
meta_kms_page_flip_data_set_timings_in_impl (page_flip_data,
|
|
sequence, tv_sec, tv_usec);
|
|
meta_kms_impl_device_handle_page_flip_callback (impl_device, page_flip_data);
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_setup_drm_event_context (MetaKmsImplDevice *impl_device,
|
|
drmEventContext *drm_event_context)
|
|
{
|
|
drm_event_context->version = 3;
|
|
drm_event_context->page_flip_handler2 = atomic_page_flip_handler;
|
|
}
|
|
|
|
static const char *
|
|
commit_flags_string (uint32_t commit_flags)
|
|
{
|
|
static char static_commit_flags_string[255];
|
|
const char *commit_flag_strings[4] = { NULL };
|
|
int i = 0;
|
|
g_autofree char *commit_flags_string = NULL;
|
|
|
|
if (commit_flags & DRM_MODE_ATOMIC_NONBLOCK)
|
|
commit_flag_strings[i++] = "ATOMIC_NONBLOCK";
|
|
if (commit_flags & DRM_MODE_ATOMIC_ALLOW_MODESET)
|
|
commit_flag_strings[i++] = "ATOMIC_ALLOW_MODESET";
|
|
if (commit_flags & DRM_MODE_PAGE_FLIP_EVENT)
|
|
commit_flag_strings[i++] = "PAGE_FLIP_EVENT";
|
|
if (commit_flags & DRM_MODE_ATOMIC_TEST_ONLY)
|
|
commit_flag_strings[i++] = "TEST_ONLY";
|
|
|
|
commit_flags_string = g_strjoinv ("|", (char **) commit_flag_strings);
|
|
strncpy (static_commit_flags_string, commit_flags_string,
|
|
(sizeof static_commit_flags_string) - 1);
|
|
|
|
return static_commit_flags_string;
|
|
}
|
|
|
|
static gboolean
|
|
disable_connectors (MetaKmsImplDevice *impl_device,
|
|
drmModeAtomicReq *req,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = meta_kms_impl_device_peek_connectors (impl_device); l; l = l->next)
|
|
{
|
|
MetaKmsConnector *connector = l->data;
|
|
|
|
if (!add_connector_property (impl_device,
|
|
connector, req,
|
|
META_KMS_CONNECTOR_PROP_CRTC_ID,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
disable_planes (MetaKmsImplDevice *impl_device,
|
|
drmModeAtomicReq *req,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = meta_kms_impl_device_peek_planes (impl_device); l; l = l->next)
|
|
{
|
|
MetaKmsPlane *plane = l->data;
|
|
|
|
if (!add_plane_property (impl_device,
|
|
plane, req,
|
|
META_KMS_PLANE_PROP_CRTC_ID,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
|
|
if (!add_plane_property (impl_device,
|
|
plane, req,
|
|
META_KMS_PLANE_PROP_FB_ID,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
disable_crtcs (MetaKmsImplDevice *impl_device,
|
|
drmModeAtomicReq *req,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = meta_kms_impl_device_peek_crtcs (impl_device); l; l = l->next)
|
|
{
|
|
MetaKmsCrtc *crtc = l->data;
|
|
|
|
if (!add_crtc_property (impl_device,
|
|
crtc, req,
|
|
META_KMS_CRTC_PROP_ACTIVE,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
|
|
if (!add_crtc_property (impl_device,
|
|
crtc, req,
|
|
META_KMS_CRTC_PROP_MODE_ID,
|
|
0,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
disable_planes_and_connectors (MetaKmsImplDevice *impl_device,
|
|
drmModeAtomicReq *req,
|
|
GError **error)
|
|
{
|
|
if (!disable_connectors (impl_device, req, error))
|
|
return FALSE;
|
|
if (!disable_planes (impl_device, req, error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static MetaKmsFeedback *
|
|
meta_kms_impl_device_atomic_process_update (MetaKmsImplDevice *impl_device,
|
|
MetaKmsUpdate *update,
|
|
MetaKmsUpdateFlag flags)
|
|
{
|
|
GError *error = NULL;
|
|
GList *failed_planes = NULL;
|
|
drmModeAtomicReq *req;
|
|
g_autoptr (GArray) blob_ids = NULL;
|
|
int fd;
|
|
uint32_t commit_flags = 0;
|
|
int ret;
|
|
|
|
blob_ids = g_array_new (FALSE, TRUE, sizeof (uint32_t));
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Processing update %" G_GUINT64_FORMAT,
|
|
meta_kms_update_get_sequence_number (update));
|
|
|
|
req = drmModeAtomicAlloc ();
|
|
if (!req)
|
|
{
|
|
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create atomic transaction request: %s",
|
|
g_strerror (errno));
|
|
goto err;
|
|
}
|
|
|
|
if (meta_kms_update_get_mode_sets (update))
|
|
{
|
|
if (!disable_planes_and_connectors (impl_device, req, &error))
|
|
goto err;
|
|
}
|
|
|
|
if (!process_entries (impl_device,
|
|
update,
|
|
req,
|
|
blob_ids,
|
|
meta_kms_update_get_connector_updates (update),
|
|
NULL,
|
|
process_connector_update,
|
|
&error))
|
|
goto err;
|
|
|
|
if (!process_entries (impl_device,
|
|
update,
|
|
req,
|
|
blob_ids,
|
|
meta_kms_update_get_mode_sets (update),
|
|
NULL,
|
|
process_mode_set,
|
|
&error))
|
|
goto err;
|
|
|
|
if (!process_entries (impl_device,
|
|
update,
|
|
req,
|
|
blob_ids,
|
|
meta_kms_update_get_plane_assignments (update),
|
|
NULL,
|
|
process_plane_assignment,
|
|
&error))
|
|
goto err;
|
|
|
|
if (!process_entries (impl_device,
|
|
update,
|
|
req,
|
|
blob_ids,
|
|
meta_kms_update_get_crtc_gammas (update),
|
|
NULL,
|
|
process_crtc_gamma,
|
|
&error))
|
|
goto err;
|
|
|
|
if (meta_kms_update_get_mode_sets (update))
|
|
commit_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
else
|
|
commit_flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
|
|
|
if (meta_kms_update_get_page_flip_listeners (update))
|
|
commit_flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
|
|
|
if (flags & META_KMS_UPDATE_FLAG_TEST_ONLY)
|
|
commit_flags |= DRM_MODE_ATOMIC_TEST_ONLY;
|
|
|
|
meta_topic (META_DEBUG_KMS,
|
|
"[atomic] Committing update %" G_GUINT64_FORMAT ", flags: %s",
|
|
meta_kms_update_get_sequence_number (update),
|
|
commit_flags_string (commit_flags));
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
ret = drmModeAtomicCommit (fd, req, commit_flags, impl_device);
|
|
drmModeAtomicFree (req);
|
|
if (ret < 0)
|
|
{
|
|
g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
"drmModeAtomicCommit: %s", g_strerror (-ret));
|
|
goto err;
|
|
}
|
|
|
|
process_entries (impl_device,
|
|
update,
|
|
req,
|
|
blob_ids,
|
|
meta_kms_update_get_page_flip_listeners (update),
|
|
NULL,
|
|
process_page_flip_listener,
|
|
NULL);
|
|
|
|
release_blob_ids (impl_device, blob_ids);
|
|
|
|
return meta_kms_feedback_new_passed (NULL);
|
|
|
|
err:
|
|
meta_topic (META_DEBUG_KMS, "[atomic] KMS update failed: %s", error->message);
|
|
|
|
if (!(flags & META_KMS_UPDATE_FLAG_PRESERVE_ON_ERROR))
|
|
{
|
|
process_entries (impl_device,
|
|
update,
|
|
req,
|
|
blob_ids,
|
|
meta_kms_update_get_page_flip_listeners (update),
|
|
error,
|
|
discard_page_flip_listener,
|
|
NULL);
|
|
}
|
|
|
|
release_blob_ids (impl_device, blob_ids);
|
|
|
|
return meta_kms_feedback_new_failed (failed_planes, error);
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_disable (MetaKmsImplDevice *impl_device)
|
|
{
|
|
g_autoptr (GError) error = NULL;
|
|
drmModeAtomicReq *req;
|
|
int fd;
|
|
int ret;
|
|
|
|
meta_topic (META_DEBUG_KMS, "[atomic] Disabling '%s'",
|
|
meta_kms_impl_device_get_path (impl_device));
|
|
|
|
req = drmModeAtomicAlloc ();
|
|
if (!req)
|
|
{
|
|
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to create atomic transaction request: %s",
|
|
g_strerror (errno));
|
|
goto err;
|
|
}
|
|
|
|
if (!disable_connectors (impl_device, req, &error))
|
|
goto err;
|
|
if (!disable_planes (impl_device, req, &error))
|
|
goto err;
|
|
if (!disable_crtcs (impl_device, req, &error))
|
|
goto err;
|
|
|
|
meta_topic (META_DEBUG_KMS, "[atomic] Committing disable-device transaction");
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
ret = drmModeAtomicCommit (fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, impl_device);
|
|
drmModeAtomicFree (req);
|
|
if (ret < 0)
|
|
{
|
|
g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
|
"drmModeAtomicCommit: %s", g_strerror (-ret));
|
|
goto err;
|
|
}
|
|
|
|
return;
|
|
|
|
err:
|
|
g_warning ("[atomic] Failed to disable device '%s': %s",
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
error->message);
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_handle_page_flip_callback (MetaKmsImplDevice *impl_device,
|
|
MetaKmsPageFlipData *page_flip_data)
|
|
{
|
|
meta_kms_page_flip_data_flipped_in_impl (page_flip_data);
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_discard_pending_page_flips (MetaKmsImplDevice *impl_device)
|
|
{
|
|
}
|
|
|
|
static gboolean
|
|
dispose_page_flip_data (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
MetaKmsPageFlipData *page_flip_data = value;
|
|
MetaKmsImplDevice *impl_device = user_data;
|
|
|
|
meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
|
|
meta_kms_impl_device_unhold_fd (impl_device);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_prepare_shutdown (MetaKmsImplDevice *impl_device)
|
|
{
|
|
MetaKmsImplDeviceAtomic *impl_device_atomic =
|
|
META_KMS_IMPL_DEVICE_ATOMIC (impl_device);
|
|
|
|
g_hash_table_foreach_remove (impl_device_atomic->page_flip_datas,
|
|
dispose_page_flip_data,
|
|
impl_device);
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_finalize (GObject *object)
|
|
{
|
|
MetaKmsImplDeviceAtomic *impl_device_atomic =
|
|
META_KMS_IMPL_DEVICE_ATOMIC (object);
|
|
|
|
g_assert (g_hash_table_size (impl_device_atomic->page_flip_datas) == 0);
|
|
|
|
g_hash_table_unref (impl_device_atomic->page_flip_datas);
|
|
|
|
G_OBJECT_CLASS (meta_kms_impl_device_atomic_parent_class)->finalize (object);
|
|
}
|
|
|
|
static MetaDeviceFile *
|
|
meta_kms_impl_device_atomic_open_device_file (MetaKmsImplDevice *impl_device,
|
|
const char *path,
|
|
GError **error)
|
|
{
|
|
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
|
|
MetaKms *kms = meta_kms_device_get_kms (device);
|
|
MetaBackend *backend = meta_kms_get_backend (kms);
|
|
MetaDevicePool *device_pool =
|
|
meta_backend_native_get_device_pool (META_BACKEND_NATIVE (backend));
|
|
g_autoptr (MetaDeviceFile) device_file = NULL;
|
|
|
|
device_file = meta_device_pool_open (device_pool, path,
|
|
META_DEVICE_FILE_FLAG_TAKE_CONTROL,
|
|
error);
|
|
if (!device_file)
|
|
return NULL;
|
|
|
|
if (!meta_device_file_has_tag (device_file,
|
|
META_DEVICE_FILE_TAG_KMS,
|
|
META_KMS_DEVICE_FILE_TAG_ATOMIC))
|
|
{
|
|
int fd = meta_device_file_get_fd (device_file);
|
|
|
|
g_warn_if_fail (!meta_device_file_has_tag (device_file,
|
|
META_DEVICE_FILE_TAG_KMS,
|
|
META_KMS_DEVICE_FILE_TAG_SIMPLE));
|
|
|
|
if (drmSetClientCap (fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0)
|
|
{
|
|
g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
|
|
"DRM_CLIENT_CAP_UNIVERSAL_PLANES not supported");
|
|
return NULL;
|
|
}
|
|
|
|
if (drmSetClientCap (fd, DRM_CLIENT_CAP_ATOMIC, 1) != 0)
|
|
{
|
|
g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_NOT_SUPPORTED,
|
|
"DRM_CLIENT_CAP_ATOMIC not supported");
|
|
return NULL;
|
|
}
|
|
|
|
meta_device_file_tag (device_file,
|
|
META_DEVICE_FILE_TAG_KMS,
|
|
META_KMS_DEVICE_FILE_TAG_ATOMIC);
|
|
}
|
|
|
|
return g_steal_pointer (&device_file);
|
|
}
|
|
|
|
static gboolean
|
|
is_atomic_allowed (const char *driver_name)
|
|
{
|
|
const char *atomic_driver_deny_list[] = {
|
|
"qxl",
|
|
"vmwgfx",
|
|
"vboxvideo",
|
|
"nvidia-drm",
|
|
"virtio_gpu",
|
|
NULL,
|
|
};
|
|
|
|
return !g_strv_contains (atomic_driver_deny_list, driver_name);
|
|
}
|
|
|
|
static gboolean
|
|
meta_kms_impl_device_atomic_initable_init (GInitable *initable,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (initable);
|
|
const char *atomic_kms_enable_env;
|
|
|
|
atomic_kms_enable_env = getenv ("MUTTER_DEBUG_ENABLE_ATOMIC_KMS");
|
|
if (atomic_kms_enable_env && g_strcmp0 (atomic_kms_enable_env, "1") != 0)
|
|
{
|
|
g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_USER_INHIBITED,
|
|
"Atomic mode setting disable via env var");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!initable_parent_iface->init (initable, cancellable, error))
|
|
return FALSE;
|
|
|
|
if (!is_atomic_allowed (meta_kms_impl_device_get_driver_name (impl_device)))
|
|
{
|
|
g_set_error (error, META_KMS_ERROR, META_KMS_ERROR_DENY_LISTED,
|
|
"Atomic mode setting disable via driver deny list");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!meta_kms_impl_device_init_mode_setting (impl_device, error))
|
|
return FALSE;
|
|
|
|
g_message ("Added device '%s' (%s) using atomic mode setting.",
|
|
meta_kms_impl_device_get_path (impl_device),
|
|
meta_kms_impl_device_get_driver_name (impl_device));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_init (MetaKmsImplDeviceAtomic *impl_device_atomic)
|
|
{
|
|
impl_device_atomic->page_flip_datas = g_hash_table_new (NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
initable_iface_init (GInitableIface *iface)
|
|
{
|
|
initable_parent_iface = g_type_interface_peek_parent (iface);
|
|
|
|
iface->init = meta_kms_impl_device_atomic_initable_init;
|
|
}
|
|
|
|
static void
|
|
meta_kms_impl_device_atomic_class_init (MetaKmsImplDeviceAtomicClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
MetaKmsImplDeviceClass *impl_device_class =
|
|
META_KMS_IMPL_DEVICE_CLASS (klass);
|
|
|
|
object_class->finalize = meta_kms_impl_device_atomic_finalize;
|
|
|
|
impl_device_class->open_device_file =
|
|
meta_kms_impl_device_atomic_open_device_file;
|
|
impl_device_class->setup_drm_event_context =
|
|
meta_kms_impl_device_atomic_setup_drm_event_context;
|
|
impl_device_class->process_update =
|
|
meta_kms_impl_device_atomic_process_update;
|
|
impl_device_class->disable =
|
|
meta_kms_impl_device_atomic_disable;
|
|
impl_device_class->handle_page_flip_callback =
|
|
meta_kms_impl_device_atomic_handle_page_flip_callback;
|
|
impl_device_class->discard_pending_page_flips =
|
|
meta_kms_impl_device_atomic_discard_pending_page_flips;
|
|
impl_device_class->prepare_shutdown =
|
|
meta_kms_impl_device_atomic_prepare_shutdown;
|
|
}
|