i915_irq.c 17.7 KB
Newer Older
D
Dave Airlie 已提交
1
/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
L
Linus Torvalds 已提交
2
 */
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

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

#define MAX_NOPID ((u32)~0)

36 37 38
/** These are the interrupts used by the driver */
#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT |		\
				    I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \
39 40 41
				    I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT | \
				    I915_ASLE_INTERRUPT |		\
				    I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
42

43
void
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
{
	if ((dev_priv->irq_mask_reg & mask) != 0) {
		dev_priv->irq_mask_reg &= ~mask;
		I915_WRITE(IMR, dev_priv->irq_mask_reg);
		(void) I915_READ(IMR);
	}
}

static inline void
i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
{
	if ((dev_priv->irq_mask_reg & mask) != mask) {
		dev_priv->irq_mask_reg |= mask;
		I915_WRITE(IMR, dev_priv->irq_mask_reg);
		(void) I915_READ(IMR);
	}
}

63 64 65 66 67
/**
 * Emit blits for scheduled buffer swaps.
 *
 * This function will be called with the HW lock held.
 */
68
static void i915_vblank_tasklet(struct drm_device *dev)
69 70
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
71
	unsigned long irqflags;
72
	struct list_head *list, *tmp, hits, *hit;
73 74 75
	int nhits, nrects, slice[2], upper[2], lower[2], i;
	unsigned counter[2] = { atomic_read(&dev->vbl_received),
				atomic_read(&dev->vbl_received2) };
76
	struct drm_drawable_info *drw;
77
	drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
78
	u32 cpp = dev_priv->cpp;
79 80 81 82
	u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |
				XY_SRC_COPY_BLT_WRITE_ALPHA |
				XY_SRC_COPY_BLT_WRITE_RGB)
			     : XY_SRC_COPY_BLT_CMD;
83 84 85
	u32 src_pitch = sarea_priv->pitch * cpp;
	u32 dst_pitch = sarea_priv->pitch * cpp;
	u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24);
86
	RING_LOCALS;
87

88
	if (IS_I965G(dev) && sarea_priv->front_tiled) {
89 90 91
		cmd |= XY_SRC_COPY_BLT_DST_TILED;
		dst_pitch >>= 2;
	}
92
	if (IS_I965G(dev) && sarea_priv->back_tiled) {
93 94 95 96
		cmd |= XY_SRC_COPY_BLT_SRC_TILED;
		src_pitch >>= 2;
	}

97 98
	DRM_DEBUG("\n");

99 100 101 102
	INIT_LIST_HEAD(&hits);

	nhits = nrects = 0;

103
	spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
104

105
	/* Find buffer swaps scheduled for this vertical blank */
106 107 108 109
	list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {
		drm_i915_vbl_swap_t *vbl_swap =
			list_entry(list, drm_i915_vbl_swap_t, head);

110
		if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23))
111 112 113 114 115 116 117
			continue;

		list_del(list);
		dev_priv->swaps_pending--;

		spin_unlock(&dev_priv->swaps_lock);
		spin_lock(&dev->drw_lock);
118

119
		drw = drm_get_drawable_info(dev, vbl_swap->drw_id);
120

121 122 123 124 125 126
		if (!drw) {
			spin_unlock(&dev->drw_lock);
			drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
			spin_lock(&dev_priv->swaps_lock);
			continue;
		}
127

128 129 130
		list_for_each(hit, &hits) {
			drm_i915_vbl_swap_t *swap_cmp =
				list_entry(hit, drm_i915_vbl_swap_t, head);
131
			struct drm_drawable_info *drw_cmp =
132
				drm_get_drawable_info(dev, swap_cmp->drw_id);
133

134 135 136 137
			if (drw_cmp &&
			    drw_cmp->rects[0].y1 > drw->rects[0].y1) {
				list_add_tail(list, hit);
				break;
138
			}
139
		}
140

141
		spin_unlock(&dev->drw_lock);
142

143 144 145
		/* List of hits was empty, or we reached the end of it */
		if (hit == &hits)
			list_add_tail(list, hits.prev);
146

147
		nhits++;
148

149 150 151
		spin_lock(&dev_priv->swaps_lock);
	}

152 153
	if (nhits == 0) {
		spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
154
		return;
155 156 157
	}

	spin_unlock(&dev_priv->swaps_lock);
158

159
	i915_kernel_lost_context(dev);
160

