drm_bufs.c revision 207066
1265236Sken/*-
2283990Sslm * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
3283990Sslm * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
4265236Sken * All Rights Reserved.
5265236Sken *
6265236Sken * Permission is hereby granted, free of charge, to any person obtaining a
7265236Sken * copy of this software and associated documentation files (the "Software"),
8265236Sken * to deal in the Software without restriction, including without limitation
9265236Sken * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10265236Sken * and/or sell copies of the Software, and to permit persons to whom the
11265236Sken * Software is furnished to do so, subject to the following conditions:
12265236Sken *
13265236Sken * The above copyright notice and this permission notice (including the next
14265236Sken * paragraph) shall be included in all copies or substantial portions of the
15265236Sken * Software.
16265236Sken *
17265236Sken * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18265236Sken * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19265236Sken * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20265236Sken * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21265236Sken * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22265236Sken * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23265236Sken * OTHER DEALINGS IN THE SOFTWARE.
24265236Sken *
25265236Sken * Authors:
26265236Sken *    Rickard E. (Rik) Faith <faith@valinux.com>
27283990Sslm *    Gareth Hughes <gareth@valinux.com>
28265236Sken *
29265236Sken */
30265236Sken
31265236Sken#include <sys/cdefs.h>
32265236Sken__FBSDID("$FreeBSD: head/sys/dev/drm/drm_bufs.c 207066 2010-04-22 18:21:25Z rnoland $");
33283990Sslm
34265236Sken/** @file drm_bufs.c
35265236Sken * Implementation of the ioctls for setup of DRM mappings and DMA buffers.
36265236Sken */
37265236Sken
38265236Sken#include "dev/pci/pcireg.h"
39265236Sken
40265236Sken#include "dev/drm/drmP.h"
41265236Sken
42265236Sken/* Allocation of PCI memory resources (framebuffer, registers, etc.) for
43265236Sken * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
44265236Sken * address for accessing them.  Cleaned up at unload.
45265236Sken */
46265236Skenstatic int drm_alloc_resource(struct drm_device *dev, int resource)
47265236Sken{
48265236Sken	struct resource *res;
49265236Sken	int rid;
50265236Sken
51265236Sken	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
52265236Sken
53265236Sken	if (resource >= DRM_MAX_PCI_RESOURCE) {
54265236Sken		DRM_ERROR("Resource %d too large\n", resource);
55265236Sken		return 1;
56265236Sken	}
57265236Sken
58265236Sken	if (dev->pcir[resource] != NULL) {
59265236Sken		return 0;
60265236Sken	}
61265236Sken
62265236Sken	DRM_UNLOCK();
63265236Sken	rid = PCIR_BAR(resource);
64265236Sken	res = bus_alloc_resource_any(dev->device, SYS_RES_MEMORY, &rid,
65265236Sken	    RF_SHAREABLE);
66265236Sken	DRM_LOCK();
67265236Sken	if (res == NULL) {
68265236Sken		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
69265236Sken		return 1;
70265236Sken	}
71265236Sken
72265236Sken	if (dev->pcir[resource] == NULL) {
73265236Sken		dev->pcirid[resource] = rid;
74265236Sken		dev->pcir[resource] = res;
75265236Sken	}
76265236Sken
77265236Sken	return 0;
78265236Sken}
79265236Sken
80265236Skenunsigned long drm_get_resource_start(struct drm_device *dev,
81265236Sken				     unsigned int resource)
82265236Sken{
83265236Sken	if (drm_alloc_resource(dev, resource) != 0)
84265236Sken		return 0;
85265236Sken
86265236Sken	return rman_get_start(dev->pcir[resource]);
87265236Sken}
88265236Sken
89265236Skenunsigned long drm_get_resource_len(struct drm_device *dev,
90265236Sken				   unsigned int resource)
91265236Sken{
92265236Sken	if (drm_alloc_resource(dev, resource) != 0)
93265236Sken		return 0;
94265236Sken
95265236Sken	return rman_get_size(dev->pcir[resource]);
96265236Sken}
97265236Sken
98265236Skenint drm_addmap(struct drm_device * dev, unsigned long offset,
99265236Sken	       unsigned long size,
100265236Sken    enum drm_map_type type, enum drm_map_flags flags, drm_local_map_t **map_ptr)
101265236Sken{
102265236Sken	drm_local_map_t *map;
103265236Sken	int align;
104265236Sken	/*drm_agp_mem_t *entry;
105265236Sken	int valid;*/
106265236Sken
107265236Sken	/* Only allow shared memory to be removable since we only keep enough
108265236Sken	 * book keeping information about shared memory to allow for removal
109283990Sslm	 * when processes fork.
110283990Sslm	 */
111283990Sslm	if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
112265236Sken		DRM_ERROR("Requested removable map for non-DRM_SHM\n");
113265236Sken		return EINVAL;
114265236Sken	}
115265236Sken	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
116265236Sken		DRM_ERROR("offset/size not page aligned: 0x%lx/0x%lx\n",
117265236Sken		    offset, size);
118265236Sken		return EINVAL;
119265236Sken	}
120265236Sken	if (offset + size < offset) {
121265236Sken		DRM_ERROR("offset and size wrap around: 0x%lx/0x%lx\n",
122283990Sslm		    offset, size);
123265236Sken		return EINVAL;
124283990Sslm	}
125265236Sken
126265236Sken	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", offset,
127265236Sken	    size, type);
128265236Sken
129265236Sken	/* Check if this is just another version of a kernel-allocated map, and
130265236Sken	 * just hand that back if so.
131265236Sken	 */
132265236Sken	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
133265236Sken	    type == _DRM_SHM) {
134265236Sken		TAILQ_FOREACH(map, &dev->maplist, link) {
135265236Sken			if (map->type == type && (map->offset == offset ||
136265236Sken			    (map->type == _DRM_SHM &&
137265236Sken			    map->flags == _DRM_CONTAINS_LOCK))) {
138265236Sken				map->size = size;
139265236Sken				DRM_DEBUG("Found kernel map %d\n", type);
140265236Sken				goto done;
141265236Sken			}
142265236Sken		}
143265236Sken	}
144265236Sken	DRM_UNLOCK();
145265236Sken
146265236Sken	/* Allocate a new map structure, fill it in, and do any type-specific
147265236Sken	 * initialization necessary.
148265236Sken	 */
149265236Sken	map = malloc(sizeof(*map), DRM_MEM_MAPS, M_ZERO | M_NOWAIT);
150265236Sken	if (!map) {
151265236Sken		DRM_LOCK();
152265236Sken		return ENOMEM;
153265236Sken	}
154265236Sken
155265236Sken	map->offset = offset;
156265236Sken	map->size = size;
157265236Sken	map->type = type;
158265236Sken	map->flags = flags;
159265236Sken	map->handle = (void *)((unsigned long)alloc_unr(dev->map_unrhdr) <<
160265236Sken	    DRM_MAP_HANDLE_SHIFT);
161265236Sken
162265236Sken	switch (map->type) {
163265236Sken	case _DRM_REGISTERS:
164265236Sken		map->virtual = drm_ioremap(dev, map);
165265236Sken		if (!(map->flags & _DRM_WRITE_COMBINING))
166265236Sken			break;
167265236Sken		/* FALLTHROUGH */
168265236Sken	case _DRM_FRAME_BUFFER:
169265236Sken		if (drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC) == 0)
170265236Sken			map->mtrr = 1;
171265236Sken		break;
172265236Sken	case _DRM_SHM:
173265236Sken		map->virtual = malloc(map->size, DRM_MEM_MAPS, M_NOWAIT);
174265236Sken		DRM_DEBUG("%lu %d %p\n",
175265236Sken		    map->size, drm_order(map->size), map->virtual);
176265236Sken		if (!map->virtual) {
177265236Sken			free(map, DRM_MEM_MAPS);
178265236Sken			DRM_LOCK();
179265236Sken			return ENOMEM;
180265236Sken		}
181265236Sken		map->offset = (unsigned long)map->virtual;
182265236Sken		if (map->flags & _DRM_CONTAINS_LOCK) {
183265236Sken			/* Prevent a 2nd X Server from creating a 2nd lock */
184265236Sken			DRM_LOCK();
185265236Sken			if (dev->lock.hw_lock != NULL) {
186265236Sken				DRM_UNLOCK();
187265236Sken				free(map->virtual, DRM_MEM_MAPS);
188265236Sken				free(map, DRM_MEM_MAPS);
189265236Sken				return EBUSY;
190265236Sken			}
191265236Sken			dev->lock.hw_lock = map->virtual; /* Pointer to lock */
192265236Sken			DRM_UNLOCK();
193265236Sken		}
194265236Sken		break;
195265236Sken	case _DRM_AGP:
196265236Sken		/*valid = 0;*/
197265236Sken		/* In some cases (i810 driver), user space may have already
198265236Sken		 * added the AGP base itself, because dev->agp->base previously
199265236Sken		 * only got set during AGP enable.  So, only add the base
200265236Sken		 * address if the map's offset isn't already within the
201265236Sken		 * aperture.
202265236Sken		 */
203265236Sken		if (map->offset < dev->agp->base ||
204265236Sken		    map->offset > dev->agp->base +
205265236Sken		    dev->agp->info.ai_aperture_size - 1) {
206265236Sken			map->offset += dev->agp->base;
207265236Sken		}
208265236Sken		map->mtrr   = dev->agp->mtrr; /* for getmap */
209265236Sken		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
210265236Sken			if ((map->offset >= entry->bound) &&
211265236Sken			    (map->offset + map->size <=
212265236Sken			    entry->bound + entry->pages * PAGE_SIZE)) {
213265236Sken				valid = 1;
214265236Sken				break;
215265236Sken			}
216265236Sken		}
217265236Sken		if (!valid) {
218265236Sken			free(map, DRM_MEM_MAPS);
219265236Sken			DRM_LOCK();
220265236Sken			return EACCES;
221265236Sken		}*/
222265236Sken		break;
223265236Sken	case _DRM_SCATTER_GATHER:
224265236Sken		if (!dev->sg) {
225265236Sken			free(map, DRM_MEM_MAPS);
226265236Sken			DRM_LOCK();
227265236Sken			return EINVAL;
228265236Sken		}
229265236Sken		map->virtual = (void *)(dev->sg->handle + offset);
230265236Sken		map->offset = dev->sg->handle + offset;
231265236Sken		break;
232265236Sken	case _DRM_CONSISTENT:
233265236Sken		/* Unfortunately, we don't get any alignment specification from
234265236Sken		 * the caller, so we have to guess.  drm_pci_alloc requires
235265236Sken		 * a power-of-two alignment, so try to align the bus address of
236265236Sken		 * the map to it size if possible, otherwise just assume
237265236Sken		 * PAGE_SIZE alignment.
238265236Sken		 */
239265236Sken		align = map->size;
240265236Sken		if ((align & (align - 1)) != 0)
241265236Sken			align = PAGE_SIZE;
242265236Sken		map->dmah = drm_pci_alloc(dev, map->size, align, 0xfffffffful);
243265236Sken		if (map->dmah == NULL) {
244265236Sken			free(map, DRM_MEM_MAPS);
245265236Sken			DRM_LOCK();
246265236Sken			return ENOMEM;
247265236Sken		}
248265236Sken		map->virtual = map->dmah->vaddr;
249265236Sken		map->offset = map->dmah->busaddr;
250265236Sken		break;
251265236Sken	default:
252265236Sken		DRM_ERROR("Bad map type %d\n", map->type);
253265236Sken		free(map, DRM_MEM_MAPS);
254265236Sken		DRM_LOCK();
255265236Sken		return EINVAL;
256265236Sken	}
257265236Sken
258265236Sken	DRM_LOCK();
259265236Sken	TAILQ_INSERT_TAIL(&dev->maplist, map, link);
260265236Sken
261265236Skendone:
262265236Sken	/* Jumped to, with lock held, when a kernel map is found. */
263265236Sken
264265236Sken	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
265265236Sken	    map->size);
266265236Sken
267265236Sken	*map_ptr = map;
268265236Sken
269265236Sken	return 0;
270265236Sken}
271265236Sken
272265236Skenint drm_addmap_ioctl(struct drm_device *dev, void *data,
273265236Sken		     struct drm_file *file_priv)
274265236Sken{
275265236Sken	struct drm_map *request = data;
276265236Sken	drm_local_map_t *map;
277265236Sken	int err;
278265236Sken
279265236Sken	if (!(dev->flags & (FREAD|FWRITE)))
280265236Sken		return EACCES; /* Require read/write */
281265236Sken
282265236Sken	if (!DRM_SUSER(DRM_CURPROC) && request->type != _DRM_AGP)
283265236Sken		return EACCES;
284265236Sken
285265236Sken	DRM_LOCK();
286265236Sken	err = drm_addmap(dev, request->offset, request->size, request->type,
287265236Sken	    request->flags, &map);
288265236Sken	DRM_UNLOCK();
289265236Sken	if (err != 0)
290265236Sken		return err;
291265236Sken
292265236Sken	request->offset = map->offset;
293265236Sken	request->size = map->size;
294265236Sken	request->type = map->type;
295265236Sken	request->flags = map->flags;
296265236Sken	request->mtrr   = map->mtrr;
297265236Sken	request->handle = (void *)map->handle;
298265236Sken
299265236Sken	return 0;
300265236Sken}
301265236Sken
302265236Skenvoid drm_rmmap(struct drm_device *dev, drm_local_map_t *map)
303265236Sken{
304265236Sken	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
305265236Sken
306265236Sken	if (map == NULL)
307265236Sken		return;
308265236Sken
309265236Sken	TAILQ_REMOVE(&dev->maplist, map, link);
310265236Sken
311265236Sken	switch (map->type) {
312265236Sken	case _DRM_REGISTERS:
313265236Sken		if (map->bsr == NULL)
314265236Sken			drm_ioremapfree(map);
315265236Sken		/* FALLTHROUGH */
316265236Sken	case _DRM_FRAME_BUFFER:
317265236Sken		if (map->mtrr) {
318265236Sken			int __unused retcode;
319265236Sken
320265236Sken			retcode = drm_mtrr_del(0, map->offset, map->size,
321265236Sken			    DRM_MTRR_WC);
322265236Sken			DRM_DEBUG("mtrr_del = %d\n", retcode);
323265236Sken		}
324265236Sken		break;
325265236Sken	case _DRM_SHM:
326265236Sken		free(map->virtual, DRM_MEM_MAPS);
327265236Sken		break;
328265236Sken	case _DRM_AGP:
329265236Sken	case _DRM_SCATTER_GATHER:
330265236Sken		break;
331265236Sken	case _DRM_CONSISTENT:
332283990Sslm		drm_pci_free(dev, map->dmah);
333265236Sken		break;
334265236Sken	default:
335265236Sken		DRM_ERROR("Bad map type %d\n", map->type);
336265236Sken		break;
337265236Sken	}
338265236Sken
339265236Sken	if (map->bsr != NULL) {
340265236Sken		bus_release_resource(dev->device, SYS_RES_MEMORY, map->rid,
341265236Sken		    map->bsr);
342265236Sken	}
343265236Sken
344265236Sken	DRM_UNLOCK();
345265236Sken	if (map->handle)
346265236Sken		free_unr(dev->map_unrhdr, (unsigned long)map->handle >>
347265236Sken		    DRM_MAP_HANDLE_SHIFT);
348265236Sken	DRM_LOCK();
349265236Sken
350265236Sken	free(map, DRM_MEM_MAPS);
351265236Sken}
352265236Sken
353265236Sken/* Remove a map private from list and deallocate resources if the mapping
354265236Sken * isn't in use.
355265236Sken */
356265236Sken
357283990Sslmint drm_rmmap_ioctl(struct drm_device *dev, void *data,
358283990Sslm		    struct drm_file *file_priv)
359283990Sslm{
360265236Sken	drm_local_map_t *map;
361265236Sken	struct drm_map *request = data;
362265236Sken
363265236Sken	DRM_LOCK();
364265236Sken	TAILQ_FOREACH(map, &dev->maplist, link) {
365265236Sken		if (map->handle == request->handle &&
366265236Sken		    map->flags & _DRM_REMOVABLE)
367283990Sslm			break;
368265236Sken	}
369265236Sken
370265236Sken	/* No match found. */
371265236Sken	if (map == NULL) {
372265236Sken		DRM_UNLOCK();
373265236Sken		return EINVAL;
374265236Sken	}
375265236Sken
376265236Sken	drm_rmmap(dev, map);
377265236Sken
378265236Sken	DRM_UNLOCK();
379265236Sken
380265236Sken	return 0;
381265236Sken}
382265236Sken
383265236Sken
384265236Skenstatic void drm_cleanup_buf_error(struct drm_device *dev,
385265236Sken				  drm_buf_entry_t *entry)
386265236Sken{
387265236Sken	int i;
388265236Sken
389265236Sken	if (entry->seg_count) {
390265236Sken		for (i = 0; i < entry->seg_count; i++) {
391265236Sken			drm_pci_free(dev, entry->seglist[i]);
392265236Sken		}
393265236Sken		free(entry->seglist, DRM_MEM_SEGS);
394265236Sken
395265236Sken		entry->seg_count = 0;
396265236Sken	}
397265236Sken
398265236Sken   	if (entry->buf_count) {
399265236Sken	   	for (i = 0; i < entry->buf_count; i++) {
400265236Sken			free(entry->buflist[i].dev_private, DRM_MEM_BUFS);
401265236Sken		}
402265236Sken		free(entry->buflist, DRM_MEM_BUFS);
403265236Sken
404265236Sken		entry->buf_count = 0;
405265236Sken	}
406265236Sken}
407265236Sken
408265236Skenstatic int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
409265236Sken{
410265236Sken	drm_device_dma_t *dma = dev->dma;
411265236Sken	drm_buf_entry_t *entry;
412265236Sken	/*drm_agp_mem_t *agp_entry;
413265236Sken	int valid*/
414265236Sken	drm_buf_t *buf;
415265236Sken	unsigned long offset;
416265236Sken	unsigned long agp_offset;
417265236Sken	int count;
418265236Sken	int order;
419265236Sken	int size;
420265236Sken	int alignment;
421265236Sken	int page_order;
422265236Sken	int total;
423265236Sken	int byte_count;
424265236Sken	int i;
425265236Sken	drm_buf_t **temp_buflist;
426265236Sken
427265236Sken	count = request->count;
428265236Sken	order = drm_order(request->size);
429265236Sken	size = 1 << order;
430265236Sken
431265236Sken	alignment  = (request->flags & _DRM_PAGE_ALIGN)
432265236Sken	    ? round_page(size) : size;
433265236Sken	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
434265236Sken	total = PAGE_SIZE << page_order;
435265236Sken
436265236Sken	byte_count = 0;
437265236Sken	agp_offset = dev->agp->base + request->agp_start;
438265236Sken
439265236Sken	DRM_DEBUG("count:      %d\n",  count);
440265236Sken	DRM_DEBUG("order:      %d\n",  order);
441265236Sken	DRM_DEBUG("size:       %d\n",  size);
442265236Sken	DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset);
443265236Sken	DRM_DEBUG("alignment:  %d\n",  alignment);
444265236Sken	DRM_DEBUG("page_order: %d\n",  page_order);
445265236Sken	DRM_DEBUG("total:      %d\n",  total);
446265236Sken
447265236Sken	/* Make sure buffers are located in AGP memory that we own */
448265236Sken	/* Breaks MGA due to drm_alloc_agp not setting up entries for the
449265236Sken	 * memory.  Safe to ignore for now because these ioctls are still
450265236Sken	 * root-only.
451265236Sken	 */
452265236Sken	/*valid = 0;
453265236Sken	for (agp_entry = dev->agp->memory; agp_entry;
454265236Sken	    agp_entry = agp_entry->next) {
455265236Sken		if ((agp_offset >= agp_entry->bound) &&
456265236Sken		    (agp_offset + total * count <=
457265236Sken		    agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
458265236Sken			valid = 1;
459265236Sken			break;
460265236Sken		}
461265236Sken	}
462265236Sken	if (!valid) {
463265236Sken		DRM_DEBUG("zone invalid\n");
464265236Sken		return EINVAL;
465265236Sken	}*/
466265236Sken
467265236Sken	entry = &dma->bufs[order];
468265236Sken
469265236Sken	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
470265236Sken	    M_NOWAIT | M_ZERO);
471265236Sken	if (!entry->buflist) {
472265236Sken		return ENOMEM;
473265236Sken	}
474265236Sken
475265236Sken	entry->buf_size = size;
476265236Sken	entry->page_order = page_order;
477265236Sken
478265236Sken	offset = 0;
479265236Sken
480265236Sken	while (entry->buf_count < count) {
481265236Sken		buf          = &entry->buflist[entry->buf_count];
482265236Sken		buf->idx     = dma->buf_count + entry->buf_count;
483265236Sken		buf->total   = alignment;
484265236Sken		buf->order   = order;
485265236Sken		buf->used    = 0;
486265236Sken
487265236Sken		buf->offset  = (dma->byte_count + offset);
488265236Sken		buf->bus_address = agp_offset + offset;
489265236Sken		buf->address = (void *)(agp_offset + offset);
490265236Sken		buf->next    = NULL;
491265236Sken		buf->pending = 0;
492265236Sken		buf->file_priv = NULL;
493265236Sken
494265236Sken		buf->dev_priv_size = dev->driver->buf_priv_size;
495265236Sken		buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
496265236Sken		    M_NOWAIT | M_ZERO);
497265236Sken		if (buf->dev_private == NULL) {
498265236Sken			/* Set count correctly so we free the proper amount. */
499265236Sken			entry->buf_count = count;
500265236Sken			drm_cleanup_buf_error(dev, entry);
501265236Sken			return ENOMEM;
502265236Sken		}
503265236Sken
504265236Sken		offset += alignment;
505265236Sken		entry->buf_count++;
506265236Sken		byte_count += PAGE_SIZE << page_order;
507265236Sken	}
508265236Sken
509265236Sken	DRM_DEBUG("byte_count: %d\n", byte_count);
510265236Sken
511265236Sken	temp_buflist = realloc(dma->buflist,
512265236Sken	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
513265236Sken	    DRM_MEM_BUFS, M_NOWAIT);
514265236Sken	if (temp_buflist == NULL) {
515265236Sken		/* Free the entry because it isn't valid */
516265236Sken		drm_cleanup_buf_error(dev, entry);
517265236Sken		return ENOMEM;
518265236Sken	}
519265236Sken	dma->buflist = temp_buflist;
520265236Sken
521265236Sken	for (i = 0; i < entry->buf_count; i++) {
522265236Sken		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
523265236Sken	}
524265236Sken
525265236Sken	dma->buf_count += entry->buf_count;
526265236Sken	dma->byte_count += byte_count;
527265236Sken
528265236Sken	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
529265236Sken	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
530265236Sken
531265236Sken	request->count = entry->buf_count;
532265236Sken	request->size = size;
533265236Sken
534265236Sken	dma->flags = _DRM_DMA_USE_AGP;
535265236Sken
536265236Sken	return 0;
537265236Sken}
538265236Sken
539265236Skenstatic int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
540265236Sken{
541265236Sken	drm_device_dma_t *dma = dev->dma;
542265236Sken	int count;
543265236Sken	int order;
544265236Sken	int size;
545265236Sken	int total;
546265236Sken	int page_order;
547265236Sken	drm_buf_entry_t *entry;
548265236Sken	drm_buf_t *buf;
549265236Sken	int alignment;
550265236Sken	unsigned long offset;
551265236Sken	int i;
552265236Sken	int byte_count;
553265236Sken	int page_count;
554265236Sken	unsigned long *temp_pagelist;
555265236Sken	drm_buf_t **temp_buflist;
556265236Sken
557265236Sken	count = request->count;
558265236Sken	order = drm_order(request->size);
559265236Sken	size = 1 << order;
560265236Sken
561265236Sken	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
562265236Sken	    request->count, request->size, size, order);
563265236Sken
564265236Sken	alignment = (request->flags & _DRM_PAGE_ALIGN)
565265236Sken	    ? round_page(size) : size;
566265236Sken	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
567265236Sken	total = PAGE_SIZE << page_order;
568265236Sken
569265236Sken	entry = &dma->bufs[order];
570265236Sken
571265236Sken	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
572265236Sken	    M_NOWAIT | M_ZERO);
573265236Sken	entry->seglist = malloc(count * sizeof(*entry->seglist), DRM_MEM_SEGS,
574265236Sken	    M_NOWAIT | M_ZERO);
575265236Sken
576265236Sken	/* Keep the original pagelist until we know all the allocations
577265236Sken	 * have succeeded
578265236Sken	 */
579265236Sken	temp_pagelist = malloc((dma->page_count + (count << page_order)) *
580265236Sken	    sizeof(*dma->pagelist), DRM_MEM_PAGES, M_NOWAIT);
581265236Sken
582265236Sken	if (entry->buflist == NULL || entry->seglist == NULL ||
583265236Sken	    temp_pagelist == NULL) {
584265236Sken		free(temp_pagelist, DRM_MEM_PAGES);
585265236Sken		free(entry->seglist, DRM_MEM_SEGS);
586265236Sken		free(entry->buflist, DRM_MEM_BUFS);
587265236Sken		return ENOMEM;
588265236Sken	}
589265236Sken
590265236Sken	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
591265236Sken	    sizeof(*dma->pagelist));
592265236Sken
593265236Sken	DRM_DEBUG("pagelist: %d entries\n",
594265236Sken	    dma->page_count + (count << page_order));
595265236Sken
596265236Sken	entry->buf_size	= size;
597265236Sken	entry->page_order = page_order;
598265236Sken	byte_count = 0;
599265236Sken	page_count = 0;
600265236Sken
601265236Sken	while (entry->buf_count < count) {
602265236Sken		DRM_SPINUNLOCK(&dev->dma_lock);
603265236Sken		drm_dma_handle_t *dmah = drm_pci_alloc(dev, size, alignment,
604265236Sken		    0xfffffffful);
605265236Sken		DRM_SPINLOCK(&dev->dma_lock);
606265236Sken		if (dmah == NULL) {
607265236Sken			/* Set count correctly so we free the proper amount. */
608265236Sken			entry->buf_count = count;
609265236Sken			entry->seg_count = count;
610265236Sken			drm_cleanup_buf_error(dev, entry);
611265236Sken			free(temp_pagelist, DRM_MEM_PAGES);
612265236Sken			return ENOMEM;
613265236Sken		}
614265236Sken
615265236Sken		entry->seglist[entry->seg_count++] = dmah;
616265236Sken		for (i = 0; i < (1 << page_order); i++) {
617265236Sken			DRM_DEBUG("page %d @ %p\n",
618265236Sken			    dma->page_count + page_count,
619265236Sken			    (char *)dmah->vaddr + PAGE_SIZE * i);
620265236Sken			temp_pagelist[dma->page_count + page_count++] =
621265236Sken			    (long)dmah->vaddr + PAGE_SIZE * i;
622265236Sken		}
623265236Sken		for (offset = 0;
624265236Sken		    offset + size <= total && entry->buf_count < count;
625265236Sken		    offset += alignment, ++entry->buf_count) {
626265236Sken			buf	     = &entry->buflist[entry->buf_count];
627265236Sken			buf->idx     = dma->buf_count + entry->buf_count;
628265236Sken			buf->total   = alignment;
629265236Sken			buf->order   = order;
630265236Sken			buf->used    = 0;
631265236Sken			buf->offset  = (dma->byte_count + byte_count + offset);
632265236Sken			buf->address = ((char *)dmah->vaddr + offset);
633265236Sken			buf->bus_address = dmah->busaddr + offset;
634265236Sken			buf->next    = NULL;
635265236Sken			buf->pending = 0;
636265236Sken			buf->file_priv = NULL;
637265236Sken
638265236Sken			buf->dev_priv_size = dev->driver->buf_priv_size;
639265236Sken			buf->dev_private = malloc(buf->dev_priv_size,
640265236Sken			    DRM_MEM_BUFS, M_NOWAIT | M_ZERO);
641265236Sken			if (buf->dev_private == NULL) {
642265236Sken				/* Set count correctly so we free the proper amount. */
643265236Sken				entry->buf_count = count;
644265236Sken				entry->seg_count = count;
645265236Sken				drm_cleanup_buf_error(dev, entry);
646265236Sken				free(temp_pagelist, DRM_MEM_PAGES);
647265236Sken				return ENOMEM;
648265236Sken			}
649265236Sken
650265236Sken			DRM_DEBUG("buffer %d @ %p\n",
651265236Sken			    entry->buf_count, buf->address);
652265236Sken		}
653265236Sken		byte_count += PAGE_SIZE << page_order;
654265236Sken	}
655265236Sken
656265236Sken	temp_buflist = realloc(dma->buflist,
657265236Sken	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
658265236Sken	    DRM_MEM_BUFS, M_NOWAIT);
659265236Sken	if (temp_buflist == NULL) {
660265236Sken		/* Free the entry because it isn't valid */
661265236Sken		drm_cleanup_buf_error(dev, entry);
662265236Sken		free(temp_pagelist, DRM_MEM_PAGES);
663265236Sken		return ENOMEM;
664265236Sken	}
665265236Sken	dma->buflist = temp_buflist;
666265236Sken
667265236Sken	for (i = 0; i < entry->buf_count; i++) {
668265236Sken		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
669265236Sken	}
670265236Sken
671265236Sken	/* No allocations failed, so now we can replace the orginal pagelist
672265236Sken	 * with the new one.
673265236Sken	 */
674265236Sken	free(dma->pagelist, DRM_MEM_PAGES);
675265236Sken	dma->pagelist = temp_pagelist;
676265236Sken
677265236Sken	dma->buf_count += entry->buf_count;
678265236Sken	dma->seg_count += entry->seg_count;
679265236Sken	dma->page_count += entry->seg_count << page_order;
680265236Sken	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
681265236Sken
682265236Sken	request->count = entry->buf_count;
683265236Sken	request->size = size;
684283990Sslm
685265236Sken	return 0;
686265236Sken
687283990Sslm}
688265236Sken
689283990Sslmstatic int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
690283990Sslm{
691265236Sken	drm_device_dma_t *dma = dev->dma;
692265236Sken	drm_buf_entry_t *entry;
693265236Sken	drm_buf_t *buf;
694265236Sken	unsigned long offset;
695265236Sken	unsigned long agp_offset;
696265236Sken	int count;
697265236Sken	int order;
698265236Sken	int size;
699265236Sken	int alignment;
700265236Sken	int page_order;
701265236Sken	int total;
702265236Sken	int byte_count;
703265236Sken	int i;
704265236Sken	drm_buf_t **temp_buflist;
705265236Sken
706265236Sken	count = request->count;
707265236Sken	order = drm_order(request->size);
708265236Sken	size = 1 << order;
709265236Sken
710265236Sken	alignment  = (request->flags & _DRM_PAGE_ALIGN)
711265236Sken	    ? round_page(size) : size;
712265236Sken	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
713265236Sken	total = PAGE_SIZE << page_order;
714265236Sken
715265236Sken	byte_count = 0;
716265236Sken	agp_offset = request->agp_start;
717265236Sken
718265236Sken	DRM_DEBUG("count:      %d\n",  count);
719265236Sken	DRM_DEBUG("order:      %d\n",  order);
720265236Sken	DRM_DEBUG("size:       %d\n",  size);
721265236Sken	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
722283990Sslm	DRM_DEBUG("alignment:  %d\n",  alignment);
723283990Sslm	DRM_DEBUG("page_order: %d\n",  page_order);
724283990Sslm	DRM_DEBUG("total:      %d\n",  total);
725265236Sken
726283990Sslm	entry = &dma->bufs[order];
727283990Sslm
728283990Sslm	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
729283990Sslm	    M_NOWAIT | M_ZERO);
730283990Sslm	if (entry->buflist == NULL)
731283990Sslm		return ENOMEM;
732283990Sslm
733283990Sslm	entry->buf_size = size;
734283990Sslm	entry->page_order = page_order;
735283990Sslm
736283990Sslm	offset = 0;
737283990Sslm
738283990Sslm	while (entry->buf_count < count) {
739283990Sslm		buf          = &entry->buflist[entry->buf_count];
740283990Sslm		buf->idx     = dma->buf_count + entry->buf_count;
741283990Sslm		buf->total   = alignment;
742283990Sslm		buf->order   = order;
743265236Sken		buf->used    = 0;
744283990Sslm
745265236Sken		buf->offset  = (dma->byte_count + offset);
746265236Sken		buf->bus_address = agp_offset + offset;
747265236Sken		buf->address = (void *)(agp_offset + offset + dev->sg->handle);
748265236Sken		buf->next    = NULL;
749265236Sken		buf->pending = 0;
750265236Sken		buf->file_priv = NULL;
751265236Sken
752265236Sken		buf->dev_priv_size = dev->driver->buf_priv_size;
753265236Sken		buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
754265236Sken		    M_NOWAIT | M_ZERO);
755265236Sken		if (buf->dev_private == NULL) {
756265236Sken			/* Set count correctly so we free the proper amount. */
757265236Sken			entry->buf_count = count;
758283990Sslm			drm_cleanup_buf_error(dev, entry);
759265236Sken			return ENOMEM;
760265236Sken		}
761265236Sken
762265236Sken		DRM_DEBUG("buffer %d @ %p\n",
763265236Sken		    entry->buf_count, buf->address);
764265236Sken
765265236Sken		offset += alignment;
766265236Sken		entry->buf_count++;
767265236Sken		byte_count += PAGE_SIZE << page_order;
768265236Sken	}
769265236Sken
770265236Sken	DRM_DEBUG("byte_count: %d\n", byte_count);
771265236Sken
772265236Sken	temp_buflist = realloc(dma->buflist,
773265236Sken	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
774265236Sken	    DRM_MEM_BUFS, M_NOWAIT);
775265236Sken	if (temp_buflist == NULL) {
776265236Sken		/* Free the entry because it isn't valid */
777265236Sken		drm_cleanup_buf_error(dev, entry);
778265236Sken		return ENOMEM;
779265236Sken	}
780265236Sken	dma->buflist = temp_buflist;
781283990Sslm
782283990Sslm	for (i = 0; i < entry->buf_count; i++) {
783283990Sslm		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
784265236Sken	}
785265236Sken
786265236Sken	dma->buf_count += entry->buf_count;
787265236Sken	dma->byte_count += byte_count;
788265236Sken
789265236Sken	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
790265236Sken	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
791265236Sken
792265236Sken	request->count = entry->buf_count;
793265236Sken	request->size = size;
794265236Sken
795265236Sken	dma->flags = _DRM_DMA_USE_SG;
796265236Sken
797265236Sken	return 0;
798265236Sken}
799265236Sken
800265236Skenint drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
801265236Sken{
802265236Sken	int order, ret;
803283990Sslm
804265236Sken	if (request->count < 0 || request->count > 4096)
805265236Sken		return EINVAL;
806265236Sken
807265236Sken	order = drm_order(request->size);
808283990Sslm	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
809265236Sken		return EINVAL;
810265236Sken
811265236Sken	DRM_SPINLOCK(&dev->dma_lock);
812266548Sken
813266548Sken	/* No more allocations after first buffer-using ioctl. */
814265236Sken	if (dev->buf_use != 0) {
815265236Sken		DRM_SPINUNLOCK(&dev->dma_lock);
816265236Sken		return EBUSY;
817265236Sken	}
818283990Sslm	/* No more than one allocation per order */
819283990Sslm	if (dev->dma->bufs[order].buf_count != 0) {
820283990Sslm		DRM_SPINUNLOCK(&dev->dma_lock);
821283990Sslm		return ENOMEM;
822283990Sslm	}
823283990Sslm
824283990Sslm	ret = drm_do_addbufs_agp(dev, request);
825283990Sslm
826283990Sslm	DRM_SPINUNLOCK(&dev->dma_lock);
827283990Sslm
828283990Sslm	return ret;
829283990Sslm}
830283990Sslm
831283990Sslmint drm_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
832283990Sslm{
833283990Sslm	int order, ret;
834283990Sslm
835283990Sslm	if (!DRM_SUSER(DRM_CURPROC))
836283990Sslm		return EACCES;
837283990Sslm
838283990Sslm	if (request->count < 0 || request->count > 4096)
839283990Sslm		return EINVAL;
840283990Sslm
841283990Sslm	order = drm_order(request->size);
842283990Sslm	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
843283990Sslm		return EINVAL;
844283990Sslm
845283990Sslm	DRM_SPINLOCK(&dev->dma_lock);
846283990Sslm
847283990Sslm	/* No more allocations after first buffer-using ioctl. */
848283990Sslm	if (dev->buf_use != 0) {
849283990Sslm		DRM_SPINUNLOCK(&dev->dma_lock);
850283990Sslm		return EBUSY;
851283990Sslm	}
852265236Sken	/* No more than one allocation per order */
853283990Sslm	if (dev->dma->bufs[order].buf_count != 0) {
854283990Sslm		DRM_SPINUNLOCK(&dev->dma_lock);
855283990Sslm		return ENOMEM;
856283990Sslm	}
857283990Sslm
858283990Sslm	ret = drm_do_addbufs_sg(dev, request);
859283990Sslm
860283990Sslm	DRM_SPINUNLOCK(&dev->dma_lock);
861283990Sslm
862265236Sken	return ret;
863265236Sken}
864265236Sken
865265236Skenint drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
866265236Sken{
867265236Sken	int order, ret;
868283990Sslm
869265236Sken	if (!DRM_SUSER(DRM_CURPROC))
870265236Sken		return EACCES;
871265236Sken
872265236Sken	if (request->count < 0 || request->count > 4096)
873265236Sken		return EINVAL;
874265236Sken
875265236Sken	order = drm_order(request->size);
876265236Sken	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
877265236Sken		return EINVAL;
878265236Sken
879265236Sken	DRM_SPINLOCK(&dev->dma_lock);
880265236Sken
881265236Sken	/* No more allocations after first buffer-using ioctl. */
882265236Sken	if (dev->buf_use != 0) {
883265236Sken		DRM_SPINUNLOCK(&dev->dma_lock);
884265236Sken		return EBUSY;
885265236Sken	}
886265236Sken	/* No more than one allocation per order */
887265236Sken	if (dev->dma->bufs[order].buf_count != 0) {
888283990Sslm		DRM_SPINUNLOCK(&dev->dma_lock);
889283990Sslm		return ENOMEM;
890283990Sslm	}
891283990Sslm
892283990Sslm	ret = drm_do_addbufs_pci(dev, request);
893283990Sslm
894283990Sslm	DRM_SPINUNLOCK(&dev->dma_lock);
895283990Sslm
896283990Sslm	return ret;
897283990Sslm}
898265236Sken
899265236Skenint drm_addbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
900265236Sken{
901265236Sken	struct drm_buf_desc *request = data;
902265236Sken	int err;
903265236Sken
904265236Sken	if (request->flags & _DRM_AGP_BUFFER)
905265236Sken		err = drm_addbufs_agp(dev, request);
906265236Sken	else if (request->flags & _DRM_SG_BUFFER)
907265236Sken		err = drm_addbufs_sg(dev, request);
908265236Sken	else
909265236Sken		err = drm_addbufs_pci(dev, request);
910265236Sken
911265236Sken	return err;
912265236Sken}
913265236Sken
914265236Skenint drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
915265236Sken{
916265236Sken	drm_device_dma_t *dma = dev->dma;
917265236Sken	struct drm_buf_info *request = data;
918265236Sken	int i;
919265236Sken	int count;
920265236Sken	int retcode = 0;
921265236Sken
922265236Sken	DRM_SPINLOCK(&dev->dma_lock);
923265236Sken	++dev->buf_use;		/* Can't allocate more after this call */
924265236Sken	DRM_SPINUNLOCK(&dev->dma_lock);
925265236Sken
926265236Sken	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
927265236Sken		if (dma->bufs[i].buf_count)
928265236Sken			++count;
929265236Sken	}
930265236Sken
931265236Sken	DRM_DEBUG("count = %d\n", count);
932265236Sken
933265236Sken	if (request->count >= count) {
934265236Sken		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
935265236Sken			if (dma->bufs[i].buf_count) {
936265236Sken				struct drm_buf_desc from;
937265236Sken
938265236Sken				from.count = dma->bufs[i].buf_count;
939265236Sken				from.size = dma->bufs[i].buf_size;
940265236Sken				from.low_mark = dma->bufs[i].freelist.low_mark;
941265236Sken				from.high_mark = dma->bufs[i].freelist.high_mark;
942265236Sken
943265236Sken				if (DRM_COPY_TO_USER(&request->list[count], &from,
944265236Sken				    sizeof(struct drm_buf_desc)) != 0) {
945265236Sken					retcode = EFAULT;
946283990Sslm					break;
947283990Sslm				}
948283990Sslm
949283990Sslm				DRM_DEBUG("%d %d %d %d %d\n",
950265236Sken				    i, dma->bufs[i].buf_count,
951265236Sken				    dma->bufs[i].buf_size,
952265236Sken				    dma->bufs[i].freelist.low_mark,
953265236Sken				    dma->bufs[i].freelist.high_mark);
954265236Sken				++count;
955265236Sken			}
956265236Sken		}
957265236Sken	}
958265236Sken	request->count = count;
959265236Sken
960265236Sken	return retcode;
961265236Sken}
962265236Sken
963265236Skenint drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
964265236Sken{
965265236Sken	drm_device_dma_t *dma = dev->dma;
966265236Sken	struct drm_buf_desc *request = data;
967265236Sken	int order;
968265236Sken
969265236Sken	DRM_DEBUG("%d, %d, %d\n",
970265236Sken		  request->size, request->low_mark, request->high_mark);
971265236Sken
972265236Sken
973265236Sken	order = drm_order(request->size);
974265236Sken	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
975265236Sken	    request->low_mark < 0 || request->high_mark < 0) {
976265236Sken		return EINVAL;
977265236Sken	}
978265236Sken
979265236Sken	DRM_SPINLOCK(&dev->dma_lock);
980265236Sken	if (request->low_mark > dma->bufs[order].buf_count ||
981265236Sken	    request->high_mark > dma->bufs[order].buf_count) {
982265236Sken		DRM_SPINUNLOCK(&dev->dma_lock);
983265236Sken		return EINVAL;
984265236Sken	}
985265236Sken
986265236Sken	dma->bufs[order].freelist.low_mark  = request->low_mark;
987265236Sken	dma->bufs[order].freelist.high_mark = request->high_mark;
988265236Sken	DRM_SPINUNLOCK(&dev->dma_lock);
989283990Sslm
990283990Sslm	return 0;
991283990Sslm}
992283990Sslm
993283990Sslmint drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
994283990Sslm{
995283990Sslm	drm_device_dma_t *dma = dev->dma;
996283990Sslm	struct drm_buf_free *request = data;
997283990Sslm	int i;
998283990Sslm	int idx;
999283990Sslm	drm_buf_t *buf;
1000265236Sken	int retcode = 0;
1001283990Sslm
1002283990Sslm	DRM_DEBUG("%d\n", request->count);
1003283990Sslm
1004283990Sslm	DRM_SPINLOCK(&dev->dma_lock);
1005265236Sken	for (i = 0; i < request->count; i++) {
1006265236Sken		if (DRM_COPY_FROM_USER(&idx, &request->list[i], sizeof(idx))) {
1007265236Sken			retcode = EFAULT;
1008265236Sken			break;
1009265236Sken		}
1010265236Sken		if (idx < 0 || idx >= dma->buf_count) {
1011283990Sslm			DRM_ERROR("Index %d (of %d max)\n",
1012265236Sken			    idx, dma->buf_count - 1);
1013265236Sken			retcode = EINVAL;
1014265236Sken			break;
1015265236Sken		}
1016265236Sken		buf = dma->buflist[idx];
1017265236Sken		if (buf->file_priv != file_priv) {
1018265236Sken			DRM_ERROR("Process %d freeing buffer not owned\n",
1019265236Sken			    DRM_CURRENTPID);
1020265236Sken			retcode = EINVAL;
1021265236Sken			break;
1022265236Sken		}
1023265236Sken		drm_free_buffer(dev, buf);
1024265236Sken	}
1025265236Sken	DRM_SPINUNLOCK(&dev->dma_lock);
1026265236Sken
1027283990Sslm	return retcode;
1028283990Sslm}
1029283990Sslm
1030283990Sslmint drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
1031283990Sslm{
1032283990Sslm	drm_device_dma_t *dma = dev->dma;
1033283990Sslm	int retcode = 0;
1034283990Sslm	const int zero = 0;
1035283990Sslm	vm_offset_t address;
1036283990Sslm	struct vmspace *vms;
1037265236Sken	vm_ooffset_t foff;
1038265236Sken	vm_size_t size;
1039265236Sken	vm_offset_t vaddr;
1040283990Sslm	struct drm_buf_map *request = data;
1041283990Sslm	int i;
1042283990Sslm
1043283990Sslm	vms = DRM_CURPROC->td_proc->p_vmspace;
1044283990Sslm
1045283990Sslm	DRM_SPINLOCK(&dev->dma_lock);
1046283990Sslm	dev->buf_use++;		/* Can't allocate more after this call */
1047283990Sslm	DRM_SPINUNLOCK(&dev->dma_lock);
1048283990Sslm
1049283990Sslm	if (request->count < dma->buf_count)
1050283990Sslm		goto done;
1051283990Sslm
1052283990Sslm	if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) ||
1053283990Sslm	    (drm_core_check_feature(dev, DRIVER_SG) &&
1054283990Sslm	    (dma->flags & _DRM_DMA_USE_SG))) {
1055283990Sslm		drm_local_map_t *map = dev->agp_buffer_map;
1056283990Sslm
1057283990Sslm		if (map == NULL) {
1058283990Sslm			retcode = EINVAL;
1059283990Sslm			goto done;
1060283990Sslm		}
1061283990Sslm		size = round_page(map->size);
1062283990Sslm		foff = (unsigned long)map->handle;
1063283990Sslm	} else {
1064283990Sslm		size = round_page(dma->byte_count),
1065283990Sslm		foff = 0;
1066283990Sslm	}
1067283990Sslm
1068283990Sslm	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
1069283990Sslm#if __FreeBSD_version >= 600023
1070283990Sslm	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1071283990Sslm	    VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC, OBJT_DEVICE,
1072283990Sslm	    dev->devnode, foff);
1073283990Sslm#else
1074283990Sslm	retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1075283990Sslm	    VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC,
1076283990Sslm	    SLIST_FIRST(&dev->devnode->si_hlist), foff);
1077283990Sslm#endif
1078283990Sslm	if (retcode)
1079283990Sslm		goto done;
1080283990Sslm
1081283990Sslm	request->virtual = (void *)vaddr;
1082283990Sslm
1083283990Sslm	for (i = 0; i < dma->buf_count; i++) {
1084283990Sslm		if (DRM_COPY_TO_USER(&request->list[i].idx,
1085283990Sslm		    &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
1086283990Sslm			retcode = EFAULT;
1087265236Sken			goto done;
1088265236Sken		}
1089265236Sken		if (DRM_COPY_TO_USER(&request->list[i].total,
1090265236Sken		    &dma->buflist[i]->total, sizeof(request->list[0].total))) {
1091265236Sken			retcode = EFAULT;
1092265236Sken			goto done;
1093265236Sken		}
1094265236Sken		if (DRM_COPY_TO_USER(&request->list[i].used, &zero,
1095265236Sken		    sizeof(zero))) {
1096265236Sken			retcode = EFAULT;
1097265236Sken			goto done;
1098265236Sken		}
1099265236Sken		address = vaddr + dma->buflist[i]->offset; /* *** */
1100265236Sken		if (DRM_COPY_TO_USER(&request->list[i].address, &address,
1101265236Sken		    sizeof(address))) {
1102265236Sken			retcode = EFAULT;
1103265236Sken			goto done;
1104265236Sken		}
1105265236Sken	}
1106265236Sken
1107265236Sken done:
1108265236Sken	request->count = dma->buf_count;
1109265236Sken
1110265236Sken	DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
1111265236Sken
1112265236Sken	return retcode;
1113265236Sken}
1114265236Sken
1115265236Sken/*
1116265236Sken * Compute order.  Can be made faster.
1117265236Sken */
1118265236Skenint drm_order(unsigned long size)
1119265236Sken{
1120265236Sken	int order;
1121265236Sken
1122265236Sken	if (size == 0)
1123265236Sken		return 0;
1124265236Sken
1125265236Sken	order = flsl(size) - 1;
1126265236Sken	if (size & ~(1ul << order))
1127265236Sken		++order;
1128265236Sken
1129266548Sken	return order;
1130266548Sken}
1131265236Sken