i915_dma.c 21.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*-
 */
D
Dave Airlie 已提交
3
/*
L
Linus Torvalds 已提交
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.
 *
D
Dave Airlie 已提交
27
 */
L
Linus Torvalds 已提交
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)
L
Linus Torvalds 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
	u32 last_head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
	int i;

	for (i = 0; i < 10000; i++) {
		ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
		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;

		last_head = ring->head;
	}

E
Eric Anholt 已提交
62
	return -EBUSY;
L
Linus Torvalds 已提交
63 64
}

65
void i915_kernel_lost_context(struct drm_device * dev)
L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);

	ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
	ring->tail = I915_READ(LP_RING + RING_TAIL) & TAIL_ADDR;
	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;
}

80
static int i915_dma_cleanup(struct drm_device * dev)
L
Linus Torvalds 已提交
81
{
J
Jesse Barnes 已提交
82
	drm_i915_private_t *dev_priv = dev->dev_private;
L
Linus Torvalds 已提交
83 84 85 86 87
	/* 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.
	 */
	if (dev->irq)
D
Dave Airlie 已提交
88
		drm_irq_uninstall(dev);
L
Linus Torvalds 已提交
89

J
Jesse Barnes 已提交
90 91 92 93 94 95
	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;
	}
96

J
Jesse Barnes 已提交
97 98 99 100 101 102
	if (dev_priv->status_page_dmah) {
		drm_pci_free(dev, dev_priv->status_page_dmah);
		dev_priv->status_page_dmah = NULL;
		/* Need to rewrite hardware status page */
		I915_WRITE(0x02080, 0x1ffff000);
	}
L
Linus Torvalds 已提交
103

J
Jesse Barnes 已提交
104 105 106 107
	if (dev_priv->status_gfx_addr) {
		dev_priv->status_gfx_addr = 0;
		drm_core_ioremapfree(&dev_priv->hws_map, dev);
		I915_WRITE(0x2080, 0x1ffff000);
L
Linus Torvalds 已提交
108 109 110 111 112
	}

	return 0;
}

J
Jesse Barnes 已提交
113
static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
L
Linus Torvalds 已提交
114
{
J
Jesse Barnes 已提交
115
	drm_i915_private_t *dev_priv = dev->dev_private;
L
Linus Torvalds 已提交
116

117
	dev_priv->sarea = drm_getsarea(dev);
L
Linus Torvalds 已提交
118 119 120
	if (!dev_priv->sarea) {
		DRM_ERROR("can not find sarea!\n");
		i915_dma_cleanup(dev);
E
Eric Anholt 已提交
121
		return -EINVAL;
L
Linus Torvalds 已提交
122 123 124 125 126 127
	}

	dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset);
	if (!dev_priv->mmio_map) {
		i915_dma_cleanup(dev);
		DRM_ERROR("can not find mmio map!\n");
E
Eric Anholt 已提交
128
		return -EINVAL;
L
Linus Torvalds 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	}

	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;

D
Dave Airlie 已提交
145
	drm_core_ioremap(&dev_priv->ring.map, dev);
L
Linus Torvalds 已提交
146 147 148 149 150

	if (dev_priv->ring.map.handle == NULL) {
		i915_dma_cleanup(dev);
		DRM_ERROR("can not ioremap virtual address for"
			  " ring buffer\n");
E
Eric Anholt 已提交
151
		return -ENOMEM;
L
Linus Torvalds 已提交
152 153 154 155
	}

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

156
	dev_priv->cpp = init->cpp;
L
Linus Torvalds 已提交
157 158 159 160 161 162 163 164 165
	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;

	/* We are using separate values as placeholders for mechanisms for
	 * private backbuffer/depthbuffer usage.
	 */
	dev_priv->use_mi_batchbuffer_start = 0;
166 167
	if (IS_I965G(dev)) /* 965 doesn't support older method */
		dev_priv->use_mi_batchbuffer_start = 1;
