1b8e80941Smrg/*
2b8e80941Smrg * Copyright © 2017 Keith Packard
3b8e80941Smrg *
4b8e80941Smrg * Permission to use, copy, modify, distribute, and sell this software and its
5b8e80941Smrg * documentation for any purpose is hereby granted without fee, provided that
6b8e80941Smrg * the above copyright notice appear in all copies and that both that copyright
7b8e80941Smrg * notice and this permission notice appear in supporting documentation, and
8b8e80941Smrg * that the name of the copyright holders not be used in advertising or
9b8e80941Smrg * publicity pertaining to distribution of the software without specific,
10b8e80941Smrg * written prior permission.  The copyright holders make no representations
11b8e80941Smrg * about the suitability of this software for any purpose.  It is provided "as
12b8e80941Smrg * is" without express or implied warranty.
13b8e80941Smrg *
14b8e80941Smrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15b8e80941Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16b8e80941Smrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17b8e80941Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18b8e80941Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19b8e80941Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20b8e80941Smrg * OF THIS SOFTWARE.
21b8e80941Smrg */
22b8e80941Smrg
23b8e80941Smrg#include "util/macros.h"
24b8e80941Smrg#include <stdlib.h>
25b8e80941Smrg#include <stdio.h>
26b8e80941Smrg#include <unistd.h>
27b8e80941Smrg#include <errno.h>
28b8e80941Smrg#include <string.h>
29b8e80941Smrg#include <fcntl.h>
30b8e80941Smrg#include <poll.h>
31b8e80941Smrg#include <stdbool.h>
32b8e80941Smrg#include <math.h>
33b8e80941Smrg#include <xf86drm.h>
34b8e80941Smrg#include <xf86drmMode.h>
35b8e80941Smrg#include "drm-uapi/drm_fourcc.h"
36b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
37b8e80941Smrg#include <xcb/randr.h>
38b8e80941Smrg#include <X11/Xlib-xcb.h>
39b8e80941Smrg#endif
40b8e80941Smrg#include "util/hash_table.h"
41b8e80941Smrg#include "util/list.h"
42b8e80941Smrg
43b8e80941Smrg#include "vk_util.h"
44b8e80941Smrg#include "wsi_common_private.h"
45b8e80941Smrg#include "wsi_common_display.h"
46b8e80941Smrg#include "wsi_common_queue.h"
47b8e80941Smrg
48b8e80941Smrg#if 0
49b8e80941Smrg#define wsi_display_debug(...) fprintf(stderr, __VA_ARGS__)
50b8e80941Smrg#define wsi_display_debug_code(...)     __VA_ARGS__
51b8e80941Smrg#else
52b8e80941Smrg#define wsi_display_debug(...)
53b8e80941Smrg#define wsi_display_debug_code(...)
54b8e80941Smrg#endif
55b8e80941Smrg
56b8e80941Smrg/* These have lifetime equal to the instance, so they effectively
57b8e80941Smrg * never go away. This means we must keep track of them separately
58b8e80941Smrg * from all other resources.
59b8e80941Smrg */
60b8e80941Smrgtypedef struct wsi_display_mode {
61b8e80941Smrg   struct list_head             list;
62b8e80941Smrg   struct wsi_display_connector *connector;
63b8e80941Smrg   bool                         valid; /* was found in most recent poll */
64b8e80941Smrg   bool                         preferred;
65b8e80941Smrg   uint32_t                     clock; /* in kHz */
66b8e80941Smrg   uint16_t                     hdisplay, hsync_start, hsync_end, htotal, hskew;
67b8e80941Smrg   uint16_t                     vdisplay, vsync_start, vsync_end, vtotal, vscan;
68b8e80941Smrg   uint32_t                     flags;
69b8e80941Smrg} wsi_display_mode;
70b8e80941Smrg
71b8e80941Smrgtypedef struct wsi_display_connector {
72b8e80941Smrg   struct list_head             list;
73b8e80941Smrg   struct wsi_display           *wsi;
74b8e80941Smrg   uint32_t                     id;
75b8e80941Smrg   uint32_t                     crtc_id;
76b8e80941Smrg   char                         *name;
77b8e80941Smrg   bool                         connected;
78b8e80941Smrg   bool                         active;
79b8e80941Smrg   struct list_head             display_modes;
80b8e80941Smrg   wsi_display_mode             *current_mode;
81b8e80941Smrg   drmModeModeInfo              current_drm_mode;
82b8e80941Smrg   uint32_t                     dpms_property;
83b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
84b8e80941Smrg   xcb_randr_output_t           output;
85b8e80941Smrg#endif
86b8e80941Smrg} wsi_display_connector;
87b8e80941Smrg
88b8e80941Smrgstruct wsi_display {
89b8e80941Smrg   struct wsi_interface         base;
90b8e80941Smrg
91b8e80941Smrg   const VkAllocationCallbacks  *alloc;
92b8e80941Smrg
93b8e80941Smrg   int                          fd;
94b8e80941Smrg
95b8e80941Smrg   pthread_mutex_t              wait_mutex;
96b8e80941Smrg   pthread_cond_t               wait_cond;
97b8e80941Smrg   pthread_t                    wait_thread;
98b8e80941Smrg
99b8e80941Smrg   struct list_head             connectors; /* list of all discovered connectors */
100b8e80941Smrg};
101b8e80941Smrg
102b8e80941Smrg#define wsi_for_each_display_mode(_mode, _conn)                 \
103b8e80941Smrg   list_for_each_entry_safe(struct wsi_display_mode, _mode,     \
104b8e80941Smrg                            &(_conn)->display_modes, list)
105b8e80941Smrg
106b8e80941Smrg#define wsi_for_each_connector(_conn, _dev)                             \
107b8e80941Smrg   list_for_each_entry_safe(struct wsi_display_connector, _conn,        \
108b8e80941Smrg                            &(_dev)->connectors, list)
109b8e80941Smrg
110b8e80941Smrgenum wsi_image_state {
111b8e80941Smrg   WSI_IMAGE_IDLE,
112b8e80941Smrg   WSI_IMAGE_DRAWING,
113b8e80941Smrg   WSI_IMAGE_QUEUED,
114b8e80941Smrg   WSI_IMAGE_FLIPPING,
115b8e80941Smrg   WSI_IMAGE_DISPLAYING
116b8e80941Smrg};
117b8e80941Smrg
118b8e80941Smrgstruct wsi_display_image {
119b8e80941Smrg   struct wsi_image             base;
120b8e80941Smrg   struct wsi_display_swapchain *chain;
121b8e80941Smrg   enum wsi_image_state         state;
122b8e80941Smrg   uint32_t                     fb_id;
123b8e80941Smrg   uint32_t                     buffer[4];
124b8e80941Smrg   uint64_t                     flip_sequence;
125b8e80941Smrg};
126b8e80941Smrg
127b8e80941Smrgstruct wsi_display_swapchain {
128b8e80941Smrg   struct wsi_swapchain         base;
129b8e80941Smrg   struct wsi_display           *wsi;
130b8e80941Smrg   VkIcdSurfaceDisplay          *surface;
131b8e80941Smrg   uint64_t                     flip_sequence;
132b8e80941Smrg   VkResult                     status;
133b8e80941Smrg   struct wsi_display_image     images[0];
134b8e80941Smrg};
135b8e80941Smrg
136b8e80941Smrgstruct wsi_display_fence {
137b8e80941Smrg   struct wsi_fence             base;
138b8e80941Smrg   bool                         event_received;
139b8e80941Smrg   bool                         destroyed;
140b8e80941Smrg   uint64_t                     sequence;
141b8e80941Smrg};
142b8e80941Smrg
143b8e80941Smrgstatic uint64_t fence_sequence;
144b8e80941Smrg
145b8e80941SmrgICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR)
146b8e80941SmrgICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR)
147b8e80941Smrg
148b8e80941Smrgstatic bool
149b8e80941Smrgwsi_display_mode_matches_drm(wsi_display_mode *wsi,
150b8e80941Smrg                             drmModeModeInfoPtr drm)
151b8e80941Smrg{
152b8e80941Smrg   return wsi->clock == drm->clock &&
153b8e80941Smrg      wsi->hdisplay == drm->hdisplay &&
154b8e80941Smrg      wsi->hsync_start == drm->hsync_start &&
155b8e80941Smrg      wsi->hsync_end == drm->hsync_end &&
156b8e80941Smrg      wsi->htotal == drm->htotal &&
157b8e80941Smrg      wsi->hskew == drm->hskew &&
158b8e80941Smrg      wsi->vdisplay == drm->vdisplay &&
159b8e80941Smrg      wsi->vsync_start == drm->vsync_start &&
160b8e80941Smrg      wsi->vsync_end == drm->vsync_end &&
161b8e80941Smrg      wsi->vtotal == drm->vtotal &&
162b8e80941Smrg      MAX2(wsi->vscan, 1) == MAX2(drm->vscan, 1) &&
163b8e80941Smrg      wsi->flags == drm->flags;
164b8e80941Smrg}
165b8e80941Smrg
166b8e80941Smrgstatic double
167b8e80941Smrgwsi_display_mode_refresh(struct wsi_display_mode *wsi)
168b8e80941Smrg{
169b8e80941Smrg   return (double) wsi->clock * 1000.0 / ((double) wsi->htotal *
170b8e80941Smrg                                          (double) wsi->vtotal *
171b8e80941Smrg                                          (double) MAX2(wsi->vscan, 1));
172b8e80941Smrg}
173b8e80941Smrg
174b8e80941Smrgstatic uint64_t wsi_rel_to_abs_time(uint64_t rel_time)
175b8e80941Smrg{
176b8e80941Smrg   uint64_t current_time = wsi_common_get_current_time();
177b8e80941Smrg
178b8e80941Smrg   /* check for overflow */
179b8e80941Smrg   if (rel_time > UINT64_MAX - current_time)
180b8e80941Smrg      return UINT64_MAX;
181b8e80941Smrg
182b8e80941Smrg   return current_time + rel_time;
183b8e80941Smrg}
184b8e80941Smrg
185b8e80941Smrgstatic struct wsi_display_mode *
186b8e80941Smrgwsi_display_find_drm_mode(struct wsi_device *wsi_device,
187b8e80941Smrg                          struct wsi_display_connector *connector,
188b8e80941Smrg                          drmModeModeInfoPtr mode)
189b8e80941Smrg{
190b8e80941Smrg   wsi_for_each_display_mode(display_mode, connector) {
191b8e80941Smrg      if (wsi_display_mode_matches_drm(display_mode, mode))
192b8e80941Smrg         return display_mode;
193b8e80941Smrg   }
194b8e80941Smrg   return NULL;
195b8e80941Smrg}
196b8e80941Smrg
197b8e80941Smrgstatic void
198b8e80941Smrgwsi_display_invalidate_connector_modes(struct wsi_device *wsi_device,
199b8e80941Smrg                                       struct wsi_display_connector *connector)
200b8e80941Smrg{
201b8e80941Smrg   wsi_for_each_display_mode(display_mode, connector) {
202b8e80941Smrg      display_mode->valid = false;
203b8e80941Smrg   }
204b8e80941Smrg}
205b8e80941Smrg
206b8e80941Smrgstatic VkResult
207b8e80941Smrgwsi_display_register_drm_mode(struct wsi_device *wsi_device,
208b8e80941Smrg                              struct wsi_display_connector *connector,
209b8e80941Smrg                              drmModeModeInfoPtr drm_mode)
210b8e80941Smrg{
211b8e80941Smrg   struct wsi_display *wsi =
212b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
213b8e80941Smrg   struct wsi_display_mode *display_mode =
214b8e80941Smrg      wsi_display_find_drm_mode(wsi_device, connector, drm_mode);
215b8e80941Smrg
216b8e80941Smrg   if (display_mode) {
217b8e80941Smrg      display_mode->valid = true;
218b8e80941Smrg      return VK_SUCCESS;
219b8e80941Smrg   }
220b8e80941Smrg
221b8e80941Smrg   display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),
222b8e80941Smrg                            8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
223b8e80941Smrg   if (!display_mode)
224b8e80941Smrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
225b8e80941Smrg
226b8e80941Smrg   display_mode->connector = connector;
227b8e80941Smrg   display_mode->valid = true;
228b8e80941Smrg   display_mode->preferred = (drm_mode->type & DRM_MODE_TYPE_PREFERRED) != 0;
229b8e80941Smrg   display_mode->clock = drm_mode->clock; /* kHz */
230b8e80941Smrg   display_mode->hdisplay = drm_mode->hdisplay;
231b8e80941Smrg   display_mode->hsync_start = drm_mode->hsync_start;
232b8e80941Smrg   display_mode->hsync_end = drm_mode->hsync_end;
233b8e80941Smrg   display_mode->htotal = drm_mode->htotal;
234b8e80941Smrg   display_mode->hskew = drm_mode->hskew;
235b8e80941Smrg   display_mode->vdisplay = drm_mode->vdisplay;
236b8e80941Smrg   display_mode->vsync_start = drm_mode->vsync_start;
237b8e80941Smrg   display_mode->vsync_end = drm_mode->vsync_end;
238b8e80941Smrg   display_mode->vtotal = drm_mode->vtotal;
239b8e80941Smrg   display_mode->vscan = drm_mode->vscan;
240b8e80941Smrg   display_mode->flags = drm_mode->flags;
241b8e80941Smrg
242b8e80941Smrg   list_addtail(&display_mode->list, &connector->display_modes);
243b8e80941Smrg   return VK_SUCCESS;
244b8e80941Smrg}
245b8e80941Smrg
246b8e80941Smrg/*
247b8e80941Smrg * Update our information about a specific connector
248b8e80941Smrg */
249b8e80941Smrg
250b8e80941Smrgstatic struct wsi_display_connector *
251b8e80941Smrgwsi_display_find_connector(struct wsi_device *wsi_device,
252b8e80941Smrg                          uint32_t connector_id)
253b8e80941Smrg{
254b8e80941Smrg   struct wsi_display *wsi =
255b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
256b8e80941Smrg
257b8e80941Smrg   wsi_for_each_connector(connector, wsi) {
258b8e80941Smrg      if (connector->id == connector_id)
259b8e80941Smrg         return connector;
260b8e80941Smrg   }
261b8e80941Smrg
262b8e80941Smrg   return NULL;
263b8e80941Smrg}
264b8e80941Smrg
265b8e80941Smrgstatic struct wsi_display_connector *
266b8e80941Smrgwsi_display_alloc_connector(struct wsi_display *wsi,
267b8e80941Smrg                            uint32_t connector_id)
268b8e80941Smrg{
269b8e80941Smrg   struct wsi_display_connector *connector =
270b8e80941Smrg      vk_zalloc(wsi->alloc, sizeof (struct wsi_display_connector),
271b8e80941Smrg                8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
272b8e80941Smrg
273b8e80941Smrg   connector->id = connector_id;
274b8e80941Smrg   connector->wsi = wsi;
275b8e80941Smrg   connector->active = false;
276b8e80941Smrg   /* XXX use EDID name */
277b8e80941Smrg   connector->name = "monitor";
278b8e80941Smrg   list_inithead(&connector->display_modes);
279b8e80941Smrg   return connector;
280b8e80941Smrg}
281b8e80941Smrg
282b8e80941Smrgstatic struct wsi_display_connector *
283b8e80941Smrgwsi_display_get_connector(struct wsi_device *wsi_device,
284b8e80941Smrg                          uint32_t connector_id)
285b8e80941Smrg{
286b8e80941Smrg   struct wsi_display *wsi =
287b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
288b8e80941Smrg
289b8e80941Smrg   if (wsi->fd < 0)
290b8e80941Smrg      return NULL;
291b8e80941Smrg
292b8e80941Smrg   drmModeConnectorPtr drm_connector =
293b8e80941Smrg      drmModeGetConnector(wsi->fd, connector_id);
294b8e80941Smrg
295b8e80941Smrg   if (!drm_connector)
296b8e80941Smrg      return NULL;
297b8e80941Smrg
298b8e80941Smrg   struct wsi_display_connector *connector =
299b8e80941Smrg      wsi_display_find_connector(wsi_device, connector_id);
300b8e80941Smrg
301b8e80941Smrg   if (!connector) {
302b8e80941Smrg      connector = wsi_display_alloc_connector(wsi, connector_id);
303b8e80941Smrg      if (!connector) {
304b8e80941Smrg         drmModeFreeConnector(drm_connector);
305b8e80941Smrg         return NULL;
306b8e80941Smrg      }
307b8e80941Smrg      list_addtail(&connector->list, &wsi->connectors);
308b8e80941Smrg   }
309b8e80941Smrg
310b8e80941Smrg   connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;
311b8e80941Smrg
312b8e80941Smrg   /* Look for a DPMS property if we haven't already found one */
313b8e80941Smrg   for (int p = 0; connector->dpms_property == 0 &&
314b8e80941Smrg           p < drm_connector->count_props; p++)
315b8e80941Smrg   {
316b8e80941Smrg      drmModePropertyPtr prop = drmModeGetProperty(wsi->fd,
317b8e80941Smrg                                                   drm_connector->props[p]);
318b8e80941Smrg      if (!prop)
319b8e80941Smrg         continue;
320b8e80941Smrg      if (prop->flags & DRM_MODE_PROP_ENUM) {
321b8e80941Smrg         if (!strcmp(prop->name, "DPMS"))
322b8e80941Smrg            connector->dpms_property = drm_connector->props[p];
323b8e80941Smrg      }
324b8e80941Smrg      drmModeFreeProperty(prop);
325b8e80941Smrg   }
326b8e80941Smrg
327b8e80941Smrg   /* Mark all connector modes as invalid */
328b8e80941Smrg   wsi_display_invalidate_connector_modes(wsi_device, connector);
329b8e80941Smrg
330b8e80941Smrg   /*
331b8e80941Smrg    * List current modes, adding new ones and marking existing ones as
332b8e80941Smrg    * valid
333b8e80941Smrg    */
334b8e80941Smrg   for (int m = 0; m < drm_connector->count_modes; m++) {
335b8e80941Smrg      VkResult result = wsi_display_register_drm_mode(wsi_device,
336b8e80941Smrg                                                      connector,
337b8e80941Smrg                                                      &drm_connector->modes[m]);
338b8e80941Smrg      if (result != VK_SUCCESS) {
339b8e80941Smrg         drmModeFreeConnector(drm_connector);
340b8e80941Smrg         return NULL;
341b8e80941Smrg      }
342b8e80941Smrg   }
343b8e80941Smrg
344b8e80941Smrg   drmModeFreeConnector(drm_connector);
345b8e80941Smrg
346b8e80941Smrg   return connector;
347b8e80941Smrg}
348b8e80941Smrg
349b8e80941Smrg#define MM_PER_PIXEL     (1.0/96.0 * 25.4)
350b8e80941Smrg
351b8e80941Smrgstatic uint32_t
352b8e80941Smrgmode_size(struct wsi_display_mode *mode)
353b8e80941Smrg{
354b8e80941Smrg   /* fortunately, these are both uint16_t, so this is easy */
355b8e80941Smrg   return (uint32_t) mode->hdisplay * (uint32_t) mode->vdisplay;
356b8e80941Smrg}
357b8e80941Smrg
358b8e80941Smrgstatic void
359b8e80941Smrgwsi_display_fill_in_display_properties(struct wsi_device *wsi_device,
360b8e80941Smrg                                       struct wsi_display_connector *connector,
361b8e80941Smrg                                       VkDisplayProperties2KHR *properties2)
362b8e80941Smrg{
363b8e80941Smrg   assert(properties2->sType == VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR);
364b8e80941Smrg   VkDisplayPropertiesKHR *properties = &properties2->displayProperties;
365b8e80941Smrg
366b8e80941Smrg   properties->display = wsi_display_connector_to_handle(connector);
367b8e80941Smrg   properties->displayName = connector->name;
368b8e80941Smrg
369b8e80941Smrg   /* Find the first preferred mode and assume that's the physical
370b8e80941Smrg    * resolution. If there isn't a preferred mode, find the largest mode and
371b8e80941Smrg    * use that.
372b8e80941Smrg    */
373b8e80941Smrg
374b8e80941Smrg   struct wsi_display_mode *preferred_mode = NULL, *largest_mode = NULL;
375b8e80941Smrg   wsi_for_each_display_mode(display_mode, connector) {
376b8e80941Smrg      if (!display_mode->valid)
377b8e80941Smrg         continue;
378b8e80941Smrg      if (display_mode->preferred) {
379b8e80941Smrg         preferred_mode = display_mode;
380b8e80941Smrg         break;
381b8e80941Smrg      }
382b8e80941Smrg      if (largest_mode == NULL ||
383b8e80941Smrg          mode_size(display_mode) > mode_size(largest_mode))
384b8e80941Smrg      {
385b8e80941Smrg         largest_mode = display_mode;
386b8e80941Smrg      }
387b8e80941Smrg   }
388b8e80941Smrg
389b8e80941Smrg   if (preferred_mode) {
390b8e80941Smrg      properties->physicalResolution.width = preferred_mode->hdisplay;
391b8e80941Smrg      properties->physicalResolution.height = preferred_mode->vdisplay;
392b8e80941Smrg   } else if (largest_mode) {
393b8e80941Smrg      properties->physicalResolution.width = largest_mode->hdisplay;
394b8e80941Smrg      properties->physicalResolution.height = largest_mode->vdisplay;
395b8e80941Smrg   } else {
396b8e80941Smrg      properties->physicalResolution.width = 1024;
397b8e80941Smrg      properties->physicalResolution.height = 768;
398b8e80941Smrg   }
399b8e80941Smrg
400b8e80941Smrg   /* Make up physical size based on 96dpi */
401b8e80941Smrg   properties->physicalDimensions.width =
402b8e80941Smrg      floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5);
403b8e80941Smrg   properties->physicalDimensions.height =
404b8e80941Smrg      floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5);
405b8e80941Smrg
406b8e80941Smrg   properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
407b8e80941Smrg   properties->planeReorderPossible = VK_FALSE;
408b8e80941Smrg   properties->persistentContent = VK_FALSE;
409b8e80941Smrg}
410b8e80941Smrg
411b8e80941Smrg/*
412b8e80941Smrg * Implement vkGetPhysicalDeviceDisplayPropertiesKHR (VK_KHR_display)
413b8e80941Smrg */
414b8e80941SmrgVkResult
415b8e80941Smrgwsi_display_get_physical_device_display_properties(
416b8e80941Smrg   VkPhysicalDevice physical_device,
417b8e80941Smrg   struct wsi_device *wsi_device,
418b8e80941Smrg   uint32_t *property_count,
419b8e80941Smrg   VkDisplayPropertiesKHR *properties)
420b8e80941Smrg{
421b8e80941Smrg   struct wsi_display *wsi =
422b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
423b8e80941Smrg
424b8e80941Smrg   if (properties == NULL) {
425b8e80941Smrg      return wsi_display_get_physical_device_display_properties2(
426b8e80941Smrg            physical_device, wsi_device, property_count, NULL);
427b8e80941Smrg   } else {
428b8e80941Smrg      /* If we're actually returning properties, allocate a temporary array of
429b8e80941Smrg       * VkDisplayProperties2KHR structs, call properties2 to fill them out,
430b8e80941Smrg       * and then copy them to the client.  This seems a bit expensive but
431b8e80941Smrg       * wsi_display_get_physical_device_display_properties2() calls
432b8e80941Smrg       * drmModeGetResources() which does an ioctl and then a bunch of
433b8e80941Smrg       * allocations so this should get lost in the noise.
434b8e80941Smrg       */
435b8e80941Smrg      VkDisplayProperties2KHR *props2 =
436b8e80941Smrg         vk_zalloc(wsi->alloc, sizeof(*props2) * *property_count, 8,
437b8e80941Smrg                   VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
438b8e80941Smrg      if (props2 == NULL)
439b8e80941Smrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
440b8e80941Smrg
441b8e80941Smrg      for (uint32_t i = 0; i < *property_count; i++)
442b8e80941Smrg         props2[i].sType = VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR;
443b8e80941Smrg
444b8e80941Smrg      VkResult result = wsi_display_get_physical_device_display_properties2(
445b8e80941Smrg            physical_device, wsi_device, property_count, props2);
446b8e80941Smrg
447b8e80941Smrg      if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
448b8e80941Smrg         for (uint32_t i = 0; i < *property_count; i++)
449b8e80941Smrg            properties[i] = props2[i].displayProperties;
450b8e80941Smrg      }
451b8e80941Smrg
452b8e80941Smrg      vk_free(wsi->alloc, props2);
453b8e80941Smrg
454b8e80941Smrg      return result;
455b8e80941Smrg   }
456b8e80941Smrg}
457b8e80941Smrg
458b8e80941SmrgVkResult
459b8e80941Smrgwsi_display_get_physical_device_display_properties2(
460b8e80941Smrg   VkPhysicalDevice physical_device,
461b8e80941Smrg   struct wsi_device *wsi_device,
462b8e80941Smrg   uint32_t *property_count,
463b8e80941Smrg   VkDisplayProperties2KHR *properties)
464b8e80941Smrg{
465b8e80941Smrg   struct wsi_display *wsi =
466b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
467b8e80941Smrg
468b8e80941Smrg   if (wsi->fd < 0)
469b8e80941Smrg      goto bail;
470b8e80941Smrg
471b8e80941Smrg   drmModeResPtr mode_res = drmModeGetResources(wsi->fd);
472b8e80941Smrg
473b8e80941Smrg   if (!mode_res)
474b8e80941Smrg      goto bail;
475b8e80941Smrg
476b8e80941Smrg   VK_OUTARRAY_MAKE(conn, properties, property_count);
477b8e80941Smrg
478b8e80941Smrg   /* Get current information */
479b8e80941Smrg
480b8e80941Smrg   for (int c = 0; c < mode_res->count_connectors; c++) {
481b8e80941Smrg      struct wsi_display_connector *connector =
482b8e80941Smrg         wsi_display_get_connector(wsi_device, mode_res->connectors[c]);
483b8e80941Smrg
484b8e80941Smrg      if (!connector) {
485b8e80941Smrg         drmModeFreeResources(mode_res);
486b8e80941Smrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
487b8e80941Smrg      }
488b8e80941Smrg
489b8e80941Smrg      if (connector->connected) {
490b8e80941Smrg         vk_outarray_append(&conn, prop) {
491b8e80941Smrg            wsi_display_fill_in_display_properties(wsi_device,
492b8e80941Smrg                                                   connector,
493b8e80941Smrg                                                   prop);
494b8e80941Smrg         }
495b8e80941Smrg      }
496b8e80941Smrg   }
497b8e80941Smrg
498b8e80941Smrg   drmModeFreeResources(mode_res);
499b8e80941Smrg
500b8e80941Smrg   return vk_outarray_status(&conn);
501b8e80941Smrg
502b8e80941Smrgbail:
503b8e80941Smrg   *property_count = 0;
504b8e80941Smrg   return VK_SUCCESS;
505b8e80941Smrg}
506b8e80941Smrg
507b8e80941Smrg/*
508b8e80941Smrg * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR (VK_KHR_display
509b8e80941Smrg */
510b8e80941Smrgstatic void
511b8e80941Smrgwsi_display_fill_in_display_plane_properties(
512b8e80941Smrg   struct wsi_device *wsi_device,
513b8e80941Smrg   struct wsi_display_connector *connector,
514b8e80941Smrg   VkDisplayPlaneProperties2KHR *properties)
515b8e80941Smrg{
516b8e80941Smrg   assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR);
517b8e80941Smrg   VkDisplayPlanePropertiesKHR *prop = &properties->displayPlaneProperties;
518b8e80941Smrg
519b8e80941Smrg   if (connector && connector->active) {
520b8e80941Smrg      prop->currentDisplay = wsi_display_connector_to_handle(connector);
521b8e80941Smrg      prop->currentStackIndex = 0;
522b8e80941Smrg   } else {
523b8e80941Smrg      prop->currentDisplay = VK_NULL_HANDLE;
524b8e80941Smrg      prop->currentStackIndex = 0;
525b8e80941Smrg   }
526b8e80941Smrg}
527b8e80941Smrg
528b8e80941SmrgVkResult
529b8e80941Smrgwsi_display_get_physical_device_display_plane_properties(
530b8e80941Smrg   VkPhysicalDevice physical_device,
531b8e80941Smrg   struct wsi_device *wsi_device,
532b8e80941Smrg   uint32_t *property_count,
533b8e80941Smrg   VkDisplayPlanePropertiesKHR *properties)
534b8e80941Smrg{
535b8e80941Smrg   struct wsi_display *wsi =
536b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
537b8e80941Smrg
538b8e80941Smrg   VK_OUTARRAY_MAKE(conn, properties, property_count);
539b8e80941Smrg
540b8e80941Smrg   wsi_for_each_connector(connector, wsi) {
541b8e80941Smrg      vk_outarray_append(&conn, prop) {
542b8e80941Smrg         VkDisplayPlaneProperties2KHR prop2 = {
543b8e80941Smrg            .sType = VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR,
544b8e80941Smrg         };
545b8e80941Smrg         wsi_display_fill_in_display_plane_properties(wsi_device, connector,
546b8e80941Smrg                                                      &prop2);
547b8e80941Smrg         *prop = prop2.displayPlaneProperties;
548b8e80941Smrg      }
549b8e80941Smrg   }
550b8e80941Smrg   return vk_outarray_status(&conn);
551b8e80941Smrg}
552b8e80941Smrg
553b8e80941SmrgVkResult
554b8e80941Smrgwsi_display_get_physical_device_display_plane_properties2(
555b8e80941Smrg   VkPhysicalDevice physical_device,
556b8e80941Smrg   struct wsi_device *wsi_device,
557b8e80941Smrg   uint32_t *property_count,
558b8e80941Smrg   VkDisplayPlaneProperties2KHR *properties)
559b8e80941Smrg{
560b8e80941Smrg   struct wsi_display *wsi =
561b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
562b8e80941Smrg
563b8e80941Smrg   VK_OUTARRAY_MAKE(conn, properties, property_count);
564b8e80941Smrg
565b8e80941Smrg   wsi_for_each_connector(connector, wsi) {
566b8e80941Smrg      vk_outarray_append(&conn, prop) {
567b8e80941Smrg         wsi_display_fill_in_display_plane_properties(wsi_device, connector,
568b8e80941Smrg                                                      prop);
569b8e80941Smrg      }
570b8e80941Smrg   }
571b8e80941Smrg   return vk_outarray_status(&conn);
572b8e80941Smrg}
573b8e80941Smrg
574b8e80941Smrg/*
575b8e80941Smrg * Implement vkGetDisplayPlaneSupportedDisplaysKHR (VK_KHR_display)
576b8e80941Smrg */
577b8e80941Smrg
578b8e80941SmrgVkResult
579b8e80941Smrgwsi_display_get_display_plane_supported_displays(
580b8e80941Smrg   VkPhysicalDevice physical_device,
581b8e80941Smrg   struct wsi_device *wsi_device,
582b8e80941Smrg   uint32_t plane_index,
583b8e80941Smrg   uint32_t *display_count,
584b8e80941Smrg   VkDisplayKHR *displays)
585b8e80941Smrg{
586b8e80941Smrg   struct wsi_display *wsi =
587b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
588b8e80941Smrg
589b8e80941Smrg   VK_OUTARRAY_MAKE(conn, displays, display_count);
590b8e80941Smrg
591b8e80941Smrg   int c = 0;
592b8e80941Smrg
593b8e80941Smrg   wsi_for_each_connector(connector, wsi) {
594b8e80941Smrg      if (c == plane_index && connector->connected) {
595b8e80941Smrg         vk_outarray_append(&conn, display) {
596b8e80941Smrg            *display = wsi_display_connector_to_handle(connector);
597b8e80941Smrg         }
598b8e80941Smrg      }
599b8e80941Smrg      c++;
600b8e80941Smrg   }
601b8e80941Smrg   return vk_outarray_status(&conn);
602b8e80941Smrg}
603b8e80941Smrg
604b8e80941Smrg/*
605b8e80941Smrg * Implement vkGetDisplayModePropertiesKHR (VK_KHR_display)
606b8e80941Smrg */
607b8e80941Smrg
608b8e80941Smrgstatic void
609b8e80941Smrgwsi_display_fill_in_display_mode_properties(
610b8e80941Smrg   struct wsi_device *wsi_device,
611b8e80941Smrg   struct wsi_display_mode *display_mode,
612b8e80941Smrg   VkDisplayModeProperties2KHR *properties)
613b8e80941Smrg{
614b8e80941Smrg   assert(properties->sType == VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR);
615b8e80941Smrg   VkDisplayModePropertiesKHR *prop = &properties->displayModeProperties;
616b8e80941Smrg
617b8e80941Smrg   prop->displayMode = wsi_display_mode_to_handle(display_mode);
618b8e80941Smrg   prop->parameters.visibleRegion.width = display_mode->hdisplay;
619b8e80941Smrg   prop->parameters.visibleRegion.height = display_mode->vdisplay;
620b8e80941Smrg   prop->parameters.refreshRate =
621b8e80941Smrg      (uint32_t) (wsi_display_mode_refresh(display_mode) * 1000 + 0.5);
622b8e80941Smrg}
623b8e80941Smrg
624b8e80941SmrgVkResult
625b8e80941Smrgwsi_display_get_display_mode_properties(VkPhysicalDevice physical_device,
626b8e80941Smrg                                        struct wsi_device *wsi_device,
627b8e80941Smrg                                        VkDisplayKHR display,
628b8e80941Smrg                                        uint32_t *property_count,
629b8e80941Smrg                                        VkDisplayModePropertiesKHR *properties)
630b8e80941Smrg{
631b8e80941Smrg   struct wsi_display_connector *connector =
632b8e80941Smrg      wsi_display_connector_from_handle(display);
633b8e80941Smrg
634b8e80941Smrg   VK_OUTARRAY_MAKE(conn, properties, property_count);
635b8e80941Smrg
636b8e80941Smrg   wsi_for_each_display_mode(display_mode, connector) {
637b8e80941Smrg      if (!display_mode->valid)
638b8e80941Smrg         continue;
639b8e80941Smrg
640b8e80941Smrg      vk_outarray_append(&conn, prop) {
641b8e80941Smrg         VkDisplayModeProperties2KHR prop2 = {
642b8e80941Smrg            .sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR,
643b8e80941Smrg         };
644b8e80941Smrg         wsi_display_fill_in_display_mode_properties(wsi_device,
645b8e80941Smrg                                                     display_mode, &prop2);
646b8e80941Smrg         *prop = prop2.displayModeProperties;
647b8e80941Smrg      }
648b8e80941Smrg   }
649b8e80941Smrg   return vk_outarray_status(&conn);
650b8e80941Smrg}
651b8e80941Smrg
652b8e80941SmrgVkResult
653b8e80941Smrgwsi_display_get_display_mode_properties2(VkPhysicalDevice physical_device,
654b8e80941Smrg                                         struct wsi_device *wsi_device,
655b8e80941Smrg                                         VkDisplayKHR display,
656b8e80941Smrg                                         uint32_t *property_count,
657b8e80941Smrg                                         VkDisplayModeProperties2KHR *properties)
658b8e80941Smrg{
659b8e80941Smrg   struct wsi_display_connector *connector =
660b8e80941Smrg      wsi_display_connector_from_handle(display);
661b8e80941Smrg
662b8e80941Smrg   VK_OUTARRAY_MAKE(conn, properties, property_count);
663b8e80941Smrg
664b8e80941Smrg   wsi_for_each_display_mode(display_mode, connector) {
665b8e80941Smrg      if (!display_mode->valid)
666b8e80941Smrg         continue;
667b8e80941Smrg
668b8e80941Smrg      vk_outarray_append(&conn, prop) {
669b8e80941Smrg         wsi_display_fill_in_display_mode_properties(wsi_device,
670b8e80941Smrg                                                     display_mode, prop);
671b8e80941Smrg      }
672b8e80941Smrg   }
673b8e80941Smrg   return vk_outarray_status(&conn);
674b8e80941Smrg}
675b8e80941Smrg
676b8e80941Smrgstatic bool
677b8e80941Smrgwsi_display_mode_matches_vk(wsi_display_mode *wsi,
678b8e80941Smrg                            const VkDisplayModeParametersKHR *vk)
679b8e80941Smrg{
680b8e80941Smrg   return (vk->visibleRegion.width == wsi->hdisplay &&
681b8e80941Smrg           vk->visibleRegion.height == wsi->vdisplay &&
682b8e80941Smrg           fabs(wsi_display_mode_refresh(wsi) * 1000.0 - vk->refreshRate) < 10);
683b8e80941Smrg}
684b8e80941Smrg
685b8e80941Smrg/*
686b8e80941Smrg * Implement vkCreateDisplayModeKHR (VK_KHR_display)
687b8e80941Smrg */
688b8e80941SmrgVkResult
689b8e80941Smrgwsi_display_create_display_mode(VkPhysicalDevice physical_device,
690b8e80941Smrg                                struct wsi_device *wsi_device,
691b8e80941Smrg                                VkDisplayKHR display,
692b8e80941Smrg                                const VkDisplayModeCreateInfoKHR *create_info,
693b8e80941Smrg                                const VkAllocationCallbacks *allocator,
694b8e80941Smrg                                VkDisplayModeKHR *mode)
695b8e80941Smrg{
696b8e80941Smrg   struct wsi_display_connector *connector =
697b8e80941Smrg      wsi_display_connector_from_handle(display);
698b8e80941Smrg
699b8e80941Smrg   if (create_info->flags != 0)
700b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
701b8e80941Smrg
702b8e80941Smrg   /* Check and see if the requested mode happens to match an existing one and
703b8e80941Smrg    * return that. This makes the conformance suite happy. Doing more than
704b8e80941Smrg    * this would involve embedding the CVT function into the driver, which seems
705b8e80941Smrg    * excessive.
706b8e80941Smrg    */
707b8e80941Smrg   wsi_for_each_display_mode(display_mode, connector) {
708b8e80941Smrg      if (display_mode->valid) {
709b8e80941Smrg         if (wsi_display_mode_matches_vk(display_mode, &create_info->parameters)) {
710b8e80941Smrg            *mode = wsi_display_mode_to_handle(display_mode);
711b8e80941Smrg            return VK_SUCCESS;
712b8e80941Smrg         }
713b8e80941Smrg      }
714b8e80941Smrg   }
715b8e80941Smrg   return VK_ERROR_INITIALIZATION_FAILED;
716b8e80941Smrg}
717b8e80941Smrg
718b8e80941Smrg/*
719b8e80941Smrg * Implement vkGetDisplayPlaneCapabilities
720b8e80941Smrg */
721b8e80941SmrgVkResult
722b8e80941Smrgwsi_get_display_plane_capabilities(VkPhysicalDevice physical_device,
723b8e80941Smrg                                   struct wsi_device *wsi_device,
724b8e80941Smrg                                   VkDisplayModeKHR mode_khr,
725b8e80941Smrg                                   uint32_t plane_index,
726b8e80941Smrg                                   VkDisplayPlaneCapabilitiesKHR *capabilities)
727b8e80941Smrg{
728b8e80941Smrg   struct wsi_display_mode *mode = wsi_display_mode_from_handle(mode_khr);
729b8e80941Smrg
730b8e80941Smrg   /* XXX use actual values */
731b8e80941Smrg   capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
732b8e80941Smrg   capabilities->minSrcPosition.x = 0;
733b8e80941Smrg   capabilities->minSrcPosition.y = 0;
734b8e80941Smrg   capabilities->maxSrcPosition.x = 0;
735b8e80941Smrg   capabilities->maxSrcPosition.y = 0;
736b8e80941Smrg   capabilities->minSrcExtent.width = mode->hdisplay;
737b8e80941Smrg   capabilities->minSrcExtent.height = mode->vdisplay;
738b8e80941Smrg   capabilities->maxSrcExtent.width = mode->hdisplay;
739b8e80941Smrg   capabilities->maxSrcExtent.height = mode->vdisplay;
740b8e80941Smrg   capabilities->minDstPosition.x = 0;
741b8e80941Smrg   capabilities->minDstPosition.y = 0;
742b8e80941Smrg   capabilities->maxDstPosition.x = 0;
743b8e80941Smrg   capabilities->maxDstPosition.y = 0;
744b8e80941Smrg   capabilities->minDstExtent.width = mode->hdisplay;
745b8e80941Smrg   capabilities->minDstExtent.height = mode->vdisplay;
746b8e80941Smrg   capabilities->maxDstExtent.width = mode->hdisplay;
747b8e80941Smrg   capabilities->maxDstExtent.height = mode->vdisplay;
748b8e80941Smrg   return VK_SUCCESS;
749b8e80941Smrg}
750b8e80941Smrg
751b8e80941SmrgVkResult
752b8e80941Smrgwsi_get_display_plane_capabilities2(
753b8e80941Smrg   VkPhysicalDevice physical_device,
754b8e80941Smrg   struct wsi_device *wsi_device,
755b8e80941Smrg   const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
756b8e80941Smrg   VkDisplayPlaneCapabilities2KHR *capabilities)
757b8e80941Smrg{
758b8e80941Smrg   assert(capabilities->sType ==
759b8e80941Smrg          VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR);
760b8e80941Smrg
761b8e80941Smrg   VkResult result =
762b8e80941Smrg      wsi_get_display_plane_capabilities(physical_device, wsi_device,
763b8e80941Smrg                                         pDisplayPlaneInfo->mode,
764b8e80941Smrg                                         pDisplayPlaneInfo->planeIndex,
765b8e80941Smrg                                         &capabilities->capabilities);
766b8e80941Smrg
767b8e80941Smrg   vk_foreach_struct(ext, capabilities->pNext) {
768b8e80941Smrg      switch (ext->sType) {
769b8e80941Smrg      case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
770b8e80941Smrg         VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext;
771b8e80941Smrg         protected->supportsProtected = VK_FALSE;
772b8e80941Smrg         break;
773b8e80941Smrg      }
774b8e80941Smrg
775b8e80941Smrg      default:
776b8e80941Smrg         /* Ignored */
777b8e80941Smrg         break;
778b8e80941Smrg      }
779b8e80941Smrg   }
780b8e80941Smrg
781b8e80941Smrg   return result;
782b8e80941Smrg}
783b8e80941Smrg
784b8e80941SmrgVkResult
785b8e80941Smrgwsi_create_display_surface(VkInstance instance,
786b8e80941Smrg                           const VkAllocationCallbacks *allocator,
787b8e80941Smrg                           const VkDisplaySurfaceCreateInfoKHR *create_info,
788b8e80941Smrg                           VkSurfaceKHR *surface_khr)
789b8e80941Smrg{
790b8e80941Smrg   VkIcdSurfaceDisplay *surface = vk_zalloc(allocator, sizeof *surface, 8,
791b8e80941Smrg                                            VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
792b8e80941Smrg
793b8e80941Smrg   if (surface == NULL)
794b8e80941Smrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
795b8e80941Smrg
796b8e80941Smrg   surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY;
797b8e80941Smrg
798b8e80941Smrg   surface->displayMode = create_info->displayMode;
799b8e80941Smrg   surface->planeIndex = create_info->planeIndex;
800b8e80941Smrg   surface->planeStackIndex = create_info->planeStackIndex;
801b8e80941Smrg   surface->transform = create_info->transform;
802b8e80941Smrg   surface->globalAlpha = create_info->globalAlpha;
803b8e80941Smrg   surface->alphaMode = create_info->alphaMode;
804b8e80941Smrg   surface->imageExtent = create_info->imageExtent;
805b8e80941Smrg
806b8e80941Smrg   *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base);
807b8e80941Smrg   return VK_SUCCESS;
808b8e80941Smrg}
809b8e80941Smrg
810b8e80941Smrg
811b8e80941Smrgstatic VkResult
812b8e80941Smrgwsi_display_surface_get_support(VkIcdSurfaceBase *surface,
813b8e80941Smrg                                struct wsi_device *wsi_device,
814b8e80941Smrg                                uint32_t queueFamilyIndex,
815b8e80941Smrg                                VkBool32* pSupported)
816b8e80941Smrg{
817b8e80941Smrg   *pSupported = VK_TRUE;
818b8e80941Smrg   return VK_SUCCESS;
819b8e80941Smrg}
820b8e80941Smrg
821b8e80941Smrgstatic VkResult
822b8e80941Smrgwsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base,
823b8e80941Smrg                                     struct wsi_device *wsi_device,
824b8e80941Smrg                                     VkSurfaceCapabilitiesKHR* caps)
825b8e80941Smrg{
826b8e80941Smrg   VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
827b8e80941Smrg   wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
828b8e80941Smrg
829b8e80941Smrg   caps->currentExtent.width = mode->hdisplay;
830b8e80941Smrg   caps->currentExtent.height = mode->vdisplay;
831b8e80941Smrg
832b8e80941Smrg   caps->minImageExtent = (VkExtent2D) { 1, 1 };
833b8e80941Smrg   caps->maxImageExtent = (VkExtent2D) {
834b8e80941Smrg      wsi_device->maxImageDimension2D,
835b8e80941Smrg      wsi_device->maxImageDimension2D,
836b8e80941Smrg   };
837b8e80941Smrg
838b8e80941Smrg   caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
839b8e80941Smrg
840b8e80941Smrg   caps->minImageCount = 2;
841b8e80941Smrg   caps->maxImageCount = 0;
842b8e80941Smrg
843b8e80941Smrg   caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
844b8e80941Smrg   caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
845b8e80941Smrg   caps->maxImageArrayLayers = 1;
846b8e80941Smrg   caps->supportedUsageFlags =
847b8e80941Smrg      VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
848b8e80941Smrg      VK_IMAGE_USAGE_SAMPLED_BIT |
849b8e80941Smrg      VK_IMAGE_USAGE_TRANSFER_DST_BIT |
850b8e80941Smrg      VK_IMAGE_USAGE_STORAGE_BIT |
851b8e80941Smrg      VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
852b8e80941Smrg
853b8e80941Smrg   return VK_SUCCESS;
854b8e80941Smrg}
855b8e80941Smrg
856b8e80941Smrgstatic VkResult
857b8e80941Smrgwsi_display_surface_get_surface_counters(
858b8e80941Smrg   VkIcdSurfaceBase *surface_base,
859b8e80941Smrg   VkSurfaceCounterFlagsEXT *counters)
860b8e80941Smrg{
861b8e80941Smrg   *counters = VK_SURFACE_COUNTER_VBLANK_EXT;
862b8e80941Smrg   return VK_SUCCESS;
863b8e80941Smrg}
864b8e80941Smrg
865b8e80941Smrgstatic VkResult
866b8e80941Smrgwsi_display_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,
867b8e80941Smrg                                      struct wsi_device *wsi_device,
868b8e80941Smrg                                      const void *info_next,
869b8e80941Smrg                                      VkSurfaceCapabilities2KHR *caps)
870b8e80941Smrg{
871b8e80941Smrg   assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
872b8e80941Smrg   VkResult result;
873b8e80941Smrg
874b8e80941Smrg   result = wsi_display_surface_get_capabilities(icd_surface, wsi_device,
875b8e80941Smrg                                                 &caps->surfaceCapabilities);
876b8e80941Smrg   if (result != VK_SUCCESS)
877b8e80941Smrg      return result;
878b8e80941Smrg
879b8e80941Smrg   struct wsi_surface_supported_counters *counters =
880b8e80941Smrg      vk_find_struct( caps->pNext, WSI_SURFACE_SUPPORTED_COUNTERS_MESA);
881b8e80941Smrg
882b8e80941Smrg   if (counters) {
883b8e80941Smrg      result = wsi_display_surface_get_surface_counters(
884b8e80941Smrg         icd_surface,
885b8e80941Smrg         &counters->supported_surface_counters);
886b8e80941Smrg   }
887b8e80941Smrg
888b8e80941Smrg   return result;
889b8e80941Smrg}
890b8e80941Smrg
891b8e80941Smrgstatic const struct {
892b8e80941Smrg   VkFormat     format;
893b8e80941Smrg   uint32_t     drm_format;
894b8e80941Smrg} available_surface_formats[] = {
895b8e80941Smrg   { .format = VK_FORMAT_B8G8R8A8_SRGB, .drm_format = DRM_FORMAT_XRGB8888 },
896b8e80941Smrg   { .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 },
897b8e80941Smrg};
898b8e80941Smrg
899b8e80941Smrgstatic VkResult
900b8e80941Smrgwsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface,
901b8e80941Smrg                                struct wsi_device *wsi_device,
902b8e80941Smrg                                uint32_t *surface_format_count,
903b8e80941Smrg                                VkSurfaceFormatKHR *surface_formats)
904b8e80941Smrg{
905b8e80941Smrg   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
906b8e80941Smrg
907b8e80941Smrg   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
908b8e80941Smrg      vk_outarray_append(&out, f) {
909b8e80941Smrg         f->format = available_surface_formats[i].format;
910b8e80941Smrg         f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
911b8e80941Smrg      }
912b8e80941Smrg   }
913b8e80941Smrg
914b8e80941Smrg   return vk_outarray_status(&out);
915b8e80941Smrg}
916b8e80941Smrg
917b8e80941Smrgstatic VkResult
918b8e80941Smrgwsi_display_surface_get_formats2(VkIcdSurfaceBase *surface,
919b8e80941Smrg                                 struct wsi_device *wsi_device,
920b8e80941Smrg                                 const void *info_next,
921b8e80941Smrg                                 uint32_t *surface_format_count,
922b8e80941Smrg                                 VkSurfaceFormat2KHR *surface_formats)
923b8e80941Smrg{
924b8e80941Smrg   VK_OUTARRAY_MAKE(out, surface_formats, surface_format_count);
925b8e80941Smrg
926b8e80941Smrg   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
927b8e80941Smrg      vk_outarray_append(&out, f) {
928b8e80941Smrg         assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
929b8e80941Smrg         f->surfaceFormat.format = available_surface_formats[i].format;
930b8e80941Smrg         f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
931b8e80941Smrg      }
932b8e80941Smrg   }
933b8e80941Smrg
934b8e80941Smrg   return vk_outarray_status(&out);
935b8e80941Smrg}
936b8e80941Smrg
937b8e80941Smrgstatic VkResult
938b8e80941Smrgwsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface,
939b8e80941Smrg                                      uint32_t *present_mode_count,
940b8e80941Smrg                                      VkPresentModeKHR *present_modes)
941b8e80941Smrg{
942b8e80941Smrg   VK_OUTARRAY_MAKE(conn, present_modes, present_mode_count);
943b8e80941Smrg
944b8e80941Smrg   vk_outarray_append(&conn, present) {
945b8e80941Smrg      *present = VK_PRESENT_MODE_FIFO_KHR;
946b8e80941Smrg   }
947b8e80941Smrg
948b8e80941Smrg   return vk_outarray_status(&conn);
949b8e80941Smrg}
950b8e80941Smrg
951b8e80941Smrgstatic VkResult
952b8e80941Smrgwsi_display_surface_get_present_rectangles(VkIcdSurfaceBase *surface_base,
953b8e80941Smrg                                           struct wsi_device *wsi_device,
954b8e80941Smrg                                           uint32_t* pRectCount,
955b8e80941Smrg                                           VkRect2D* pRects)
956b8e80941Smrg{
957b8e80941Smrg   VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base;
958b8e80941Smrg   wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode);
959b8e80941Smrg   VK_OUTARRAY_MAKE(out, pRects, pRectCount);
960b8e80941Smrg
961b8e80941Smrg   if (wsi_device_matches_drm_fd(wsi_device, mode->connector->wsi->fd)) {
962b8e80941Smrg      vk_outarray_append(&out, rect) {
963b8e80941Smrg         *rect = (VkRect2D) {
964b8e80941Smrg            .offset = { 0, 0 },
965b8e80941Smrg            .extent = { mode->hdisplay, mode->vdisplay },
966b8e80941Smrg         };
967b8e80941Smrg      }
968b8e80941Smrg   }
969b8e80941Smrg
970b8e80941Smrg   return vk_outarray_status(&out);
971b8e80941Smrg}
972b8e80941Smrg
973b8e80941Smrgstatic void
974b8e80941Smrgwsi_display_destroy_buffer(struct wsi_display *wsi,
975b8e80941Smrg                           uint32_t buffer)
976b8e80941Smrg{
977b8e80941Smrg   (void) drmIoctl(wsi->fd, DRM_IOCTL_GEM_CLOSE,
978b8e80941Smrg                   &((struct drm_gem_close) { .handle = buffer }));
979b8e80941Smrg}
980b8e80941Smrg
981b8e80941Smrgstatic VkResult
982b8e80941Smrgwsi_display_image_init(VkDevice device_h,
983b8e80941Smrg                       struct wsi_swapchain *drv_chain,
984b8e80941Smrg                       const VkSwapchainCreateInfoKHR *create_info,
985b8e80941Smrg                       const VkAllocationCallbacks *allocator,
986b8e80941Smrg                       struct wsi_display_image *image)
987b8e80941Smrg{
988b8e80941Smrg   struct wsi_display_swapchain *chain =
989b8e80941Smrg      (struct wsi_display_swapchain *) drv_chain;
990b8e80941Smrg   struct wsi_display *wsi = chain->wsi;
991b8e80941Smrg   uint32_t drm_format = 0;
992b8e80941Smrg
993b8e80941Smrg   for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
994b8e80941Smrg      if (create_info->imageFormat == available_surface_formats[i].format) {
995b8e80941Smrg         drm_format = available_surface_formats[i].drm_format;
996b8e80941Smrg         break;
997b8e80941Smrg      }
998b8e80941Smrg   }
999b8e80941Smrg
1000b8e80941Smrg   /* the application provided an invalid format, bail */
1001b8e80941Smrg   if (drm_format == 0)
1002b8e80941Smrg      return VK_ERROR_DEVICE_LOST;
1003b8e80941Smrg
1004b8e80941Smrg   VkResult result = wsi_create_native_image(&chain->base, create_info,
1005b8e80941Smrg                                             0, NULL, NULL,
1006b8e80941Smrg                                             &image->base);
1007b8e80941Smrg   if (result != VK_SUCCESS)
1008b8e80941Smrg      return result;
1009b8e80941Smrg
1010b8e80941Smrg   memset(image->buffer, 0, sizeof (image->buffer));
1011b8e80941Smrg
1012b8e80941Smrg   for (unsigned int i = 0; i < image->base.num_planes; i++) {
1013b8e80941Smrg      int ret = drmPrimeFDToHandle(wsi->fd, image->base.fds[i],
1014b8e80941Smrg                                   &image->buffer[i]);
1015b8e80941Smrg
1016b8e80941Smrg      close(image->base.fds[i]);
1017b8e80941Smrg      image->base.fds[i] = -1;
1018b8e80941Smrg      if (ret < 0)
1019b8e80941Smrg         goto fail_handle;
1020b8e80941Smrg   }
1021b8e80941Smrg
1022b8e80941Smrg   image->chain = chain;
1023b8e80941Smrg   image->state = WSI_IMAGE_IDLE;
1024b8e80941Smrg   image->fb_id = 0;
1025b8e80941Smrg
1026b8e80941Smrg   int ret = drmModeAddFB2(wsi->fd,
1027b8e80941Smrg                           create_info->imageExtent.width,
1028b8e80941Smrg                           create_info->imageExtent.height,
1029b8e80941Smrg                           drm_format,
1030b8e80941Smrg                           image->buffer,
1031b8e80941Smrg                           image->base.row_pitches,
1032b8e80941Smrg                           image->base.offsets,
1033b8e80941Smrg                           &image->fb_id, 0);
1034b8e80941Smrg
1035b8e80941Smrg   if (ret)
1036b8e80941Smrg      goto fail_fb;
1037b8e80941Smrg
1038b8e80941Smrg   return VK_SUCCESS;
1039b8e80941Smrg
1040b8e80941Smrgfail_fb:
1041b8e80941Smrgfail_handle:
1042b8e80941Smrg   for (unsigned int i = 0; i < image->base.num_planes; i++) {
1043b8e80941Smrg      if (image->buffer[i])
1044b8e80941Smrg         wsi_display_destroy_buffer(wsi, image->buffer[i]);
1045b8e80941Smrg      if (image->base.fds[i] != -1) {
1046b8e80941Smrg         close(image->base.fds[i]);
1047b8e80941Smrg         image->base.fds[i] = -1;
1048b8e80941Smrg      }
1049b8e80941Smrg   }
1050b8e80941Smrg
1051b8e80941Smrg   wsi_destroy_image(&chain->base, &image->base);
1052b8e80941Smrg
1053b8e80941Smrg   return VK_ERROR_OUT_OF_HOST_MEMORY;
1054b8e80941Smrg}
1055b8e80941Smrg
1056b8e80941Smrgstatic void
1057b8e80941Smrgwsi_display_image_finish(struct wsi_swapchain *drv_chain,
1058b8e80941Smrg                         const VkAllocationCallbacks *allocator,
1059b8e80941Smrg                         struct wsi_display_image *image)
1060b8e80941Smrg{
1061b8e80941Smrg   struct wsi_display_swapchain *chain =
1062b8e80941Smrg      (struct wsi_display_swapchain *) drv_chain;
1063b8e80941Smrg   struct wsi_display *wsi = chain->wsi;
1064b8e80941Smrg
1065b8e80941Smrg   drmModeRmFB(wsi->fd, image->fb_id);
1066b8e80941Smrg   for (unsigned int i = 0; i < image->base.num_planes; i++)
1067b8e80941Smrg      wsi_display_destroy_buffer(wsi, image->buffer[i]);
1068b8e80941Smrg   wsi_destroy_image(&chain->base, &image->base);
1069b8e80941Smrg}
1070b8e80941Smrg
1071b8e80941Smrgstatic VkResult
1072b8e80941Smrgwsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain,
1073b8e80941Smrg                              const VkAllocationCallbacks *allocator)
1074b8e80941Smrg{
1075b8e80941Smrg   struct wsi_display_swapchain *chain =
1076b8e80941Smrg      (struct wsi_display_swapchain *) drv_chain;
1077b8e80941Smrg
1078b8e80941Smrg   for (uint32_t i = 0; i < chain->base.image_count; i++)
1079b8e80941Smrg      wsi_display_image_finish(drv_chain, allocator, &chain->images[i]);
1080b8e80941Smrg
1081b8e80941Smrg   wsi_swapchain_finish(&chain->base);
1082b8e80941Smrg   vk_free(allocator, chain);
1083b8e80941Smrg   return VK_SUCCESS;
1084b8e80941Smrg}
1085b8e80941Smrg
1086b8e80941Smrgstatic struct wsi_image *
1087b8e80941Smrgwsi_display_get_wsi_image(struct wsi_swapchain *drv_chain,
1088b8e80941Smrg                          uint32_t image_index)
1089b8e80941Smrg{
1090b8e80941Smrg   struct wsi_display_swapchain *chain =
1091b8e80941Smrg      (struct wsi_display_swapchain *) drv_chain;
1092b8e80941Smrg
1093b8e80941Smrg   return &chain->images[image_index].base;
1094b8e80941Smrg}
1095b8e80941Smrg
1096b8e80941Smrgstatic void
1097b8e80941Smrgwsi_display_idle_old_displaying(struct wsi_display_image *active_image)
1098b8e80941Smrg{
1099b8e80941Smrg   struct wsi_display_swapchain *chain = active_image->chain;
1100b8e80941Smrg
1101b8e80941Smrg   wsi_display_debug("idle everyone but %ld\n",
1102b8e80941Smrg                     active_image - &(chain->images[0]));
1103b8e80941Smrg   for (uint32_t i = 0; i < chain->base.image_count; i++)
1104b8e80941Smrg      if (chain->images[i].state == WSI_IMAGE_DISPLAYING &&
1105b8e80941Smrg          &chain->images[i] != active_image)
1106b8e80941Smrg      {
1107b8e80941Smrg         wsi_display_debug("idle %d\n", i);
1108b8e80941Smrg         chain->images[i].state = WSI_IMAGE_IDLE;
1109b8e80941Smrg      }
1110b8e80941Smrg}
1111b8e80941Smrg
1112b8e80941Smrgstatic VkResult
1113b8e80941Smrg_wsi_display_queue_next(struct wsi_swapchain *drv_chain);
1114b8e80941Smrg
1115b8e80941Smrgstatic void
1116b8e80941Smrgwsi_display_page_flip_handler2(int fd,
1117b8e80941Smrg                               unsigned int frame,
1118b8e80941Smrg                               unsigned int sec,
1119b8e80941Smrg                               unsigned int usec,
1120b8e80941Smrg                               uint32_t crtc_id,
1121b8e80941Smrg                               void *data)
1122b8e80941Smrg{
1123b8e80941Smrg   struct wsi_display_image *image = data;
1124b8e80941Smrg   struct wsi_display_swapchain *chain = image->chain;
1125b8e80941Smrg
1126b8e80941Smrg   wsi_display_debug("image %ld displayed at %d\n",
1127b8e80941Smrg                     image - &(image->chain->images[0]), frame);
1128b8e80941Smrg   image->state = WSI_IMAGE_DISPLAYING;
1129b8e80941Smrg   wsi_display_idle_old_displaying(image);
1130b8e80941Smrg   VkResult result = _wsi_display_queue_next(&(chain->base));
1131b8e80941Smrg   if (result != VK_SUCCESS)
1132b8e80941Smrg      chain->status = result;
1133b8e80941Smrg}
1134b8e80941Smrg
1135b8e80941Smrgstatic void wsi_display_fence_event_handler(struct wsi_display_fence *fence);
1136b8e80941Smrg
1137b8e80941Smrgstatic void wsi_display_page_flip_handler(int fd,
1138b8e80941Smrg                                          unsigned int frame,
1139b8e80941Smrg                                          unsigned int sec,
1140b8e80941Smrg                                          unsigned int usec,
1141b8e80941Smrg                                          void *data)
1142b8e80941Smrg{
1143b8e80941Smrg   wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data);
1144b8e80941Smrg}
1145b8e80941Smrg
1146b8e80941Smrgstatic void wsi_display_vblank_handler(int fd, unsigned int frame,
1147b8e80941Smrg                                       unsigned int sec, unsigned int usec,
1148b8e80941Smrg                                       void *data)
1149b8e80941Smrg{
1150b8e80941Smrg   struct wsi_display_fence *fence = data;
1151b8e80941Smrg
1152b8e80941Smrg   wsi_display_fence_event_handler(fence);
1153b8e80941Smrg}
1154b8e80941Smrg
1155b8e80941Smrgstatic void wsi_display_sequence_handler(int fd, uint64_t frame,
1156b8e80941Smrg                                         uint64_t nsec, uint64_t user_data)
1157b8e80941Smrg{
1158b8e80941Smrg   struct wsi_display_fence *fence =
1159b8e80941Smrg      (struct wsi_display_fence *) (uintptr_t) user_data;
1160b8e80941Smrg
1161b8e80941Smrg   wsi_display_fence_event_handler(fence);
1162b8e80941Smrg}
1163b8e80941Smrg
1164b8e80941Smrgstatic drmEventContext event_context = {
1165b8e80941Smrg   .version = DRM_EVENT_CONTEXT_VERSION,
1166b8e80941Smrg   .page_flip_handler = wsi_display_page_flip_handler,
1167b8e80941Smrg#if DRM_EVENT_CONTEXT_VERSION >= 3
1168b8e80941Smrg   .page_flip_handler2 = wsi_display_page_flip_handler2,
1169b8e80941Smrg#endif
1170b8e80941Smrg   .vblank_handler = wsi_display_vblank_handler,
1171b8e80941Smrg   .sequence_handler = wsi_display_sequence_handler,
1172b8e80941Smrg};
1173b8e80941Smrg
1174b8e80941Smrgstatic void *
1175b8e80941Smrgwsi_display_wait_thread(void *data)
1176b8e80941Smrg{
1177b8e80941Smrg   struct wsi_display *wsi = data;
1178b8e80941Smrg   struct pollfd pollfd = {
1179b8e80941Smrg      .fd = wsi->fd,
1180b8e80941Smrg      .events = POLLIN
1181b8e80941Smrg   };
1182b8e80941Smrg
1183b8e80941Smrg   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
1184b8e80941Smrg   for (;;) {
1185b8e80941Smrg      int ret = poll(&pollfd, 1, -1);
1186b8e80941Smrg      if (ret > 0) {
1187b8e80941Smrg         pthread_mutex_lock(&wsi->wait_mutex);
1188b8e80941Smrg         (void) drmHandleEvent(wsi->fd, &event_context);
1189b8e80941Smrg         pthread_mutex_unlock(&wsi->wait_mutex);
1190b8e80941Smrg         pthread_cond_broadcast(&wsi->wait_cond);
1191b8e80941Smrg      }
1192b8e80941Smrg   }
1193b8e80941Smrg   return NULL;
1194b8e80941Smrg}
1195b8e80941Smrg
1196b8e80941Smrgstatic int
1197b8e80941Smrgwsi_display_start_wait_thread(struct wsi_display *wsi)
1198b8e80941Smrg{
1199b8e80941Smrg   if (!wsi->wait_thread) {
1200b8e80941Smrg      int ret = pthread_create(&wsi->wait_thread, NULL,
1201b8e80941Smrg                               wsi_display_wait_thread, wsi);
1202b8e80941Smrg      if (ret)
1203b8e80941Smrg         return ret;
1204b8e80941Smrg   }
1205b8e80941Smrg   return 0;
1206b8e80941Smrg}
1207b8e80941Smrg
1208b8e80941Smrg/*
1209b8e80941Smrg * Wait for at least one event from the kernel to be processed.
1210b8e80941Smrg * Call with wait_mutex held
1211b8e80941Smrg */
1212b8e80941Smrgstatic int
1213b8e80941Smrgwsi_display_wait_for_event(struct wsi_display *wsi,
1214b8e80941Smrg                           uint64_t timeout_ns)
1215b8e80941Smrg{
1216b8e80941Smrg   int ret;
1217b8e80941Smrg
1218b8e80941Smrg   ret = wsi_display_start_wait_thread(wsi);
1219b8e80941Smrg
1220b8e80941Smrg   if (ret)
1221b8e80941Smrg      return ret;
1222b8e80941Smrg
1223b8e80941Smrg   struct timespec abs_timeout = {
1224b8e80941Smrg      .tv_sec = timeout_ns / 1000000000ULL,
1225b8e80941Smrg      .tv_nsec = timeout_ns % 1000000000ULL,
1226b8e80941Smrg   };
1227b8e80941Smrg
1228b8e80941Smrg   ret = pthread_cond_timedwait(&wsi->wait_cond, &wsi->wait_mutex,
1229b8e80941Smrg                                &abs_timeout);
1230b8e80941Smrg
1231b8e80941Smrg   wsi_display_debug("%9ld done waiting for event %d\n", pthread_self(), ret);
1232b8e80941Smrg   return ret;
1233b8e80941Smrg}
1234b8e80941Smrg
1235b8e80941Smrgstatic VkResult
1236b8e80941Smrgwsi_display_acquire_next_image(struct wsi_swapchain *drv_chain,
1237b8e80941Smrg                               const VkAcquireNextImageInfoKHR *info,
1238b8e80941Smrg                               uint32_t *image_index)
1239b8e80941Smrg{
1240b8e80941Smrg   struct wsi_display_swapchain *chain =
1241b8e80941Smrg      (struct wsi_display_swapchain *)drv_chain;
1242b8e80941Smrg   struct wsi_display *wsi = chain->wsi;
1243b8e80941Smrg   int ret = 0;
1244b8e80941Smrg   VkResult result = VK_SUCCESS;
1245b8e80941Smrg
1246b8e80941Smrg   /* Bail early if the swapchain is broken */
1247b8e80941Smrg   if (chain->status != VK_SUCCESS)
1248b8e80941Smrg      return chain->status;
1249b8e80941Smrg
1250b8e80941Smrg   uint64_t timeout = info->timeout;
1251b8e80941Smrg   if (timeout != 0 && timeout != UINT64_MAX)
1252b8e80941Smrg      timeout = wsi_rel_to_abs_time(timeout);
1253b8e80941Smrg
1254b8e80941Smrg   pthread_mutex_lock(&wsi->wait_mutex);
1255b8e80941Smrg   for (;;) {
1256b8e80941Smrg      for (uint32_t i = 0; i < chain->base.image_count; i++) {
1257b8e80941Smrg         if (chain->images[i].state == WSI_IMAGE_IDLE) {
1258b8e80941Smrg            *image_index = i;
1259b8e80941Smrg            wsi_display_debug("image %d available\n", i);
1260b8e80941Smrg            chain->images[i].state = WSI_IMAGE_DRAWING;
1261b8e80941Smrg            result = VK_SUCCESS;
1262b8e80941Smrg            goto done;
1263b8e80941Smrg         }
1264b8e80941Smrg         wsi_display_debug("image %d state %d\n", i, chain->images[i].state);
1265b8e80941Smrg      }
1266b8e80941Smrg
1267b8e80941Smrg      if (ret == ETIMEDOUT) {
1268b8e80941Smrg         result = VK_TIMEOUT;
1269b8e80941Smrg         goto done;
1270b8e80941Smrg      }
1271b8e80941Smrg
1272b8e80941Smrg      ret = wsi_display_wait_for_event(wsi, timeout);
1273b8e80941Smrg
1274b8e80941Smrg      if (ret && ret != ETIMEDOUT) {
1275b8e80941Smrg         result = VK_ERROR_SURFACE_LOST_KHR;
1276b8e80941Smrg         goto done;
1277b8e80941Smrg      }
1278b8e80941Smrg   }
1279b8e80941Smrgdone:
1280b8e80941Smrg   pthread_mutex_unlock(&wsi->wait_mutex);
1281b8e80941Smrg
1282b8e80941Smrg   if (result != VK_SUCCESS)
1283b8e80941Smrg      return result;
1284b8e80941Smrg
1285b8e80941Smrg   return chain->status;
1286b8e80941Smrg}
1287b8e80941Smrg
1288b8e80941Smrg/*
1289b8e80941Smrg * Check whether there are any other connectors driven by this crtc
1290b8e80941Smrg */
1291b8e80941Smrgstatic bool
1292b8e80941Smrgwsi_display_crtc_solo(struct wsi_display *wsi,
1293b8e80941Smrg                      drmModeResPtr mode_res,
1294b8e80941Smrg                      drmModeConnectorPtr connector,
1295b8e80941Smrg                      uint32_t crtc_id)
1296b8e80941Smrg{
1297b8e80941Smrg   /* See if any other connectors share the same encoder */
1298b8e80941Smrg   for (int c = 0; c < mode_res->count_connectors; c++) {
1299b8e80941Smrg      if (mode_res->connectors[c] == connector->connector_id)
1300b8e80941Smrg         continue;
1301b8e80941Smrg
1302b8e80941Smrg      drmModeConnectorPtr other_connector =
1303b8e80941Smrg         drmModeGetConnector(wsi->fd, mode_res->connectors[c]);
1304b8e80941Smrg
1305b8e80941Smrg      if (other_connector) {
1306b8e80941Smrg         bool match = (other_connector->encoder_id == connector->encoder_id);
1307b8e80941Smrg         drmModeFreeConnector(other_connector);
1308b8e80941Smrg         if (match)
1309b8e80941Smrg            return false;
1310b8e80941Smrg      }
1311b8e80941Smrg   }
1312b8e80941Smrg
1313b8e80941Smrg   /* See if any other encoders share the same crtc */
1314b8e80941Smrg   for (int e = 0; e < mode_res->count_encoders; e++) {
1315b8e80941Smrg      if (mode_res->encoders[e] == connector->encoder_id)
1316b8e80941Smrg         continue;
1317b8e80941Smrg
1318b8e80941Smrg      drmModeEncoderPtr other_encoder =
1319b8e80941Smrg         drmModeGetEncoder(wsi->fd, mode_res->encoders[e]);
1320b8e80941Smrg
1321b8e80941Smrg      if (other_encoder) {
1322b8e80941Smrg         bool match = (other_encoder->crtc_id == crtc_id);
1323b8e80941Smrg         drmModeFreeEncoder(other_encoder);
1324b8e80941Smrg         if (match)
1325b8e80941Smrg            return false;
1326b8e80941Smrg      }
1327b8e80941Smrg   }
1328b8e80941Smrg   return true;
1329b8e80941Smrg}
1330b8e80941Smrg
1331b8e80941Smrg/*
1332b8e80941Smrg * Pick a suitable CRTC to drive this connector. Prefer a CRTC which is
1333b8e80941Smrg * currently driving this connector and not any others. Settle for a CRTC
1334b8e80941Smrg * which is currently idle.
1335b8e80941Smrg */
1336b8e80941Smrgstatic uint32_t
1337b8e80941Smrgwsi_display_select_crtc(const struct wsi_display_connector *connector,
1338b8e80941Smrg                        drmModeResPtr mode_res,
1339b8e80941Smrg                        drmModeConnectorPtr drm_connector)
1340b8e80941Smrg{
1341b8e80941Smrg   struct wsi_display *wsi = connector->wsi;
1342b8e80941Smrg
1343b8e80941Smrg   /* See what CRTC is currently driving this connector */
1344b8e80941Smrg   if (drm_connector->encoder_id) {
1345b8e80941Smrg      drmModeEncoderPtr encoder =
1346b8e80941Smrg         drmModeGetEncoder(wsi->fd, drm_connector->encoder_id);
1347b8e80941Smrg
1348b8e80941Smrg      if (encoder) {
1349b8e80941Smrg         uint32_t crtc_id = encoder->crtc_id;
1350b8e80941Smrg         drmModeFreeEncoder(encoder);
1351b8e80941Smrg         if (crtc_id) {
1352b8e80941Smrg            if (wsi_display_crtc_solo(wsi, mode_res, drm_connector, crtc_id))
1353b8e80941Smrg               return crtc_id;
1354b8e80941Smrg         }
1355b8e80941Smrg      }
1356b8e80941Smrg   }
1357b8e80941Smrg   uint32_t crtc_id = 0;
1358b8e80941Smrg   for (int c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) {
1359b8e80941Smrg      drmModeCrtcPtr crtc = drmModeGetCrtc(wsi->fd, mode_res->crtcs[c]);
1360b8e80941Smrg      if (crtc && crtc->buffer_id == 0)
1361b8e80941Smrg         crtc_id = crtc->crtc_id;
1362b8e80941Smrg      drmModeFreeCrtc(crtc);
1363b8e80941Smrg   }
1364b8e80941Smrg   return crtc_id;
1365b8e80941Smrg}
1366b8e80941Smrg
1367b8e80941Smrgstatic VkResult
1368b8e80941Smrgwsi_display_setup_connector(wsi_display_connector *connector,
1369b8e80941Smrg                            wsi_display_mode *display_mode)
1370b8e80941Smrg{
1371b8e80941Smrg   struct wsi_display *wsi = connector->wsi;
1372b8e80941Smrg
1373b8e80941Smrg   if (connector->current_mode == display_mode && connector->crtc_id)
1374b8e80941Smrg      return VK_SUCCESS;
1375b8e80941Smrg
1376b8e80941Smrg   VkResult result = VK_SUCCESS;
1377b8e80941Smrg
1378b8e80941Smrg   drmModeResPtr mode_res = drmModeGetResources(wsi->fd);
1379b8e80941Smrg   if (!mode_res) {
1380b8e80941Smrg      if (errno == ENOMEM)
1381b8e80941Smrg         result = VK_ERROR_OUT_OF_HOST_MEMORY;
1382b8e80941Smrg      else
1383b8e80941Smrg         result = VK_ERROR_SURFACE_LOST_KHR;
1384b8e80941Smrg      goto bail;
1385b8e80941Smrg   }
1386b8e80941Smrg
1387b8e80941Smrg   drmModeConnectorPtr drm_connector =
1388b8e80941Smrg      drmModeGetConnectorCurrent(wsi->fd, connector->id);
1389b8e80941Smrg
1390b8e80941Smrg   if (!drm_connector) {
1391b8e80941Smrg      if (errno == ENOMEM)
1392b8e80941Smrg         result = VK_ERROR_OUT_OF_HOST_MEMORY;
1393b8e80941Smrg      else
1394b8e80941Smrg         result = VK_ERROR_SURFACE_LOST_KHR;
1395b8e80941Smrg      goto bail_mode_res;
1396b8e80941Smrg   }
1397b8e80941Smrg
1398b8e80941Smrg   /* Pick a CRTC if we don't have one */
1399b8e80941Smrg   if (!connector->crtc_id) {
1400b8e80941Smrg      connector->crtc_id = wsi_display_select_crtc(connector,
1401b8e80941Smrg                                                   mode_res, drm_connector);
1402b8e80941Smrg      if (!connector->crtc_id) {
1403b8e80941Smrg         result = VK_ERROR_SURFACE_LOST_KHR;
1404b8e80941Smrg         goto bail_connector;
1405b8e80941Smrg      }
1406b8e80941Smrg   }
1407b8e80941Smrg
1408b8e80941Smrg   if (connector->current_mode != display_mode) {
1409b8e80941Smrg
1410b8e80941Smrg      /* Find the drm mode corresponding to the requested VkDisplayMode */
1411b8e80941Smrg      drmModeModeInfoPtr drm_mode = NULL;
1412b8e80941Smrg
1413b8e80941Smrg      for (int m = 0; m < drm_connector->count_modes; m++) {
1414b8e80941Smrg         drm_mode = &drm_connector->modes[m];
1415b8e80941Smrg         if (wsi_display_mode_matches_drm(display_mode, drm_mode))
1416b8e80941Smrg            break;
1417b8e80941Smrg         drm_mode = NULL;
1418b8e80941Smrg      }
1419b8e80941Smrg
1420b8e80941Smrg      if (!drm_mode) {
1421b8e80941Smrg         result = VK_ERROR_SURFACE_LOST_KHR;
1422b8e80941Smrg         goto bail_connector;
1423b8e80941Smrg      }
1424b8e80941Smrg
1425b8e80941Smrg      connector->current_mode = display_mode;
1426b8e80941Smrg      connector->current_drm_mode = *drm_mode;
1427b8e80941Smrg   }
1428b8e80941Smrg
1429b8e80941Smrgbail_connector:
1430b8e80941Smrg   drmModeFreeConnector(drm_connector);
1431b8e80941Smrgbail_mode_res:
1432b8e80941Smrg   drmModeFreeResources(mode_res);
1433b8e80941Smrgbail:
1434b8e80941Smrg   return result;
1435b8e80941Smrg
1436b8e80941Smrg}
1437b8e80941Smrg
1438b8e80941Smrgstatic VkResult
1439b8e80941Smrgwsi_display_fence_wait(struct wsi_fence *fence_wsi, uint64_t timeout)
1440b8e80941Smrg{
1441b8e80941Smrg   const struct wsi_device *wsi_device = fence_wsi->wsi_device;
1442b8e80941Smrg   struct wsi_display *wsi =
1443b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
1444b8e80941Smrg   struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
1445b8e80941Smrg
1446b8e80941Smrg   wsi_display_debug("%9lu wait fence %lu %ld\n",
1447b8e80941Smrg                     pthread_self(), fence->sequence,
1448b8e80941Smrg                     (int64_t) (timeout - wsi_common_get_current_time()));
1449b8e80941Smrg   wsi_display_debug_code(uint64_t start_ns = wsi_common_get_current_time());
1450b8e80941Smrg   pthread_mutex_lock(&wsi->wait_mutex);
1451b8e80941Smrg
1452b8e80941Smrg   VkResult result;
1453b8e80941Smrg   int ret = 0;
1454b8e80941Smrg   for (;;) {
1455b8e80941Smrg      if (fence->event_received) {
1456b8e80941Smrg         wsi_display_debug("%9lu fence %lu passed\n",
1457b8e80941Smrg                           pthread_self(), fence->sequence);
1458b8e80941Smrg         result = VK_SUCCESS;
1459b8e80941Smrg         break;
1460b8e80941Smrg      }
1461b8e80941Smrg
1462b8e80941Smrg      if (ret == ETIMEDOUT) {
1463b8e80941Smrg         wsi_display_debug("%9lu fence %lu timeout\n",
1464b8e80941Smrg                           pthread_self(), fence->sequence);
1465b8e80941Smrg         result = VK_TIMEOUT;
1466b8e80941Smrg         break;
1467b8e80941Smrg      }
1468b8e80941Smrg
1469b8e80941Smrg      ret = wsi_display_wait_for_event(wsi, timeout);
1470b8e80941Smrg
1471b8e80941Smrg      if (ret && ret != ETIMEDOUT) {
1472b8e80941Smrg         wsi_display_debug("%9lu fence %lu error\n",
1473b8e80941Smrg                           pthread_self(), fence->sequence);
1474b8e80941Smrg         result = VK_ERROR_DEVICE_LOST;
1475b8e80941Smrg         break;
1476b8e80941Smrg      }
1477b8e80941Smrg   }
1478b8e80941Smrg   pthread_mutex_unlock(&wsi->wait_mutex);
1479b8e80941Smrg   wsi_display_debug("%9lu fence wait %f ms\n",
1480b8e80941Smrg                     pthread_self(),
1481b8e80941Smrg                     ((int64_t) (wsi_common_get_current_time() - start_ns)) /
1482b8e80941Smrg                     1.0e6);
1483b8e80941Smrg   return result;
1484b8e80941Smrg}
1485b8e80941Smrg
1486b8e80941Smrgstatic void
1487b8e80941Smrgwsi_display_fence_check_free(struct wsi_display_fence *fence)
1488b8e80941Smrg{
1489b8e80941Smrg   if (fence->event_received && fence->destroyed)
1490b8e80941Smrg      vk_free(fence->base.alloc, fence);
1491b8e80941Smrg}
1492b8e80941Smrg
1493b8e80941Smrgstatic void wsi_display_fence_event_handler(struct wsi_display_fence *fence)
1494b8e80941Smrg{
1495b8e80941Smrg   fence->event_received = true;
1496b8e80941Smrg   wsi_display_fence_check_free(fence);
1497b8e80941Smrg}
1498b8e80941Smrg
1499b8e80941Smrgstatic void
1500b8e80941Smrgwsi_display_fence_destroy(struct wsi_fence *fence_wsi)
1501b8e80941Smrg{
1502b8e80941Smrg   struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;
1503b8e80941Smrg
1504b8e80941Smrg   assert(!fence->destroyed);
1505b8e80941Smrg   fence->destroyed = true;
1506b8e80941Smrg   wsi_display_fence_check_free(fence);
1507b8e80941Smrg}
1508b8e80941Smrg
1509b8e80941Smrgstatic struct wsi_display_fence *
1510b8e80941Smrgwsi_display_fence_alloc(VkDevice device,
1511b8e80941Smrg                        const struct wsi_device *wsi_device,
1512b8e80941Smrg                        VkDisplayKHR display,
1513b8e80941Smrg                        const VkAllocationCallbacks *allocator)
1514b8e80941Smrg{
1515b8e80941Smrg   struct wsi_display *wsi =
1516b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
1517b8e80941Smrg   struct wsi_display_fence *fence =
1518b8e80941Smrg      vk_zalloc2(wsi->alloc, allocator, sizeof (*fence),
1519b8e80941Smrg                8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
1520b8e80941Smrg
1521b8e80941Smrg   if (!fence)
1522b8e80941Smrg      return NULL;
1523b8e80941Smrg
1524b8e80941Smrg   fence->base.device = device;
1525b8e80941Smrg   fence->base.display = display;
1526b8e80941Smrg   fence->base.wsi_device = wsi_device;
1527b8e80941Smrg   fence->base.alloc = allocator ? allocator : wsi->alloc;
1528b8e80941Smrg   fence->base.wait = wsi_display_fence_wait;
1529b8e80941Smrg   fence->base.destroy = wsi_display_fence_destroy;
1530b8e80941Smrg   fence->event_received = false;
1531b8e80941Smrg   fence->destroyed = false;
1532b8e80941Smrg   fence->sequence = ++fence_sequence;
1533b8e80941Smrg   return fence;
1534b8e80941Smrg}
1535b8e80941Smrg
1536b8e80941Smrgstatic VkResult
1537b8e80941Smrgwsi_register_vblank_event(struct wsi_display_fence *fence,
1538b8e80941Smrg                          const struct wsi_device *wsi_device,
1539b8e80941Smrg                          VkDisplayKHR display,
1540b8e80941Smrg                          uint32_t flags,
1541b8e80941Smrg                          uint64_t frame_requested,
1542b8e80941Smrg                          uint64_t *frame_queued)
1543b8e80941Smrg{
1544b8e80941Smrg   struct wsi_display *wsi =
1545b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
1546b8e80941Smrg   struct wsi_display_connector *connector =
1547b8e80941Smrg      wsi_display_connector_from_handle(display);
1548b8e80941Smrg
1549b8e80941Smrg   if (wsi->fd < 0)
1550b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
1551b8e80941Smrg
1552b8e80941Smrg   for (;;) {
1553b8e80941Smrg      int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id,
1554b8e80941Smrg                                     flags,
1555b8e80941Smrg                                     frame_requested,
1556b8e80941Smrg                                     frame_queued,
1557b8e80941Smrg                                     (uintptr_t) fence);
1558b8e80941Smrg
1559b8e80941Smrg      if (!ret)
1560b8e80941Smrg         return VK_SUCCESS;
1561b8e80941Smrg
1562b8e80941Smrg      if (errno != ENOMEM) {
1563b8e80941Smrg
1564b8e80941Smrg         /* Something unexpected happened. Pause for a moment so the
1565b8e80941Smrg          * application doesn't just spin and then return a failure indication
1566b8e80941Smrg          */
1567b8e80941Smrg
1568b8e80941Smrg         wsi_display_debug("queue vblank event %lu failed\n", fence->sequence);
1569b8e80941Smrg         struct timespec delay = {
1570b8e80941Smrg            .tv_sec = 0,
1571b8e80941Smrg            .tv_nsec = 100000000ull,
1572b8e80941Smrg         };
1573b8e80941Smrg         nanosleep(&delay, NULL);
1574b8e80941Smrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
1575b8e80941Smrg      }
1576b8e80941Smrg
1577b8e80941Smrg      /* The kernel event queue is full. Wait for some events to be
1578b8e80941Smrg       * processed and try again
1579b8e80941Smrg       */
1580b8e80941Smrg
1581b8e80941Smrg      pthread_mutex_lock(&wsi->wait_mutex);
1582b8e80941Smrg      ret = wsi_display_wait_for_event(wsi, wsi_rel_to_abs_time(100000000ull));
1583b8e80941Smrg      pthread_mutex_unlock(&wsi->wait_mutex);
1584b8e80941Smrg
1585b8e80941Smrg      if (ret) {
1586b8e80941Smrg         wsi_display_debug("vblank queue full, event wait failed\n");
1587b8e80941Smrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
1588b8e80941Smrg      }
1589b8e80941Smrg   }
1590b8e80941Smrg}
1591b8e80941Smrg
1592b8e80941Smrg/*
1593b8e80941Smrg * Check to see if the kernel has no flip queued and if there's an image
1594b8e80941Smrg * waiting to be displayed.
1595b8e80941Smrg */
1596b8e80941Smrgstatic VkResult
1597b8e80941Smrg_wsi_display_queue_next(struct wsi_swapchain *drv_chain)
1598b8e80941Smrg{
1599b8e80941Smrg   struct wsi_display_swapchain *chain =
1600b8e80941Smrg      (struct wsi_display_swapchain *) drv_chain;
1601b8e80941Smrg   struct wsi_display *wsi = chain->wsi;
1602b8e80941Smrg   VkIcdSurfaceDisplay *surface = chain->surface;
1603b8e80941Smrg   wsi_display_mode *display_mode =
1604b8e80941Smrg      wsi_display_mode_from_handle(surface->displayMode);
1605b8e80941Smrg   wsi_display_connector *connector = display_mode->connector;
1606b8e80941Smrg
1607b8e80941Smrg   if (wsi->fd < 0)
1608b8e80941Smrg      return VK_ERROR_SURFACE_LOST_KHR;
1609b8e80941Smrg
1610b8e80941Smrg   if (display_mode != connector->current_mode)
1611b8e80941Smrg      connector->active = false;
1612b8e80941Smrg
1613b8e80941Smrg   for (;;) {
1614b8e80941Smrg
1615b8e80941Smrg      /* Check to see if there is an image to display, or if some image is
1616b8e80941Smrg       * already queued */
1617b8e80941Smrg
1618b8e80941Smrg      struct wsi_display_image *image = NULL;
1619b8e80941Smrg
1620b8e80941Smrg      for (uint32_t i = 0; i < chain->base.image_count; i++) {
1621b8e80941Smrg         struct wsi_display_image *tmp_image = &chain->images[i];
1622b8e80941Smrg
1623b8e80941Smrg         switch (tmp_image->state) {
1624b8e80941Smrg         case WSI_IMAGE_FLIPPING:
1625b8e80941Smrg            /* already flipping, don't send another to the kernel yet */
1626b8e80941Smrg            return VK_SUCCESS;
1627b8e80941Smrg         case WSI_IMAGE_QUEUED:
1628b8e80941Smrg            /* find the oldest queued */
1629b8e80941Smrg            if (!image || tmp_image->flip_sequence < image->flip_sequence)
1630b8e80941Smrg               image = tmp_image;
1631b8e80941Smrg            break;
1632b8e80941Smrg         default:
1633b8e80941Smrg            break;
1634b8e80941Smrg         }
1635b8e80941Smrg      }
1636b8e80941Smrg
1637b8e80941Smrg      if (!image)
1638b8e80941Smrg         return VK_SUCCESS;
1639b8e80941Smrg
1640b8e80941Smrg      int ret;
1641b8e80941Smrg      if (connector->active) {
1642b8e80941Smrg         ret = drmModePageFlip(wsi->fd, connector->crtc_id, image->fb_id,
1643b8e80941Smrg                                   DRM_MODE_PAGE_FLIP_EVENT, image);
1644b8e80941Smrg         if (ret == 0) {
1645b8e80941Smrg            image->state = WSI_IMAGE_FLIPPING;
1646b8e80941Smrg            return VK_SUCCESS;
1647b8e80941Smrg         }
1648b8e80941Smrg         wsi_display_debug("page flip err %d %s\n", ret, strerror(-ret));
1649b8e80941Smrg      } else {
1650b8e80941Smrg         ret = -EINVAL;
1651b8e80941Smrg      }
1652b8e80941Smrg
1653b8e80941Smrg      if (ret == -EINVAL) {
1654b8e80941Smrg         VkResult result = wsi_display_setup_connector(connector, display_mode);
1655b8e80941Smrg
1656b8e80941Smrg         if (result != VK_SUCCESS) {
1657b8e80941Smrg            image->state = WSI_IMAGE_IDLE;
1658b8e80941Smrg            return result;
1659b8e80941Smrg         }
1660b8e80941Smrg
1661b8e80941Smrg         /* XXX allow setting of position */
1662b8e80941Smrg         ret = drmModeSetCrtc(wsi->fd, connector->crtc_id,
1663b8e80941Smrg                              image->fb_id, 0, 0,
1664b8e80941Smrg                              &connector->id, 1,
1665b8e80941Smrg                              &connector->current_drm_mode);
1666b8e80941Smrg         if (ret == 0) {
1667b8e80941Smrg            /* Assume that the mode set is synchronous and that any
1668b8e80941Smrg             * previous image is now idle.
1669b8e80941Smrg             */
1670b8e80941Smrg            image->state = WSI_IMAGE_DISPLAYING;
1671b8e80941Smrg            wsi_display_idle_old_displaying(image);
1672b8e80941Smrg            connector->active = true;
1673b8e80941Smrg            return VK_SUCCESS;
1674b8e80941Smrg         }
1675b8e80941Smrg      }
1676b8e80941Smrg
1677b8e80941Smrg      if (ret != -EACCES) {
1678b8e80941Smrg         connector->active = false;
1679b8e80941Smrg         image->state = WSI_IMAGE_IDLE;
1680b8e80941Smrg         return VK_ERROR_SURFACE_LOST_KHR;
1681b8e80941Smrg      }
1682b8e80941Smrg
1683b8e80941Smrg      /* Some other VT is currently active. Sit here waiting for
1684b8e80941Smrg       * our VT to become active again by polling once a second
1685b8e80941Smrg       */
1686b8e80941Smrg      usleep(1000 * 1000);
1687b8e80941Smrg      connector->active = false;
1688b8e80941Smrg   }
1689b8e80941Smrg}
1690b8e80941Smrg
1691b8e80941Smrgstatic VkResult
1692b8e80941Smrgwsi_display_queue_present(struct wsi_swapchain *drv_chain,
1693b8e80941Smrg                          uint32_t image_index,
1694b8e80941Smrg                          const VkPresentRegionKHR *damage)
1695b8e80941Smrg{
1696b8e80941Smrg   struct wsi_display_swapchain *chain =
1697b8e80941Smrg      (struct wsi_display_swapchain *) drv_chain;
1698b8e80941Smrg   struct wsi_display *wsi = chain->wsi;
1699b8e80941Smrg   struct wsi_display_image *image = &chain->images[image_index];
1700b8e80941Smrg   VkResult result;
1701b8e80941Smrg
1702b8e80941Smrg   /* Bail early if the swapchain is broken */
1703b8e80941Smrg   if (chain->status != VK_SUCCESS)
1704b8e80941Smrg      return chain->status;
1705b8e80941Smrg
1706b8e80941Smrg   assert(image->state == WSI_IMAGE_DRAWING);
1707b8e80941Smrg   wsi_display_debug("present %d\n", image_index);
1708b8e80941Smrg
1709b8e80941Smrg   pthread_mutex_lock(&wsi->wait_mutex);
1710b8e80941Smrg
1711b8e80941Smrg   image->flip_sequence = ++chain->flip_sequence;
1712b8e80941Smrg   image->state = WSI_IMAGE_QUEUED;
1713b8e80941Smrg
1714b8e80941Smrg   result = _wsi_display_queue_next(drv_chain);
1715b8e80941Smrg   if (result != VK_SUCCESS)
1716b8e80941Smrg      chain->status = result;
1717b8e80941Smrg
1718b8e80941Smrg   pthread_mutex_unlock(&wsi->wait_mutex);
1719b8e80941Smrg
1720b8e80941Smrg   if (result != VK_SUCCESS)
1721b8e80941Smrg      return result;
1722b8e80941Smrg
1723b8e80941Smrg   return chain->status;
1724b8e80941Smrg}
1725b8e80941Smrg
1726b8e80941Smrgstatic VkResult
1727b8e80941Smrgwsi_display_surface_create_swapchain(
1728b8e80941Smrg   VkIcdSurfaceBase *icd_surface,
1729b8e80941Smrg   VkDevice device,
1730b8e80941Smrg   struct wsi_device *wsi_device,
1731b8e80941Smrg   const VkSwapchainCreateInfoKHR *create_info,
1732b8e80941Smrg   const VkAllocationCallbacks *allocator,
1733b8e80941Smrg   struct wsi_swapchain **swapchain_out)
1734b8e80941Smrg{
1735b8e80941Smrg   struct wsi_display *wsi =
1736b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
1737b8e80941Smrg
1738b8e80941Smrg   assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
1739b8e80941Smrg
1740b8e80941Smrg   const unsigned num_images = create_info->minImageCount;
1741b8e80941Smrg   struct wsi_display_swapchain *chain =
1742b8e80941Smrg      vk_zalloc(allocator,
1743b8e80941Smrg                sizeof(*chain) + num_images * sizeof(chain->images[0]),
1744b8e80941Smrg                8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
1745b8e80941Smrg
1746b8e80941Smrg   if (chain == NULL)
1747b8e80941Smrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
1748b8e80941Smrg
1749b8e80941Smrg   VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device,
1750b8e80941Smrg                                        create_info, allocator);
1751b8e80941Smrg   if (result != VK_SUCCESS) {
1752b8e80941Smrg      vk_free(allocator, chain);
1753b8e80941Smrg      return result;
1754b8e80941Smrg   }
1755b8e80941Smrg
1756b8e80941Smrg   chain->base.destroy = wsi_display_swapchain_destroy;
1757b8e80941Smrg   chain->base.get_wsi_image = wsi_display_get_wsi_image;
1758b8e80941Smrg   chain->base.acquire_next_image = wsi_display_acquire_next_image;
1759b8e80941Smrg   chain->base.queue_present = wsi_display_queue_present;
1760b8e80941Smrg   chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);
1761b8e80941Smrg   chain->base.image_count = num_images;
1762b8e80941Smrg
1763b8e80941Smrg   chain->wsi = wsi;
1764b8e80941Smrg   chain->status = VK_SUCCESS;
1765b8e80941Smrg
1766b8e80941Smrg   chain->surface = (VkIcdSurfaceDisplay *) icd_surface;
1767b8e80941Smrg
1768b8e80941Smrg   for (uint32_t image = 0; image < chain->base.image_count; image++) {
1769b8e80941Smrg      result = wsi_display_image_init(device, &chain->base,
1770b8e80941Smrg                                      create_info, allocator,
1771b8e80941Smrg                                      &chain->images[image]);
1772b8e80941Smrg      if (result != VK_SUCCESS) {
1773b8e80941Smrg         while (image > 0) {
1774b8e80941Smrg            --image;
1775b8e80941Smrg            wsi_display_image_finish(&chain->base, allocator,
1776b8e80941Smrg                                     &chain->images[image]);
1777b8e80941Smrg         }
1778b8e80941Smrg         vk_free(allocator, chain);
1779b8e80941Smrg         goto fail_init_images;
1780b8e80941Smrg      }
1781b8e80941Smrg   }
1782b8e80941Smrg
1783b8e80941Smrg   *swapchain_out = &chain->base;
1784b8e80941Smrg
1785b8e80941Smrg   return VK_SUCCESS;
1786b8e80941Smrg
1787b8e80941Smrgfail_init_images:
1788b8e80941Smrg   return result;
1789b8e80941Smrg}
1790b8e80941Smrg
1791b8e80941Smrgstatic bool
1792b8e80941Smrgwsi_init_pthread_cond_monotonic(pthread_cond_t *cond)
1793b8e80941Smrg{
1794b8e80941Smrg   pthread_condattr_t condattr;
1795b8e80941Smrg   bool ret = false;
1796b8e80941Smrg
1797b8e80941Smrg   if (pthread_condattr_init(&condattr) != 0)
1798b8e80941Smrg      goto fail_attr_init;
1799b8e80941Smrg
1800b8e80941Smrg   if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC) != 0)
1801b8e80941Smrg      goto fail_attr_set;
1802b8e80941Smrg
1803b8e80941Smrg   if (pthread_cond_init(cond, &condattr) != 0)
1804b8e80941Smrg      goto fail_cond_init;
1805b8e80941Smrg
1806b8e80941Smrg   ret = true;
1807b8e80941Smrg
1808b8e80941Smrgfail_cond_init:
1809b8e80941Smrgfail_attr_set:
1810b8e80941Smrg   pthread_condattr_destroy(&condattr);
1811b8e80941Smrgfail_attr_init:
1812b8e80941Smrg   return ret;
1813b8e80941Smrg}
1814b8e80941Smrg
1815b8e80941Smrg
1816b8e80941Smrg/*
1817b8e80941Smrg * Local version fo the libdrm helper. Added to avoid depending on bleeding
1818b8e80941Smrg * edge version of the library.
1819b8e80941Smrg */
1820b8e80941Smrgstatic int
1821b8e80941Smrglocal_drmIsMaster(int fd)
1822b8e80941Smrg{
1823b8e80941Smrg   /* Detect master by attempting something that requires master.
1824b8e80941Smrg    *
1825b8e80941Smrg    * Authenticating magic tokens requires master and 0 is an
1826b8e80941Smrg    * internal kernel detail which we could use. Attempting this on
1827b8e80941Smrg    * a master fd would fail therefore fail with EINVAL because 0
1828b8e80941Smrg    * is invalid.
1829b8e80941Smrg    *
1830b8e80941Smrg    * A non-master fd will fail with EACCES, as the kernel checks
1831b8e80941Smrg    * for master before attempting to do anything else.
1832b8e80941Smrg    *
1833b8e80941Smrg    * Since we don't want to leak implementation details, use
1834b8e80941Smrg    * EACCES.
1835b8e80941Smrg    */
1836b8e80941Smrg   return drmAuthMagic(fd, 0) != -EACCES;
1837b8e80941Smrg}
1838b8e80941Smrg
1839b8e80941SmrgVkResult
1840b8e80941Smrgwsi_display_init_wsi(struct wsi_device *wsi_device,
1841b8e80941Smrg                     const VkAllocationCallbacks *alloc,
1842b8e80941Smrg                     int display_fd)
1843b8e80941Smrg{
1844b8e80941Smrg   struct wsi_display *wsi = vk_zalloc(alloc, sizeof(*wsi), 8,
1845b8e80941Smrg                                       VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
1846b8e80941Smrg   VkResult result;
1847b8e80941Smrg
1848b8e80941Smrg   if (!wsi) {
1849b8e80941Smrg      result = VK_ERROR_OUT_OF_HOST_MEMORY;
1850b8e80941Smrg      goto fail;
1851b8e80941Smrg   }
1852b8e80941Smrg
1853b8e80941Smrg   wsi->fd = display_fd;
1854b8e80941Smrg   if (wsi->fd != -1 && !local_drmIsMaster(wsi->fd))
1855b8e80941Smrg      wsi->fd = -1;
1856b8e80941Smrg
1857b8e80941Smrg   wsi->alloc = alloc;
1858b8e80941Smrg
1859b8e80941Smrg   list_inithead(&wsi->connectors);
1860b8e80941Smrg
1861b8e80941Smrg   int ret = pthread_mutex_init(&wsi->wait_mutex, NULL);
1862b8e80941Smrg   if (ret) {
1863b8e80941Smrg      result = VK_ERROR_OUT_OF_HOST_MEMORY;
1864b8e80941Smrg      goto fail_mutex;
1865b8e80941Smrg   }
1866b8e80941Smrg
1867b8e80941Smrg   if (!wsi_init_pthread_cond_monotonic(&wsi->wait_cond)) {
1868b8e80941Smrg      result = VK_ERROR_OUT_OF_HOST_MEMORY;
1869b8e80941Smrg      goto fail_cond;
1870b8e80941Smrg   }
1871b8e80941Smrg
1872b8e80941Smrg   wsi->base.get_support = wsi_display_surface_get_support;
1873b8e80941Smrg   wsi->base.get_capabilities2 = wsi_display_surface_get_capabilities2;
1874b8e80941Smrg   wsi->base.get_formats = wsi_display_surface_get_formats;
1875b8e80941Smrg   wsi->base.get_formats2 = wsi_display_surface_get_formats2;
1876b8e80941Smrg   wsi->base.get_present_modes = wsi_display_surface_get_present_modes;
1877b8e80941Smrg   wsi->base.get_present_rectangles = wsi_display_surface_get_present_rectangles;
1878b8e80941Smrg   wsi->base.create_swapchain = wsi_display_surface_create_swapchain;
1879b8e80941Smrg
1880b8e80941Smrg   wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base;
1881b8e80941Smrg
1882b8e80941Smrg   return VK_SUCCESS;
1883b8e80941Smrg
1884b8e80941Smrgfail_cond:
1885b8e80941Smrg   pthread_mutex_destroy(&wsi->wait_mutex);
1886b8e80941Smrgfail_mutex:
1887b8e80941Smrg   vk_free(alloc, wsi);
1888b8e80941Smrgfail:
1889b8e80941Smrg   return result;
1890b8e80941Smrg}
1891b8e80941Smrg
1892b8e80941Smrgvoid
1893b8e80941Smrgwsi_display_finish_wsi(struct wsi_device *wsi_device,
1894b8e80941Smrg                       const VkAllocationCallbacks *alloc)
1895b8e80941Smrg{
1896b8e80941Smrg   struct wsi_display *wsi =
1897b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
1898b8e80941Smrg
1899b8e80941Smrg   if (wsi) {
1900b8e80941Smrg      wsi_for_each_connector(connector, wsi) {
1901b8e80941Smrg         wsi_for_each_display_mode(mode, connector) {
1902b8e80941Smrg            vk_free(wsi->alloc, mode);
1903b8e80941Smrg         }
1904b8e80941Smrg         vk_free(wsi->alloc, connector);
1905b8e80941Smrg      }
1906b8e80941Smrg
1907b8e80941Smrg      pthread_mutex_lock(&wsi->wait_mutex);
1908b8e80941Smrg      if (wsi->wait_thread) {
1909b8e80941Smrg         pthread_cancel(wsi->wait_thread);
1910b8e80941Smrg         pthread_join(wsi->wait_thread, NULL);
1911b8e80941Smrg      }
1912b8e80941Smrg      pthread_mutex_unlock(&wsi->wait_mutex);
1913b8e80941Smrg      pthread_mutex_destroy(&wsi->wait_mutex);
1914b8e80941Smrg      pthread_cond_destroy(&wsi->wait_cond);
1915b8e80941Smrg
1916b8e80941Smrg      vk_free(alloc, wsi);
1917b8e80941Smrg   }
1918b8e80941Smrg}
1919b8e80941Smrg
1920b8e80941Smrg/*
1921b8e80941Smrg * Implement vkReleaseDisplay
1922b8e80941Smrg */
1923b8e80941SmrgVkResult
1924b8e80941Smrgwsi_release_display(VkPhysicalDevice            physical_device,
1925b8e80941Smrg                    struct wsi_device           *wsi_device,
1926b8e80941Smrg                    VkDisplayKHR                display)
1927b8e80941Smrg{
1928b8e80941Smrg   struct wsi_display *wsi =
1929b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
1930b8e80941Smrg
1931b8e80941Smrg   if (wsi->fd >= 0) {
1932b8e80941Smrg      close(wsi->fd);
1933b8e80941Smrg      wsi->fd = -1;
1934b8e80941Smrg   }
1935b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
1936b8e80941Smrg   wsi_display_connector_from_handle(display)->output = None;
1937b8e80941Smrg#endif
1938b8e80941Smrg
1939b8e80941Smrg   return VK_SUCCESS;
1940b8e80941Smrg}
1941b8e80941Smrg
1942b8e80941Smrg#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
1943b8e80941Smrg
1944b8e80941Smrgstatic struct wsi_display_connector *
1945b8e80941Smrgwsi_display_find_output(struct wsi_device *wsi_device,
1946b8e80941Smrg                        xcb_randr_output_t output)
1947b8e80941Smrg{
1948b8e80941Smrg   struct wsi_display *wsi =
1949b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
1950b8e80941Smrg
1951b8e80941Smrg   wsi_for_each_connector(connector, wsi) {
1952b8e80941Smrg      if (connector->output == output)
1953b8e80941Smrg         return connector;
1954b8e80941Smrg   }
1955b8e80941Smrg
1956b8e80941Smrg   return NULL;
1957b8e80941Smrg}
1958b8e80941Smrg
1959b8e80941Smrg/*
1960b8e80941Smrg * Given a RandR output, find the associated kernel connector_id by
1961b8e80941Smrg * looking at the CONNECTOR_ID property provided by the X server
1962b8e80941Smrg */
1963b8e80941Smrg
1964b8e80941Smrgstatic uint32_t
1965b8e80941Smrgwsi_display_output_to_connector_id(xcb_connection_t *connection,
1966b8e80941Smrg                                   xcb_atom_t *connector_id_atom_p,
1967b8e80941Smrg                                   xcb_randr_output_t output)
1968b8e80941Smrg{
1969b8e80941Smrg   uint32_t connector_id = 0;
1970b8e80941Smrg   xcb_atom_t connector_id_atom = *connector_id_atom_p;
1971b8e80941Smrg
1972b8e80941Smrg   if (connector_id_atom == 0) {
1973b8e80941Smrg   /* Go dig out the CONNECTOR_ID property */
1974b8e80941Smrg      xcb_intern_atom_cookie_t ia_c = xcb_intern_atom(connection,
1975b8e80941Smrg                                                          true,
1976b8e80941Smrg                                                          12,
1977b8e80941Smrg                                                          "CONNECTOR_ID");
1978b8e80941Smrg      xcb_intern_atom_reply_t *ia_r = xcb_intern_atom_reply(connection,
1979b8e80941Smrg                                                                 ia_c,
1980b8e80941Smrg                                                                 NULL);
1981b8e80941Smrg      if (ia_r) {
1982b8e80941Smrg         *connector_id_atom_p = connector_id_atom = ia_r->atom;
1983b8e80941Smrg         free(ia_r);
1984b8e80941Smrg      }
1985b8e80941Smrg   }
1986b8e80941Smrg
1987b8e80941Smrg   /* If there's an CONNECTOR_ID atom in the server, then there may be a
1988b8e80941Smrg    * CONNECTOR_ID property. Otherwise, there will not be and we don't even
1989b8e80941Smrg    * need to bother.
1990b8e80941Smrg    */
1991b8e80941Smrg   if (connector_id_atom) {
1992b8e80941Smrg
1993b8e80941Smrg      xcb_randr_query_version_cookie_t qv_c =
1994b8e80941Smrg         xcb_randr_query_version(connection, 1, 6);
1995b8e80941Smrg      xcb_randr_get_output_property_cookie_t gop_c =
1996b8e80941Smrg         xcb_randr_get_output_property(connection,
1997b8e80941Smrg                                       output,
1998b8e80941Smrg                                       connector_id_atom,
1999b8e80941Smrg                                       0,
2000b8e80941Smrg                                       0,
2001b8e80941Smrg                                       0xffffffffUL,
2002b8e80941Smrg                                       0,
2003b8e80941Smrg                                       0);
2004b8e80941Smrg      xcb_randr_query_version_reply_t *qv_r =
2005b8e80941Smrg         xcb_randr_query_version_reply(connection, qv_c, NULL);
2006b8e80941Smrg      free(qv_r);
2007b8e80941Smrg      xcb_randr_get_output_property_reply_t *gop_r =
2008b8e80941Smrg         xcb_randr_get_output_property_reply(connection, gop_c, NULL);
2009b8e80941Smrg      if (gop_r) {
2010b8e80941Smrg         if (gop_r->num_items == 1 && gop_r->format == 32)
2011b8e80941Smrg            memcpy(&connector_id, xcb_randr_get_output_property_data(gop_r), 4);
2012b8e80941Smrg         free(gop_r);
2013b8e80941Smrg      }
2014b8e80941Smrg   }
2015b8e80941Smrg   return connector_id;
2016b8e80941Smrg}
2017b8e80941Smrg
2018b8e80941Smrgstatic bool
2019b8e80941Smrgwsi_display_check_randr_version(xcb_connection_t *connection)
2020b8e80941Smrg{
2021b8e80941Smrg   xcb_randr_query_version_cookie_t qv_c =
2022b8e80941Smrg      xcb_randr_query_version(connection, 1, 6);
2023b8e80941Smrg   xcb_randr_query_version_reply_t *qv_r =
2024b8e80941Smrg      xcb_randr_query_version_reply(connection, qv_c, NULL);
2025b8e80941Smrg   bool ret = false;
2026b8e80941Smrg
2027b8e80941Smrg   if (!qv_r)
2028b8e80941Smrg      return false;
2029b8e80941Smrg
2030b8e80941Smrg   /* Check for version 1.6 or newer */
2031b8e80941Smrg   ret = (qv_r->major_version > 1 ||
2032b8e80941Smrg          (qv_r->major_version == 1 && qv_r->minor_version >= 6));
2033b8e80941Smrg
2034b8e80941Smrg   free(qv_r);
2035b8e80941Smrg   return ret;
2036b8e80941Smrg}
2037b8e80941Smrg
2038b8e80941Smrg/*
2039b8e80941Smrg * Given a kernel connector id, find the associated RandR output using the
2040b8e80941Smrg * CONNECTOR_ID property
2041b8e80941Smrg */
2042b8e80941Smrg
2043b8e80941Smrgstatic xcb_randr_output_t
2044b8e80941Smrgwsi_display_connector_id_to_output(xcb_connection_t *connection,
2045b8e80941Smrg                                   uint32_t connector_id)
2046b8e80941Smrg{
2047b8e80941Smrg   if (!wsi_display_check_randr_version(connection))
2048b8e80941Smrg      return 0;
2049b8e80941Smrg
2050b8e80941Smrg   const xcb_setup_t *setup = xcb_get_setup(connection);
2051b8e80941Smrg
2052b8e80941Smrg   xcb_atom_t connector_id_atom = 0;
2053b8e80941Smrg   xcb_randr_output_t output = 0;
2054b8e80941Smrg
2055b8e80941Smrg   /* Search all of the screens for the provided output */
2056b8e80941Smrg   xcb_screen_iterator_t iter;
2057b8e80941Smrg   for (iter = xcb_setup_roots_iterator(setup);
2058b8e80941Smrg        output == 0 && iter.rem;
2059b8e80941Smrg        xcb_screen_next(&iter))
2060b8e80941Smrg   {
2061b8e80941Smrg      xcb_randr_get_screen_resources_cookie_t gsr_c =
2062b8e80941Smrg         xcb_randr_get_screen_resources(connection, iter.data->root);
2063b8e80941Smrg      xcb_randr_get_screen_resources_reply_t *gsr_r =
2064b8e80941Smrg         xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
2065b8e80941Smrg
2066b8e80941Smrg      if (!gsr_r)
2067b8e80941Smrg         return 0;
2068b8e80941Smrg
2069b8e80941Smrg      xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
2070b8e80941Smrg      int o;
2071b8e80941Smrg
2072b8e80941Smrg      for (o = 0; o < gsr_r->num_outputs; o++) {
2073b8e80941Smrg         if (wsi_display_output_to_connector_id(connection,
2074b8e80941Smrg                                                &connector_id_atom, ro[o])
2075b8e80941Smrg             == connector_id)
2076b8e80941Smrg         {
2077b8e80941Smrg            output = ro[o];
2078b8e80941Smrg            break;
2079b8e80941Smrg         }
2080b8e80941Smrg      }
2081b8e80941Smrg      free(gsr_r);
2082b8e80941Smrg   }
2083b8e80941Smrg   return output;
2084b8e80941Smrg}
2085b8e80941Smrg
2086b8e80941Smrg/*
2087b8e80941Smrg * Given a RandR output, find out which screen it's associated with
2088b8e80941Smrg */
2089b8e80941Smrgstatic xcb_window_t
2090b8e80941Smrgwsi_display_output_to_root(xcb_connection_t *connection,
2091b8e80941Smrg                           xcb_randr_output_t output)
2092b8e80941Smrg{
2093b8e80941Smrg   if (!wsi_display_check_randr_version(connection))
2094b8e80941Smrg      return 0;
2095b8e80941Smrg
2096b8e80941Smrg   const xcb_setup_t *setup = xcb_get_setup(connection);
2097b8e80941Smrg   xcb_window_t root = 0;
2098b8e80941Smrg
2099b8e80941Smrg   /* Search all of the screens for the provided output */
2100b8e80941Smrg   for (xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
2101b8e80941Smrg        root == 0 && iter.rem;
2102b8e80941Smrg        xcb_screen_next(&iter))
2103b8e80941Smrg   {
2104b8e80941Smrg      xcb_randr_get_screen_resources_cookie_t gsr_c =
2105b8e80941Smrg         xcb_randr_get_screen_resources(connection, iter.data->root);
2106b8e80941Smrg      xcb_randr_get_screen_resources_reply_t *gsr_r =
2107b8e80941Smrg         xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
2108b8e80941Smrg
2109b8e80941Smrg      if (!gsr_r)
2110b8e80941Smrg         return 0;
2111b8e80941Smrg
2112b8e80941Smrg      xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r);
2113b8e80941Smrg
2114b8e80941Smrg      for (int o = 0; o < gsr_r->num_outputs; o++) {
2115b8e80941Smrg         if (ro[o] == output) {
2116b8e80941Smrg            root = iter.data->root;
2117b8e80941Smrg            break;
2118b8e80941Smrg         }
2119b8e80941Smrg      }
2120b8e80941Smrg      free(gsr_r);
2121b8e80941Smrg   }
2122b8e80941Smrg   return root;
2123b8e80941Smrg}
2124b8e80941Smrg
2125b8e80941Smrgstatic bool
2126b8e80941Smrgwsi_display_mode_matches_x(struct wsi_display_mode *wsi,
2127b8e80941Smrg                           xcb_randr_mode_info_t *xcb)
2128b8e80941Smrg{
2129b8e80941Smrg   return wsi->clock == (xcb->dot_clock + 500) / 1000 &&
2130b8e80941Smrg      wsi->hdisplay == xcb->width &&
2131b8e80941Smrg      wsi->hsync_start == xcb->hsync_start &&
2132b8e80941Smrg      wsi->hsync_end == xcb->hsync_end &&
2133b8e80941Smrg      wsi->htotal == xcb->htotal &&
2134b8e80941Smrg      wsi->hskew == xcb->hskew &&
2135b8e80941Smrg      wsi->vdisplay == xcb->height &&
2136b8e80941Smrg      wsi->vsync_start == xcb->vsync_start &&
2137b8e80941Smrg      wsi->vsync_end == xcb->vsync_end &&
2138b8e80941Smrg      wsi->vtotal == xcb->vtotal &&
2139b8e80941Smrg      wsi->vscan <= 1 &&
2140b8e80941Smrg      wsi->flags == xcb->mode_flags;
2141b8e80941Smrg}
2142b8e80941Smrg
2143b8e80941Smrgstatic struct wsi_display_mode *
2144b8e80941Smrgwsi_display_find_x_mode(struct wsi_device *wsi_device,
2145b8e80941Smrg                        struct wsi_display_connector *connector,
2146b8e80941Smrg                        xcb_randr_mode_info_t *mode)
2147b8e80941Smrg{
2148b8e80941Smrg   wsi_for_each_display_mode(display_mode, connector) {
2149b8e80941Smrg      if (wsi_display_mode_matches_x(display_mode, mode))
2150b8e80941Smrg         return display_mode;
2151b8e80941Smrg   }
2152b8e80941Smrg   return NULL;
2153b8e80941Smrg}
2154b8e80941Smrg
2155b8e80941Smrgstatic VkResult
2156b8e80941Smrgwsi_display_register_x_mode(struct wsi_device *wsi_device,
2157b8e80941Smrg                            struct wsi_display_connector *connector,
2158b8e80941Smrg                            xcb_randr_mode_info_t *x_mode,
2159b8e80941Smrg                            bool preferred)
2160b8e80941Smrg{
2161b8e80941Smrg   struct wsi_display *wsi =
2162b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
2163b8e80941Smrg   struct wsi_display_mode *display_mode =
2164b8e80941Smrg      wsi_display_find_x_mode(wsi_device, connector, x_mode);
2165b8e80941Smrg
2166b8e80941Smrg   if (display_mode) {
2167b8e80941Smrg      display_mode->valid = true;
2168b8e80941Smrg      return VK_SUCCESS;
2169b8e80941Smrg   }
2170b8e80941Smrg
2171b8e80941Smrg   display_mode = vk_zalloc(wsi->alloc, sizeof (struct wsi_display_mode),
2172b8e80941Smrg                            8, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
2173b8e80941Smrg   if (!display_mode)
2174b8e80941Smrg      return VK_ERROR_OUT_OF_HOST_MEMORY;
2175b8e80941Smrg
2176b8e80941Smrg   display_mode->connector = connector;
2177b8e80941Smrg   display_mode->valid = true;
2178b8e80941Smrg   display_mode->preferred = preferred;
2179b8e80941Smrg   display_mode->clock = (x_mode->dot_clock + 500) / 1000; /* kHz */
2180b8e80941Smrg   display_mode->hdisplay = x_mode->width;
2181b8e80941Smrg   display_mode->hsync_start = x_mode->hsync_start;
2182b8e80941Smrg   display_mode->hsync_end = x_mode->hsync_end;
2183b8e80941Smrg   display_mode->htotal = x_mode->htotal;
2184b8e80941Smrg   display_mode->hskew = x_mode->hskew;
2185b8e80941Smrg   display_mode->vdisplay = x_mode->height;
2186b8e80941Smrg   display_mode->vsync_start = x_mode->vsync_start;
2187b8e80941Smrg   display_mode->vsync_end = x_mode->vsync_end;
2188b8e80941Smrg   display_mode->vtotal = x_mode->vtotal;
2189b8e80941Smrg   display_mode->vscan = 0;
2190b8e80941Smrg   display_mode->flags = x_mode->mode_flags;
2191b8e80941Smrg
2192b8e80941Smrg   list_addtail(&display_mode->list, &connector->display_modes);
2193b8e80941Smrg   return VK_SUCCESS;
2194b8e80941Smrg}
2195b8e80941Smrg
2196b8e80941Smrgstatic struct wsi_display_connector *
2197b8e80941Smrgwsi_display_get_output(struct wsi_device *wsi_device,
2198b8e80941Smrg                       xcb_connection_t *connection,
2199b8e80941Smrg                       xcb_randr_output_t output)
2200b8e80941Smrg{
2201b8e80941Smrg   struct wsi_display *wsi =
2202b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
2203b8e80941Smrg   struct wsi_display_connector *connector;
2204b8e80941Smrg   uint32_t connector_id;
2205b8e80941Smrg
2206b8e80941Smrg   xcb_window_t root = wsi_display_output_to_root(connection, output);
2207b8e80941Smrg   if (!root)
2208b8e80941Smrg      return NULL;
2209b8e80941Smrg
2210b8e80941Smrg   /* See if we already have a connector for this output */
2211b8e80941Smrg   connector = wsi_display_find_output(wsi_device, output);
2212b8e80941Smrg
2213b8e80941Smrg   if (!connector) {
2214b8e80941Smrg      xcb_atom_t connector_id_atom = 0;
2215b8e80941Smrg
2216b8e80941Smrg      /*
2217b8e80941Smrg       * Go get the kernel connector ID for this X output
2218b8e80941Smrg       */
2219b8e80941Smrg      connector_id = wsi_display_output_to_connector_id(connection,
2220b8e80941Smrg                                                        &connector_id_atom,
2221b8e80941Smrg                                                        output);
2222b8e80941Smrg
2223b8e80941Smrg      /* Any X server with lease support will have this atom */
2224b8e80941Smrg      if (!connector_id) {
2225b8e80941Smrg         return NULL;
2226b8e80941Smrg      }
2227b8e80941Smrg
2228b8e80941Smrg      /* See if we already have a connector for this id */
2229b8e80941Smrg      connector = wsi_display_find_connector(wsi_device, connector_id);
2230b8e80941Smrg
2231b8e80941Smrg      if (connector == NULL) {
2232b8e80941Smrg         connector = wsi_display_alloc_connector(wsi, connector_id);
2233b8e80941Smrg         if (!connector) {
2234b8e80941Smrg            return NULL;
2235b8e80941Smrg         }
2236b8e80941Smrg         list_addtail(&connector->list, &wsi->connectors);
2237b8e80941Smrg      }
2238b8e80941Smrg      connector->output = output;
2239b8e80941Smrg   }
2240b8e80941Smrg
2241b8e80941Smrg   xcb_randr_get_screen_resources_cookie_t src =
2242b8e80941Smrg      xcb_randr_get_screen_resources(connection, root);
2243b8e80941Smrg   xcb_randr_get_output_info_cookie_t oic =
2244b8e80941Smrg      xcb_randr_get_output_info(connection, output, XCB_CURRENT_TIME);
2245b8e80941Smrg   xcb_randr_get_screen_resources_reply_t *srr =
2246b8e80941Smrg      xcb_randr_get_screen_resources_reply(connection, src, NULL);
2247b8e80941Smrg   xcb_randr_get_output_info_reply_t *oir =
2248b8e80941Smrg      xcb_randr_get_output_info_reply(connection, oic, NULL);
2249b8e80941Smrg
2250b8e80941Smrg   if (oir && srr) {
2251b8e80941Smrg      /* Get X modes and add them */
2252b8e80941Smrg
2253b8e80941Smrg      connector->connected =
2254b8e80941Smrg         oir->connection != XCB_RANDR_CONNECTION_DISCONNECTED;
2255b8e80941Smrg
2256b8e80941Smrg      wsi_display_invalidate_connector_modes(wsi_device, connector);
2257b8e80941Smrg
2258b8e80941Smrg      xcb_randr_mode_t *x_modes = xcb_randr_get_output_info_modes(oir);
2259b8e80941Smrg      for (int m = 0; m < oir->num_modes; m++) {
2260b8e80941Smrg         xcb_randr_mode_info_iterator_t i =
2261b8e80941Smrg            xcb_randr_get_screen_resources_modes_iterator(srr);
2262b8e80941Smrg         while (i.rem) {
2263b8e80941Smrg            xcb_randr_mode_info_t *mi = i.data;
2264b8e80941Smrg            if (mi->id == x_modes[m]) {
2265b8e80941Smrg               VkResult result = wsi_display_register_x_mode(
2266b8e80941Smrg                  wsi_device, connector, mi, m < oir->num_preferred);
2267b8e80941Smrg               if (result != VK_SUCCESS) {
2268b8e80941Smrg                  free(oir);
2269b8e80941Smrg                  free(srr);
2270b8e80941Smrg                  return NULL;
2271b8e80941Smrg               }
2272b8e80941Smrg               break;
2273b8e80941Smrg            }
2274b8e80941Smrg            xcb_randr_mode_info_next(&i);
2275b8e80941Smrg         }
2276b8e80941Smrg      }
2277b8e80941Smrg   }
2278b8e80941Smrg
2279b8e80941Smrg   free(oir);
2280b8e80941Smrg   free(srr);
2281b8e80941Smrg   return connector;
2282b8e80941Smrg}
2283b8e80941Smrg
2284b8e80941Smrgstatic xcb_randr_crtc_t
2285b8e80941Smrgwsi_display_find_crtc_for_output(xcb_connection_t *connection,
2286b8e80941Smrg                                 xcb_window_t root,
2287b8e80941Smrg                                 xcb_randr_output_t output)
2288b8e80941Smrg{
2289b8e80941Smrg   xcb_randr_get_screen_resources_cookie_t gsr_c =
2290b8e80941Smrg      xcb_randr_get_screen_resources(connection, root);
2291b8e80941Smrg   xcb_randr_get_screen_resources_reply_t *gsr_r =
2292b8e80941Smrg      xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL);
2293b8e80941Smrg
2294b8e80941Smrg   if (!gsr_r)
2295b8e80941Smrg      return 0;
2296b8e80941Smrg
2297b8e80941Smrg   xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r);
2298b8e80941Smrg   xcb_randr_crtc_t idle_crtc = 0;
2299b8e80941Smrg   xcb_randr_crtc_t active_crtc = 0;
2300b8e80941Smrg
2301b8e80941Smrg   /* Find either a crtc already connected to the desired output or idle */
2302b8e80941Smrg   for (int c = 0; active_crtc == 0 && c < gsr_r->num_crtcs; c++) {
2303b8e80941Smrg      xcb_randr_get_crtc_info_cookie_t gci_c =
2304b8e80941Smrg         xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp);
2305b8e80941Smrg      xcb_randr_get_crtc_info_reply_t *gci_r =
2306b8e80941Smrg         xcb_randr_get_crtc_info_reply(connection, gci_c, NULL);
2307b8e80941Smrg
2308b8e80941Smrg      if (gci_r) {
2309b8e80941Smrg         if (gci_r->mode) {
2310b8e80941Smrg            int num_outputs = xcb_randr_get_crtc_info_outputs_length(gci_r);
2311b8e80941Smrg            xcb_randr_output_t *outputs =
2312b8e80941Smrg               xcb_randr_get_crtc_info_outputs(gci_r);
2313b8e80941Smrg
2314b8e80941Smrg            if (num_outputs == 1 && outputs[0] == output)
2315b8e80941Smrg               active_crtc = rc[c];
2316b8e80941Smrg
2317b8e80941Smrg         } else if (idle_crtc == 0) {
2318b8e80941Smrg            int num_possible = xcb_randr_get_crtc_info_possible_length(gci_r);
2319b8e80941Smrg            xcb_randr_output_t *possible =
2320b8e80941Smrg               xcb_randr_get_crtc_info_possible(gci_r);
2321b8e80941Smrg
2322b8e80941Smrg            for (int p = 0; p < num_possible; p++)
2323b8e80941Smrg               if (possible[p] == output) {
2324b8e80941Smrg                  idle_crtc = rc[c];
2325b8e80941Smrg                  break;
2326b8e80941Smrg               }
2327b8e80941Smrg         }
2328b8e80941Smrg         free(gci_r);
2329b8e80941Smrg      }
2330b8e80941Smrg   }
2331b8e80941Smrg   free(gsr_r);
2332b8e80941Smrg
2333b8e80941Smrg   if (active_crtc)
2334b8e80941Smrg      return active_crtc;
2335b8e80941Smrg   return idle_crtc;
2336b8e80941Smrg}
2337b8e80941Smrg
2338b8e80941SmrgVkResult
2339b8e80941Smrgwsi_acquire_xlib_display(VkPhysicalDevice physical_device,
2340b8e80941Smrg                         struct wsi_device *wsi_device,
2341b8e80941Smrg                         Display *dpy,
2342b8e80941Smrg                         VkDisplayKHR display)
2343b8e80941Smrg{
2344b8e80941Smrg   struct wsi_display *wsi =
2345b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
2346b8e80941Smrg   xcb_connection_t *connection = XGetXCBConnection(dpy);
2347b8e80941Smrg   struct wsi_display_connector *connector =
2348b8e80941Smrg      wsi_display_connector_from_handle(display);
2349b8e80941Smrg   xcb_window_t root;
2350b8e80941Smrg
2351b8e80941Smrg   /* XXX no support for multiple leases yet */
2352b8e80941Smrg   if (wsi->fd >= 0)
2353b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2354b8e80941Smrg
2355b8e80941Smrg   if (!connector->output) {
2356b8e80941Smrg      connector->output = wsi_display_connector_id_to_output(connection,
2357b8e80941Smrg                                                             connector->id);
2358b8e80941Smrg
2359b8e80941Smrg      /* Check and see if we found the output */
2360b8e80941Smrg      if (!connector->output)
2361b8e80941Smrg         return VK_ERROR_INITIALIZATION_FAILED;
2362b8e80941Smrg   }
2363b8e80941Smrg
2364b8e80941Smrg   root = wsi_display_output_to_root(connection, connector->output);
2365b8e80941Smrg   if (!root)
2366b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2367b8e80941Smrg
2368b8e80941Smrg   xcb_randr_crtc_t crtc = wsi_display_find_crtc_for_output(connection,
2369b8e80941Smrg                                                            root,
2370b8e80941Smrg                                                            connector->output);
2371b8e80941Smrg
2372b8e80941Smrg   if (!crtc)
2373b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2374b8e80941Smrg
2375b8e80941Smrg#ifdef HAVE_DRI3_MODIFIERS
2376b8e80941Smrg   xcb_randr_lease_t lease = xcb_generate_id(connection);
2377b8e80941Smrg   xcb_randr_create_lease_cookie_t cl_c =
2378b8e80941Smrg      xcb_randr_create_lease(connection, root, lease, 1, 1,
2379b8e80941Smrg                             &crtc, &connector->output);
2380b8e80941Smrg   xcb_randr_create_lease_reply_t *cl_r =
2381b8e80941Smrg      xcb_randr_create_lease_reply(connection, cl_c, NULL);
2382b8e80941Smrg   if (!cl_r)
2383b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2384b8e80941Smrg
2385b8e80941Smrg   int fd = -1;
2386b8e80941Smrg   if (cl_r->nfd > 0) {
2387b8e80941Smrg      int *rcl_f = xcb_randr_create_lease_reply_fds(connection, cl_r);
2388b8e80941Smrg
2389b8e80941Smrg      fd = rcl_f[0];
2390b8e80941Smrg   }
2391b8e80941Smrg   free (cl_r);
2392b8e80941Smrg   if (fd < 0)
2393b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2394b8e80941Smrg
2395b8e80941Smrg   wsi->fd = fd;
2396b8e80941Smrg#endif
2397b8e80941Smrg
2398b8e80941Smrg   return VK_SUCCESS;
2399b8e80941Smrg}
2400b8e80941Smrg
2401b8e80941SmrgVkResult
2402b8e80941Smrgwsi_get_randr_output_display(VkPhysicalDevice physical_device,
2403b8e80941Smrg                             struct wsi_device *wsi_device,
2404b8e80941Smrg                             Display *dpy,
2405b8e80941Smrg                             RROutput output,
2406b8e80941Smrg                             VkDisplayKHR *display)
2407b8e80941Smrg{
2408b8e80941Smrg   xcb_connection_t *connection = XGetXCBConnection(dpy);
2409b8e80941Smrg   struct wsi_display_connector *connector =
2410b8e80941Smrg      wsi_display_get_output(wsi_device, connection, (xcb_randr_output_t) output);
2411b8e80941Smrg
2412b8e80941Smrg   if (connector)
2413b8e80941Smrg      *display = wsi_display_connector_to_handle(connector);
2414b8e80941Smrg   else
2415b8e80941Smrg      *display = VK_NULL_HANDLE;
2416b8e80941Smrg   return VK_SUCCESS;
2417b8e80941Smrg}
2418b8e80941Smrg
2419b8e80941Smrg#endif
2420b8e80941Smrg
2421b8e80941Smrg/* VK_EXT_display_control */
2422b8e80941SmrgVkResult
2423b8e80941Smrgwsi_display_power_control(VkDevice device,
2424b8e80941Smrg                          struct wsi_device *wsi_device,
2425b8e80941Smrg                          VkDisplayKHR display,
2426b8e80941Smrg                          const VkDisplayPowerInfoEXT *display_power_info)
2427b8e80941Smrg{
2428b8e80941Smrg   struct wsi_display *wsi =
2429b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
2430b8e80941Smrg   struct wsi_display_connector *connector =
2431b8e80941Smrg      wsi_display_connector_from_handle(display);
2432b8e80941Smrg   int mode;
2433b8e80941Smrg
2434b8e80941Smrg   if (wsi->fd < 0)
2435b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2436b8e80941Smrg
2437b8e80941Smrg   switch (display_power_info->powerState) {
2438b8e80941Smrg   case VK_DISPLAY_POWER_STATE_OFF_EXT:
2439b8e80941Smrg      mode = DRM_MODE_DPMS_OFF;
2440b8e80941Smrg      break;
2441b8e80941Smrg   case VK_DISPLAY_POWER_STATE_SUSPEND_EXT:
2442b8e80941Smrg      mode = DRM_MODE_DPMS_SUSPEND;
2443b8e80941Smrg      break;
2444b8e80941Smrg   default:
2445b8e80941Smrg      mode = DRM_MODE_DPMS_ON;
2446b8e80941Smrg      break;
2447b8e80941Smrg   }
2448b8e80941Smrg   drmModeConnectorSetProperty(wsi->fd,
2449b8e80941Smrg                               connector->id,
2450b8e80941Smrg                               connector->dpms_property,
2451b8e80941Smrg                               mode);
2452b8e80941Smrg   return VK_SUCCESS;
2453b8e80941Smrg}
2454b8e80941Smrg
2455b8e80941SmrgVkResult
2456b8e80941Smrgwsi_register_device_event(VkDevice device,
2457b8e80941Smrg                          struct wsi_device *wsi_device,
2458b8e80941Smrg                          const VkDeviceEventInfoEXT *device_event_info,
2459b8e80941Smrg                          const VkAllocationCallbacks *allocator,
2460b8e80941Smrg                          struct wsi_fence **fence_p)
2461b8e80941Smrg{
2462b8e80941Smrg   return VK_ERROR_FEATURE_NOT_PRESENT;
2463b8e80941Smrg}
2464b8e80941Smrg
2465b8e80941SmrgVkResult
2466b8e80941Smrgwsi_register_display_event(VkDevice device,
2467b8e80941Smrg                           struct wsi_device *wsi_device,
2468b8e80941Smrg                           VkDisplayKHR display,
2469b8e80941Smrg                           const VkDisplayEventInfoEXT *display_event_info,
2470b8e80941Smrg                           const VkAllocationCallbacks *allocator,
2471b8e80941Smrg                           struct wsi_fence **fence_p)
2472b8e80941Smrg{
2473b8e80941Smrg   struct wsi_display *wsi =
2474b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
2475b8e80941Smrg   struct wsi_display_fence *fence;
2476b8e80941Smrg   VkResult ret;
2477b8e80941Smrg
2478b8e80941Smrg   switch (display_event_info->displayEvent) {
2479b8e80941Smrg   case VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT:
2480b8e80941Smrg
2481b8e80941Smrg      fence = wsi_display_fence_alloc(device, wsi_device, display, allocator);
2482b8e80941Smrg
2483b8e80941Smrg      if (!fence)
2484b8e80941Smrg         return VK_ERROR_OUT_OF_HOST_MEMORY;
2485b8e80941Smrg
2486b8e80941Smrg      ret = wsi_register_vblank_event(fence, wsi_device, display,
2487b8e80941Smrg                                      DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL);
2488b8e80941Smrg
2489b8e80941Smrg      if (ret == VK_SUCCESS)
2490b8e80941Smrg         *fence_p = &fence->base;
2491b8e80941Smrg      else if (fence != NULL)
2492b8e80941Smrg         vk_free2(wsi->alloc, allocator, fence);
2493b8e80941Smrg
2494b8e80941Smrg      break;
2495b8e80941Smrg   default:
2496b8e80941Smrg      ret = VK_ERROR_FEATURE_NOT_PRESENT;
2497b8e80941Smrg      break;
2498b8e80941Smrg   }
2499b8e80941Smrg
2500b8e80941Smrg   return ret;
2501b8e80941Smrg}
2502b8e80941Smrg
2503b8e80941Smrg
2504b8e80941SmrgVkResult
2505b8e80941Smrgwsi_get_swapchain_counter(VkDevice device,
2506b8e80941Smrg                          struct wsi_device *wsi_device,
2507b8e80941Smrg                          VkSwapchainKHR _swapchain,
2508b8e80941Smrg                          VkSurfaceCounterFlagBitsEXT flag_bits,
2509b8e80941Smrg                          uint64_t *value)
2510b8e80941Smrg{
2511b8e80941Smrg   struct wsi_display *wsi =
2512b8e80941Smrg      (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY];
2513b8e80941Smrg   struct wsi_display_swapchain *swapchain =
2514b8e80941Smrg      (struct wsi_display_swapchain *) wsi_swapchain_from_handle(_swapchain);
2515b8e80941Smrg   struct wsi_display_connector *connector =
2516b8e80941Smrg      wsi_display_mode_from_handle(swapchain->surface->displayMode)->connector;
2517b8e80941Smrg
2518b8e80941Smrg   if (wsi->fd < 0)
2519b8e80941Smrg      return VK_ERROR_INITIALIZATION_FAILED;
2520b8e80941Smrg
2521b8e80941Smrg   if (!connector->active) {
2522b8e80941Smrg      *value = 0;
2523b8e80941Smrg      return VK_SUCCESS;
2524b8e80941Smrg   }
2525b8e80941Smrg
2526b8e80941Smrg   int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id, value, NULL);
2527b8e80941Smrg   if (ret)
2528b8e80941Smrg      *value = 0;
2529b8e80941Smrg
2530b8e80941Smrg   return VK_SUCCESS;
2531b8e80941Smrg}
2532b8e80941Smrg
2533