101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2017 Keith Packard
301e04c3fSmrg *
401e04c3fSmrg * Permission to use, copy, modify, distribute, and sell this software and its
501e04c3fSmrg * documentation for any purpose is hereby granted without fee, provided that
601e04c3fSmrg * the above copyright notice appear in all copies and that both that copyright
701e04c3fSmrg * notice and this permission notice appear in supporting documentation, and
801e04c3fSmrg * that the name of the copyright holders not be used in advertising or
901e04c3fSmrg * publicity pertaining to distribution of the software without specific,
1001e04c3fSmrg * written prior permission.  The copyright holders make no representations
1101e04c3fSmrg * about the suitability of this software for any purpose.  It is provided "as
1201e04c3fSmrg * is" without express or implied warranty.
1301e04c3fSmrg *
1401e04c3fSmrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1501e04c3fSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1601e04c3fSmrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1701e04c3fSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1801e04c3fSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1901e04c3fSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2001e04c3fSmrg * OF THIS SOFTWARE.
2101e04c3fSmrg */
2201e04c3fSmrg
2301e04c3fSmrg#include "util/macros.h"
2401e04c3fSmrg#include <stdlib.h>
2501e04c3fSmrg#include <stdio.h>
2601e04c3fSmrg#include <unistd.h>
2701e04c3fSmrg#include <errno.h>
2801e04c3fSmrg#include <string.h>
2901e04c3fSmrg#include <fcntl.h>
3001e04c3fSmrg#include <poll.h>
3101e04c3fSmrg#include <stdbool.h>
3201e04c3fSmrg#include <math.h>
3301e04c3fSmrg#include <xf86drm.h>
3401e04c3fSmrg#include <xf86drmMode.h>
358a1362adSmaya#include "drm-uapi/drm_fourcc.h"
3601e04c3fSmrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
3701e04c3fSmrg#include <xcb/randr.h>
3801e04c3fSmrg#include <X11/Xlib-xcb.h>
3901e04c3fSmrg#endif
4001e04c3fSmrg#include "util/hash_table.h"
4101e04c3fSmrg#include "util/list.h"
4201e04c3fSmrg
437ec681f3Smrg#include "vk_device.h"
447ec681f3Smrg#include "vk_instance.h"
457ec681f3Smrg#include "vk_physical_device.h"
4601e04c3fSmrg#include "vk_util.h"
477ec681f3Smrg#include "wsi_common_entrypoints.h"
4801e04c3fSmrg#include "wsi_common_private.h"
4901e04c3fSmrg#include "wsi_common_display.h"
5001e04c3fSmrg#include "wsi_common_queue.h"
5101e04c3fSmrg
5201e04c3fSmrg#if 0
5301e04c3fSmrg#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)
5401e04c3fSmrg#define wsi_display_debug_code(...)     __VA_ARGS__
5501e04c3fSmrg#else
5601e04c3fSmrg#define wsi_display_debug(...)
5701e04c3fSmrg#define wsi_display_debug_code(...)
5801e04c3fSmrg#endif
5901e04c3fSmrg
6001e04c3fSmrg/* These have lifetime equal to the instance, so they effectively
6101e04c3fSmrg * never go away. This means we must keep track of them separately
6201e04c3fSmrg * from all other resources.
6301e04c3fSmrg */
6401e04c3fSmrgtypedef struct wsi_display_mode {
6501e04c3fSmrg   struct list_head             list;
6601e04c3fSmrg   struct wsi_display_connector *connector;
6701e04c3fSmrg   bool                         valid; /* was found in most recent poll */
6801e04c3fSmrg   bool                         preferred;
6901e04c3fSmrg   uint32_t                     clock; /* in kHz */
7001e04c3fSmrg   uint16_t                     hdisplay, hsync_start, hsync_end, htotal, hskew;
7101e04c3fSmrg   uint16_t                     vdisplay, vsync_start, vsync_end, vtotal, vscan;
7201e04c3fSmrg   uint32_t                     flags;
7301e04c3fSmrg} wsi_display_mode;
7401e04c3fSmrg
7501e04c3fSmrgtypedef struct wsi_display_connector {
7601e04c3fSmrg   struct list_head             list;
7701e04c3fSmrg   struct wsi_display           *wsi;
7801e04c3fSmrg   uint32_t                     id;
7901e04c3fSmrg   uint32_t                     crtc_id;
8001e04c3fSmrg   char                         *name;
8101e04c3fSmrg   bool                         connected;
8201e04c3fSmrg   bool                         active;
8301e04c3fSmrg   struct list_head             display_modes;
8401e04c3fSmrg   wsi_display_mode             *current_mode;
8501e04c3fSmrg   drmModeModeInfo              current_drm_mode;
8601e04c3fSmrg   uint32_t                     dpms_property;
8701e04c3fSmrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
8801e04c3fSmrg   xcb_randr_output_t           output;
8901e04c3fSmrg#endif
9001e04c3fSmrg} wsi_display_connector;
9101e04c3fSmrg
9201e04c3fSmrgstruct wsi_display {
9301e04c3fSmrg   struct wsi_interface         base;
9401e04c3fSmrg
9501e04c3fSmrg   const VkAllocationCallbacks  *alloc;
9601e04c3fSmrg
9701e04c3fSmrg   int                          fd;
9801e04c3fSmrg
9901e04c3fSmrg   pthread_mutex_t              wait_mutex;
10001e04c3fSmrg   pthread_cond_t               wait_cond;
10101e04c3fSmrg   pthread_t                    wait_thread;
10201e04c3fSmrg
1038a1362adSmaya   struct list_head             connectors; /* list of all discovered connectors */
10401e04c3fSmrg};
10501e04c3fSmrg
10601e04c3fSmrg#define wsi_for_each_display_mode(_mode, _conn)                 \
10701e04c3fSmrg   list_for_each_entry_safe(struct wsi_display_mode, _mode,     \
10801e04c3fSmrg                            &(_conn)->display_modes, list)
10901e04c3fSmrg
11001e04c3fSmrg#define wsi_for_each_connector(_conn, _dev)                             \
11101e04c3fSmrg   list_for_each_entry_safe(struct wsi_display_connector, _conn,        \
11201e04c3fSmrg                            &(_dev)->connectors, list)
11301e04c3fSmrg
11401e04c3fSmrgenum wsi_image_state {
11501e04c3fSmrg   WSI_IMAGE_IDLE,
11601e04c3fSmrg   WSI_IMAGE_DRAWING,
11701e04c3fSmrg   WSI_IMAGE_QUEUED,
11801e04c3fSmrg   WSI_IMAGE_FLIPPING,
11901e04c3fSmrg   WSI_IMAGE_DISPLAYING
12001e04c3fSmrg};
12101e04c3fSmrg
12201e04c3fSmrgstruct wsi_display_image {
12301e04c3fSmrg   struct wsi_image             base;
12401e04c3fSmrg   struct wsi_display_swapchain *chain;
12501e04c3fSmrg   enum wsi_image_state         state;
12601e04c3fSmrg   uint32_t                     fb_id;
12701e04c3fSmrg   uint32_t                     buffer[4];
12801e04c3fSmrg   uint64_t                     flip_sequence;
12901e04c3fSmrg};
13001e04c3fSmrg
13101e04c3fSmrgstruct wsi_display_swapchain {
13201e04c3fSmrg   struct wsi_swapchain         base;
13301e04c3fSmrg   struct wsi_display           *wsi;
13401e04c3fSmrg   VkIcdSurfaceDisplay          *surface;
13501e04c3fSmrg   uint64_t                     flip_sequence;
13601e04c3fSmrg   VkResult                     status;
13701e04c3fSmrg   struct wsi_display_image     images[0];
13801e04c3fSmrg};
13901e04c3fSmrg
14001e04c3fSmrgstruct wsi_display_fence {
14101e04c3fSmrg   struct wsi_fence             base;
14201e04c3fSmrg   bool                         event_received;
14301e04c3fSmrg   bool                         destroyed;
1447ec681f3Smrg   uint32_t                     syncobj; /* syncobj to signal on event */
14501e04c3fSmrg   uint64_t                     sequence;
14601e04c3fSmrg};
14701e04c3fSmrg
14801e04c3fSmrgstatic uint64_t fence_sequence;
14901e04c3fSmrg
15001e04c3fSmrgICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR)
15101e04c3fSmrgICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR)
15201e04c3fSmrg
15301e04c3fSmrgstatic bool
15401e04c3fSmrgwsi_display_mode_matches_drm(wsi_display_mode *wsi,
15501e04c3fSmrg                             drmModeModeInfoPtr drm)
15601e04c3fSmrg{
15701e04c3fSmrg   return wsi->clock == drm->clock &&
15801e04c3fSmrg      wsi->hdisplay == drm->hdisplay &&
15901e04c3fSmrg      wsi->hsync_start == drm->hsync_start &&
16001e04c3fSmrg      wsi->hsync_end == drm->hsync_end &&
16101e04c3fSmrg      wsi->htotal == drm->htotal &&
16201e04c3fSmrg      wsi->hskew == drm->hskew &&
16301e04c3fSmrg      wsi->vdisplay == drm->vdisplay &&
16401e04c3fSmrg      wsi->vsync_start == drm->vsync_start &&
16501e04c3fSmrg      wsi->vsync_end == drm->vsync_end &&
16601e04c3fSmrg      wsi->vtotal == drm->vtotal &&
16701e04c3fSmrg      MAX2(wsi->vscan, 1) == MAX2(drm->vscan, 1) &&
16801e04c3fSmrg      wsi->flags == drm->flags;
16901e04c3fSmrg}
17001e04c3fSmrg
17101e04c3fSmrgstatic double
17201e04c3fSmrgwsi_display_mode_refresh(struct wsi_display_mode *wsi)
17301e04c3fSmrg{
17401e04c3fSmrg   return (double) wsi->clock * 1000.0 / ((double) wsi->htotal *
17501e04c3fSmrg                                          (double) wsi->vtotal *
17601e04c3fSmrg                                          (double) MAX2(wsi->vscan, 1));
17701e04c3fSmrg}
17801e04c3fSmrg
17901e04c3fSmrgstatic uint64_t wsi_rel_to_abs_time(uint64_t rel_time)
18001e04c3fSmrg{
1818a1362adSmaya   uint64_t current_time = wsi_common_get_current_time();
18201e04c3fSmrg
18301e04c3fSmrg   /* check for overflow */
18401e04c3fSmrg   if (rel_time > UINT64_MAX - current_time)
18501e04c3fSmrg      return UINT64_MAX;
18601e04c3fSmrg
18701e04c3fSmrg   return current_time + rel_time;
18801e04c3fSmrg}
18901e04c3fSmrg
19001e04c3fSmrgstatic struct wsi_display_mode *
19101e04c3fSmrgwsi_display_find_drm_mode(struct wsi_device *wsi_device,
19201e04c3fSmrg                          struct wsi_display_connector *connector,
19301e04c3fSmrg                          drmModeModeInfoPtr mode)
19401e04c3fSmrg{
19501e04c3fSmrg   wsi_for_each_display_mode(display_mode, connector) {
19601e04c3fSmrg      if (wsi_display_mode_matches_drm(display_mode, mode))
19701e04c3fSmrg         return display_mode;
19801e04c3fSmrg   }
19901e04c3fSmrg   return NULL;
20001e04c3fSmrg}
20101e04c3fSmrg
20201e04c3fSmrgstatic void
20301e04c3fSmrgwsi_display_invalidate_connector_modes(struct wsi_device *wsi_device,
20401e04c3fSmrg                                       struct wsi_display_connector *connector)
20501e04c3fSmrg{
20601e04c3fSmrg   wsi_for_each_display_mode(display_mode, connector) {
20701e04c3fSmrg      display_mode->valid = false;
20801e04c3fSmrg   }
20901e04c3fSmrg}
21001e04c3fSmrg
21101e04c3fSmrgstatic VkResult
21201e04c3fSmrgwsi_display_register_drm_mode(struct wsi_device *wsi_device,
21301e04c3fSmrg                              struct wsi_display_connector *connector,
21401e04c3fSmrg                              drmModeModeInfoPtr drm_mode)
21501e04c3fSmrg{
21601e04c3fSmrg   struct wsi_display *wsi =
21701e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
21801e04c3fSmrg   struct wsi_display_mode *display_mode =
21901e04c3fSmrg      wsi_display_find_drm_mode(wsi_device, connector, drm_mode);
22001e04c3fSmrg
22101e04c3fSmrg   if (display_mode) {
22201e04c3fSmrg      display_mode->valid = true;
22301e04c3fSmrg      return VK_SUCCESS;
22401e04c3fSmrg   }
22501e04c3fSmrg
22601e04c3fSmrg   display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),
22701e04c3fSmrg                            8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
22801e04c3fSmrg   if (!display_mode)
22901e04c3fSmrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
23001e04c3fSmrg
23101e04c3fSmrg   display_mode->connector = connector;
23201e04c3fSmrg   display_mode->valid = true;
23301e04c3fSmrg   display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0;
23401e04c3fSmrg   display_mode->clock = drm_mode->clock; /* kHz */
23501e04c3fSmrg   display_mode->hdisplay = drm_mode->hdisplay;
23601e04c3fSmrg   display_mode->hsync_start = drm_mode->hsync_start;
23701e04c3fSmrg   display_mode->hsync_end = drm_mode->hsync_end;
23801e04c3fSmrg   display_mode->htotal = drm_mode->htotal;
23901e04c3fSmrg   display_mode->hskew = drm_mode->hskew;
24001e04c3fSmrg   display_mode->vdisplay = drm_mode->vdisplay;
24101e04c3fSmrg   display_mode->vsync_start = drm_mode->vsync_start;
24201e04c3fSmrg   display_mode->vsync_end = drm_mode->vsync_end;
24301e04c3fSmrg   display_mode->vtotal = drm_mode->vtotal;
24401e04c3fSmrg   display_mode->vscan = drm_mode->vscan;
24501e04c3fSmrg   display_mode->flags = drm_mode->flags;
24601e04c3fSmrg
24701e04c3fSmrg   list_addtail(&display_mode->list, &connector->display_modes);
24801e04c3fSmrg   return VK_SUCCESS;
24901e04c3fSmrg}
25001e04c3fSmrg
25101e04c3fSmrg/*
25201e04c3fSmrg * Update our information about a specific connector
25301e04c3fSmrg */
25401e04c3fSmrg
25501e04c3fSmrgstatic struct wsi_display_connector *
25601e04c3fSmrgwsi_display_find_connector(struct wsi_device *wsi_device,
25701e04c3fSmrg                          uint32_t connector_id)
25801e04c3fSmrg{
25901e04c3fSmrg   struct wsi_display *wsi =
26001e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
26101e04c3fSmrg
26201e04c3fSmrg   wsi_for_each_connector(connector, wsi) {
26301e04c3fSmrg      if (connector->id == connector_id)
26401e04c3fSmrg         return connector;
26501e04c3fSmrg   }
26601e04c3fSmrg
26701e04c3fSmrg   return NULL;
26801e04c3fSmrg}
26901e04c3fSmrg
27001e04c3fSmrgstatic struct wsi_display_connector *
27101e04c3fSmrgwsi_display_alloc_connector(struct wsi_display *wsi,
27201e04c3fSmrg                            uint32_t connector_id)
27301e04c3fSmrg{
27401e04c3fSmrg   struct wsi_display_connector *connector =
27501e04c3fSmrg      vk_zalloc(wsi->alloc, sizeof (struct wsi_display_connector),
27601e04c3fSmrg                8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
27701e04c3fSmrg
27801e04c3fSmrg   connector->id = connector_id;
27901e04c3fSmrg   connector->wsi = wsi;
28001e04c3fSmrg   connector->active = false;
28101e04c3fSmrg   /* XXX use EDID name */
28201e04c3fSmrg   connector->name = "monitor";
28301e04c3fSmrg   list_inithead(&connector->display_modes);
28401e04c3fSmrg   return connector;
28501e04c3fSmrg}
28601e04c3fSmrg
28701e04c3fSmrgstatic struct wsi_display_connector *
28801e04c3fSmrgwsi_display_get_connector(struct wsi_device *wsi_device,
2897ec681f3Smrg                          int drm_fd,
29001e04c3fSmrg                          uint32_t connector_id)
29101e04c3fSmrg{
29201e04c3fSmrg   struct wsi_display *wsi =
29301e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
29401e04c3fSmrg
2957ec681f3Smrg   if (drm_fd < 0)
29601e04c3fSmrg      return NULL;
29701e04c3fSmrg
29801e04c3fSmrg   drmModeConnectorPtr drm_connector =
2997ec681f3Smrg      drmModeGetConnector(drm_fd, connector_id);
30001e04c3fSmrg
30101e04c3fSmrg   if (!drm_connector)
30201e04c3fSmrg      return NULL;
30301e04c3fSmrg
30401e04c3fSmrg   struct wsi_display_connector *connector =
30501e04c3fSmrg      wsi_display_find_connector(wsi_device, connector_id);
30601e04c3fSmrg
30701e04c3fSmrg   if (!connector) {
30801e04c3fSmrg      connector = wsi_display_alloc_connector(wsi, connector_id);
30901e04c3fSmrg      if (!connector) {
31001e04c3fSmrg         drmModeFreeConnector(drm_connector);
31101e04c3fSmrg         return NULL;
31201e04c3fSmrg      }
31301e04c3fSmrg      list_addtail(&connector->list, &wsi->connectors);
31401e04c3fSmrg   }
31501e04c3fSmrg
31601e04c3fSmrg   connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
31701e04c3fSmrg
31801e04c3fSmrg   /* Look for a DPMS property if we haven't already found one */
31901e04c3fSmrg   for (int p = 0; connector->dpms_property == 0 &&
32001e04c3fSmrg           p < drm_connector->count_props; p++)
32101e04c3fSmrg   {
3227ec681f3Smrg      drmModePropertyPtr prop = drmModeGetProperty(drm_fd,
32301e04c3fSmrg                                                   drm_connector->props[p]);
32401e04c3fSmrg      if (!prop)
32501e04c3fSmrg         continue;
32601e04c3fSmrg      if (prop->flags & DRM_MODE_PROP_ENUM) {
32701e04c3fSmrg         if (!strcmp(prop->name, "DPMS"))
32801e04c3fSmrg            connector->dpms_property = drm_connector->props[p];
32901e04c3fSmrg      }
33001e04c3fSmrg      drmModeFreeProperty(prop);
33101e04c3fSmrg   }
33201e04c3fSmrg
33301e04c3fSmrg   /* Mark all connector modes as invalid */
33401e04c3fSmrg   wsi_display_invalidate_connector_modes(wsi_device, connector);
33501e04c3fSmrg
33601e04c3fSmrg   /*
33701e04c3fSmrg    * List current modes, adding new ones and marking existing ones as
33801e04c3fSmrg    * valid
33901e04c3fSmrg    */
34001e04c3fSmrg   for (int m = 0; m < drm_connector->count_modes; m++) {
34101e04c3fSmrg      VkResult result = wsi_display_register_drm_mode(wsi_device,
34201e04c3fSmrg                                                      connector,
34301e04c3fSmrg                                                      &drm_connector->modes[m]);
34401e04c3fSmrg      if (result != VK_SUCCESS) {
34501e04c3fSmrg         drmModeFreeConnector(drm_connector);
34601e04c3fSmrg         return NULL;
34701e04c3fSmrg      }
34801e04c3fSmrg   }
34901e04c3fSmrg
35001e04c3fSmrg   drmModeFreeConnector(drm_connector);
35101e04c3fSmrg
35201e04c3fSmrg   return connector;
35301e04c3fSmrg}
35401e04c3fSmrg
35501e04c3fSmrg#define MM_PER_PIXEL     (1.0/96.0 * 25.4)
35601e04c3fSmrg
35701e04c3fSmrgstatic uint32_t
35801e04c3fSmrgmode_size(struct wsi_display_mode *mode)
35901e04c3fSmrg{
36001e04c3fSmrg   /* fortunately, these are both uint16_t, so this is easy */
36101e04c3fSmrg   return (uint32_t) mode->hdisplay * (uint32_t) mode->vdisplay;
36201e04c3fSmrg}
36301e04c3fSmrg
36401e04c3fSmrgstatic void
36501e04c3fSmrgwsi_display_fill_in_display_properties(struct wsi_device *wsi_device,
36601e04c3fSmrg                                       struct wsi_display_connector *connector,
36701e04c3fSmrg                                       VkDisplayProperties2KHR *properties2)
36801e04c3fSmrg{
36901e04c3fSmrg   assert(properties2->sType == VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR);
37001e04c3fSmrg   VkDisplayPropertiesKHR *properties = &properties2->displayProperties;
37101e04c3fSmrg
37201e04c3fSmrg   properties->display = wsi_display_connector_to_handle(connector);
37301e04c3fSmrg   properties->displayName = connector->name;
37401e04c3fSmrg
37501e04c3fSmrg   /* Find the first preferred mode and assume that's the physical
37601e04c3fSmrg    * resolution. If there isn't a preferred mode, find the largest mode and
37701e04c3fSmrg    * use that.
37801e04c3fSmrg    */
37901e04c3fSmrg
38001e04c3fSmrg   struct wsi_display_mode *preferred_mode = NULL, *largest_mode = NULL;
38101e04c3fSmrg   wsi_for_each_display_mode(display_mode, connector) {
38201e04c3fSmrg      if (!display_mode->valid)
38301e04c3fSmrg         continue;
38401e04c3fSmrg      if (display_mode->preferred) {
38501e04c3fSmrg         preferred_mode = display_mode;
38601e04c3fSmrg         break;
38701e04c3fSmrg      }
38801e04c3fSmrg      if (largest_mode == NULL ||
38901e04c3fSmrg          mode_size(display_mode) > mode_size(largest_mode))
39001e04c3fSmrg      {
39101e04c3fSmrg         largest_mode = display_mode;
39201e04c3fSmrg      }
39301e04c3fSmrg   }
39401e04c3fSmrg
39501e04c3fSmrg   if (preferred_mode) {
39601e04c3fSmrg      properties->physicalResolution.width = preferred_mode->hdisplay;
39701e04c3fSmrg      properties->physicalResolution.height = preferred_mode->vdisplay;
39801e04c3fSmrg   } else if (largest_mode) {
39901e04c3fSmrg      properties->physicalResolution.width = largest_mode->hdisplay;
40001e04c3fSmrg      properties->physicalResolution.height = largest_mode->vdisplay;
40101e04c3fSmrg   } else {
40201e04c3fSmrg      properties->physicalResolution.width = 1024;
40301e04c3fSmrg      properties->physicalResolution.height = 768;
40401e04c3fSmrg   }
40501e04c3fSmrg
40601e04c3fSmrg   /* Make up physical size based on 96dpi */
40701e04c3fSmrg   properties->physicalDimensions.width =
40801e04c3fSmrg      floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5);
40901e04c3fSmrg   properties->physicalDimensions.height =
41001e04c3fSmrg      floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5);
41101e04c3fSmrg
41201e04c3fSmrg   properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
41301e04c3fSmrg   properties->planeReorderPossible = VK_FALSE;
41401e04c3fSmrg   properties->persistentContent = VK_FALSE;
41501e04c3fSmrg}
41601e04c3fSmrg
4177ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
4187ec681f3Smrgwsi_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice,
4197ec681f3Smrg                                          uint32_t *pPropertyCount,
4207ec681f3Smrg                                          VkDisplayPropertiesKHR *pProperties)
42101e04c3fSmrg{
4227ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
4237ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
42401e04c3fSmrg   struct wsi_display *wsi =
42501e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
42601e04c3fSmrg
4277ec681f3Smrg   if (pProperties == NULL) {
4287ec681f3Smrg      return wsi_GetPhysicalDeviceDisplayProperties2KHR(physicalDevice,
4297ec681f3Smrg                                                        pPropertyCount,
4307ec681f3Smrg                                                        NULL);
43101e04c3fSmrg   } else {
43201e04c3fSmrg      /* If we're actually returning properties, allocate a temporary array of
43301e04c3fSmrg       * VkDisplayProperties2KHR structs, call properties2 to fill them out,
43401e04c3fSmrg       * and then copy them to the client.  This seems a bit expensive but
43501e04c3fSmrg       * wsi_display_get_physical_device_display_properties2() calls
43601e04c3fSmrg       * drmModeGetResources() which does an ioctl and then a bunch of
43701e04c3fSmrg       * allocations so this should get lost in the noise.
43801e04c3fSmrg       */
43901e04c3fSmrg      VkDisplayProperties2KHR *props2 =
4407ec681f3Smrg         vk_zalloc(wsi->alloc, sizeof(*props2) * *pPropertyCount, 8,
44101e04c3fSmrg                   VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
44201e04c3fSmrg      if (props2 == NULL)
44301e04c3fSmrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
44401e04c3fSmrg
4457ec681f3Smrg      for (uint32_t i = 0; i < *pPropertyCount; i++)
44601e04c3fSmrg         props2[i].sType = VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR;
44701e04c3fSmrg
4487ec681f3Smrg      VkResult result =
4497ec681f3Smrg         wsi_GetPhysicalDeviceDisplayProperties2KHR(physicalDevice,
4507ec681f3Smrg                                                    pPropertyCount, props2);
45101e04c3fSmrg
45201e04c3fSmrg      if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
4537ec681f3Smrg         for (uint32_t i = 0; i < *pPropertyCount; i++)
4547ec681f3Smrg            pProperties[i] = props2[i].displayProperties;
45501e04c3fSmrg      }
45601e04c3fSmrg
45701e04c3fSmrg      vk_free(wsi->alloc, props2);
45801e04c3fSmrg
45901e04c3fSmrg      return result;
46001e04c3fSmrg   }
46101e04c3fSmrg}
46201e04c3fSmrg
4637ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
4647ec681f3Smrgwsi_GetPhysicalDeviceDisplayProperties2KHR(VkPhysicalDevice physicalDevice,
4657ec681f3Smrg                                           uint32_t *pPropertyCount,
4667ec681f3Smrg                                           VkDisplayProperties2KHR *pProperties)
46701e04c3fSmrg{
4687ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
4697ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
47001e04c3fSmrg   struct wsi_display *wsi =
47101e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
47201e04c3fSmrg
47301e04c3fSmrg   if (wsi->fd < 0)
47401e04c3fSmrg      goto bail;
47501e04c3fSmrg
47601e04c3fSmrg   drmModeResPtr mode_res = drmModeGetResources(wsi->fd);
47701e04c3fSmrg
47801e04c3fSmrg   if (!mode_res)
47901e04c3fSmrg      goto bail;
48001e04c3fSmrg
4817ec681f3Smrg   VK_OUTARRAY_MAKE(conn, pProperties, pPropertyCount);
48201e04c3fSmrg
48301e04c3fSmrg   /* Get current information */
48401e04c3fSmrg
48501e04c3fSmrg   for (int c = 0; c < mode_res->count_connectors; c++) {
48601e04c3fSmrg      struct wsi_display_connector *connector =
4877ec681f3Smrg         wsi_display_get_connector(wsi_device, wsi->fd,
4887ec681f3Smrg               mode_res->connectors[c]);
48901e04c3fSmrg
49001e04c3fSmrg      if (!connector) {
49101e04c3fSmrg         drmModeFreeResources(mode_res);
49201e04c3fSmrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
49301e04c3fSmrg      }
49401e04c3fSmrg
49501e04c3fSmrg      if (connector->connected) {
49601e04c3fSmrg         vk_outarray_append(&conn, prop) {
49701e04c3fSmrg            wsi_display_fill_in_display_properties(wsi_device,
49801e04c3fSmrg                                                   connector,
49901e04c3fSmrg                                                   prop);
50001e04c3fSmrg         }
50101e04c3fSmrg      }
50201e04c3fSmrg   }
50301e04c3fSmrg
50401e04c3fSmrg   drmModeFreeResources(mode_res);
50501e04c3fSmrg
50601e04c3fSmrg   return vk_outarray_status(&conn);
50701e04c3fSmrg
50801e04c3fSmrgbail:
5097ec681f3Smrg   *pPropertyCount = 0;
51001e04c3fSmrg   return VK_SUCCESS;
51101e04c3fSmrg}
51201e04c3fSmrg
51301e04c3fSmrg/*
51401e04c3fSmrg * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display
51501e04c3fSmrg */
51601e04c3fSmrgstatic void
51701e04c3fSmrgwsi_display_fill_in_display_plane_properties(
51801e04c3fSmrg   struct wsi_device *wsi_device,
51901e04c3fSmrg   struct wsi_display_connector *connector,
52001e04c3fSmrg   VkDisplayPlaneProperties2KHR *properties)
52101e04c3fSmrg{
52201e04c3fSmrg   assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR);
52301e04c3fSmrg   VkDisplayPlanePropertiesKHR *prop = &properties->displayPlaneProperties;
52401e04c3fSmrg
52501e04c3fSmrg   if (connector && connector->active) {
52601e04c3fSmrg      prop->currentDisplay = wsi_display_connector_to_handle(connector);
52701e04c3fSmrg      prop->currentStackIndex = 0;
52801e04c3fSmrg   } else {
52901e04c3fSmrg      prop->currentDisplay = VK_NULL_HANDLE;
53001e04c3fSmrg      prop->currentStackIndex = 0;
53101e04c3fSmrg   }
53201e04c3fSmrg}
53301e04c3fSmrg
5347ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
5357ec681f3Smrgwsi_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physicalDevice,
5367ec681f3Smrg                                               uint32_t *pPropertyCount,
5377ec681f3Smrg                                               VkDisplayPlanePropertiesKHR *pProperties)
53801e04c3fSmrg{
5397ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
5407ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
54101e04c3fSmrg   struct wsi_display *wsi =
54201e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
54301e04c3fSmrg
5447ec681f3Smrg   VK_OUTARRAY_MAKE(conn, pProperties, pPropertyCount);
54501e04c3fSmrg
54601e04c3fSmrg   wsi_for_each_connector(connector, wsi) {
54701e04c3fSmrg      vk_outarray_append(&conn, prop) {
54801e04c3fSmrg         VkDisplayPlaneProperties2KHR prop2 = {
54901e04c3fSmrg            .sType = VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR,
55001e04c3fSmrg         };
55101e04c3fSmrg         wsi_display_fill_in_display_plane_properties(wsi_device, connector,
55201e04c3fSmrg                                                      &prop2);
55301e04c3fSmrg         *prop = prop2.displayPlaneProperties;
55401e04c3fSmrg      }
55501e04c3fSmrg   }
55601e04c3fSmrg   return vk_outarray_status(&conn);
55701e04c3fSmrg}
55801e04c3fSmrg
5597ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
5607ec681f3Smrgwsi_GetPhysicalDeviceDisplayPlaneProperties2KHR(VkPhysicalDevice physicalDevice,
5617ec681f3Smrg                                                uint32_t *pPropertyCount,
5627ec681f3Smrg                                                VkDisplayPlaneProperties2KHR *pProperties)
56301e04c3fSmrg{
5647ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
5657ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
56601e04c3fSmrg   struct wsi_display *wsi =
56701e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
56801e04c3fSmrg
5697ec681f3Smrg   VK_OUTARRAY_MAKE(conn, pProperties, pPropertyCount);
57001e04c3fSmrg
57101e04c3fSmrg   wsi_for_each_connector(connector, wsi) {
57201e04c3fSmrg      vk_outarray_append(&conn, prop) {
57301e04c3fSmrg         wsi_display_fill_in_display_plane_properties(wsi_device, connector,
57401e04c3fSmrg                                                      prop);
57501e04c3fSmrg      }
57601e04c3fSmrg   }
57701e04c3fSmrg   return vk_outarray_status(&conn);
57801e04c3fSmrg}
57901e04c3fSmrg
58001e04c3fSmrg/*
58101e04c3fSmrg * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display)
58201e04c3fSmrg */
58301e04c3fSmrg
5847ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
5857ec681f3Smrgwsi_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice,
5867ec681f3Smrg                                        uint32_t planeIndex,
5877ec681f3Smrg                                        uint32_t *pDisplayCount,
5887ec681f3Smrg                                        VkDisplayKHR *pDisplays)
58901e04c3fSmrg{
5907ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
5917ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
59201e04c3fSmrg   struct wsi_display *wsi =
59301e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
59401e04c3fSmrg
5957ec681f3Smrg   VK_OUTARRAY_MAKE(conn, pDisplays, pDisplayCount);
59601e04c3fSmrg
59701e04c3fSmrg   int c = 0;
59801e04c3fSmrg
59901e04c3fSmrg   wsi_for_each_connector(connector, wsi) {
6007ec681f3Smrg      if (c == planeIndex && connector->connected) {
60101e04c3fSmrg         vk_outarray_append(&conn, display) {
60201e04c3fSmrg            *display = wsi_display_connector_to_handle(connector);
60301e04c3fSmrg         }
60401e04c3fSmrg      }
60501e04c3fSmrg      c++;
60601e04c3fSmrg   }
60701e04c3fSmrg   return vk_outarray_status(&conn);
60801e04c3fSmrg}
60901e04c3fSmrg
61001e04c3fSmrg/*
61101e04c3fSmrg * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)
61201e04c3fSmrg */
61301e04c3fSmrg
61401e04c3fSmrgstatic void
61501e04c3fSmrgwsi_display_fill_in_display_mode_properties(
61601e04c3fSmrg   struct wsi_device *wsi_device,
61701e04c3fSmrg   struct wsi_display_mode *display_mode,
61801e04c3fSmrg   VkDisplayModeProperties2KHR *properties)
61901e04c3fSmrg{
62001e04c3fSmrg   assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR);
62101e04c3fSmrg   VkDisplayModePropertiesKHR *prop = &properties->displayModeProperties;
62201e04c3fSmrg
62301e04c3fSmrg   prop->displayMode = wsi_display_mode_to_handle(display_mode);
62401e04c3fSmrg   prop->parameters.visibleRegion.width = display_mode->hdisplay;
62501e04c3fSmrg   prop->parameters.visibleRegion.height = display_mode->vdisplay;
62601e04c3fSmrg   prop->parameters.refreshRate =
62701e04c3fSmrg      (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);
62801e04c3fSmrg}
62901e04c3fSmrg
6307ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
6317ec681f3Smrgwsi_GetDisplayModePropertiesKHR(VkPhysicalDevice physicalDevice,
6327ec681f3Smrg                                VkDisplayKHR display,
6337ec681f3Smrg                                uint32_t *pPropertyCount,
6347ec681f3Smrg                                VkDisplayModePropertiesKHR *pProperties)
63501e04c3fSmrg{
6367ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
6377ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
63801e04c3fSmrg   struct wsi_display_connector *connector =
63901e04c3fSmrg      wsi_display_connector_from_handle(display);
64001e04c3fSmrg
6417ec681f3Smrg   VK_OUTARRAY_MAKE(conn, pProperties, pPropertyCount);
64201e04c3fSmrg
64301e04c3fSmrg   wsi_for_each_display_mode(display_mode, connector) {
64401e04c3fSmrg      if (!display_mode->valid)
64501e04c3fSmrg         continue;
64601e04c3fSmrg
64701e04c3fSmrg      vk_outarray_append(&conn, prop) {
64801e04c3fSmrg         VkDisplayModeProperties2KHR prop2 = {
64901e04c3fSmrg            .sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR,
65001e04c3fSmrg         };
65101e04c3fSmrg         wsi_display_fill_in_display_mode_properties(wsi_device,
65201e04c3fSmrg                                                     display_mode, &prop2);
65301e04c3fSmrg         *prop = prop2.displayModeProperties;
65401e04c3fSmrg      }
65501e04c3fSmrg   }
65601e04c3fSmrg   return vk_outarray_status(&conn);
65701e04c3fSmrg}
65801e04c3fSmrg
6597ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
6607ec681f3Smrgwsi_GetDisplayModeProperties2KHR(VkPhysicalDevice physicalDevice,
6617ec681f3Smrg                                 VkDisplayKHR display,
6627ec681f3Smrg                                 uint32_t *pPropertyCount,
6637ec681f3Smrg                                 VkDisplayModeProperties2KHR *pProperties)
66401e04c3fSmrg{
6657ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
6667ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
66701e04c3fSmrg   struct wsi_display_connector *connector =
66801e04c3fSmrg      wsi_display_connector_from_handle(display);
66901e04c3fSmrg
6707ec681f3Smrg   VK_OUTARRAY_MAKE(conn, pProperties, pPropertyCount);
67101e04c3fSmrg
67201e04c3fSmrg   wsi_for_each_display_mode(display_mode, connector) {
67301e04c3fSmrg      if (!display_mode->valid)
67401e04c3fSmrg         continue;
67501e04c3fSmrg
67601e04c3fSmrg      vk_outarray_append(&conn, prop) {
67701e04c3fSmrg         wsi_display_fill_in_display_mode_properties(wsi_device,
67801e04c3fSmrg                                                     display_mode, prop);
67901e04c3fSmrg      }
68001e04c3fSmrg   }
68101e04c3fSmrg   return vk_outarray_status(&conn);
68201e04c3fSmrg}
68301e04c3fSmrg
68401e04c3fSmrgstatic bool
68501e04c3fSmrgwsi_display_mode_matches_vk(wsi_display_mode *wsi,
68601e04c3fSmrg                            const VkDisplayModeParametersKHR *vk)
68701e04c3fSmrg{
68801e04c3fSmrg   return (vk->visibleRegion.width == wsi->hdisplay &&
68901e04c3fSmrg           vk->visibleRegion.height == wsi->vdisplay &&
69001e04c3fSmrg           fabs(wsi_display_mode_refresh(wsi) * 1000.0 - vk->refreshRate) < 10);
69101e04c3fSmrg}
69201e04c3fSmrg
69301e04c3fSmrg/*
69401e04c3fSmrg * Implement vkCreateDisplayModeKHR (VK_KHR_display)
69501e04c3fSmrg */
6967ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
6977ec681f3Smrgwsi_CreateDisplayModeKHR(VkPhysicalDevice physicalDevice,
6987ec681f3Smrg                         VkDisplayKHR display,
6997ec681f3Smrg                         const VkDisplayModeCreateInfoKHR *pCreateInfo,
7007ec681f3Smrg                         const VkAllocationCallbacks *pAllocator,
7017ec681f3Smrg                         VkDisplayModeKHR *pMode)
70201e04c3fSmrg{
70301e04c3fSmrg   struct wsi_display_connector *connector =
70401e04c3fSmrg      wsi_display_connector_from_handle(display);
70501e04c3fSmrg
7067ec681f3Smrg   if (pCreateInfo->flags != 0)
70701e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
70801e04c3fSmrg
70901e04c3fSmrg   /* Check and see if the requested mode happens to match an existing one and
71001e04c3fSmrg    * return that. This makes the conformance suite happy. Doing more than
71101e04c3fSmrg    * this would involve embedding the CVT function into the driver, which seems
71201e04c3fSmrg    * excessive.
71301e04c3fSmrg    */
71401e04c3fSmrg   wsi_for_each_display_mode(display_mode, connector) {
71501e04c3fSmrg      if (display_mode->valid) {
7167ec681f3Smrg         if (wsi_display_mode_matches_vk(display_mode, &pCreateInfo->parameters)) {
7177ec681f3Smrg            *pMode = wsi_display_mode_to_handle(display_mode);
71801e04c3fSmrg            return VK_SUCCESS;
71901e04c3fSmrg         }
72001e04c3fSmrg      }
72101e04c3fSmrg   }
72201e04c3fSmrg   return VK_ERROR_INITIALIZATION_FAILED;
72301e04c3fSmrg}
72401e04c3fSmrg
72501e04c3fSmrg/*
72601e04c3fSmrg * Implement vkGetDisplayPlaneCapabilities
72701e04c3fSmrg */
7287ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
7297ec681f3Smrgwsi_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice,
7307ec681f3Smrg                                   VkDisplayModeKHR _mode,
7317ec681f3Smrg                                   uint32_t planeIndex,
7327ec681f3Smrg                                   VkDisplayPlaneCapabilitiesKHR *pCapabilities)
73301e04c3fSmrg{
7347ec681f3Smrg   struct wsi_display_mode *mode = wsi_display_mode_from_handle(_mode);
73501e04c3fSmrg
73601e04c3fSmrg   /* XXX use actual values */
7377ec681f3Smrg   pCapabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
7387ec681f3Smrg   pCapabilities->minSrcPosition.x = 0;
7397ec681f3Smrg   pCapabilities->minSrcPosition.y = 0;
7407ec681f3Smrg   pCapabilities->maxSrcPosition.x = 0;
7417ec681f3Smrg   pCapabilities->maxSrcPosition.y = 0;
7427ec681f3Smrg   pCapabilities->minSrcExtent.width = mode->hdisplay;
7437ec681f3Smrg   pCapabilities->minSrcExtent.height = mode->vdisplay;
7447ec681f3Smrg   pCapabilities->maxSrcExtent.width = mode->hdisplay;
7457ec681f3Smrg   pCapabilities->maxSrcExtent.height = mode->vdisplay;
7467ec681f3Smrg   pCapabilities->minDstPosition.x = 0;
7477ec681f3Smrg   pCapabilities->minDstPosition.y = 0;
7487ec681f3Smrg   pCapabilities->maxDstPosition.x = 0;
7497ec681f3Smrg   pCapabilities->maxDstPosition.y = 0;
7507ec681f3Smrg   pCapabilities->minDstExtent.width = mode->hdisplay;
7517ec681f3Smrg   pCapabilities->minDstExtent.height = mode->vdisplay;
7527ec681f3Smrg   pCapabilities->maxDstExtent.width = mode->hdisplay;
7537ec681f3Smrg   pCapabilities->maxDstExtent.height = mode->vdisplay;
75401e04c3fSmrg   return VK_SUCCESS;
75501e04c3fSmrg}
75601e04c3fSmrg
7577ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
7587ec681f3Smrgwsi_GetDisplayPlaneCapabilities2KHR(VkPhysicalDevice physicalDevice,
7597ec681f3Smrg                                    const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
7607ec681f3Smrg                                    VkDisplayPlaneCapabilities2KHR *pCapabilities)
76101e04c3fSmrg{
7627ec681f3Smrg   assert(pCapabilities->sType ==
76301e04c3fSmrg          VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR);
76401e04c3fSmrg
7658a1362adSmaya   VkResult result =
7667ec681f3Smrg      wsi_GetDisplayPlaneCapabilitiesKHR(physicalDevice,
7678a1362adSmaya                                         pDisplayPlaneInfo->mode,
7688a1362adSmaya                                         pDisplayPlaneInfo->planeIndex,
7697ec681f3Smrg                                         &pCapabilities->capabilities);
7708a1362adSmaya
7717ec681f3Smrg   vk_foreach_struct(ext, pCapabilities->pNext) {
7728a1362adSmaya      switch (ext->sType) {
7738a1362adSmaya      case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
7748a1362adSmaya         VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext;
7758a1362adSmaya         protected->supportsProtected = VK_FALSE;
7768a1362adSmaya         break;
7778a1362adSmaya      }
7788a1362adSmaya
7798a1362adSmaya      default:
7808a1362adSmaya         /* Ignored */
7818a1362adSmaya         break;
7828a1362adSmaya      }
7838a1362adSmaya   }
7848a1362adSmaya
7858a1362adSmaya   return result;
78601e04c3fSmrg}
78701e04c3fSmrg
7887ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
7897ec681f3Smrgwsi_CreateDisplayPlaneSurfaceKHR(VkInstance _instance,
7907ec681f3Smrg                                 const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
7917ec681f3Smrg                                 const VkAllocationCallbacks *pAllocator,
7927ec681f3Smrg                                 VkSurfaceKHR *pSurface)
79301e04c3fSmrg{
7947ec681f3Smrg   VK_FROM_HANDLE(vk_instance, instance, _instance);
7957ec681f3Smrg   VkIcdSurfaceDisplay *surface;
79601e04c3fSmrg
7977ec681f3Smrg   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR);
7987ec681f3Smrg
7997ec681f3Smrg   surface = vk_zalloc2(&instance->alloc, pAllocator, sizeof(*surface), 8,
8007ec681f3Smrg                        VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
80101e04c3fSmrg   if (surface == NULL)
80201e04c3fSmrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
80301e04c3fSmrg
80401e04c3fSmrg   surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY;
80501e04c3fSmrg
8067ec681f3Smrg   surface->displayMode = pCreateInfo->displayMode;
8077ec681f3Smrg   surface->planeIndex = pCreateInfo->planeIndex;
8087ec681f3Smrg   surface->planeStackIndex = pCreateInfo->planeStackIndex;
8097ec681f3Smrg   surface->transform = pCreateInfo->transform;
8107ec681f3Smrg   surface->globalAlpha = pCreateInfo->globalAlpha;
8117ec681f3Smrg   surface->alphaMode = pCreateInfo->alphaMode;
8127ec681f3Smrg   surface->imageExtent = pCreateInfo->imageExtent;
8137ec681f3Smrg
8147ec681f3Smrg   *pSurface = VkIcdSurfaceBase_to_handle(&surface->base);
81501e04c3fSmrg
81601e04c3fSmrg   return VK_SUCCESS;
81701e04c3fSmrg}
81801e04c3fSmrg
81901e04c3fSmrgstatic VkResult
82001e04c3fSmrgwsi_display_surface_get_support(VkIcdSurfaceBase *surface,
82101e04c3fSmrg                                struct wsi_device *wsi_device,
82201e04c3fSmrg                                uint32_t queueFamilyIndex,
82301e04c3fSmrg                                VkBool32* pSupported)
82401e04c3fSmrg{
8257ec681f3Smrg   struct wsi_display *wsi =
8267ec681f3Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
8277ec681f3Smrg
8287ec681f3Smrg   *pSupported = wsi->fd != -1;
82901e04c3fSmrg   return VK_SUCCESS;
83001e04c3fSmrg}
83101e04c3fSmrg
83201e04c3fSmrgstatic VkResult
83301e04c3fSmrgwsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
8348a1362adSmaya                                     struct wsi_device *wsi_device,
83501e04c3fSmrg                                     VkSurfaceCapabilitiesKHR* caps)
83601e04c3fSmrg{
83701e04c3fSmrg   VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
83801e04c3fSmrg   wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
83901e04c3fSmrg
84001e04c3fSmrg   caps->currentExtent.width = mode->hdisplay;
84101e04c3fSmrg   caps->currentExtent.height = mode->vdisplay;
84201e04c3fSmrg
8438a1362adSmaya   caps->minImageExtent = (VkExtent2D) { 1, 1 };
8448a1362adSmaya   caps->maxImageExtent = (VkExtent2D) {
8458a1362adSmaya      wsi_device->maxImageDimension2D,
8468a1362adSmaya      wsi_device->maxImageDimension2D,
8478a1362adSmaya   };
84801e04c3fSmrg
84901e04c3fSmrg   caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
85001e04c3fSmrg
85101e04c3fSmrg   caps->minImageCount = 2;
85201e04c3fSmrg   caps->maxImageCount = 0;
85301e04c3fSmrg
85401e04c3fSmrg   caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
85501e04c3fSmrg   caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
85601e04c3fSmrg   caps->maxImageArrayLayers = 1;
85701e04c3fSmrg   caps->supportedUsageFlags =
85801e04c3fSmrg      VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
85901e04c3fSmrg      VK_IMAGE_USAGE_SAMPLED_BIT |
86001e04c3fSmrg      VK_IMAGE_USAGE_TRANSFER_DST_BIT |
8618a1362adSmaya      VK_IMAGE_USAGE_STORAGE_BIT |
86201e04c3fSmrg      VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
86301e04c3fSmrg
86401e04c3fSmrg   return VK_SUCCESS;
86501e04c3fSmrg}
86601e04c3fSmrg
86701e04c3fSmrgstatic VkResult
86801e04c3fSmrgwsi_display_surface_get_surface_counters(
86901e04c3fSmrg   VkIcdSurfaceBase *surface_base,
87001e04c3fSmrg   VkSurfaceCounterFlagsEXT *counters)
87101e04c3fSmrg{
87201e04c3fSmrg   *counters = VK_SURFACE_COUNTER_VBLANK_EXT;
87301e04c3fSmrg   return VK_SUCCESS;
87401e04c3fSmrg}
87501e04c3fSmrg
87601e04c3fSmrgstatic VkResult
87701e04c3fSmrgwsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
8788a1362adSmaya                                      struct wsi_device *wsi_device,
87901e04c3fSmrg                                      const void *info_next,
88001e04c3fSmrg                                      VkSurfaceCapabilities2KHR *caps)
88101e04c3fSmrg{
88201e04c3fSmrg   assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
88301e04c3fSmrg   VkResult result;
88401e04c3fSmrg
8858a1362adSmaya   result = wsi_display_surface_get_capabilities(icd_surface, wsi_device,
88601e04c3fSmrg                                                 &caps->surfaceCapabilities);
88701e04c3fSmrg   if (result != VK_SUCCESS)
88801e04c3fSmrg      return result;
88901e04c3fSmrg
89001e04c3fSmrg   struct wsi_surface_supported_counters *counters =
89101e04c3fSmrg      vk_find_struct( caps->pNext, WSI_SURFACE_SUPPORTED_COUNTERS_MESA);
89201e04c3fSmrg
89301e04c3fSmrg   if (counters) {
89401e04c3fSmrg      result = wsi_display_surface_get_surface_counters(
89501e04c3fSmrg         icd_surface,
89601e04c3fSmrg         &counters->supported_surface_counters);
89701e04c3fSmrg   }
89801e04c3fSmrg
89901e04c3fSmrg   return result;
90001e04c3fSmrg}
90101e04c3fSmrg
90201e04c3fSmrgstatic const struct {
90301e04c3fSmrg   VkFormat     format;
90401e04c3fSmrg   uint32_t     drm_format;
90501e04c3fSmrg} available_surface_formats[] = {
90601e04c3fSmrg   { .format = VK_FORMAT_B8G8R8A8_SRGB, .drm_format = DRM_FORMAT_XRGB8888 },
90701e04c3fSmrg   { .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 },
90801e04c3fSmrg};
90901e04c3fSmrg
9107ec681f3Smrgstatic void
9117ec681f3Smrgget_sorted_vk_formats(struct wsi_device *wsi_device, VkFormat *sorted_formats)
9127ec681f3Smrg{
9137ec681f3Smrg   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++)
9147ec681f3Smrg      sorted_formats[i] = available_surface_formats[i].format;
9157ec681f3Smrg
9167ec681f3Smrg   if (wsi_device->force_bgra8_unorm_first) {
9177ec681f3Smrg      for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
9187ec681f3Smrg         if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) {
9197ec681f3Smrg            sorted_formats[i] = sorted_formats[0];
9207ec681f3Smrg            sorted_formats[0] = VK_FORMAT_B8G8R8A8_UNORM;
9217ec681f3Smrg            break;
9227ec681f3Smrg         }
9237ec681f3Smrg      }
9247ec681f3Smrg   }
9257ec681f3Smrg}
9267ec681f3Smrg
92701e04c3fSmrgstatic VkResult
92801e04c3fSmrgwsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface,
92901e04c3fSmrg                                struct wsi_device *wsi_device,
93001e04c3fSmrg                                uint32_t *surface_format_count,
93101e04c3fSmrg                                VkSurfaceFormatKHR *surface_formats)
93201e04c3fSmrg{
93301e04c3fSmrg   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
93401e04c3fSmrg
9357ec681f3Smrg   VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
9367ec681f3Smrg   get_sorted_vk_formats(wsi_device, sorted_formats);
9377ec681f3Smrg
9387ec681f3Smrg   for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
93901e04c3fSmrg      vk_outarray_append(&out, f) {
9407ec681f3Smrg         f->format = sorted_formats[i];
94101e04c3fSmrg         f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
94201e04c3fSmrg      }
94301e04c3fSmrg   }
94401e04c3fSmrg
94501e04c3fSmrg   return vk_outarray_status(&out);
94601e04c3fSmrg}
94701e04c3fSmrg
94801e04c3fSmrgstatic VkResult
94901e04c3fSmrgwsi_display_surface_get_formats2(VkIcdSurfaceBase *surface,
95001e04c3fSmrg                                 struct wsi_device *wsi_device,
95101e04c3fSmrg                                 const void *info_next,
95201e04c3fSmrg                                 uint32_t *surface_format_count,
95301e04c3fSmrg                                 VkSurfaceFormat2KHR *surface_formats)
95401e04c3fSmrg{
95501e04c3fSmrg   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
95601e04c3fSmrg
9577ec681f3Smrg   VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
9587ec681f3Smrg   get_sorted_vk_formats(wsi_device, sorted_formats);
9597ec681f3Smrg
9607ec681f3Smrg   for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
96101e04c3fSmrg      vk_outarray_append(&out, f) {
96201e04c3fSmrg         assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
9637ec681f3Smrg         f->surfaceFormat.format = sorted_formats[i];
96401e04c3fSmrg         f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
96501e04c3fSmrg      }
96601e04c3fSmrg   }
96701e04c3fSmrg
96801e04c3fSmrg   return vk_outarray_status(&out);
96901e04c3fSmrg}
97001e04c3fSmrg
97101e04c3fSmrgstatic VkResult
97201e04c3fSmrgwsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface,
97301e04c3fSmrg                                      uint32_t *present_mode_count,
97401e04c3fSmrg                                      VkPresentModeKHR *present_modes)
97501e04c3fSmrg{
97601e04c3fSmrg   VK_OUTARRAY_MAKE(conn, present_modes, present_mode_count);
97701e04c3fSmrg
97801e04c3fSmrg   vk_outarray_append(&conn, present) {
97901e04c3fSmrg      *present = VK_PRESENT_MODE_FIFO_KHR;
98001e04c3fSmrg   }
98101e04c3fSmrg
98201e04c3fSmrg   return vk_outarray_status(&conn);
98301e04c3fSmrg}
98401e04c3fSmrg
98501e04c3fSmrgstatic VkResult
98601e04c3fSmrgwsi_display_surface_get_present_rectangles(VkIcdSurfaceBase *surface_base,
98701e04c3fSmrg                                           struct wsi_device *wsi_device,
98801e04c3fSmrg                                           uint32_t* pRectCount,
98901e04c3fSmrg                                           VkRect2D* pRects)
99001e04c3fSmrg{
99101e04c3fSmrg   VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
99201e04c3fSmrg   wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
99301e04c3fSmrg   VK_OUTARRAY_MAKE(out, pRects, pRectCount);
99401e04c3fSmrg
99501e04c3fSmrg   if (wsi_device_matches_drm_fd(wsi_device, mode->connector->wsi->fd)) {
99601e04c3fSmrg      vk_outarray_append(&out, rect) {
99701e04c3fSmrg         *rect = (VkRect2D) {
99801e04c3fSmrg            .offset = { 0, 0 },
99901e04c3fSmrg            .extent = { mode->hdisplay, mode->vdisplay },
100001e04c3fSmrg         };
100101e04c3fSmrg      }
100201e04c3fSmrg   }
100301e04c3fSmrg
100401e04c3fSmrg   return vk_outarray_status(&out);
100501e04c3fSmrg}
100601e04c3fSmrg
100701e04c3fSmrgstatic void
100801e04c3fSmrgwsi_display_destroy_buffer(struct wsi_display *wsi,
100901e04c3fSmrg                           uint32_t buffer)
101001e04c3fSmrg{
10118a1362adSmaya   (void) drmIoctl(wsi->fd, DRM_IOCTL_GEM_CLOSE,
10128a1362adSmaya                   &((struct drm_gem_close) { .handle = buffer }));
101301e04c3fSmrg}
101401e04c3fSmrg
101501e04c3fSmrgstatic VkResult
101601e04c3fSmrgwsi_display_image_init(VkDevice device_h,
101701e04c3fSmrg                       struct wsi_swapchain *drv_chain,
101801e04c3fSmrg                       const VkSwapchainCreateInfoKHR *create_info,
101901e04c3fSmrg                       const VkAllocationCallbacks *allocator,
102001e04c3fSmrg                       struct wsi_display_image *image)
102101e04c3fSmrg{
102201e04c3fSmrg   struct wsi_display_swapchain *chain =
102301e04c3fSmrg      (struct wsi_display_swapchain *) drv_chain;
102401e04c3fSmrg   struct wsi_display *wsi = chain->wsi;
102501e04c3fSmrg   uint32_t drm_format = 0;
102601e04c3fSmrg
102701e04c3fSmrg   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
102801e04c3fSmrg      if (create_info->imageFormat == available_surface_formats[i].format) {
102901e04c3fSmrg         drm_format = available_surface_formats[i].drm_format;
103001e04c3fSmrg         break;
103101e04c3fSmrg      }
103201e04c3fSmrg   }
103301e04c3fSmrg
103401e04c3fSmrg   /* the application provided an invalid format, bail */
103501e04c3fSmrg   if (drm_format == 0)
103601e04c3fSmrg      return VK_ERROR_DEVICE_LOST;
103701e04c3fSmrg
103801e04c3fSmrg   VkResult result = wsi_create_native_image(&chain->base, create_info,
10397ec681f3Smrg                                             0, NULL, NULL, NULL,
104001e04c3fSmrg                                             &image->base);
104101e04c3fSmrg   if (result != VK_SUCCESS)
104201e04c3fSmrg      return result;
104301e04c3fSmrg
104401e04c3fSmrg   memset(image->buffer, 0, sizeof (image->buffer));
104501e04c3fSmrg
104601e04c3fSmrg   for (unsigned int i = 0; i < image->base.num_planes; i++) {
104701e04c3fSmrg      int ret = drmPrimeFDToHandle(wsi->fd, image->base.fds[i],
104801e04c3fSmrg                                   &image->buffer[i]);
104901e04c3fSmrg
105001e04c3fSmrg      close(image->base.fds[i]);
105101e04c3fSmrg      image->base.fds[i] = -1;
105201e04c3fSmrg      if (ret < 0)
105301e04c3fSmrg         goto fail_handle;
105401e04c3fSmrg   }
105501e04c3fSmrg
105601e04c3fSmrg   image->chain = chain;
105701e04c3fSmrg   image->state = WSI_IMAGE_IDLE;
105801e04c3fSmrg   image->fb_id = 0;
105901e04c3fSmrg
106001e04c3fSmrg   int ret = drmModeAddFB2(wsi->fd,
106101e04c3fSmrg                           create_info->imageExtent.width,
106201e04c3fSmrg                           create_info->imageExtent.height,
106301e04c3fSmrg                           drm_format,
106401e04c3fSmrg                           image->buffer,
106501e04c3fSmrg                           image->base.row_pitches,
106601e04c3fSmrg                           image->base.offsets,
106701e04c3fSmrg                           &image->fb_id, 0);
106801e04c3fSmrg
106901e04c3fSmrg   if (ret)
107001e04c3fSmrg      goto fail_fb;
107101e04c3fSmrg
107201e04c3fSmrg   return VK_SUCCESS;
107301e04c3fSmrg
107401e04c3fSmrgfail_fb:
107501e04c3fSmrgfail_handle:
107601e04c3fSmrg   for (unsigned int i = 0; i < image->base.num_planes; i++) {
107701e04c3fSmrg      if (image->buffer[i])
107801e04c3fSmrg         wsi_display_destroy_buffer(wsi, image->buffer[i]);
107901e04c3fSmrg      if (image->base.fds[i] != -1) {
108001e04c3fSmrg         close(image->base.fds[i]);
108101e04c3fSmrg         image->base.fds[i] = -1;
108201e04c3fSmrg      }
108301e04c3fSmrg   }
108401e04c3fSmrg
108501e04c3fSmrg   wsi_destroy_image(&chain->base, &image->base);
108601e04c3fSmrg
108701e04c3fSmrg   return VK_ERROR_OUT_OF_HOST_MEMORY;
108801e04c3fSmrg}
108901e04c3fSmrg
109001e04c3fSmrgstatic void
109101e04c3fSmrgwsi_display_image_finish(struct wsi_swapchain *drv_chain,
109201e04c3fSmrg                         const VkAllocationCallbacks *allocator,
109301e04c3fSmrg                         struct wsi_display_image *image)
109401e04c3fSmrg{
109501e04c3fSmrg   struct wsi_display_swapchain *chain =
109601e04c3fSmrg      (struct wsi_display_swapchain *) drv_chain;
109701e04c3fSmrg   struct wsi_display *wsi = chain->wsi;
109801e04c3fSmrg
109901e04c3fSmrg   drmModeRmFB(wsi->fd, image->fb_id);
110001e04c3fSmrg   for (unsigned int i = 0; i < image->base.num_planes; i++)
110101e04c3fSmrg      wsi_display_destroy_buffer(wsi, image->buffer[i]);
110201e04c3fSmrg   wsi_destroy_image(&chain->base, &image->base);
110301e04c3fSmrg}
110401e04c3fSmrg
110501e04c3fSmrgstatic VkResult
110601e04c3fSmrgwsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain,
110701e04c3fSmrg                              const VkAllocationCallbacks *allocator)
110801e04c3fSmrg{
110901e04c3fSmrg   struct wsi_display_swapchain *chain =
111001e04c3fSmrg      (struct wsi_display_swapchain *) drv_chain;
111101e04c3fSmrg
111201e04c3fSmrg   for (uint32_t i = 0; i < chain->base.image_count; i++)
111301e04c3fSmrg      wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);
111401e04c3fSmrg
111501e04c3fSmrg   wsi_swapchain_finish(&chain->base);
111601e04c3fSmrg   vk_free(allocator, chain);
111701e04c3fSmrg   return VK_SUCCESS;
111801e04c3fSmrg}
111901e04c3fSmrg
112001e04c3fSmrgstatic struct wsi_image *
112101e04c3fSmrgwsi_display_get_wsi_image(struct wsi_swapchain *drv_chain,
112201e04c3fSmrg                          uint32_t image_index)
112301e04c3fSmrg{
112401e04c3fSmrg   struct wsi_display_swapchain *chain =
112501e04c3fSmrg      (struct wsi_display_swapchain *) drv_chain;
112601e04c3fSmrg
112701e04c3fSmrg   return &chain->images[image_index].base;
112801e04c3fSmrg}
112901e04c3fSmrg
113001e04c3fSmrgstatic void
113101e04c3fSmrgwsi_display_idle_old_displaying(struct wsi_display_image *active_image)
113201e04c3fSmrg{
113301e04c3fSmrg   struct wsi_display_swapchain *chain = active_image->chain;
113401e04c3fSmrg
113501e04c3fSmrg   wsi_display_debug("idle everyone but %ld\n",
113601e04c3fSmrg                     active_image - &(chain->images[0]));
113701e04c3fSmrg   for (uint32_t i = 0; i < chain->base.image_count; i++)
113801e04c3fSmrg      if (chain->images[i].state == WSI_IMAGE_DISPLAYING &&
113901e04c3fSmrg          &chain->images[i] != active_image)
114001e04c3fSmrg      {
114101e04c3fSmrg         wsi_display_debug("idle %d\n", i);
114201e04c3fSmrg         chain->images[i].state = WSI_IMAGE_IDLE;
114301e04c3fSmrg      }
114401e04c3fSmrg}
114501e04c3fSmrg
114601e04c3fSmrgstatic VkResult
114701e04c3fSmrg_wsi_display_queue_next(struct wsi_swapchain *drv_chain);
114801e04c3fSmrg
114901e04c3fSmrgstatic void
115001e04c3fSmrgwsi_display_page_flip_handler2(int fd,
115101e04c3fSmrg                               unsigned int frame,
115201e04c3fSmrg                               unsigned int sec,
115301e04c3fSmrg                               unsigned int usec,
115401e04c3fSmrg                               uint32_t crtc_id,
115501e04c3fSmrg                               void *data)
115601e04c3fSmrg{
115701e04c3fSmrg   struct wsi_display_image *image = data;
115801e04c3fSmrg   struct wsi_display_swapchain *chain = image->chain;
115901e04c3fSmrg
116001e04c3fSmrg   wsi_display_debug("image %ld displayed at %d\n",
116101e04c3fSmrg                     image - &(image->chain->images[0]), frame);
116201e04c3fSmrg   image->state = WSI_IMAGE_DISPLAYING;
116301e04c3fSmrg   wsi_display_idle_old_displaying(image);
116401e04c3fSmrg   VkResult result = _wsi_display_queue_next(&(chain->base));
116501e04c3fSmrg   if (result != VK_SUCCESS)
116601e04c3fSmrg      chain->status = result;
116701e04c3fSmrg}
116801e04c3fSmrg
116901e04c3fSmrgstatic void wsi_display_fence_event_handler(struct wsi_display_fence *fence);
117001e04c3fSmrg
117101e04c3fSmrgstatic void wsi_display_page_flip_handler(int fd,
117201e04c3fSmrg                                          unsigned int frame,
117301e04c3fSmrg                                          unsigned int sec,
117401e04c3fSmrg                                          unsigned int usec,
117501e04c3fSmrg                                          void *data)
117601e04c3fSmrg{
117701e04c3fSmrg   wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data);
117801e04c3fSmrg}
117901e04c3fSmrg
118001e04c3fSmrgstatic void wsi_display_vblank_handler(int fd, unsigned int frame,
118101e04c3fSmrg                                       unsigned int sec, unsigned int usec,
118201e04c3fSmrg                                       void *data)
118301e04c3fSmrg{
118401e04c3fSmrg   struct wsi_display_fence *fence = data;
118501e04c3fSmrg
118601e04c3fSmrg   wsi_display_fence_event_handler(fence);
118701e04c3fSmrg}
118801e04c3fSmrg
118901e04c3fSmrgstatic void wsi_display_sequence_handler(int fd, uint64_t frame,
119001e04c3fSmrg                                         uint64_t nsec, uint64_t user_data)
119101e04c3fSmrg{
119201e04c3fSmrg   struct wsi_display_fence *fence =
119301e04c3fSmrg      (struct wsi_display_fence *) (uintptr_t) user_data;
119401e04c3fSmrg
119501e04c3fSmrg   wsi_display_fence_event_handler(fence);
119601e04c3fSmrg}
119701e04c3fSmrg
119801e04c3fSmrgstatic drmEventContext event_context = {
119901e04c3fSmrg   .version = DRM_EVENT_CONTEXT_VERSION,
120001e04c3fSmrg   .page_flip_handler = wsi_display_page_flip_handler,
120101e04c3fSmrg#if DRM_EVENT_CONTEXT_VERSION >= 3
120201e04c3fSmrg   .page_flip_handler2 = wsi_display_page_flip_handler2,
120301e04c3fSmrg#endif
120401e04c3fSmrg   .vblank_handler = wsi_display_vblank_handler,
120501e04c3fSmrg   .sequence_handler = wsi_display_sequence_handler,
120601e04c3fSmrg};
120701e04c3fSmrg
120801e04c3fSmrgstatic void *
120901e04c3fSmrgwsi_display_wait_thread(void *data)
121001e04c3fSmrg{
121101e04c3fSmrg   struct wsi_display *wsi = data;
121201e04c3fSmrg   struct pollfd pollfd = {
121301e04c3fSmrg      .fd = wsi->fd,
121401e04c3fSmrg      .events = POLLIN
121501e04c3fSmrg   };
121601e04c3fSmrg
121701e04c3fSmrg   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
121801e04c3fSmrg   for (;;) {
121901e04c3fSmrg      int ret = poll(&pollfd, 1, -1);
122001e04c3fSmrg      if (ret > 0) {
122101e04c3fSmrg         pthread_mutex_lock(&wsi->wait_mutex);
122201e04c3fSmrg         (void) drmHandleEvent(wsi->fd, &event_context);
122301e04c3fSmrg         pthread_cond_broadcast(&wsi->wait_cond);
12247ec681f3Smrg         pthread_mutex_unlock(&wsi->wait_mutex);
122501e04c3fSmrg      }
122601e04c3fSmrg   }
122701e04c3fSmrg   return NULL;
122801e04c3fSmrg}
122901e04c3fSmrg
123001e04c3fSmrgstatic int
123101e04c3fSmrgwsi_display_start_wait_thread(struct wsi_display *wsi)
123201e04c3fSmrg{
123301e04c3fSmrg   if (!wsi->wait_thread) {
123401e04c3fSmrg      int ret = pthread_create(&wsi->wait_thread, NULL,
123501e04c3fSmrg                               wsi_display_wait_thread, wsi);
123601e04c3fSmrg      if (ret)
123701e04c3fSmrg         return ret;
123801e04c3fSmrg   }
123901e04c3fSmrg   return 0;
124001e04c3fSmrg}
124101e04c3fSmrg
12427ec681f3Smrgstatic void
12437ec681f3Smrgwsi_display_stop_wait_thread(struct wsi_display *wsi)
12447ec681f3Smrg{
12457ec681f3Smrg   pthread_mutex_lock(&wsi->wait_mutex);
12467ec681f3Smrg   if (wsi->wait_thread) {
12477ec681f3Smrg      pthread_cancel(wsi->wait_thread);
12487ec681f3Smrg      pthread_join(wsi->wait_thread, NULL);
12497ec681f3Smrg      wsi->wait_thread = 0;
12507ec681f3Smrg   }
12517ec681f3Smrg   pthread_mutex_unlock(&wsi->wait_mutex);
12527ec681f3Smrg}
12537ec681f3Smrg
125401e04c3fSmrg/*
125501e04c3fSmrg * Wait for at least one event from the kernel to be processed.
125601e04c3fSmrg * Call with wait_mutex held
125701e04c3fSmrg */
125801e04c3fSmrgstatic int
125901e04c3fSmrgwsi_display_wait_for_event(struct wsi_display *wsi,
126001e04c3fSmrg                           uint64_t timeout_ns)
126101e04c3fSmrg{
126201e04c3fSmrg   int ret;
126301e04c3fSmrg
126401e04c3fSmrg   ret = wsi_display_start_wait_thread(wsi);
126501e04c3fSmrg
126601e04c3fSmrg   if (ret)
126701e04c3fSmrg      return ret;
126801e04c3fSmrg
126901e04c3fSmrg   struct timespec abs_timeout = {
127001e04c3fSmrg      .tv_sec = timeout_ns / 1000000000ULL,
127101e04c3fSmrg      .tv_nsec = timeout_ns % 1000000000ULL,
127201e04c3fSmrg   };
127301e04c3fSmrg
127401e04c3fSmrg   ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex,
127501e04c3fSmrg                                &abs_timeout);
127601e04c3fSmrg
127701e04c3fSmrg   wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret);
127801e04c3fSmrg   return ret;
127901e04c3fSmrg}
128001e04c3fSmrg
128101e04c3fSmrgstatic VkResult
128201e04c3fSmrgwsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
128301e04c3fSmrg                               const VkAcquireNextImageInfoKHR *info,
128401e04c3fSmrg                               uint32_t *image_index)
128501e04c3fSmrg{
128601e04c3fSmrg   struct wsi_display_swapchain *chain =
128701e04c3fSmrg      (struct wsi_display_swapchain *)drv_chain;
128801e04c3fSmrg   struct wsi_display *wsi = chain->wsi;
128901e04c3fSmrg   int ret = 0;
129001e04c3fSmrg   VkResult result = VK_SUCCESS;
129101e04c3fSmrg
129201e04c3fSmrg   /* Bail early if the swapchain is broken */
129301e04c3fSmrg   if (chain->status != VK_SUCCESS)
129401e04c3fSmrg      return chain->status;
129501e04c3fSmrg
129601e04c3fSmrg   uint64_t timeout = info->timeout;
129701e04c3fSmrg   if (timeout != 0 && timeout != UINT64_MAX)
129801e04c3fSmrg      timeout = wsi_rel_to_abs_time(timeout);
129901e04c3fSmrg
130001e04c3fSmrg   pthread_mutex_lock(&wsi->wait_mutex);
130101e04c3fSmrg   for (;;) {
130201e04c3fSmrg      for (uint32_t i = 0; i < chain->base.image_count; i++) {
130301e04c3fSmrg         if (chain->images[i].state == WSI_IMAGE_IDLE) {
130401e04c3fSmrg            *image_index = i;
130501e04c3fSmrg            wsi_display_debug("image %d available\n", i);
130601e04c3fSmrg            chain->images[i].state = WSI_IMAGE_DRAWING;
130701e04c3fSmrg            result = VK_SUCCESS;
130801e04c3fSmrg            goto done;
130901e04c3fSmrg         }
131001e04c3fSmrg         wsi_display_debug("image %d state %d\n", i, chain->images[i].state);
131101e04c3fSmrg      }
131201e04c3fSmrg
131301e04c3fSmrg      if (ret == ETIMEDOUT) {
131401e04c3fSmrg         result = VK_TIMEOUT;
131501e04c3fSmrg         goto done;
131601e04c3fSmrg      }
131701e04c3fSmrg
131801e04c3fSmrg      ret = wsi_display_wait_for_event(wsi, timeout);
131901e04c3fSmrg
132001e04c3fSmrg      if (ret && ret != ETIMEDOUT) {
132101e04c3fSmrg         result = VK_ERROR_SURFACE_LOST_KHR;
132201e04c3fSmrg         goto done;
132301e04c3fSmrg      }
132401e04c3fSmrg   }
132501e04c3fSmrgdone:
132601e04c3fSmrg   pthread_mutex_unlock(&wsi->wait_mutex);
132701e04c3fSmrg
132801e04c3fSmrg   if (result != VK_SUCCESS)
132901e04c3fSmrg      return result;
133001e04c3fSmrg
133101e04c3fSmrg   return chain->status;
133201e04c3fSmrg}
133301e04c3fSmrg
133401e04c3fSmrg/*
133501e04c3fSmrg * Check whether there are any other connectors driven by this crtc
133601e04c3fSmrg */
133701e04c3fSmrgstatic bool
133801e04c3fSmrgwsi_display_crtc_solo(struct wsi_display *wsi,
133901e04c3fSmrg                      drmModeResPtr mode_res,
134001e04c3fSmrg                      drmModeConnectorPtr connector,
134101e04c3fSmrg                      uint32_t crtc_id)
134201e04c3fSmrg{
134301e04c3fSmrg   /* See if any other connectors share the same encoder */
134401e04c3fSmrg   for (int c = 0; c < mode_res->count_connectors; c++) {
134501e04c3fSmrg      if (mode_res->connectors[c] == connector->connector_id)
134601e04c3fSmrg         continue;
134701e04c3fSmrg
134801e04c3fSmrg      drmModeConnectorPtr other_connector =
134901e04c3fSmrg         drmModeGetConnector(wsi->fd, mode_res->connectors[c]);
135001e04c3fSmrg
135101e04c3fSmrg      if (other_connector) {
135201e04c3fSmrg         bool match = (other_connector->encoder_id == connector->encoder_id);
135301e04c3fSmrg         drmModeFreeConnector(other_connector);
135401e04c3fSmrg         if (match)
135501e04c3fSmrg            return false;
135601e04c3fSmrg      }
135701e04c3fSmrg   }
135801e04c3fSmrg
135901e04c3fSmrg   /* See if any other encoders share the same crtc */
136001e04c3fSmrg   for (int e = 0; e < mode_res->count_encoders; e++) {
136101e04c3fSmrg      if (mode_res->encoders[e] == connector->encoder_id)
136201e04c3fSmrg         continue;
136301e04c3fSmrg
136401e04c3fSmrg      drmModeEncoderPtr other_encoder =
136501e04c3fSmrg         drmModeGetEncoder(wsi->fd, mode_res->encoders[e]);
136601e04c3fSmrg
136701e04c3fSmrg      if (other_encoder) {
136801e04c3fSmrg         bool match = (other_encoder->crtc_id == crtc_id);
136901e04c3fSmrg         drmModeFreeEncoder(other_encoder);
137001e04c3fSmrg         if (match)
137101e04c3fSmrg            return false;
137201e04c3fSmrg      }
137301e04c3fSmrg   }
137401e04c3fSmrg   return true;
137501e04c3fSmrg}
137601e04c3fSmrg
137701e04c3fSmrg/*
137801e04c3fSmrg * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is
137901e04c3fSmrg * currently driving this connector and not any others. Settle for a CRTC
138001e04c3fSmrg * which is currently idle.
138101e04c3fSmrg */
138201e04c3fSmrgstatic uint32_t
138301e04c3fSmrgwsi_display_select_crtc(const struct wsi_display_connector *connector,
138401e04c3fSmrg                        drmModeResPtr mode_res,
138501e04c3fSmrg                        drmModeConnectorPtr drm_connector)
138601e04c3fSmrg{
138701e04c3fSmrg   struct wsi_display *wsi = connector->wsi;
138801e04c3fSmrg
138901e04c3fSmrg   /* See what CRTC is currently driving this connector */
139001e04c3fSmrg   if (drm_connector->encoder_id) {
139101e04c3fSmrg      drmModeEncoderPtr encoder =
139201e04c3fSmrg         drmModeGetEncoder(wsi->fd, drm_connector->encoder_id);
139301e04c3fSmrg
139401e04c3fSmrg      if (encoder) {
139501e04c3fSmrg         uint32_t crtc_id = encoder->crtc_id;
139601e04c3fSmrg         drmModeFreeEncoder(encoder);
139701e04c3fSmrg         if (crtc_id) {
139801e04c3fSmrg            if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id))
139901e04c3fSmrg               return crtc_id;
140001e04c3fSmrg         }
140101e04c3fSmrg      }
140201e04c3fSmrg   }
140301e04c3fSmrg   uint32_t crtc_id = 0;
140401e04c3fSmrg   for (int c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
140501e04c3fSmrg      drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->fd, mode_res->crtcs[c]);
140601e04c3fSmrg      if (crtc && crtc->buffer_id == 0)
140701e04c3fSmrg         crtc_id = crtc->crtc_id;
140801e04c3fSmrg      drmModeFreeCrtc(crtc);
140901e04c3fSmrg   }
141001e04c3fSmrg   return crtc_id;
141101e04c3fSmrg}
141201e04c3fSmrg
141301e04c3fSmrgstatic VkResult
141401e04c3fSmrgwsi_display_setup_connector(wsi_display_connector *connector,
141501e04c3fSmrg                            wsi_display_mode *display_mode)
141601e04c3fSmrg{
141701e04c3fSmrg   struct wsi_display *wsi = connector->wsi;
141801e04c3fSmrg
141901e04c3fSmrg   if (connector->current_mode == display_mode && connector->crtc_id)
142001e04c3fSmrg      return VK_SUCCESS;
142101e04c3fSmrg
142201e04c3fSmrg   VkResult result = VK_SUCCESS;
142301e04c3fSmrg
142401e04c3fSmrg   drmModeResPtr mode_res = drmModeGetResources(wsi->fd);
142501e04c3fSmrg   if (!mode_res) {
142601e04c3fSmrg      if (errno == ENOMEM)
142701e04c3fSmrg         result = VK_ERROR_OUT_OF_HOST_MEMORY;
142801e04c3fSmrg      else
142901e04c3fSmrg         result = VK_ERROR_SURFACE_LOST_KHR;
143001e04c3fSmrg      goto bail;
143101e04c3fSmrg   }
143201e04c3fSmrg
143301e04c3fSmrg   drmModeConnectorPtr drm_connector =
143401e04c3fSmrg      drmModeGetConnectorCurrent(wsi->fd, connector->id);
143501e04c3fSmrg
143601e04c3fSmrg   if (!drm_connector) {
143701e04c3fSmrg      if (errno == ENOMEM)
143801e04c3fSmrg         result = VK_ERROR_OUT_OF_HOST_MEMORY;
143901e04c3fSmrg      else
144001e04c3fSmrg         result = VK_ERROR_SURFACE_LOST_KHR;
144101e04c3fSmrg      goto bail_mode_res;
144201e04c3fSmrg   }
144301e04c3fSmrg
144401e04c3fSmrg   /* Pick a CRTC if we don't have one */
144501e04c3fSmrg   if (!connector->crtc_id) {
144601e04c3fSmrg      connector->crtc_id = wsi_display_select_crtc(connector,
144701e04c3fSmrg                                                   mode_res, drm_connector);
144801e04c3fSmrg      if (!connector->crtc_id) {
144901e04c3fSmrg         result = VK_ERROR_SURFACE_LOST_KHR;
145001e04c3fSmrg         goto bail_connector;
145101e04c3fSmrg      }
145201e04c3fSmrg   }
145301e04c3fSmrg
145401e04c3fSmrg   if (connector->current_mode != display_mode) {
145501e04c3fSmrg
145601e04c3fSmrg      /* Find the drm mode corresponding to the requested VkDisplayMode */
145701e04c3fSmrg      drmModeModeInfoPtr drm_mode = NULL;
145801e04c3fSmrg
145901e04c3fSmrg      for (int m = 0; m < drm_connector->count_modes; m++) {
146001e04c3fSmrg         drm_mode = &drm_connector->modes[m];
146101e04c3fSmrg         if (wsi_display_mode_matches_drm(display_mode, drm_mode))
146201e04c3fSmrg            break;
146301e04c3fSmrg         drm_mode = NULL;
146401e04c3fSmrg      }
146501e04c3fSmrg
146601e04c3fSmrg      if (!drm_mode) {
146701e04c3fSmrg         result = VK_ERROR_SURFACE_LOST_KHR;
146801e04c3fSmrg         goto bail_connector;
146901e04c3fSmrg      }
147001e04c3fSmrg
147101e04c3fSmrg      connector->current_mode = display_mode;
147201e04c3fSmrg      connector->current_drm_mode = *drm_mode;
147301e04c3fSmrg   }
147401e04c3fSmrg
147501e04c3fSmrgbail_connector:
147601e04c3fSmrg   drmModeFreeConnector(drm_connector);
147701e04c3fSmrgbail_mode_res:
147801e04c3fSmrg   drmModeFreeResources(mode_res);
147901e04c3fSmrgbail:
148001e04c3fSmrg   return result;
148101e04c3fSmrg
148201e04c3fSmrg}
148301e04c3fSmrg
148401e04c3fSmrgstatic VkResult
148501e04c3fSmrgwsi_display_fence_wait(struct wsi_fence *fence_wsi, uint64_t timeout)
148601e04c3fSmrg{
148701e04c3fSmrg   const struct wsi_device *wsi_device = fence_wsi->wsi_device;
148801e04c3fSmrg   struct wsi_display *wsi =
148901e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
149001e04c3fSmrg   struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
149101e04c3fSmrg
149201e04c3fSmrg   wsi_display_debug("%9lu wait fence %lu %ld\n",
149301e04c3fSmrg                     pthread_self(), fence->sequence,
14948a1362adSmaya                     (int64_t) (timeout - wsi_common_get_current_time()));
14958a1362adSmaya   wsi_display_debug_code(uint64_t start_ns = wsi_common_get_current_time());
149601e04c3fSmrg   pthread_mutex_lock(&wsi->wait_mutex);
149701e04c3fSmrg
149801e04c3fSmrg   VkResult result;
149901e04c3fSmrg   int ret = 0;
150001e04c3fSmrg   for (;;) {
150101e04c3fSmrg      if (fence->event_received) {
150201e04c3fSmrg         wsi_display_debug("%9lu fence %lu passed\n",
150301e04c3fSmrg                           pthread_self(), fence->sequence);
150401e04c3fSmrg         result = VK_SUCCESS;
150501e04c3fSmrg         break;
150601e04c3fSmrg      }
150701e04c3fSmrg
150801e04c3fSmrg      if (ret == ETIMEDOUT) {
150901e04c3fSmrg         wsi_display_debug("%9lu fence %lu timeout\n",
151001e04c3fSmrg                           pthread_self(), fence->sequence);
151101e04c3fSmrg         result = VK_TIMEOUT;
151201e04c3fSmrg         break;
151301e04c3fSmrg      }
151401e04c3fSmrg
151501e04c3fSmrg      ret = wsi_display_wait_for_event(wsi, timeout);
151601e04c3fSmrg
151701e04c3fSmrg      if (ret && ret != ETIMEDOUT) {
151801e04c3fSmrg         wsi_display_debug("%9lu fence %lu error\n",
151901e04c3fSmrg                           pthread_self(), fence->sequence);
152001e04c3fSmrg         result = VK_ERROR_DEVICE_LOST;
152101e04c3fSmrg         break;
152201e04c3fSmrg      }
152301e04c3fSmrg   }
152401e04c3fSmrg   pthread_mutex_unlock(&wsi->wait_mutex);
152501e04c3fSmrg   wsi_display_debug("%9lu fence wait %f ms\n",
152601e04c3fSmrg                     pthread_self(),
15278a1362adSmaya                     ((int64_t) (wsi_common_get_current_time() - start_ns)) /
152801e04c3fSmrg                     1.0e6);
152901e04c3fSmrg   return result;
153001e04c3fSmrg}
153101e04c3fSmrg
153201e04c3fSmrgstatic void
153301e04c3fSmrgwsi_display_fence_check_free(struct wsi_display_fence *fence)
153401e04c3fSmrg{
153501e04c3fSmrg   if (fence->event_received && fence->destroyed)
153601e04c3fSmrg      vk_free(fence->base.alloc, fence);
153701e04c3fSmrg}
153801e04c3fSmrg
153901e04c3fSmrgstatic void wsi_display_fence_event_handler(struct wsi_display_fence *fence)
154001e04c3fSmrg{
15417ec681f3Smrg   struct wsi_display *wsi =
15427ec681f3Smrg      (struct wsi_display *) fence->base.wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
15437ec681f3Smrg
15447ec681f3Smrg   if (fence->syncobj) {
15457ec681f3Smrg      (void) drmSyncobjSignal(wsi->fd, &fence->syncobj, 1);
15467ec681f3Smrg      (void) drmSyncobjDestroy(wsi->fd, fence->syncobj);
15477ec681f3Smrg   }
15487ec681f3Smrg
154901e04c3fSmrg   fence->event_received = true;
155001e04c3fSmrg   wsi_display_fence_check_free(fence);
155101e04c3fSmrg}
155201e04c3fSmrg
155301e04c3fSmrgstatic void
155401e04c3fSmrgwsi_display_fence_destroy(struct wsi_fence *fence_wsi)
155501e04c3fSmrg{
155601e04c3fSmrg   struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
155701e04c3fSmrg
155801e04c3fSmrg   assert(!fence->destroyed);
155901e04c3fSmrg   fence->destroyed = true;
156001e04c3fSmrg   wsi_display_fence_check_free(fence);
156101e04c3fSmrg}
156201e04c3fSmrg
156301e04c3fSmrgstatic struct wsi_display_fence *
156401e04c3fSmrgwsi_display_fence_alloc(VkDevice device,
156501e04c3fSmrg                        const struct wsi_device *wsi_device,
156601e04c3fSmrg                        VkDisplayKHR display,
15677ec681f3Smrg                        const VkAllocationCallbacks *allocator,
15687ec681f3Smrg                        int sync_fd)
156901e04c3fSmrg{
157001e04c3fSmrg   struct wsi_display *wsi =
157101e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
157201e04c3fSmrg   struct wsi_display_fence *fence =
157301e04c3fSmrg      vk_zalloc2(wsi->alloc, allocator, sizeof (*fence),
157401e04c3fSmrg                8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
157501e04c3fSmrg
157601e04c3fSmrg   if (!fence)
157701e04c3fSmrg      return NULL;
157801e04c3fSmrg
15797ec681f3Smrg   if (sync_fd >= 0) {
15807ec681f3Smrg      int ret = drmSyncobjFDToHandle(wsi->fd, sync_fd, &fence->syncobj);
15817ec681f3Smrg      if (ret) {
15827ec681f3Smrg         vk_free2(wsi->alloc, allocator, fence);
15837ec681f3Smrg         return NULL;
15847ec681f3Smrg      }
15857ec681f3Smrg   }
15867ec681f3Smrg
158701e04c3fSmrg   fence->base.device = device;
158801e04c3fSmrg   fence->base.display = display;
158901e04c3fSmrg   fence->base.wsi_device = wsi_device;
159001e04c3fSmrg   fence->base.alloc = allocator ? allocator : wsi->alloc;
159101e04c3fSmrg   fence->base.wait = wsi_display_fence_wait;
159201e04c3fSmrg   fence->base.destroy = wsi_display_fence_destroy;
159301e04c3fSmrg   fence->event_received = false;
159401e04c3fSmrg   fence->destroyed = false;
159501e04c3fSmrg   fence->sequence = ++fence_sequence;
159601e04c3fSmrg   return fence;
159701e04c3fSmrg}
159801e04c3fSmrg
159901e04c3fSmrgstatic VkResult
160001e04c3fSmrgwsi_register_vblank_event(struct wsi_display_fence *fence,
160101e04c3fSmrg                          const struct wsi_device *wsi_device,
160201e04c3fSmrg                          VkDisplayKHR display,
160301e04c3fSmrg                          uint32_t flags,
160401e04c3fSmrg                          uint64_t frame_requested,
160501e04c3fSmrg                          uint64_t *frame_queued)
160601e04c3fSmrg{
160701e04c3fSmrg   struct wsi_display *wsi =
160801e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
160901e04c3fSmrg   struct wsi_display_connector *connector =
161001e04c3fSmrg      wsi_display_connector_from_handle(display);
161101e04c3fSmrg
161201e04c3fSmrg   if (wsi->fd < 0)
161301e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
161401e04c3fSmrg
161501e04c3fSmrg   for (;;) {
161601e04c3fSmrg      int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id,
161701e04c3fSmrg                                     flags,
161801e04c3fSmrg                                     frame_requested,
161901e04c3fSmrg                                     frame_queued,
162001e04c3fSmrg                                     (uintptr_t) fence);
162101e04c3fSmrg
162201e04c3fSmrg      if (!ret)
162301e04c3fSmrg         return VK_SUCCESS;
162401e04c3fSmrg
162501e04c3fSmrg      if (errno != ENOMEM) {
162601e04c3fSmrg
162701e04c3fSmrg         /* Something unexpected happened. Pause for a moment so the
162801e04c3fSmrg          * application doesn't just spin and then return a failure indication
162901e04c3fSmrg          */
163001e04c3fSmrg
163101e04c3fSmrg         wsi_display_debug("queue vblank event %lu failed\n", fence->sequence);
163201e04c3fSmrg         struct timespec delay = {
163301e04c3fSmrg            .tv_sec = 0,
163401e04c3fSmrg            .tv_nsec = 100000000ull,
163501e04c3fSmrg         };
163601e04c3fSmrg         nanosleep(&delay, NULL);
163701e04c3fSmrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
163801e04c3fSmrg      }
163901e04c3fSmrg
164001e04c3fSmrg      /* The kernel event queue is full. Wait for some events to be
164101e04c3fSmrg       * processed and try again
164201e04c3fSmrg       */
164301e04c3fSmrg
164401e04c3fSmrg      pthread_mutex_lock(&wsi->wait_mutex);
164501e04c3fSmrg      ret = wsi_display_wait_for_event(wsi, wsi_rel_to_abs_time(100000000ull));
164601e04c3fSmrg      pthread_mutex_unlock(&wsi->wait_mutex);
164701e04c3fSmrg
164801e04c3fSmrg      if (ret) {
164901e04c3fSmrg         wsi_display_debug("vblank queue full, event wait failed\n");
165001e04c3fSmrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
165101e04c3fSmrg      }
165201e04c3fSmrg   }
165301e04c3fSmrg}
165401e04c3fSmrg
165501e04c3fSmrg/*
165601e04c3fSmrg * Check to see if the kernel has no flip queued and if there's an image
165701e04c3fSmrg * waiting to be displayed.
165801e04c3fSmrg */
165901e04c3fSmrgstatic VkResult
166001e04c3fSmrg_wsi_display_queue_next(struct wsi_swapchain *drv_chain)
166101e04c3fSmrg{
166201e04c3fSmrg   struct wsi_display_swapchain *chain =
166301e04c3fSmrg      (struct wsi_display_swapchain *) drv_chain;
166401e04c3fSmrg   struct wsi_display *wsi = chain->wsi;
166501e04c3fSmrg   VkIcdSurfaceDisplay *surface = chain->surface;
166601e04c3fSmrg   wsi_display_mode *display_mode =
166701e04c3fSmrg      wsi_display_mode_from_handle(surface->displayMode);
166801e04c3fSmrg   wsi_display_connector *connector = display_mode->connector;
166901e04c3fSmrg
167001e04c3fSmrg   if (wsi->fd < 0)
167101e04c3fSmrg      return VK_ERROR_SURFACE_LOST_KHR;
167201e04c3fSmrg
167301e04c3fSmrg   if (display_mode != connector->current_mode)
167401e04c3fSmrg      connector->active = false;
167501e04c3fSmrg
167601e04c3fSmrg   for (;;) {
167701e04c3fSmrg
167801e04c3fSmrg      /* Check to see if there is an image to display, or if some image is
167901e04c3fSmrg       * already queued */
168001e04c3fSmrg
168101e04c3fSmrg      struct wsi_display_image *image = NULL;
168201e04c3fSmrg
168301e04c3fSmrg      for (uint32_t i = 0; i < chain->base.image_count; i++) {
168401e04c3fSmrg         struct wsi_display_image *tmp_image = &chain->images[i];
168501e04c3fSmrg
168601e04c3fSmrg         switch (tmp_image->state) {
168701e04c3fSmrg         case WSI_IMAGE_FLIPPING:
168801e04c3fSmrg            /* already flipping, don't send another to the kernel yet */
168901e04c3fSmrg            return VK_SUCCESS;
169001e04c3fSmrg         case WSI_IMAGE_QUEUED:
169101e04c3fSmrg            /* find the oldest queued */
169201e04c3fSmrg            if (!image || tmp_image->flip_sequence < image->flip_sequence)
169301e04c3fSmrg               image = tmp_image;
169401e04c3fSmrg            break;
169501e04c3fSmrg         default:
169601e04c3fSmrg            break;
169701e04c3fSmrg         }
169801e04c3fSmrg      }
169901e04c3fSmrg
170001e04c3fSmrg      if (!image)
170101e04c3fSmrg         return VK_SUCCESS;
170201e04c3fSmrg
170301e04c3fSmrg      int ret;
170401e04c3fSmrg      if (connector->active) {
170501e04c3fSmrg         ret = drmModePageFlip(wsi->fd, connector->crtc_id, image->fb_id,
170601e04c3fSmrg                                   DRM_MODE_PAGE_FLIP_EVENT, image);
170701e04c3fSmrg         if (ret == 0) {
170801e04c3fSmrg            image->state = WSI_IMAGE_FLIPPING;
170901e04c3fSmrg            return VK_SUCCESS;
171001e04c3fSmrg         }
171101e04c3fSmrg         wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret));
171201e04c3fSmrg      } else {
171301e04c3fSmrg         ret = -EINVAL;
171401e04c3fSmrg      }
171501e04c3fSmrg
171601e04c3fSmrg      if (ret == -EINVAL) {
171701e04c3fSmrg         VkResult result = wsi_display_setup_connector(connector, display_mode);
171801e04c3fSmrg
171901e04c3fSmrg         if (result != VK_SUCCESS) {
172001e04c3fSmrg            image->state = WSI_IMAGE_IDLE;
172101e04c3fSmrg            return result;
172201e04c3fSmrg         }
172301e04c3fSmrg
172401e04c3fSmrg         /* XXX allow setting of position */
172501e04c3fSmrg         ret = drmModeSetCrtc(wsi->fd, connector->crtc_id,
172601e04c3fSmrg                              image->fb_id, 0, 0,
172701e04c3fSmrg                              &connector->id, 1,
172801e04c3fSmrg                              &connector->current_drm_mode);
172901e04c3fSmrg         if (ret == 0) {
17307ec681f3Smrg            /* Disable the HW cursor as the app doesn't have a mechanism
17317ec681f3Smrg             * to control it.
17327ec681f3Smrg             * Refer to question 12 of the VK_KHR_display spec.
17337ec681f3Smrg             */
17347ec681f3Smrg            ret = drmModeSetCursor(wsi->fd, connector->crtc_id, 0, 0, 0 );
17357ec681f3Smrg            if (ret != 0) {
17367ec681f3Smrg               wsi_display_debug("failed to hide cursor err %d %s\n", ret, strerror(-ret));
17377ec681f3Smrg            }
17387ec681f3Smrg
173901e04c3fSmrg            /* Assume that the mode set is synchronous and that any
174001e04c3fSmrg             * previous image is now idle.
174101e04c3fSmrg             */
174201e04c3fSmrg            image->state = WSI_IMAGE_DISPLAYING;
174301e04c3fSmrg            wsi_display_idle_old_displaying(image);
174401e04c3fSmrg            connector->active = true;
174501e04c3fSmrg            return VK_SUCCESS;
174601e04c3fSmrg         }
174701e04c3fSmrg      }
174801e04c3fSmrg
174901e04c3fSmrg      if (ret != -EACCES) {
175001e04c3fSmrg         connector->active = false;
175101e04c3fSmrg         image->state = WSI_IMAGE_IDLE;
175201e04c3fSmrg         return VK_ERROR_SURFACE_LOST_KHR;
175301e04c3fSmrg      }
175401e04c3fSmrg
175501e04c3fSmrg      /* Some other VT is currently active. Sit here waiting for
175601e04c3fSmrg       * our VT to become active again by polling once a second
175701e04c3fSmrg       */
175801e04c3fSmrg      usleep(1000 * 1000);
175901e04c3fSmrg      connector->active = false;
176001e04c3fSmrg   }
176101e04c3fSmrg}
176201e04c3fSmrg
176301e04c3fSmrgstatic VkResult
176401e04c3fSmrgwsi_display_queue_present(struct wsi_swapchain *drv_chain,
176501e04c3fSmrg                          uint32_t image_index,
176601e04c3fSmrg                          const VkPresentRegionKHR *damage)
176701e04c3fSmrg{
176801e04c3fSmrg   struct wsi_display_swapchain *chain =
176901e04c3fSmrg      (struct wsi_display_swapchain *) drv_chain;
177001e04c3fSmrg   struct wsi_display *wsi = chain->wsi;
177101e04c3fSmrg   struct wsi_display_image *image = &chain->images[image_index];
177201e04c3fSmrg   VkResult result;
177301e04c3fSmrg
177401e04c3fSmrg   /* Bail early if the swapchain is broken */
177501e04c3fSmrg   if (chain->status != VK_SUCCESS)
177601e04c3fSmrg      return chain->status;
177701e04c3fSmrg
177801e04c3fSmrg   assert(image->state == WSI_IMAGE_DRAWING);
177901e04c3fSmrg   wsi_display_debug("present %d\n", image_index);
178001e04c3fSmrg
178101e04c3fSmrg   pthread_mutex_lock(&wsi->wait_mutex);
178201e04c3fSmrg
178301e04c3fSmrg   image->flip_sequence = ++chain->flip_sequence;
178401e04c3fSmrg   image->state = WSI_IMAGE_QUEUED;
178501e04c3fSmrg
178601e04c3fSmrg   result = _wsi_display_queue_next(drv_chain);
178701e04c3fSmrg   if (result != VK_SUCCESS)
178801e04c3fSmrg      chain->status = result;
178901e04c3fSmrg
179001e04c3fSmrg   pthread_mutex_unlock(&wsi->wait_mutex);
179101e04c3fSmrg
179201e04c3fSmrg   if (result != VK_SUCCESS)
179301e04c3fSmrg      return result;
179401e04c3fSmrg
179501e04c3fSmrg   return chain->status;
179601e04c3fSmrg}
179701e04c3fSmrg
179801e04c3fSmrgstatic VkResult
179901e04c3fSmrgwsi_display_surface_create_swapchain(
180001e04c3fSmrg   VkIcdSurfaceBase *icd_surface,
180101e04c3fSmrg   VkDevice device,
180201e04c3fSmrg   struct wsi_device *wsi_device,
180301e04c3fSmrg   const VkSwapchainCreateInfoKHR *create_info,
180401e04c3fSmrg   const VkAllocationCallbacks *allocator,
180501e04c3fSmrg   struct wsi_swapchain **swapchain_out)
180601e04c3fSmrg{
180701e04c3fSmrg   struct wsi_display *wsi =
180801e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
180901e04c3fSmrg
181001e04c3fSmrg   assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
181101e04c3fSmrg
181201e04c3fSmrg   const unsigned num_images = create_info->minImageCount;
181301e04c3fSmrg   struct wsi_display_swapchain *chain =
181401e04c3fSmrg      vk_zalloc(allocator,
181501e04c3fSmrg                sizeof(*chain) + num_images * sizeof(chain->images[0]),
181601e04c3fSmrg                8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
181701e04c3fSmrg
181801e04c3fSmrg   if (chain == NULL)
181901e04c3fSmrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
182001e04c3fSmrg
182101e04c3fSmrg   VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device,
182201e04c3fSmrg                                        create_info, allocator);
182301e04c3fSmrg   if (result != VK_SUCCESS) {
182401e04c3fSmrg      vk_free(allocator, chain);
182501e04c3fSmrg      return result;
182601e04c3fSmrg   }
182701e04c3fSmrg
182801e04c3fSmrg   chain->base.destroy = wsi_display_swapchain_destroy;
182901e04c3fSmrg   chain->base.get_wsi_image = wsi_display_get_wsi_image;
183001e04c3fSmrg   chain->base.acquire_next_image = wsi_display_acquire_next_image;
183101e04c3fSmrg   chain->base.queue_present = wsi_display_queue_present;
18328a1362adSmaya   chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);
183301e04c3fSmrg   chain->base.image_count = num_images;
183401e04c3fSmrg
183501e04c3fSmrg   chain->wsi = wsi;
183601e04c3fSmrg   chain->status = VK_SUCCESS;
183701e04c3fSmrg
183801e04c3fSmrg   chain->surface = (VkIcdSurfaceDisplay *) icd_surface;
183901e04c3fSmrg
184001e04c3fSmrg   for (uint32_t image = 0; image < chain->base.image_count; image++) {
184101e04c3fSmrg      result = wsi_display_image_init(device, &chain->base,
184201e04c3fSmrg                                      create_info, allocator,
184301e04c3fSmrg                                      &chain->images[image]);
184401e04c3fSmrg      if (result != VK_SUCCESS) {
184501e04c3fSmrg         while (image > 0) {
184601e04c3fSmrg            --image;
184701e04c3fSmrg            wsi_display_image_finish(&chain->base, allocator,
184801e04c3fSmrg                                     &chain->images[image]);
184901e04c3fSmrg         }
185001e04c3fSmrg         vk_free(allocator, chain);
185101e04c3fSmrg         goto fail_init_images;
185201e04c3fSmrg      }
185301e04c3fSmrg   }
185401e04c3fSmrg
185501e04c3fSmrg   *swapchain_out = &chain->base;
185601e04c3fSmrg
185701e04c3fSmrg   return VK_SUCCESS;
185801e04c3fSmrg
185901e04c3fSmrgfail_init_images:
186001e04c3fSmrg   return result;
186101e04c3fSmrg}
186201e04c3fSmrg
186301e04c3fSmrgstatic bool
186401e04c3fSmrgwsi_init_pthread_cond_monotonic(pthread_cond_t *cond)
186501e04c3fSmrg{
186601e04c3fSmrg   pthread_condattr_t condattr;
186701e04c3fSmrg   bool ret = false;
186801e04c3fSmrg
186901e04c3fSmrg   if (pthread_condattr_init(&condattr) != 0)
187001e04c3fSmrg      goto fail_attr_init;
187101e04c3fSmrg
187201e04c3fSmrg   if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC) != 0)
187301e04c3fSmrg      goto fail_attr_set;
187401e04c3fSmrg
187501e04c3fSmrg   if (pthread_cond_init(cond, &condattr) != 0)
187601e04c3fSmrg      goto fail_cond_init;
187701e04c3fSmrg
187801e04c3fSmrg   ret = true;
187901e04c3fSmrg
188001e04c3fSmrgfail_cond_init:
188101e04c3fSmrgfail_attr_set:
188201e04c3fSmrg   pthread_condattr_destroy(&condattr);
188301e04c3fSmrgfail_attr_init:
188401e04c3fSmrg   return ret;
188501e04c3fSmrg}
188601e04c3fSmrg
18878a1362adSmaya
18888a1362adSmaya/*
18898a1362adSmaya * Local version fo the libdrm helper. Added to avoid depending on bleeding
18908a1362adSmaya * edge version of the library.
18918a1362adSmaya */
18928a1362adSmayastatic int
18938a1362adSmayalocal_drmIsMaster(int fd)
18948a1362adSmaya{
18958a1362adSmaya   /* Detect master by attempting something that requires master.
18968a1362adSmaya    *
18978a1362adSmaya    * Authenticating magic tokens requires master and 0 is an
18988a1362adSmaya    * internal kernel detail which we could use. Attempting this on
18998a1362adSmaya    * a master fd would fail therefore fail with EINVAL because 0
19008a1362adSmaya    * is invalid.
19018a1362adSmaya    *
19028a1362adSmaya    * A non-master fd will fail with EACCES, as the kernel checks
19038a1362adSmaya    * for master before attempting to do anything else.
19048a1362adSmaya    *
19058a1362adSmaya    * Since we don't want to leak implementation details, use
19068a1362adSmaya    * EACCES.
19078a1362adSmaya    */
19088a1362adSmaya   return drmAuthMagic(fd, 0) != -EACCES;
19098a1362adSmaya}
19108a1362adSmaya
191101e04c3fSmrgVkResult
191201e04c3fSmrgwsi_display_init_wsi(struct wsi_device *wsi_device,
191301e04c3fSmrg                     const VkAllocationCallbacks *alloc,
191401e04c3fSmrg                     int display_fd)
191501e04c3fSmrg{
191601e04c3fSmrg   struct wsi_display *wsi = vk_zalloc(alloc, sizeof(*wsi), 8,
191701e04c3fSmrg                                       VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
191801e04c3fSmrg   VkResult result;
191901e04c3fSmrg
192001e04c3fSmrg   if (!wsi) {
192101e04c3fSmrg      result = VK_ERROR_OUT_OF_HOST_MEMORY;
192201e04c3fSmrg      goto fail;
192301e04c3fSmrg   }
192401e04c3fSmrg
192501e04c3fSmrg   wsi->fd = display_fd;
19268a1362adSmaya   if (wsi->fd != -1 && !local_drmIsMaster(wsi->fd))
19278a1362adSmaya      wsi->fd = -1;
19288a1362adSmaya
192901e04c3fSmrg   wsi->alloc = alloc;
193001e04c3fSmrg
193101e04c3fSmrg   list_inithead(&wsi->connectors);
193201e04c3fSmrg
193301e04c3fSmrg   int ret = pthread_mutex_init(&wsi->wait_mutex, NULL);
193401e04c3fSmrg   if (ret) {
193501e04c3fSmrg      result = VK_ERROR_OUT_OF_HOST_MEMORY;
193601e04c3fSmrg      goto fail_mutex;
193701e04c3fSmrg   }
193801e04c3fSmrg
193901e04c3fSmrg   if (!wsi_init_pthread_cond_monotonic(&wsi->wait_cond)) {
194001e04c3fSmrg      result = VK_ERROR_OUT_OF_HOST_MEMORY;
194101e04c3fSmrg      goto fail_cond;
194201e04c3fSmrg   }
194301e04c3fSmrg
194401e04c3fSmrg   wsi->base.get_support = wsi_display_surface_get_support;
194501e04c3fSmrg   wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2;
194601e04c3fSmrg   wsi->base.get_formats = wsi_display_surface_get_formats;
194701e04c3fSmrg   wsi->base.get_formats2 = wsi_display_surface_get_formats2;
194801e04c3fSmrg   wsi->base.get_present_modes = wsi_display_surface_get_present_modes;
194901e04c3fSmrg   wsi->base.get_present_rectangles = wsi_display_surface_get_present_rectangles;
195001e04c3fSmrg   wsi->base.create_swapchain = wsi_display_surface_create_swapchain;
195101e04c3fSmrg
195201e04c3fSmrg   wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;
195301e04c3fSmrg
195401e04c3fSmrg   return VK_SUCCESS;
195501e04c3fSmrg
195601e04c3fSmrgfail_cond:
195701e04c3fSmrg   pthread_mutex_destroy(&wsi->wait_mutex);
195801e04c3fSmrgfail_mutex:
195901e04c3fSmrg   vk_free(alloc, wsi);
196001e04c3fSmrgfail:
196101e04c3fSmrg   return result;
196201e04c3fSmrg}
196301e04c3fSmrg
196401e04c3fSmrgvoid
196501e04c3fSmrgwsi_display_finish_wsi(struct wsi_device *wsi_device,
196601e04c3fSmrg                       const VkAllocationCallbacks *alloc)
196701e04c3fSmrg{
196801e04c3fSmrg   struct wsi_display *wsi =
196901e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
197001e04c3fSmrg
197101e04c3fSmrg   if (wsi) {
197201e04c3fSmrg      wsi_for_each_connector(connector, wsi) {
197301e04c3fSmrg         wsi_for_each_display_mode(mode, connector) {
197401e04c3fSmrg            vk_free(wsi->alloc, mode);
197501e04c3fSmrg         }
197601e04c3fSmrg         vk_free(wsi->alloc, connector);
197701e04c3fSmrg      }
197801e04c3fSmrg
19797ec681f3Smrg      wsi_display_stop_wait_thread(wsi);
198001e04c3fSmrg      pthread_mutex_destroy(&wsi->wait_mutex);
198101e04c3fSmrg      pthread_cond_destroy(&wsi->wait_cond);
198201e04c3fSmrg
198301e04c3fSmrg      vk_free(alloc, wsi);
198401e04c3fSmrg   }
198501e04c3fSmrg}
198601e04c3fSmrg
198701e04c3fSmrg/*
198801e04c3fSmrg * Implement vkReleaseDisplay
198901e04c3fSmrg */
19907ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
19917ec681f3Smrgwsi_ReleaseDisplayEXT(VkPhysicalDevice physicalDevice,
19927ec681f3Smrg                      VkDisplayKHR display)
199301e04c3fSmrg{
19947ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
19957ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
199601e04c3fSmrg   struct wsi_display *wsi =
199701e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
199801e04c3fSmrg
199901e04c3fSmrg   if (wsi->fd >= 0) {
20007ec681f3Smrg      wsi_display_stop_wait_thread(wsi);
20017ec681f3Smrg
200201e04c3fSmrg      close(wsi->fd);
200301e04c3fSmrg      wsi->fd = -1;
200401e04c3fSmrg   }
20057ec681f3Smrg
200601e04c3fSmrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
200701e04c3fSmrg   wsi_display_connector_from_handle(display)->output = None;
200801e04c3fSmrg#endif
200901e04c3fSmrg
201001e04c3fSmrg   return VK_SUCCESS;
201101e04c3fSmrg}
201201e04c3fSmrg
201301e04c3fSmrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
201401e04c3fSmrg
201501e04c3fSmrgstatic struct wsi_display_connector *
201601e04c3fSmrgwsi_display_find_output(struct wsi_device *wsi_device,
201701e04c3fSmrg                        xcb_randr_output_t output)
201801e04c3fSmrg{
201901e04c3fSmrg   struct wsi_display *wsi =
202001e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
202101e04c3fSmrg
202201e04c3fSmrg   wsi_for_each_connector(connector, wsi) {
202301e04c3fSmrg      if (connector->output == output)
202401e04c3fSmrg         return connector;
202501e04c3fSmrg   }
202601e04c3fSmrg
202701e04c3fSmrg   return NULL;
202801e04c3fSmrg}
202901e04c3fSmrg
203001e04c3fSmrg/*
203101e04c3fSmrg * Given a RandR output, find the associated kernel connector_id by
203201e04c3fSmrg * looking at the CONNECTOR_ID property provided by the X server
203301e04c3fSmrg */
203401e04c3fSmrg
203501e04c3fSmrgstatic uint32_t
203601e04c3fSmrgwsi_display_output_to_connector_id(xcb_connection_t *connection,
203701e04c3fSmrg                                   xcb_atom_t *connector_id_atom_p,
203801e04c3fSmrg                                   xcb_randr_output_t output)
203901e04c3fSmrg{
204001e04c3fSmrg   uint32_t connector_id = 0;
204101e04c3fSmrg   xcb_atom_t connector_id_atom = *connector_id_atom_p;
204201e04c3fSmrg
204301e04c3fSmrg   if (connector_id_atom == 0) {
204401e04c3fSmrg   /* Go dig out the CONNECTOR_ID property */
204501e04c3fSmrg      xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection,
204601e04c3fSmrg                                                          true,
204701e04c3fSmrg                                                          12,
204801e04c3fSmrg                                                          "CONNECTOR_ID");
204901e04c3fSmrg      xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection,
205001e04c3fSmrg                                                                 ia_c,
205101e04c3fSmrg                                                                 NULL);
205201e04c3fSmrg      if (ia_r) {
205301e04c3fSmrg         *connector_id_atom_p = connector_id_atom = ia_r->atom;
205401e04c3fSmrg         free(ia_r);
205501e04c3fSmrg      }
205601e04c3fSmrg   }
205701e04c3fSmrg
205801e04c3fSmrg   /* If there's an CONNECTOR_ID atom in the server, then there may be a
205901e04c3fSmrg    * CONNECTOR_ID property. Otherwise, there will not be and we don't even
206001e04c3fSmrg    * need to bother.
206101e04c3fSmrg    */
206201e04c3fSmrg   if (connector_id_atom) {
206301e04c3fSmrg
206401e04c3fSmrg      xcb_randr_query_version_cookie_t qv_c =
206501e04c3fSmrg         xcb_randr_query_version(connection, 1, 6);
206601e04c3fSmrg      xcb_randr_get_output_property_cookie_t gop_c =
206701e04c3fSmrg         xcb_randr_get_output_property(connection,
206801e04c3fSmrg                                       output,
206901e04c3fSmrg                                       connector_id_atom,
207001e04c3fSmrg                                       0,
207101e04c3fSmrg                                       0,
207201e04c3fSmrg                                       0xffffffffUL,
207301e04c3fSmrg                                       0,
207401e04c3fSmrg                                       0);
207501e04c3fSmrg      xcb_randr_query_version_reply_t *qv_r =
207601e04c3fSmrg         xcb_randr_query_version_reply(connection, qv_c, NULL);
207701e04c3fSmrg      free(qv_r);
207801e04c3fSmrg      xcb_randr_get_output_property_reply_t *gop_r =
207901e04c3fSmrg         xcb_randr_get_output_property_reply(connection, gop_c, NULL);
208001e04c3fSmrg      if (gop_r) {
208101e04c3fSmrg         if (gop_r->num_items == 1 && gop_r->format == 32)
208201e04c3fSmrg            memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4);
208301e04c3fSmrg         free(gop_r);
208401e04c3fSmrg      }
208501e04c3fSmrg   }
208601e04c3fSmrg   return connector_id;
208701e04c3fSmrg}
208801e04c3fSmrg
208901e04c3fSmrgstatic bool
209001e04c3fSmrgwsi_display_check_randr_version(xcb_connection_t *connection)
209101e04c3fSmrg{
209201e04c3fSmrg   xcb_randr_query_version_cookie_t qv_c =
209301e04c3fSmrg      xcb_randr_query_version(connection, 1, 6);
209401e04c3fSmrg   xcb_randr_query_version_reply_t *qv_r =
209501e04c3fSmrg      xcb_randr_query_version_reply(connection, qv_c, NULL);
209601e04c3fSmrg   bool ret = false;
209701e04c3fSmrg
209801e04c3fSmrg   if (!qv_r)
209901e04c3fSmrg      return false;
210001e04c3fSmrg
210101e04c3fSmrg   /* Check for version 1.6 or newer */
210201e04c3fSmrg   ret = (qv_r->major_version > 1 ||
210301e04c3fSmrg          (qv_r->major_version == 1 && qv_r->minor_version >= 6));
210401e04c3fSmrg
210501e04c3fSmrg   free(qv_r);
210601e04c3fSmrg   return ret;
210701e04c3fSmrg}
210801e04c3fSmrg
210901e04c3fSmrg/*
211001e04c3fSmrg * Given a kernel connector id, find the associated RandR output using the
211101e04c3fSmrg * CONNECTOR_ID property
211201e04c3fSmrg */
211301e04c3fSmrg
211401e04c3fSmrgstatic xcb_randr_output_t
211501e04c3fSmrgwsi_display_connector_id_to_output(xcb_connection_t *connection,
211601e04c3fSmrg                                   uint32_t connector_id)
211701e04c3fSmrg{
211801e04c3fSmrg   if (!wsi_display_check_randr_version(connection))
211901e04c3fSmrg      return 0;
212001e04c3fSmrg
212101e04c3fSmrg   const xcb_setup_t *setup = xcb_get_setup(connection);
212201e04c3fSmrg
212301e04c3fSmrg   xcb_atom_t connector_id_atom = 0;
212401e04c3fSmrg   xcb_randr_output_t output = 0;
212501e04c3fSmrg
212601e04c3fSmrg   /* Search all of the screens for the provided output */
212701e04c3fSmrg   xcb_screen_iterator_t iter;
212801e04c3fSmrg   for (iter = xcb_setup_roots_iterator(setup);
212901e04c3fSmrg        output == 0 && iter.rem;
213001e04c3fSmrg        xcb_screen_next(&iter))
213101e04c3fSmrg   {
213201e04c3fSmrg      xcb_randr_get_screen_resources_cookie_t gsr_c =
213301e04c3fSmrg         xcb_randr_get_screen_resources(connection, iter.data->root);
213401e04c3fSmrg      xcb_randr_get_screen_resources_reply_t *gsr_r =
213501e04c3fSmrg         xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
213601e04c3fSmrg
213701e04c3fSmrg      if (!gsr_r)
213801e04c3fSmrg         return 0;
213901e04c3fSmrg
214001e04c3fSmrg      xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
214101e04c3fSmrg      int o;
214201e04c3fSmrg
214301e04c3fSmrg      for (o = 0; o < gsr_r->num_outputs; o++) {
214401e04c3fSmrg         if (wsi_display_output_to_connector_id(connection,
214501e04c3fSmrg                                                &connector_id_atom, ro[o])
214601e04c3fSmrg             == connector_id)
214701e04c3fSmrg         {
214801e04c3fSmrg            output = ro[o];
214901e04c3fSmrg            break;
215001e04c3fSmrg         }
215101e04c3fSmrg      }
215201e04c3fSmrg      free(gsr_r);
215301e04c3fSmrg   }
215401e04c3fSmrg   return output;
215501e04c3fSmrg}
215601e04c3fSmrg
215701e04c3fSmrg/*
215801e04c3fSmrg * Given a RandR output, find out which screen it's associated with
215901e04c3fSmrg */
216001e04c3fSmrgstatic xcb_window_t
216101e04c3fSmrgwsi_display_output_to_root(xcb_connection_t *connection,
216201e04c3fSmrg                           xcb_randr_output_t output)
216301e04c3fSmrg{
216401e04c3fSmrg   if (!wsi_display_check_randr_version(connection))
216501e04c3fSmrg      return 0;
216601e04c3fSmrg
216701e04c3fSmrg   const xcb_setup_t *setup = xcb_get_setup(connection);
216801e04c3fSmrg   xcb_window_t root = 0;
216901e04c3fSmrg
217001e04c3fSmrg   /* Search all of the screens for the provided output */
217101e04c3fSmrg   for (xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
217201e04c3fSmrg        root == 0 && iter.rem;
217301e04c3fSmrg        xcb_screen_next(&iter))
217401e04c3fSmrg   {
217501e04c3fSmrg      xcb_randr_get_screen_resources_cookie_t gsr_c =
217601e04c3fSmrg         xcb_randr_get_screen_resources(connection, iter.data->root);
217701e04c3fSmrg      xcb_randr_get_screen_resources_reply_t *gsr_r =
217801e04c3fSmrg         xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
217901e04c3fSmrg
218001e04c3fSmrg      if (!gsr_r)
218101e04c3fSmrg         return 0;
218201e04c3fSmrg
218301e04c3fSmrg      xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
218401e04c3fSmrg
218501e04c3fSmrg      for (int o = 0; o < gsr_r->num_outputs; o++) {
218601e04c3fSmrg         if (ro[o] == output) {
218701e04c3fSmrg            root = iter.data->root;
218801e04c3fSmrg            break;
218901e04c3fSmrg         }
219001e04c3fSmrg      }
219101e04c3fSmrg      free(gsr_r);
219201e04c3fSmrg   }
219301e04c3fSmrg   return root;
219401e04c3fSmrg}
219501e04c3fSmrg
219601e04c3fSmrgstatic bool
219701e04c3fSmrgwsi_display_mode_matches_x(struct wsi_display_mode *wsi,
219801e04c3fSmrg                           xcb_randr_mode_info_t *xcb)
219901e04c3fSmrg{
220001e04c3fSmrg   return wsi->clock == (xcb->dot_clock + 500) / 1000 &&
220101e04c3fSmrg      wsi->hdisplay == xcb->width &&
220201e04c3fSmrg      wsi->hsync_start == xcb->hsync_start &&
220301e04c3fSmrg      wsi->hsync_end == xcb->hsync_end &&
220401e04c3fSmrg      wsi->htotal == xcb->htotal &&
220501e04c3fSmrg      wsi->hskew == xcb->hskew &&
220601e04c3fSmrg      wsi->vdisplay == xcb->height &&
220701e04c3fSmrg      wsi->vsync_start == xcb->vsync_start &&
220801e04c3fSmrg      wsi->vsync_end == xcb->vsync_end &&
220901e04c3fSmrg      wsi->vtotal == xcb->vtotal &&
221001e04c3fSmrg      wsi->vscan <= 1 &&
221101e04c3fSmrg      wsi->flags == xcb->mode_flags;
221201e04c3fSmrg}
221301e04c3fSmrg
221401e04c3fSmrgstatic struct wsi_display_mode *
221501e04c3fSmrgwsi_display_find_x_mode(struct wsi_device *wsi_device,
221601e04c3fSmrg                        struct wsi_display_connector *connector,
221701e04c3fSmrg                        xcb_randr_mode_info_t *mode)
221801e04c3fSmrg{
221901e04c3fSmrg   wsi_for_each_display_mode(display_mode, connector) {
222001e04c3fSmrg      if (wsi_display_mode_matches_x(display_mode, mode))
222101e04c3fSmrg         return display_mode;
222201e04c3fSmrg   }
222301e04c3fSmrg   return NULL;
222401e04c3fSmrg}
222501e04c3fSmrg
222601e04c3fSmrgstatic VkResult
222701e04c3fSmrgwsi_display_register_x_mode(struct wsi_device *wsi_device,
222801e04c3fSmrg                            struct wsi_display_connector *connector,
222901e04c3fSmrg                            xcb_randr_mode_info_t *x_mode,
223001e04c3fSmrg                            bool preferred)
223101e04c3fSmrg{
223201e04c3fSmrg   struct wsi_display *wsi =
223301e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
223401e04c3fSmrg   struct wsi_display_mode *display_mode =
223501e04c3fSmrg      wsi_display_find_x_mode(wsi_device, connector, x_mode);
223601e04c3fSmrg
223701e04c3fSmrg   if (display_mode) {
223801e04c3fSmrg      display_mode->valid = true;
223901e04c3fSmrg      return VK_SUCCESS;
224001e04c3fSmrg   }
224101e04c3fSmrg
224201e04c3fSmrg   display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),
224301e04c3fSmrg                            8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
224401e04c3fSmrg   if (!display_mode)
224501e04c3fSmrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
224601e04c3fSmrg
224701e04c3fSmrg   display_mode->connector = connector;
224801e04c3fSmrg   display_mode->valid = true;
224901e04c3fSmrg   display_mode->preferred = preferred;
225001e04c3fSmrg   display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */
225101e04c3fSmrg   display_mode->hdisplay = x_mode->width;
225201e04c3fSmrg   display_mode->hsync_start = x_mode->hsync_start;
225301e04c3fSmrg   display_mode->hsync_end = x_mode->hsync_end;
225401e04c3fSmrg   display_mode->htotal = x_mode->htotal;
225501e04c3fSmrg   display_mode->hskew = x_mode->hskew;
225601e04c3fSmrg   display_mode->vdisplay = x_mode->height;
225701e04c3fSmrg   display_mode->vsync_start = x_mode->vsync_start;
225801e04c3fSmrg   display_mode->vsync_end = x_mode->vsync_end;
225901e04c3fSmrg   display_mode->vtotal = x_mode->vtotal;
226001e04c3fSmrg   display_mode->vscan = 0;
226101e04c3fSmrg   display_mode->flags = x_mode->mode_flags;
226201e04c3fSmrg
226301e04c3fSmrg   list_addtail(&display_mode->list, &connector->display_modes);
226401e04c3fSmrg   return VK_SUCCESS;
226501e04c3fSmrg}
226601e04c3fSmrg
226701e04c3fSmrgstatic struct wsi_display_connector *
226801e04c3fSmrgwsi_display_get_output(struct wsi_device *wsi_device,
226901e04c3fSmrg                       xcb_connection_t *connection,
227001e04c3fSmrg                       xcb_randr_output_t output)
227101e04c3fSmrg{
227201e04c3fSmrg   struct wsi_display *wsi =
227301e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
227401e04c3fSmrg   struct wsi_display_connector *connector;
227501e04c3fSmrg   uint32_t connector_id;
227601e04c3fSmrg
227701e04c3fSmrg   xcb_window_t root = wsi_display_output_to_root(connection, output);
227801e04c3fSmrg   if (!root)
227901e04c3fSmrg      return NULL;
228001e04c3fSmrg
228101e04c3fSmrg   /* See if we already have a connector for this output */
228201e04c3fSmrg   connector = wsi_display_find_output(wsi_device, output);
228301e04c3fSmrg
228401e04c3fSmrg   if (!connector) {
228501e04c3fSmrg      xcb_atom_t connector_id_atom = 0;
228601e04c3fSmrg
228701e04c3fSmrg      /*
228801e04c3fSmrg       * Go get the kernel connector ID for this X output
228901e04c3fSmrg       */
229001e04c3fSmrg      connector_id = wsi_display_output_to_connector_id(connection,
229101e04c3fSmrg                                                        &connector_id_atom,
229201e04c3fSmrg                                                        output);
229301e04c3fSmrg
229401e04c3fSmrg      /* Any X server with lease support will have this atom */
229501e04c3fSmrg      if (!connector_id) {
229601e04c3fSmrg         return NULL;
229701e04c3fSmrg      }
229801e04c3fSmrg
229901e04c3fSmrg      /* See if we already have a connector for this id */
230001e04c3fSmrg      connector = wsi_display_find_connector(wsi_device, connector_id);
230101e04c3fSmrg
230201e04c3fSmrg      if (connector == NULL) {
230301e04c3fSmrg         connector = wsi_display_alloc_connector(wsi, connector_id);
230401e04c3fSmrg         if (!connector) {
230501e04c3fSmrg            return NULL;
230601e04c3fSmrg         }
230701e04c3fSmrg         list_addtail(&connector->list, &wsi->connectors);
230801e04c3fSmrg      }
230901e04c3fSmrg      connector->output = output;
231001e04c3fSmrg   }
231101e04c3fSmrg
231201e04c3fSmrg   xcb_randr_get_screen_resources_cookie_t src =
231301e04c3fSmrg      xcb_randr_get_screen_resources(connection, root);
231401e04c3fSmrg   xcb_randr_get_output_info_cookie_t oic =
231501e04c3fSmrg      xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME);
231601e04c3fSmrg   xcb_randr_get_screen_resources_reply_t *srr =
231701e04c3fSmrg      xcb_randr_get_screen_resources_reply(connection, src, NULL);
231801e04c3fSmrg   xcb_randr_get_output_info_reply_t *oir =
231901e04c3fSmrg      xcb_randr_get_output_info_reply(connection, oic, NULL);
232001e04c3fSmrg
232101e04c3fSmrg   if (oir && srr) {
232201e04c3fSmrg      /* Get X modes and add them */
232301e04c3fSmrg
232401e04c3fSmrg      connector->connected =
232501e04c3fSmrg         oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED;
232601e04c3fSmrg
232701e04c3fSmrg      wsi_display_invalidate_connector_modes(wsi_device, connector);
232801e04c3fSmrg
232901e04c3fSmrg      xcb_randr_mode_t *x_modes = xcb_randr_get_output_info_modes(oir);
233001e04c3fSmrg      for (int m = 0; m < oir->num_modes; m++) {
233101e04c3fSmrg         xcb_randr_mode_info_iterator_t i =
233201e04c3fSmrg            xcb_randr_get_screen_resources_modes_iterator(srr);
233301e04c3fSmrg         while (i.rem) {
233401e04c3fSmrg            xcb_randr_mode_info_t *mi = i.data;
233501e04c3fSmrg            if (mi->id == x_modes[m]) {
233601e04c3fSmrg               VkResult result = wsi_display_register_x_mode(
233701e04c3fSmrg                  wsi_device, connector, mi, m < oir->num_preferred);
233801e04c3fSmrg               if (result != VK_SUCCESS) {
233901e04c3fSmrg                  free(oir);
234001e04c3fSmrg                  free(srr);
234101e04c3fSmrg                  return NULL;
234201e04c3fSmrg               }
234301e04c3fSmrg               break;
234401e04c3fSmrg            }
234501e04c3fSmrg            xcb_randr_mode_info_next(&i);
234601e04c3fSmrg         }
234701e04c3fSmrg      }
234801e04c3fSmrg   }
234901e04c3fSmrg
235001e04c3fSmrg   free(oir);
235101e04c3fSmrg   free(srr);
235201e04c3fSmrg   return connector;
235301e04c3fSmrg}
235401e04c3fSmrg
235501e04c3fSmrgstatic xcb_randr_crtc_t
235601e04c3fSmrgwsi_display_find_crtc_for_output(xcb_connection_t *connection,
235701e04c3fSmrg                                 xcb_window_t root,
235801e04c3fSmrg                                 xcb_randr_output_t output)
235901e04c3fSmrg{
236001e04c3fSmrg   xcb_randr_get_screen_resources_cookie_t gsr_c =
236101e04c3fSmrg      xcb_randr_get_screen_resources(connection, root);
236201e04c3fSmrg   xcb_randr_get_screen_resources_reply_t *gsr_r =
236301e04c3fSmrg      xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
236401e04c3fSmrg
236501e04c3fSmrg   if (!gsr_r)
236601e04c3fSmrg      return 0;
236701e04c3fSmrg
236801e04c3fSmrg   xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r);
236901e04c3fSmrg   xcb_randr_crtc_t idle_crtc = 0;
237001e04c3fSmrg   xcb_randr_crtc_t active_crtc = 0;
237101e04c3fSmrg
237201e04c3fSmrg   /* Find either a crtc already connected to the desired output or idle */
237301e04c3fSmrg   for (int c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) {
237401e04c3fSmrg      xcb_randr_get_crtc_info_cookie_t gci_c =
237501e04c3fSmrg         xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp);
237601e04c3fSmrg      xcb_randr_get_crtc_info_reply_t *gci_r =
237701e04c3fSmrg         xcb_randr_get_crtc_info_reply(connection, gci_c, NULL);
237801e04c3fSmrg
237901e04c3fSmrg      if (gci_r) {
238001e04c3fSmrg         if (gci_r->mode) {
238101e04c3fSmrg            int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r);
238201e04c3fSmrg            xcb_randr_output_t *outputs =
238301e04c3fSmrg               xcb_randr_get_crtc_info_outputs(gci_r);
238401e04c3fSmrg
238501e04c3fSmrg            if (num_outputs == 1 && outputs[0] == output)
238601e04c3fSmrg               active_crtc = rc[c];
238701e04c3fSmrg
238801e04c3fSmrg         } else if (idle_crtc == 0) {
238901e04c3fSmrg            int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r);
239001e04c3fSmrg            xcb_randr_output_t *possible =
239101e04c3fSmrg               xcb_randr_get_crtc_info_possible(gci_r);
239201e04c3fSmrg
239301e04c3fSmrg            for (int p = 0; p < num_possible; p++)
239401e04c3fSmrg               if (possible[p] == output) {
239501e04c3fSmrg                  idle_crtc = rc[c];
239601e04c3fSmrg                  break;
239701e04c3fSmrg               }
239801e04c3fSmrg         }
239901e04c3fSmrg         free(gci_r);
240001e04c3fSmrg      }
240101e04c3fSmrg   }
240201e04c3fSmrg   free(gsr_r);
240301e04c3fSmrg
240401e04c3fSmrg   if (active_crtc)
240501e04c3fSmrg      return active_crtc;
240601e04c3fSmrg   return idle_crtc;
240701e04c3fSmrg}
240801e04c3fSmrg
24097ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
24107ec681f3Smrgwsi_AcquireXlibDisplayEXT(VkPhysicalDevice physicalDevice,
24117ec681f3Smrg                          Display *dpy,
24127ec681f3Smrg                          VkDisplayKHR display)
241301e04c3fSmrg{
24147ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
24157ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
241601e04c3fSmrg   struct wsi_display *wsi =
241701e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
241801e04c3fSmrg   xcb_connection_t *connection = XGetXCBConnection(dpy);
241901e04c3fSmrg   struct wsi_display_connector *connector =
242001e04c3fSmrg      wsi_display_connector_from_handle(display);
242101e04c3fSmrg   xcb_window_t root;
242201e04c3fSmrg
242301e04c3fSmrg   /* XXX no support for multiple leases yet */
242401e04c3fSmrg   if (wsi->fd >= 0)
242501e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
242601e04c3fSmrg
242701e04c3fSmrg   if (!connector->output) {
242801e04c3fSmrg      connector->output = wsi_display_connector_id_to_output(connection,
242901e04c3fSmrg                                                             connector->id);
243001e04c3fSmrg
243101e04c3fSmrg      /* Check and see if we found the output */
243201e04c3fSmrg      if (!connector->output)
243301e04c3fSmrg         return VK_ERROR_INITIALIZATION_FAILED;
243401e04c3fSmrg   }
243501e04c3fSmrg
243601e04c3fSmrg   root = wsi_display_output_to_root(connection, connector->output);
243701e04c3fSmrg   if (!root)
243801e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
243901e04c3fSmrg
244001e04c3fSmrg   xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection,
244101e04c3fSmrg                                                            root,
244201e04c3fSmrg                                                            connector->output);
244301e04c3fSmrg
244401e04c3fSmrg   if (!crtc)
244501e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
244601e04c3fSmrg
244701e04c3fSmrg#ifdef HAVE_DRI3_MODIFIERS
244801e04c3fSmrg   xcb_randr_lease_t lease = xcb_generate_id(connection);
244901e04c3fSmrg   xcb_randr_create_lease_cookie_t cl_c =
245001e04c3fSmrg      xcb_randr_create_lease(connection, root, lease, 1, 1,
245101e04c3fSmrg                             &crtc, &connector->output);
245201e04c3fSmrg   xcb_randr_create_lease_reply_t *cl_r =
245301e04c3fSmrg      xcb_randr_create_lease_reply(connection, cl_c, NULL);
245401e04c3fSmrg   if (!cl_r)
245501e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
245601e04c3fSmrg
245701e04c3fSmrg   int fd = -1;
245801e04c3fSmrg   if (cl_r->nfd > 0) {
245901e04c3fSmrg      int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r);
246001e04c3fSmrg
246101e04c3fSmrg      fd = rcl_f[0];
246201e04c3fSmrg   }
246301e04c3fSmrg   free (cl_r);
246401e04c3fSmrg   if (fd < 0)
246501e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
246601e04c3fSmrg
246701e04c3fSmrg   wsi->fd = fd;
246801e04c3fSmrg#endif
246901e04c3fSmrg
247001e04c3fSmrg   return VK_SUCCESS;
247101e04c3fSmrg}
247201e04c3fSmrg
24737ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
24747ec681f3Smrgwsi_GetRandROutputDisplayEXT(VkPhysicalDevice physicalDevice,
247501e04c3fSmrg                             Display *dpy,
24767ec681f3Smrg                             RROutput rrOutput,
24777ec681f3Smrg                             VkDisplayKHR *pDisplay)
247801e04c3fSmrg{
24797ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
24807ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
248101e04c3fSmrg   xcb_connection_t *connection = XGetXCBConnection(dpy);
248201e04c3fSmrg   struct wsi_display_connector *connector =
24837ec681f3Smrg      wsi_display_get_output(wsi_device, connection,
24847ec681f3Smrg                             (xcb_randr_output_t) rrOutput);
248501e04c3fSmrg
248601e04c3fSmrg   if (connector)
24877ec681f3Smrg      *pDisplay = wsi_display_connector_to_handle(connector);
248801e04c3fSmrg   else
24897ec681f3Smrg      *pDisplay = VK_NULL_HANDLE;
249001e04c3fSmrg   return VK_SUCCESS;
249101e04c3fSmrg}
249201e04c3fSmrg
249301e04c3fSmrg#endif
249401e04c3fSmrg
249501e04c3fSmrg/* VK_EXT_display_control */
24967ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
24977ec681f3Smrgwsi_DisplayPowerControlEXT(VkDevice _device,
24987ec681f3Smrg                           VkDisplayKHR display,
24997ec681f3Smrg                           const VkDisplayPowerInfoEXT *pDisplayPowerInfo)
250001e04c3fSmrg{
25017ec681f3Smrg   VK_FROM_HANDLE(vk_device, device, _device);
25027ec681f3Smrg   struct wsi_device *wsi_device = device->physical->wsi_device;
250301e04c3fSmrg   struct wsi_display *wsi =
250401e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
250501e04c3fSmrg   struct wsi_display_connector *connector =
250601e04c3fSmrg      wsi_display_connector_from_handle(display);
250701e04c3fSmrg   int mode;
250801e04c3fSmrg
250901e04c3fSmrg   if (wsi->fd < 0)
251001e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
251101e04c3fSmrg
25127ec681f3Smrg   switch (pDisplayPowerInfo->powerState) {
251301e04c3fSmrg   case VK_DISPLAY_POWER_STATE_OFF_EXT:
251401e04c3fSmrg      mode = DRM_MODE_DPMS_OFF;
251501e04c3fSmrg      break;
251601e04c3fSmrg   case VK_DISPLAY_POWER_STATE_SUSPEND_EXT:
251701e04c3fSmrg      mode = DRM_MODE_DPMS_SUSPEND;
251801e04c3fSmrg      break;
251901e04c3fSmrg   default:
252001e04c3fSmrg      mode = DRM_MODE_DPMS_ON;
252101e04c3fSmrg      break;
252201e04c3fSmrg   }
252301e04c3fSmrg   drmModeConnectorSetProperty(wsi->fd,
252401e04c3fSmrg                               connector->id,
252501e04c3fSmrg                               connector->dpms_property,
252601e04c3fSmrg                               mode);
252701e04c3fSmrg   return VK_SUCCESS;
252801e04c3fSmrg}
252901e04c3fSmrg
253001e04c3fSmrgVkResult
253101e04c3fSmrgwsi_register_device_event(VkDevice device,
253201e04c3fSmrg                          struct wsi_device *wsi_device,
253301e04c3fSmrg                          const VkDeviceEventInfoEXT *device_event_info,
253401e04c3fSmrg                          const VkAllocationCallbacks *allocator,
25357ec681f3Smrg                          struct wsi_fence **fence_p,
25367ec681f3Smrg                          int sync_fd)
253701e04c3fSmrg{
253801e04c3fSmrg   return VK_ERROR_FEATURE_NOT_PRESENT;
253901e04c3fSmrg}
254001e04c3fSmrg
25417ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
25427ec681f3Smrgwsi_RegisterDeviceEventEXT(VkDevice device,
25437ec681f3Smrg                           const VkDeviceEventInfoEXT *pDeviceEventInfo,
25447ec681f3Smrg                           const VkAllocationCallbacks *pAllocator,
25457ec681f3Smrg                           VkFence *pFence)
25467ec681f3Smrg{
25477ec681f3Smrg   unreachable("Not enough common infrastructure to implement this yet");
25487ec681f3Smrg}
25497ec681f3Smrg
255001e04c3fSmrgVkResult
255101e04c3fSmrgwsi_register_display_event(VkDevice device,
255201e04c3fSmrg                           struct wsi_device *wsi_device,
255301e04c3fSmrg                           VkDisplayKHR display,
255401e04c3fSmrg                           const VkDisplayEventInfoEXT *display_event_info,
255501e04c3fSmrg                           const VkAllocationCallbacks *allocator,
25567ec681f3Smrg                           struct wsi_fence **fence_p,
25577ec681f3Smrg                           int sync_fd)
255801e04c3fSmrg{
255901e04c3fSmrg   struct wsi_display *wsi =
256001e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
256101e04c3fSmrg   struct wsi_display_fence *fence;
256201e04c3fSmrg   VkResult ret;
256301e04c3fSmrg
256401e04c3fSmrg   switch (display_event_info->displayEvent) {
256501e04c3fSmrg   case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT:
256601e04c3fSmrg
25677ec681f3Smrg      fence = wsi_display_fence_alloc(device, wsi_device, display, allocator, sync_fd);
256801e04c3fSmrg
256901e04c3fSmrg      if (!fence)
257001e04c3fSmrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
257101e04c3fSmrg
257201e04c3fSmrg      ret = wsi_register_vblank_event(fence, wsi_device, display,
257301e04c3fSmrg                                      DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL);
257401e04c3fSmrg
25757ec681f3Smrg      if (ret == VK_SUCCESS) {
25767ec681f3Smrg         if (fence_p)
25777ec681f3Smrg            *fence_p = &fence->base;
25787ec681f3Smrg         else
25797ec681f3Smrg            fence->base.destroy(&fence->base);
25807ec681f3Smrg      } else if (fence != NULL) {
25817ec681f3Smrg         if (fence->syncobj)
25827ec681f3Smrg            drmSyncobjDestroy(wsi->fd, fence->syncobj);
258301e04c3fSmrg         vk_free2(wsi->alloc, allocator, fence);
25847ec681f3Smrg      }
258501e04c3fSmrg
258601e04c3fSmrg      break;
258701e04c3fSmrg   default:
258801e04c3fSmrg      ret = VK_ERROR_FEATURE_NOT_PRESENT;
258901e04c3fSmrg      break;
259001e04c3fSmrg   }
259101e04c3fSmrg
259201e04c3fSmrg   return ret;
259301e04c3fSmrg}
259401e04c3fSmrg
25957ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
25967ec681f3Smrgwsi_RegisterDisplayEventEXT(VkDevice device,
25977ec681f3Smrg                            VkDisplayKHR display,
25987ec681f3Smrg                            const VkDisplayEventInfoEXT *pDisplayEventInfo,
25997ec681f3Smrg                            const VkAllocationCallbacks *pAllocator,
26007ec681f3Smrg                            VkFence *pFence)
26017ec681f3Smrg{
26027ec681f3Smrg   unreachable("Not enough common infrastructure to implement this yet");
26037ec681f3Smrg}
260401e04c3fSmrg
26057ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
26067ec681f3Smrgwsi_GetSwapchainCounterEXT(VkDevice _device,
26077ec681f3Smrg                           VkSwapchainKHR _swapchain,
26087ec681f3Smrg                           VkSurfaceCounterFlagBitsEXT counter,
26097ec681f3Smrg                           uint64_t *pCounterValue)
261001e04c3fSmrg{
26117ec681f3Smrg   VK_FROM_HANDLE(vk_device, device, _device);
26127ec681f3Smrg   struct wsi_device *wsi_device = device->physical->wsi_device;
261301e04c3fSmrg   struct wsi_display *wsi =
261401e04c3fSmrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
261501e04c3fSmrg   struct wsi_display_swapchain *swapchain =
261601e04c3fSmrg      (struct wsi_display_swapchain *) wsi_swapchain_from_handle(_swapchain);
261701e04c3fSmrg   struct wsi_display_connector *connector =
261801e04c3fSmrg      wsi_display_mode_from_handle(swapchain->surface->displayMode)->connector;
261901e04c3fSmrg
262001e04c3fSmrg   if (wsi->fd < 0)
262101e04c3fSmrg      return VK_ERROR_INITIALIZATION_FAILED;
262201e04c3fSmrg
262301e04c3fSmrg   if (!connector->active) {
26247ec681f3Smrg      *pCounterValue = 0;
262501e04c3fSmrg      return VK_SUCCESS;
262601e04c3fSmrg   }
262701e04c3fSmrg
26287ec681f3Smrg   int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id,
26297ec681f3Smrg                                pCounterValue, NULL);
263001e04c3fSmrg   if (ret)
26317ec681f3Smrg      *pCounterValue = 0;
263201e04c3fSmrg
263301e04c3fSmrg   return VK_SUCCESS;
263401e04c3fSmrg}
263501e04c3fSmrg
26367ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
26377ec681f3Smrgwsi_AcquireDrmDisplayEXT(VkPhysicalDevice physicalDevice,
26387ec681f3Smrg                         int32_t drmFd,
26397ec681f3Smrg                         VkDisplayKHR display)
26407ec681f3Smrg{
26417ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
26427ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
26437ec681f3Smrg
26447ec681f3Smrg   if (!wsi_device_matches_drm_fd(wsi_device, drmFd))
26457ec681f3Smrg      return VK_ERROR_UNKNOWN;
26467ec681f3Smrg
26477ec681f3Smrg   struct wsi_display *wsi =
26487ec681f3Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
26497ec681f3Smrg
26507ec681f3Smrg   /* XXX no support for mulitple leases yet */
26517ec681f3Smrg   if (wsi->fd >= 0 || !local_drmIsMaster(drmFd))
26527ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
26537ec681f3Smrg
26547ec681f3Smrg   struct wsi_display_connector *connector =
26557ec681f3Smrg         wsi_display_connector_from_handle(display);
26567ec681f3Smrg
26577ec681f3Smrg   drmModeConnectorPtr drm_connector =
26587ec681f3Smrg         drmModeGetConnectorCurrent(drmFd, connector->id);
26597ec681f3Smrg
26607ec681f3Smrg   if (!drm_connector)
26617ec681f3Smrg      return VK_ERROR_INITIALIZATION_FAILED;
26627ec681f3Smrg
26637ec681f3Smrg   drmModeFreeConnector(drm_connector);
26647ec681f3Smrg
26657ec681f3Smrg   wsi->fd = drmFd;
26667ec681f3Smrg   return VK_SUCCESS;
26677ec681f3Smrg}
26687ec681f3Smrg
26697ec681f3SmrgVKAPI_ATTR VkResult VKAPI_CALL
26707ec681f3Smrgwsi_GetDrmDisplayEXT(VkPhysicalDevice physicalDevice,
26717ec681f3Smrg                     int32_t drmFd,
26727ec681f3Smrg                     uint32_t connectorId,
26737ec681f3Smrg                     VkDisplayKHR *pDisplay)
26747ec681f3Smrg{
26757ec681f3Smrg   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
26767ec681f3Smrg   struct wsi_device *wsi_device = pdevice->wsi_device;
26777ec681f3Smrg
26787ec681f3Smrg   if (!wsi_device_matches_drm_fd(wsi_device, drmFd))
26797ec681f3Smrg      return VK_ERROR_UNKNOWN;
26807ec681f3Smrg
26817ec681f3Smrg   struct wsi_display_connector *connector =
26827ec681f3Smrg      wsi_display_get_connector(wsi_device, drmFd, connectorId);
26837ec681f3Smrg
26847ec681f3Smrg   if (!connector) {
26857ec681f3Smrg      *pDisplay = VK_NULL_HANDLE;
26867ec681f3Smrg      return VK_ERROR_UNKNOWN;
26877ec681f3Smrg   }
26887ec681f3Smrg
26897ec681f3Smrg   *pDisplay = wsi_display_connector_to_handle(connector);
26907ec681f3Smrg   return VK_SUCCESS;
26917ec681f3Smrg}
2692