1 /**************************************************************************
2 *
3 * Copyright 2009, VMware, Inc.
4 * All Rights Reserved.
5 * Copyright 2010 George Sapountzis <gsapountzis@gmail.com>
6 * 2013 Red Hat, Inc.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial portions
18 * of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
23 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
24 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 *
28 **************************************************************************/
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <limits.h>
36
37 #include <sys/types.h>
38 #include <sys/mman.h>
39 #include <unistd.h>
40 #include <dlfcn.h>
41 #include <fcntl.h>
42 #include <xf86drm.h>
43
44 #include "pipe/p_compiler.h"
45 #include "pipe/p_format.h"
46 #include "pipe/p_state.h"
47 #include "util/u_inlines.h"
48 #include "util/format/u_format.h"
49 #include "util/u_math.h"
50 #include "util/u_memory.h"
51 #include "util/list.h"
52
53 #include "frontend/sw_winsys.h"
54 #include "frontend/drm_driver.h"
55 #include "kms_dri_sw_winsys.h"
56
57 #ifdef DEBUG
58 #define DEBUG_PRINT(msg, ...) fprintf(stderr, msg, __VA_ARGS__)
59 #else
60 #define DEBUG_PRINT(msg, ...)
61 #endif
62
63 struct kms_sw_displaytarget;
64
65 struct kms_sw_plane
66 {
67 unsigned width;
68 unsigned height;
69 unsigned stride;
70 unsigned offset;
71 struct kms_sw_displaytarget *dt;
72 struct list_head link;
73 };
74
75 struct kms_sw_displaytarget
76 {
77 enum pipe_format format;
78 unsigned size;
79
80 uint32_t handle;
81 void *mapped;
82 void *ro_mapped;
83
84 int ref_count;
85 int map_count;
86 struct list_head link;
87 struct list_head planes;
88 };
89
90 struct kms_sw_winsys
91 {
92 struct sw_winsys base;
93
94 int fd;
95 struct list_head bo_list;
96 };
97
98 static inline struct kms_sw_plane *
kms_sw_plane(struct sw_displaytarget * dt)99 kms_sw_plane( struct sw_displaytarget *dt )
100 {
101 return (struct kms_sw_plane *)dt;
102 }
103
104 static inline struct sw_displaytarget *
sw_displaytarget(struct kms_sw_plane * pl)105 sw_displaytarget( struct kms_sw_plane *pl)
106 {
107 return (struct sw_displaytarget *)pl;
108 }
109
110 static inline struct kms_sw_winsys *
kms_sw_winsys(struct sw_winsys * ws)111 kms_sw_winsys( struct sw_winsys *ws )
112 {
113 return (struct kms_sw_winsys *)ws;
114 }
115
116
117 static bool
kms_sw_is_displaytarget_format_supported(struct sw_winsys * ws,unsigned tex_usage,enum pipe_format format)118 kms_sw_is_displaytarget_format_supported( struct sw_winsys *ws,
119 unsigned tex_usage,
120 enum pipe_format format )
121 {
122 /* TODO: check visuals or other sensible thing here */
123 return true;
124 }
125
get_plane(struct kms_sw_displaytarget * kms_sw_dt,enum pipe_format format,unsigned width,unsigned height,unsigned stride,unsigned offset)126 static struct kms_sw_plane *get_plane(struct kms_sw_displaytarget *kms_sw_dt,
127 enum pipe_format format,
128 unsigned width, unsigned height,
129 unsigned stride, unsigned offset)
130 {
131 struct kms_sw_plane *plane = NULL;
132
133 if (offset + util_format_get_2d_size(format, stride, height) >
134 kms_sw_dt->size) {
135 DEBUG_PRINT("KMS-DEBUG: plane too big. format: %d stride: %d height: %d "
136 "offset: %d size:%d\n", format, stride, height, offset,
137 kms_sw_dt->size);
138 return NULL;
139 }
140
141 LIST_FOR_EACH_ENTRY(plane, &kms_sw_dt->planes, link) {
142 if (plane->offset == offset)
143 return plane;
144 }
145
146 plane = CALLOC_STRUCT(kms_sw_plane);
147 if (!plane)
148 return NULL;
149
150 plane->width = width;
151 plane->height = height;
152 plane->stride = stride;
153 plane->offset = offset;
154 plane->dt = kms_sw_dt;
155 list_add(&plane->link, &kms_sw_dt->planes);
156 return plane;
157 }
158
159 static struct sw_displaytarget *
kms_sw_displaytarget_create(struct sw_winsys * ws,unsigned tex_usage,enum pipe_format format,unsigned width,unsigned height,unsigned alignment,const void * front_private,unsigned * stride)160 kms_sw_displaytarget_create(struct sw_winsys *ws,
161 unsigned tex_usage,
162 enum pipe_format format,
163 unsigned width, unsigned height,
164 unsigned alignment,
165 const void *front_private,
166 unsigned *stride)
167 {
168 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
169 struct kms_sw_displaytarget *kms_sw_dt;
170 struct drm_mode_create_dumb create_req;
171 struct drm_mode_destroy_dumb destroy_req;
172 int ret;
173
174 kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget);
175 if (!kms_sw_dt)
176 goto no_dt;
177
178 list_inithead(&kms_sw_dt->planes);
179 kms_sw_dt->ref_count = 1;
180 kms_sw_dt->mapped = MAP_FAILED;
181 kms_sw_dt->ro_mapped = MAP_FAILED;
182
183 kms_sw_dt->format = format;
184
185 memset(&create_req, 0, sizeof(create_req));
186 create_req.bpp = util_format_get_blocksizebits(format);
187 create_req.width = width;
188 create_req.height = height;
189 ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
190 if (ret)
191 goto free_bo;
192
193 kms_sw_dt->size = create_req.size;
194 kms_sw_dt->handle = create_req.handle;
195 struct kms_sw_plane *plane = get_plane(kms_sw_dt, format, width, height,
196 create_req.pitch, 0);
197 if (!plane)
198 goto free_bo;
199
200 list_add(&kms_sw_dt->link, &kms_sw->bo_list);
201
202 DEBUG_PRINT("KMS-DEBUG: created buffer %u (size %u)\n", kms_sw_dt->handle, kms_sw_dt->size);
203
204 *stride = create_req.pitch;
205 return sw_displaytarget(plane);
206
207 free_bo:
208 memset(&destroy_req, 0, sizeof destroy_req);
209 destroy_req.handle = create_req.handle;
210 drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
211 FREE(kms_sw_dt);
212 no_dt:
213 return NULL;
214 }
215
216 static void
kms_sw_displaytarget_destroy(struct sw_winsys * ws,struct sw_displaytarget * dt)217 kms_sw_displaytarget_destroy(struct sw_winsys *ws,
218 struct sw_displaytarget *dt)
219 {
220 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
221 struct kms_sw_plane *plane = kms_sw_plane(dt);
222 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
223 struct drm_mode_destroy_dumb destroy_req;
224
225 kms_sw_dt->ref_count --;
226 if (kms_sw_dt->ref_count > 0)
227 return;
228
229 if (kms_sw_dt->map_count > 0) {
230 DEBUG_PRINT("KMS-DEBUG: leaked map buffer %u\n", kms_sw_dt->handle);
231 }
232
233 memset(&destroy_req, 0, sizeof destroy_req);
234 destroy_req.handle = kms_sw_dt->handle;
235 drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
236
237 list_del(&kms_sw_dt->link);
238
239 DEBUG_PRINT("KMS-DEBUG: destroyed buffer %u\n", kms_sw_dt->handle);
240
241 struct kms_sw_plane *tmp;
242 LIST_FOR_EACH_ENTRY_SAFE(plane, tmp, &kms_sw_dt->planes, link) {
243 FREE(plane);
244 }
245
246 FREE(kms_sw_dt);
247 }
248
249 static void *
kms_sw_displaytarget_map(struct sw_winsys * ws,struct sw_displaytarget * dt,unsigned flags)250 kms_sw_displaytarget_map(struct sw_winsys *ws,
251 struct sw_displaytarget *dt,
252 unsigned flags)
253 {
254 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
255 struct kms_sw_plane *plane = kms_sw_plane(dt);
256 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
257 struct drm_mode_map_dumb map_req;
258 int prot, ret;
259
260 memset(&map_req, 0, sizeof map_req);
261 map_req.handle = kms_sw_dt->handle;
262 ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_req);
263 if (ret)
264 return NULL;
265
266 prot = (flags == PIPE_MAP_READ) ? PROT_READ : (PROT_READ | PROT_WRITE);
267 void **ptr = (flags == PIPE_MAP_READ) ? &kms_sw_dt->ro_mapped : &kms_sw_dt->mapped;
268 if (*ptr == MAP_FAILED) {
269 void *tmp = mmap(0, kms_sw_dt->size, prot, MAP_SHARED,
270 kms_sw->fd, map_req.offset);
271 if (tmp == MAP_FAILED)
272 return NULL;
273 *ptr = tmp;
274 }
275
276 DEBUG_PRINT("KMS-DEBUG: mapped buffer %u (size %u) at %p\n",
277 kms_sw_dt->handle, kms_sw_dt->size, *ptr);
278
279 kms_sw_dt->map_count++;
280
281 return *ptr + plane->offset;
282 }
283
284 static struct kms_sw_displaytarget *
kms_sw_displaytarget_find_and_ref(struct kms_sw_winsys * kms_sw,unsigned int kms_handle)285 kms_sw_displaytarget_find_and_ref(struct kms_sw_winsys *kms_sw,
286 unsigned int kms_handle)
287 {
288 struct kms_sw_displaytarget *kms_sw_dt;
289
290 LIST_FOR_EACH_ENTRY(kms_sw_dt, &kms_sw->bo_list, link) {
291 if (kms_sw_dt->handle == kms_handle) {
292 kms_sw_dt->ref_count++;
293
294 DEBUG_PRINT("KMS-DEBUG: imported buffer %u (size %u)\n",
295 kms_sw_dt->handle, kms_sw_dt->size);
296
297 return kms_sw_dt;
298 }
299 }
300
301 return NULL;
302 }
303
304 static struct kms_sw_plane *
kms_sw_displaytarget_add_from_prime(struct kms_sw_winsys * kms_sw,int fd,enum pipe_format format,unsigned width,unsigned height,unsigned stride,unsigned offset)305 kms_sw_displaytarget_add_from_prime(struct kms_sw_winsys *kms_sw, int fd,
306 enum pipe_format format,
307 unsigned width, unsigned height,
308 unsigned stride, unsigned offset)
309 {
310 uint32_t handle = -1;
311 struct kms_sw_displaytarget * kms_sw_dt;
312 int ret;
313
314 ret = drmPrimeFDToHandle(kms_sw->fd, fd, &handle);
315
316 if (ret)
317 return NULL;
318
319 kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, handle);
320 struct kms_sw_plane *plane = NULL;
321 if (kms_sw_dt) {
322 plane = get_plane(kms_sw_dt, format, width, height, stride, offset);
323 if (!plane)
324 kms_sw_dt->ref_count --;
325 return plane;
326 }
327
328 kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget);
329 if (!kms_sw_dt)
330 return NULL;
331
332 list_inithead(&kms_sw_dt->planes);
333 off_t lseek_ret = lseek(fd, 0, SEEK_END);
334 if (lseek_ret == -1) {
335 FREE(kms_sw_dt);
336 return NULL;
337 }
338 kms_sw_dt->mapped = MAP_FAILED;
339 kms_sw_dt->ro_mapped = MAP_FAILED;
340 kms_sw_dt->size = lseek_ret;
341 kms_sw_dt->ref_count = 1;
342 kms_sw_dt->handle = handle;
343
344 lseek(fd, 0, SEEK_SET);
345 plane = get_plane(kms_sw_dt, format, width, height, stride, offset);
346 if (!plane) {
347 FREE(kms_sw_dt);
348 return NULL;
349 }
350
351 list_add(&kms_sw_dt->link, &kms_sw->bo_list);
352
353 return plane;
354 }
355
356 static void
kms_sw_displaytarget_unmap(struct sw_winsys * ws,struct sw_displaytarget * dt)357 kms_sw_displaytarget_unmap(struct sw_winsys *ws,
358 struct sw_displaytarget *dt)
359 {
360 struct kms_sw_plane *plane = kms_sw_plane(dt);
361 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
362
363 if (!kms_sw_dt->map_count) {
364 DEBUG_PRINT("KMS-DEBUG: ignore duplicated unmap %u", kms_sw_dt->handle);
365 return;
366 }
367 kms_sw_dt->map_count--;
368 if (kms_sw_dt->map_count) {
369 DEBUG_PRINT("KMS-DEBUG: ignore unmap for busy buffer %u", kms_sw_dt->handle);
370 return;
371 }
372
373 DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->mapped);
374 DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->ro_mapped);
375
376 if (kms_sw_dt->mapped != MAP_FAILED) {
377 munmap(kms_sw_dt->mapped, kms_sw_dt->size);
378 kms_sw_dt->mapped = MAP_FAILED;
379 }
380 if (kms_sw_dt->ro_mapped != MAP_FAILED) {
381 munmap(kms_sw_dt->ro_mapped, kms_sw_dt->size);
382 kms_sw_dt->ro_mapped = MAP_FAILED;
383 }
384 }
385
386 static struct sw_displaytarget *
kms_sw_displaytarget_from_handle(struct sw_winsys * ws,const struct pipe_resource * templ,struct winsys_handle * whandle,unsigned * stride)387 kms_sw_displaytarget_from_handle(struct sw_winsys *ws,
388 const struct pipe_resource *templ,
389 struct winsys_handle *whandle,
390 unsigned *stride)
391 {
392 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
393 struct kms_sw_displaytarget *kms_sw_dt;
394 struct kms_sw_plane *kms_sw_pl;
395
396
397 assert(whandle->type == WINSYS_HANDLE_TYPE_KMS ||
398 whandle->type == WINSYS_HANDLE_TYPE_FD);
399
400 switch(whandle->type) {
401 case WINSYS_HANDLE_TYPE_FD:
402 kms_sw_pl = kms_sw_displaytarget_add_from_prime(kms_sw, whandle->handle,
403 templ->format,
404 templ->width0,
405 templ->height0,
406 whandle->stride,
407 whandle->offset);
408 if (kms_sw_pl)
409 *stride = kms_sw_pl->stride;
410 return sw_displaytarget(kms_sw_pl);
411 case WINSYS_HANDLE_TYPE_KMS:
412 kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, whandle->handle);
413 if (kms_sw_dt) {
414 struct kms_sw_plane *plane;
415 LIST_FOR_EACH_ENTRY(plane, &kms_sw_dt->planes, link) {
416 if (whandle->offset == plane->offset) {
417 *stride = plane->stride;
418 return sw_displaytarget(plane);
419 }
420 }
421 kms_sw_dt->ref_count --;
422 }
423 FALLTHROUGH;
424 default:
425 break;
426 }
427
428 assert(0);
429 return NULL;
430 }
431
432 static bool
kms_sw_displaytarget_get_handle(struct sw_winsys * winsys,struct sw_displaytarget * dt,struct winsys_handle * whandle)433 kms_sw_displaytarget_get_handle(struct sw_winsys *winsys,
434 struct sw_displaytarget *dt,
435 struct winsys_handle *whandle)
436 {
437 struct kms_sw_winsys *kms_sw = kms_sw_winsys(winsys);
438 struct kms_sw_plane *plane = kms_sw_plane(dt);
439 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
440
441 switch(whandle->type) {
442 case WINSYS_HANDLE_TYPE_KMS:
443 whandle->handle = kms_sw_dt->handle;
444 whandle->stride = plane->stride;
445 whandle->offset = plane->offset;
446 return true;
447 case WINSYS_HANDLE_TYPE_FD:
448 if (!drmPrimeHandleToFD(kms_sw->fd, kms_sw_dt->handle,
449 DRM_CLOEXEC, (int*)&whandle->handle)) {
450 whandle->stride = plane->stride;
451 whandle->offset = plane->offset;
452 return true;
453 }
454 FALLTHROUGH;
455 default:
456 whandle->handle = 0;
457 whandle->stride = 0;
458 whandle->offset = 0;
459 return false;
460 }
461 }
462
463 static void
kms_sw_displaytarget_display(struct sw_winsys * ws,struct sw_displaytarget * dt,void * context_private,struct pipe_box * box)464 kms_sw_displaytarget_display(struct sw_winsys *ws,
465 struct sw_displaytarget *dt,
466 void *context_private,
467 struct pipe_box *box)
468 {
469 /* This function should not be called, instead the dri2 loader should
470 handle swap buffers internally.
471 */
472 assert(0);
473 }
474
475
476 static void
kms_destroy_sw_winsys(struct sw_winsys * winsys)477 kms_destroy_sw_winsys(struct sw_winsys *winsys)
478 {
479 FREE(winsys);
480 }
481
482 struct sw_winsys *
kms_dri_create_winsys(int fd)483 kms_dri_create_winsys(int fd)
484 {
485 struct kms_sw_winsys *ws;
486
487 ws = CALLOC_STRUCT(kms_sw_winsys);
488 if (!ws)
489 return NULL;
490
491 ws->fd = fd;
492 list_inithead(&ws->bo_list);
493
494 ws->base.destroy = kms_destroy_sw_winsys;
495
496 ws->base.is_displaytarget_format_supported = kms_sw_is_displaytarget_format_supported;
497
498 /* screen texture functions */
499 ws->base.displaytarget_create = kms_sw_displaytarget_create;
500 ws->base.displaytarget_destroy = kms_sw_displaytarget_destroy;
501 ws->base.displaytarget_from_handle = kms_sw_displaytarget_from_handle;
502 ws->base.displaytarget_get_handle = kms_sw_displaytarget_get_handle;
503
504 /* texture functions */
505 ws->base.displaytarget_map = kms_sw_displaytarget_map;
506 ws->base.displaytarget_unmap = kms_sw_displaytarget_unmap;
507
508 ws->base.displaytarget_display = kms_sw_displaytarget_display;
509
510 return &ws->base;
511 }
512
513 /* vim: set sw=3 ts=8 sts=3 expandtab: */
514