nouveau.c revision e6188e58
1e88f27b3Smrg/*
2e88f27b3Smrg * Copyright 2012 Red Hat Inc.
3e88f27b3Smrg *
4e88f27b3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5e88f27b3Smrg * copy of this software and associated documentation files (the "Software"),
6e88f27b3Smrg * to deal in the Software without restriction, including without limitation
7e88f27b3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8e88f27b3Smrg * and/or sell copies of the Software, and to permit persons to whom the
9e88f27b3Smrg * Software is furnished to do so, subject to the following conditions:
10e88f27b3Smrg *
11e88f27b3Smrg * The above copyright notice and this permission notice shall be included in
12e88f27b3Smrg * all copies or substantial portions of the Software.
13e88f27b3Smrg *
14e88f27b3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15e88f27b3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16e88f27b3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17e88f27b3Smrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18e88f27b3Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19e88f27b3Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20e88f27b3Smrg * OTHER DEALINGS IN THE SOFTWARE.
21e88f27b3Smrg *
22e88f27b3Smrg * Authors: Ben Skeggs
23e88f27b3Smrg */
24e88f27b3Smrg
25e88f27b3Smrg#ifdef HAVE_CONFIG_H
26e88f27b3Smrg#include <config.h>
27e88f27b3Smrg#endif
28e88f27b3Smrg
29e88f27b3Smrg#include <stdio.h>
30e88f27b3Smrg#include <stdlib.h>
31e88f27b3Smrg#include <stdint.h>
32e88f27b3Smrg#include <string.h>
33e6188e58Smrg#include <strings.h>
34e88f27b3Smrg#include <stdbool.h>
35e88f27b3Smrg#include <assert.h>
36e88f27b3Smrg#include <errno.h>
37e88f27b3Smrg#include <fcntl.h>
38e88f27b3Smrg
39e88f27b3Smrg#include <xf86drm.h>
40e88f27b3Smrg#include <xf86atomic.h>
41e6188e58Smrg#include "libdrm_macros.h"
42e88f27b3Smrg#include "libdrm_lists.h"
43e88f27b3Smrg#include "nouveau_drm.h"
44e88f27b3Smrg
45e88f27b3Smrg#include "nouveau.h"
46e88f27b3Smrg#include "private.h"
47e88f27b3Smrg
48e88f27b3Smrg#ifdef DEBUG
49e6188e58Smrgdrm_private uint32_t nouveau_debug = 0;
50e88f27b3Smrg
51e88f27b3Smrgstatic void
52e88f27b3Smrgdebug_init(char *args)
53e88f27b3Smrg{
54e88f27b3Smrg	if (args) {
55e88f27b3Smrg		int n = strtol(args, NULL, 0);
56e88f27b3Smrg		if (n >= 0)
57e88f27b3Smrg			nouveau_debug = n;
58e88f27b3Smrg	}
59e88f27b3Smrg}
60e88f27b3Smrg#endif
61e88f27b3Smrg
62e88f27b3Smrg/* this is the old libdrm's version of nouveau_device_wrap(), the symbol
63e88f27b3Smrg * is kept here to prevent AIGLX from crashing if the DDX is linked against
64e88f27b3Smrg * the new libdrm, but the DRI driver against the old
65e88f27b3Smrg */
66e6188e58Smrgint
67e88f27b3Smrgnouveau_device_open_existing(struct nouveau_device **pdev, int close, int fd,
68e88f27b3Smrg			     drm_context_t ctx)
69e88f27b3Smrg{
70e88f27b3Smrg	return -EACCES;
71e88f27b3Smrg}
72e88f27b3Smrg
73e6188e58Smrgint
74e88f27b3Smrgnouveau_device_wrap(int fd, int close, struct nouveau_device **pdev)
75e88f27b3Smrg{
76e88f27b3Smrg	struct nouveau_device_priv *nvdev = calloc(1, sizeof(*nvdev));
77e88f27b3Smrg	struct nouveau_device *dev = &nvdev->base;
78e88f27b3Smrg	uint64_t chipset, vram, gart, bousage;
79e88f27b3Smrg	drmVersionPtr ver;
80e88f27b3Smrg	int ret;
81e88f27b3Smrg	char *tmp;
82e88f27b3Smrg
83e88f27b3Smrg#ifdef DEBUG
84e88f27b3Smrg	debug_init(getenv("NOUVEAU_LIBDRM_DEBUG"));
85e88f27b3Smrg#endif
86e88f27b3Smrg
87e88f27b3Smrg	if (!nvdev)
88e88f27b3Smrg		return -ENOMEM;
89857b0bc6Smrg	ret = pthread_mutex_init(&nvdev->lock, NULL);
90857b0bc6Smrg	if (ret) {
91857b0bc6Smrg		free(nvdev);
92857b0bc6Smrg		return ret;
93857b0bc6Smrg	}
94857b0bc6Smrg
95e88f27b3Smrg	nvdev->base.fd = fd;
96e88f27b3Smrg
97e88f27b3Smrg	ver = drmGetVersion(fd);
98e88f27b3Smrg	if (ver) dev->drm_version = (ver->version_major << 24) |
99e88f27b3Smrg				    (ver->version_minor << 8) |
100e88f27b3Smrg				     ver->version_patchlevel;
101e88f27b3Smrg	drmFreeVersion(ver);
102e88f27b3Smrg
103e88f27b3Smrg	if ( dev->drm_version != 0x00000010 &&
104e88f27b3Smrg	    (dev->drm_version <  0x01000000 ||
105e88f27b3Smrg	     dev->drm_version >= 0x02000000)) {
106e88f27b3Smrg		nouveau_device_del(&dev);
107e88f27b3Smrg		return -EINVAL;
108e88f27b3Smrg	}
109e88f27b3Smrg
110e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_CHIPSET_ID, &chipset);
111e88f27b3Smrg	if (ret == 0)
112e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_FB_SIZE, &vram);
113e88f27b3Smrg	if (ret == 0)
114e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_AGP_SIZE, &gart);
115e88f27b3Smrg	if (ret) {
116e88f27b3Smrg		nouveau_device_del(&dev);
117e88f27b3Smrg		return ret;
118e88f27b3Smrg	}
119e88f27b3Smrg
120e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_HAS_BO_USAGE, &bousage);
121e88f27b3Smrg	if (ret == 0)
122e88f27b3Smrg		nvdev->have_bo_usage = (bousage != 0);
123e88f27b3Smrg
124e88f27b3Smrg	nvdev->close = close;
125e88f27b3Smrg
126e88f27b3Smrg	tmp = getenv("NOUVEAU_LIBDRM_VRAM_LIMIT_PERCENT");
127e88f27b3Smrg	if (tmp)
128e88f27b3Smrg		nvdev->vram_limit_percent = atoi(tmp);
129e88f27b3Smrg	else
130e88f27b3Smrg		nvdev->vram_limit_percent = 80;
131e88f27b3Smrg	tmp = getenv("NOUVEAU_LIBDRM_GART_LIMIT_PERCENT");
132e88f27b3Smrg	if (tmp)
133e88f27b3Smrg		nvdev->gart_limit_percent = atoi(tmp);
134e88f27b3Smrg	else
135e88f27b3Smrg		nvdev->gart_limit_percent = 80;
136e88f27b3Smrg	DRMINITLISTHEAD(&nvdev->bo_list);
137e88f27b3Smrg	nvdev->base.object.oclass = NOUVEAU_DEVICE_CLASS;
138e88f27b3Smrg	nvdev->base.lib_version = 0x01000000;
139e88f27b3Smrg	nvdev->base.chipset = chipset;
140e88f27b3Smrg	nvdev->base.vram_size = vram;
141e88f27b3Smrg	nvdev->base.gart_size = gart;
142e88f27b3Smrg	nvdev->base.vram_limit =
143e88f27b3Smrg		(nvdev->base.vram_size * nvdev->vram_limit_percent) / 100;
144e88f27b3Smrg	nvdev->base.gart_limit =
145e88f27b3Smrg		(nvdev->base.gart_size * nvdev->gart_limit_percent) / 100;
146e88f27b3Smrg
147e88f27b3Smrg	*pdev = &nvdev->base;
148e88f27b3Smrg	return 0;
149e88f27b3Smrg}
150e88f27b3Smrg
151e6188e58Smrgint
152e88f27b3Smrgnouveau_device_open(const char *busid, struct nouveau_device **pdev)
153e88f27b3Smrg{
154e88f27b3Smrg	int ret = -ENODEV, fd = drmOpen("nouveau", busid);
155e88f27b3Smrg	if (fd >= 0) {
156e88f27b3Smrg		ret = nouveau_device_wrap(fd, 1, pdev);
157e88f27b3Smrg		if (ret)
158e88f27b3Smrg			drmClose(fd);
159e88f27b3Smrg	}
160e88f27b3Smrg	return ret;
161e88f27b3Smrg}
162e88f27b3Smrg
163e6188e58Smrgvoid
164e88f27b3Smrgnouveau_device_del(struct nouveau_device **pdev)
165e88f27b3Smrg{
166e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(*pdev);
167e88f27b3Smrg	if (nvdev) {
168e88f27b3Smrg		if (nvdev->close)
169e88f27b3Smrg			drmClose(nvdev->base.fd);
170e88f27b3Smrg		free(nvdev->client);
171857b0bc6Smrg		pthread_mutex_destroy(&nvdev->lock);
172e88f27b3Smrg		free(nvdev);
173e88f27b3Smrg		*pdev = NULL;
174e88f27b3Smrg	}
175e88f27b3Smrg}
176e88f27b3Smrg
177e6188e58Smrgint
178e88f27b3Smrgnouveau_getparam(struct nouveau_device *dev, uint64_t param, uint64_t *value)
179e88f27b3Smrg{
180e88f27b3Smrg	struct drm_nouveau_getparam r = { param, 0 };
181e88f27b3Smrg	int fd = dev->fd, ret =
182e88f27b3Smrg		drmCommandWriteRead(fd, DRM_NOUVEAU_GETPARAM, &r, sizeof(r));
183e88f27b3Smrg	*value = r.value;
184e88f27b3Smrg	return ret;
185e88f27b3Smrg}
186e88f27b3Smrg
187e6188e58Smrgint
188e88f27b3Smrgnouveau_setparam(struct nouveau_device *dev, uint64_t param, uint64_t value)
189e88f27b3Smrg{
190e88f27b3Smrg	struct drm_nouveau_setparam r = { param, value };
191e88f27b3Smrg	return drmCommandWrite(dev->fd, DRM_NOUVEAU_SETPARAM, &r, sizeof(r));
192e88f27b3Smrg}
193e88f27b3Smrg
194e6188e58Smrgint
195e88f27b3Smrgnouveau_client_new(struct nouveau_device *dev, struct nouveau_client **pclient)
196e88f27b3Smrg{
197e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
198e88f27b3Smrg	struct nouveau_client_priv *pcli;
199e88f27b3Smrg	int id = 0, i, ret = -ENOMEM;
200e88f27b3Smrg	uint32_t *clients;
201e88f27b3Smrg
202857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
203857b0bc6Smrg
204e88f27b3Smrg	for (i = 0; i < nvdev->nr_client; i++) {
205e88f27b3Smrg		id = ffs(nvdev->client[i]) - 1;
206e88f27b3Smrg		if (id >= 0)
207e88f27b3Smrg			goto out;
208e88f27b3Smrg	}
209e88f27b3Smrg
210e88f27b3Smrg	clients = realloc(nvdev->client, sizeof(uint32_t) * (i + 1));
211e88f27b3Smrg	if (!clients)
212857b0bc6Smrg		goto unlock;
213e88f27b3Smrg	nvdev->client = clients;
214e88f27b3Smrg	nvdev->client[i] = 0;
215e88f27b3Smrg	nvdev->nr_client++;
216e88f27b3Smrg
217e88f27b3Smrgout:
218e88f27b3Smrg	pcli = calloc(1, sizeof(*pcli));
219e88f27b3Smrg	if (pcli) {
220e88f27b3Smrg		nvdev->client[i] |= (1 << id);
221e88f27b3Smrg		pcli->base.device = dev;
222e88f27b3Smrg		pcli->base.id = (i * 32) + id;
223e88f27b3Smrg		ret = 0;
224e88f27b3Smrg	}
225e88f27b3Smrg
226e88f27b3Smrg	*pclient = &pcli->base;
227857b0bc6Smrg
228857b0bc6Smrgunlock:
229857b0bc6Smrg	pthread_mutex_unlock(&nvdev->lock);
230e88f27b3Smrg	return ret;
231e88f27b3Smrg}
232e88f27b3Smrg
233e6188e58Smrgvoid
234e88f27b3Smrgnouveau_client_del(struct nouveau_client **pclient)
235e88f27b3Smrg{
236e88f27b3Smrg	struct nouveau_client_priv *pcli = nouveau_client(*pclient);
237e88f27b3Smrg	struct nouveau_device_priv *nvdev;
238e88f27b3Smrg	if (pcli) {
239e88f27b3Smrg		int id = pcli->base.id;
240e88f27b3Smrg		nvdev = nouveau_device(pcli->base.device);
241857b0bc6Smrg		pthread_mutex_lock(&nvdev->lock);
242e88f27b3Smrg		nvdev->client[id / 32] &= ~(1 << (id % 32));
243857b0bc6Smrg		pthread_mutex_unlock(&nvdev->lock);
244e88f27b3Smrg		free(pcli->kref);
245e88f27b3Smrg		free(pcli);
246e88f27b3Smrg	}
247e88f27b3Smrg}
248e88f27b3Smrg
249e6188e58Smrgint
250e88f27b3Smrgnouveau_object_new(struct nouveau_object *parent, uint64_t handle,
251e88f27b3Smrg		   uint32_t oclass, void *data, uint32_t length,
252e88f27b3Smrg		   struct nouveau_object **pobj)
253e88f27b3Smrg{
254e88f27b3Smrg	struct nouveau_device *dev;
255e88f27b3Smrg	struct nouveau_object *obj;
256e88f27b3Smrg	int ret = -EINVAL;
257e88f27b3Smrg
258e88f27b3Smrg	if (length == 0)
259e88f27b3Smrg		length = sizeof(struct nouveau_object *);
260e88f27b3Smrg	obj = malloc(sizeof(*obj) + length);
261e88f27b3Smrg	obj->parent = parent;
262e88f27b3Smrg	obj->handle = handle;
263e88f27b3Smrg	obj->oclass = oclass;
264e88f27b3Smrg	obj->length = length;
265e88f27b3Smrg	obj->data = obj + 1;
266e88f27b3Smrg	if (data)
267e88f27b3Smrg		memcpy(obj->data, data, length);
268e88f27b3Smrg	*(struct nouveau_object **)obj->data = obj;
269e88f27b3Smrg
270e88f27b3Smrg	dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
271e88f27b3Smrg	switch (parent->oclass) {
272e88f27b3Smrg	case NOUVEAU_DEVICE_CLASS:
273e88f27b3Smrg		switch (obj->oclass) {
274e88f27b3Smrg		case NOUVEAU_FIFO_CHANNEL_CLASS:
275e88f27b3Smrg		{
276e88f27b3Smrg			if (dev->chipset < 0xc0)
277e88f27b3Smrg				ret = abi16_chan_nv04(obj);
278e88f27b3Smrg			else
279e88f27b3Smrg			if (dev->chipset < 0xe0)
280e88f27b3Smrg				ret = abi16_chan_nvc0(obj);
281e88f27b3Smrg			else
282e88f27b3Smrg				ret = abi16_chan_nve0(obj);
283e88f27b3Smrg		}
284e88f27b3Smrg			break;
285e88f27b3Smrg		default:
286e88f27b3Smrg			break;
287e88f27b3Smrg		}
288e88f27b3Smrg		break;
289e88f27b3Smrg	case NOUVEAU_FIFO_CHANNEL_CLASS:
290e88f27b3Smrg		switch (obj->oclass) {
291e88f27b3Smrg		case NOUVEAU_NOTIFIER_CLASS:
292e88f27b3Smrg			ret = abi16_ntfy(obj);
293e88f27b3Smrg			break;
294e88f27b3Smrg		default:
295e88f27b3Smrg			ret = abi16_engobj(obj);
296e88f27b3Smrg			break;
297e88f27b3Smrg		}
298e88f27b3Smrg	default:
299e88f27b3Smrg		break;
300e88f27b3Smrg	}
301e88f27b3Smrg
302e88f27b3Smrg	if (ret) {
303e88f27b3Smrg		free(obj);
304e88f27b3Smrg		return ret;
305e88f27b3Smrg	}
306e88f27b3Smrg
307e88f27b3Smrg	*pobj = obj;
308e88f27b3Smrg	return 0;
309e88f27b3Smrg}
310e88f27b3Smrg
311e6188e58Smrgvoid
312e88f27b3Smrgnouveau_object_del(struct nouveau_object **pobj)
313e88f27b3Smrg{
314e88f27b3Smrg	struct nouveau_object *obj = *pobj;
315e88f27b3Smrg	struct nouveau_device *dev;
316e88f27b3Smrg	if (obj) {
317e88f27b3Smrg		dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
318e88f27b3Smrg		if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
319e88f27b3Smrg			struct drm_nouveau_channel_free req;
320e88f27b3Smrg			req.channel = obj->handle;
321e88f27b3Smrg			drmCommandWrite(dev->fd, DRM_NOUVEAU_CHANNEL_FREE,
322e88f27b3Smrg					&req, sizeof(req));
323e88f27b3Smrg		} else {
324e88f27b3Smrg			struct drm_nouveau_gpuobj_free req;
325e88f27b3Smrg			req.channel = obj->parent->handle;
326e88f27b3Smrg			req.handle  = obj->handle;
327e88f27b3Smrg			drmCommandWrite(dev->fd, DRM_NOUVEAU_GPUOBJ_FREE,
328e88f27b3Smrg					&req, sizeof(req));
329e88f27b3Smrg		}
330e88f27b3Smrg	}
331e88f27b3Smrg	free(obj);
332e88f27b3Smrg	*pobj = NULL;
333e88f27b3Smrg}
334e88f27b3Smrg
335e6188e58Smrgvoid *
336e88f27b3Smrgnouveau_object_find(struct nouveau_object *obj, uint32_t pclass)
337e88f27b3Smrg{
338e88f27b3Smrg	while (obj && obj->oclass != pclass) {
339e88f27b3Smrg		obj = obj->parent;
340e88f27b3Smrg		if (pclass == NOUVEAU_PARENT_CLASS)
341e88f27b3Smrg			break;
342e88f27b3Smrg	}
343e88f27b3Smrg	return obj;
344e88f27b3Smrg}
345e88f27b3Smrg
346e88f27b3Smrgstatic void
347e88f27b3Smrgnouveau_bo_del(struct nouveau_bo *bo)
348e88f27b3Smrg{
349857b0bc6Smrg	struct nouveau_device_priv *nvdev = nouveau_device(bo->device);
350e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
351e88f27b3Smrg	struct drm_gem_close req = { bo->handle };
352857b0bc6Smrg
353e6188e58Smrg	if (nvbo->head.next) {
354e6188e58Smrg		pthread_mutex_lock(&nvdev->lock);
355e6188e58Smrg		if (atomic_read(&nvbo->refcnt) == 0) {
356e6188e58Smrg			DRMLISTDEL(&nvbo->head);
357857b0bc6Smrg			/*
358e6188e58Smrg			 * This bo has to be closed with the lock held because
359e6188e58Smrg			 * gem handles are not refcounted. If a shared bo is
360e6188e58Smrg			 * closed and re-opened in another thread a race
361e6188e58Smrg			 * against DRM_IOCTL_GEM_OPEN or drmPrimeFDToHandle
362e6188e58Smrg			 * might cause the bo to be closed accidentally while
363e6188e58Smrg			 * re-importing.
364857b0bc6Smrg			 */
365e6188e58Smrg			drmIoctl(bo->device->fd, DRM_IOCTL_GEM_CLOSE, &req);
366857b0bc6Smrg		}
367857b0bc6Smrg		pthread_mutex_unlock(&nvdev->lock);
368857b0bc6Smrg	} else {
369857b0bc6Smrg		drmIoctl(bo->device->fd, DRM_IOCTL_GEM_CLOSE, &req);
370857b0bc6Smrg	}
371e88f27b3Smrg	if (bo->map)
372baaff307Smrg		drm_munmap(bo->map, bo->size);
373e88f27b3Smrg	free(nvbo);
374e88f27b3Smrg}
375e88f27b3Smrg
376e6188e58Smrgint
377e88f27b3Smrgnouveau_bo_new(struct nouveau_device *dev, uint32_t flags, uint32_t align,
378e88f27b3Smrg	       uint64_t size, union nouveau_bo_config *config,
379e88f27b3Smrg	       struct nouveau_bo **pbo)
380e88f27b3Smrg{
381e88f27b3Smrg	struct nouveau_bo_priv *nvbo = calloc(1, sizeof(*nvbo));
382e88f27b3Smrg	struct nouveau_bo *bo = &nvbo->base;
383e88f27b3Smrg	int ret;
384e88f27b3Smrg
385e88f27b3Smrg	if (!nvbo)
386e88f27b3Smrg		return -ENOMEM;
387e88f27b3Smrg	atomic_set(&nvbo->refcnt, 1);
388e88f27b3Smrg	bo->device = dev;
389e88f27b3Smrg	bo->flags = flags;
390e88f27b3Smrg	bo->size = size;
391e88f27b3Smrg
392e88f27b3Smrg	ret = abi16_bo_init(bo, align, config);
393e88f27b3Smrg	if (ret) {
394e88f27b3Smrg		free(nvbo);
395e88f27b3Smrg		return ret;
396e88f27b3Smrg	}
397e88f27b3Smrg
398e88f27b3Smrg	*pbo = bo;
399e88f27b3Smrg	return 0;
400e88f27b3Smrg}
401e88f27b3Smrg
402857b0bc6Smrgstatic int
403857b0bc6Smrgnouveau_bo_wrap_locked(struct nouveau_device *dev, uint32_t handle,
404e6188e58Smrg		       struct nouveau_bo **pbo, int name)
405e88f27b3Smrg{
406e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
407e88f27b3Smrg	struct drm_nouveau_gem_info req = { .handle = handle };
408e88f27b3Smrg	struct nouveau_bo_priv *nvbo;
409e88f27b3Smrg	int ret;
410e88f27b3Smrg
411e88f27b3Smrg	DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
412e88f27b3Smrg		if (nvbo->base.handle == handle) {
413e6188e58Smrg			if (atomic_inc_return(&nvbo->refcnt) == 1) {
414e6188e58Smrg				/*
415e6188e58Smrg				 * Uh oh, this bo is dead and someone else
416e6188e58Smrg				 * will free it, but because refcnt is
417e6188e58Smrg				 * now non-zero fortunately they won't
418e6188e58Smrg				 * call the ioctl to close the bo.
419e6188e58Smrg				 *
420e6188e58Smrg				 * Remove this bo from the list so other
421e6188e58Smrg				 * calls to nouveau_bo_wrap_locked will
422e6188e58Smrg				 * see our replacement nvbo.
423e6188e58Smrg				 */
424e6188e58Smrg				DRMLISTDEL(&nvbo->head);
425e6188e58Smrg				if (!name)
426e6188e58Smrg					name = nvbo->name;
427e6188e58Smrg				break;
428e6188e58Smrg			}
429e6188e58Smrg
430e6188e58Smrg			*pbo = &nvbo->base;
431e88f27b3Smrg			return 0;
432e88f27b3Smrg		}
433e88f27b3Smrg	}
434e88f27b3Smrg
435e88f27b3Smrg	ret = drmCommandWriteRead(dev->fd, DRM_NOUVEAU_GEM_INFO,
436e88f27b3Smrg				  &req, sizeof(req));
437e88f27b3Smrg	if (ret)
438e88f27b3Smrg		return ret;
439e88f27b3Smrg
440e88f27b3Smrg	nvbo = calloc(1, sizeof(*nvbo));
441e88f27b3Smrg	if (nvbo) {
442e88f27b3Smrg		atomic_set(&nvbo->refcnt, 1);
443e88f27b3Smrg		nvbo->base.device = dev;
444e88f27b3Smrg		abi16_bo_info(&nvbo->base, &req);
445e6188e58Smrg		nvbo->name = name;
446e88f27b3Smrg		DRMLISTADD(&nvbo->head, &nvdev->bo_list);
447e88f27b3Smrg		*pbo = &nvbo->base;
448e88f27b3Smrg		return 0;
449e88f27b3Smrg	}
450e88f27b3Smrg
451e88f27b3Smrg	return -ENOMEM;
452e88f27b3Smrg}
453e88f27b3Smrg
454e6188e58Smrgstatic void
455e6188e58Smrgnouveau_bo_make_global(struct nouveau_bo_priv *nvbo)
456e6188e58Smrg{
457e6188e58Smrg	if (!nvbo->head.next) {
458e6188e58Smrg		struct nouveau_device_priv *nvdev = nouveau_device(nvbo->base.device);
459e6188e58Smrg		pthread_mutex_lock(&nvdev->lock);
460e6188e58Smrg		if (!nvbo->head.next)
461e6188e58Smrg			DRMLISTADD(&nvbo->head, &nvdev->bo_list);
462e6188e58Smrg		pthread_mutex_unlock(&nvdev->lock);
463e6188e58Smrg	}
464e6188e58Smrg}
465e6188e58Smrg
466e6188e58Smrgint
467857b0bc6Smrgnouveau_bo_wrap(struct nouveau_device *dev, uint32_t handle,
468857b0bc6Smrg		struct nouveau_bo **pbo)
469857b0bc6Smrg{
470857b0bc6Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
471857b0bc6Smrg	int ret;
472857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
473e6188e58Smrg	ret = nouveau_bo_wrap_locked(dev, handle, pbo, 0);
474857b0bc6Smrg	pthread_mutex_unlock(&nvdev->lock);
475857b0bc6Smrg	return ret;
476857b0bc6Smrg}
477857b0bc6Smrg
478e6188e58Smrgint
479e88f27b3Smrgnouveau_bo_name_ref(struct nouveau_device *dev, uint32_t name,
480e88f27b3Smrg		    struct nouveau_bo **pbo)
481e88f27b3Smrg{
482e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
483e88f27b3Smrg	struct nouveau_bo_priv *nvbo;
484e88f27b3Smrg	struct drm_gem_open req = { .name = name };
485e88f27b3Smrg	int ret;
486e88f27b3Smrg
487857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
488e88f27b3Smrg	DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
489e88f27b3Smrg		if (nvbo->name == name) {
490e6188e58Smrg			ret = nouveau_bo_wrap_locked(dev, nvbo->base.handle,
491e6188e58Smrg						     pbo, name);
492857b0bc6Smrg			pthread_mutex_unlock(&nvdev->lock);
493e6188e58Smrg			return ret;
494e88f27b3Smrg		}
495e88f27b3Smrg	}
496e88f27b3Smrg
497e88f27b3Smrg	ret = drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req);
498e88f27b3Smrg	if (ret == 0) {
499e6188e58Smrg		ret = nouveau_bo_wrap_locked(dev, req.handle, pbo, name);
500e88f27b3Smrg	}
501e88f27b3Smrg
502e6188e58Smrg	pthread_mutex_unlock(&nvdev->lock);
503e88f27b3Smrg	return ret;
504e88f27b3Smrg}
505e88f27b3Smrg
506e6188e58Smrgint
507e88f27b3Smrgnouveau_bo_name_get(struct nouveau_bo *bo, uint32_t *name)
508e88f27b3Smrg{
509e88f27b3Smrg	struct drm_gem_flink req = { .handle = bo->handle };
510e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
511857b0bc6Smrg
512857b0bc6Smrg	*name = nvbo->name;
513e6188e58Smrg	if (!*name) {
514e88f27b3Smrg		int ret = drmIoctl(bo->device->fd, DRM_IOCTL_GEM_FLINK, &req);
515e6188e58Smrg
516857b0bc6Smrg		if (ret) {
517857b0bc6Smrg			*name = 0;
518e88f27b3Smrg			return ret;
519857b0bc6Smrg		}
520857b0bc6Smrg		nvbo->name = *name = req.name;
521e6188e58Smrg
522e6188e58Smrg		nouveau_bo_make_global(nvbo);
523e88f27b3Smrg	}
524e88f27b3Smrg	return 0;
525e88f27b3Smrg}
526e88f27b3Smrg
527e6188e58Smrgvoid
528e88f27b3Smrgnouveau_bo_ref(struct nouveau_bo *bo, struct nouveau_bo **pref)
529e88f27b3Smrg{
530e88f27b3Smrg	struct nouveau_bo *ref = *pref;
531e88f27b3Smrg	if (bo) {
532e88f27b3Smrg		atomic_inc(&nouveau_bo(bo)->refcnt);
533e88f27b3Smrg	}
534e88f27b3Smrg	if (ref) {
535e88f27b3Smrg		if (atomic_dec_and_test(&nouveau_bo(ref)->refcnt))
536e88f27b3Smrg			nouveau_bo_del(ref);
537e88f27b3Smrg	}
538e88f27b3Smrg	*pref = bo;
539e88f27b3Smrg}
540e88f27b3Smrg
541e6188e58Smrgint
542e88f27b3Smrgnouveau_bo_prime_handle_ref(struct nouveau_device *dev, int prime_fd,
543e88f27b3Smrg			    struct nouveau_bo **bo)
544e88f27b3Smrg{
545857b0bc6Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
546e88f27b3Smrg	int ret;
547e88f27b3Smrg	unsigned int handle;
548e88f27b3Smrg
549857b0bc6Smrg	nouveau_bo_ref(NULL, bo);
550e88f27b3Smrg
551857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
552857b0bc6Smrg	ret = drmPrimeFDToHandle(dev->fd, prime_fd, &handle);
553857b0bc6Smrg	if (ret == 0) {
554e6188e58Smrg		ret = nouveau_bo_wrap_locked(dev, handle, bo, 0);
555e88f27b3Smrg	}
556857b0bc6Smrg	pthread_mutex_unlock(&nvdev->lock);
557857b0bc6Smrg	return ret;
558e88f27b3Smrg}
559e88f27b3Smrg
560e6188e58Smrgint
561e88f27b3Smrgnouveau_bo_set_prime(struct nouveau_bo *bo, int *prime_fd)
562e88f27b3Smrg{
563e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
564e88f27b3Smrg	int ret;
565e88f27b3Smrg
566e88f27b3Smrg	ret = drmPrimeHandleToFD(bo->device->fd, nvbo->base.handle, DRM_CLOEXEC, prime_fd);
567e88f27b3Smrg	if (ret)
568e88f27b3Smrg		return ret;
569e6188e58Smrg
570e6188e58Smrg	nouveau_bo_make_global(nvbo);
571e88f27b3Smrg	return 0;
572e88f27b3Smrg}
573e88f27b3Smrg
574e6188e58Smrgint
575e88f27b3Smrgnouveau_bo_wait(struct nouveau_bo *bo, uint32_t access,
576e88f27b3Smrg		struct nouveau_client *client)
577e88f27b3Smrg{
578e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
579e88f27b3Smrg	struct drm_nouveau_gem_cpu_prep req;
580e88f27b3Smrg	struct nouveau_pushbuf *push;
581e88f27b3Smrg	int ret = 0;
582e88f27b3Smrg
583e88f27b3Smrg	if (!(access & NOUVEAU_BO_RDWR))
584e88f27b3Smrg		return 0;
585e88f27b3Smrg
586e88f27b3Smrg	push = cli_push_get(client, bo);
587e88f27b3Smrg	if (push && push->channel)
588e88f27b3Smrg		nouveau_pushbuf_kick(push, push->channel);
589e88f27b3Smrg
590e6188e58Smrg	if (!nvbo->head.next && !(nvbo->access & NOUVEAU_BO_WR) &&
591e6188e58Smrg				!(access & NOUVEAU_BO_WR))
592e88f27b3Smrg		return 0;
593e88f27b3Smrg
594e88f27b3Smrg	req.handle = bo->handle;
595e88f27b3Smrg	req.flags = 0;
596e88f27b3Smrg	if (access & NOUVEAU_BO_WR)
597e88f27b3Smrg		req.flags |= NOUVEAU_GEM_CPU_PREP_WRITE;
598e88f27b3Smrg	if (access & NOUVEAU_BO_NOBLOCK)
599e88f27b3Smrg		req.flags |= NOUVEAU_GEM_CPU_PREP_NOWAIT;
600e88f27b3Smrg
601e88f27b3Smrg	ret = drmCommandWrite(bo->device->fd, DRM_NOUVEAU_GEM_CPU_PREP,
602e88f27b3Smrg			      &req, sizeof(req));
603e88f27b3Smrg	if (ret == 0)
604e88f27b3Smrg		nvbo->access = 0;
605e88f27b3Smrg	return ret;
606e88f27b3Smrg}
607e88f27b3Smrg
608e6188e58Smrgint
609e88f27b3Smrgnouveau_bo_map(struct nouveau_bo *bo, uint32_t access,
610e88f27b3Smrg	       struct nouveau_client *client)
611e88f27b3Smrg{
612e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
613e88f27b3Smrg	if (bo->map == NULL) {
614baaff307Smrg		bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
615e88f27b3Smrg			       MAP_SHARED, bo->device->fd, nvbo->map_handle);
616e88f27b3Smrg		if (bo->map == MAP_FAILED) {
617e88f27b3Smrg			bo->map = NULL;
618e88f27b3Smrg			return -errno;
619e88f27b3Smrg		}
620e88f27b3Smrg	}
621e88f27b3Smrg	return nouveau_bo_wait(bo, access, client);
622e88f27b3Smrg}
623