Home | History | Annotate | Download | only in kms-dri
      1 /**************************************************************************
      2  *
      3  * Copyright 2009, VMware, Inc.
      4  * All Rights Reserved.
      5  * Copyright 2010 George Sapountzis <gsapountzis (at) 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 "util/u_inlines.h"
     47 #include "util/u_format.h"
     48 #include "util/u_math.h"
     49 #include "util/u_memory.h"
     50 #include "util/list.h"
     51 
     52 #include "state_tracker/sw_winsys.h"
     53 #include "state_tracker/drm_driver.h"
     54 #include "kms_dri_sw_winsys.h"
     55 
     56 #ifdef DEBUG
     57 #define DEBUG_PRINT(msg, ...) fprintf(stderr, msg, __VA_ARGS__)
     58 #else
     59 #define DEBUG_PRINT(msg, ...)
     60 #endif
     61 
     62 
     63 struct kms_sw_displaytarget
     64 {
     65    enum pipe_format format;
     66    unsigned width;
     67    unsigned height;
     68    unsigned stride;
     69    unsigned size;
     70 
     71    uint32_t handle;
     72    void *mapped;
     73 
     74    int ref_count;
     75    struct list_head link;
     76 };
     77 
     78 struct kms_sw_winsys
     79 {
     80    struct sw_winsys base;
     81 
     82    int fd;
     83    struct list_head bo_list;
     84 };
     85 
     86 static inline struct kms_sw_displaytarget *
     87 kms_sw_displaytarget( struct sw_displaytarget *dt )
     88 {
     89    return (struct kms_sw_displaytarget *)dt;
     90 }
     91 
     92 static inline struct kms_sw_winsys *
     93 kms_sw_winsys( struct sw_winsys *ws )
     94 {
     95    return (struct kms_sw_winsys *)ws;
     96 }
     97 
     98 
     99 static boolean
    100 kms_sw_is_displaytarget_format_supported( struct sw_winsys *ws,
    101                                           unsigned tex_usage,
    102                                           enum pipe_format format )
    103 {
    104    /* TODO: check visuals or other sensible thing here */
    105    return TRUE;
    106 }
    107 
    108 static struct sw_displaytarget *
    109 kms_sw_displaytarget_create(struct sw_winsys *ws,
    110                             unsigned tex_usage,
    111                             enum pipe_format format,
    112                             unsigned width, unsigned height,
    113                             unsigned alignment,
    114                             const void *front_private,
    115                             unsigned *stride)
    116 {
    117    struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
    118    struct kms_sw_displaytarget *kms_sw_dt;
    119    struct drm_mode_create_dumb create_req;
    120    struct drm_mode_destroy_dumb destroy_req;
    121    int ret;
    122 
    123    kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget);
    124    if (!kms_sw_dt)
    125       goto no_dt;
    126 
    127    kms_sw_dt->ref_count = 1;
    128 
    129    kms_sw_dt->format = format;
    130    kms_sw_dt->width = width;
    131    kms_sw_dt->height = height;
    132 
    133    memset(&create_req, 0, sizeof(create_req));
    134    create_req.bpp = 32;
    135    create_req.width = width;
    136    create_req.height = height;
    137    ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
    138    if (ret)
    139       goto free_bo;
    140 
    141    kms_sw_dt->stride = create_req.pitch;
    142    kms_sw_dt->size = create_req.size;
    143    kms_sw_dt->handle = create_req.handle;
    144 
    145    list_add(&kms_sw_dt->link, &kms_sw->bo_list);
    146 
    147    DEBUG_PRINT("KMS-DEBUG: created buffer %u (size %u)\n", kms_sw_dt->handle, kms_sw_dt->size);
    148 
    149    *stride = kms_sw_dt->stride;
    150    return (struct sw_displaytarget *)kms_sw_dt;
    151 
    152  free_bo:
    153    memset(&destroy_req, 0, sizeof destroy_req);
    154    destroy_req.handle = create_req.handle;
    155    drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
    156    FREE(kms_sw_dt);
    157  no_dt:
    158    return NULL;
    159 }
    160 
    161 static void
    162 kms_sw_displaytarget_destroy(struct sw_winsys *ws,
    163                              struct sw_displaytarget *dt)
    164 {
    165    struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
    166    struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt);
    167    struct drm_mode_destroy_dumb destroy_req;
    168 
    169    kms_sw_dt->ref_count --;
    170    if (kms_sw_dt->ref_count > 0)
    171       return;
    172 
    173    memset(&destroy_req, 0, sizeof destroy_req);
    174    destroy_req.handle = kms_sw_dt->handle;
    175    drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
    176 
    177    list_del(&kms_sw_dt->link);
    178 
    179    DEBUG_PRINT("KMS-DEBUG: destroyed buffer %u\n", kms_sw_dt->handle);
    180 
    181    FREE(kms_sw_dt);
    182 }
    183 
    184 static void *
    185 kms_sw_displaytarget_map(struct sw_winsys *ws,
    186                          struct sw_displaytarget *dt,
    187                          unsigned flags)
    188 {
    189    struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
    190    struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt);
    191    struct drm_mode_map_dumb map_req;
    192    int prot, ret;
    193 
    194    memset(&map_req, 0, sizeof map_req);
    195    map_req.handle = kms_sw_dt->handle;
    196    ret = drmIoctl(kms_sw->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_req);
    197    if (ret)
    198       return NULL;
    199 
    200    prot = (flags == PIPE_TRANSFER_READ) ? PROT_READ : (PROT_READ | PROT_WRITE);
    201    kms_sw_dt->mapped = mmap(0, kms_sw_dt->size, prot, MAP_SHARED,
    202                             kms_sw->fd, map_req.offset);
    203 
    204    if (kms_sw_dt->mapped == MAP_FAILED)
    205       return NULL;
    206 
    207    DEBUG_PRINT("KMS-DEBUG: mapped buffer %u (size %u) at %p\n",
    208          kms_sw_dt->handle, kms_sw_dt->size, kms_sw_dt->mapped);
    209 
    210    return kms_sw_dt->mapped;
    211 }
    212 
    213 static struct kms_sw_displaytarget *
    214 kms_sw_displaytarget_find_and_ref(struct kms_sw_winsys *kms_sw,
    215                                   unsigned int kms_handle)
    216 {
    217    struct kms_sw_displaytarget *kms_sw_dt;
    218 
    219    LIST_FOR_EACH_ENTRY(kms_sw_dt, &kms_sw->bo_list, link) {
    220       if (kms_sw_dt->handle == kms_handle) {
    221          kms_sw_dt->ref_count++;
    222 
    223          DEBUG_PRINT("KMS-DEBUG: imported buffer %u (size %u)\n",
    224                      kms_sw_dt->handle, kms_sw_dt->size);
    225 
    226          return kms_sw_dt;
    227       }
    228    }
    229 
    230    return NULL;
    231 }
    232 
    233 static struct kms_sw_displaytarget *
    234 kms_sw_displaytarget_add_from_prime(struct kms_sw_winsys *kms_sw, int fd,
    235                                     unsigned width, unsigned height,
    236                                     unsigned stride)
    237 {
    238    uint32_t handle = -1;
    239    struct kms_sw_displaytarget * kms_sw_dt;
    240    int ret;
    241 
    242    ret = drmPrimeFDToHandle(kms_sw->fd, fd, &handle);
    243 
    244    if (ret)
    245       return NULL;
    246 
    247    kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, handle);
    248    if (kms_sw_dt)
    249       return kms_sw_dt;
    250 
    251    kms_sw_dt = CALLOC_STRUCT(kms_sw_displaytarget);
    252    if (!kms_sw_dt)
    253       return NULL;
    254 
    255    off_t lseek_ret = lseek(fd, 0, SEEK_END);
    256    if (lseek_ret == -1) {
    257       FREE(kms_sw_dt);
    258       return NULL;
    259    }
    260    kms_sw_dt->size = lseek_ret;
    261    kms_sw_dt->ref_count = 1;
    262    kms_sw_dt->handle = handle;
    263    kms_sw_dt->width = width;
    264    kms_sw_dt->height = height;
    265    kms_sw_dt->stride = stride;
    266 
    267    lseek(fd, 0, SEEK_SET);
    268 
    269    list_add(&kms_sw_dt->link, &kms_sw->bo_list);
    270 
    271    return kms_sw_dt;
    272 }
    273 
    274 static void
    275 kms_sw_displaytarget_unmap(struct sw_winsys *ws,
    276                            struct sw_displaytarget *dt)
    277 {
    278    struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt);
    279 
    280    DEBUG_PRINT("KMS-DEBUG: unmapped buffer %u (was %p)\n", kms_sw_dt->handle, kms_sw_dt->mapped);
    281 
    282    munmap(kms_sw_dt->mapped, kms_sw_dt->size);
    283    kms_sw_dt->mapped = NULL;
    284 }
    285 
    286 static struct sw_displaytarget *
    287 kms_sw_displaytarget_from_handle(struct sw_winsys *ws,
    288                                  const struct pipe_resource *templ,
    289                                  struct winsys_handle *whandle,
    290                                  unsigned *stride)
    291 {
    292    struct kms_sw_winsys *kms_sw = kms_sw_winsys(ws);
    293    struct kms_sw_displaytarget *kms_sw_dt;
    294 
    295    assert(whandle->type == DRM_API_HANDLE_TYPE_KMS ||
    296           whandle->type == DRM_API_HANDLE_TYPE_FD);
    297 
    298    if (whandle->offset != 0) {
    299       DEBUG_PRINT("KMS-DEBUG: attempt to import unsupported winsys offset %d\n",
    300                   whandle->offset);
    301       return NULL;
    302    }
    303 
    304    switch(whandle->type) {
    305    case DRM_API_HANDLE_TYPE_FD:
    306       kms_sw_dt = kms_sw_displaytarget_add_from_prime(kms_sw, whandle->handle,
    307                                                       templ->width0,
    308                                                       templ->height0,
    309                                                       whandle->stride);
    310       if (kms_sw_dt)
    311          *stride = kms_sw_dt->stride;
    312       return (struct sw_displaytarget *)kms_sw_dt;
    313    case DRM_API_HANDLE_TYPE_KMS:
    314       kms_sw_dt = kms_sw_displaytarget_find_and_ref(kms_sw, whandle->handle);
    315       if (kms_sw_dt) {
    316          *stride = kms_sw_dt->stride;
    317          return (struct sw_displaytarget *)kms_sw_dt;
    318       }
    319       /* fallthrough */
    320    default:
    321       break;
    322    }
    323 
    324    assert(0);
    325    return NULL;
    326 }
    327 
    328 static boolean
    329 kms_sw_displaytarget_get_handle(struct sw_winsys *winsys,
    330                                 struct sw_displaytarget *dt,
    331                                 struct winsys_handle *whandle)
    332 {
    333    struct kms_sw_winsys *kms_sw = kms_sw_winsys(winsys);
    334    struct kms_sw_displaytarget *kms_sw_dt = kms_sw_displaytarget(dt);
    335 
    336    switch(whandle->type) {
    337    case DRM_API_HANDLE_TYPE_KMS:
    338       whandle->handle = kms_sw_dt->handle;
    339       whandle->stride = kms_sw_dt->stride;
    340       whandle->offset = 0;
    341       return TRUE;
    342    case DRM_API_HANDLE_TYPE_FD:
    343       if (!drmPrimeHandleToFD(kms_sw->fd, kms_sw_dt->handle,
    344                              DRM_CLOEXEC, (int*)&whandle->handle)) {
    345          whandle->stride = kms_sw_dt->stride;
    346          whandle->offset = 0;
    347          return TRUE;
    348       }
    349       /* fallthrough */
    350    default:
    351       whandle->handle = 0;
    352       whandle->stride = 0;
    353       whandle->offset = 0;
    354       return FALSE;
    355    }
    356 }
    357 
    358 static void
    359 kms_sw_displaytarget_display(struct sw_winsys *ws,
    360                              struct sw_displaytarget *dt,
    361                              void *context_private,
    362                              struct pipe_box *box)
    363 {
    364    /* This function should not be called, instead the dri2 loader should
    365       handle swap buffers internally.
    366    */
    367    assert(0);
    368 }
    369 
    370 
    371 static void
    372 kms_destroy_sw_winsys(struct sw_winsys *winsys)
    373 {
    374    FREE(winsys);
    375 }
    376 
    377 struct sw_winsys *
    378 kms_dri_create_winsys(int fd)
    379 {
    380    struct kms_sw_winsys *ws;
    381 
    382    ws = CALLOC_STRUCT(kms_sw_winsys);
    383    if (!ws)
    384       return NULL;
    385 
    386    ws->fd = fd;
    387    list_inithead(&ws->bo_list);
    388 
    389    ws->base.destroy = kms_destroy_sw_winsys;
    390 
    391    ws->base.is_displaytarget_format_supported = kms_sw_is_displaytarget_format_supported;
    392 
    393    /* screen texture functions */
    394    ws->base.displaytarget_create = kms_sw_displaytarget_create;
    395    ws->base.displaytarget_destroy = kms_sw_displaytarget_destroy;
    396    ws->base.displaytarget_from_handle = kms_sw_displaytarget_from_handle;
    397    ws->base.displaytarget_get_handle = kms_sw_displaytarget_get_handle;
    398 
    399    /* texture functions */
    400    ws->base.displaytarget_map = kms_sw_displaytarget_map;
    401    ws->base.displaytarget_unmap = kms_sw_displaytarget_unmap;
    402 
    403    ws->base.displaytarget_display = kms_sw_displaytarget_display;
    404 
    405    return &ws->base;
    406 }
    407 
    408 /* vim: set sw=3 ts=8 sts=3 expandtab: */
    409