L
Linus Torvalds 已提交
168 169 170 171 172 173

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

	/* Program Hardware Status Page */
174
	if (!I915_NEED_GFX_HWS(dev)) {
175 176 177 178 179 180
		dev_priv->status_page_dmah =
			drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, 0xffffffff);

		if (!dev_priv->status_page_dmah) {
			i915_dma_cleanup(dev);
			DRM_ERROR("Can not allocate hardware status page\n");
E
Eric Anholt 已提交
181
			return -ENOMEM;
182 183 184
		}
		dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr;
		dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;
L
Linus Torvalds 已提交
185

186 187
		memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
		I915_WRITE(0x02080, dev_priv->dma_status_page);
L
Linus Torvalds 已提交
188 189 190 191 192
	}
	DRM_DEBUG("Enabled hardware status page\n");
	return 0;
}

193
static int i915_dma_resume(struct drm_device * dev)
L
Linus Torvalds 已提交
194 195 196 197 198 199 200
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;

	DRM_DEBUG("%s\n", __FUNCTION__);

	if (!dev_priv->sarea) {
		DRM_ERROR("can not find sarea!\n");
E
Eric Anholt 已提交
201
		return -EINVAL;
L
Linus Torvalds 已提交
202 203 204 205
	}

	if (!dev_priv->mmio_map) {
		DRM_ERROR("can not find mmio map!\n");
E
Eric Anholt 已提交
206
		return -EINVAL;
L
Linus Torvalds 已提交
207 208 209 210 211
	}

	if (dev_priv->ring.map.handle == NULL) {
		DRM_ERROR("can not ioremap virtual address for"
			  " ring buffer\n");
E
Eric Anholt 已提交
212
		return -ENOMEM;
L
Linus Torvalds 已提交
213 214 215 216 217
	}

	/* Program Hardware Status Page */
	if (!dev_priv->hw_status_page) {
		DRM_ERROR("Can not find hardware status page\n");
E
Eric Anholt 已提交
218
		return -EINVAL;
L
Linus Torvalds 已提交
219 220 221
	}
	DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);

222 223 224 225
	if (dev_priv->status_gfx_addr != 0)
		I915_WRITE(0x02080, dev_priv->status_gfx_addr);
	else
		I915_WRITE(0x02080, dev_priv->dma_status_page);
L
Linus Torvalds 已提交
226 227 228 229 230
	DRM_DEBUG("Enabled hardware status page\n");

	return 0;
}

231 232
static int i915_dma_init(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
L
Linus Torvalds 已提交
233
{
234
	drm_i915_init_t *init = data;
L
Linus Torvalds 已提交
235 236
	int retcode = 0;

237
	switch (init->func) {
L
Linus Torvalds 已提交
238
	case I915_INIT_DMA:
J
Jesse Barnes 已提交
239
		retcode = i915_initialize(dev, init);
L
Linus Torvalds 已提交
240 241 242 243 244
		break;
	case I915_CLEANUP_DMA:
		retcode = i915_dma_cleanup(dev);
		break;
	case I915_RESUME_DMA:
D
Dave Airlie 已提交
245
		retcode = i915_dma_resume(dev);
L
Linus Torvalds 已提交
246 247
		break;
	default:
E
Eric Anholt 已提交
248
		retcode = -EINVAL;
L
Linus Torvalds 已提交
249 250 251 252 253 254 255 256 257 258 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
		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:
D
Dave Airlie 已提交
289
			switch ((cmd >> 16) & 0xff) {
L
Linus Torvalds 已提交
290 291 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 324 325
			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);

D
Dave Airlie 已提交
326
/*	printk("validate_cmd( %x ): %d\n", cmd, ret); */
L
Linus Torvalds 已提交
327 328 329 330

	return ret;
}

331
static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwords)
L
Linus Torvalds 已提交
332 333 334 335 336
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	int i;
	RING_LOCALS;

337
	if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8)
