main dumb.c
  1/* wld: dumb.c
  2 *
  3 * Copyright (c) 2013, 2014 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 "drm-private.h"
 25#include "drm.h"
 26#include "pixman.h"
 27
 28#include <fcntl.h>
 29#include <sys/mman.h>
 30#include <unistd.h>
 31#include <xf86drm.h>
 32
 33#include <errno.h>
 34#include <string.h>
 35
 36struct dumb_context {
 37	struct wld_context base;
 38	int fd;
 39};
 40
 41struct dumb_buffer {
 42	struct buffer base;
 43	struct wld_exporter exporter;
 44	struct dumb_context *context;
 45	uint32_t handle;
 46	void *scanout;
 47	bool shadowed;
 48};
 49
 50#define BUFFER_IMPLEMENTS_FLUSH
 51#include "interface/buffer.h"
 52#include "interface/context.h"
 53#define DRM_DRIVER_NAME dumb
 54#include "interface/drm.h"
 55IMPL(dumb_context, wld_context)
 56IMPL(dumb_buffer, wld_buffer)
 57
 58const struct wld_context_impl *dumb_context_impl = &wld_context_impl;
 59
 60bool
 61driver_device_supported(uint32_t vendor_id, uint32_t device_id)
 62{
 63	return true;
 64}
 65
 66struct wld_context *
 67driver_create_context(int drm_fd)
 68{
 69	struct dumb_context *context;
 70
 71	if (!(context = malloc(sizeof *context)))
 72		return NULL;
 73
 74	context_initialize(&context->base, &wld_context_impl);
 75	context->fd = drm_fd;
 76
 77	return &context->base;
 78}
 79
 80struct wld_renderer *
 81context_create_renderer(struct wld_context *context)
 82{
 83	return wld_create_renderer(wld_pixman_context);
 84}
 85
 86static bool export(struct wld_exporter *exporter, struct wld_buffer *base,
 87                   uint32_t type, union wld_object *object)
 88{
 89	struct dumb_buffer *buffer = dumb_buffer(base);
 90
 91	switch (type) {
 92	case WLD_DRM_OBJECT_HANDLE:
 93		object->u32 = buffer->handle;
 94		return true;
 95	case WLD_DRM_OBJECT_PRIME_FD:
 96		if (drmPrimeHandleToFD(buffer->context->fd, buffer->handle,
 97		                       DRM_CLOEXEC, &object->i)
 98		    != 0) {
 99			return false;
100		}
101
102		return true;
103	default:
104		return false;
105	}
106}
107
108static struct buffer *
109new_buffer(struct dumb_context *context,
110           uint32_t width, uint32_t height,
111           uint32_t format, uint32_t handle,
112           unsigned long pitch, bool shadowed)
113{
114	struct dumb_buffer *buffer;
115
116	if (!(buffer = malloc(sizeof *buffer)))
117		return NULL;
118
119	buffer_initialize(&buffer->base, &wld_buffer_impl,
120	                  width, height, format, pitch);
121	buffer->context = context;
122	buffer->handle = handle;
123	buffer->scanout = NULL;
124	buffer->shadowed = shadowed;
125	buffer->exporter.export = &export;
126	wld_buffer_add_exporter(&buffer->base.base, &buffer->exporter);
127
128	return &buffer->base;
129}
130
131struct buffer *
132context_create_buffer(struct wld_context *base,
133                      uint32_t width, uint32_t height,
134                      uint32_t format, uint32_t flags)
135{
136	struct dumb_context *context = dumb_context(base);
137	struct buffer *buffer;
138	struct drm_mode_create_dumb create_dumb = {
139		.height = height,
140		.width = width,
141		.bpp = format_bytes_per_pixel(format) * 8,
142	};
143
144	if (drmIoctl(context->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) != 0)
145		goto error0;
146
147	buffer = new_buffer(context, width, height, format,
148	                    create_dumb.handle, create_dumb.pitch,
149	                    flags & WLD_DRM_FLAG_SCANOUT);
150
151	if (!buffer)
152		goto error1;
153
154	return buffer;
155
156error1 : {
157	struct drm_mode_destroy_dumb destroy_dumb = {
158		.handle = create_dumb.handle
159	};
160
161	drmIoctl(context->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
162}
163error0:
164	return NULL;
165}
166
167struct buffer *
168context_import_buffer(struct wld_context *base,
169                      uint32_t type, union wld_object object,
170                      uint32_t width, uint32_t height,
171                      uint32_t format, uint32_t pitch)
172{
173	struct dumb_context *context = dumb_context(base);
174	uint32_t handle;
175
176	switch (type) {
177	case WLD_DRM_OBJECT_PRIME_FD:
178		if (drmPrimeFDToHandle(context->fd, object.i, &handle) != 0)
179			return NULL;
180		break;
181	default:
182		return NULL;
183	}
184
185	return new_buffer(context, width, height, format, handle, pitch, false);
186}
187
188void
189context_destroy(struct wld_context *base)
190{
191	struct dumb_context *context = dumb_context(base);
192
193	close(context->fd);
194	free(context);
195}
196
197/**** Buffer ****/
198
199bool
200buffer_map(struct buffer *base)
201{
202	struct dumb_buffer *buffer = dumb_buffer(&base->base);
203	struct drm_mode_map_dumb map_dumb = { .handle = buffer->handle };
204	void *data;
205
206	if (drmIoctl(buffer->context->fd, DRM_IOCTL_MODE_MAP_DUMB,
207	             &map_dumb)
208	    != 0) {
209		return false;
210	}
211
212	data = mmap(NULL, buffer->base.base.pitch * buffer->base.base.height,
213	            PROT_READ | PROT_WRITE, MAP_SHARED,
214	            buffer->context->fd, map_dumb.offset);
215
216	if (data == MAP_FAILED)
217		return false;
218
219	buffer->scanout = data;
220	if (buffer->shadowed) {
221		buffer->base.base.map = calloc(buffer->base.base.height,
222		                               buffer->base.base.pitch);
223		if (!buffer->base.base.map) {
224			munmap(data, buffer->base.base.pitch * buffer->base.base.height);
225			buffer->scanout = NULL;
226			return false;
227		}
228	} else {
229		buffer->base.base.map = data;
230	}
231
232	return true;
233}
234
235void
236buffer_flush(struct buffer *base)
237{
238	struct dumb_buffer *buffer = dumb_buffer(&base->base);
239	pixman_box32_t *boxes;
240	int count;
241
242	if (!buffer->shadowed || !buffer->scanout)
243		return;
244
245	boxes = pixman_region32_rectangles(&base->base.damage, &count);
246	while (count--) {
247		int32_t y;
248		size_t offset = (size_t)boxes->y1 * base->base.pitch +
249		                (size_t)boxes->x1 * 4;
250		size_t size = (size_t)(boxes->x2 - boxes->x1) * 4;
251
252		for (y = boxes->y1; y < boxes->y2; ++y) {
253			memcpy((uint8_t *)buffer->scanout + offset,
254			       (uint8_t *)base->base.map + offset, size);
255			offset += base->base.pitch;
256		}
257		++boxes;
258	}
259}
260
261bool
262buffer_unmap(struct buffer *buffer)
263{
264	struct dumb_buffer *dumb = dumb_buffer(&buffer->base);
265
266	if (munmap(dumb->scanout,
267	           buffer->base.pitch * buffer->base.height)
268	    == -1) {
269		return false;
270	}
271
272	if (dumb->shadowed)
273		free(buffer->base.map);
274	dumb->scanout = NULL;
275	buffer->base.map = NULL;
276
277	return true;
278}
279
280void
281buffer_destroy(struct buffer *base)
282{
283	struct dumb_buffer *buffer = dumb_buffer(&base->base);
284	struct drm_mode_destroy_dumb destroy_dumb = {
285		.handle = buffer->handle
286	};
287
288	drmIoctl(buffer->context->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
289	free(buffer);
290}