blob: 52307edc07bca1bc31847425004a7976f62fe68d [file] [log] [blame]
Pekka Paalanen230f3b12014-09-29 14:18:40 -04001/*
2 * Copyright © 2014, 2015 Collabora, Ltd.
3 *
Yong Bakos53361532017-01-23 06:17:44 -08004 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
Pekka Paalanen230f3b12014-09-29 14:18:40 -040011 *
Yong Bakos53361532017-01-23 06:17:44 -080012 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the 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 HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
Pekka Paalanen230f3b12014-09-29 14:18:40 -040024 */
25
26#include "config.h"
27
28#include <assert.h>
Leandro Ribeiro8eb84142021-01-18 19:36:48 -030029#include <errno.h>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030030#include <stdint.h>
Pekka Paalanen230f3b12014-09-29 14:18:40 -040031#include <unistd.h>
Leandro Ribeiro8eb84142021-01-18 19:36:48 -030032#include <string.h>
33#include <sys/mman.h>
Pekka Paalanen230f3b12014-09-29 14:18:40 -040034#include <sys/types.h>
35
Pekka Paalanen3d5d9472019-03-28 16:28:47 +020036#include <libweston/libweston.h>
Pekka Paalanen230f3b12014-09-29 14:18:40 -040037#include "linux-dmabuf.h"
Jonas Ådahl57e48f02015-11-17 16:00:28 +080038#include "linux-dmabuf-unstable-v1-server-protocol.h"
Leandro Ribeiro8eb84142021-01-18 19:36:48 -030039#include "shared/os-compatibility.h"
Marius Vlad56f3a682019-07-10 14:48:39 +030040#include "libweston-internal.h"
Pekka Paalanen4b301fe2021-02-04 17:39:45 +020041#include "shared/weston-drm-fourcc.h"
Pekka Paalanen230f3b12014-09-29 14:18:40 -040042
43static void
44linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
45{
46 int i;
47
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000048 for (i = 0; i < buffer->attributes.n_planes; i++) {
49 close(buffer->attributes.fd[i]);
50 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040051 }
52
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +000053 buffer->attributes.n_planes = 0;
Pekka Paalanen230f3b12014-09-29 14:18:40 -040054 free(buffer);
55}
56
57static void
58destroy_params(struct wl_resource *params_resource)
59{
60 struct linux_dmabuf_buffer *buffer;
61
62 buffer = wl_resource_get_user_data(params_resource);
63
64 if (!buffer)
65 return;
66
67 linux_dmabuf_buffer_destroy(buffer);
68}
69
70static void
71params_destroy(struct wl_client *client, struct wl_resource *resource)
72{
73 wl_resource_destroy(resource);
74}
75
76static void
77params_add(struct wl_client *client,
78 struct wl_resource *params_resource,
79 int32_t name_fd,
80 uint32_t plane_idx,
81 uint32_t offset,
82 uint32_t stride,
83 uint32_t modifier_hi,
84 uint32_t modifier_lo)
85{
86 struct linux_dmabuf_buffer *buffer;
87
88 buffer = wl_resource_get_user_data(params_resource);
89 if (!buffer) {
90 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +080091 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -040092 "params was already used to create a wl_buffer");
93 close(name_fd);
94 return;
95 }
96
97 assert(buffer->params_resource == params_resource);
98 assert(!buffer->buffer_resource);
99
100 if (plane_idx >= MAX_DMABUF_PLANES) {
101 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800102 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400103 "plane index %u is too high", plane_idx);
104 close(name_fd);
105 return;
106 }
107
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000108 if (buffer->attributes.fd[plane_idx] != -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400109 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800110 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400111 "a dmabuf has already been added for plane %u",
112 plane_idx);
113 close(name_fd);
114 return;
115 }
116
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000117 buffer->attributes.fd[plane_idx] = name_fd;
118 buffer->attributes.offset[plane_idx] = offset;
119 buffer->attributes.stride[plane_idx] = stride;
Emre Ucan99ef8162018-01-09 14:00:30 +0100120
121 if (wl_resource_get_version(params_resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION)
122 buffer->attributes.modifier[plane_idx] = DRM_FORMAT_MOD_INVALID;
123 else
124 buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
125 modifier_lo;
126
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000127 buffer->attributes.n_planes++;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400128}
129
130static void
131linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
132 struct wl_resource *resource)
133{
134 wl_resource_destroy(resource);
135}
136
137static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
138 linux_dmabuf_wl_buffer_destroy
139};
140
141static void
142destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
143{
144 struct linux_dmabuf_buffer *buffer;
145
146 buffer = wl_resource_get_user_data(resource);
147 assert(buffer->buffer_resource == resource);
148 assert(!buffer->params_resource);
149
150 if (buffer->user_data_destroy_func)
151 buffer->user_data_destroy_func(buffer);
152
153 linux_dmabuf_buffer_destroy(buffer);
154}
155
156static void
Varad Gautam65c94b82017-04-26 19:15:59 +0530157params_create_common(struct wl_client *client,
158 struct wl_resource *params_resource,
159 uint32_t buffer_id,
160 int32_t width,
161 int32_t height,
162 uint32_t format,
163 uint32_t flags)
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400164{
165 struct linux_dmabuf_buffer *buffer;
166 int i;
167
168 buffer = wl_resource_get_user_data(params_resource);
169
170 if (!buffer) {
171 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800172 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400173 "params was already used to create a wl_buffer");
174 return;
175 }
176
177 assert(buffer->params_resource == params_resource);
178 assert(!buffer->buffer_resource);
179
180 /* Switch the linux_dmabuf_buffer object from params resource to
181 * eventually wl_buffer resource.
182 */
183 wl_resource_set_user_data(buffer->params_resource, NULL);
184 buffer->params_resource = NULL;
185
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000186 if (!buffer->attributes.n_planes) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400187 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800188 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400189 "no dmabuf has been added to the params");
190 goto err_out;
191 }
192
193 /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000194 for (i = 0; i < buffer->attributes.n_planes; i++) {
195 if (buffer->attributes.fd[i] == -1) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400196 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800197 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400198 "no dmabuf has been added for plane %i", i);
199 goto err_out;
200 }
201 }
202
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000203 buffer->attributes.width = width;
204 buffer->attributes.height = height;
205 buffer->attributes.format = format;
206 buffer->attributes.flags = flags;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400207
208 if (width < 1 || height < 1) {
209 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800210 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400211 "invalid width %d or height %d", width, height);
212 goto err_out;
213 }
214
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000215 for (i = 0; i < buffer->attributes.n_planes; i++) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400216 off_t size;
217
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000218 if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400219 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800220 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400221 "size overflow for plane %i", i);
222 goto err_out;
223 }
224
225 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000226 (uint64_t) buffer->attributes.offset[i] +
227 (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400228 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800229 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400230 "size overflow for plane %i", i);
231 goto err_out;
232 }
233
234 /* Don't report an error as it might be caused
235 * by the kernel not supporting seeking on dmabuf */
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000236 size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400237 if (size == -1)
Derek Foremanc06389a2016-04-25 09:23:24 -0500238 continue;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400239
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000240 if (buffer->attributes.offset[i] >= size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400241 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800242 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400243 "invalid offset %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000244 buffer->attributes.offset[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400245 goto err_out;
246 }
247
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000248 if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400249 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800250 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400251 "invalid stride %i for plane %i",
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000252 buffer->attributes.stride[i], i);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400253 goto err_out;
254 }
255
256 /* Only valid for first plane as other planes might be
257 * sub-sampled according to fourcc format */
258 if (i == 0 &&
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000259 buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400260 wl_resource_post_error(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800261 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400262 "invalid buffer stride or height for plane %i", i);
263 goto err_out;
264 }
265 }
266
Marius Vlad5a701542019-11-16 20:26:52 +0200267 if (buffer->direct_display) {
268 if (!weston_compositor_dmabuf_can_scanout(buffer->compositor,
269 buffer))
270 goto err_failed;
271
272 goto avoid_gpu_import;
273 }
274
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400275 if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
276 goto err_failed;
277
Marius Vlad5a701542019-11-16 20:26:52 +0200278avoid_gpu_import:
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400279 buffer->buffer_resource = wl_resource_create(client,
280 &wl_buffer_interface,
Varad Gautam65c94b82017-04-26 19:15:59 +0530281 1, buffer_id);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400282 if (!buffer->buffer_resource) {
283 wl_resource_post_no_memory(params_resource);
284 goto err_buffer;
285 }
286
287 wl_resource_set_implementation(buffer->buffer_resource,
288 &linux_dmabuf_buffer_implementation,
289 buffer, destroy_linux_dmabuf_wl_buffer);
290
Varad Gautam65c94b82017-04-26 19:15:59 +0530291 /* send 'created' event when the request is not for an immediate
292 * import, ie buffer_id is zero */
293 if (buffer_id == 0)
294 zwp_linux_buffer_params_v1_send_created(params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800295 buffer->buffer_resource);
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400296
297 return;
298
299err_buffer:
300 if (buffer->user_data_destroy_func)
301 buffer->user_data_destroy_func(buffer);
302
303err_failed:
Varad Gautam65c94b82017-04-26 19:15:59 +0530304 if (buffer_id == 0)
305 zwp_linux_buffer_params_v1_send_failed(params_resource);
306 else
307 /* since the behavior is left implementation defined by the
308 * protocol in case of create_immed failure due to an unknown cause,
309 * we choose to treat it as a fatal error and immediately kill the
310 * client instead of creating an invalid handle and waiting for it
311 * to be used.
312 */
313 wl_resource_post_error(params_resource,
314 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
315 "importing the supplied dmabufs failed");
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400316
317err_out:
318 linux_dmabuf_buffer_destroy(buffer);
319}
320
Varad Gautam65c94b82017-04-26 19:15:59 +0530321static void
322params_create(struct wl_client *client,
323 struct wl_resource *params_resource,
324 int32_t width,
325 int32_t height,
326 uint32_t format,
327 uint32_t flags)
328{
329 params_create_common(client, params_resource, 0, width, height, format,
330 flags);
331}
332
333static void
334params_create_immed(struct wl_client *client,
335 struct wl_resource *params_resource,
336 uint32_t buffer_id,
337 int32_t width,
338 int32_t height,
339 uint32_t format,
340 uint32_t flags)
341{
342 params_create_common(client, params_resource, buffer_id, width, height,
343 format, flags);
344}
345
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800346static const struct zwp_linux_buffer_params_v1_interface
347zwp_linux_buffer_params_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400348 params_destroy,
349 params_add,
Varad Gautam65c94b82017-04-26 19:15:59 +0530350 params_create,
351 params_create_immed
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400352};
353
354static void
355linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
356{
357 wl_resource_destroy(resource);
358}
359
360static void
361linux_dmabuf_create_params(struct wl_client *client,
362 struct wl_resource *linux_dmabuf_resource,
363 uint32_t params_id)
364{
365 struct weston_compositor *compositor;
366 struct linux_dmabuf_buffer *buffer;
367 uint32_t version;
368 int i;
369
370 version = wl_resource_get_version(linux_dmabuf_resource);
371 compositor = wl_resource_get_user_data(linux_dmabuf_resource);
372
373 buffer = zalloc(sizeof *buffer);
374 if (!buffer)
375 goto err_out;
376
377 for (i = 0; i < MAX_DMABUF_PLANES; i++)
Emmanuel Gil Peyrotc3996922015-11-24 19:28:24 +0000378 buffer->attributes.fd[i] = -1;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400379
380 buffer->compositor = compositor;
381 buffer->params_resource =
382 wl_resource_create(client,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800383 &zwp_linux_buffer_params_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400384 version, params_id);
Marius Vlad5a701542019-11-16 20:26:52 +0200385 buffer->direct_display = false;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400386 if (!buffer->params_resource)
387 goto err_dealloc;
388
389 wl_resource_set_implementation(buffer->params_resource,
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800390 &zwp_linux_buffer_params_implementation,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400391 buffer, destroy_params);
392
393 return;
394
395err_dealloc:
396 free(buffer);
397
398err_out:
399 wl_resource_post_no_memory(linux_dmabuf_resource);
400}
401
Leandro Ribeiro8eb84142021-01-18 19:36:48 -0300402/** Creates dma-buf feedback tranche
403 *
404 * The tranche is added to dma-buf feedback's tranche list
405 *
406 * @param dmabuf_feedback The dma-buf feedback object to which the tranche is added
407 * @param format_table The dma-buf feedback formats table
408 * @param target_device The target device of the new tranche
409 * @param flags The flags of the new tranche
410 * @param preference The preference of the new tranche
411 * @return The tranche created, or NULL on failure
412 */
413WL_EXPORT struct weston_dmabuf_feedback_tranche *
414weston_dmabuf_feedback_tranche_create(struct weston_dmabuf_feedback *dmabuf_feedback,
415 struct weston_dmabuf_feedback_format_table *format_table,
416 dev_t target_device, uint32_t flags,
417 enum weston_dmabuf_feedback_tranche_preference preference)
418{
419 struct weston_dmabuf_feedback_tranche *tranche, *ptr;
420 struct wl_list *pos;
421
422 tranche = zalloc(sizeof(*tranche));
423 if (!tranche) {
424 weston_log("%s: out of memory\n", __func__);
425 return NULL;
426 }
427
428 tranche->target_device = target_device;
429 tranche->flags = flags;
430 tranche->preference = preference;
431
432 /* Get the formats indices array */
433 if (flags == 0) {
434 if (wl_array_copy(&tranche->formats_indices,
435 &format_table->renderer_formats_indices) < 0) {
436 weston_log("%s: out of memory\n", __func__);
437 goto err;
438 }
439 } else if (flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT) {
440 /* we still don't support scanout tranches */
441 assert(0);
442 } else {
443 weston_log("error: for now we just have renderer and scanout "
444 "tranches, can't create other type of tranche\n");
445 goto err;
446 }
447
448 /* The list of tranches is ordered by preference.
449 * Highest preference comes first. */
450 pos = &dmabuf_feedback->tranche_list;
451 wl_list_for_each(ptr, &dmabuf_feedback->tranche_list, link) {
452 pos = &ptr->link;
453 if (ptr->preference <= tranche->preference)
454 break;
455 }
456 wl_list_insert(pos->prev, &tranche->link);
457
458 return tranche;
459
460err:
461 free(tranche);
462 return NULL;
463}
464
465static void
466weston_dmabuf_feedback_tranche_destroy(struct weston_dmabuf_feedback_tranche *tranche)
467{
468 wl_array_release(&tranche->formats_indices);
469 wl_list_remove(&tranche->link);
470 free(tranche);
471}
472
473static int
474format_table_add_renderer_formats(struct weston_dmabuf_feedback_format_table *format_table,
475 const struct weston_drm_format_array *renderer_formats)
476{
477 struct weston_drm_format *fmt;
478 unsigned int num_modifiers;
479 const uint64_t *modifiers;
480 uint16_t index, *index_ptr;
481 unsigned int size;
482 unsigned int i;
483
484 size = sizeof(index) *
485 weston_drm_format_array_count_pairs(renderer_formats);
486
487 if (!wl_array_add(&format_table->renderer_formats_indices, size)) {
488 weston_log("%s: out of memory\n", __func__);
489 return -1;
490 }
491
492 index = 0;
493 wl_array_for_each(fmt, &renderer_formats->arr) {
494 modifiers = weston_drm_format_get_modifiers(fmt, &num_modifiers);
495 for (i = 0; i < num_modifiers; i++) {
496 format_table->data[index].format = fmt->format;
497 format_table->data[index].modifier = modifiers[i];
498 index++;
499 }
500 }
501
502 index = 0;
503 wl_array_for_each(index_ptr, &format_table->renderer_formats_indices)
504 *index_ptr = index++;
505
506 return 0;
507}
508
509/** Creates dma-buf feedback format table
510 *
511 * @param renderer_formats The formats that compose the table
512 * @return The dma-buf feedback format table, or NULL on failure
513 */
514WL_EXPORT struct weston_dmabuf_feedback_format_table *
515weston_dmabuf_feedback_format_table_create(const struct weston_drm_format_array *renderer_formats)
516{
517 struct weston_dmabuf_feedback_format_table *format_table;
518 int ret;
519
520 format_table = zalloc(sizeof(*format_table));
521 if (!format_table) {
522 weston_log("%s: out of memory\n", __func__);
523 return NULL;
524 }
525 wl_array_init(&format_table->renderer_formats_indices);
526
527 /* Creates formats file table and mmap it */
528 format_table->size = weston_drm_format_array_count_pairs(renderer_formats) *
529 sizeof(*format_table->data);
530 format_table->fd = os_create_anonymous_file(format_table->size);
531 if (format_table->fd < 0) {
532 weston_log("error: failed to create format table file: %s\n",
533 strerror(errno));
534 goto err_fd;
535 }
536 format_table->data = mmap(NULL, format_table->size, PROT_READ | PROT_WRITE,
537 MAP_SHARED, format_table->fd, 0);
538 if (format_table->data == MAP_FAILED) {
539 weston_log("error: mmap for format table failed: %s\n",
540 strerror(errno));
541 goto err_mmap;
542 }
543
544 /* Add renderer formats to file table */
545 ret = format_table_add_renderer_formats(format_table, renderer_formats);
546 if (ret < 0)
547 goto err_formats;
548
549 return format_table;
550
551err_formats:
552 munmap(format_table->data, format_table->size);
553err_mmap:
554 close(format_table->fd);
555err_fd:
556 wl_array_release(&format_table->renderer_formats_indices);
557 free(format_table);
558 return NULL;
559}
560
561/** Destroys dma-buf feedback formats table
562 *
563 * @param format_table The dma-buf feedback format table to destroy
564 */
565WL_EXPORT void
566weston_dmabuf_feedback_format_table_destroy(struct weston_dmabuf_feedback_format_table *format_table)
567{
568 wl_array_release(&format_table->renderer_formats_indices);
569
570 munmap(format_table->data, format_table->size);
571 close(format_table->fd);
572
573 free(format_table);
574}
575
576/** Creates dma-buf feedback object
577 *
578 * @param main_device The main device of the dma-buf feedback
579 * @return The dma-buf feedback object created, or NULL on failure
580 */
581WL_EXPORT struct weston_dmabuf_feedback *
582weston_dmabuf_feedback_create(dev_t main_device)
583{
584 struct weston_dmabuf_feedback *dmabuf_feedback;
585
586 dmabuf_feedback = zalloc(sizeof(*dmabuf_feedback));
587 if (!dmabuf_feedback) {
588 weston_log("%s: out of memory\n", __func__);
589 return NULL;
590 }
591
592 dmabuf_feedback->main_device = main_device;
593 wl_list_init(&dmabuf_feedback->tranche_list);
594 wl_list_init(&dmabuf_feedback->resource_list);
595
596 return dmabuf_feedback;
597}
598
599/** Destroy dma-buf feedback object
600 *
601 * @param dmabuf_feedback The dma-buf feedback object to destroy
602 */
603WL_EXPORT void
604weston_dmabuf_feedback_destroy(struct weston_dmabuf_feedback *dmabuf_feedback)
605{
606 struct weston_dmabuf_feedback_tranche *tranche, *tranche_tmp;
607 struct wl_resource *res, *res_tmp;
608
609 wl_list_for_each_safe(tranche, tranche_tmp, &dmabuf_feedback->tranche_list, link)
610 weston_dmabuf_feedback_tranche_destroy(tranche);
611
612 wl_resource_for_each_safe(res, res_tmp, &dmabuf_feedback->resource_list) {
613 wl_list_remove(wl_resource_get_link(res));
614 wl_list_init(wl_resource_get_link(res));
615 }
616
617 free(dmabuf_feedback);
618}
619
620static void
621weston_dmabuf_feedback_send(struct weston_dmabuf_feedback *dmabuf_feedback,
622 struct weston_dmabuf_feedback_format_table *format_table,
623 struct wl_resource *res, bool advertise_format_table)
624{
625 struct weston_dmabuf_feedback_tranche *tranche;
626 struct wl_array device;
627 dev_t *dev;
628
629 /* main_device and target_device events need a dev_t as parameter,
630 * but we can't use this directly to communicate with the Wayland
631 * client. The solution is to use a wl_array, which is supported by
632 * Wayland, and add the dev_t as an element of the array. */
633 wl_array_init(&device);
634 dev = wl_array_add(&device, sizeof(*dev));
635 if (!dev) {
636 wl_resource_post_no_memory(res);
637 return;
638 }
639
640 /* format_table event - In Weston, we never modify the dma-buf feedback
641 * format table. So we have this flag in order to advertise the format
642 * table only if the client has just subscribed to receive the events
643 * for this feedback object. When we need to re-send the feedback events
644 * for this client, the table event won't be sent. */
645 if (advertise_format_table)
646 zwp_linux_dmabuf_feedback_v1_send_format_table(res, format_table->fd,
647 format_table->size);
648
649 /* main_device event */
650 *dev = dmabuf_feedback->main_device;
651 zwp_linux_dmabuf_feedback_v1_send_main_device(res, &device);
652
653 /* send events for each tranche */
654 wl_list_for_each(tranche, &dmabuf_feedback->tranche_list, link) {
655 /* tranche_target_device event */
656 *dev = tranche->target_device;
657 zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(res, &device);
658
659 /* tranche_flags event */
660 zwp_linux_dmabuf_feedback_v1_send_tranche_flags(res, tranche->flags);
661
662 /* tranche_formats event */
663 zwp_linux_dmabuf_feedback_v1_send_tranche_formats(res, &tranche->formats_indices);
664
665 /* tranche_done_event */
666 zwp_linux_dmabuf_feedback_v1_send_tranche_done(res);
667 }
668
669 /* compositor_done_event */
670 zwp_linux_dmabuf_feedback_v1_send_done(res);
671
672 wl_array_release(&device);
673}
674
675static void
676dmabuf_feedback_resource_destroy(struct wl_resource *resource)
677{
678 wl_list_remove(wl_resource_get_link(resource));
679}
680
681static void
682dmabuf_feedback_destroy(struct wl_client *client, struct wl_resource *resource)
683{
684 wl_resource_destroy(resource);
685}
686
687static const struct zwp_linux_dmabuf_feedback_v1_interface
688zwp_linux_dmabuf_feedback_implementation = {
689 dmabuf_feedback_destroy
690};
691
692static struct wl_resource *
693dmabuf_feedback_resource_create(struct wl_resource *dmabuf_resource,
694 struct wl_client *client, uint32_t dmabuf_feedback_id)
695{
696 struct wl_resource *dmabuf_feedback_res;
697 uint32_t version;
698
699 version = wl_resource_get_version(dmabuf_resource);
700
701 dmabuf_feedback_res =
702 wl_resource_create(client, &zwp_linux_dmabuf_feedback_v1_interface,
703 version, dmabuf_feedback_id);
704 if (!dmabuf_feedback_res)
705 return NULL;
706
707 wl_list_init(wl_resource_get_link(dmabuf_feedback_res));
708 wl_resource_set_implementation(dmabuf_feedback_res,
709 &zwp_linux_dmabuf_feedback_implementation,
710 NULL, dmabuf_feedback_resource_destroy);
711
712 return dmabuf_feedback_res;
713}
714
715static void
716linux_dmabuf_get_default_feedback(struct wl_client *client,
717 struct wl_resource *dmabuf_resource,
718 uint32_t dmabuf_feedback_id)
719{
720 struct weston_compositor *compositor =
721 wl_resource_get_user_data(dmabuf_resource);
722 struct wl_resource *dmabuf_feedback_resource;
723
724 dmabuf_feedback_resource =
725 dmabuf_feedback_resource_create(dmabuf_resource,
726 client, dmabuf_feedback_id);
727 if (!dmabuf_feedback_resource) {
728 wl_resource_post_no_memory(dmabuf_resource);
729 return;
730 }
731
732 weston_dmabuf_feedback_send(compositor->default_dmabuf_feedback,
733 compositor->dmabuf_feedback_format_table,
734 dmabuf_feedback_resource, true);
735}
736
737static void
738linux_dmabuf_get_per_surface_feedback(struct wl_client *client,
739 struct wl_resource *dmabuf_resource,
740 uint32_t dmabuf_feedback_id,
741 struct wl_resource *surface_resource)
742{
743 struct weston_surface *surface =
744 wl_resource_get_user_data(surface_resource);
745 struct wl_resource *dmabuf_feedback_resource;
746
747 dmabuf_feedback_resource =
748 dmabuf_feedback_resource_create(dmabuf_resource,
749 client, dmabuf_feedback_id);
750 if (!dmabuf_feedback_resource) {
751 wl_resource_post_no_memory(dmabuf_resource);
752 return;
753 }
754
755 /* Surface dma-buf feedback is dynamic and may need to be resent to
756 * clients when they change. So we need to keep the resources list */
757 wl_list_insert(&surface->dmabuf_feedback->resource_list,
758 wl_resource_get_link(dmabuf_feedback_resource));
759
760 weston_dmabuf_feedback_send(surface->dmabuf_feedback,
761 surface->compositor->dmabuf_feedback_format_table,
762 dmabuf_feedback_resource, true);
763}
764
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400765/** Get the linux_dmabuf_buffer from a wl_buffer resource
766 *
767 * If the given wl_buffer resource was created through the linux_dmabuf
768 * protocol interface, returns the linux_dmabuf_buffer object. This can
769 * be used as a type check for a wl_buffer.
770 *
771 * \param resource A wl_buffer resource.
772 * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
773 */
774WL_EXPORT struct linux_dmabuf_buffer *
775linux_dmabuf_buffer_get(struct wl_resource *resource)
776{
777 struct linux_dmabuf_buffer *buffer;
778
779 if (!resource)
780 return NULL;
781
782 if (!wl_resource_instance_of(resource, &wl_buffer_interface,
783 &linux_dmabuf_buffer_implementation))
784 return NULL;
785
786 buffer = wl_resource_get_user_data(resource);
787 assert(buffer);
788 assert(!buffer->params_resource);
789 assert(buffer->buffer_resource == resource);
790
791 return buffer;
792}
793
794/** Set renderer-private data
795 *
796 * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
797 * a non-NULL user data with a new non-NULL pointer. This is meant to
798 * protect against renderers fighting over linux_dmabuf_buffer user data
799 * ownership.
800 *
801 * The renderer-private data is usually set from the
802 * weston_renderer::import_dmabuf hook.
803 *
804 * \param buffer The linux_dmabuf_buffer object to set for.
805 * \param data The new renderer-private data pointer.
806 * \param func Destructor function to be called for the renderer-private
807 * data when the linux_dmabuf_buffer gets destroyed.
808 *
809 * \sa weston_compositor_import_dmabuf
810 */
811WL_EXPORT void
812linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
813 void *data,
814 dmabuf_user_data_destroy_func func)
815{
816 assert(data == NULL || buffer->user_data == NULL);
817
818 buffer->user_data = data;
819 buffer->user_data_destroy_func = func;
820}
821
822/** Get renderer-private data
823 *
824 * Get the user data from the linux_dmabuf_buffer.
825 *
826 * \param buffer The linux_dmabuf_buffer to query.
827 * \return Renderer-private data pointer.
828 *
829 * \sa linux_dmabuf_buffer_set_user_data
830 */
831WL_EXPORT void *
832linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
833{
834 return buffer->user_data;
835}
836
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800837static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400838 linux_dmabuf_destroy,
Leandro Ribeiro8eb84142021-01-18 19:36:48 -0300839 linux_dmabuf_create_params,
840 linux_dmabuf_get_default_feedback,
841 linux_dmabuf_get_per_surface_feedback
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400842};
843
844static void
845bind_linux_dmabuf(struct wl_client *client,
846 void *data, uint32_t version, uint32_t id)
847{
848 struct weston_compositor *compositor = data;
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300849 const struct weston_drm_format_array *supported_formats;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400850 struct wl_resource *resource;
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300851 struct weston_drm_format *fmt;
852 const uint64_t *modifiers;
853 unsigned int num_modifiers;
854 unsigned int i;
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400855
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800856 resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400857 version, id);
858 if (resource == NULL) {
859 wl_client_post_no_memory(client);
860 return;
861 }
862
863 wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
864 compositor, NULL);
865
Leandro Ribeiro8eb84142021-01-18 19:36:48 -0300866 /* Advertise formats/modifiers. From version 4 onwards, we should not send
867 * zwp_linux_dmabuf_v1_send_modifier and zwp_linux_dmabuf_v1_send_format
868 * events, instead we must send the dma-buf feedback events. */
869 if (version >= 4)
870 return;
871
Leandro Ribeiro18f53ab2021-10-18 11:27:54 -0300872 /* If we got here, it means that the renderer is able to import dma-buf
873 * buffers, and so it must have get_supported_formats() set. */
874 assert(compositor->renderer->get_supported_formats != NULL);
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300875 supported_formats = compositor->renderer->get_supported_formats(compositor);
Leandro Ribeiro18f53ab2021-10-18 11:27:54 -0300876
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300877 wl_array_for_each(fmt, &supported_formats->arr) {
878 modifiers = weston_drm_format_get_modifiers(fmt, &num_modifiers);
879 for (i = 0; i < num_modifiers; i++) {
Michael Tretterb0a749d2018-01-17 17:54:31 +0100880 if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300881 uint32_t modifier_lo = modifiers[i] & 0xFFFFFFFF;
882 uint32_t modifier_hi = modifiers[i] >> 32;
Michael Tretterb0a749d2018-01-17 17:54:31 +0100883 zwp_linux_dmabuf_v1_send_modifier(resource,
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300884 fmt->format,
Michael Tretterb0a749d2018-01-17 17:54:31 +0100885 modifier_hi,
886 modifier_lo);
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300887 } else if (modifiers[i] == DRM_FORMAT_MOD_LINEAR ||
888 modifiers[i] == DRM_FORMAT_MOD_INVALID) {
Michael Tretterb0a749d2018-01-17 17:54:31 +0100889 zwp_linux_dmabuf_v1_send_format(resource,
Leandro Ribeiro1b403262021-03-04 17:47:13 -0300890 fmt->format);
Michael Tretterb0a749d2018-01-17 17:54:31 +0100891 }
Varad Gautam41b4b8f2017-04-26 19:17:17 +0530892 }
Varad Gautam41b4b8f2017-04-26 19:17:17 +0530893 }
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400894}
895
896/** Advertise linux_dmabuf support
897 *
Jonas Ådahl57e48f02015-11-17 16:00:28 +0800898 * Calling this initializes the zwp_linux_dmabuf protocol support, so that
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400899 * the interface will be advertised to clients. Essentially it creates a
900 * global. Do not call this function multiple times in the compositor's
901 * lifetime. There is no way to deinit explicitly, globals will be reaped
902 * when the wl_display gets destroyed.
903 *
904 * \param compositor The compositor to init for.
905 * \return Zero on success, -1 on failure.
906 */
907WL_EXPORT int
908linux_dmabuf_setup(struct weston_compositor *compositor)
909{
Leandro Ribeiro8eb84142021-01-18 19:36:48 -0300910 int max_version;
911
912 /* If we were able to create the default dma-buf feedback for the
913 * compositor, that means that we are able to advertise dma-buf feedback
914 * events. In such case we support the version 4 of the protocol. */
915 max_version = compositor->default_dmabuf_feedback ? 4 : 3;
916
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400917 if (!wl_global_create(compositor->wl_display,
Leandro Ribeiro8eb84142021-01-18 19:36:48 -0300918 &zwp_linux_dmabuf_v1_interface,
919 max_version, compositor, bind_linux_dmabuf))
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400920 return -1;
921
922 return 0;
923}
924
925/** Resolve an internal compositor error by disconnecting the client.
926 *
927 * This function is used in cases when the dmabuf-based wl_buffer
928 * turns out unusable and there is no fallback path. This is used by
929 * renderers which are the fallback path in the first place.
930 *
931 * It is possible the fault is caused by a compositor bug, the underlying
932 * graphics stack bug or normal behaviour, or perhaps a client mistake.
933 * In any case, the options are to either composite garbage or nothing,
934 * or disconnect the client. This is a helper function for the latter.
935 *
Bryce Harringtonba63fae2016-07-06 15:30:54 -0700936 * The error is sent as an INVALID_OBJECT error on the client's wl_display.
Pekka Paalanen230f3b12014-09-29 14:18:40 -0400937 *
938 * \param buffer The linux_dmabuf_buffer that is unusable.
939 * \param msg A custom error message attached to the protocol error.
940 */
941WL_EXPORT void
942linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
943 const char *msg)
944{
945 struct wl_client *client;
946 struct wl_resource *display_resource;
947 uint32_t id;
948
949 assert(buffer->buffer_resource);
950 id = wl_resource_get_id(buffer->buffer_resource);
951 client = wl_resource_get_client(buffer->buffer_resource);
952 display_resource = wl_client_get_object(client, 1);
953
954 assert(display_resource);
955 wl_resource_post_error(display_resource,
956 WL_DISPLAY_ERROR_INVALID_OBJECT,
957 "linux_dmabuf server error with "
958 "wl_buffer@%u: %s", id, msg);
959}