db6d2cd2dda30c7fbaaff07a32251035f5f67378
[mesa.git] / src / mesa / drivers / dri / i915 / intel_ioctl.c
1 /**************************************************************************
2 *
3 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sched.h>
33
34 #include "mtypes.h"
35 #include "context.h"
36 #include "swrast/swrast.h"
37
38 #include "intel_context.h"
39 #include "intel_ioctl.h"
40 #include "intel_batchbuffer.h"
41 #include "drm.h"
42
43
44
45 static int intelEmitIrqLocked( intelContextPtr intel )
46 {
47 drmI830IrqEmit ie;
48 int ret, seq;
49
50 assert(((*(int *)intel->driHwLock) & ~DRM_LOCK_CONT) ==
51 (DRM_LOCK_HELD|intel->hHWContext));
52
53 ie.irq_seq = &seq;
54
55 ret = drmCommandWriteRead( intel->driFd, DRM_I830_IRQ_EMIT,
56 &ie, sizeof(ie) );
57 if ( ret ) {
58 fprintf( stderr, "%s: drmI830IrqEmit: %d\n", __FUNCTION__, ret );
59 exit(1);
60 }
61
62 if (0)
63 fprintf(stderr, "%s --> %d\n", __FUNCTION__, seq );
64
65 return seq;
66 }
67
68 static void intelWaitIrq( intelContextPtr intel, int seq )
69 {
70 drmI830IrqWait iw;
71 int ret;
72
73 if (0)
74 fprintf(stderr, "%s %d\n", __FUNCTION__, seq );
75
76 iw.irq_seq = seq;
77
78 do {
79 ret = drmCommandWrite( intel->driFd, DRM_I830_IRQ_WAIT, &iw, sizeof(iw) );
80 } while (ret == -EAGAIN || ret == -EINTR);
81
82 if ( ret ) {
83 fprintf( stderr, "%s: drmI830IrqWait: %d\n", __FUNCTION__, ret );
84 if (0)
85 intel_dump_batchbuffer( intel->alloc.offset,
86 intel->alloc.ptr,
87 intel->alloc.size );
88 exit(1);
89 }
90 }
91
92
93
94 static void age_intel( intelContextPtr intel, int age )
95 {
96 GLuint i;
97
98 for (i = 0 ; i < MAX_TEXTURE_UNITS ; i++)
99 if (intel->CurrentTexObj[i])
100 intel->CurrentTexObj[i]->age = age;
101 }
102
103 void intel_dump_batchbuffer( long offset,
104 int *ptr,
105 int count )
106 {
107 int i;
108 fprintf(stderr, "\n\n\nSTART BATCH (%d dwords):\n", count);
109 for (i = 0; i < count/4; i += 4)
110 fprintf(stderr, "\t0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
111 offset + i*4, ptr[i], ptr[i+1], ptr[i+2], ptr[i+3]);
112 fprintf(stderr, "END BATCH\n\n\n");
113 }
114
115 void intelRefillBatchLocked( intelContextPtr intel, GLboolean allow_unlock )
116 {
117 GLuint last_irq = intel->alloc.irq_emitted;
118 GLuint half = intel->alloc.size / 2;
119 GLuint buf = (intel->alloc.active_buf ^= 1);
120
121 intel->alloc.irq_emitted = intelEmitIrqLocked( intel );
122
123 if (last_irq) {
124 if (allow_unlock) UNLOCK_HARDWARE( intel );
125 intelWaitIrq( intel, last_irq );
126 if (allow_unlock) LOCK_HARDWARE( intel );
127 }
128
129 if (0)
130 fprintf(stderr, "%s: now using half %d\n", __FUNCTION__, buf);
131
132 intel->batch.start_offset = intel->alloc.offset + buf * half;
133 intel->batch.ptr = (char *)intel->alloc.ptr + buf * half;
134 intel->batch.size = half - 8;
135 intel->batch.space = half - 8;
136 assert(intel->batch.space >= 0);
137 }
138
139 #define MI_BATCH_BUFFER_END (0xA<<23)
140
141
142 void intelFlushBatchLocked( intelContextPtr intel,
143 GLboolean ignore_cliprects,
144 GLboolean refill,
145 GLboolean allow_unlock)
146 {
147 drmI830BatchBuffer batch;
148
149 assert(intel->locked);
150
151 if (0)
152 fprintf(stderr, "%s used %d of %d offset %x..%x refill %d\n",
153 __FUNCTION__,
154 (intel->batch.size - intel->batch.space),
155 intel->batch.size,
156 intel->batch.start_offset,
157 intel->batch.start_offset +
158 (intel->batch.size - intel->batch.space),
159 refill);
160
161 /* Throw away non-effective packets. Won't work once we have
162 * hardware contexts which would preserve statechanges beyond a
163 * single buffer.
164 */
165 if (intel->numClipRects == 0 && !ignore_cliprects) {
166
167 /* Without this yeild, an application with no cliprects can hog
168 * the hardware. Without unlocking, the effect is much worse -
169 * effectively a lock-out of other contexts.
170 */
171 if (allow_unlock) {
172 UNLOCK_HARDWARE( intel );
173 sched_yield();
174 LOCK_HARDWARE( intel );
175 }
176
177 /* Note that any state thought to have been emitted actually
178 * hasn't:
179 */
180 intel->batch.ptr -= (intel->batch.size - intel->batch.space);
181 intel->batch.space = intel->batch.size;
182 intel->vtbl.lost_hardware( intel );
183 }
184
185 if (intel->batch.space != intel->batch.size) {
186 batch.start = intel->batch.start_offset;
187 batch.used = intel->batch.size - intel->batch.space;
188 batch.cliprects = intel->pClipRects;
189 batch.num_cliprects = ignore_cliprects ? 0 : intel->numClipRects;
190 batch.DR1 = 0;
191 batch.DR4 = ((((GLuint)intel->drawX) & 0xffff) |
192 (((GLuint)intel->drawY) << 16));
193
194 if (intel->alloc.offset) {
195 if ((batch.used & 0x4) == 0) {
196 ((int *)intel->batch.ptr)[0] = 0;
197 ((int *)intel->batch.ptr)[1] = MI_BATCH_BUFFER_END;
198 batch.used += 0x8;
199 intel->batch.ptr += 0x8;
200 }
201 else {
202 ((int *)intel->batch.ptr)[0] = MI_BATCH_BUFFER_END;
203 batch.used += 0x4;
204 intel->batch.ptr += 0x4;
205 }
206 }
207
208 if (0)
209 intel_dump_batchbuffer( batch.start,
210 (int *)(intel->batch.ptr - batch.used),
211 batch.used );
212
213 if (0)
214 fprintf(stderr, "%s: 0x%x..0x%x DR4: %x cliprects: %d\n",
215 __FUNCTION__,
216 batch.start,
217 batch.start + batch.used,
218 batch.DR4, batch.num_cliprects);
219
220 intel->batch.start_offset += batch.used;
221 intel->batch.size -= batch.used;
222
223 if (intel->batch.size < 8) {
224 refill = GL_TRUE;
225 intel->batch.space = intel->batch.size = 0;
226 }
227 else {
228 intel->batch.size -= 8;
229 intel->batch.space = intel->batch.size;
230 }
231
232
233 assert(intel->batch.space >= 0);
234 assert(batch.start >= intel->alloc.offset);
235 assert(batch.start < intel->alloc.offset + intel->alloc.size);
236 assert(batch.start + batch.used > intel->alloc.offset);
237 assert(batch.start + batch.used <=
238 intel->alloc.offset + intel->alloc.size);
239
240
241 if (intel->alloc.offset) {
242 if (drmCommandWrite (intel->driFd, DRM_I830_BATCHBUFFER, &batch,
243 sizeof(batch))) {
244 fprintf(stderr, "DRM_I830_BATCHBUFFER: %d\n", -errno);
245 UNLOCK_HARDWARE(intel);
246 exit(1);
247 }
248 } else {
249 drmI830CmdBuffer cmd;
250 cmd.buf = intel->alloc.ptr + batch.start;
251 cmd.sz = batch.used;
252 cmd.DR1 = batch.DR1;
253 cmd.DR4 = batch.DR4;
254 cmd.num_cliprects = batch.num_cliprects;
255 cmd.cliprects = batch.cliprects;
256
257 if (drmCommandWrite (intel->driFd, DRM_I830_CMDBUFFER, &cmd,
258 sizeof(cmd))) {
259 fprintf(stderr, "DRM_I830_CMDBUFFER: %d\n", -errno);
260 UNLOCK_HARDWARE(intel);
261 exit(1);
262 }
263 }
264
265
266 age_intel(intel, intel->sarea->last_enqueue);
267
268 /* FIXME: use hardware contexts to avoid 'losing' hardware after
269 * each buffer flush.
270 */
271 intel->vtbl.lost_hardware( intel );
272 }
273
274 if (refill)
275 intelRefillBatchLocked( intel, allow_unlock );
276 }
277
278 void intelFlushBatch( intelContextPtr intel, GLboolean refill )
279 {
280 if (intel->locked) {
281 intelFlushBatchLocked( intel, GL_FALSE, refill, GL_FALSE );
282 }
283 else {
284 LOCK_HARDWARE(intel);
285 intelFlushBatchLocked( intel, GL_FALSE, refill, GL_TRUE );
286 UNLOCK_HARDWARE(intel);
287 }
288 }
289
290
291
292
293
294
295
296 void intelWaitForIdle( intelContextPtr intel )
297 {
298 if (0)
299 fprintf(stderr, "%s\n", __FUNCTION__);
300
301 intel->vtbl.emit_flush( intel );
302 intelFlushBatch( intel, GL_TRUE );
303
304 /* Use an irq to wait for dma idle -- Need to track lost contexts
305 * to shortcircuit consecutive calls to this function:
306 */
307 intelWaitIrq( intel, intel->alloc.irq_emitted );
308 intel->alloc.irq_emitted = 0;
309 }
310
311
312
313 void intelFlush( GLcontext *ctx )
314 {
315 intelContextPtr intel = INTEL_CONTEXT( ctx );
316
317 if (intel->Fallback)
318 _swrast_flush( ctx );
319
320 INTEL_FIREVERTICES( intel );
321
322 if (intel->batch.size != intel->batch.space)
323 intelFlushBatch( intel, GL_FALSE );
324 }
325
326 void intelFinish( GLcontext *ctx )
327 {
328 intelContextPtr intel = INTEL_CONTEXT( ctx );
329 intelFlush( ctx );
330 intelWaitForIdle( intel );
331 }
332
333
334 void intelClear(GLcontext *ctx, GLbitfield mask, GLboolean all,
335 GLint cx, GLint cy, GLint cw, GLint ch)
336 {
337 intelContextPtr intel = INTEL_CONTEXT( ctx );
338 const GLuint colorMask = *((GLuint *) &ctx->Color.ColorMask);
339 GLbitfield tri_mask = 0;
340 GLbitfield blit_mask = 0;
341 GLbitfield swrast_mask = 0;
342
343 if (0)
344 fprintf(stderr, "%s\n", __FUNCTION__);
345
346 /* Take care of cliprects, which are handled differently for
347 * clears, etc.
348 */
349 intelFlush( &intel->ctx );
350
351 if (mask & DD_FRONT_LEFT_BIT) {
352 if (colorMask == ~0) {
353 blit_mask |= DD_FRONT_LEFT_BIT;
354 }
355 else {
356 tri_mask |= DD_FRONT_LEFT_BIT;
357 }
358 }
359
360 if (mask & DD_BACK_LEFT_BIT) {
361 if (colorMask == ~0) {
362 blit_mask |= DD_BACK_LEFT_BIT;
363 }
364 else {
365 tri_mask |= DD_BACK_LEFT_BIT;
366 }
367 }
368
369 if (mask & DD_DEPTH_BIT) {
370 blit_mask |= DD_DEPTH_BIT;
371 }
372
373 if (mask & DD_STENCIL_BIT) {
374 if (!intel->hw_stencil) {
375 swrast_mask |= DD_STENCIL_BIT;
376 }
377 else if (ctx->Stencil.WriteMask[0] != 0xff) {
378 tri_mask |= DD_STENCIL_BIT;
379 }
380 else {
381 blit_mask |= DD_STENCIL_BIT;
382 }
383 }
384
385 swrast_mask |= (mask & DD_ACCUM_BIT);
386
387 if (blit_mask)
388 intelClearWithBlit( ctx, blit_mask, all, cx, cy, cw, ch );
389
390 if (tri_mask)
391 intel->vtbl.clear_with_tris( intel, tri_mask, all, cx, cy, cw, ch);
392
393 if (swrast_mask)
394 _swrast_Clear( ctx, swrast_mask, all, cx, cy, cw, ch );
395 }
396
397
398
399 void *intelAllocateAGP( intelContextPtr intel, GLsizei size )
400 {
401 int region_offset;
402 drmI830MemAlloc alloc;
403 int ret;
404
405 if (0)
406 fprintf(stderr, "%s: %d bytes\n", __FUNCTION__, size);
407
408 alloc.region = I830_MEM_REGION_AGP;
409 alloc.alignment = 0;
410 alloc.size = size;
411 alloc.region_offset = &region_offset;
412
413 LOCK_HARDWARE(intel);
414
415 /* Make sure the global heap is initialized
416 */
417 if (intel->texture_heaps[0])
418 driAgeTextures( intel->texture_heaps[0] );
419
420
421 ret = drmCommandWriteRead( intel->driFd,
422 DRM_I830_ALLOC,
423 &alloc, sizeof(alloc));
424
425 if (ret) {
426 fprintf(stderr, "%s: DRM_I830_ALLOC ret %d\n", __FUNCTION__, ret);
427 UNLOCK_HARDWARE(intel);
428 return NULL;
429 }
430
431 if (0)
432 fprintf(stderr, "%s: allocated %d bytes\n", __FUNCTION__, size);
433
434 /* Need to propogate this information (agp memory in use) to our
435 * local texture lru. The kernel has already updated the global
436 * lru. An alternative would have been to allocate memory the
437 * usual way and then notify the kernel to pin the allocation.
438 */
439 if (intel->texture_heaps[0])
440 driAgeTextures( intel->texture_heaps[0] );
441
442 UNLOCK_HARDWARE(intel);
443
444 return (void *)((char *)intel->intelScreen->tex.map + region_offset);
445 }
446
447 void intelFreeAGP( intelContextPtr intel, void *pointer )
448 {
449 int region_offset;
450 drmI830MemFree memfree;
451 int ret;
452
453 region_offset = (char *)pointer - (char *)intel->intelScreen->tex.map;
454
455 if (region_offset < 0 ||
456 region_offset > intel->intelScreen->tex.size) {
457 fprintf(stderr, "offset %d outside range 0..%d\n", region_offset,
458 intel->intelScreen->tex.size);
459 return;
460 }
461
462 memfree.region = I830_MEM_REGION_AGP;
463 memfree.region_offset = region_offset;
464
465 ret = drmCommandWrite( intel->driFd,
466 DRM_I830_FREE,
467 &memfree, sizeof(memfree));
468
469 if (ret)
470 fprintf(stderr, "%s: DRM_I830_FREE ret %d\n", __FUNCTION__, ret);
471 }
472
473 /* This version of AllocateMemoryMESA allocates only agp memory, and
474 * only does so after the point at which the driver has been
475 * initialized.
476 *
477 * Theoretically a valid context isn't required. However, in this
478 * implementation, it is, as I'm using the hardware lock to protect
479 * the kernel data structures, and the current context to get the
480 * device fd.
481 */
482 void *intelAllocateMemoryMESA(__DRInativeDisplay *dpy, int scrn,
483 GLsizei size, GLfloat readfreq,
484 GLfloat writefreq, GLfloat priority)
485 {
486 GET_CURRENT_CONTEXT(ctx);
487
488 if (INTEL_DEBUG & DEBUG_IOCTL)
489 fprintf(stderr, "%s sz %d %f/%f/%f\n", __FUNCTION__, size, readfreq,
490 writefreq, priority);
491
492 if (getenv("INTEL_NO_ALLOC"))
493 return NULL;
494
495 if (!ctx || INTEL_CONTEXT(ctx) == 0)
496 return NULL;
497
498 return intelAllocateAGP( INTEL_CONTEXT(ctx), size );
499 }
500
501
502 /* Called via glXFreeMemoryMESA() */
503 void intelFreeMemoryMESA(__DRInativeDisplay *dpy, int scrn, GLvoid *pointer)
504 {
505 GET_CURRENT_CONTEXT(ctx);
506 if (INTEL_DEBUG & DEBUG_IOCTL)
507 fprintf(stderr, "%s %p\n", __FUNCTION__, pointer);
508
509 if (!ctx || INTEL_CONTEXT(ctx) == 0) {
510 fprintf(stderr, "%s: no context\n", __FUNCTION__);
511 return;
512 }
513
514 intelFreeAGP( INTEL_CONTEXT(ctx), pointer );
515 }
516
517 /* Called via glXGetMemoryOffsetMESA()
518 *
519 * Returns offset of pointer from the start of agp aperture.
520 */
521 GLuint intelGetMemoryOffsetMESA(__DRInativeDisplay *dpy, int scrn,
522 const GLvoid *pointer)
523 {
524 GET_CURRENT_CONTEXT(ctx);
525 intelContextPtr intel;
526
527 if (!ctx || !(intel = INTEL_CONTEXT(ctx)) ) {
528 fprintf(stderr, "%s: no context\n", __FUNCTION__);
529 return ~0;
530 }
531
532 if (!intelIsAgpMemory( intel, pointer, 0 ))
533 return ~0;
534
535 return intelAgpOffsetFromVirtual( intel, pointer );
536 }
537
538
539 GLboolean intelIsAgpMemory( intelContextPtr intel, const GLvoid *pointer,
540 GLint size )
541 {
542 int offset = (char *)pointer - (char *)intel->intelScreen->tex.map;
543 int valid = (size >= 0 &&
544 offset >= 0 &&
545 offset + size < intel->intelScreen->tex.size);
546
547 if (INTEL_DEBUG & DEBUG_IOCTL)
548 fprintf(stderr, "intelIsAgpMemory( %p ) : %d\n", pointer, valid );
549
550 return valid;
551 }
552
553
554 GLuint intelAgpOffsetFromVirtual( intelContextPtr intel, const GLvoid *pointer )
555 {
556 int offset = (char *)pointer - (char *)intel->intelScreen->tex.map;
557
558 if (offset < 0 || offset > intel->intelScreen->tex.size)
559 return ~0;
560 else
561 return intel->intelScreen->textureOffset + offset;
562 }
563
564
565
566
567
568 /* Flip the front & back buffes
569 */
570 void intelPageFlip( const __DRIdrawablePrivate *dPriv )
571 {
572 #if 0
573 intelContextPtr intel;
574 int tmp, ret;
575
576 if (INTEL_DEBUG & DEBUG_IOCTL)
577 fprintf(stderr, "%s\n", __FUNCTION__);
578
579 assert(dPriv);
580 assert(dPriv->driContextPriv);
581 assert(dPriv->driContextPriv->driverPrivate);
582
583 intel = (intelContextPtr) dPriv->driContextPriv->driverPrivate;
584
585 intelFlush( &intel->ctx );
586 LOCK_HARDWARE( intel );
587
588 if (dPriv->pClipRects) {
589 *(drm_clip_rect_t *)intel->sarea->boxes = dPriv->pClipRects[0];
590 intel->sarea->nbox = 1;
591 }
592
593 ret = drmCommandNone(intel->driFd, DRM_I830_FLIP);
594 if (ret) {
595 fprintf(stderr, "%s: %d\n", __FUNCTION__, ret);
596 UNLOCK_HARDWARE( intel );
597 exit(1);
598 }
599
600 tmp = intel->sarea->last_enqueue;
601 intelRefillBatchLocked( intel );
602 UNLOCK_HARDWARE( intel );
603
604
605 intelSetDrawBuffer( &intel->ctx, intel->ctx.Color.DriverDrawBuffer );
606 #endif
607 }