radeon_irq.c 10.4 KB
Newer Older
Dave Airlie's avatar
Dave Airlie committed
1
2
/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*- */
/*
Linus Torvalds's avatar
Linus Torvalds committed
3
 * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
Dave Airlie's avatar
Dave Airlie committed
4
 *
Linus Torvalds's avatar
Linus Torvalds committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 * The Weather Channel (TM) funded Tungsten Graphics to develop the
 * initial release of the Radeon 8500 driver under the XFree86 license.
 * This notice must be preserved.
 *
 * 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, sublicense,
 * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * PRECISION INSIGHT 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.
 *
 * Authors:
 *    Keith Whitwell <keith@tungstengraphics.com>
30
 *    Michel D�zer <michel@daenzer.net>
Linus Torvalds's avatar
Linus Torvalds committed
31
32
33
34
35
36
37
 */

#include "drmP.h"
#include "drm.h"
#include "radeon_drm.h"
#include "radeon_drv.h"

38
void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state)
Dave Airlie's avatar
Dave Airlie committed
39
{
40
41
42
43
44
45
46
	drm_radeon_private_t *dev_priv = dev->dev_private;

	if (state)
		dev_priv->irq_enable_reg |= mask;
	else
		dev_priv->irq_enable_reg &= ~mask;

47
	if (dev->irq_enabled)
48
		RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
49
50
51
52
53
54
55
56
57
58
59
}

static void r500_vbl_irq_set_state(struct drm_device *dev, u32 mask, int state)
{
	drm_radeon_private_t *dev_priv = dev->dev_private;

	if (state)
		dev_priv->r500_disp_irq_reg |= mask;
	else
		dev_priv->r500_disp_irq_reg &= ~mask;

60
	if (dev->irq_enabled)
61
		RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg);
62
63
64
65
66
67
}

int radeon_enable_vblank(struct drm_device *dev, int crtc)
{
	drm_radeon_private_t *dev_priv = dev->dev_private;

68
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) {
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
		switch (crtc) {
		case 0:
			r500_vbl_irq_set_state(dev, R500_D1MODE_INT_MASK, 1);
			break;
		case 1:
			r500_vbl_irq_set_state(dev, R500_D2MODE_INT_MASK, 1);
			break;
		default:
			DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
				  crtc);
			return EINVAL;
		}
	} else {
		switch (crtc) {
		case 0:
			radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 1);
			break;
		case 1:
			radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 1);
			break;
		default:
			DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
				  crtc);
			return EINVAL;
		}
	}

	return 0;
}

void radeon_disable_vblank(struct drm_device *dev, int crtc)
{
	drm_radeon_private_t *dev_priv = dev->dev_private;

103
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) {
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
		switch (crtc) {
		case 0:
			r500_vbl_irq_set_state(dev, R500_D1MODE_INT_MASK, 0);
			break;
		case 1:
			r500_vbl_irq_set_state(dev, R500_D2MODE_INT_MASK, 0);
			break;
		default:
			DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
				  crtc);
			break;
		}
	} else {
		switch (crtc) {
		case 0:
			radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 0);
			break;
		case 1:
			radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 0);
			break;
		default:
			DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
				  crtc);
			break;
		}
	}
}

static inline u32 radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv, u32 *r500_disp_int)
{
	u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS);
	u32 irq_mask = RADEON_SW_INT_TEST;

	*r500_disp_int = 0;
138
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) {
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
		/* vbl interrupts in a different place */

		if (irqs & R500_DISPLAY_INT_STATUS) {
			/* if a display interrupt */
			u32 disp_irq;

			disp_irq = RADEON_READ(R500_DISP_INTERRUPT_STATUS);

			*r500_disp_int = disp_irq;
			if (disp_irq & R500_D1_VBLANK_INTERRUPT)
				RADEON_WRITE(R500_D1MODE_VBLANK_STATUS, R500_VBLANK_ACK);
			if (disp_irq & R500_D2_VBLANK_INTERRUPT)
				RADEON_WRITE(R500_D2MODE_VBLANK_STATUS, R500_VBLANK_ACK);
		}
		irq_mask |= R500_DISPLAY_INT_STATUS;
	} else
		irq_mask |= RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT;

	irqs &=	irq_mask;

Dave Airlie's avatar
Dave Airlie committed
159
160
	if (irqs)
		RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs);
161

Dave Airlie's avatar
Dave Airlie committed
162
163
164
	return irqs;
}

Linus Torvalds's avatar
Linus Torvalds committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* Interrupts - Used for device synchronization and flushing in the
 * following circumstances:
 *
 * - Exclusive FB access with hw idle:
 *    - Wait for GUI Idle (?) interrupt, then do normal flush.
 *
 * - Frame throttling, NV_fence:
 *    - Drop marker irq's into command stream ahead of time.
 *    - Wait on irq's with lock *not held*
 *    - Check each for termination condition
 *
 * - Internally in cp_getbuffer, etc:
 *    - as above, but wait with lock held???
 *
 * NOTE: These functions are misleadingly named -- the irq's aren't
 * tied to dma at all, this is just a hangover from dri prehistory.
 */