E
Eric Anholt 已提交
338
		return -EINVAL;
339

340
	BEGIN_LP_RING((dwords+1)&~1);
341

L
Linus Torvalds 已提交
342 343 344 345
	for (i = 0; i < dwords;) {
		int cmd, sz;

		if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd)))
E
Eric Anholt 已提交
346
			return -EINVAL;
L
Linus Torvalds 已提交
347 348

		if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords)
E
Eric Anholt 已提交
349
			return -EINVAL;
L
Linus Torvalds 已提交
350 351 352 353 354 355

		OUT_RING(cmd);

		while (++i, --sz) {
			if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i],
							 sizeof(cmd))) {
E
Eric Anholt 已提交
356
				return -EINVAL;
L
Linus Torvalds 已提交
357 358 359 360 361
			}
			OUT_RING(cmd);
		}
	}

362 363 364 365 366
	if (dwords & 1)
		OUT_RING(0);

	ADVANCE_LP_RING();

L
Linus Torvalds 已提交
367 368 369
	return 0;
}

370
static int i915_emit_box(struct drm_device * dev,
371
			 struct drm_clip_rect __user * boxes,
L
Linus Torvalds 已提交
372 373 374
			 int i, int DR1, int DR4)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
375
	struct drm_clip_rect box;
L
Linus Torvalds 已提交
376 377 378
	RING_LOCALS;

	if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
E
Eric Anholt 已提交
379
		return -EFAULT;
L
Linus Torvalds 已提交
380 381 382 383 384
	}

	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);
E
Eric Anholt 已提交
385
		return -EINVAL;
L
Linus Torvalds 已提交
386 387
	}

388 389 390 391
	if (IS_I965G(dev)) {
		BEGIN_LP_RING(4);
		OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
		OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
A
Andrew Morton 已提交
392
		OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
393 394 395 396 397 398 399 400 401 402 403 404
		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();
	}
L
Linus Torvalds 已提交
405 406 407 408

	return 0;
}

409 410 411 412
/* XXX: Emitting the counter should really be moved to part of the IRQ
 * emit. For now, do it in both places:
 */

413
static void i915_emit_breadcrumb(struct drm_device *dev)
414 415 416 417
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	RING_LOCALS;

418 419 420 421
	dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;

	if (dev_priv->counter > 0x7FFFFFFFUL)
		dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;
422 423 424 425 426 427 428 429 430

	BEGIN_LP_RING(4);
	OUT_RING(CMD_STORE_DWORD_IDX);
	OUT_RING(20);
	OUT_RING(dev_priv->counter);
	OUT_RING(0);
	ADVANCE_LP_RING();
}

431
static int i915_dispatch_cmdbuffer(struct drm_device * dev,
L
Linus Torvalds 已提交
432 433 434 435 436 437 438
				   drm_i915_cmdbuffer_t * cmd)
{
	int nbox = cmd->num_cliprects;
	int i = 0, count, ret;

	if (cmd->sz & 0x3) {
		DRM_ERROR("alignment");
E
Eric Anholt 已提交
439
		return -EINVAL;
L
Linus Torvalds 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
	}

	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;
	}

459
	i915_emit_breadcrumb(dev);
L
Linus Torvalds 已提交
460 461 462
	return 0;
}

463
static int i915_dispatch_batchbuffer(struct drm_device * dev,
L
Linus Torvalds 已提交
464 465 466
				     drm_i915_batchbuffer_t * batch)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
467
	struct drm_clip_rect __user *boxes = batch->cliprects;
L
Linus Torvalds 已提交
468 469 470 471 472 473
	int nbox = batch->num_cliprects;
	int i = 0, count;
	RING_LOCALS;

	if ((batch->start | batch->used) & 0x7) {
		DRM_ERROR("alignment");
E
Eric Anholt 已提交
474
		return -EINVAL;
L
Linus Torvalds 已提交
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
	}

	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;
		}

		if (dev_priv->use_mi_batchbuffer_start) {
			BEGIN_LP_RING(2);
491 492 493 494 495 496 497
			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);
			}
