main neuswc / libswc / dmabuf.c
  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, &params_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}