Dave Airlie's avatar
Dave Airlie committed
183
irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS)
Linus Torvalds's avatar
Linus Torvalds committed
184
{
185
	struct drm_device *dev = (struct drm_device *) arg;
Dave Airlie's avatar
Dave Airlie committed
186
187
188
	drm_radeon_private_t *dev_priv =
	    (drm_radeon_private_t *) dev->dev_private;
	u32 stat;
189
	u32 r500_disp_int;
Linus Torvalds's avatar
Linus Torvalds committed
190

191
192
193
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
		return IRQ_NONE;

Linus Torvalds's avatar
Linus Torvalds committed
194
195
196
	/* Only consider the bits we're interested in - others could be used
	 * outside the DRM
	 */
197
	stat = radeon_acknowledge_irqs(dev_priv, &r500_disp_int);
Linus Torvalds's avatar
Linus Torvalds committed
198
199
200
	if (!stat)
		return IRQ_NONE;

201
202
	stat &= dev_priv->irq_enable_reg;

Linus Torvalds's avatar
Linus Torvalds committed
203
	/* SW interrupt */
204
	if (stat & RADEON_SW_INT_TEST)
Dave Airlie's avatar
Dave Airlie committed
205
		DRM_WAKEUP(&dev_priv->swi_queue);
Linus Torvalds's avatar
Linus Torvalds committed
206
207

	/* VBLANK interrupt */
208
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) {
209
210
211
212
213
214
215
216
217
		if (r500_disp_int & R500_D1_VBLANK_INTERRUPT)
			drm_handle_vblank(dev, 0);
		if (r500_disp_int & R500_D2_VBLANK_INTERRUPT)
			drm_handle_vblank(dev, 1);
	} else {
		if (stat & RADEON_CRTC_VBLANK_STAT)
			drm_handle_vblank(dev, 0);
		if (stat & RADEON_CRTC2_VBLANK_STAT)
			drm_handle_vblank(dev, 1);
218
	}
Linus Torvalds's avatar
Linus Torvalds committed
219
220
221
	return IRQ_HANDLED;
}

222
static int radeon_emit_irq(struct drm_device * dev)
Linus Torvalds's avatar
Linus Torvalds committed
223
224
225
226
227
228
229
230
{
	drm_radeon_private_t *dev_priv = dev->dev_private;
	unsigned int ret;
	RING_LOCALS;

	atomic_inc(&dev_priv->swi_emitted);
	ret = atomic_read(&dev_priv->swi_emitted);

Dave Airlie's avatar
Dave Airlie committed
231
232
233
234
235
	BEGIN_RING(4);
	OUT_RING_REG(RADEON_LAST_SWI_REG, ret);
	OUT_RING_REG(RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE);
	ADVANCE_RING();
	COMMIT_RING();
Linus Torvalds's avatar
Linus Torvalds committed
236
237
238
239

	return ret;
}

240
static int radeon_wait_irq(struct drm_device * dev, int swi_nr)
Linus Torvalds's avatar
Linus Torvalds committed
241
{
Dave Airlie's avatar
Dave Airlie committed
242
243
	drm_radeon_private_t *dev_priv =
	    (drm_radeon_private_t *) dev->dev_private;
Linus Torvalds's avatar
Linus Torvalds committed
244
245
	int ret = 0;

Dave Airlie's avatar
Dave Airlie committed
246
247
	if (RADEON_READ(RADEON_LAST_SWI_REG) >= swi_nr)
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
248
249
250

	dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;

Dave Airlie's avatar
Dave Airlie committed
251
252
	DRM_WAIT_ON(ret, dev_priv->swi_queue, 3 * DRM_HZ,
		    RADEON_READ(RADEON_LAST_SWI_REG) >= swi_nr);
Linus Torvalds's avatar
Linus Torvalds committed
253
254
255
256

	return ret;
}

257
u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc)
Linus Torvalds's avatar
Linus Torvalds committed
258
{
259
260
	drm_radeon_private_t *dev_priv = dev->dev_private;

Dave Airlie's avatar
Dave Airlie committed
261
	if (!dev_priv) {
262
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
263
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
264
265
	}

266
267
	if (crtc < 0 || crtc > 1) {
		DRM_ERROR("Invalid crtc %d\n", crtc);
Eric Anholt's avatar
Eric Anholt committed
268
		return -EINVAL;
269
	}
270

271
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600) {
272
273
274
275
276
277
278
279
280
281
		if (crtc == 0)
			return RADEON_READ(R500_D1CRTC_FRAME_COUNT);
		else
			return RADEON_READ(R500_D2CRTC_FRAME_COUNT);
	} else {
		if (crtc == 0)
			return RADEON_READ(RADEON_CRTC_CRNT_FRAME);
		else
			return RADEON_READ(RADEON_CRTC2_CRNT_FRAME);
	}
282
283
}

Linus Torvalds's avatar
Linus Torvalds committed
284
285
/* Needs the lock as it touches the ring.
 */
