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