[mutter] backends/native: Move output code to its own file



commit 7ea01693a79964259e5f3b8678f412a0ac6ab066
Author: Jonas Ådahl <jadahl gmail com>
Date:   Tue Jul 4 12:11:44 2017 +0800

    backends/native: Move output code to its own file
    
    Move code dealing with MetaOutputKms and related functionality to its
    own file. Eventually, MetaOutputKms should become a GObject based on
    MetaOutput, and this commit is in preparation for that.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=785381

 src/Makefile.am                                |    2 +
 src/backends/native/gen-default-modes.py       |    2 +-
 src/backends/native/meta-default-modes.h       |    2 +-
 src/backends/native/meta-monitor-manager-kms.c |  619 +++---------------------
 src/backends/native/meta-monitor-manager-kms.h |   17 +
 src/backends/native/meta-output-kms.c          |  616 +++++++++++++++++++++++
 src/backends/native/meta-output-kms.h          |   41 ++
 7 files changed, 741 insertions(+), 558 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 45d4e7d..c985784 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -457,6 +457,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES +=             \
        backends/native/meta-monitor-manager-kms.h      \
        backends/native/meta-launcher.c                 \
        backends/native/meta-launcher.h                 \
+       backends/native/meta-output-kms.c               \
+       backends/native/meta-output-kms.h               \
        backends/native/meta-renderer-native.c          \
        backends/native/meta-renderer-native.h          \
        backends/native/meta-stage-native.c             \
