drm_bufs.c revision 152909
1145132Sanholt/* drm_bufs.h -- Generic buffer template -*- linux-c -*-
2145132Sanholt * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
3145132Sanholt */
4145132Sanholt/*-
5145132Sanholt * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
6145132Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
7145132Sanholt * All Rights Reserved.
8145132Sanholt *
9145132Sanholt * Permission is hereby granted, free of charge, to any person obtaining a
10145132Sanholt * copy of this software and associated documentation files (the "Software"),
11145132Sanholt * to deal in the Software without restriction, including without limitation
12145132Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13145132Sanholt * and/or sell copies of the Software, and to permit persons to whom the
14145132Sanholt * Software is furnished to do so, subject to the following conditions:
15145132Sanholt *
16145132Sanholt * The above copyright notice and this permission notice (including the next
17145132Sanholt * paragraph) shall be included in all copies or substantial portions of the
18145132Sanholt * Software.
19145132Sanholt *
20145132Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21145132Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22145132Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23145132Sanholt * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24145132Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25145132Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26145132Sanholt * OTHER DEALINGS IN THE SOFTWARE.
27145132Sanholt *
28145132Sanholt * Authors:
29145132Sanholt *    Rickard E. (Rik) Faith <faith@valinux.com>
30145132Sanholt *    Gareth Hughes <gareth@valinux.com>
31145132Sanholt *
32145132Sanholt */
33145132Sanholt
34152909Sanholt#include <sys/cdefs.h>
35152909Sanholt__FBSDID("$FreeBSD: head/sys/dev/drm/drm_bufs.c 152909 2005-11-28 23:13:57Z anholt $");
36152909Sanholt
37152909Sanholt#include "dev/pci/pcireg.h"
38152909Sanholt
39145132Sanholt#include "dev/drm/drmP.h"
40145132Sanholt
41145132Sanholt/*
42145132Sanholt * Compute order.  Can be made faster.
43145132Sanholt */
44145132Sanholtint drm_order(unsigned long size)
45145132Sanholt{
46145132Sanholt	int order;
47145132Sanholt	unsigned long tmp;
48145132Sanholt
49145132Sanholt	for ( order = 0, tmp = size ; tmp >>= 1 ; ++order );
50145132Sanholt
51145132Sanholt	if ( size & ~(1 << order) )
52145132Sanholt		++order;
53145132Sanholt
54145132Sanholt	return order;
55145132Sanholt}
56145132Sanholt
57152909Sanholt/* Allocation of PCI memory resources (framebuffer, registers, etc.) for
58152909Sanholt * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
59152909Sanholt * address for accessing them.  Cleaned up at unload.
60152909Sanholt */
61152909Sanholtstatic int drm_alloc_resource(drm_device_t *dev, int resource)
62145132Sanholt{
63152909Sanholt	if (resource >= DRM_MAX_PCI_RESOURCE) {
64152909Sanholt		DRM_ERROR("Resource %d too large\n", resource);
65152909Sanholt		return 1;
66152909Sanholt	}
67145132Sanholt
68152909Sanholt	DRM_UNLOCK();
69152909Sanholt	if (dev->pcir[resource] != NULL) {
70152909Sanholt		DRM_LOCK();
71145132Sanholt		return 0;
72145132Sanholt	}
73145132Sanholt
74152909Sanholt	dev->pcirid[resource] = PCIR_BAR(resource);
75152909Sanholt	dev->pcir[resource] = bus_alloc_resource_any(dev->device,
76152909Sanholt	    SYS_RES_MEMORY, &dev->pcirid[resource], RF_SHAREABLE);
77152909Sanholt	DRM_LOCK();
78145132Sanholt
79152909Sanholt	if (dev->pcir[resource] == NULL) {
80152909Sanholt		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
81152909Sanholt		return 1;
82152909Sanholt	}
83145132Sanholt
84152909Sanholt	return 0;
85145132Sanholt}
86145132Sanholt
87152909Sanholtunsigned long drm_get_resource_start(drm_device_t *dev, unsigned int resource)
88145132Sanholt{
89152909Sanholt	if (drm_alloc_resource(dev, resource) != 0)
90152909Sanholt		return 0;
91145132Sanholt
92152909Sanholt	return rman_get_start(dev->pcir[resource]);
93145132Sanholt}
94145132Sanholt
95152909Sanholtunsigned long drm_get_resource_len(drm_device_t *dev, unsigned int resource)
96145132Sanholt{
97152909Sanholt	if (drm_alloc_resource(dev, resource) != 0)
98152909Sanholt		return 0;
99145132Sanholt
100152909Sanholt	return rman_get_size(dev->pcir[resource]);
101145132Sanholt}
102145132Sanholt
103152909Sanholtint drm_addmap(drm_device_t * dev, unsigned long offset, unsigned long size,
104152909Sanholt    drm_map_type_t type, drm_map_flags_t flags, drm_local_map_t **map_ptr)
105145132Sanholt{
106145132Sanholt	drm_local_map_t *map;
107152909Sanholt	int align;
108152909Sanholt	/*drm_agp_mem_t *entry;
109152909Sanholt	int valid;*/
110145132Sanholt
111145132Sanholt	/* Only allow shared memory to be removable since we only keep enough
112145132Sanholt	 * book keeping information about shared memory to allow for removal
113145132Sanholt	 * when processes fork.
114145132Sanholt	 */
115152909Sanholt	if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
116152909Sanholt		DRM_ERROR("Requested removable map for non-DRM_SHM\n");
117145132Sanholt		return EINVAL;
118152909Sanholt	}
119152909Sanholt	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
120152909Sanholt		DRM_ERROR("offset/size not page aligned: 0x%lx/0x%lx\n",
121152909Sanholt		    offset, size);
122145132Sanholt		return EINVAL;
123152909Sanholt	}
124152909Sanholt	if (offset + size < offset) {
125152909Sanholt		DRM_ERROR("offset and size wrap around: 0x%lx/0x%lx\n",
126152909Sanholt		    offset, size);
127145132Sanholt		return EINVAL;
128152909Sanholt	}
129145132Sanholt
130152909Sanholt	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", offset,
131152909Sanholt	    size, type);
132145132Sanholt
133145132Sanholt	/* Check if this is just another version of a kernel-allocated map, and
134145132Sanholt	 * just hand that back if so.
135145132Sanholt	 */
136152909Sanholt	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
137152909Sanholt	    type == _DRM_SHM) {
138145132Sanholt		TAILQ_FOREACH(map, &dev->maplist, link) {
139152909Sanholt			if (map->type == type && (map->offset == offset ||
140152909Sanholt			    (map->type == _DRM_SHM &&
141152909Sanholt			    map->flags == _DRM_CONTAINS_LOCK))) {
142152909Sanholt				map->size = size;
143152909Sanholt				DRM_DEBUG("Found kernel map %d\n", type);
144145132Sanholt				goto done;
145145132Sanholt			}
146145132Sanholt		}
147145132Sanholt	}
148152909Sanholt	DRM_UNLOCK();
149145132Sanholt
150145132Sanholt	/* Allocate a new map structure, fill it in, and do any type-specific
151145132Sanholt	 * initialization necessary.
152145132Sanholt	 */
153145132Sanholt	map = malloc(sizeof(*map), M_DRM, M_ZERO | M_NOWAIT);
154145132Sanholt	if ( !map )
155145132Sanholt		return DRM_ERR(ENOMEM);
156145132Sanholt
157152909Sanholt	map->offset = offset;
158152909Sanholt	map->size = size;
159152909Sanholt	map->type = type;
160152909Sanholt	map->flags = flags;
161145132Sanholt
162145132Sanholt	switch ( map->type ) {
163145132Sanholt	case _DRM_REGISTERS:
164145478Sanholt		map->handle = drm_ioremap(dev, map);
165145132Sanholt		if (!(map->flags & _DRM_WRITE_COMBINING))
166145132Sanholt			break;
167145132Sanholt		/* FALLTHROUGH */
168145132Sanholt	case _DRM_FRAME_BUFFER:
169145132Sanholt		if (drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC) == 0)
170145132Sanholt			map->mtrr = 1;
171145132Sanholt		break;
172145132Sanholt	case _DRM_SHM:
173145132Sanholt		map->handle = malloc(map->size, M_DRM, M_NOWAIT);
174145132Sanholt		DRM_DEBUG( "%lu %d %p\n",
175145132Sanholt			   map->size, drm_order(map->size), map->handle );
176145132Sanholt		if ( !map->handle ) {
177145132Sanholt			free(map, M_DRM);
178145132Sanholt			return DRM_ERR(ENOMEM);
179145132Sanholt		}
180145132Sanholt		map->offset = (unsigned long)map->handle;
181145132Sanholt		if ( map->flags & _DRM_CONTAINS_LOCK ) {
182145132Sanholt			/* Prevent a 2nd X Server from creating a 2nd lock */
183145132Sanholt			DRM_LOCK();
184145132Sanholt			if (dev->lock.hw_lock != NULL) {
185145132Sanholt				DRM_UNLOCK();
186145132Sanholt				free(map->handle, M_DRM);
187145132Sanholt				free(map, M_DRM);
188145132Sanholt				return DRM_ERR(EBUSY);
189145132Sanholt			}
190145132Sanholt			dev->lock.hw_lock = map->handle; /* Pointer to lock */
191145132Sanholt			DRM_UNLOCK();
192145132Sanholt		}
193145132Sanholt		break;
194145132Sanholt	case _DRM_AGP:
195152909Sanholt		/*valid = 0;*/
196145132Sanholt		map->offset += dev->agp->base;
197145132Sanholt		map->mtrr   = dev->agp->mtrr; /* for getmap */
198152909Sanholt		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
199152909Sanholt			if ((map->offset >= entry->bound) &&
200152909Sanholt			    (map->offset + map->size <=
201152909Sanholt			    entry->bound + entry->pages * PAGE_SIZE)) {
202152909Sanholt				valid = 1;
203152909Sanholt				break;
204152909Sanholt			}
205152909Sanholt		}
206152909Sanholt		if (!valid) {
207152909Sanholt			free(map, M_DRM);
208152909Sanholt			return DRM_ERR(EACCES);
209152909Sanholt		}*/
210145132Sanholt		break;
211145132Sanholt	case _DRM_SCATTER_GATHER:
212145132Sanholt		if (!dev->sg) {
213145132Sanholt			free(map, M_DRM);
214145132Sanholt			return DRM_ERR(EINVAL);
215145132Sanholt		}
216145132Sanholt		map->offset = map->offset + dev->sg->handle;
217145132Sanholt		break;
218145132Sanholt	case _DRM_CONSISTENT:
219152909Sanholt		/* Unfortunately, we don't get any alignment specification from
220152909Sanholt		 * the caller, so we have to guess.  drm_pci_alloc requires
221152909Sanholt		 * a power-of-two alignment, so try to align the bus address of
222152909Sanholt		 * the map to it size if possible, otherwise just assume
223152909Sanholt		 * PAGE_SIZE alignment.
224152909Sanholt		 */
225152909Sanholt		align = map->size;
226152909Sanholt		if ((align & (align - 1)) != 0)
227152909Sanholt			align = PAGE_SIZE;
228152909Sanholt		map->dmah = drm_pci_alloc(dev, map->size, align, 0xfffffffful);
229152909Sanholt		if (map->dmah == NULL) {
230145132Sanholt			free(map, M_DRM);
231152909Sanholt			return DRM_ERR(ENOMEM);
232145132Sanholt		}
233152909Sanholt		map->handle = map->dmah->vaddr;
234152909Sanholt		map->offset = map->dmah->busaddr;
235145132Sanholt		break;
236145132Sanholt	default:
237152909Sanholt		DRM_ERROR("Bad map type %d\n", map->type);
238145132Sanholt		free(map, M_DRM);
239145132Sanholt		return DRM_ERR(EINVAL);
240145132Sanholt	}
241145132Sanholt
242145132Sanholt	DRM_LOCK();
243145132Sanholt	TAILQ_INSERT_TAIL(&dev->maplist, map, link);
244145132Sanholt
245145132Sanholtdone:
246145132Sanholt	/* Jumped to, with lock held, when a kernel map is found. */
247152909Sanholt
248152909Sanholt	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
249152909Sanholt	    map->size);
250152909Sanholt
251152909Sanholt	*map_ptr = map;
252152909Sanholt
253152909Sanholt	return 0;
254152909Sanholt}
255152909Sanholt
256152909Sanholtint drm_addmap_ioctl(DRM_IOCTL_ARGS)
257152909Sanholt{
258152909Sanholt	drm_map_t request;
259152909Sanholt	drm_local_map_t *map;
260152909Sanholt	int err;
261152909Sanholt	DRM_DEVICE;
262152909Sanholt
263152909Sanholt	if (!(dev->flags & (FREAD|FWRITE)))
264152909Sanholt		return DRM_ERR(EACCES); /* Require read/write */
265152909Sanholt
266152909Sanholt	DRM_COPY_FROM_USER_IOCTL(request, (drm_map_t *)data, sizeof(drm_map_t));
267152909Sanholt
268152909Sanholt	if (!DRM_SUSER(p) && request.type != _DRM_AGP)
269152909Sanholt		return DRM_ERR(EACCES);
270152909Sanholt
271152909Sanholt	DRM_LOCK();
272152909Sanholt	err = drm_addmap(dev, request.offset, request.size, request.type,
273152909Sanholt	    request.flags, &map);
274152909Sanholt	DRM_UNLOCK();
275152909Sanholt	if (err != 0)
276152909Sanholt		return err;
277152909Sanholt
278145132Sanholt	request.offset = map->offset;
279145132Sanholt	request.size = map->size;
280145132Sanholt	request.type = map->type;
281145132Sanholt	request.flags = map->flags;
282145132Sanholt	request.mtrr   = map->mtrr;
283145132Sanholt	request.handle = map->handle;
284145132Sanholt
285152909Sanholt	if (request.type != _DRM_SHM) {
286145132Sanholt		request.handle = (void *)request.offset;
287145132Sanholt	}
288152909Sanholt	DRM_COPY_TO_USER_IOCTL((drm_map_t *)data, request, sizeof(drm_map_t));
289145132Sanholt
290145132Sanholt	return 0;
291145132Sanholt}
292145132Sanholt
293152909Sanholtvoid drm_rmmap(drm_device_t *dev, drm_local_map_t *map)
294145132Sanholt{
295145132Sanholt	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
296145132Sanholt
297145132Sanholt	TAILQ_REMOVE(&dev->maplist, map, link);
298145132Sanholt
299145132Sanholt	switch (map->type) {
300145132Sanholt	case _DRM_REGISTERS:
301145132Sanholt		if (map->bsr == NULL)
302145132Sanholt			drm_ioremapfree(map);
303145132Sanholt		/* FALLTHROUGH */
304145132Sanholt	case _DRM_FRAME_BUFFER:
305145132Sanholt		if (map->mtrr) {
306145132Sanholt			int __unused retcode;
307145132Sanholt
308152909Sanholt			retcode = drm_mtrr_del(0, map->offset, map->size,
309145132Sanholt			    DRM_MTRR_WC);
310145132Sanholt			DRM_DEBUG("mtrr_del = %d\n", retcode);
311145132Sanholt		}
312145132Sanholt		break;
313145132Sanholt	case _DRM_SHM:
314145132Sanholt		free(map->handle, M_DRM);
315145132Sanholt		break;
316145132Sanholt	case _DRM_AGP:
317145132Sanholt	case _DRM_SCATTER_GATHER:
318145132Sanholt		break;
319145132Sanholt	case _DRM_CONSISTENT:
320152909Sanholt		drm_pci_free(dev, map->dmah);
321145132Sanholt		break;
322145132Sanholt	}
323145132Sanholt
324145132Sanholt	if (map->bsr != NULL) {
325145132Sanholt		bus_release_resource(dev->device, SYS_RES_MEMORY, map->rid,
326145132Sanholt		    map->bsr);
327145132Sanholt	}
328145132Sanholt
329145132Sanholt	free(map, M_DRM);
330145132Sanholt}
331145132Sanholt
332145132Sanholt/* Remove a map private from list and deallocate resources if the mapping
333145132Sanholt * isn't in use.
334145132Sanholt */
335145132Sanholt
336152909Sanholtint drm_rmmap_ioctl(DRM_IOCTL_ARGS)
337145132Sanholt{
338145132Sanholt	DRM_DEVICE;
339145132Sanholt	drm_local_map_t *map;
340145132Sanholt	drm_map_t request;
341145132Sanholt
342145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_map_t *)data, sizeof(request) );
343145132Sanholt
344145132Sanholt	DRM_LOCK();
345145132Sanholt	TAILQ_FOREACH(map, &dev->maplist, link) {
346145132Sanholt		if (map->handle == request.handle &&
347145132Sanholt		    map->flags & _DRM_REMOVABLE)
348145132Sanholt			break;
349145132Sanholt	}
350145132Sanholt
351145132Sanholt	/* No match found. */
352145132Sanholt	if (map == NULL) {
353145132Sanholt		DRM_UNLOCK();
354145132Sanholt		return DRM_ERR(EINVAL);
355145132Sanholt	}
356145132Sanholt
357152909Sanholt	drm_rmmap(dev, map);
358145132Sanholt
359145132Sanholt	DRM_UNLOCK();
360145132Sanholt
361145132Sanholt	return 0;
362145132Sanholt}
363145132Sanholt
364145132Sanholt
365145132Sanholtstatic void drm_cleanup_buf_error(drm_device_t *dev, drm_buf_entry_t *entry)
366145132Sanholt{
367145132Sanholt	int i;
368145132Sanholt
369145132Sanholt	if (entry->seg_count) {
370145132Sanholt		for (i = 0; i < entry->seg_count; i++) {
371152909Sanholt			drm_pci_free(dev, entry->seglist[i]);
372145132Sanholt		}
373145132Sanholt		free(entry->seglist, M_DRM);
374145132Sanholt
375145132Sanholt		entry->seg_count = 0;
376145132Sanholt	}
377145132Sanholt
378145132Sanholt   	if (entry->buf_count) {
379145132Sanholt	   	for (i = 0; i < entry->buf_count; i++) {
380145132Sanholt			free(entry->buflist[i].dev_private, M_DRM);
381145132Sanholt		}
382145132Sanholt		free(entry->buflist, M_DRM);
383145132Sanholt
384145132Sanholt		entry->buf_count = 0;
385145132Sanholt	}
386145132Sanholt}
387145132Sanholt
388152909Sanholtstatic int drm_do_addbufs_agp(drm_device_t *dev, drm_buf_desc_t *request)
389145132Sanholt{
390145132Sanholt	drm_device_dma_t *dma = dev->dma;
391145132Sanholt	drm_buf_entry_t *entry;
392152909Sanholt	/*drm_agp_mem_t *agp_entry;
393152909Sanholt	int valid*/
394145132Sanholt	drm_buf_t *buf;
395145132Sanholt	unsigned long offset;
396145132Sanholt	unsigned long agp_offset;
397145132Sanholt	int count;
398145132Sanholt	int order;
399145132Sanholt	int size;
400145132Sanholt	int alignment;
401145132Sanholt	int page_order;
402145132Sanholt	int total;
403145132Sanholt	int byte_count;
404145132Sanholt	int i;
405145132Sanholt	drm_buf_t **temp_buflist;
406145132Sanholt
407145132Sanholt	count = request->count;
408145132Sanholt	order = drm_order(request->size);
409145132Sanholt	size = 1 << order;
410145132Sanholt
411145132Sanholt	alignment  = (request->flags & _DRM_PAGE_ALIGN)
412145132Sanholt		? round_page(size) : size;
413145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
414145132Sanholt	total = PAGE_SIZE << page_order;
415145132Sanholt
416145132Sanholt	byte_count = 0;
417145132Sanholt	agp_offset = dev->agp->base + request->agp_start;
418145132Sanholt
419145132Sanholt	DRM_DEBUG( "count:      %d\n",  count );
420145132Sanholt	DRM_DEBUG( "order:      %d\n",  order );
421145132Sanholt	DRM_DEBUG( "size:       %d\n",  size );
422145132Sanholt	DRM_DEBUG( "agp_offset: 0x%lx\n", agp_offset );
423145132Sanholt	DRM_DEBUG( "alignment:  %d\n",  alignment );
424145132Sanholt	DRM_DEBUG( "page_order: %d\n",  page_order );
425145132Sanholt	DRM_DEBUG( "total:      %d\n",  total );
426145132Sanholt
427152909Sanholt	/* Make sure buffers are located in AGP memory that we own */
428152909Sanholt	/* Breaks MGA due to drm_alloc_agp not setting up entries for the
429152909Sanholt	 * memory.  Safe to ignore for now because these ioctls are still
430152909Sanholt	 * root-only.
431152909Sanholt	 */
432152909Sanholt	/*valid = 0;
433152909Sanholt	for (agp_entry = dev->agp->memory; agp_entry;
434152909Sanholt	    agp_entry = agp_entry->next) {
435152909Sanholt		if ((agp_offset >= agp_entry->bound) &&
436152909Sanholt		    (agp_offset + total * count <=
437152909Sanholt		    agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
438152909Sanholt			valid = 1;
439152909Sanholt			break;
440152909Sanholt		}
441152909Sanholt	}
442152909Sanholt	if (!valid) {
443152909Sanholt		DRM_DEBUG("zone invalid\n");
444152909Sanholt		return DRM_ERR(EINVAL);
445152909Sanholt	}*/
446152909Sanholt
447145132Sanholt	entry = &dma->bufs[order];
448145132Sanholt
449145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
450145132Sanholt	    M_NOWAIT | M_ZERO);
451145132Sanholt	if ( !entry->buflist ) {
452145132Sanholt		return DRM_ERR(ENOMEM);
453145132Sanholt	}
454145132Sanholt
455145132Sanholt	entry->buf_size = size;
456145132Sanholt	entry->page_order = page_order;
457145132Sanholt
458145132Sanholt	offset = 0;
459145132Sanholt
460145132Sanholt	while ( entry->buf_count < count ) {
461145132Sanholt		buf          = &entry->buflist[entry->buf_count];
462145132Sanholt		buf->idx     = dma->buf_count + entry->buf_count;
463145132Sanholt		buf->total   = alignment;
464145132Sanholt		buf->order   = order;
465145132Sanholt		buf->used    = 0;
466145132Sanholt
467145132Sanholt		buf->offset  = (dma->byte_count + offset);
468145132Sanholt		buf->bus_address = agp_offset + offset;
469145132Sanholt		buf->address = (void *)(agp_offset + offset);
470145132Sanholt		buf->next    = NULL;
471145132Sanholt		buf->pending = 0;
472145132Sanholt		buf->filp    = NULL;
473145132Sanholt
474152909Sanholt		buf->dev_priv_size = dev->driver.buf_priv_size;
475145132Sanholt		buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
476145132Sanholt		    M_NOWAIT | M_ZERO);
477145132Sanholt		if (buf->dev_private == NULL) {
478145132Sanholt			/* Set count correctly so we free the proper amount. */
479145132Sanholt			entry->buf_count = count;
480145132Sanholt			drm_cleanup_buf_error(dev, entry);
481145132Sanholt			return DRM_ERR(ENOMEM);
482145132Sanholt		}
483145132Sanholt
484145132Sanholt		offset += alignment;
485145132Sanholt		entry->buf_count++;
486145132Sanholt		byte_count += PAGE_SIZE << page_order;
487145132Sanholt	}
488145132Sanholt
489145132Sanholt	DRM_DEBUG( "byte_count: %d\n", byte_count );
490145132Sanholt
491145132Sanholt	temp_buflist = realloc(dma->buflist,
492145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
493145132Sanholt	    M_NOWAIT);
494145132Sanholt	if (temp_buflist == NULL) {
495145132Sanholt		/* Free the entry because it isn't valid */
496145132Sanholt		drm_cleanup_buf_error(dev, entry);
497145132Sanholt		return DRM_ERR(ENOMEM);
498145132Sanholt	}
499145132Sanholt	dma->buflist = temp_buflist;
500145132Sanholt
501145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
502145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
503145132Sanholt	}
504145132Sanholt
505145132Sanholt	dma->buf_count += entry->buf_count;
506145132Sanholt	dma->byte_count += byte_count;
507145132Sanholt
508145132Sanholt	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
509145132Sanholt	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
510145132Sanholt
511145132Sanholt	request->count = entry->buf_count;
512145132Sanholt	request->size = size;
513145132Sanholt
514145132Sanholt	dma->flags = _DRM_DMA_USE_AGP;
515145132Sanholt
516145132Sanholt	return 0;
517145132Sanholt}
518145132Sanholt
519152909Sanholtstatic int drm_do_addbufs_pci(drm_device_t *dev, drm_buf_desc_t *request)
520145132Sanholt{
521145132Sanholt	drm_device_dma_t *dma = dev->dma;
522145132Sanholt	int count;
523145132Sanholt	int order;
524145132Sanholt	int size;
525145132Sanholt	int total;
526145132Sanholt	int page_order;
527145132Sanholt	drm_buf_entry_t *entry;
528145132Sanholt	drm_buf_t *buf;
529145132Sanholt	int alignment;
530145132Sanholt	unsigned long offset;
531145132Sanholt	int i;
532145132Sanholt	int byte_count;
533145132Sanholt	int page_count;
534145132Sanholt	unsigned long *temp_pagelist;
535145132Sanholt	drm_buf_t **temp_buflist;
536145132Sanholt
537145132Sanholt	count = request->count;
538145132Sanholt	order = drm_order(request->size);
539145132Sanholt	size = 1 << order;
540145132Sanholt
541145132Sanholt	DRM_DEBUG( "count=%d, size=%d (%d), order=%d\n",
542145132Sanholt		   request->count, request->size, size, order );
543145132Sanholt
544145132Sanholt	alignment = (request->flags & _DRM_PAGE_ALIGN)
545145132Sanholt		? round_page(size) : size;
546145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
547145132Sanholt	total = PAGE_SIZE << page_order;
548145132Sanholt
549145132Sanholt	entry = &dma->bufs[order];
550145132Sanholt
551145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
552145132Sanholt	    M_NOWAIT | M_ZERO);
553145132Sanholt	entry->seglist = malloc(count * sizeof(*entry->seglist), M_DRM,
554145132Sanholt	    M_NOWAIT | M_ZERO);
555145132Sanholt
556145132Sanholt	/* Keep the original pagelist until we know all the allocations
557145132Sanholt	 * have succeeded
558145132Sanholt	 */
559145132Sanholt	temp_pagelist = malloc((dma->page_count + (count << page_order)) *
560145132Sanholt	    sizeof(*dma->pagelist), M_DRM, M_NOWAIT);
561145132Sanholt
562145132Sanholt	if (entry->buflist == NULL || entry->seglist == NULL ||
563152909Sanholt	    temp_pagelist == NULL) {
564145132Sanholt		free(entry->buflist, M_DRM);
565145132Sanholt		free(entry->seglist, M_DRM);
566145132Sanholt		return DRM_ERR(ENOMEM);
567145132Sanholt	}
568145132Sanholt
569145132Sanholt	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
570145132Sanholt	    sizeof(*dma->pagelist));
571145132Sanholt
572145132Sanholt	DRM_DEBUG( "pagelist: %d entries\n",
573145132Sanholt		   dma->page_count + (count << page_order) );
574145132Sanholt
575145132Sanholt	entry->buf_size	= size;
576145132Sanholt	entry->page_order = page_order;
577145132Sanholt	byte_count = 0;
578145132Sanholt	page_count = 0;
579145132Sanholt
580145132Sanholt	while ( entry->buf_count < count ) {
581152909Sanholt		drm_dma_handle_t *dmah = drm_pci_alloc(dev, size, alignment,
582152909Sanholt		    0xfffffffful);
583152909Sanholt		if (dmah == NULL) {
584145132Sanholt			/* Set count correctly so we free the proper amount. */
585145132Sanholt			entry->buf_count = count;
586145132Sanholt			entry->seg_count = count;
587145132Sanholt			drm_cleanup_buf_error(dev, entry);
588145132Sanholt			free(temp_pagelist, M_DRM);
589145132Sanholt			return DRM_ERR(ENOMEM);
590145132Sanholt		}
591152909Sanholt
592152909Sanholt		entry->seglist[entry->seg_count++] = dmah;
593145132Sanholt		for ( i = 0 ; i < (1 << page_order) ; i++ ) {
594152909Sanholt			DRM_DEBUG( "page %d @ %p\n",
595145132Sanholt				   dma->page_count + page_count,
596152909Sanholt				   (char *)dmah->vaddr + PAGE_SIZE * i );
597145132Sanholt			temp_pagelist[dma->page_count + page_count++] =
598152909Sanholt			    (long)dmah->vaddr + PAGE_SIZE * i;
599145132Sanholt		}
600145132Sanholt		for ( offset = 0 ;
601145132Sanholt		      offset + size <= total && entry->buf_count < count ;
602145132Sanholt		      offset += alignment, ++entry->buf_count ) {
603145132Sanholt			buf	     = &entry->buflist[entry->buf_count];
604145132Sanholt			buf->idx     = dma->buf_count + entry->buf_count;
605145132Sanholt			buf->total   = alignment;
606145132Sanholt			buf->order   = order;
607145132Sanholt			buf->used    = 0;
608145132Sanholt			buf->offset  = (dma->byte_count + byte_count + offset);
609152909Sanholt			buf->address = ((char *)dmah->vaddr + offset);
610152909Sanholt			buf->bus_address = dmah->busaddr + offset;
611145132Sanholt			buf->next    = NULL;
612145132Sanholt			buf->pending = 0;
613145132Sanholt			buf->filp    = NULL;
614145132Sanholt
615152909Sanholt			buf->dev_priv_size = dev->driver.buf_priv_size;
616145132Sanholt			buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
617145132Sanholt			    M_NOWAIT | M_ZERO);
618145132Sanholt			if (buf->dev_private == NULL) {
619145132Sanholt				/* Set count correctly so we free the proper amount. */
620145132Sanholt				entry->buf_count = count;
621145132Sanholt				entry->seg_count = count;
622145132Sanholt				drm_cleanup_buf_error(dev, entry);
623145132Sanholt				free(temp_pagelist, M_DRM);
624145132Sanholt				return DRM_ERR(ENOMEM);
625145132Sanholt			}
626145132Sanholt
627145132Sanholt			DRM_DEBUG( "buffer %d @ %p\n",
628145132Sanholt				   entry->buf_count, buf->address );
629145132Sanholt		}
630145132Sanholt		byte_count += PAGE_SIZE << page_order;
631145132Sanholt	}
632145132Sanholt
633145132Sanholt	temp_buflist = realloc(dma->buflist,
634145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
635145132Sanholt	    M_NOWAIT);
636145132Sanholt	if (temp_buflist == NULL) {
637145132Sanholt		/* Free the entry because it isn't valid */
638145132Sanholt		drm_cleanup_buf_error(dev, entry);
639145132Sanholt		free(temp_pagelist, M_DRM);
640145132Sanholt		return DRM_ERR(ENOMEM);
641145132Sanholt	}
642145132Sanholt	dma->buflist = temp_buflist;
643145132Sanholt
644145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
645145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
646145132Sanholt	}
647145132Sanholt
648145132Sanholt	/* No allocations failed, so now we can replace the orginal pagelist
649145132Sanholt	 * with the new one.
650145132Sanholt	 */
651145132Sanholt	free(dma->pagelist, M_DRM);
652145132Sanholt	dma->pagelist = temp_pagelist;
653145132Sanholt
654145132Sanholt	dma->buf_count += entry->buf_count;
655145132Sanholt	dma->seg_count += entry->seg_count;
656145132Sanholt	dma->page_count += entry->seg_count << page_order;
657145132Sanholt	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
658145132Sanholt
659145132Sanholt	request->count = entry->buf_count;
660145132Sanholt	request->size = size;
661145132Sanholt
662145132Sanholt	return 0;
663145132Sanholt
664145132Sanholt}
665145132Sanholt
666152909Sanholtstatic int drm_do_addbufs_sg(drm_device_t *dev, drm_buf_desc_t *request)
667145132Sanholt{
668145132Sanholt	drm_device_dma_t *dma = dev->dma;
669145132Sanholt	drm_buf_entry_t *entry;
670145132Sanholt	drm_buf_t *buf;
671145132Sanholt	unsigned long offset;
672145132Sanholt	unsigned long agp_offset;
673145132Sanholt	int count;
674145132Sanholt	int order;
675145132Sanholt	int size;
676145132Sanholt	int alignment;
677145132Sanholt	int page_order;
678145132Sanholt	int total;
679145132Sanholt	int byte_count;
680145132Sanholt	int i;
681145132Sanholt	drm_buf_t **temp_buflist;
682145132Sanholt
683145132Sanholt	count = request->count;
684145132Sanholt	order = drm_order(request->size);
685145132Sanholt	size = 1 << order;
686145132Sanholt
687145132Sanholt	alignment  = (request->flags & _DRM_PAGE_ALIGN)
688145132Sanholt		? round_page(size) : size;
689145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
690145132Sanholt	total = PAGE_SIZE << page_order;
691145132Sanholt
692145132Sanholt	byte_count = 0;
693145132Sanholt	agp_offset = request->agp_start;
694145132Sanholt
695145132Sanholt	DRM_DEBUG( "count:      %d\n",  count );
696145132Sanholt	DRM_DEBUG( "order:      %d\n",  order );
697145132Sanholt	DRM_DEBUG( "size:       %d\n",  size );
698145132Sanholt	DRM_DEBUG( "agp_offset: %ld\n", agp_offset );
699145132Sanholt	DRM_DEBUG( "alignment:  %d\n",  alignment );
700145132Sanholt	DRM_DEBUG( "page_order: %d\n",  page_order );
701145132Sanholt	DRM_DEBUG( "total:      %d\n",  total );
702145132Sanholt
703145132Sanholt	entry = &dma->bufs[order];
704145132Sanholt
705145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
706145132Sanholt	    M_NOWAIT | M_ZERO);
707145132Sanholt	if (entry->buflist == NULL)
708145132Sanholt		return DRM_ERR(ENOMEM);
709145132Sanholt
710145132Sanholt	entry->buf_size = size;
711145132Sanholt	entry->page_order = page_order;
712145132Sanholt
713145132Sanholt	offset = 0;
714145132Sanholt
715145132Sanholt	while ( entry->buf_count < count ) {
716145132Sanholt		buf          = &entry->buflist[entry->buf_count];
717145132Sanholt		buf->idx     = dma->buf_count + entry->buf_count;
718145132Sanholt		buf->total   = alignment;
719145132Sanholt		buf->order   = order;
720145132Sanholt		buf->used    = 0;
721145132Sanholt
722145132Sanholt		buf->offset  = (dma->byte_count + offset);
723145132Sanholt		buf->bus_address = agp_offset + offset;
724145132Sanholt		buf->address = (void *)(agp_offset + offset + dev->sg->handle);
725145132Sanholt		buf->next    = NULL;
726145132Sanholt		buf->pending = 0;
727145132Sanholt		buf->filp    = NULL;
728145132Sanholt
729152909Sanholt		buf->dev_priv_size = dev->driver.buf_priv_size;
730145132Sanholt		buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
731145132Sanholt		    M_NOWAIT | M_ZERO);
732145132Sanholt		if (buf->dev_private == NULL) {
733145132Sanholt			/* Set count correctly so we free the proper amount. */
734145132Sanholt			entry->buf_count = count;
735145132Sanholt			drm_cleanup_buf_error(dev, entry);
736145132Sanholt			return DRM_ERR(ENOMEM);
737145132Sanholt		}
738145132Sanholt
739145132Sanholt		DRM_DEBUG( "buffer %d @ %p\n",
740145132Sanholt			   entry->buf_count, buf->address );
741145132Sanholt
742145132Sanholt		offset += alignment;
743145132Sanholt		entry->buf_count++;
744145132Sanholt		byte_count += PAGE_SIZE << page_order;
745145132Sanholt	}
746145132Sanholt
747145132Sanholt	DRM_DEBUG( "byte_count: %d\n", byte_count );
748145132Sanholt
749145132Sanholt	temp_buflist = realloc(dma->buflist,
750145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
751145132Sanholt	    M_NOWAIT);
752145132Sanholt	if (temp_buflist == NULL) {
753145132Sanholt		/* Free the entry because it isn't valid */
754145132Sanholt		drm_cleanup_buf_error(dev, entry);
755145132Sanholt		return DRM_ERR(ENOMEM);
756145132Sanholt	}
757145132Sanholt	dma->buflist = temp_buflist;
758145132Sanholt
759145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
760145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
761145132Sanholt	}
762145132Sanholt
763145132Sanholt	dma->buf_count += entry->buf_count;
764145132Sanholt	dma->byte_count += byte_count;
765145132Sanholt
766145132Sanholt	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
767145132Sanholt	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
768145132Sanholt
769145132Sanholt	request->count = entry->buf_count;
770145132Sanholt	request->size = size;
771145132Sanholt
772145132Sanholt	dma->flags = _DRM_DMA_USE_SG;
773145132Sanholt
774145132Sanholt	return 0;
775145132Sanholt}
776145132Sanholt
777152909Sanholtint drm_addbufs_agp(drm_device_t *dev, drm_buf_desc_t *request)
778145132Sanholt{
779152909Sanholt	int order, ret;
780145132Sanholt
781152909Sanholt	DRM_SPINLOCK(&dev->dma_lock);
782145132Sanholt
783152909Sanholt	if (request->count < 0 || request->count > 4096)
784145132Sanholt		return DRM_ERR(EINVAL);
785152909Sanholt
786152909Sanholt	order = drm_order(request->size);
787152909Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
788152909Sanholt		return DRM_ERR(EINVAL);
789145132Sanholt
790152909Sanholt	/* No more allocations after first buffer-using ioctl. */
791152909Sanholt	if (dev->buf_use != 0) {
792152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
793152909Sanholt		return DRM_ERR(EBUSY);
794152909Sanholt	}
795152909Sanholt	/* No more than one allocation per order */
796152909Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
797152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
798152909Sanholt		return DRM_ERR(ENOMEM);
799152909Sanholt	}
800152909Sanholt
801152909Sanholt	ret = drm_do_addbufs_agp(dev, request);
802152909Sanholt
803152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
804152909Sanholt
805152909Sanholt	return ret;
806152909Sanholt}
807152909Sanholt
808152909Sanholtint drm_addbufs_sg(drm_device_t *dev, drm_buf_desc_t *request)
809152909Sanholt{
810152909Sanholt	int order, ret;
811152909Sanholt
812152909Sanholt	DRM_SPINLOCK(&dev->dma_lock);
813152909Sanholt
814152909Sanholt	if (!DRM_SUSER(DRM_CURPROC))
815152909Sanholt		return DRM_ERR(EACCES);
816152909Sanholt
817152909Sanholt	if (request->count < 0 || request->count > 4096)
818152909Sanholt		return DRM_ERR(EINVAL);
819152909Sanholt
820152909Sanholt	order = drm_order(request->size);
821145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
822145132Sanholt		return DRM_ERR(EINVAL);
823145132Sanholt
824152909Sanholt	/* No more allocations after first buffer-using ioctl. */
825152909Sanholt	if (dev->buf_use != 0) {
826152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
827152909Sanholt		return DRM_ERR(EBUSY);
828152909Sanholt	}
829152909Sanholt	/* No more than one allocation per order */
830152909Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
831152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
832152909Sanholt		return DRM_ERR(ENOMEM);
833152909Sanholt	}
834152909Sanholt
835152909Sanholt	ret = drm_do_addbufs_sg(dev, request);
836152909Sanholt
837152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
838152909Sanholt
839152909Sanholt	return ret;
840152909Sanholt}
841152909Sanholt
842152909Sanholtint drm_addbufs_pci(drm_device_t *dev, drm_buf_desc_t *request)
843152909Sanholt{
844152909Sanholt	int order, ret;
845152909Sanholt
846145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
847152909Sanholt
848152909Sanholt	if (!DRM_SUSER(DRM_CURPROC))
849152909Sanholt		return DRM_ERR(EACCES);
850152909Sanholt
851152909Sanholt	if (request->count < 0 || request->count > 4096)
852152909Sanholt		return DRM_ERR(EINVAL);
853152909Sanholt
854152909Sanholt	order = drm_order(request->size);
855152909Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
856152909Sanholt		return DRM_ERR(EINVAL);
857152909Sanholt
858145132Sanholt	/* No more allocations after first buffer-using ioctl. */
859145132Sanholt	if (dev->buf_use != 0) {
860145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
861145132Sanholt		return DRM_ERR(EBUSY);
862145132Sanholt	}
863145132Sanholt	/* No more than one allocation per order */
864145132Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
865145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
866145132Sanholt		return DRM_ERR(ENOMEM);
867145132Sanholt	}
868145132Sanholt
869152909Sanholt	ret = drm_do_addbufs_pci(dev, request);
870152909Sanholt
871152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
872152909Sanholt
873152909Sanholt	return ret;
874152909Sanholt}
875152909Sanholt
876152909Sanholtint drm_addbufs_ioctl(DRM_IOCTL_ARGS)
877152909Sanholt{
878152909Sanholt	DRM_DEVICE;
879152909Sanholt	drm_buf_desc_t request;
880152909Sanholt	int err;
881152909Sanholt
882152909Sanholt	DRM_COPY_FROM_USER_IOCTL(request, (drm_buf_desc_t *)data,
883152909Sanholt	    sizeof(request));
884152909Sanholt
885152909Sanholt	if (request.flags & _DRM_AGP_BUFFER)
886145132Sanholt		err = drm_addbufs_agp(dev, &request);
887152909Sanholt	else if (request.flags & _DRM_SG_BUFFER)
888145132Sanholt		err = drm_addbufs_sg(dev, &request);
889145132Sanholt	else
890145132Sanholt		err = drm_addbufs_pci(dev, &request);
891145132Sanholt
892152909Sanholt	DRM_COPY_TO_USER_IOCTL((drm_buf_desc_t *)data, request,
893152909Sanholt	    sizeof(request));
894145132Sanholt
895145132Sanholt	return err;
896145132Sanholt}
897145132Sanholt
898145132Sanholtint drm_infobufs(DRM_IOCTL_ARGS)
899145132Sanholt{
900145132Sanholt	DRM_DEVICE;
901145132Sanholt	drm_device_dma_t *dma = dev->dma;
902145132Sanholt	drm_buf_info_t request;
903145132Sanholt	int i;
904145132Sanholt	int count;
905145132Sanholt	int retcode = 0;
906145132Sanholt
907145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_info_t *)data, sizeof(request) );
908145132Sanholt
909145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
910145132Sanholt	++dev->buf_use;		/* Can't allocate more after this call */
911145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
912145132Sanholt
913145132Sanholt	for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
914145132Sanholt		if ( dma->bufs[i].buf_count ) ++count;
915145132Sanholt	}
916145132Sanholt
917145132Sanholt	DRM_DEBUG( "count = %d\n", count );
918145132Sanholt
919145132Sanholt	if ( request.count >= count ) {
920145132Sanholt		for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
921145132Sanholt			if ( dma->bufs[i].buf_count ) {
922145132Sanholt				drm_buf_desc_t from;
923145132Sanholt
924145132Sanholt				from.count = dma->bufs[i].buf_count;
925145132Sanholt				from.size = dma->bufs[i].buf_size;
926145132Sanholt				from.low_mark = dma->bufs[i].freelist.low_mark;
927145132Sanholt				from.high_mark = dma->bufs[i].freelist.high_mark;
928145132Sanholt
929145132Sanholt				if (DRM_COPY_TO_USER(&request.list[count], &from,
930145132Sanholt				    sizeof(drm_buf_desc_t)) != 0) {
931145132Sanholt					retcode = DRM_ERR(EFAULT);
932145132Sanholt					break;
933145132Sanholt				}
934145132Sanholt
935145132Sanholt				DRM_DEBUG( "%d %d %d %d %d\n",
936145132Sanholt					   i,
937145132Sanholt					   dma->bufs[i].buf_count,
938145132Sanholt					   dma->bufs[i].buf_size,
939145132Sanholt					   dma->bufs[i].freelist.low_mark,
940145132Sanholt					   dma->bufs[i].freelist.high_mark );
941145132Sanholt				++count;
942145132Sanholt			}
943145132Sanholt		}
944145132Sanholt	}
945145132Sanholt	request.count = count;
946145132Sanholt
947145132Sanholt	DRM_COPY_TO_USER_IOCTL( (drm_buf_info_t *)data, request, sizeof(request) );
948145132Sanholt
949145132Sanholt	return retcode;
950145132Sanholt}
951145132Sanholt
952145132Sanholtint drm_markbufs(DRM_IOCTL_ARGS)
953145132Sanholt{
954145132Sanholt	DRM_DEVICE;
955145132Sanholt	drm_device_dma_t *dma = dev->dma;
956145132Sanholt	drm_buf_desc_t request;
957145132Sanholt	int order;
958145132Sanholt
959145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_desc_t *)data, sizeof(request) );
960145132Sanholt
961145132Sanholt	DRM_DEBUG( "%d, %d, %d\n",
962145132Sanholt		   request.size, request.low_mark, request.high_mark );
963145132Sanholt
964145132Sanholt
965145132Sanholt	order = drm_order(request.size);
966145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
967145132Sanholt	    request.low_mark < 0 || request.high_mark < 0) {
968145132Sanholt		return DRM_ERR(EINVAL);
969145132Sanholt	}
970145132Sanholt
971145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
972145132Sanholt	if (request.low_mark > dma->bufs[order].buf_count ||
973145132Sanholt	    request.high_mark > dma->bufs[order].buf_count) {
974145132Sanholt		return DRM_ERR(EINVAL);
975145132Sanholt	}
976145132Sanholt
977145132Sanholt	dma->bufs[order].freelist.low_mark  = request.low_mark;
978145132Sanholt	dma->bufs[order].freelist.high_mark = request.high_mark;
979145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
980145132Sanholt
981145132Sanholt	return 0;
982145132Sanholt}
983145132Sanholt
984145132Sanholtint drm_freebufs(DRM_IOCTL_ARGS)
985145132Sanholt{
986145132Sanholt	DRM_DEVICE;
987145132Sanholt	drm_device_dma_t *dma = dev->dma;
988145132Sanholt	drm_buf_free_t request;
989145132Sanholt	int i;
990145132Sanholt	int idx;
991145132Sanholt	drm_buf_t *buf;
992145132Sanholt	int retcode = 0;
993145132Sanholt
994145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_free_t *)data, sizeof(request) );
995145132Sanholt
996145132Sanholt	DRM_DEBUG( "%d\n", request.count );
997145132Sanholt
998145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
999145132Sanholt	for ( i = 0 ; i < request.count ; i++ ) {
1000145132Sanholt		if (DRM_COPY_FROM_USER(&idx, &request.list[i], sizeof(idx))) {
1001145132Sanholt			retcode = DRM_ERR(EFAULT);
1002145132Sanholt			break;
1003145132Sanholt		}
1004145132Sanholt		if ( idx < 0 || idx >= dma->buf_count ) {
1005145132Sanholt			DRM_ERROR( "Index %d (of %d max)\n",
1006145132Sanholt				   idx, dma->buf_count - 1 );
1007145132Sanholt			retcode = DRM_ERR(EINVAL);
1008145132Sanholt			break;
1009145132Sanholt		}
1010145132Sanholt		buf = dma->buflist[idx];
1011145132Sanholt		if ( buf->filp != filp ) {
1012145132Sanholt			DRM_ERROR("Process %d freeing buffer not owned\n",
1013145132Sanholt				   DRM_CURRENTPID);
1014145132Sanholt			retcode = DRM_ERR(EINVAL);
1015145132Sanholt			break;
1016145132Sanholt		}
1017145132Sanholt		drm_free_buffer(dev, buf);
1018145132Sanholt	}
1019145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
1020145132Sanholt
1021145132Sanholt	return retcode;
1022145132Sanholt}
1023145132Sanholt
1024145132Sanholtint drm_mapbufs(DRM_IOCTL_ARGS)
1025145132Sanholt{
1026145132Sanholt	DRM_DEVICE;
1027145132Sanholt	drm_device_dma_t *dma = dev->dma;
1028145132Sanholt	int retcode = 0;
1029145132Sanholt	const int zero = 0;
1030145132Sanholt	vm_offset_t address;
1031145132Sanholt	struct vmspace *vms;
1032145132Sanholt#ifdef __FreeBSD__
1033145132Sanholt	vm_ooffset_t foff;
1034145132Sanholt	vm_size_t size;
1035145132Sanholt	vm_offset_t vaddr;
1036145132Sanholt#elif defined(__NetBSD__) || defined(__OpenBSD__)
1037145132Sanholt	struct vnode *vn;
1038152909Sanholt	voff_t foff;
1039152909Sanholt	vsize_t size;
1040145132Sanholt	vaddr_t vaddr;
1041145132Sanholt#endif /* __NetBSD__ || __OpenBSD__ */
1042145132Sanholt
1043145132Sanholt	drm_buf_map_t request;
1044145132Sanholt	int i;
1045145132Sanholt
1046145132Sanholt	DRM_COPY_FROM_USER_IOCTL( request, (drm_buf_map_t *)data, sizeof(request) );
1047145132Sanholt
1048145132Sanholt#if defined(__NetBSD__) || defined(__OpenBSD__)
1049145132Sanholt	if (!vfinddev(kdev, VCHR, &vn))
1050145132Sanholt		return 0;	/* FIXME: Shouldn't this be EINVAL or something? */
1051145132Sanholt#endif /* __NetBSD__ || __OpenBSD */
1052145132Sanholt
1053145132Sanholt#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
1054145132Sanholt	vms = p->td_proc->p_vmspace;
1055145132Sanholt#else
1056145132Sanholt	vms = p->p_vmspace;
1057145132Sanholt#endif
1058145132Sanholt
1059145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
1060145132Sanholt	dev->buf_use++;		/* Can't allocate more after this call */
1061145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
1062145132Sanholt
1063145132Sanholt	if (request.count < dma->buf_count)
1064145132Sanholt		goto done;
1065145132Sanholt
1066152909Sanholt	if ((dev->driver.use_agp && (dma->flags & _DRM_DMA_USE_AGP)) ||
1067152909Sanholt	    (dev->driver.use_sg && (dma->flags & _DRM_DMA_USE_SG))) {
1068145132Sanholt		drm_local_map_t *map = dev->agp_buffer_map;
1069145132Sanholt
1070145132Sanholt		if (map == NULL) {
1071145132Sanholt			retcode = EINVAL;
1072145132Sanholt			goto done;
1073145132Sanholt		}
1074145132Sanholt		size = round_page(map->size);
1075145132Sanholt		foff = map->offset;
1076145132Sanholt	} else {
1077145132Sanholt		size = round_page(dma->byte_count),
1078145132Sanholt		foff = 0;
1079145132Sanholt	}
1080145132Sanholt
1081145132Sanholt#ifdef __FreeBSD__
1082145132Sanholt	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
1083145132Sanholt#if __FreeBSD_version >= 600023
1084145132Sanholt	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1085145132Sanholt	    VM_PROT_ALL, MAP_SHARED, OBJT_DEVICE, kdev, foff );
1086145132Sanholt#else
1087145132Sanholt	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1088145132Sanholt	    VM_PROT_ALL, MAP_SHARED, SLIST_FIRST(&kdev->si_hlist), foff );
1089145132Sanholt#endif
1090145132Sanholt#elif defined(__NetBSD__) || defined(__OpenBSD__)
1091145132Sanholt	vaddr = round_page((vaddr_t)vms->vm_daddr + MAXDSIZ);
1092145132Sanholt	retcode = uvm_mmap(&vms->vm_map, &vaddr, size,
1093145132Sanholt	    UVM_PROT_READ | UVM_PROT_WRITE, UVM_PROT_ALL, MAP_SHARED,
1094145132Sanholt	    &vn->v_uobj, foff, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
1095145132Sanholt#endif /* __NetBSD__ || __OpenBSD */
1096145132Sanholt	if (retcode)
1097145132Sanholt		goto done;
1098145132Sanholt
1099145132Sanholt	request.virtual = (void *)vaddr;
1100145132Sanholt
1101145132Sanholt	for ( i = 0 ; i < dma->buf_count ; i++ ) {
1102145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].idx,
1103145132Sanholt		    &dma->buflist[i]->idx, sizeof(request.list[0].idx))) {
1104145132Sanholt			retcode = EFAULT;
1105145132Sanholt			goto done;
1106145132Sanholt		}
1107145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].total,
1108145132Sanholt		    &dma->buflist[i]->total, sizeof(request.list[0].total))) {
1109145132Sanholt			retcode = EFAULT;
1110145132Sanholt			goto done;
1111145132Sanholt		}
1112145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].used, &zero,
1113145132Sanholt		    sizeof(zero))) {
1114145132Sanholt			retcode = EFAULT;
1115145132Sanholt			goto done;
1116145132Sanholt		}
1117145132Sanholt		address = vaddr + dma->buflist[i]->offset; /* *** */
1118145132Sanholt		if (DRM_COPY_TO_USER(&request.list[i].address, &address,
1119145132Sanholt		    sizeof(address))) {
1120145132Sanholt			retcode = EFAULT;
1121145132Sanholt			goto done;
1122145132Sanholt		}
1123145132Sanholt	}
1124145132Sanholt
1125145132Sanholt done:
1126145132Sanholt	request.count = dma->buf_count;
1127145132Sanholt
1128145132Sanholt	DRM_DEBUG( "%d buffers, retcode = %d\n", request.count, retcode );
1129145132Sanholt
1130145132Sanholt	DRM_COPY_TO_USER_IOCTL((drm_buf_map_t *)data, request, sizeof(request));
1131145132Sanholt
1132145132Sanholt	return DRM_ERR(retcode);
1133145132Sanholt}
1134