/* * Copyright © 2008 Dave Airlie * Copyright © 2008 Jérôme Glisse * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. */ /* * Authors: * Dave Airlie * Jérôme Glisse <glisse@freedesktop.org> */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include "libdrm_macros.h" #include "xf86drm.h" #include "xf86atomic.h" #include "drm.h" #include "radeon_drm.h" #include "radeon_bo.h" #include "radeon_bo_int.h" #include "radeon_bo_gem.h" #include <fcntl.h> struct radeon_bo_gem { struct radeon_bo_int base; uint32_t name; int map_count; atomic_t reloc_in_cs; void *priv_ptr; }; struct bo_manager_gem { struct radeon_bo_manager base; }; static int bo_wait(struct radeon_bo_int *boi); static struct radeon_bo *bo_open(struct radeon_bo_manager *bom, uint32_t handle, uint32_t size, uint32_t alignment, uint32_t domains, uint32_t flags) { struct radeon_bo_gem *bo; int r; bo = (struct radeon_bo_gem*)calloc(1, sizeof(struct radeon_bo_gem)); if (bo == NULL) { return NULL; } bo->base.bom = bom; bo->base.handle = 0; bo->base.size = size; bo->base.alignment = alignment; bo->base.domains = domains; bo->base.flags = flags; bo->base.ptr = NULL; atomic_set(&bo->reloc_in_cs, 0); bo->map_count = 0; if (handle) { struct drm_gem_open open_arg; memset(&open_arg, 0, sizeof(open_arg)); open_arg.name = handle; r = drmIoctl(bom->fd, DRM_IOCTL_GEM_OPEN, &open_arg); if (r != 0) { free(bo); return NULL; } bo->base.handle = open_arg.handle; bo->base.size = open_arg.size; bo->name = handle; } else { struct drm_radeon_gem_create args; args.size = size; args.alignment = alignment; args.initial_domain = bo->base.domains; args.flags = flags; args.handle = 0; r = drmCommandWriteRead(bom->fd, DRM_RADEON_GEM_CREATE, &args, sizeof(args)); bo->base.handle = args.handle; if (r) { fprintf(stderr, "Failed to allocate :\n"); fprintf(stderr, " size : %d bytes\n", size); fprintf(stderr, " alignment : %d bytes\n", alignment); fprintf(stderr, " domains : %d\n", bo->base.domains); free(bo); return NULL; } } radeon_bo_ref((struct radeon_bo*)bo); return (struct radeon_bo*)bo; } static void bo_ref(struct radeon_bo_int *boi) { } static struct radeon_bo *bo_unref(struct radeon_bo_int *boi) { struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)boi; struct drm_gem_close args; if (boi->cref) { return (struct radeon_bo *)boi; } if (bo_gem->priv_ptr) { drm_munmap(bo_gem->priv_ptr, boi->size); } /* Zero out args to make valgrind happy */ memset(&args, 0, sizeof(args)); /* close object */ args.handle = boi->handle; drmIoctl(boi->bom->fd, DRM_IOCTL_GEM_CLOSE, &args); memset(bo_gem, 0, sizeof(struct radeon_bo_gem)); free(bo_gem); return NULL; } static int bo_map(struct radeon_bo_int *boi, int write) { struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)boi; struct drm_radeon_gem_mmap args; int r; void *ptr; if (bo_gem->map_count++ != 0) { return 0; } if (bo_gem->priv_ptr) { goto wait; } boi->ptr = NULL; /* Zero out args to make valgrind happy */ memset(&args, 0, sizeof(args)); args.handle = boi->handle; args.offset = 0; args.size = (uint64_t)boi->size; r = drmCommandWriteRead(boi->bom->fd, DRM_RADEON_GEM_MMAP, &args, sizeof(args)); if (r) { fprintf(stderr, "error mapping %p 0x%08X (error = %d)\n", boi, boi->handle, r); return r; } ptr = drm_mmap(0, args.size, PROT_READ|PROT_WRITE, MAP_SHARED, boi->bom->fd, args.addr_ptr); if (ptr == MAP_FAILED) return -errno; bo_gem->priv_ptr = ptr; wait: boi->ptr = bo_gem->priv_ptr; r = bo_wait(boi); if (r) return r; return 0; } static int bo_unmap(struct radeon_bo_int *boi) { struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)boi; if (--bo_gem->map_count > 0) { return 0; } //drm_munmap(bo->ptr, bo->size); boi->ptr = NULL; return 0; } static int bo_wait(struct radeon_bo_int *boi) { struct drm_radeon_gem_wait_idle args; int ret; /* Zero out args to make valgrind happy */ memset(&args, 0, sizeof(args)); args.handle = boi->handle; do { ret = drmCommandWrite(boi->bom->fd, DRM_RADEON_GEM_WAIT_IDLE, &args, sizeof(args)); } while (ret == -EBUSY); return ret; } static int bo_is_busy(struct radeon_bo_int *boi, uint32_t *domain) { struct drm_radeon_gem_busy args; int ret; args.handle = boi->handle; args.domain = 0; ret = drmCommandWriteRead(boi->bom->fd, DRM_RADEON_GEM_BUSY, &args, sizeof(args)); *domain = args.domain; return ret; } static int bo_set_tiling(struct radeon_bo_int *boi, uint32_t tiling_flags, uint32_t pitch) { struct drm_radeon_gem_set_tiling args; int r; args.handle = boi->handle; args.tiling_flags = tiling_flags; args.pitch = pitch; r = drmCommandWriteRead(boi->bom->fd, DRM_RADEON_GEM_SET_TILING, &args, sizeof(args)); return r; } static int bo_get_tiling(struct radeon_bo_int *boi, uint32_t *tiling_flags, uint32_t *pitch) { struct drm_radeon_gem_set_tiling args = {}; int r; args.handle = boi->handle; r = drmCommandWriteRead(boi->bom->fd, DRM_RADEON_GEM_GET_TILING, &args, sizeof(args)); if (r) return r; *tiling_flags = args.tiling_flags; *pitch = args.pitch; return r; } static const struct radeon_bo_funcs bo_gem_funcs = { .bo_open = bo_open, .bo_ref = bo_ref, .bo_unref = bo_unref, .bo_map = bo_map, .bo_unmap = bo_unmap, .bo_wait = bo_wait, .bo_is_static = NULL, .bo_set_tiling = bo_set_tiling, .bo_get_tiling = bo_get_tiling, .bo_is_busy = bo_is_busy, .bo_is_referenced_by_cs = NULL, }; struct radeon_bo_manager *radeon_bo_manager_gem_ctor(int fd) { struct bo_manager_gem *bomg; bomg = (struct bo_manager_gem*)calloc(1, sizeof(struct bo_manager_gem)); if (bomg == NULL) { return NULL; } bomg->base.funcs = &bo_gem_funcs; bomg->base.fd = fd; return (struct radeon_bo_manager*)bomg; } void radeon_bo_manager_gem_dtor(struct radeon_bo_manager *bom) { struct bo_manager_gem *bomg = (struct bo_manager_gem*)bom; if (bom == NULL) { return; } free(bomg); } uint32_t radeon_gem_name_bo(struct radeon_bo *bo) { struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; return bo_gem->name; } void * radeon_gem_get_reloc_in_cs(struct radeon_bo *bo) { struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; return &bo_gem->reloc_in_cs; } int radeon_gem_get_kernel_name(struct radeon_bo *bo, uint32_t *name) { struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; struct drm_gem_flink flink; int r; if (bo_gem->name) { *name = bo_gem->name; return 0; } flink.handle = bo->handle; r = drmIoctl(boi->bom->fd, DRM_IOCTL_GEM_FLINK, &flink); if (r) { return r; } bo_gem->name = flink.name; *name = flink.name; return 0; } int radeon_gem_set_domain(struct radeon_bo *bo, uint32_t read_domains, uint32_t write_domain) { struct radeon_bo_int *boi = (struct radeon_bo_int *)bo; struct drm_radeon_gem_set_domain args; int r; args.handle = bo->handle; args.read_domains = read_domains; args.write_domain = write_domain; r = drmCommandWriteRead(boi->bom->fd, DRM_RADEON_GEM_SET_DOMAIN, &args, sizeof(args)); return r; } int radeon_gem_prime_share_bo(struct radeon_bo *bo, int *handle) { struct radeon_bo_gem *bo_gem = (struct radeon_bo_gem*)bo; int ret; ret = drmPrimeHandleToFD(bo_gem->base.bom->fd, bo->handle, DRM_CLOEXEC, handle); return ret; } struct radeon_bo * radeon_gem_bo_open_prime(struct radeon_bo_manager *bom, int fd_handle, uint32_t size) { struct radeon_bo_gem *bo; int r; uint32_t handle; bo = (struct radeon_bo_gem*)calloc(1, sizeof(struct radeon_bo_gem)); if (bo == NULL) { return NULL; } bo->base.bom = bom; bo->base.handle = 0; bo->base.size = size; bo->base.alignment = 0; bo->base.domains = RADEON_GEM_DOMAIN_GTT; bo->base.flags = 0; bo->base.ptr = NULL; atomic_set(&bo->reloc_in_cs, 0); bo->map_count = 0; r = drmPrimeFDToHandle(bom->fd, fd_handle, &handle); if (r != 0) { free(bo); return NULL; } bo->base.handle = handle; bo->name = handle; radeon_bo_ref((struct radeon_bo *)bo); return (struct radeon_bo *)bo; }