summaryrefslogtreecommitdiff
path: root/src/backends/native/meta-kms-connector.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backends/native/meta-kms-connector.c')
-rw-r--r--src/backends/native/meta-kms-connector.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/src/backends/native/meta-kms-connector.c b/src/backends/native/meta-kms-connector.c
index 98b9a2f1b..bcd97e1c3 100644
--- a/src/backends/native/meta-kms-connector.c
+++ b/src/backends/native/meta-kms-connector.c
@@ -24,6 +24,7 @@
#include <errno.h>
+#include "backends/native/meta-kms-device-private.h"
#include "backends/native/meta-kms-impl-device.h"
struct _MetaKmsConnector
@@ -35,6 +36,8 @@ struct _MetaKmsConnector
uint32_t id;
MetaConnectorType type;
char *name;
+
+ MetaKmsConnectorState *current_state;
};
G_DEFINE_TYPE (MetaKmsConnector, meta_kms_connector, G_TYPE_OBJECT)
@@ -63,6 +66,377 @@ meta_kms_connector_get_name (MetaKmsConnector *connector)
return connector->name;
}
+gboolean
+meta_kms_connector_can_clone (MetaKmsConnector *connector,
+ MetaKmsConnector *other_connector)
+{
+ MetaKmsConnectorState *state = connector->current_state;
+ MetaKmsConnectorState *other_state = other_connector->current_state;
+
+ if (state->common_possible_clones == 0 ||
+ other_state->common_possible_clones == 0)
+ return FALSE;
+
+ if (state->encoder_device_idxs != other_state->encoder_device_idxs)
+ return FALSE;
+
+ return TRUE;
+}
+
+const MetaKmsConnectorState *
+meta_kms_connector_get_current_state (MetaKmsConnector *connector)
+{
+ return connector->current_state;
+}
+
+static void
+set_panel_orientation (MetaKmsConnectorState *state,
+ drmModePropertyPtr prop,
+ uint64_t orientation)
+{
+ const char *name;
+
+ name = prop->enums[orientation].name;
+ if (strcmp (name, "Upside Down") == 0)
+ {
+ state->panel_orientation_transform = META_MONITOR_TRANSFORM_180;
+ }
+ else if (strcmp (name, "Left Side Up") == 0)
+ {
+ /* Left side up, rotate 90 degrees counter clockwise to correct */
+ state->panel_orientation_transform = META_MONITOR_TRANSFORM_90;
+ }
+ else if (strcmp (name, "Right Side Up") == 0)
+ {
+ /* Right side up, rotate 270 degrees counter clockwise to correct */
+ state->panel_orientation_transform = META_MONITOR_TRANSFORM_270;
+ }
+ else
+ {
+ state->panel_orientation_transform = META_MONITOR_TRANSFORM_NORMAL;
+ }
+}
+
+static void
+state_set_properties (MetaKmsConnectorState *state,
+ MetaKmsImplDevice *impl_device,
+ drmModeConnector *drm_connector)
+{
+ int fd;
+ int i;
+
+ fd = meta_kms_impl_device_get_fd (impl_device);
+
+ for (i = 0; i < drm_connector->count_props; i++)
+ {
+ drmModePropertyPtr prop;
+
+ prop = drmModeGetProperty (fd, drm_connector->props[i]);
+ if (!prop)
+ continue;
+
+ if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+ strcmp (prop->name, "suggested X") == 0)
+ state->suggested_x = drm_connector->prop_values[i];
+ else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+ strcmp (prop->name, "suggested Y") == 0)
+ state->suggested_y = drm_connector->prop_values[i];
+ else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+ strcmp (prop->name, "hotplug_mode_update") == 0)
+ state->hotplug_mode_update = drm_connector->prop_values[i];
+ else if (strcmp (prop->name, "scaling mode") == 0)
+ state->has_scaling = TRUE;
+ else if ((prop->flags & DRM_MODE_PROP_ENUM) &&
+ strcmp (prop->name, "panel orientation") == 0)
+ set_panel_orientation (state, prop, drm_connector->prop_values[i]);
+
+ drmModeFreeProperty (prop);
+ }
+}
+
+static CoglSubpixelOrder
+drm_subpixel_order_to_cogl_subpixel_order (drmModeSubPixel subpixel)
+{
+ switch (subpixel)
+ {
+ case DRM_MODE_SUBPIXEL_NONE:
+ return COGL_SUBPIXEL_ORDER_NONE;
+ break;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
+ return COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
+ break;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
+ return COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
+ break;
+ case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
+ return COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
+ break;
+ case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
+ return COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
+ break;
+ case DRM_MODE_SUBPIXEL_UNKNOWN:
+ return COGL_SUBPIXEL_ORDER_UNKNOWN;
+ }
+ return COGL_SUBPIXEL_ORDER_UNKNOWN;
+}
+
+static void
+state_set_edid (MetaKmsConnectorState *state,
+ MetaKmsConnector *connector,
+ MetaKmsImplDevice *impl_device,
+ uint32_t blob_id)
+{
+ int fd;
+ drmModePropertyBlobPtr edid_blob;
+ GBytes *edid_data;
+
+ fd = meta_kms_impl_device_get_fd (impl_device);
+ edid_blob = drmModeGetPropertyBlob (fd, blob_id);
+ if (!edid_blob)
+ {
+ g_warning ("Failed to read EDID of connector %s: %s",
+ connector->name, g_strerror (errno));
+ return;
+ }
+
+ edid_data = g_bytes_new (edid_blob->data, edid_blob->length);
+ drmModeFreePropertyBlob (edid_blob);
+
+ state->edid_data = edid_data;
+}
+
+static void
+state_set_tile_info (MetaKmsConnectorState *state,
+ MetaKmsConnector *connector,
+ MetaKmsImplDevice *impl_device,
+ uint32_t blob_id)
+{
+ int fd;
+ drmModePropertyBlobPtr tile_blob;
+
+ state->tile_info = (MetaTileInfo) { 0 };
+
+ fd = meta_kms_impl_device_get_fd (impl_device);
+ tile_blob = drmModeGetPropertyBlob (fd, blob_id);
+ if (!tile_blob)
+ {
+ g_warning ("Failed to read TILE of connector %s: %s",
+ connector->name, strerror (errno));
+ return;
+ }
+
+ if (tile_blob->length > 0)
+ {
+ if (sscanf ((char *) tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &state->tile_info.group_id,
+ &state->tile_info.flags,
+ &state->tile_info.max_h_tiles,
+ &state->tile_info.max_v_tiles,
+ &state->tile_info.loc_h_tile,
+ &state->tile_info.loc_v_tile,
+ &state->tile_info.tile_w,
+ &state->tile_info.tile_h) != 8)
+ {
+ g_warning ("Couldn't understand TILE property blob of connector %s",
+ connector->name);
+ state->tile_info = (MetaTileInfo) { 0 };
+ }
+ }
+
+ drmModeFreePropertyBlob (tile_blob);
+}
+
+static void
+state_set_blobs (MetaKmsConnectorState *state,
+ MetaKmsConnector *connector,
+ MetaKmsImplDevice *impl_device,
+ drmModeConnector *drm_connector)
+{
+ int fd;
+ int i;
+
+ fd = meta_kms_impl_device_get_fd (impl_device);
+
+ for (i = 0; i < drm_connector->count_props; i++)
+ {
+ drmModePropertyPtr prop;
+
+ prop = drmModeGetProperty (fd, drm_connector->props[i]);
+ if (!prop)
+ continue;
+
+ if (prop->flags & DRM_MODE_PROP_BLOB)
+ {
+ uint32_t blob_id;
+
+ blob_id = drm_connector->prop_values[i];
+
+ if (blob_id)
+ {
+ if (strcmp (prop->name, "EDID") == 0)
+ state_set_edid (state, connector, impl_device, blob_id);
+ else if (strcmp (prop->name, "TILE") == 0)
+ state_set_tile_info (state, connector, impl_device, blob_id);
+ }
+ }
+
+ drmModeFreeProperty (prop);
+ }
+}
+
+static void
+state_set_physical_dimensions (MetaKmsConnectorState *state,
+ drmModeConnector *drm_connector)
+{
+ state->width_mm = drm_connector->mmWidth;
+ state->height_mm = drm_connector->mmHeight;
+}
+
+static void
+state_set_modes (MetaKmsConnectorState *state,
+ drmModeConnector *drm_connector)
+{
+ state->modes =
+ g_memdup (drm_connector->modes,
+ drm_connector->count_modes * sizeof (drmModeModeInfo));
+ state->n_modes = drm_connector->count_modes;
+}
+
+static void
+set_encoder_device_idx_bit (uint32_t *encoder_device_idxs,
+ uint32_t encoder_id,
+ MetaKmsImplDevice *impl_device,
+ drmModeRes *drm_resources)
+{
+ int fd;
+ int i;
+
+ fd = meta_kms_impl_device_get_fd (impl_device);
+
+ for (i = 0; i < drm_resources->count_encoders; i++)
+ {
+ drmModeEncoder *drm_encoder;
+
+ drm_encoder = drmModeGetEncoder (fd, drm_resources->encoders[i]);
+ if (!drm_encoder)
+ continue;
+
+ if (drm_encoder->encoder_id == encoder_id)
+ {
+ *encoder_device_idxs |= (1 << i);
+ break;
+ }
+ }
+}
+
+static void
+state_set_crtc_state (MetaKmsConnectorState *state,
+ drmModeConnector *drm_connector,
+ MetaKmsImplDevice *impl_device,
+ drmModeRes *drm_resources)
+{
+ int fd;
+ int i;
+ uint32_t common_possible_crtcs;
+ uint32_t common_possible_clones;
+ uint32_t encoder_device_idxs;
+
+ fd = meta_kms_impl_device_get_fd (impl_device);
+
+ common_possible_crtcs = UINT32_MAX;
+ common_possible_clones = UINT32_MAX;
+ encoder_device_idxs = 0;
+ for (i = 0; i < drm_connector->count_encoders; i++)
+ {
+ drmModeEncoder *drm_encoder;
+
+ drm_encoder = drmModeGetEncoder (fd, drm_connector->encoders[i]);
+ if (!drm_encoder)
+ continue;
+
+ common_possible_crtcs &= drm_encoder->possible_crtcs;
+ common_possible_clones &= drm_encoder->possible_clones;
+
+ set_encoder_device_idx_bit (&encoder_device_idxs,
+ drm_encoder->encoder_id,
+ impl_device,
+ drm_resources);
+
+ if (drm_connector->encoder_id == drm_encoder->encoder_id)
+ state->current_crtc_id = drm_encoder->crtc_id;
+ }
+
+ state->common_possible_crtcs = common_possible_crtcs;
+ state->common_possible_clones = common_possible_clones;
+ state->encoder_device_idxs = encoder_device_idxs;
+}
+
+static MetaKmsConnectorState *
+meta_kms_connector_state_new (void)
+{
+ MetaKmsConnectorState *state;
+
+ state = g_new0 (MetaKmsConnectorState, 1);
+ state->suggested_x = -1;
+ state->suggested_y = -1;
+
+ return state;
+}
+
+static void
+meta_kms_connector_state_free (MetaKmsConnectorState *state)
+{
+ g_clear_pointer (&state->edid_data, g_bytes_unref);
+ g_free (state->modes);
+ g_free (state);
+}
+
+static void
+meta_kms_connector_read_state (MetaKmsConnector *connector,
+ MetaKmsImplDevice *impl_device,
+ drmModeConnector *drm_connector,
+ drmModeRes *drm_resources)
+{
+ MetaKmsConnectorState *state;
+
+ g_clear_pointer (&connector->current_state, meta_kms_connector_state_free);
+
+ if (drm_connector->connection != DRM_MODE_CONNECTED)
+ return;
+
+ state = meta_kms_connector_state_new ();
+
+ state_set_blobs (state, connector, impl_device, drm_connector);
+
+ state_set_properties (state, impl_device, drm_connector);
+
+ state->subpixel_order =
+ drm_subpixel_order_to_cogl_subpixel_order (drm_connector->subpixel);
+
+ state_set_physical_dimensions (state, drm_connector);
+
+ state_set_modes (state, drm_connector);
+
+ state_set_crtc_state (state, drm_connector, impl_device, drm_resources);
+
+ connector->current_state = state;
+}
+
+void
+meta_kms_connector_update_state (MetaKmsConnector *connector,
+ drmModeRes *drm_resources)
+{
+ MetaKmsImplDevice *impl_device;
+ drmModeConnector *drm_connector;
+
+ impl_device = meta_kms_device_get_impl_device (connector->device);
+ drm_connector = drmModeGetConnector (meta_kms_impl_device_get_fd (impl_device),
+ connector->id);
+ meta_kms_connector_read_state (connector, impl_device,
+ drm_connector,
+ drm_resources);
+}
+
static char *
make_connector_name (drmModeConnector *drm_connector)
{
@@ -109,6 +483,10 @@ meta_kms_connector_new (MetaKmsImplDevice *impl_device,
connector->type = (MetaConnectorType) drm_connector->connector_type;
connector->name = make_connector_name (drm_connector);
+ meta_kms_connector_read_state (connector, impl_device,
+ drm_connector,
+ drm_resources);
+
return connector;
}
@@ -117,6 +495,7 @@ meta_kms_connector_finalize (GObject *object)
{
MetaKmsConnector *connector = META_KMS_CONNECTOR (object);
+ g_clear_pointer (&connector->current_state, meta_kms_connector_state_free);
g_free (connector->name);
G_OBJECT_CLASS (meta_kms_connector_parent_class)->finalize (object);