1 /*
2 * Copyright © 2011 Kristian Høgsberg
3 * Copyright © 2011 Benjamin Franzke
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 * Kristian Høgsberg <krh@bitplanet.net>
27 * Benjamin Franzke <benjaminfranzke@googlemail.com>
28 */
29
30 #ifndef WL_DRM_FORMAT_ENUM
31 #define WL_DRM_FORMAT_ENUM
32 enum wl_drm_format {
33 WL_DRM_FORMAT_C8 = 0x20203843,
34 WL_DRM_FORMAT_RGB332 = 0x38424752,
35 WL_DRM_FORMAT_BGR233 = 0x38524742,
36 WL_DRM_FORMAT_XRGB4444 = 0x32315258,
37 WL_DRM_FORMAT_XBGR4444 = 0x32314258,
38 WL_DRM_FORMAT_RGBX4444 = 0x32315852,
39 WL_DRM_FORMAT_BGRX4444 = 0x32315842,
40 WL_DRM_FORMAT_ARGB4444 = 0x32315241,
41 WL_DRM_FORMAT_ABGR4444 = 0x32314241,
42 WL_DRM_FORMAT_RGBA4444 = 0x32314152,
43 WL_DRM_FORMAT_BGRA4444 = 0x32314142,
44 WL_DRM_FORMAT_XRGB1555 = 0x35315258,
45 WL_DRM_FORMAT_XBGR1555 = 0x35314258,
46 WL_DRM_FORMAT_RGBX5551 = 0x35315852,
47 WL_DRM_FORMAT_BGRX5551 = 0x35315842,
48 WL_DRM_FORMAT_ARGB1555 = 0x35315241,
49 WL_DRM_FORMAT_ABGR1555 = 0x35314241,
50 WL_DRM_FORMAT_RGBA5551 = 0x35314152,
51 WL_DRM_FORMAT_BGRA5551 = 0x35314142,
52 WL_DRM_FORMAT_RGB565 = 0x36314752,
53 WL_DRM_FORMAT_BGR565 = 0x36314742,
54 WL_DRM_FORMAT_RGB888 = 0x34324752,
55 WL_DRM_FORMAT_BGR888 = 0x34324742,
56 WL_DRM_FORMAT_XRGB8888 = 0x34325258,
57 WL_DRM_FORMAT_XBGR8888 = 0x34324258,
58 WL_DRM_FORMAT_RGBX8888 = 0x34325852,
59 WL_DRM_FORMAT_BGRX8888 = 0x34325842,
60 WL_DRM_FORMAT_ARGB8888 = 0x34325241,
61 WL_DRM_FORMAT_ABGR8888 = 0x34324241,
62 WL_DRM_FORMAT_RGBA8888 = 0x34324152,
63 WL_DRM_FORMAT_BGRA8888 = 0x34324142,
64 WL_DRM_FORMAT_XRGB2101010 = 0x30335258,
65 WL_DRM_FORMAT_XBGR2101010 = 0x30334258,
66 WL_DRM_FORMAT_RGBX1010102 = 0x30335852,
67 WL_DRM_FORMAT_BGRX1010102 = 0x30335842,
68 WL_DRM_FORMAT_ARGB2101010 = 0x30335241,
69 WL_DRM_FORMAT_ABGR2101010 = 0x30334241,
70 WL_DRM_FORMAT_RGBA1010102 = 0x30334152,
71 WL_DRM_FORMAT_BGRA1010102 = 0x30334142,
72 WL_DRM_FORMAT_YUYV = 0x56595559,
73 WL_DRM_FORMAT_YVYU = 0x55595659,
74 WL_DRM_FORMAT_UYVY = 0x59565955,
75 WL_DRM_FORMAT_VYUY = 0x59555956,
76 WL_DRM_FORMAT_AYUV = 0x56555941,
77 WL_DRM_FORMAT_NV12 = 0x3231564e,
78 WL_DRM_FORMAT_NV21 = 0x3132564e,
79 WL_DRM_FORMAT_NV16 = 0x3631564e,
80 WL_DRM_FORMAT_NV61 = 0x3136564e,
81 WL_DRM_FORMAT_YUV410 = 0x39565559,
82 WL_DRM_FORMAT_YVU410 = 0x39555659,
83 WL_DRM_FORMAT_YUV411 = 0x31315559,
84 WL_DRM_FORMAT_YVU411 = 0x31315659,
85 WL_DRM_FORMAT_YUV420 = 0x32315559,
86 WL_DRM_FORMAT_YVU420 = 0x32315659,
87 WL_DRM_FORMAT_YUV422 = 0x36315559,
88 WL_DRM_FORMAT_YVU422 = 0x36315659,
89 WL_DRM_FORMAT_YUV444 = 0x34325559,
90 WL_DRM_FORMAT_YVU444 = 0x34325659,
91 };
92 #endif /* WL_DRM_FORMAT_ENUM */
93
94 struct wl_drm_buffer {
95 struct wl_resource *resource;
96 struct wl_drm *drm;
97 int32_t width, height;
98 int fd;
99 uint32_t format;
100 const void *driver_format;
101 int32_t offset[3];
102 int32_t stride[3];
103 void *driver_buffer;
104 };
105
106 enum { WAYLAND_DRM_PRIME = 0x01 };
107
108 #define MIN(x,y) (((x)<(y))?(x):(y))
109
110 struct wl_drm {
111 struct wl_display *display;
112 struct wl_global *wl_drm_global;
113
114 void *user_data;
115 char *device_name;
116 uint32_t flags;
117
118 struct wl_buffer_interface buffer_interface;
119 };
120
121 static void
destroy_buffer(struct wl_resource * resource)122 destroy_buffer(struct wl_resource *resource)
123 {
124 trace(TRACE_DRM, "");
125 struct wl_drm_buffer *buffer = resource->data;
126 if (buffer->fd > 0)
127 close(buffer->fd);
128 free(buffer);
129 }
130
131 static void
drm_buffer_destroy(struct wl_client * client,struct wl_resource * resource)132 drm_buffer_destroy(struct wl_client *client, struct wl_resource *resource)
133 {
134 trace(TRACE_DRM, "");
135 /*
136 * struct drm_buffer = return wl_resource_get_user_data(resource);
137 struct wl_drm_buffer *buffer;
138 */
139
140 wl_resource_destroy(resource);
141 }
142
create_buffer(struct wl_client * client,struct wl_resource * resource,uint32_t id,uint32_t name,int fd,int32_t width,int32_t height,uint32_t format,int32_t offset0,int32_t stride0,int32_t offset1,int32_t stride1,int32_t offset2,int32_t stride2)143 static void create_buffer(struct wl_client *client,
144 struct wl_resource *resource, uint32_t id, uint32_t name, int fd,
145 int32_t width, int32_t height, uint32_t format,
146 int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1,
147 int32_t offset2, int32_t stride2)
148 {
149 struct wl_drm *drm = resource->data;
150 struct wl_drm_buffer *buffer;
151
152 buffer = calloc(1, sizeof *buffer);
153 if (buffer == NULL) {
154 wl_resource_post_no_memory(resource);
155 return;
156 }
157
158 buffer->drm = drm;
159 buffer->width = width;
160 buffer->height = height;
161 buffer->format = format;
162 buffer->fd = fd;
163 buffer->offset[0] = offset0;
164 buffer->stride[0] = stride0;
165 buffer->offset[1] = offset1;
166 buffer->stride[1] = stride1;
167 buffer->offset[2] = offset2;
168 buffer->stride[2] = stride2;
169 trace(TRACE_DRM, "create:%"PRIu32",%"PRIu32", fd:%d", width, height, fd);
170
171 /*
172 * these are just for hooking into the 'next' drm buffer layer
173 * and thus not needed here
174 drm->callbacks->reference_buffer(drm->user_data, name, fd, buffer);
175 if (buffer->driver_buffer == NULL){
176 wl_resource_post_error(
177 resource, WL_DRM_ERROR_INVALID_NAME, "invalid name");
178 return;
179 }
180 */
181
182 buffer->resource = wl_resource_create(client, &wl_buffer_interface, 1, id);
183 if (!buffer->resource) {
184 wl_resource_post_no_memory(resource);
185 free(buffer);
186 return;
187 }
188
189 wl_resource_set_implementation(
190 buffer->resource, (void (**)(void)) &drm->buffer_interface,
191 buffer, destroy_buffer
192 );
193 }
194
195 static void
drm_create_buffer(struct wl_client * client,struct wl_resource * resource,uint32_t id,uint32_t name,int32_t width,int32_t height,uint32_t stride,uint32_t format)196 drm_create_buffer(struct wl_client *client, struct wl_resource *resource,
197 uint32_t id, uint32_t name, int32_t width, int32_t height,
198 uint32_t stride, uint32_t format)
199 {
200 switch (format) {
201 case WL_DRM_FORMAT_ARGB8888:
202 case WL_DRM_FORMAT_XRGB8888:
203 case WL_DRM_FORMAT_YUYV:
204 case WL_DRM_FORMAT_RGB565:
205 break;
206 default:
207 wl_resource_post_error(resource,
208 WL_DRM_ERROR_INVALID_FORMAT, "invalid format");
209 return;
210 }
211
212 create_buffer(client, resource, id,
213 name, -1, width, height, format, 0, stride, 0, 0, 0, 0);
214 }
215
216 static void
drm_create_planar_buffer(struct wl_client * client,struct wl_resource * resource,uint32_t id,uint32_t name,int32_t width,int32_t height,uint32_t format,int32_t offset0,int32_t stride0,int32_t offset1,int32_t stride1,int32_t offset2,int32_t stride2)217 drm_create_planar_buffer(struct wl_client *client,
218 struct wl_resource *resource,
219 uint32_t id, uint32_t name,
220 int32_t width, int32_t height, uint32_t format,
221 int32_t offset0, int32_t stride0,
222 int32_t offset1, int32_t stride1,
223 int32_t offset2, int32_t stride2)
224 {
225 trace(TRACE_DRM,
226 "%"PRId32",%"PRId32" fmt:%"PRId32, width, height, format);
227
228 switch (format) {
229 case WL_DRM_FORMAT_YUV410:
230 case WL_DRM_FORMAT_YUV411:
231 case WL_DRM_FORMAT_YUV420:
232 case WL_DRM_FORMAT_YUV422:
233 case WL_DRM_FORMAT_YUV444:
234 case WL_DRM_FORMAT_NV12:
235 case WL_DRM_FORMAT_NV16:
236 break;
237 default:
238 wl_resource_post_error(resource,
239 WL_DRM_ERROR_INVALID_FORMAT, "invalid format");
240 return;
241 }
242
243 create_buffer(client, resource, id, name, -1, width, height,
244 format, offset0, stride0, offset1, stride1, offset2, stride2);
245 }
246
247 static void
drm_create_prime_buffer(struct wl_client * client,struct wl_resource * resource,uint32_t id,int fd,int32_t width,int32_t height,uint32_t format,int32_t offset0,int32_t stride0,int32_t offset1,int32_t stride1,int32_t offset2,int32_t stride2)248 drm_create_prime_buffer(struct wl_client *client,
249 struct wl_resource *resource,
250 uint32_t id, int fd,
251 int32_t width, int32_t height, uint32_t format,
252 int32_t offset0, int32_t stride0,
253 int32_t offset1, int32_t stride1,
254 int32_t offset2, int32_t stride2)
255 {
256 trace(TRACE_DRM,
257 "%"PRId32",%"PRId32" fmt:%"PRId32, width, height, format);
258 create_buffer(client, resource, id, 0, fd, width, height, format,
259 offset0, stride0, offset1, stride1, offset2, stride2);
260 /* buffer close part of shmif */
261 }
262
263 static void
drm_authenticate(struct wl_client * client,struct wl_resource * resource,uint32_t id)264 drm_authenticate(struct wl_client *client,
265 struct wl_resource *resource, uint32_t id)
266 {
267 /*
268 * there is the full- GPU sharing to support authenticating
269 * like this, uncertain if we actually need to do more though
270 *
271 struct wl_drm *drm = resource->data;
272 if (drm->callbacks->authenticate(drm->user_data, id) < 0)
273 wl_resource_post_error(resource,
274 WL_DRM_ERROR_AUTHENTICATE_FAIL,
275 "authenicate failed");
276 else
277 */
278 wl_resource_post_event(resource, WL_DRM_AUTHENTICATED);
279 }
280
281 static const struct wl_drm_interface drm_interface = {
282 drm_authenticate,
283 drm_create_buffer,
284 drm_create_planar_buffer,
285 drm_create_prime_buffer
286 };
287
288 static void
bind_drm(struct wl_client * client,void * data,uint32_t version,uint32_t id)289 bind_drm(struct wl_client *client, void *data, uint32_t version, uint32_t id)
290 {
291 struct wl_drm *drm = data;
292 struct wl_resource *resource;
293 uint32_t capabilities;
294
295 resource = wl_resource_create(client,
296 &wl_drm_interface, MIN(version, 2), id);
297 if (!resource) {
298 wl_client_post_no_memory(client);
299 return;
300 }
301
302 wl_resource_set_implementation(resource, &drm_interface, data, NULL);
303
304 /*
305 * This should really be dynamic
306 */
307 wl_resource_post_event(resource, WL_DRM_DEVICE, drm->device_name);
308
309 /*
310 * Other formats:
311 * - ARGB2101010
312 * - XRGB2101010
313 * - ABGR2101010
314 * - XBGR2101010
315 */
316 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_ARGB8888);
317 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_XRGB8888);
318 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_RGB565);
319 /*
320 * Upstream shader processing doesn't cover these yet for sampling
321 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_YUV410);
322 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_YUV411);
323 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_YUV420);
324 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_YUV422);
325 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_YUV444);
326 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_NV12);
327 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_NV16);
328 wl_resource_post_event(resource, WL_DRM_FORMAT, WL_DRM_FORMAT_YUYV);
329 */
330
331 /* we only support render nodes, not GEM */
332 capabilities = WL_DRM_CAPABILITY_PRIME;
333
334 if (version >= 2)
335 wl_resource_post_event(resource, WL_DRM_CAPABILITIES, capabilities);
336 }
337
buffer_to_plane(struct wl_drm_buffer * buf)338 static struct shmifext_buffer_plane buffer_to_plane(struct wl_drm_buffer* buf)
339 {
340 return (struct shmifext_buffer_plane){
341 .fd = arcan_shmif_dupfd(buf->fd, -1, false),
342 .fence = -1,
343 .w = buf->width,
344 .h = buf->height,
345 .gbm = {
346 .format = buf->format,
347 .stride = buf->stride[0],
348 .offset = buf->offset[0]
349 }
350 };
351 }
352
wayland_drm_commit(struct comp_surf * surf,struct wl_drm_buffer * buf,struct arcan_shmif_cont * con)353 static void wayland_drm_commit(struct comp_surf* surf,
354 struct wl_drm_buffer* buf, struct arcan_shmif_cont* con)
355 {
356 trace(TRACE_DRM, "");
357
358 if (buf->width != con->w || buf->height != con->h){
359 arcan_shmif_resize(con, buf->width, buf->height);
360 }
361
362 /* If it is not permitted to push handles onwards, we need to map the buffer
363 * into the wl.control context, and then signalhandle will take care of reading
364 * back into shm and forwarding - make sure that no new context is allocated
365 * as we want it allocated into the shared one */
366 if (!arcan_shmif_handle_permitted(con) ||
367 !arcan_shmif_handle_permitted(&wl.control)){
368 if (!arcan_shmifext_isext(con)){
369 trace(TRACE_DRM, "context allocated on blocked handle transfer");
370 struct arcan_shmifext_setup defs = arcan_shmifext_defaults(con);
371 defs.no_context = true;
372 arcan_shmifext_setup(con, defs);
373 }
374
375 /* the interface can deal with multiple planes to a buffer, though the current
376 * implementation restricts us to 1, so assume that for now - we need to dup
377 * the handle since the import procedure closes the handle */
378 struct shmifext_buffer_plane plane = buffer_to_plane(buf);
379
380 /* now we can readback into the store (possible asynch through a PBO if we have
381 * multiple clients and don't want to stall, or as a separate thread that
382 * imports the fence and waits on that */
383 if (arcan_shmifext_import_buffer(con,
384 SHMIFEXT_BUFFER_GBM, &plane, 1, sizeof(plane))){
385 trace(TRACE_DRM, "buffer imported, to readback");
386 arcan_shmifext_signal(con, 0, SHMIF_SIGVID, SHMIFEXT_BUILTIN);
387 }
388 else
389 close(plane.fd);
390
391 return;
392 }
393
394 /*
395 * The patches for the multi- buffer formats are coming in, but are not yet
396 * here - basically in this layer it should only be setting a flag on the
397 * SHMIF_SIGNAL to indicate a continuation, and repeat for all buffers
398 */
399 struct shmifext_buffer_plane planes[4] = {buffer_to_plane(buf)};
400
401 arcan_shmifext_signal_planes(con, SHMIF_SIGVID, 1, planes);
402 }
403
404 static struct wl_drm_buffer *
wayland_drm_buffer_get(struct wl_drm * drm,struct wl_resource * resource)405 wayland_drm_buffer_get(struct wl_drm *drm, struct wl_resource *resource)
406 {
407 trace(TRACE_DRM, "");
408 if (resource == NULL)
409 return NULL;
410
411 if (wl_resource_instance_of(resource,
412 &wl_buffer_interface, &drm->buffer_interface))
413 return wl_resource_get_user_data(resource);
414 else
415 return NULL;
416 }
417
wayland_drm_init(struct wl_display * display,char * device_name,void * user_data,uint32_t flags)418 static struct wl_drm* wayland_drm_init(
419 struct wl_display *display, char *device_name, void *user_data, uint32_t flags)
420 {
421 struct wl_drm *drm;
422
423 drm = malloc(sizeof *drm);
424 if (!drm)
425 return NULL;
426
427 /* this should switch to what we get from _initial as well as react on devicehint */
428 drm->display = display;
429 drm->device_name = getenv("ARCAN_RENDER_NODE");
430 drm->user_data = user_data;
431 drm->flags = flags;
432
433 drm->buffer_interface.destroy = drm_buffer_destroy;
434
435 drm->wl_drm_global =
436 wl_global_create(display, &wl_drm_interface, 2, drm, bind_drm);
437
438 return drm;
439 }
440
441 static void
wayland_drm_uninit(struct wl_drm * drm)442 wayland_drm_uninit(struct wl_drm *drm)
443 {
444 free(drm->device_name);
445 wl_global_destroy(drm->wl_drm_global);
446 free(drm);
447 }
448
449 static uint32_t
wayland_drm_buffer_get_format(struct wl_drm_buffer * buffer)450 wayland_drm_buffer_get_format(struct wl_drm_buffer *buffer)
451 {
452 return buffer->format;
453 }
454
455 static void *
wayland_drm_buffer_get_buffer(struct wl_drm_buffer * buffer)456 wayland_drm_buffer_get_buffer(struct wl_drm_buffer *buffer)
457 {
458 return buffer->driver_buffer;
459 }
460