i915_dma.c 59 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

#include "drmP.h"
#include "drm.h"
31
#include "drm_crtc_helper.h"
32
#include "drm_fb_helper.h"
33
#include "intel_drv.h"
Linus Torvalds's avatar
Linus Torvalds committed
34
35
#include "i915_drm.h"
#include "i915_drv.h"
Chris Wilson's avatar
Chris Wilson committed
36
#include "i915_trace.h"
37
#include <linux/pci.h>
38
#include <linux/vgaarb.h>
39
40
#include <linux/acpi.h>
#include <linux/pnp.h>
41
#include <linux/vga_switcheroo.h>
42
#include <linux/slab.h>
43
#include <acpi/video.h>
Linus Torvalds's avatar
Linus Torvalds committed
44

45
46
47
48
/**
 * Sets up the hardware status page for devices that need a physical address
 * in the register.
 */
49
static int i915_init_phys_hws(struct drm_device *dev)
50
51
52
53
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	/* Program Hardware Status Page */
	dev_priv->status_page_dmah =
54
		drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE);
55
56
57
58
59

	if (!dev_priv->status_page_dmah) {
		DRM_ERROR("Can not allocate hardware status page\n");
		return -ENOMEM;
	}
60
61
	dev_priv->render_ring.status_page.page_addr
		= dev_priv->status_page_dmah->vaddr;
62
63
	dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;

64
	memset(dev_priv->render_ring.status_page.page_addr, 0, PAGE_SIZE);
65

66
	if (INTEL_INFO(dev)->gen >= 4)
67
68
69
		dev_priv->dma_status_page |= (dev_priv->dma_status_page >> 28) &
					     0xf0;

70
	I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
71
	DRM_DEBUG_DRIVER("Enabled hardware status page\n");
72
73
74
75
76
77
78
	return 0;
}

/**
 * Frees the hardware status page, whether it's a physical address or a virtual
 * address set up by the X Server.
 */
79
static void i915_free_hws(struct drm_device *dev)
80
81
82
83
84
85
86
{
	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;
	}

87
88
	if (dev_priv->render_ring.status_page.gfx_addr) {
		dev_priv->render_ring.status_page.gfx_addr = 0;
89
90
91
92
93
94
95
		drm_core_ioremapfree(&dev_priv->hws_map, dev);
	}

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

