diff --git a/cogl/cogl-kms-display.h b/cogl/cogl-kms-display.h
index d8bfd9ea6..cfa33ea0a 100644
--- a/cogl/cogl-kms-display.h
+++ b/cogl/cogl-kms-display.h
@@ -32,6 +32,8 @@
 #include <cogl/cogl-types.h>
 #include <cogl/cogl-display.h>
 
+#include <xf86drmMode.h>
+
 COGL_BEGIN_DECLS
 
 /**
@@ -54,5 +56,42 @@ COGL_BEGIN_DECLS
 void
 cogl_kms_display_queue_modes_reset (CoglDisplay *display);
 
+typedef struct {
+  uint32_t id;
+  uint32_t x, y;
+  drmModeModeInfo mode;
+
+  uint32_t *connectors;
+  uint32_t  count;
+} CoglKmsCrtc;
+
+/**
+ * cogl_kms_display_set_layout:
+ * @onscreen: a #CoglDisplay
+ * @width: the framebuffer width
+ * @height: the framebuffer height
+ * @crtcs: the array of #CoglKmsCrtc structure with the desired CRTC layout
+ *
+ * Configures @display to use a framebuffer sized @width x @height, covering
+ * the CRTCS in @crtcs.
+ * @width and @height must be within the driver framebuffer limits, and @crtcs
+ * must be valid KMS API IDs.
+ *
+ * Calling this function overrides the automatic mode setting done by Cogl,
+ * and for this reason must be called before the first call to cogl_onscreen_swap_buffers().
+ *
+ * If you want to restore the default behaviour, you can call this function
+ * with @width and @height set to -1.
+ *
+ * Stability: unstable
+ */
+CoglBool
+cogl_kms_display_set_layout (CoglDisplay *display,
+                             int width,
+                             int height,
+                             CoglKmsCrtc **crtcs,
+                             int n_crtcs,
+                             CoglError **error);
+
 COGL_END_DECLS
 #endif /* __COGL_KMS_DISPLAY_H__ */
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index 1c9cafb29..a6cf3c20a 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -79,9 +79,13 @@ typedef struct _CoglOutputKMS
 typedef struct _CoglDisplayKMS
 {
   GList *outputs;
+  GList *crtcs;
+
   int width, height;
   CoglBool pending_set_crtc;
   struct gbm_surface *dummy_gbm_surface;
+
+  CoglOnscreen *onscreen;
 } CoglDisplayKMS;
 
 typedef struct _CoglFlipKMS
@@ -393,13 +397,13 @@ find_mirror_modes (drmModeModeInfo *modes0,
 
 static drmModeModeInfo builtin_1024x768 =
 {
-	63500,			/* clock */
-	1024, 1072, 1176, 1328, 0,
-	768, 771, 775, 798, 0,
-	59920,
-	DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
-	0,
-	"1024x768"
+        63500,                        /* clock */
+        1024, 1072, 1176, 1328, 0,
+        768, 771, 775, 798, 0,
+        59920,
+        DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
+        0,
+        "1024x768"
 };
 
 static CoglBool
@@ -520,19 +524,71 @@ setup_crtc_modes (CoglDisplay *display, int fb_id)
   CoglRendererKMS *kms_renderer = egl_renderer->platform;
   GList *l;
 
-  for (l = kms_display->outputs; l; l = l->next)
+  for (l = kms_display->crtcs; l; l = l->next)
     {
-      CoglOutputKMS *output = l->data;
+      CoglKmsCrtc *crtc = l->data;
+
       int ret = drmModeSetCrtc (kms_renderer->fd,
-                                output->encoder->crtc_id,
-                                fb_id, 0, 0,
-                                &output->connector->connector_id, 1,
-                                &output->mode);
+                                crtc->id,
+                                fb_id, crtc->x, crtc->y,
+                                crtc->connectors, crtc->count,
+                                crtc->count ? &crtc->mode : NULL);
       if (ret)
-        g_warning ("Failed to set crtc mode %s: %m", output->mode.name);
+        g_warning ("Failed to set crtc mode %s: %m", crtc->mode.name);
     }
 }
 
