Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* i915_dma.c -- DMA support for the I915 -*- linux-c -*- |
2 | */ | |
3 | /************************************************************************** | |
bc54fd1a | 4 | * |
1da177e4 LT |
5 | * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. |
6 | * All Rights Reserved. | |
bc54fd1a DA |
7 | * |
8 | * Permission is hereby granted, free of charge, to any person obtaining a | |
9 | * copy of this software and associated documentation files (the | |
10 | * "Software"), to deal in the Software without restriction, including | |
11 | * without limitation the rights to use, copy, modify, merge, publish, | |
12 | * distribute, sub license, and/or sell copies of the Software, and to | |
13 | * permit persons to whom the Software is furnished to do so, subject to | |
14 | * the following conditions: | |
15 | * | |
16 | * The above copyright notice and this permission notice (including the | |
17 | * next paragraph) shall be included in all copies or substantial portions | |
18 | * of the Software. | |
19 | * | |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. | |
23 | * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR | |
24 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
25 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
26 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
27 | * | |
1da177e4 LT |
28 | **************************************************************************/ |
29 | ||
30 | #include "drmP.h" | |
31 | #include "drm.h" | |
32 | #include "i915_drm.h" | |
33 | #include "i915_drv.h" | |
34 | ||
35 | drm_ioctl_desc_t i915_ioctls[] = { | |
36 | [DRM_IOCTL_NR(DRM_I915_INIT)] = {i915_dma_init, 1, 1}, | |
37 | [DRM_IOCTL_NR(DRM_I915_FLUSH)] = {i915_flush_ioctl, 1, 0}, | |
38 | [DRM_IOCTL_NR(DRM_I915_FLIP)] = {i915_flip_bufs, 1, 0}, | |
39 | [DRM_IOCTL_NR(DRM_I915_BATCHBUFFER)] = {i915_batchbuffer, 1, 0}, | |
40 | [DRM_IOCTL_NR(DRM_I915_IRQ_EMIT)] = {i915_irq_emit, 1, 0}, | |
41 | [DRM_IOCTL_NR(DRM_I915_IRQ_WAIT)] = {i915_irq_wait, 1, 0}, | |
42 | [DRM_IOCTL_NR(DRM_I915_GETPARAM)] = {i915_getparam, 1, 0}, | |
43 | [DRM_IOCTL_NR(DRM_I915_SETPARAM)] = {i915_setparam, 1, 1}, | |
44 | [DRM_IOCTL_NR(DRM_I915_ALLOC)] = {i915_mem_alloc, 1, 0}, | |
45 | [DRM_IOCTL_NR(DRM_I915_FREE)] = {i915_mem_free, 1, 0}, | |
46 | [DRM_IOCTL_NR(DRM_I915_INIT_HEAP)] = {i915_mem_init_heap, 1, 1}, | |
47 | [DRM_IOCTL_NR(DRM_I915_CMDBUFFER)] = {i915_cmdbuffer, 1, 0} | |
48 | }; | |
49 | ||
50 | int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); | |
51 | ||
52 | /* Really want an OS-independent resettable timer. Would like to have | |
53 | * this loop run for (eg) 3 sec, but have the timer reset every time | |
54 | * the head pointer changes, so that EBUSY only happens if the ring | |
55 | * actually stalls for (eg) 3 seconds. | |
56 | */ | |
57 | int i915_wait_ring(drm_device_t * dev, int n, const char *caller) | |
58 | { | |
59 | drm_i915_private_t *dev_priv = dev->dev_private; | |
60 | drm_i915_ring_buffer_t *ring = &(dev_priv->ring); | |
61 | u32 last_head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR; | |
62 | int i; | |
63 | ||
64 | for (i = 0; i < 10000; i++) { | |
65 | ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR; | |
66 | ring->space = ring->head - (ring->tail + 8); | |
67 | if (ring->space < 0) | |
68 | ring->space += ring->Size; | |
69 | if (ring->space >= n) | |
70 | return 0; | |
71 | ||
72 | dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; | |
73 | ||
74 | if (ring->head != last_head) | |
75 | i = 0; | |
76 | ||
77 | last_head = ring->head; | |
78 | } | |
79 | ||
80 | return DRM_ERR(EBUSY); | |
81 | } | |
82 | ||
83 | void i915_kernel_lost_context(drm_device_t * dev) | |
84 | { | |
85 | drm_i915_private_t *dev_priv = dev->dev_private; | |
86 | drm_i915_ring_buffer_t *ring = &(dev_priv->ring); | |
87 | ||
88 | ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR; | |
89 | ring->tail = I915_READ(LP_RING + RING_TAIL) & TAIL_ADDR; | |
90 | ring->space = ring->head - (ring->tail + 8); | |
91 | if (ring->space < 0) | |
92 | ring->space += ring->Size; | |
93 | ||
94 | if (ring->head == ring->tail) | |
95 | dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY; | |
96 | } | |
97 | ||
98 | int i915_dma_cleanup(drm_device_t * dev) | |
99 | { | |
100 | /* Make sure interrupts are disabled here because the uninstall ioctl | |
101 | * may not have been called from userspace and after dev_private | |
102 | * is freed, it's too late. | |
103 | */ | |
104 | if (dev->irq) | |
105 | drm_irq_uninstall (dev); | |
106 | ||
107 | if (dev->dev_private) { | |
108 | drm_i915_private_t *dev_priv = | |
109 | (drm_i915_private_t *) dev->dev_private; | |
110 | ||
111 | if (dev_priv->ring.virtual_start) { | |
112 | drm_core_ioremapfree( &dev_priv->ring.map, dev); | |
113 | } | |
114 | ||
115 | if (dev_priv->hw_status_page) { | |
116 | drm_pci_free(dev, PAGE_SIZE, dev_priv->hw_status_page, | |
117 | dev_priv->dma_status_page); | |
118 | /* Need to rewrite hardware status page */ | |
119 | I915_WRITE(0x02080, 0x1ffff000); | |
120 | } | |
121 | ||
122 | drm_free (dev->dev_private, sizeof(drm_i915_private_t), | |
123 | DRM_MEM_DRIVER); | |
124 | ||
125 | dev->dev_private = NULL; | |
126 | } | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static int i915_initialize(drm_device_t * dev, | |
132 | drm_i915_private_t * dev_priv, | |
133 | drm_i915_init_t * init) | |
134 | { | |
135 | memset(dev_priv, 0, sizeof(drm_i915_private_t)); | |
136 | ||
137 | DRM_GETSAREA(); | |
138 | if (!dev_priv->sarea) { | |
139 | DRM_ERROR("can not find sarea!\n"); | |
140 | dev->dev_private = (void *)dev_priv; | |
141 | i915_dma_cleanup(dev); | |
142 | return DRM_ERR(EINVAL); | |
143 | } | |
144 | ||
145 | dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset); | |
146 | if (!dev_priv->mmio_map) { | |
147 | dev->dev_private = (void *)dev_priv; | |
148 | i915_dma_cleanup(dev); | |
149 | DRM_ERROR("can not find mmio map!\n"); | |
150 | return DRM_ERR(EINVAL); | |
151 | } | |
152 | ||
153 | dev_priv->sarea_priv = (drm_i915_sarea_t *) | |
154 | ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset); | |
155 | ||
156 | dev_priv->ring.Start = init->ring_start; | |
157 | dev_priv->ring.End = init->ring_end; | |
158 | dev_priv->ring.Size = init->ring_size; | |
159 | dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; | |
160 | ||
161 | dev_priv->ring.map.offset = init->ring_start; | |
162 | dev_priv->ring.map.size = init->ring_size; | |
163 | dev_priv->ring.map.type = 0; | |
164 | dev_priv->ring.map.flags = 0; | |
165 | dev_priv->ring.map.mtrr = 0; | |
166 | ||
167 | drm_core_ioremap( &dev_priv->ring.map, dev ); | |
168 | ||
169 | if (dev_priv->ring.map.handle == NULL) { | |
170 | dev->dev_private = (void *)dev_priv; | |
171 | i915_dma_cleanup(dev); | |
172 | DRM_ERROR("can not ioremap virtual address for" | |
173 | " ring buffer\n"); | |
174 | return DRM_ERR(ENOMEM); | |
175 | } | |
176 | ||
177 | dev_priv->ring.virtual_start = dev_priv->ring.map.handle; | |
178 | ||
179 | dev_priv->back_offset = init->back_offset; | |
180 | dev_priv->front_offset = init->front_offset; | |
181 | dev_priv->current_page = 0; | |
182 | dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; | |
183 | ||
184 | /* We are using separate values as placeholders for mechanisms for | |
185 | * private backbuffer/depthbuffer usage. | |
186 | */ | |
187 | dev_priv->use_mi_batchbuffer_start = 0; | |
188 | ||
189 | /* Allow hardware batchbuffers unless told otherwise. | |
190 | */ | |
191 | dev_priv->allow_batchbuffer = 1; | |
192 | ||
193 | /* Program Hardware Status Page */ | |
194 | dev_priv->hw_status_page = drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, | |
195 | 0xffffffff, | |
196 | &dev_priv->dma_status_page); | |
197 | ||
198 | if (!dev_priv->hw_status_page) { | |
199 | dev->dev_private = (void *)dev_priv; | |
200 | i915_dma_cleanup(dev); | |
201 | DRM_ERROR("Can not allocate hardware status page\n"); | |
202 | return DRM_ERR(ENOMEM); | |
203 | } | |
204 | memset(dev_priv->hw_status_page, 0, PAGE_SIZE); | |
205 | DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); | |
206 | ||
207 | I915_WRITE(0x02080, dev_priv->dma_status_page); | |
208 | DRM_DEBUG("Enabled hardware status page\n"); | |
209 | ||
210 | dev->dev_private = (void *)dev_priv; | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | static int i915_resume(drm_device_t * dev) | |
216 | { | |
217 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
218 | ||
219 | DRM_DEBUG("%s\n", __FUNCTION__); | |
220 | ||
221 | if (!dev_priv->sarea) { | |
222 | DRM_ERROR("can not find sarea!\n"); | |
223 | return DRM_ERR(EINVAL); | |
224 | } | |
225 | ||
226 | if (!dev_priv->mmio_map) { | |
227 | DRM_ERROR("can not find mmio map!\n"); | |
228 | return DRM_ERR(EINVAL); | |
229 | } | |
230 | ||
231 | if (dev_priv->ring.map.handle == NULL) { | |
232 | DRM_ERROR("can not ioremap virtual address for" | |
233 | " ring buffer\n"); | |
234 | return DRM_ERR(ENOMEM); | |
235 | } | |
236 | ||
237 | /* Program Hardware Status Page */ | |
238 | if (!dev_priv->hw_status_page) { | |
239 | DRM_ERROR("Can not find hardware status page\n"); | |
240 | return DRM_ERR(EINVAL); | |
241 | } | |
242 | DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); | |
243 | ||
244 | I915_WRITE(0x02080, dev_priv->dma_status_page); | |
245 | DRM_DEBUG("Enabled hardware status page\n"); | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | int i915_dma_init(DRM_IOCTL_ARGS) | |
251 | { | |
252 | DRM_DEVICE; | |
253 | drm_i915_private_t *dev_priv; | |
254 | drm_i915_init_t init; | |
255 | int retcode = 0; | |
256 | ||
257 | DRM_COPY_FROM_USER_IOCTL(init, (drm_i915_init_t __user *) data, | |
258 | sizeof(init)); | |
259 | ||
260 | switch (init.func) { | |
261 | case I915_INIT_DMA: | |
262 | dev_priv = drm_alloc (sizeof(drm_i915_private_t), | |
263 | DRM_MEM_DRIVER); | |
264 | if (dev_priv == NULL) | |
265 | return DRM_ERR(ENOMEM); | |
266 | retcode = i915_initialize(dev, dev_priv, &init); | |
267 | break; | |
268 | case I915_CLEANUP_DMA: | |
269 | retcode = i915_dma_cleanup(dev); | |
270 | break; | |
271 | case I915_RESUME_DMA: | |
272 | retcode = i915_resume(dev); | |
273 | break; | |
274 | default: | |
275 | retcode = -EINVAL; | |
276 | break; | |
277 | } | |
278 | ||
279 | return retcode; | |
280 | } | |
281 | ||
282 | /* Implement basically the same security restrictions as hardware does | |
283 | * for MI_BATCH_NON_SECURE. These can be made stricter at any time. | |
284 | * | |
285 | * Most of the calculations below involve calculating the size of a | |
286 | * particular instruction. It's important to get the size right as | |
287 | * that tells us where the next instruction to check is. Any illegal | |
288 | * instruction detected will be given a size of zero, which is a | |
289 | * signal to abort the rest of the buffer. | |
290 | */ | |
291 | static int do_validate_cmd(int cmd) | |
292 | { | |
293 | switch (((cmd >> 29) & 0x7)) { | |
294 | case 0x0: | |
295 | switch ((cmd >> 23) & 0x3f) { | |
296 | case 0x0: | |
297 | return 1; /* MI_NOOP */ | |
298 | case 0x4: | |
299 | return 1; /* MI_FLUSH */ | |
300 | default: | |
301 | return 0; /* disallow everything else */ | |
302 | } | |
303 | break; | |
304 | case 0x1: | |
305 | return 0; /* reserved */ | |
306 | case 0x2: | |
307 | return (cmd & 0xff) + 2; /* 2d commands */ | |
308 | case 0x3: | |
309 | if (((cmd >> 24) & 0x1f) <= 0x18) | |
310 | return 1; | |
311 | ||
312 | switch ((cmd >> 24) & 0x1f) { | |
313 | case 0x1c: | |
314 | return 1; | |
315 | case 0x1d: | |
316 | switch ((cmd>>16)&0xff) { | |
317 | case 0x3: | |
318 | return (cmd & 0x1f) + 2; | |
319 | case 0x4: | |
320 | return (cmd & 0xf) + 2; | |
321 | default: | |
322 | return (cmd & 0xffff) + 2; | |
323 | } | |
324 | case 0x1e: | |
325 | if (cmd & (1 << 23)) | |
326 | return (cmd & 0xffff) + 1; | |
327 | else | |
328 | return 1; | |
329 | case 0x1f: | |
330 | if ((cmd & (1 << 23)) == 0) /* inline vertices */ | |
331 | return (cmd & 0x1ffff) + 2; | |
332 | else if (cmd & (1 << 17)) /* indirect random */ | |
333 | if ((cmd & 0xffff) == 0) | |
334 | return 0; /* unknown length, too hard */ | |
335 | else | |
336 | return (((cmd & 0xffff) + 1) / 2) + 1; | |
337 | else | |
338 | return 2; /* indirect sequential */ | |
339 | default: | |
340 | return 0; | |
341 | } | |
342 | default: | |
343 | return 0; | |
344 | } | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
349 | static int validate_cmd(int cmd) | |
350 | { | |
351 | int ret = do_validate_cmd(cmd); | |
352 | ||
353 | /* printk("validate_cmd( %x ): %d\n", cmd, ret); */ | |
354 | ||
355 | return ret; | |
356 | } | |
357 | ||
358 | static int i915_emit_cmds(drm_device_t * dev, int __user * buffer, int dwords) | |
359 | { | |
360 | drm_i915_private_t *dev_priv = dev->dev_private; | |
361 | int i; | |
362 | RING_LOCALS; | |
363 | ||
364 | for (i = 0; i < dwords;) { | |
365 | int cmd, sz; | |
366 | ||
367 | if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) | |
368 | return DRM_ERR(EINVAL); | |
369 | ||
370 | /* printk("%d/%d ", i, dwords); */ | |
371 | ||
372 | if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) | |
373 | return DRM_ERR(EINVAL); | |
374 | ||
375 | BEGIN_LP_RING(sz); | |
376 | OUT_RING(cmd); | |
377 | ||
378 | while (++i, --sz) { | |
379 | if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], | |
380 | sizeof(cmd))) { | |
381 | return DRM_ERR(EINVAL); | |
382 | } | |
383 | OUT_RING(cmd); | |
384 | } | |
385 | ADVANCE_LP_RING(); | |
386 | } | |
387 | ||
388 | return 0; | |
389 | } | |
390 | ||
391 | static int i915_emit_box(drm_device_t * dev, | |
392 | drm_clip_rect_t __user * boxes, | |
393 | int i, int DR1, int DR4) | |
394 | { | |
395 | drm_i915_private_t *dev_priv = dev->dev_private; | |
396 | drm_clip_rect_t box; | |
397 | RING_LOCALS; | |
398 | ||
399 | if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) { | |
400 | return EFAULT; | |
401 | } | |
402 | ||
403 | if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) { | |
404 | DRM_ERROR("Bad box %d,%d..%d,%d\n", | |
405 | box.x1, box.y1, box.x2, box.y2); | |
406 | return DRM_ERR(EINVAL); | |
407 | } | |
408 | ||
409 | BEGIN_LP_RING(6); | |
410 | OUT_RING(GFX_OP_DRAWRECT_INFO); | |
411 | OUT_RING(DR1); | |
412 | OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); | |
413 | OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); | |
414 | OUT_RING(DR4); | |
415 | OUT_RING(0); | |
416 | ADVANCE_LP_RING(); | |
417 | ||
418 | return 0; | |
419 | } | |
420 | ||
421 | static int i915_dispatch_cmdbuffer(drm_device_t * dev, | |
422 | drm_i915_cmdbuffer_t * cmd) | |
423 | { | |
424 | int nbox = cmd->num_cliprects; | |
425 | int i = 0, count, ret; | |
426 | ||
427 | if (cmd->sz & 0x3) { | |
428 | DRM_ERROR("alignment"); | |
429 | return DRM_ERR(EINVAL); | |
430 | } | |
431 | ||
432 | i915_kernel_lost_context(dev); | |
433 | ||
434 | count = nbox ? nbox : 1; | |
435 | ||
436 | for (i = 0; i < count; i++) { | |
437 | if (i < nbox) { | |
438 | ret = i915_emit_box(dev, cmd->cliprects, i, | |
439 | cmd->DR1, cmd->DR4); | |
440 | if (ret) | |
441 | return ret; | |
442 | } | |
443 | ||
444 | ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4); | |
445 | if (ret) | |
446 | return ret; | |
447 | } | |
448 | ||
449 | return 0; | |
450 | } | |
451 | ||
452 | static int i915_dispatch_batchbuffer(drm_device_t * dev, | |
453 | drm_i915_batchbuffer_t * batch) | |
454 | { | |
455 | drm_i915_private_t *dev_priv = dev->dev_private; | |
456 | drm_clip_rect_t __user *boxes = batch->cliprects; | |
457 | int nbox = batch->num_cliprects; | |
458 | int i = 0, count; | |
459 | RING_LOCALS; | |
460 | ||
461 | if ((batch->start | batch->used) & 0x7) { | |
462 | DRM_ERROR("alignment"); | |
463 | return DRM_ERR(EINVAL); | |
464 | } | |
465 | ||
466 | i915_kernel_lost_context(dev); | |
467 | ||
468 | count = nbox ? nbox : 1; | |
469 | ||
470 | for (i = 0; i < count; i++) { | |
471 | if (i < nbox) { | |
472 | int ret = i915_emit_box(dev, boxes, i, | |
473 | batch->DR1, batch->DR4); | |
474 | if (ret) | |
475 | return ret; | |
476 | } | |
477 | ||
478 | if (dev_priv->use_mi_batchbuffer_start) { | |
479 | BEGIN_LP_RING(2); | |
480 | OUT_RING(MI_BATCH_BUFFER_START | (2 << 6)); | |
481 | OUT_RING(batch->start | MI_BATCH_NON_SECURE); | |
482 | ADVANCE_LP_RING(); | |
483 | } else { | |
484 | BEGIN_LP_RING(4); | |
485 | OUT_RING(MI_BATCH_BUFFER); | |
486 | OUT_RING(batch->start | MI_BATCH_NON_SECURE); | |
487 | OUT_RING(batch->start + batch->used - 4); | |
488 | OUT_RING(0); | |
489 | ADVANCE_LP_RING(); | |
490 | } | |
491 | } | |
492 | ||
493 | dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; | |
494 | ||
495 | BEGIN_LP_RING(4); | |
496 | OUT_RING(CMD_STORE_DWORD_IDX); | |
497 | OUT_RING(20); | |
498 | OUT_RING(dev_priv->counter); | |
499 | OUT_RING(0); | |
500 | ADVANCE_LP_RING(); | |
501 | ||
502 | return 0; | |
503 | } | |
504 | ||
505 | static int i915_dispatch_flip(drm_device_t * dev) | |
506 | { | |
507 | drm_i915_private_t *dev_priv = dev->dev_private; | |
508 | RING_LOCALS; | |
509 | ||
510 | DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n", | |
511 | __FUNCTION__, | |
512 | dev_priv->current_page, | |
513 | dev_priv->sarea_priv->pf_current_page); | |
514 | ||
515 | i915_kernel_lost_context(dev); | |
516 | ||
517 | BEGIN_LP_RING(2); | |
518 | OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE); | |
519 | OUT_RING(0); | |
520 | ADVANCE_LP_RING(); | |
521 | ||
522 | BEGIN_LP_RING(6); | |
523 | OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); | |
524 | OUT_RING(0); | |
525 | if (dev_priv->current_page == 0) { | |
526 | OUT_RING(dev_priv->back_offset); | |
527 | dev_priv->current_page = 1; | |
528 | } else { | |
529 | OUT_RING(dev_priv->front_offset); | |
530 | dev_priv->current_page = 0; | |
531 | } | |
532 | OUT_RING(0); | |
533 | ADVANCE_LP_RING(); | |
534 | ||
535 | BEGIN_LP_RING(2); | |
536 | OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP); | |
537 | OUT_RING(0); | |
538 | ADVANCE_LP_RING(); | |
539 | ||
540 | dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; | |
541 | ||
542 | BEGIN_LP_RING(4); | |
543 | OUT_RING(CMD_STORE_DWORD_IDX); | |
544 | OUT_RING(20); | |
545 | OUT_RING(dev_priv->counter); | |
546 | OUT_RING(0); | |
547 | ADVANCE_LP_RING(); | |
548 | ||
549 | dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; | |
550 | return 0; | |
551 | } | |
552 | ||
553 | static int i915_quiescent(drm_device_t * dev) | |
554 | { | |
555 | drm_i915_private_t *dev_priv = dev->dev_private; | |
556 | ||
557 | i915_kernel_lost_context(dev); | |
558 | return i915_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__); | |
559 | } | |
560 | ||
561 | int i915_flush_ioctl(DRM_IOCTL_ARGS) | |
562 | { | |
563 | DRM_DEVICE; | |
564 | ||
565 | LOCK_TEST_WITH_RETURN(dev, filp); | |
566 | ||
567 | return i915_quiescent(dev); | |
568 | } | |
569 | ||
570 | int i915_batchbuffer(DRM_IOCTL_ARGS) | |
571 | { | |
572 | DRM_DEVICE; | |
573 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
574 | u32 *hw_status = dev_priv->hw_status_page; | |
575 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) | |
576 | dev_priv->sarea_priv; | |
577 | drm_i915_batchbuffer_t batch; | |
578 | int ret; | |
579 | ||
580 | if (!dev_priv->allow_batchbuffer) { | |
581 | DRM_ERROR("Batchbuffer ioctl disabled\n"); | |
582 | return DRM_ERR(EINVAL); | |
583 | } | |
584 | ||
585 | DRM_COPY_FROM_USER_IOCTL(batch, (drm_i915_batchbuffer_t __user *) data, | |
586 | sizeof(batch)); | |
587 | ||
588 | DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n", | |
589 | batch.start, batch.used, batch.num_cliprects); | |
590 | ||
591 | LOCK_TEST_WITH_RETURN(dev, filp); | |
592 | ||
593 | if (batch.num_cliprects && DRM_VERIFYAREA_READ(batch.cliprects, | |
594 | batch.num_cliprects * | |
595 | sizeof(drm_clip_rect_t))) | |
596 | return DRM_ERR(EFAULT); | |
597 | ||
598 | ret = i915_dispatch_batchbuffer(dev, &batch); | |
599 | ||
600 | sarea_priv->last_dispatch = (int)hw_status[5]; | |
601 | return ret; | |
602 | } | |
603 | ||
604 | int i915_cmdbuffer(DRM_IOCTL_ARGS) | |
605 | { | |
606 | DRM_DEVICE; | |
607 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
608 | u32 *hw_status = dev_priv->hw_status_page; | |
609 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) | |
610 | dev_priv->sarea_priv; | |
611 | drm_i915_cmdbuffer_t cmdbuf; | |
612 | int ret; | |
613 | ||
614 | DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_i915_cmdbuffer_t __user *) data, | |
615 | sizeof(cmdbuf)); | |
616 | ||
617 | DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", | |
618 | cmdbuf.buf, cmdbuf.sz, cmdbuf.num_cliprects); | |
619 | ||
620 | LOCK_TEST_WITH_RETURN(dev, filp); | |
621 | ||
622 | if (cmdbuf.num_cliprects && | |
623 | DRM_VERIFYAREA_READ(cmdbuf.cliprects, | |
624 | cmdbuf.num_cliprects * | |
625 | sizeof(drm_clip_rect_t))) { | |
626 | DRM_ERROR("Fault accessing cliprects\n"); | |
627 | return DRM_ERR(EFAULT); | |
628 | } | |
629 | ||
630 | ret = i915_dispatch_cmdbuffer(dev, &cmdbuf); | |
631 | if (ret) { | |
632 | DRM_ERROR("i915_dispatch_cmdbuffer failed\n"); | |
633 | return ret; | |
634 | } | |
635 | ||
636 | sarea_priv->last_dispatch = (int)hw_status[5]; | |
637 | return 0; | |
638 | } | |
639 | ||
640 | int i915_do_cleanup_pageflip(drm_device_t * dev) | |
641 | { | |
642 | drm_i915_private_t *dev_priv = dev->dev_private; | |
643 | ||
644 | DRM_DEBUG("%s\n", __FUNCTION__); | |
645 | if (dev_priv->current_page != 0) | |
646 | i915_dispatch_flip(dev); | |
647 | ||
648 | return 0; | |
649 | } | |
650 | ||
651 | int i915_flip_bufs(DRM_IOCTL_ARGS) | |
652 | { | |
653 | DRM_DEVICE; | |
654 | ||
655 | DRM_DEBUG("%s\n", __FUNCTION__); | |
656 | ||
657 | LOCK_TEST_WITH_RETURN(dev, filp); | |
658 | ||
659 | return i915_dispatch_flip(dev); | |
660 | } | |
661 | ||
662 | int i915_getparam(DRM_IOCTL_ARGS) | |
663 | { | |
664 | DRM_DEVICE; | |
665 | drm_i915_private_t *dev_priv = dev->dev_private; | |
666 | drm_i915_getparam_t param; | |
667 | int value; | |
668 | ||
669 | if (!dev_priv) { | |
670 | DRM_ERROR("%s called with no initialization\n", __FUNCTION__); | |
671 | return DRM_ERR(EINVAL); | |
672 | } | |
673 | ||
674 | DRM_COPY_FROM_USER_IOCTL(param, (drm_i915_getparam_t __user *) data, | |
675 | sizeof(param)); | |
676 | ||
677 | switch (param.param) { | |
678 | case I915_PARAM_IRQ_ACTIVE: | |
679 | value = dev->irq ? 1 : 0; | |
680 | break; | |
681 | case I915_PARAM_ALLOW_BATCHBUFFER: | |
682 | value = dev_priv->allow_batchbuffer ? 1 : 0; | |
683 | break; | |
684 | default: | |
685 | DRM_ERROR("Unkown parameter %d\n", param.param); | |
686 | return DRM_ERR(EINVAL); | |
687 | } | |
688 | ||
689 | if (DRM_COPY_TO_USER(param.value, &value, sizeof(int))) { | |
690 | DRM_ERROR("DRM_COPY_TO_USER failed\n"); | |
691 | return DRM_ERR(EFAULT); | |
692 | } | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
697 | int i915_setparam(DRM_IOCTL_ARGS) | |
698 | { | |
699 | DRM_DEVICE; | |
700 | drm_i915_private_t *dev_priv = dev->dev_private; | |
701 | drm_i915_setparam_t param; | |
702 | ||
703 | if (!dev_priv) { | |
704 | DRM_ERROR("%s called with no initialization\n", __FUNCTION__); | |
705 | return DRM_ERR(EINVAL); | |
706 | } | |
707 | ||
708 | DRM_COPY_FROM_USER_IOCTL(param, (drm_i915_setparam_t __user *) data, | |
709 | sizeof(param)); | |
710 | ||
711 | switch (param.param) { | |
712 | case I915_SETPARAM_USE_MI_BATCHBUFFER_START: | |
713 | dev_priv->use_mi_batchbuffer_start = param.value; | |
714 | break; | |
715 | case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY: | |
716 | dev_priv->tex_lru_log_granularity = param.value; | |
717 | break; | |
718 | case I915_SETPARAM_ALLOW_BATCHBUFFER: | |
719 | dev_priv->allow_batchbuffer = param.value; | |
720 | break; | |
721 | default: | |
722 | DRM_ERROR("unknown parameter %d\n", param.param); | |
723 | return DRM_ERR(EINVAL); | |
724 | } | |
725 | ||
726 | return 0; | |
727 | } | |
728 | ||
729 | void i915_driver_pretakedown(drm_device_t *dev) | |
730 | { | |
731 | if ( dev->dev_private ) { | |
732 | drm_i915_private_t *dev_priv = dev->dev_private; | |
733 | i915_mem_takedown( &(dev_priv->agp_heap) ); | |
734 | } | |
735 | i915_dma_cleanup( dev ); | |
736 | } | |
737 | ||
738 | void i915_driver_prerelease(drm_device_t *dev, DRMFILE filp) | |
739 | { | |
740 | if ( dev->dev_private ) { | |
741 | drm_i915_private_t *dev_priv = dev->dev_private; | |
742 | i915_mem_release( dev, filp, dev_priv->agp_heap ); | |
743 | } | |
744 | } | |
745 |