1
0
Fork 0
mutter-performance-source/src/backends/native/meta-kms-impl-simple.c
Jonas Ådahl c9a5b2b22f kms-impl-simple: Handle lack of cached mode set in flip fallback
When a page flip fails with a certain error code, we've treated this as
a hint that page flipping is broken and we should try to use mode
setting instead.

On some drivers, it seems that this error is also reported when there
was no mode set, which means we'll have no cached mode set to use in the
fallback. The lack of prior mode set tends to happen when we hit a race
when the DRM objects change before we have the time to process a hotplug
event.

Handle the lack a missing mode set in the flip fallback path, with the
assumption that we'll get a hotplug event that'll fix things up for us
eventually.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/917
2020-03-18 00:33:03 +00:00

1088 lines
34 KiB
C

/*
* Copyright (C) 2018-2019 Red Hat
* Copyright (C) 2019 DisplayLink (UK) Ltd.
*
* 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-simple.h"
#include <errno.h>
#include <gbm.h>
#include <xf86drmMode.h>
#include "backends/native/meta-kms-connector.h"
#include "backends/native/meta-kms-crtc.h"
#include "backends/native/meta-kms-device-private.h"
#include "backends/native/meta-kms-page-flip-private.h"
#include "backends/native/meta-kms-plane.h"
#include "backends/native/meta-kms-private.h"
#include "backends/native/meta-kms-update-private.h"
#include "backends/native/meta-kms-utils.h"
typedef struct _CachedModeSet
{
GList *connectors;
drmModeModeInfo *drm_mode;
} CachedModeSet;
struct _MetaKmsImplSimple
{
MetaKmsImpl parent;
GSource *mode_set_fallback_feedback_source;
GList *mode_set_fallback_page_flip_datas;
GList *pending_page_flip_retries;
GSource *retry_page_flips_source;
GList *postponed_page_flip_datas;
GList *postponed_mode_set_fallback_datas;
GHashTable *cached_mode_sets;
};
G_DEFINE_TYPE (MetaKmsImplSimple, meta_kms_impl_simple,
META_TYPE_KMS_IMPL)
static void
flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple);
MetaKmsImplSimple *
meta_kms_impl_simple_new (MetaKms *kms,
GError **error)
{
return g_object_new (META_TYPE_KMS_IMPL_SIMPLE,
"kms", kms,
NULL);
}
static gboolean
process_connector_property (MetaKmsImpl *impl,
MetaKmsUpdate *update,
gpointer update_entry,
GError **error)
{
MetaKmsConnectorProperty *connector_property = update_entry;
MetaKmsConnector *connector = connector_property->connector;
MetaKmsDevice *device = meta_kms_connector_get_device (connector);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
int fd;
int ret;
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeObjectSetProperty (fd,
meta_kms_connector_get_id (connector),
DRM_MODE_OBJECT_CONNECTOR,
connector_property->prop_id,
connector_property->value);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set connector %u property %u: %s",
meta_kms_connector_get_id (connector),
connector_property->prop_id,
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static gboolean
process_plane_property (MetaKmsImpl *impl,
MetaKmsPlane *plane,
MetaKmsProperty *prop,
GError **error)
{
MetaKmsDevice *device = meta_kms_plane_get_device (plane);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
int fd;
int ret;
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeObjectSetProperty (fd,
meta_kms_plane_get_id (plane),
DRM_MODE_OBJECT_PLANE,
prop->prop_id,
prop->value);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set plane %u property %u: %s",
meta_kms_plane_get_id (plane),
prop->prop_id,
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static CachedModeSet *
cached_mode_set_new (GList *connectors,
const drmModeModeInfo *drm_mode)
{
CachedModeSet *cached_mode_set;
cached_mode_set = g_new0 (CachedModeSet, 1);
*cached_mode_set = (CachedModeSet) {
.connectors = g_list_copy (connectors),
.drm_mode = g_memdup (drm_mode, sizeof *drm_mode),
};
return cached_mode_set;
}
static void
cached_mode_set_free (CachedModeSet *cached_mode_set)
{
g_list_free (cached_mode_set->connectors);
g_free (cached_mode_set->drm_mode);
g_free (cached_mode_set);
}
static void
fill_connector_ids_array (GList *connectors,
uint32_t **out_connectors,
int *out_n_connectors)
{
GList *l;
int i;
*out_n_connectors = g_list_length (connectors);
*out_connectors = g_new0 (uint32_t, *out_n_connectors);
i = 0;
for (l = connectors; l; l = l->next)
{
MetaKmsConnector *connector = l->data;
(*out_connectors)[i++] = meta_kms_connector_get_id (connector);
}
}
static gboolean
process_mode_set (MetaKmsImpl *impl,
MetaKmsUpdate *update,
gpointer update_entry,
GError **error)
{
MetaKmsModeSet *mode_set = update_entry;
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
MetaKmsCrtc *crtc = mode_set->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
g_autofree uint32_t *connectors = NULL;
int n_connectors;
MetaKmsPlaneAssignment *plane_assignment;
uint32_t x, y;
uint32_t fb_id;
int fd;
int ret;
crtc = mode_set->crtc;
if (mode_set->drm_mode)
{
GList *l;
fill_connector_ids_array (mode_set->connectors,
&connectors,
&n_connectors);
plane_assignment = meta_kms_update_get_primary_plane_assignment (update,
crtc);
if (!plane_assignment)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing primary plane assignment for legacy mode set on CRTC %u",
meta_kms_crtc_get_id (crtc));
return FALSE;
}
x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
for (l = plane_assignment->plane_properties; l; l = l->next)
{
MetaKmsProperty *prop = l->data;
if (!process_plane_property (impl, plane_assignment->plane,
prop, error))
return FALSE;
}
fb_id = plane_assignment->fb_id;
}
else
{
x = y = 0;
n_connectors = 0;
connectors = NULL;
fb_id = 0;
}
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeSetCrtc (fd,
meta_kms_crtc_get_id (crtc),
fb_id,
x, y,
connectors, n_connectors,
mode_set->drm_mode);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"Failed to set mode %s on CRTC %u: %s",
mode_set->drm_mode ? mode_set->drm_mode->name : "off",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
if (mode_set->drm_mode)
{
g_hash_table_replace (impl_simple->cached_mode_sets,
crtc,
cached_mode_set_new (mode_set->connectors,
mode_set->drm_mode));
}
else
{
g_hash_table_remove (impl_simple->cached_mode_sets, crtc);
}
return TRUE;
}
static gboolean
process_crtc_gamma (MetaKmsImpl *impl,
MetaKmsUpdate *update,
gpointer update_entry,
GError **error)
{
MetaKmsCrtcGamma *gamma = update_entry;
MetaKmsCrtc *crtc = gamma->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
int fd;
int ret;
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeCrtcSetGamma (fd, meta_kms_crtc_get_id (crtc),
gamma->size,
gamma->red,
gamma->green,
gamma->blue);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeCrtcSetGamma on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
return TRUE;
}
static gboolean
is_timestamp_earlier_than (uint64_t ts1,
uint64_t ts2)
{
if (ts1 == ts2)
return FALSE;
else
return ts2 - ts1 < UINT64_MAX / 2;
}
typedef struct _RetryPageFlipData
{
MetaKmsCrtc *crtc;
uint32_t fb_id;
MetaKmsPageFlipData *page_flip_data;
float refresh_rate;
uint64_t retry_time_us;
} RetryPageFlipData;
static void
retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data)
{
g_assert (!retry_page_flip_data->page_flip_data);
g_free (retry_page_flip_data);
}
static CachedModeSet *
get_cached_mode_set (MetaKmsImplSimple *impl_simple,
MetaKmsCrtc *crtc)
{
return g_hash_table_lookup (impl_simple->cached_mode_sets, crtc);
}
static float
get_cached_crtc_refresh_rate (MetaKmsImplSimple *impl_simple,
MetaKmsCrtc *crtc)
{
CachedModeSet *cached_mode_set;
cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets,
crtc);
g_assert (cached_mode_set);
return meta_calculate_drm_mode_refresh_rate (cached_mode_set->drm_mode);
}
static gboolean
retry_page_flips (gpointer user_data)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (user_data);
uint64_t now_us;
GList *l;
meta_assert_in_kms_impl (meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple)));
now_us = g_source_get_time (impl_simple->retry_page_flips_source);
l = impl_simple->pending_page_flip_retries;
while (l)
{
RetryPageFlipData *retry_page_flip_data = l->data;
MetaKmsCrtc *crtc = retry_page_flip_data->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
GList *l_next = l->next;
int fd;
int ret;
MetaKmsPageFlipData *page_flip_data;
if (is_timestamp_earlier_than (now_us,
retry_page_flip_data->retry_time_us))
{
l = l_next;
continue;
}
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
retry_page_flip_data->fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
retry_page_flip_data->page_flip_data);
if (ret == -EBUSY)
{
float refresh_rate;
refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc);
retry_page_flip_data->retry_time_us +=
(uint64_t) (G_USEC_PER_SEC / refresh_rate);
l = l_next;
continue;
}
impl_simple->pending_page_flip_retries =
g_list_remove_link (impl_simple->pending_page_flip_retries, l);
page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
if (ret != 0)
{
g_autoptr (GError) error = NULL;
g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModePageFlip on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
if (!g_error_matches (error,
G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED))
g_critical ("Failed to page flip: %s", error->message);
meta_kms_page_flip_data_discard_in_impl (page_flip_data, error);
}
retry_page_flip_data_free (retry_page_flip_data);
l = l_next;
}
if (impl_simple->pending_page_flip_retries)
{
GList *l;
uint64_t earliest_retry_time_us = 0;
for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *retry_page_flip_data = l->data;
if (l == impl_simple->pending_page_flip_retries ||
is_timestamp_earlier_than (retry_page_flip_data->retry_time_us,
earliest_retry_time_us))
earliest_retry_time_us = retry_page_flip_data->retry_time_us;
}
g_source_set_ready_time (impl_simple->retry_page_flips_source,
earliest_retry_time_us);
return G_SOURCE_CONTINUE;
}
else
{
g_clear_pointer (&impl_simple->retry_page_flips_source,
g_source_unref);
flush_postponed_page_flip_datas (impl_simple);
return G_SOURCE_REMOVE;
}
}
static void
schedule_retry_page_flip (MetaKmsImplSimple *impl_simple,
MetaKmsCrtc *crtc,
uint32_t fb_id,
float refresh_rate,
MetaKmsPageFlipData *page_flip_data)
{
RetryPageFlipData *retry_page_flip_data;
uint64_t now_us;
uint64_t retry_time_us;
now_us = g_get_monotonic_time ();
retry_time_us = now_us + (uint64_t) (G_USEC_PER_SEC / refresh_rate);
retry_page_flip_data = g_new0 (RetryPageFlipData, 1);
*retry_page_flip_data = (RetryPageFlipData) {
.crtc = crtc,
.fb_id = fb_id,
.page_flip_data = meta_kms_page_flip_data_ref (page_flip_data),
.refresh_rate = refresh_rate,
.retry_time_us = retry_time_us,
};
if (!impl_simple->retry_page_flips_source)
{
MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple));
GSource *source;
source = meta_kms_add_source_in_impl (kms, retry_page_flips,
impl_simple, NULL);
g_source_set_ready_time (source, retry_time_us);
impl_simple->retry_page_flips_source = source;
}
else
{
GList *l;
for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *pending_retry_page_flip_data = l->data;
uint64_t pending_retry_time_us =
pending_retry_page_flip_data->retry_time_us;
if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us))
{
g_source_set_ready_time (impl_simple->retry_page_flips_source,
retry_time_us);
break;
}
}
}
impl_simple->pending_page_flip_retries =
g_list_append (impl_simple->pending_page_flip_retries,
retry_page_flip_data);
}
static void
invoke_page_flip_datas (GList *page_flip_datas,
MetaPageFlipDataFeedbackFunc func)
{
g_list_foreach (page_flip_datas, (GFunc) func, NULL);
}
static void
clear_page_flip_datas (GList **page_flip_datas)
{
g_list_free_full (*page_flip_datas,
(GDestroyNotify) meta_kms_page_flip_data_unref);
*page_flip_datas = NULL;
}
static gboolean
mode_set_fallback_feedback_idle (gpointer user_data)
{
MetaKmsImplSimple *impl_simple = user_data;
g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source,
g_source_unref);
if (impl_simple->pending_page_flip_retries)
{
impl_simple->postponed_mode_set_fallback_datas =
g_steal_pointer (&impl_simple->mode_set_fallback_page_flip_datas);
}
else
{
invoke_page_flip_datas (impl_simple->mode_set_fallback_page_flip_datas,
meta_kms_page_flip_data_mode_set_fallback_in_impl);
clear_page_flip_datas (&impl_simple->mode_set_fallback_page_flip_datas);
}
return G_SOURCE_REMOVE;
}
static gboolean
mode_set_fallback (MetaKmsImplSimple *impl_simple,
MetaKmsUpdate *update,
MetaKmsPageFlip *page_flip,
MetaKmsPlaneAssignment *plane_assignment,
MetaKmsPageFlipData *page_flip_data,
GError **error)
{
MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple));
MetaKmsCrtc *crtc = page_flip->crtc;
MetaKmsDevice *device = meta_kms_crtc_get_device (crtc);
MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device);
CachedModeSet *cached_mode_set;
g_autofree uint32_t *connectors = NULL;
int n_connectors;
uint32_t x, y;
int fd;
int ret;
cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets,
crtc);
if (!cached_mode_set)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Missing mode set for page flip fallback");
return FALSE;
}
fill_connector_ids_array (cached_mode_set->connectors,
&connectors,
&n_connectors);
x = meta_fixed_16_to_int (plane_assignment->src_rect.x);
y = meta_fixed_16_to_int (plane_assignment->src_rect.y);
fd = meta_kms_impl_device_get_fd (impl_device);
ret = drmModeSetCrtc (fd,
meta_kms_crtc_get_id (crtc),
plane_assignment->fb_id,
x, y,
connectors, n_connectors,
cached_mode_set->drm_mode);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeSetCrtc mode '%s' on CRTC %u failed: %s",
cached_mode_set->drm_mode->name,
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
return FALSE;
}
if (!impl_simple->mode_set_fallback_feedback_source)
{
GSource *source;
source = meta_kms_add_source_in_impl (kms,
mode_set_fallback_feedback_idle,
impl_simple,
NULL);
impl_simple->mode_set_fallback_feedback_source = source;
}
impl_simple->mode_set_fallback_page_flip_datas =
g_list_prepend (impl_simple->mode_set_fallback_page_flip_datas,
meta_kms_page_flip_data_ref (page_flip_data));
return TRUE;
}
static gboolean
process_page_flip (MetaKmsImpl *impl,
MetaKmsUpdate *update,
gpointer update_entry,
GError **error)
{
MetaKmsPageFlip *page_flip = update_entry;
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
MetaKmsCrtc *crtc;
MetaKmsDevice *device;
MetaKmsImplDevice *impl_device;
MetaKmsPlaneAssignment *plane_assignment;
MetaKmsPageFlipData *page_flip_data;
MetaKmsCustomPageFlipFunc custom_page_flip_func;
int fd;
int ret;
crtc = page_flip->crtc;
plane_assignment = meta_kms_update_get_primary_plane_assignment (update,
crtc);
page_flip_data = meta_kms_page_flip_data_new (impl,
crtc,
page_flip->feedback,
page_flip->user_data);
device = meta_kms_crtc_get_device (crtc);
impl_device = meta_kms_device_get_impl_device (device);
fd = meta_kms_impl_device_get_fd (impl_device);
custom_page_flip_func = page_flip->custom_page_flip_func;
if (custom_page_flip_func)
{
ret = custom_page_flip_func (page_flip->custom_page_flip_user_data,
meta_kms_page_flip_data_ref (page_flip_data));
}
else
{
ret = drmModePageFlip (fd,
meta_kms_crtc_get_id (crtc),
plane_assignment->fb_id,
DRM_MODE_PAGE_FLIP_EVENT,
meta_kms_page_flip_data_ref (page_flip_data));
}
if (ret == -EBUSY)
{
CachedModeSet *cached_mode_set;
cached_mode_set = get_cached_mode_set (impl_simple, crtc);
if (cached_mode_set)
{
drmModeModeInfo *drm_mode;
float refresh_rate;
drm_mode = cached_mode_set->drm_mode;
refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
schedule_retry_page_flip (impl_simple,
crtc,
plane_assignment->fb_id,
refresh_rate,
page_flip_data);
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Page flip of %u failed, and no mode set available",
meta_kms_crtc_get_id (crtc));
meta_kms_page_flip_data_unref (page_flip_data);
return FALSE;
}
}
else if (ret == -EINVAL)
{
if (!mode_set_fallback (impl_simple,
update,
page_flip,
plane_assignment,
page_flip_data,
error))
{
meta_kms_page_flip_data_unref (page_flip_data);
return FALSE;
}
}
else if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModePageFlip on CRTC %u failed: %s",
meta_kms_crtc_get_id (crtc),
g_strerror (-ret));
meta_kms_page_flip_data_unref (page_flip_data);
return FALSE;
}
meta_kms_page_flip_data_unref (page_flip_data);
return TRUE;
}
static void
discard_page_flip (MetaKmsImpl *impl,
MetaKmsUpdate *update,
MetaKmsPageFlip *page_flip)
{
MetaKmsCrtc *crtc;
MetaKmsPageFlipData *page_flip_data;
crtc = page_flip->crtc;
page_flip_data = meta_kms_page_flip_data_new (impl,
crtc,
page_flip->feedback,
page_flip->user_data);
meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
meta_kms_page_flip_data_unref (page_flip_data);
}
static gboolean
process_entries (MetaKmsImpl *impl,
MetaKmsUpdate *update,
GList *entries,
gboolean (* func) (MetaKmsImpl *impl,
MetaKmsUpdate *update,
gpointer entry_data,
GError **error),
GError **error)
{
GList *l;
for (l = entries; l; l = l->next)
{
if (!func (impl, update, l->data, error))
return FALSE;
}
return TRUE;
}
static gboolean
process_cursor_plane_assignment (MetaKmsImpl *impl,
MetaKmsUpdate *update,
MetaKmsPlaneAssignment *plane_assignment,
GError **error)
{
MetaKmsPlane *plane;
MetaKmsDevice *device;
MetaKmsImplDevice *impl_device;
int fd;
plane = plane_assignment->plane;
device = meta_kms_plane_get_device (plane);
impl_device = meta_kms_device_get_impl_device (device);
fd = meta_kms_impl_device_get_fd (impl_device);
if (!(plane_assignment->flags & META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED))
{
int width, height;
int ret;
width = meta_fixed_16_to_int (plane_assignment->dst_rect.width);
height = meta_fixed_16_to_int (plane_assignment->dst_rect.height);
ret = drmModeSetCursor (fd, meta_kms_crtc_get_id (plane_assignment->crtc),
plane_assignment->fb_id,
width, height);
if (ret != 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
"drmModeSetCursor failed: %s", g_strerror (-ret));
return FALSE;
}
}
drmModeMoveCursor (fd,
meta_kms_crtc_get_id (plane_assignment->crtc),
meta_fixed_16_to_int (plane_assignment->dst_rect.x),
meta_fixed_16_to_int (plane_assignment->dst_rect.y));
return TRUE;
}
static gboolean
process_plane_assignment (MetaKmsImpl *impl,
MetaKmsUpdate *update,
MetaKmsPlaneAssignment *plane_assignment,
MetaKmsPlaneFeedback **plane_feedback)
{
MetaKmsPlane *plane;
MetaKmsPlaneType plane_type;
GError *error = NULL;
plane = plane_assignment->plane;
plane_type = meta_kms_plane_get_plane_type (plane);
switch (plane_type)
{
case META_KMS_PLANE_TYPE_PRIMARY:
/* Handled as part of the mode-set and page flip. */
return TRUE;
case META_KMS_PLANE_TYPE_CURSOR:
if (!process_cursor_plane_assignment (impl, update,
plane_assignment,
&error))
{
*plane_feedback =
meta_kms_plane_feedback_new_take_error (plane,
plane_assignment->crtc,
g_steal_pointer (&error));
return FALSE;
}
else
{
return TRUE;
}
case META_KMS_PLANE_TYPE_OVERLAY:
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED,
"Overlay planes cannot be assigned");
*plane_feedback =
meta_kms_plane_feedback_new_take_error (plane,
plane_assignment->crtc,
g_steal_pointer (&error));
return TRUE;
}
g_assert_not_reached ();
}
static GList *
process_plane_assignments (MetaKmsImpl *impl,
MetaKmsUpdate *update)
{
GList *failed_planes = NULL;
GList *l;
for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next)
{
MetaKmsPlaneAssignment *plane_assignment = l->data;
MetaKmsPlaneFeedback *plane_feedback;
if (!process_plane_assignment (impl, update, plane_assignment,
&plane_feedback))
failed_planes = g_list_prepend (failed_planes, plane_feedback);
}
return failed_planes;
}
static GList *
generate_all_failed_feedbacks (MetaKmsUpdate *update)
{
GList *failed_planes = NULL;
GList *l;
for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next)
{
MetaKmsPlaneAssignment *plane_assignment = l->data;
MetaKmsPlane *plane;
MetaKmsPlaneType plane_type;
MetaKmsPlaneFeedback *plane_feedback;
plane = plane_assignment->plane;
plane_type = meta_kms_plane_get_plane_type (plane);
switch (plane_type)
{
case META_KMS_PLANE_TYPE_PRIMARY:
continue;
case META_KMS_PLANE_TYPE_CURSOR:
case META_KMS_PLANE_TYPE_OVERLAY:
break;
}
plane_feedback =
meta_kms_plane_feedback_new_take_error (plane_assignment->plane,
plane_assignment->crtc,
g_error_new (G_IO_ERROR,
G_IO_ERROR_FAILED,
"Discarded"));
failed_planes = g_list_prepend (failed_planes, plane_feedback);
}
return failed_planes;
}
static MetaKmsFeedback *
meta_kms_impl_simple_process_update (MetaKmsImpl *impl,
MetaKmsUpdate *update)
{
GError *error = NULL;
GList *failed_planes;
GList *l;
meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl));
if (!process_entries (impl,
update,
meta_kms_update_get_connector_properties (update),
process_connector_property,
&error))
goto err_planes_not_assigned;
if (!process_entries (impl,
update,
meta_kms_update_get_mode_sets (update),
process_mode_set,
&error))
goto err_planes_not_assigned;
if (!process_entries (impl,
update,
meta_kms_update_get_crtc_gammas (update),
process_crtc_gamma,
&error))
goto err_planes_not_assigned;
failed_planes = process_plane_assignments (impl, update);
if (failed_planes)
{
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to assign one or more planes");
goto err_planes_assigned;
}
if (!process_entries (impl,
update,
meta_kms_update_get_page_flips (update),
process_page_flip,
&error))
goto err_planes_assigned;
return meta_kms_feedback_new_passed ();
err_planes_not_assigned:
failed_planes = generate_all_failed_feedbacks (update);
err_planes_assigned:
for (l = meta_kms_update_get_page_flips (update); l; l = l->next)
{
MetaKmsPageFlip *page_flip = l->data;
discard_page_flip (impl, update, page_flip);
}
return meta_kms_feedback_new_failed (failed_planes, error);
}
static void
flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple)
{
invoke_page_flip_datas (impl_simple->postponed_page_flip_datas,
meta_kms_page_flip_data_flipped_in_impl);
clear_page_flip_datas (&impl_simple->postponed_page_flip_datas);
invoke_page_flip_datas (impl_simple->postponed_mode_set_fallback_datas,
meta_kms_page_flip_data_mode_set_fallback_in_impl);
clear_page_flip_datas (&impl_simple->postponed_mode_set_fallback_datas);
}
static void
meta_kms_impl_simple_handle_page_flip_callback (MetaKmsImpl *impl,
MetaKmsPageFlipData *page_flip_data)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
if (impl_simple->pending_page_flip_retries)
{
impl_simple->postponed_page_flip_datas =
g_list_append (impl_simple->postponed_page_flip_datas,
meta_kms_page_flip_data_ref (page_flip_data));
}
else
{
meta_kms_page_flip_data_flipped_in_impl (page_flip_data);
}
meta_kms_page_flip_data_unref (page_flip_data);
}
static void
meta_kms_impl_simple_discard_pending_page_flips (MetaKmsImpl *impl)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
GList *l;
if (!impl_simple->pending_page_flip_retries)
return;
for (l = impl_simple->pending_page_flip_retries; l; l = l->next)
{
RetryPageFlipData *retry_page_flip_data = l->data;
MetaKmsPageFlipData *page_flip_data;
page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data);
meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL);
retry_page_flip_data_free (retry_page_flip_data);
}
g_clear_pointer (&impl_simple->pending_page_flip_retries, g_list_free);
g_clear_pointer (&impl_simple->retry_page_flips_source,
g_source_destroy);
}
static void
meta_kms_impl_simple_dispatch_idle (MetaKmsImpl *impl)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl);
if (impl_simple->mode_set_fallback_feedback_source)
mode_set_fallback_feedback_idle (impl_simple);
}
static void
meta_kms_impl_simple_notify_device_created (MetaKmsImpl *impl,
MetaKmsDevice *device)
{
GList *l;
for (l = meta_kms_device_get_crtcs (device); l; l = l->next)
{
MetaKmsCrtc *crtc = l->data;
MetaKmsPlane *plane;
plane = meta_kms_device_get_cursor_plane_for (device, crtc);
if (plane)
continue;
meta_kms_device_add_fake_plane_in_impl (device,
META_KMS_PLANE_TYPE_CURSOR,
crtc);
}
}
static void
meta_kms_impl_simple_finalize (GObject *object)
{
MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (object);
g_list_free_full (impl_simple->pending_page_flip_retries,
(GDestroyNotify) retry_page_flip_data_free);
g_list_free_full (impl_simple->postponed_page_flip_datas,
(GDestroyNotify) meta_kms_page_flip_data_unref);
g_list_free_full (impl_simple->postponed_mode_set_fallback_datas,
(GDestroyNotify) meta_kms_page_flip_data_unref);
g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source,
g_source_destroy);
g_hash_table_destroy (impl_simple->cached_mode_sets);
G_OBJECT_CLASS (meta_kms_impl_simple_parent_class)->finalize (object);
}
static void
meta_kms_impl_simple_init (MetaKmsImplSimple *impl_simple)
{
impl_simple->cached_mode_sets =
g_hash_table_new_full (NULL,
NULL,
NULL,
(GDestroyNotify) cached_mode_set_free);
}
static void
meta_kms_impl_simple_class_init (MetaKmsImplSimpleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MetaKmsImplClass *impl_class = META_KMS_IMPL_CLASS (klass);
object_class->finalize = meta_kms_impl_simple_finalize;
impl_class->process_update = meta_kms_impl_simple_process_update;
impl_class->handle_page_flip_callback = meta_kms_impl_simple_handle_page_flip_callback;
impl_class->discard_pending_page_flips = meta_kms_impl_simple_discard_pending_page_flips;
impl_class->dispatch_idle = meta_kms_impl_simple_dispatch_idle;
impl_class->notify_device_created = meta_kms_impl_simple_notify_device_created;
}