+static void
+flip_all_crtcs (CoglDisplay *display, CoglFlipKMS *flip, int fb_id)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRendererEGL *egl_renderer = display->renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *l;
+
+  for (l = kms_display->crtcs; l; l = l->next)
+    {
+      CoglKmsCrtc *crtc = l->data;
+      int ret;
+
+      if (crtc->count == 0)
+        continue;
+
+      ret = drmModePageFlip (kms_renderer->fd,
+                             crtc->id, fb_id,
+                             DRM_MODE_PAGE_FLIP_EVENT, flip);
+
+      if (ret)
+        {
+          g_warning ("Failed to flip: %m");
+          continue;
+        }
+
+      flip->pending++;
+    }
+}
+
+static void
+crtc_free (CoglKmsCrtc *crtc)
+{
+  g_free (crtc->connectors);
+  g_slice_free (CoglKmsCrtc, crtc);
+}
+
+static CoglKmsCrtc *
+crtc_copy (CoglKmsCrtc *from)
+{
+  CoglKmsCrtc *new;
+
+  new = g_slice_new (CoglKmsCrtc);
+
+  *new = *from;
+  new->connectors = g_memdup (from->connectors, from->count * sizeof(uint32_t));
+
+  return new;
+}
+
 static CoglBool
 _cogl_winsys_egl_display_setup (CoglDisplay *display,
                                 CoglError **error)
@@ -544,6 +600,7 @@ _cogl_winsys_egl_display_setup (CoglDisplay *display,
   drmModeRes *resources;
   CoglOutputKMS *output0, *output1;
   CoglBool mirror;
+  CoglKmsCrtc *crtc0, *crtc1;
 
   kms_display = g_slice_new0 (CoglDisplayKMS);
   egl_display->platform = kms_display;
@@ -598,7 +655,33 @@ _cogl_winsys_egl_display_setup (CoglDisplay *display,
         }
     }
   else
-    output0->mode = output0->modes[0];
+    {
+      output0->mode = output0->modes[0];
+      output1 = NULL;
+    }
+
+  crtc0 = g_slice_new (CoglKmsCrtc);
+  crtc0->id = output0->encoder->crtc_id;
+  crtc0->x = 0;
+  crtc0->y = 0;
+  crtc0->mode = output0->mode;
+  crtc0->connectors = g_new (uint32_t, 1);
+  crtc0->connectors[0] = output0->connector->connector_id;
+  crtc0->count = 1;
+  kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc0);
+
+  if (output1)
+    {
+      crtc1 = g_slice_new (CoglKmsCrtc);
+      crtc1->id = output1->encoder->crtc_id;
+      crtc1->x = 0;
+      crtc1->y = 0;
+      crtc1->mode = output1->mode;
+      crtc1->connectors = g_new (uint32_t, 1);
+      crtc1->connectors[0] = output1->connector->connector_id;
+      crtc1->count = 1;
+      kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc1);
+    }
 
   kms_display->width = output0->mode.hdisplay;
   kms_display->height = output0->mode.vdisplay;
@@ -654,6 +737,8 @@ _cogl_winsys_egl_display_destroy (CoglDisplay *display)
   g_list_free (kms_display->outputs);
   kms_display->outputs = NULL;
 
+  g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free);
+
   g_slice_free (CoglDisplayKMS, egl_display->platform);
 }
 
@@ -742,7 +827,6 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
   CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
   uint32_t handle, stride;
   CoglFlipKMS *flip;
-  GList *l;
 
   /* If we already have a pending swap then block until it completes */
   while (kms_onscreen->next_fb_id != 0)
