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
drm_tegra_bo_free(struct drm_tegra_bo * bo)38 static 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
drm_tegra_wrap(struct drm_tegra ** drmp,int fd,bool close)50 static 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
drm_tegra_new(int fd,struct drm_tegra ** drmp)69 drm_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
drm_tegra_close(struct drm_tegra * drm)89 drm_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
drm_tegra_bo_alloc(struct drm_tegra * drm,uint32_t handle,uint32_t flags,uint32_t size)100 static 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
120 drm_public int
drm_tegra_bo_new(struct drm_tegra * drm,uint32_t flags,uint32_t size,struct drm_tegra_bo ** bop)121 drm_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
154 drm_public int
drm_tegra_bo_wrap(struct drm_tegra * drm,uint32_t handle,uint32_t flags,uint32_t size,struct drm_tegra_bo ** bop)155 drm_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
drm_tegra_bo_ref(struct drm_tegra_bo * bo)172 drm_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
drm_tegra_bo_unref(struct drm_tegra_bo * bo)180 drm_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
186 drm_public int
drm_tegra_bo_get_handle(struct drm_tegra_bo * bo,uint32_t * handle)187 drm_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
drm_tegra_bo_map(struct drm_tegra_bo * bo,void ** ptr)197 drm_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
drm_tegra_bo_unmap(struct drm_tegra_bo * bo)229 drm_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
drm_tegra_bo_get_name(struct drm_tegra_bo * bo,uint32_t * name)245 drm_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
264 drm_public int
drm_tegra_bo_open(struct drm_tegra * drm,uint32_t name,uint32_t flags,struct drm_tegra_bo ** bop)265 drm_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
290 free:
291 free(bo);
292 return err;
293 }
294
drm_tegra_bo_export(struct drm_tegra_bo * bo,uint32_t flags)295 drm_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
fd_get_size(int fd)308 static 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
328 drm_public int
drm_tegra_bo_import(struct drm_tegra * drm,int fd,struct drm_tegra_bo ** bop)329 drm_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
351 free:
352 free(bo);
353 return err;
354 }
355