286
int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
287
288
{
	drm_radeon_private_t *dev_priv = dev->dev_private;
289
	drm_radeon_irq_emit_t *emit = data;
Linus Torvalds's avatar
Linus Torvalds committed
290
291
	int result;

Dave Airlie's avatar
Dave Airlie committed
292
	if (!dev_priv) {
293
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
294
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
295
296
	}

297
298
299
300
301
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
		return -EINVAL;

	LOCK_TEST_WITH_RETURN(dev, file_priv);

Dave Airlie's avatar
Dave Airlie committed
302
	result = radeon_emit_irq(dev);
Linus Torvalds's avatar
Linus Torvalds committed
303

304
	if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) {
Dave Airlie's avatar
Dave Airlie committed
305
		DRM_ERROR("copy_to_user\n");
Eric Anholt's avatar
Eric Anholt committed
306
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
307
308
309
310
311
312
313
	}

	return 0;
}

/* Doesn't need the hardware lock.
 */
314
int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv)
Linus Torvalds's avatar
Linus Torvalds committed
315
316
{
	drm_radeon_private_t *dev_priv = dev->dev_private;
317
	drm_radeon_irq_wait_t *irqwait = data;
Linus Torvalds's avatar
Linus Torvalds committed
318

Dave Airlie's avatar
Dave Airlie committed
319
	if (!dev_priv) {
320
		DRM_ERROR("called with no initialization\n");
Eric Anholt's avatar
Eric Anholt committed
321
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
322
323
	}

324
325
326
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
		return -EINVAL;

327
	return radeon_wait_irq(dev, irqwait->irq_seq);
Linus Torvalds's avatar
Linus Torvalds committed
328
329
330
331
}

/* drm_dma.h hooks
*/
332
void radeon_driver_irq_preinstall(struct drm_device * dev)
Dave Airlie's avatar
Dave Airlie committed
333
{
Linus Torvalds's avatar
Linus Torvalds committed
334
	drm_radeon_private_t *dev_priv =
Dave Airlie's avatar
Dave Airlie committed
335
	    (drm_radeon_private_t *) dev->dev_private;
336
	u32 dummy;
Linus Torvalds's avatar
Linus Torvalds committed
337

338
339
340
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
		return;

Dave Airlie's avatar
Dave Airlie committed
341
	/* Disable *all* interrupts */
342
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600)
343
		RADEON_WRITE(R500_DxMODE_INT_MASK, 0);
Dave Airlie's avatar
Dave Airlie committed
344
	RADEON_WRITE(RADEON_GEN_INT_CNTL, 0);
Linus Torvalds's avatar
Linus Torvalds committed
345
346

	/* Clear bits if they're already high */
347
	radeon_acknowledge_irqs(dev_priv, &dummy);
Linus Torvalds's avatar
Linus Torvalds committed
348
349
}

350
int radeon_driver_irq_postinstall(struct drm_device *dev)
Dave Airlie's avatar
Dave Airlie committed
351
{
Linus Torvalds's avatar
Linus Torvalds committed
352
	drm_radeon_private_t *dev_priv =
Dave Airlie's avatar
Dave Airlie committed
353
	    (drm_radeon_private_t *) dev->dev_private;
Linus Torvalds's avatar
Linus Torvalds committed
354

Dave Airlie's avatar
Dave Airlie committed
355
356
	atomic_set(&dev_priv->swi_emitted, 0);
	DRM_INIT_WAITQUEUE(&dev_priv->swi_queue);
Linus Torvalds's avatar
Linus Torvalds committed
357

358
359
	dev->max_vblank_count = 0x001fffff;

360
361
362
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
		return 0;

363
364
365
	radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
366
367
}

368
void radeon_driver_irq_uninstall(struct drm_device * dev)
Dave Airlie's avatar
Dave Airlie committed
369
{
Linus Torvalds's avatar
Linus Torvalds committed
370
	drm_radeon_private_t *dev_priv =
Dave Airlie's avatar
Dave Airlie committed
371
	    (drm_radeon_private_t *) dev->dev_private;
Linus Torvalds's avatar
Linus Torvalds committed
372
373
374
	if (!dev_priv)
		return;

375
376
377
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
		return;

378
	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS600)
379
		RADEON_WRITE(R500_DxMODE_INT_MASK, 0);
Linus Torvalds's avatar
Linus Torvalds committed
380
	/* Disable *all* interrupts */
Dave Airlie's avatar
Dave Airlie committed
381
	RADEON_WRITE(RADEON_GEN_INT_CNTL, 0);
Linus Torvalds's avatar
Linus Torvalds committed
382
}
383
384


385
int radeon_vblank_crtc_get(struct drm_device *dev)
386
387
388
{
	drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private;

389
	return dev_priv->vblank_crtc;
390
391
}

392
int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value)
393
394
395
396
{
	drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private;
	if (value & ~(DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) {
		DRM_ERROR("called with invalid crtc 0x%x\n", (unsigned int)value);
Eric Anholt's avatar
Eric Anholt committed
397
		return -EINVAL;
398
399
400
401
	}
	dev_priv->vblank_crtc = (unsigned int)value;
	return 0;
}