161 162 163 164 165 166 167 168 169 170
	if (IS_I965G(dev)) {
		BEGIN_LP_RING(4);

		OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
		OUT_RING(0);
		OUT_RING(((sarea_priv->width - 1) & 0xffff) | ((sarea_priv->height - 1) << 16));
		OUT_RING(0);
		ADVANCE_LP_RING();
	} else {
		BEGIN_LP_RING(6);
171

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
		OUT_RING(GFX_OP_DRAWRECT_INFO);
		OUT_RING(0);
		OUT_RING(0);
		OUT_RING(sarea_priv->width | sarea_priv->height << 16);
		OUT_RING(sarea_priv->width | sarea_priv->height << 16);
		OUT_RING(0);

		ADVANCE_LP_RING();
	}

	sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;

	upper[0] = upper[1] = 0;
	slice[0] = max(sarea_priv->pipeA_h / nhits, 1);
	slice[1] = max(sarea_priv->pipeB_h / nhits, 1);
	lower[0] = sarea_priv->pipeA_y + slice[0];
	lower[1] = sarea_priv->pipeB_y + slice[0];
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

	spin_lock(&dev->drw_lock);

	/* Emit blits for buffer swaps, partitioning both outputs into as many
	 * slices as there are buffer swaps scheduled in order to avoid tearing
	 * (based on the assumption that a single buffer swap would always
	 * complete before scanout starts).
	 */
	for (i = 0; i++ < nhits;
	     upper[0] = lower[0], lower[0] += slice[0],
	     upper[1] = lower[1], lower[1] += slice[1]) {
		if (i == nhits)
			lower[0] = lower[1] = sarea_priv->height;

		list_for_each(hit, &hits) {
			drm_i915_vbl_swap_t *swap_hit =
				list_entry(hit, drm_i915_vbl_swap_t, head);
206
			struct drm_clip_rect *rect;
207
			int num_rects, pipe;
208 209 210 211 212 213 214 215
			unsigned short top, bottom;

			drw = drm_get_drawable_info(dev, swap_hit->drw_id);

			if (!drw)
				continue;

			rect = drw->rects;
216 217 218
			pipe = swap_hit->pipe;
			top = upper[pipe];
			bottom = lower[pipe];
219 220 221 222 223 224 225 226 227 228 229

			for (num_rects = drw->num_rects; num_rects--; rect++) {
				int y1 = max(rect->y1, top);
				int y2 = min(rect->y2, bottom);

				if (y1 >= y2)
					continue;

				BEGIN_LP_RING(8);

				OUT_RING(cmd);
230
				OUT_RING(ropcpp | dst_pitch);
231 232
				OUT_RING((y1 << 16) | rect->x1);
				OUT_RING((y2 << 16) | rect->x2);
233
				OUT_RING(sarea_priv->front_offset);
234
				OUT_RING((y1 << 16) | rect->x1);
235
				OUT_RING(src_pitch);
236
				OUT_RING(sarea_priv->back_offset);
237 238 239

				ADVANCE_LP_RING();
			}
240 241 242
		}
	}

243
	spin_unlock_irqrestore(&dev->drw_lock, irqflags);
244 245 246 247 248 249 250 251 252

	list_for_each_safe(hit, tmp, &hits) {
		drm_i915_vbl_swap_t *swap_hit =
			list_entry(hit, drm_i915_vbl_swap_t, head);

		list_del(hit);

		drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER);
	}
253 254
}

L
Linus Torvalds 已提交
255 256
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
257
	struct drm_device *dev = (struct drm_device *) arg;
L
Linus Torvalds 已提交
258
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
259
	u32 pipea_stats, pipeb_stats;
260
	u32 iir;
L
Linus Torvalds 已提交
261

262 263
	pipea_stats = I915_READ(PIPEASTAT);
	pipeb_stats = I915_READ(PIPEBSTAT);
264

265 266 267
	if (dev->pdev->msi_enabled)
		I915_WRITE(IMR, ~0);
	iir = I915_READ(IIR);
268

269
	DRM_DEBUG("iir=%08x\n", iir);
270

271 272 273 274 275
	if (iir == 0) {
		if (dev->pdev->msi_enabled) {
			I915_WRITE(IMR, dev_priv->irq_mask_reg);
			(void) I915_READ(IMR);
		}
276
		return IRQ_NONE;
277
	}
278

279 280 281
	I915_WRITE(PIPEASTAT, pipea_stats);
	I915_WRITE(PIPEBSTAT, pipeb_stats);

