diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/drm/i810_dma.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/drm/i810_dma.c')
-rw-r--r-- | drivers/char/drm/i810_dma.c | 1385 |
1 files changed, 1385 insertions, 0 deletions
diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c new file mode 100644 index 000000000000..24857cc6c23b --- /dev/null +++ b/drivers/char/drm/i810_dma.c | |||
@@ -0,0 +1,1385 @@ | |||
1 | /* i810_dma.c -- DMA support for the i810 -*- linux-c -*- | ||
2 | * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com | ||
3 | * | ||
4 | * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. | ||
5 | * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. | ||
6 | * All Rights Reserved. | ||
7 | * | ||
8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
9 | * copy of this software and associated documentation files (the "Software"), | ||
10 | * to deal in the Software without restriction, including without limitation | ||
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
13 | * Software is furnished to do so, subject to the following conditions: | ||
14 | * | ||
15 | * The above copyright notice and this permission notice (including the next | ||
16 | * paragraph) shall be included in all copies or substantial portions of the | ||
17 | * Software. | ||
18 | * | ||
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
22 | * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
25 | * DEALINGS IN THE SOFTWARE. | ||
26 | * | ||
27 | * Authors: Rickard E. (Rik) Faith <faith@valinux.com> | ||
28 | * Jeff Hartmann <jhartmann@valinux.com> | ||
29 | * Keith Whitwell <keith@tungstengraphics.com> | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include "drmP.h" | ||
34 | #include "drm.h" | ||
35 | #include "i810_drm.h" | ||
36 | #include "i810_drv.h" | ||
37 | #include <linux/interrupt.h> /* For task queue support */ | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/pagemap.h> | ||
40 | |||
41 | #define I810_BUF_FREE 2 | ||
42 | #define I810_BUF_CLIENT 1 | ||
43 | #define I810_BUF_HARDWARE 0 | ||
44 | |||
45 | #define I810_BUF_UNMAPPED 0 | ||
46 | #define I810_BUF_MAPPED 1 | ||
47 | |||
48 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,2) | ||
49 | #define down_write down | ||
50 | #define up_write up | ||
51 | #endif | ||
52 | |||
53 | static drm_buf_t *i810_freelist_get(drm_device_t *dev) | ||
54 | { | ||
55 | drm_device_dma_t *dma = dev->dma; | ||
56 | int i; | ||
57 | int used; | ||
58 | |||
59 | /* Linear search might not be the best solution */ | ||
60 | |||
61 | for (i = 0; i < dma->buf_count; i++) { | ||
62 | drm_buf_t *buf = dma->buflist[ i ]; | ||
63 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
64 | /* In use is already a pointer */ | ||
65 | used = cmpxchg(buf_priv->in_use, I810_BUF_FREE, | ||
66 | I810_BUF_CLIENT); | ||
67 | if (used == I810_BUF_FREE) { | ||
68 | return buf; | ||
69 | } | ||
70 | } | ||
71 | return NULL; | ||
72 | } | ||
73 | |||
74 | /* This should only be called if the buffer is not sent to the hardware | ||
75 | * yet, the hardware updates in use for us once its on the ring buffer. | ||
76 | */ | ||
77 | |||
78 | static int i810_freelist_put(drm_device_t *dev, drm_buf_t *buf) | ||
79 | { | ||
80 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
81 | int used; | ||
82 | |||
83 | /* In use is already a pointer */ | ||
84 | used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, I810_BUF_FREE); | ||
85 | if (used != I810_BUF_CLIENT) { | ||
86 | DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx); | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static struct file_operations i810_buffer_fops = { | ||
94 | .open = drm_open, | ||
95 | .flush = drm_flush, | ||
96 | .release = drm_release, | ||
97 | .ioctl = drm_ioctl, | ||
98 | .mmap = i810_mmap_buffers, | ||
99 | .fasync = drm_fasync, | ||
100 | }; | ||
101 | |||
102 | int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) | ||
103 | { | ||
104 | drm_file_t *priv = filp->private_data; | ||
105 | drm_device_t *dev; | ||
106 | drm_i810_private_t *dev_priv; | ||
107 | drm_buf_t *buf; | ||
108 | drm_i810_buf_priv_t *buf_priv; | ||
109 | |||
110 | lock_kernel(); | ||
111 | dev = priv->head->dev; | ||
112 | dev_priv = dev->dev_private; | ||
113 | buf = dev_priv->mmap_buffer; | ||
114 | buf_priv = buf->dev_private; | ||
115 | |||
116 | vma->vm_flags |= (VM_IO | VM_DONTCOPY); | ||
117 | vma->vm_file = filp; | ||
118 | |||
119 | buf_priv->currently_mapped = I810_BUF_MAPPED; | ||
120 | unlock_kernel(); | ||
121 | |||
122 | if (io_remap_pfn_range(vma, vma->vm_start, | ||
123 | VM_OFFSET(vma) >> PAGE_SHIFT, | ||
124 | vma->vm_end - vma->vm_start, | ||
125 | vma->vm_page_prot)) return -EAGAIN; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int i810_map_buffer(drm_buf_t *buf, struct file *filp) | ||
130 | { | ||
131 | drm_file_t *priv = filp->private_data; | ||
132 | drm_device_t *dev = priv->head->dev; | ||
133 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
134 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
135 | struct file_operations *old_fops; | ||
136 | int retcode = 0; | ||
137 | |||
138 | if (buf_priv->currently_mapped == I810_BUF_MAPPED) | ||
139 | return -EINVAL; | ||
140 | |||
141 | down_write( ¤t->mm->mmap_sem ); | ||
142 | old_fops = filp->f_op; | ||
143 | filp->f_op = &i810_buffer_fops; | ||
144 | dev_priv->mmap_buffer = buf; | ||
145 | buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, | ||
146 | PROT_READ|PROT_WRITE, | ||
147 | MAP_SHARED, | ||
148 | buf->bus_address); | ||
149 | dev_priv->mmap_buffer = NULL; | ||
150 | filp->f_op = old_fops; | ||
151 | if ((unsigned long)buf_priv->virtual > -1024UL) { | ||
152 | /* Real error */ | ||
153 | DRM_ERROR("mmap error\n"); | ||
154 | retcode = (signed int)buf_priv->virtual; | ||
155 | buf_priv->virtual = NULL; | ||
156 | } | ||
157 | up_write( ¤t->mm->mmap_sem ); | ||
158 | |||
159 | return retcode; | ||
160 | } | ||
161 | |||
162 | static int i810_unmap_buffer(drm_buf_t *buf) | ||
163 | { | ||
164 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
165 | int retcode = 0; | ||
166 | |||
167 | if (buf_priv->currently_mapped != I810_BUF_MAPPED) | ||
168 | return -EINVAL; | ||
169 | |||
170 | down_write(¤t->mm->mmap_sem); | ||
171 | retcode = do_munmap(current->mm, | ||
172 | (unsigned long)buf_priv->virtual, | ||
173 | (size_t) buf->total); | ||
174 | up_write(¤t->mm->mmap_sem); | ||
175 | |||
176 | buf_priv->currently_mapped = I810_BUF_UNMAPPED; | ||
177 | buf_priv->virtual = NULL; | ||
178 | |||
179 | return retcode; | ||
180 | } | ||
181 | |||
182 | static int i810_dma_get_buffer(drm_device_t *dev, drm_i810_dma_t *d, | ||
183 | struct file *filp) | ||
184 | { | ||
185 | drm_buf_t *buf; | ||
186 | drm_i810_buf_priv_t *buf_priv; | ||
187 | int retcode = 0; | ||
188 | |||
189 | buf = i810_freelist_get(dev); | ||
190 | if (!buf) { | ||
191 | retcode = -ENOMEM; | ||
192 | DRM_DEBUG("retcode=%d\n", retcode); | ||
193 | return retcode; | ||
194 | } | ||
195 | |||
196 | retcode = i810_map_buffer(buf, filp); | ||
197 | if (retcode) { | ||
198 | i810_freelist_put(dev, buf); | ||
199 | DRM_ERROR("mapbuf failed, retcode %d\n", retcode); | ||
200 | return retcode; | ||
201 | } | ||
202 | buf->filp = filp; | ||
203 | buf_priv = buf->dev_private; | ||
204 | d->granted = 1; | ||
205 | d->request_idx = buf->idx; | ||
206 | d->request_size = buf->total; | ||
207 | d->virtual = buf_priv->virtual; | ||
208 | |||
209 | return retcode; | ||
210 | } | ||
211 | |||
212 | static int i810_dma_cleanup(drm_device_t *dev) | ||
213 | { | ||
214 | drm_device_dma_t *dma = dev->dma; | ||
215 | |||
216 | /* Make sure interrupts are disabled here because the uninstall ioctl | ||
217 | * may not have been called from userspace and after dev_private | ||
218 | * is freed, it's too late. | ||
219 | */ | ||
220 | if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ) && dev->irq_enabled) | ||
221 | drm_irq_uninstall(dev); | ||
222 | |||
223 | if (dev->dev_private) { | ||
224 | int i; | ||
225 | drm_i810_private_t *dev_priv = | ||
226 | (drm_i810_private_t *) dev->dev_private; | ||
227 | |||
228 | if (dev_priv->ring.virtual_start) { | ||
229 | drm_ioremapfree((void *) dev_priv->ring.virtual_start, | ||
230 | dev_priv->ring.Size, dev); | ||
231 | } | ||
232 | if (dev_priv->hw_status_page) { | ||
233 | pci_free_consistent(dev->pdev, PAGE_SIZE, | ||
234 | dev_priv->hw_status_page, | ||
235 | dev_priv->dma_status_page); | ||
236 | /* Need to rewrite hardware status page */ | ||
237 | I810_WRITE(0x02080, 0x1ffff000); | ||
238 | } | ||
239 | drm_free(dev->dev_private, sizeof(drm_i810_private_t), | ||
240 | DRM_MEM_DRIVER); | ||
241 | dev->dev_private = NULL; | ||
242 | |||
243 | for (i = 0; i < dma->buf_count; i++) { | ||
244 | drm_buf_t *buf = dma->buflist[ i ]; | ||
245 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
246 | if ( buf_priv->kernel_virtual && buf->total ) | ||
247 | drm_ioremapfree(buf_priv->kernel_virtual, buf->total, dev); | ||
248 | } | ||
249 | } | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int i810_wait_ring(drm_device_t *dev, int n) | ||
254 | { | ||
255 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
256 | drm_i810_ring_buffer_t *ring = &(dev_priv->ring); | ||
257 | int iters = 0; | ||
258 | unsigned long end; | ||
259 | unsigned int last_head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; | ||
260 | |||
261 | end = jiffies + (HZ*3); | ||
262 | while (ring->space < n) { | ||
263 | ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; | ||
264 | ring->space = ring->head - (ring->tail+8); | ||
265 | if (ring->space < 0) ring->space += ring->Size; | ||
266 | |||
267 | if (ring->head != last_head) { | ||
268 | end = jiffies + (HZ*3); | ||
269 | last_head = ring->head; | ||
270 | } | ||
271 | |||
272 | iters++; | ||
273 | if (time_before(end, jiffies)) { | ||
274 | DRM_ERROR("space: %d wanted %d\n", ring->space, n); | ||
275 | DRM_ERROR("lockup\n"); | ||
276 | goto out_wait_ring; | ||
277 | } | ||
278 | udelay(1); | ||
279 | } | ||
280 | |||
281 | out_wait_ring: | ||
282 | return iters; | ||
283 | } | ||
284 | |||
285 | static void i810_kernel_lost_context(drm_device_t *dev) | ||
286 | { | ||
287 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
288 | drm_i810_ring_buffer_t *ring = &(dev_priv->ring); | ||
289 | |||
290 | ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; | ||
291 | ring->tail = I810_READ(LP_RING + RING_TAIL); | ||
292 | ring->space = ring->head - (ring->tail+8); | ||
293 | if (ring->space < 0) ring->space += ring->Size; | ||
294 | } | ||
295 | |||
296 | static int i810_freelist_init(drm_device_t *dev, drm_i810_private_t *dev_priv) | ||
297 | { | ||
298 | drm_device_dma_t *dma = dev->dma; | ||
299 | int my_idx = 24; | ||
300 | u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx); | ||
301 | int i; | ||
302 | |||
303 | if (dma->buf_count > 1019) { | ||
304 | /* Not enough space in the status page for the freelist */ | ||
305 | return -EINVAL; | ||
306 | } | ||
307 | |||
308 | for (i = 0; i < dma->buf_count; i++) { | ||
309 | drm_buf_t *buf = dma->buflist[ i ]; | ||
310 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
311 | |||
312 | buf_priv->in_use = hw_status++; | ||
313 | buf_priv->my_use_idx = my_idx; | ||
314 | my_idx += 4; | ||
315 | |||
316 | *buf_priv->in_use = I810_BUF_FREE; | ||
317 | |||
318 | buf_priv->kernel_virtual = drm_ioremap(buf->bus_address, | ||
319 | buf->total, dev); | ||
320 | } | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int i810_dma_initialize(drm_device_t *dev, | ||
325 | drm_i810_private_t *dev_priv, | ||
326 | drm_i810_init_t *init) | ||
327 | { | ||
328 | struct list_head *list; | ||
329 | |||
330 | memset(dev_priv, 0, sizeof(drm_i810_private_t)); | ||
331 | |||
332 | list_for_each(list, &dev->maplist->head) { | ||
333 | drm_map_list_t *r_list = list_entry(list, drm_map_list_t, head); | ||
334 | if (r_list->map && | ||
335 | r_list->map->type == _DRM_SHM && | ||
336 | r_list->map->flags & _DRM_CONTAINS_LOCK ) { | ||
337 | dev_priv->sarea_map = r_list->map; | ||
338 | break; | ||
339 | } | ||
340 | } | ||
341 | if (!dev_priv->sarea_map) { | ||
342 | dev->dev_private = (void *)dev_priv; | ||
343 | i810_dma_cleanup(dev); | ||
344 | DRM_ERROR("can not find sarea!\n"); | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset); | ||
348 | if (!dev_priv->mmio_map) { | ||
349 | dev->dev_private = (void *)dev_priv; | ||
350 | i810_dma_cleanup(dev); | ||
351 | DRM_ERROR("can not find mmio map!\n"); | ||
352 | return -EINVAL; | ||
353 | } | ||
354 | dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); | ||
355 | if (!dev->agp_buffer_map) { | ||
356 | dev->dev_private = (void *)dev_priv; | ||
357 | i810_dma_cleanup(dev); | ||
358 | DRM_ERROR("can not find dma buffer map!\n"); | ||
359 | return -EINVAL; | ||
360 | } | ||
361 | |||
362 | dev_priv->sarea_priv = (drm_i810_sarea_t *) | ||
363 | ((u8 *)dev_priv->sarea_map->handle + | ||
364 | init->sarea_priv_offset); | ||
365 | |||
366 | dev_priv->ring.Start = init->ring_start; | ||
367 | dev_priv->ring.End = init->ring_end; | ||
368 | dev_priv->ring.Size = init->ring_size; | ||
369 | |||
370 | dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base + | ||
371 | init->ring_start, | ||
372 | init->ring_size, dev); | ||
373 | |||
374 | if (dev_priv->ring.virtual_start == NULL) { | ||
375 | dev->dev_private = (void *) dev_priv; | ||
376 | i810_dma_cleanup(dev); | ||
377 | DRM_ERROR("can not ioremap virtual address for" | ||
378 | " ring buffer\n"); | ||
379 | return -ENOMEM; | ||
380 | } | ||
381 | |||
382 | dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; | ||
383 | |||
384 | dev_priv->w = init->w; | ||
385 | dev_priv->h = init->h; | ||
386 | dev_priv->pitch = init->pitch; | ||
387 | dev_priv->back_offset = init->back_offset; | ||
388 | dev_priv->depth_offset = init->depth_offset; | ||
389 | dev_priv->front_offset = init->front_offset; | ||
390 | |||
391 | dev_priv->overlay_offset = init->overlay_offset; | ||
392 | dev_priv->overlay_physical = init->overlay_physical; | ||
393 | |||
394 | dev_priv->front_di1 = init->front_offset | init->pitch_bits; | ||
395 | dev_priv->back_di1 = init->back_offset | init->pitch_bits; | ||
396 | dev_priv->zi1 = init->depth_offset | init->pitch_bits; | ||
397 | |||
398 | /* Program Hardware Status Page */ | ||
399 | dev_priv->hw_status_page = | ||
400 | pci_alloc_consistent(dev->pdev, PAGE_SIZE, | ||
401 | &dev_priv->dma_status_page); | ||
402 | if (!dev_priv->hw_status_page) { | ||
403 | dev->dev_private = (void *)dev_priv; | ||
404 | i810_dma_cleanup(dev); | ||
405 | DRM_ERROR("Can not allocate hardware status page\n"); | ||
406 | return -ENOMEM; | ||
407 | } | ||
408 | memset(dev_priv->hw_status_page, 0, PAGE_SIZE); | ||
409 | DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); | ||
410 | |||
411 | I810_WRITE(0x02080, dev_priv->dma_status_page); | ||
412 | DRM_DEBUG("Enabled hardware status page\n"); | ||
413 | |||
414 | /* Now we need to init our freelist */ | ||
415 | if (i810_freelist_init(dev, dev_priv) != 0) { | ||
416 | dev->dev_private = (void *)dev_priv; | ||
417 | i810_dma_cleanup(dev); | ||
418 | DRM_ERROR("Not enough space in the status page for" | ||
419 | " the freelist\n"); | ||
420 | return -ENOMEM; | ||
421 | } | ||
422 | dev->dev_private = (void *)dev_priv; | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | /* i810 DRM version 1.1 used a smaller init structure with different | ||
428 | * ordering of values than is currently used (drm >= 1.2). There is | ||
429 | * no defined way to detect the XFree version to correct this problem, | ||
430 | * however by checking using this procedure we can detect the correct | ||
431 | * thing to do. | ||
432 | * | ||
433 | * #1 Read the Smaller init structure from user-space | ||
434 | * #2 Verify the overlay_physical is a valid physical address, or NULL | ||
435 | * If it isn't then we have a v1.1 client. Fix up params. | ||
436 | * If it is, then we have a 1.2 client... get the rest of the data. | ||
437 | */ | ||
438 | static int i810_dma_init_compat(drm_i810_init_t *init, unsigned long arg) | ||
439 | { | ||
440 | |||
441 | /* Get v1.1 init data */ | ||
442 | if (copy_from_user(init, (drm_i810_pre12_init_t __user *)arg, | ||
443 | sizeof(drm_i810_pre12_init_t))) { | ||
444 | return -EFAULT; | ||
445 | } | ||
446 | |||
447 | if ((!init->overlay_physical) || (init->overlay_physical > 4096)) { | ||
448 | |||
449 | /* This is a v1.2 client, just get the v1.2 init data */ | ||
450 | DRM_INFO("Using POST v1.2 init.\n"); | ||
451 | if (copy_from_user(init, (drm_i810_init_t __user *)arg, | ||
452 | sizeof(drm_i810_init_t))) { | ||
453 | return -EFAULT; | ||
454 | } | ||
455 | } else { | ||
456 | |||
457 | /* This is a v1.1 client, fix the params */ | ||
458 | DRM_INFO("Using PRE v1.2 init.\n"); | ||
459 | init->pitch_bits = init->h; | ||
460 | init->pitch = init->w; | ||
461 | init->h = init->overlay_physical; | ||
462 | init->w = init->overlay_offset; | ||
463 | init->overlay_physical = 0; | ||
464 | init->overlay_offset = 0; | ||
465 | } | ||
466 | |||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | static int i810_dma_init(struct inode *inode, struct file *filp, | ||
471 | unsigned int cmd, unsigned long arg) | ||
472 | { | ||
473 | drm_file_t *priv = filp->private_data; | ||
474 | drm_device_t *dev = priv->head->dev; | ||
475 | drm_i810_private_t *dev_priv; | ||
476 | drm_i810_init_t init; | ||
477 | int retcode = 0; | ||
478 | |||
479 | /* Get only the init func */ | ||
480 | if (copy_from_user(&init, (void __user *)arg, sizeof(drm_i810_init_func_t))) | ||
481 | return -EFAULT; | ||
482 | |||
483 | switch(init.func) { | ||
484 | case I810_INIT_DMA: | ||
485 | /* This case is for backward compatibility. It | ||
486 | * handles XFree 4.1.0 and 4.2.0, and has to | ||
487 | * do some parameter checking as described below. | ||
488 | * It will someday go away. | ||
489 | */ | ||
490 | retcode = i810_dma_init_compat(&init, arg); | ||
491 | if (retcode) | ||
492 | return retcode; | ||
493 | |||
494 | dev_priv = drm_alloc(sizeof(drm_i810_private_t), | ||
495 | DRM_MEM_DRIVER); | ||
496 | if (dev_priv == NULL) | ||
497 | return -ENOMEM; | ||
498 | retcode = i810_dma_initialize(dev, dev_priv, &init); | ||
499 | break; | ||
500 | |||
501 | default: | ||
502 | case I810_INIT_DMA_1_4: | ||
503 | DRM_INFO("Using v1.4 init.\n"); | ||
504 | if (copy_from_user(&init, (drm_i810_init_t __user *)arg, | ||
505 | sizeof(drm_i810_init_t))) { | ||
506 | return -EFAULT; | ||
507 | } | ||
508 | dev_priv = drm_alloc(sizeof(drm_i810_private_t), | ||
509 | DRM_MEM_DRIVER); | ||
510 | if (dev_priv == NULL) | ||
511 | return -ENOMEM; | ||
512 | retcode = i810_dma_initialize(dev, dev_priv, &init); | ||
513 | break; | ||
514 | |||
515 | case I810_CLEANUP_DMA: | ||
516 | DRM_INFO("DMA Cleanup\n"); | ||
517 | retcode = i810_dma_cleanup(dev); | ||
518 | break; | ||
519 | } | ||
520 | |||
521 | return retcode; | ||
522 | } | ||
523 | |||
524 | |||
525 | |||
526 | /* Most efficient way to verify state for the i810 is as it is | ||
527 | * emitted. Non-conformant state is silently dropped. | ||
528 | * | ||
529 | * Use 'volatile' & local var tmp to force the emitted values to be | ||
530 | * identical to the verified ones. | ||
531 | */ | ||
532 | static void i810EmitContextVerified( drm_device_t *dev, | ||
533 | volatile unsigned int *code ) | ||
534 | { | ||
535 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
536 | int i, j = 0; | ||
537 | unsigned int tmp; | ||
538 | RING_LOCALS; | ||
539 | |||
540 | BEGIN_LP_RING( I810_CTX_SETUP_SIZE ); | ||
541 | |||
542 | OUT_RING( GFX_OP_COLOR_FACTOR ); | ||
543 | OUT_RING( code[I810_CTXREG_CF1] ); | ||
544 | |||
545 | OUT_RING( GFX_OP_STIPPLE ); | ||
546 | OUT_RING( code[I810_CTXREG_ST1] ); | ||
547 | |||
548 | for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) { | ||
549 | tmp = code[i]; | ||
550 | |||
551 | if ((tmp & (7<<29)) == (3<<29) && | ||
552 | (tmp & (0x1f<<24)) < (0x1d<<24)) | ||
553 | { | ||
554 | OUT_RING( tmp ); | ||
555 | j++; | ||
556 | } | ||
557 | else printk("constext state dropped!!!\n"); | ||
558 | } | ||
559 | |||
560 | if (j & 1) | ||
561 | OUT_RING( 0 ); | ||
562 | |||
563 | ADVANCE_LP_RING(); | ||
564 | } | ||
565 | |||
566 | static void i810EmitTexVerified( drm_device_t *dev, | ||
567 | volatile unsigned int *code ) | ||
568 | { | ||
569 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
570 | int i, j = 0; | ||
571 | unsigned int tmp; | ||
572 | RING_LOCALS; | ||
573 | |||
574 | BEGIN_LP_RING( I810_TEX_SETUP_SIZE ); | ||
575 | |||
576 | OUT_RING( GFX_OP_MAP_INFO ); | ||
577 | OUT_RING( code[I810_TEXREG_MI1] ); | ||
578 | OUT_RING( code[I810_TEXREG_MI2] ); | ||
579 | OUT_RING( code[I810_TEXREG_MI3] ); | ||
580 | |||
581 | for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) { | ||
582 | tmp = code[i]; | ||
583 | |||
584 | if ((tmp & (7<<29)) == (3<<29) && | ||
585 | (tmp & (0x1f<<24)) < (0x1d<<24)) | ||
586 | { | ||
587 | OUT_RING( tmp ); | ||
588 | j++; | ||
589 | } | ||
590 | else printk("texture state dropped!!!\n"); | ||
591 | } | ||
592 | |||
593 | if (j & 1) | ||
594 | OUT_RING( 0 ); | ||
595 | |||
596 | ADVANCE_LP_RING(); | ||
597 | } | ||
598 | |||
599 | |||
600 | /* Need to do some additional checking when setting the dest buffer. | ||
601 | */ | ||
602 | static void i810EmitDestVerified( drm_device_t *dev, | ||
603 | volatile unsigned int *code ) | ||
604 | { | ||
605 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
606 | unsigned int tmp; | ||
607 | RING_LOCALS; | ||
608 | |||
609 | BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 ); | ||
610 | |||
611 | tmp = code[I810_DESTREG_DI1]; | ||
612 | if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) { | ||
613 | OUT_RING( CMD_OP_DESTBUFFER_INFO ); | ||
614 | OUT_RING( tmp ); | ||
615 | } else | ||
616 | DRM_DEBUG("bad di1 %x (allow %x or %x)\n", | ||
617 | tmp, dev_priv->front_di1, dev_priv->back_di1); | ||
618 | |||
619 | /* invarient: | ||
620 | */ | ||
621 | OUT_RING( CMD_OP_Z_BUFFER_INFO ); | ||
622 | OUT_RING( dev_priv->zi1 ); | ||
623 | |||
624 | OUT_RING( GFX_OP_DESTBUFFER_VARS ); | ||
625 | OUT_RING( code[I810_DESTREG_DV1] ); | ||
626 | |||
627 | OUT_RING( GFX_OP_DRAWRECT_INFO ); | ||
628 | OUT_RING( code[I810_DESTREG_DR1] ); | ||
629 | OUT_RING( code[I810_DESTREG_DR2] ); | ||
630 | OUT_RING( code[I810_DESTREG_DR3] ); | ||
631 | OUT_RING( code[I810_DESTREG_DR4] ); | ||
632 | OUT_RING( 0 ); | ||
633 | |||
634 | ADVANCE_LP_RING(); | ||
635 | } | ||
636 | |||
637 | |||
638 | |||
639 | static void i810EmitState( drm_device_t *dev ) | ||
640 | { | ||
641 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
642 | drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; | ||
643 | unsigned int dirty = sarea_priv->dirty; | ||
644 | |||
645 | DRM_DEBUG("%s %x\n", __FUNCTION__, dirty); | ||
646 | |||
647 | if (dirty & I810_UPLOAD_BUFFERS) { | ||
648 | i810EmitDestVerified( dev, sarea_priv->BufferState ); | ||
649 | sarea_priv->dirty &= ~I810_UPLOAD_BUFFERS; | ||
650 | } | ||
651 | |||
652 | if (dirty & I810_UPLOAD_CTX) { | ||
653 | i810EmitContextVerified( dev, sarea_priv->ContextState ); | ||
654 | sarea_priv->dirty &= ~I810_UPLOAD_CTX; | ||
655 | } | ||
656 | |||
657 | if (dirty & I810_UPLOAD_TEX0) { | ||
658 | i810EmitTexVerified( dev, sarea_priv->TexState[0] ); | ||
659 | sarea_priv->dirty &= ~I810_UPLOAD_TEX0; | ||
660 | } | ||
661 | |||
662 | if (dirty & I810_UPLOAD_TEX1) { | ||
663 | i810EmitTexVerified( dev, sarea_priv->TexState[1] ); | ||
664 | sarea_priv->dirty &= ~I810_UPLOAD_TEX1; | ||
665 | } | ||
666 | } | ||
667 | |||
668 | |||
669 | |||
670 | /* need to verify | ||
671 | */ | ||
672 | static void i810_dma_dispatch_clear( drm_device_t *dev, int flags, | ||
673 | unsigned int clear_color, | ||
674 | unsigned int clear_zval ) | ||
675 | { | ||
676 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
677 | drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; | ||
678 | int nbox = sarea_priv->nbox; | ||
679 | drm_clip_rect_t *pbox = sarea_priv->boxes; | ||
680 | int pitch = dev_priv->pitch; | ||
681 | int cpp = 2; | ||
682 | int i; | ||
683 | RING_LOCALS; | ||
684 | |||
685 | if ( dev_priv->current_page == 1 ) { | ||
686 | unsigned int tmp = flags; | ||
687 | |||
688 | flags &= ~(I810_FRONT | I810_BACK); | ||
689 | if (tmp & I810_FRONT) flags |= I810_BACK; | ||
690 | if (tmp & I810_BACK) flags |= I810_FRONT; | ||
691 | } | ||
692 | |||
693 | i810_kernel_lost_context(dev); | ||
694 | |||
695 | if (nbox > I810_NR_SAREA_CLIPRECTS) | ||
696 | nbox = I810_NR_SAREA_CLIPRECTS; | ||
697 | |||
698 | for (i = 0 ; i < nbox ; i++, pbox++) { | ||
699 | unsigned int x = pbox->x1; | ||
700 | unsigned int y = pbox->y1; | ||
701 | unsigned int width = (pbox->x2 - x) * cpp; | ||
702 | unsigned int height = pbox->y2 - y; | ||
703 | unsigned int start = y * pitch + x * cpp; | ||
704 | |||
705 | if (pbox->x1 > pbox->x2 || | ||
706 | pbox->y1 > pbox->y2 || | ||
707 | pbox->x2 > dev_priv->w || | ||
708 | pbox->y2 > dev_priv->h) | ||
709 | continue; | ||
710 | |||
711 | if ( flags & I810_FRONT ) { | ||
712 | BEGIN_LP_RING( 6 ); | ||
713 | OUT_RING( BR00_BITBLT_CLIENT | | ||
714 | BR00_OP_COLOR_BLT | 0x3 ); | ||
715 | OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); | ||
716 | OUT_RING( (height << 16) | width ); | ||
717 | OUT_RING( start ); | ||
718 | OUT_RING( clear_color ); | ||
719 | OUT_RING( 0 ); | ||
720 | ADVANCE_LP_RING(); | ||
721 | } | ||
722 | |||
723 | if ( flags & I810_BACK ) { | ||
724 | BEGIN_LP_RING( 6 ); | ||
725 | OUT_RING( BR00_BITBLT_CLIENT | | ||
726 | BR00_OP_COLOR_BLT | 0x3 ); | ||
727 | OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); | ||
728 | OUT_RING( (height << 16) | width ); | ||
729 | OUT_RING( dev_priv->back_offset + start ); | ||
730 | OUT_RING( clear_color ); | ||
731 | OUT_RING( 0 ); | ||
732 | ADVANCE_LP_RING(); | ||
733 | } | ||
734 | |||
735 | if ( flags & I810_DEPTH ) { | ||
736 | BEGIN_LP_RING( 6 ); | ||
737 | OUT_RING( BR00_BITBLT_CLIENT | | ||
738 | BR00_OP_COLOR_BLT | 0x3 ); | ||
739 | OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); | ||
740 | OUT_RING( (height << 16) | width ); | ||
741 | OUT_RING( dev_priv->depth_offset + start ); | ||
742 | OUT_RING( clear_zval ); | ||
743 | OUT_RING( 0 ); | ||
744 | ADVANCE_LP_RING(); | ||
745 | } | ||
746 | } | ||
747 | } | ||
748 | |||
749 | static void i810_dma_dispatch_swap( drm_device_t *dev ) | ||
750 | { | ||
751 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
752 | drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; | ||
753 | int nbox = sarea_priv->nbox; | ||
754 | drm_clip_rect_t *pbox = sarea_priv->boxes; | ||
755 | int pitch = dev_priv->pitch; | ||
756 | int cpp = 2; | ||
757 | int i; | ||
758 | RING_LOCALS; | ||
759 | |||
760 | DRM_DEBUG("swapbuffers\n"); | ||
761 | |||
762 | i810_kernel_lost_context(dev); | ||
763 | |||
764 | if (nbox > I810_NR_SAREA_CLIPRECTS) | ||
765 | nbox = I810_NR_SAREA_CLIPRECTS; | ||
766 | |||
767 | for (i = 0 ; i < nbox; i++, pbox++) | ||
768 | { | ||
769 | unsigned int w = pbox->x2 - pbox->x1; | ||
770 | unsigned int h = pbox->y2 - pbox->y1; | ||
771 | unsigned int dst = pbox->x1*cpp + pbox->y1*pitch; | ||
772 | unsigned int start = dst; | ||
773 | |||
774 | if (pbox->x1 > pbox->x2 || | ||
775 | pbox->y1 > pbox->y2 || | ||
776 | pbox->x2 > dev_priv->w || | ||
777 | pbox->y2 > dev_priv->h) | ||
778 | continue; | ||
779 | |||
780 | BEGIN_LP_RING( 6 ); | ||
781 | OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 ); | ||
782 | OUT_RING( pitch | (0xCC << 16)); | ||
783 | OUT_RING( (h << 16) | (w * cpp)); | ||
784 | if (dev_priv->current_page == 0) | ||
785 | OUT_RING(dev_priv->front_offset + start); | ||
786 | else | ||
787 | OUT_RING(dev_priv->back_offset + start); | ||
788 | OUT_RING( pitch ); | ||
789 | if (dev_priv->current_page == 0) | ||
790 | OUT_RING(dev_priv->back_offset + start); | ||
791 | else | ||
792 | OUT_RING(dev_priv->front_offset + start); | ||
793 | ADVANCE_LP_RING(); | ||
794 | } | ||
795 | } | ||
796 | |||
797 | |||
798 | static void i810_dma_dispatch_vertex(drm_device_t *dev, | ||
799 | drm_buf_t *buf, | ||
800 | int discard, | ||
801 | int used) | ||
802 | { | ||
803 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
804 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
805 | drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; | ||
806 | drm_clip_rect_t *box = sarea_priv->boxes; | ||
807 | int nbox = sarea_priv->nbox; | ||
808 | unsigned long address = (unsigned long)buf->bus_address; | ||
809 | unsigned long start = address - dev->agp->base; | ||
810 | int i = 0; | ||
811 | RING_LOCALS; | ||
812 | |||
813 | i810_kernel_lost_context(dev); | ||
814 | |||
815 | if (nbox > I810_NR_SAREA_CLIPRECTS) | ||
816 | nbox = I810_NR_SAREA_CLIPRECTS; | ||
817 | |||
818 | if (used > 4*1024) | ||
819 | used = 0; | ||
820 | |||
821 | if (sarea_priv->dirty) | ||
822 | i810EmitState( dev ); | ||
823 | |||
824 | if (buf_priv->currently_mapped == I810_BUF_MAPPED) { | ||
825 | unsigned int prim = (sarea_priv->vertex_prim & PR_MASK); | ||
826 | |||
827 | *(u32 *)buf_priv->kernel_virtual = ((GFX_OP_PRIMITIVE | prim | ((used/4)-2))); | ||
828 | |||
829 | if (used & 4) { | ||
830 | *(u32 *)((u32)buf_priv->kernel_virtual + used) = 0; | ||
831 | used += 4; | ||
832 | } | ||
833 | |||
834 | i810_unmap_buffer(buf); | ||
835 | } | ||
836 | |||
837 | if (used) { | ||
838 | do { | ||
839 | if (i < nbox) { | ||
840 | BEGIN_LP_RING(4); | ||
841 | OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR | | ||
842 | SC_ENABLE ); | ||
843 | OUT_RING( GFX_OP_SCISSOR_INFO ); | ||
844 | OUT_RING( box[i].x1 | (box[i].y1<<16) ); | ||
845 | OUT_RING( (box[i].x2-1) | ((box[i].y2-1)<<16) ); | ||
846 | ADVANCE_LP_RING(); | ||
847 | } | ||
848 | |||
849 | BEGIN_LP_RING(4); | ||
850 | OUT_RING( CMD_OP_BATCH_BUFFER ); | ||
851 | OUT_RING( start | BB1_PROTECTED ); | ||
852 | OUT_RING( start + used - 4 ); | ||
853 | OUT_RING( 0 ); | ||
854 | ADVANCE_LP_RING(); | ||
855 | |||
856 | } while (++i < nbox); | ||
857 | } | ||
858 | |||
859 | if (discard) { | ||
860 | dev_priv->counter++; | ||
861 | |||
862 | (void) cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, | ||
863 | I810_BUF_HARDWARE); | ||
864 | |||
865 | BEGIN_LP_RING(8); | ||
866 | OUT_RING( CMD_STORE_DWORD_IDX ); | ||
867 | OUT_RING( 20 ); | ||
868 | OUT_RING( dev_priv->counter ); | ||
869 | OUT_RING( CMD_STORE_DWORD_IDX ); | ||
870 | OUT_RING( buf_priv->my_use_idx ); | ||
871 | OUT_RING( I810_BUF_FREE ); | ||
872 | OUT_RING( CMD_REPORT_HEAD ); | ||
873 | OUT_RING( 0 ); | ||
874 | ADVANCE_LP_RING(); | ||
875 | } | ||
876 | } | ||
877 | |||
878 | static void i810_dma_dispatch_flip( drm_device_t *dev ) | ||
879 | { | ||
880 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
881 | int pitch = dev_priv->pitch; | ||
882 | RING_LOCALS; | ||
883 | |||
884 | DRM_DEBUG( "%s: page=%d pfCurrentPage=%d\n", | ||
885 | __FUNCTION__, | ||
886 | dev_priv->current_page, | ||
887 | dev_priv->sarea_priv->pf_current_page); | ||
888 | |||
889 | i810_kernel_lost_context(dev); | ||
890 | |||
891 | BEGIN_LP_RING( 2 ); | ||
892 | OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); | ||
893 | OUT_RING( 0 ); | ||
894 | ADVANCE_LP_RING(); | ||
895 | |||
896 | BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 ); | ||
897 | /* On i815 at least ASYNC is buggy */ | ||
898 | /* pitch<<5 is from 11.2.8 p158, | ||
899 | its the pitch / 8 then left shifted 8, | ||
900 | so (pitch >> 3) << 8 */ | ||
901 | OUT_RING( CMD_OP_FRONTBUFFER_INFO | (pitch<<5) /*| ASYNC_FLIP */ ); | ||
902 | if ( dev_priv->current_page == 0 ) { | ||
903 | OUT_RING( dev_priv->back_offset ); | ||
904 | dev_priv->current_page = 1; | ||
905 | } else { | ||
906 | OUT_RING( dev_priv->front_offset ); | ||
907 | dev_priv->current_page = 0; | ||
908 | } | ||
909 | OUT_RING(0); | ||
910 | ADVANCE_LP_RING(); | ||
911 | |||
912 | BEGIN_LP_RING(2); | ||
913 | OUT_RING( CMD_OP_WAIT_FOR_EVENT | WAIT_FOR_PLANE_A_FLIP ); | ||
914 | OUT_RING( 0 ); | ||
915 | ADVANCE_LP_RING(); | ||
916 | |||
917 | /* Increment the frame counter. The client-side 3D driver must | ||
918 | * throttle the framerate by waiting for this value before | ||
919 | * performing the swapbuffer ioctl. | ||
920 | */ | ||
921 | dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; | ||
922 | |||
923 | } | ||
924 | |||
925 | static void i810_dma_quiescent(drm_device_t *dev) | ||
926 | { | ||
927 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
928 | RING_LOCALS; | ||
929 | |||
930 | /* printk("%s\n", __FUNCTION__); */ | ||
931 | |||
932 | i810_kernel_lost_context(dev); | ||
933 | |||
934 | BEGIN_LP_RING(4); | ||
935 | OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); | ||
936 | OUT_RING( CMD_REPORT_HEAD ); | ||
937 | OUT_RING( 0 ); | ||
938 | OUT_RING( 0 ); | ||
939 | ADVANCE_LP_RING(); | ||
940 | |||
941 | i810_wait_ring( dev, dev_priv->ring.Size - 8 ); | ||
942 | } | ||
943 | |||
944 | static int i810_flush_queue(drm_device_t *dev) | ||
945 | { | ||
946 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
947 | drm_device_dma_t *dma = dev->dma; | ||
948 | int i, ret = 0; | ||
949 | RING_LOCALS; | ||
950 | |||
951 | /* printk("%s\n", __FUNCTION__); */ | ||
952 | |||
953 | i810_kernel_lost_context(dev); | ||
954 | |||
955 | BEGIN_LP_RING(2); | ||
956 | OUT_RING( CMD_REPORT_HEAD ); | ||
957 | OUT_RING( 0 ); | ||
958 | ADVANCE_LP_RING(); | ||
959 | |||
960 | i810_wait_ring( dev, dev_priv->ring.Size - 8 ); | ||
961 | |||
962 | for (i = 0; i < dma->buf_count; i++) { | ||
963 | drm_buf_t *buf = dma->buflist[ i ]; | ||
964 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
965 | |||
966 | int used = cmpxchg(buf_priv->in_use, I810_BUF_HARDWARE, | ||
967 | I810_BUF_FREE); | ||
968 | |||
969 | if (used == I810_BUF_HARDWARE) | ||
970 | DRM_DEBUG("reclaimed from HARDWARE\n"); | ||
971 | if (used == I810_BUF_CLIENT) | ||
972 | DRM_DEBUG("still on client\n"); | ||
973 | } | ||
974 | |||
975 | return ret; | ||
976 | } | ||
977 | |||
978 | /* Must be called with the lock held */ | ||
979 | void i810_reclaim_buffers(drm_device_t *dev, struct file *filp) | ||
980 | { | ||
981 | drm_device_dma_t *dma = dev->dma; | ||
982 | int i; | ||
983 | |||
984 | if (!dma) return; | ||
985 | if (!dev->dev_private) return; | ||
986 | if (!dma->buflist) return; | ||
987 | |||
988 | i810_flush_queue(dev); | ||
989 | |||
990 | for (i = 0; i < dma->buf_count; i++) { | ||
991 | drm_buf_t *buf = dma->buflist[ i ]; | ||
992 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
993 | |||
994 | if (buf->filp == filp && buf_priv) { | ||
995 | int used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, | ||
996 | I810_BUF_FREE); | ||
997 | |||
998 | if (used == I810_BUF_CLIENT) | ||
999 | DRM_DEBUG("reclaimed from client\n"); | ||
1000 | if (buf_priv->currently_mapped == I810_BUF_MAPPED) | ||
1001 | buf_priv->currently_mapped = I810_BUF_UNMAPPED; | ||
1002 | } | ||
1003 | } | ||
1004 | } | ||
1005 | |||
1006 | int i810_flush_ioctl(struct inode *inode, struct file *filp, | ||
1007 | unsigned int cmd, unsigned long arg) | ||
1008 | { | ||
1009 | drm_file_t *priv = filp->private_data; | ||
1010 | drm_device_t *dev = priv->head->dev; | ||
1011 | |||
1012 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1013 | |||
1014 | i810_flush_queue(dev); | ||
1015 | return 0; | ||
1016 | } | ||
1017 | |||
1018 | |||
1019 | static int i810_dma_vertex(struct inode *inode, struct file *filp, | ||
1020 | unsigned int cmd, unsigned long arg) | ||
1021 | { | ||
1022 | drm_file_t *priv = filp->private_data; | ||
1023 | drm_device_t *dev = priv->head->dev; | ||
1024 | drm_device_dma_t *dma = dev->dma; | ||
1025 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1026 | u32 *hw_status = dev_priv->hw_status_page; | ||
1027 | drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) | ||
1028 | dev_priv->sarea_priv; | ||
1029 | drm_i810_vertex_t vertex; | ||
1030 | |||
1031 | if (copy_from_user(&vertex, (drm_i810_vertex_t __user *)arg, sizeof(vertex))) | ||
1032 | return -EFAULT; | ||
1033 | |||
1034 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1035 | |||
1036 | DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n", | ||
1037 | vertex.idx, vertex.used, vertex.discard); | ||
1038 | |||
1039 | if (vertex.idx < 0 || vertex.idx > dma->buf_count) | ||
1040 | return -EINVAL; | ||
1041 | |||
1042 | i810_dma_dispatch_vertex( dev, | ||
1043 | dma->buflist[ vertex.idx ], | ||
1044 | vertex.discard, vertex.used ); | ||
1045 | |||
1046 | atomic_add(vertex.used, &dev->counts[_DRM_STAT_SECONDARY]); | ||
1047 | atomic_inc(&dev->counts[_DRM_STAT_DMA]); | ||
1048 | sarea_priv->last_enqueue = dev_priv->counter-1; | ||
1049 | sarea_priv->last_dispatch = (int) hw_status[5]; | ||
1050 | |||
1051 | return 0; | ||
1052 | } | ||
1053 | |||
1054 | |||
1055 | |||
1056 | static int i810_clear_bufs(struct inode *inode, struct file *filp, | ||
1057 | unsigned int cmd, unsigned long arg) | ||
1058 | { | ||
1059 | drm_file_t *priv = filp->private_data; | ||
1060 | drm_device_t *dev = priv->head->dev; | ||
1061 | drm_i810_clear_t clear; | ||
1062 | |||
1063 | if (copy_from_user(&clear, (drm_i810_clear_t __user *)arg, sizeof(clear))) | ||
1064 | return -EFAULT; | ||
1065 | |||
1066 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1067 | |||
1068 | /* GH: Someone's doing nasty things... */ | ||
1069 | if (!dev->dev_private) { | ||
1070 | return -EINVAL; | ||
1071 | } | ||
1072 | |||
1073 | i810_dma_dispatch_clear( dev, clear.flags, | ||
1074 | clear.clear_color, | ||
1075 | clear.clear_depth ); | ||
1076 | return 0; | ||
1077 | } | ||
1078 | |||
1079 | static int i810_swap_bufs(struct inode *inode, struct file *filp, | ||
1080 | unsigned int cmd, unsigned long arg) | ||
1081 | { | ||
1082 | drm_file_t *priv = filp->private_data; | ||
1083 | drm_device_t *dev = priv->head->dev; | ||
1084 | |||
1085 | DRM_DEBUG("i810_swap_bufs\n"); | ||
1086 | |||
1087 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1088 | |||
1089 | i810_dma_dispatch_swap( dev ); | ||
1090 | return 0; | ||
1091 | } | ||
1092 | |||
1093 | static int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd, | ||
1094 | unsigned long arg) | ||
1095 | { | ||
1096 | drm_file_t *priv = filp->private_data; | ||
1097 | drm_device_t *dev = priv->head->dev; | ||
1098 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1099 | u32 *hw_status = dev_priv->hw_status_page; | ||
1100 | drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) | ||
1101 | dev_priv->sarea_priv; | ||
1102 | |||
1103 | sarea_priv->last_dispatch = (int) hw_status[5]; | ||
1104 | return 0; | ||
1105 | } | ||
1106 | |||
1107 | static int i810_getbuf(struct inode *inode, struct file *filp, unsigned int cmd, | ||
1108 | unsigned long arg) | ||
1109 | { | ||
1110 | drm_file_t *priv = filp->private_data; | ||
1111 | drm_device_t *dev = priv->head->dev; | ||
1112 | int retcode = 0; | ||
1113 | drm_i810_dma_t d; | ||
1114 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1115 | u32 *hw_status = dev_priv->hw_status_page; | ||
1116 | drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) | ||
1117 | dev_priv->sarea_priv; | ||
1118 | |||
1119 | if (copy_from_user(&d, (drm_i810_dma_t __user *)arg, sizeof(d))) | ||
1120 | return -EFAULT; | ||
1121 | |||
1122 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1123 | |||
1124 | d.granted = 0; | ||
1125 | |||
1126 | retcode = i810_dma_get_buffer(dev, &d, filp); | ||
1127 | |||
1128 | DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n", | ||
1129 | current->pid, retcode, d.granted); | ||
1130 | |||
1131 | if (copy_to_user((drm_dma_t __user *)arg, &d, sizeof(d))) | ||
1132 | return -EFAULT; | ||
1133 | sarea_priv->last_dispatch = (int) hw_status[5]; | ||
1134 | |||
1135 | return retcode; | ||
1136 | } | ||
1137 | |||
1138 | static int i810_copybuf(struct inode *inode, | ||
1139 | struct file *filp, unsigned int cmd, unsigned long arg) | ||
1140 | { | ||
1141 | /* Never copy - 2.4.x doesn't need it */ | ||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | static int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd, | ||
1146 | unsigned long arg) | ||
1147 | { | ||
1148 | /* Never copy - 2.4.x doesn't need it */ | ||
1149 | return 0; | ||
1150 | } | ||
1151 | |||
1152 | static void i810_dma_dispatch_mc(drm_device_t *dev, drm_buf_t *buf, int used, | ||
1153 | unsigned int last_render) | ||
1154 | { | ||
1155 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
1156 | drm_i810_buf_priv_t *buf_priv = buf->dev_private; | ||
1157 | drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; | ||
1158 | unsigned long address = (unsigned long)buf->bus_address; | ||
1159 | unsigned long start = address - dev->agp->base; | ||
1160 | int u; | ||
1161 | RING_LOCALS; | ||
1162 | |||
1163 | i810_kernel_lost_context(dev); | ||
1164 | |||
1165 | u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, | ||
1166 | I810_BUF_HARDWARE); | ||
1167 | if (u != I810_BUF_CLIENT) { | ||
1168 | DRM_DEBUG("MC found buffer that isn't mine!\n"); | ||
1169 | } | ||
1170 | |||
1171 | if (used > 4*1024) | ||
1172 | used = 0; | ||
1173 | |||
1174 | sarea_priv->dirty = 0x7f; | ||
1175 | |||
1176 | DRM_DEBUG("dispatch mc addr 0x%lx, used 0x%x\n", | ||
1177 | address, used); | ||
1178 | |||
1179 | dev_priv->counter++; | ||
1180 | DRM_DEBUG("dispatch counter : %ld\n", dev_priv->counter); | ||
1181 | DRM_DEBUG("i810_dma_dispatch_mc\n"); | ||
1182 | DRM_DEBUG("start : %lx\n", start); | ||
1183 | DRM_DEBUG("used : %d\n", used); | ||
1184 | DRM_DEBUG("start + used - 4 : %ld\n", start + used - 4); | ||
1185 | |||
1186 | if (buf_priv->currently_mapped == I810_BUF_MAPPED) { | ||
1187 | if (used & 4) { | ||
1188 | *(u32 *)((u32)buf_priv->virtual + used) = 0; | ||
1189 | used += 4; | ||
1190 | } | ||
1191 | |||
1192 | i810_unmap_buffer(buf); | ||
1193 | } | ||
1194 | BEGIN_LP_RING(4); | ||
1195 | OUT_RING( CMD_OP_BATCH_BUFFER ); | ||
1196 | OUT_RING( start | BB1_PROTECTED ); | ||
1197 | OUT_RING( start + used - 4 ); | ||
1198 | OUT_RING( 0 ); | ||
1199 | ADVANCE_LP_RING(); | ||
1200 | |||
1201 | |||
1202 | BEGIN_LP_RING(8); | ||
1203 | OUT_RING( CMD_STORE_DWORD_IDX ); | ||
1204 | OUT_RING( buf_priv->my_use_idx ); | ||
1205 | OUT_RING( I810_BUF_FREE ); | ||
1206 | OUT_RING( 0 ); | ||
1207 | |||
1208 | OUT_RING( CMD_STORE_DWORD_IDX ); | ||
1209 | OUT_RING( 16 ); | ||
1210 | OUT_RING( last_render ); | ||
1211 | OUT_RING( 0 ); | ||
1212 | ADVANCE_LP_RING(); | ||
1213 | } | ||
1214 | |||
1215 | static int i810_dma_mc(struct inode *inode, struct file *filp, | ||
1216 | unsigned int cmd, unsigned long arg) | ||
1217 | { | ||
1218 | drm_file_t *priv = filp->private_data; | ||
1219 | drm_device_t *dev = priv->head->dev; | ||
1220 | drm_device_dma_t *dma = dev->dma; | ||
1221 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1222 | u32 *hw_status = dev_priv->hw_status_page; | ||
1223 | drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) | ||
1224 | dev_priv->sarea_priv; | ||
1225 | drm_i810_mc_t mc; | ||
1226 | |||
1227 | if (copy_from_user(&mc, (drm_i810_mc_t __user *)arg, sizeof(mc))) | ||
1228 | return -EFAULT; | ||
1229 | |||
1230 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1231 | |||
1232 | if (mc.idx >= dma->buf_count || mc.idx < 0) | ||
1233 | return -EINVAL; | ||
1234 | |||
1235 | i810_dma_dispatch_mc(dev, dma->buflist[mc.idx], mc.used, | ||
1236 | mc.last_render ); | ||
1237 | |||
1238 | atomic_add(mc.used, &dev->counts[_DRM_STAT_SECONDARY]); | ||
1239 | atomic_inc(&dev->counts[_DRM_STAT_DMA]); | ||
1240 | sarea_priv->last_enqueue = dev_priv->counter-1; | ||
1241 | sarea_priv->last_dispatch = (int) hw_status[5]; | ||
1242 | |||
1243 | return 0; | ||
1244 | } | ||
1245 | |||
1246 | static int i810_rstatus(struct inode *inode, struct file *filp, | ||
1247 | unsigned int cmd, unsigned long arg) | ||
1248 | { | ||
1249 | drm_file_t *priv = filp->private_data; | ||
1250 | drm_device_t *dev = priv->head->dev; | ||
1251 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1252 | |||
1253 | return (int)(((u32 *)(dev_priv->hw_status_page))[4]); | ||
1254 | } | ||
1255 | |||
1256 | static int i810_ov0_info(struct inode *inode, struct file *filp, | ||
1257 | unsigned int cmd, unsigned long arg) | ||
1258 | { | ||
1259 | drm_file_t *priv = filp->private_data; | ||
1260 | drm_device_t *dev = priv->head->dev; | ||
1261 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1262 | drm_i810_overlay_t data; | ||
1263 | |||
1264 | data.offset = dev_priv->overlay_offset; | ||
1265 | data.physical = dev_priv->overlay_physical; | ||
1266 | if (copy_to_user((drm_i810_overlay_t __user *)arg,&data,sizeof(data))) | ||
1267 | return -EFAULT; | ||
1268 | return 0; | ||
1269 | } | ||
1270 | |||
1271 | static int i810_fstatus(struct inode *inode, struct file *filp, | ||
1272 | unsigned int cmd, unsigned long arg) | ||
1273 | { | ||
1274 | drm_file_t *priv = filp->private_data; | ||
1275 | drm_device_t *dev = priv->head->dev; | ||
1276 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1277 | |||
1278 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1279 | |||
1280 | return I810_READ(0x30008); | ||
1281 | } | ||
1282 | |||
1283 | static int i810_ov0_flip(struct inode *inode, struct file *filp, | ||
1284 | unsigned int cmd, unsigned long arg) | ||
1285 | { | ||
1286 | drm_file_t *priv = filp->private_data; | ||
1287 | drm_device_t *dev = priv->head->dev; | ||
1288 | drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; | ||
1289 | |||
1290 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1291 | |||
1292 | //Tell the overlay to update | ||
1293 | I810_WRITE(0x30000,dev_priv->overlay_physical | 0x80000000); | ||
1294 | |||
1295 | return 0; | ||
1296 | } | ||
1297 | |||
1298 | |||
1299 | /* Not sure why this isn't set all the time: | ||
1300 | */ | ||
1301 | static void i810_do_init_pageflip( drm_device_t *dev ) | ||
1302 | { | ||
1303 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
1304 | |||
1305 | DRM_DEBUG("%s\n", __FUNCTION__); | ||
1306 | dev_priv->page_flipping = 1; | ||
1307 | dev_priv->current_page = 0; | ||
1308 | dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; | ||
1309 | } | ||
1310 | |||
1311 | static int i810_do_cleanup_pageflip( drm_device_t *dev ) | ||
1312 | { | ||
1313 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
1314 | |||
1315 | DRM_DEBUG("%s\n", __FUNCTION__); | ||
1316 | if (dev_priv->current_page != 0) | ||
1317 | i810_dma_dispatch_flip( dev ); | ||
1318 | |||
1319 | dev_priv->page_flipping = 0; | ||
1320 | return 0; | ||
1321 | } | ||
1322 | |||
1323 | static int i810_flip_bufs(struct inode *inode, struct file *filp, | ||
1324 | unsigned int cmd, unsigned long arg) | ||
1325 | { | ||
1326 | drm_file_t *priv = filp->private_data; | ||
1327 | drm_device_t *dev = priv->head->dev; | ||
1328 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
1329 | |||
1330 | DRM_DEBUG("%s\n", __FUNCTION__); | ||
1331 | |||
1332 | LOCK_TEST_WITH_RETURN(dev, filp); | ||
1333 | |||
1334 | if (!dev_priv->page_flipping) | ||
1335 | i810_do_init_pageflip( dev ); | ||
1336 | |||
1337 | i810_dma_dispatch_flip( dev ); | ||
1338 | return 0; | ||
1339 | } | ||
1340 | |||
1341 | void i810_driver_pretakedown(drm_device_t *dev) | ||
1342 | { | ||
1343 | i810_dma_cleanup( dev ); | ||
1344 | } | ||
1345 | |||
1346 | void i810_driver_prerelease(drm_device_t *dev, DRMFILE filp) | ||
1347 | { | ||
1348 | if (dev->dev_private) { | ||
1349 | drm_i810_private_t *dev_priv = dev->dev_private; | ||
1350 | if (dev_priv->page_flipping) { | ||
1351 | i810_do_cleanup_pageflip(dev); | ||
1352 | } | ||
1353 | } | ||
1354 | } | ||
1355 | |||
1356 | void i810_driver_release(drm_device_t *dev, struct file *filp) | ||
1357 | { | ||
1358 | i810_reclaim_buffers(dev, filp); | ||
1359 | } | ||
1360 | |||
1361 | int i810_driver_dma_quiescent(drm_device_t *dev) | ||
1362 | { | ||
1363 | i810_dma_quiescent( dev ); | ||
1364 | return 0; | ||
1365 | } | ||
1366 | |||
1367 | drm_ioctl_desc_t i810_ioctls[] = { | ||
1368 | [DRM_IOCTL_NR(DRM_I810_INIT)] = { i810_dma_init, 1, 1 }, | ||
1369 | [DRM_IOCTL_NR(DRM_I810_VERTEX)] = { i810_dma_vertex, 1, 0 }, | ||
1370 | [DRM_IOCTL_NR(DRM_I810_CLEAR)] = { i810_clear_bufs, 1, 0 }, | ||
1371 | [DRM_IOCTL_NR(DRM_I810_FLUSH)] = { i810_flush_ioctl, 1, 0 }, | ||
1372 | [DRM_IOCTL_NR(DRM_I810_GETAGE)] = { i810_getage, 1, 0 }, | ||
1373 | [DRM_IOCTL_NR(DRM_I810_GETBUF)] = { i810_getbuf, 1, 0 }, | ||
1374 | [DRM_IOCTL_NR(DRM_I810_SWAP)] = { i810_swap_bufs, 1, 0 }, | ||
1375 | [DRM_IOCTL_NR(DRM_I810_COPY)] = { i810_copybuf, 1, 0 }, | ||
1376 | [DRM_IOCTL_NR(DRM_I810_DOCOPY)] = { i810_docopy, 1, 0 }, | ||
1377 | [DRM_IOCTL_NR(DRM_I810_OV0INFO)] = { i810_ov0_info, 1, 0 }, | ||
1378 | [DRM_IOCTL_NR(DRM_I810_FSTATUS)] = { i810_fstatus, 1, 0 }, | ||
1379 | [DRM_IOCTL_NR(DRM_I810_OV0FLIP)] = { i810_ov0_flip, 1, 0 }, | ||
1380 | [DRM_IOCTL_NR(DRM_I810_MC)] = { i810_dma_mc, 1, 1 }, | ||
1381 | [DRM_IOCTL_NR(DRM_I810_RSTATUS)] = { i810_rstatus, 1, 0 }, | ||
1382 | [DRM_IOCTL_NR(DRM_I810_FLIP)] = { i810_flip_bufs, 1, 0 } | ||
1383 | }; | ||
1384 | |||
1385 | int i810_max_ioctl = DRM_ARRAY_SIZE(i810_ioctls); | ||