@@ -781,7 +865,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
     }
 
   /* If this is the first framebuffer to be presented then we now setup the
-   * crtc modes... */
+   * crtc modes, else we flip from the previous buffer */
   if (kms_display->pending_set_crtc)
     {
       setup_crtc_modes (context->display, kms_onscreen->next_fb_id);
@@ -791,22 +875,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
   flip = g_slice_new0 (CoglFlipKMS);
   flip->onscreen = onscreen;
 
-  for (l = kms_display->outputs; l; l = l->next)
-    {
-      CoglOutputKMS *output = l->data;
-
-      if (drmModePageFlip (kms_renderer->fd,
-                           output->encoder->crtc_id,
-                           kms_onscreen->next_fb_id,
-                           DRM_MODE_PAGE_FLIP_EVENT,
-                           flip))
-        {
-          g_warning ("Failed to flip: %m");
-          continue;
-        }
-
-      flip->pending++;
-    }
+  flip_all_crtcs (context->display, flip, kms_onscreen->next_fb_id);
 
   if (flip->pending == 0)
     {
@@ -859,6 +928,16 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
 
   _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
 
+  if (kms_display->onscreen)
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                       "Cannot have multiple onscreens in the KMS platform");
+      return FALSE;
+    }
+
+  kms_display->onscreen = onscreen;
+
   onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
   egl_onscreen = onscreen->winsys;
 
@@ -906,6 +985,9 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
 {
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
   CoglContext *context = framebuffer->context;
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
   CoglRenderer *renderer = context->display->renderer;
   CoglRendererEGL *egl_renderer = renderer->winsys;
   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
@@ -915,6 +997,8 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
   if (egl_onscreen == NULL)
     return;
 
+  kms_display->onscreen = NULL;
+
   kms_onscreen = egl_onscreen->platform;
 
   /* flip state takes a reference on the onscreen so there should
@@ -1009,3 +1093,85 @@ cogl_kms_display_queue_modes_reset (CoglDisplay *display)
       kms_display->pending_set_crtc = TRUE;
     }
 }
+
+CoglBool
+cogl_kms_display_set_layout (CoglDisplay *display,
+                             int width,
+                             int height,
+                             CoglKmsCrtc **crtcs,
+                             int n_crtcs,
+                             CoglError **error)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *crtc_list;
+  int i;
+
+  if ((width != kms_display->width ||
+       height != kms_display->height) &&
+      kms_display->onscreen)
+    {
+      CoglOnscreenEGL *egl_onscreen = kms_display->onscreen->winsys;
+      CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+      struct gbm_surface *new_surface;
+      EGLSurface new_egl_surface;
+
+      /* Need to drop the GBM surface and create a new one */
+
+      new_surface = gbm_surface_create (kms_renderer->gbm,
+                                        width, height,
+                                        GBM_BO_FORMAT_XRGB8888,
+                                        GBM_BO_USE_SCANOUT |
+                                        GBM_BO_USE_RENDERING);
+
+      if (!new_surface)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                           "Failed to allocate new surface");
+          return FALSE;
+        }
+
+      new_egl_surface =
+        eglCreateWindowSurface (egl_renderer->edpy,
+                                egl_display->egl_config,
+                                (NativeWindowType) new_surface,
+                                NULL);
+      if (new_egl_surface == EGL_NO_SURFACE)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                           "Failed to allocate new surface");
+          gbm_surface_destroy (new_surface);
+          return FALSE;
+        }
+
+      eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface);
+      gbm_surface_destroy (kms_onscreen->surface);
+
+      kms_onscreen->surface = new_surface;
+      egl_onscreen->egl_surface = new_egl_surface;
+
+      _cogl_framebuffer_winsys_update_size (COGL_FRAMEBUFFER (kms_display->onscreen), width, height);
+    }
+
+  kms_display->width = width;
+  kms_display->height = height;
+
+  g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free);
+
+  crtc_list = NULL;
+  for (i = 0; i < n_crtcs; i++)
+    {
+      crtc_list = g_list_prepend (crtc_list, crtc_copy (crtcs[i]));
+    }
+  crtc_list = g_list_reverse (crtc_list);
+  kms_display->crtcs = crtc_list;
+
+  kms_display->pending_set_crtc = TRUE;
+
+  return TRUE;
+}