f092b6c78c
Allows to prepare KMS updates to set the color space and HDR Static Metadata on the output. For some reason we need ALLOW_MODESET on commits which change the HDR Static Metadata InfoFrame on AMDGPU. There is no technical reason why one needs to mode set to send an InfoFrame and the driver should just manage without ALLOW_MODESET. Until this is resolved in the kernel we just prepare KMS updates which might mode set. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2879>
1049 lines
30 KiB
C
1049 lines
30 KiB
C
/*
|
|
* Copyright (C) 2018 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-update.h"
|
|
#include "backends/native/meta-kms-update-private.h"
|
|
|
|
#include "backends/meta-display-config-shared.h"
|
|
#include "backends/native/meta-kms-connector.h"
|
|
#include "backends/native/meta-kms-crtc.h"
|
|
#include "backends/native/meta-kms-impl-device.h"
|
|
#include "backends/native/meta-kms-mode-private.h"
|
|
#include "backends/native/meta-kms-plane.h"
|
|
#include "backends/native/meta-kms-private.h"
|
|
|
|
struct _MetaKmsUpdate
|
|
{
|
|
MetaKmsDevice *device;
|
|
|
|
gboolean is_sealed;
|
|
|
|
GList *mode_sets;
|
|
GList *plane_assignments;
|
|
GList *connector_updates;
|
|
GList *crtc_color_updates;
|
|
|
|
MetaKmsCustomPageFlip *custom_page_flip;
|
|
|
|
GList *page_flip_listeners;
|
|
GList *result_listeners;
|
|
|
|
gboolean needs_modeset;
|
|
|
|
MetaKmsImplDevice *impl_device;
|
|
};
|
|
|
|
void
|
|
meta_kms_plane_feedback_free (MetaKmsPlaneFeedback *plane_feedback)
|
|
{
|
|
g_error_free (plane_feedback->error);
|
|
g_free (plane_feedback);
|
|
}
|
|
|
|
MetaKmsPlaneFeedback *
|
|
meta_kms_plane_feedback_new_take_error (MetaKmsPlane *plane,
|
|
MetaKmsCrtc *crtc,
|
|
GError *error)
|
|
{
|
|
MetaKmsPlaneFeedback *plane_feedback;
|
|
|
|
plane_feedback = g_new0 (MetaKmsPlaneFeedback, 1);
|
|
*plane_feedback = (MetaKmsPlaneFeedback) {
|
|
.plane = plane,
|
|
.crtc = crtc,
|
|
.error = error,
|
|
};
|
|
|
|
return plane_feedback;
|
|
}
|
|
|
|
MetaKmsPlaneFeedback *
|
|
meta_kms_plane_feedback_new_failed (MetaKmsPlane *plane,
|
|
MetaKmsCrtc *crtc,
|
|
const char *error_message)
|
|
{
|
|
GError *error;
|
|
|
|
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, error_message);
|
|
return meta_kms_plane_feedback_new_take_error (plane, crtc, error);
|
|
}
|
|
|
|
MetaKmsFeedback *
|
|
meta_kms_feedback_new_passed (GList *failed_planes)
|
|
{
|
|
MetaKmsFeedback *feedback;
|
|
|
|
feedback = g_new0 (MetaKmsFeedback, 1);
|
|
*feedback = (MetaKmsFeedback) {
|
|
.result = META_KMS_FEEDBACK_PASSED,
|
|
.failed_planes = failed_planes,
|
|
};
|
|
g_atomic_ref_count_init (&feedback->ref_count);
|
|
|
|
return feedback;
|
|
}
|
|
|
|
MetaKmsFeedback *
|
|
meta_kms_feedback_new_failed (GList *failed_planes,
|
|
GError *error)
|
|
{
|
|
MetaKmsFeedback *feedback;
|
|
|
|
feedback = g_new0 (MetaKmsFeedback, 1);
|
|
*feedback = (MetaKmsFeedback) {
|
|
.result = META_KMS_FEEDBACK_FAILED,
|
|
.error = error,
|
|
.failed_planes = failed_planes,
|
|
};
|
|
g_atomic_ref_count_init (&feedback->ref_count);
|
|
|
|
return feedback;
|
|
}
|
|
|
|
MetaKmsFeedback *
|
|
meta_kms_feedback_ref (MetaKmsFeedback *feedback)
|
|
{
|
|
g_atomic_ref_count_inc (&feedback->ref_count);
|
|
return feedback;
|
|
}
|
|
|
|
void
|
|
meta_kms_feedback_unref (MetaKmsFeedback *feedback)
|
|
{
|
|
if (g_atomic_ref_count_dec (&feedback->ref_count))
|
|
{
|
|
g_list_free_full (feedback->failed_planes,
|
|
(GDestroyNotify) meta_kms_plane_feedback_free);
|
|
g_clear_error (&feedback->error);
|
|
g_free (feedback);
|
|
}
|
|
}
|
|
|
|
MetaKmsFeedbackResult
|
|
meta_kms_feedback_get_result (const MetaKmsFeedback *feedback)
|
|
{
|
|
return feedback->result;
|
|
}
|
|
|
|
GList *
|
|
meta_kms_feedback_get_failed_planes (const MetaKmsFeedback *feedback)
|
|
{
|
|
return feedback->failed_planes;
|
|
}
|
|
|
|
const GError *
|
|
meta_kms_feedback_get_error (const MetaKmsFeedback *feedback)
|
|
{
|
|
return feedback->error;
|
|
}
|
|
|
|
void
|
|
meta_kms_feedback_dispatch_result (MetaKmsFeedback *feedback,
|
|
MetaKms *kms,
|
|
GList *result_listeners)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = result_listeners; l; l = l->next)
|
|
{
|
|
MetaKmsResultListener *listener = l->data;
|
|
|
|
meta_kms_result_listener_set_feedback (listener, feedback);
|
|
meta_kms_queue_result_callback (kms, listener);
|
|
}
|
|
g_list_free (result_listeners);
|
|
}
|
|
|
|
static void
|
|
meta_kms_fb_damage_free (MetaKmsFbDamage *fb_damage)
|
|
{
|
|
g_free (fb_damage->rects);
|
|
g_free (fb_damage);
|
|
}
|
|
|
|
static void
|
|
meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment)
|
|
{
|
|
g_clear_pointer (&plane_assignment->fb_damage, meta_kms_fb_damage_free);
|
|
g_free (plane_assignment);
|
|
}
|
|
|
|
static void
|
|
meta_kms_mode_set_free (MetaKmsModeSet *mode_set)
|
|
{
|
|
g_list_free (mode_set->connectors);
|
|
g_free (mode_set);
|
|
}
|
|
|
|
static void
|
|
meta_kms_page_flip_listener_unref (MetaKmsPageFlipListener *listener)
|
|
{
|
|
if (!g_atomic_ref_count_dec (&listener->ref_count))
|
|
return;
|
|
|
|
g_clear_pointer (&listener->user_data, listener->destroy_notify);
|
|
g_free (listener);
|
|
}
|
|
|
|
static gboolean
|
|
drop_plane_assignment (MetaKmsUpdate *update,
|
|
MetaKmsPlane *plane,
|
|
MetaKmsAssignPlaneFlag *out_flags)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = update->plane_assignments; l; l = l->next)
|
|
{
|
|
MetaKmsPlaneAssignment *plane_assignment = l->data;
|
|
|
|
if (plane_assignment->plane == plane)
|
|
{
|
|
update->plane_assignments =
|
|
g_list_delete_link (update->plane_assignments, l);
|
|
if (out_flags)
|
|
*out_flags = plane_assignment->flags;
|
|
meta_kms_plane_assignment_free (plane_assignment);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_drop_plane_assignment (MetaKmsUpdate *update,
|
|
MetaKmsPlane *plane)
|
|
{
|
|
drop_plane_assignment (update, plane, NULL);
|
|
}
|
|
|
|
MetaKmsPlaneAssignment *
|
|
meta_kms_update_assign_plane (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc,
|
|
MetaKmsPlane *plane,
|
|
MetaDrmBuffer *buffer,
|
|
MetaFixed16Rectangle src_rect,
|
|
MetaRectangle dst_rect,
|
|
MetaKmsAssignPlaneFlag flags)
|
|
{
|
|
MetaKmsPlaneAssignment *plane_assignment;
|
|
MetaKmsAssignPlaneFlag old_flags;
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
|
|
g_assert (meta_kms_plane_get_device (plane) == update->device);
|
|
g_assert (meta_kms_plane_get_plane_type (plane) !=
|
|
META_KMS_PLANE_TYPE_PRIMARY ||
|
|
!(flags & META_KMS_ASSIGN_PLANE_FLAG_ALLOW_FAIL));
|
|
|
|
if (drop_plane_assignment (update, plane, &old_flags))
|
|
{
|
|
if (!(old_flags & META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED))
|
|
flags &= ~META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED;
|
|
}
|
|
|
|
plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1);
|
|
*plane_assignment = (MetaKmsPlaneAssignment) {
|
|
.update = update,
|
|
.crtc = crtc,
|
|
.plane = plane,
|
|
.buffer = buffer,
|
|
.src_rect = src_rect,
|
|
.dst_rect = dst_rect,
|
|
.flags = flags,
|
|
};
|
|
|
|
update->plane_assignments = g_list_prepend (update->plane_assignments,
|
|
plane_assignment);
|
|
|
|
return plane_assignment;
|
|
}
|
|
|
|
MetaKmsPlaneAssignment *
|
|
meta_kms_update_unassign_plane (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc,
|
|
MetaKmsPlane *plane)
|
|
{
|
|
MetaKmsPlaneAssignment *plane_assignment;
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
|
|
g_assert (meta_kms_plane_get_device (plane) == update->device);
|
|
|
|
plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1);
|
|
*plane_assignment = (MetaKmsPlaneAssignment) {
|
|
.update = update,
|
|
.crtc = crtc,
|
|
.plane = plane,
|
|
.buffer = NULL,
|
|
};
|
|
|
|
update->plane_assignments = g_list_prepend (update->plane_assignments,
|
|
plane_assignment);
|
|
|
|
return plane_assignment;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_mode_set (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc,
|
|
GList *connectors,
|
|
MetaKmsMode *mode)
|
|
{
|
|
MetaKmsModeSet *mode_set;
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
|
|
|
|
mode_set = g_new0 (MetaKmsModeSet, 1);
|
|
*mode_set = (MetaKmsModeSet) {
|
|
.crtc = crtc,
|
|
.connectors = connectors,
|
|
.mode = mode,
|
|
};
|
|
|
|
update->mode_sets = g_list_prepend (update->mode_sets, mode_set);
|
|
}
|
|
|
|
static MetaKmsConnectorUpdate *
|
|
ensure_connector_update (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector)
|
|
{
|
|
GList *l;
|
|
MetaKmsConnectorUpdate *connector_update;
|
|
|
|
for (l = update->connector_updates; l; l = l->next)
|
|
{
|
|
connector_update = l->data;
|
|
|
|
if (connector_update->connector == connector)
|
|
return connector_update;
|
|
}
|
|
|
|
connector_update = g_new0 (MetaKmsConnectorUpdate, 1);
|
|
connector_update->connector = connector;
|
|
|
|
update->connector_updates = g_list_prepend (update->connector_updates,
|
|
connector_update);
|
|
|
|
return connector_update;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_set_underscanning (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector,
|
|
uint64_t hborder,
|
|
uint64_t vborder)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update;
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
g_assert (meta_kms_connector_get_device (connector) == update->device);
|
|
|
|
connector_update = ensure_connector_update (update, connector);
|
|
connector_update->underscanning.has_update = TRUE;
|
|
connector_update->underscanning.is_active = TRUE;
|
|
connector_update->underscanning.hborder = hborder;
|
|
connector_update->underscanning.vborder = vborder;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_unset_underscanning (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update;
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
g_assert (meta_kms_connector_get_device (connector) == update->device);
|
|
|
|
connector_update = ensure_connector_update (update, connector);
|
|
connector_update->underscanning.has_update = TRUE;
|
|
connector_update->underscanning.is_active = FALSE;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_set_privacy_screen (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector,
|
|
gboolean enabled)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update;
|
|
|
|
g_assert (meta_kms_connector_get_device (connector) == update->device);
|
|
|
|
connector_update = ensure_connector_update (update, connector);
|
|
connector_update->privacy_screen.has_update = TRUE;
|
|
connector_update->privacy_screen.is_enabled = enabled;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_set_max_bpc (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector,
|
|
uint64_t max_bpc)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update;
|
|
|
|
g_assert (meta_kms_connector_get_device (connector) == update->device);
|
|
|
|
connector_update = ensure_connector_update (update, connector);
|
|
connector_update->max_bpc.value = max_bpc;
|
|
connector_update->max_bpc.has_update = TRUE;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_set_color_space (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector,
|
|
MetaOutputColorspace color_space)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update;
|
|
|
|
g_assert (meta_kms_connector_get_device (connector) == update->device);
|
|
g_return_if_fail (meta_kms_connector_is_color_space_supported (connector,
|
|
color_space));
|
|
|
|
connector_update = ensure_connector_update (update, connector);
|
|
connector_update->colorspace.has_update = TRUE;
|
|
connector_update->colorspace.value = color_space;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_set_hdr_metadata (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector,
|
|
MetaOutputHdrMetadata *metadata)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update;
|
|
|
|
g_assert (meta_kms_connector_get_device (connector) == update->device);
|
|
g_return_if_fail (meta_kms_connector_is_hdr_metadata_supported (connector));
|
|
|
|
connector_update = ensure_connector_update (update, connector);
|
|
connector_update->hdr.has_update = TRUE;
|
|
connector_update->hdr.value = *metadata;
|
|
|
|
/* Currently required on AMDGPU but should in general not require mode sets */
|
|
update->needs_modeset = TRUE;
|
|
}
|
|
|
|
static MetaKmsCrtcColorUpdate *
|
|
ensure_color_update (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
GList *l;
|
|
MetaKmsCrtcColorUpdate *color_update;
|
|
|
|
for (l = update->crtc_color_updates; l; l = l->next)
|
|
{
|
|
color_update = l->data;
|
|
|
|
if (color_update->crtc == crtc)
|
|
return color_update;
|
|
}
|
|
|
|
color_update = g_new0 (MetaKmsCrtcColorUpdate, 1);
|
|
color_update->crtc = crtc;
|
|
|
|
update->crtc_color_updates = g_list_prepend (update->crtc_color_updates,
|
|
color_update);
|
|
|
|
return color_update;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc,
|
|
const MetaGammaLut *gamma)
|
|
{
|
|
MetaKmsCrtcColorUpdate *color_update;
|
|
MetaGammaLut *gamma_update = NULL;
|
|
const MetaKmsCrtcState *crtc_state = meta_kms_crtc_get_current_state (crtc);
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
|
|
|
|
if (gamma)
|
|
gamma_update = meta_gamma_lut_copy_to_size (gamma, crtc_state->gamma.size);
|
|
|
|
color_update = ensure_color_update (update, crtc);
|
|
color_update->gamma.state = gamma_update;
|
|
color_update->gamma.has_update = TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_kms_crtc_color_updates_free (MetaKmsCrtcColorUpdate *color_update)
|
|
{
|
|
if (color_update->gamma.has_update)
|
|
g_clear_pointer (&color_update->gamma.state, meta_gamma_lut_free);
|
|
}
|
|
|
|
void
|
|
meta_kms_update_add_page_flip_listener (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc,
|
|
const MetaKmsPageFlipListenerVtable *vtable,
|
|
MetaKmsPageFlipListenerFlag flags,
|
|
gpointer user_data,
|
|
GDestroyNotify destroy_notify)
|
|
{
|
|
MetaKmsPageFlipListener *listener;
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
g_assert (meta_kms_crtc_get_device (crtc) == update->device);
|
|
|
|
listener = g_new0 (MetaKmsPageFlipListener, 1);
|
|
*listener = (MetaKmsPageFlipListener) {
|
|
.crtc = crtc,
|
|
.vtable = vtable,
|
|
.flags = flags,
|
|
.user_data = user_data,
|
|
.destroy_notify = destroy_notify,
|
|
};
|
|
g_atomic_ref_count_init (&listener->ref_count);
|
|
|
|
update->page_flip_listeners = g_list_prepend (update->page_flip_listeners,
|
|
listener);
|
|
}
|
|
|
|
void
|
|
meta_kms_update_set_custom_page_flip (MetaKmsUpdate *update,
|
|
MetaKmsCustomPageFlipFunc func,
|
|
gpointer user_data)
|
|
{
|
|
MetaKmsCustomPageFlip *custom_page_flip;
|
|
|
|
g_assert (!meta_kms_update_is_sealed (update));
|
|
|
|
custom_page_flip = g_new0 (MetaKmsCustomPageFlip, 1);
|
|
custom_page_flip->func = func;
|
|
custom_page_flip->user_data = user_data;
|
|
|
|
update->custom_page_flip = custom_page_flip;
|
|
}
|
|
|
|
void
|
|
meta_kms_plane_assignment_set_fb_damage (MetaKmsPlaneAssignment *plane_assignment,
|
|
const int *rectangles,
|
|
int n_rectangles)
|
|
{
|
|
MetaKmsFbDamage *fb_damage;
|
|
struct drm_mode_rect *mode_rects;
|
|
int i;
|
|
|
|
mode_rects = g_new0 (struct drm_mode_rect, n_rectangles);
|
|
for (i = 0; i < n_rectangles; ++i)
|
|
{
|
|
mode_rects[i].x1 = rectangles[i * 4];
|
|
mode_rects[i].y1 = rectangles[i * 4 + 1];
|
|
mode_rects[i].x2 = mode_rects[i].x1 + rectangles[i * 4 + 2];
|
|
mode_rects[i].y2 = mode_rects[i].y1 + rectangles[i * 4 + 3];
|
|
}
|
|
|
|
fb_damage = g_new0 (MetaKmsFbDamage, 1);
|
|
*fb_damage = (MetaKmsFbDamage) {
|
|
.rects = mode_rects,
|
|
.n_rects = n_rectangles,
|
|
};
|
|
|
|
plane_assignment->fb_damage = fb_damage;
|
|
}
|
|
|
|
void
|
|
meta_kms_plane_assignment_set_rotation (MetaKmsPlaneAssignment *plane_assignment,
|
|
MetaKmsPlaneRotation rotation)
|
|
{
|
|
g_assert (!meta_kms_update_is_sealed (plane_assignment->update));
|
|
g_warn_if_fail (rotation);
|
|
|
|
plane_assignment->rotation = rotation;
|
|
}
|
|
|
|
void
|
|
meta_kms_plane_assignment_set_cursor_hotspot (MetaKmsPlaneAssignment *plane_assignment,
|
|
int x,
|
|
int y)
|
|
{
|
|
plane_assignment->cursor_hotspot.is_valid = TRUE;
|
|
plane_assignment->cursor_hotspot.x = x;
|
|
plane_assignment->cursor_hotspot.y = y;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_add_result_listener (MetaKmsUpdate *update,
|
|
MetaKmsResultListenerFunc func,
|
|
gpointer user_data)
|
|
{
|
|
MetaKmsResultListener *listener;
|
|
|
|
listener = g_new0 (MetaKmsResultListener, 1);
|
|
*listener = (MetaKmsResultListener) {
|
|
.func = func,
|
|
.user_data = user_data,
|
|
};
|
|
|
|
update->result_listeners = g_list_append (update->result_listeners,
|
|
listener);
|
|
}
|
|
|
|
GList *
|
|
meta_kms_update_take_result_listeners (MetaKmsUpdate *update)
|
|
{
|
|
return g_steal_pointer (&update->result_listeners);
|
|
}
|
|
|
|
void
|
|
meta_kms_result_listener_set_feedback (MetaKmsResultListener *listener,
|
|
MetaKmsFeedback *feedback)
|
|
{
|
|
g_return_if_fail (!listener->feedback);
|
|
|
|
listener->feedback = meta_kms_feedback_ref (feedback);
|
|
}
|
|
|
|
void
|
|
meta_kms_result_listener_notify (MetaKmsResultListener *listener)
|
|
{
|
|
g_return_if_fail (listener->feedback);
|
|
|
|
listener->func (listener->feedback, listener->user_data);
|
|
}
|
|
|
|
void
|
|
meta_kms_result_listener_free (MetaKmsResultListener *listener)
|
|
{
|
|
g_clear_pointer (&listener->feedback, meta_kms_feedback_unref);
|
|
g_free (listener);
|
|
}
|
|
|
|
static MetaKmsPlaneAssignment *
|
|
get_first_plane_assignment (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc,
|
|
MetaKmsPlaneType plane_type)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next)
|
|
{
|
|
MetaKmsPlaneAssignment *plane_assignment = l->data;
|
|
|
|
if (meta_kms_plane_get_plane_type (plane_assignment->plane) !=
|
|
plane_type)
|
|
continue;
|
|
|
|
if (plane_assignment->crtc != crtc)
|
|
continue;
|
|
|
|
return plane_assignment;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
MetaKmsPlaneAssignment *
|
|
meta_kms_update_get_primary_plane_assignment (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
return get_first_plane_assignment (update, crtc, META_KMS_PLANE_TYPE_PRIMARY);
|
|
}
|
|
|
|
MetaKmsPlaneAssignment *
|
|
meta_kms_update_get_cursor_plane_assignment (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
return get_first_plane_assignment (update, crtc, META_KMS_PLANE_TYPE_CURSOR);
|
|
}
|
|
|
|
GList *
|
|
meta_kms_update_get_plane_assignments (MetaKmsUpdate *update)
|
|
{
|
|
return update->plane_assignments;
|
|
}
|
|
|
|
GList *
|
|
meta_kms_update_get_mode_sets (MetaKmsUpdate *update)
|
|
{
|
|
return update->mode_sets;
|
|
}
|
|
|
|
GList *
|
|
meta_kms_update_get_page_flip_listeners (MetaKmsUpdate *update)
|
|
{
|
|
return update->page_flip_listeners;
|
|
}
|
|
|
|
GList *
|
|
meta_kms_update_get_connector_updates (MetaKmsUpdate *update)
|
|
{
|
|
return update->connector_updates;
|
|
}
|
|
|
|
GList *
|
|
meta_kms_update_get_crtc_color_updates (MetaKmsUpdate *update)
|
|
{
|
|
return update->crtc_color_updates;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_seal (MetaKmsUpdate *update)
|
|
{
|
|
g_warn_if_fail (!update->is_sealed);
|
|
|
|
update->is_sealed = TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_update_is_sealed (MetaKmsUpdate *update)
|
|
{
|
|
return update->is_sealed;
|
|
}
|
|
|
|
MetaKmsDevice *
|
|
meta_kms_update_get_device (MetaKmsUpdate *update)
|
|
{
|
|
return update->device;
|
|
}
|
|
|
|
MetaKmsCustomPageFlip *
|
|
meta_kms_update_take_custom_page_flip_func (MetaKmsUpdate *update)
|
|
{
|
|
return g_steal_pointer (&update->custom_page_flip);
|
|
}
|
|
|
|
void
|
|
meta_kms_custom_page_flip_free (MetaKmsCustomPageFlip *custom_page_flip)
|
|
{
|
|
g_free (custom_page_flip);
|
|
}
|
|
|
|
static GList *
|
|
find_mode_set_link_for (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = update->mode_sets; l; l = l->next)
|
|
{
|
|
MetaKmsModeSet *mode_set = l->data;
|
|
|
|
if (mode_set->crtc == crtc)
|
|
return l;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
merge_mode_sets (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
while (other_update->mode_sets)
|
|
{
|
|
GList *l = other_update->mode_sets;
|
|
MetaKmsModeSet *other_mode_set = l->data;
|
|
MetaKmsCrtc *crtc = other_mode_set->crtc;
|
|
GList *el;
|
|
|
|
other_update->mode_sets =
|
|
g_list_remove_link (other_update->mode_sets, l);
|
|
|
|
el = find_mode_set_link_for (update, crtc);
|
|
if (el)
|
|
{
|
|
meta_kms_mode_set_free (el->data);
|
|
update->mode_sets =
|
|
g_list_insert_before_link (update->mode_sets, el, l);
|
|
update->mode_sets =
|
|
g_list_delete_link (update->mode_sets, el);
|
|
}
|
|
else
|
|
{
|
|
update->mode_sets =
|
|
g_list_insert_before_link (update->mode_sets,
|
|
update->mode_sets,
|
|
l);
|
|
}
|
|
}
|
|
}
|
|
|
|
static GList *
|
|
find_plane_assignment_link_for (MetaKmsUpdate *update,
|
|
MetaKmsPlane *plane)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = update->plane_assignments; l; l = l->next)
|
|
{
|
|
MetaKmsPlaneAssignment *plane_assignment = l->data;
|
|
|
|
if (plane_assignment->plane == plane)
|
|
return l;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
merge_plane_assignments_from (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
while (other_update->plane_assignments)
|
|
{
|
|
GList *l = other_update->plane_assignments;
|
|
MetaKmsPlaneAssignment *other_plane_assignment = l->data;
|
|
MetaKmsPlane *plane = other_plane_assignment->plane;
|
|
GList *el;
|
|
|
|
other_update->plane_assignments =
|
|
g_list_remove_link (other_update->plane_assignments, l);
|
|
|
|
el = find_plane_assignment_link_for (update, plane);
|
|
if (el)
|
|
{
|
|
meta_kms_plane_assignment_free (el->data);
|
|
update->plane_assignments =
|
|
g_list_insert_before_link (update->plane_assignments, el, l);
|
|
update->plane_assignments =
|
|
g_list_delete_link (update->plane_assignments, el);
|
|
}
|
|
else
|
|
{
|
|
update->plane_assignments =
|
|
g_list_insert_before_link (update->plane_assignments,
|
|
update->plane_assignments,
|
|
l);
|
|
}
|
|
other_plane_assignment->update = update;
|
|
}
|
|
}
|
|
|
|
static GList *
|
|
find_color_update_link_for (MetaKmsUpdate *update,
|
|
MetaKmsCrtc *crtc)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = update->crtc_color_updates; l; l = l->next)
|
|
{
|
|
MetaKmsCrtcColorUpdate *color_update = l->data;
|
|
|
|
if (color_update->crtc == crtc)
|
|
return l;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
merge_crtc_color_updates_from (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
while (other_update->crtc_color_updates)
|
|
{
|
|
GList *l = other_update->crtc_color_updates;
|
|
MetaKmsCrtcColorUpdate *other_crtc_color_update = l->data;
|
|
MetaKmsCrtc *crtc = other_crtc_color_update->crtc;
|
|
GList *el;
|
|
|
|
other_update->crtc_color_updates =
|
|
g_list_remove_link (other_update->crtc_color_updates, l);
|
|
|
|
el = find_color_update_link_for (update, crtc);
|
|
if (el)
|
|
{
|
|
meta_kms_crtc_color_updates_free (el->data);
|
|
update->crtc_color_updates =
|
|
g_list_insert_before_link (update->crtc_color_updates, el, l);
|
|
update->crtc_color_updates =
|
|
g_list_delete_link (update->crtc_color_updates, el);
|
|
}
|
|
else
|
|
{
|
|
update->crtc_color_updates =
|
|
g_list_insert_before_link (update->crtc_color_updates,
|
|
update->crtc_color_updates,
|
|
l);
|
|
}
|
|
}
|
|
}
|
|
|
|
static GList *
|
|
find_connector_update_link_for (MetaKmsUpdate *update,
|
|
MetaKmsConnector *connector)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = update->connector_updates; l; l = l->next)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update = l->data;
|
|
|
|
if (connector_update->connector == connector)
|
|
return l;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
merge_connector_updates_from (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
while (other_update->connector_updates)
|
|
{
|
|
GList *l = other_update->connector_updates;
|
|
MetaKmsConnectorUpdate *other_connector_update = l->data;
|
|
MetaKmsConnector *connector = other_connector_update->connector;
|
|
GList *el;
|
|
|
|
other_update->connector_updates =
|
|
g_list_remove_link (other_update->connector_updates, l);
|
|
el = find_connector_update_link_for (update, connector);
|
|
if (el)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update = el->data;
|
|
|
|
if (other_connector_update->underscanning.has_update)
|
|
{
|
|
connector_update->underscanning =
|
|
other_connector_update->underscanning;
|
|
}
|
|
|
|
if (other_connector_update->privacy_screen.has_update)
|
|
{
|
|
connector_update->privacy_screen =
|
|
other_connector_update->privacy_screen;
|
|
}
|
|
|
|
if (other_connector_update->max_bpc.has_update)
|
|
{
|
|
connector_update->max_bpc =
|
|
other_connector_update->max_bpc;
|
|
}
|
|
|
|
if (other_connector_update->colorspace.has_update)
|
|
{
|
|
connector_update->colorspace =
|
|
other_connector_update->colorspace;
|
|
}
|
|
|
|
if (other_connector_update->hdr.has_update)
|
|
{
|
|
connector_update->hdr = other_connector_update->hdr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
update->connector_updates =
|
|
g_list_insert_before_link (update->connector_updates,
|
|
update->connector_updates,
|
|
l);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
merge_custom_page_flip_from (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
g_warn_if_fail ((!update->custom_page_flip &&
|
|
!other_update->custom_page_flip) ||
|
|
((!!update->custom_page_flip) ^
|
|
(!!other_update->custom_page_flip)));
|
|
|
|
g_clear_pointer (&update->custom_page_flip, meta_kms_custom_page_flip_free);
|
|
update->custom_page_flip = g_steal_pointer (&other_update->custom_page_flip);
|
|
}
|
|
|
|
static void
|
|
merge_page_flip_listeners_from (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
update->page_flip_listeners =
|
|
g_list_concat (update->page_flip_listeners,
|
|
g_steal_pointer (&other_update->page_flip_listeners));
|
|
}
|
|
|
|
static void
|
|
merge_result_listeners_from (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
update->result_listeners =
|
|
g_list_concat (update->result_listeners,
|
|
g_steal_pointer (&other_update->result_listeners));
|
|
}
|
|
|
|
void
|
|
meta_kms_update_merge_from (MetaKmsUpdate *update,
|
|
MetaKmsUpdate *other_update)
|
|
{
|
|
g_return_if_fail (update->device == other_update->device);
|
|
|
|
merge_mode_sets (update, other_update);
|
|
merge_plane_assignments_from (update, other_update);
|
|
merge_crtc_color_updates_from (update, other_update);
|
|
merge_connector_updates_from (update, other_update);
|
|
merge_custom_page_flip_from (update, other_update);
|
|
merge_page_flip_listeners_from (update, other_update);
|
|
merge_result_listeners_from (update, other_update);
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_update_get_needs_modeset (MetaKmsUpdate *update)
|
|
{
|
|
return update->needs_modeset || update->mode_sets;
|
|
}
|
|
|
|
MetaKmsUpdate *
|
|
meta_kms_update_new (MetaKmsDevice *device)
|
|
{
|
|
MetaKmsUpdate *update;
|
|
|
|
update = g_new0 (MetaKmsUpdate, 1);
|
|
update->device = device;
|
|
|
|
return update;
|
|
}
|
|
|
|
void
|
|
meta_kms_update_free (MetaKmsUpdate *update)
|
|
{
|
|
if (update->impl_device)
|
|
meta_kms_impl_device_unhold_fd (update->impl_device);
|
|
|
|
g_list_free_full (update->result_listeners,
|
|
(GDestroyNotify) meta_kms_result_listener_free);
|
|
g_list_free_full (update->plane_assignments,
|
|
(GDestroyNotify) meta_kms_plane_assignment_free);
|
|
g_list_free_full (update->mode_sets,
|
|
(GDestroyNotify) meta_kms_mode_set_free);
|
|
g_list_free_full (update->page_flip_listeners,
|
|
(GDestroyNotify) meta_kms_page_flip_listener_unref);
|
|
g_list_free_full (update->connector_updates, g_free);
|
|
g_list_free_full (update->crtc_color_updates,
|
|
(GDestroyNotify) meta_kms_crtc_color_updates_free);
|
|
g_clear_pointer (&update->custom_page_flip, meta_kms_custom_page_flip_free);
|
|
|
|
g_free (update);
|
|
}
|
|
|
|
void
|
|
meta_kms_update_realize (MetaKmsUpdate *update,
|
|
MetaKmsImplDevice *impl_device)
|
|
{
|
|
update->impl_device = impl_device;
|
|
meta_kms_impl_device_hold_fd (impl_device);
|
|
}
|