drm_bufs.c revision 182080
1145132Sanholt/*-
2145132Sanholt * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
3145132Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
4145132Sanholt * All Rights Reserved.
5145132Sanholt *
6145132Sanholt * Permission is hereby granted, free of charge, to any person obtaining a
7145132Sanholt * copy of this software and associated documentation files (the "Software"),
8145132Sanholt * to deal in the Software without restriction, including without limitation
9145132Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10145132Sanholt * and/or sell copies of the Software, and to permit persons to whom the
11145132Sanholt * Software is furnished to do so, subject to the following conditions:
12145132Sanholt *
13145132Sanholt * The above copyright notice and this permission notice (including the next
14145132Sanholt * paragraph) shall be included in all copies or substantial portions of the
15145132Sanholt * Software.
16145132Sanholt *
17145132Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18145132Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19145132Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20145132Sanholt * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21145132Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22145132Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23145132Sanholt * OTHER DEALINGS IN THE SOFTWARE.
24145132Sanholt *
25145132Sanholt * Authors:
26145132Sanholt *    Rickard E. (Rik) Faith <faith@valinux.com>
27145132Sanholt *    Gareth Hughes <gareth@valinux.com>
28145132Sanholt *
29145132Sanholt */
30145132Sanholt
31152909Sanholt#include <sys/cdefs.h>
32152909Sanholt__FBSDID("$FreeBSD: head/sys/dev/drm/drm_bufs.c 182080 2008-08-23 20:59:12Z rnoland $");
33152909Sanholt
34182080Srnoland/** @file drm_bufs.c
35182080Srnoland * Implementation of the ioctls for setup of DRM mappings and DMA buffers.
36182080Srnoland */
37182080Srnoland
38152909Sanholt#include "dev/pci/pcireg.h"
39152909Sanholt
40145132Sanholt#include "dev/drm/drmP.h"
41145132Sanholt
42145132Sanholt/*
43145132Sanholt * Compute order.  Can be made faster.
44145132Sanholt */
45145132Sanholtint drm_order(unsigned long size)
46145132Sanholt{
47145132Sanholt	int order;
48145132Sanholt	unsigned long tmp;
49145132Sanholt
50145132Sanholt	for ( order = 0, tmp = size ; tmp >>= 1 ; ++order );
51145132Sanholt
52145132Sanholt	if ( size & ~(1 << order) )
53145132Sanholt		++order;
54145132Sanholt
55145132Sanholt	return order;
56145132Sanholt}
57145132Sanholt
58152909Sanholt/* Allocation of PCI memory resources (framebuffer, registers, etc.) for
59152909Sanholt * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
60152909Sanholt * address for accessing them.  Cleaned up at unload.
61152909Sanholt */
62182080Srnolandstatic int drm_alloc_resource(struct drm_device *dev, int resource)
63145132Sanholt{
64152909Sanholt	if (resource >= DRM_MAX_PCI_RESOURCE) {
65152909Sanholt		DRM_ERROR("Resource %d too large\n", resource);
66152909Sanholt		return 1;
67152909Sanholt	}
68145132Sanholt
69152909Sanholt	DRM_UNLOCK();
70152909Sanholt	if (dev->pcir[resource] != NULL) {
71152909Sanholt		DRM_LOCK();
72145132Sanholt		return 0;
73145132Sanholt	}
74145132Sanholt
75152909Sanholt	dev->pcirid[resource] = PCIR_BAR(resource);
76152909Sanholt	dev->pcir[resource] = bus_alloc_resource_any(dev->device,
77152909Sanholt	    SYS_RES_MEMORY, &dev->pcirid[resource], RF_SHAREABLE);
78152909Sanholt	DRM_LOCK();
79145132Sanholt
80152909Sanholt	if (dev->pcir[resource] == NULL) {
81152909Sanholt		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
82152909Sanholt		return 1;
83152909Sanholt	}
84145132Sanholt
85152909Sanholt	return 0;
86145132Sanholt}
87145132Sanholt
88182080Srnolandunsigned long drm_get_resource_start(struct drm_device *dev,
89182080Srnoland				     unsigned int resource)
90145132Sanholt{
91152909Sanholt	if (drm_alloc_resource(dev, resource) != 0)
92152909Sanholt		return 0;
93145132Sanholt
94152909Sanholt	return rman_get_start(dev->pcir[resource]);
95145132Sanholt}
96145132Sanholt
97182080Srnolandunsigned long drm_get_resource_len(struct drm_device *dev,
98182080Srnoland				   unsigned int resource)
99145132Sanholt{
100152909Sanholt	if (drm_alloc_resource(dev, resource) != 0)
101152909Sanholt		return 0;
102145132Sanholt
103152909Sanholt	return rman_get_size(dev->pcir[resource]);
104145132Sanholt}
105145132Sanholt
106182080Srnolandint drm_addmap(struct drm_device * dev, unsigned long offset,
107182080Srnoland	       unsigned long size,
108152909Sanholt    drm_map_type_t type, drm_map_flags_t flags, drm_local_map_t **map_ptr)
109145132Sanholt{
110145132Sanholt	drm_local_map_t *map;
111152909Sanholt	int align;
112152909Sanholt	/*drm_agp_mem_t *entry;
113152909Sanholt	int valid;*/
114145132Sanholt
115145132Sanholt	/* Only allow shared memory to be removable since we only keep enough
116145132Sanholt	 * book keeping information about shared memory to allow for removal
117145132Sanholt	 * when processes fork.
118145132Sanholt	 */
119152909Sanholt	if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
120152909Sanholt		DRM_ERROR("Requested removable map for non-DRM_SHM\n");
121145132Sanholt		return EINVAL;
122152909Sanholt	}
123152909Sanholt	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
124152909Sanholt		DRM_ERROR("offset/size not page aligned: 0x%lx/0x%lx\n",
125152909Sanholt		    offset, size);
126145132Sanholt		return EINVAL;
127152909Sanholt	}
128152909Sanholt	if (offset + size < offset) {
129152909Sanholt		DRM_ERROR("offset and size wrap around: 0x%lx/0x%lx\n",
130152909Sanholt		    offset, size);
131145132Sanholt		return EINVAL;
132152909Sanholt	}
133145132Sanholt
134152909Sanholt	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", offset,
135152909Sanholt	    size, type);
136145132Sanholt
137145132Sanholt	/* Check if this is just another version of a kernel-allocated map, and
138145132Sanholt	 * just hand that back if so.
139145132Sanholt	 */
140152909Sanholt	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
141152909Sanholt	    type == _DRM_SHM) {
142145132Sanholt		TAILQ_FOREACH(map, &dev->maplist, link) {
143152909Sanholt			if (map->type == type && (map->offset == offset ||
144152909Sanholt			    (map->type == _DRM_SHM &&
145152909Sanholt			    map->flags == _DRM_CONTAINS_LOCK))) {
146152909Sanholt				map->size = size;
147152909Sanholt				DRM_DEBUG("Found kernel map %d\n", type);
148145132Sanholt				goto done;
149145132Sanholt			}
150145132Sanholt		}
151145132Sanholt	}
152152909Sanholt	DRM_UNLOCK();
153145132Sanholt
154145132Sanholt	/* Allocate a new map structure, fill it in, and do any type-specific
155145132Sanholt	 * initialization necessary.
156145132Sanholt	 */
157145132Sanholt	map = malloc(sizeof(*map), M_DRM, M_ZERO | M_NOWAIT);
158182080Srnoland	if ( !map ) {
159182080Srnoland		DRM_LOCK();
160182080Srnoland		return ENOMEM;
161182080Srnoland	}
162145132Sanholt
163152909Sanholt	map->offset = offset;
164152909Sanholt	map->size = size;
165152909Sanholt	map->type = type;
166152909Sanholt	map->flags = flags;
167145132Sanholt
168145132Sanholt	switch ( map->type ) {
169145132Sanholt	case _DRM_REGISTERS:
170145478Sanholt		map->handle = drm_ioremap(dev, map);
171145132Sanholt		if (!(map->flags & _DRM_WRITE_COMBINING))
172145132Sanholt			break;
173145132Sanholt		/* FALLTHROUGH */
174145132Sanholt	case _DRM_FRAME_BUFFER:
175145132Sanholt		if (drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC) == 0)
176145132Sanholt			map->mtrr = 1;
177145132Sanholt		break;
178145132Sanholt	case _DRM_SHM:
179145132Sanholt		map->handle = malloc(map->size, M_DRM, M_NOWAIT);
180145132Sanholt		DRM_DEBUG( "%lu %d %p\n",
181145132Sanholt			   map->size, drm_order(map->size), map->handle );
182145132Sanholt		if ( !map->handle ) {
183145132Sanholt			free(map, M_DRM);
184182080Srnoland			DRM_LOCK();
185182080Srnoland			return ENOMEM;
186145132Sanholt		}
187145132Sanholt		map->offset = (unsigned long)map->handle;
188145132Sanholt		if ( map->flags & _DRM_CONTAINS_LOCK ) {
189145132Sanholt			/* Prevent a 2nd X Server from creating a 2nd lock */
190145132Sanholt			DRM_LOCK();
191145132Sanholt			if (dev->lock.hw_lock != NULL) {
192145132Sanholt				DRM_UNLOCK();
193145132Sanholt				free(map->handle, M_DRM);
194145132Sanholt				free(map, M_DRM);
195182080Srnoland				return EBUSY;
196145132Sanholt			}
197145132Sanholt			dev->lock.hw_lock = map->handle; /* Pointer to lock */
198145132Sanholt			DRM_UNLOCK();
199145132Sanholt		}
200145132Sanholt		break;
201145132Sanholt	case _DRM_AGP:
202152909Sanholt		/*valid = 0;*/
203182080Srnoland		/* In some cases (i810 driver), user space may have already
204182080Srnoland		 * added the AGP base itself, because dev->agp->base previously
205182080Srnoland		 * only got set during AGP enable.  So, only add the base
206182080Srnoland		 * address if the map's offset isn't already within the
207182080Srnoland		 * aperture.
208182080Srnoland		 */
209182080Srnoland		if (map->offset < dev->agp->base ||
210182080Srnoland		    map->offset > dev->agp->base +
211182080Srnoland		    dev->agp->info.ai_aperture_size - 1) {
212182080Srnoland			map->offset += dev->agp->base;
213182080Srnoland		}
214145132Sanholt		map->mtrr   = dev->agp->mtrr; /* for getmap */
215152909Sanholt		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
216152909Sanholt			if ((map->offset >= entry->bound) &&
217152909Sanholt			    (map->offset + map->size <=
218152909Sanholt			    entry->bound + entry->pages * PAGE_SIZE)) {
219152909Sanholt				valid = 1;
220152909Sanholt				break;
221152909Sanholt			}
222152909Sanholt		}
223152909Sanholt		if (!valid) {
224152909Sanholt			free(map, M_DRM);
225182080Srnoland			DRM_LOCK();
226182080Srnoland			return EACCES;
227152909Sanholt		}*/
228145132Sanholt		break;
229145132Sanholt	case _DRM_SCATTER_GATHER:
230145132Sanholt		if (!dev->sg) {
231145132Sanholt			free(map, M_DRM);
232182080Srnoland			DRM_LOCK();
233182080Srnoland			return EINVAL;
234145132Sanholt		}
235145132Sanholt		map->offset = map->offset + dev->sg->handle;
236145132Sanholt		break;
237145132Sanholt	case _DRM_CONSISTENT:
238152909Sanholt		/* Unfortunately, we don't get any alignment specification from
239152909Sanholt		 * the caller, so we have to guess.  drm_pci_alloc requires
240152909Sanholt		 * a power-of-two alignment, so try to align the bus address of
241152909Sanholt		 * the map to it size if possible, otherwise just assume
242152909Sanholt		 * PAGE_SIZE alignment.
243152909Sanholt		 */
244152909Sanholt		align = map->size;
245152909Sanholt		if ((align & (align - 1)) != 0)
246152909Sanholt			align = PAGE_SIZE;
247152909Sanholt		map->dmah = drm_pci_alloc(dev, map->size, align, 0xfffffffful);
248152909Sanholt		if (map->dmah == NULL) {
249145132Sanholt			free(map, M_DRM);
250182080Srnoland			DRM_LOCK();
251182080Srnoland			return ENOMEM;
252145132Sanholt		}
253152909Sanholt		map->handle = map->dmah->vaddr;
254152909Sanholt		map->offset = map->dmah->busaddr;
255145132Sanholt		break;
256145132Sanholt	default:
257152909Sanholt		DRM_ERROR("Bad map type %d\n", map->type);
258145132Sanholt		free(map, M_DRM);
259182080Srnoland		DRM_LOCK();
260182080Srnoland		return EINVAL;
261145132Sanholt	}
262145132Sanholt
263145132Sanholt	DRM_LOCK();
264145132Sanholt	TAILQ_INSERT_TAIL(&dev->maplist, map, link);
265145132Sanholt
266145132Sanholtdone:
267145132Sanholt	/* Jumped to, with lock held, when a kernel map is found. */
268152909Sanholt
269152909Sanholt	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
270152909Sanholt	    map->size);
271152909Sanholt
272152909Sanholt	*map_ptr = map;
273152909Sanholt
274152909Sanholt	return 0;
275152909Sanholt}
276152909Sanholt
277182080Srnolandint drm_addmap_ioctl(struct drm_device *dev, void *data,
278182080Srnoland		     struct drm_file *file_priv)
279152909Sanholt{
280182080Srnoland	drm_map_t *request = data;
281152909Sanholt	drm_local_map_t *map;
282152909Sanholt	int err;
283152909Sanholt
284152909Sanholt	if (!(dev->flags & (FREAD|FWRITE)))
285182080Srnoland		return EACCES; /* Require read/write */
286152909Sanholt
287182080Srnoland	if (!DRM_SUSER(DRM_CURPROC) && request->type != _DRM_AGP)
288182080Srnoland		return EACCES;
289152909Sanholt
290152909Sanholt	DRM_LOCK();
291182080Srnoland	err = drm_addmap(dev, request->offset, request->size, request->type,
292182080Srnoland	    request->flags, &map);
293152909Sanholt	DRM_UNLOCK();
294152909Sanholt	if (err != 0)
295152909Sanholt		return err;
296152909Sanholt
297182080Srnoland	request->offset = map->offset;
298182080Srnoland	request->size = map->size;
299182080Srnoland	request->type = map->type;
300182080Srnoland	request->flags = map->flags;
301182080Srnoland	request->mtrr   = map->mtrr;
302182080Srnoland	request->handle = map->handle;
303145132Sanholt
304182080Srnoland	if (request->type != _DRM_SHM) {
305182080Srnoland		request->handle = (void *)request->offset;
306145132Sanholt	}
307145132Sanholt
308145132Sanholt	return 0;
309145132Sanholt}
310145132Sanholt
311182080Srnolandvoid drm_rmmap(struct drm_device *dev, drm_local_map_t *map)
312145132Sanholt{
313145132Sanholt	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
314145132Sanholt
315145132Sanholt	TAILQ_REMOVE(&dev->maplist, map, link);
316145132Sanholt
317145132Sanholt	switch (map->type) {
318145132Sanholt	case _DRM_REGISTERS:
319145132Sanholt		if (map->bsr == NULL)
320145132Sanholt			drm_ioremapfree(map);
321145132Sanholt		/* FALLTHROUGH */
322145132Sanholt	case _DRM_FRAME_BUFFER:
323145132Sanholt		if (map->mtrr) {
324145132Sanholt			int __unused retcode;
325145132Sanholt
326152909Sanholt			retcode = drm_mtrr_del(0, map->offset, map->size,
327145132Sanholt			    DRM_MTRR_WC);
328145132Sanholt			DRM_DEBUG("mtrr_del = %d\n", retcode);
329145132Sanholt		}
330145132Sanholt		break;
331145132Sanholt	case _DRM_SHM:
332145132Sanholt		free(map->handle, M_DRM);
333145132Sanholt		break;
334145132Sanholt	case _DRM_AGP:
335145132Sanholt	case _DRM_SCATTER_GATHER:
336145132Sanholt		break;
337145132Sanholt	case _DRM_CONSISTENT:
338152909Sanholt		drm_pci_free(dev, map->dmah);
339145132Sanholt		break;
340182080Srnoland	default:
341182080Srnoland		DRM_ERROR("Bad map type %d\n", map->type);
342182080Srnoland		break;
343145132Sanholt	}
344145132Sanholt
345145132Sanholt	if (map->bsr != NULL) {
346145132Sanholt		bus_release_resource(dev->device, SYS_RES_MEMORY, map->rid,
347145132Sanholt		    map->bsr);
348145132Sanholt	}
349145132Sanholt
350145132Sanholt	free(map, M_DRM);
351145132Sanholt}
352145132Sanholt
353145132Sanholt/* Remove a map private from list and deallocate resources if the mapping
354145132Sanholt * isn't in use.
355145132Sanholt */
356145132Sanholt
357182080Srnolandint drm_rmmap_ioctl(struct drm_device *dev, void *data,
358182080Srnoland		    struct drm_file *file_priv)
359145132Sanholt{
360145132Sanholt	drm_local_map_t *map;
361182080Srnoland	drm_map_t *request = data;
362145132Sanholt
363145132Sanholt	DRM_LOCK();
364145132Sanholt	TAILQ_FOREACH(map, &dev->maplist, link) {
365182080Srnoland		if (map->handle == request->handle &&
366145132Sanholt		    map->flags & _DRM_REMOVABLE)
367145132Sanholt			break;
368145132Sanholt	}
369145132Sanholt
370145132Sanholt	/* No match found. */
371145132Sanholt	if (map == NULL) {
372145132Sanholt		DRM_UNLOCK();
373182080Srnoland		return EINVAL;
374145132Sanholt	}
375145132Sanholt
376152909Sanholt	drm_rmmap(dev, map);
377145132Sanholt
378145132Sanholt	DRM_UNLOCK();
379145132Sanholt
380145132Sanholt	return 0;
381145132Sanholt}
382145132Sanholt
383145132Sanholt
384182080Srnolandstatic void drm_cleanup_buf_error(struct drm_device *dev,
385182080Srnoland				  drm_buf_entry_t *entry)
386145132Sanholt{
387145132Sanholt	int i;
388145132Sanholt
389145132Sanholt	if (entry->seg_count) {
390145132Sanholt		for (i = 0; i < entry->seg_count; i++) {
391152909Sanholt			drm_pci_free(dev, entry->seglist[i]);
392145132Sanholt		}
393145132Sanholt		free(entry->seglist, M_DRM);
394145132Sanholt
395145132Sanholt		entry->seg_count = 0;
396145132Sanholt	}
397145132Sanholt
398145132Sanholt   	if (entry->buf_count) {
399145132Sanholt	   	for (i = 0; i < entry->buf_count; i++) {
400145132Sanholt			free(entry->buflist[i].dev_private, M_DRM);
401145132Sanholt		}
402145132Sanholt		free(entry->buflist, M_DRM);
403145132Sanholt
404145132Sanholt		entry->buf_count = 0;
405145132Sanholt	}
406145132Sanholt}
407145132Sanholt
408182080Srnolandstatic int drm_do_addbufs_agp(struct drm_device *dev, drm_buf_desc_t *request)
409145132Sanholt{
410145132Sanholt	drm_device_dma_t *dma = dev->dma;
411145132Sanholt	drm_buf_entry_t *entry;
412152909Sanholt	/*drm_agp_mem_t *agp_entry;
413152909Sanholt	int valid*/
414145132Sanholt	drm_buf_t *buf;
415145132Sanholt	unsigned long offset;
416145132Sanholt	unsigned long agp_offset;
417145132Sanholt	int count;
418145132Sanholt	int order;
419145132Sanholt	int size;
420145132Sanholt	int alignment;
421145132Sanholt	int page_order;
422145132Sanholt	int total;
423145132Sanholt	int byte_count;
424145132Sanholt	int i;
425145132Sanholt	drm_buf_t **temp_buflist;
426145132Sanholt
427145132Sanholt	count = request->count;
428145132Sanholt	order = drm_order(request->size);
429145132Sanholt	size = 1 << order;
430145132Sanholt
431145132Sanholt	alignment  = (request->flags & _DRM_PAGE_ALIGN)
432145132Sanholt		? round_page(size) : size;
433145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
434145132Sanholt	total = PAGE_SIZE << page_order;
435145132Sanholt
436145132Sanholt	byte_count = 0;
437145132Sanholt	agp_offset = dev->agp->base + request->agp_start;
438145132Sanholt
439145132Sanholt	DRM_DEBUG( "count:      %d\n",  count );
440145132Sanholt	DRM_DEBUG( "order:      %d\n",  order );
441145132Sanholt	DRM_DEBUG( "size:       %d\n",  size );
442145132Sanholt	DRM_DEBUG( "agp_offset: 0x%lx\n", agp_offset );
443145132Sanholt	DRM_DEBUG( "alignment:  %d\n",  alignment );
444145132Sanholt	DRM_DEBUG( "page_order: %d\n",  page_order );
445145132Sanholt	DRM_DEBUG( "total:      %d\n",  total );
446145132Sanholt
447152909Sanholt	/* Make sure buffers are located in AGP memory that we own */
448152909Sanholt	/* Breaks MGA due to drm_alloc_agp not setting up entries for the
449152909Sanholt	 * memory.  Safe to ignore for now because these ioctls are still
450152909Sanholt	 * root-only.
451152909Sanholt	 */
452152909Sanholt	/*valid = 0;
453152909Sanholt	for (agp_entry = dev->agp->memory; agp_entry;
454152909Sanholt	    agp_entry = agp_entry->next) {
455152909Sanholt		if ((agp_offset >= agp_entry->bound) &&
456152909Sanholt		    (agp_offset + total * count <=
457152909Sanholt		    agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
458152909Sanholt			valid = 1;
459152909Sanholt			break;
460152909Sanholt		}
461152909Sanholt	}
462152909Sanholt	if (!valid) {
463152909Sanholt		DRM_DEBUG("zone invalid\n");
464182080Srnoland		return EINVAL;
465152909Sanholt	}*/
466152909Sanholt
467145132Sanholt	entry = &dma->bufs[order];
468145132Sanholt
469145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
470145132Sanholt	    M_NOWAIT | M_ZERO);
471145132Sanholt	if ( !entry->buflist ) {
472182080Srnoland		return ENOMEM;
473145132Sanholt	}
474145132Sanholt
475145132Sanholt	entry->buf_size = size;
476145132Sanholt	entry->page_order = page_order;
477145132Sanholt
478145132Sanholt	offset = 0;
479145132Sanholt
480145132Sanholt	while ( entry->buf_count < count ) {
481145132Sanholt		buf          = &entry->buflist[entry->buf_count];
482145132Sanholt		buf->idx     = dma->buf_count + entry->buf_count;
483145132Sanholt		buf->total   = alignment;
484145132Sanholt		buf->order   = order;
485145132Sanholt		buf->used    = 0;
486145132Sanholt
487145132Sanholt		buf->offset  = (dma->byte_count + offset);
488145132Sanholt		buf->bus_address = agp_offset + offset;
489145132Sanholt		buf->address = (void *)(agp_offset + offset);
490145132Sanholt		buf->next    = NULL;
491145132Sanholt		buf->pending = 0;
492182080Srnoland		buf->file_priv = NULL;
493145132Sanholt
494152909Sanholt		buf->dev_priv_size = dev->driver.buf_priv_size;
495145132Sanholt		buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
496145132Sanholt		    M_NOWAIT | M_ZERO);
497145132Sanholt		if (buf->dev_private == NULL) {
498145132Sanholt			/* Set count correctly so we free the proper amount. */
499145132Sanholt			entry->buf_count = count;
500145132Sanholt			drm_cleanup_buf_error(dev, entry);
501182080Srnoland			return ENOMEM;
502145132Sanholt		}
503145132Sanholt
504145132Sanholt		offset += alignment;
505145132Sanholt		entry->buf_count++;
506145132Sanholt		byte_count += PAGE_SIZE << page_order;
507145132Sanholt	}
508145132Sanholt
509145132Sanholt	DRM_DEBUG( "byte_count: %d\n", byte_count );
510145132Sanholt
511145132Sanholt	temp_buflist = realloc(dma->buflist,
512145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
513145132Sanholt	    M_NOWAIT);
514145132Sanholt	if (temp_buflist == NULL) {
515145132Sanholt		/* Free the entry because it isn't valid */
516145132Sanholt		drm_cleanup_buf_error(dev, entry);
517182080Srnoland		return ENOMEM;
518145132Sanholt	}
519145132Sanholt	dma->buflist = temp_buflist;
520145132Sanholt
521145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
522145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
523145132Sanholt	}
524145132Sanholt
525145132Sanholt	dma->buf_count += entry->buf_count;
526145132Sanholt	dma->byte_count += byte_count;
527145132Sanholt
528145132Sanholt	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
529145132Sanholt	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
530145132Sanholt
531145132Sanholt	request->count = entry->buf_count;
532145132Sanholt	request->size = size;
533145132Sanholt
534145132Sanholt	dma->flags = _DRM_DMA_USE_AGP;
535145132Sanholt
536145132Sanholt	return 0;
537145132Sanholt}
538145132Sanholt
539182080Srnolandstatic int drm_do_addbufs_pci(struct drm_device *dev, drm_buf_desc_t *request)
540145132Sanholt{
541145132Sanholt	drm_device_dma_t *dma = dev->dma;
542145132Sanholt	int count;
543145132Sanholt	int order;
544145132Sanholt	int size;
545145132Sanholt	int total;
546145132Sanholt	int page_order;
547145132Sanholt	drm_buf_entry_t *entry;
548145132Sanholt	drm_buf_t *buf;
549145132Sanholt	int alignment;
550145132Sanholt	unsigned long offset;
551145132Sanholt	int i;
552145132Sanholt	int byte_count;
553145132Sanholt	int page_count;
554145132Sanholt	unsigned long *temp_pagelist;
555145132Sanholt	drm_buf_t **temp_buflist;
556145132Sanholt
557145132Sanholt	count = request->count;
558145132Sanholt	order = drm_order(request->size);
559145132Sanholt	size = 1 << order;
560145132Sanholt
561145132Sanholt	DRM_DEBUG( "count=%d, size=%d (%d), order=%d\n",
562145132Sanholt		   request->count, request->size, size, order );
563145132Sanholt
564145132Sanholt	alignment = (request->flags & _DRM_PAGE_ALIGN)
565145132Sanholt		? round_page(size) : size;
566145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
567145132Sanholt	total = PAGE_SIZE << page_order;
568145132Sanholt
569145132Sanholt	entry = &dma->bufs[order];
570145132Sanholt
571145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
572145132Sanholt	    M_NOWAIT | M_ZERO);
573145132Sanholt	entry->seglist = malloc(count * sizeof(*entry->seglist), M_DRM,
574145132Sanholt	    M_NOWAIT | M_ZERO);
575145132Sanholt
576145132Sanholt	/* Keep the original pagelist until we know all the allocations
577145132Sanholt	 * have succeeded
578145132Sanholt	 */
579145132Sanholt	temp_pagelist = malloc((dma->page_count + (count << page_order)) *
580145132Sanholt	    sizeof(*dma->pagelist), M_DRM, M_NOWAIT);
581145132Sanholt
582145132Sanholt	if (entry->buflist == NULL || entry->seglist == NULL ||
583152909Sanholt	    temp_pagelist == NULL) {
584145132Sanholt		free(entry->buflist, M_DRM);
585145132Sanholt		free(entry->seglist, M_DRM);
586182080Srnoland		return ENOMEM;
587145132Sanholt	}
588145132Sanholt
589145132Sanholt	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
590145132Sanholt	    sizeof(*dma->pagelist));
591145132Sanholt
592145132Sanholt	DRM_DEBUG( "pagelist: %d entries\n",
593145132Sanholt		   dma->page_count + (count << page_order) );
594145132Sanholt
595145132Sanholt	entry->buf_size	= size;
596145132Sanholt	entry->page_order = page_order;
597145132Sanholt	byte_count = 0;
598145132Sanholt	page_count = 0;
599145132Sanholt
600145132Sanholt	while ( entry->buf_count < count ) {
601152909Sanholt		drm_dma_handle_t *dmah = drm_pci_alloc(dev, size, alignment,
602152909Sanholt		    0xfffffffful);
603152909Sanholt		if (dmah == NULL) {
604145132Sanholt			/* Set count correctly so we free the proper amount. */
605145132Sanholt			entry->buf_count = count;
606145132Sanholt			entry->seg_count = count;
607145132Sanholt			drm_cleanup_buf_error(dev, entry);
608145132Sanholt			free(temp_pagelist, M_DRM);
609182080Srnoland			return ENOMEM;
610145132Sanholt		}
611152909Sanholt
612152909Sanholt		entry->seglist[entry->seg_count++] = dmah;
613145132Sanholt		for ( i = 0 ; i < (1 << page_order) ; i++ ) {
614152909Sanholt			DRM_DEBUG( "page %d @ %p\n",
615145132Sanholt				   dma->page_count + page_count,
616152909Sanholt				   (char *)dmah->vaddr + PAGE_SIZE * i );
617145132Sanholt			temp_pagelist[dma->page_count + page_count++] =
618152909Sanholt			    (long)dmah->vaddr + PAGE_SIZE * i;
619145132Sanholt		}
620145132Sanholt		for ( offset = 0 ;
621145132Sanholt		      offset + size <= total && entry->buf_count < count ;
622145132Sanholt		      offset += alignment, ++entry->buf_count ) {
623145132Sanholt			buf	     = &entry->buflist[entry->buf_count];
624145132Sanholt			buf->idx     = dma->buf_count + entry->buf_count;
625145132Sanholt			buf->total   = alignment;
626145132Sanholt			buf->order   = order;
627145132Sanholt			buf->used    = 0;
628145132Sanholt			buf->offset  = (dma->byte_count + byte_count + offset);
629152909Sanholt			buf->address = ((char *)dmah->vaddr + offset);
630152909Sanholt			buf->bus_address = dmah->busaddr + offset;
631145132Sanholt			buf->next    = NULL;
632145132Sanholt			buf->pending = 0;
633182080Srnoland			buf->file_priv = NULL;
634145132Sanholt
635152909Sanholt			buf->dev_priv_size = dev->driver.buf_priv_size;
636145132Sanholt			buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
637145132Sanholt			    M_NOWAIT | M_ZERO);
638145132Sanholt			if (buf->dev_private == NULL) {
639145132Sanholt				/* Set count correctly so we free the proper amount. */
640145132Sanholt				entry->buf_count = count;
641145132Sanholt				entry->seg_count = count;
642145132Sanholt				drm_cleanup_buf_error(dev, entry);
643145132Sanholt				free(temp_pagelist, M_DRM);
644182080Srnoland				return ENOMEM;
645145132Sanholt			}
646145132Sanholt
647145132Sanholt			DRM_DEBUG( "buffer %d @ %p\n",
648145132Sanholt				   entry->buf_count, buf->address );
649145132Sanholt		}
650145132Sanholt		byte_count += PAGE_SIZE << page_order;
651145132Sanholt	}
652145132Sanholt
653145132Sanholt	temp_buflist = realloc(dma->buflist,
654145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
655145132Sanholt	    M_NOWAIT);
656145132Sanholt	if (temp_buflist == NULL) {
657145132Sanholt		/* Free the entry because it isn't valid */
658145132Sanholt		drm_cleanup_buf_error(dev, entry);
659145132Sanholt		free(temp_pagelist, M_DRM);
660182080Srnoland		return ENOMEM;
661145132Sanholt	}
662145132Sanholt	dma->buflist = temp_buflist;
663145132Sanholt
664145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
665145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
666145132Sanholt	}
667145132Sanholt
668145132Sanholt	/* No allocations failed, so now we can replace the orginal pagelist
669145132Sanholt	 * with the new one.
670145132Sanholt	 */
671145132Sanholt	free(dma->pagelist, M_DRM);
672145132Sanholt	dma->pagelist = temp_pagelist;
673145132Sanholt
674145132Sanholt	dma->buf_count += entry->buf_count;
675145132Sanholt	dma->seg_count += entry->seg_count;
676145132Sanholt	dma->page_count += entry->seg_count << page_order;
677145132Sanholt	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
678145132Sanholt
679145132Sanholt	request->count = entry->buf_count;
680145132Sanholt	request->size = size;
681145132Sanholt
682145132Sanholt	return 0;
683145132Sanholt
684145132Sanholt}
685145132Sanholt
686182080Srnolandstatic int drm_do_addbufs_sg(struct drm_device *dev, drm_buf_desc_t *request)
687145132Sanholt{
688145132Sanholt	drm_device_dma_t *dma = dev->dma;
689145132Sanholt	drm_buf_entry_t *entry;
690145132Sanholt	drm_buf_t *buf;
691145132Sanholt	unsigned long offset;
692145132Sanholt	unsigned long agp_offset;
693145132Sanholt	int count;
694145132Sanholt	int order;
695145132Sanholt	int size;
696145132Sanholt	int alignment;
697145132Sanholt	int page_order;
698145132Sanholt	int total;
699145132Sanholt	int byte_count;
700145132Sanholt	int i;
701145132Sanholt	drm_buf_t **temp_buflist;
702145132Sanholt
703145132Sanholt	count = request->count;
704145132Sanholt	order = drm_order(request->size);
705145132Sanholt	size = 1 << order;
706145132Sanholt
707145132Sanholt	alignment  = (request->flags & _DRM_PAGE_ALIGN)
708145132Sanholt		? round_page(size) : size;
709145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
710145132Sanholt	total = PAGE_SIZE << page_order;
711145132Sanholt
712145132Sanholt	byte_count = 0;
713145132Sanholt	agp_offset = request->agp_start;
714145132Sanholt
715145132Sanholt	DRM_DEBUG( "count:      %d\n",  count );
716145132Sanholt	DRM_DEBUG( "order:      %d\n",  order );
717145132Sanholt	DRM_DEBUG( "size:       %d\n",  size );
718145132Sanholt	DRM_DEBUG( "agp_offset: %ld\n", agp_offset );
719145132Sanholt	DRM_DEBUG( "alignment:  %d\n",  alignment );
720145132Sanholt	DRM_DEBUG( "page_order: %d\n",  page_order );
721145132Sanholt	DRM_DEBUG( "total:      %d\n",  total );
722145132Sanholt
723145132Sanholt	entry = &dma->bufs[order];
724145132Sanholt
725145132Sanholt	entry->buflist = malloc(count * sizeof(*entry->buflist), M_DRM,
726145132Sanholt	    M_NOWAIT | M_ZERO);
727145132Sanholt	if (entry->buflist == NULL)
728182080Srnoland		return ENOMEM;
729145132Sanholt
730145132Sanholt	entry->buf_size = size;
731145132Sanholt	entry->page_order = page_order;
732145132Sanholt
733145132Sanholt	offset = 0;
734145132Sanholt
735145132Sanholt	while ( entry->buf_count < count ) {
736145132Sanholt		buf          = &entry->buflist[entry->buf_count];
737145132Sanholt		buf->idx     = dma->buf_count + entry->buf_count;
738145132Sanholt		buf->total   = alignment;
739145132Sanholt		buf->order   = order;
740145132Sanholt		buf->used    = 0;
741145132Sanholt
742145132Sanholt		buf->offset  = (dma->byte_count + offset);
743145132Sanholt		buf->bus_address = agp_offset + offset;
744145132Sanholt		buf->address = (void *)(agp_offset + offset + dev->sg->handle);
745145132Sanholt		buf->next    = NULL;
746145132Sanholt		buf->pending = 0;
747182080Srnoland		buf->file_priv = NULL;
748145132Sanholt
749152909Sanholt		buf->dev_priv_size = dev->driver.buf_priv_size;
750145132Sanholt		buf->dev_private = malloc(buf->dev_priv_size, M_DRM,
751145132Sanholt		    M_NOWAIT | M_ZERO);
752145132Sanholt		if (buf->dev_private == NULL) {
753145132Sanholt			/* Set count correctly so we free the proper amount. */
754145132Sanholt			entry->buf_count = count;
755145132Sanholt			drm_cleanup_buf_error(dev, entry);
756182080Srnoland			return ENOMEM;
757145132Sanholt		}
758145132Sanholt
759145132Sanholt		DRM_DEBUG( "buffer %d @ %p\n",
760145132Sanholt			   entry->buf_count, buf->address );
761145132Sanholt
762145132Sanholt		offset += alignment;
763145132Sanholt		entry->buf_count++;
764145132Sanholt		byte_count += PAGE_SIZE << page_order;
765145132Sanholt	}
766145132Sanholt
767145132Sanholt	DRM_DEBUG( "byte_count: %d\n", byte_count );
768145132Sanholt
769145132Sanholt	temp_buflist = realloc(dma->buflist,
770145132Sanholt	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), M_DRM,
771145132Sanholt	    M_NOWAIT);
772145132Sanholt	if (temp_buflist == NULL) {
773145132Sanholt		/* Free the entry because it isn't valid */
774145132Sanholt		drm_cleanup_buf_error(dev, entry);
775182080Srnoland		return ENOMEM;
776145132Sanholt	}
777145132Sanholt	dma->buflist = temp_buflist;
778145132Sanholt
779145132Sanholt	for ( i = 0 ; i < entry->buf_count ; i++ ) {
780145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
781145132Sanholt	}
782145132Sanholt
783145132Sanholt	dma->buf_count += entry->buf_count;
784145132Sanholt	dma->byte_count += byte_count;
785145132Sanholt
786145132Sanholt	DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
787145132Sanholt	DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
788145132Sanholt
789145132Sanholt	request->count = entry->buf_count;
790145132Sanholt	request->size = size;
791145132Sanholt
792145132Sanholt	dma->flags = _DRM_DMA_USE_SG;
793145132Sanholt
794145132Sanholt	return 0;
795145132Sanholt}
796145132Sanholt
797182080Srnolandint drm_addbufs_agp(struct drm_device *dev, drm_buf_desc_t *request)
798145132Sanholt{
799152909Sanholt	int order, ret;
800145132Sanholt
801152909Sanholt	if (request->count < 0 || request->count > 4096)
802182080Srnoland		return EINVAL;
803152909Sanholt
804152909Sanholt	order = drm_order(request->size);
805152909Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
806182080Srnoland		return EINVAL;
807145132Sanholt
808182080Srnoland	DRM_SPINLOCK(&dev->dma_lock);
809182080Srnoland
810152909Sanholt	/* No more allocations after first buffer-using ioctl. */
811152909Sanholt	if (dev->buf_use != 0) {
812152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
813182080Srnoland		return EBUSY;
814152909Sanholt	}
815152909Sanholt	/* No more than one allocation per order */
816152909Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
817152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
818182080Srnoland		return ENOMEM;
819152909Sanholt	}
820152909Sanholt
821152909Sanholt	ret = drm_do_addbufs_agp(dev, request);
822152909Sanholt
823152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
824152909Sanholt
825152909Sanholt	return ret;
826152909Sanholt}
827152909Sanholt
828182080Srnolandint drm_addbufs_sg(struct drm_device *dev, drm_buf_desc_t *request)
829152909Sanholt{
830152909Sanholt	int order, ret;
831152909Sanholt
832152909Sanholt	if (!DRM_SUSER(DRM_CURPROC))
833182080Srnoland		return EACCES;
834152909Sanholt
835152909Sanholt	if (request->count < 0 || request->count > 4096)
836182080Srnoland		return EINVAL;
837182080Srnoland
838152909Sanholt	order = drm_order(request->size);
839145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
840182080Srnoland		return EINVAL;
841145132Sanholt
842182080Srnoland	DRM_SPINLOCK(&dev->dma_lock);
843182080Srnoland
844152909Sanholt	/* No more allocations after first buffer-using ioctl. */
845152909Sanholt	if (dev->buf_use != 0) {
846152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
847182080Srnoland		return EBUSY;
848152909Sanholt	}
849152909Sanholt	/* No more than one allocation per order */
850152909Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
851152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
852182080Srnoland		return ENOMEM;
853152909Sanholt	}
854152909Sanholt
855152909Sanholt	ret = drm_do_addbufs_sg(dev, request);
856152909Sanholt
857152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
858152909Sanholt
859152909Sanholt	return ret;
860152909Sanholt}
861152909Sanholt
862182080Srnolandint drm_addbufs_pci(struct drm_device *dev, drm_buf_desc_t *request)
863152909Sanholt{
864152909Sanholt	int order, ret;
865152909Sanholt
866152909Sanholt	if (!DRM_SUSER(DRM_CURPROC))
867182080Srnoland		return EACCES;
868152909Sanholt
869152909Sanholt	if (request->count < 0 || request->count > 4096)
870182080Srnoland		return EINVAL;
871182080Srnoland
872152909Sanholt	order = drm_order(request->size);
873152909Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
874182080Srnoland		return EINVAL;
875152909Sanholt
876182080Srnoland	DRM_SPINLOCK(&dev->dma_lock);
877182080Srnoland
878145132Sanholt	/* No more allocations after first buffer-using ioctl. */
879145132Sanholt	if (dev->buf_use != 0) {
880145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
881182080Srnoland		return EBUSY;
882145132Sanholt	}
883145132Sanholt	/* No more than one allocation per order */
884145132Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
885145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
886182080Srnoland		return ENOMEM;
887145132Sanholt	}
888145132Sanholt
889152909Sanholt	ret = drm_do_addbufs_pci(dev, request);
890152909Sanholt
891152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
892152909Sanholt
893152909Sanholt	return ret;
894152909Sanholt}
895152909Sanholt
896182080Srnolandint drm_addbufs_ioctl(struct drm_device *dev, void *data,
897182080Srnoland		      struct drm_file *file_priv)
898152909Sanholt{
899182080Srnoland	drm_buf_desc_t *request = data;
900152909Sanholt	int err;
901152909Sanholt
902182080Srnoland	if (request->flags & _DRM_AGP_BUFFER)
903182080Srnoland		err = drm_addbufs_agp(dev, request);
904182080Srnoland	else if (request->flags & _DRM_SG_BUFFER)
905182080Srnoland		err = drm_addbufs_sg(dev, request);
906145132Sanholt	else
907182080Srnoland		err = drm_addbufs_pci(dev, request);
908145132Sanholt
909145132Sanholt	return err;
910145132Sanholt}
911145132Sanholt
912182080Srnolandint drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
913145132Sanholt{
914145132Sanholt	drm_device_dma_t *dma = dev->dma;
915182080Srnoland	drm_buf_info_t *request = data;
916145132Sanholt	int i;
917145132Sanholt	int count;
918145132Sanholt	int retcode = 0;
919145132Sanholt
920145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
921145132Sanholt	++dev->buf_use;		/* Can't allocate more after this call */
922145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
923145132Sanholt
924145132Sanholt	for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
925145132Sanholt		if ( dma->bufs[i].buf_count ) ++count;
926145132Sanholt	}
927145132Sanholt
928145132Sanholt	DRM_DEBUG( "count = %d\n", count );
929145132Sanholt
930182080Srnoland	if ( request->count >= count ) {
931145132Sanholt		for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
932145132Sanholt			if ( dma->bufs[i].buf_count ) {
933145132Sanholt				drm_buf_desc_t from;
934145132Sanholt
935145132Sanholt				from.count = dma->bufs[i].buf_count;
936145132Sanholt				from.size = dma->bufs[i].buf_size;
937145132Sanholt				from.low_mark = dma->bufs[i].freelist.low_mark;
938145132Sanholt				from.high_mark = dma->bufs[i].freelist.high_mark;
939145132Sanholt
940182080Srnoland				if (DRM_COPY_TO_USER(&request->list[count], &from,
941145132Sanholt				    sizeof(drm_buf_desc_t)) != 0) {
942182080Srnoland					retcode = EFAULT;
943145132Sanholt					break;
944145132Sanholt				}
945145132Sanholt
946145132Sanholt				DRM_DEBUG( "%d %d %d %d %d\n",
947145132Sanholt					   i,
948145132Sanholt					   dma->bufs[i].buf_count,
949145132Sanholt					   dma->bufs[i].buf_size,
950145132Sanholt					   dma->bufs[i].freelist.low_mark,
951145132Sanholt					   dma->bufs[i].freelist.high_mark );
952145132Sanholt				++count;
953145132Sanholt			}
954145132Sanholt		}
955145132Sanholt	}
956182080Srnoland	request->count = count;
957145132Sanholt
958145132Sanholt	return retcode;
959145132Sanholt}
960145132Sanholt
961182080Srnolandint drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
962145132Sanholt{
963145132Sanholt	drm_device_dma_t *dma = dev->dma;
964182080Srnoland	drm_buf_desc_t *request = data;
965145132Sanholt	int order;
966145132Sanholt
967145132Sanholt	DRM_DEBUG( "%d, %d, %d\n",
968182080Srnoland		   request->size, request->low_mark, request->high_mark );
969145132Sanholt
970145132Sanholt
971182080Srnoland	order = drm_order(request->size);
972145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
973182080Srnoland	    request->low_mark < 0 || request->high_mark < 0) {
974182080Srnoland		return EINVAL;
975145132Sanholt	}
976145132Sanholt
977145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
978182080Srnoland	if (request->low_mark > dma->bufs[order].buf_count ||
979182080Srnoland	    request->high_mark > dma->bufs[order].buf_count) {
980182080Srnoland		DRM_SPINUNLOCK(&dev->dma_lock);
981182080Srnoland		return EINVAL;
982145132Sanholt	}
983145132Sanholt
984182080Srnoland	dma->bufs[order].freelist.low_mark  = request->low_mark;
985182080Srnoland	dma->bufs[order].freelist.high_mark = request->high_mark;
986145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
987145132Sanholt
988145132Sanholt	return 0;
989145132Sanholt}
990145132Sanholt
991182080Srnolandint drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
992145132Sanholt{
993145132Sanholt	drm_device_dma_t *dma = dev->dma;
994182080Srnoland	drm_buf_free_t *request = data;
995145132Sanholt	int i;
996145132Sanholt	int idx;
997145132Sanholt	drm_buf_t *buf;
998145132Sanholt	int retcode = 0;
999145132Sanholt
1000182080Srnoland	DRM_DEBUG( "%d\n", request->count );
1001145132Sanholt
1002145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
1003182080Srnoland	for ( i = 0 ; i < request->count ; i++ ) {
1004182080Srnoland		if (DRM_COPY_FROM_USER(&idx, &request->list[i], sizeof(idx))) {
1005182080Srnoland			retcode = EFAULT;
1006145132Sanholt			break;
1007145132Sanholt		}
1008145132Sanholt		if ( idx < 0 || idx >= dma->buf_count ) {
1009145132Sanholt			DRM_ERROR( "Index %d (of %d max)\n",
1010145132Sanholt				   idx, dma->buf_count - 1 );
1011182080Srnoland			retcode = EINVAL;
1012145132Sanholt			break;
1013145132Sanholt		}
1014145132Sanholt		buf = dma->buflist[idx];
1015182080Srnoland		if ( buf->file_priv != file_priv ) {
1016145132Sanholt			DRM_ERROR("Process %d freeing buffer not owned\n",
1017145132Sanholt				   DRM_CURRENTPID);
1018182080Srnoland			retcode = EINVAL;
1019145132Sanholt			break;
1020145132Sanholt		}
1021145132Sanholt		drm_free_buffer(dev, buf);
1022145132Sanholt	}
1023145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
1024145132Sanholt
1025145132Sanholt	return retcode;
1026145132Sanholt}
1027145132Sanholt
1028182080Srnolandint drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
1029145132Sanholt{
1030145132Sanholt	drm_device_dma_t *dma = dev->dma;
1031145132Sanholt	int retcode = 0;
1032145132Sanholt	const int zero = 0;
1033145132Sanholt	vm_offset_t address;
1034145132Sanholt	struct vmspace *vms;
1035145132Sanholt#ifdef __FreeBSD__
1036145132Sanholt	vm_ooffset_t foff;
1037145132Sanholt	vm_size_t size;
1038145132Sanholt	vm_offset_t vaddr;
1039145132Sanholt#elif defined(__NetBSD__) || defined(__OpenBSD__)
1040145132Sanholt	struct vnode *vn;
1041152909Sanholt	voff_t foff;
1042152909Sanholt	vsize_t size;
1043145132Sanholt	vaddr_t vaddr;
1044145132Sanholt#endif /* __NetBSD__ || __OpenBSD__ */
1045145132Sanholt
1046182080Srnoland	drm_buf_map_t *request = data;
1047145132Sanholt	int i;
1048145132Sanholt
1049145132Sanholt#if defined(__NetBSD__) || defined(__OpenBSD__)
1050145132Sanholt	if (!vfinddev(kdev, VCHR, &vn))
1051145132Sanholt		return 0;	/* FIXME: Shouldn't this be EINVAL or something? */
1052145132Sanholt#endif /* __NetBSD__ || __OpenBSD */
1053145132Sanholt
1054145132Sanholt#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
1055182080Srnoland	vms = DRM_CURPROC->td_proc->p_vmspace;
1056145132Sanholt#else
1057182080Srnoland	vms = DRM_CURPROC->p_vmspace;
1058145132Sanholt#endif
1059145132Sanholt
1060145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
1061145132Sanholt	dev->buf_use++;		/* Can't allocate more after this call */
1062145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
1063145132Sanholt
1064182080Srnoland	if (request->count < dma->buf_count)
1065145132Sanholt		goto done;
1066145132Sanholt
1067152909Sanholt	if ((dev->driver.use_agp && (dma->flags & _DRM_DMA_USE_AGP)) ||
1068152909Sanholt	    (dev->driver.use_sg && (dma->flags & _DRM_DMA_USE_SG))) {
1069145132Sanholt		drm_local_map_t *map = dev->agp_buffer_map;
1070145132Sanholt
1071145132Sanholt		if (map == NULL) {
1072145132Sanholt			retcode = EINVAL;
1073145132Sanholt			goto done;
1074145132Sanholt		}
1075145132Sanholt		size = round_page(map->size);
1076145132Sanholt		foff = map->offset;
1077145132Sanholt	} else {
1078145132Sanholt		size = round_page(dma->byte_count),
1079145132Sanholt		foff = 0;
1080145132Sanholt	}
1081145132Sanholt
1082145132Sanholt#ifdef __FreeBSD__
1083145132Sanholt	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
1084145132Sanholt#if __FreeBSD_version >= 600023
1085145132Sanholt	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1086182080Srnoland	    VM_PROT_ALL, MAP_SHARED, OBJT_DEVICE, dev->devnode, foff);
1087145132Sanholt#else
1088145132Sanholt	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1089182080Srnoland	    VM_PROT_ALL, MAP_SHARED, SLIST_FIRST(&dev->devnode->si_hlist),
1090182080Srnoland	    foff);
1091145132Sanholt#endif
1092145132Sanholt#elif defined(__NetBSD__) || defined(__OpenBSD__)
1093145132Sanholt	vaddr = round_page((vaddr_t)vms->vm_daddr + MAXDSIZ);
1094145132Sanholt	retcode = uvm_mmap(&vms->vm_map, &vaddr, size,
1095145132Sanholt	    UVM_PROT_READ | UVM_PROT_WRITE, UVM_PROT_ALL, MAP_SHARED,
1096145132Sanholt	    &vn->v_uobj, foff, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
1097145132Sanholt#endif /* __NetBSD__ || __OpenBSD */
1098145132Sanholt	if (retcode)
1099145132Sanholt		goto done;
1100145132Sanholt
1101182080Srnoland	request->virtual = (void *)vaddr;
1102145132Sanholt
1103145132Sanholt	for ( i = 0 ; i < dma->buf_count ; i++ ) {
1104182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].idx,
1105182080Srnoland		    &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
1106145132Sanholt			retcode = EFAULT;
1107145132Sanholt			goto done;
1108145132Sanholt		}
1109182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].total,
1110182080Srnoland		    &dma->buflist[i]->total, sizeof(request->list[0].total))) {
1111145132Sanholt			retcode = EFAULT;
1112145132Sanholt			goto done;
1113145132Sanholt		}
1114182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].used, &zero,
1115145132Sanholt		    sizeof(zero))) {
1116145132Sanholt			retcode = EFAULT;
1117145132Sanholt			goto done;
1118145132Sanholt		}
1119145132Sanholt		address = vaddr + dma->buflist[i]->offset; /* *** */
1120182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].address, &address,
1121145132Sanholt		    sizeof(address))) {
1122145132Sanholt			retcode = EFAULT;
1123145132Sanholt			goto done;
1124145132Sanholt		}
1125145132Sanholt	}
1126145132Sanholt
1127145132Sanholt done:
1128182080Srnoland	request->count = dma->buf_count;
1129145132Sanholt
1130182080Srnoland	DRM_DEBUG( "%d buffers, retcode = %d\n", request->count, retcode );
1131145132Sanholt
1132182080Srnoland	return retcode;
1133145132Sanholt}
1134