[Mesa-dev] [PATCH 1/3] radv: Add VK_KHR_display, VK_KEITHP_kms_display and VK_EXT_direct_mode_display [v2]

Keith Packard keithp at keithp.com
Wed Aug 2 10:12:39 UTC 2017


Implements VK_KHR_display and VK_EXT_direct_mode_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 at keithp.com>
---
 configure.ac                                  |    3 +-
 include/vulkan/vulkan.h                       |    1 +
 include/vulkan/vulkan_keithp.h                |   40 +
 src/Makefile.am                               |    7 +
 src/amd/vulkan/Makefile.am                    |   10 +
 src/amd/vulkan/Makefile.sources               |    3 +
 src/amd/vulkan/radv_device.c                  |   73 +-
 src/amd/vulkan/radv_entrypoints_gen.py        |    3 +
 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             |  155 +++
 src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c |   18 +
 src/intel/vulkan/anv_wsi.c                    |    2 +-
 src/vulkan/Makefile.am                        |    7 +
 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           | 1255 +++++++++++++++++++++++++
 src/vulkan/wsi/wsi_common_display.h           |   77 ++
 src/vulkan/wsi/wsi_common_wayland.c           |    2 +-
 src/vulkan/wsi/wsi_common_x11.c               |    4 +-
 22 files changed, 1728 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 6302aa2b0c8..d95b915b984 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,7 +74,7 @@ AC_SUBST([OPENCL_VERSION])
 # in the first entry.
 LIBDRM_REQUIRED=2.4.75
 LIBDRM_RADEON_REQUIRED=2.4.71
-LIBDRM_AMDGPU_REQUIRED=2.4.82
+LIBDRM_AMDGPU_REQUIRED=2.4.83
 LIBDRM_INTEL_REQUIRED=2.4.75
 LIBDRM_NVVIEUX_REQUIRED=2.4.66
 LIBDRM_NOUVEAU_REQUIRED=2.4.66
@@ -1752,6 +1752,7 @@ fi
 AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$platforms" | grep -q 'x11')
 AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$platforms" | grep -q 'wayland')
 AM_CONDITIONAL(HAVE_PLATFORM_DRM, echo "$platforms" | grep -q 'drm')
+AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$platforms" | grep -q 'drm')
 AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless')
 AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android')
 
diff --git a/include/vulkan/vulkan.h b/include/vulkan/vulkan.h
index 16434fefbe6..943ff7c82b5 100644
--- a/include/vulkan/vulkan.h
+++ b/include/vulkan/vulkan.h
@@ -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 00000000000..acf59fa4e13
--- /dev/null
+++ b/include/vulkan/vulkan_keithp.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2017 Keith Packard <keithp at 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 5aee6b01417..1a0f32c9f25 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -74,6 +74,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 3350f545403..3a6ada825b5 100644
--- a/src/amd/vulkan/Makefile.am
+++ b/src/amd/vulkan/Makefile.am
@@ -76,6 +76,16 @@ VULKAN_LIB_DEPS = \
 	$(DLOPEN_LIBS) \
 	-lm
 
+if HAVE_PLATFORM_DISPLAY
+AM_CPPFLAGS += \
+	-DVK_USE_PLATFORM_DISPLAY_KHR \
+	-DVK_USE_PLATFORM_KMS_KEITHP \
+	-DVK_USE_PLATFORM_XLIB_XRANDR_EXT
+
+VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
+
+endif
+
 if HAVE_PLATFORM_X11
 AM_CPPFLAGS += \
 	$(XCB_DRI3_CFLAGS) \
diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources
index d3e0c81e9a3..1119e908d19 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 19f1e105779..da8e7a89f66 100644
--- a/src/amd/vulkan/radv_device.c
+++ b/src/amd/vulkan/radv_device.c
@@ -1,6 +1,7 @@
 /*
  * Copyright © 2016 Red Hat.
  * Copyright © 2016 Bas Nieuwenhuizen
+ * Copyright © 2017 Keith Packard
  *
  * based in part on anv driver which is:
  * Copyright © 2015 Intel Corporation
@@ -106,6 +107,22 @@ static const VkExtensionProperties instance_extensions[] = {
 		.extensionName = VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME,
 		.specVersion = 1,
 	},
+#ifdef VK_USE_PLATFORM_DISPLAY_KHR
+        {
+                .extensionName = VK_KHR_DISPLAY_EXTENSION_NAME,
+                .specVersion = VK_KHR_DISPLAY_SPEC_VERSION
+        },
+        {
+                .extensionName = VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME,
+                .specVersion = 1,
+        },
+#endif
+#ifdef VK_USE_PLATFORM_KMS_KEITHP
+        {
+                .extensionName = VK_KEITHP_KMS_DISPLAY_EXTENSION_NAME,
+                .specVersion = 1,
+        },
+#endif
 };
 
 static const VkExtensionProperties common_device_extensions[] = {
@@ -263,6 +280,12 @@ get_chip_name(enum radeon_family family)
 	}
 }
 
+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,
@@ -271,9 +294,24 @@ radv_physical_device_init(struct radv_physical_device *device,
 	const char *path = drm_device->nodes[DRM_NODE_RENDER];
 	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))
+                {
+                        fd = dup(instance->master_fd);
+                } else {
+                        return VK_ERROR_INCOMPATIBLE_DRIVER;
+                }
+        }
+
+        if (fd < 0)
+                fd = open(path, O_RDWR | O_CLOEXEC);
 	if (fd < 0)
 		return VK_ERROR_INCOMPATIBLE_DRIVER;
 
@@ -469,6 +507,20 @@ VkResult radv_CreateInstance(
 	instance->perftest_flags = parse_debug_string(getenv("RADV_PERFTEST"),
 						   radv_perftest_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;
+                        instance->master_fd = vk_kms->fd;
+                        break;
+                default:
+                        break;
+                }
+        }
+
 	*pInstance = radv_instance_to_handle(instance);
 
 	return VK_SUCCESS;
@@ -2222,6 +2274,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 9634f76fcd6..48125da2e76 100644
--- a/src/amd/vulkan/radv_entrypoints_gen.py
+++ b/src/amd/vulkan/radv_entrypoints_gen.py
@@ -57,6 +57,9 @@ SUPPORTED_EXTENSIONS = [
     'VK_KHR_external_semaphore_capabilities',
     'VK_KHR_external_semaphore',
     'VK_KHR_external_semaphore_fd',
+    'VK_KEITHP_kms_display',
+    'VK_KHR_display',
+    'VK_EXT_direct_mode_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 25afd497da0..27e1e6d6ed7 100644
--- a/src/amd/vulkan/radv_private.h
+++ b/src/amd/vulkan/radv_private.h
@@ -69,11 +69,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
@@ -287,6 +289,8 @@ struct radv_instance {
 	int                                         physicalDeviceCount;
 	struct radv_physical_device                 physicalDevices[RADV_MAX_DRM_DEVICES];
 
+        int                                         master_fd;
+
 	uint64_t debug_flags;
 	uint64_t perftest_flags;
 };
@@ -924,6 +928,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 215ef0bfc15..a68671fa152 100644
--- a/src/amd/vulkan/radv_radeon_winsys.h
+++ b/src/amd/vulkan/radv_radeon_winsys.h
@@ -172,6 +172,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 adc43111122..6b58f51cbd8 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, physical_device->local_fd,
+                                      &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 00000000000..26fb4bee803
--- /dev/null
+++ b/src/amd/vulkan/radv_wsi_display.c
@@ -0,0 +1,155 @@
+/*
+ * 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 "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_ReleaseDisplayEXT(VkPhysicalDevice physical_device,
+                       VkDisplayKHR     display)
+{
+   RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device);
+
+   return wsi_release_display(physical_device,
+                              &pdevice->wsi_device,
+                              display);
+}
+
+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 5c374a238d6..e56113cb5dc 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) {
@@ -507,6 +524,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 9369f26a8fa..991a98fa206 100644
--- a/src/intel/vulkan/anv_wsi.c
+++ b/src/intel/vulkan/anv_wsi.c
@@ -178,7 +178,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 c897a07d6a8..693b93e0c11 100644
--- a/src/vulkan/Makefile.am
+++ b/src/vulkan/Makefile.am
@@ -48,6 +48,13 @@ AM_CPPFLAGS += \
 VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES)
 endif
 
+if HAVE_PLATFORM_DISPLAY
+AM_CPPFLAGS += \
+	-DVK_USE_PLATFORM_DISPLAY
+
+VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
+endif
+
 BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES)
 CLEANFILES = $(BUILT_SOURCES)
 
diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources
index 2cf7218e926..c7bde8cc1aa 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_FILES := \
 	util/vk_alloc.h \
 	util/vk_util.c \
diff --git a/src/vulkan/registry/vk.xml b/src/vulkan/registry/vk.xml
index a3e142f7bad..bbf33c817e0 100644
--- a/src/vulkan/registry/vk.xml
+++ b/src/vulkan/registry/vk.xml
@@ -61,6 +61,7 @@ private version is maintained in the 1.0 branch of the member gitlab server.
         <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 -->
@@ -1547,6 +1548,16 @@ private version is maintained in the 1.0 branch of the member gitlab server.
             <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 -->
@@ -5456,6 +5467,13 @@ private version is maintained in the 1.0 branch of the member gitlab server.
                 <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=""VK_KEITHP_kms_display""         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 8166b7dd344..9ce3b071122 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,
@@ -112,7 +113,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];
@@ -170,5 +171,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 master_fd, int render_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 00000000000..1ec0004be91
--- /dev/null
+++ b/src/vulkan/wsi/wsi_common_display.c
@@ -0,0 +1,1255 @@
+/*
+ * 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 <stdbool.h>
+#include <math.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"
+
+#if 0
+#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)
+#define wsi_display_debug_code(...)     __VA_ARGS__
+#else
+#define wsi_display_debug(...)
+#define wsi_display_debug_code(...)
+#endif
+
+/* 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;
+   uint32_t                     edid_length;
+   uint8_t                      *edid;
+} wsi_display_connector;
+
+struct wsi_display {
+   struct wsi_interface         base;
+
+   const VkAllocationCallbacks  *alloc;
+   VkPhysicalDevice             physical_device;
+
+   const struct wsi_callbacks   *cbs;
+
+   int                          master_fd;
+
+   pthread_mutex_t              wait_mutex;
+   pthread_cond_t               wait_cond;
+   pthread_t                    wait_thread;
+
+   struct list_head             connectors;
+
+   struct list_head             display_modes;
+
+   drmModeResPtr                mode_res;
+};
+
+enum wsi_image_state {
+   wsi_image_idle = 0,
+   wsi_image_drawing = 1,
+   wsi_image_flipping = 2,
+   wsi_image_displaying = 3
+};
+
+struct wsi_display_image {
+   struct wsi_display_swapchain *chain;
+   VkImage                      image;
+   VkImage                      linear_image;
+   VkDeviceMemory               memory;
+   VkDeviceMemory               linear_memory;
+   enum wsi_image_state         state;
+   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;
+   struct wsi_display           *wsi;
+   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)
+
+static bool
+same_mode(drmModeModeInfoPtr    a,
+          drmModeModeInfoPtr    b)
+{
+   return memcmp(a, b, sizeof (drmModeModeInfo)) == 0;
+}
+
+static uint64_t wsi_get_current_realtime(void)
+{
+   struct timespec tv;
+
+   clock_gettime(CLOCK_REALTIME, &tv);
+   return tv.tv_nsec + tv.tv_sec*1000000000ull;
+}
+
+static int
+wsi_display_wait_for_event(struct wsi_display           *wsi,
+                           uint64_t                     timeout_ns);
+
+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;
+   drmModeConnectorPtr          drm_connector;
+   int                          drm_fd = wsi->master_fd;
+   int                          p, m;
+   VkResult                     result;
+
+   drm_connector = drmModeGetConnector(drm_fd, connector_id);
+   if (!drm_connector)
+      return NULL;
+
+   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 = drm_connector;
+
+   /* Look for an EDID property */
+   for (p = 0; p < connector->connector->count_props; p++) {
+      drmModePropertyPtr prop = drmModeGetProperty(drm_fd, drm_connector->props[p]);
+      if (!prop)
+         continue;
+      if (prop->flags & DRM_MODE_PROP_BLOB) {
+         if (!strcmp(prop->name, "EDID")) {
+            drmModePropertyBlobPtr edid_blob = drmModeGetPropertyBlob(drm_fd,
+                                                                      drm_connector->prop_values[p]);
+            if (edid_blob) {
+               uint8_t  *edid = vk_alloc(wsi->alloc, edid_blob->length, 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+               if (edid) {
+                  memcpy(edid, edid_blob->data, edid_blob->length);
+                  if (connector->edid)
+                     vk_free(wsi->alloc, connector->edid);
+                  connector->edid_length = edid_blob->length;
+                  connector->edid = edid;
+               }
+            }
+            /* XXX dig name out of EDID data */
+         }
+      }
+      drmModeFreeProperty(prop);
+   }
+
+   /* XXX use EDID name */
+   connector->name = "monitor";
+
+   /* Register modes */
+   for (m = 0; m < drm_connector->count_modes; m++) {
+      result = wsi_display_register_mode(wsi_device,
+                                         connector,
+                                         &drm_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                          drm_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(drm_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_release_display(VkPhysicalDevice            physical_device,
+                    struct wsi_device           *wsi_device,
+                    VkDisplayKHR                display)
+{
+   struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+
+   pthread_mutex_lock(&wsi->wait_mutex);
+   if (wsi->wait_thread) {
+      pthread_cancel(wsi->wait_thread);
+      pthread_join(wsi->wait_thread, NULL);
+   }
+   pthread_mutex_unlock(&wsi->wait_mutex);
+   pthread_mutex_destroy(&wsi->wait_mutex);
+   pthread_cond_destroy(&wsi->wait_cond);
+
+   if (!wsi->master_is_render && wsi->master_fd >= 0)
+      close(wsi->master_fd);
+   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;
+   struct wsi_display           *wsi = chain->wsi;
+   VkResult                     result;
+   uint32_t                     row_pitch;
+   uint32_t                     offset;
+   uint32_t                     handle;
+   uint32_t                     size;
+   int                          ret;
+
+   memset(image, '\0', sizeof (*image));
+   image->chain = chain;
+   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(wsi->master_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_idle_old_displaying(struct wsi_display_image *active_image)
+{
+   struct wsi_display_swapchain *chain = active_image->chain;
+
+   wsi_display_debug("idle everyone but %ld\n", active_image - &(chain->images[0]));
+   for (uint32_t i = 0; i < chain->base.image_count; i++)
+      if (chain->images[i].state == wsi_image_displaying && &chain->images[i] != active_image) {
+         wsi_display_debug("idle %d\n", i);
+         chain->images[i].state = wsi_image_idle;
+      }
+}
+
+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;
+
+   wsi_display_debug("image %ld displayed at %d\n", image - &(image->chain->images[0]), frame);
+   image->state = wsi_image_displaying;
+   image->sec = sec;
+   image->usec = usec;
+   wsi_display_idle_old_displaying(image);
+}
+
+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 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
+};
+
+static void *
+wsi_display_wait_thread(void *data)
+{
+   struct wsi_display   *wsi = data;
+   struct pollfd pollfd = {
+      .fd = wsi->master_fd,
+      .events = POLLIN
+   };
+   int ret;
+
+   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+   for (;;) {
+      ret = poll(&pollfd, 1, -1);
+      if (ret > 0) {
+         pthread_mutex_lock(&wsi->wait_mutex);
+         (void) drmHandleEvent(wsi->master_fd, &event_context);
+         pthread_mutex_unlock(&wsi->wait_mutex);
+         pthread_cond_broadcast(&wsi->wait_cond);
+      }
+   }
+   return NULL;
+}
+
+static int
+wsi_display_wait_for_event(struct wsi_display           *wsi,
+                           uint64_t                     timeout_ns)
+{
+   int ret;
+
+   if (!wsi->wait_thread) {
+      ret = pthread_create(&wsi->wait_thread, NULL, wsi_display_wait_thread, wsi);
+      if (ret)
+         return ret;
+   }
+
+   struct timespec abs_timeout = {
+      .tv_sec = timeout_ns / ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000),
+      .tv_nsec = timeout_ns % ((uint64_t) 1000 * (uint64_t) 1000 * (uint64_t) 1000)
+   };
+
+   ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex, &abs_timeout);
+
+   wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret);
+   return ret;
+}
+
+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;
+   struct wsi_display           *wsi = chain->wsi;
+   int                          ret = 0;
+
+   if (timeout != 0 && timeout != UINT64_MAX)
+      timeout += wsi_get_current_realtime();
+
+   for (;;) {
+      for (uint32_t i = 0; i < chain->base.image_count; i++) {
+         if (chain->images[i].state == wsi_image_idle) {
+            *image_index = i;
+            wsi_display_debug("image %d available\n", i);
+            chain->images[i].state = wsi_image_drawing;
+            return VK_SUCCESS;
+         }
+         wsi_display_debug("image %d state %d\n", i, chain->images[i].state);
+      }
+
+      if (ret == ETIMEDOUT)
+         return VK_TIMEOUT;
+
+      ret = wsi_display_wait_for_event(wsi, timeout);
+
+      if (ret && ret != ETIMEDOUT)
+         return VK_ERROR_OUT_OF_DATE_KHR;
+   }
+}
+
+/*
+ * Check whether there are any other connectors driven by this crtc
+ */
+static bool
+wsi_display_crtc_solo(struct wsi_display        *wsi,
+                      drmModeConnectorPtr       connector,
+                      uint32_t                  crtc_id)
+{
+   drmModeResPtr        mode_res = wsi->mode_res;
+   int                  drm_fd = wsi->master_fd;
+   int                  c, e;
+
+   /* See if any other connectors share the same encoder */
+   for (c = 0; c < mode_res->count_connectors; c++) {
+      if (mode_res->connectors[c] == connector->connector_id)
+         continue;
+
+      drmModeConnectorPtr       other_connector = drmModeGetConnector(drm_fd, mode_res->connectors[c]);
+      if (other_connector) {
+         bool                      match = (other_connector->encoder_id == connector->encoder_id);
+         drmModeFreeConnector(other_connector);
+         if (match)
+            return false;
+      }
+   }
+
+   /* See if any other encoders share the same crtc */
+   for (e = 0; e < mode_res->count_encoders; e++) {
+      if (mode_res->encoders[e] == connector->encoder_id)
+         continue;
+
+      drmModeEncoderPtr         other_encoder = drmModeGetEncoder(drm_fd, mode_res->encoders[e]);
+      if (other_encoder) {
+         bool                      match = (other_encoder->crtc_id == crtc_id);
+         drmModeFreeEncoder(other_encoder);
+         if (match)
+            return false;
+      }
+   }
+   return true;
+}
+
+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;
+   int                  drm_fd = wsi->master_fd;
+   uint32_t             crtc_id;
+
+   if (!mode_res)
+      return 0;
+
+   /* See what CRTC is currently driving this connector */
+   if (connector->connector && connector->connector->encoder_id) {
+      drmModeEncoderPtr encoder = drmModeGetEncoder(drm_fd, connector->connector->encoder_id);
+      if (encoder) {
+         crtc_id = encoder->crtc_id;
+         drmModeFreeEncoder(encoder);
+         if (crtc_id) {
+            if (wsi_display_crtc_solo(wsi, connector->connector, crtc_id))
+               return crtc_id;
+         }
+      }
+   }
+   crtc_id = 0;
+   for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
+      drmModeCrtcPtr crtc = drmModeGetCrtc(drm_fd, mode_res->crtcs[c]);
+      if (crtc && crtc->buffer_id == 0)
+         crtc_id = crtc->crtc_id;
+      drmModeFreeCrtc(crtc);
+   }
+   return crtc_id;
+}
+
+static void
+wsi_display_wait_flip_idle(struct wsi_swapchain         *drv_chain)
+{
+   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
+   struct wsi_display           *wsi = chain->wsi;
+   int ret;
+   uint64_t timeout = 0;
+
+   wsi_display_debug("%9lu wait flip idle\n", pthread_self());
+
+   wsi_display_debug_code(uint64_t start_ns = wsi_get_current_realtime());
+   pthread_mutex_lock(&wsi->wait_mutex);
+   for (;;) {
+      bool busy = false;
+      for (uint32_t i = 0; i < chain->base.image_count; i++)
+         if (chain->images[i].state == wsi_image_flipping) {
+            wsi_display_debug("image %d flipping\n", i);
+            busy = true;
+            break;
+         }
+      if (!busy)
+         break;
+
+      /* wait for at most 100ms; a flip shouldn't take that long... */
+      if (!timeout)
+         timeout = wsi_get_current_realtime() + 100000000ull;
+
+      ret = wsi_display_wait_for_event(wsi, timeout);
+
+      if (ret != 0) {
+         wsi_display_debug("wait for flip idle failed %d %s\n", ret, strerror(ret));
+         break;
+      }
+   }
+
+   wsi_display_debug("%9lu wait flip idle %f ms\n", pthread_self(), ((int64_t) (wsi_get_current_realtime() - start_ns)) / 1.0e6);
+   pthread_mutex_unlock(&wsi->wait_mutex);
+}
+
+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           *wsi = chain->wsi;
+   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;
+   }
+
+   assert(image->state == wsi_image_drawing);
+   wsi_display_debug("present %d\n", image_index);
+   for (;;) {
+      if (connector->active) {
+         wsi_display_wait_flip_idle(drv_chain);
+         ret = drmModePageFlip(wsi->master_fd, connector->crtc_id, image->fb_id,
+                               DRM_MODE_PAGE_FLIP_EVENT, image);
+         if (ret == 0) {
+            image->state = wsi_image_flipping;
+            return VK_SUCCESS;
+         }
+         wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret));
+      } else
+         ret = -EINVAL;
+
+      if (ret) {
+         switch(-ret) {
+         case EINVAL:
+
+            /* XXX allow setting of position */
+            mode_info = wsi_display_find_mode_info(connector, mode);
+            if (!mode_info) {
+               image->state = wsi_image_idle;
+               return VK_ERROR_OUT_OF_DATE_KHR;
+            }
+
+            ret = drmModeSetCrtc(wsi->master_fd, connector->crtc_id, image->fb_id, 0, 0,
+                                 &connector->id, 1, mode_info);
+
+            if (ret == 0) {
+               image->state = wsi_image_displaying;
+               wsi_display_idle_old_displaying(image);
+               connector->active = true;
+               return VK_SUCCESS;
+            }
+            break;
+         case EACCES:
+            usleep(1000 * 1000);
+            connector->active = false;
+            break;
+         default:
+            connector->active = false;
+            image->state = wsi_image_idle;
+            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)
+{
+   struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
+   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->wsi = wsi;
+
+   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,
+                     int render_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;
+   pthread_mutex_init(&wsi->wait_mutex, NULL);
+   wsi->physical_device = physical_device;
+   wsi->alloc = alloc;
+   wsi->cbs = cbs;
+
+   LIST_INITHEAD(&wsi->display_modes);
+   LIST_INITHEAD(&wsi->connectors);
+
+   pthread_mutex_init(&wsi->wait_mutex, NULL);
+   pthread_cond_init(&wsi->wait_cond, NULL);
+
+   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 00000000000..173c019c4bd
--- /dev/null
+++ b/src/vulkan/wsi/wsi_common_display.h
@@ -0,0 +1,77 @@
+/*
+ * 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))); \
+})
+
+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_release_display(VkPhysicalDevice            physical_device,
+                    struct wsi_device           *wsi_device,
+                    VkDisplayKHR                display);
+
+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 dd283a12111..f30c6e01ff2 100644
--- a/src/vulkan/wsi/wsi_common_wayland.c
+++ b/src/vulkan/wsi/wsi_common_wayland.c
@@ -708,7 +708,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 ecdaf914344..6ecfd3ee510 100644
--- a/src/vulkan/wsi/wsi_common_x11.c
+++ b/src/vulkan/wsi/wsi_common_x11.c
@@ -975,7 +975,7 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
                                                     &size,
                                                     &offset,
                                                     &row_pitch,
-                                                    &fd);
+                                                    &fd, NULL);
    if (result != VK_SUCCESS)
       return result;
 
@@ -990,7 +990,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



More information about the mesa-dev mailing list