1/* swc: dmabuf.c
2 *
3 * Copyright (c) 2019 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "dmabuf.h"
25#include "drm.h"
26#include "internal.h"
27#include "util.h"
28#include "wayland_buffer.h"
29
30#include "linux-dmabuf-unstable-v1-server-protocol.h"
31#include <drm_fourcc.h>
32#include <stdint.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <wld/drm.h>
36#include <wld/wld.h>
37
38struct params {
39 struct wl_resource *resource;
40 int fd[4];
41 uint32_t offset[4];
42 uint32_t stride[4];
43 uint64_t modifier[4];
44 bool created;
45};
46
47static void
48add(struct wl_client *client, struct wl_resource *resource, int32_t fd,
49 uint32_t i, uint32_t offset, uint32_t stride, uint32_t modifier_hi,
50 uint32_t modifier_lo)
51{
52 struct params *params = wl_resource_get_user_data(resource);
53
54 if (params->created) {
55 wl_resource_post_error(resource,
56 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
57 "buffer already created");
58 return;
59 }
60 if (i > ARRAY_LENGTH(params->fd)) {
61 wl_resource_post_error(resource,
62 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
63 "plane index too large");
64 return;
65 }
66 if (params->fd[i] != -1) {
67 wl_resource_post_error(resource,
68 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
69 "buffer plane already set");
70 return;
71 }
72 params->fd[i] = fd;
73 params->offset[i] = offset;
74 params->stride[i] = stride;
75 params->modifier[i] = (uint64_t)modifier_hi << 32 | modifier_lo;
76}
77
78static void
79create_immed(struct wl_client *client, struct wl_resource *resource,
80 uint32_t id, int32_t width, int32_t height, uint32_t format,
81 uint32_t flags)
82{
83 struct params *params = wl_resource_get_user_data(resource);
84 struct wld_buffer *buffer;
85 struct wl_resource *buffer_resource;
86 union wld_object object;
87 int num_planes, i;
88
89 if (params->created) {
90 wl_resource_post_error(resource,
91 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
92 "buffer already created");
93 return;
94 }
95 params->created = true;
96 switch (format) {
97 case DRM_FORMAT_XRGB8888:
98 case DRM_FORMAT_ARGB8888:
99 num_planes = 1;
100 break;
101 default:
102 wl_resource_post_error(resource,
103 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
104 "unsupported format %#" PRIx32, format);
105 return;
106 }
107 for (i = 0; i < num_planes; ++i) {
108 if (params->fd[i] == -1) {
109 wl_resource_post_error(resource,
110 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
111 "missing plane %d", i);
112 }
113 }
114 for (; i < ARRAY_LENGTH(params->fd); ++i) {
115 if (params->fd[i] != -1) {
116 wl_resource_post_error(resource,
117 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
118 "too many planes");
119 }
120 }
121 object.i = params->fd[0];
122 buffer =
123 wld_import_buffer(swc.drm->context, WLD_DRM_OBJECT_PRIME_FD, object,
124 width, height, format, params->stride[0]);
125 for (i = 0; i < num_planes; ++i) {
126 close(params->fd[i]);
127 params->fd[i] = -1;
128 }
129 if (!buffer) {
130 zwp_linux_buffer_params_v1_send_failed(resource);
131 }
132
133 buffer_resource = wayland_buffer_create_resource(client, 1, id, buffer);
134 if (!buffer_resource) {
135 if (buffer) {
136 wld_buffer_unreference(buffer);
137 }
138 wl_resource_post_no_memory(resource);
139 return;
140 }
141 if (id == 0 && buffer) {
142 zwp_linux_buffer_params_v1_send_created(resource, buffer_resource);
143 }
144}
145
146static void
147create(struct wl_client *client, struct wl_resource *resource, int32_t width,
148 int32_t height, uint32_t format, uint32_t flags)
149{
150 create_immed(client, resource, 0, width, height, format, flags);
151}
152
153static const struct zwp_linux_buffer_params_v1_interface params_impl = {
154 .destroy = destroy_resource,
155 .add = add,
156 .create = create,
157 .create_immed = create_immed,
158};
159
160static void
161params_destroy(struct wl_resource *resource)
162{
163 struct params *params = wl_resource_get_user_data(resource);
164 int i;
165
166 for (i = 0; i < ARRAY_LENGTH(params->fd); ++i) {
167 close(params->fd[i]);
168 }
169}
170
171static void
172create_params(struct wl_client *client, struct wl_resource *resource,
173 uint32_t id)
174{
175 struct params *params;
176 int i;
177
178 params = malloc(sizeof(*params));
179 if (!params) {
180 goto error0;
181 }
182 params->created = false;
183 params->resource =
184 wl_resource_create(client, &zwp_linux_buffer_params_v1_interface,
185 wl_resource_get_version(resource), id);
186 if (!params->resource) {
187 goto error1;
188 }
189 for (i = 0; i < ARRAY_LENGTH(params->fd); ++i) {
190 params->fd[i] = -1;
191 }
192 wl_resource_set_implementation(params->resource, ¶ms_impl, params,
193 params_destroy);
194 return;
195
196error1:
197 free(params);
198error0:
199 wl_resource_post_no_memory(resource);
200}
201
202static const struct zwp_linux_dmabuf_v1_interface dmabuf_impl = {
203 .destroy = destroy_resource,
204 .create_params = create_params,
205};
206
207static void
208bind_dmabuf(struct wl_client *client, void *data, uint32_t version, uint32_t id)
209{
210 static const uint32_t formats[] = {
211 DRM_FORMAT_XRGB8888,
212 DRM_FORMAT_ARGB8888,
213 };
214 uint64_t modifier = DRM_FORMAT_MOD_INVALID;
215 struct wl_resource *resource;
216 size_t i;
217
218 resource =
219 wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, version, id);
220 if (!resource) {
221 wl_client_post_no_memory(client);
222 return;
223 }
224 wl_resource_set_implementation(resource, &dmabuf_impl, NULL, NULL);
225 for (i = 0; i < ARRAY_LENGTH(formats); ++i) {
226 if (version >= 3) {
227 /* TODO: need a way to query DRM modifiers of wld */
228 zwp_linux_dmabuf_v1_send_modifier(
229 resource, formats[i], modifier >> 32, modifier & 0xffffffff);
230 } else {
231 zwp_linux_dmabuf_v1_send_format(resource, formats[i]);
232 }
233 }
234}
235
236struct wl_global *
237swc_dmabuf_create(struct wl_display *display)
238{
239 return wl_global_create(display, &zwp_linux_dmabuf_v1_interface, 3, NULL,
240 &bind_dmabuf);
241}