diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c index 126621cc0..982a8176a 100644 --- a/src/backends/native/meta-crtc-kms.c +++ b/src/backends/native/meta-crtc-kms.c @@ -29,6 +29,8 @@ #include "backends/meta-backend-private.h" #include "backends/native/meta-gpu-kms.h" +#include "backends/native/meta-kms-device.h" +#include "backends/native/meta-kms-plane.h" /* added in libdrm 2.4.95 */ #ifndef DRM_FORMAT_INVALID @@ -41,17 +43,10 @@ typedef struct _MetaCrtcKms { MetaKmsCrtc *kms_crtc; - uint32_t primary_plane_id; uint32_t rotation_prop_id; uint32_t rotation_map[META_MONITOR_N_TRANSFORMS]; - uint32_t all_hw_transforms; - /* - * primary plane's supported formats and maybe modifiers - * key: GUINT_TO_POINTER (format) - * value: owned GArray* (uint64_t modifier), or NULL - */ - GHashTable *formats_modifiers; + MetaKmsPlane *primary_plane; } MetaCrtcKms; /** @@ -93,10 +88,11 @@ meta_crtc_kms_is_transform_handled (MetaCrtc *crtc, { MetaCrtcKms *crtc_kms = crtc->driver_private; - if ((1 << transform) & crtc_kms->all_hw_transforms) - return TRUE; - else + if (!crtc_kms->primary_plane) return FALSE; + + return meta_kms_plane_is_transform_handled (crtc_kms->primary_plane, + transform); } void @@ -110,28 +106,18 @@ meta_crtc_kms_apply_transform (MetaCrtc *crtc) kms_fd = meta_gpu_kms_get_fd (gpu_kms); - if (crtc_kms->all_hw_transforms & (1 << crtc->transform)) - hw_transform = crtc->transform; - else + hw_transform = crtc->transform; + if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform)) hw_transform = META_MONITOR_TRANSFORM_NORMAL; - - if (!meta_crtc_kms_is_transform_handled (crtc, META_MONITOR_TRANSFORM_NORMAL)) + if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform)) return; if (drmModeObjectSetProperty (kms_fd, - crtc_kms->primary_plane_id, + meta_kms_plane_get_id (crtc_kms->primary_plane), DRM_MODE_OBJECT_PLANE, crtc_kms->rotation_prop_id, crtc_kms->rotation_map[hw_transform]) != 0) - { - g_warning ("Failed to apply DRM plane transform %d: %m", hw_transform); - - /* - * Blacklist this HW transform, we want to fallback to our - * fallbacks in this case. - */ - crtc_kms->all_hw_transforms &= ~(1 << hw_transform); - } + g_warning ("Failed to apply DRM plane transform %d: %m", hw_transform); } static int @@ -194,8 +180,8 @@ meta_crtc_kms_get_modifiers (MetaCrtc *crtc, { MetaCrtcKms *crtc_kms = crtc->driver_private; - return g_hash_table_lookup (crtc_kms->formats_modifiers, - GUINT_TO_POINTER (format)); + return meta_kms_plane_get_modifiers_for_format (crtc_kms->primary_plane, + format); } /** @@ -210,24 +196,8 @@ GArray * meta_crtc_kms_copy_drm_format_list (MetaCrtc *crtc) { MetaCrtcKms *crtc_kms = crtc->driver_private; - GArray *formats; - GHashTableIter it; - gpointer key; - unsigned int n_formats_modifiers; - n_formats_modifiers = g_hash_table_size (crtc_kms->formats_modifiers); - formats = g_array_sized_new (FALSE, - FALSE, - sizeof (uint32_t), - n_formats_modifiers); - g_hash_table_iter_init (&it, crtc_kms->formats_modifiers); - while (g_hash_table_iter_next (&it, &key, NULL)) - { - uint32_t drm_format = GPOINTER_TO_UINT (key); - g_array_append_val (formats, drm_format); - } - - return formats; + return meta_kms_plane_copy_drm_format_list (crtc_kms->primary_plane); } /** @@ -243,124 +213,8 @@ meta_crtc_kms_supports_format (MetaCrtc *crtc, { MetaCrtcKms *crtc_kms = crtc->driver_private; - return g_hash_table_lookup_extended (crtc_kms->formats_modifiers, - GUINT_TO_POINTER (drm_format), - NULL, - NULL); -} - -static inline uint32_t * -formats_ptr (struct drm_format_modifier_blob *blob) -{ - return (uint32_t *) (((char *) blob) + blob->formats_offset); -} - -static inline struct drm_format_modifier * -modifiers_ptr (struct drm_format_modifier_blob *blob) -{ - return (struct drm_format_modifier *) (((char *) blob) + - blob->modifiers_offset); -} - -static void -free_modifier_array (GArray *array) -{ - if (!array) - return; - - g_array_free (array, TRUE); -} - -/* - * In case the DRM driver does not expose a format list for the - * primary plane (does not support universal planes nor - * IN_FORMATS property), hardcode something that is probably supported. - */ -static const uint32_t drm_default_formats[] = - { - DRM_FORMAT_XRGB8888 /* The format everything should always support by convention */, -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - DRM_FORMAT_XBGR8888 /* OpenGL GL_RGBA, GL_UNSIGNED_BYTE format, hopefully supported */ -#endif - }; - -static void -set_formats_from_array (MetaCrtc *crtc, - const uint32_t *formats, - size_t n_formats) -{ - MetaCrtcKms *crtc_kms = crtc->driver_private; - size_t i; - - for (i = 0; i < n_formats; i++) - { - g_hash_table_insert (crtc_kms->formats_modifiers, - GUINT_TO_POINTER (formats[i]), NULL); - } -} - -static void -parse_formats (MetaCrtc *crtc, - int kms_fd, - uint32_t blob_id) -{ - MetaCrtcKms *crtc_kms = crtc->driver_private; - drmModePropertyBlobPtr blob; - struct drm_format_modifier_blob *blob_fmt; - uint32_t *formats; - struct drm_format_modifier *modifiers; - unsigned int fmt_i, mod_i; - - g_return_if_fail (g_hash_table_size (crtc_kms->formats_modifiers) == 0); - - if (blob_id == 0) - return; - - blob = drmModeGetPropertyBlob (kms_fd, blob_id); - if (!blob) - return; - - if (blob->length < sizeof (struct drm_format_modifier_blob)) - { - drmModeFreePropertyBlob (blob); - return; - } - - blob_fmt = blob->data; - - formats = formats_ptr (blob_fmt); - modifiers = modifiers_ptr (blob_fmt); - - for (fmt_i = 0; fmt_i < blob_fmt->count_formats; fmt_i++) - { - GArray *mod_tmp = g_array_new (FALSE, FALSE, sizeof (uint64_t)); - - for (mod_i = 0; mod_i < blob_fmt->count_modifiers; mod_i++) - { - struct drm_format_modifier *modifier = &modifiers[mod_i]; - - /* The modifier advertisement blob is partitioned into groups of - * 64 formats. */ - if (fmt_i < modifier->offset || fmt_i > modifier->offset + 63) - continue; - - if (!(modifier->formats & (1 << (fmt_i - modifier->offset)))) - continue; - - g_array_append_val (mod_tmp, modifier->modifier); - } - - if (mod_tmp->len == 0) - { - free_modifier_array (mod_tmp); - mod_tmp = NULL; - } - - g_hash_table_insert (crtc_kms->formats_modifiers, - GUINT_TO_POINTER (formats[fmt_i]), mod_tmp); - } - - drmModeFreePropertyBlob (blob); + return meta_kms_plane_is_format_supported (crtc_kms->primary_plane, + drm_format); } static void @@ -384,28 +238,10 @@ parse_transforms (MetaCrtc *crtc, transform = META_MONITOR_TRANSFORM_270; if (transform != -1) - { - crtc_kms->all_hw_transforms |= 1 << transform; - crtc_kms->rotation_map[transform] = 1 << prop->enums[i].value; - } + crtc_kms->rotation_map[transform] = 1 << prop->enums[i].value; } } -static gboolean -is_primary_plane (MetaGpu *gpu, - drmModeObjectPropertiesPtr props) -{ - drmModePropertyPtr prop; - int idx; - - idx = find_property_index (gpu, props, "type", &prop); - if (idx < 0) - return FALSE; - - drmModeFreeProperty (prop); - return props->prop_values[idx] == DRM_PLANE_TYPE_PRIMARY; -} - static void init_crtc_rotations (MetaCrtc *crtc, MetaGpu *gpu) @@ -413,91 +249,35 @@ init_crtc_rotations (MetaCrtc *crtc, MetaCrtcKms *crtc_kms = crtc->driver_private; MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); int kms_fd; - drmModeObjectPropertiesPtr props; - drmModePlaneRes *planes; + uint32_t primary_plane_id; drmModePlane *drm_plane; - unsigned int i; - int crtc_idx; + drmModeObjectPropertiesPtr props; + drmModePropertyPtr prop; + int rotation_idx; kms_fd = meta_gpu_kms_get_fd (gpu_kms); + primary_plane_id = meta_kms_plane_get_id (crtc_kms->primary_plane); + drm_plane = drmModeGetPlane (kms_fd, primary_plane_id); + props = drmModeObjectGetProperties (kms_fd, + primary_plane_id, + DRM_MODE_OBJECT_PLANE); - planes = drmModeGetPlaneResources (kms_fd); - if (planes == NULL) - return; - - crtc_idx = meta_kms_crtc_get_idx (crtc_kms->kms_crtc); - for (i = 0; i < planes->count_planes; i++) + rotation_idx = find_property_index (gpu, props, + "rotation", &prop); + if (rotation_idx >= 0) { - drmModePropertyPtr prop; - - drm_plane = drmModeGetPlane (kms_fd, planes->planes[i]); - - if (!drm_plane) - continue; - - if ((drm_plane->possible_crtcs & (1 << crtc_idx))) - { - props = drmModeObjectGetProperties (kms_fd, - drm_plane->plane_id, - DRM_MODE_OBJECT_PLANE); - - if (props && is_primary_plane (gpu, props)) - { - int rotation_idx, fmts_idx; - - crtc_kms->primary_plane_id = drm_plane->plane_id; - rotation_idx = find_property_index (gpu, props, - "rotation", &prop); - if (rotation_idx >= 0) - { - crtc_kms->rotation_prop_id = props->props[rotation_idx]; - parse_transforms (crtc, prop); - drmModeFreeProperty (prop); - } - - fmts_idx = find_property_index (gpu, props, - "IN_FORMATS", &prop); - if (fmts_idx >= 0) - { - parse_formats (crtc, kms_fd, props->prop_values[fmts_idx]); - drmModeFreeProperty (prop); - } - - /* fall back to universal plane formats without modifiers */ - if (g_hash_table_size (crtc_kms->formats_modifiers) == 0) - { - set_formats_from_array (crtc, - drm_plane->formats, - drm_plane->count_formats); - } - } - - if (props) - drmModeFreeObjectProperties (props); - } - - drmModeFreePlane (drm_plane); + crtc_kms->rotation_prop_id = props->props[rotation_idx]; + parse_transforms (crtc, prop); + drmModeFreeProperty (prop); } - crtc->all_transforms |= crtc_kms->all_hw_transforms; - - drmModeFreePlaneResources (planes); - - /* final formats fallback to something hardcoded */ - if (g_hash_table_size (crtc_kms->formats_modifiers) == 0) - { - set_formats_from_array (crtc, - drm_default_formats, - G_N_ELEMENTS (drm_default_formats)); - } + drmModeFreeObjectProperties (props); + drmModeFreePlane (drm_plane); } static void meta_crtc_destroy_notify (MetaCrtc *crtc) { - MetaCrtcKms *crtc_kms = crtc->driver_private; - - g_hash_table_destroy (crtc_kms->formats_modifiers); g_free (crtc->driver_private); } @@ -506,10 +286,15 @@ meta_create_kms_crtc (MetaGpuKms *gpu_kms, MetaKmsCrtc *kms_crtc) { MetaGpu *gpu = META_GPU (gpu_kms); + MetaKmsDevice *kms_device; MetaCrtc *crtc; MetaCrtcKms *crtc_kms; + MetaKmsPlane *primary_plane; const MetaKmsCrtcState *crtc_state; + kms_device = meta_gpu_kms_get_kms_device (gpu_kms); + primary_plane = meta_kms_device_get_primary_plane_for (kms_device, + kms_crtc); crtc_state = meta_kms_crtc_get_current_state (kms_crtc); crtc = g_object_new (META_TYPE_CRTC, NULL); @@ -538,12 +323,7 @@ meta_create_kms_crtc (MetaGpuKms *gpu_kms, crtc_kms = g_new0 (MetaCrtcKms, 1); crtc_kms->kms_crtc = kms_crtc; - - crtc_kms->formats_modifiers = - g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) free_modifier_array); + crtc_kms->primary_plane = primary_plane; crtc->driver_private = crtc_kms; crtc->driver_notify = (GDestroyNotify) meta_crtc_destroy_notify; diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c index 93f98430b..8822251fa 100644 --- a/src/backends/native/meta-gpu-kms.c +++ b/src/backends/native/meta-gpu-kms.c @@ -381,6 +381,12 @@ meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms, return TRUE; } +MetaKmsDevice * +meta_gpu_kms_get_kms_device (MetaGpuKms *gpu_kms) +{ + return gpu_kms->kms_device; +} + int meta_gpu_kms_get_fd (MetaGpuKms *gpu_kms) { diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h index b03596076..42715004f 100644 --- a/src/backends/native/meta-gpu-kms.h +++ b/src/backends/native/meta-gpu-kms.h @@ -64,6 +64,8 @@ gboolean meta_gpu_kms_flip_crtc (MetaGpuKms *gpu_kms, gboolean meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms, GError **error); +MetaKmsDevice * meta_gpu_kms_get_kms_device (MetaGpuKms *gpu_kms); + int meta_gpu_kms_get_fd (MetaGpuKms *gpu_kms); uint32_t meta_gpu_kms_get_id (MetaGpuKms *gpu_kms); diff --git a/src/backends/native/meta-kms-device.c b/src/backends/native/meta-kms-device.c index 6f5caf02d..a4dd0006d 100644 --- a/src/backends/native/meta-kms-device.c +++ b/src/backends/native/meta-kms-device.c @@ -25,6 +25,7 @@ #include "backends/native/meta-backend-native.h" #include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-impl.h" +#include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-private.h" struct _MetaKmsDevice @@ -81,6 +82,32 @@ meta_kms_device_get_crtcs (MetaKmsDevice *device) return device->crtcs; } +static GList * +meta_kms_device_get_planes (MetaKmsDevice *device) +{ + return device->planes; +} + +MetaKmsPlane * +meta_kms_device_get_primary_plane_for (MetaKmsDevice *device, + MetaKmsCrtc *crtc) +{ + GList *l; + + for (l = meta_kms_device_get_planes (device); l; l = l->next) + { + MetaKmsPlane *plane = l->data; + + if (meta_kms_plane_get_plane_type (plane) != META_KMS_PLANE_TYPE_PRIMARY) + continue; + + if (meta_kms_plane_is_usable_with (plane, crtc)) + return plane; + } + + return NULL; +} + typedef struct _CreateImplDeviceData { MetaKmsDevice *device; diff --git a/src/backends/native/meta-kms-device.h b/src/backends/native/meta-kms-device.h index 77037f237..c4ec034c3 100644 --- a/src/backends/native/meta-kms-device.h +++ b/src/backends/native/meta-kms-device.h @@ -39,6 +39,9 @@ GList * meta_kms_device_get_connectors (MetaKmsDevice *device); GList * meta_kms_device_get_crtcs (MetaKmsDevice *device); +MetaKmsPlane * meta_kms_device_get_primary_plane_for (MetaKmsDevice *device, + MetaKmsCrtc *crtc); + MetaKmsDevice * meta_kms_device_new (MetaKms *kms, const char *path, MetaKmsDeviceFlag flags, diff --git a/src/backends/native/meta-kms-plane.c b/src/backends/native/meta-kms-plane.c index d4073d672..94a407bd8 100644 --- a/src/backends/native/meta-kms-plane.c +++ b/src/backends/native/meta-kms-plane.c @@ -41,11 +41,24 @@ struct _MetaKmsPlane uint32_t rotation_map[META_MONITOR_N_TRANSFORMS]; uint32_t all_hw_transforms; + /* + * primary plane's supported formats and maybe modifiers + * key: GUINT_TO_POINTER (format) + * value: owned GArray* (uint64_t modifier), or NULL + */ + GHashTable *formats_modifiers; + MetaKmsDevice *device; }; G_DEFINE_TYPE (MetaKmsPlane, meta_kms_plane, G_TYPE_OBJECT) +uint32_t +meta_kms_plane_get_id (MetaKmsPlane *plane) +{ + return plane->id; +} + MetaKmsPlaneType meta_kms_plane_get_plane_type (MetaKmsPlane *plane) { @@ -75,10 +88,49 @@ meta_kms_plane_is_transform_handled (MetaKmsPlane *plane, */ return FALSE; } - return plane->all_hw_transforms & (1 << transform); } +GArray * +meta_kms_plane_get_modifiers_for_format (MetaKmsPlane *plane, + uint32_t format) +{ + return g_hash_table_lookup (plane->formats_modifiers, + GUINT_TO_POINTER (format)); +} + +GArray * +meta_kms_plane_copy_drm_format_list (MetaKmsPlane *plane) +{ + GArray *formats; + GHashTableIter it; + gpointer key; + unsigned int n_formats_modifiers; + + n_formats_modifiers = g_hash_table_size (plane->formats_modifiers); + formats = g_array_sized_new (FALSE, FALSE, + sizeof (uint32_t), + n_formats_modifiers); + g_hash_table_iter_init (&it, plane->formats_modifiers); + while (g_hash_table_iter_next (&it, &key, NULL)) + { + uint32_t drm_format = GPOINTER_TO_UINT (key); + + g_array_append_val (formats, drm_format); + } + + return formats; +} + +gboolean +meta_kms_plane_is_format_supported (MetaKmsPlane *plane, + uint32_t drm_format) +{ + return g_hash_table_lookup_extended (plane->formats_modifiers, + GUINT_TO_POINTER (drm_format), + NULL, NULL); +} + gboolean meta_kms_plane_is_usable_with (MetaKmsPlane *plane, MetaKmsCrtc *crtc) @@ -132,6 +184,122 @@ init_rotations (MetaKmsPlane *plane, } } +static inline uint32_t * +drm_formats_ptr (struct drm_format_modifier_blob *blob) +{ + return (uint32_t *) (((char *) blob) + blob->formats_offset); +} + +static inline struct drm_format_modifier * +drm_modifiers_ptr (struct drm_format_modifier_blob *blob) +{ + return (struct drm_format_modifier *) (((char *) blob) + + blob->modifiers_offset); +} + +static void +free_modifier_array (GArray *array) +{ + if (!array) + return; + + g_array_free (array, TRUE); +} + +static void +parse_formats (MetaKmsPlane *plane, + MetaKmsImplDevice *impl_device, + uint32_t blob_id) +{ + int fd; + drmModePropertyBlobPtr blob; + struct drm_format_modifier_blob *blob_fmt; + uint32_t *formats; + struct drm_format_modifier *drm_modifiers; + unsigned int fmt_i, mod_i; + + g_return_if_fail (g_hash_table_size (plane->formats_modifiers) == 0); + + if (blob_id == 0) + return; + + fd = meta_kms_impl_device_get_fd (impl_device); + blob = drmModeGetPropertyBlob (fd, blob_id); + if (!blob) + return; + + if (blob->length < sizeof (struct drm_format_modifier_blob)) + { + drmModeFreePropertyBlob (blob); + return; + } + + blob_fmt = blob->data; + + formats = drm_formats_ptr (blob_fmt); + drm_modifiers = drm_modifiers_ptr (blob_fmt); + + for (fmt_i = 0; fmt_i < blob_fmt->count_formats; fmt_i++) + { + GArray *modifiers = g_array_new (FALSE, FALSE, sizeof (uint64_t)); + + for (mod_i = 0; mod_i < blob_fmt->count_modifiers; mod_i++) + { + struct drm_format_modifier *drm_modifier = &drm_modifiers[mod_i]; + + /* + * The modifier advertisement blob is partitioned into groups of + * 64 formats. + */ + if (fmt_i < drm_modifier->offset || fmt_i > drm_modifier->offset + 63) + continue; + + if (!(drm_modifier->formats & (1 << (fmt_i - drm_modifier->offset)))) + continue; + + g_array_append_val (modifiers, drm_modifier->modifier); + } + + if (modifiers->len == 0) + { + free_modifier_array (modifiers); + modifiers = NULL; + } + + g_hash_table_insert (plane->formats_modifiers, + GUINT_TO_POINTER (formats[fmt_i]), + modifiers); + } + + drmModeFreePropertyBlob (blob); +} + +static void +init_formats (MetaKmsPlane *plane, + MetaKmsImplDevice *impl_device, + drmModeObjectProperties *drm_plane_props) +{ + drmModePropertyPtr prop; + int idx; + + plane->formats_modifiers = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) free_modifier_array); + + prop = meta_kms_impl_device_find_property (impl_device, drm_plane_props, + "IN_FORMATS", &idx); + if (prop) + { + uint32_t blob_id; + + blob_id = drm_plane_props->prop_values[idx]; + parse_formats (plane, impl_device, blob_id); + drmModeFreeProperty (prop); + } +} + MetaKmsPlane * meta_kms_plane_new (MetaKmsPlaneType type, MetaKmsImplDevice *impl_device, @@ -147,10 +315,21 @@ meta_kms_plane_new (MetaKmsPlaneType type, plane->device = meta_kms_impl_device_get_device (impl_device); init_rotations (plane, impl_device, drm_plane_props); + init_formats (plane, impl_device, drm_plane_props); return plane; } +static void +meta_kms_plane_finalize (GObject *object) +{ + MetaKmsPlane *plane = META_KMS_PLANE (object); + + g_hash_table_destroy (plane->formats_modifiers); + + G_OBJECT_CLASS (meta_kms_plane_parent_class)->finalize (object); +} + static void meta_kms_plane_init (MetaKmsPlane *plane) { @@ -159,4 +338,7 @@ meta_kms_plane_init (MetaKmsPlane *plane) static void meta_kms_plane_class_init (MetaKmsPlaneClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_kms_plane_finalize; } diff --git a/src/backends/native/meta-kms-plane.h b/src/backends/native/meta-kms-plane.h index 76020d8d9..11cb3ad2f 100644 --- a/src/backends/native/meta-kms-plane.h +++ b/src/backends/native/meta-kms-plane.h @@ -43,11 +43,21 @@ MetaKmsPlane * meta_kms_plane_new (MetaKmsPlaneType type, drmModePlane *drm_plane, drmModeObjectProperties *drm_plane_props); +uint32_t meta_kms_plane_get_id (MetaKmsPlane *plane); + MetaKmsPlaneType meta_kms_plane_get_plane_type (MetaKmsPlane *plane); gboolean meta_kms_plane_is_transform_handled (MetaKmsPlane *plane, MetaMonitorTransform transform); +GArray * meta_kms_plane_get_modifiers_for_format (MetaKmsPlane *plane, + uint32_t format); + +GArray * meta_kms_plane_copy_drm_format_list (MetaKmsPlane *plane); + +gboolean meta_kms_plane_is_format_supported (MetaKmsPlane *plane, + uint32_t format); + gboolean meta_kms_plane_is_usable_with (MetaKmsPlane *plane, MetaKmsCrtc *crtc); diff --git a/src/backends/native/meta-kms-types.h b/src/backends/native/meta-kms-types.h index d8d099626..7a9264934 100644 --- a/src/backends/native/meta-kms-types.h +++ b/src/backends/native/meta-kms-types.h @@ -23,6 +23,7 @@ typedef struct _MetaKms MetaKms; typedef struct _MetaKmsDevice MetaKmsDevice; +typedef struct _MetaKmsPlane MetaKmsPlane; typedef struct _MetaKmsCrtc MetaKmsCrtc; typedef struct _MetaKmsConnector MetaKmsConnector;