Re: [PATCH 1/7] vulkan: Add KHR_display extension to anv and radv using DRM

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

 



On Friday, 2018-02-09 20:45:10 -0800, Keith Packard wrote:
> This adds support for the KHR_display extension to the anv and radv
> Vulkan drivers. The drivers now attempt to open the master DRM node
> when the KHR_display extension is requested so that the common winsys
> code can perform the necessary operations.
> 
> Signed-off-by: Keith Packard <keithp@xxxxxxxxxx>
> ---
>  configure.ac                           |    1 +
>  meson.build                            |    4 +-
>  src/amd/vulkan/Makefile.am             |    8 +
>  src/amd/vulkan/Makefile.sources        |    3 +
>  src/amd/vulkan/meson.build             |    7 +
>  src/amd/vulkan/radv_device.c           |   28 +-
>  src/amd/vulkan/radv_extensions.py      |    7 +-
>  src/amd/vulkan/radv_private.h          |    2 +
>  src/amd/vulkan/radv_wsi.c              |    3 +-
>  src/amd/vulkan/radv_wsi_display.c      |  143 ++++
>  src/intel/Makefile.sources             |    3 +
>  src/intel/Makefile.vulkan.am           |    7 +
>  src/intel/vulkan/anv_device.c          |   18 +-
>  src/intel/vulkan/anv_extensions.py     |    1 +
>  src/intel/vulkan/anv_extensions_gen.py |    5 +-
>  src/intel/vulkan/anv_wsi.c             |    3 +-
>  src/intel/vulkan/anv_wsi_display.c     |  129 +++
>  src/intel/vulkan/meson.build           |    7 +
>  src/vulkan/Makefile.am                 |    7 +
>  src/vulkan/Makefile.sources            |    4 +
>  src/vulkan/wsi/meson.build             |   10 +
>  src/vulkan/wsi/wsi_common.c            |   19 +-
>  src/vulkan/wsi/wsi_common.h            |    5 +-
>  src/vulkan/wsi/wsi_common_display.c    | 1368 ++++++++++++++++++++++++++++++++
>  src/vulkan/wsi/wsi_common_display.h    |   72 ++
>  src/vulkan/wsi/wsi_common_private.h    |   10 +
>  26 files changed, 1858 insertions(+), 16 deletions(-)
>  create mode 100644 src/amd/vulkan/radv_wsi_display.c
>  create mode 100644 src/intel/vulkan/anv_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 8ed606c7694..46318365603 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1849,6 +1849,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')

copy/paste error: s/drm/display/

>  AM_CONDITIONAL(HAVE_PLATFORM_SURFACELESS, echo "$platforms" | grep -q 'surfaceless')
>  AM_CONDITIONAL(HAVE_PLATFORM_ANDROID, echo "$platforms" | grep -q 'android')
>  
> diff --git a/meson.build b/meson.build
> index b39e2f8ab96..aeb7f5e2917 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -239,11 +239,12 @@ with_platform_wayland = false
>  with_platform_x11 = false
>  with_platform_drm = false
>  with_platform_surfaceless = false
> +with_platform_display = false
>  egl_native_platform = ''
>  _platforms = get_option('platforms')
>  if _platforms == 'auto'
>    if system_has_kms_drm
> -    _platforms = 'x11,wayland,drm,surfaceless'
> +    _platforms = 'x11,wayland,drm,surfaceless,display'
>    elif ['darwin', 'windows', 'cygwin'].contains(host_machine.system())
>      _platforms = 'x11,surfaceless'
>    else
> @@ -257,6 +258,7 @@ if _platforms != ''
>    with_platform_wayland = _split.contains('wayland')
>    with_platform_drm = _split.contains('drm')
>    with_platform_surfaceless = _split.contains('surfaceless')
> +  with_platform_display = _split.contains('display')
>    egl_native_platform = _split[0]
>  endif
>  
> diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am
> index 61025968942..061b8144b88 100644
> --- a/src/amd/vulkan/Makefile.am
> +++ b/src/amd/vulkan/Makefile.am
> @@ -76,6 +76,14 @@ VULKAN_LIB_DEPS = \
>  	$(DLOPEN_LIBS) \
>  	-lm
>  
> +if HAVE_PLATFORM_DISPLAY
> +AM_CPPFLAGS += \
> +	-DVK_USE_PLATFORM_DISPLAY_KHR
> +
> +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 a510d88d965..618a6cdaed0 100644
> --- a/src/amd/vulkan/Makefile.sources
> +++ b/src/amd/vulkan/Makefile.sources
> @@ -78,6 +78,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/meson.build b/src/amd/vulkan/meson.build
> index 0a7b7c0bf3c..b7bb1075e7d 100644
> --- a/src/amd/vulkan/meson.build
> +++ b/src/amd/vulkan/meson.build
> @@ -112,6 +112,13 @@ if with_platform_wayland
>    libradv_files += files('radv_wsi_wayland.c')
>  endif
>  
> +if with_platform_display
> +  radv_flags += [
> +    '-DVK_USE_PLATFORM_DISPLAY_KHR',
> +  ]

Nit: this can be a simple
  radv_flags += '-DVK_USE_PLATFORM_DISPLAY_KHR'

