861 lines
27 KiB
C
861 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 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)
|
|
{
|
|
float refresh_rate;
|
|
|
|
refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc);
|
|
schedule_retry_page_flip (impl_simple,
|
|
crtc,
|
|
plane_assignment->fb_id,
|
|
refresh_rate,
|
|
page_flip_data);
|
|
}
|
|
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;
|
|
}
|