282 283 284 285
	I915_WRITE(IIR, iir);
	if (dev->pdev->msi_enabled)
		I915_WRITE(IMR, dev_priv->irq_mask_reg);
	(void) I915_READ(IIR); /* Flush posted writes */
286 287 288

	dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);

289
	if (iir & I915_USER_INTERRUPT)
290
		DRM_WAKEUP(&dev_priv->irq_queue);
291

292 293
	if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
		   I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
294 295 296 297 298
		int vblank_pipe = dev_priv->vblank_pipe;

		if ((vblank_pipe &
		     (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
		    == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
299
			if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
300
				atomic_inc(&dev->vbl_received);
301
			if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
302
				atomic_inc(&dev->vbl_received2);
303
		} else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
304
			    (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
305
			   ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
306 307 308 309 310 311
			    (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
			atomic_inc(&dev->vbl_received);

		DRM_WAKEUP(&dev->vbl_queue);
		drm_vbl_send_signals(dev);

=
=?utf-8?q?Michel_D=C3=A4nzer?= 已提交
312 313
		if (dev_priv->swaps_pending > 0)
			drm_locked_tasklet(dev, i915_vblank_tasklet);
D
Dave Airlie 已提交
314
	}
L
Linus Torvalds 已提交
315

316 317 318 319 320 321
	if (iir & I915_ASLE_INTERRUPT)
		opregion_asle_intr(dev);

	if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
		opregion_asle_intr(dev);

L
Linus Torvalds 已提交
322 323 324
	return IRQ_HANDLED;
}

325
static int i915_emit_irq(struct drm_device * dev)
L
Linus Torvalds 已提交
326 327 328 329 330 331
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	RING_LOCALS;

	i915_kernel_lost_context(dev);

332
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
333

334
	dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;
L
Linus Torvalds 已提交
335

336 337 338 339
	if (dev_priv->counter > 0x7FFFFFFFUL)
		dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;

	BEGIN_LP_RING(6);
340 341
	OUT_RING(MI_STORE_DWORD_INDEX);
	OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT);
342 343
	OUT_RING(dev_priv->counter);
	OUT_RING(0);
L
Linus Torvalds 已提交
344
	OUT_RING(0);
345
	OUT_RING(MI_USER_INTERRUPT);
L
Linus Torvalds 已提交
346
	ADVANCE_LP_RING();
D
Dave Airlie 已提交
347

348
	return dev_priv->counter;
L
Linus Torvalds 已提交
349 350
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
static void i915_user_irq_get(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;

	spin_lock(&dev_priv->user_irq_lock);
	if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
		i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
	spin_unlock(&dev_priv->user_irq_lock);
}

static void i915_user_irq_put(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;

	spin_lock(&dev_priv->user_irq_lock);
	BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
	if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
		i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
	spin_unlock(&dev_priv->user_irq_lock);
}

372
static int i915_wait_irq(struct drm_device * dev, int irq_nr)
L
Linus Torvalds 已提交
373 374 375 376
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
	int ret = 0;

377
	DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
L
Linus Torvalds 已提交
378 379
		  READ_BREADCRUMB(dev_priv));

380 381
	if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
		dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
L
Linus Torvalds 已提交
382
		return 0;
383
	}
L
Linus Torvalds 已提交
384 385 386

	dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;

387
	i915_user_irq_get(dev);
L
Linus Torvalds 已提交
388 389
	DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
		    READ_BREADCRUMB(dev_priv) >= irq_nr);
390
	i915_user_irq_put(dev);
L
Linus Torvalds 已提交
391

E
Eric Anholt 已提交
392
	if (ret == -EBUSY) {
393
		DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
L
Linus Torvalds 已提交
394 395 396
			  READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
	}

397
	dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
L
Linus Torvalds 已提交
398 399 400
	return ret;
}

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
static int i915_driver_vblank_do_wait(struct drm_device *dev, unsigned int *sequence,
				      atomic_t *counter)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	unsigned int cur_vblank;
	int ret = 0;

	if (!dev_priv) {
		DRM_ERROR("called with no initialization\n");
		return -EINVAL;
	}

	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
		    (((cur_vblank = atomic_read(counter))
			- *sequence) <= (1<<23)));

	*sequence = cur_vblank;

	return ret;
}


int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence)
{
	return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);
}

int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence)
{
	return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2);
}

L
Linus Torvalds 已提交
433 434
/* Needs the lock as it touches the ring.
 */
