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#ifdef HAVE_CONFIG_H
26#  include "config.h"
27#endif
28
29#include <errno.h>
30#include <fcntl.h>
31#include <string.h>
32#include <unistd.h>
33
34#include <sys/mman.h>
35
36#include <xf86drm.h>
37
38#include <tegra_drm.h>
39
40#include "private.h"
41
42static void drm_tegra_bo_free(struct drm_tegra_bo *bo)
43{
44	struct drm_tegra *drm = bo->drm;
45	struct drm_gem_close args;
46
47	if (bo->map)
48		munmap(bo->map, bo->size);
49
50	memset(&args, 0, sizeof(args));
51	args.handle = bo->handle;
52
53	drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &args);
54
55	free(bo);
56}
57
58static int drm_tegra_wrap(struct drm_tegra **drmp, int fd, bool close)
59{
60	struct drm_tegra *drm;
61
62	if (fd < 0 || !drmp)
63		return -EINVAL;
64
65	drm = calloc(1, sizeof(*drm));
66	if (!drm)
67		return -ENOMEM;
68
69	drm->close = close;
70	drm->fd = fd;
71
72	*drmp = drm;
73
74	return 0;
75}
76
77int drm_tegra_new(struct drm_tegra **drmp, int fd)
78{
79	bool supported = false;
80	drmVersionPtr version;
81
82	version = drmGetVersion(fd);
83	if (!version)
84		return -ENOMEM;
85
86	if (!strncmp(version->name, "tegra", version->name_len))
87		supported = true;
88
89	drmFreeVersion(version);
90
91	if (!supported)
92		return -ENOTSUP;
93
94	return drm_tegra_wrap(drmp, fd, false);
95}
96
97void drm_tegra_close(struct drm_tegra *drm)
98{
99	if (!drm)
100		return;
101
102	if (drm->close)
103		close(drm->fd);
104
105	free(drm);
106}
107
108int drm_tegra_bo_new(struct drm_tegra_bo **bop, struct drm_tegra *drm,
109		     uint32_t flags, uint32_t size)
110{
111	struct drm_tegra_gem_create args;
112	struct drm_tegra_bo *bo;
113	int err;
114
115	if (!drm || size == 0 || !bop)
116		return -EINVAL;
117
118	bo = calloc(1, sizeof(*bo));
119	if (!bo)
120		return -ENOMEM;
121
122	atomic_set(&bo->ref, 1);
123	bo->flags = flags;
124	bo->size = size;
125	bo->drm = drm;
126
127	memset(&args, 0, sizeof(args));
128	args.flags = flags;
129	args.size = size;
130
131	err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_CREATE, &args,
132				  sizeof(args));
133	if (err < 0) {
134		err = -errno;
135		free(bo);
136		return err;
137	}
138
139	bo->handle = args.handle;
140
141	*bop = bo;
142
143	return 0;
144}
145
146int drm_tegra_bo_wrap(struct drm_tegra_bo **bop, struct drm_tegra *drm,
147		      uint32_t handle, uint32_t flags, uint32_t size)
148{
149	struct drm_tegra_bo *bo;
150
151	if (!drm || !bop)
152		return -EINVAL;
153
154	bo = calloc(1, sizeof(*bo));
155	if (!bo)
156		return -ENOMEM;
157
158	atomic_set(&bo->ref, 1);
159	bo->handle = handle;
160	bo->flags = flags;
161	bo->size = size;
162	bo->drm = drm;
163
164	*bop = bo;
165
166	return 0;
167}
168
169int drm_tegra_bo_name_ref(struct drm_tegra *drm, uint32_t name, uint32_t size,
170			 struct drm_tegra_bo **bop)
171{
172	struct drm_tegra_bo *bo;
173	struct drm_gem_open open_args;
174	struct drm_gem_close close_args;
175	int ret;
176
177	memset(&open_args, 0, sizeof(open_args));
178
179	open_args.name = name;
180
181	ret = drmIoctl(drm->fd, DRM_IOCTL_GEM_OPEN, &open_args);
182	if (ret)
183		return ret;
184
185	ret = drm_tegra_bo_wrap(bop, drm, open_args.handle, 0, size);
186	if (ret)
187		goto err;
188
189	(*bop)->name = name;
190
191	return 0;
192
193err:
194	memset(&close_args, 0, sizeof(close_args));
195	close_args.handle = open_args.handle;
196	drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &close_args);
197
198	return ret;
199}
200
201int drm_tegra_bo_name_get(struct drm_tegra_bo *bo, uint32_t *name)
202{
203	struct drm_gem_flink args;
204	int ret;
205
206	args.handle =  bo->handle;
207
208	*name = bo->name;
209	if (*name && *name != ~0U)
210		return 0;
211
212	ret = drmIoctl(bo->drm->fd, DRM_IOCTL_GEM_FLINK, &args);
213	if (ret) {
214		*name = 0;
215		return ret;
216	}
217
218	bo->name = args.name;
219	*name = bo->name;
220
221	return 0;
222}
223
224struct drm_tegra_bo *drm_tegra_bo_ref(struct drm_tegra_bo *bo)
225{
226	if (bo)
227		atomic_inc(&bo->ref);
228
229	return bo;
230}
231
232void drm_tegra_bo_unref(struct drm_tegra_bo *bo)
233{
234	if (bo && atomic_dec_and_test(&bo->ref))
235		drm_tegra_bo_free(bo);
236}
237
238int drm_tegra_bo_get_handle(struct drm_tegra_bo *bo, uint32_t *handle)
239{
240	if (!bo || !handle)
241		return -EINVAL;
242
243	*handle = bo->handle;
244
245	return 0;
246}
247
248int drm_tegra_bo_map(struct drm_tegra_bo *bo, void **ptr)
249{
250	struct drm_tegra *drm = bo->drm;
251
252	if (!bo->map) {
253		struct drm_tegra_gem_mmap args;
254		int err;
255
256		memset(&args, 0, sizeof(args));
257		args.handle = bo->handle;
258
259		err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_MMAP, &args,
260					  sizeof(args));
261		if (err < 0)
262			return -errno;
263
264		bo->offset = args.offset;
265
266		bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
267			       drm->fd, bo->offset);
268		if (bo->map == MAP_FAILED) {
269			bo->map = NULL;
270			return -errno;
271		}
272	}
273
274	if (ptr)
275		*ptr = bo->map;
276
277	return 0;
278}
279
280int drm_tegra_bo_unmap(struct drm_tegra_bo *bo)
281{
282	if (!bo)
283		return -EINVAL;
284
285	if (!bo->map)
286		return 0;
287
288	if (munmap(bo->map, bo->size))
289		return -errno;
290
291	bo->map = NULL;
292
293	return 0;
294}
295
296int drm_tegra_bo_get_flags(struct drm_tegra_bo *bo, uint32_t *flags)
297{
298	struct drm_tegra_gem_get_flags args;
299	struct drm_tegra *drm = bo->drm;
300	int err;
301
302	if (!bo)
303		return -EINVAL;
304
305	memset(&args, 0, sizeof(args));
306	args.handle = bo->handle;
307
308	err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_GET_FLAGS, &args,
309				  sizeof(args));
310	if (err < 0)
311		return -errno;
312
313	if (flags)
314		*flags = args.flags;
315
316	return 0;
317}
318
319int drm_tegra_bo_set_flags(struct drm_tegra_bo *bo, uint32_t flags)
320{
321	struct drm_tegra_gem_get_flags args;
322	struct drm_tegra *drm = bo->drm;
323	int err;
324
325	if (!bo)
326		return -EINVAL;
327
328	memset(&args, 0, sizeof(args));
329	args.handle = bo->handle;
330	args.flags = flags;
331
332	err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_SET_FLAGS, &args,
333				  sizeof(args));
334	if (err < 0)
335		return -errno;
336
337	return 0;
338}
339
340int drm_tegra_bo_get_tiling(struct drm_tegra_bo *bo,
341			    struct drm_tegra_bo_tiling *tiling)
342{
343	struct drm_tegra_gem_get_tiling args;
344	struct drm_tegra *drm = bo->drm;
345	int err;
346
347	if (!bo)
348		return -EINVAL;
349
350	memset(&args, 0, sizeof(args));
351	args.handle = bo->handle;
352
353	err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_GET_TILING, &args,
354				  sizeof(args));
355	if (err < 0)
356		return -errno;
357
358	if (tiling) {
359		tiling->mode = args.mode;
360		tiling->value = args.value;
361	}
362
363	return 0;
364}
365
366int drm_tegra_bo_set_tiling(struct drm_tegra_bo *bo,
367			    const struct drm_tegra_bo_tiling *tiling)
368{
369	struct drm_tegra_gem_set_tiling args;
370	struct drm_tegra *drm = bo->drm;
371	int err;
372
373	if (!bo)
374		return -EINVAL;
375
376	memset(&args, 0, sizeof(args));
377	args.handle = bo->handle;
378	args.mode = tiling->mode;
379	args.value = tiling->value;
380
381	err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_SET_TILING, &args,
382				  sizeof(args));
383	if (err < 0)
384		return -errno;
385
386	return 0;
387}
388