| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* |
| * Copyright 2020 Intel Corporation. All rights reserved. See COPYING file |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <drm.h> |
| #include <i915_drm.h> |
| #include <amdgpu_drm.h> |
| #include "dmabuf_alloc.h" |
| |
| /* |
| * Abstraction of the buffer allocation mechanism using the DRM interface. |
| * The interface is accessed by ioctl() calls over the '/dev/dri/renderD*' |
| * device. Successful access usually requires the effective user id being |
| * in the 'render' group. |
| */ |
| |
| struct drm { |
| int fd; |
| int (*alloc)(struct drm *drm, uint64_t size, uint32_t *handle, int gtt); |
| int (*mmap_offset)(struct drm *drm, uint32_t handle, uint64_t *offset); |
| }; |
| |
| static int i915_alloc(struct drm *drm, uint64_t size, uint32_t *handle, int gtt) |
| { |
| struct drm_i915_gem_create gem_create = {}; |
| int err; |
| |
| gem_create.size = size; |
| err = ioctl(drm->fd, DRM_IOCTL_I915_GEM_CREATE, &gem_create); |
| if (err) |
| return err; |
| |
| *handle = gem_create.handle; |
| return 0; |
| } |
| |
| static int amdgpu_alloc(struct drm *drm, size_t size, uint32_t *handle, int gtt) |
| { |
| union drm_amdgpu_gem_create gem_create = {{}}; |
| int err; |
| |
| gem_create.in.bo_size = size; |
| if (gtt) { |
| gem_create.in.domains = AMDGPU_GEM_DOMAIN_GTT; |
| gem_create.in.domain_flags = AMDGPU_GEM_CREATE_CPU_GTT_USWC; |
| } else { |
| gem_create.in.domains = AMDGPU_GEM_DOMAIN_VRAM; |
| gem_create.in.domain_flags = |
| AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; |
| } |
| err = ioctl(drm->fd, DRM_IOCTL_AMDGPU_GEM_CREATE, &gem_create); |
| if (err) |
| return err; |
| |
| *handle = gem_create.out.handle; |
| return 0; |
| } |
| |
| static int i915_mmap_offset(struct drm *drm, uint32_t handle, uint64_t *offset) |
| { |
| struct drm_i915_gem_mmap_gtt gem_mmap = {}; |
| int err; |
| |
| gem_mmap.handle = handle; |
| err = ioctl(drm->fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &gem_mmap); |
| if (err) |
| return err; |
| |
| *offset = gem_mmap.offset; |
| return 0; |
| } |
| |
| static int amdgpu_mmap_offset(struct drm *drm, uint32_t handle, |
| uint64_t *offset) |
| { |
| union drm_amdgpu_gem_mmap gem_mmap = {{}}; |
| int err; |
| |
| gem_mmap.in.handle = handle; |
| err = ioctl(drm->fd, DRM_IOCTL_AMDGPU_GEM_MMAP, &gem_mmap); |
| if (err) |
| return err; |
| |
| *offset = gem_mmap.out.addr_ptr; |
| return 0; |
| } |
| |
| static struct drm *drm_open(int gpu) |
| { |
| char path[32]; |
| struct drm_version version = {}; |
| char name[16] = {}; |
| int err; |
| struct drm *drm; |
| |
| drm = malloc(sizeof(*drm)); |
| if (!drm) |
| return NULL; |
| |
| snprintf(path, sizeof(path), "/dev/dri/renderD%d", gpu + 128); |
| |
| drm->fd = open(path, O_RDWR); |
| if (drm->fd < 0) |
| goto out_free; |
| |
| version.name = name; |
| version.name_len = 16; |
| err = ioctl(drm->fd, DRM_IOCTL_VERSION, &version); |
| if (err) |
| goto out_close; |
| |
| if (!strcmp(name, "amdgpu")) { |
| drm->alloc = amdgpu_alloc; |
| drm->mmap_offset = amdgpu_mmap_offset; |
| } else if (!strcmp(name, "i915")) { |
| drm->alloc = i915_alloc; |
| drm->mmap_offset = i915_mmap_offset; |
| } else { |
| errno = EOPNOTSUPP; |
| goto out_close; |
| } |
| return drm; |
| |
| out_close: |
| close(drm->fd); |
| |
| out_free: |
| free(drm); |
| return NULL; |
| } |
| |
| static void drm_close(struct drm *drm) |
| { |
| if (!drm || drm->fd < 0) |
| return; |
| |
| close(drm->fd); |
| free(drm); |
| } |
| |
| static void drm_free_buf(struct drm *drm, uint32_t handle) |
| { |
| struct drm_gem_close close = {}; |
| |
| close.handle = handle; |
| ioctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &close); |
| } |
| |
| static int drm_alloc_buf(struct drm *drm, size_t size, uint32_t *handle, |
| int *fd, int gtt) |
| { |
| struct drm_prime_handle prime_handle = {}; |
| int err; |
| |
| if (!drm || drm->fd < 0) |
| return -EINVAL; |
| |
| err = drm->alloc(drm, size, handle, gtt); |
| if (err) |
| return err; |
| |
| prime_handle.handle = *handle; |
| prime_handle.flags = O_RDWR; |
| err = ioctl(drm->fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime_handle); |
| if (err) { |
| drm_free_buf(drm, *handle); |
| return err; |
| } |
| |
| *fd = prime_handle.fd; |
| return 0; |
| } |
| |
| static int drm_map_buf(struct drm *drm, uint32_t handle, uint64_t *offset) |
| { |
| if (!drm || drm->fd < 0) |
| return -EINVAL; |
| |
| return drm->mmap_offset(drm, handle, offset); |
| } |
| |
| /* |
| * Abstraction of dmabuf object, allocated using the DRI abstraction defined |
| * above. |
| */ |
| |
| struct dmabuf { |
| struct drm *drm; |
| int fd; |
| uint32_t handle; |
| uint64_t map_offset; |
| }; |
| |
| /* |
| * dmabuf_alloc - allocate a dmabuf from GPU |
| * @size - byte size of the buffer to allocate |
| * @gpu - the GPU unit to use |
| * @gtt - if true, allocate from GTT (Graphics Translation Table) instead of VRAM |
| */ |
| struct dmabuf *dmabuf_alloc(uint64_t size, int gpu, int gtt) |
| { |
| struct dmabuf *dmabuf; |
| int err; |
| |
| dmabuf = malloc(sizeof(*dmabuf)); |
| if (!dmabuf) |
| return NULL; |
| |
| dmabuf->drm = drm_open(gpu); |
| if (!dmabuf->drm) |
| goto out_free; |
| |
| err = drm_alloc_buf(dmabuf->drm, size, &dmabuf->handle, &dmabuf->fd, gtt); |
| if (err) |
| goto out_close; |
| |
| err = drm_map_buf(dmabuf->drm, dmabuf->handle, &dmabuf->map_offset); |
| if (err) |
| goto out_free_buf; |
| |
| return dmabuf; |
| |
| out_free_buf: |
| drm_free_buf(dmabuf->drm, dmabuf->handle); |
| |
| out_close: |
| drm_close(dmabuf->drm); |
| |
| out_free: |
| free(dmabuf); |
| return NULL; |
| } |
| |
| void dmabuf_free(struct dmabuf *dmabuf) |
| { |
| if (!dmabuf) |
| return; |
| |
| close(dmabuf->fd); |
| drm_free_buf(dmabuf->drm, dmabuf->handle); |
| drm_close(dmabuf->drm); |
| free(dmabuf); |
| } |
| |
| int dmabuf_get_drm_fd(struct dmabuf *dmabuf) |
| { |
| if (!dmabuf || !dmabuf->drm) |
| return -1; |
| |
| return dmabuf->drm->fd; |
| } |
| |
| int dmabuf_get_fd(struct dmabuf *dmabuf) |
| { |
| if (!dmabuf) |
| return -1; |
| |
| return dmabuf->fd; |
| } |
| |
| uint64_t dmabuf_get_offset(struct dmabuf *dmabuf) |
| { |
| if (!dmabuf) |
| return -1; |
| |
| return dmabuf->map_offset; |
| } |
| |