• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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