xref: /openbmc/linux/drivers/gpu/drm/i915/i915_irq.c (revision bd95e0a4a6bb9485fe35dda62719663f6ceabae1)
1c0e09200SDave Airlie /* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
2c0e09200SDave Airlie  */
3c0e09200SDave Airlie /*
4c0e09200SDave Airlie  * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
5c0e09200SDave Airlie  * All Rights Reserved.
6c0e09200SDave Airlie  *
7c0e09200SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
8c0e09200SDave Airlie  * copy of this software and associated documentation files (the
9c0e09200SDave Airlie  * "Software"), to deal in the Software without restriction, including
10c0e09200SDave Airlie  * without limitation the rights to use, copy, modify, merge, publish,
11c0e09200SDave Airlie  * distribute, sub license, and/or sell copies of the Software, and to
12c0e09200SDave Airlie  * permit persons to whom the Software is furnished to do so, subject to
13c0e09200SDave Airlie  * the following conditions:
14c0e09200SDave Airlie  *
15c0e09200SDave Airlie  * The above copyright notice and this permission notice (including the
16c0e09200SDave Airlie  * next paragraph) shall be included in all copies or substantial portions
17c0e09200SDave Airlie  * of the Software.
18c0e09200SDave Airlie  *
19c0e09200SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20c0e09200SDave Airlie  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21c0e09200SDave Airlie  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22c0e09200SDave Airlie  * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23c0e09200SDave Airlie  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24c0e09200SDave Airlie  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25c0e09200SDave Airlie  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26c0e09200SDave Airlie  *
27c0e09200SDave Airlie  */
28c0e09200SDave Airlie 
29c0e09200SDave Airlie #include "drmP.h"
30c0e09200SDave Airlie #include "drm.h"
31c0e09200SDave Airlie #include "i915_drm.h"
32c0e09200SDave Airlie #include "i915_drv.h"
33c0e09200SDave Airlie 
34c0e09200SDave Airlie #define MAX_NOPID ((u32)~0)
35c0e09200SDave Airlie 
36ed4cb414SEric Anholt /** These are the interrupts used by the driver */
37ed4cb414SEric Anholt #define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT |		\
388ee1c3dbSMatthew Garrett 				    I915_ASLE_INTERRUPT |		\
390a3e67a4SJesse Barnes 				    I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
408ee1c3dbSMatthew Garrett 				    I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
41ed4cb414SEric Anholt 
428ee1c3dbSMatthew Garrett void
43ed4cb414SEric Anholt i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
44ed4cb414SEric Anholt {
45ed4cb414SEric Anholt 	if ((dev_priv->irq_mask_reg & mask) != 0) {
46ed4cb414SEric Anholt 		dev_priv->irq_mask_reg &= ~mask;
47ed4cb414SEric Anholt 		I915_WRITE(IMR, dev_priv->irq_mask_reg);
48ed4cb414SEric Anholt 		(void) I915_READ(IMR);
49ed4cb414SEric Anholt 	}
50ed4cb414SEric Anholt }
51ed4cb414SEric Anholt 
52ed4cb414SEric Anholt static inline void
53ed4cb414SEric Anholt i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
54ed4cb414SEric Anholt {
55ed4cb414SEric Anholt 	if ((dev_priv->irq_mask_reg & mask) != mask) {
56ed4cb414SEric Anholt 		dev_priv->irq_mask_reg |= mask;
57ed4cb414SEric Anholt 		I915_WRITE(IMR, dev_priv->irq_mask_reg);
58ed4cb414SEric Anholt 		(void) I915_READ(IMR);
59ed4cb414SEric Anholt 	}
60ed4cb414SEric Anholt }
61ed4cb414SEric Anholt 
62c0e09200SDave Airlie /**
630a3e67a4SJesse Barnes  * i915_pipe_enabled - check if a pipe is enabled
640a3e67a4SJesse Barnes  * @dev: DRM device
650a3e67a4SJesse Barnes  * @pipe: pipe to check
660a3e67a4SJesse Barnes  *
670a3e67a4SJesse Barnes  * Reading certain registers when the pipe is disabled can hang the chip.
680a3e67a4SJesse Barnes  * Use this routine to make sure the PLL is running and the pipe is active
690a3e67a4SJesse Barnes  * before reading such registers if unsure.
700a3e67a4SJesse Barnes  */
710a3e67a4SJesse Barnes static int
720a3e67a4SJesse Barnes i915_pipe_enabled(struct drm_device *dev, int pipe)
730a3e67a4SJesse Barnes {
740a3e67a4SJesse Barnes 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
750a3e67a4SJesse Barnes 	unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF;
760a3e67a4SJesse Barnes 
770a3e67a4SJesse Barnes 	if (I915_READ(pipeconf) & PIPEACONF_ENABLE)
780a3e67a4SJesse Barnes 		return 1;
790a3e67a4SJesse Barnes 
800a3e67a4SJesse Barnes 	return 0;
810a3e67a4SJesse Barnes }
820a3e67a4SJesse Barnes 
8342f52ef8SKeith Packard /* Called from drm generic code, passed a 'crtc', which
8442f52ef8SKeith Packard  * we use as a pipe index
8542f52ef8SKeith Packard  */
8642f52ef8SKeith Packard u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
870a3e67a4SJesse Barnes {
880a3e67a4SJesse Barnes 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
890a3e67a4SJesse Barnes 	unsigned long high_frame;
900a3e67a4SJesse Barnes 	unsigned long low_frame;
910a3e67a4SJesse Barnes 	u32 high1, high2, low, count;
920a3e67a4SJesse Barnes 
930a3e67a4SJesse Barnes 	high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
940a3e67a4SJesse Barnes 	low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
950a3e67a4SJesse Barnes 
960a3e67a4SJesse Barnes 	if (!i915_pipe_enabled(dev, pipe)) {
970a3e67a4SJesse Barnes 		DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
980a3e67a4SJesse Barnes 		return 0;
990a3e67a4SJesse Barnes 	}
1000a3e67a4SJesse Barnes 
1010a3e67a4SJesse Barnes 	/*
1020a3e67a4SJesse Barnes 	 * High & low register fields aren't synchronized, so make sure
1030a3e67a4SJesse Barnes 	 * we get a low value that's stable across two reads of the high
1040a3e67a4SJesse Barnes 	 * register.
1050a3e67a4SJesse Barnes 	 */
1060a3e67a4SJesse Barnes 	do {
1070a3e67a4SJesse Barnes 		high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
1080a3e67a4SJesse Barnes 			 PIPE_FRAME_HIGH_SHIFT);
1090a3e67a4SJesse Barnes 		low =  ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
1100a3e67a4SJesse Barnes 			PIPE_FRAME_LOW_SHIFT);
1110a3e67a4SJesse Barnes 		high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
1120a3e67a4SJesse Barnes 			 PIPE_FRAME_HIGH_SHIFT);
1130a3e67a4SJesse Barnes 	} while (high1 != high2);
1140a3e67a4SJesse Barnes 
1150a3e67a4SJesse Barnes 	count = (high1 << 8) | low;
1160a3e67a4SJesse Barnes 
1170a3e67a4SJesse Barnes 	return count;
1180a3e67a4SJesse Barnes }
1190a3e67a4SJesse Barnes 
120c0e09200SDave Airlie irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
121c0e09200SDave Airlie {
122c0e09200SDave Airlie 	struct drm_device *dev = (struct drm_device *) arg;
123c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
124ed4cb414SEric Anholt 	u32 iir;
1250a3e67a4SJesse Barnes 	u32 pipea_stats, pipeb_stats;
1260a3e67a4SJesse Barnes 	int vblank = 0;
127c0e09200SDave Airlie 
128630681d9SEric Anholt 	atomic_inc(&dev_priv->irq_received);
129630681d9SEric Anholt 
130ed4cb414SEric Anholt 	if (dev->pdev->msi_enabled)
131ed4cb414SEric Anholt 		I915_WRITE(IMR, ~0);
132ed4cb414SEric Anholt 	iir = I915_READ(IIR);
133c0e09200SDave Airlie 
134ed4cb414SEric Anholt 	if (iir == 0) {
135ed4cb414SEric Anholt 		if (dev->pdev->msi_enabled) {
136ed4cb414SEric Anholt 			I915_WRITE(IMR, dev_priv->irq_mask_reg);
137ed4cb414SEric Anholt 			(void) I915_READ(IMR);
138ed4cb414SEric Anholt 		}
139c0e09200SDave Airlie 		return IRQ_NONE;
140ed4cb414SEric Anholt 	}
141c0e09200SDave Airlie 
1420a3e67a4SJesse Barnes 	/*
1430a3e67a4SJesse Barnes 	 * Clear the PIPE(A|B)STAT regs before the IIR otherwise
1440a3e67a4SJesse Barnes 	 * we may get extra interrupts.
1450a3e67a4SJesse Barnes 	 */
1460a3e67a4SJesse Barnes 	if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
1470a3e67a4SJesse Barnes 		pipea_stats = I915_READ(PIPEASTAT);
1480a3e67a4SJesse Barnes 		if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A))
1490a3e67a4SJesse Barnes 			pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
1500a3e67a4SJesse Barnes 					 PIPE_VBLANK_INTERRUPT_ENABLE);
1510a3e67a4SJesse Barnes 		else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
1520a3e67a4SJesse Barnes 					PIPE_VBLANK_INTERRUPT_STATUS)) {
1530a3e67a4SJesse Barnes 			vblank++;
15442f52ef8SKeith Packard 			drm_handle_vblank(dev, 0);
1550a3e67a4SJesse Barnes 		}
1560a3e67a4SJesse Barnes 
1578ee1c3dbSMatthew Garrett 		I915_WRITE(PIPEASTAT, pipea_stats);
1580a3e67a4SJesse Barnes 	}
1590a3e67a4SJesse Barnes 	if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
1600a3e67a4SJesse Barnes 		pipeb_stats = I915_READ(PIPEBSTAT);
1610a3e67a4SJesse Barnes 		/* Ack the event */
1628ee1c3dbSMatthew Garrett 		I915_WRITE(PIPEBSTAT, pipeb_stats);
1638ee1c3dbSMatthew Garrett 
1640a3e67a4SJesse Barnes 		/* The vblank interrupt gets enabled even if we didn't ask for
1650a3e67a4SJesse Barnes 		   it, so make sure it's shut down again */
1660a3e67a4SJesse Barnes 		if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B))
1670a3e67a4SJesse Barnes 			pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
1680a3e67a4SJesse Barnes 					 PIPE_VBLANK_INTERRUPT_ENABLE);
1690a3e67a4SJesse Barnes 		else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
1700a3e67a4SJesse Barnes 					PIPE_VBLANK_INTERRUPT_STATUS)) {
1710a3e67a4SJesse Barnes 			vblank++;
17242f52ef8SKeith Packard 			drm_handle_vblank(dev, 1);
1730a3e67a4SJesse Barnes 		}
174c0e09200SDave Airlie 
1750a3e67a4SJesse Barnes 		if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS)
1760a3e67a4SJesse Barnes 			opregion_asle_intr(dev);
1770a3e67a4SJesse Barnes 		I915_WRITE(PIPEBSTAT, pipeb_stats);
178c0e09200SDave Airlie 	}
179c0e09200SDave Airlie 
180673a394bSEric Anholt 	I915_WRITE(IIR, iir);
181673a394bSEric Anholt 	if (dev->pdev->msi_enabled)
182673a394bSEric Anholt 		I915_WRITE(IMR, dev_priv->irq_mask_reg);
183673a394bSEric Anholt 	(void) I915_READ(IIR); /* Flush posted writes */
1848ee1c3dbSMatthew Garrett 
185c99b058fSKristian Høgsberg 	if (dev_priv->sarea_priv)
186c99b058fSKristian Høgsberg 		dev_priv->sarea_priv->last_dispatch =
187c99b058fSKristian Høgsberg 			READ_BREADCRUMB(dev_priv);
1880a3e67a4SJesse Barnes 
189673a394bSEric Anholt 	if (iir & I915_USER_INTERRUPT) {
190673a394bSEric Anholt 		dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
191673a394bSEric Anholt 		DRM_WAKEUP(&dev_priv->irq_queue);
192673a394bSEric Anholt 	}
193673a394bSEric Anholt 
194673a394bSEric Anholt 	if (iir & I915_ASLE_INTERRUPT)
195673a394bSEric Anholt 		opregion_asle_intr(dev);
1960a3e67a4SJesse Barnes 
197c0e09200SDave Airlie 	return IRQ_HANDLED;
198c0e09200SDave Airlie }
199c0e09200SDave Airlie 
200c0e09200SDave Airlie static int i915_emit_irq(struct drm_device * dev)
201c0e09200SDave Airlie {
202c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
203c0e09200SDave Airlie 	RING_LOCALS;
204c0e09200SDave Airlie 
205c0e09200SDave Airlie 	i915_kernel_lost_context(dev);
206c0e09200SDave Airlie 
207c0e09200SDave Airlie 	DRM_DEBUG("\n");
208c0e09200SDave Airlie 
209c99b058fSKristian Høgsberg 	dev_priv->counter++;
210c0e09200SDave Airlie 	if (dev_priv->counter > 0x7FFFFFFFUL)
211c99b058fSKristian Høgsberg 		dev_priv->counter = 1;
212c99b058fSKristian Høgsberg 	if (dev_priv->sarea_priv)
213c99b058fSKristian Høgsberg 		dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
214c0e09200SDave Airlie 
215c0e09200SDave Airlie 	BEGIN_LP_RING(6);
216585fb111SJesse Barnes 	OUT_RING(MI_STORE_DWORD_INDEX);
217585fb111SJesse Barnes 	OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT);
218c0e09200SDave Airlie 	OUT_RING(dev_priv->counter);
219c0e09200SDave Airlie 	OUT_RING(0);
220c0e09200SDave Airlie 	OUT_RING(0);
221585fb111SJesse Barnes 	OUT_RING(MI_USER_INTERRUPT);
222c0e09200SDave Airlie 	ADVANCE_LP_RING();
223c0e09200SDave Airlie 
224c0e09200SDave Airlie 	return dev_priv->counter;
225c0e09200SDave Airlie }
226c0e09200SDave Airlie 
227673a394bSEric Anholt void i915_user_irq_get(struct drm_device *dev)
228ed4cb414SEric Anholt {
229ed4cb414SEric Anholt 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
230e9d21d7fSKeith Packard 	unsigned long irqflags;
231ed4cb414SEric Anholt 
232e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
233ed4cb414SEric Anholt 	if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
234ed4cb414SEric Anholt 		i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
235e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
236ed4cb414SEric Anholt }
237ed4cb414SEric Anholt 
2380a3e67a4SJesse Barnes void i915_user_irq_put(struct drm_device *dev)
239ed4cb414SEric Anholt {
240ed4cb414SEric Anholt 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
241e9d21d7fSKeith Packard 	unsigned long irqflags;
242ed4cb414SEric Anholt 
243e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
244ed4cb414SEric Anholt 	BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
245ed4cb414SEric Anholt 	if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
246ed4cb414SEric Anholt 		i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
247e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
248ed4cb414SEric Anholt }
249ed4cb414SEric Anholt 
250c0e09200SDave Airlie static int i915_wait_irq(struct drm_device * dev, int irq_nr)
251c0e09200SDave Airlie {
252c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
253c0e09200SDave Airlie 	int ret = 0;
254c0e09200SDave Airlie 
255c0e09200SDave Airlie 	DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
256c0e09200SDave Airlie 		  READ_BREADCRUMB(dev_priv));
257c0e09200SDave Airlie 
258ed4cb414SEric Anholt 	if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
259c99b058fSKristian Høgsberg 		if (dev_priv->sarea_priv) {
260c99b058fSKristian Høgsberg 			dev_priv->sarea_priv->last_dispatch =
261c99b058fSKristian Høgsberg 				READ_BREADCRUMB(dev_priv);
262c99b058fSKristian Høgsberg 		}
263c0e09200SDave Airlie 		return 0;
264ed4cb414SEric Anholt 	}
265c0e09200SDave Airlie 
266c99b058fSKristian Høgsberg 	if (dev_priv->sarea_priv)
267c0e09200SDave Airlie 		dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
268c0e09200SDave Airlie 
269ed4cb414SEric Anholt 	i915_user_irq_get(dev);
270c0e09200SDave Airlie 	DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
271c0e09200SDave Airlie 		    READ_BREADCRUMB(dev_priv) >= irq_nr);
272ed4cb414SEric Anholt 	i915_user_irq_put(dev);
273c0e09200SDave Airlie 
274c0e09200SDave Airlie 	if (ret == -EBUSY) {
275c0e09200SDave Airlie 		DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
276c0e09200SDave Airlie 			  READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
277c0e09200SDave Airlie 	}
278c0e09200SDave Airlie 
279c99b058fSKristian Høgsberg 	if (dev_priv->sarea_priv)
280c99b058fSKristian Høgsberg 		dev_priv->sarea_priv->last_dispatch =
281c99b058fSKristian Høgsberg 			READ_BREADCRUMB(dev_priv);
282c0e09200SDave Airlie 
283c0e09200SDave Airlie 	return ret;
284c0e09200SDave Airlie }
285c0e09200SDave Airlie 
286c0e09200SDave Airlie /* Needs the lock as it touches the ring.
287c0e09200SDave Airlie  */
288c0e09200SDave Airlie int i915_irq_emit(struct drm_device *dev, void *data,
289c0e09200SDave Airlie 			 struct drm_file *file_priv)
290c0e09200SDave Airlie {
291c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
292c0e09200SDave Airlie 	drm_i915_irq_emit_t *emit = data;
293c0e09200SDave Airlie 	int result;
294c0e09200SDave Airlie 
295546b0974SEric Anholt 	RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
296c0e09200SDave Airlie 
297c0e09200SDave Airlie 	if (!dev_priv) {
298c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
299c0e09200SDave Airlie 		return -EINVAL;
300c0e09200SDave Airlie 	}
301546b0974SEric Anholt 	mutex_lock(&dev->struct_mutex);
302c0e09200SDave Airlie 	result = i915_emit_irq(dev);
303546b0974SEric Anholt 	mutex_unlock(&dev->struct_mutex);
304c0e09200SDave Airlie 
305c0e09200SDave Airlie 	if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) {
306c0e09200SDave Airlie 		DRM_ERROR("copy_to_user\n");
307c0e09200SDave Airlie 		return -EFAULT;
308c0e09200SDave Airlie 	}
309c0e09200SDave Airlie 
310c0e09200SDave Airlie 	return 0;
311c0e09200SDave Airlie }
312c0e09200SDave Airlie 
313c0e09200SDave Airlie /* Doesn't need the hardware lock.
314c0e09200SDave Airlie  */
315c0e09200SDave Airlie int i915_irq_wait(struct drm_device *dev, void *data,
316c0e09200SDave Airlie 			 struct drm_file *file_priv)
317c0e09200SDave Airlie {
318c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
319c0e09200SDave Airlie 	drm_i915_irq_wait_t *irqwait = data;
320c0e09200SDave Airlie 
321c0e09200SDave Airlie 	if (!dev_priv) {
322c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
323c0e09200SDave Airlie 		return -EINVAL;
324c0e09200SDave Airlie 	}
325c0e09200SDave Airlie 
326c0e09200SDave Airlie 	return i915_wait_irq(dev, irqwait->irq_seq);
327c0e09200SDave Airlie }
328c0e09200SDave Airlie 
32942f52ef8SKeith Packard /* Called from drm generic code, passed 'crtc' which
33042f52ef8SKeith Packard  * we use as a pipe index
33142f52ef8SKeith Packard  */
33242f52ef8SKeith Packard int i915_enable_vblank(struct drm_device *dev, int pipe)
3330a3e67a4SJesse Barnes {
3340a3e67a4SJesse Barnes 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
3350a3e67a4SJesse Barnes 	u32	pipestat_reg = 0;
3360a3e67a4SJesse Barnes 	u32	pipestat;
337e9d21d7fSKeith Packard 	u32	interrupt = 0;
338e9d21d7fSKeith Packard 	unsigned long irqflags;
3390a3e67a4SJesse Barnes 
3400a3e67a4SJesse Barnes 	switch (pipe) {
3410a3e67a4SJesse Barnes 	case 0:
3420a3e67a4SJesse Barnes 		pipestat_reg = PIPEASTAT;
343e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
3440a3e67a4SJesse Barnes 		break;
3450a3e67a4SJesse Barnes 	case 1:
3460a3e67a4SJesse Barnes 		pipestat_reg = PIPEBSTAT;
347e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
3480a3e67a4SJesse Barnes 		break;
3490a3e67a4SJesse Barnes 	default:
3500a3e67a4SJesse Barnes 		DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
3510a3e67a4SJesse Barnes 			  pipe);
352e9d21d7fSKeith Packard 		return 0;
3530a3e67a4SJesse Barnes 	}
3540a3e67a4SJesse Barnes 
355e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
356053d7f24SEric Anholt 	/* Enabling vblank events in IMR comes before PIPESTAT write, or
357053d7f24SEric Anholt 	 * there's a race where the PIPESTAT vblank bit gets set to 1, so
358053d7f24SEric Anholt 	 * the OR of enabled PIPESTAT bits goes to 1, so the PIPExEVENT in
359053d7f24SEric Anholt 	 * ISR flashes to 1, but the IIR bit doesn't get set to 1 because
360053d7f24SEric Anholt 	 * IMR masks it.  It doesn't ever get set after we clear the masking
361053d7f24SEric Anholt 	 * in IMR because the ISR bit is edge, not level-triggered, on the
362053d7f24SEric Anholt 	 * OR of PIPESTAT bits.
363053d7f24SEric Anholt 	 */
364053d7f24SEric Anholt 	i915_enable_irq(dev_priv, interrupt);
3650a3e67a4SJesse Barnes 	pipestat = I915_READ(pipestat_reg);
3660a3e67a4SJesse Barnes 	if (IS_I965G(dev))
3670a3e67a4SJesse Barnes 		pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE;
3680a3e67a4SJesse Barnes 	else
3690a3e67a4SJesse Barnes 		pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE;
3700a3e67a4SJesse Barnes 	/* Clear any stale interrupt status */
3710a3e67a4SJesse Barnes 	pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
3720a3e67a4SJesse Barnes 		     PIPE_VBLANK_INTERRUPT_STATUS);
3730a3e67a4SJesse Barnes 	I915_WRITE(pipestat_reg, pipestat);
374e9d21d7fSKeith Packard 	(void) I915_READ(pipestat_reg);	/* Posting read */
375e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
3760a3e67a4SJesse Barnes 
3770a3e67a4SJesse Barnes 	return 0;
3780a3e67a4SJesse Barnes }
3790a3e67a4SJesse Barnes 
38042f52ef8SKeith Packard /* Called from drm generic code, passed 'crtc' which
38142f52ef8SKeith Packard  * we use as a pipe index
38242f52ef8SKeith Packard  */
38342f52ef8SKeith Packard void i915_disable_vblank(struct drm_device *dev, int pipe)
3840a3e67a4SJesse Barnes {
3850a3e67a4SJesse Barnes 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
3860a3e67a4SJesse Barnes 	u32	pipestat_reg = 0;
3870a3e67a4SJesse Barnes 	u32	pipestat;
388e9d21d7fSKeith Packard 	u32	interrupt = 0;
389e9d21d7fSKeith Packard 	unsigned long irqflags;
3900a3e67a4SJesse Barnes 
3910a3e67a4SJesse Barnes 	switch (pipe) {
3920a3e67a4SJesse Barnes 	case 0:
3930a3e67a4SJesse Barnes 		pipestat_reg = PIPEASTAT;
394e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
3950a3e67a4SJesse Barnes 		break;
3960a3e67a4SJesse Barnes 	case 1:
3970a3e67a4SJesse Barnes 		pipestat_reg = PIPEBSTAT;
398e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
3990a3e67a4SJesse Barnes 		break;
4000a3e67a4SJesse Barnes 	default:
4010a3e67a4SJesse Barnes 		DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
4020a3e67a4SJesse Barnes 			  pipe);
403e9d21d7fSKeith Packard 		return;
4040a3e67a4SJesse Barnes 		break;
4050a3e67a4SJesse Barnes 	}
4060a3e67a4SJesse Barnes 
407e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
408e9d21d7fSKeith Packard 	i915_disable_irq(dev_priv, interrupt);
4090a3e67a4SJesse Barnes 	pipestat = I915_READ(pipestat_reg);
4100a3e67a4SJesse Barnes 	pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
4110a3e67a4SJesse Barnes 		      PIPE_VBLANK_INTERRUPT_ENABLE);
4120a3e67a4SJesse Barnes 	/* Clear any stale interrupt status */
4130a3e67a4SJesse Barnes 	pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
4140a3e67a4SJesse Barnes 		     PIPE_VBLANK_INTERRUPT_STATUS);
4150a3e67a4SJesse Barnes 	I915_WRITE(pipestat_reg, pipestat);
416e9d21d7fSKeith Packard 	(void) I915_READ(pipestat_reg);	/* Posting read */
417e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
4180a3e67a4SJesse Barnes }
4190a3e67a4SJesse Barnes 
420c0e09200SDave Airlie /* Set the vblank monitor pipe
421c0e09200SDave Airlie  */
422c0e09200SDave Airlie int i915_vblank_pipe_set(struct drm_device *dev, void *data,
423c0e09200SDave Airlie 			 struct drm_file *file_priv)
424c0e09200SDave Airlie {
425c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
426c0e09200SDave Airlie 
427c0e09200SDave Airlie 	if (!dev_priv) {
428c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
429c0e09200SDave Airlie 		return -EINVAL;
430c0e09200SDave Airlie 	}
431c0e09200SDave Airlie 
432c0e09200SDave Airlie 	return 0;
433c0e09200SDave Airlie }
434c0e09200SDave Airlie 
435c0e09200SDave Airlie int i915_vblank_pipe_get(struct drm_device *dev, void *data,
436c0e09200SDave Airlie 			 struct drm_file *file_priv)
437c0e09200SDave Airlie {
438c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
439c0e09200SDave Airlie 	drm_i915_vblank_pipe_t *pipe = data;
440c0e09200SDave Airlie 
441c0e09200SDave Airlie 	if (!dev_priv) {
442c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
443c0e09200SDave Airlie 		return -EINVAL;
444c0e09200SDave Airlie 	}
445c0e09200SDave Airlie 
4460a3e67a4SJesse Barnes 	pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
447c0e09200SDave Airlie 
448c0e09200SDave Airlie 	return 0;
449c0e09200SDave Airlie }
450c0e09200SDave Airlie 
451c0e09200SDave Airlie /**
452c0e09200SDave Airlie  * Schedule buffer swap at given vertical blank.
453c0e09200SDave Airlie  */
454c0e09200SDave Airlie int i915_vblank_swap(struct drm_device *dev, void *data,
455c0e09200SDave Airlie 		     struct drm_file *file_priv)
456c0e09200SDave Airlie {
457*bd95e0a4SEric Anholt 	/* The delayed swap mechanism was fundamentally racy, and has been
458*bd95e0a4SEric Anholt 	 * removed.  The model was that the client requested a delayed flip/swap
459*bd95e0a4SEric Anholt 	 * from the kernel, then waited for vblank before continuing to perform
460*bd95e0a4SEric Anholt 	 * rendering.  The problem was that the kernel might wake the client
461*bd95e0a4SEric Anholt 	 * up before it dispatched the vblank swap (since the lock has to be
462*bd95e0a4SEric Anholt 	 * held while touching the ringbuffer), in which case the client would
463*bd95e0a4SEric Anholt 	 * clear and start the next frame before the swap occurred, and
464*bd95e0a4SEric Anholt 	 * flicker would occur in addition to likely missing the vblank.
465*bd95e0a4SEric Anholt 	 *
466*bd95e0a4SEric Anholt 	 * In the absence of this ioctl, userland falls back to a correct path
467*bd95e0a4SEric Anholt 	 * of waiting for a vblank, then dispatching the swap on its own.
468*bd95e0a4SEric Anholt 	 * Context switching to userland and back is plenty fast enough for
469*bd95e0a4SEric Anholt 	 * meeting the requirements of vblank swapping.
4700a3e67a4SJesse Barnes 	 */
471c0e09200SDave Airlie 	return -EINVAL;
472c0e09200SDave Airlie }
473c0e09200SDave Airlie 
474c0e09200SDave Airlie /* drm_dma.h hooks
475c0e09200SDave Airlie */
476c0e09200SDave Airlie void i915_driver_irq_preinstall(struct drm_device * dev)
477c0e09200SDave Airlie {
478c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
479c0e09200SDave Airlie 
4800a3e67a4SJesse Barnes 	I915_WRITE(HWSTAM, 0xeffe);
4810a3e67a4SJesse Barnes 	I915_WRITE(IMR, 0xffffffff);
482ed4cb414SEric Anholt 	I915_WRITE(IER, 0x0);
483c0e09200SDave Airlie }
484c0e09200SDave Airlie 
4850a3e67a4SJesse Barnes int i915_driver_irq_postinstall(struct drm_device *dev)
486c0e09200SDave Airlie {
487c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
4880a3e67a4SJesse Barnes 	int ret, num_pipes = 2;
489c0e09200SDave Airlie 
490ed4cb414SEric Anholt 	/* Set initial unmasked IRQs to just the selected vblank pipes. */
491ed4cb414SEric Anholt 	dev_priv->irq_mask_reg = ~0;
4920a3e67a4SJesse Barnes 
4930a3e67a4SJesse Barnes 	ret = drm_vblank_init(dev, num_pipes);
4940a3e67a4SJesse Barnes 	if (ret)
4950a3e67a4SJesse Barnes 		return ret;
4960a3e67a4SJesse Barnes 
4970a3e67a4SJesse Barnes 	dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
498ed4cb414SEric Anholt 	dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
499ed4cb414SEric Anholt 	dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
500ed4cb414SEric Anholt 
5010a3e67a4SJesse Barnes 	dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
5020a3e67a4SJesse Barnes 
5038ee1c3dbSMatthew Garrett 	dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK;
5048ee1c3dbSMatthew Garrett 
505ed4cb414SEric Anholt 	I915_WRITE(IMR, dev_priv->irq_mask_reg);
506ed4cb414SEric Anholt 	I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
507ed4cb414SEric Anholt 	(void) I915_READ(IER);
508ed4cb414SEric Anholt 
5098ee1c3dbSMatthew Garrett 	opregion_enable_asle(dev);
510c0e09200SDave Airlie 	DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
5110a3e67a4SJesse Barnes 
5120a3e67a4SJesse Barnes 	return 0;
513c0e09200SDave Airlie }
514c0e09200SDave Airlie 
515c0e09200SDave Airlie void i915_driver_irq_uninstall(struct drm_device * dev)
516c0e09200SDave Airlie {
517c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
5180a3e67a4SJesse Barnes 	u32 temp;
519c0e09200SDave Airlie 
520c0e09200SDave Airlie 	if (!dev_priv)
521c0e09200SDave Airlie 		return;
522c0e09200SDave Airlie 
5230a3e67a4SJesse Barnes 	dev_priv->vblank_pipe = 0;
5240a3e67a4SJesse Barnes 
5250a3e67a4SJesse Barnes 	I915_WRITE(HWSTAM, 0xffffffff);
5260a3e67a4SJesse Barnes 	I915_WRITE(IMR, 0xffffffff);
527ed4cb414SEric Anholt 	I915_WRITE(IER, 0x0);
528c0e09200SDave Airlie 
5290a3e67a4SJesse Barnes 	temp = I915_READ(PIPEASTAT);
5300a3e67a4SJesse Barnes 	I915_WRITE(PIPEASTAT, temp);
5310a3e67a4SJesse Barnes 	temp = I915_READ(PIPEBSTAT);
5320a3e67a4SJesse Barnes 	I915_WRITE(PIPEBSTAT, temp);
533ed4cb414SEric Anholt 	temp = I915_READ(IIR);
534ed4cb414SEric Anholt 	I915_WRITE(IIR, temp);
535c0e09200SDave Airlie }
536