L
Linus Torvalds 已提交
498 499 500 501 502 503 504 505 506 507 508
			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();
		}
	}

509
	i915_emit_breadcrumb(dev);
L
Linus Torvalds 已提交
510 511 512 513

	return 0;
}

514
static int i915_dispatch_flip(struct drm_device * dev)
L
Linus Torvalds 已提交
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	RING_LOCALS;

	DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n",
		  __FUNCTION__,
		  dev_priv->current_page,
		  dev_priv->sarea_priv->pf_current_page);

	i915_kernel_lost_context(dev);

	BEGIN_LP_RING(2);
	OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE);
	OUT_RING(0);
	ADVANCE_LP_RING();

	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;
	} else {
		OUT_RING(dev_priv->front_offset);
		dev_priv->current_page = 0;
	}
	OUT_RING(0);
	ADVANCE_LP_RING();

	BEGIN_LP_RING(2);
	OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP);
	OUT_RING(0);
	ADVANCE_LP_RING();

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

	BEGIN_LP_RING(4);
	OUT_RING(CMD_STORE_DWORD_IDX);
	OUT_RING(20);
	OUT_RING(dev_priv->counter);
	OUT_RING(0);
	ADVANCE_LP_RING();

	dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
	return 0;
}

562
static int i915_quiescent(struct drm_device * dev)
L
Linus Torvalds 已提交
563 564 565 566 567 568 569
{
	drm_i915_private_t *dev_priv = dev->dev_private;

	i915_kernel_lost_context(dev);
	return i915_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__);
}

570 571
static int i915_flush_ioctl(struct drm_device *dev, void *data,
			    struct drm_file *file_priv)
L
Linus Torvalds 已提交
572
{
573
	LOCK_TEST_WITH_RETURN(dev, file_priv);
L
Linus Torvalds 已提交
574 575 576 577

	return i915_quiescent(dev);
}

578 579
static int i915_batchbuffer(struct drm_device *dev, void *data,
			    struct drm_file *file_priv)
L
Linus Torvalds 已提交
580 581 582 583 584
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
	u32 *hw_status = dev_priv->hw_status_page;
	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
	    dev_priv->sarea_priv;
585
	drm_i915_batchbuffer_t *batch = data;
L
Linus Torvalds 已提交
586 587 588 589
	int ret;

	if (!dev_priv->allow_batchbuffer) {
		DRM_ERROR("Batchbuffer ioctl disabled\n");
E
Eric Anholt 已提交
590
		return -EINVAL;
L
Linus Torvalds 已提交
591 592 593
	}

	DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n",
594
		  batch->start, batch->used, batch->num_cliprects);
L
Linus Torvalds 已提交
595

596
	LOCK_TEST_WITH_RETURN(dev, file_priv);
L
Linus Torvalds 已提交
597

598 599
	if (batch->num_cliprects && DRM_VERIFYAREA_READ(batch->cliprects,
						       batch->num_cliprects *
600
						       sizeof(struct drm_clip_rect)))
E
Eric Anholt 已提交
601
		return -EFAULT;
L
Linus Torvalds 已提交
602

603
	ret = i915_dispatch_batchbuffer(dev, batch);
L
Linus Torvalds 已提交
604 605 606 607 608

	sarea_priv->last_dispatch = (int)hw_status[5];
	return ret;
}

609 610
static int i915_cmdbuffer(struct drm_device *dev, void *data,
			  struct drm_file *file_priv)
L
Linus Torvalds 已提交
611 612 613 614 615
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
	u32 *hw_status = dev_priv->hw_status_page;
	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
	    dev_priv->sarea_priv;
616
	drm_i915_cmdbuffer_t *cmdbuf = data;
