• 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 
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