xref: /aosp_15_r20/external/libva/va/wayland/va_wayland_linux_dmabuf.c (revision 54e60f844a168e9a219354de272cd517ee8cd4b7)
1 /*
2  * va_wayland_drm.c - Wayland/linux-dmabuf helpers
3  *
4  * Copyright (c) 2024 Simon Ser
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26 
27 #include "sysdeps.h"
28 #include <unistd.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <dlfcn.h>
32 #include <sys/stat.h>
33 #include <xf86drm.h>
34 #include "va_drmcommon.h"
35 #include "drm/va_drm_utils.h"
36 #include "va_wayland_linux_dmabuf.h"
37 #include "va_wayland_private.h"
38 #include "linux-dmabuf-v1-client-protocol.h"
39 
40 typedef struct va_wayland_linux_dmabuf_context {
41     struct va_wayland_context base;
42     bool                      has_linux_dmabuf;
43     bool                      default_feedback_done;
44 } VADisplayContextWaylandLinuxDmabuf;
45 
46 static void
feedback_handle_done(void * data,struct zwp_linux_dmabuf_feedback_v1 * feedback)47 feedback_handle_done(
48     void                                *data,
49     struct zwp_linux_dmabuf_feedback_v1 *feedback
50 )
51 {
52     VADisplayContextP const pDisplayContext = data;
53     struct va_wayland_linux_dmabuf_context *wl_linux_dmabuf_ctx = pDisplayContext->opaque;
54 
55     wl_linux_dmabuf_ctx->default_feedback_done = true;
56 
57     zwp_linux_dmabuf_feedback_v1_destroy(feedback);
58 }
59 
60 static void
feedback_handle_format_table(void * data,struct zwp_linux_dmabuf_feedback_v1 * feedback,int fd,uint32_t size)61 feedback_handle_format_table(
62     void                                *data,
63     struct zwp_linux_dmabuf_feedback_v1 *feedback,
64     int                                  fd,
65     uint32_t                             size
66 )
67 {
68     close(fd);
69 }
70 
71 /* XXX: replace with drmGetDeviceFromDevId() */
72 static drmDevice *
get_drm_device_from_dev_id(dev_t dev_id)73 get_drm_device_from_dev_id(dev_t dev_id)
74 {
75     uint32_t flags = 0;
76     int devices_len, i, node_type;
77     drmDevice *match = NULL, *dev;
78     struct stat statbuf;
79 
80     devices_len = drmGetDevices2(flags, NULL, 0);
81     if (devices_len < 0) {
82         return NULL;
83     }
84     drmDevice **devices = calloc(devices_len, sizeof(*devices));
85     if (devices == NULL) {
86         return NULL;
87     }
88     devices_len = drmGetDevices2(flags, devices, devices_len);
89     if (devices_len < 0) {
90         free(devices);
91         return NULL;
92     }
93 
94     for (i = 0; i < devices_len; i++) {
95         dev = devices[i];
96         for (node_type = 0; node_type < DRM_NODE_MAX; node_type++) {
97             if (!(dev->available_nodes & (1 << node_type)))
98                 continue;
99 
100             if (stat(dev->nodes[node_type], &statbuf) != 0) {
101                 va_wayland_error("stat() failed for %s", dev->nodes[node_type]);
102                 continue;
103             }
104 
105             if (statbuf.st_rdev == dev_id) {
106                 match = dev;
107                 break;
108             }
109         }
110     }
111 
112     for (i = 0; i < devices_len; i++) {
113         dev = devices[i];
114         if (dev != match)
115             drmFreeDevice(&dev);
116     }
117     free(devices);
118 
119     return match;
120 }
121 
122 static void
feedback_handle_main_device(void * data,struct zwp_linux_dmabuf_feedback_v1 * feedback,struct wl_array * device_array)123 feedback_handle_main_device(
124     void                                *data,
125     struct zwp_linux_dmabuf_feedback_v1 *feedback,
126     struct wl_array                     *device_array
127 )
128 {
129     dev_t dev_id;
130     drmDevice *dev;
131     const char *dev_path;
132     VADisplayContextP const pDisplayContext = data;
133     VADriverContextP const ctx = pDisplayContext->pDriverContext;
134     struct drm_state * const drm_state = ctx->drm_state;
135 
136     assert(device_array->size == sizeof(dev_id));
137     memcpy(&dev_id, device_array->data, sizeof(dev_id));
138 
139     dev = get_drm_device_from_dev_id(dev_id);
140     if (!dev) {
141         va_wayland_error("failed to get DRM device from device ID");
142         return;
143     }
144 
145     if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)))
146         goto end;
147 
148     dev_path = dev->nodes[DRM_NODE_RENDER];
149     drm_state->fd = open(dev_path, O_RDWR | O_CLOEXEC);
150     if (drm_state->fd < 0) {
151         va_wayland_error("failed to open %s", dev_path);
152         goto end;
153     }
154 
155     drm_state->auth_type = VA_DRM_AUTH_CUSTOM;
156 
157 end:
158     drmFreeDevice(&dev);
159 }
160 
161 static void
feedback_handle_tranche_done(void * data,struct zwp_linux_dmabuf_feedback_v1 * feedback)162 feedback_handle_tranche_done(
163     void                                *data,
164     struct zwp_linux_dmabuf_feedback_v1 *feedback
165 )
166 {
167 }
168 
169 static void
feedback_handle_tranche_target_device(void * data,struct zwp_linux_dmabuf_feedback_v1 * feedback,struct wl_array * device_array)170 feedback_handle_tranche_target_device(
171     void                                *data,
172     struct zwp_linux_dmabuf_feedback_v1 *feedback,
173     struct wl_array                     *device_array
174 )
175 {
176 }
177 
178 static void
feedback_handle_tranche_formats(void * data,struct zwp_linux_dmabuf_feedback_v1 * feedback,struct wl_array * indices_array)179 feedback_handle_tranche_formats(
180     void                                *data,
181     struct zwp_linux_dmabuf_feedback_v1 *feedback,
182     struct wl_array                     *indices_array
183 )
184 {
185 }
186 
187 static void
feedback_handle_tranche_flags(void * data,struct zwp_linux_dmabuf_feedback_v1 * feedback,uint32_t flags)188 feedback_handle_tranche_flags(
189     void                                *data,
190     struct zwp_linux_dmabuf_feedback_v1 *feedback,
191     uint32_t                             flags
192 )
193 {
194 }
195 
196 static const struct zwp_linux_dmabuf_feedback_v1_listener feedback_listener = {
197     .done = feedback_handle_done,
198     .format_table = feedback_handle_format_table,
199     .main_device = feedback_handle_main_device,
200     .tranche_done = feedback_handle_tranche_done,
201     .tranche_target_device = feedback_handle_tranche_target_device,
202     .tranche_formats = feedback_handle_tranche_formats,
203     .tranche_flags = feedback_handle_tranche_flags,
204 };
205 
206 static void
registry_handle_global(void * data,struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)207 registry_handle_global(
208     void               *data,
209     struct wl_registry *registry,
210     uint32_t            name,
211     const char         *interface,
212     uint32_t            version
213 )
214 {
215     VADisplayContextP const pDisplayContext = data;
216     struct va_wayland_linux_dmabuf_context *wl_linux_dmabuf_ctx = pDisplayContext->opaque;
217     struct zwp_linux_dmabuf_v1 *linux_dmabuf;
218     struct zwp_linux_dmabuf_feedback_v1 *feedback;
219 
220     if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
221         version >= 4) {
222         wl_linux_dmabuf_ctx->has_linux_dmabuf = true;
223         linux_dmabuf =
224             wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, 4);
225         feedback = zwp_linux_dmabuf_v1_get_default_feedback(linux_dmabuf);
226         zwp_linux_dmabuf_feedback_v1_add_listener(feedback, &feedback_listener, data);
227         zwp_linux_dmabuf_v1_destroy(linux_dmabuf);
228     }
229 }
230 
231 static void
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)232 registry_handle_global_remove(
233     void               *data,
234     struct wl_registry *registry,
235     uint32_t            name
236 )
237 {
238 }
239 
240 static const struct wl_registry_listener registry_listener = {
241     .global        = registry_handle_global,
242     .global_remove = registry_handle_global_remove,
243 };
244 
245 static VAStatus
va_DisplayContextGetDriverNames(VADisplayContextP pDisplayContext,char ** drivers,unsigned * num_drivers)246 va_DisplayContextGetDriverNames(
247     VADisplayContextP pDisplayContext,
248     char            **drivers,
249     unsigned         *num_drivers
250 )
251 {
252     VADriverContextP const ctx = pDisplayContext->pDriverContext;
253 
254     return VA_DRM_GetDriverNames(ctx, drivers, num_drivers);
255 }
256 
257 bool
va_wayland_linux_dmabuf_create(VADisplayContextP pDisplayContext)258 va_wayland_linux_dmabuf_create(VADisplayContextP pDisplayContext)
259 {
260     bool result = false;
261     VADriverContextP const ctx = pDisplayContext->pDriverContext;
262     struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
263     struct va_wayland_linux_dmabuf_context *wl_linux_dmabuf_ctx;
264     struct drm_state *drm_state;
265     struct wl_event_queue *queue = NULL;
266     struct wl_display *display = NULL;
267     struct wl_registry *registry = NULL;
268 
269     wl_linux_dmabuf_ctx = calloc(1, sizeof(*wl_linux_dmabuf_ctx));
270     if (!wl_linux_dmabuf_ctx) {
271         va_wayland_error("could not allocate wl_linux_dmabuf_ctx");
272         goto end;
273     }
274     wl_linux_dmabuf_ctx->base.destroy = va_wayland_linux_dmabuf_destroy;
275     pDisplayContext->opaque           = wl_linux_dmabuf_ctx;
276     pDisplayContext->vaGetDriverNames = va_DisplayContextGetDriverNames;
277 
278     drm_state = calloc(1, sizeof(*drm_state));
279     if (!drm_state) {
280         va_wayland_error("could not allocate drm_state");
281         goto end;
282     }
283     drm_state->fd        = -1;
284     drm_state->auth_type = 0;
285     ctx->drm_state       = drm_state;
286 
287     vtable->has_prime_sharing = 0;
288 
289     /* Use wrapped wl_display with private event queue to prevent
290      * thread safety issues with applications that e.g. run an event pump
291      * parallel to libva initialization.
292      * Using the default queue, events might get lost and crashes occur
293      * because wl_display_roundtrip is not thread-safe with respect to the
294      * same queue.
295      */
296     queue = wl_display_create_queue(ctx->native_dpy);
297     if (!queue) {
298         va_wayland_error("could not create Wayland event queue");
299         goto end;
300     }
301 
302     display = wl_proxy_create_wrapper(ctx->native_dpy);
303     if (!display) {
304         va_wayland_error("could not create Wayland proxy wrapper");
305         goto end;
306     }
307     wl_proxy_set_queue((struct wl_proxy *) display, queue);
308 
309     registry = wl_display_get_registry(display);
310     if (!registry) {
311         va_wayland_error("could not create wl_registry");
312         goto end;
313     }
314     wl_registry_add_listener(registry, &registry_listener, pDisplayContext);
315 
316     if (wl_display_roundtrip_queue(ctx->native_dpy, queue) < 0) {
317         va_wayland_error("failed to roundtrip Wayland queue");
318         goto end;
319     }
320 
321     if (!wl_linux_dmabuf_ctx->has_linux_dmabuf)
322         goto end;
323 
324     while (!wl_linux_dmabuf_ctx->default_feedback_done) {
325         if (wl_display_dispatch_queue(ctx->native_dpy, queue) < 0) {
326             va_wayland_error("failed to dispatch Wayland queue");
327             goto end;
328         }
329     }
330 
331     if (drm_state->fd < 0)
332         goto end;
333 
334     result = true;
335     vtable->has_prime_sharing = true;
336 
337 end:
338     if (registry)
339         wl_registry_destroy(registry);
340     if (display)
341         wl_proxy_wrapper_destroy(display);
342     if (queue)
343         wl_event_queue_destroy(queue);
344     return result;
345 }
346 
347 void
va_wayland_linux_dmabuf_destroy(VADisplayContextP pDisplayContext)348 va_wayland_linux_dmabuf_destroy(VADisplayContextP pDisplayContext)
349 {
350     VADriverContextP const ctx = pDisplayContext->pDriverContext;
351     struct drm_state * const drm_state = ctx->drm_state;
352     struct VADriverVTableWayland *vtable = ctx->vtable_wayland;
353 
354     vtable->has_prime_sharing = 0;
355 
356     if (drm_state) {
357         if (drm_state->fd >= 0) {
358             close(drm_state->fd);
359             drm_state->fd = -1;
360         }
361         free(ctx->drm_state);
362         ctx->drm_state = NULL;
363     }
364 }
365