diff --git a/src/backends/native/gen-default-modes.py b/src/backends/native/gen-default-modes.py
index aa731ee..75c4e24 100644
--- a/src/backends/native/gen-default-modes.py
+++ b/src/backends/native/gen-default-modes.py
@@ -51,7 +51,7 @@ common_resolutions = [
 
 output_lines = [
     "/* Generated by gen-default-modes.py */\n",
-    "const drmModeModeInfo meta_default_drm_mode_infos[] = {",
+    "static const drmModeModeInfo meta_default_drm_mode_infos[] = {",
 ]
 
 def sync_flags(hsync, vsync):
diff --git a/src/backends/native/meta-default-modes.h b/src/backends/native/meta-default-modes.h
index 155bf9f..b111204 100644
--- a/src/backends/native/meta-default-modes.h
+++ b/src/backends/native/meta-default-modes.h
@@ -1,6 +1,6 @@
 /* Generated by gen-default-modes.py */
 
-const drmModeModeInfo meta_default_drm_mode_infos[] = {
+static const drmModeModeInfo meta_default_drm_mode_infos[] = {
 { 38250, 800, 832, 912, 1024, 0, 600, 603, 607, 624, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, 
DRM_MODE_TYPE_DEFAULT, "800x600_60.00" },
 { 63500, 1024, 1072, 1176, 1328, 0, 768, 771, 775, 798, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, 
DRM_MODE_TYPE_DEFAULT, "1024x768_60.00" },
 { 81750, 1152, 1216, 1336, 1520, 0, 864, 867, 871, 897, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, 
DRM_MODE_TYPE_DEFAULT, "1152x864_60.00" },
diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
index bc4df6a..8b0f85d 100644
--- a/src/backends/native/meta-monitor-manager-kms.c
+++ b/src/backends/native/meta-monitor-manager-kms.c
@@ -29,6 +29,7 @@
 #include "meta-output.h"
 #include "meta-backend-private.h"
 #include "meta-renderer-native.h"
+#include "meta-output-kms.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -39,8 +40,6 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
-#include <xf86drm.h>
-#include <xf86drmMode.h>
 
 #include <meta/main.h>
 #include <meta/errors.h>
@@ -51,33 +50,6 @@
 
 #define ALL_TRANSFORMS (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)
 #define ALL_TRANSFORMS_MASK ((1 << ALL_TRANSFORMS) - 1)
-#define SYNC_TOLERANCE 0.01    /* 1 percent */
-
-typedef struct
-{
-  drmModeConnector *connector;
-
-  unsigned n_encoders;
-  drmModeEncoderPtr *encoders;
-  drmModeEncoderPtr  current_encoder;
-
-  /*
-   * Bitmasks of encoder position in the resources array (used during clone
-   * setup).
-   */
-  uint32_t encoder_mask;
-  uint32_t enc_clone_mask;
-
-  uint32_t dpms_prop_id;
-  uint32_t edid_blob_id;
-  uint32_t tile_blob_id;
-
-  int suggested_x;
-  int suggested_y;
-  uint32_t hotplug_mode_update;
-
-  gboolean has_scaling;
-} MetaOutputKms;
 
 typedef struct
 {
@@ -124,6 +96,12 @@ struct _MetaMonitorManagerKmsClass
 
 G_DEFINE_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META_TYPE_MONITOR_MANAGER);
 
+int
+meta_monitor_manager_kms_get_fd (MetaMonitorManagerKms *manager_kms)
+{
+  return manager_kms->fd;
+}
+
 static void
 free_resources (MetaMonitorManagerKms *manager_kms)
 {
@@ -144,54 +122,6 @@ compare_outputs (gconstpointer one,
   return strcmp (o_one->name, o_two->name);
 }
 
-static char *
-make_output_name (drmModeConnector *connector)
-{
-  static const char * const connector_type_names[] = {
-    "None",
-    "VGA",
-    "DVI-I",
-    "DVI-D",
-    "DVI-A",
-    "Composite",
-    "SVIDEO",
-    "LVDS",
-    "Component",
-    "DIN",
-    "DP",
-    "HDMI",
-    "HDMI-B",
-    "TV",
-    "eDP",
-    "Virtual",
-    "DSI",
-  };
-
-  if (connector->connector_type < G_N_ELEMENTS (connector_type_names))
-    return g_strdup_printf ("%s-%d",
-                            connector_type_names[connector->connector_type],
-                            connector->connector_type_id);
-  else
-    return g_strdup_printf ("Unknown%d-%d",
-                            connector->connector_type,
-                            connector->connector_type_id);
-}
-
-static void
-meta_output_destroy_notify (MetaOutput *output)
-{
-  MetaOutputKms *output_kms;
-  unsigned i;
-
-  output_kms = output->driver_private;
-
-  for (i = 0; i < output_kms->n_encoders; i++)
-    drmModeFreeEncoder (output_kms->encoders[i]);
-  g_free (output_kms->encoders);
-
-  g_slice_free (MetaOutputKms, output_kms);
-}
-
 static void
 meta_monitor_mode_destroy_notify (MetaCrtcMode *mode)
 {
@@ -248,44 +178,6 @@ drm_mode_hash (gconstpointer ptr)
 }
 
 static void
-find_connector_properties (MetaMonitorManagerKms *manager_kms,
-                           MetaOutputKms         *output_kms)
-{
-  int i;
-
-  output_kms->hotplug_mode_update = 0;
-  output_kms->suggested_x = -1;
-  output_kms->suggested_y = -1;
-  for (i = 0; i < output_kms->connector->count_props; i++)
-    {
-      drmModePropertyPtr prop = drmModeGetProperty (manager_kms->fd, output_kms->connector->props[i]);
-      if (!prop)
-        continue;
-
-      if ((prop->flags & DRM_MODE_PROP_ENUM) && strcmp (prop->name, "DPMS") == 0)
-        output_kms->dpms_prop_id = prop->prop_id;
-      else if ((prop->flags & DRM_MODE_PROP_BLOB) && strcmp (prop->name, "EDID") == 0)
-        output_kms->edid_blob_id = output_kms->connector->prop_values[i];
-      else if ((prop->flags & DRM_MODE_PROP_BLOB) &&
-               strcmp (prop->name, "TILE") == 0)
-        output_kms->tile_blob_id = output_kms->connector->prop_values[i];
-      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
-               strcmp (prop->name, "suggested X") == 0)
-        output_kms->suggested_x = output_kms->connector->prop_values[i];
-      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
-               strcmp (prop->name, "suggested Y") == 0)
-        output_kms->suggested_y = output_kms->connector->prop_values[i];
-      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
-               strcmp (prop->name, "hotplug_mode_update") == 0)
-        output_kms->hotplug_mode_update = output_kms->connector->prop_values[i];
-      else if (strcmp (prop->name, "scaling mode") == 0)
-        output_kms->has_scaling = TRUE;
-
-      drmModeFreeProperty (prop);
-    }
-}
-
-static void
 find_crtc_properties (MetaMonitorManagerKms *manager_kms,
                       MetaCrtc *meta_crtc)
 {
@@ -316,99 +208,11 @@ find_crtc_properties (MetaMonitorManagerKms *manager_kms,
     }
 }
 
-static drmModePropertyBlobPtr
-read_edid_blob (MetaMonitorManagerKms *manager_kms,
-                uint32_t               edid_blob_id,
-                GError               **error)
-{
-  drmModePropertyBlobPtr edid_blob = NULL;
-
-  edid_blob = drmModeGetPropertyBlob (manager_kms->fd, edid_blob_id);
-  if (!edid_blob)
-    {
-      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
-                   "%s", strerror (errno));
-      return NULL;
-    }
-
-  return edid_blob;
-}
-
-static GBytes *
-read_output_edid (MetaMonitorManagerKms *manager_kms,
-                  MetaOutput            *output,
-                  GError               **error)
-{
-  MetaOutputKms *output_kms = output->driver_private;
-  drmModePropertyBlobPtr edid_blob;
-
-  g_assert (output_kms->edid_blob_id != 0);
-
-  edid_blob = read_edid_blob (manager_kms, output_kms->edid_blob_id, error);
-  if (!edid_blob)
-    return NULL;
-
-  if (edid_blob->length == 0)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EDID blob was empty");
-      drmModeFreePropertyBlob (edid_blob);
-      return NULL;
-    }
-
-  return g_bytes_new_with_free_func (edid_blob->data, edid_blob->length,
-                                     (GDestroyNotify) drmModeFreePropertyBlob,
-                                     edid_blob);
-}
-
-static gboolean
-output_get_tile_info (MetaMonitorManagerKms *manager_kms,
-                      MetaOutput            *output)
-{
-  MetaOutputKms *output_kms = output->driver_private;
-  drmModePropertyBlobPtr tile_blob = NULL;
-  int ret;
-
-  if (output_kms->tile_blob_id == 0)
-    return FALSE;
-
-  tile_blob = drmModeGetPropertyBlob (manager_kms->fd, output_kms->tile_blob_id);
-  if (!tile_blob)
-    {
-      meta_warning ("Failed to read TILE of output %s: %s\n", output->name, strerror(errno));
-      return FALSE;
-    }
-
-  if (tile_blob->length > 0)
-    {
-      ret = sscanf ((char *)tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d",
-                    &output->tile_info.group_id,
-                    &output->tile_info.flags,
-                    &output->tile_info.max_h_tiles,
-                    &output->tile_info.max_v_tiles,
-                    &output->tile_info.loc_h_tile,
-                    &output->tile_info.loc_v_tile,
-                    &output->tile_info.tile_w,
-                    &output->tile_info.tile_h);
-      drmModeFreePropertyBlob (tile_blob);
-
-      if (ret != 8)
-        {
-          meta_warning ("Couldn't understand output tile property blob\n");
-          return FALSE;
-        }
-      return TRUE;
-    }
-  else
-    {
-      drmModeFreePropertyBlob (tile_blob);
-      return FALSE;
-    }
-}
-
-static MetaCrtcMode *
-mode_from_drm_mode (MetaMonitorManager    *manager,
-                    const drmModeModeInfo *drm_mode)
+MetaCrtcMode *
+meta_monitor_manager_kms_get_mode_from_drm_mode (MetaMonitorManagerKms *manager_kms,
+                                                 const drmModeModeInfo *drm_mode)
 {
+  MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
   GList *l;
 
   for (l = manager->modes; l; l = l->next)
@@ -423,8 +227,8 @@ mode_from_drm_mode (MetaMonitorManager    *manager,
   return NULL;
 }
 
-static float
-drm_mode_vrefresh (const drmModeModeInfo *mode)
+float
+meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *mode)
 {
   float refresh = 0.0;
 
@@ -453,30 +257,13 @@ create_mode (const drmModeModeInfo *drm_mode,
   mode->width = drm_mode->hdisplay;
   mode->height = drm_mode->vdisplay;
   mode->flags = drm_mode->flags;
-  mode->refresh_rate = drm_mode_vrefresh (drm_mode);
+  mode->refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
   mode->driver_private = g_slice_dup (drmModeModeInfo, drm_mode);
-  mode->driver_notify = (GDestroyNotify)meta_monitor_mode_destroy_notify;
+  mode->driver_notify = (GDestroyNotify) meta_monitor_mode_destroy_notify;
 
   return mode;
 }
 
-static int
-compare_modes (const void *one,
-               const void *two)
-{
-  MetaCrtcMode *a = *(MetaCrtcMode **) one;
-  MetaCrtcMode *b = *(MetaCrtcMode **) two;
-
-  if (a->width != b->width)
-    return a->width > b->width ? -1 : 1;
-  if (a->height != b->height)
-    return a->height > b->height ? -1 : 1;
-  if (a->refresh_rate != b->refresh_rate)
-    return a->refresh_rate > b->refresh_rate ? -1 : 1;
-
-  return g_strcmp0 (b->name, a->name);
-}
-
 static MetaOutput *
 find_output_by_id (GList *outputs,
                    glong  id)
@@ -627,49 +414,6 @@ init_crtc_rotations (MetaMonitorManager *manager,
   drmModeFreePlaneResources (planes);
 }
 
-static void
-add_common_modes (MetaMonitorManager *manager,
-                  MetaOutput         *output)
-{
-  const drmModeModeInfo *drm_mode;
-  GPtrArray *array;
-  unsigned i;
-  unsigned max_hdisplay = 0;
-  unsigned max_vdisplay = 0;
-  float max_vrefresh = 0.0;
-
-  for (i = 0; i < output->n_modes; i++)
-    {
-      drm_mode = output->modes[i]->driver_private;
-      max_hdisplay = MAX (max_hdisplay, drm_mode->hdisplay);
-      max_vdisplay = MAX (max_vdisplay, drm_mode->vdisplay);
-      max_vrefresh = MAX (max_vrefresh, drm_mode_vrefresh (drm_mode));
-    }
-
-  max_vrefresh = MAX (max_vrefresh, 60.0);
-  max_vrefresh *= (1 + SYNC_TOLERANCE);
-
-  array = g_ptr_array_new ();
-  for (i = 0; i < G_N_ELEMENTS (meta_default_drm_mode_infos); i++)
-    {
-      drm_mode = &meta_default_drm_mode_infos[i];
-      if (drm_mode->hdisplay > max_hdisplay ||
-          drm_mode->vdisplay > max_vdisplay ||
-          drm_mode_vrefresh (drm_mode) > max_vrefresh)
-        continue;
-
-      g_ptr_array_add (array, mode_from_drm_mode (manager, drm_mode));
-    }
-
-  output->modes = g_renew (MetaCrtcMode *, output->modes,
-                           output->n_modes + array->len);
-  memcpy (output->modes + output->n_modes, array->pdata,
-          array->len * sizeof (MetaCrtcMode *));
-  output->n_modes += array->len;
-
-  g_ptr_array_free (array, TRUE);
-}
-
 static MetaCrtc *
 create_crtc (MetaMonitorManager *manager,
              drmModeCrtc        *drm_crtc)
@@ -710,268 +454,31 @@ create_crtc (MetaMonitorManager *manager,
   return crtc;
 }
 
-static MetaOutput *
-create_output (MetaMonitorManager *manager,
-               drmModeConnector   *connector,
-               MetaOutput         *old_output)
-{
-  MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
-  MetaOutput *output;
-  MetaOutputKms *output_kms;
-  GArray *crtcs;
-  GBytes *edid;
-  GList *l;
-  unsigned int i;
-  unsigned int crtc_mask;
-
-  output = g_object_new (META_TYPE_OUTPUT, NULL);
-
-  output_kms = g_slice_new0 (MetaOutputKms);
-  output->driver_private = output_kms;
-  output->driver_notify = (GDestroyNotify)meta_output_destroy_notify;
-
-  output->monitor_manager = manager;
-  output->winsys_id = connector->connector_id;
-  output->name = make_output_name (connector);
-  output->width_mm = connector->mmWidth;
-  output->height_mm = connector->mmHeight;
-
-  switch (connector->subpixel)
-    {
-    case DRM_MODE_SUBPIXEL_NONE:
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
-      break;
-    case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
-      break;
-    case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
-      break;
-    case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
-      break;
-    case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
-      break;
-    case DRM_MODE_SUBPIXEL_UNKNOWN:
-    default:
-      output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
-      break;
-    }
-
-  output->preferred_mode = NULL;
-  output->n_modes = connector->count_modes;
-  output->modes = g_new0 (MetaCrtcMode *, output->n_modes);
-  for (i = 0; i < output->n_modes; i++) {
-      output->modes[i] = mode_from_drm_mode (manager, &connector->modes[i]);
-      if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED)
-        output->preferred_mode = output->modes[i];
-  }
-
-  if (!output->preferred_mode)
-    output->preferred_mode = output->modes[0];
-
-  output_kms->connector = connector;
-  find_connector_properties (manager_kms, output_kms);
-
-  /* FIXME: MSC feature bit? */
-  /* Presume that if the output supports scaling, then we have
-   * a panel fitter capable of adjusting any mode to suit.
-   */
-  if (output_kms->has_scaling)
-    add_common_modes (manager, output);
-
-  qsort (output->modes, output->n_modes, sizeof (MetaCrtcMode *), compare_modes);
-
-  output_kms->n_encoders = connector->count_encoders;
-  output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders);
-
-  crtc_mask = ~(unsigned int) 0;
-  for (i = 0; i < output_kms->n_encoders; i++)
-    {
-      output_kms->encoders[i] = drmModeGetEncoder (manager_kms->fd,
-                                                   connector->encoders[i]);
-      if (!output_kms->encoders[i])
-        continue;
-
-      /* We only list CRTCs as supported if they are supported by all encoders
-         for this connectors.
-
-         This is what xf86-video-modesetting does (see drmmode_output_init())
-         */
-      crtc_mask &= output_kms->encoders[i]->possible_crtcs;
-
-      if (output_kms->encoders[i]->encoder_id == connector->encoder_id)
-        output_kms->current_encoder = output_kms->encoders[i];
-    }
-
-  crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc*));
-
-  for (l = manager->crtcs, i = 0; l; l = l->next, i++)
-    {
-      if (crtc_mask & (1 << i))
-        {
-          MetaCrtc *crtc = l->data;
-
-          g_array_append_val (crtcs, crtc);
-        }
-    }
-
-  output->n_possible_crtcs = crtcs->len;
-  output->possible_crtcs = (void*)g_array_free (crtcs, FALSE);
-
-  if (output_kms->current_encoder && output_kms->current_encoder->crtc_id != 0)
-    {
-      for (l = manager->crtcs; l; l = l->next)
-        {
-          MetaCrtc *crtc = l->data;
-
-          if (crtc->crtc_id == output_kms->current_encoder->crtc_id)
-            {
-              output->crtc = crtc;
-              break;
-            }
-        }
-    }
-  else
-    {
-      output->crtc = NULL;
-    }
-
-  if (old_output)
-    {
-      output->is_primary = old_output->is_primary;
-      output->is_presentation = old_output->is_presentation;
-    }
-  else
-    {
-      output->is_primary = FALSE;
-      output->is_presentation = FALSE;
-    }
-
-  output->suggested_x = output_kms->suggested_x;
-  output->suggested_y = output_kms->suggested_y;
-  output->hotplug_mode_update = output_kms->hotplug_mode_update;
-
-  if (output_kms->edid_blob_id != 0)
-    {
-      GError *error = NULL;
-
-      edid = read_output_edid (manager_kms, output, &error);
-      if (!edid)
-        {
-          g_warning ("Failed to read EDID blob from %s: %s",
-                     output->name, error->message);
-          g_error_free (error);
-        }
-    }
-  else
-    {
-      edid = NULL;
-    }
-
-  meta_output_parse_edid (output, edid);
-  g_bytes_unref (edid);
-
-  /* MetaConnectorType matches DRM's connector types */
-  output->connector_type = (MetaConnectorType) connector->connector_type;
-
-  output_get_tile_info (manager_kms, output);
-
-  /* FIXME: backlight is a very driver specific thing unfortunately,
-     every DDX does its own thing, and the dumb KMS API does not include it.
-
-     For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight
-     (one for each major HW maker, and then some).
-     We can't do the same because we're not root.
-     It might be best to leave backlight out of the story and rely on the setuid
-     helper in gnome-settings-daemon.
-     */
-  output->backlight_min = 0;
-  output->backlight_max = 0;
-  output->backlight = -1;
-
-  return output;
-}
-
 static void