L
Linus Torvalds 已提交
617 618 619
	int ret;

	DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
620
		  cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects);
L
Linus Torvalds 已提交
621

622
	LOCK_TEST_WITH_RETURN(dev, file_priv);
L
Linus Torvalds 已提交
623

624 625 626
	if (cmdbuf->num_cliprects &&
	    DRM_VERIFYAREA_READ(cmdbuf->cliprects,
				cmdbuf->num_cliprects *
627
				sizeof(struct drm_clip_rect))) {
L
Linus Torvalds 已提交
628
		DRM_ERROR("Fault accessing cliprects\n");
E
Eric Anholt 已提交
629
		return -EFAULT;
L
Linus Torvalds 已提交
630 631
	}

632
	ret = i915_dispatch_cmdbuffer(dev, cmdbuf);
L
Linus Torvalds 已提交
633 634 635 636 637 638 639 640 641
	if (ret) {
		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
		return ret;
	}

	sarea_priv->last_dispatch = (int)hw_status[5];
	return 0;
}

642 643
static int i915_flip_bufs(struct drm_device *dev, void *data,
			  struct drm_file *file_priv)
L
Linus Torvalds 已提交
644 645 646
{
	DRM_DEBUG("%s\n", __FUNCTION__);

647
	LOCK_TEST_WITH_RETURN(dev, file_priv);
L
Linus Torvalds 已提交
648 649 650 651

	return i915_dispatch_flip(dev);
}

652 653
static int i915_getparam(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
L
Linus Torvalds 已提交
654 655
{
	drm_i915_private_t *dev_priv = dev->dev_private;
656
	drm_i915_getparam_t *param = data;
L
Linus Torvalds 已提交
657 658 659
	int value;

	if (!dev_priv) {
660
		DRM_ERROR("called with no initialization\n");
E
Eric Anholt 已提交
661
		return -EINVAL;
L
Linus Torvalds 已提交
662 663
	}

664
	switch (param->param) {
L
Linus Torvalds 已提交
665 666 667 668 669 670
	case I915_PARAM_IRQ_ACTIVE:
		value = dev->irq ? 1 : 0;
		break;
	case I915_PARAM_ALLOW_BATCHBUFFER:
		value = dev_priv->allow_batchbuffer ? 1 : 0;
		break;
D
Dave Airlie 已提交
671 672 673
	case I915_PARAM_LAST_DISPATCH:
		value = READ_BREADCRUMB(dev_priv);
		break;
L
Linus Torvalds 已提交
674
	default:
675
		DRM_ERROR("Unknown parameter %d\n", param->param);
E
Eric Anholt 已提交
676
		return -EINVAL;
L
Linus Torvalds 已提交
677 678
	}

679
	if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) {
L
Linus Torvalds 已提交
680
		DRM_ERROR("DRM_COPY_TO_USER failed\n");
E
Eric Anholt 已提交
681
		return -EFAULT;
L
Linus Torvalds 已提交
682 683 684 685 686
	}

	return 0;
}