> +  libradv_files += files('radv_wsi_display.c')
> +endif
> +
>  libvulkan_radeon = shared_library(
>    'vulkan_radeon',
>    [libradv_files, radv_entrypoints, radv_extensions_c, vk_format_table_c],
> diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c
> index 09bb382eeb8..adf33eb35dc 100644
> --- a/src/amd/vulkan/radv_device.c
> +++ b/src/amd/vulkan/radv_device.c
> @@ -191,9 +191,26 @@ 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;
> +
> +        if (instance->khr_display_requested) {
> +                fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
> +                if (fd >= 0) {
> +                        uint32_t accel_working = 0;
> +                        struct drm_amdgpu_info request = {
> +                                .return_pointer = (uintptr_t)&accel_working,
> +                                .return_size = sizeof(accel_working),
> +                                .query = AMDGPU_INFO_ACCEL_WORKING
> +                        };
> +
> +                        if (drmCommandWrite(fd, DRM_AMDGPU_INFO, &request, sizeof (struct drm_amdgpu_info)) < 0 || !accel_working) {
> +                                close(fd);
> +                                fd = -1;
> +                        }
> +                }
> +        }
> +        if (fd < 0)
> +                fd = open(path, O_RDWR | O_CLOEXEC);

Nit: src/amd/vulkan/ uses tabs for indent, you used spaces.

>  	if (fd < 0)
>  		return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER);
>  
> @@ -209,6 +226,7 @@ radv_physical_device_init(struct radv_physical_device *device,
>  		close(fd);
>  		return VK_ERROR_INCOMPATIBLE_DRIVER;
>  	}
> +
>  	drmFreeVersion(version);
>  
>  	device->_loader_data.loaderMagic = ICD_LOADER_MAGIC;
> @@ -387,6 +405,7 @@ VkResult radv_CreateInstance(
>  {
>  	struct radv_instance *instance;
>  	VkResult result;
> +        bool khr_display_requested = false;
>  
>  	assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
>  
> @@ -411,6 +430,8 @@ VkResult radv_CreateInstance(
>  	        const char *ext_name = pCreateInfo->ppEnabledExtensionNames[i];
>  		if (!radv_instance_extension_supported(ext_name))
>  			return vk_error(VK_ERROR_EXTENSION_NOT_PRESENT);
> +                if (strcmp(ext_name, VK_KHR_DISPLAY_EXTENSION_NAME) == 0)
> +                        khr_display_requested = true;
>  	}
>  
>  	instance = vk_zalloc2(&default_alloc, pAllocator, sizeof(*instance), 8,
> @@ -427,6 +448,7 @@ VkResult radv_CreateInstance(
>  
>  	instance->apiVersion = client_version;
>  	instance->physicalDeviceCount = -1;
> +        instance->khr_display_requested = khr_display_requested;
>  
>  	result = vk_debug_report_instance_init(&instance->debug_report_callbacks);
>  	if (result != VK_SUCCESS) {
> diff --git a/src/amd/vulkan/radv_extensions.py b/src/amd/vulkan/radv_extensions.py
> index d761895d3a0..24cab8cbb39 100644
> --- a/src/amd/vulkan/radv_extensions.py
> +++ b/src/amd/vulkan/radv_extensions.py
> @@ -81,6 +81,7 @@ EXTENSIONS = [
>      Extension('VK_KHR_wayland_surface',                   6, 'VK_USE_PLATFORM_WAYLAND_KHR'),
>      Extension('VK_KHR_xcb_surface',                       6, 'VK_USE_PLATFORM_XCB_KHR'),
>      Extension('VK_KHR_xlib_surface',                      6, 'VK_USE_PLATFORM_XLIB_KHR'),
> +    Extension('VK_KHR_display',                           1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
>      Extension('VK_KHX_multiview',                         1, '!ANDROID'),
>      Extension('VK_EXT_debug_report',                      9, True),
>      Extension('VK_EXT_discard_rectangles',                1, True),
> @@ -168,7 +169,7 @@ _TEMPLATE = Template(COPYRIGHT + """
>  #include "vk_util.h"
>  
>  /* Convert the VK_USE_PLATFORM_* defines to booleans */
> -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']:
> +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']:
>  #ifdef VK_USE_PLATFORM_${platform}_KHR
>  #   undef VK_USE_PLATFORM_${platform}_KHR
>  #   define VK_USE_PLATFORM_${platform}_KHR true
> @@ -187,7 +188,9 @@ _TEMPLATE = Template(COPYRIGHT + """
>  
>  #define RADV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\
>                           VK_USE_PLATFORM_XCB_KHR || \\
> -                         VK_USE_PLATFORM_XLIB_KHR)
> +                         VK_USE_PLATFORM_XLIB_KHR || \\
> +                         VK_USE_PLATFORM_DISPLAY_KHR)
> +
>  
>  bool
>  radv_instance_extension_supported(const char *name)
> diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h
> index be9e8f43964..1e3719bcc4f 100644
> --- a/src/amd/vulkan/radv_private.h
> +++ b/src/amd/vulkan/radv_private.h
> @@ -75,6 +75,7 @@ typedef uint32_t xcb_window_t;
>  #include "radv_entrypoints.h"
>  
>  #include "wsi_common.h"
> +#include "wsi_common_display.h"
>  
>  #define ATI_VENDOR_ID 0x1002
>  
> @@ -300,6 +301,7 @@ struct radv_instance {
>  	uint64_t perftest_flags;
>  
>  	struct vk_debug_report_instance             debug_report_callbacks;
> +        bool                                        khr_display_requested;
>  };
>  
>  VkResult radv_init_wsi(struct radv_physical_device *physical_device);
> diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c
> index e016e837102..5ec872a63d0 100644
> --- a/src/amd/vulkan/radv_wsi.c
> +++ b/src/amd/vulkan/radv_wsi.c
> @@ -41,7 +41,8 @@ radv_init_wsi(struct radv_physical_device *physical_device)
>  	return wsi_device_init(&physical_device->wsi_device,
>  			       radv_physical_device_to_handle(physical_device),
>  			       radv_wsi_proc_addr,
> -			       &physical_device->instance->alloc);
> +			       &physical_device->instance->alloc,
> +                               physical_device->local_fd);
>  }
>  
>  void
> diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c
> new file mode 100644
> index 00000000000..b0a4db0344b
> --- /dev/null
> +++ b/src/amd/vulkan/radv_wsi_display.c
> @@ -0,0 +1,143 @@
> +/*
> + * 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 "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)

unused

> +
> +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/intel/Makefile.sources b/src/intel/Makefile.sources
> index 9595bf42582..6c142729d94 100644
> --- a/src/intel/Makefile.sources
> +++ b/src/intel/Makefile.sources
> @@ -240,6 +240,9 @@ VULKAN_WSI_WAYLAND_FILES := \
>  VULKAN_WSI_X11_FILES := \
>  	vulkan/anv_wsi_x11.c
>  
> +VULKAN_WSI_DISPLAY_FILES := \
> +	vulkan/anv_wsi_display.c
> +
>  VULKAN_GEM_FILES := \
>  	vulkan/anv_gem.c
>  
> diff --git a/src/intel/Makefile.vulkan.am b/src/intel/Makefile.vulkan.am
> index 23fa877e77d..7c428a799d7 100644
> --- a/src/intel/Makefile.vulkan.am
> +++ b/src/intel/Makefile.vulkan.am
> @@ -187,6 +187,13 @@ VULKAN_SOURCES += $(VULKAN_WSI_WAYLAND_FILES)
>  VULKAN_LIB_DEPS += $(WAYLAND_CLIENT_LIBS)
>  endif
>  
> +if HAVE_PLATFORM_DISPLAY
> +VULKAN_CPPFLAGS += \
> +	-DVK_USE_PLATFORM_DISPLAY_KHR
> +
> +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES)
> +endif
> +
>  noinst_LTLIBRARIES += vulkan/libvulkan_common.la
>  vulkan_libvulkan_common_la_SOURCES = $(VULKAN_SOURCES)
>  vulkan_libvulkan_common_la_CFLAGS = $(VULKAN_CFLAGS)
> diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
> index 86c1bdc1d51..9614907fda3 100644
> --- a/src/intel/vulkan/anv_device.c
> +++ b/src/intel/vulkan/anv_device.c
> @@ -277,14 +277,25 @@ anv_physical_device_init_uuids(struct anv_physical_device *device)
>  static VkResult
>  anv_physical_device_init(struct anv_physical_device *device,
>                           struct anv_instance *instance,
> -                         const char *path)
> +                         const char *primary_path,
> +                         const char *render_path)
>  {
>     VkResult result;
> -   int fd;
> +   int fd = -1;
> +   const char *path;
>  
>     brw_process_intel_debug_variable();
>  
> -   fd = open(path, O_RDWR | O_CLOEXEC);
> +   if (instance->enabled_extensions.KHR_display) {
> +      path = primary_path;
> +      fd = open(path, O_RDWR | O_CLOEXEC);
> +   }
> +
> +   if (fd < 0) {
> +      path = render_path;
> +      fd = open(path, O_RDWR | O_CLOEXEC);
> +   }
> +
>     if (fd < 0)
>        return vk_error(VK_ERROR_INCOMPATIBLE_DRIVER);
>  
> @@ -652,6 +663,7 @@ anv_enumerate_devices(struct anv_instance *instance)
>  
>           result = anv_physical_device_init(&instance->physicalDevice,
>                          instance,
> +                        devices[i]->nodes[DRM_NODE_PRIMARY],
>                          devices[i]->nodes[DRM_NODE_RENDER]);
>           if (result != VK_ERROR_INCOMPATIBLE_DRIVER)
>              break;
> diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py
> index 581921e62a1..978a219e2b2 100644
> --- a/src/intel/vulkan/anv_extensions.py
> +++ b/src/intel/vulkan/anv_extensions.py
> @@ -83,6 +83,7 @@ EXTENSIONS = [
>      Extension('VK_KHR_wayland_surface',                   6, 'VK_USE_PLATFORM_WAYLAND_KHR'),
>      Extension('VK_KHR_xcb_surface',                       6, 'VK_USE_PLATFORM_XCB_KHR'),
>      Extension('VK_KHR_xlib_surface',                      6, 'VK_USE_PLATFORM_XLIB_KHR'),
> +    Extension('VK_KHR_display',                           1, 'VK_USE_PLATFORM_DISPLAY_KHR'),
>      Extension('VK_KHX_multiview',                         1, True),
>      Extension('VK_EXT_debug_report',                      8, True),
>      Extension('VK_EXT_external_memory_dma_buf',           1, True),
> diff --git a/src/intel/vulkan/anv_extensions_gen.py b/src/intel/vulkan/anv_extensions_gen.py
> index 33827ecd015..84d07f9767a 100644
> --- a/src/intel/vulkan/anv_extensions_gen.py
> +++ b/src/intel/vulkan/anv_extensions_gen.py
> @@ -113,7 +113,7 @@ _TEMPLATE_C = Template(COPYRIGHT + """
>  #include "vk_util.h"
>  
>  /* Convert the VK_USE_PLATFORM_* defines to booleans */
> -%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB']:
> +%for platform in ['ANDROID', 'WAYLAND', 'XCB', 'XLIB', 'DISPLAY']:
>  #ifdef VK_USE_PLATFORM_${platform}_KHR
>  #   undef VK_USE_PLATFORM_${platform}_KHR
>  #   define VK_USE_PLATFORM_${platform}_KHR true
> @@ -132,7 +132,8 @@ _TEMPLATE_C = Template(COPYRIGHT + """
>  
>  #define ANV_HAS_SURFACE (VK_USE_PLATFORM_WAYLAND_KHR || \\
>                           VK_USE_PLATFORM_XCB_KHR || \\
> -                         VK_USE_PLATFORM_XLIB_KHR)
> +                         VK_USE_PLATFORM_XLIB_KHR || \\
> +                         VK_USE_PLATFORM_DISPLAY_KHR)
>  
>  const VkExtensionProperties anv_instance_extensions[ANV_INSTANCE_EXTENSION_COUNT] = {
>  %for ext in instance_extensions:
> diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c
> index 6082c3dd093..f86d83589ea 100644
> --- a/src/intel/vulkan/anv_wsi.c
> +++ b/src/intel/vulkan/anv_wsi.c
> @@ -39,7 +39,8 @@ anv_init_wsi(struct anv_physical_device *physical_device)
>     return wsi_device_init(&physical_device->wsi_device,
>                            anv_physical_device_to_handle(physical_device),
>                            anv_wsi_proc_addr,
> -                          &physical_device->instance->alloc);
> +                          &physical_device->instance->alloc,
> +                          physical_device->local_fd);
>  }
>  
>  void
> diff --git a/src/intel/vulkan/anv_wsi_display.c b/src/intel/vulkan/anv_wsi_display.c
> new file mode 100644
> index 00000000000..9b00d7f02e4
> --- /dev/null
> +++ b/src/intel/vulkan/anv_wsi_display.c
> @@ -0,0 +1,129 @@
> +/*
> + * 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 "anv_private.h"
> +#include "wsi_common.h"
> +#include "vk_format_info.h"
> +#include "vk_util.h"
> +#include "wsi_common_display.h"
> +
> +#define MM_PER_PIXEL     (1.0/96.0 * 25.4)

unused

> +
> +VkResult
> +anv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice             physical_device,
> +                                           uint32_t                     *property_count,
> +                                           VkDisplayPropertiesKHR       *properties)
> +{
> +   ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device);
> +
> +   return wsi_display_get_physical_device_display_properties(physical_device,
> +                                                             &pdevice->wsi_device,
> +                                                             property_count,
> +                                                             properties);
> +}
> +
> +VkResult
> +anv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice                physical_device,
> +                                                uint32_t                        *property_count,
> +                                                VkDisplayPlanePropertiesKHR     *properties)
> +{
> +   ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device);
> +
> +   return wsi_display_get_physical_device_display_plane_properties(physical_device,
> +                                                                   &pdevice->wsi_device,
> +                                                                   property_count,
> +                                                                   properties);
> +}
> +
> +VkResult
> +anv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice               physical_device,
> +                                         uint32_t                       plane_index,
> +                                         uint32_t                       *display_count,
> +                                         VkDisplayKHR                   *displays)
> +{
> +   ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device);
> +
> +   return wsi_display_get_display_plane_supported_displays(physical_device,
> +                                                           &pdevice->wsi_device,
> +                                                           plane_index,
> +                                                           display_count,
> +                                                           displays);
> +}
> +
> +
> +VkResult
> +anv_GetDisplayModePropertiesKHR(VkPhysicalDevice               physical_device,
> +                                 VkDisplayKHR                   display,
> +                                 uint32_t                       *property_count,
> +                                 VkDisplayModePropertiesKHR     *properties)
> +{
> +   ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device);
> +
> +   return wsi_display_get_display_mode_properties(physical_device,
> +                                                  &pdevice->wsi_device,
> +                                                  display,
> +                                                  property_count,
> +                                                  properties);
> +}
> +
> +VkResult
> +anv_CreateDisplayModeKHR(VkPhysicalDevice                      physical_device,
> +                          VkDisplayKHR                          display,
> +                          const VkDisplayModeCreateInfoKHR      *create_info,
> +                          const VkAllocationCallbacks           *allocator,
> +                          VkDisplayModeKHR                      *mode)
> +{
> +   return VK_ERROR_INITIALIZATION_FAILED;
> +}
> +
> +
> +VkResult
> +anv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice                    physical_device,
> +                                    VkDisplayModeKHR                    mode_khr,
> +                                    uint32_t                            plane_index,
> +                                    VkDisplayPlaneCapabilitiesKHR       *capabilities)
> +{
> +   ANV_FROM_HANDLE(anv_physical_device, pdevice, physical_device);
> +
> +   return wsi_get_display_plane_capabilities(physical_device,
> +                                             &pdevice->wsi_device,
> +                                             mode_khr,
> +                                             plane_index,
> +                                             capabilities);
> +}
> +
> +VkResult
> +anv_CreateDisplayPlaneSurfaceKHR(VkInstance                            _instance,
> +                                  const VkDisplaySurfaceCreateInfoKHR   *create_info,
> +                                  const VkAllocationCallbacks           *allocator,
> +                                  VkSurfaceKHR                          *surface)
> +{
> +   ANV_FROM_HANDLE(anv_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/intel/vulkan/meson.build b/src/intel/vulkan/meson.build
> index 69ec26e19b6..2e2ab8f7ecd 100644
> --- a/src/intel/vulkan/meson.build
> +++ b/src/intel/vulkan/meson.build
> @@ -171,6 +171,13 @@ if with_platform_wayland
>    libanv_files += files('anv_wsi_wayland.c')
>  endif
>  
> +if with_platform_display
> +  anv_flags += [
> +    '-DVK_USE_PLATFORM_DISPLAY_KHR',
> +  ]
> +  libanv_files += files('anv_wsi_display.c')
> +endif
> +
>  libanv_common = static_library(
>    'anv_common',
>    [libanv_files, anv_entrypoints, anv_extensions_c, anv_extensions_h],
> diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am
> index 037436c1cd7..c33ac5758f7 100644
> --- a/src/vulkan/Makefile.am
> +++ b/src/vulkan/Makefile.am
> @@ -57,6 +57,13 @@ AM_CPPFLAGS += \
>  VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES)
>  endif
>  
> +if HAVE_PLATFORM_DISPLAY
> +AM_CPPFLAGS += \
> +	-DVK_USE_PLATFORM_DISPLAY_KHR
> +
> +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 a0a24ce7de8..3642c7662c4 100644
> --- a/src/vulkan/Makefile.sources
> +++ b/src/vulkan/Makefile.sources
> @@ -17,6 +17,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_debug_report.c \
> diff --git a/src/vulkan/wsi/meson.build b/src/vulkan/wsi/meson.build
> index bd0fd3cc53e..743631a6113 100644
> --- a/src/vulkan/wsi/meson.build
> +++ b/src/vulkan/wsi/meson.build
> @@ -57,6 +57,16 @@ if with_platform_wayland
>    ]
>  endif
>  
> +if with_platform_display
> +  vulkan_wsi_args += [
> +    '-DVK_USE_PLATFORM_DISPLAY_KHR',
> +  ]

Ditto:
  vulkan_wsi_args += '-DVK_USE_PLATFORM_DISPLAY_KHR'

> +  files_vulkan_wsi += files(
> +    'wsi_common_display.c',
> +    'wsi_common_display.h',
> +  )
> +endif
> +
>  libvulkan_wsi = static_library(
>    'vulkan_wsi',
>    files_vulkan_wsi,
> diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c
> index 90ed07b7857..c0a285e5814 100644
> --- a/src/vulkan/wsi/wsi_common.c
> +++ b/src/vulkan/wsi/wsi_common.c
> @@ -29,7 +29,8 @@ VkResult
>  wsi_device_init(struct wsi_device *wsi,
>                  VkPhysicalDevice pdevice,
>                  WSI_FN_GetPhysicalDeviceProcAddr proc_addr,
> -                const VkAllocationCallbacks *alloc)
> +                const VkAllocationCallbacks *alloc,
> +                int device_fd)
>  {
>     VkResult result;
>  
> @@ -89,6 +90,19 @@ wsi_device_init(struct wsi_device *wsi,
>     }
>  #endif
>  
> +#ifdef VK_USE_PLATFORM_DISPLAY_KHR
> +   result = wsi_display_init_wsi(wsi, alloc, pdevice, device_fd);
> +   if (result != VK_SUCCESS) {
> +#ifdef VK_USE_PLATFORM_WAYLAND_KHR
> +      wsi_wl_finish_wsi(wsi, alloc);
> +#endif
> +#ifdef VK_USE_PLATFORM_XCB_KHR
> +      wsi_x11_finish_wsi(wsi, alloc);
> +#endif
> +      return result;
> +   }
> +#endif
> +
>     return VK_SUCCESS;
>  }
>  
> @@ -96,6 +110,9 @@ void
>  wsi_device_finish(struct wsi_device *wsi,
>                    const VkAllocationCallbacks *alloc)
>  {
> +#ifdef VK_USE_PLATFORM_DISPLAY_KHR
> +   wsi_display_finish_wsi(wsi, alloc);
> +#endif
>  #ifdef VK_USE_PLATFORM_WAYLAND_KHR
>     wsi_wl_finish_wsi(wsi, alloc);
>  #endif
> diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
> index 3e0d3be1c24..1cb6aaebca0 100644
> --- a/src/vulkan/wsi/wsi_common.h
> +++ b/src/vulkan/wsi/wsi_common.h
> @@ -50,7 +50,7 @@ struct wsi_memory_allocate_info {
>  
>  struct wsi_interface;
>  
> -#define VK_ICD_WSI_PLATFORM_MAX 5
> +#define VK_ICD_WSI_PLATFORM_MAX 6
>  
>  struct wsi_device {
>     VkPhysicalDeviceMemoryProperties memory_props;
> @@ -93,7 +93,8 @@ VkResult
>  wsi_device_init(struct wsi_device *wsi,
>                  VkPhysicalDevice pdevice,
>                  WSI_FN_GetPhysicalDeviceProcAddr proc_addr,
> -                const VkAllocationCallbacks *alloc);
> +                const VkAllocationCallbacks *alloc,
> +                int device_fd);
>  
>  void
>  wsi_device_finish(struct wsi_device *wsi,
> diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
> new file mode 100644
> index 00000000000..2732b1dd721
> --- /dev/null
> +++ b/src/vulkan/wsi/wsi_common_display.c
> @@ -0,0 +1,1368 @@
> +/*
> + * 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 "vk_util.h"
> +#include "wsi_common_private.h"
> +#include "wsi_common_display.h"
> +#include "wsi_common_queue.h"
> +
> +#if 0

`#if DEBUG` maybe?

> +#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)
> +#define wsi_display_debug_code(...)     __VA_ARGS__

that 2nd one is unused

> +#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;
> +   bool                         valid;          /* was found in most recent poll */
> +   bool                         preferred;
> +   uint32_t                     clock;          /* in kHz */
> +   uint16_t                     hdisplay, hsync_start, hsync_end, htotal, hskew;
> +   uint16_t                     vdisplay, vsync_start, vsync_end, vtotal, vscan;
> +   uint32_t                     flags;
> +} 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                         connected;
> +   bool                         active;
> +   wsi_display_mode             *current_mode;
> +   drmModeModeInfo              current_drm_mode;
> +} wsi_display_connector;
> +
> +struct wsi_display {
> +   struct wsi_interface         base;
> +
> +   const VkAllocationCallbacks  *alloc;
> +   VkPhysicalDevice             physical_device;
> +
> +   int                          master_fd;
> +   int                          render_fd;
> +
> +   pthread_mutex_t              wait_mutex;
> +   pthread_cond_t               wait_cond;
> +   pthread_t                    wait_thread;
> +
> +   struct list_head             connectors;
> +
> +   struct list_head             display_modes;
> +};
> +
> +enum wsi_image_state {
> +   wsi_image_idle,
> +   wsi_image_drawing,
> +   wsi_image_queued,
> +   wsi_image_flipping,
> +   wsi_image_displaying
> +};
> +
> +struct wsi_display_image {
> +   struct wsi_image             base;
> +   struct wsi_display_swapchain *chain;
> +   enum wsi_image_state         state;
> +   uint32_t                     fb_id;
> +   uint64_t                     flip_sequence;
> +};
> +
> +struct wsi_display_swapchain {
> +   struct wsi_swapchain         base;
> +   struct wsi_display           *wsi;
> +   VkIcdSurfaceDisplay          *surface;
> +   uint64_t                     flip_sequence;
> +   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
> +wsi_display_mode_matches_drm(wsi_display_mode   *wsi,
> +                             drmModeModeInfoPtr drm)
> +{
> +   return wsi->clock == drm->clock &&
> +      wsi->hdisplay == drm->hdisplay &&
> +      wsi->hsync_start == drm->hsync_start &&
> +      wsi->hsync_end == drm->hsync_end &&
> +      wsi->htotal == drm->htotal &&
> +      wsi->hskew == drm->hskew &&
> +      wsi->vdisplay == drm->vdisplay &&
> +      wsi->vsync_start == drm->vsync_start &&
> +      wsi->vsync_end == drm->vsync_end &&
> +      wsi->vtotal == drm->vtotal &&
> +      wsi->vscan == drm->vscan &&
> +      wsi->flags == drm->flags;
> +}
> +
> +static double
> +wsi_display_mode_refresh(struct wsi_display_mode        *wsi)
> +{
> +   return (double) wsi->clock * 1000.0 / ((double) wsi->htotal * (double) wsi->vtotal * (double) (wsi->vscan + 1));
> +}
> +
> +static uint64_t wsi_get_current_monotonic(void)
> +{
> +   struct timespec tv;
> +
> +   clock_gettime(CLOCK_MONOTONIC, &tv);
> +   return tv.tv_nsec + tv.tv_sec*1000000000ull;
> +}
> +
> +static struct wsi_display_mode *
> +wsi_display_find_drm_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;
> +
> +   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
> +      if (display_mode->connector == connector &&
> +          wsi_display_mode_matches_drm(display_mode, mode))
> +         return display_mode;
> +   }
> +   return NULL;
> +}
> +
> +static void
> +wsi_display_invalidate_connector_modes(struct wsi_device            *wsi_device,
> +                                       struct wsi_display_connector *connector)
> +{
> +   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
> +   struct wsi_display_mode      *display_mode;
> +
> +   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list)
> +      if (display_mode->connector == connector)
> +         display_mode->valid = false;
> +}
> +
> +static VkResult
> +wsi_display_register_drm_mode(struct wsi_device            *wsi_device,
> +                              struct wsi_display_connector *connector,
> +                              drmModeModeInfoPtr           drm_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_drm_mode(wsi_device, connector, drm_mode);
> +
> +   if (display_mode) {
> +      display_mode->valid = true;
> +      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->valid = true;
> +   display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0;
> +   display_mode->clock = drm_mode->clock; /* kHz */
> +   display_mode->hdisplay = drm_mode->hdisplay;
> +   display_mode->hsync_start = drm_mode->hsync_start;
> +   display_mode->hsync_end = drm_mode->hsync_end;
> +   display_mode->htotal = drm_mode->htotal;
> +   display_mode->hskew = drm_mode->hskew;
> +   display_mode->vdisplay = drm_mode->vdisplay;
> +   display_mode->vsync_start = drm_mode->vsync_start;
> +   display_mode->vsync_end = drm_mode->vsync_end;
> +   display_mode->vtotal = drm_mode->vtotal;
> +   display_mode->vscan = drm_mode->vscan;
> +   display_mode->flags = drm_mode->flags;
> +
> +   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_alloc_connector(struct wsi_display  *wsi,
> +                            uint32_t            connector_id)
> +{
> +   struct wsi_display_connector *connector;
> +
> +   connector = vk_alloc(wsi->alloc, sizeof (struct wsi_display_connector), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
> +   memset(connector, '\0', sizeof (*connector));
> +   connector->id = connector_id;
> +   connector->wsi = wsi;
> +   connector->active = false;
> +   /* XXX use EDID name */
> +   connector->name = "monitor";
> +   return connector;
> +}
> +
> +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;
> +   VkResult                     result;
> +   int                          m;
> +
> +   if (wsi->master_fd < 0)
> +      return NULL;
> +
> +   drm_connector = drmModeGetConnector(wsi->master_fd, connector_id);
> +   if (!drm_connector)
> +      return NULL;
> +
> +   connector = wsi_display_find_connector(wsi_device, connector_id);
> +
> +   if (!connector) {
> +      connector = wsi_display_alloc_connector(wsi, connector_id);
> +      if (!connector) {
> +         drmModeFreeConnector(drm_connector);
> +         return NULL;
> +      }
> +      LIST_ADDTAIL(&connector->list, &wsi->connectors);
> +   }
> +
> +   connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
> +
> +   /* Mark all connector modes as invalid */
> +   wsi_display_invalidate_connector_modes(wsi_device, connector);
> +
> +   /*
> +    * List current modes, adding new ones and marking existing ones as
> +    * valid
> +    */
> +   for (m = 0; m < drm_connector->count_modes; m++) {
> +      result = wsi_display_register_drm_mode(wsi_device,
> +                                             connector,
> +                                             &drm_connector->modes[m]);
> +      if (result != VK_SUCCESS) {
> +         drmModeFreeConnector(drm_connector);
> +         return NULL;
> +      }
> +   }
> +
> +   drmModeFreeConnector(drm_connector);
> +
> +   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)
> +{
> +   struct wsi_display           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
> +   struct wsi_display_mode      *display_mode, *preferred_mode = NULL;;
> +
> +   properties->display = wsi_display_connector_to_handle(connector);
> +   properties->displayName = connector->name;
> +
> +   /* Find the preferred mode and assume that's the physical resolution */
> +
> +   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
> +      if (display_mode->valid && display_mode->connector == connector && display_mode->preferred) {
> +         preferred_mode = display_mode;
> +         break;
> +      }
> +   }
> +
> +   if (preferred_mode) {
> +      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 (VK_KHR_display)
> + */
> +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];
> +   struct wsi_display_connector *connector;
> +   int                          c;
> +   uint32_t                     connected;
> +   uint32_t                     property_count_requested = *property_count;
> +   drmModeResPtr                mode_res;
> +
> +   if (wsi->master_fd < 0)
> +      return VK_ERROR_INITIALIZATION_FAILED;
> +
> +   mode_res = drmModeGetResources(wsi->master_fd);
> +
> +   if (!mode_res)
> +      return VK_ERROR_INITIALIZATION_FAILED;
> +
> +   connected = 0;
> +
> +   /* Get current information */
> +   for (c = 0; c < mode_res->count_connectors; c++) {
> +      connector = wsi_display_get_connector(wsi_device, mode_res->connectors[c]);
> +
> +      if (!connector) {
> +         drmModeFreeResources(mode_res);
> +         return VK_ERROR_OUT_OF_HOST_MEMORY;
> +      }
> +
> +      if (connector->connected)
> +         connected++;
> +   }
> +
> +   /* Fill in property information if requested */
> +   if (properties != NULL) {
> +      connected = 0;
> +
> +      for (c = 0; c < mode_res->count_connectors; c++) {
> +         connector  = wsi_display_find_connector(wsi_device, mode_res->connectors[c]);
> +
> +         if (connector && connector->connected) {
> +            if (connected < property_count_requested) {
> +               wsi_display_fill_in_display_properties(wsi_device,
> +                                                      connector,
> +                                                      &properties[connected]);
> +            }
> +            connected++;
> +         }
> +      }
> +   }
> +
> +   drmModeFreeResources(mode_res);
> +
> +   *property_count = connected;
> +
> +   if (connected > property_count_requested && properties != NULL)
> +      return VK_INCOMPLETE;
> +
> +   return VK_SUCCESS;
> +}
> +
> +/*
> + * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display
> + */
> +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;
> +   uint32_t                     property_count_requested = *property_count;
> +   int                          c;
> +
> +   if (!properties)
> +      property_count_requested = 0;
> +
> +   c = 0;
> +   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
> +      if (c < property_count_requested) {
> +         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;
> +         }
> +      }
> +      c++;
> +   }
> +
> +   *property_count = c;
> +
> +   if (c > property_count_requested && properties != NULL)
> +      return VK_INCOMPLETE;
> +
> +   return VK_SUCCESS;
> +}
> +
> +/*
> + * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display)
> + */
> +
> +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;
> +   int                          c;
> +
> +
> +   if (displays == NULL) {
> +      *display_count = 1;
> +      return VK_SUCCESS;
> +   }
> +
> +   if (*display_count < 1)
> +      return VK_INCOMPLETE;
> +
> +   c = 0;
> +   LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) {
> +      if (c == plane_index) {
> +         *displays = wsi_display_connector_to_handle(connector);
> +         *display_count = 1;
> +         return VK_SUCCESS;
> +      }
> +      c++;
> +   }
> +
> +   *displays = 0;
> +   *display_count = 0;
> +
> +   return VK_SUCCESS;
> +}
> +
> +/*
> + * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)
> + */
> +
> +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           *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
> +   struct wsi_display_connector *connector = wsi_display_connector_from_handle(display);
> +   int                          i;
> +   struct wsi_display_mode      *display_mode;
> +   uint32_t                     property_count_requested = *property_count;
> +
> +   i = 0;
> +
> +   if (properties == NULL)
> +      property_count_requested = 0;
> +
> +   LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) {
> +      if (display_mode->valid && display_mode->connector == connector) {
> +         if (i < property_count_requested) {
> +            properties[i].displayMode = wsi_display_mode_to_handle(display_mode);
> +            properties[i].parameters.visibleRegion.width = display_mode->hdisplay;
> +            properties[i].parameters.visibleRegion.height = display_mode->vdisplay;
> +            properties[i].parameters.refreshRate = (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);
> +         }
> +         i++;
> +      }
> +   }
> +
> +   *property_count = i;
> +
> +   if (i > property_count_requested && properties != NULL)
> +      return VK_INCOMPLETE;
> +
> +   return VK_SUCCESS;
> +
> +}
> +
> +/*
> + * Implement vkGetDisplayPlaneCapabilities
> + */
> +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->hdisplay;
> +   capabilities->minSrcExtent.height = mode->vdisplay;
> +   capabilities->maxSrcExtent.width = mode->hdisplay;
> +   capabilities->maxSrcExtent.height = mode->vdisplay;
> +   capabilities->minDstPosition.x = 0;
> +   capabilities->minDstPosition.y = 0;
> +   capabilities->maxDstPosition.x = 0;
> +   capabilities->maxDstPosition.y = 0;
> +   capabilities->minDstExtent.width = mode->hdisplay;
> +   capabilities->minDstExtent.height = mode->vdisplay;
> +   capabilities->maxDstExtent.width = mode->hdisplay;
> +   capabilities->maxDstExtent.height = 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,
> +                                VkBool32* pSupported)
> +{
> +   *pSupported = VK_TRUE;
> +   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->hdisplay;
> +   caps->currentExtent.height = 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 VkResult
> +wsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
> +                                      const void *info_next,
> +                                      VkSurfaceCapabilities2KHR *caps)
> +{
> +   assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
> +
> +   return wsi_display_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities);
> +}
> +
> +static const VkFormat available_surface_formats[] = {
> +   VK_FORMAT_B8G8R8A8_SRGB,
> +   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)
> +{
> +   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
> +
> +   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
> +      vk_outarray_append(&out, f) {
> +         f->format = available_surface_formats[i];
> +         f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
> +      }
> +   }
> +
> +   return vk_outarray_status(&out);
> +}
> +
> +static VkResult
> +wsi_display_surface_get_formats2(VkIcdSurfaceBase *surface,
> +                                 struct wsi_device *wsi_device,
> +                                 const void *info_next,
> +                                 uint32_t *surface_format_count,
> +                                 VkSurfaceFormat2KHR *surface_formats)
> +{
> +   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
> +
> +   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
> +      vk_outarray_append(&out, f) {
> +         assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
> +         f->surfaceFormat.format = available_surface_formats[i];
> +         f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
> +      }
> +   }
> +
> +   return vk_outarray_status(&out);
> +}
> +
> +static const VkPresentModeKHR available_present_modes[] = {
> +   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;
> +   int                          ret;
> +   uint32_t                     image_handle;
> +
> +   if (chain->base.use_prime_blit)
> +      result = wsi_create_prime_image(&chain->base, create_info, &image->base);
> +   else
> +      result = wsi_create_native_image(&chain->base, create_info, &image->base);
> +   if (result != VK_SUCCESS)
> +      return result;
> +
> +   ret = drmPrimeFDToHandle(wsi->master_fd, image->base.fd, &image_handle);
> +
> +   close(image->base.fd);
> +   image->base.fd = -1;
> +
> +   if (ret < 0)
> +      goto fail_handle;
> +
> +   image->chain = chain;
> +   image->state = wsi_image_idle;
> +   image->fb_id = 0;
> +
> +   /* XXX extract depth and bpp from image somehow */
> +   ret = drmModeAddFB(wsi->master_fd, create_info->imageExtent.width, create_info->imageExtent.height,
> +                      24, 32, image->base.row_pitch, image_handle, &image->fb_id);
> +
> +   if (ret)
> +      goto fail_fb;
> +
> +   return VK_SUCCESS;
> +
> +fail_fb:
> +   /* fall through */
> +
> +fail_handle:
> +   wsi_destroy_image(&chain->base, &image->base);
> +
> +   return VK_ERROR_OUT_OF_HOST_MEMORY;
> +}
> +
> +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;
> +
> +   wsi_destroy_image(&chain->base, &image->base);
> +}
> +
> +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 struct wsi_image *
> +wsi_display_get_wsi_image(struct wsi_swapchain  *drv_chain,
> +                          uint32_t              image_index)
> +{
> +   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
> +
> +   return &chain->images[image_index].base;
> +}
> +
> +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 VkResult
> +_wsi_display_queue_next(struct wsi_swapchain     *drv_chain);
> +
> +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;
> +   wsi_display_idle_old_displaying(image);
> +   (void) _wsi_display_queue_next(&(image->chain->base));
> +}
> +
> +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_start_wait_thread(struct wsi_display        *wsi)
> +{
> +   if (!wsi->wait_thread) {
> +      int ret = pthread_create(&wsi->wait_thread, NULL, wsi_display_wait_thread, wsi);
> +      if (ret)
> +         return ret;
> +   }
> +   return 0;
> +}
> +
> +/* call with wait_mutex held */
> +static int
> +wsi_display_wait_for_event(struct wsi_display           *wsi,
> +                           uint64_t                     timeout_ns)
> +{
> +   int ret;
> +
> +   ret = wsi_display_start_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;
> +   VkResult                     result = VK_SUCCESS;
> +
> +   if (timeout != 0 && timeout != UINT64_MAX)
> +      timeout += wsi_get_current_monotonic();
> +
> +   pthread_mutex_lock(&wsi->wait_mutex);
> +   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;
> +            result = VK_SUCCESS;
> +            goto done;
> +         }
> +         wsi_display_debug("image %d state %d\n", i, chain->images[i].state);
> +      }
> +
> +      if (ret == ETIMEDOUT) {
> +         result = VK_TIMEOUT;
> +         goto done;
> +      }
> +
> +      ret = wsi_display_wait_for_event(wsi, timeout);
> +
> +      if (ret && ret != ETIMEDOUT) {
> +         result = VK_ERROR_OUT_OF_DATE_KHR;
> +         goto done;
> +      }
> +   }
> +done:
> +   pthread_mutex_unlock(&wsi->wait_mutex);
> +   return result;
> +}
> +
> +/*
> + * Check whether there are any other connectors driven by this crtc
> + */
> +static bool
> +wsi_display_crtc_solo(struct wsi_display        *wsi,
> +                      drmModeResPtr             mode_res,
> +                      drmModeConnectorPtr       connector,
> +                      uint32_t                  crtc_id)
> +{
> +   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(wsi->master_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(wsi->master_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;
> +}
> +
> +/*
> + * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is
> + * currently driving this connector and not any others. Settle for a CRTC
> + * which is currently idle.
> + */
> +static uint32_t
> +wsi_display_select_crtc(struct wsi_display_connector    *connector,
> +                        drmModeResPtr                   mode_res,
> +                        drmModeConnectorPtr             drm_connector)
> +{
> +   struct wsi_display   *wsi = connector->wsi;
> +   int                  c;
> +   uint32_t             crtc_id;
> +
> +   /* See what CRTC is currently driving this connector */
> +   if (drm_connector->encoder_id) {
> +      drmModeEncoderPtr encoder = drmModeGetEncoder(wsi->master_fd, drm_connector->encoder_id);
> +      if (encoder) {
> +         crtc_id = encoder->crtc_id;
> +         drmModeFreeEncoder(encoder);
> +         if (crtc_id) {
> +            if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id))
> +               return crtc_id;
> +         }
> +      }
> +   }
> +   crtc_id = 0;
> +   for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
> +      drmModeCrtcPtr 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_setup_connector(wsi_display_connector       *connector,
> +                            wsi_display_mode            *display_mode)
> +{
> +   struct wsi_display   *wsi = connector->wsi;
> +   drmModeModeInfoPtr   drm_mode;
> +   drmModeConnectorPtr  drm_connector;
> +   drmModeResPtr        mode_res;
> +   VkResult             result;
> +   int                  m;
> +
> +   if (connector->current_mode == display_mode && connector->crtc_id)
> +      return VK_SUCCESS;
> +
> +   mode_res = drmModeGetResources(wsi->master_fd);
> +   if (!mode_res) {
> +      result = VK_ERROR_INITIALIZATION_FAILED;
> +      goto bail;
> +   }
> +
> +   drm_connector = drmModeGetConnectorCurrent(wsi->master_fd, connector->id);
> +   if (!drm_connector) {
> +      result = VK_ERROR_INITIALIZATION_FAILED;
> +      goto bail_mode_res;
> +   }
> +
> +   /* Pick a CRTC if we don't have one */
> +   if (!connector->crtc_id) {
> +      connector->crtc_id = wsi_display_select_crtc(connector, mode_res, drm_connector);
> +      if (!connector->crtc_id) {
> +         result = VK_ERROR_OUT_OF_DATE_KHR;
> +         goto bail_connector;
> +      }
> +   }
> +
> +   if (connector->current_mode != display_mode) {
> +
> +      /* Find the drm mode cooresponding to the requested VkDisplayMode */
> +      drm_mode = NULL;
> +      for (m = 0; m < drm_connector->count_modes; m++) {
> +         drm_mode = &drm_connector->modes[m];
> +         if (wsi_display_mode_matches_drm(display_mode, drm_mode))
> +            break;
> +         drm_mode = NULL;
> +      }
> +
> +      if (!drm_mode) {
> +         result = VK_ERROR_OUT_OF_DATE_KHR;
> +         goto bail_connector;
> +      }
> +
> +      connector->current_mode = display_mode;
> +      connector->current_drm_mode = *drm_mode;
> +   }
> +
> +   result = VK_SUCCESS;
> +
> +bail_connector:
> +   drmModeFreeConnector(drm_connector);
> +bail_mode_res:
> +   drmModeFreeResources(mode_res);
> +bail:
> +   return result;
> +
> +}
> +
> +/*
> + * Check to see if the kernel has no flip queued and if there's an image
> + * waiting to be displayed.
> + */
> +static VkResult
> +_wsi_display_queue_next(struct wsi_swapchain     *drv_chain)
> +{
> +   struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain;
> +   struct wsi_display           *wsi = chain->wsi;
> +   uint32_t                     i;
> +   struct wsi_display_image     *image = NULL;
> +   VkIcdSurfaceDisplay          *surface = chain->surface;
> +   wsi_display_mode             *display_mode = wsi_display_mode_from_handle(surface->displayMode);
> +   wsi_display_connector        *connector = display_mode->connector;
> +   int                          ret;
> +   VkResult                     result;
> +
> +   if (wsi->master_fd < 0)
> +      return VK_ERROR_INITIALIZATION_FAILED;
> +
> +   if (display_mode != connector->current_mode)
> +      connector->active = false;
> +
> +   for (;;) {
> +      /* Check to see if there is an image to display, or if some image is already queued */
> +
> +      for (i = 0; i < chain->base.image_count; i++) {
> +         struct wsi_display_image  *tmp_image = &chain->images[i];
> +
> +         switch (tmp_image->state) {
> +         case wsi_image_flipping:
> +            /* already flipping, don't send another to the kernel yet */
> +            return VK_SUCCESS;
> +         case wsi_image_queued:
> +            /* find the oldest queued */
> +            if (!image || tmp_image->flip_sequence < image->flip_sequence)
> +               image = tmp_image;
> +            break;
> +         default:
> +            break;
> +         }
> +      }
> +
> +      if (!image)
> +         return VK_SUCCESS;
> +
> +      if (connector->active) {
> +         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:
> +
> +            result = wsi_display_setup_connector(connector, display_mode);
> +
> +            if (result != VK_SUCCESS) {
> +               image->state = wsi_image_idle;
> +               return result;
> +            }
> +
> +            /* XXX allow setting of position */
> +
> +            ret = drmModeSetCrtc(wsi->master_fd, connector->crtc_id, image->fb_id, 0, 0,
> +                                 &connector->id, 1, &connector->current_drm_mode);
> +
> +            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_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];
> +   VkResult                     result;
> +
> +   assert(image->state == wsi_image_drawing);
> +   wsi_display_debug("present %d\n", image_index);
> +
> +   pthread_mutex_lock(&wsi->wait_mutex);
> +
> +   image->flip_sequence = ++chain->flip_sequence;
> +   image->state = wsi_image_queued;
> +
> +   result = _wsi_display_queue_next(drv_chain);
> +
> +   pthread_mutex_unlock(&wsi->wait_mutex);
> +
> +   return result;
> +}
> +
> +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,
> +                                     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, 8,
> +                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
> +
> +   if (chain == NULL)
> +      return VK_ERROR_OUT_OF_HOST_MEMORY;
> +
> +   result = wsi_swapchain_init(wsi_device, &chain->base, device,
> +                               create_info, allocator);
> +
> +   chain->base.destroy = wsi_display_swapchain_destroy;
> +   chain->base.get_wsi_image = wsi_display_get_wsi_image;
> +   chain->base.acquire_next_image = wsi_display_acquire_next_image;
> +   chain->base.queue_present = wsi_display_queue_present;
> +   chain->base.present_mode = create_info->presentMode;
> +   chain->base.image_count = num_images;
> +
> +   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 device_fd)
> +{
> +   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 = -1;
> +   if (drmGetNodeTypeFromFd(device_fd) == DRM_NODE_PRIMARY)
> +      wsi->master_fd = device_fd;
> +   wsi->render_fd = device_fd;
> +
> +   pthread_mutex_init(&wsi->wait_mutex, NULL);
> +   wsi->physical_device = physical_device;
> +   wsi->alloc = alloc;
> +
> +   LIST_INITHEAD(&wsi->display_modes);
> +   LIST_INITHEAD(&wsi->connectors);
> +
> +   pthread_condattr_t condattr;
> +   int ret;
> +
> +   ret = pthread_mutex_init(&wsi->wait_mutex, NULL);
> +   if (ret) {
> +      result = VK_ERROR_OUT_OF_HOST_MEMORY;
> +      goto fail_mutex;
> +   }
> +
> +   ret = pthread_condattr_init(&condattr);
> +   if (ret) {
> +      result = VK_ERROR_OUT_OF_HOST_MEMORY;
> +      goto fail_condattr;
> +   }
> +
> +   ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
> +   if (ret) {
> +      result = VK_ERROR_OUT_OF_HOST_MEMORY;
> +      goto fail_setclock;
> +   }
> +
> +   ret = pthread_cond_init(&wsi->wait_cond, &condattr);
> +   if (ret) {
> +      result = VK_ERROR_OUT_OF_HOST_MEMORY;
> +      goto fail_cond;
> +   }
> +
> +   pthread_condattr_destroy(&condattr);
> +
> +   wsi->base.get_support = wsi_display_surface_get_support;
> +   wsi->base.get_capabilities = wsi_display_surface_get_capabilities;
> +   wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2;
> +   wsi->base.get_formats = wsi_display_surface_get_formats;
> +   wsi->base.get_formats2 = wsi_display_surface_get_formats2;
> +   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_cond:
> +fail_setclock:
> +   pthread_condattr_destroy(&condattr);
> +fail_condattr:
> +   pthread_mutex_destroy(&wsi->wait_mutex);
> +fail_mutex:
> +   vk_free(alloc, wsi);
> +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) {

if (!wsi) return;
and carry on without the extra indent

I don't know enough for this to be an actual review though, but the rest
of this file looks reasonable to me :)

> +
> +      struct wsi_display_connector *connector, *connector_storage;
> +      LIST_FOR_EACH_ENTRY_SAFE(connector, connector_storage, &wsi->connectors, list) {
> +         vk_free(wsi->alloc, connector);
> +      }
> +
> +      struct wsi_display_mode *mode, *mode_storage;
> +      LIST_FOR_EACH_ENTRY_SAFE(mode, mode_storage, &wsi->display_modes, list) {
> +         vk_free(wsi->alloc, mode);
> +      }
> +
> +      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);
> +
> +      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..b414a226293
> --- /dev/null
> +++ b/src/vulkan/wsi/wsi_common_display.h
> @@ -0,0 +1,72 @@
> +/*
> + * 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 <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_create_display_surface(VkInstance instance,
> +                           const VkAllocationCallbacks *pAllocator,
> +                           const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
> +                           VkSurfaceKHR *pSurface);
> +
> +#endif
> diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h
> index 503b2a015dc..d38d2efa116 100644
> --- a/src/vulkan/wsi/wsi_common_private.h
> +++ b/src/vulkan/wsi/wsi_common_private.h
> @@ -135,6 +135,16 @@ 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 device_fd);
> +
> +void
> +wsi_display_finish_wsi(struct wsi_device *wsi_device,
> +                       const VkAllocationCallbacks *alloc);
> +
>  #define WSI_DEFINE_NONDISP_HANDLE_CASTS(__wsi_type, __VkType)              \
>                                                                             \
>     static inline struct __wsi_type *                                       \
> -- 
> 2.15.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@xxxxxxxxxxxxxxxxxxxxx
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/dri-devel




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux