i915_dma.c 22.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*-
 */
Dave Airlie's avatar
Dave Airlie committed
3
/*
Linus Torvalds's avatar
Linus Torvalds committed
4
5
 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
 * All Rights Reserved.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
Dave Airlie's avatar
Dave Airlie committed
27
 */
Linus Torvalds's avatar
Linus Torvalds committed
28
29
30
31
32
33
34
35
36
37
38

#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"

/* Really want an OS-independent resettable timer.  Would like to have
 * this loop run for (eg) 3 sec, but have the timer reset every time
 * the head pointer changes, so that EBUSY only happens if the ring
 * actually stalls for (eg) 3 seconds.
 */
39
int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
Linus Torvalds's avatar
Linus Torvalds committed
40
41
42
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
43
44
45
	u32 acthd_reg = IS_I965G(dev) ? ACTHD_I965 : ACTHD;
	u32 last_acthd = I915_READ(acthd_reg);
	u32 acthd;
46
	u32 last_head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
Linus Torvalds's avatar
Linus Torvalds committed
47
48
	int i;

49
	for (i = 0; i < 100000; i++) {
50
		ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
51
		acthd = I915_READ(acthd_reg);
Linus Torvalds's avatar
Linus Torvalds committed
52
53
54
55
56
57
58
59
60
61
		ring->space = ring->head - (ring->tail + 8);
		if (ring->space < 0)
			ring->space += ring->Size;
		if (ring->space >= n)
			return 0;

		dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;

		if (ring->head != last_head)
			i = 0;
62
63
		if (acthd != last_acthd)
			i = 0;
Linus Torvalds's avatar
Linus Torvalds committed
64
65

		last_head = ring->head;
66
67
68
		last_acthd = acthd;
		msleep_interruptible(10);

Linus Torvalds's avatar
Linus Torvalds committed
69
70
	}

Eric Anholt's avatar
Eric Anholt committed
71
	return -EBUSY;
Linus Torvalds's avatar
Linus Torvalds committed
72
73
}

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
 * Sets up the hardware status page for devices that need a physical address
 * in the register.
 */
int i915_init_phys_hws(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	/* Program Hardware Status Page */
	dev_priv->status_page_dmah =
		drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, 0xffffffff);

	if (!dev_priv->status_page_dmah) {
		DRM_ERROR("Can not allocate hardware status page\n");
		return -ENOMEM;
	}
	dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr;
	dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;

	memset(dev_priv->hw_status_page, 0, PAGE_SIZE);

	I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
	DRM_DEBUG("Enabled hardware status page\n");
	return 0;
}

/**
 * Frees the hardware status page, whether it's a physical address or a virtual
 * address set up by the X Server.
 */
void i915_free_hws(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	if (dev_priv->status_page_dmah) {
		drm_pci_free(dev, dev_priv->status_page_dmah);
		dev_priv->status_page_dmah = NULL;
	}

	if (dev_priv->status_gfx_addr) {
		dev_priv->status_gfx_addr = 0;
		drm_core_ioremapfree(&dev_priv->hws_map, dev);
	}

	/* Need to rewrite hardware status page */
	I915_WRITE(HWS_PGA, 0x1ffff000);
}

120
void i915_kernel_lost_context(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
121
122
123
124
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);

125
126
	ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
	ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
Linus Torvalds's avatar
Linus Torvalds committed
127
128
129
130
131
132
133
134
	ring->space = ring->head - (ring->tail + 8);
	if (ring->space < 0)
		ring->space += ring->Size;

	if (ring->head == ring->tail)
		dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
}

135
static int i915_dma_cleanup(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
136
{
137
	drm_i915_private_t *dev_priv = dev->dev_private;
Linus Torvalds's avatar
Linus Torvalds committed
138
139
140
141
	/* Make sure interrupts are disabled here because the uninstall ioctl
	 * may not have been called from userspace and after dev_private
	 * is freed, it's too late.
	 */
142
	if (dev->irq_enabled)
Dave Airlie's avatar
Dave Airlie committed
143
		drm_irq_uninstall(dev);
Linus Torvalds's avatar
Linus Torvalds committed
144

145
146
147
148
149
150
	if (dev_priv->ring.virtual_start) {
		drm_core_ioremapfree(&dev_priv->ring.map, dev);
		dev_priv->ring.virtual_start = 0;
		dev_priv->ring.map.handle = 0;
		dev_priv->ring.map.size = 0;
	}
151

152
153
154
	/* Clear the HWS virtual address at teardown */
	if (I915_NEED_GFX_HWS(dev))
		i915_free_hws(dev);
Linus Torvalds's avatar
Linus Torvalds committed
155
156
157
158

	return 0;
}

159
static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
Linus Torvalds's avatar
Linus Torvalds committed
160
{
161
	drm_i915_private_t *dev_priv = dev->dev_private;
Linus Torvalds's avatar
Linus Torvalds committed
162

163
	dev_priv->sarea = drm_getsarea(dev);
Linus Torvalds's avatar
Linus Torvalds committed
164
165
166
	if (!dev_priv->sarea) {
		DRM_ERROR("can not find sarea!\n");
		i915_dma_cleanup(dev);
Eric Anholt's avatar
Eric Anholt committed
167
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
	}

	dev_priv->sarea_priv = (drm_i915_sarea_t *)
	    ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset);

	dev_priv->ring.Start = init->ring_start;
	dev_priv->ring.End = init->ring_end;
	dev_priv->ring.Size = init->ring_size;
	dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;

	dev_priv->ring.map.offset = init->ring_start;
	dev_priv->ring.map.size = init->ring_size;
	dev_priv->ring.map.type = 0;
	dev_priv->ring.map.flags = 0;
	dev_priv->ring.map.mtrr = 0;

Dave Airlie's avatar
Dave Airlie committed
184
	drm_core_ioremap(&dev_priv->ring.map, dev);
Linus Torvalds's avatar
Linus Torvalds committed
185
186
187
188
189

	if (dev_priv->ring.map.handle == NULL) {
		i915_dma_cleanup(dev);
		DRM_ERROR("can not ioremap virtual address for"
			  " ring buffer\n");
Eric Anholt's avatar
Eric Anholt committed
190
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
191
192
193
194
	}

	dev_priv->ring.virtual_start = dev_priv->ring.map.handle;

195
	dev_priv->cpp = init->cpp;
Linus Torvalds's avatar
Linus Torvalds committed
196
197
198
199
200
201
202
203
204
205
206
207
	dev_priv->back_offset = init->back_offset;
	dev_priv->front_offset = init->front_offset;
	dev_priv->current_page = 0;
	dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;

	/* Allow hardware batchbuffers unless told otherwise.
	 */
	dev_priv->allow_batchbuffer = 1;

	return 0;
}

208
static int i915_dma_resume(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
209
210
211
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;

212
	DRM_DEBUG("%s\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
213
214
215

	if (!dev_priv->sarea) {
		DRM_ERROR("can not find sarea!\n");
Eric Anholt's avatar
Eric Anholt committed
216
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
217
218
219
220
221
	}

	if (dev_priv->ring.map.handle == NULL) {
		DRM_ERROR("can not ioremap virtual address for"
			  " ring buffer\n");
Eric Anholt's avatar
Eric Anholt committed
222
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
223
224
225
226
227
	}

	/* Program Hardware Status Page */
	if (!dev_priv->hw_status_page) {
		DRM_ERROR("Can not find hardware status page\n");
Eric Anholt's avatar
Eric Anholt committed
228
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
229
230
231
	}
	DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);

232
	if (dev_priv->status_gfx_addr != 0)
233
		I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
234
	else
235
		I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
Linus Torvalds's avatar
Linus Torvalds committed
236
237
238
239
240
	DRM_DEBUG("Enabled hardware status page\n");

	return 0;
}

241
242
static int i915_dma_init(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
243
{
244
	drm_i915_init_t *init = data;
Linus Torvalds's avatar
Linus Torvalds committed
245
246
	int retcode = 0;

247
	switch (init->func) {
Linus Torvalds's avatar
Linus Torvalds committed
248
	case I915_INIT_DMA:
249
		retcode = i915_initialize(dev, init);
Linus Torvalds's avatar
Linus Torvalds committed
250
251
252
253
254
		break;
	case I915_CLEANUP_DMA:
		retcode = i915_dma_cleanup(dev);
		break;
	case I915_RESUME_DMA:
Dave Airlie's avatar
Dave Airlie committed
255
		retcode = i915_dma_resume(dev);
Linus Torvalds's avatar
Linus Torvalds committed
256
257
		break;
	default:
Eric Anholt's avatar
Eric Anholt committed
258
		retcode = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
		break;
	}

	return retcode;
}

/* Implement basically the same security restrictions as hardware does
 * for MI_BATCH_NON_SECURE.  These can be made stricter at any time.
 *
 * Most of the calculations below involve calculating the size of a
 * particular instruction.  It's important to get the size right as
 * that tells us where the next instruction to check is.  Any illegal
 * instruction detected will be given a size of zero, which is a
 * signal to abort the rest of the buffer.
 */
static int do_validate_cmd(int cmd)
{
	switch (((cmd >> 29) & 0x7)) {
	case 0x0:
		switch ((cmd >> 23) & 0x3f) {
		case 0x0:
			return 1;	/* MI_NOOP */
		case 0x4:
			return 1;	/* MI_FLUSH */
		default:
			return 0;	/* disallow everything else */
		}
		break;
	case 0x1:
		return 0;	/* reserved */
	case 0x2:
		return (cmd & 0xff) + 2;	/* 2d commands */
	case 0x3:
		if (((cmd >> 24) & 0x1f) <= 0x18)
			return 1;

		switch ((cmd >> 24) & 0x1f) {
		case 0x1c:
			return 1;
		case 0x1d:
Dave Airlie's avatar
Dave Airlie committed
299
			switch ((cmd >> 16) & 0xff) {
Linus Torvalds's avatar
Linus Torvalds committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
			case 0x3:
				return (cmd & 0x1f) + 2;
			case 0x4:
				return (cmd & 0xf) + 2;
			default:
				return (cmd & 0xffff) + 2;
			}
		case 0x1e:
			if (cmd & (1 << 23))
				return (cmd & 0xffff) + 1;
			else
				return 1;
		case 0x1f:
			if ((cmd & (1 << 23)) == 0)	/* inline vertices */
				return (cmd & 0x1ffff) + 2;
			else if (cmd & (1 << 17))	/* indirect random */
				if ((cmd & 0xffff) == 0)
					return 0;	/* unknown length, too hard */
				else
					return (((cmd & 0xffff) + 1) / 2) + 1;
			else
				return 2;	/* indirect sequential */
		default:
			return 0;
		}
	default:
		return 0;
	}

	return 0;
}

static int validate_cmd(int cmd)
{
	int ret = do_validate_cmd(cmd);

336
/*	printk("validate_cmd( %x ): %d\n", cmd, ret); */
Linus Torvalds's avatar
Linus Torvalds committed
337
338
339
340

	return ret;
}

341
static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwords)
Linus Torvalds's avatar
Linus Torvalds committed
342
343
344
345
346
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	int i;
	RING_LOCALS;

347
	if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8)
Eric Anholt's avatar
Eric Anholt committed
348
		return -EINVAL;
349

350
	BEGIN_LP_RING((dwords+1)&~1);
351

Linus Torvalds's avatar
Linus Torvalds committed
352
353
354
355
	for (i = 0; i < dwords;) {
		int cmd, sz;

		if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd)))
Eric Anholt's avatar
Eric Anholt committed
356
			return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
357
358

		if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords)
Eric Anholt's avatar
Eric Anholt committed
359
			return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
360
361
362
363
364
365

		OUT_RING(cmd);

		while (++i, --sz) {
			if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i],
							 sizeof(cmd))) {
Eric Anholt's avatar
Eric Anholt committed
366
				return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
367
368
369
370
371
			}
			OUT_RING(cmd);
		}
	}

372
373
374
375
376
	if (dwords & 1)
		OUT_RING(0);

	ADVANCE_LP_RING();

Linus Torvalds's avatar
Linus Torvalds committed
377
378
379
	return 0;
}

380
static int i915_emit_box(struct drm_device * dev,
381
			 struct drm_clip_rect __user * boxes,
Linus Torvalds's avatar
Linus Torvalds committed
382
383
384
			 int i, int DR1, int DR4)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
385
	struct drm_clip_rect box;
Linus Torvalds's avatar
Linus Torvalds committed
386
387
388
	RING_LOCALS;

	if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
Eric Anholt's avatar
Eric Anholt committed
389
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
390
391
392
393
394
	}

	if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) {
		DRM_ERROR("Bad box %d,%d..%d,%d\n",
			  box.x1, box.y1, box.x2, box.y2);
Eric Anholt's avatar
Eric Anholt committed
395
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
396
397
	}

398
399
400
401
	if (IS_I965G(dev)) {
		BEGIN_LP_RING(4);
		OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
		OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
Andrew Morton's avatar
Andrew Morton committed
402
		OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
403
404
405
406
407
408
409
410
411
412
413
414
		OUT_RING(DR4);
		ADVANCE_LP_RING();
	} else {
		BEGIN_LP_RING(6);
		OUT_RING(GFX_OP_DRAWRECT_INFO);
		OUT_RING(DR1);
		OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
		OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
		OUT_RING(DR4);
		OUT_RING(0);
		ADVANCE_LP_RING();
	}
Linus Torvalds's avatar
Linus Torvalds committed
415
416
417
418

	return 0;
}

419
420
421
422
/* XXX: Emitting the counter should really be moved to part of the IRQ
 * emit. For now, do it in both places:
 */

423
static void i915_emit_breadcrumb(struct drm_device *dev)
424
425
426
427
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	RING_LOCALS;

428
	dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;
429

430
431
	if (dev_priv->counter > 0x7FFFFFFFUL)
		dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;
432
433

	BEGIN_LP_RING(4);
434
435
	OUT_RING(MI_STORE_DWORD_INDEX);
	OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT);
436
437
438
439
440
	OUT_RING(dev_priv->counter);
	OUT_RING(0);
	ADVANCE_LP_RING();
}

441
static int i915_dispatch_cmdbuffer(struct drm_device * dev,
Linus Torvalds's avatar
Linus Torvalds committed
442
443
444
445
446
447
448
				   drm_i915_cmdbuffer_t * cmd)
{
	int nbox = cmd->num_cliprects;
	int i = 0, count, ret;

	if (cmd->sz & 0x3) {
		DRM_ERROR("alignment");
Eric Anholt's avatar
Eric Anholt committed
449
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
	}

	i915_kernel_lost_context(dev);

	count = nbox ? nbox : 1;

	for (i = 0; i < count; i++) {
		if (i < nbox) {
			ret = i915_emit_box(dev, cmd->cliprects, i,
					    cmd->DR1, cmd->DR4);
			if (ret)
				return ret;
		}

		ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4);
		if (ret)
			return ret;
	}

469
	i915_emit_breadcrumb(dev);
Linus Torvalds's avatar
Linus Torvalds committed
470
471
472
	return 0;
}

473
static int i915_dispatch_batchbuffer(struct drm_device * dev,
Linus Torvalds's avatar
Linus Torvalds committed
474
475
476
				     drm_i915_batchbuffer_t * batch)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
477
	struct drm_clip_rect __user *boxes = batch->cliprects;
Linus Torvalds's avatar
Linus Torvalds committed
478
479
480
481
482
483
	int nbox = batch->num_cliprects;
	int i = 0, count;
	RING_LOCALS;

	if ((batch->start | batch->used) & 0x7) {
		DRM_ERROR("alignment");
Eric Anholt's avatar
Eric Anholt committed
484
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
485
486
487
488
489
490
491
492
493
494
495
496
497
498
	}

	i915_kernel_lost_context(dev);

	count = nbox ? nbox : 1;

	for (i = 0; i < count; i++) {
		if (i < nbox) {
			int ret = i915_emit_box(dev, boxes, i,
						batch->DR1, batch->DR4);
			if (ret)
				return ret;
		}

499
		if (!IS_I830(dev) && !IS_845G(dev)) {
Linus Torvalds's avatar
Linus Torvalds committed
500
			BEGIN_LP_RING(2);
501
502
503
504
505
506
507
			if (IS_I965G(dev)) {
				OUT_RING(MI_BATCH_BUFFER_START | (2 << 6) | MI_BATCH_NON_SECURE_I965);
				OUT_RING(batch->start);
			} else {
				OUT_RING(MI_BATCH_BUFFER_START | (2 << 6));
				OUT_RING(batch->start | MI_BATCH_NON_SECURE);
			}
Linus Torvalds's avatar
Linus Torvalds committed
508
509
510
511
512
513
514
515
516
517
518
			ADVANCE_LP_RING();
		} else {
			BEGIN_LP_RING(4);
			OUT_RING(MI_BATCH_BUFFER);
			OUT_RING(batch->start | MI_BATCH_NON_SECURE);
			OUT_RING(batch->start + batch->used - 4);
			OUT_RING(0);
			ADVANCE_LP_RING();
		}
	}

519
	i915_emit_breadcrumb(dev);
Linus Torvalds's avatar
Linus Torvalds committed
520
521
522
523

	return 0;
}

524
static int i915_dispatch_flip(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
525
526
527
528
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	RING_LOCALS;

529
	DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n",
530
		  __func__,
531
532
		  dev_priv->current_page,
		  dev_priv->sarea_priv->pf_current_page);
Linus Torvalds's avatar
Linus Torvalds committed
533

534
535
536
	i915_kernel_lost_context(dev);

	BEGIN_LP_RING(2);
537
	OUT_RING(MI_FLUSH | MI_READ_FLUSH);
538
539
	OUT_RING(0);
	ADVANCE_LP_RING();
Linus Torvalds's avatar
Linus Torvalds committed
540

541
542
543
544
545
546
	BEGIN_LP_RING(6);
	OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP);
	OUT_RING(0);
	if (dev_priv->current_page == 0) {
		OUT_RING(dev_priv->back_offset);
		dev_priv->current_page = 1;
Linus Torvalds's avatar
Linus Torvalds committed
547
	} else {
548
549
		OUT_RING(dev_priv->front_offset);
		dev_priv->current_page = 0;
Linus Torvalds's avatar
Linus Torvalds committed
550
	}
551
552
	OUT_RING(0);
	ADVANCE_LP_RING();
Linus Torvalds's avatar
Linus Torvalds committed
553

554
555
556
557
	BEGIN_LP_RING(2);
	OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP);
	OUT_RING(0);
	ADVANCE_LP_RING();
Linus Torvalds's avatar
Linus Torvalds committed
558

559
	dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;
Linus Torvalds's avatar
Linus Torvalds committed
560
561

	BEGIN_LP_RING(4);
562
563
	OUT_RING(MI_STORE_DWORD_INDEX);
	OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT);
564
565
	OUT_RING(dev_priv->counter);
	OUT_RING(0);
Linus Torvalds's avatar
Linus Torvalds committed
566
567
	ADVANCE_LP_RING();

568
569
	dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
570
571
}

572
static int i915_quiescent(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
573
574
575
576
{
	drm_i915_private_t *dev_priv = dev->dev_private;

	i915_kernel_lost_context(dev);
577
	return i915_wait_ring(dev, dev_priv->ring.Size - 8, __func__);
Linus Torvalds's avatar
Linus Torvalds committed
578
579
}

580
581
static int i915_flush_ioctl(struct drm_device *dev, void *data,
			    struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
582
{
583
	LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
584
585
586
587

	return i915_quiescent(dev);
}

588
589
static int i915_batchbuffer(struct drm_device *dev, void *data,
			    struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
590
591
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
592
	u32 *hw_status = dev_priv->hw_status_page;
Linus Torvalds's avatar
Linus Torvalds committed
593
594
	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
	    dev_priv->sarea_priv;
595
	drm_i915_batchbuffer_t *batch = data;
Linus Torvalds's avatar
Linus Torvalds committed
596
597
598
599
	int ret;

	if (!dev_priv->allow_batchbuffer) {
		DRM_ERROR("Batchbuffer ioctl disabled\n");
Eric Anholt's avatar
Eric Anholt committed
600
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
601
602
603
	}

	DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n",
604
		  batch->start, batch->used, batch->num_cliprects);
Linus Torvalds's avatar
Linus Torvalds committed
605

606
	LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
607

608
609
	if (batch->num_cliprects && DRM_VERIFYAREA_READ(batch->cliprects,
						       batch->num_cliprects *
610
						       sizeof(struct drm_clip_rect)))
Eric Anholt's avatar
Eric Anholt committed
611
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
612

613
	ret = i915_dispatch_batchbuffer(dev, batch);
Linus Torvalds's avatar
Linus Torvalds committed
614

615
	sarea_priv->last_dispatch = (int)hw_status[5];
Linus Torvalds's avatar
Linus Torvalds committed
616
617
618
	return ret;
}

619
620
static int i915_cmdbuffer(struct drm_device *dev, void *data,
			  struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
621
622
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
623
	u32 *hw_status = dev_priv->hw_status_page;
Linus Torvalds's avatar
Linus Torvalds committed
624
625
	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
	    dev_priv->sarea_priv;
626
	drm_i915_cmdbuffer_t *cmdbuf = data;
Linus Torvalds's avatar
Linus Torvalds committed
627
628
629
	int ret;

	DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
630
		  cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects);
Linus Torvalds's avatar
Linus Torvalds committed
631

632
	LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
633

634
635
636
	if (cmdbuf->num_cliprects &&
	    DRM_VERIFYAREA_READ(cmdbuf->cliprects,
				cmdbuf->num_cliprects *
637
				sizeof(struct drm_clip_rect))) {
Linus Torvalds's avatar
Linus Torvalds committed
638
		DRM_ERROR("Fault accessing cliprects\n");
Eric Anholt's avatar
Eric Anholt committed
639
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
640
641
	}

642
	ret = i915_dispatch_cmdbuffer(dev, cmdbuf);
Linus Torvalds's avatar
Linus Torvalds committed
643
644
645
646
647
	if (ret) {
		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
		return ret;
	}

648
	sarea_priv->last_dispatch = (int)hw_status[5];
Linus Torvalds's avatar
Linus Torvalds committed
649
650
651
	return 0;
}

652
653
static int i915_flip_bufs(struct drm_device *dev, void *data,
			  struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
654
{
655
	DRM_DEBUG("%s\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
656

657
	LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
658

659
	return i915_dispatch_flip(dev);
Linus Torvalds's avatar
Linus Torvalds committed
660
661
}

662
663
static int i915_getparam(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
664
665
{
	drm_i915_private_t *dev_priv = dev->dev_private;
666
	drm_i915_getparam_t *param = data;
Linus Torvalds's avatar
Linus Torvalds committed
667
668
669
	int value;

	if (!dev_priv) {
670
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
671
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
672
673
	}

674
	switch (param->param) {
Linus Torvalds's avatar
Linus Torvalds committed
675
	case I915_PARAM_IRQ_ACTIVE:
676
		value = dev->irq_enabled;
Linus Torvalds's avatar
Linus Torvalds committed
677
678
679
680
		break;
	case I915_PARAM_ALLOW_BATCHBUFFER:
		value = dev_priv->allow_batchbuffer ? 1 : 0;
		break;
Dave Airlie's avatar
Dave Airlie committed
681
682
683
	case I915_PARAM_LAST_DISPATCH:
		value = READ_BREADCRUMB(dev_priv);
		break;
Linus Torvalds's avatar
Linus Torvalds committed
684
	default:
685
		DRM_ERROR("Unknown parameter %d\n", param->param);
Eric Anholt's avatar
Eric Anholt committed
686
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
687
688
	}

689
	if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) {
Linus Torvalds's avatar
Linus Torvalds committed
690
		DRM_ERROR("DRM_COPY_TO_USER failed\n");
Eric Anholt's avatar
Eric Anholt committed
691
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
692
693
694
695
696
	}

	return 0;
}

697
698
static int i915_setparam(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
699
700
{
	drm_i915_private_t *dev_priv = dev->dev_private;
701
	drm_i915_setparam_t *param = data;
Linus Torvalds's avatar
Linus Torvalds committed
702
703

	if (!dev_priv) {
704
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
705
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
706
707
	}

708
	switch (param->param) {
Linus Torvalds's avatar
Linus Torvalds committed
709
710
711
	case I915_SETPARAM_USE_MI_BATCHBUFFER_START:
		break;
	case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY:
712
		dev_priv->tex_lru_log_granularity = param->value;
Linus Torvalds's avatar
Linus Torvalds committed
713
714
		break;
	case I915_SETPARAM_ALLOW_BATCHBUFFER:
715
		dev_priv->allow_batchbuffer = param->value;
Linus Torvalds's avatar
Linus Torvalds committed
716
717
		break;
	default:
718
		DRM_ERROR("unknown parameter %d\n", param->param);
Eric Anholt's avatar
Eric Anholt committed
719
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
720
721
722
723
724
	}

	return 0;
}

725
726
static int i915_set_status_page(struct drm_device *dev, void *data,
				struct drm_file *file_priv)
727
728
{
	drm_i915_private_t *dev_priv = dev->dev_private;
729
	drm_i915_hws_addr_t *hws = data;
730
731
732

	if (!I915_NEED_GFX_HWS(dev))
		return -EINVAL;
733
734

	if (!dev_priv) {
735
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
736
		return -EINVAL;
737
738
	}

739
740
741
	printk(KERN_DEBUG "set status page addr 0x%08x\n", (u32)hws->addr);

	dev_priv->status_gfx_addr = hws->addr & (0x1ffff<<12);
742

743
	dev_priv->hws_map.offset = dev->agp->base + hws->addr;
744
745
746
747
748
749
750
751
752
753
754
	dev_priv->hws_map.size = 4*1024;
	dev_priv->hws_map.type = 0;
	dev_priv->hws_map.flags = 0;
	dev_priv->hws_map.mtrr = 0;

	drm_core_ioremap(&dev_priv->hws_map, dev);
	if (dev_priv->hws_map.handle == NULL) {
		i915_dma_cleanup(dev);
		dev_priv->status_gfx_addr = 0;
		DRM_ERROR("can not ioremap virtual address for"
				" G33 hw status page\n");
Eric Anholt's avatar
Eric Anholt committed
755
		return -ENOMEM;
756
757
758
759
	}
	dev_priv->hw_status_page = dev_priv->hws_map.handle;

	memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
760
761
	I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
	DRM_DEBUG("load hws HWS_PGA with gfx mem 0x%x\n",
762
763
764
765
766
			dev_priv->status_gfx_addr);
	DRM_DEBUG("load hws at %p\n", dev_priv->hw_status_page);
	return 0;
}

767
int i915_driver_load(struct drm_device *dev, unsigned long flags)
768
{
769
770
771
772
	struct drm_i915_private *dev_priv = dev->dev_private;
	unsigned long base, size;
	int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1;

773
774
775
776
777
778
779
	/* i915 has 4 more counters */
	dev->counters += 4;
	dev->types[6] = _DRM_STAT_IRQ;
	dev->types[7] = _DRM_STAT_PRIMARY;
	dev->types[8] = _DRM_STAT_SECONDARY;
	dev->types[9] = _DRM_STAT_DMA;

780
781
782
783
784
785
786
787
788
789
790
791
	dev_priv = drm_alloc(sizeof(drm_i915_private_t), DRM_MEM_DRIVER);
	if (dev_priv == NULL)
		return -ENOMEM;

	memset(dev_priv, 0, sizeof(drm_i915_private_t));

	dev->dev_private = (void *)dev_priv;

	/* Add register map (needed for suspend/resume) */
	base = drm_get_resource_start(dev, mmio_bar);
	size = drm_get_resource_len(dev, mmio_bar);

792
793
	ret = drm_addmap(dev, base, size, _DRM_REGISTERS,
			 _DRM_KERNEL | _DRM_DRIVER,
794
			 &dev_priv->mmio_map);
795

796
797
798
799
800
801
	/* Init HWS */
	if (!I915_NEED_GFX_HWS(dev)) {
		ret = i915_init_phys_hws(dev);
		if (ret != 0)
			return ret;
	}
802
803
804
805
806
807
808
809
810
811
812

	/* On the 945G/GM, the chipset reports the MSI capability on the
	 * integrated graphics even though the support isn't actually there
	 * according to the published specs.  It doesn't appear to function
	 * correctly in testing on 945G.
	 * This may be a side effect of MSI having been made available for PEG
	 * and the registers being closely associated.
	 */
	if (!IS_I945G(dev) && !IS_I945GM(dev))
		pci_enable_msi(dev->pdev);

813
814
	intel_opregion_init(dev);

815
816
	spin_lock_init(&dev_priv->user_irq_lock);

817
818
819
820
821
822
823
	return ret;
}

int i915_driver_unload(struct drm_device *dev)
{
	struct drm_i915_private *dev_priv = dev->dev_private;

824
825
826
	if (dev->pdev->msi_enabled)
		pci_disable_msi(dev->pdev);

827
828
	i915_free_hws(dev);

829
830
831
	if (dev_priv->mmio_map)
		drm_rmmap(dev, dev_priv->mmio_map);

832
833
	intel_opregion_free(dev);

834
835
836
	drm_free(dev->dev_private, sizeof(drm_i915_private_t),
		 DRM_MEM_DRIVER);

837
838
839
	return 0;
}

840
void i915_driver_lastclose(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
841
{
842
843
	drm_i915_private_t *dev_priv = dev->dev_private;

Dave Airlie's avatar
Dave Airlie committed
844
845
846
	if (!dev_priv)
		return;

847
	if (dev_priv->agp_heap)
Dave Airlie's avatar
Dave Airlie committed
848
		i915_mem_takedown(&(dev_priv->agp_heap));
849

Dave Airlie's avatar
Dave Airlie committed
850
	i915_dma_cleanup(dev);
Linus Torvalds's avatar
Linus Torvalds committed
851
852
}

853
void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
854
{
855
856
	drm_i915_private_t *dev_priv = dev->dev_private;
	i915_mem_release(dev, file_priv, dev_priv->agp_heap);
Linus Torvalds's avatar
Linus Torvalds committed
857
858
}

859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
struct drm_ioctl_desc i915_ioctls[] = {
	DRM_IOCTL_DEF(DRM_I915_INIT, i915_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_I915_FLUSH, i915_flush_ioctl, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_FLIP, i915_flip_bufs, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_BATCHBUFFER, i915_batchbuffer, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_IRQ_EMIT, i915_irq_emit, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_IRQ_WAIT, i915_irq_wait, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_GETPARAM, i915_getparam, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_SETPARAM, i915_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_I915_ALLOC, i915_mem_alloc, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_FREE, i915_mem_free, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_INIT_HEAP, i915_mem_init_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_I915_CMDBUFFER, i915_cmdbuffer, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_DESTROY_HEAP,  i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY ),
	DRM_IOCTL_DEF(DRM_I915_SET_VBLANK_PIPE,  i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY ),
	DRM_IOCTL_DEF(DRM_I915_GET_VBLANK_PIPE,  i915_vblank_pipe_get, DRM_AUTH ),
	DRM_IOCTL_DEF(DRM_I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_I915_HWS_ADDR, i915_set_status_page, DRM_AUTH),
Dave Airlie's avatar
Dave Airlie committed
877
878
879
};

int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
880
881
882
883
884
885
886
887
888
889
890
891

/**
 * Determine if the device really is AGP or not.
 *
 * All Intel graphics chipsets are treated as AGP, even if they are really
 * PCI-e.
 *
 * \param dev   The device to be tested.
 *
 * \returns
 * A value of 1 is always retured to indictate every i9x5 is AGP.
 */
892
int i915_driver_device_is_agp(struct drm_device * dev)
893
894
895
{
	return 1;
}