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 #include <errno.h>
37
38 #include <sys/types.h>
39 #include <sys/mman.h>
40 #include <unistd.h>
41 #include <dlfcn.h>
42 #include <fcntl.h>
43 #include <xf86drm.h>
44
45 #include "pipe/p_compiler.h"
46 #include "pipe/p_format.h"
47 #include "pipe/p_state.h"
48 #include "util/u_inlines.h"
49 #include "util/format/u_format.h"
50 #include "util/u_math.h"
51 #include "util/u_memory.h"
52 #include "util/list.h"
53
54 #include "frontend/sw_winsys.h"
55 #include "frontend/drm_driver.h"
56 #include "kms_dri_sw_winsys.h"
57
58 #include "util/simple_mtx.h"
59
60 #ifdef DEBUG
61 #define DEBUG_PRINT(msg, ...) fprintf(stderr, msg, __VA_ARGS__)
62 #else
63 #define DEBUG_PRINT(msg, ...)
64 #endif
65
66 struct kms_sw_displaytarget;
67
68 struct kms_sw_plane
69 {
70 unsigned width;
71 unsigned height;
72 unsigned stride;
73 unsigned offset;
74 struct kms_sw_displaytarget *dt;
75 struct list_head link;
76 };
77
78 struct kms_sw_displaytarget
79 {
80 enum pipe_format format;
81 unsigned size;
82
83 uint32_t handle;
84 void *mapped;
85 void *ro_mapped;
86
87 int ref_count;
88 int map_count;
89 struct list_head link;
90 struct list_head planes;
91 mtx_t map_lock;
92 };
93
94 struct kms_sw_winsys
95 {
96 struct sw_winsys base;
97
98 int fd;
99 struct list_head bo_list;
100 };
101
102 static inline struct kms_sw_plane *
kms_sw_plane(struct sw_displaytarget * dt)103 kms_sw_plane( struct sw_displaytarget *dt )
104 {
105 return (struct kms_sw_plane *)dt;
106 }
107
108 static inline struct sw_displaytarget *
sw_displaytarget(struct kms_sw_plane * pl)109 sw_displaytarget( struct kms_sw_plane *pl)
110 {
111 return (struct sw_displaytarget *)pl;
112 }
113
114 static inline struct kms_sw_winsys *
kms_sw_winsys(struct sw_winsys * ws)115 kms_sw_winsys( struct sw_winsys *ws )
116 {
117 return (struct kms_sw_winsys *)ws;
118 }
119
120
121 static bool
kms_sw_is_displaytarget_format_supported(struct sw_winsys * ws,unsigned tex_usage,enum pipe_format format)122 kms_sw_is_displaytarget_format_supported( struct sw_winsys *ws,
123 unsigned tex_usage,
124 enum pipe_format format )
125 {
126 /* TODO: check visuals or other sensible thing here */
127 return true;
128 }
129
get_plane(struct kms_sw_displaytarget * kms_sw_dt,enum pipe_format format,unsigned width,unsigned height,unsigned stride,unsigned offset)130 static struct kms_sw_plane *get_plane(struct kms_sw_displaytarget *kms_sw_dt,
131 enum pipe_format format,
132 unsigned width, unsigned height,
133 unsigned stride, unsigned offset)
134 {
135 struct kms_sw_plane *plane = NULL;
136
137 if (offset + util_format_get_2d_size(format, stride, height) >
138 kms_sw_dt->size) {
139 DEBUG_PRINT("KMS-DEBUG: plane too big. format: %d stride: %d height: %d "
140 "offset: %d size:%d\n", format, stride, height, offset,
141 kms_sw_dt->size);
142 return NULL;
143 }
144
145 LIST_FOR_EACH_ENTRY(plane, &kms_sw_dt->planes, link) {
146 if (plane->offset == offset)
147 return plane;
148 }
149
150 plane = CALLOC_STRUCT(kms_sw_plane);
151 if (!plane)
152 return NULL;
153
154 plane->width = width;
155 plane->height = height;
156 plane->stride = stride;
157 plane->offset = offset;
158 plane->dt = kms_sw_dt;
159 list_add(&plane->link, &kms_sw_dt->planes);
160 return plane;
161 }
162
163 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)164 kms_sw_displaytarget_create(struct sw_winsys *ws,
165 unsigned tex_usage,
166 enum pipe_format format,
167 unsigned width, unsigned height,
168 unsigned alignment,
169 const void *front_private,
170 unsigned *stride)
171 {
172 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
173 struct kms_sw_displaytarget *kms_sw_dt;
174 struct drm_mode_create_dumb create_req;
175 struct drm_mode_destroy_dumb destroy_req;
176 int ret;
177
178 kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget);
179 if (!kms_sw_dt)
180 goto no_dt;
181
182 list_inithead(&kms_sw_dt->planes);
183 kms_sw_dt->ref_count = 1;
184 kms_sw_dt->mapped = MAP_FAILED;
185 kms_sw_dt->ro_mapped = MAP_FAILED;
186
187 kms_sw_dt->format = format;
188
189 mtx_init(&kms_sw_dt->map_lock, mtx_plain);
190
191 memset(&create_req, 0, sizeof(create_req));
192 create_req.bpp = util_format_get_blocksizebits(format);
193 create_req.width = width;
194 create_req.height = height;
195 ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
196 if (ret) {
197 fprintf(stderr, "KMS: DRM_IOCTL_MODE_CREATE_DUMB failed: %s\n", strerror(errno));
198 goto free_bo;
199 }
200
201 kms_sw_dt->size = create_req.size;
202 kms_sw_dt->handle = create_req.handle;
203 struct kms_sw_plane *plane = get_plane(kms_sw_dt, format, width, height,
204 create_req.pitch, 0);
205 if (!plane)
206 goto free_bo;
207
208 list_add(&kms_sw_dt->link, &kms_sw->bo_list);
209
210 DEBUG_PRINT("KMS-DEBUG: created buffer %u (size %u)\n", kms_sw_dt->handle, kms_sw_dt->size);
211
212 *stride = create_req.pitch;
213 return sw_displaytarget(plane);
214
215 free_bo:
216 memset(&destroy_req, 0, sizeof destroy_req);
217 destroy_req.handle = create_req.handle;
218 drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
219 FREE(kms_sw_dt);
220 no_dt:
221 return NULL;
222 }
223
224 static void
kms_sw_displaytarget_destroy(struct sw_winsys * ws,struct sw_displaytarget * dt)225 kms_sw_displaytarget_destroy(struct sw_winsys *ws,
226 struct sw_displaytarget *dt)
227 {
228 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
229 struct kms_sw_plane *plane = kms_sw_plane(dt);
230 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
231 struct drm_mode_destroy_dumb destroy_req;
232
233 kms_sw_dt->ref_count --;
234 if (kms_sw_dt->ref_count > 0)
235 return;
236
237 if (kms_sw_dt->map_count > 0) {
238 DEBUG_PRINT("KMS-DEBUG: leaked map buffer %u\n", kms_sw_dt->handle);
239 }
240
241 memset(&destroy_req, 0, sizeof destroy_req);
242 destroy_req.handle = kms_sw_dt->handle;
243 drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
244
245 list_del(&kms_sw_dt->link);
246
247 mtx_destroy(&kms_sw_dt->map_lock);
248 DEBUG_PRINT("KMS-DEBUG: destroyed buffer %u\n", kms_sw_dt->handle);
249
250 struct kms_sw_plane *tmp;
251 LIST_FOR_EACH_ENTRY_SAFE(plane, tmp, &kms_sw_dt->planes, link) {
252 FREE(plane);
253 }
254
255 FREE(kms_sw_dt);
256 }
257
258 static void *
kms_sw_displaytarget_map(struct sw_winsys * ws,struct sw_displaytarget * dt,unsigned flags)259 kms_sw_displaytarget_map(struct sw_winsys *ws,
260 struct sw_displaytarget *dt,
261 unsigned flags)
262 {
263 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
264 struct kms_sw_plane *plane = kms_sw_plane(dt);
265 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
266 struct drm_mode_map_dumb map_req;
267 int prot, ret;
268
269 mtx_lock(&kms_sw_dt->map_lock);
270 memset(&map_req, 0, sizeof map_req);
271 map_req.handle = kms_sw_dt->handle;
272 ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_req);
273 if (ret)
274 goto fail_locked;
275
276 prot = (flags == PIPE_MAP_READ) ? PROT_READ : (PROT_READ | PROT_WRITE);
277 void **ptr = (flags == PIPE_MAP_READ) ? &kms_sw_dt->ro_mapped : &kms_sw_dt->mapped;
278 if (*ptr == MAP_FAILED) {
279 void *tmp = mmap(NULL, kms_sw_dt->size, prot, MAP_SHARED,
280 kms_sw->fd, map_req.offset);
281 if (tmp == MAP_FAILED)
282 goto fail_locked;
283 *ptr = tmp;
284 }
285
286 DEBUG_PRINT("KMS-DEBUG: mapped buffer %u (size %u) at %p\n",
287 kms_sw_dt->handle, kms_sw_dt->size, *ptr);
288
289 kms_sw_dt->map_count++;
290
291 mtx_unlock(&kms_sw_dt->map_lock);
292
293 return *ptr + plane->offset;
294 fail_locked:
295 mtx_unlock(&kms_sw_dt->map_lock);
296 return NULL;
297 }
298
299 static struct kms_sw_displaytarget *
kms_sw_displaytarget_find_and_ref(struct kms_sw_winsys * kms_sw,unsigned int kms_handle)300 kms_sw_displaytarget_find_and_ref(struct kms_sw_winsys *kms_sw,
301 unsigned int kms_handle)
302 {
303 struct kms_sw_displaytarget *kms_sw_dt;
304
305 LIST_FOR_EACH_ENTRY(kms_sw_dt, &kms_sw->bo_list, link) {
306 if (kms_sw_dt->handle == kms_handle) {
307 kms_sw_dt->ref_count++;
308
309 DEBUG_PRINT("KMS-DEBUG: imported buffer %u (size %u)\n",
310 kms_sw_dt->handle, kms_sw_dt->size);
311
312 return kms_sw_dt;
313 }
314 }
315
316 return NULL;
317 }
318
319 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)320 kms_sw_displaytarget_add_from_prime(struct kms_sw_winsys *kms_sw, int fd,
321 enum pipe_format format,
322 unsigned width, unsigned height,
323 unsigned stride, unsigned offset)
324 {
325 uint32_t handle = -1;
326 struct kms_sw_displaytarget * kms_sw_dt;
327 int ret;
328
329 ret = drmPrimeFDToHandle(kms_sw->fd, fd, &handle);
330
331 if (ret)
332 return NULL;
333
334 kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, handle);
335 struct kms_sw_plane *plane = NULL;
336 if (kms_sw_dt) {
337 plane = get_plane(kms_sw_dt, format, width, height, stride, offset);
338 if (!plane)
339 kms_sw_dt->ref_count --;
340 return plane;
341 }
342
343 kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget);
344 if (!kms_sw_dt)
345 return NULL;
346
347 list_inithead(&kms_sw_dt->planes);
348 off_t lseek_ret = lseek(fd, 0, SEEK_END);
349 if (lseek_ret == -1) {
350 FREE(kms_sw_dt);
351 return NULL;
352 }
353 kms_sw_dt->mapped = MAP_FAILED;
354 kms_sw_dt->ro_mapped = MAP_FAILED;
355 kms_sw_dt->size = lseek_ret;
356 kms_sw_dt->ref_count = 1;
357 kms_sw_dt->handle = handle;
358
359 lseek(fd, 0, SEEK_SET);
360 plane = get_plane(kms_sw_dt, format, width, height, stride, offset);
361 if (!plane) {
362 FREE(kms_sw_dt);
363 return NULL;
364 }
365
366 list_add(&kms_sw_dt->link, &kms_sw->bo_list);
367
368 return plane;
369 }
370
371 static void
kms_sw_displaytarget_unmap(struct sw_winsys * ws,struct sw_displaytarget * dt)372 kms_sw_displaytarget_unmap(struct sw_winsys *ws,
373 struct sw_displaytarget *dt)
374 {
375 struct kms_sw_plane *plane = kms_sw_plane(dt);
376 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
377
378 mtx_lock(&kms_sw_dt->map_lock);
379 if (!kms_sw_dt->map_count) {
380 DEBUG_PRINT("KMS-DEBUG: ignore duplicated unmap %u", kms_sw_dt->handle);
381 mtx_unlock(&kms_sw_dt->map_lock);
382 return;
383 }
384 kms_sw_dt->map_count--;
385 if (kms_sw_dt->map_count) {
386 DEBUG_PRINT("KMS-DEBUG: ignore unmap for busy buffer %u", kms_sw_dt->handle);
387 mtx_unlock(&kms_sw_dt->map_lock);
388 return;
389 }
390
391 DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->mapped);
392 DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->ro_mapped);
393
394 if (kms_sw_dt->mapped != MAP_FAILED) {
395 munmap(kms_sw_dt->mapped, kms_sw_dt->size);
396 kms_sw_dt->mapped = MAP_FAILED;
397 }
398 if (kms_sw_dt->ro_mapped != MAP_FAILED) {
399 munmap(kms_sw_dt->ro_mapped, kms_sw_dt->size);
400 kms_sw_dt->ro_mapped = MAP_FAILED;
401 }
402 mtx_unlock(&kms_sw_dt->map_lock);
403 }
404
405 static struct sw_displaytarget *
kms_sw_displaytarget_from_handle(struct sw_winsys * ws,const struct pipe_resource * templ,struct winsys_handle * whandle,unsigned * stride)406 kms_sw_displaytarget_from_handle(struct sw_winsys *ws,
407 const struct pipe_resource *templ,
408 struct winsys_handle *whandle,
409 unsigned *stride)
410 {
411 struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
412 struct kms_sw_displaytarget *kms_sw_dt;
413 struct kms_sw_plane *kms_sw_pl;
414
415
416 assert(whandle->type == WINSYS_HANDLE_TYPE_KMS ||
417 whandle->type == WINSYS_HANDLE_TYPE_FD);
418
419 switch(whandle->type) {
420 case WINSYS_HANDLE_TYPE_FD:
421 kms_sw_pl = kms_sw_displaytarget_add_from_prime(kms_sw, whandle->handle,
422 templ->format,
423 templ->width0,
424 templ->height0,
425 whandle->stride,
426 whandle->offset);
427 if (kms_sw_pl)
428 *stride = kms_sw_pl->stride;
429 return sw_displaytarget(kms_sw_pl);
430 case WINSYS_HANDLE_TYPE_KMS:
431 kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, whandle->handle);
432 if (kms_sw_dt) {
433 struct kms_sw_plane *plane;
434 LIST_FOR_EACH_ENTRY(plane, &kms_sw_dt->planes, link) {
435 if (whandle->offset == plane->offset) {
436 *stride = plane->stride;
437 return sw_displaytarget(plane);
438 }
439 }
440 kms_sw_dt->ref_count --;
441 }
442 FALLTHROUGH;
443 default:
444 break;
445 }
446
447 assert(0);
448 return NULL;
449 }
450
451 static bool
kms_sw_displaytarget_get_handle(struct sw_winsys * winsys,struct sw_displaytarget * dt,struct winsys_handle * whandle)452 kms_sw_displaytarget_get_handle(struct sw_winsys *winsys,
453 struct sw_displaytarget *dt,
454 struct winsys_handle *whandle)
455 {
456 struct kms_sw_winsys *kms_sw = kms_sw_winsys(winsys);
457 struct kms_sw_plane *plane = kms_sw_plane(dt);
458 struct kms_sw_displaytarget *kms_sw_dt = plane->dt;
459
460 switch(whandle->type) {
461 case WINSYS_HANDLE_TYPE_KMS:
462 whandle->handle = kms_sw_dt->handle;
463 whandle->stride = plane->stride;
464 whandle->offset = plane->offset;
465 return true;
466 case WINSYS_HANDLE_TYPE_FD:
467 if (!drmPrimeHandleToFD(kms_sw->fd, kms_sw_dt->handle,
468 DRM_CLOEXEC, (int*)&whandle->handle)) {
469 whandle->stride = plane->stride;
470 whandle->offset = plane->offset;
471 return true;
472 }
473 FALLTHROUGH;
474 default:
475 whandle->handle = 0;
476 whandle->stride = 0;
477 whandle->offset = 0;
478 return false;
479 }
480 }
481
482 static void
kms_sw_displaytarget_display(struct sw_winsys * ws,struct sw_displaytarget * dt,void * context_private,struct pipe_box * box)483 kms_sw_displaytarget_display(struct sw_winsys *ws,
484 struct sw_displaytarget *dt,
485 void *context_private,
486 struct pipe_box *box)
487 {
488 /* This function should not be called, instead the dri2 loader should
489 handle swap buffers internally.
490 */
491 assert(0);
492 }
493
494
495 static void
kms_destroy_sw_winsys(struct sw_winsys * winsys)496 kms_destroy_sw_winsys(struct sw_winsys *winsys)
497 {
498 FREE(winsys);
499 }
500
501 struct sw_winsys *
kms_dri_create_winsys(int fd)502 kms_dri_create_winsys(int fd)
503 {
504 struct kms_sw_winsys *ws;
505
506 ws = CALLOC_STRUCT(kms_sw_winsys);
507 if (!ws)
508 return NULL;
509
510 ws->fd = fd;
511 list_inithead(&ws->bo_list);
512
513 ws->base.destroy = kms_destroy_sw_winsys;
514
515 ws->base.is_displaytarget_format_supported = kms_sw_is_displaytarget_format_supported;
516
517 /* screen texture functions */
518 ws->base.displaytarget_create = kms_sw_displaytarget_create;
519 ws->base.displaytarget_destroy = kms_sw_displaytarget_destroy;
520 ws->base.displaytarget_from_handle = kms_sw_displaytarget_from_handle;
521 ws->base.displaytarget_get_handle = kms_sw_displaytarget_get_handle;
522
523 /* texture functions */
524 ws->base.displaytarget_map = kms_sw_displaytarget_map;
525 ws->base.displaytarget_unmap = kms_sw_displaytarget_unmap;
526
527 ws->base.displaytarget_display = kms_sw_displaytarget_display;
528
529 return &ws->base;
530 }
531
532 /* vim: set sw=3 ts=8 sts=3 expandtab: */
533