xref: /openbmc/linux/drivers/gpu/drm/i915/i915_irq.c (revision 0baf823a10bd4131f70e9712d1f02de3c247f1df)
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 
215*0baf823aSKeith Packard 	BEGIN_LP_RING(4);
216585fb111SJesse Barnes 	OUT_RING(MI_STORE_DWORD_INDEX);
217*0baf823aSKeith Packard 	OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
218c0e09200SDave Airlie 	OUT_RING(dev_priv->counter);
219585fb111SJesse Barnes 	OUT_RING(MI_USER_INTERRUPT);
220c0e09200SDave Airlie 	ADVANCE_LP_RING();
221c0e09200SDave Airlie 
222c0e09200SDave Airlie 	return dev_priv->counter;
223c0e09200SDave Airlie }
224c0e09200SDave Airlie 
225673a394bSEric Anholt void i915_user_irq_get(struct drm_device *dev)
226ed4cb414SEric Anholt {
227ed4cb414SEric Anholt 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
228e9d21d7fSKeith Packard 	unsigned long irqflags;
229ed4cb414SEric Anholt 
230e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
231ed4cb414SEric Anholt 	if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
232ed4cb414SEric Anholt 		i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
233e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
234ed4cb414SEric Anholt }
235ed4cb414SEric Anholt 
2360a3e67a4SJesse Barnes void i915_user_irq_put(struct drm_device *dev)
237ed4cb414SEric Anholt {
238ed4cb414SEric Anholt 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
239e9d21d7fSKeith Packard 	unsigned long irqflags;
240ed4cb414SEric Anholt 
241e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
242ed4cb414SEric Anholt 	BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
243ed4cb414SEric Anholt 	if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
244ed4cb414SEric Anholt 		i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
245e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
246ed4cb414SEric Anholt }
247ed4cb414SEric Anholt 
248c0e09200SDave Airlie static int i915_wait_irq(struct drm_device * dev, int irq_nr)
249c0e09200SDave Airlie {
250c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
251c0e09200SDave Airlie 	int ret = 0;
252c0e09200SDave Airlie 
253c0e09200SDave Airlie 	DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
254c0e09200SDave Airlie 		  READ_BREADCRUMB(dev_priv));
255c0e09200SDave Airlie 
256ed4cb414SEric Anholt 	if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
257c99b058fSKristian Høgsberg 		if (dev_priv->sarea_priv) {
258c99b058fSKristian Høgsberg 			dev_priv->sarea_priv->last_dispatch =
259c99b058fSKristian Høgsberg 				READ_BREADCRUMB(dev_priv);
260c99b058fSKristian Høgsberg 		}
261c0e09200SDave Airlie 		return 0;
262ed4cb414SEric Anholt 	}
263c0e09200SDave Airlie 
264c99b058fSKristian Høgsberg 	if (dev_priv->sarea_priv)
265c0e09200SDave Airlie 		dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
266c0e09200SDave Airlie 
267ed4cb414SEric Anholt 	i915_user_irq_get(dev);
268c0e09200SDave Airlie 	DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
269c0e09200SDave Airlie 		    READ_BREADCRUMB(dev_priv) >= irq_nr);
270ed4cb414SEric Anholt 	i915_user_irq_put(dev);
271c0e09200SDave Airlie 
272c0e09200SDave Airlie 	if (ret == -EBUSY) {
273c0e09200SDave Airlie 		DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
274c0e09200SDave Airlie 			  READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
275c0e09200SDave Airlie 	}
276c0e09200SDave Airlie 
277c99b058fSKristian Høgsberg 	if (dev_priv->sarea_priv)
278c99b058fSKristian Høgsberg 		dev_priv->sarea_priv->last_dispatch =
279c99b058fSKristian Høgsberg 			READ_BREADCRUMB(dev_priv);
280c0e09200SDave Airlie 
281c0e09200SDave Airlie 	return ret;
282c0e09200SDave Airlie }
283c0e09200SDave Airlie 
284c0e09200SDave Airlie /* Needs the lock as it touches the ring.
285c0e09200SDave Airlie  */
286c0e09200SDave Airlie int i915_irq_emit(struct drm_device *dev, void *data,
287c0e09200SDave Airlie 			 struct drm_file *file_priv)
288c0e09200SDave Airlie {
289c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
290c0e09200SDave Airlie 	drm_i915_irq_emit_t *emit = data;
291c0e09200SDave Airlie 	int result;
292c0e09200SDave Airlie 
293546b0974SEric Anholt 	RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
294c0e09200SDave Airlie 
295c0e09200SDave Airlie 	if (!dev_priv) {
296c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
297c0e09200SDave Airlie 		return -EINVAL;
298c0e09200SDave Airlie 	}
299546b0974SEric Anholt 	mutex_lock(&dev->struct_mutex);
300c0e09200SDave Airlie 	result = i915_emit_irq(dev);
301546b0974SEric Anholt 	mutex_unlock(&dev->struct_mutex);
302c0e09200SDave Airlie 
303c0e09200SDave Airlie 	if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) {
304c0e09200SDave Airlie 		DRM_ERROR("copy_to_user\n");
305c0e09200SDave Airlie 		return -EFAULT;
306c0e09200SDave Airlie 	}
307c0e09200SDave Airlie 
308c0e09200SDave Airlie 	return 0;
309c0e09200SDave Airlie }
310c0e09200SDave Airlie 
311c0e09200SDave Airlie /* Doesn't need the hardware lock.
312c0e09200SDave Airlie  */
313c0e09200SDave Airlie int i915_irq_wait(struct drm_device *dev, void *data,
314c0e09200SDave Airlie 			 struct drm_file *file_priv)
315c0e09200SDave Airlie {
316c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
317c0e09200SDave Airlie 	drm_i915_irq_wait_t *irqwait = data;
318c0e09200SDave Airlie 
319c0e09200SDave Airlie 	if (!dev_priv) {
320c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
321c0e09200SDave Airlie 		return -EINVAL;
322c0e09200SDave Airlie 	}
323c0e09200SDave Airlie 
324c0e09200SDave Airlie 	return i915_wait_irq(dev, irqwait->irq_seq);
325c0e09200SDave Airlie }
326c0e09200SDave Airlie 
32742f52ef8SKeith Packard /* Called from drm generic code, passed 'crtc' which
32842f52ef8SKeith Packard  * we use as a pipe index
32942f52ef8SKeith Packard  */
33042f52ef8SKeith Packard int i915_enable_vblank(struct drm_device *dev, int pipe)
3310a3e67a4SJesse Barnes {
3320a3e67a4SJesse Barnes 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
3330a3e67a4SJesse Barnes 	u32	pipestat_reg = 0;
3340a3e67a4SJesse Barnes 	u32	pipestat;
335e9d21d7fSKeith Packard 	u32	interrupt = 0;
336e9d21d7fSKeith Packard 	unsigned long irqflags;
3370a3e67a4SJesse Barnes 
3380a3e67a4SJesse Barnes 	switch (pipe) {
3390a3e67a4SJesse Barnes 	case 0:
3400a3e67a4SJesse Barnes 		pipestat_reg = PIPEASTAT;
341e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
3420a3e67a4SJesse Barnes 		break;
3430a3e67a4SJesse Barnes 	case 1:
3440a3e67a4SJesse Barnes 		pipestat_reg = PIPEBSTAT;
345e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
3460a3e67a4SJesse Barnes 		break;
3470a3e67a4SJesse Barnes 	default:
3480a3e67a4SJesse Barnes 		DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
3490a3e67a4SJesse Barnes 			  pipe);
350e9d21d7fSKeith Packard 		return 0;
3510a3e67a4SJesse Barnes 	}
3520a3e67a4SJesse Barnes 
353e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
354053d7f24SEric Anholt 	/* Enabling vblank events in IMR comes before PIPESTAT write, or
355053d7f24SEric Anholt 	 * there's a race where the PIPESTAT vblank bit gets set to 1, so
356053d7f24SEric Anholt 	 * the OR of enabled PIPESTAT bits goes to 1, so the PIPExEVENT in
357053d7f24SEric Anholt 	 * ISR flashes to 1, but the IIR bit doesn't get set to 1 because
358053d7f24SEric Anholt 	 * IMR masks it.  It doesn't ever get set after we clear the masking
359053d7f24SEric Anholt 	 * in IMR because the ISR bit is edge, not level-triggered, on the
360053d7f24SEric Anholt 	 * OR of PIPESTAT bits.
361053d7f24SEric Anholt 	 */
362053d7f24SEric Anholt 	i915_enable_irq(dev_priv, interrupt);
3630a3e67a4SJesse Barnes 	pipestat = I915_READ(pipestat_reg);
3640a3e67a4SJesse Barnes 	if (IS_I965G(dev))
3650a3e67a4SJesse Barnes 		pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE;
3660a3e67a4SJesse Barnes 	else
3670a3e67a4SJesse Barnes 		pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE;
3680a3e67a4SJesse Barnes 	/* Clear any stale interrupt status */
3690a3e67a4SJesse Barnes 	pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
3700a3e67a4SJesse Barnes 		     PIPE_VBLANK_INTERRUPT_STATUS);
3710a3e67a4SJesse Barnes 	I915_WRITE(pipestat_reg, pipestat);
372e9d21d7fSKeith Packard 	(void) I915_READ(pipestat_reg);	/* Posting read */
373e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
3740a3e67a4SJesse Barnes 
3750a3e67a4SJesse Barnes 	return 0;
3760a3e67a4SJesse Barnes }
3770a3e67a4SJesse Barnes 
37842f52ef8SKeith Packard /* Called from drm generic code, passed 'crtc' which
37942f52ef8SKeith Packard  * we use as a pipe index
38042f52ef8SKeith Packard  */
38142f52ef8SKeith Packard void i915_disable_vblank(struct drm_device *dev, int pipe)
3820a3e67a4SJesse Barnes {
3830a3e67a4SJesse Barnes 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
3840a3e67a4SJesse Barnes 	u32	pipestat_reg = 0;
3850a3e67a4SJesse Barnes 	u32	pipestat;
386e9d21d7fSKeith Packard 	u32	interrupt = 0;
387e9d21d7fSKeith Packard 	unsigned long irqflags;
3880a3e67a4SJesse Barnes 
3890a3e67a4SJesse Barnes 	switch (pipe) {
3900a3e67a4SJesse Barnes 	case 0:
3910a3e67a4SJesse Barnes 		pipestat_reg = PIPEASTAT;
392e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
3930a3e67a4SJesse Barnes 		break;
3940a3e67a4SJesse Barnes 	case 1:
3950a3e67a4SJesse Barnes 		pipestat_reg = PIPEBSTAT;
396e9d21d7fSKeith Packard 		interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
3970a3e67a4SJesse Barnes 		break;
3980a3e67a4SJesse Barnes 	default:
3990a3e67a4SJesse Barnes 		DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
4000a3e67a4SJesse Barnes 			  pipe);
401e9d21d7fSKeith Packard 		return;
4020a3e67a4SJesse Barnes 		break;
4030a3e67a4SJesse Barnes 	}
4040a3e67a4SJesse Barnes 
405e9d21d7fSKeith Packard 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
406e9d21d7fSKeith Packard 	i915_disable_irq(dev_priv, interrupt);
4070a3e67a4SJesse Barnes 	pipestat = I915_READ(pipestat_reg);
4080a3e67a4SJesse Barnes 	pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
4090a3e67a4SJesse Barnes 		      PIPE_VBLANK_INTERRUPT_ENABLE);
4100a3e67a4SJesse Barnes 	/* Clear any stale interrupt status */
4110a3e67a4SJesse Barnes 	pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
4120a3e67a4SJesse Barnes 		     PIPE_VBLANK_INTERRUPT_STATUS);
4130a3e67a4SJesse Barnes 	I915_WRITE(pipestat_reg, pipestat);
414e9d21d7fSKeith Packard 	(void) I915_READ(pipestat_reg);	/* Posting read */
415e9d21d7fSKeith Packard 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
4160a3e67a4SJesse Barnes }
4170a3e67a4SJesse Barnes 
418c0e09200SDave Airlie /* Set the vblank monitor pipe
419c0e09200SDave Airlie  */
420c0e09200SDave Airlie int i915_vblank_pipe_set(struct drm_device *dev, void *data,
421c0e09200SDave Airlie 			 struct drm_file *file_priv)
422c0e09200SDave Airlie {
423c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
424c0e09200SDave Airlie 
425c0e09200SDave Airlie 	if (!dev_priv) {
426c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
427c0e09200SDave Airlie 		return -EINVAL;
428c0e09200SDave Airlie 	}
429c0e09200SDave Airlie 
430c0e09200SDave Airlie 	return 0;
431c0e09200SDave Airlie }
432c0e09200SDave Airlie 
433c0e09200SDave Airlie int i915_vblank_pipe_get(struct drm_device *dev, void *data,
434c0e09200SDave Airlie 			 struct drm_file *file_priv)
435c0e09200SDave Airlie {
436c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = dev->dev_private;
437c0e09200SDave Airlie 	drm_i915_vblank_pipe_t *pipe = data;
438c0e09200SDave Airlie 
439c0e09200SDave Airlie 	if (!dev_priv) {
440c0e09200SDave Airlie 		DRM_ERROR("called with no initialization\n");
441c0e09200SDave Airlie 		return -EINVAL;
442c0e09200SDave Airlie 	}
443c0e09200SDave Airlie 
4440a3e67a4SJesse Barnes 	pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
445c0e09200SDave Airlie 
446c0e09200SDave Airlie 	return 0;
447c0e09200SDave Airlie }
448c0e09200SDave Airlie 
449c0e09200SDave Airlie /**
450c0e09200SDave Airlie  * Schedule buffer swap at given vertical blank.
451c0e09200SDave Airlie  */
452c0e09200SDave Airlie int i915_vblank_swap(struct drm_device *dev, void *data,
453c0e09200SDave Airlie 		     struct drm_file *file_priv)
454c0e09200SDave Airlie {
455bd95e0a4SEric Anholt 	/* The delayed swap mechanism was fundamentally racy, and has been
456bd95e0a4SEric Anholt 	 * removed.  The model was that the client requested a delayed flip/swap
457bd95e0a4SEric Anholt 	 * from the kernel, then waited for vblank before continuing to perform
458bd95e0a4SEric Anholt 	 * rendering.  The problem was that the kernel might wake the client
459bd95e0a4SEric Anholt 	 * up before it dispatched the vblank swap (since the lock has to be
460bd95e0a4SEric Anholt 	 * held while touching the ringbuffer), in which case the client would
461bd95e0a4SEric Anholt 	 * clear and start the next frame before the swap occurred, and
462bd95e0a4SEric Anholt 	 * flicker would occur in addition to likely missing the vblank.
463bd95e0a4SEric Anholt 	 *
464bd95e0a4SEric Anholt 	 * In the absence of this ioctl, userland falls back to a correct path
465bd95e0a4SEric Anholt 	 * of waiting for a vblank, then dispatching the swap on its own.
466bd95e0a4SEric Anholt 	 * Context switching to userland and back is plenty fast enough for
467bd95e0a4SEric Anholt 	 * meeting the requirements of vblank swapping.
4680a3e67a4SJesse Barnes 	 */
469c0e09200SDave Airlie 	return -EINVAL;
470c0e09200SDave Airlie }
471c0e09200SDave Airlie 
472c0e09200SDave Airlie /* drm_dma.h hooks
473c0e09200SDave Airlie */
474c0e09200SDave Airlie void i915_driver_irq_preinstall(struct drm_device * dev)
475c0e09200SDave Airlie {
476c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
477c0e09200SDave Airlie 
4780a3e67a4SJesse Barnes 	I915_WRITE(HWSTAM, 0xeffe);
4790a3e67a4SJesse Barnes 	I915_WRITE(IMR, 0xffffffff);
480ed4cb414SEric Anholt 	I915_WRITE(IER, 0x0);
481c0e09200SDave Airlie }
482c0e09200SDave Airlie 
4830a3e67a4SJesse Barnes int i915_driver_irq_postinstall(struct drm_device *dev)
484c0e09200SDave Airlie {
485c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
4860a3e67a4SJesse Barnes 	int ret, num_pipes = 2;
487c0e09200SDave Airlie 
488ed4cb414SEric Anholt 	/* Set initial unmasked IRQs to just the selected vblank pipes. */
489ed4cb414SEric Anholt 	dev_priv->irq_mask_reg = ~0;
4900a3e67a4SJesse Barnes 
4910a3e67a4SJesse Barnes 	ret = drm_vblank_init(dev, num_pipes);
4920a3e67a4SJesse Barnes 	if (ret)
4930a3e67a4SJesse Barnes 		return ret;
4940a3e67a4SJesse Barnes 
4950a3e67a4SJesse Barnes 	dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
496ed4cb414SEric Anholt 	dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
497ed4cb414SEric Anholt 	dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
498ed4cb414SEric Anholt 
4990a3e67a4SJesse Barnes 	dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
5000a3e67a4SJesse Barnes 
5018ee1c3dbSMatthew Garrett 	dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK;
5028ee1c3dbSMatthew Garrett 
503ed4cb414SEric Anholt 	I915_WRITE(IMR, dev_priv->irq_mask_reg);
504ed4cb414SEric Anholt 	I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
505ed4cb414SEric Anholt 	(void) I915_READ(IER);
506ed4cb414SEric Anholt 
5078ee1c3dbSMatthew Garrett 	opregion_enable_asle(dev);
508c0e09200SDave Airlie 	DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
5090a3e67a4SJesse Barnes 
5100a3e67a4SJesse Barnes 	return 0;
511c0e09200SDave Airlie }
512c0e09200SDave Airlie 
513c0e09200SDave Airlie void i915_driver_irq_uninstall(struct drm_device * dev)
514c0e09200SDave Airlie {
515c0e09200SDave Airlie 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
5160a3e67a4SJesse Barnes 	u32 temp;
517c0e09200SDave Airlie 
518c0e09200SDave Airlie 	if (!dev_priv)
519c0e09200SDave Airlie 		return;
520c0e09200SDave Airlie 
5210a3e67a4SJesse Barnes 	dev_priv->vblank_pipe = 0;
5220a3e67a4SJesse Barnes 
5230a3e67a4SJesse Barnes 	I915_WRITE(HWSTAM, 0xffffffff);
5240a3e67a4SJesse Barnes 	I915_WRITE(IMR, 0xffffffff);
525ed4cb414SEric Anholt 	I915_WRITE(IER, 0x0);
526c0e09200SDave Airlie 
5270a3e67a4SJesse Barnes 	temp = I915_READ(PIPEASTAT);
5280a3e67a4SJesse Barnes 	I915_WRITE(PIPEASTAT, temp);
5290a3e67a4SJesse Barnes 	temp = I915_READ(PIPEBSTAT);
5300a3e67a4SJesse Barnes 	I915_WRITE(PIPEBSTAT, temp);
531ed4cb414SEric Anholt 	temp = I915_READ(IIR);
532ed4cb414SEric Anholt 	I915_WRITE(IIR, temp);
533c0e09200SDave Airlie }
534