i915_irq.c revision 7053:56f8bd9583f3
1/* BEGIN CSTYLED */
2
3/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
4 */
5/*
6 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
7 * All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the
11 * "Software"), to deal in the Software without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sub license, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
16 *
17 * The above copyright notice and this permission notice (including the
18 * next paragraph) shall be included in all copies or substantial portions
19 * of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
24 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
25 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 *
29 */
30
31/*
32 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
33 * Use is subject to license terms.
34 */
35
36#pragma ident	"%Z%%M%	%I%	%E% SMI"
37
38#include "drmP.h"
39#include "drm.h"
40#include "i915_drm.h"
41#include "i915_drv.h"
42
43#define USER_INT_FLAG (1<<1)
44#define VSYNC_PIPEB_FLAG (1<<5)
45#define VSYNC_PIPEA_FLAG (1<<7)
46
47#define MAX_NOPID ((u32)~0)
48
49/**
50 * Emit blits for scheduled buffer swaps.
51 *
52 * This function will be called with the HW lock held.
53 */
54static void i915_vblank_tasklet(drm_device_t *dev)
55{
56	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
57	/* LINTED E_FUNC_VAR_UNUSED */
58	unsigned long irqflags;
59	struct list_head *list, *tmp, hits, *hit;
60	int nhits, slice[2], upper[2], lower[2], i;
61	unsigned counter[2] = { atomic_read(&dev->vbl_received),
62				atomic_read(&dev->vbl_received2) };
63	drm_drawable_info_t *drw;
64	drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
65	u32 cpp = dev_priv->cpp;
66	u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |
67				XY_SRC_COPY_BLT_WRITE_ALPHA |
68				XY_SRC_COPY_BLT_WRITE_RGB)
69			     : XY_SRC_COPY_BLT_CMD;
70	u32 pitchropcpp = (sarea_priv->pitch * cpp) | (0xcc << 16) |
71			  (cpp << 23) | (1 << 24);
72	RING_LOCALS;
73
74	DRM_DEBUG("%s\n", __FUNCTION__);
75
76	INIT_LIST_HEAD(&hits);
77
78	nhits = 0;
79	spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
80
81	/* Find buffer swaps scheduled for this vertical blank */
82	list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {
83		drm_i915_vbl_swap_t *vbl_swap =
84			list_entry(list, drm_i915_vbl_swap_t, head);
85
86		if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23))
87			continue;
88
89		list_del(list);
90		dev_priv->swaps_pending--;
91
92		spin_unlock(&dev_priv->swaps_lock);
93		spin_lock(&dev->drw_lock);
94
95		drw = drm_get_drawable_info(dev, vbl_swap->drw_id);
96
97		if (!drw) {
98			spin_unlock(&dev->drw_lock);
99			drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
100			spin_lock(&dev_priv->swaps_lock);
101			continue;
102		}
103
104		list_for_each(hit, &hits) {
105			drm_i915_vbl_swap_t *swap_cmp =
106				list_entry(hit, drm_i915_vbl_swap_t, head);
107			drm_drawable_info_t *drw_cmp =
108				drm_get_drawable_info(dev, swap_cmp->drw_id);
109
110			if (drw_cmp &&
111			    drw_cmp->rects[0].y1 > drw->rects[0].y1) {
112				list_add_tail(list, hit);
113				break;
114			}
115		}
116
117		spin_unlock(&dev->drw_lock);
118
119		/* List of hits was empty, or we reached the end of it */
120		if (hit == &hits)
121			list_add_tail(list, hits.prev);
122
123		nhits++;
124
125		spin_lock(&dev_priv->swaps_lock);
126	}
127
128	if (nhits == 0) {
129		spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
130		return;
131	}
132
133	spin_unlock(&dev_priv->swaps_lock);
134
135	i915_kernel_lost_context(dev);
136
137	BEGIN_LP_RING(6);
138
139	OUT_RING(GFX_OP_DRAWRECT_INFO);
140	OUT_RING(0);
141	OUT_RING(0);
142	OUT_RING(sarea_priv->width | sarea_priv->height << 16);
143	OUT_RING(sarea_priv->width | sarea_priv->height << 16);
144	OUT_RING(0);
145
146	ADVANCE_LP_RING();
147
148	sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;
149
150	upper[0] = upper[1] = 0;
151	slice[0] = max(sarea_priv->pipeA_h / nhits, 1);
152	slice[1] = max(sarea_priv->pipeB_h / nhits, 1);
153	lower[0] = sarea_priv->pipeA_y + slice[0];
154	lower[1] = sarea_priv->pipeB_y + slice[0];
155
156	spin_lock(&dev->drw_lock);
157
158	/* Emit blits for buffer swaps, partitioning both outputs into as many
159	 * slices as there are buffer swaps scheduled in order to avoid tearing
160	 * (based on the assumption that a single buffer swap would always
161	 * complete before scanout starts).
162	 */
163	for (i = 0; i++ < nhits;
164	     upper[0] = lower[0], lower[0] += slice[0],
165	     upper[1] = lower[1], lower[1] += slice[1]) {
166		if (i == nhits)
167			lower[0] = lower[1] = sarea_priv->height;
168
169		list_for_each(hit, &hits) {
170			drm_i915_vbl_swap_t *swap_hit =
171				list_entry(hit, drm_i915_vbl_swap_t, head);
172			drm_clip_rect_t *rect;
173			int num_rects, pipe;
174			unsigned short top, bottom;
175
176			drw = drm_get_drawable_info(dev, swap_hit->drw_id);
177
178			if (!drw)
179				continue;
180
181			rect = drw->rects;
182			pipe = swap_hit->pipe;
183			top = upper[pipe];
184			bottom = lower[pipe];
185
186			for (num_rects = drw->num_rects; num_rects--; rect++) {
187				int y1 = max(rect->y1, top);
188				int y2 = min(rect->y2, bottom);
189
190				if (y1 >= y2)
191					continue;
192
193				BEGIN_LP_RING(8);
194
195				OUT_RING(cmd);
196				OUT_RING(pitchropcpp);
197				OUT_RING((y1 << 16) | rect->x1);
198				OUT_RING((y2 << 16) | rect->x2);
199				OUT_RING(sarea_priv->front_offset);
200				OUT_RING((y1 << 16) | rect->x1);
201				OUT_RING(pitchropcpp & 0xffff);
202				OUT_RING(sarea_priv->back_offset);
203
204				ADVANCE_LP_RING();
205			}
206		}
207	}
208
209	spin_unlock_irqrestore(&dev->drw_lock, irqflags);
210
211	list_for_each_safe(hit, tmp, &hits) {
212		drm_i915_vbl_swap_t *swap_hit =
213			list_entry(hit, drm_i915_vbl_swap_t, head);
214
215		list_del(hit);
216
217		drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER);
218	}
219}
220
221irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
222{
223	drm_device_t *dev = (drm_device_t *) (void *)arg;
224	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
225	u16 temp;
226	u32 pipea_stats, pipeb_stats;
227
228	pipea_stats = I915_READ(I915REG_PIPEASTAT);
229	pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
230
231	temp = I915_READ16(I915REG_INT_IDENTITY_R);
232	temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG);
233
234#if 0
235	DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp);
236#endif
237        if (temp == 0)
238                return IRQ_NONE;
239
240	I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
241
242	dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
243
244	if (temp & USER_INT_FLAG) {
245		DRM_WAKEUP(&dev_priv->irq_queue);
246#ifdef I915_HAVE_FENCE
247		i915_fence_handler(dev);
248#endif
249	}
250
251	if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) {
252		int vblank_pipe = dev_priv->vblank_pipe;
253
254		if ((vblank_pipe &
255		     (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
256		    == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
257			if (temp & VSYNC_PIPEA_FLAG)
258				atomic_inc(&dev->vbl_received);
259			if (temp & VSYNC_PIPEB_FLAG)
260				atomic_inc(&dev->vbl_received2);
261		} else if (((temp & VSYNC_PIPEA_FLAG) &&
262			    (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
263			   ((temp & VSYNC_PIPEB_FLAG) &&
264			    (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
265			atomic_inc(&dev->vbl_received);
266
267		DRM_WAKEUP(&dev->vbl_queue);
268		drm_vbl_send_signals(dev);
269
270		if (dev_priv->swaps_pending > 0)
271			drm_locked_tasklet(dev, i915_vblank_tasklet);
272		I915_WRITE(I915REG_PIPEASTAT,
273			pipea_stats|I915_VBLANK_INTERRUPT_ENABLE|
274			I915_VBLANK_CLEAR);
275		I915_WRITE(I915REG_PIPEBSTAT,
276			pipeb_stats|I915_VBLANK_INTERRUPT_ENABLE|
277			I915_VBLANK_CLEAR);
278	}
279
280        return IRQ_HANDLED;
281}
282
283int i915_emit_irq(drm_device_t * dev)
284{
285
286	drm_i915_private_t *dev_priv = dev->dev_private;
287	RING_LOCALS;
288
289	i915_kernel_lost_context(dev);
290
291	DRM_DEBUG("%s\n", __FUNCTION__);
292
293	dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;
294
295	if (dev_priv->counter > 0x7FFFFFFFUL)
296		 dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;
297
298	BEGIN_LP_RING(6);
299	OUT_RING(CMD_STORE_DWORD_IDX);
300	OUT_RING(20);
301	OUT_RING(dev_priv->counter);
302
303	OUT_RING(0);
304	OUT_RING(0);
305	OUT_RING(GFX_OP_USER_INTERRUPT);
306	ADVANCE_LP_RING();
307
308	return dev_priv->counter;
309
310
311}
312
313void i915_user_irq_on(drm_i915_private_t *dev_priv)
314{
315	spin_lock(&dev_priv->user_irq_lock);
316	if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){
317		dev_priv->irq_enable_reg |= USER_INT_FLAG;
318		I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
319	}
320	spin_unlock(&dev_priv->user_irq_lock);
321
322}
323
324void i915_user_irq_off(drm_i915_private_t *dev_priv)
325{
326	spin_lock(&dev_priv->user_irq_lock);
327	if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
328		/*EMPTY*/;
329		//		dev_priv->irq_enable_reg &= ~USER_INT_FLAG;
330		//		I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
331	}
332	spin_unlock(&dev_priv->user_irq_lock);
333}
334
335
336static int i915_wait_irq(drm_device_t * dev, int irq_nr)
337{
338	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
339	int ret = 0;
340
341	DRM_DEBUG("%s irq_nr=%d breadcrumb=%d\n", __FUNCTION__, irq_nr,
342		  READ_BREADCRUMB(dev_priv));
343
344	if (READ_BREADCRUMB(dev_priv) >= irq_nr)
345		return 0;
346
347	dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
348
349	i915_user_irq_on(dev_priv);
350	DRM_WAIT_ON(ret, &dev_priv->irq_queue, 3 * DRM_HZ,
351		    READ_BREADCRUMB(dev_priv) >= irq_nr);
352	i915_user_irq_off(dev_priv);
353
354	if (ret == EBUSY) {
355		DRM_ERROR("%s: EBUSY -- rec: %d emitted: %d\n",
356			  __FUNCTION__,
357			  READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
358	}
359
360	dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
361	return ret;
362}
363
364static int i915_driver_vblank_do_wait(drm_device_t *dev, unsigned int *sequence,
365				      atomic_t *counter)
366{
367	drm_i915_private_t *dev_priv = dev->dev_private;
368	unsigned int cur_vblank;
369	int ret = 0;
370
371	if (!dev_priv) {
372		DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
373		return (EINVAL);
374	}
375
376	DRM_WAIT_ON(ret, &dev->vbl_queue, 3 * DRM_HZ,
377		    (((cur_vblank = atomic_read(counter))
378			- *sequence) <= (1<<23)));
379
380	*sequence = cur_vblank;
381
382	return ret;
383}
384
385int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence)
386{
387	return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);
388}
389
390int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence)
391{
392	return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2);
393}
394
395/* Needs the lock as it touches the ring.
396 */
397/*ARGSUSED*/
398int i915_irq_emit(DRM_IOCTL_ARGS)
399{
400	DRM_DEVICE;
401	drm_i915_private_t *dev_priv = dev->dev_private;
402	drm_i915_irq_emit_t emit;
403	int result;
404
405	LOCK_TEST_WITH_RETURN(dev, fpriv);
406
407	if (!dev_priv) {
408		DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
409		return (EINVAL);
410	}
411
412	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
413		drm_i915_irq_emit32_t irq_emit32;
414
415		DRM_COPYFROM_WITH_RETURN(&irq_emit32,
416			(drm_i915_irq_emit32_t __user *) data,
417			sizeof (drm_i915_irq_emit32_t));
418		emit.irq_seq = (int __user *)(uintptr_t)irq_emit32.irq_seq;
419	} else
420		DRM_COPYFROM_WITH_RETURN(&emit,
421		    (drm_i915_irq_emit_t __user *) data, sizeof(emit));
422
423	result = i915_emit_irq(dev);
424
425	if (DRM_COPY_TO_USER(emit.irq_seq, &result, sizeof(int))) {
426		DRM_ERROR("copy_to_user\n");
427		return (EFAULT);
428	}
429
430	return 0;
431}
432
433/* Doesn't need the hardware lock.
434 */
435/*ARGSUSED*/
436int i915_irq_wait(DRM_IOCTL_ARGS)
437{
438	DRM_DEVICE;
439	drm_i915_private_t *dev_priv = dev->dev_private;
440	drm_i915_irq_wait_t irqwait;
441
442	if (!dev_priv) {
443		DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
444		return (EINVAL);
445	}
446
447	DRM_COPYFROM_WITH_RETURN(&irqwait,
448	    (drm_i915_irq_wait_t __user *) data, sizeof(irqwait));
449
450	return i915_wait_irq(dev, irqwait.irq_seq);
451}
452
453static void i915_enable_interrupt (drm_device_t *dev)
454{
455	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
456
457	dev_priv->irq_enable_reg = USER_INT_FLAG;
458	if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
459		dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG;
460	if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
461		dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG;
462
463	I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
464	dev_priv->irq_enabled = 1;
465}
466
467/* drm_dma.h hooks
468*/
469void i915_driver_irq_preinstall(drm_device_t * dev)
470{
471	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
472
473	I915_WRITE16(I915REG_HWSTAM, 0xeffe);
474	I915_WRITE16(I915REG_INT_MASK_R, 0x0);
475	I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);
476}
477
478void i915_driver_irq_postinstall(drm_device_t * dev)
479{
480	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
481
482	mutex_init(&dev_priv->swaps_lock, NULL, MUTEX_DRIVER, NULL);
483	INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
484	dev_priv->swaps_pending = 0;
485
486	if (!dev_priv->vblank_pipe)
487		dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
488
489	mutex_init(&dev_priv->user_irq_lock, NULL, MUTEX_DRIVER, NULL);
490	dev_priv->user_irq_refcount = 0;
491
492	if (!dev_priv->vblank_pipe)
493		dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
494
495	DRM_INIT_WAITQUEUE(&dev_priv->irq_queue, DRM_INTR_PRI(dev));
496
497	i915_enable_interrupt(dev);
498
499
500	/*
501	 * Initialize the hardware status page IRQ location.
502	 */
503
504	I915_WRITE(I915REG_INSTPM, (1 << 5) | (1 << 21));
505}
506
507void i915_driver_irq_uninstall(drm_device_t * dev)
508{
509	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
510	u16 temp;
511	if (!dev_priv)
512		return;
513
514	dev_priv->irq_enabled = 0;
515	I915_WRITE16(I915REG_HWSTAM, 0xffff);
516	I915_WRITE16(I915REG_INT_MASK_R, 0xffff);
517	I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);
518
519	temp = I915_READ16(I915REG_INT_IDENTITY_R);
520	I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
521
522	DRM_FINI_WAITQUEUE(&dev_priv->irq_queue);
523	mutex_destroy(&dev_priv->swaps_lock);
524	mutex_destroy(&dev_priv->user_irq_lock);
525
526}
527