435 436
int i915_irq_emit(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
L
Linus Torvalds 已提交
437 438
{
	drm_i915_private_t *dev_priv = dev->dev_private;
439
	drm_i915_irq_emit_t *emit = data;
L
Linus Torvalds 已提交
440 441
	int result;

442
	LOCK_TEST_WITH_RETURN(dev, file_priv);
L
Linus Torvalds 已提交
443 444

	if (!dev_priv) {
445
		DRM_ERROR("called with no initialization\n");
E
Eric Anholt 已提交
446
		return -EINVAL;
L
Linus Torvalds 已提交
447 448 449 450
	}

	result = i915_emit_irq(dev);

451
	if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) {
L
Linus Torvalds 已提交
452
		DRM_ERROR("copy_to_user\n");
E
Eric Anholt 已提交
453
		return -EFAULT;
L
Linus Torvalds 已提交
454 455 456 457 458 459 460
	}

	return 0;
}

/* Doesn't need the hardware lock.
 */
461 462
int i915_irq_wait(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
L
Linus Torvalds 已提交
463 464
{
	drm_i915_private_t *dev_priv = dev->dev_private;
465
	drm_i915_irq_wait_t *irqwait = data;
L
Linus Torvalds 已提交
466 467

	if (!dev_priv) {
468
		DRM_ERROR("called with no initialization\n");
E
Eric Anholt 已提交
469
		return -EINVAL;
L
Linus Torvalds 已提交
470 471
	}

472
	return i915_wait_irq(dev, irqwait->irq_seq);
L
Linus Torvalds 已提交
473 474
}

475 476
/* Set the vblank monitor pipe
 */
477 478
int i915_vblank_pipe_set(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
479 480
{
	drm_i915_private_t *dev_priv = dev->dev_private;
481
	drm_i915_vblank_pipe_t *pipe = data;
482
	u32 enable_mask = 0, disable_mask = 0;
483 484

	if (!dev_priv) {
485
		DRM_ERROR("called with no initialization\n");
E
Eric Anholt 已提交
486
		return -EINVAL;
487 488
	}

489
	if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) {
490
		DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe);
E
Eric Anholt 已提交
491
		return -EINVAL;
492 493
	}

494 495 496 497 498 499 500 501 502
	if (pipe->pipe & DRM_I915_VBLANK_PIPE_A)
		enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
	else
		disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;

	if (pipe->pipe & DRM_I915_VBLANK_PIPE_B)
		enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
	else
		disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
503

504 505 506 507
	i915_enable_irq(dev_priv, enable_mask);
	i915_disable_irq(dev_priv, disable_mask);

	dev_priv->vblank_pipe = pipe->pipe;
508

509
	return 0;
510 511
}

512 513
int i915_vblank_pipe_get(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
514 515
{
	drm_i915_private_t *dev_priv = dev->dev_private;
516
	drm_i915_vblank_pipe_t *pipe = data;
517 518 519
	u16 flag;

	if (!dev_priv) {
520
		DRM_ERROR("called with no initialization\n");
E
Eric Anholt 已提交
521
		return -EINVAL;
522 523
	}

524
	flag = I915_READ(IMR);
525
	pipe->pipe = 0;
526
	if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
527
		pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
528
	if (flag & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
529 530
		pipe->pipe |= DRM_I915_VBLANK_PIPE_B;

531 532 533
	return 0;
}

534 535 536
/**
 * Schedule buffer swap at given vertical blank.
 */
537 538
int i915_vblank_swap(struct drm_device *dev, void *data,
		     struct drm_file *file_priv)
539 540
{
	drm_i915_private_t *dev_priv = dev->dev_private;
541
	drm_i915_vblank_swap_t *swap = data;
542
	drm_i915_vbl_swap_t *vbl_swap;
543
	unsigned int pipe, seqtype, curseq;
544
	unsigned long irqflags;
545 546 547 548
	struct list_head *list;

	if (!dev_priv) {
		DRM_ERROR("%s called with no initialization\n", __func__);
E
Eric Anholt 已提交
549
		return -EINVAL;
550 551
	}

552
	if (dev_priv->sarea_priv->rotation) {
553
		DRM_DEBUG("Rotation not supported\n");
E
Eric Anholt 已提交
554
		return -EINVAL;
555 556
	}

557
	if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE |
558
			     _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) {
559
		DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype);
E
Eric Anholt 已提交
560
		return -EINVAL;
561 562
	}

563
	pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0;
564

565
	seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE);
