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}