-detect_and_setup_output_clones (MetaMonitorManager *manager,
-                                drmModeRes         *resources)
+setup_output_clones (MetaMonitorManager *manager)
 {
-  MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
-  drmModeEncoder **encoders;
-  unsigned int i, n_encoders;
   GList *l;
 
-  n_encoders = (unsigned int) resources->count_encoders;
-  encoders = g_new (drmModeEncoder *, n_encoders);
-  for (i = 0; i < n_encoders; i++)
-    encoders[i] = drmModeGetEncoder (manager_kms->fd, resources->encoders[i]);
-
-  /*
-   * Setup encoder position mask and encoder clone mask.
-   */
-  for (l = manager->outputs; l; l = l->next)
-    {
-      MetaOutput *output = l->data;
-      MetaOutputKms *output_kms = output->driver_private;
-      unsigned int j;
-
-      output_kms->enc_clone_mask = 0xff;
-      output_kms->encoder_mask = 0;
-
-      for (j = 0; j < output_kms->n_encoders; j++)
-        {
-          unsigned int k;
-
-          for (k = 0; k < n_encoders; k++)
-            {
-              if (output_kms->encoders[j] && encoders[k] &&
-                  output_kms->encoders[j]->encoder_id == encoders[k]->encoder_id)
-                {
-                  output_kms->encoder_mask |= (1 << k);
-                  break;
-                }
-            }
-
-          output_kms->enc_clone_mask &= output_kms->encoders[j]->possible_clones;
-        }
-    }
-
-  for (i = 0; i < (unsigned)resources->count_encoders; i++)
-    drmModeFreeEncoder (encoders[i]);
-  g_free (encoders);
-
-  /*
-   * Setup MetaOutput <-> MetaOutput clone associations.
-   */
   for (l = manager->outputs; l; l = l->next)
     {
       MetaOutput *output = l->data;
-      MetaOutputKms *output_kms = output->driver_private;
       GList *k;
 
-      if (output_kms->enc_clone_mask == 0)
-        continue;
-
       for (k = manager->outputs; k; k = k->next)
         {
-          MetaOutput *clone = k->data;
-          MetaOutputKms *clone_kms = clone->driver_private;
-
-          if (clone == output)
-            continue;
+          MetaOutput *other_output = k->data;
 
-          if (clone_kms->encoder_mask == 0)
+          if (other_output == output)
             continue;
 
-          if (clone_kms->encoder_mask == output_kms->enc_clone_mask)
+          if (meta_output_kms_can_clone (output, other_output))
             {
               output->n_possible_clones++;
               output->possible_clones = g_renew (MetaOutput *,
                                                  output->possible_clones,
                                                  output->n_possible_clones);
-              output->possible_clones[output->n_possible_clones - 1] = clone;
+              output->possible_clones[output->n_possible_clones - 1] =
+                other_output;
             }
         }
     }
@@ -1080,7 +587,7 @@ init_crtcs (MetaMonitorManager *manager,
 
 static void
 init_outputs (MetaMonitorManager *manager,
-              drmModeRes         *resources)
+              MetaKmsResources   *resources)
 {
   MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
   GList *old_outputs;
@@ -1102,7 +609,7 @@ init_outputs (MetaMonitorManager *manager,
           MetaOutput *old_output;
 
           old_output = find_output_by_id (old_outputs, connector->connector_id);
-          output = create_output (manager, connector, old_output);
+          output = meta_create_kms_output (manager, connector, resources, old_output);
           manager->outputs = g_list_prepend (manager->outputs, output);
         }
     }
@@ -1111,19 +618,47 @@ init_outputs (MetaMonitorManager *manager,
   /* Sort the outputs for easier handling in MetaMonitorConfig */
   manager->outputs = g_list_sort (manager->outputs, compare_outputs);
 
-  detect_and_setup_output_clones (manager, resources);
+  setup_output_clones (manager);
+}
+
+static void
+meta_kms_resources_init (MetaKmsResources *resources,
+                         int               fd)
+{
+  drmModeRes *drm_resources;
+  unsigned int i;
+
+  drm_resources = drmModeGetResources (fd);
+  resources->resources = drm_resources;
+
+  resources->n_encoders = (unsigned int) drm_resources->count_encoders;
+  resources->encoders = g_new (drmModeEncoder *, resources->n_encoders);
+  for (i = 0; i < resources->n_encoders; i++)
+    resources->encoders[i] = drmModeGetEncoder (fd, drm_resources->encoders[i]);
+}
+
+static void
+meta_kms_resources_release (MetaKmsResources *resources)
+{
+  unsigned int i;
+
+  for (i = 0; i < resources->n_encoders; i++)
+    drmModeFreeEncoder (resources->encoders[i]);
+  g_free (resources->encoders);
+
+  drmModeFreeResources (resources->resources);
 }
 
 static void
 meta_monitor_manager_kms_read_current (MetaMonitorManager *manager)
 {
   MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
-  drmModeRes *resources;
+  MetaKmsResources resources;
 
-  resources = drmModeGetResources (manager_kms->fd);
+  meta_kms_resources_init (&resources, manager_kms->fd);
 
-  manager_kms->max_buffer_width = resources->max_width;
-  manager_kms->max_buffer_height = resources->max_height;
+  manager_kms->max_buffer_width = resources.resources->max_width;
+  manager_kms->max_buffer_height = resources.resources->max_height;
 
   manager->power_save_mode = META_POWER_SAVE_ON;
 
@@ -1133,43 +668,25 @@ meta_monitor_manager_kms_read_current (MetaMonitorManager *manager)
      are freed by the platform-independent layer. */
   free_resources (manager_kms);
 
-  init_connectors (manager, resources);
-  init_modes (manager, resources);
-  init_crtcs (manager, resources);
-  init_outputs (manager, resources);
+  init_connectors (manager, resources.resources);
+  init_modes (manager, resources.resources);
+  init_crtcs (manager, resources.resources);
+  init_outputs (manager, &resources);
 
-  drmModeFreeResources (resources);
+  meta_kms_resources_release (&resources);
 }
 
 static GBytes *
 meta_monitor_manager_kms_read_edid (MetaMonitorManager *manager,
                                     MetaOutput         *output)
 {
-  MetaOutputKms *output_kms = output->driver_private;
-  MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
-  GError *error = NULL;
-  GBytes *edid;
-
-  if (output_kms->edid_blob_id == 0)
-    return NULL;
-
-  edid = read_output_edid (manager_kms, output, &error);
-  if (!edid)
-    {
-      g_warning ("Failed to read EDID from '%s': %s",
-                 output->name, error->message);
-      g_error_free (error);
-      return NULL;
-    }
-
-  return edid;
+  return meta_output_kms_read_edid (output);
 }
 
 static void
 meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
                                               MetaPowerSave       mode)
 {
-  MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
   uint64_t state;
   GList *l;
 
@@ -1193,18 +710,8 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
   for (l = manager->outputs; l; l = l->next)
     {
       MetaOutput *output = l->data;
-      MetaOutputKms *output_kms = output->driver_private;
 
-      if (output_kms->dpms_prop_id != 0)
-        {
-          int ok = drmModeObjectSetProperty (manager_kms->fd, output->winsys_id,
-                                             DRM_MODE_OBJECT_CONNECTOR,
-                                             output_kms->dpms_prop_id, state);
-
-          if (ok < 0)
-            meta_warning ("Failed to set power save mode for output %s: %s\n",
-                          output->name, strerror (errno));
-        }
+      meta_output_kms_set_power_save_mode (output, state);
     }
 }
 
