[1/1] Adding VK_KHR_display to mesa based on KMS
diff mbox

Message ID 86r2z43b0f.fsf@hiro.keithp.com
State New
Headers show

Commit Message

Keith Packard May 31, 2017, 10:41 p.m. UTC
As part of my adventures to implement 'DRM leases', it turns out to be
easiest to just implement 'real' VK_KHR_display in mesa now, rather than
a pile of ugly kludges. Because DRM leases just provide a DRM master
fd with limited resource access, mesa doesn't have to care whether you
provide a 'real' master or a lease.

That means all of the mesa code is separate from DRM leases, and can
also be used to run applications directly on bare hardware.

The implementation of the KHR_display extension lives pretty much
entirely within the common vulkan code in
src/vulkan/wsi/wsi_common_display.c

The driver functions in radv simply call those functions, which means
this should be 'trivial' to enable in the anv driver, but I haven't done
that work.

To get the master FD into the driver, I've created a new extension,
VK_KEITHP_kms_display which provides a DRM master file descriptor that
the driver uses in place of creating it's own render node file
descriptor. There's a bit of a dance involved in making sure we use the
right driver; that could probably be improved.

There are a few bits of unimplemented functionality within the
KHR_display implementation:

 1) I don't dig the monitor name out of EDID. I'm thinking this
    should be done by the kernel and exposed as a property. It already
    has the code for this...

 2) plane and surface capabilities are 'faked'. These expose the range
    of source surface sizes and positions which can be used; obviously
    the hardware can do a lot more than display an image at full size.

 3) Planes in general. I'm exposing only a single plane per connector at
    this point. Obviously we'd want more.

 4) bpp/depth. For now, I'm assuming depth 24, 32bpp. I think I just
    need a driver hook to get this out of the image; I sure don't want
    to try and parse the format.

I haven't rebased this in a while; it's a few revs back on master. I'm
looking for general feedback and comments about the plan at this point;
this is the second implementation of the code, I'm entirely willing to
do at least one more to make it 'right'.

git://people.freedesktop.org/~keithp/mesa.git   khr_display

There are also some simple changes in the vulkan looader (replicated in
the relevant files in mesa) needed to gain access to the
VK_KEITHP_kms_display extension (thanks, Vulkan, for not exposing vendor
instance extensions by default)

git://people.freedesktop.org/~keithp/Vulkan-LoaderAndValidationLayers.git drm-leases

Patch
diff mbox

From 06088071af2a396ce58b0490ceb228e3aa079a09 Mon Sep 17 00:00:00 2001
From: Keith Packard <keithp@keithp.com>
Date: Wed, 17 May 2017 23:02:33 -0700
Subject: [PATCH] Add VK_KHR_display and VK_KEITHP_kms_display to radv vulkan
 driver [v2]

Implements VK_KHR_display using DRM/KMS.

Expose ability to get to that using VK_KEITHP_kms_display, which
allows the application to provide a master fd to the library which it
uses instead of opening its own.

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 configure.ac                                  |    1 +
 include/vulkan/vulkan.h                       |    3 +-
 include/vulkan/vulkan_keithp.h                |   40 +
 src/Makefile.am                               |    7 +
 src/amd/vulkan/Makefile.am                    |    9 +
 src/amd/vulkan/Makefile.sources               |    3 +
 src/amd/vulkan/radv_device.c                  |   71 +-
 src/amd/vulkan/radv_entrypoints_gen.py        |    2 +
 src/amd/vulkan/radv_private.h                 |    7 +
 src/amd/vulkan/radv_radeon_winsys.h           |    4 +
 src/amd/vulkan/radv_wsi.c                     |   37 +-
 src/amd/vulkan/radv_wsi_display.c             |  144 ++++
 src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c |   18 +
 src/intel/vulkan/anv_wsi.c                    |    2 +-
 src/vulkan/Makefile.am                        |    9 +
 src/vulkan/Makefile.sources                   |    4 +
 src/vulkan/registry/vk.xml                    |   18 +
 src/vulkan/wsi/wsi_common.h                   |   12 +-
 src/vulkan/wsi/wsi_common_display.c           | 1063 +++++++++++++++++++++++++
 src/vulkan/wsi/wsi_common_display.h           |   80 ++
 src/vulkan/wsi/wsi_common_wayland.c           |    2 +-
 src/vulkan/wsi/wsi_common_x11.c               |    4 +-
 22 files changed, 1526 insertions(+), 14 deletions(-)
 create mode 100644 include/vulkan/vulkan_keithp.h
 create mode 100644 src/amd/vulkan/radv_wsi_display.c
 create mode 100644 src/vulkan/wsi/wsi_common_display.c
 create mode 100644 src/vulkan/wsi/wsi_common_display.h

diff --git a/configure.ac b/configure.ac
index df3eb6b29a..4712efcd5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2238,6 +2238,7 @@  fi
 
 AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$egl_platforms" | grep -q 'x11')
 AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$egl_platforms" | grep -q 'wayland')
+AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$egl_platforms" | grep -q 'drm')
 AM_CONDITIONAL(HAVE_EGL_PLATFORM_DRM, echo "$egl_platforms" | grep -q 'drm')
 AM_CONDITIONAL(HAVE_EGL_PLATFORM_SURFACELESS, echo "$egl_platforms" | grep -q 'surfaceless')
 AM_CONDITIONAL(HAVE_EGL_PLATFORM_ANDROID, echo "$egl_platforms" | grep -q 'android')
