/* * Copyright (C) 2019 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-crtc.h" #include "backends/native/meta-kms-crtc-private.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-impl-device-atomic.h" #include "backends/native/meta-kms-impl-device-simple.h" #include "backends/native/meta-kms-mode.h" #include "backends/native/meta-kms-update-private.h" typedef struct _MetaKmsCrtcPropTable { MetaKmsProp props[META_KMS_CRTC_N_PROPS]; } MetaKmsCrtcPropTable; struct _MetaKmsCrtc { GObject parent; MetaKmsDevice *device; uint32_t id; int idx; MetaKmsCrtcState current_state; MetaKmsCrtcPropTable prop_table; }; G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT) MetaKmsDevice * meta_kms_crtc_get_device (MetaKmsCrtc *crtc) { return crtc->device; } const MetaKmsCrtcState * meta_kms_crtc_get_current_state (MetaKmsCrtc *crtc) { return &crtc->current_state; } uint32_t meta_kms_crtc_get_id (MetaKmsCrtc *crtc) { return crtc->id; } int meta_kms_crtc_get_idx (MetaKmsCrtc *crtc) { return crtc->idx; } uint32_t meta_kms_crtc_get_prop_id (MetaKmsCrtc *crtc, MetaKmsCrtcProp prop) { return crtc->prop_table.props[prop].prop_id; } const char * meta_kms_crtc_get_prop_name (MetaKmsCrtc *crtc, MetaKmsCrtcProp prop) { return crtc->prop_table.props[prop].name; } uint64_t meta_kms_crtc_get_prop_drm_value (MetaKmsCrtc *crtc, MetaKmsCrtcProp property, uint64_t value) { MetaKmsProp *prop = &crtc->prop_table.props[property]; return meta_kms_prop_convert_value (prop, value); } gboolean meta_kms_crtc_is_active (MetaKmsCrtc *crtc) { return crtc->current_state.is_active; } static void read_crtc_gamma (MetaKmsCrtc *crtc, MetaKmsCrtcState *crtc_state, MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc) { MetaKmsProp *prop_lut; MetaKmsProp *prop_size; uint64_t blob_id; uint64_t lut_size; int fd; drmModePropertyBlobPtr blob; int drm_lut_size; struct drm_color_lut *drm_lut; int i; prop_lut = &crtc->prop_table.props[META_KMS_CRTC_PROP_GAMMA_LUT]; prop_size = &crtc->prop_table.props[META_KMS_CRTC_PROP_GAMMA_LUT_SIZE]; if (!prop_lut->prop_id || !prop_size->prop_id) return; lut_size = prop_size->value; if (lut_size <= 0) return; crtc_state->gamma.size = lut_size; crtc_state->gamma.supported = TRUE; blob_id = prop_lut->value; if (blob_id == 0) return; fd = meta_kms_impl_device_get_fd (impl_device); blob = drmModeGetPropertyBlob (fd, blob_id); if (!blob) return; drm_lut_size = blob->length / sizeof (struct drm_color_lut); if (drm_lut_size == 0) { drmModeFreePropertyBlob (blob); return; } drm_lut = blob->data; crtc_state->gamma.value = meta_gamma_lut_new_sized (drm_lut_size); for (i = 0; i < drm_lut_size; i++) { crtc_state->gamma.value->red[i] = drm_lut[i].red; crtc_state->gamma.value->green[i] = drm_lut[i].green; crtc_state->gamma.value->blue[i] = drm_lut[i].blue; } drmModeFreePropertyBlob (blob); } static void read_crtc_legacy_gamma (MetaKmsCrtc *crtc, MetaKmsCrtcState *crtc_state, MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc) { crtc_state->gamma.size = drm_crtc->gamma_size; crtc_state->gamma.supported = drm_crtc->gamma_size != 0; crtc_state->gamma.value = meta_gamma_lut_new_sized (drm_crtc->gamma_size); drmModeCrtcGetGamma (meta_kms_impl_device_get_fd (impl_device), crtc->id, crtc_state->gamma.value->size, crtc_state->gamma.value->red, crtc_state->gamma.value->green, crtc_state->gamma.value->blue); } static void read_gamma_state (MetaKmsCrtc *crtc, MetaKmsCrtcState *crtc_state, MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc) { g_assert_null (crtc_state->gamma.value); crtc_state->gamma.size = 0; crtc_state->gamma.supported = FALSE; if (META_IS_KMS_IMPL_DEVICE_ATOMIC (impl_device)) read_crtc_gamma (crtc, crtc_state, impl_device, drm_crtc); else if (META_IS_KMS_IMPL_DEVICE_SIMPLE (impl_device)) read_crtc_legacy_gamma (crtc, crtc_state, impl_device, drm_crtc); } static gboolean gamma_equal (MetaKmsCrtcState *state, MetaKmsCrtcState *other_state) { return state->gamma.size == other_state->gamma.size && state->gamma.supported == other_state->gamma.supported && meta_gamma_lut_equal (state->gamma.value, other_state->gamma.value); } static MetaKmsResourceChanges meta_kms_crtc_state_changes (MetaKmsCrtcState *state, MetaKmsCrtcState *other_state) { if (state->is_active != other_state->is_active) return META_KMS_RESOURCE_CHANGE_FULL; if (!meta_rectangle_equal (&state->rect, &other_state->rect)) return META_KMS_RESOURCE_CHANGE_FULL; if (state->is_drm_mode_valid != other_state->is_drm_mode_valid) return META_KMS_RESOURCE_CHANGE_FULL; if (!meta_drm_mode_equal (&state->drm_mode, &other_state->drm_mode)) return META_KMS_RESOURCE_CHANGE_FULL; if (!gamma_equal (state, other_state)) return META_KMS_RESOURCE_CHANGE_GAMMA; return META_KMS_RESOURCE_CHANGE_NONE; } static MetaKmsResourceChanges meta_kms_crtc_read_state (MetaKmsCrtc *crtc, MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc, drmModeObjectProperties *drm_props) { MetaKmsCrtcState crtc_state = {0}; MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE; MetaKmsProp *active_prop; meta_kms_impl_device_update_prop_table (impl_device, drm_props->props, drm_props->prop_values, drm_props->count_props, crtc->prop_table.props, META_KMS_CRTC_N_PROPS); crtc_state.rect = (MetaRectangle) { .x = drm_crtc->x, .y = drm_crtc->y, .width = drm_crtc->width, .height = drm_crtc->height, }; crtc_state.is_drm_mode_valid = drm_crtc->mode_valid; crtc_state.drm_mode = drm_crtc->mode; active_prop = &crtc->prop_table.props[META_KMS_CRTC_PROP_ACTIVE]; if (active_prop->prop_id) crtc_state.is_active = !!active_prop->value; else crtc_state.is_active = drm_crtc->mode_valid; read_gamma_state (crtc, &crtc_state, impl_device, drm_crtc); if (!crtc_state.is_active) { if (crtc->current_state.is_active) changes |= META_KMS_RESOURCE_CHANGE_FULL; } else { changes = meta_kms_crtc_state_changes (&crtc->current_state, &crtc_state); } g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free); crtc->current_state = crtc_state; meta_topic (META_DEBUG_KMS, "Read CRTC %u state: active: %d, mode: %s, changed: %s", crtc->id, crtc->current_state.is_active, crtc->current_state.is_drm_mode_valid ? crtc->current_state.drm_mode.name : "(nil)", changes == META_KMS_RESOURCE_CHANGE_NONE ? "no" : "yes"); return changes; } MetaKmsResourceChanges meta_kms_crtc_update_state_in_impl (MetaKmsCrtc *crtc) { MetaKmsImplDevice *impl_device; MetaKmsResourceChanges changes; int fd; drmModeCrtc *drm_crtc; drmModeObjectProperties *drm_props; impl_device = meta_kms_device_get_impl_device (crtc->device); fd = meta_kms_impl_device_get_fd (impl_device); drm_crtc = drmModeGetCrtc (fd, crtc->id); drm_props = drmModeObjectGetProperties (fd, crtc->id, DRM_MODE_OBJECT_CRTC); if (!drm_crtc || !drm_props) { crtc->current_state.is_active = FALSE; crtc->current_state.rect = (MetaRectangle) { }; crtc->current_state.is_drm_mode_valid = FALSE; changes = META_KMS_RESOURCE_CHANGE_FULL; goto out; } changes = meta_kms_crtc_read_state (crtc, impl_device, drm_crtc, drm_props); out: g_clear_pointer (&drm_props, drmModeFreeObjectProperties); g_clear_pointer (&drm_crtc, drmModeFreeCrtc); return changes; } void meta_kms_crtc_disable_in_impl (MetaKmsCrtc *crtc) { crtc->current_state.is_active = FALSE; crtc->current_state.rect = (MetaRectangle) { 0 }; crtc->current_state.is_drm_mode_valid = FALSE; crtc->current_state.drm_mode = (drmModeModeInfo) { 0 }; } void meta_kms_crtc_predict_state_in_impl (MetaKmsCrtc *crtc, MetaKmsUpdate *update) { GList *mode_sets; GList *crtc_color_updates; GList *l; mode_sets = meta_kms_update_get_mode_sets (update); for (l = mode_sets; l; l = l->next) { MetaKmsModeSet *mode_set = l->data; if (mode_set->crtc != crtc) continue; if (mode_set->mode) { MetaKmsPlaneAssignment *plane_assignment; const drmModeModeInfo *drm_mode; plane_assignment = meta_kms_update_get_primary_plane_assignment (update, crtc); drm_mode = meta_kms_mode_get_drm_mode (mode_set->mode); crtc->current_state.is_active = TRUE; crtc->current_state.rect = meta_fixed_16_rectangle_to_rectangle (plane_assignment->src_rect); crtc->current_state.is_drm_mode_valid = TRUE; crtc->current_state.drm_mode = *drm_mode; } else { crtc->current_state.is_active = FALSE; crtc->current_state.rect = (MetaRectangle) { 0 }; crtc->current_state.is_drm_mode_valid = FALSE; crtc->current_state.drm_mode = (drmModeModeInfo) { 0 }; } break; } crtc_color_updates = meta_kms_update_get_crtc_color_updates (update); for (l = crtc_color_updates; l; l = l->next) { MetaKmsCrtcColorUpdate *color_update = l->data; MetaGammaLut *gamma = color_update->gamma.state; if (color_update->crtc != crtc) continue; if (color_update->gamma.has_update) { if (gamma) gamma = meta_gamma_lut_copy (gamma); g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free); crtc->current_state.gamma.value = gamma; } break; } } static void init_properties (MetaKmsCrtc *crtc, MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc) { MetaKmsCrtcPropTable *prop_table = &crtc->prop_table; *prop_table = (MetaKmsCrtcPropTable) { .props = { [META_KMS_CRTC_PROP_MODE_ID] = { .name = "MODE_ID", .type = DRM_MODE_PROP_BLOB, }, [META_KMS_CRTC_PROP_ACTIVE] = { .name = "ACTIVE", .type = DRM_MODE_PROP_RANGE, }, [META_KMS_CRTC_PROP_GAMMA_LUT] = { .name = "GAMMA_LUT", .type = DRM_MODE_PROP_BLOB, }, [META_KMS_CRTC_PROP_GAMMA_LUT_SIZE] = { .name = "GAMMA_LUT_SIZE", .type = DRM_MODE_PROP_RANGE, }, } }; } MetaKmsCrtc * meta_kms_crtc_new (MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc, int idx, GError **error) { int fd; drmModeObjectProperties *drm_props; MetaKmsCrtc *crtc; fd = meta_kms_impl_device_get_fd (impl_device); drm_props = drmModeObjectGetProperties (fd, drm_crtc->crtc_id, DRM_MODE_OBJECT_CRTC); if (!drm_props) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "drmModeObjectGetProperties: %s", g_strerror (errno)); return NULL; } crtc = g_object_new (META_TYPE_KMS_CRTC, NULL); crtc->device = meta_kms_impl_device_get_device (impl_device); crtc->id = drm_crtc->crtc_id; crtc->idx = idx; init_properties (crtc, impl_device, drm_crtc); meta_kms_crtc_read_state (crtc, impl_device, drm_crtc, drm_props); drmModeFreeObjectProperties (drm_props); return crtc; } static void meta_kms_crtc_finalize (GObject *object) { MetaKmsCrtc *crtc = META_KMS_CRTC (object); g_clear_pointer (&crtc->current_state.gamma.value, meta_gamma_lut_free); G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object); } static void meta_kms_crtc_init (MetaKmsCrtc *crtc) { crtc->current_state.gamma.size = 0; crtc->current_state.gamma.value = NULL; } static void meta_kms_crtc_class_init (MetaKmsCrtcClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_crtc_finalize; }