1/* 2 * Copyright © 2012, 2013 Thierry Reding 3 * Copyright © 2013 Erik Faye-Lund 4 * Copyright © 2014 NVIDIA Corporation 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25#include <errno.h> 26#include <fcntl.h> 27#include <string.h> 28#include <unistd.h> 29 30#include <sys/mman.h> 31 32#include <xf86drm.h> 33 34#include <tegra_drm.h> 35 36#include "private.h" 37 38static void drm_tegra_bo_free(struct drm_tegra_bo *bo) 39{ 40 struct drm_tegra *drm = bo->drm; 41 42 if (bo->map) 43 munmap(bo->map, bo->size); 44 45 drmCloseBufferHandle(drm->fd, bo->handle); 46 47 free(bo); 48} 49 50static int drm_tegra_wrap(struct drm_tegra **drmp, int fd, bool close) 51{ 52 struct drm_tegra *drm; 53 54 if (fd < 0 || !drmp) 55 return -EINVAL; 56 57 drm = calloc(1, sizeof(*drm)); 58 if (!drm) 59 return -ENOMEM; 60 61 drm->close = close; 62 drm->fd = fd; 63 64 *drmp = drm; 65 66 return 0; 67} 68 69drm_public int drm_tegra_new(int fd, struct drm_tegra **drmp) 70{ 71 bool supported = false; 72 drmVersionPtr version; 73 74 version = drmGetVersion(fd); 75 if (!version) 76 return -ENOMEM; 77 78 if (!strncmp(version->name, "tegra", version->name_len)) 79 supported = true; 80 81 drmFreeVersion(version); 82 83 if (!supported) 84 return -ENOTSUP; 85 86 return drm_tegra_wrap(drmp, fd, false); 87} 88 89drm_public void drm_tegra_close(struct drm_tegra *drm) 90{ 91 if (!drm) 92 return; 93 94 if (drm->close) 95 close(drm->fd); 96 97 free(drm); 98} 99 100static struct drm_tegra_bo *drm_tegra_bo_alloc(struct drm_tegra *drm, 101 uint32_t handle, 102 uint32_t flags, 103 uint32_t size) 104{ 105 struct drm_tegra_bo *bo; 106 107 bo = calloc(1, sizeof(*bo)); 108 if (!bo) 109 return NULL; 110 111 atomic_set(&bo->ref, 1); 112 bo->handle = handle; 113 bo->flags = flags; 114 bo->size = size; 115 bo->drm = drm; 116 117 return bo; 118} 119 120drm_public int 121drm_tegra_bo_new(struct drm_tegra *drm, uint32_t flags, uint32_t size, 122 struct drm_tegra_bo **bop) 123{ 124 struct drm_tegra_gem_create args; 125 struct drm_tegra_bo *bo; 126 int err; 127 128 if (!drm || size == 0 || !bop) 129 return -EINVAL; 130 131 bo = drm_tegra_bo_alloc(drm, 0, flags, size); 132 if (!bo) 133 return -ENOMEM; 134 135 memset(&args, 0, sizeof(args)); 136 args.flags = flags; 137 args.size = size; 138 139 err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_CREATE, &args, 140 sizeof(args)); 141 if (err < 0) { 142 err = -errno; 143 free(bo); 144 return err; 145 } 146 147 bo->handle = args.handle; 148 149 *bop = bo; 150 151 return 0; 152} 153 154drm_public int 155drm_tegra_bo_wrap(struct drm_tegra *drm, uint32_t handle, uint32_t flags, 156 uint32_t size, struct drm_tegra_bo **bop) 157{ 158 struct drm_tegra_bo *bo; 159 160 if (!drm || !bop) 161 return -EINVAL; 162 163 bo = drm_tegra_bo_alloc(drm, handle, flags, size); 164 if (!bo) 165 return -ENOMEM; 166 167 *bop = bo; 168 169 return 0; 170} 171 172drm_public struct drm_tegra_bo *drm_tegra_bo_ref(struct drm_tegra_bo *bo) 173{ 174 if (bo) 175 atomic_inc(&bo->ref); 176 177 return bo; 178} 179 180drm_public void drm_tegra_bo_unref(struct drm_tegra_bo *bo) 181{ 182 if (bo && atomic_dec_and_test(&bo->ref)) 183 drm_tegra_bo_free(bo); 184} 185 186drm_public int 187drm_tegra_bo_get_handle(struct drm_tegra_bo *bo, uint32_t *handle) 188{ 189 if (!bo || !handle) 190 return -EINVAL; 191 192 *handle = bo->handle; 193 194 return 0; 195} 196 197drm_public int drm_tegra_bo_map(struct drm_tegra_bo *bo, void **ptr) 198{ 199 struct drm_tegra *drm = bo->drm; 200 201 if (!bo->map) { 202 struct drm_tegra_gem_mmap args; 203 int err; 204 205 memset(&args, 0, sizeof(args)); 206 args.handle = bo->handle; 207 208 err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_MMAP, &args, 209 sizeof(args)); 210 if (err < 0) 211 return -errno; 212 213 bo->offset = args.offset; 214 215 bo->map = drm_mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, 216 drm->fd, bo->offset); 217 if (bo->map == MAP_FAILED) { 218 bo->map = NULL; 219 return -errno; 220 } 221 } 222 223 if (ptr) 224 *ptr = bo->map; 225 226 return 0; 227} 228 229drm_public int drm_tegra_bo_unmap(struct drm_tegra_bo *bo) 230{ 231 if (!bo) 232 return -EINVAL; 233 234 if (!bo->map) 235 return 0; 236 237 if (munmap(bo->map, bo->size)) 238 return -errno; 239 240 bo->map = NULL; 241 242 return 0; 243} 244 245drm_public int drm_tegra_bo_get_name(struct drm_tegra_bo *bo, uint32_t *name) 246{ 247 struct drm_tegra *drm = bo->drm; 248 struct drm_gem_flink args; 249 int err; 250 251 memset(&args, 0, sizeof(args)); 252 args.handle = bo->handle; 253 254 err = drmIoctl(drm->fd, DRM_IOCTL_GEM_FLINK, &args); 255 if (err < 0) 256 return err; 257 258 if (name) 259 *name = args.name; 260 261 return 0; 262} 263 264drm_public int 265drm_tegra_bo_open(struct drm_tegra *drm, uint32_t name, uint32_t flags, 266 struct drm_tegra_bo **bop) 267{ 268 struct drm_gem_open args; 269 struct drm_tegra_bo *bo; 270 int err; 271 272 bo = drm_tegra_bo_alloc(drm, 0, flags, 0); 273 if (!bo) 274 return -ENOMEM; 275 276 memset(&args, 0, sizeof(args)); 277 args.name = name; 278 279 err = drmIoctl(drm->fd, DRM_IOCTL_GEM_OPEN, &args); 280 if (err < 0) 281 goto free; 282 283 bo->handle = args.handle; 284 bo->size = args.size; 285 286 *bop = bo; 287 288 return 0; 289 290free: 291 free(bo); 292 return err; 293} 294 295drm_public int drm_tegra_bo_export(struct drm_tegra_bo *bo, uint32_t flags) 296{ 297 int fd, err; 298 299 flags |= DRM_CLOEXEC; 300 301 err = drmPrimeHandleToFD(bo->drm->fd, bo->handle, flags, &fd); 302 if (err < 0) 303 return err; 304 305 return fd; 306} 307 308static ssize_t fd_get_size(int fd) 309{ 310 ssize_t size, offset; 311 int err; 312 313 offset = lseek(fd, 0, SEEK_CUR); 314 if (offset < 0) 315 return -errno; 316 317 size = lseek(fd, 0, SEEK_END); 318 if (size < 0) 319 return -errno; 320 321 err = lseek(fd, offset, SEEK_SET); 322 if (err < 0) 323 return -errno; 324 325 return size; 326} 327 328drm_public int 329drm_tegra_bo_import(struct drm_tegra *drm, int fd, struct drm_tegra_bo **bop) 330{ 331 struct drm_tegra_bo *bo; 332 ssize_t size; 333 int err; 334 335 size = fd_get_size(fd); 336 if (size < 0) 337 return size; 338 339 bo = drm_tegra_bo_alloc(drm, 0, 0, size); 340 if (!bo) 341 return -ENOMEM; 342 343 err = drmPrimeFDToHandle(drm->fd, fd, &bo->handle); 344 if (err < 0) 345 goto free; 346 347 *bop = bo; 348 349 return 0; 350 351free: 352 free(bo); 353 return err; 354} 355