1/* radeon_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
2 *
3 * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
4 * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * Authors: Kevin E. Martin <martin@valinux.com>
27 *          Rickard E. (Rik) Faith <faith@valinux.com>
28 *	    Jeff Hartmann <jhartmann@valinux.com>
29 *
30 */
31
32#define __NO_VERSION__
33#include <linux/config.h>
34#include "drmP.h"
35#include "radeon_drv.h"
36#include "linux/un.h"
37
38
39#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
40int radeon_addbufs_agp(struct inode *inode, struct file *filp,
41		       unsigned int cmd, unsigned long arg)
42{
43	drm_file_t       *priv = filp->private_data;
44	drm_device_t     *dev  = priv->dev;
45	drm_device_dma_t *dma  = dev->dma;
46	drm_buf_desc_t    request;
47	drm_buf_entry_t  *entry;
48	drm_buf_t        *buf;
49	unsigned long     offset;
50	unsigned long     agp_offset;
51	int               count;
52	int               order;
53	int               size;
54	int               alignment;
55	int               page_order;
56	int               total;
57	int               byte_count;
58	int               i;
59
60	if (!dma) return -EINVAL;
61
62	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
63		return -EFAULT;
64
65	count      = request.count;
66	order      = drm_order(request.size);
67	size       = 1 << order;
68
69	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
70	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
71	total      = PAGE_SIZE << page_order;
72
73	byte_count = 0;
74	agp_offset = dev->agp->base + request.agp_start;
75
76	DRM_DEBUG("count:      %d\n",  count);
77	DRM_DEBUG("order:      %d\n",  order);
78	DRM_DEBUG("size:       %d\n",  size);
79	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
80	DRM_DEBUG("alignment:  %d\n",  alignment);
81	DRM_DEBUG("page_order: %d\n",  page_order);
82	DRM_DEBUG("total:      %d\n",  total);
83
84	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
85	if (dev->queue_count) return -EBUSY; /* Not while in use */
86
87	spin_lock(&dev->count_lock);
88	if (dev->buf_use) {
89		spin_unlock(&dev->count_lock);
90		return -EBUSY;
91	}
92	atomic_inc(&dev->buf_alloc);
93	spin_unlock(&dev->count_lock);
94
95	down(&dev->struct_sem);
96	entry = &dma->bufs[order];
97	if (entry->buf_count) {
98		up(&dev->struct_sem);
99		atomic_dec(&dev->buf_alloc);
100		return -ENOMEM; /* May only call once for each order */
101	}
102
103	/* Might be too low a limit. XFree folks need to fix this properly */
104
105	if(count < 0 || count > 4096)
106	{
107		up(&dev->struct_sem);
108		atomic_dec(&dev->buf_alloc);
109		return -EINVAL;
110	}
111
112	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
113				   DRM_MEM_BUFS);
114	if (!entry->buflist) {
115		up(&dev->struct_sem);
116		atomic_dec(&dev->buf_alloc);
117		return -ENOMEM;
118	}
119	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
120
121	entry->buf_size   = size;
122	entry->page_order = page_order;
123	offset            = 0;
124
125	for (offset = 0;
126	     entry->buf_count < count;
127	     offset += alignment, ++entry->buf_count) {
128		buf          = &entry->buflist[entry->buf_count];
129		buf->idx     = dma->buf_count + entry->buf_count;
130		buf->total   = alignment;
131		buf->order   = order;
132		buf->used    = 0;
133		buf->offset  = (dma->byte_count + offset);
134		buf->address = (void *)(agp_offset + offset);
135		buf->next    = NULL;
136		buf->waiting = 0;
137		buf->pending = 0;
138		init_waitqueue_head(&buf->dma_wait);
139		buf->pid     = 0;
140
141		buf->dev_priv_size = sizeof(drm_radeon_buf_priv_t);
142		buf->dev_private   = drm_alloc(sizeof(drm_radeon_buf_priv_t),
143					       DRM_MEM_BUFS);
144                if (!buf->dev_private) {
145                        up(&dev->struct_sem);
146                        atomic_dec(&dev->buf_alloc);
147                        return -ENOMEM;
148                }
149		memset(buf->dev_private, 0, buf->dev_priv_size);
150
151#if DRM_DMA_HISTOGRAM
152		buf->time_queued     = 0;
153		buf->time_dispatched = 0;
154		buf->time_completed  = 0;
155		buf->time_freed      = 0;
156#endif
157
158		byte_count += PAGE_SIZE << page_order;
159
160		DRM_DEBUG("buffer %d @ %p\n",
161			  entry->buf_count, buf->address);
162	}
163
164	DRM_DEBUG("byte_count: %d\n", byte_count);
165
166	dma->buflist = drm_realloc(dma->buflist,
167				   dma->buf_count * sizeof(*dma->buflist),
168				   (dma->buf_count + entry->buf_count)
169				   * sizeof(*dma->buflist),
170				   DRM_MEM_BUFS);
171	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
172		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
173
174	dma->buf_count  += entry->buf_count;
175	dma->byte_count += byte_count;
176
177	drm_freelist_create(&entry->freelist, entry->buf_count);
178	for (i = 0; i < entry->buf_count; i++) {
179		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
180	}
181
182	up(&dev->struct_sem);
183
184	request.count = entry->buf_count;
185	request.size  = size;
186
187	if (copy_to_user((drm_buf_desc_t *)arg, &request, sizeof(request)))
188		return -EFAULT;
189
190	dma->flags = _DRM_DMA_USE_AGP;
191
192	atomic_dec(&dev->buf_alloc);
193	return 0;
194}
195#endif
196
197int radeon_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
198		   unsigned long arg)
199{
200	drm_file_t		*priv		= filp->private_data;
201	drm_device_t		*dev		= priv->dev;
202	drm_radeon_private_t	*dev_priv	= dev->dev_private;
203	drm_buf_desc_t		request;
204
205	if (!dev_priv || dev_priv->is_pci) return -EINVAL;
206
207	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
208		return -EFAULT;
209
210#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
211	if (request.flags & _DRM_AGP_BUFFER)
212		return radeon_addbufs_agp(inode, filp, cmd, arg);
213	else
214#endif
215		return -EINVAL;
216}
217
218int radeon_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
219		   unsigned long arg)
220{
221	drm_file_t		*priv		= filp->private_data;
222	drm_device_t		*dev		= priv->dev;
223	drm_radeon_private_t	*dev_priv	= dev->dev_private;
224	drm_device_dma_t	*dma		= dev->dma;
225	int			 retcode	= 0;
226	const int		 zero		= 0;
227	unsigned long		 virtual;
228	unsigned long		 address;
229	drm_buf_map_t		 request;
230	int			 i;
231
232	if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL;
233
234	DRM_DEBUG("\n");
235
236	spin_lock(&dev->count_lock);
237	if (atomic_read(&dev->buf_alloc)) {
238		spin_unlock(&dev->count_lock);
239		return -EBUSY;
240	}
241	++dev->buf_use;		/* Can't allocate more after this call */
242	spin_unlock(&dev->count_lock);
243
244	if (copy_from_user(&request, (drm_buf_map_t *)arg, sizeof(request)))
245		return -EFAULT;
246
247	if (request.count >= dma->buf_count) {
248		if (dma->flags & _DRM_DMA_USE_AGP) {
249			drm_map_t *map;
250
251			map = dev_priv->buffers;
252			if (!map) {
253				retcode = -EINVAL;
254				goto done;
255			}
256
257			down_write(&current->mm->mmap_sem);
258			virtual = do_mmap(filp, 0, map->size,
259					  PROT_READ|PROT_WRITE,
260					  MAP_SHARED,
261					  (unsigned long)map->offset);
262			up_write(&current->mm->mmap_sem);
263		} else {
264			down_write(&current->mm->mmap_sem);
265			virtual = do_mmap(filp, 0, dma->byte_count,
266					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
267			up_write(&current->mm->mmap_sem);
268		}
269		if (virtual > -1024UL) {
270			/* Real error */
271			retcode = (signed long)virtual;
272			goto done;
273		}
274		request.virtual = (void *)virtual;
275
276		for (i = 0; i < dma->buf_count; i++) {
277			if (copy_to_user(&request.list[i].idx,
278					 &dma->buflist[i]->idx,
279					 sizeof(request.list[0].idx))) {
280				retcode = -EFAULT;
281				goto done;
282			}
283			if (copy_to_user(&request.list[i].total,
284					 &dma->buflist[i]->total,
285					 sizeof(request.list[0].total))) {
286				retcode = -EFAULT;
287				goto done;
288			}
289			if (copy_to_user(&request.list[i].used,
290					 &zero,
291					 sizeof(zero))) {
292				retcode = -EFAULT;
293				goto done;
294			}
295			address = virtual + dma->buflist[i]->offset;
296			if (copy_to_user(&request.list[i].address,
297					 &address,
298					 sizeof(address))) {
299				retcode = -EFAULT;
300				goto done;
301			}
302		}
303	}
304 done:
305	request.count = dma->buf_count;
306	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
307
308	if (copy_to_user((drm_buf_map_t *)arg, &request, sizeof(request)))
309		return -EFAULT;
310
311	return retcode;
312}
313