diff --git a/src/backends/native/meta-monitor-manager-kms.h b/src/backends/native/meta-monitor-manager-kms.h
index 37eabb3..24d7c4e 100644
--- a/src/backends/native/meta-monitor-manager-kms.h
+++ b/src/backends/native/meta-monitor-manager-kms.h
@@ -23,6 +23,9 @@
 #ifndef META_MONITOR_MANAGER_KMS_H
 #define META_MONITOR_MANAGER_KMS_H
 
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
 #include "meta-monitor-manager-private.h"
 
 #define META_TYPE_MONITOR_MANAGER_KMS (meta_monitor_manager_kms_get_type ())
@@ -30,8 +33,20 @@ G_DECLARE_FINAL_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms,
                       META, MONITOR_MANAGER_KMS,
                       MetaMonitorManager)
 
+typedef struct _MetaKmsResources
+{
+  drmModeRes *resources;
+  drmModeEncoder **encoders;
+  unsigned int n_encoders;
+} MetaKmsResources;
+
 typedef void (*MetaKmsFlipCallback) (void *user_data);
 
+int meta_monitor_manager_kms_get_fd (MetaMonitorManagerKms *manager_kms);
+
+MetaCrtcMode * meta_monitor_manager_kms_get_mode_from_drm_mode (MetaMonitorManagerKms *manager_kms,
+                                                                const drmModeModeInfo *drm_mode);
+
 gboolean meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms,
                                                    MetaCrtc              *crtc,
                                                    int                    x,
