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: stable/11/sys/dev/drm/drm_bufs.c 331411 2018-03-23 02:37:08Z emaste $");
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
42152909Sanholt/* Allocation of PCI memory resources (framebuffer, registers, etc.) for
43152909Sanholt * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
44152909Sanholt * address for accessing them.  Cleaned up at unload.
45152909Sanholt */
46182080Srnolandstatic int drm_alloc_resource(struct drm_device *dev, int resource)
47145132Sanholt{
48196464Srnoland	struct resource *res;
49196464Srnoland	int rid;
50196464Srnoland
51196464Srnoland	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
52196464Srnoland
53152909Sanholt	if (resource >= DRM_MAX_PCI_RESOURCE) {
54152909Sanholt		DRM_ERROR("Resource %d too large\n", resource);
55152909Sanholt		return 1;
56152909Sanholt	}
57145132Sanholt
58152909Sanholt	if (dev->pcir[resource] != NULL) {
59145132Sanholt		return 0;
60145132Sanholt	}
61145132Sanholt
62196464Srnoland	DRM_UNLOCK();
63196464Srnoland	rid = PCIR_BAR(resource);
64196464Srnoland	res = bus_alloc_resource_any(dev->device, SYS_RES_MEMORY, &rid,
65196464Srnoland	    RF_SHAREABLE);
66152909Sanholt	DRM_LOCK();
67196464Srnoland	if (res == NULL) {
68152909Sanholt		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
69152909Sanholt		return 1;
70152909Sanholt	}
71145132Sanholt
72196464Srnoland	if (dev->pcir[resource] == NULL) {
73196464Srnoland		dev->pcirid[resource] = rid;
74196464Srnoland		dev->pcir[resource] = res;
75196464Srnoland	}
76196464Srnoland
77152909Sanholt	return 0;
78145132Sanholt}
79145132Sanholt
80182080Srnolandunsigned long drm_get_resource_start(struct drm_device *dev,
81182080Srnoland				     unsigned int resource)
82145132Sanholt{
83152909Sanholt	if (drm_alloc_resource(dev, resource) != 0)
84152909Sanholt		return 0;
85145132Sanholt
86152909Sanholt	return rman_get_start(dev->pcir[resource]);
87145132Sanholt}
88145132Sanholt
89182080Srnolandunsigned long drm_get_resource_len(struct drm_device *dev,
90182080Srnoland				   unsigned int resource)
91145132Sanholt{
92152909Sanholt	if (drm_alloc_resource(dev, resource) != 0)
93152909Sanholt		return 0;
94145132Sanholt
95152909Sanholt	return rman_get_size(dev->pcir[resource]);
96145132Sanholt}
97145132Sanholt
98182080Srnolandint drm_addmap(struct drm_device * dev, unsigned long offset,
99182080Srnoland	       unsigned long size,
100183573Srnoland    enum drm_map_type type, enum drm_map_flags flags, drm_local_map_t **map_ptr)
101145132Sanholt{
102145132Sanholt	drm_local_map_t *map;
103152909Sanholt	int align;
104152909Sanholt	/*drm_agp_mem_t *entry;
105152909Sanholt	int valid;*/
106145132Sanholt
107145132Sanholt	/* Only allow shared memory to be removable since we only keep enough
108145132Sanholt	 * book keeping information about shared memory to allow for removal
109145132Sanholt	 * when processes fork.
110145132Sanholt	 */
111152909Sanholt	if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
112152909Sanholt		DRM_ERROR("Requested removable map for non-DRM_SHM\n");
113145132Sanholt		return EINVAL;
114152909Sanholt	}
115152909Sanholt	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
116152909Sanholt		DRM_ERROR("offset/size not page aligned: 0x%lx/0x%lx\n",
117152909Sanholt		    offset, size);
118145132Sanholt		return EINVAL;
119152909Sanholt	}
120152909Sanholt	if (offset + size < offset) {
121152909Sanholt		DRM_ERROR("offset and size wrap around: 0x%lx/0x%lx\n",
122152909Sanholt		    offset, size);
123145132Sanholt		return EINVAL;
124152909Sanholt	}
125145132Sanholt
126152909Sanholt	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", offset,
127152909Sanholt	    size, type);
128145132Sanholt
129145132Sanholt	/* Check if this is just another version of a kernel-allocated map, and
130145132Sanholt	 * just hand that back if so.
131145132Sanholt	 */
132152909Sanholt	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
133152909Sanholt	    type == _DRM_SHM) {
134145132Sanholt		TAILQ_FOREACH(map, &dev->maplist, link) {
135152909Sanholt			if (map->type == type && (map->offset == offset ||
136152909Sanholt			    (map->type == _DRM_SHM &&
137152909Sanholt			    map->flags == _DRM_CONTAINS_LOCK))) {
138152909Sanholt				map->size = size;
139152909Sanholt				DRM_DEBUG("Found kernel map %d\n", type);
140145132Sanholt				goto done;
141145132Sanholt			}
142145132Sanholt		}
143145132Sanholt	}
144152909Sanholt	DRM_UNLOCK();
145145132Sanholt
146145132Sanholt	/* Allocate a new map structure, fill it in, and do any type-specific
147145132Sanholt	 * initialization necessary.
148145132Sanholt	 */
149183833Srnoland	map = malloc(sizeof(*map), DRM_MEM_MAPS, M_ZERO | M_NOWAIT);
150183573Srnoland	if (!map) {
151182080Srnoland		DRM_LOCK();
152182080Srnoland		return ENOMEM;
153182080Srnoland	}
154145132Sanholt
155152909Sanholt	map->offset = offset;
156152909Sanholt	map->size = size;
157152909Sanholt	map->type = type;
158152909Sanholt	map->flags = flags;
159207066Srnoland	map->handle = (void *)((unsigned long)alloc_unr(dev->map_unrhdr) <<
160207066Srnoland	    DRM_MAP_HANDLE_SHIFT);
161145132Sanholt
162183573Srnoland	switch (map->type) {
163145132Sanholt	case _DRM_REGISTERS:
164207066Srnoland		map->virtual = 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:
173207066Srnoland		map->virtual = malloc(map->size, DRM_MEM_MAPS, M_NOWAIT);
174183573Srnoland		DRM_DEBUG("%lu %d %p\n",
175207066Srnoland		    map->size, drm_order(map->size), map->virtual);
176207066Srnoland		if (!map->virtual) {
177183833Srnoland			free(map, DRM_MEM_MAPS);
178182080Srnoland			DRM_LOCK();
179182080Srnoland			return ENOMEM;
180145132Sanholt		}
181207066Srnoland		map->offset = (unsigned long)map->virtual;
182183573Srnoland		if (map->flags & _DRM_CONTAINS_LOCK) {
183145132Sanholt			/* Prevent a 2nd X Server from creating a 2nd lock */
184145132Sanholt			DRM_LOCK();
185145132Sanholt			if (dev->lock.hw_lock != NULL) {
186145132Sanholt				DRM_UNLOCK();
187207066Srnoland				free(map->virtual, DRM_MEM_MAPS);
188183833Srnoland				free(map, DRM_MEM_MAPS);
189182080Srnoland				return EBUSY;
190145132Sanholt			}
191207066Srnoland			dev->lock.hw_lock = map->virtual; /* Pointer to lock */
192145132Sanholt			DRM_UNLOCK();
193145132Sanholt		}
194145132Sanholt		break;
195145132Sanholt	case _DRM_AGP:
196152909Sanholt		/*valid = 0;*/
197182080Srnoland		/* In some cases (i810 driver), user space may have already
198182080Srnoland		 * added the AGP base itself, because dev->agp->base previously
199182080Srnoland		 * only got set during AGP enable.  So, only add the base
200182080Srnoland		 * address if the map's offset isn't already within the
201182080Srnoland		 * aperture.
202182080Srnoland		 */
203182080Srnoland		if (map->offset < dev->agp->base ||
204182080Srnoland		    map->offset > dev->agp->base +
205182080Srnoland		    dev->agp->info.ai_aperture_size - 1) {
206182080Srnoland			map->offset += dev->agp->base;
207182080Srnoland		}
208145132Sanholt		map->mtrr   = dev->agp->mtrr; /* for getmap */
209152909Sanholt		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
210152909Sanholt			if ((map->offset >= entry->bound) &&
211152909Sanholt			    (map->offset + map->size <=
212152909Sanholt			    entry->bound + entry->pages * PAGE_SIZE)) {
213152909Sanholt				valid = 1;
214152909Sanholt				break;
215152909Sanholt			}
216152909Sanholt		}
217152909Sanholt		if (!valid) {
218183833Srnoland			free(map, DRM_MEM_MAPS);
219182080Srnoland			DRM_LOCK();
220182080Srnoland			return EACCES;
221152909Sanholt		}*/
222145132Sanholt		break;
223145132Sanholt	case _DRM_SCATTER_GATHER:
224145132Sanholt		if (!dev->sg) {
225183833Srnoland			free(map, DRM_MEM_MAPS);
226182080Srnoland			DRM_LOCK();
227182080Srnoland			return EINVAL;
228145132Sanholt		}
229207067Srnoland		map->virtual = (void *)(dev->sg->vaddr + offset);
230207067Srnoland		map->offset = dev->sg->vaddr + offset;
231145132Sanholt		break;
232145132Sanholt	case _DRM_CONSISTENT:
233152909Sanholt		/* Unfortunately, we don't get any alignment specification from
234152909Sanholt		 * the caller, so we have to guess.  drm_pci_alloc requires
235152909Sanholt		 * a power-of-two alignment, so try to align the bus address of
236152909Sanholt		 * the map to it size if possible, otherwise just assume
237152909Sanholt		 * PAGE_SIZE alignment.
238152909Sanholt		 */
239152909Sanholt		align = map->size;
240152909Sanholt		if ((align & (align - 1)) != 0)
241152909Sanholt			align = PAGE_SIZE;
242152909Sanholt		map->dmah = drm_pci_alloc(dev, map->size, align, 0xfffffffful);
243152909Sanholt		if (map->dmah == NULL) {
244183833Srnoland			free(map, DRM_MEM_MAPS);
245182080Srnoland			DRM_LOCK();
246182080Srnoland			return ENOMEM;
247145132Sanholt		}
248207066Srnoland		map->virtual = map->dmah->vaddr;
249152909Sanholt		map->offset = map->dmah->busaddr;
250145132Sanholt		break;
251145132Sanholt	default:
252152909Sanholt		DRM_ERROR("Bad map type %d\n", map->type);
253183833Srnoland		free(map, DRM_MEM_MAPS);
254182080Srnoland		DRM_LOCK();
255182080Srnoland		return EINVAL;
256145132Sanholt	}
257145132Sanholt
258145132Sanholt	DRM_LOCK();
259145132Sanholt	TAILQ_INSERT_TAIL(&dev->maplist, map, link);
260145132Sanholt
261145132Sanholtdone:
262145132Sanholt	/* Jumped to, with lock held, when a kernel map is found. */
263152909Sanholt
264152909Sanholt	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
265152909Sanholt	    map->size);
266152909Sanholt
267152909Sanholt	*map_ptr = map;
268152909Sanholt
269152909Sanholt	return 0;
270152909Sanholt}
271152909Sanholt
272182080Srnolandint drm_addmap_ioctl(struct drm_device *dev, void *data,
273182080Srnoland		     struct drm_file *file_priv)
274152909Sanholt{
275183573Srnoland	struct drm_map *request = data;
276152909Sanholt	drm_local_map_t *map;
277152909Sanholt	int err;
278152909Sanholt
279152909Sanholt	if (!(dev->flags & (FREAD|FWRITE)))
280182080Srnoland		return EACCES; /* Require read/write */
281152909Sanholt
282182080Srnoland	if (!DRM_SUSER(DRM_CURPROC) && request->type != _DRM_AGP)
283182080Srnoland		return EACCES;
284152909Sanholt
285152909Sanholt	DRM_LOCK();
286182080Srnoland	err = drm_addmap(dev, request->offset, request->size, request->type,
287182080Srnoland	    request->flags, &map);
288152909Sanholt	DRM_UNLOCK();
289152909Sanholt	if (err != 0)
290152909Sanholt		return err;
291152909Sanholt
292182080Srnoland	request->offset = map->offset;
293182080Srnoland	request->size = map->size;
294182080Srnoland	request->type = map->type;
295182080Srnoland	request->flags = map->flags;
296182080Srnoland	request->mtrr   = map->mtrr;
297207066Srnoland	request->handle = (void *)map->handle;
298145132Sanholt
299145132Sanholt	return 0;
300145132Sanholt}
301145132Sanholt
302182080Srnolandvoid drm_rmmap(struct drm_device *dev, drm_local_map_t *map)
303145132Sanholt{
304145132Sanholt	DRM_SPINLOCK_ASSERT(&dev->dev_lock);
305145132Sanholt
306194537Srnoland	if (map == NULL)
307194537Srnoland		return;
308194537Srnoland
309145132Sanholt	TAILQ_REMOVE(&dev->maplist, map, link);
310145132Sanholt
311145132Sanholt	switch (map->type) {
312145132Sanholt	case _DRM_REGISTERS:
313145132Sanholt		if (map->bsr == NULL)
314145132Sanholt			drm_ioremapfree(map);
315145132Sanholt		/* FALLTHROUGH */
316145132Sanholt	case _DRM_FRAME_BUFFER:
317145132Sanholt		if (map->mtrr) {
318145132Sanholt			int __unused retcode;
319145132Sanholt
320152909Sanholt			retcode = drm_mtrr_del(0, map->offset, map->size,
321145132Sanholt			    DRM_MTRR_WC);
322145132Sanholt			DRM_DEBUG("mtrr_del = %d\n", retcode);
323145132Sanholt		}
324145132Sanholt		break;
325145132Sanholt	case _DRM_SHM:
326207066Srnoland		free(map->virtual, DRM_MEM_MAPS);
327145132Sanholt		break;
328145132Sanholt	case _DRM_AGP:
329145132Sanholt	case _DRM_SCATTER_GATHER:
330145132Sanholt		break;
331145132Sanholt	case _DRM_CONSISTENT:
332152909Sanholt		drm_pci_free(dev, map->dmah);
333145132Sanholt		break;
334182080Srnoland	default:
335182080Srnoland		DRM_ERROR("Bad map type %d\n", map->type);
336182080Srnoland		break;
337145132Sanholt	}
338145132Sanholt
339145132Sanholt	if (map->bsr != NULL) {
340145132Sanholt		bus_release_resource(dev->device, SYS_RES_MEMORY, map->rid,
341145132Sanholt		    map->bsr);
342145132Sanholt	}
343145132Sanholt
344207066Srnoland	DRM_UNLOCK();
345207066Srnoland	if (map->handle)
346207066Srnoland		free_unr(dev->map_unrhdr, (unsigned long)map->handle >>
347207066Srnoland		    DRM_MAP_HANDLE_SHIFT);
348207066Srnoland	DRM_LOCK();
349207066Srnoland
350183833Srnoland	free(map, DRM_MEM_MAPS);
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;
361183573Srnoland	struct drm_map *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		}
393183833Srnoland		free(entry->seglist, DRM_MEM_SEGS);
394145132Sanholt
395145132Sanholt		entry->seg_count = 0;
396145132Sanholt	}
397145132Sanholt
398145132Sanholt   	if (entry->buf_count) {
399145132Sanholt	   	for (i = 0; i < entry->buf_count; i++) {
400183833Srnoland			free(entry->buflist[i].dev_private, DRM_MEM_BUFS);
401145132Sanholt		}
402183833Srnoland		free(entry->buflist, DRM_MEM_BUFS);
403145132Sanholt
404145132Sanholt		entry->buf_count = 0;
405145132Sanholt	}
406145132Sanholt}
407145132Sanholt
408183573Srnolandstatic int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *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)
432183573Srnoland	    ? 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
439183573Srnoland	DRM_DEBUG("count:      %d\n",  count);
440183573Srnoland	DRM_DEBUG("order:      %d\n",  order);
441183573Srnoland	DRM_DEBUG("size:       %d\n",  size);
442183573Srnoland	DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset);
443183573Srnoland	DRM_DEBUG("alignment:  %d\n",  alignment);
444183573Srnoland	DRM_DEBUG("page_order: %d\n",  page_order);
445183573Srnoland	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
469183833Srnoland	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
470145132Sanholt	    M_NOWAIT | M_ZERO);
471183573Srnoland	if (!entry->buflist) {
472182080Srnoland		return ENOMEM;
473145132Sanholt	}
474145132Sanholt
475145132Sanholt	entry->buf_size = size;
476145132Sanholt	entry->page_order = page_order;
477145132Sanholt
478145132Sanholt	offset = 0;
479145132Sanholt
480183573Srnoland	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
494183573Srnoland		buf->dev_priv_size = dev->driver->buf_priv_size;
495183833Srnoland		buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
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
509183573Srnoland	DRM_DEBUG("byte_count: %d\n", byte_count);
510145132Sanholt
511145132Sanholt	temp_buflist = realloc(dma->buflist,
512183833Srnoland	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
513183833Srnoland	    DRM_MEM_BUFS, 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
521183573Srnoland	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
528183573Srnoland	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
529183573Srnoland	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
539183573Srnolandstatic int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *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
561183573Srnoland	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
562183573Srnoland	    request->count, request->size, size, order);
563145132Sanholt
564145132Sanholt	alignment = (request->flags & _DRM_PAGE_ALIGN)
565183573Srnoland	    ? 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
571183833Srnoland	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
572145132Sanholt	    M_NOWAIT | M_ZERO);
573183833Srnoland	entry->seglist = malloc(count * sizeof(*entry->seglist), DRM_MEM_SEGS,
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)) *
580183833Srnoland	    sizeof(*dma->pagelist), DRM_MEM_PAGES, M_NOWAIT);
581145132Sanholt
582145132Sanholt	if (entry->buflist == NULL || entry->seglist == NULL ||
583152909Sanholt	    temp_pagelist == NULL) {
584183833Srnoland		free(temp_pagelist, DRM_MEM_PAGES);
585183833Srnoland		free(entry->seglist, DRM_MEM_SEGS);
586183833Srnoland		free(entry->buflist, DRM_MEM_BUFS);
587182080Srnoland		return ENOMEM;
588145132Sanholt	}
589183573Srnoland
590145132Sanholt	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
591145132Sanholt	    sizeof(*dma->pagelist));
592145132Sanholt
593183573Srnoland	DRM_DEBUG("pagelist: %d entries\n",
594183573Srnoland	    dma->page_count + (count << page_order));
595145132Sanholt
596145132Sanholt	entry->buf_size	= size;
597145132Sanholt	entry->page_order = page_order;
598145132Sanholt	byte_count = 0;
599145132Sanholt	page_count = 0;
600145132Sanholt
601183573Srnoland	while (entry->buf_count < count) {
602182883Srnoland		DRM_SPINUNLOCK(&dev->dma_lock);
603152909Sanholt		drm_dma_handle_t *dmah = drm_pci_alloc(dev, size, alignment,
604152909Sanholt		    0xfffffffful);
605182883Srnoland		DRM_SPINLOCK(&dev->dma_lock);
606152909Sanholt		if (dmah == NULL) {
607145132Sanholt			/* Set count correctly so we free the proper amount. */
608145132Sanholt			entry->buf_count = count;
609145132Sanholt			entry->seg_count = count;
610145132Sanholt			drm_cleanup_buf_error(dev, entry);
611183833Srnoland			free(temp_pagelist, DRM_MEM_PAGES);
612182080Srnoland			return ENOMEM;
613145132Sanholt		}
614152909Sanholt
615152909Sanholt		entry->seglist[entry->seg_count++] = dmah;
616183573Srnoland		for (i = 0; i < (1 << page_order); i++) {
617183573Srnoland			DRM_DEBUG("page %d @ %p\n",
618183573Srnoland			    dma->page_count + page_count,
619183573Srnoland			    (char *)dmah->vaddr + PAGE_SIZE * i);
620145132Sanholt			temp_pagelist[dma->page_count + page_count++] =
621152909Sanholt			    (long)dmah->vaddr + PAGE_SIZE * i;
622145132Sanholt		}
623183573Srnoland		for (offset = 0;
624183573Srnoland		    offset + size <= total && entry->buf_count < count;
625183573Srnoland		    offset += alignment, ++entry->buf_count) {
626145132Sanholt			buf	     = &entry->buflist[entry->buf_count];
627145132Sanholt			buf->idx     = dma->buf_count + entry->buf_count;
628145132Sanholt			buf->total   = alignment;
629145132Sanholt			buf->order   = order;
630145132Sanholt			buf->used    = 0;
631145132Sanholt			buf->offset  = (dma->byte_count + byte_count + offset);
632152909Sanholt			buf->address = ((char *)dmah->vaddr + offset);
633152909Sanholt			buf->bus_address = dmah->busaddr + offset;
634145132Sanholt			buf->next    = NULL;
635145132Sanholt			buf->pending = 0;
636182080Srnoland			buf->file_priv = NULL;
637145132Sanholt
638183573Srnoland			buf->dev_priv_size = dev->driver->buf_priv_size;
639183833Srnoland			buf->dev_private = malloc(buf->dev_priv_size,
640183833Srnoland			    DRM_MEM_BUFS, M_NOWAIT | M_ZERO);
641145132Sanholt			if (buf->dev_private == NULL) {
642145132Sanholt				/* Set count correctly so we free the proper amount. */
643145132Sanholt				entry->buf_count = count;
644145132Sanholt				entry->seg_count = count;
645145132Sanholt				drm_cleanup_buf_error(dev, entry);
646183833Srnoland				free(temp_pagelist, DRM_MEM_PAGES);
647182080Srnoland				return ENOMEM;
648145132Sanholt			}
649145132Sanholt
650183573Srnoland			DRM_DEBUG("buffer %d @ %p\n",
651183573Srnoland			    entry->buf_count, buf->address);
652145132Sanholt		}
653145132Sanholt		byte_count += PAGE_SIZE << page_order;
654145132Sanholt	}
655145132Sanholt
656145132Sanholt	temp_buflist = realloc(dma->buflist,
657183833Srnoland	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
658183833Srnoland	    DRM_MEM_BUFS, M_NOWAIT);
659145132Sanholt	if (temp_buflist == NULL) {
660145132Sanholt		/* Free the entry because it isn't valid */
661145132Sanholt		drm_cleanup_buf_error(dev, entry);
662183833Srnoland		free(temp_pagelist, DRM_MEM_PAGES);
663182080Srnoland		return ENOMEM;
664145132Sanholt	}
665145132Sanholt	dma->buflist = temp_buflist;
666145132Sanholt
667183573Srnoland	for (i = 0; i < entry->buf_count; i++) {
668145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
669145132Sanholt	}
670145132Sanholt
671298955Spfg	/* No allocations failed, so now we can replace the original pagelist
672145132Sanholt	 * with the new one.
673145132Sanholt	 */
674183833Srnoland	free(dma->pagelist, DRM_MEM_PAGES);
675145132Sanholt	dma->pagelist = temp_pagelist;
676145132Sanholt
677145132Sanholt	dma->buf_count += entry->buf_count;
678145132Sanholt	dma->seg_count += entry->seg_count;
679145132Sanholt	dma->page_count += entry->seg_count << page_order;
680145132Sanholt	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
681145132Sanholt
682145132Sanholt	request->count = entry->buf_count;
683145132Sanholt	request->size = size;
684145132Sanholt
685145132Sanholt	return 0;
686145132Sanholt
687145132Sanholt}
688145132Sanholt
689183573Srnolandstatic int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
690145132Sanholt{
691145132Sanholt	drm_device_dma_t *dma = dev->dma;
692145132Sanholt	drm_buf_entry_t *entry;
693145132Sanholt	drm_buf_t *buf;
694145132Sanholt	unsigned long offset;
695145132Sanholt	unsigned long agp_offset;
696145132Sanholt	int count;
697145132Sanholt	int order;
698145132Sanholt	int size;
699145132Sanholt	int alignment;
700145132Sanholt	int page_order;
701145132Sanholt	int total;
702145132Sanholt	int byte_count;
703145132Sanholt	int i;
704145132Sanholt	drm_buf_t **temp_buflist;
705145132Sanholt
706145132Sanholt	count = request->count;
707145132Sanholt	order = drm_order(request->size);
708145132Sanholt	size = 1 << order;
709145132Sanholt
710145132Sanholt	alignment  = (request->flags & _DRM_PAGE_ALIGN)
711183573Srnoland	    ? round_page(size) : size;
712145132Sanholt	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
713145132Sanholt	total = PAGE_SIZE << page_order;
714145132Sanholt
715145132Sanholt	byte_count = 0;
716145132Sanholt	agp_offset = request->agp_start;
717145132Sanholt
718183573Srnoland	DRM_DEBUG("count:      %d\n",  count);
719183573Srnoland	DRM_DEBUG("order:      %d\n",  order);
720183573Srnoland	DRM_DEBUG("size:       %d\n",  size);
721183573Srnoland	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
722183573Srnoland	DRM_DEBUG("alignment:  %d\n",  alignment);
723183573Srnoland	DRM_DEBUG("page_order: %d\n",  page_order);
724183573Srnoland	DRM_DEBUG("total:      %d\n",  total);
725145132Sanholt
726145132Sanholt	entry = &dma->bufs[order];
727145132Sanholt
728183833Srnoland	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
729145132Sanholt	    M_NOWAIT | M_ZERO);
730145132Sanholt	if (entry->buflist == NULL)
731182080Srnoland		return ENOMEM;
732145132Sanholt
733145132Sanholt	entry->buf_size = size;
734145132Sanholt	entry->page_order = page_order;
735145132Sanholt
736145132Sanholt	offset = 0;
737145132Sanholt
738183573Srnoland	while (entry->buf_count < count) {
739145132Sanholt		buf          = &entry->buflist[entry->buf_count];
740145132Sanholt		buf->idx     = dma->buf_count + entry->buf_count;
741145132Sanholt		buf->total   = alignment;
742145132Sanholt		buf->order   = order;
743145132Sanholt		buf->used    = 0;
744145132Sanholt
745145132Sanholt		buf->offset  = (dma->byte_count + offset);
746145132Sanholt		buf->bus_address = agp_offset + offset;
747207067Srnoland		buf->address = (void *)(agp_offset + offset + dev->sg->vaddr);
748145132Sanholt		buf->next    = NULL;
749145132Sanholt		buf->pending = 0;
750182080Srnoland		buf->file_priv = NULL;
751145132Sanholt
752183573Srnoland		buf->dev_priv_size = dev->driver->buf_priv_size;
753183833Srnoland		buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
754145132Sanholt		    M_NOWAIT | M_ZERO);
755145132Sanholt		if (buf->dev_private == NULL) {
756145132Sanholt			/* Set count correctly so we free the proper amount. */
757145132Sanholt			entry->buf_count = count;
758145132Sanholt			drm_cleanup_buf_error(dev, entry);
759182080Srnoland			return ENOMEM;
760145132Sanholt		}
761145132Sanholt
762183573Srnoland		DRM_DEBUG("buffer %d @ %p\n",
763183573Srnoland		    entry->buf_count, buf->address);
764145132Sanholt
765145132Sanholt		offset += alignment;
766145132Sanholt		entry->buf_count++;
767145132Sanholt		byte_count += PAGE_SIZE << page_order;
768145132Sanholt	}
769145132Sanholt
770183573Srnoland	DRM_DEBUG("byte_count: %d\n", byte_count);
771145132Sanholt
772145132Sanholt	temp_buflist = realloc(dma->buflist,
773183833Srnoland	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
774183833Srnoland	    DRM_MEM_BUFS, M_NOWAIT);
775145132Sanholt	if (temp_buflist == NULL) {
776145132Sanholt		/* Free the entry because it isn't valid */
777145132Sanholt		drm_cleanup_buf_error(dev, entry);
778182080Srnoland		return ENOMEM;
779145132Sanholt	}
780145132Sanholt	dma->buflist = temp_buflist;
781145132Sanholt
782183573Srnoland	for (i = 0; i < entry->buf_count; i++) {
783145132Sanholt		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
784145132Sanholt	}
785145132Sanholt
786145132Sanholt	dma->buf_count += entry->buf_count;
787145132Sanholt	dma->byte_count += byte_count;
788145132Sanholt
789183573Srnoland	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
790183573Srnoland	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
791145132Sanholt
792145132Sanholt	request->count = entry->buf_count;
793145132Sanholt	request->size = size;
794145132Sanholt
795145132Sanholt	dma->flags = _DRM_DMA_USE_SG;
796145132Sanholt
797145132Sanholt	return 0;
798145132Sanholt}
799145132Sanholt
800183573Srnolandint drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
801145132Sanholt{
802152909Sanholt	int order, ret;
803145132Sanholt
804152909Sanholt	if (request->count < 0 || request->count > 4096)
805182080Srnoland		return EINVAL;
806152909Sanholt
807152909Sanholt	order = drm_order(request->size);
808152909Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
809182080Srnoland		return EINVAL;
810145132Sanholt
811182080Srnoland	DRM_SPINLOCK(&dev->dma_lock);
812182080Srnoland
813152909Sanholt	/* No more allocations after first buffer-using ioctl. */
814152909Sanholt	if (dev->buf_use != 0) {
815152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
816182080Srnoland		return EBUSY;
817152909Sanholt	}
818152909Sanholt	/* No more than one allocation per order */
819152909Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
820152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
821182080Srnoland		return ENOMEM;
822152909Sanholt	}
823152909Sanholt
824152909Sanholt	ret = drm_do_addbufs_agp(dev, request);
825152909Sanholt
826152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
827152909Sanholt
828152909Sanholt	return ret;
829152909Sanholt}
830152909Sanholt
831183573Srnolandint drm_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
832152909Sanholt{
833152909Sanholt	int order, ret;
834152909Sanholt
835152909Sanholt	if (!DRM_SUSER(DRM_CURPROC))
836182080Srnoland		return EACCES;
837152909Sanholt
838152909Sanholt	if (request->count < 0 || request->count > 4096)
839182080Srnoland		return EINVAL;
840182080Srnoland
841152909Sanholt	order = drm_order(request->size);
842145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
843182080Srnoland		return EINVAL;
844145132Sanholt
845182080Srnoland	DRM_SPINLOCK(&dev->dma_lock);
846182080Srnoland
847152909Sanholt	/* No more allocations after first buffer-using ioctl. */
848152909Sanholt	if (dev->buf_use != 0) {
849152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
850182080Srnoland		return EBUSY;
851152909Sanholt	}
852152909Sanholt	/* No more than one allocation per order */
853152909Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
854152909Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
855182080Srnoland		return ENOMEM;
856152909Sanholt	}
857152909Sanholt
858152909Sanholt	ret = drm_do_addbufs_sg(dev, request);
859152909Sanholt
860152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
861152909Sanholt
862152909Sanholt	return ret;
863152909Sanholt}
864152909Sanholt
865183573Srnolandint drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
866152909Sanholt{
867152909Sanholt	int order, ret;
868152909Sanholt
869152909Sanholt	if (!DRM_SUSER(DRM_CURPROC))
870182080Srnoland		return EACCES;
871152909Sanholt
872152909Sanholt	if (request->count < 0 || request->count > 4096)
873182080Srnoland		return EINVAL;
874182080Srnoland
875152909Sanholt	order = drm_order(request->size);
876152909Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
877182080Srnoland		return EINVAL;
878152909Sanholt
879182080Srnoland	DRM_SPINLOCK(&dev->dma_lock);
880182080Srnoland
881145132Sanholt	/* No more allocations after first buffer-using ioctl. */
882145132Sanholt	if (dev->buf_use != 0) {
883145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
884182080Srnoland		return EBUSY;
885145132Sanholt	}
886145132Sanholt	/* No more than one allocation per order */
887145132Sanholt	if (dev->dma->bufs[order].buf_count != 0) {
888145132Sanholt		DRM_SPINUNLOCK(&dev->dma_lock);
889182080Srnoland		return ENOMEM;
890145132Sanholt	}
891145132Sanholt
892152909Sanholt	ret = drm_do_addbufs_pci(dev, request);
893152909Sanholt
894152909Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
895152909Sanholt
896152909Sanholt	return ret;
897152909Sanholt}
898152909Sanholt
899189099Srnolandint drm_addbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
900152909Sanholt{
901183573Srnoland	struct drm_buf_desc *request = data;
902152909Sanholt	int err;
903152909Sanholt
904182080Srnoland	if (request->flags & _DRM_AGP_BUFFER)
905182080Srnoland		err = drm_addbufs_agp(dev, request);
906182080Srnoland	else if (request->flags & _DRM_SG_BUFFER)
907182080Srnoland		err = drm_addbufs_sg(dev, request);
908145132Sanholt	else
909182080Srnoland		err = drm_addbufs_pci(dev, request);
910145132Sanholt
911145132Sanholt	return err;
912145132Sanholt}
913145132Sanholt
914182080Srnolandint drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
915145132Sanholt{
916145132Sanholt	drm_device_dma_t *dma = dev->dma;
917183573Srnoland	struct drm_buf_info *request = data;
918145132Sanholt	int i;
919145132Sanholt	int count;
920145132Sanholt	int retcode = 0;
921145132Sanholt
922145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
923145132Sanholt	++dev->buf_use;		/* Can't allocate more after this call */
924145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
925145132Sanholt
926183573Srnoland	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
927183573Srnoland		if (dma->bufs[i].buf_count)
928183573Srnoland			++count;
929145132Sanholt	}
930145132Sanholt
931183573Srnoland	DRM_DEBUG("count = %d\n", count);
932145132Sanholt
933183573Srnoland	if (request->count >= count) {
934183573Srnoland		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
935183573Srnoland			if (dma->bufs[i].buf_count) {
936183573Srnoland				struct drm_buf_desc from;
937145132Sanholt
938331411Semaste				memset(&from, 0, sizeof(from));
939145132Sanholt				from.count = dma->bufs[i].buf_count;
940145132Sanholt				from.size = dma->bufs[i].buf_size;
941145132Sanholt				from.low_mark = dma->bufs[i].freelist.low_mark;
942145132Sanholt				from.high_mark = dma->bufs[i].freelist.high_mark;
943145132Sanholt
944182080Srnoland				if (DRM_COPY_TO_USER(&request->list[count], &from,
945183573Srnoland				    sizeof(struct drm_buf_desc)) != 0) {
946182080Srnoland					retcode = EFAULT;
947145132Sanholt					break;
948145132Sanholt				}
949145132Sanholt
950183573Srnoland				DRM_DEBUG("%d %d %d %d %d\n",
951183573Srnoland				    i, dma->bufs[i].buf_count,
952183573Srnoland				    dma->bufs[i].buf_size,
953183573Srnoland				    dma->bufs[i].freelist.low_mark,
954183573Srnoland				    dma->bufs[i].freelist.high_mark);
955145132Sanholt				++count;
956145132Sanholt			}
957145132Sanholt		}
958145132Sanholt	}
959182080Srnoland	request->count = count;
960145132Sanholt
961145132Sanholt	return retcode;
962145132Sanholt}
963145132Sanholt
964182080Srnolandint drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
965145132Sanholt{
966145132Sanholt	drm_device_dma_t *dma = dev->dma;
967183573Srnoland	struct drm_buf_desc *request = data;
968145132Sanholt	int order;
969145132Sanholt
970183573Srnoland	DRM_DEBUG("%d, %d, %d\n",
971183573Srnoland		  request->size, request->low_mark, request->high_mark);
972145132Sanholt
973145132Sanholt
974182080Srnoland	order = drm_order(request->size);
975145132Sanholt	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
976182080Srnoland	    request->low_mark < 0 || request->high_mark < 0) {
977182080Srnoland		return EINVAL;
978145132Sanholt	}
979145132Sanholt
980145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
981182080Srnoland	if (request->low_mark > dma->bufs[order].buf_count ||
982182080Srnoland	    request->high_mark > dma->bufs[order].buf_count) {
983182080Srnoland		DRM_SPINUNLOCK(&dev->dma_lock);
984182080Srnoland		return EINVAL;
985145132Sanholt	}
986145132Sanholt
987182080Srnoland	dma->bufs[order].freelist.low_mark  = request->low_mark;
988182080Srnoland	dma->bufs[order].freelist.high_mark = request->high_mark;
989145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
990145132Sanholt
991145132Sanholt	return 0;
992145132Sanholt}
993145132Sanholt
994182080Srnolandint drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
995145132Sanholt{
996145132Sanholt	drm_device_dma_t *dma = dev->dma;
997183573Srnoland	struct drm_buf_free *request = data;
998145132Sanholt	int i;
999145132Sanholt	int idx;
1000145132Sanholt	drm_buf_t *buf;
1001145132Sanholt	int retcode = 0;
1002145132Sanholt
1003183573Srnoland	DRM_DEBUG("%d\n", request->count);
1004145132Sanholt
1005145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
1006183573Srnoland	for (i = 0; i < request->count; i++) {
1007182080Srnoland		if (DRM_COPY_FROM_USER(&idx, &request->list[i], sizeof(idx))) {
1008182080Srnoland			retcode = EFAULT;
1009145132Sanholt			break;
1010145132Sanholt		}
1011183573Srnoland		if (idx < 0 || idx >= dma->buf_count) {
1012183573Srnoland			DRM_ERROR("Index %d (of %d max)\n",
1013183573Srnoland			    idx, dma->buf_count - 1);
1014182080Srnoland			retcode = EINVAL;
1015145132Sanholt			break;
1016145132Sanholt		}
1017145132Sanholt		buf = dma->buflist[idx];
1018183573Srnoland		if (buf->file_priv != file_priv) {
1019145132Sanholt			DRM_ERROR("Process %d freeing buffer not owned\n",
1020183573Srnoland			    DRM_CURRENTPID);
1021182080Srnoland			retcode = EINVAL;
1022145132Sanholt			break;
1023145132Sanholt		}
1024145132Sanholt		drm_free_buffer(dev, buf);
1025145132Sanholt	}
1026145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
1027145132Sanholt
1028145132Sanholt	return retcode;
1029145132Sanholt}
1030145132Sanholt
1031182080Srnolandint drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
1032145132Sanholt{
1033145132Sanholt	drm_device_dma_t *dma = dev->dma;
1034145132Sanholt	int retcode = 0;
1035145132Sanholt	const int zero = 0;
1036145132Sanholt	vm_offset_t address;
1037145132Sanholt	struct vmspace *vms;
1038145132Sanholt	vm_ooffset_t foff;
1039145132Sanholt	vm_size_t size;
1040145132Sanholt	vm_offset_t vaddr;
1041183573Srnoland	struct drm_buf_map *request = data;
1042145132Sanholt	int i;
1043145132Sanholt
1044182080Srnoland	vms = DRM_CURPROC->td_proc->p_vmspace;
1045145132Sanholt
1046145132Sanholt	DRM_SPINLOCK(&dev->dma_lock);
1047145132Sanholt	dev->buf_use++;		/* Can't allocate more after this call */
1048145132Sanholt	DRM_SPINUNLOCK(&dev->dma_lock);
1049145132Sanholt
1050182080Srnoland	if (request->count < dma->buf_count)
1051145132Sanholt		goto done;
1052145132Sanholt
1053183573Srnoland	if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) ||
1054183573Srnoland	    (drm_core_check_feature(dev, DRIVER_SG) &&
1055183573Srnoland	    (dma->flags & _DRM_DMA_USE_SG))) {
1056145132Sanholt		drm_local_map_t *map = dev->agp_buffer_map;
1057145132Sanholt
1058145132Sanholt		if (map == NULL) {
1059145132Sanholt			retcode = EINVAL;
1060145132Sanholt			goto done;
1061145132Sanholt		}
1062145132Sanholt		size = round_page(map->size);
1063207066Srnoland		foff = (unsigned long)map->handle;
1064145132Sanholt	} else {
1065145132Sanholt		size = round_page(dma->byte_count),
1066145132Sanholt		foff = 0;
1067145132Sanholt	}
1068145132Sanholt
1069145132Sanholt	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
1070283998Sjhb	retcode = vm_mmap(&vms->vm_map, &vaddr, size, VM_PROT_READ |
1071283998Sjhb	    VM_PROT_WRITE, VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC, OBJT_DEVICE,
1072189561Srnoland	    dev->devnode, foff);
1073145132Sanholt	if (retcode)
1074145132Sanholt		goto done;
1075145132Sanholt
1076182080Srnoland	request->virtual = (void *)vaddr;
1077145132Sanholt
1078183573Srnoland	for (i = 0; i < dma->buf_count; i++) {
1079182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].idx,
1080182080Srnoland		    &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
1081145132Sanholt			retcode = EFAULT;
1082145132Sanholt			goto done;
1083145132Sanholt		}
1084182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].total,
1085182080Srnoland		    &dma->buflist[i]->total, sizeof(request->list[0].total))) {
1086145132Sanholt			retcode = EFAULT;
1087145132Sanholt			goto done;
1088145132Sanholt		}
1089182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].used, &zero,
1090145132Sanholt		    sizeof(zero))) {
1091145132Sanholt			retcode = EFAULT;
1092145132Sanholt			goto done;
1093145132Sanholt		}
1094145132Sanholt		address = vaddr + dma->buflist[i]->offset; /* *** */
1095182080Srnoland		if (DRM_COPY_TO_USER(&request->list[i].address, &address,
1096145132Sanholt		    sizeof(address))) {
1097145132Sanholt			retcode = EFAULT;
1098145132Sanholt			goto done;
1099145132Sanholt		}
1100145132Sanholt	}
1101145132Sanholt
1102145132Sanholt done:
1103182080Srnoland	request->count = dma->buf_count;
1104145132Sanholt
1105183573Srnoland	DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
1106145132Sanholt
1107182080Srnoland	return retcode;
1108145132Sanholt}
1109183573Srnoland
1110183573Srnoland/*
1111183573Srnoland * Compute order.  Can be made faster.
1112183573Srnoland */
1113183573Srnolandint drm_order(unsigned long size)
1114183573Srnoland{
1115183573Srnoland	int order;
1116183573Srnoland
1117183603Srnoland	if (size == 0)
1118183603Srnoland		return 0;
1119183573Srnoland
1120189908Srnoland	order = flsl(size) - 1;
1121183603Srnoland	if (size & ~(1ul << order))
1122183573Srnoland		++order;
1123183573Srnoland
1124183573Srnoland	return order;
1125183573Srnoland}
1126