ce3409b2b7
If we end up trying to do a mode set on a DRM state that has already changed behind our back without us yet having seen the hotplug event we may fail with `EINVAL`. Since the renderer layer doesn't handle mode set failure, it'll still try to page flip later on, which will then also fail. When failing, it'll try to look up the cached mode set in order to retry the mode set later on, as is needed to handle other error conditions. However, if the mode set prior to the page flip failed, we won't cache the mode set, and the page flip error handling code will get confused. Instead of asserting that a page flip always has a valid cached mode set ready to look up, handle it being missing more gracefully by failing to mode set. It is expected that things will correct themself as there should be a hotplug event waiting around the the corner, to reconfigure the monitor configuration setting new modes. Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/917 https://gitlab.gnome.org/GNOME/mutter/merge_requests/1007
884 lines
27 KiB
C
884 lines
27 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,
|
|
MetaKmsConnectorProperty *connector_property,
|
|
GError **error)
|
|
{
|
|
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,
|
|
MetaKmsModeSet *mode_set,
|
|
GError **error)
|
|
{
|
|
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,
|
|
MetaKmsCrtcGamma *gamma,
|
|
GError **error)
|
|
{
|
|
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);
|
|
g_assert (cached_mode_set);
|
|
|
|
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,
|
|
MetaKmsPageFlip *page_flip,
|
|
GError **error)
|
|
{
|
|
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
|
|
meta_kms_impl_simple_process_update (MetaKmsImpl *impl,
|
|
MetaKmsUpdate *update,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl));
|
|
|
|
for (l = meta_kms_update_get_connector_properties (update); l; l = l->next)
|
|
{
|
|
MetaKmsConnectorProperty *connector_property = l->data;
|
|
|
|
if (!process_connector_property (impl, update, connector_property, error))
|
|
goto discard_page_flips;
|
|
}
|
|
|
|
for (l = meta_kms_update_get_mode_sets (update); l; l = l->next)
|
|
{
|
|
MetaKmsModeSet *mode_set = l->data;
|
|
|
|
if (!process_mode_set (impl, update, mode_set, error))
|
|
goto discard_page_flips;
|
|
}
|
|
|
|
for (l = meta_kms_update_get_crtc_gammas (update); l; l = l->next)
|
|
{
|
|
MetaKmsCrtcGamma *gamma = l->data;
|
|
|
|
if (!process_crtc_gamma (impl, gamma, error))
|
|
goto discard_page_flips;
|
|
}
|
|
|
|
for (l = meta_kms_update_get_page_flips (update); l; l = l->next)
|
|
{
|
|
MetaKmsPageFlip *page_flip = l->data;
|
|
|
|
if (!process_page_flip (impl, update, page_flip, error))
|
|
goto discard_page_flips;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
discard_page_flips:
|
|
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 FALSE;
|
|
}
|
|
|
|
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_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;
|
|
}
|