687 688
static int i915_setparam(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
L
Linus Torvalds 已提交
689 690
{
	drm_i915_private_t *dev_priv = dev->dev_private;
691
	drm_i915_setparam_t *param = data;
L
Linus Torvalds 已提交
692 693

	if (!dev_priv) {
694
		DRM_ERROR("called with no initialization\n");
E
Eric Anholt 已提交
695
		return -EINVAL;
L
Linus Torvalds 已提交
696 697
	}

698
	switch (param->param) {
L
Linus Torvalds 已提交
699
	case I915_SETPARAM_USE_MI_BATCHBUFFER_START:
700
		if (!IS_I965G(dev))
701
			dev_priv->use_mi_batchbuffer_start = param->value;
L
Linus Torvalds 已提交
702 703
		break;
	case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY:
704
		dev_priv->tex_lru_log_granularity = param->value;
L
Linus Torvalds 已提交
705 706
		break;
	case I915_SETPARAM_ALLOW_BATCHBUFFER:
707
		dev_priv->allow_batchbuffer = param->value;
L
Linus Torvalds 已提交
708 709
		break;
	default:
710
		DRM_ERROR("unknown parameter %d\n", param->param);
E
Eric Anholt 已提交
711
		return -EINVAL;
L
Linus Torvalds 已提交
712 713 714 715 716
	}

	return 0;
}

717 718
static int i915_set_status_page(struct drm_device *dev, void *data,
				struct drm_file *file_priv)
719 720
{
	drm_i915_private_t *dev_priv = dev->dev_private;
721
	drm_i915_hws_addr_t *hws = data;
722 723 724

	if (!I915_NEED_GFX_HWS(dev))
		return -EINVAL;
725 726

	if (!dev_priv) {
727
		DRM_ERROR("called with no initialization\n");
E
Eric Anholt 已提交
728
		return -EINVAL;
729 730
	}

731 732 733
	printk(KERN_DEBUG "set status page addr 0x%08x\n", (u32)hws->addr);

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

735
	dev_priv->hws_map.offset = dev->agp->base + hws->addr;
736 737 738 739 740 741 742 743 744 745 746
	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");
E
Eric Anholt 已提交
747
		return -ENOMEM;
748 749 750 751 752 753 754 755 756 757 758
	}
	dev_priv->hw_status_page = dev_priv->hws_map.handle;

	memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
	I915_WRITE(0x02080, dev_priv->status_gfx_addr);
	DRM_DEBUG("load hws 0x2080 with gfx mem 0x%x\n",
			dev_priv->status_gfx_addr);
	DRM_DEBUG("load hws at %p\n", dev_priv->hw_status_page);
	return 0;
}

759
int i915_driver_load(struct drm_device *dev, unsigned long flags)
760
{
J
Jesse Barnes 已提交
761 762 763 764
	struct drm_i915_private *dev_priv = dev->dev_private;
	unsigned long base, size;
	int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1;

765 766 767 768 769 770 771
	/* 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;

J
Jesse Barnes 已提交
772 773 774 775 776 777 778 779 780 781 782 783
	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);

784 785
	ret = drm_addmap(dev, base, size, _DRM_REGISTERS,
			 _DRM_KERNEL | _DRM_DRIVER,
J
Jesse Barnes 已提交
786 787 788 789 790 791 792 793 794 795 796 797 798 799
			 &dev_priv->mmio_map);
	return ret;
}

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

	if (dev_priv->mmio_map)
		drm_rmmap(dev, dev_priv->mmio_map);

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

800 801 802
	return 0;
}

803
void i915_driver_lastclose(struct drm_device * dev)
L
Linus Torvalds 已提交
804
{
J
Jesse Barnes 已提交
805 806
	drm_i915_private_t *dev_priv = dev->dev_private;

D
Dave Airlie 已提交
807 808 809
	if (!dev_priv)
		return;

J
Jesse Barnes 已提交
810
	if (dev_priv->agp_heap)
D
Dave Airlie 已提交
811
		i915_mem_takedown(&(dev_priv->agp_heap));
J
Jesse Barnes 已提交
812

D
Dave Airlie 已提交
813
	i915_dma_cleanup(dev);
L
Linus Torvalds 已提交
814 815
}

816
void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
L
Linus Torvalds 已提交
817
{
J
Jesse Barnes 已提交
818 819
	drm_i915_private_t *dev_priv = dev->dev_private;
	i915_mem_release(dev, file_priv, dev_priv->agp_heap);
L
Linus Torvalds 已提交
820 821
}

822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
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),
D
Dave Airlie 已提交
840 841 842
};

int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
843 844 845 846 847 848 849 850 851 852 853 854

/**
 * 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.
 */
855
int i915_driver_device_is_agp(struct drm_device * dev)
856 857 858
{
	return 1;
}