@@ -55,4 +70,6 @@ void meta_monitor_manager_kms_pause (MetaMonitorManagerKms *manager_kms);
 
 void meta_monitor_manager_kms_resume (MetaMonitorManagerKms *manager_kms);
 
+float meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *mode);
+
 #endif /* META_MONITOR_MANAGER_KMS_H */
diff --git a/src/backends/native/meta-output-kms.c b/src/backends/native/meta-output-kms.c
new file mode 100644
index 0000000..6181c8c
--- /dev/null
+++ b/src/backends/native/meta-output-kms.c
@@ -0,0 +1,616 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2013-2017 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-output-kms.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backends/meta-crtc.h"
+#include "backends/native/meta-default-modes.h"
+#include "backends/native/meta-monitor-manager-kms.h"
+
+#define SYNC_TOLERANCE 0.01    /* 1 percent */
+
+typedef struct _MetaOutputKms
+{
+  MetaOutput parent;
+
+  drmModeConnector *connector;
+
+  unsigned int n_encoders;
+  drmModeEncoderPtr *encoders;
+  drmModeEncoderPtr current_encoder;
+
+  /*
+   * Bitmasks of encoder position in the resources array (used during clone
+   * setup).
+   */
+  uint32_t encoder_mask;
+  uint32_t enc_clone_mask;
+
+  uint32_t dpms_prop_id;
+  uint32_t edid_blob_id;
+  uint32_t tile_blob_id;
+
+  int suggested_x;
+  int suggested_y;
+  uint32_t hotplug_mode_update;
+
+  gboolean has_scaling;
+} MetaOutputKms;
+
+void
+meta_output_kms_set_power_save_mode (MetaOutput *output,
+                                     uint64_t    state)
+{
+  MetaOutputKms *output_kms = output->driver_private;
+  MetaMonitorManager *monitor_manager =
+    meta_output_get_monitor_manager (output);
+  MetaMonitorManagerKms *monitor_manager_kms =
+    META_MONITOR_MANAGER_KMS (monitor_manager);
+
+  if (output_kms->dpms_prop_id != 0)
+    {
+      int fd;
+
+      fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms);
+      if (drmModeObjectSetProperty (fd, output->winsys_id,
+                                    DRM_MODE_OBJECT_CONNECTOR,
+                                    output_kms->dpms_prop_id, state) < 0)
+        g_warning ("Failed to set power save mode for output %s: %s",
+                   output->name, strerror (errno));
+    }
+}
+
+gboolean
+meta_output_kms_can_clone (MetaOutput *output,
+                           MetaOutput *other_output)
+{
+  MetaOutputKms *output_kms = output->driver_private;
+  MetaOutputKms *other_output_kms = other_output->driver_private;
+
+  if (output_kms->enc_clone_mask == 0 ||
+      other_output_kms->enc_clone_mask == 0)
+    return FALSE;
+
+  if (output_kms->encoder_mask != other_output_kms->enc_clone_mask)
+    return FALSE;
+
+  return TRUE;
+}
+
+static drmModePropertyBlobPtr
+read_edid_blob (MetaMonitorManagerKms *monitor_manager_kms,
+                uint32_t               edid_blob_id,
+                GError               **error)
+{
+  int fd;
+  drmModePropertyBlobPtr edid_blob = NULL;
+
+  fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms);
+  edid_blob = drmModeGetPropertyBlob (fd, edid_blob_id);
+  if (!edid_blob)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+                   "%s", strerror (errno));
+      return NULL;
+    }
+
+  return edid_blob;
+}
+
+static GBytes *
+read_output_edid (MetaMonitorManagerKms *monitor_manager_kms,
+                  MetaOutput            *output,
+                  GError               **error)
+{
+  MetaOutputKms *output_kms = output->driver_private;
+  drmModePropertyBlobPtr edid_blob;
+
+  g_assert (output_kms->edid_blob_id != 0);
+
+  edid_blob = read_edid_blob (monitor_manager_kms, output_kms->edid_blob_id, error);
+  if (!edid_blob)
+    return NULL;
+
+  if (edid_blob->length == 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EDID blob was empty");
+      drmModeFreePropertyBlob (edid_blob);
+      return NULL;
+    }
+
+  return g_bytes_new_with_free_func (edid_blob->data, edid_blob->length,
+                                     (GDestroyNotify) drmModeFreePropertyBlob,
+                                     edid_blob);
+}
+
+static gboolean
+output_get_tile_info (MetaMonitorManagerKms *monitor_manager_kms,
+                      MetaOutput            *output)
+{
+  MetaOutputKms *output_kms = output->driver_private;
+  int fd;
+  drmModePropertyBlobPtr tile_blob = NULL;
+
+  if (output_kms->tile_blob_id == 0)
+    return FALSE;
+
+  fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms);
+  tile_blob = drmModeGetPropertyBlob (fd, output_kms->tile_blob_id);
+  if (!tile_blob)
+    {
+      g_warning ("Failed to read TILE of output %s: %s",
+                 output->name, strerror (errno));
+      return FALSE;
+    }
+
+  if (tile_blob->length > 0)
+    {
+      int ret;
+
+      ret = sscanf ((char *)tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d",
+                    &output->tile_info.group_id,
+                    &output->tile_info.flags,
+                    &output->tile_info.max_h_tiles,
+                    &output->tile_info.max_v_tiles,
+                    &output->tile_info.loc_h_tile,
+                    &output->tile_info.loc_v_tile,
+                    &output->tile_info.tile_w,
+                    &output->tile_info.tile_h);
+      drmModeFreePropertyBlob (tile_blob);
+
+      if (ret != 8)
+        {
+          g_warning ("Couldn't understand output tile property blob");
+          return FALSE;
+        }
+      return TRUE;
+    }
+  else
+    {
+      drmModeFreePropertyBlob (tile_blob);
+      return FALSE;
+    }
+}
+
+GBytes *
+meta_output_kms_read_edid (MetaOutput *output)
+{
+  MetaOutputKms *output_kms = output->driver_private;
+  MetaMonitorManager *monitor_manager =
+    meta_output_get_monitor_manager (output);
+  MetaMonitorManagerKms *monitor_manager_kms =
+    META_MONITOR_MANAGER_KMS (monitor_manager);
+  GError *error = NULL;
+  GBytes *edid;
+
+  if (output_kms->edid_blob_id == 0)
+    return NULL;
+
+  edid = read_output_edid (monitor_manager_kms, output, &error);
+  if (!edid)
+    {
+      g_warning ("Failed to read EDID from '%s': %s",
+                 output->name, error->message);
+      g_error_free (error);
+      return NULL;
+    }
+
+  return edid;
+}
+
+static void
+find_connector_properties (MetaMonitorManagerKms *monitor_manager_kms,
+                           MetaOutputKms         *output_kms)
+{
+  drmModeConnector *connector = output_kms->connector;
+  int fd;
+  int i;
+
+  fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms);
+
+  output_kms->hotplug_mode_update = 0;
+  output_kms->suggested_x = -1;
+  output_kms->suggested_y = -1;
+
+  for (i = 0; i < connector->count_props; i++)
+    {
+      drmModePropertyPtr prop = drmModeGetProperty (fd, connector->props[i]);
+      if (!prop)
+        continue;
+
+      if ((prop->flags & DRM_MODE_PROP_ENUM) &&
+          strcmp (prop->name, "DPMS") == 0)
+        output_kms->dpms_prop_id = prop->prop_id;
+      else if ((prop->flags & DRM_MODE_PROP_BLOB) &&
+               strcmp (prop->name, "EDID") == 0)
+        output_kms->edid_blob_id = connector->prop_values[i];
+      else if ((prop->flags & DRM_MODE_PROP_BLOB) &&
+               strcmp (prop->name, "TILE") == 0)
+        output_kms->tile_blob_id = connector->prop_values[i];
+      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+               strcmp (prop->name, "suggested X") == 0)
+        output_kms->suggested_x = connector->prop_values[i];
+      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+               strcmp (prop->name, "suggested Y") == 0)
+        output_kms->suggested_y = connector->prop_values[i];
+      else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
+               strcmp (prop->name, "hotplug_mode_update") == 0)
+        output_kms->hotplug_mode_update = connector->prop_values[i];
+      else if (strcmp (prop->name, "scaling mode") == 0)
+        output_kms->has_scaling = TRUE;
+
+      drmModeFreeProperty (prop);
+    }
+}
+
+static char *
+make_output_name (drmModeConnector *connector)
+{
+  static const char * const connector_type_names[] = {
+    "None",
+    "VGA",
+    "DVI-I",
+    "DVI-D",
+    "DVI-A",
+    "Composite",
+    "SVIDEO",
+    "LVDS",
+    "Component",
+    "DIN",
+    "DP",
+    "HDMI",
+    "HDMI-B",
+    "TV",
+    "eDP",
+    "Virtual",
+    "DSI",
+  };
+
+  if (connector->connector_type < G_N_ELEMENTS (connector_type_names))
+    return g_strdup_printf ("%s-%d",
+                            connector_type_names[connector->connector_type],
+                            connector->connector_type_id);
+  else
+    return g_strdup_printf ("Unknown%d-%d",
+                            connector->connector_type,
+                            connector->connector_type_id);
+}
+
+static void
+meta_output_destroy_notify (MetaOutput *output)
+{
+  MetaOutputKms *output_kms;
+  unsigned i;
+
+  output_kms = output->driver_private;
+
+  for (i = 0; i < output_kms->n_encoders; i++)
+    drmModeFreeEncoder (output_kms->encoders[i]);
+  g_free (output_kms->encoders);
+
+  g_slice_free (MetaOutputKms, output_kms);
+}
+
+static void
+add_common_modes (MetaOutput            *output,
+                  MetaMonitorManagerKms *monitor_manager_kms)
+{
+  GPtrArray *array;
+  unsigned i;
+  unsigned max_hdisplay = 0;
+  unsigned max_vdisplay = 0;
+  float max_refresh_rate = 0.0;
+
+  for (i = 0; i < output->n_modes; i++)
+    {
+      const drmModeModeInfo *drm_mode;
+      float refresh_rate;
+
+      drm_mode = output->modes[i]->driver_private;
+      refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
+      max_hdisplay = MAX (max_hdisplay, drm_mode->hdisplay);
+      max_vdisplay = MAX (max_vdisplay, drm_mode->vdisplay);
+      max_refresh_rate = MAX (max_refresh_rate, refresh_rate);
+    }
+
+  max_refresh_rate = MAX (max_refresh_rate, 60.0);
+  max_refresh_rate *= (1 + SYNC_TOLERANCE);
+
+  array = g_ptr_array_new ();
+  for (i = 0; i < G_N_ELEMENTS (meta_default_drm_mode_infos); i++)
+    {
+      const drmModeModeInfo *drm_mode;
+      float refresh_rate;
+      MetaCrtcMode *crtc_mode;
+
+      drm_mode = &meta_default_drm_mode_infos[i];
+      refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
+      if (drm_mode->hdisplay > max_hdisplay ||
+          drm_mode->vdisplay > max_vdisplay ||
+          refresh_rate > max_refresh_rate)
+        continue;
+
+      crtc_mode = meta_monitor_manager_kms_get_mode_from_drm_mode (monitor_manager_kms,
+                                                                   drm_mode);
+      g_ptr_array_add (array, crtc_mode);
+    }
+
+  output->modes = g_renew (MetaCrtcMode *, output->modes,
+                           output->n_modes + array->len);
+  memcpy (output->modes + output->n_modes, array->pdata,
+          array->len * sizeof (MetaCrtcMode *));
+  output->n_modes += array->len;
+
+  g_ptr_array_free (array, TRUE);
+}
+
+static int
+compare_modes (const void *one,
+               const void *two)
+{
+  MetaCrtcMode *a = *(MetaCrtcMode **) one;
+  MetaCrtcMode *b = *(MetaCrtcMode **) two;
+
+  if (a->width != b->width)
+    return a->width > b->width ? -1 : 1;
+  if (a->height != b->height)
+    return a->height > b->height ? -1 : 1;
+  if (a->refresh_rate != b->refresh_rate)
+    return a->refresh_rate > b->refresh_rate ? -1 : 1;
+
+  return g_strcmp0 (b->name, a->name);
+}
+
+static void
+init_output_modes (MetaOutput            *output,
+                   MetaMonitorManagerKms *monitor_manager_kms)
+{
+  MetaOutputKms *output_kms = output->driver_private;
+  unsigned int i;
+
+  output->preferred_mode = NULL;
+  output->n_modes = output_kms->connector->count_modes;
+  output->modes = g_new0 (MetaCrtcMode *, output->n_modes);
+  for (i = 0; i < output->n_modes; i++)
+    {
+      drmModeModeInfo *drm_mode;
+      MetaCrtcMode *crtc_mode;
+
+      drm_mode = &output_kms->connector->modes[i];
+      crtc_mode =
+        meta_monitor_manager_kms_get_mode_from_drm_mode (monitor_manager_kms,
+                                                         drm_mode);
+      output->modes[i] = crtc_mode;
+      if (output_kms->connector->modes[i].type & DRM_MODE_TYPE_PREFERRED)
+        output->preferred_mode = output->modes[i];
+    }
+
+  if (!output->preferred_mode)
+    output->preferred_mode = output->modes[0];
+}
+
+MetaOutput *
+meta_create_kms_output (MetaMonitorManager *monitor_manager,
+                        drmModeConnector   *connector,
+                        MetaKmsResources   *resources,
+                        MetaOutput         *old_output)
+{
+  MetaMonitorManagerKms *monitor_manager_kms =
+    META_MONITOR_MANAGER_KMS (monitor_manager);
+  MetaOutput *output;
+  MetaOutputKms *output_kms;
+  GArray *crtcs;
+  GBytes *edid;
+  GList *l;
+  unsigned int i;
+  unsigned int crtc_mask;
+  int fd;
+
+  output = g_object_new (META_TYPE_OUTPUT, NULL);
+
+  output_kms = g_slice_new0 (MetaOutputKms);
+  output->driver_private = output_kms;
+  output->driver_notify = (GDestroyNotify) meta_output_destroy_notify;
+
+  output->monitor_manager = monitor_manager;
+  output->winsys_id = connector->connector_id;
+  output->name = make_output_name (connector);
+  output->width_mm = connector->mmWidth;
+  output->height_mm = connector->mmHeight;
+
+  switch (connector->subpixel)
+    {
+    case DRM_MODE_SUBPIXEL_NONE:
+      output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
+      break;
+    case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
+      output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
+      break;
+    case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
+      output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
+      break;
+    case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
+      output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
+      break;
+    case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
+      output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
+      break;
+    case DRM_MODE_SUBPIXEL_UNKNOWN:
+    default:
+      output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+      break;
+    }
+
+  output_kms->connector = connector;
+  find_connector_properties (monitor_manager_kms, output_kms);
+
+  init_output_modes (output, monitor_manager_kms);
+
+  /* FIXME: MSC feature bit? */
+  /* Presume that if the output supports scaling, then we have
+   * a panel fitter capable of adjusting any mode to suit.
+   */
+  if (output_kms->has_scaling)
+    add_common_modes (output, monitor_manager_kms);
+
+  qsort (output->modes, output->n_modes,
+         sizeof (MetaCrtcMode *), compare_modes);
+
+  output_kms->n_encoders = connector->count_encoders;
+  output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders);
+
+  fd = meta_monitor_manager_kms_get_fd (monitor_manager_kms);
+
+  crtc_mask = ~(unsigned int) 0;
+  for (i = 0; i < output_kms->n_encoders; i++)
+    {
+      output_kms->encoders[i] = drmModeGetEncoder (fd, connector->encoders[i]);
+      if (!output_kms->encoders[i])
+        continue;
+
+      /* We only list CRTCs as supported if they are supported by all encoders
+         for this connectors.
+
+         This is what xf86-video-modesetting does (see drmmode_output_init())
+         */
+      crtc_mask &= output_kms->encoders[i]->possible_crtcs;
+
+      if (output_kms->encoders[i]->encoder_id == connector->encoder_id)
+        output_kms->current_encoder = output_kms->encoders[i];
+    }
+
+  crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc*));
+
+  for (l = monitor_manager->crtcs, i = 0; l; l = l->next, i++)
+    {
+      if (crtc_mask & (1 << i))
+        {
+          MetaCrtc *crtc = l->data;
+
+          g_array_append_val (crtcs, crtc);
+        }
+    }
+
+  output->n_possible_crtcs = crtcs->len;
+  output->possible_crtcs = (void*)g_array_free (crtcs, FALSE);
+
+  if (output_kms->current_encoder && output_kms->current_encoder->crtc_id != 0)
+    {
+      for (l = monitor_manager->crtcs; l; l = l->next)
+        {
+          MetaCrtc *crtc = l->data;
+
+          if (crtc->crtc_id == output_kms->current_encoder->crtc_id)
+            {
+              output->crtc = crtc;
+              break;
+            }
+        }
+    }
+  else
+    {
+      output->crtc = NULL;
+    }
+
+  if (old_output)
+    {
+      output->is_primary = old_output->is_primary;
+      output->is_presentation = old_output->is_presentation;
+    }
+  else
+    {
+      output->is_primary = FALSE;
+      output->is_presentation = FALSE;
+    }
+
+  output->suggested_x = output_kms->suggested_x;
+  output->suggested_y = output_kms->suggested_y;
+  output->hotplug_mode_update = output_kms->hotplug_mode_update;
+
+  if (output_kms->edid_blob_id != 0)
+    {
+      GError *error = NULL;
+
+      edid = read_output_edid (monitor_manager_kms, output, &error);
+      if (!edid)
+        {
+          g_warning ("Failed to read EDID blob from %s: %s",
+                     output->name, error->message);
+          g_error_free (error);
+        }
+    }
+  else
+    {
+      edid = NULL;
+    }
+
+  meta_output_parse_edid (output, edid);
+  g_bytes_unref (edid);
+
+  /* MetaConnectorType matches DRM's connector types */
+  output->connector_type = (MetaConnectorType) connector->connector_type;
+
+  output_get_tile_info (monitor_manager_kms, output);
+
+  /* FIXME: backlight is a very driver specific thing unfortunately,
+     every DDX does its own thing, and the dumb KMS API does not include it.
+
+     For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight
+     (one for each major HW maker, and then some).
+     We can't do the same because we're not root.
+     It might be best to leave backlight out of the story and rely on the setuid
+     helper in gnome-settings-daemon.
+     */
+  output->backlight_min = 0;
+  output->backlight_max = 0;
+  output->backlight = -1;
+
+  output_kms->enc_clone_mask = 0xff;
+  output_kms->encoder_mask = 0;
+
+  for (i = 0; i < output_kms->n_encoders; i++)
+    {
+      drmModeEncoder *output_encoder = output_kms->encoders[i];
+      unsigned int j;
+
+      for (j = 0; j < resources->n_encoders; j++)
+        {
+          drmModeEncoder *encoder = resources->encoders[j];
+
+          if (output_encoder && encoder &&
+              output_encoder->encoder_id == encoder->encoder_id)
+            {
+              output_kms->encoder_mask |= (1 << j);
+              break;
+            }
+        }
+
+      output_kms->enc_clone_mask &= output_encoder->possible_clones;
+    }
+
+  return output;
+}
diff --git a/src/backends/native/meta-output-kms.h b/src/backends/native/meta-output-kms.h
new file mode 100644
index 0000000..61846a9
--- /dev/null
+++ b/src/backends/native/meta-output-kms.h
@@ -0,0 +1,41 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef META_OUTPUT_KMS_H
+#define META_OUTPUT_KMS_H
+
+#include "backends/meta-output.h"
+#include "backends/native/meta-monitor-manager-kms.h"
+
+void meta_output_kms_set_power_save_mode (MetaOutput *output,
+                                          uint64_t    state);
+
+gboolean meta_output_kms_can_clone (MetaOutput *output,
+                                    MetaOutput *other_output);
+
+GBytes * meta_output_kms_read_edid (MetaOutput *output);
+
+MetaOutput * meta_create_kms_output (MetaMonitorManager *monitor_manager,
+                                     drmModeConnector   *connector,
+                                     MetaKmsResources   *resources,
+                                     MetaOutput         *old_output);
+
+#endif /* META_OUTPUT_KMS_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]