diff --git a/include/vulkan/vulkan.h b/include/vulkan/vulkan.h
index ef0c246780..8b5be61e29 100644
--- a/include/vulkan/vulkan.h
+++ b/include/vulkan/vulkan.h
@@ -43,7 +43,7 @@  extern "C" {
 #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
 #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
 // Version of this file
-#define VK_HEADER_VERSION 46
+#define VK_HEADER_VERSION 48
 
 
 #define VK_NULL_HANDLE 0
@@ -214,6 +214,7 @@  typedef enum VkStructureType {
     VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000,
     VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000,
     VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
+    VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP = 1000010000,
     VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000,
     VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000,
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000,
diff --git a/include/vulkan/vulkan_keithp.h b/include/vulkan/vulkan_keithp.h
new file mode 100644
index 0000000000..acf59fa4e1
--- /dev/null
+++ b/include/vulkan/vulkan_keithp.h
@@ -0,0 +1,40 @@ 
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * 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.
+ */
+
+#ifndef _VULKAN_KEITHP_H_
+#define _VULKAN_KEITHP_H_
+
+#include "vulkan.h"
+#include <xf86drmMode.h>
+#include <xf86drm.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+typedef struct VkKmsDisplayInfoKEITHP {
+	VkStructureType         sType;	/* VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP */
+	const void*             pNext;
+	int                     fd;
+} VkKmsDisplayInfoKEITHP;
+
+#define VK_KEITHP_KMS_DISPLAY_SPEC_VERSION      1
+#define VK_KEITHP_KMS_DISPLAY_EXTENSION_NAME    "VK_KEITHP_kms_display"
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _VULKAN_KEITHP_H_ */
diff --git a/src/Makefile.am b/src/Makefile.am
index aa5f8aaf7d..6fcd8b9d24 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -85,6 +85,13 @@  SUBDIRS += vulkan
 endif
 EXTRA_DIST += vulkan/registry/vk.xml
 
+EXTRA_DIST += include/vulkan/vulkan_keithp.h
+
+vulkan_includedir = $(includedir)/vulkan
+
+vulkan_include_HEADERS = \
+	$(top_srcdir)/include/vulkan/vulkan_keithp.h
+
 if HAVE_AMD_DRIVERS
 SUBDIRS += amd
 endif
diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am
index fbd9f5a030..cf1f5219b9 100644
--- a/src/amd/vulkan/Makefile.am
+++ b/src/amd/vulkan/Makefile.am
@@ -61,6 +61,15 @@  VULKAN_SOURCES = \
 
 VULKAN_LIB_DEPS =
 
+if HAVE_PLATFORM_DISPLAY
+AM_CPPFLAGS += \
+	-DVK_USE_PLATFORM_DISPLAY_KHR \
+	-DVK_USE_PLATFORM_KMS_KEITHP
+
+VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
+
+VULKAN_LIB_DEPS += $(LIBDRM_LIBS)
+endif
 
 if HAVE_PLATFORM_X11
 AM_CPPFLAGS += \
diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources
index d3e0c81e9a..1119e908d1 100644
--- a/src/amd/vulkan/Makefile.sources
+++ b/src/amd/vulkan/Makefile.sources
@@ -72,6 +72,9 @@  VULKAN_WSI_WAYLAND_FILES := \
 VULKAN_WSI_X11_FILES := \
 	radv_wsi_x11.c
 
+VULKAN_WSI_DISPLAY_FILES := \
+	radv_wsi_display.c
+
 VULKAN_GENERATED_FILES := \
 	radv_entrypoints.c \
 	radv_entrypoints.h
diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c
index 921b8e48f5..1a30992d48 100644
--- a/src/amd/vulkan/radv_device.c
+++ b/src/amd/vulkan/radv_device.c
@@ -88,6 +88,18 @@  static const VkExtensionProperties instance_extensions[] = {
 		.extensionName = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
 		.specVersion = 1,
 	},
+        {
+                .extensionName = VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME,
+                .specVersion = VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION
+        },
+        {
+                .extensionName = VK_KHR_DISPLAY_EXTENSION_NAME,
+                .specVersion = VK_KHR_DISPLAY_SPEC_VERSION
+        },
+        {
+                .extensionName = VK_KEITHP_KMS_DISPLAY_EXTENSION_NAME,
+                .specVersion = 1,
+        }
 };
 
 static const VkExtensionProperties common_device_extensions[] = {
@@ -187,6 +199,12 @@  is_extension_enabled(const VkExtensionProperties *extensions,
 	return false;
 }
 
+static bool
+is_same_device(dev_t a, dev_t b)
+{
+        return major(a) == major(b) && (minor(a) & 0x3f) == (minor(b) & 0x3f);
+}
+
 static VkResult
 radv_physical_device_init(struct radv_physical_device *device,
 			  struct radv_instance *instance,
@@ -194,9 +212,26 @@  radv_physical_device_init(struct radv_physical_device *device,
 {
 	VkResult result;
 	drmVersionPtr version;
-	int fd;
-
-	fd = open(path, O_RDWR | O_CLOEXEC);
+	int fd = -1;
+        struct stat     stat_info, stat_path;
+
+        if (instance->master_fd >= 0) {
+                if (fstat(instance->master_fd, &stat_info) >= 0 &&
+                    stat(path, &stat_path) >= 0 &&
+                    S_ISCHR(stat_info.st_mode) &&
+                    S_ISCHR(stat_path.st_mode) &&
+                    is_same_device(stat_info.st_rdev, stat_path.st_rdev))
+                {
+                        printf("matched physical device to provided device info\n");
+                        fd = dup(instance->master_fd);
+                } else {
+                        printf("no match for provided device info\n");
+                        return VK_ERROR_INCOMPATIBLE_DRIVER;
+                }
+        }
+
+        if (fd < 0)
+                fd = open(path, O_RDWR | O_CLOEXEC);
 	if (fd < 0)
 		return VK_ERROR_INCOMPATIBLE_DRIVER;
 
@@ -366,6 +401,21 @@  VkResult radv_CreateInstance(
 	instance->debug_flags = parse_debug_string(getenv("RADV_DEBUG"),
 						   radv_debug_options);
 
+        instance->master_fd = -1;
+
+	vk_foreach_struct(ext, pCreateInfo->pNext) {
+                VkKmsDisplayInfoKEITHP *vk_kms;
+                switch (ext->sType) {
+                case VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP:
+                        vk_kms = (void *) ext;
+                        fprintf(stderr, "Found kms display info keithp struct fd %d\n", vk_kms->fd);
+                        instance->master_fd = vk_kms->fd;
+                        break;
+                default:
+                        break;
+                }
+        }
+
 	*pInstance = radv_instance_to_handle(instance);
 
 	return VK_SUCCESS;
@@ -2004,6 +2054,21 @@  bool radv_get_memory_fd(struct radv_device *device,
 					 pFD);
 }
 
+bool radv_get_memory_handle(struct radv_device *device,
+                            struct radv_device_memory *memory,
+                            uint32_t *pHandle)
+{
+	struct radeon_bo_metadata metadata;
+
+	if (memory->image) {
+		radv_init_metadata(device, memory->image, &metadata);
+		device->ws->buffer_set_metadata(memory->bo, &metadata);
+	}
+
+	return device->ws->buffer_get_handle(device->ws, memory->bo,
+                                             pHandle);
+}
+
 VkResult radv_AllocateMemory(
 	VkDevice                                    _device,
 	const VkMemoryAllocateInfo*                 pAllocateInfo,
diff --git a/src/amd/vulkan/radv_entrypoints_gen.py b/src/amd/vulkan/radv_entrypoints_gen.py
index 3474c789ea..e8e24b8da7 100644
--- a/src/amd/vulkan/radv_entrypoints_gen.py
+++ b/src/amd/vulkan/radv_entrypoints_gen.py
@@ -42,6 +42,8 @@  supported_extensions = [
    'VK_KHR_wayland_surface',
    'VK_KHR_xcb_surface',
    'VK_KHR_xlib_surface',
+   'VK_KEITHP_kms_display',
+   'VK_KHR_display'
 ]
 
 # We generate a static hash table for entry point lookup
diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h
index 088d24a706..7a2e1442d7 100644
--- a/src/amd/vulkan/radv_private.h
+++ b/src/amd/vulkan/radv_private.h
@@ -67,11 +67,13 @@  typedef uint32_t xcb_window_t;
 
 #include <vulkan/vulkan.h>
 #include <vulkan/vulkan_intel.h>
+#include <vulkan/vulkan_keithp.h>
 #include <vulkan/vk_icd.h>
 
 #include "radv_entrypoints.h"
 
 #include "wsi_common.h"
+#include "wsi_common_display.h"
 
 #define MAX_VBS         32
 #define MAX_VERTEX_ATTRIBS 32
@@ -281,6 +283,8 @@  struct radv_instance {
 	int                                         physicalDeviceCount;
 	struct radv_physical_device                 physicalDevices[RADV_MAX_DRM_DEVICES];
 
+        int                                         master_fd;
+
 	uint64_t debug_flags;
 };
 
@@ -894,6 +898,9 @@  void radv_cmd_buffer_trace_emit(struct radv_cmd_buffer *cmd_buffer);
 bool radv_get_memory_fd(struct radv_device *device,
 			struct radv_device_memory *memory,
 			int *pFD);
+bool radv_get_memory_handle(struct radv_device *device,
+                            struct radv_device_memory *memory,
+                            uint32_t *pHandle);
 /*
  * Takes x,y,z as exact numbers of invocations, instead of blocks.
  *
diff --git a/src/amd/vulkan/radv_radeon_winsys.h b/src/amd/vulkan/radv_radeon_winsys.h
index 84b1d73780..cc7bca73bc 100644
--- a/src/amd/vulkan/radv_radeon_winsys.h
+++ b/src/amd/vulkan/radv_radeon_winsys.h
@@ -283,6 +283,10 @@  struct radeon_winsys {
 			      struct radeon_winsys_bo *bo,
 			      int *fd);
 
+	bool (*buffer_get_handle)(struct radeon_winsys *ws,
+                                  struct radeon_winsys_bo *bo,
+                                  uint32_t *handle);
+
 	void (*buffer_unmap)(struct radeon_winsys_bo *bo);
 
 	uint64_t (*buffer_get_va)(struct radeon_winsys_bo *bo);
diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c
index 3a8617fd8f..85efb2c279 100644
--- a/src/amd/vulkan/radv_wsi.c
+++ b/src/amd/vulkan/radv_wsi.c
@@ -57,6 +57,23 @@  radv_init_wsi(struct radv_physical_device *physical_device)
 	}
 #endif
 
+#ifdef VK_USE_PLATFORM_DISPLAY_KHR
+        result = wsi_display_init_wsi(&physical_device->wsi_device,
+                                      &physical_device->instance->alloc,
+                                      radv_physical_device_to_handle(physical_device),
+                                      physical_device->instance->master_fd >= 0 ? physical_device->local_fd : -1,
+                                      &wsi_cbs);
+        if (result != VK_SUCCESS) {
+#ifdef VK_USE_PLATFORM_XCB_KHR
+           wsi_x11_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc);
+#endif
+#ifdef VK_USE_PLATFORM_WAYLAND_KHR
+           wsi_wayland_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc);
+#endif
+           return result;
+        }
+#endif
+
 	return VK_SUCCESS;
 }
 
@@ -69,6 +86,9 @@  radv_finish_wsi(struct radv_physical_device *physical_device)
 #ifdef VK_USE_PLATFORM_XCB_KHR
 	wsi_x11_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc);
 #endif
+#ifdef VK_USE_PLATFORM_DISPLAY_KHR
+        wsi_display_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc);
+#endif
 }
 
 void radv_DestroySurfaceKHR(
@@ -147,7 +167,7 @@  radv_wsi_image_create(VkDevice device_h,
 		      VkDeviceMemory *memory_p,
 		      uint32_t *size,
 		      uint32_t *offset,
-		      uint32_t *row_pitch, int *fd_p)
+		      uint32_t *row_pitch, int *fd_p, uint32_t *handle_p)
 {
 	VkResult result = VK_SUCCESS;
 	struct radeon_surf *surface;
@@ -213,9 +233,18 @@  radv_wsi_image_create(VkDevice device_h,
 	if (!needs_linear_copy || (needs_linear_copy && linear)) {
 		RADV_FROM_HANDLE(radv_device, device, device_h);
 		RADV_FROM_HANDLE(radv_device_memory, memory, memory_h);
-		if (!radv_get_memory_fd(device, memory, &fd))
-			goto fail_alloc_memory;
-		*fd_p = fd;
+
+                if (fd_p) {
+                   if (!radv_get_memory_fd(device, memory, &fd))
+                      goto fail_alloc_memory;
+                   *fd_p = fd;
+                }
+                if (handle_p) {
+                   uint32_t     handle;
+                   if (!radv_get_memory_handle(device, memory, &handle))
+                      goto fail_alloc_memory;
+                   *handle_p = handle;
+                }
 	}
 
 	surface = &image->surface;
diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c
new file mode 100644
index 0000000000..7113b78593
--- /dev/null
+++ b/src/amd/vulkan/radv_wsi_display.c
@@ -0,0 +1,144 @@ 
+/*
+ * Copyright © 2017 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "radv_private.h"
+#include "radv_cs.h"
+#include "util/disk_cache.h"
+#include "util/strtod.h"
+#include "util/vk_util.h"
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <amdgpu.h>
+#include <amdgpu_drm.h>
+#include "amdgpu_id.h"
+#include "winsys/amdgpu/radv_amdgpu_winsys_public.h"
+#include "ac_llvm_util.h"
+#include "vk_format.h"
+#include "sid.h"
+#include "util/debug.h"
+#include "wsi_common_display.h"
+
+#define MM_PER_PIXEL     (1.0/96.0 * 25.4)
+
+VkResult
+radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice             physical_device,
+                                           uint32_t                     *property_count,
+                                           VkDisplayPropertiesKHR       *properties)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_physical_device_display_properties(physical_device,
+                                                             &pdevice->wsi_device,
+                                                             property_count,
+                                                             properties);
+}
+
+VkResult
+radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice                physical_device,
+                                                uint32_t                        *property_count,
+                                                VkDisplayPlanePropertiesKHR     *properties)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_physical_device_display_plane_properties(physical_device,
+                                                                   &pdevice->wsi_device,
+                                                                   property_count,
+                                                                   properties);
+}
+
+VkResult
+radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice               physical_device,
+                                         uint32_t                       plane_index,
+                                         uint32_t                       *display_count,
+                                         VkDisplayKHR                   *displays)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_display_plane_supported_displays(physical_device,
+                                                           &pdevice->wsi_device,
+                                                           plane_index,
+                                                           display_count,
+                                                           displays);
+}
+
+
+VkResult
+radv_GetDisplayModePropertiesKHR(VkPhysicalDevice               physical_device,
+                                 VkDisplayKHR                   display,
+                                 uint32_t                       *property_count,
+                                 VkDisplayModePropertiesKHR     *properties)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_display_get_display_mode_properties(physical_device,
+                                                  &pdevice->wsi_device,
+                                                  display,
+                                                  property_count,
+                                                  properties);
+}
+
+VkResult
+radv_CreateDisplayModeKHR(VkPhysicalDevice                      physical_device,
+                          VkDisplayKHR                          display,
+                          const VkDisplayModeCreateInfoKHR      *create_info,
+                          const VkAllocationCallbacks           *allocator,
+                          VkDisplayModeKHR                      *mode)
+{
+   return VK_ERROR_INITIALIZATION_FAILED;
+}
+
+
+VkResult
+radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice                    physical_device,
+                                    VkDisplayModeKHR                    mode_khr,
+                                    uint32_t                            plane_index,
+                                    VkDisplayPlaneCapabilitiesKHR       *capabilities)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_get_display_plane_capabilities(physical_device,
+                                             &pdevice->wsi_device,
+                                             mode_khr,
+                                             plane_index,
+                                             capabilities);
+}
+
+VkResult
+radv_CreateDisplayPlaneSurfaceKHR(VkInstance                            _instance,
+                                  const VkDisplaySurfaceCreateInfoKHR   *create_info,
+                                  const VkAllocationCallbacks           *allocator,
+                                  VkSurfaceKHR                          *surface)
+{
+   RADV_FROM_HANDLE(radv_instance, instance, _instance);
+   const VkAllocationCallbacks *alloc;
+
+   if (allocator)
+     alloc = allocator;
+   else
+     alloc = &instance->alloc;
+
+   return wsi_create_display_surface(_instance, alloc, create_info, surface);
+}
diff --git a/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c b/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c
index 7b679450cb..eeae79cb76 100644
--- a/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c
+++ b/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c
@@ -445,6 +445,23 @@  radv_amdgpu_winsys_get_fd(struct radeon_winsys *_ws,
 	return true;
 }
 
+static bool
+radv_amdgpu_winsys_get_handle(struct radeon_winsys *_ws,
+                              struct radeon_winsys_bo *_bo,
+                              uint32_t *handle)
+{
+	struct radv_amdgpu_winsys_bo *bo = radv_amdgpu_winsys_bo(_bo);
+	enum amdgpu_bo_handle_type type = amdgpu_bo_handle_type_kms;
+	int r;
+
+	r = amdgpu_bo_export(bo->bo, type, handle);
+	if (r)
+		return false;
+
+        bo->is_shared = true;
+	return true;
+}
+
 static unsigned radv_eg_tile_split_rev(unsigned eg_tile_split)
 {
 	switch (eg_tile_split) {
@@ -503,6 +520,7 @@  void radv_amdgpu_bo_init_functions(struct radv_amdgpu_winsys *ws)
 	ws->base.buffer_unmap = radv_amdgpu_winsys_bo_unmap;
 	ws->base.buffer_from_fd = radv_amdgpu_winsys_bo_from_fd;
 	ws->base.buffer_get_fd = radv_amdgpu_winsys_get_fd;
+        ws->base.buffer_get_handle = radv_amdgpu_winsys_get_handle;
 	ws->base.buffer_set_metadata = radv_amdgpu_winsys_bo_set_metadata;
 	ws->base.buffer_virtual_bind = radv_amdgpu_winsys_bo_virtual_bind;
 }
diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c
index 2ab37c30b7..d274493599 100644
--- a/src/intel/vulkan/anv_wsi.c
+++ b/src/intel/vulkan/anv_wsi.c
@@ -151,7 +151,7 @@  anv_wsi_image_create(VkDevice device_h,
                      VkDeviceMemory *memory_p,
                      uint32_t *size,
                      uint32_t *offset,
-                     uint32_t *row_pitch, int *fd_p)
+                     uint32_t *row_pitch, int *fd_p, uint32_t *handle_p)
 {
    struct anv_device *device = anv_device_from_handle(device_h);
    VkImage image_h;
diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am
index bd66901255..d7b654d266 100644
--- a/src/vulkan/Makefile.am
+++ b/src/vulkan/Makefile.am
@@ -48,6 +48,15 @@  VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES)
 VULKAN_LIB_DEPS += $(XCB_DRI3_LIBS) -lX11-xcb
 endif
 
+if HAVE_PLATFORM_DISPLAY
+AM_CPPFLAGS += \
+	-DVK_USE_PLATFORM_DISPLAY
+
+VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
+
+VULKAN_LIB_DEPS += $(LIBDRM_LIBS)
+endif
+
 BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES)
 CLEANFILES = $(BUILT_SOURCES)
 
diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources
index 63f4ac1162..109aa83e6b 100644
--- a/src/vulkan/Makefile.sources
+++ b/src/vulkan/Makefile.sources
@@ -15,6 +15,10 @@  VULKAN_WSI_X11_FILES := \
 	wsi/wsi_common_x11.c \
 	wsi/wsi_common_x11.h
 
+VULKAN_WSI_DISPLAY_FILES := \
+	wsi/wsi_common_display.c \
+	wsi/wsi_common_display.h
+
 VULKAN_UTIL_GENERATED_FILES := \
 	util/vk_enum_to_str.c \
 	util/vk_enum_to_str.h
diff --git a/src/vulkan/registry/vk.xml b/src/vulkan/registry/vk.xml
index b233108a36..d9c01f6e52 100644
--- a/src/vulkan/registry/vk.xml
+++ b/src/vulkan/registry/vk.xml
@@ -67,6 +67,7 @@  maintained in the master branch of the Khronos Vulkan GitHub project.
         <tag name="KHR"         author="Khronos"                       contact="Tom Olson @tom.olson"/>
         <tag name="EXT"         author="Multivendor"                   contact="Jon Leech @oddhack"/>
         <tag name="MESA"        author="Mesa open source project"      contact="Chad Versace @chadversary, Daniel Stone @fooishbar, David Airlie @airlied, Jason Ekstrand @jekstrand"/>
+        <tag name="KEITHP"      author="keithp.com"                    contact="keithp @keithp.com"/>
     </tags>
 
     <!-- SECTION: Vulkan type definitions -->
@@ -1538,6 +1539,16 @@  maintained in the master branch of the Khronos Vulkan GitHub project.
             <member noautovalidity="true"><type>xcb_connection_t</type>*                <name>connection</name></member>
             <member><type>xcb_window_t</type>                     <name>window</name></member>
         </type>
+	<type catagory="struct" name="VkKmsDisplayInfoKEITHP">
+	  <member values="VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP"><type>VkStructureType</type> <name>sType</name></member>
+	  <member><type>VkStructureType</type>			  <name>sType</name></member>
+	  <member>const<type>void</type>*			  <name>pNext</name></member>
+	  <member><type>int</type>                                <name>fd</name></member>
+	  <member><type>uint32_t</type>                           <name>crtc_id</name></member>
+	  <member><type>uint32_t</type>*                          <name>connector_ids</name></member>
+	  <member><type>int</type>                                <name>connector_count</name></member>
+	  <member><type>drmModeModeInfoPtr</type>                 <name>mode</name></member>
+	</type>
         <type category="struct" name="VkSurfaceFormatKHR" returnedonly="true">
             <member><type>VkFormat</type>                         <name>format</name></member>                   <!-- Supported pair of rendering format -->
             <member><type>VkColorSpaceKHR</type>                  <name>colorSpace</name></member>               <!-- and color space for the surface -->
@@ -5117,6 +5128,13 @@  maintained in the master branch of the Khronos Vulkan GitHub project.
                 <command name="vkGetPhysicalDeviceXcbPresentationSupportKHR"/>
             </require>
         </extension>
+        <extension name="VK_KEITHP_kms_display" number="1" type="instance" requires="VK_KHR_surface" protect="VK_USE_PLATFORM_KMS_KEITHP" supported="vulkan">
+            <require>
+                <enum value="1"                                         name="VK_KEITHP_KMS_DISPLAY_SPEC_VERSION"/>
+                <enum value="&quot;VK_KEITHP_kms_display&quot;"         name="VK_KEITHP_KMS_DISPLAY_EXTENSION_NAME"/>
+                <enum offset="0" extends="VkStructureType"              name="VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP"/>
+            </require>
+        </extension>
         <extension name="VK_KHR_wayland_surface" number="7" type="instance" requires="VK_KHR_surface" protect="VK_USE_PLATFORM_WAYLAND_KHR" supported="vulkan">
             <require>
                 <enum value="6"                                         name="VK_KHR_WAYLAND_SURFACE_SPEC_VERSION"/>
diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
index 5e77518c09..ccf91ecac9 100644
--- a/src/vulkan/wsi/wsi_common.h
+++ b/src/vulkan/wsi/wsi_common.h
@@ -42,7 +42,8 @@  struct wsi_image_fns {
                                 uint32_t *size_p,
                                 uint32_t *offset_p,
                                 uint32_t *row_pitch_p,
-                                int *fd_p);
+                                int *fd_p,
+                                uint32_t *handle_p);
    void (*free_wsi_image)(VkDevice device,
                           const VkAllocationCallbacks *pAllocator,
                           VkImage image_h,
@@ -104,7 +105,7 @@  struct wsi_interface {
                                 struct wsi_swapchain **swapchain);
 };
 
-#define VK_ICD_WSI_PLATFORM_MAX 5
+#define VK_ICD_WSI_PLATFORM_MAX 6
 
 struct wsi_device {
     struct wsi_interface *                  wsi[VK_ICD_WSI_PLATFORM_MAX];
@@ -162,5 +163,12 @@  VkResult wsi_wl_init_wsi(struct wsi_device *wsi_device,
 void wsi_wl_finish_wsi(struct wsi_device *wsi_device,
                        const VkAllocationCallbacks *alloc);
 
+VkResult wsi_display_init_wsi(struct wsi_device *wsi_device,
+                              const VkAllocationCallbacks *alloc,
+                              VkPhysicalDevice physical_device,
+                              int fd,
+                              const struct wsi_callbacks *cbs);
+void wsi_display_finish_wsi(struct wsi_device *wsi_device,
+                            const VkAllocationCallbacks *alloc);
 
 #endif
diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
new file mode 100644
index 0000000000..9336b2ae67
--- /dev/null
+++ b/src/vulkan/wsi/wsi_common_display.c
@@ -0,0 +1,1063 @@ 
+/*
+ * Copyright © 2017 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "util/macros.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include "util/hash_table.h"
+#include "util/list.h"
+
+#include "wsi_common.h"
+#include "wsi_common_queue.h"
+#include "wsi_common_display.h"
+
+/* These have lifetime equal to the instance, so they effectively
+ * never go away. This means we must keep track of them separately
+ * from all other resources.
+ */
+typedef struct wsi_display_mode {
+   struct list_head             list;
+   struct wsi_display_connector *connector;
+   drmModeModeInfo              mode;
+} wsi_display_mode;
+
+typedef struct wsi_display_connector {
+   struct list_head             list;
+   struct wsi_display           *wsi;
+   uint32_t                     id;
+   uint32_t                     crtc_id;
+   char                         *name;
+   bool                         active;
+   drmModeConnectorPtr          connector;
+   drmModePropertyBlobPtr       edid_blob;
+} wsi_display_connector;
+
+struct wsi_display {
+   struct wsi_interface         base;
+
+   const VkAllocationCallbacks  *alloc;
+   VkPhysicalDevice             physical_device;
+
+   const struct wsi_callbacks   *cbs;
+
+   int                          master_fd;
+
+   struct list_head             connectors;
+
+   struct list_head             display_modes;
+
+   drmModeResPtr                mode_res;
+};
+
+struct wsi_display_image {
+   struct wsi_display_swapchain *chain;
+   VkImage                      image;
+   VkImage                      linear_image;
+   VkDeviceMemory               memory;
+   VkDeviceMemory               linear_memory;
+   bool                         busy;
+   unsigned int                 frame;
+   unsigned int                 sec;
+   unsigned int                 usec;
+   uint32_t                     bo_handle;
+   uint32_t                     fb_id;
+};
+
+struct wsi_display_swapchain {
+   struct wsi_swapchain         base;
+   int                          fd;
+   VkIcdSurfaceDisplay          *surface;
+   struct wsi_display_image     images[0];
+};
+
+ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR)
+ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR)
+
+VkResult
+wsi_display_set_master_fd(VkInstance                    instance,
+                          struct wsi_device             *wsi_device,
+                          int                           master_fd)
+{
+   struct wsi_display   *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
+   wsi->master_fd = master_fd;
+
+   return VK_SUCCESS;
+}
+
+static bool
+same_mode(drmModeModeInfoPtr    a,
+          drmModeModeInfoPtr    b)
+{
+   return memcmp(a, b, sizeof (drmModeModeInfo)) == 0;
+}
+
+static struct wsi_display_mode *
+wsi_display_find_mode(struct wsi_device                 *wsi_device,
+                      struct wsi_display_connector      *connector,
+                      drmModeModeInfoPtr                mode)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   size_t                       len;
+   struct wsi_display_mode      *display_mode;
+
+   /* clear name bytes beyond string len */
+   len = strnlen(mode->name, sizeof(mode->name));
+   memset(mode->name + len, '\0', sizeof(mode->name) - len);
+
+   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
+      if (display_mode->connector == connector &&
+          same_mode(&display_mode->mode, mode))
+         return display_mode;
+   }
+   return NULL;
+}
+
+static drmModeModeInfoPtr
+wsi_display_find_mode_info(struct wsi_display_connector *connector,
+                           struct wsi_display_mode      *display_mode)
+{
+   int                          m;
+
+   for (m = 0; m < connector->connector->count_modes; m++) {
+      if (same_mode(&connector->connector->modes[m], &display_mode->mode))
+         return &connector->connector->modes[m];
+   }
+   return NULL;
+}
+
+static VkResult
+wsi_display_register_mode(struct wsi_device            *wsi_device,
+                          struct wsi_display_connector *connector,
+                          drmModeModeInfoPtr            mode)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_mode      *display_mode;
+
+   display_mode = wsi_display_find_mode(wsi_device, connector, mode);
+
+   if (display_mode)
+      return VK_SUCCESS;
+
+   display_mode = vk_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!display_mode)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   display_mode->connector = connector;
+   display_mode->mode = *mode;
+
+   LIST_ADDTAIL(&display_mode->list, &wsi->display_modes);
+   return VK_SUCCESS;
+}
+
+
+/*
+ * Update our information about a specific connector
+ */
+
+static struct wsi_display_connector *
+wsi_display_find_connector(struct wsi_device             *wsi_device,
+                          uint32_t                      connector_id)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+
+   connector = NULL;
+   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
+      if (connector->id == connector_id)
+         return connector;
+   }
+
+   return NULL;
+}
+
+static struct wsi_display_connector *
+wsi_display_get_connector(struct wsi_device             *wsi_device,
+                          uint32_t                      connector_id)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+   int                          master_fd = wsi->master_fd;
+   int                          p, m;
+   VkResult                     result;
+
+
+   connector = wsi_display_find_connector(wsi_device, connector_id);
+
+   if (!connector) {
+      connector = vk_alloc(wsi->alloc, sizeof *connector, 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+      if (!connector)
+         return NULL;
+      memset(connector, '\0', sizeof (*connector));
+      connector->id = connector_id;
+      connector->wsi = wsi;
+      connector->active = false;
+      LIST_ADDTAIL(&connector->list, &wsi->connectors);
+   }
+
+   if (connector->connector)
+      drmModeFreeConnector(connector->connector);
+
+   connector->connector = drmModeGetConnector(master_fd, connector->id);
+
+   if (!connector->connector)
+      return NULL;
+
+   /* Look for an EDID property */
+   for (p = 0; p < connector->connector->count_props; p++) {
+      drmModePropertyPtr prop = drmModeGetProperty(master_fd, connector->connector->props[p]);
+      if (prop && (prop->flags & DRM_MODE_PROP_BLOB)) {
+         if (!strcmp(prop->name, "EDID")) {
+            if (connector->edid_blob)
+               drmModeFreePropertyBlob(connector->edid_blob);
+            connector->edid_blob = drmModeGetPropertyBlob(master_fd,
+                                                       connector->connector->prop_values[p]);
+            /* XXX dig name out of EDID data */
+         }
+      }
+   }
+
+   /* XXX use EDID name */
+   connector->name = "monitor";
+
+   /* Register modes */
+   for (m = 0; m < connector->connector->count_modes; m++) {
+      result = wsi_display_register_mode(wsi_device,
+                                         connector,
+                                         &connector->connector->modes[m]);
+      if (result != VK_SUCCESS)
+         return NULL;
+   }
+
+   return connector;
+}
+
+#define MM_PER_PIXEL     (1.0/96.0 * 25.4)
+
+static void
+wsi_display_fill_in_display_properties(struct wsi_device                *wsi_device,
+                                       struct wsi_display_connector     *connector,
+                                       VkDisplayPropertiesKHR           *properties)
+{
+   int                          m;
+   drmModeModeInfoPtr           preferred_mode;
+
+   properties->display = wsi_display_connector_to_handle(connector);
+   properties->displayName = connector->name;
+
+   /* Find the preferred mode and assume that's the physical resolution */
+   if (connector->connector->count_modes > 0) {
+      preferred_mode = &connector->connector->modes[0];
+      for (m = 0; m < connector->connector->count_modes; m++)
+         if (connector->connector->modes[m].type & DRM_MODE_TYPE_PREFERRED) {
+            preferred_mode = &connector->connector->modes[m];
+            break;
+         }
+
+      properties->physicalResolution.width = preferred_mode->hdisplay;
+      properties->physicalResolution.height = preferred_mode->vdisplay;
+   } else {
+      properties->physicalResolution.width = 1024;
+      properties->physicalResolution.height = 768;
+   }
+
+   /* Make up physical size based on 96dpi */
+   properties->physicalDimensions.width = floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5);
+   properties->physicalDimensions.height = floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5);
+
+   properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   properties->persistentContent = 0;
+}
+
+/*
+ * Implement vkGetPhysicalDeviceDisplayPropertiesKHR
+ */
+VkResult
+wsi_display_get_physical_device_display_properties(VkPhysicalDevice             physical_device,
+                                                   struct wsi_device            *wsi_device,
+                                                   uint32_t                     *property_count,
+                                                   VkDisplayPropertiesKHR       *properties)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   int                          master_fd = wsi->master_fd;
+   struct wsi_display_connector *connector;
+   int                          c;
+   uint32_t                     connected = 0;
+
+   if (wsi->mode_res)
+      drmModeFreeResources(wsi->mode_res);
+
+   wsi->mode_res = drmModeGetResources(master_fd);
+
+   if (!wsi->mode_res)
+      return VK_ERROR_INITIALIZATION_FAILED;
+
+   connected = 0;
+
+   /* Erase saved connector information */
+   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
+      if (connector->connector) {
+         drmModeFreeConnector(connector->connector);
+         connector->connector = NULL;
+      }
+   }
+
+   /* Get current information */
+   for (c = 0; c < wsi->mode_res->count_connectors; c++) {
+      connector = wsi_display_get_connector(wsi_device, wsi->mode_res->connectors[c]);
+
+      if (!connector)
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+      if (connector->connector->connection != DRM_MODE_DISCONNECTED)
+         connected++;
+   }
+
+   /* Asking only for count, return that */
+   if (properties == NULL) {
+      *property_count = connected;
+      return VK_SUCCESS;
+   }
+
+   connected = 0;
+
+   for (c = 0; c < wsi->mode_res->count_connectors; c++) {
+      connector  = wsi_display_find_connector(wsi_device, wsi->mode_res->connectors[c]);
+      if (connector && connector->connector->connection != DRM_MODE_DISCONNECTED) {
+         if (connected < *property_count) {
+
+            wsi_display_fill_in_display_properties(wsi_device,
+                                                   connector,
+                                                   &properties[connected]);
+         }
+         connected++;
+      }
+   }
+
+   *property_count = connected;
+
+   if (connected <= *property_count)
+      return VK_SUCCESS;
+
+   return VK_INCOMPLETE;
+}
+
+/*
+ * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR
+ */
+
+VkResult
+wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice               physical_device,
+                                                         struct wsi_device              *wsi_device,
+                                                         uint32_t                       *property_count,
+                                                         VkDisplayPlanePropertiesKHR    *properties)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+   int                          c;
+
+   if (!properties) {
+      *property_count = wsi->mode_res->count_connectors;
+      return VK_SUCCESS;
+   }
+
+   for (c = 0; c < wsi->mode_res->count_connectors; c++) {
+      connector = wsi_display_find_connector(wsi_device, wsi->mode_res->connectors[c]);
+      if (c < *property_count) {
+         if (connector && connector->active) {
+            properties[c].currentDisplay = wsi_display_connector_to_handle(connector);
+            properties[c].currentStackIndex = c;
+         } else {
+            properties[c].currentDisplay = NULL;
+            properties[c].currentStackIndex = 0;
+         }
+      }
+   }
+
+   if (c <= *property_count)
+      return VK_SUCCESS;
+
+   return VK_INCOMPLETE;
+}
+
+/*
+ * Implement vkGetDisplayPlaneSupportedDisplaysKHR
+ */
+
+VkResult
+wsi_display_get_display_plane_supported_displays(VkPhysicalDevice               physical_device,
+                                                 struct wsi_device              *wsi_device,
+                                                 uint32_t                       plane_index,
+                                                 uint32_t                       *display_count,
+                                                 VkDisplayKHR                   *displays)
+{
+   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   struct wsi_display_connector *connector;
+
+   if (displays == NULL) {
+      *display_count = 1;
+      return VK_SUCCESS;
+   }
+
+   if (*display_count < 1)
+      return VK_INCOMPLETE;
+
+   if (plane_index >= wsi->mode_res->count_connectors)
+      return VK_ERROR_OUT_OF_DATE_KHR;
+
+   connector = wsi_display_find_connector(wsi_device, wsi->mode_res->connectors[plane_index]);
+
+   if (connector) {
+      *displays = wsi_display_connector_to_handle(connector);
+      *display_count = 1;
+   } else {
+      *display_count = 0;
+   }
+
+   return VK_SUCCESS;
+}
+
+/*
+ * Implement vkGetDisplayModePropertiesKHR
+ */
+
+VkResult
+wsi_display_get_display_mode_properties(VkPhysicalDevice               physical_device,
+                                        struct wsi_device              *wsi_device,
+                                        VkDisplayKHR                   display,
+                                        uint32_t                       *property_count,
+                                        VkDisplayModePropertiesKHR     *properties)
+{
+   struct wsi_display_connector *connector = wsi_display_connector_from_handle(display);
+   int                          m, i;
+   struct wsi_display_mode      *display_mode;
+
+   if (properties == NULL) {
+      *property_count = connector->connector->count_modes;
+      return VK_SUCCESS;
+   }
+
+   i = 0;
+   for (m = 0; m < connector->connector->count_modes; m++) {
+      display_mode = wsi_display_find_mode(wsi_device, connector, &connector->connector->modes[m]);
+      if (display_mode) {
+         if (i < *property_count) {
+            properties[i].displayMode = wsi_display_mode_to_handle(display_mode);
+            properties[i].parameters.visibleRegion.width = display_mode->mode.hdisplay;
+            properties[i].parameters.visibleRegion.height = display_mode->mode.vdisplay;
+            properties[i].parameters.refreshRate = display_mode->mode.vrefresh;
+         }
+         i++;
+      }
+   }
+
+   if (i <= *property_count)
+      return VK_SUCCESS;
+
+   return VK_INCOMPLETE;
+}
+
+VkResult
+wsi_get_display_plane_capabilities(VkPhysicalDevice                     physical_device,
+                                   struct wsi_device                    *wsi_device,
+                                    VkDisplayModeKHR                    mode_khr,
+                                    uint32_t                            plane_index,
+                                    VkDisplayPlaneCapabilitiesKHR       *capabilities)
+{
+   struct wsi_display_mode      *mode = wsi_display_mode_from_handle(mode_khr);
+
+   /* XXX use actual values */
+   capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
+   capabilities->minSrcPosition.x = 0;
+   capabilities->minSrcPosition.y = 0;
+   capabilities->maxSrcPosition.x = 0;
+   capabilities->maxSrcPosition.y = 0;
+   capabilities->minSrcExtent.width = mode->mode.hdisplay;
+   capabilities->minSrcExtent.height = mode->mode.vdisplay;
+   capabilities->maxSrcExtent.width = mode->mode.hdisplay;
+   capabilities->maxSrcExtent.height = mode->mode.vdisplay;
+   capabilities->minDstPosition.x = 0;
+   capabilities->minDstPosition.y = 0;
+   capabilities->maxDstPosition.x = 0;
+   capabilities->maxDstPosition.y = 0;
+   capabilities->minDstExtent.width = mode->mode.hdisplay;
+   capabilities->minDstExtent.height = mode->mode.vdisplay;
+   capabilities->maxDstExtent.width = mode->mode.hdisplay;
+   capabilities->maxDstExtent.height = mode->mode.vdisplay;
+   return VK_SUCCESS;
+}
+
+VkResult
+wsi_create_display_surface(VkInstance instance,
+                           const VkAllocationCallbacks   *allocator,
+                           const VkDisplaySurfaceCreateInfoKHR *create_info,
+                           VkSurfaceKHR *surface_khr)
+{
+   VkIcdSurfaceDisplay *surface;
+
+   surface = vk_alloc(allocator, sizeof *surface, 8,
+                      VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (surface == NULL)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY;
+
+   surface->displayMode = create_info->displayMode;
+   surface->planeIndex = create_info->planeIndex;
+   surface->planeStackIndex = create_info->planeStackIndex;
+   surface->transform = create_info->transform;
+   surface->globalAlpha = create_info->globalAlpha;
+   surface->alphaMode = create_info->alphaMode;
+   surface->imageExtent = create_info->imageExtent;
+
+   *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base);
+   return VK_SUCCESS;
+}
+
+
+static VkResult
+wsi_display_surface_get_support(VkIcdSurfaceBase *surface,
+                                struct wsi_device *wsi_device,
+                                const VkAllocationCallbacks *allocator,
+                                uint32_t queueFamilyIndex,
+                                int local_fd,
+                                bool can_handle_different_gpu,
+                                VkBool32* pSupported)
+{
+   struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
+   *pSupported = wsi->master_fd >= 0;
+
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
+                                     VkSurfaceCapabilitiesKHR* caps)
+{
+   VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
+   wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
+
+   caps->currentExtent.width = mode->mode.hdisplay;
+   caps->currentExtent.height = mode->mode.vdisplay;
+
+   /* XXX Figure out extents based on driver capabilities */
+   caps->maxImageExtent = caps->minImageExtent = caps->currentExtent;
+
+   caps->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |
+                                    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
+
+   caps->minImageCount = 2;
+   caps->maxImageCount = 0;
+
+   caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+   caps->maxImageArrayLayers = 1;
+   caps->supportedUsageFlags =
+      VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+      VK_IMAGE_USAGE_SAMPLED_BIT |
+      VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+      VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+   return VK_SUCCESS;
+}
+
+static const VkSurfaceFormatKHR available_surface_formats[] = {
+   { .format = VK_FORMAT_B8G8R8A8_SRGB, },
+   { .format = VK_FORMAT_B8G8R8A8_UNORM, },
+};
+
+static VkResult
+wsi_display_surface_get_formats(VkIcdSurfaceBase        *icd_surface,
+                                struct wsi_device       *wsi_device,
+                                uint32_t                *surface_format_count,
+                                VkSurfaceFormatKHR      *surface_formats)
+{
+   if (surface_formats == NULL) {
+      *surface_format_count = ARRAY_SIZE(available_surface_formats);
+      return VK_SUCCESS;
+   }
+
+   *surface_format_count = MIN2(*surface_format_count, ARRAY_SIZE(available_surface_formats));
+   typed_memcpy(surface_formats, available_surface_formats, *surface_format_count);
+
+   return *surface_format_count < ARRAY_SIZE(available_surface_formats) ?
+      VK_INCOMPLETE : VK_SUCCESS;
+}
+
+static const VkPresentModeKHR available_present_modes[] = {
+   VK_PRESENT_MODE_IMMEDIATE_KHR,
+   VK_PRESENT_MODE_MAILBOX_KHR,
+   VK_PRESENT_MODE_FIFO_KHR,
+};
+
+static VkResult
+wsi_display_surface_get_present_modes(VkIcdSurfaceBase  *surface,
+                                      uint32_t          *present_mode_count,
+                                      VkPresentModeKHR  *present_modes)
+{
+   if (present_modes == NULL) {
+      *present_mode_count = ARRAY_SIZE(available_present_modes);
+      return VK_SUCCESS;
+   }
+
+   *present_mode_count = MIN2(*present_mode_count, ARRAY_SIZE(available_present_modes));
+   typed_memcpy(present_modes, available_present_modes, *present_mode_count);
+
+   if (*present_mode_count < ARRAY_SIZE(available_present_modes))
+      return VK_INCOMPLETE;
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_display_image_init(VkDevice                         device_h,
+                       struct wsi_swapchain             *drv_chain,
+                       const VkSwapchainCreateInfoKHR   *create_info,
+                       const VkAllocationCallbacks      *allocator,
+                       struct wsi_display_image         *image)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+   VkResult                     result;
+   uint32_t                     row_pitch;
+   uint32_t                     offset;
+   uint32_t                     handle;
+   uint32_t                     size;
+   int                          ret;
+
+   result = chain->base.image_fns->create_wsi_image(device_h,
+                                                    create_info,
+                                                    allocator,
+                                                    chain->base.needs_linear_copy,
+                                                    false,
+                                                    &image->image,
+                                                    &image->memory,
+                                                    &size,
+                                                    &offset,
+                                                    &row_pitch,
+                                                    NULL,
+                                                    &handle);
+
+   if (result != VK_SUCCESS)
+      return result;
+
+   if (chain->base.needs_linear_copy) {
+      result = chain->base.image_fns->create_wsi_image(device_h,
+                                                       create_info,
+                                                       allocator,
+                                                       chain->base.needs_linear_copy,
+                                                       true,
+                                                       &image->linear_image,
+                                                       &image->linear_memory,
+                                                       &size,
+                                                       &offset,
+                                                       &row_pitch,
+                                                       NULL,
+                                                       &handle);
+
+      if (result != VK_SUCCESS) {
+         goto fail_linear;
+      }
+   }
+
+   image->bo_handle = handle;
+
+   /* XXX extract depth and bpp from image somehow */
+   ret = drmModeAddFB(chain->fd, create_info->imageExtent.width, create_info->imageExtent.height,
+                      24, 32, row_pitch, handle, &image->fb_id);
+
+   if (ret) {
+      result = VK_ERROR_OUT_OF_HOST_MEMORY;
+      goto fail_fb;
+   }
+
+   return VK_SUCCESS;
+
+fail_fb:
+
+   if (chain->base.needs_linear_copy)
+         chain->base.image_fns->free_wsi_image(device_h, allocator,
+                                               image->linear_image, image->linear_memory);
+
+fail_linear:
+
+   chain->base.image_fns->free_wsi_image(device_h, allocator,
+                                         image->image, image->memory);
+
+   return result;
+}
+
+static void
+wsi_display_image_finish(struct wsi_swapchain           *drv_chain,
+                         const VkAllocationCallbacks    *allocator,
+                         struct wsi_display_image       *image)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+
+   if (chain->base.needs_linear_copy) {
+      chain->base.image_fns->free_wsi_image(chain->base.device, allocator,
+                                            image->linear_image, image->linear_memory);
+   }
+   chain->base.image_fns->free_wsi_image(chain->base.device, allocator,
+                                        image->image, image->memory);
+}
+
+static VkResult
+wsi_display_swapchain_destroy(struct wsi_swapchain              *drv_chain,
+                              const VkAllocationCallbacks       *allocator)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+
+   for (uint32_t i = 0; i < chain->base.image_count; i++)
+      wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);
+   vk_free(allocator, chain);
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_display_get_images(struct wsi_swapchain     *drv_chain,
+                       uint32_t                 *count,
+                       VkImage                  *images)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+   uint32_t                     ret_count;
+   VkResult                     result;
+
+   if (images == NULL) {
+      *count = chain->base.image_count;
+      return VK_SUCCESS;
+   }
+
+   result = VK_SUCCESS;
+   ret_count = chain->base.image_count;
+   if (chain->base.image_count > *count) {
+     ret_count = *count;
+     result = VK_INCOMPLETE;
+   }
+
+   for (uint32_t i = 0; i < ret_count; i++)
+      images[i] = chain->images[i].image;
+
+   return result;
+}
+
+static void
+wsi_display_get_image_and_linear(struct wsi_swapchain   *drv_chain,
+                                 int                    image_index,
+                                 VkImage                *image,
+                                 VkImage                *linear_image)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain;
+   *image = chain->images[image_index].image;
+   *linear_image = chain->images[image_index].linear_image;
+}
+
+static void
+wsi_display_page_flip_handler2(int              fd,
+                               unsigned int     frame,
+                               unsigned int     sec,
+                               unsigned int     usec,
+                               uint32_t         crtc_id,
+                               void             *data)
+{
+   struct wsi_display_image     *image = data;
+
+   /* XXX Deal with multiple CRTCs */
+   image->busy = false;
+   image->sec = sec;
+   image->usec = usec;
+}
+
+static void wsi_display_page_flip_handler(int fd, unsigned int frame,
+                                          unsigned int sec, unsigned int usec, void *data)
+{
+   wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data);
+}
+
+static uint64_t wsi_get_current_time(void)
+{
+   uint64_t current_time;
+   struct timespec tv;
+
+   clock_gettime(CLOCK_MONOTONIC, &tv);
+   current_time = tv.tv_nsec + tv.tv_sec*1000000000ull;
+   return current_time;
+}
+
+static int
+wsi_display_wait_for_event(struct wsi_swapchain         *drv_chain,
+                           uint64_t                     timeout_ns)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain;
+   int                          ret_poll;
+   int                          timeout_ms;
+   struct pollfd                pollfd;
+   drmEventContext event_context = {
+      .version = DRM_EVENT_CONTEXT_VERSION,
+      .page_flip_handler = wsi_display_page_flip_handler,
+#if DRM_EVENT_CONTEXT_VERSION >= 3
+      .page_flip_handler2 = wsi_display_page_flip_handler2,
+#endif
+   };
+
+   if (timeout_ns == UINT64_MAX)
+      timeout_ms = -1;
+   else
+      timeout_ms = timeout_ns / (1000 * 1000);
+
+   pollfd.fd = chain->fd;
+   pollfd.events = POLLIN;
+
+   ret_poll = poll(&pollfd, 1, timeout_ms);
+   (void) drmHandleEvent(chain->fd, &event_context);
+   return ret_poll;
+}
+
+static VkResult
+wsi_display_acquire_next_image(struct wsi_swapchain     *drv_chain,
+                               uint64_t                 timeout,
+                               VkSemaphore              semaphore,
+                               uint32_t                 *image_index)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain;
+   uint64_t                     abs_timeout;
+   int64_t                      timeout_remaining;
+   int                          ret = 1;
+
+   if (timeout != 0 && timeout != UINT64_MAX)
+      abs_timeout = wsi_get_current_time() + timeout;
+
+   for (;;) {
+      for (uint32_t i = 0; i < chain->base.image_count; i++) {
+         if (!chain->images[i].busy) {
+            *image_index = i;
+            chain->images[i].busy = true;
+            return VK_SUCCESS;
+         }
+      }
+
+      if (ret == 0)
+         return VK_TIMEOUT;
+      if (ret < 0)
+         return VK_ERROR_OUT_OF_DATE_KHR;
+
+      ret = wsi_display_wait_for_event(drv_chain, timeout);
+
+      if (ret < 0)
+         return VK_ERROR_OUT_OF_DATE_KHR;
+
+      if (timeout != 0 && timeout != UINT64_MAX) {
+         timeout_remaining = abs_timeout - wsi_get_current_time();
+         if (timeout_remaining < 0)
+            timeout_remaining = 0;
+         timeout = timeout_remaining;
+      }
+   }
+}
+
+static uint32_t
+wsi_display_select_crtc(struct wsi_display_connector    *connector)
+{
+   struct wsi_display   *wsi = connector->wsi;
+   int                  c;
+   drmModeResPtr        mode_res = wsi->mode_res;
+   drmModeCrtcPtr       crtc;
+   uint32_t             crtc_id;
+
+   if (!mode_res)
+      return 0;
+
+   crtc_id = 0;
+   for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
+      crtc = drmModeGetCrtc(wsi->master_fd, mode_res->crtcs[c]);
+      if (crtc && crtc->buffer_id == 0)
+         crtc_id = crtc->crtc_id;
+      drmModeFreeCrtc(crtc);
+   }
+   return crtc_id;
+}
+
+static VkResult
+wsi_display_queue_present(struct wsi_swapchain          *drv_chain,
+                          uint32_t                      image_index,
+                          const VkPresentRegionKHR      *damage)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+   struct wsi_display_image     *image = &chain->images[image_index];
+   VkIcdSurfaceDisplay          *surface = chain->surface;
+   wsi_display_mode             *mode = wsi_display_mode_from_handle(surface->displayMode);
+   wsi_display_connector        *connector = mode->connector;
+   drmModeModeInfoPtr           mode_info;
+   int                          ret;
+
+   if (!connector->crtc_id) {
+      connector->crtc_id = wsi_display_select_crtc(connector);
+      if (!connector->crtc_id)
+         return VK_ERROR_OUT_OF_DATE_KHR;
+   }
+
+   for (;;) {
+      ret = drmModePageFlip(chain->fd, connector->crtc_id, image->fb_id,
+                            DRM_MODE_PAGE_FLIP_EVENT, image);
+      if (ret == 0) {
+         image->busy = true;
+         connector->active = true;
+         return VK_SUCCESS;
+      }
+
+      if (ret) {
+         switch(-ret) {
+         case EINVAL:
+
+            /* XXX allow setting of position */
+            mode_info = wsi_display_find_mode_info(connector, mode);
+            if (!mode_info)
+               return VK_ERROR_OUT_OF_DATE_KHR;
+
+            ret = drmModeSetCrtc(chain->fd, connector->crtc_id, image->fb_id, 0, 0,
+                                 &connector->id, 1, mode_info);
+
+            if (ret == 0) {
+               image->busy = true;
+               connector->active = true;
+               return VK_SUCCESS;
+            }
+            break;
+         case EACCES:
+            usleep(1000 * 1000);
+            break;
+         default:
+            return VK_ERROR_OUT_OF_DATE_KHR;
+         }
+      }
+   }
+}
+
+static VkResult
+wsi_display_surface_create_swapchain(VkIcdSurfaceBase                   *icd_surface,
+                                     VkDevice                           device,
+                                     struct wsi_device                  *wsi_device,
+                                     int                                local_fd,
+                                     const VkSwapchainCreateInfoKHR     *create_info,
+                                     const VkAllocationCallbacks        *allocator,
+                                     const struct wsi_image_fns         *image_fns,
+                                     struct wsi_swapchain               **swapchain_out)
+{
+   VkResult result;
+   assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
+
+   struct wsi_display_swapchain *chain;
+   const unsigned num_images = create_info->minImageCount;
+   size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
+
+   chain = vk_alloc(allocator, size, 0,
+                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
+   if (chain == NULL)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   chain->base.device = device;
+   chain->base.destroy = wsi_display_swapchain_destroy;
+   chain->base.get_images = wsi_display_get_images;
+   chain->base.get_image_and_linear = wsi_display_get_image_and_linear;
+   chain->base.acquire_next_image = wsi_display_acquire_next_image;
+   chain->base.queue_present = wsi_display_queue_present;
+   chain->base.image_fns = image_fns;
+   chain->base.present_mode = create_info->presentMode;
+   chain->base.image_count = num_images;
+
+   chain->base.needs_linear_copy = false;
+
+   chain->fd = local_fd;
+
+   chain->surface = (VkIcdSurfaceDisplay *) icd_surface;
+
+   for (uint32_t image = 0; image < chain->base.image_count; image++) {
+      result = wsi_display_image_init(device, &chain->base, create_info, allocator,
+                                      &chain->images[image]);
+      if (result != VK_SUCCESS)
+         goto fail_init_images;
+   }
+
+   *swapchain_out = &chain->base;
+
+   return VK_SUCCESS;
+fail_init_images:
+   return result;
+}
+
+VkResult
+wsi_display_init_wsi(struct wsi_device *wsi_device,
+                     const VkAllocationCallbacks *alloc,
+                     VkPhysicalDevice physical_device,
+                     int master_fd,
+                     const struct wsi_callbacks *cbs)
+{
+   struct wsi_display *wsi;
+   VkResult result;
+
+   wsi = vk_alloc(alloc, sizeof(*wsi), 8,
+                   VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+   if (!wsi) {
+      result = VK_ERROR_OUT_OF_HOST_MEMORY;
+      goto fail;
+   }
+   memset(wsi, '\0', sizeof (*wsi));
+
+   wsi->master_fd = master_fd;
+   wsi->physical_device = physical_device;
+   wsi->alloc = alloc;
+   wsi->cbs = cbs;
+
+   LIST_INITHEAD(&wsi->display_modes);
+   LIST_INITHEAD(&wsi->connectors);
+
+   wsi->base.get_support = wsi_display_surface_get_support;
+   wsi->base.get_capabilities = wsi_display_surface_get_capabilities;
+   wsi->base.get_formats = wsi_display_surface_get_formats;
+   wsi->base.get_present_modes = wsi_display_surface_get_present_modes;
+   wsi->base.create_swapchain = wsi_display_surface_create_swapchain;
+
+   wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;
+
+   return VK_SUCCESS;
+
+fail:
+   return result;
+}
+
+void
+wsi_display_finish_wsi(struct wsi_device *wsi_device,
+                       const VkAllocationCallbacks *alloc)
+{
+   struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
+   if (wsi) {
+      vk_free(alloc, wsi);
+   }
+}
diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h
new file mode 100644
index 0000000000..7c3db48758
--- /dev/null
+++ b/src/vulkan/wsi/wsi_common_display.h
@@ -0,0 +1,80 @@ 
+/*
+ * Copyright © 2017 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef WSI_COMMON_DISPLAY_H
+#define WSI_COMMON_DISPLAY_H
+
+#include "wsi_common.h"
+#include <vulkan/vulkan_keithp.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#define typed_memcpy(dest, src, count) ({ \
+   STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \
+   memcpy((dest), (src), (count) * sizeof(*(src))); \
+})
+
+VkBool32
+wsi_display_get_presentation_support(struct wsi_device *wsi_device);
+
+VkResult
+wsi_display_set_master_fd(VkInstance                    instance,
+                          struct wsi_device             *wsi_device,
+                          int                           master_fd);
+VkResult
+wsi_display_get_physical_device_display_properties(VkPhysicalDevice             physical_device,
+                                                   struct wsi_device            *wsi_device,
+                                                   uint32_t                     *property_count,
+                                                   VkDisplayPropertiesKHR       *properties);
+VkResult
+wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice               physical_device,
+                                                         struct wsi_device              *wsi_device,
+                                                         uint32_t                       *property_count,
+                                                         VkDisplayPlanePropertiesKHR    *properties);
+
+VkResult
+wsi_display_get_display_plane_supported_displays(VkPhysicalDevice               physical_device,
+                                                 struct wsi_device              *wsi_device,
+                                                 uint32_t                       plane_index,
+                                                 uint32_t                       *display_count,
+                                                 VkDisplayKHR                   *displays);
+VkResult
+wsi_display_get_display_mode_properties(VkPhysicalDevice               physical_device,
+                                        struct wsi_device              *wsi_device,
+                                        VkDisplayKHR                   display,
+                                        uint32_t                       *property_count,
+                                        VkDisplayModePropertiesKHR     *properties);
+
+VkResult
+wsi_get_display_plane_capabilities(VkPhysicalDevice                     physical_device,
+                                   struct wsi_device                    *wsi_device,
+                                    VkDisplayModeKHR                    mode_khr,
+                                    uint32_t                            plane_index,
+                                    VkDisplayPlaneCapabilitiesKHR       *capabilities);
+
+VkResult
+wsi_create_display_surface(VkInstance instance,
+                           const VkAllocationCallbacks *pAllocator,
+                           const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
+                           VkSurfaceKHR *pSurface);
+
+#endif
diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
index 5613283d9d..bba389faec 100644
--- a/src/vulkan/wsi/wsi_common_wayland.c
+++ b/src/vulkan/wsi/wsi_common_wayland.c
@@ -659,7 +659,7 @@  wsi_wl_image_init(struct wsi_wl_swapchain *chain,
                                                     &size,
                                                     &offset,
                                                     &row_pitch,
-                                                    &fd);
+                                                    &fd, NULL);
    if (result != VK_SUCCESS)
       return result;
 
diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
index c399aae5af..c8659ee9c5 100644
--- a/src/vulkan/wsi/wsi_common_x11.c
+++ b/src/vulkan/wsi/wsi_common_x11.c
@@ -944,7 +944,7 @@  x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
                                                     &size,
                                                     &offset,
                                                     &row_pitch,
-                                                    &fd);
+                                                    &fd, NULL);
    if (result != VK_SUCCESS)
       return result;
 
@@ -959,7 +959,7 @@  x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
                                                        &size,
                                                        &offset,
                                                        &row_pitch,
-                                                       &fd);
+                                                       &fd, NULL);
       if (result != VK_SUCCESS) {
          chain->base.image_fns->free_wsi_image(device_h, pAllocator,
                                                image->image, image->memory);
-- 
2.11.0