96
void i915_kernel_lost_context(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
97
98
{
	drm_i915_private_t *dev_priv = dev->dev_private;
99
	struct drm_i915_master_private *master_priv;
100
	struct intel_ring_buffer *ring = &dev_priv->render_ring;
Linus Torvalds's avatar
Linus Torvalds committed
101

102
103
104
105
106
107
108
	/*
	 * We should never lose context on the ring with modesetting
	 * as we don't expose it to userspace
	 */
	if (drm_core_check_feature(dev, DRIVER_MODESET))
		return;

109
110
	ring->head = I915_READ_HEAD(ring) & HEAD_ADDR;
	ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
Linus Torvalds's avatar
Linus Torvalds committed
111
112
	ring->space = ring->head - (ring->tail + 8);
	if (ring->space < 0)
113
		ring->space += ring->size;
Linus Torvalds's avatar
Linus Torvalds committed
114

115
116
117
118
119
120
	if (!dev->primary->master)
		return;

	master_priv = dev->primary->master->driver_priv;
	if (ring->head == ring->tail && master_priv->sarea_priv)
		master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
Linus Torvalds's avatar
Linus Torvalds committed
121
122
}

123
static int i915_dma_cleanup(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
124
{
125
	drm_i915_private_t *dev_priv = dev->dev_private;
Linus Torvalds's avatar
Linus Torvalds committed
126
127
128
129
	/* 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.
	 */
130
	if (dev->irq_enabled)
Dave Airlie's avatar
Dave Airlie committed
131
		drm_irq_uninstall(dev);
Linus Torvalds's avatar
Linus Torvalds committed
132

133
	mutex_lock(&dev->struct_mutex);
134
135
136
	intel_cleanup_ring_buffer(&dev_priv->render_ring);
	intel_cleanup_ring_buffer(&dev_priv->bsd_ring);
	intel_cleanup_ring_buffer(&dev_priv->blt_ring);
137
	mutex_unlock(&dev->struct_mutex);
138

139
140
141
	/* Clear the HWS virtual address at teardown */
	if (I915_NEED_GFX_HWS(dev))
		i915_free_hws(dev);
Linus Torvalds's avatar
Linus Torvalds committed
142
143
144
145

	return 0;
}

146
static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
Linus Torvalds's avatar
Linus Torvalds committed
147
{
148
	drm_i915_private_t *dev_priv = dev->dev_private;
149
	struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
Linus Torvalds's avatar
Linus Torvalds committed
150

151
152
153
154
155
	master_priv->sarea = drm_getsarea(dev);
	if (master_priv->sarea) {
		master_priv->sarea_priv = (drm_i915_sarea_t *)
			((u8 *)master_priv->sarea->handle + init->sarea_priv_offset);
	} else {
156
		DRM_DEBUG_DRIVER("sarea not found assuming DRI2 userspace\n");
157
158
	}

159
	if (init->ring_size != 0) {
160
		if (dev_priv->render_ring.gem_object != NULL) {
161
162
163
164
165
			i915_dma_cleanup(dev);
			DRM_ERROR("Client tried to initialize ringbuffer in "
				  "GEM mode\n");
			return -EINVAL;
		}
Linus Torvalds's avatar
Linus Torvalds committed
166

167
		dev_priv->render_ring.size = init->ring_size;
Linus Torvalds's avatar
Linus Torvalds committed
168

169
170
171
172
173
		dev_priv->render_ring.map.offset = init->ring_start;
		dev_priv->render_ring.map.size = init->ring_size;
		dev_priv->render_ring.map.type = 0;
		dev_priv->render_ring.map.flags = 0;
		dev_priv->render_ring.map.mtrr = 0;
Linus Torvalds's avatar
Linus Torvalds committed
174

175
		drm_core_ioremap_wc(&dev_priv->render_ring.map, dev);
176

177
		if (dev_priv->render_ring.map.handle == NULL) {
178
179
180
181
182
			i915_dma_cleanup(dev);
			DRM_ERROR("can not ioremap virtual address for"
				  " ring buffer\n");
			return -ENOMEM;
		}
Linus Torvalds's avatar
Linus Torvalds committed
183
184
	}

185
	dev_priv->render_ring.virtual_start = dev_priv->render_ring.map.handle;
Linus Torvalds's avatar
Linus Torvalds committed
186

187
	dev_priv->cpp = init->cpp;
Linus Torvalds's avatar
Linus Torvalds committed
188
189
190
	dev_priv->back_offset = init->back_offset;
	dev_priv->front_offset = init->front_offset;
	dev_priv->current_page = 0;
191
192
	if (master_priv->sarea_priv)
		master_priv->sarea_priv->pf_current_page = 0;
Linus Torvalds's avatar
Linus Torvalds committed
193
194
195
196
197
198
199
200

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

	return 0;
}

201
static int i915_dma_resume(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
202
203
204
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;

205
	struct intel_ring_buffer *ring;
206
	DRM_DEBUG_DRIVER("%s\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
207

208
209
210
	ring = &dev_priv->render_ring;

	if (ring->map.handle == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
211
212
		DRM_ERROR("can not ioremap virtual address for"
			  " ring buffer\n");
Eric Anholt's avatar
Eric Anholt committed
213
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
214
215
216
	}

	/* Program Hardware Status Page */
217
	if (!ring->status_page.page_addr) {
Linus Torvalds's avatar
Linus Torvalds committed
218
		DRM_ERROR("Can not find hardware status page\n");
Eric Anholt's avatar
Eric Anholt committed
219
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
220
	}
221
	DRM_DEBUG_DRIVER("hw status page @ %p\n",
222
223
				ring->status_page.page_addr);
	if (ring->status_page.gfx_addr != 0)
224
		intel_ring_setup_status_page(ring);
225
	else
226
		I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
227

228
	DRM_DEBUG_DRIVER("Enabled hardware status page\n");
Linus Torvalds's avatar
Linus Torvalds committed
229
230
231
232

	return 0;
}

233
234
static int i915_dma_init(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
235
{
236
	drm_i915_init_t *init = data;
Linus Torvalds's avatar
Linus Torvalds committed
237
238
	int retcode = 0;

239
	switch (init->func) {
Linus Torvalds's avatar
Linus Torvalds committed
240
	case I915_INIT_DMA:
241
		retcode = i915_initialize(dev, init);
Linus Torvalds's avatar
Linus Torvalds committed
242
243
244
245
246
		break;
	case I915_CLEANUP_DMA:
		retcode = i915_dma_cleanup(dev);
		break;
	case I915_RESUME_DMA:
Dave Airlie's avatar
Dave Airlie committed
247
		retcode = i915_dma_resume(dev);
Linus Torvalds's avatar
Linus Torvalds committed
248
249
		break;
	default:
Eric Anholt's avatar
Eric Anholt committed
250
		retcode = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
		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.
 */
266
static int validate_cmd(int cmd)
Linus Torvalds's avatar
Linus Torvalds committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
{
	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
291
			switch ((cmd >> 16) & 0xff) {
Linus Torvalds's avatar
Linus Torvalds committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
			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;
}

324
static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
Linus Torvalds's avatar
Linus Torvalds committed
325
326
{
	drm_i915_private_t *dev_priv = dev->dev_private;
327
	int i, ret;
Linus Torvalds's avatar
Linus Torvalds committed
328

329
	if ((dwords+1) * sizeof(int) >= dev_priv->render_ring.size - 8)
Eric Anholt's avatar
Eric Anholt committed
330
		return -EINVAL;
331

Linus Torvalds's avatar
Linus Torvalds committed
332
	for (i = 0; i < dwords;) {
333
334
		int sz = validate_cmd(buffer[i]);
		if (sz == 0 || i + sz > dwords)
Eric Anholt's avatar
Eric Anholt committed
335
			return -EINVAL;
336
		i += sz;
Linus Torvalds's avatar
Linus Torvalds committed
337
338
	}

339
340
341
342
343
344
	ret = BEGIN_LP_RING((dwords+1)&~1);
	if (ret)
		return ret;

	for (i = 0; i < dwords; i++)
		OUT_RING(buffer[i]);
345
346
347
348
349
	if (dwords & 1)
		OUT_RING(0);

	ADVANCE_LP_RING();

Linus Torvalds's avatar
Linus Torvalds committed
350
351
352
	return 0;
}

353
354
int
i915_emit_box(struct drm_device *dev,
355
	      struct drm_clip_rect *boxes,
356
	      int i, int DR1, int DR4)
Linus Torvalds's avatar
Linus Torvalds committed
357
{
358
	struct drm_i915_private *dev_priv = dev->dev_private;
359
	struct drm_clip_rect box = boxes[i];
360
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
361
362
363
364

	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
365
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
366
367
	}

368
	if (INTEL_INFO(dev)->gen >= 4) {
369
370
371
372
		ret = BEGIN_LP_RING(4);
		if (ret)
			return ret;

373
374
		OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
		OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
Andrew Morton's avatar
Andrew Morton committed
375
		OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
376
377
		OUT_RING(DR4);
	} else {
378
379
380
381
		ret = BEGIN_LP_RING(6);
		if (ret)
			return ret;

382
383
384
385
386
387
388
		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);
	}
389
	ADVANCE_LP_RING();
Linus Torvalds's avatar
Linus Torvalds committed
390
391
392
393

	return 0;
}

394
395
396
397
/* XXX: Emitting the counter should really be moved to part of the IRQ
 * emit. For now, do it in both places:
 */

398
static void i915_emit_breadcrumb(struct drm_device *dev)
399
400
{
	drm_i915_private_t *dev_priv = dev->dev_private;
401
	struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
402

403
	dev_priv->counter++;
404
	if (dev_priv->counter > 0x7FFFFFFFUL)
405
		dev_priv->counter = 0;
406
407
	if (master_priv->sarea_priv)
		master_priv->sarea_priv->last_enqueue = dev_priv->counter;
408

409
410
411
412
413
414
415
	if (BEGIN_LP_RING(4) == 0) {
		OUT_RING(MI_STORE_DWORD_INDEX);
		OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
		OUT_RING(dev_priv->counter);
		OUT_RING(0);
		ADVANCE_LP_RING();
	}
416
417
}

418
static int i915_dispatch_cmdbuffer(struct drm_device * dev,
419
420
421
				   drm_i915_cmdbuffer_t *cmd,
				   struct drm_clip_rect *cliprects,
				   void *cmdbuf)
Linus Torvalds's avatar
Linus Torvalds committed
422
423
424
425
426
427
{
	int nbox = cmd->num_cliprects;
	int i = 0, count, ret;

	if (cmd->sz & 0x3) {
		DRM_ERROR("alignment");
Eric Anholt's avatar
Eric Anholt committed
428
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
429
430
431
432
433
434
435
436
	}

	i915_kernel_lost_context(dev);

	count = nbox ? nbox : 1;

	for (i = 0; i < count; i++) {
		if (i < nbox) {
437
			ret = i915_emit_box(dev, cliprects, i,
Linus Torvalds's avatar
Linus Torvalds committed
438
439
440
441
442
					    cmd->DR1, cmd->DR4);
			if (ret)
				return ret;
		}

443
		ret = i915_emit_cmds(dev, cmdbuf, cmd->sz / 4);
Linus Torvalds's avatar
Linus Torvalds committed
444
445
446
447
		if (ret)
			return ret;
	}

448
	i915_emit_breadcrumb(dev);
Linus Torvalds's avatar
Linus Torvalds committed
449
450
451
	return 0;
}

452
static int i915_dispatch_batchbuffer(struct drm_device * dev,
453
454
				     drm_i915_batchbuffer_t * batch,
				     struct drm_clip_rect *cliprects)
Linus Torvalds's avatar
Linus Torvalds committed
455
{
456
	struct drm_i915_private *dev_priv = dev->dev_private;
Linus Torvalds's avatar
Linus Torvalds committed
457
	int nbox = batch->num_cliprects;
458
	int i, count, ret;
Linus Torvalds's avatar
Linus Torvalds committed
459
460
461

	if ((batch->start | batch->used) & 0x7) {
		DRM_ERROR("alignment");
Eric Anholt's avatar
Eric Anholt committed
462
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
463
464
465
466
467
468
469
	}

	i915_kernel_lost_context(dev);

	count = nbox ? nbox : 1;
	for (i = 0; i < count; i++) {
		if (i < nbox) {
470
471
			ret = i915_emit_box(dev, cliprects, i,
					    batch->DR1, batch->DR4);
Linus Torvalds's avatar
Linus Torvalds committed
472
473
474
475
			if (ret)
				return ret;
		}

476
		if (!IS_I830(dev) && !IS_845G(dev)) {
477
478
479
480
			ret = BEGIN_LP_RING(2);
			if (ret)
				return ret;

481
			if (INTEL_INFO(dev)->gen >= 4) {
482
483
484
485
486
487
				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
488
		} else {
489
490
491
492
			ret = BEGIN_LP_RING(4);
			if (ret)
				return ret;

Linus Torvalds's avatar
Linus Torvalds committed
493
494
495
496
497
			OUT_RING(MI_BATCH_BUFFER);
			OUT_RING(batch->start | MI_BATCH_NON_SECURE);
			OUT_RING(batch->start + batch->used - 4);
			OUT_RING(0);
		}
498
		ADVANCE_LP_RING();
Linus Torvalds's avatar
Linus Torvalds committed
499
500
	}

501

502
	if (IS_G4X(dev) || IS_GEN5(dev)) {
503
504
505
506
507
		if (BEGIN_LP_RING(2) == 0) {
			OUT_RING(MI_FLUSH | MI_NO_WRITE_FLUSH | MI_INVALIDATE_ISP);
			OUT_RING(MI_NOOP);
			ADVANCE_LP_RING();
		}
508
	}
Linus Torvalds's avatar
Linus Torvalds committed
509

510
	i915_emit_breadcrumb(dev);
Linus Torvalds's avatar
Linus Torvalds committed
511
512
513
	return 0;
}

514
static int i915_dispatch_flip(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
515
516
{
	drm_i915_private_t *dev_priv = dev->dev_private;
517
518
	struct drm_i915_master_private *master_priv =
		dev->primary->master->driver_priv;
519
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
520

521
	if (!master_priv->sarea_priv)
522
523
		return -EINVAL;

524
	DRM_DEBUG_DRIVER("%s: page=%d pfCurrentPage=%d\n",
525
526
527
			  __func__,
			 dev_priv->current_page,
			 master_priv->sarea_priv->pf_current_page);
Linus Torvalds's avatar
Linus Torvalds committed
528

529
530
	i915_kernel_lost_context(dev);

531
532
533
534
	ret = BEGIN_LP_RING(10);
	if (ret)
		return ret;

535
	OUT_RING(MI_FLUSH | MI_READ_FLUSH);
536
	OUT_RING(0);
Linus Torvalds's avatar
Linus Torvalds committed
537

538
539
540
541
542
	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
543
	} else {
544
545
		OUT_RING(dev_priv->front_offset);
		dev_priv->current_page = 0;
Linus Torvalds's avatar
Linus Torvalds committed
546
	}
547
	OUT_RING(0);
Linus Torvalds's avatar
Linus Torvalds committed
548

549
550
	OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP);
	OUT_RING(0);
551

552
	ADVANCE_LP_RING();
Linus Torvalds's avatar
Linus Torvalds committed
553

554
	master_priv->sarea_priv->last_enqueue = dev_priv->counter++;
Linus Torvalds's avatar
Linus Torvalds committed
555

556
557
558
559
560
561
562
	if (BEGIN_LP_RING(4) == 0) {
		OUT_RING(MI_STORE_DWORD_INDEX);
		OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
		OUT_RING(dev_priv->counter);
		OUT_RING(0);
		ADVANCE_LP_RING();
	}
Linus Torvalds's avatar
Linus Torvalds committed
563

564
	master_priv->sarea_priv->pf_current_page = dev_priv->current_page;
565
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
566
567
}

568
static int i915_quiescent(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
569
570
571
572
{
	drm_i915_private_t *dev_priv = dev->dev_private;

	i915_kernel_lost_context(dev);
573
	return intel_wait_ring_buffer(&dev_priv->render_ring,
574
				      dev_priv->render_ring.size - 8);
Linus Torvalds's avatar
Linus Torvalds committed
575
576
}

577
578
static int i915_flush_ioctl(struct drm_device *dev, void *data,
			    struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
579
{
580
581
582
	int ret;

	RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
583

584
585
586
587
588
	mutex_lock(&dev->struct_mutex);
	ret = i915_quiescent(dev);
	mutex_unlock(&dev->struct_mutex);

	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
589
590
}

591
592
static int i915_batchbuffer(struct drm_device *dev, void *data,
			    struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
593
594
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
595
	struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
Linus Torvalds's avatar
Linus Torvalds committed
596
	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
597
	    master_priv->sarea_priv;
598
	drm_i915_batchbuffer_t *batch = data;
Linus Torvalds's avatar
Linus Torvalds committed
599
	int ret;
600
	struct drm_clip_rect *cliprects = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
601
602
603

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

607
	DRM_DEBUG_DRIVER("i915 batchbuffer, start %x used %d cliprects %d\n",
608
			batch->start, batch->used, batch->num_cliprects);
Linus Torvalds's avatar
Linus Torvalds committed
609

610
	RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
611

612
613
614
615
	if (batch->num_cliprects < 0)
		return -EINVAL;

	if (batch->num_cliprects) {
616
617
618
		cliprects = kcalloc(batch->num_cliprects,
				    sizeof(struct drm_clip_rect),
				    GFP_KERNEL);
619
620
621
622
623
624
		if (cliprects == NULL)
			return -ENOMEM;

		ret = copy_from_user(cliprects, batch->cliprects,
				     batch->num_cliprects *
				     sizeof(struct drm_clip_rect));
625
626
		if (ret != 0) {
			ret = -EFAULT;
627
			goto fail_free;
628
		}
629
	}
Linus Torvalds's avatar
Linus Torvalds committed
630

631
	mutex_lock(&dev->struct_mutex);
632
	ret = i915_dispatch_batchbuffer(dev, batch, cliprects);
633
	mutex_unlock(&dev->struct_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
634

635
	if (sarea_priv)
636
		sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
637
638

fail_free:
639
	kfree(cliprects);
640

Linus Torvalds's avatar
Linus Torvalds committed
641
642
643
	return ret;
}

644
645
static int i915_cmdbuffer(struct drm_device *dev, void *data,
			  struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
646
647
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
648
	struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
Linus Torvalds's avatar
Linus Torvalds committed
649
	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
650
	    master_priv->sarea_priv;
651
	drm_i915_cmdbuffer_t *cmdbuf = data;
652
653
	struct drm_clip_rect *cliprects = NULL;
	void *batch_data;
Linus Torvalds's avatar
Linus Torvalds committed
654
655
	int ret;

656
	DRM_DEBUG_DRIVER("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
657
			cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects);
Linus Torvalds's avatar
Linus Torvalds committed
658

659
	RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
660

661
662
663
	if (cmdbuf->num_cliprects < 0)
		return -EINVAL;

664
	batch_data = kmalloc(cmdbuf->sz, GFP_KERNEL);
665
666
667
668
	if (batch_data == NULL)
		return -ENOMEM;

	ret = copy_from_user(batch_data, cmdbuf->buf, cmdbuf->sz);
669
670
	if (ret != 0) {
		ret = -EFAULT;
671
		goto fail_batch_free;
672
	}
673
674

	if (cmdbuf->num_cliprects) {
675
676
		cliprects = kcalloc(cmdbuf->num_cliprects,
				    sizeof(struct drm_clip_rect), GFP_KERNEL);
677
678
		if (cliprects == NULL) {
			ret = -ENOMEM;
679
			goto fail_batch_free;
680
		}
681
682
683
684

		ret = copy_from_user(cliprects, cmdbuf->cliprects,
				     cmdbuf->num_cliprects *
				     sizeof(struct drm_clip_rect));
685
686
		if (ret != 0) {
			ret = -EFAULT;
687
			goto fail_clip_free;
688
		}
Linus Torvalds's avatar
Linus Torvalds committed
689
690
	}

691
	mutex_lock(&dev->struct_mutex);
692
	ret = i915_dispatch_cmdbuffer(dev, cmdbuf, cliprects, batch_data);
693
	mutex_unlock(&dev->struct_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
694
695
	if (ret) {
		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
696
		goto fail_clip_free;
Linus Torvalds's avatar
Linus Torvalds committed
697
698
	}

699
	if (sarea_priv)
700
		sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
701
702

fail_clip_free:
703
	kfree(cliprects);
704
fail_batch_free:
705
	kfree(batch_data);
706
707

	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
708
709
}

710
711
static int i915_flip_bufs(struct drm_device *dev, void *data,
			  struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
712
{
713
714
	int ret;

715
	DRM_DEBUG_DRIVER("%s\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
716

717
	RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
Linus Torvalds's avatar
Linus Torvalds committed
718

719
720
721
722
723
	mutex_lock(&dev->struct_mutex);
	ret = i915_dispatch_flip(dev);
	mutex_unlock(&dev->struct_mutex);

	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
724
725
}

726
727
static int i915_getparam(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
728
729
{
	drm_i915_private_t *dev_priv = dev->dev_private;
730
	drm_i915_getparam_t *param = data;
Linus Torvalds's avatar
Linus Torvalds committed
731
732
733
	int value;

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

738
	switch (param->param) {
Linus Torvalds's avatar
Linus Torvalds committed
739
	case I915_PARAM_IRQ_ACTIVE:
740
		value = dev->pdev->irq ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
741
742
743
744
		break;
	case I915_PARAM_ALLOW_BATCHBUFFER:
		value = dev_priv->allow_batchbuffer ? 1 : 0;
		break;
Dave Airlie's avatar
Dave Airlie committed
745
746
747
	case I915_PARAM_LAST_DISPATCH:
		value = READ_BREADCRUMB(dev_priv);
		break;
748
749
750
	case I915_PARAM_CHIPSET_ID:
		value = dev->pci_device;
		break;
751
	case I915_PARAM_HAS_GEM:
752
		value = dev_priv->has_gem;
753
		break;
754
755
756
	case I915_PARAM_NUM_FENCES_AVAIL:
		value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
		break;
757
758
759
	case I915_PARAM_HAS_OVERLAY:
		value = dev_priv->overlay ? 1 : 0;
		break;
760
761
762
	case I915_PARAM_HAS_PAGEFLIPPING:
		value = 1;
		break;
Jesse Barnes's avatar
Jesse Barnes committed
763
764
765
766
	case I915_PARAM_HAS_EXECBUF2:
		/* depends on GEM */
		value = dev_priv->has_gem;
		break;
767
768
769
	case I915_PARAM_HAS_BSD:
		value = HAS_BSD(dev);
		break;
770
771
772
	case I915_PARAM_HAS_BLT:
		value = HAS_BLT(dev);
		break;
773
774
775
	case I915_PARAM_HAS_RELAXED_FENCING:
		value = 1;
		break;
Linus Torvalds's avatar
Linus Torvalds committed
776
	default:
777
		DRM_DEBUG_DRIVER("Unknown parameter %d\n",
Jesse Barnes's avatar
Jesse Barnes committed
778
				 param->param);
Eric Anholt's avatar
Eric Anholt committed
779
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
780
781
	}

782
	if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) {
Linus Torvalds's avatar
Linus Torvalds committed
783
		DRM_ERROR("DRM_COPY_TO_USER failed\n");
Eric Anholt's avatar
Eric Anholt committed
784
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
785
786
787
788
789
	}

	return 0;
}

790
791
static int i915_setparam(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
792
793
{
	drm_i915_private_t *dev_priv = dev->dev_private;
794
	drm_i915_setparam_t *param = data;
Linus Torvalds's avatar
Linus Torvalds committed
795
796

	if (!dev_priv) {
797
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
798
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
799
800
	}

801
	switch (param->param) {
Linus Torvalds's avatar
Linus Torvalds committed
802
803
804
	case I915_SETPARAM_USE_MI_BATCHBUFFER_START:
		break;
	case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY:
805
		dev_priv->tex_lru_log_granularity = param->value;
Linus Torvalds's avatar
Linus Torvalds committed
806
807
		break;
	case I915_SETPARAM_ALLOW_BATCHBUFFER:
808
		dev_priv->allow_batchbuffer = param->value;
Linus Torvalds's avatar
Linus Torvalds committed
809
		break;
810
811
812
813
814
815
816
	case I915_SETPARAM_NUM_USED_FENCES:
		if (param->value > dev_priv->num_fence_regs ||
		    param->value < 0)
			return -EINVAL;
		/* Userspace can use first N regs */
		dev_priv->fence_reg_start = param->value;
		break;
Linus Torvalds's avatar
Linus Torvalds committed
817
	default:
818
		DRM_DEBUG_DRIVER("unknown parameter %d\n",
819
					param->param);
Eric Anholt's avatar
Eric Anholt committed
820
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
821
822
823
824
825
	}

	return 0;
}

826
827
static int i915_set_status_page(struct drm_device *dev, void *data,
				struct drm_file *file_priv)
828
829
{
	drm_i915_private_t *dev_priv = dev->dev_private;
830
	drm_i915_hws_addr_t *hws = data;
831
	struct intel_ring_buffer *ring = &dev_priv->render_ring;
832
833
834

	if (!I915_NEED_GFX_HWS(dev))
		return -EINVAL;
835
836

	if (!dev_priv) {
837
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
838
		return -EINVAL;
839
840
	}

841
842
843
844
845
	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
		WARN(1, "tried to set status page when mode setting active\n");
		return 0;
	}

846
	DRM_DEBUG_DRIVER("set status page addr 0x%08x\n", (u32)hws->addr);
847

848
	ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12);
849

850
	dev_priv->hws_map.offset = dev->agp->base + hws->addr;
851
852
853
854
855
	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;

856
	drm_core_ioremap_wc(&dev_priv->hws_map, dev);
857
858
	if (dev_priv->hws_map.handle == NULL) {
		i915_dma_cleanup(dev);
859
		ring->status_page.gfx_addr = 0;
860
861
		DRM_ERROR("can not ioremap virtual address for"
				" G33 hw status page\n");
Eric Anholt's avatar
Eric Anholt committed
862
		return -ENOMEM;
863
	}
864
865
866
	ring->status_page.page_addr = dev_priv->hws_map.handle;
	memset(ring->status_page.page_addr, 0, PAGE_SIZE);
	I915_WRITE(HWS_PGA, ring->status_page.gfx_addr);
867

868
	DRM_DEBUG_DRIVER("load hws HWS_PGA with gfx mem 0x%x\n",
869
			 ring->status_page.gfx_addr);
870
	DRM_DEBUG_DRIVER("load hws at %p\n",
871
			 ring->status_page.page_addr);
872
873
874
	return 0;
}

875
876
877
878
879
880
881
882
883
884
885
886
static int i915_get_bridge_dev(struct drm_device *dev)
{
	struct drm_i915_private *dev_priv = dev->dev_private;

	dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
	if (!dev_priv->bridge_dev) {
		DRM_ERROR("bridge device not found\n");
		return -1;
	}
	return 0;
}

887
888
889
890
891
892
893
894
895
896
897
898
#define MCHBAR_I915 0x44
#define MCHBAR_I965 0x48
#define MCHBAR_SIZE (4*4096)

#define DEVEN_REG 0x54
#define   DEVEN_MCHBAR_EN (1 << 28)

/* Allocate space for the MCH regs if needed, return nonzero on error */
static int
intel_alloc_mchbar_resource(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
899
	int reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
900
901
	u32 temp_lo, temp_hi = 0;
	u64 mchbar_addr;
902
	int ret;
903

904
	if (INTEL_INFO(dev)->gen >= 4)
905
906
907
908
909
910
911
		pci_read_config_dword(dev_priv->bridge_dev, reg + 4, &temp_hi);
	pci_read_config_dword(dev_priv->bridge_dev, reg, &temp_lo);
	mchbar_addr = ((u64)temp_hi << 32) | temp_lo;

	/* If ACPI doesn't have it, assume we need to allocate it ourselves */
#ifdef CONFIG_PNP
	if (mchbar_addr &&
912
913
	    pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE))
		return 0;
914
915
916
#endif

	/* Get some space for it */
917
918
919
920
	dev_priv->mch_res.name = "i915 MCHBAR";
	dev_priv->mch_res.flags = IORESOURCE_MEM;
	ret = pci_bus_alloc_resource(dev_priv->bridge_dev->bus,
				     &dev_priv->mch_res,
921
922
				     MCHBAR_SIZE, MCHBAR_SIZE,
				     PCIBIOS_MIN_MEM,
923
				     0, pcibios_align_resource,
924
925
926
927
				     dev_priv->bridge_dev);
	if (ret) {
		DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret);
		dev_priv->mch_res.start = 0;
928
		return ret;
929
930
	}

931
	if (INTEL_INFO(dev)->gen >= 4)
932
933
934
935
936
		pci_write_config_dword(dev_priv->bridge_dev, reg + 4,
				       upper_32_bits(dev_priv->mch_res.start));

	pci_write_config_dword(dev_priv->bridge_dev, reg,
			       lower_32_bits(dev_priv->mch_res.start));
937
	return 0;
938
939
940
941
942
943
944
}

/* Setup MCHBAR if possible, return true if we should disable it again */
static void
intel_setup_mchbar(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
945
	int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
	u32 temp;
	bool enabled;

	dev_priv->mchbar_need_disable = false;

	if (IS_I915G(dev) || IS_I915GM(dev)) {
		pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
		enabled = !!(temp & DEVEN_MCHBAR_EN);
	} else {
		pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
		enabled = temp & 1;
	}

	/* If it's already enabled, don't have to do anything */
	if (enabled)
		return;

	if (intel_alloc_mchbar_resource(dev))
		return;

	dev_priv->mchbar_need_disable = true;

	/* Space is allocated or reserved, so enable it. */
	if (IS_I915G(dev) || IS_I915GM(dev)) {
		pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG,
				       temp | DEVEN_MCHBAR_EN);
	} else {
		pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
		pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp | 1);
	}
}

static void
intel_teardown_mchbar(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
982
	int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
	u32 temp;

	if (dev_priv->mchbar_need_disable) {
		if (IS_I915G(dev) || IS_I915GM(dev)) {
			pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
			temp &= ~DEVEN_MCHBAR_EN;
			pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, temp);
		} else {
			pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
			temp &= ~1;
			pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp);
		}
	}

	if (dev_priv->mch_res.start)
		release_resource(&dev_priv->mch_res);
}

For faster browsing, not all history is shown. View entire blame