xref: /aosp_15_r20/external/libva/va/wayland/va_wayland_drm.c (revision 54e60f844a168e9a219354de272cd517ee8cd4b7)
1 /*
2  * va_wayland_drm.c - Wayland/DRM helpers
3  *
4  * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
5  * Copyright (c) 2023 Emil Velikov
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sub license, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial portions
17  * of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22  * IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include "sysdeps.h"
29 #include <unistd.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <dlfcn.h>
33 #include <sys/stat.h>
34 #include <xf86drm.h>
35 #include "va_drmcommon.h"
36 #include "drm/va_drm_utils.h"
37 #include "va_wayland_drm.h"
38 #include "va_wayland_private.h"
39 #include "wayland-drm-client-protocol.h"
40 
41 typedef struct va_wayland_drm_context {
42     struct va_wayland_context   base;
43     struct wl_event_queue      *queue;
44     struct wl_drm              *drm;
45     uint32_t                    drm_name;
46     struct wl_registry         *registry;
47     unsigned int                is_authenticated        : 1;
48 } VADisplayContextWaylandDRM;
49 
50 static void
drm_handle_device(void * data,struct wl_drm * drm,const char * device)51 drm_handle_device(void *data, struct wl_drm *drm, const char *device)
52 {
53     VADisplayContextP const pDisplayContext = data;
54     VADriverContextP const ctx = pDisplayContext->pDriverContext;
55     VADisplayContextWaylandDRM * const wl_drm_ctx = pDisplayContext->opaque;
56     struct drm_state * const drm_state = ctx->drm_state;
57     drm_magic_t magic;
58     struct stat st;
59     int fd = -1;
60 
61     fd = open(device, O_RDWR);
62     if (fd < 0) {
63         va_wayland_error("failed to open %s: %s (errno %d)",
64                          device, strerror(errno), errno);
65         return;
66     }
67 
68     if (fstat(fd, &st) < 0) {
69         va_wayland_error("failed to identify %s: %s (errno %d)",
70                          device, strerror(errno), errno);
71         close(fd);
72         return;
73     }
74 
75     if (!S_ISCHR(st.st_mode)) {
76         va_wayland_error("%s is not a device", device);
77         close(fd);
78         return;
79     }
80 
81     drm_state->fd = fd;
82 
83     int type = drmGetNodeTypeFromFd(fd);
84     if (type != DRM_NODE_RENDER) {
85         drmGetMagic(drm_state->fd, &magic);
86         wl_drm_authenticate(wl_drm_ctx->drm, magic);
87     } else {
88         wl_drm_ctx->is_authenticated = 1;
89         drm_state->auth_type         = VA_DRM_AUTH_CUSTOM;
90     }
91 }
92 
93 static void
drm_handle_format(void * data,struct wl_drm * drm,uint32_t format)94 drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
95 {
96 }
97 
98 static void
drm_handle_authenticated(void * data,struct wl_drm * drm)99 drm_handle_authenticated(void *data, struct wl_drm *drm)
100 {
101     VADisplayContextP const pDisplayContext = data;
102     VADriverContextP const ctx = pDisplayContext->pDriverContext;
103     VADisplayContextWaylandDRM * const wl_drm_ctx = pDisplayContext->opaque;
104     struct drm_state * const drm_state = ctx->drm_state;
105 
106     wl_drm_ctx->is_authenticated = 1;
107     drm_state->auth_type         = VA_DRM_AUTH_CUSTOM;
108 }
109 
110 static void
drm_handle_capabilities(void * data,struct wl_drm * wl_drm,uint32_t value)111 drm_handle_capabilities(void *data, struct wl_drm *wl_drm, uint32_t value)
112 {
113     VADisplayContextP const pDisplayContext = data;
114     VADriverContextP const ctx = pDisplayContext->pDriverContext;
115     struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
116 
117     vtable->has_prime_sharing = !!(value & WL_DRM_CAPABILITY_PRIME);
118 }
119 
120 static const struct wl_drm_listener drm_listener = {
121     drm_handle_device,
122     drm_handle_format,
123     drm_handle_authenticated,
124     drm_handle_capabilities,
125 };
126 
127 static VAStatus
va_DisplayContextGetDriverNames(VADisplayContextP pDisplayContext,char ** drivers,unsigned * num_drivers)128 va_DisplayContextGetDriverNames(
129     VADisplayContextP pDisplayContext,
130     char            **drivers,
131     unsigned         *num_drivers
132 )
133 {
134     VADriverContextP const ctx = pDisplayContext->pDriverContext;
135 
136     return VA_DRM_GetDriverNames(ctx, drivers, num_drivers);
137 }
138 
139 void
va_wayland_drm_destroy(VADisplayContextP pDisplayContext)140 va_wayland_drm_destroy(VADisplayContextP pDisplayContext)
141 {
142     VADriverContextP const ctx = pDisplayContext->pDriverContext;
143     struct va_wayland_drm_context * const wl_drm_ctx = pDisplayContext->opaque;
144     struct drm_state * const drm_state = ctx->drm_state;
145     struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
146 
147     vtable->has_prime_sharing = 0;
148     vtable->wl_interface = NULL;
149 
150     wl_drm_ctx->is_authenticated = 0;
151 
152     if (drm_state) {
153         if (drm_state->fd >= 0) {
154             close(drm_state->fd);
155             drm_state->fd = -1;
156         }
157         free(ctx->drm_state);
158         ctx->drm_state = NULL;
159     }
160 }
161 
162 static void
registry_handle_global(void * data,struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)163 registry_handle_global(
164     void               *data,
165     struct wl_registry *registry,
166     uint32_t            name,
167     const char         *interface,
168     uint32_t            version
169 )
170 {
171     VADisplayContextP const pDisplayContext = data;
172     struct va_wayland_drm_context * const wl_drm_ctx = pDisplayContext->opaque;
173 
174     if (strcmp(interface, "wl_drm") == 0) {
175         /* bind to at most version 2, but also support version 1 if
176          * compositor does not have v2
177          */
178         wl_drm_ctx->drm_name = name;
179         wl_drm_ctx->drm =
180             wl_registry_bind(wl_drm_ctx->registry, name, &wl_drm_interface,
181                              (version < 2) ? version : 2);
182 
183         if (wl_drm_ctx->drm
184             && wl_drm_add_listener(wl_drm_ctx->drm, &drm_listener, pDisplayContext) != 0) {
185             va_wayland_error("could not add listener to wl_drm");
186             wl_drm_destroy(wl_drm_ctx->drm);
187             wl_drm_ctx->drm = NULL;
188         }
189     }
190 }
191 
192 static void
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)193 registry_handle_global_remove(
194     void               *data,
195     struct wl_registry *registry,
196     uint32_t            name
197 )
198 {
199     struct va_wayland_drm_context *wl_drm_ctx = data;
200 
201     if (wl_drm_ctx->drm && name == wl_drm_ctx->drm_name) {
202         wl_drm_destroy(wl_drm_ctx->drm);
203         wl_drm_ctx->drm = NULL;
204     }
205 }
206 
207 static const struct wl_registry_listener registry_listener = {
208     registry_handle_global,
209     registry_handle_global_remove
210 };
211 
212 static bool
wayland_roundtrip_queue(struct wl_display * display,struct wl_event_queue * queue)213 wayland_roundtrip_queue(struct wl_display *display,
214                         struct wl_event_queue *queue)
215 {
216     if (wl_display_roundtrip_queue(display, queue) < 0) {
217         int err = wl_display_get_error(display);
218         va_wayland_error("Wayland roundtrip error: %s (errno %d)", strerror(err), err);
219         return false;
220     } else {
221         return true;
222     }
223 }
224 
225 bool
va_wayland_drm_create(VADisplayContextP pDisplayContext)226 va_wayland_drm_create(VADisplayContextP pDisplayContext)
227 {
228     bool result = false;
229     VADriverContextP const ctx = pDisplayContext->pDriverContext;
230     struct va_wayland_drm_context *wl_drm_ctx;
231     struct drm_state *drm_state;
232     struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
233     struct wl_display *wrapped_display = NULL;
234 
235     vtable->wl_interface = NULL;
236 
237     wl_drm_ctx = malloc(sizeof(*wl_drm_ctx));
238     if (!wl_drm_ctx) {
239         va_wayland_error("could not allocate wl_drm_ctx");
240         goto end;
241     }
242     wl_drm_ctx->base.destroy            = va_wayland_drm_destroy;
243     wl_drm_ctx->queue                   = NULL;
244     wl_drm_ctx->drm                     = NULL;
245     wl_drm_ctx->registry                = NULL;
246     wl_drm_ctx->is_authenticated        = 0;
247     pDisplayContext->opaque             = wl_drm_ctx;
248     pDisplayContext->vaGetDriverNames    = va_DisplayContextGetDriverNames;
249 
250     drm_state = calloc(1, sizeof(struct drm_state));
251     if (!drm_state) {
252         va_wayland_error("could not allocate drm_state");
253         goto end;
254     }
255     drm_state->fd        = -1;
256     drm_state->auth_type = 0;
257     ctx->drm_state       = drm_state;
258     vtable->has_prime_sharing = 0;
259 
260     /* Use wrapped wl_display with private event queue to prevent
261      * thread safety issues with applications that e.g. run an event pump
262      * parallel to libva initialization.
263      * Using the default queue, events might get lost and crashes occur
264      * because wl_display_roundtrip is not thread-safe with respect to the
265      * same queue.
266      */
267     wl_drm_ctx->queue = wl_display_create_queue(ctx->native_dpy);
268     if (!wl_drm_ctx->queue) {
269         va_wayland_error("could not create Wayland event queue");
270         goto end;
271     }
272 
273     wrapped_display = wl_proxy_create_wrapper(ctx->native_dpy);
274     if (!wrapped_display) {
275         va_wayland_error("could not create Wayland proxy wrapper");
276         goto end;
277     }
278 
279     /* All created objects will inherit this queue */
280     wl_proxy_set_queue((struct wl_proxy *) wrapped_display, wl_drm_ctx->queue);
281     wl_drm_ctx->registry = wl_display_get_registry(wrapped_display);
282     if (!wl_drm_ctx->registry) {
283         va_wayland_error("could not create wl_registry");
284         goto end;
285     }
286     if (wl_registry_add_listener(wl_drm_ctx->registry, &registry_listener, pDisplayContext) != 0) {
287         va_wayland_error("could not add listener to wl_registry");
288         goto end;
289     }
290     if (!wayland_roundtrip_queue(ctx->native_dpy, wl_drm_ctx->queue))
291         goto end;
292 
293     /* registry_handle_global should have been called by the
294      * wl_display_roundtrip_queue above
295      */
296 
297     /* Do not print an error, the compositor might just not support wl_drm */
298     if (!wl_drm_ctx->drm)
299         goto end;
300     if (!wayland_roundtrip_queue(ctx->native_dpy, wl_drm_ctx->queue))
301         goto end;
302     if (drm_state->fd < 0) {
303         va_wayland_error("did not get DRM device");
304         goto end;
305     }
306 
307     if (!wayland_roundtrip_queue(ctx->native_dpy, wl_drm_ctx->queue))
308         goto end;
309 
310     if (!wl_drm_ctx->is_authenticated) {
311         va_wayland_error("Wayland compositor did not respond to DRM authentication");
312         goto end;
313     }
314 
315     vtable->wl_interface = &wl_drm_interface;
316     result = true;
317 
318 end:
319     if (wrapped_display) {
320         wl_proxy_wrapper_destroy(wrapped_display);
321         wrapped_display = NULL;
322     }
323 
324     if (wl_drm_ctx) {
325         if (wl_drm_ctx->drm) {
326             wl_drm_destroy(wl_drm_ctx->drm);
327             wl_drm_ctx->drm = NULL;
328         }
329 
330         if (wl_drm_ctx->registry) {
331             wl_registry_destroy(wl_drm_ctx->registry);
332             wl_drm_ctx->registry = NULL;
333         }
334 
335         if (wl_drm_ctx->queue) {
336             wl_event_queue_destroy(wl_drm_ctx->queue);
337             wl_drm_ctx->queue = NULL;
338         }
339     }
340 
341     return result;
342 }
343