566 567 568

	if (!(dev_priv->vblank_pipe & (1 << pipe))) {
		DRM_ERROR("Invalid pipe %d\n", pipe);
E
Eric Anholt 已提交
569
		return -EINVAL;
570 571 572 573
	}

	spin_lock_irqsave(&dev->drw_lock, irqflags);

574
	if (!drm_get_drawable_info(dev, swap->drawable)) {
575
		spin_unlock_irqrestore(&dev->drw_lock, irqflags);
576
		DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable);
E
Eric Anholt 已提交
577
		return -EINVAL;
578 579 580 581
	}

	spin_unlock_irqrestore(&dev->drw_lock, irqflags);

582
	curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received);
583

=
=?utf-8?q?Michel_D=C3=A4nzer?= 已提交
584
	if (seqtype == _DRM_VBLANK_RELATIVE)
585
		swap->sequence += curseq;
=
=?utf-8?q?Michel_D=C3=A4nzer?= 已提交
586

587 588 589
	if ((curseq - swap->sequence) <= (1<<23)) {
		if (swap->seqtype & _DRM_VBLANK_NEXTONMISS) {
			swap->sequence = curseq + 1;
=
=?utf-8?q?Michel_D=C3=A4nzer?= 已提交
590
		} else {
591
			DRM_DEBUG("Missed target sequence\n");
E
Eric Anholt 已提交
592
			return -EINVAL;
593 594 595
		}
	}

=
=?utf-8?q?Michel_D=C3=A4nzer?= 已提交
596 597
	spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);

598 599 600
	list_for_each(list, &dev_priv->vbl_swaps.head) {
		vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head);

601
		if (vbl_swap->drw_id == swap->drawable &&
602
		    vbl_swap->pipe == pipe &&
603
		    vbl_swap->sequence == swap->sequence) {
604 605 606 607 608 609 610 611
			spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
			DRM_DEBUG("Already scheduled\n");
			return 0;
		}
	}

	spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);

612 613
	if (dev_priv->swaps_pending >= 100) {
		DRM_DEBUG("Too many swaps queued\n");
E
Eric Anholt 已提交
614
		return -EBUSY;
615 616
	}

D
Dave Airlie 已提交
617
	vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER);
618 619 620

	if (!vbl_swap) {
		DRM_ERROR("Failed to allocate memory to queue swap\n");
E
Eric Anholt 已提交
621
		return -ENOMEM;
622 623 624 625
	}

	DRM_DEBUG("\n");

626
	vbl_swap->drw_id = swap->drawable;
627
	vbl_swap->pipe = pipe;
628
	vbl_swap->sequence = swap->sequence;
629 630 631

	spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);

632
	list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head);
633 634 635 636 637 638 639
	dev_priv->swaps_pending++;

	spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);

	return 0;
}

L
Linus Torvalds 已提交
640 641
/* drm_dma.h hooks
*/
642
void i915_driver_irq_preinstall(struct drm_device * dev)
L
Linus Torvalds 已提交
643 644 645
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;

646 647 648
	I915_WRITE(HWSTAM, 0xfffe);
	I915_WRITE(IMR, 0x0);
	I915_WRITE(IER, 0x0);
L
Linus Torvalds 已提交
649 650
}

651
void i915_driver_irq_postinstall(struct drm_device * dev)
L
Linus Torvalds 已提交
652 653 654
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;

655
	spin_lock_init(&dev_priv->swaps_lock);
656 657 658
	INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
	dev_priv->swaps_pending = 0;

659 660
	if (!dev_priv->vblank_pipe)
		dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
661 662 663 664 665 666 667 668

	/* Set initial unmasked IRQs to just the selected vblank pipes. */
	dev_priv->irq_mask_reg = ~0;
	if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
		dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
	if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
		dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;

669 670
	dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK;

671 672 673 674
	I915_WRITE(IMR, dev_priv->irq_mask_reg);
	I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
	(void) I915_READ(IER);

675 676
	opregion_enable_asle(dev);

L
Linus Torvalds 已提交
677 678 679
	DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
}

680
void i915_driver_irq_uninstall(struct drm_device * dev)
L
Linus Torvalds 已提交
681 682
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
683
	u16 temp;
684

L
Linus Torvalds 已提交
685 686 687
	if (!dev_priv)
		return;

688 689 690
	I915_WRITE(HWSTAM, 0xffff);
	I915_WRITE(IMR, 0xffff);
	I915_WRITE(IER, 0x0);
691

692 693
	temp = I915_READ(IIR);
	I915_WRITE(IIR, temp);
L
Linus Torvalds 已提交
694
}