xref: /onnv-gate/usr/src/uts/intel/io/drm/i915_irq.c (revision 11359:522bdb2b850d)
13446Smrj /* BEGIN CSTYLED */
23446Smrj 
33446Smrj /* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
43446Smrj  */
54194Szw161486 /*
63446Smrj  * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
711260SMiao.Chen@Sun.COM  * Copyright (c) 2009, Intel Corporation.
83446Smrj  * All Rights Reserved.
93446Smrj  *
103446Smrj  * Permission is hereby granted, free of charge, to any person obtaining a
113446Smrj  * copy of this software and associated documentation files (the
123446Smrj  * "Software"), to deal in the Software without restriction, including
133446Smrj  * without limitation the rights to use, copy, modify, merge, publish,
143446Smrj  * distribute, sub license, and/or sell copies of the Software, and to
153446Smrj  * permit persons to whom the Software is furnished to do so, subject to
163446Smrj  * the following conditions:
173446Smrj  *
183446Smrj  * The above copyright notice and this permission notice (including the
193446Smrj  * next paragraph) shall be included in all copies or substantial portions
203446Smrj  * of the Software.
213446Smrj  *
223446Smrj  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
233446Smrj  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
243446Smrj  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
253446Smrj  * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
263446Smrj  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
273446Smrj  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
283446Smrj  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
293446Smrj  *
304194Szw161486  */
314194Szw161486 
324194Szw161486 /*
338832SMiao.Chen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
344194Szw161486  * Use is subject to license terms.
354194Szw161486  */
363446Smrj 
373446Smrj #include "drmP.h"
383446Smrj #include "drm.h"
393446Smrj #include "i915_drm.h"
403446Smrj #include "i915_drv.h"
413446Smrj 
424194Szw161486 
433446Smrj #define MAX_NOPID ((u32)~0)
444194Szw161486 
4511260SMiao.Chen@Sun.COM /**
4611260SMiao.Chen@Sun.COM  * Interrupts that are always left unmasked.
4711260SMiao.Chen@Sun.COM  *
4811260SMiao.Chen@Sun.COM  * Since pipe events are edge-triggered from the PIPESTAT register to IIR,
4911260SMiao.Chen@Sun.COM  * we leave them always unmasked in IMR and then control enabling them through
5011260SMiao.Chen@Sun.COM  * PIPESTAT alone.
518832SMiao.Chen@Sun.COM  */
5211260SMiao.Chen@Sun.COM 
5311260SMiao.Chen@Sun.COM #define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT |		 \
5411260SMiao.Chen@Sun.COM 				   I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
5511260SMiao.Chen@Sun.COM 				   I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
5611260SMiao.Chen@Sun.COM 				   I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
5711260SMiao.Chen@Sun.COM 
5811260SMiao.Chen@Sun.COM /** Interrupts that we mask and unmask at runtime. */
5911260SMiao.Chen@Sun.COM #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
6011260SMiao.Chen@Sun.COM 
6111260SMiao.Chen@Sun.COM /** These are all of the interrupts used by the driver */
6211260SMiao.Chen@Sun.COM #define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \
6311260SMiao.Chen@Sun.COM 				    I915_INTERRUPT_ENABLE_VAR)
648832SMiao.Chen@Sun.COM 
65*11359SMiao.Chen@Sun.COM void
igdng_enable_irq(drm_i915_private_t * dev_priv,u32 mask,int gfx_irq)66*11359SMiao.Chen@Sun.COM igdng_enable_irq(drm_i915_private_t *dev_priv, u32 mask, int gfx_irq)
67*11359SMiao.Chen@Sun.COM {
68*11359SMiao.Chen@Sun.COM 	if (gfx_irq && ((dev_priv->gt_irq_mask_reg & mask) != 0)) {
69*11359SMiao.Chen@Sun.COM 		dev_priv->gt_irq_mask_reg &= ~mask;
70*11359SMiao.Chen@Sun.COM 		I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
71*11359SMiao.Chen@Sun.COM 		(void) I915_READ(GTIMR);
72*11359SMiao.Chen@Sun.COM 	} else if ((dev_priv->irq_mask_reg & mask) != 0) {
73*11359SMiao.Chen@Sun.COM 		dev_priv->irq_mask_reg &= ~mask;
74*11359SMiao.Chen@Sun.COM 		I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
75*11359SMiao.Chen@Sun.COM 		(void) I915_READ(DEIMR);
76*11359SMiao.Chen@Sun.COM 
77*11359SMiao.Chen@Sun.COM 	}
78*11359SMiao.Chen@Sun.COM }
79*11359SMiao.Chen@Sun.COM 
80*11359SMiao.Chen@Sun.COM static inline void
igdng_disable_irq(drm_i915_private_t * dev_priv,u32 mask,int gfx_irq)81*11359SMiao.Chen@Sun.COM igdng_disable_irq(drm_i915_private_t *dev_priv, u32 mask, int gfx_irq)
82*11359SMiao.Chen@Sun.COM {
83*11359SMiao.Chen@Sun.COM 	if (gfx_irq && ((dev_priv->gt_irq_mask_reg & mask) != mask)) {
84*11359SMiao.Chen@Sun.COM 		dev_priv->gt_irq_mask_reg |= mask;
85*11359SMiao.Chen@Sun.COM 		I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
86*11359SMiao.Chen@Sun.COM 		(void) I915_READ(GTIMR);
87*11359SMiao.Chen@Sun.COM 	} else if ((dev_priv->irq_mask_reg & mask) != mask) {
88*11359SMiao.Chen@Sun.COM 		dev_priv->irq_mask_reg |= mask;
89*11359SMiao.Chen@Sun.COM 		I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
90*11359SMiao.Chen@Sun.COM 		(void) I915_READ(DEIMR);
91*11359SMiao.Chen@Sun.COM 	}
92*11359SMiao.Chen@Sun.COM }
93*11359SMiao.Chen@Sun.COM 
94*11359SMiao.Chen@Sun.COM /* For display hotplug interrupt */
95*11359SMiao.Chen@Sun.COM void
igdng_enable_display_irq(drm_i915_private_t * dev_priv,u32 mask)96*11359SMiao.Chen@Sun.COM igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
97*11359SMiao.Chen@Sun.COM {
98*11359SMiao.Chen@Sun.COM        if ((dev_priv->irq_mask_reg & mask) != 0) {
99*11359SMiao.Chen@Sun.COM                dev_priv->irq_mask_reg &= ~mask;
100*11359SMiao.Chen@Sun.COM                I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
101*11359SMiao.Chen@Sun.COM                (void) I915_READ(DEIMR);
102*11359SMiao.Chen@Sun.COM        }
103*11359SMiao.Chen@Sun.COM }
104*11359SMiao.Chen@Sun.COM 
105*11359SMiao.Chen@Sun.COM #if 0
106*11359SMiao.Chen@Sun.COM static inline void
107*11359SMiao.Chen@Sun.COM igdng_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
108*11359SMiao.Chen@Sun.COM {
109*11359SMiao.Chen@Sun.COM        if ((dev_priv->irq_mask_reg & mask) != mask) {
110*11359SMiao.Chen@Sun.COM                dev_priv->irq_mask_reg |= mask;
111*11359SMiao.Chen@Sun.COM                I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
112*11359SMiao.Chen@Sun.COM                (void) I915_READ(DEIMR);
113*11359SMiao.Chen@Sun.COM        }
114*11359SMiao.Chen@Sun.COM }
115*11359SMiao.Chen@Sun.COM #endif
116*11359SMiao.Chen@Sun.COM 
1178832SMiao.Chen@Sun.COM static inline void
i915_enable_irq(drm_i915_private_t * dev_priv,uint32_t mask)1188832SMiao.Chen@Sun.COM i915_enable_irq(drm_i915_private_t *dev_priv, uint32_t mask)
1198832SMiao.Chen@Sun.COM {
12011260SMiao.Chen@Sun.COM         if ((dev_priv->irq_mask_reg & mask) != 0) {
12111260SMiao.Chen@Sun.COM                 dev_priv->irq_mask_reg &= ~mask;
12211260SMiao.Chen@Sun.COM                 I915_WRITE(IMR, dev_priv->irq_mask_reg);
12311260SMiao.Chen@Sun.COM                 (void) I915_READ(IMR);
12411260SMiao.Chen@Sun.COM         }
1258832SMiao.Chen@Sun.COM }
1268832SMiao.Chen@Sun.COM 
1278832SMiao.Chen@Sun.COM static inline void
i915_disable_irq(drm_i915_private_t * dev_priv,uint32_t mask)1288832SMiao.Chen@Sun.COM i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask)
1298832SMiao.Chen@Sun.COM {
1308832SMiao.Chen@Sun.COM 	if ((dev_priv->irq_mask_reg & mask) != mask) {
13111260SMiao.Chen@Sun.COM                 dev_priv->irq_mask_reg |= mask;
13211260SMiao.Chen@Sun.COM                 I915_WRITE(IMR, dev_priv->irq_mask_reg);
13311260SMiao.Chen@Sun.COM                 (void) I915_READ(IMR);
13411260SMiao.Chen@Sun.COM         }
13511260SMiao.Chen@Sun.COM }
13611260SMiao.Chen@Sun.COM 
13711260SMiao.Chen@Sun.COM static inline uint32_t
i915_pipestat(int pipe)13811260SMiao.Chen@Sun.COM i915_pipestat(int pipe)
13911260SMiao.Chen@Sun.COM {
14011260SMiao.Chen@Sun.COM 	if (pipe == 0)
14111260SMiao.Chen@Sun.COM 		return PIPEASTAT;
14211260SMiao.Chen@Sun.COM 	if (pipe == 1)
14311260SMiao.Chen@Sun.COM 		return PIPEBSTAT;
14411260SMiao.Chen@Sun.COM 	return 0;
14511260SMiao.Chen@Sun.COM }
14611260SMiao.Chen@Sun.COM 
14711260SMiao.Chen@Sun.COM void
i915_enable_pipestat(drm_i915_private_t * dev_priv,int pipe,uint32_t mask)14811260SMiao.Chen@Sun.COM i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, uint32_t mask)
14911260SMiao.Chen@Sun.COM {
15011260SMiao.Chen@Sun.COM 	if ((dev_priv->pipestat[pipe] & mask) != mask) {
15111260SMiao.Chen@Sun.COM 		u32 reg = i915_pipestat(pipe);
15211260SMiao.Chen@Sun.COM 
15311260SMiao.Chen@Sun.COM 		dev_priv->pipestat[pipe] |= mask;
15411260SMiao.Chen@Sun.COM 		/* Enable the interrupt, clear any pending status */
15511260SMiao.Chen@Sun.COM 		I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16));
15611260SMiao.Chen@Sun.COM 		(void) I915_READ(reg);
1578832SMiao.Chen@Sun.COM 	}
1588832SMiao.Chen@Sun.COM }
1598832SMiao.Chen@Sun.COM 
16011260SMiao.Chen@Sun.COM void
i915_disable_pipestat(drm_i915_private_t * dev_priv,int pipe,u32 mask)16111260SMiao.Chen@Sun.COM i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
16211260SMiao.Chen@Sun.COM {
16311260SMiao.Chen@Sun.COM 	if ((dev_priv->pipestat[pipe] & mask) != 0) {
16411260SMiao.Chen@Sun.COM 		u32 reg = i915_pipestat(pipe);
1658832SMiao.Chen@Sun.COM 
16611260SMiao.Chen@Sun.COM 		dev_priv->pipestat[pipe] &= ~mask;
16711260SMiao.Chen@Sun.COM 		I915_WRITE(reg, dev_priv->pipestat[pipe]);
16811260SMiao.Chen@Sun.COM 		(void) I915_READ(reg);
16911260SMiao.Chen@Sun.COM 	}
1708832SMiao.Chen@Sun.COM }
1718832SMiao.Chen@Sun.COM 
1728832SMiao.Chen@Sun.COM /**
1738832SMiao.Chen@Sun.COM  * i915_pipe_enabled - check if a pipe is enabled
1748832SMiao.Chen@Sun.COM  * @dev: DRM device
1758832SMiao.Chen@Sun.COM  * @pipe: pipe to check
1768832SMiao.Chen@Sun.COM  *
1778832SMiao.Chen@Sun.COM  * Reading certain registers when the pipe is disabled can hang the chip.
1788832SMiao.Chen@Sun.COM  * Use this routine to make sure the PLL is running and the pipe is active
1798832SMiao.Chen@Sun.COM  * before reading such registers if unsure.
1808832SMiao.Chen@Sun.COM  */
1818832SMiao.Chen@Sun.COM static int
i915_pipe_enabled(struct drm_device * dev,int pipe)1828832SMiao.Chen@Sun.COM i915_pipe_enabled(struct drm_device *dev, int pipe)
1838832SMiao.Chen@Sun.COM {
1848832SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
1858832SMiao.Chen@Sun.COM 	unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF;
1868832SMiao.Chen@Sun.COM 
1878832SMiao.Chen@Sun.COM 	if (I915_READ(pipeconf) & PIPEACONF_ENABLE)
1888832SMiao.Chen@Sun.COM 		return 1;
1898832SMiao.Chen@Sun.COM 
1908832SMiao.Chen@Sun.COM 	return 0;
1918832SMiao.Chen@Sun.COM }
1928832SMiao.Chen@Sun.COM 
i915_get_vblank_counter(struct drm_device * dev,int pipe)19311260SMiao.Chen@Sun.COM u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
1948832SMiao.Chen@Sun.COM {
1958832SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
1968832SMiao.Chen@Sun.COM 	unsigned long high_frame;
1978832SMiao.Chen@Sun.COM 	unsigned long low_frame;
1988832SMiao.Chen@Sun.COM 	u32 high1, high2, low, count;
1998832SMiao.Chen@Sun.COM 
2008832SMiao.Chen@Sun.COM 	high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
2018832SMiao.Chen@Sun.COM 	low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
2028832SMiao.Chen@Sun.COM 
2038832SMiao.Chen@Sun.COM 	if (!i915_pipe_enabled(dev, pipe)) {
20411260SMiao.Chen@Sun.COM 	    DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
2058832SMiao.Chen@Sun.COM 	    return 0;
2068832SMiao.Chen@Sun.COM 	}
2078832SMiao.Chen@Sun.COM 
2088832SMiao.Chen@Sun.COM 	/*
2098832SMiao.Chen@Sun.COM 	 * High & low register fields aren't synchronized, so make sure
2108832SMiao.Chen@Sun.COM 	 * we get a low value that's stable across two reads of the high
2118832SMiao.Chen@Sun.COM 	 * register.
2128832SMiao.Chen@Sun.COM 	 */
2138832SMiao.Chen@Sun.COM 	do {
2148832SMiao.Chen@Sun.COM 		high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
2158832SMiao.Chen@Sun.COM 			 PIPE_FRAME_HIGH_SHIFT);
2168832SMiao.Chen@Sun.COM 		low =  ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
2178832SMiao.Chen@Sun.COM 			PIPE_FRAME_LOW_SHIFT);
2188832SMiao.Chen@Sun.COM 		high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
2198832SMiao.Chen@Sun.COM 			 PIPE_FRAME_HIGH_SHIFT);
2208832SMiao.Chen@Sun.COM 	} while (high1 != high2);
2218832SMiao.Chen@Sun.COM 
2228832SMiao.Chen@Sun.COM 	count = (high1 << 8) | low;
2238832SMiao.Chen@Sun.COM 
2248832SMiao.Chen@Sun.COM 	return count;
2258832SMiao.Chen@Sun.COM }
2268832SMiao.Chen@Sun.COM 
22711260SMiao.Chen@Sun.COM /**
22811260SMiao.Chen@Sun.COM  * i915_capture_error_state - capture an error record for later analysis
22911260SMiao.Chen@Sun.COM  * @dev: drm device
23011260SMiao.Chen@Sun.COM  *
23111260SMiao.Chen@Sun.COM  * Should be called when an error is detected (either a hang or an error
23211260SMiao.Chen@Sun.COM  * interrupt) to capture error state from the time of the error.  Fills
23311260SMiao.Chen@Sun.COM  * out a structure which becomes available in debugfs for user level tools
23411260SMiao.Chen@Sun.COM  * to pick up.
23511260SMiao.Chen@Sun.COM  */
i915_capture_error_state(struct drm_device * dev)23611260SMiao.Chen@Sun.COM static void i915_capture_error_state(struct drm_device *dev)
23711260SMiao.Chen@Sun.COM {
23811260SMiao.Chen@Sun.COM 	struct drm_i915_private *dev_priv = dev->dev_private;
23911260SMiao.Chen@Sun.COM 	struct drm_i915_error_state *error;
24011260SMiao.Chen@Sun.COM 
24111260SMiao.Chen@Sun.COM 	spin_lock_irqsave(&dev_priv->error_lock, flags);
24211260SMiao.Chen@Sun.COM #if 0
24311260SMiao.Chen@Sun.COM 	if (dev_priv->first_error)
24411260SMiao.Chen@Sun.COM 		goto out;
24511260SMiao.Chen@Sun.COM #endif
24611260SMiao.Chen@Sun.COM 	error = drm_alloc(sizeof(*error), DRM_MEM_DRIVER);
24711260SMiao.Chen@Sun.COM 	if (!error) {
24811260SMiao.Chen@Sun.COM 		DRM_DEBUG("out ot memory, not capturing error state\n");
24911260SMiao.Chen@Sun.COM 		goto out;
25011260SMiao.Chen@Sun.COM 	}
25111260SMiao.Chen@Sun.COM 
25211260SMiao.Chen@Sun.COM 	error->eir = I915_READ(EIR);
25311260SMiao.Chen@Sun.COM 	error->pgtbl_er = I915_READ(PGTBL_ER);
25411260SMiao.Chen@Sun.COM 	error->pipeastat = I915_READ(PIPEASTAT);
25511260SMiao.Chen@Sun.COM 	error->pipebstat = I915_READ(PIPEBSTAT);
25611260SMiao.Chen@Sun.COM 	error->instpm = I915_READ(INSTPM);
25711260SMiao.Chen@Sun.COM 	if (!IS_I965G(dev)) {
25811260SMiao.Chen@Sun.COM 		error->ipeir = I915_READ(IPEIR);
25911260SMiao.Chen@Sun.COM 		error->ipehr = I915_READ(IPEHR);
26011260SMiao.Chen@Sun.COM 		error->instdone = I915_READ(INSTDONE);
26111260SMiao.Chen@Sun.COM 		error->acthd = I915_READ(ACTHD);
26211260SMiao.Chen@Sun.COM 	} else {
26311260SMiao.Chen@Sun.COM 		error->ipeir = I915_READ(IPEIR_I965);
26411260SMiao.Chen@Sun.COM 		error->ipehr = I915_READ(IPEHR_I965);
26511260SMiao.Chen@Sun.COM 		error->instdone = I915_READ(INSTDONE_I965);
26611260SMiao.Chen@Sun.COM 		error->instps = I915_READ(INSTPS);
26711260SMiao.Chen@Sun.COM 		error->instdone1 = I915_READ(INSTDONE1);
26811260SMiao.Chen@Sun.COM 		error->acthd = I915_READ(ACTHD_I965);
26911260SMiao.Chen@Sun.COM 	}
27011260SMiao.Chen@Sun.COM 
27111260SMiao.Chen@Sun.COM 	(void) uniqtime(&error->time);
27211260SMiao.Chen@Sun.COM 
27311260SMiao.Chen@Sun.COM 	dev_priv->first_error = error;
27411260SMiao.Chen@Sun.COM 
27511260SMiao.Chen@Sun.COM 	DRM_DEBUG("Time: %ld s %ld us\n", error->time.tv_sec,
27611260SMiao.Chen@Sun.COM 		   error->time.tv_usec);
27711260SMiao.Chen@Sun.COM 	DRM_DEBUG("EIR: 0x%08x\n", error->eir);
27811260SMiao.Chen@Sun.COM 	DRM_DEBUG("  PGTBL_ER: 0x%08x\n", error->pgtbl_er);
27911260SMiao.Chen@Sun.COM 	DRM_DEBUG("  INSTPM: 0x%08x\n", error->instpm);
28011260SMiao.Chen@Sun.COM 	DRM_DEBUG("  IPEIR: 0x%08x\n", error->ipeir);
28111260SMiao.Chen@Sun.COM 	DRM_DEBUG("  IPEHR: 0x%08x\n", error->ipehr);
28211260SMiao.Chen@Sun.COM 	DRM_DEBUG("  INSTDONE: 0x%08x\n", error->instdone);
28311260SMiao.Chen@Sun.COM 	DRM_DEBUG("  ACTHD: 0x%08x\n", error->acthd);
28411260SMiao.Chen@Sun.COM 	DRM_DEBUG("  DMA_FADD_P: 0x%08x\n", I915_READ(0x2078));
28511260SMiao.Chen@Sun.COM 	if (IS_I965G(dev)) {
28611260SMiao.Chen@Sun.COM 		DRM_DEBUG("  INSTPS: 0x%08x\n", error->instps);
28711260SMiao.Chen@Sun.COM 		DRM_DEBUG("  INSTDONE1: 0x%08x\n", error->instdone1);
28811260SMiao.Chen@Sun.COM 	}
28911260SMiao.Chen@Sun.COM 	drm_free(error, sizeof(*error), DRM_MEM_DRIVER);
29011260SMiao.Chen@Sun.COM out:
29111260SMiao.Chen@Sun.COM 	spin_unlock_irqrestore(&dev_priv->error_lock, flags);
29211260SMiao.Chen@Sun.COM }
29311260SMiao.Chen@Sun.COM 
29411260SMiao.Chen@Sun.COM /**
29511260SMiao.Chen@Sun.COM  * i915_handle_error - handle an error interrupt
29611260SMiao.Chen@Sun.COM  * @dev: drm device
29711260SMiao.Chen@Sun.COM  *
29811260SMiao.Chen@Sun.COM  * Do some basic checking of regsiter state at error interrupt time and
29911260SMiao.Chen@Sun.COM  * dump it to the syslog.  Also call i915_capture_error_state() to make
30011260SMiao.Chen@Sun.COM  * sure we get a record and make it available in debugfs.  Fire a uevent
30111260SMiao.Chen@Sun.COM  * so userspace knows something bad happened (should trigger collection
30211260SMiao.Chen@Sun.COM  * of a ring dump etc.).
30311260SMiao.Chen@Sun.COM  */
i915_handle_error(struct drm_device * dev)30411260SMiao.Chen@Sun.COM void i915_handle_error(struct drm_device *dev)
30511260SMiao.Chen@Sun.COM {
30611260SMiao.Chen@Sun.COM 	struct drm_i915_private *dev_priv = dev->dev_private;
30711260SMiao.Chen@Sun.COM 	u32 eir = I915_READ(EIR);
30811260SMiao.Chen@Sun.COM 	u32 pipea_stats = I915_READ(PIPEASTAT);
30911260SMiao.Chen@Sun.COM 	u32 pipeb_stats = I915_READ(PIPEBSTAT);
31011260SMiao.Chen@Sun.COM 
31111260SMiao.Chen@Sun.COM 	i915_capture_error_state(dev);
31211260SMiao.Chen@Sun.COM 
31311260SMiao.Chen@Sun.COM 	DRM_DEBUG("render error detected, EIR: 0x%08x\n",
31411260SMiao.Chen@Sun.COM 	       eir);
31511260SMiao.Chen@Sun.COM 
31611260SMiao.Chen@Sun.COM 	if (IS_G4X(dev)) {
31711260SMiao.Chen@Sun.COM 		if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) {
31811260SMiao.Chen@Sun.COM 			u32 ipeir = I915_READ(IPEIR_I965);
31911260SMiao.Chen@Sun.COM 
32011260SMiao.Chen@Sun.COM 			DRM_DEBUG("  IPEIR: 0x%08x\n",
32111260SMiao.Chen@Sun.COM 			       I915_READ(IPEIR_I965));
32211260SMiao.Chen@Sun.COM 			DRM_DEBUG("  IPEHR: 0x%08x\n",
32311260SMiao.Chen@Sun.COM 			       I915_READ(IPEHR_I965));
32411260SMiao.Chen@Sun.COM 			DRM_DEBUG("  INSTDONE: 0x%08x\n",
32511260SMiao.Chen@Sun.COM 			       I915_READ(INSTDONE_I965));
32611260SMiao.Chen@Sun.COM 			DRM_DEBUG("  INSTPS: 0x%08x\n",
32711260SMiao.Chen@Sun.COM 			       I915_READ(INSTPS));
32811260SMiao.Chen@Sun.COM 			DRM_DEBUG("  INSTDONE1: 0x%08x\n",
32911260SMiao.Chen@Sun.COM 			       I915_READ(INSTDONE1));
33011260SMiao.Chen@Sun.COM 			DRM_DEBUG("  ACTHD: 0x%08x\n",
33111260SMiao.Chen@Sun.COM 			       I915_READ(ACTHD_I965));
33211260SMiao.Chen@Sun.COM 			I915_WRITE(IPEIR_I965, ipeir);
33311260SMiao.Chen@Sun.COM 			(void)I915_READ(IPEIR_I965);
33411260SMiao.Chen@Sun.COM 		}
33511260SMiao.Chen@Sun.COM 		if (eir & GM45_ERROR_PAGE_TABLE) {
33611260SMiao.Chen@Sun.COM 			u32 pgtbl_err = I915_READ(PGTBL_ER);
33711260SMiao.Chen@Sun.COM 			DRM_DEBUG("page table error\n");
33811260SMiao.Chen@Sun.COM 			DRM_DEBUG("  PGTBL_ER: 0x%08x\n",
33911260SMiao.Chen@Sun.COM 			       pgtbl_err);
34011260SMiao.Chen@Sun.COM 			I915_WRITE(PGTBL_ER, pgtbl_err);
34111260SMiao.Chen@Sun.COM 			(void)I915_READ(PGTBL_ER);
34211260SMiao.Chen@Sun.COM 		}
34311260SMiao.Chen@Sun.COM 	}
34411260SMiao.Chen@Sun.COM 
34511260SMiao.Chen@Sun.COM 	if (IS_I9XX(dev)) {
34611260SMiao.Chen@Sun.COM 		if (eir & I915_ERROR_PAGE_TABLE) {
34711260SMiao.Chen@Sun.COM 			u32 pgtbl_err = I915_READ(PGTBL_ER);
34811260SMiao.Chen@Sun.COM 			DRM_DEBUG("page table error\n");
34911260SMiao.Chen@Sun.COM 			DRM_DEBUG("PGTBL_ER: 0x%08x\n",
35011260SMiao.Chen@Sun.COM 			       pgtbl_err);
35111260SMiao.Chen@Sun.COM 			I915_WRITE(PGTBL_ER, pgtbl_err);
35211260SMiao.Chen@Sun.COM 			(void)I915_READ(PGTBL_ER);
35311260SMiao.Chen@Sun.COM 		}
35411260SMiao.Chen@Sun.COM 	}
35511260SMiao.Chen@Sun.COM 
35611260SMiao.Chen@Sun.COM 	if (eir & I915_ERROR_MEMORY_REFRESH) {
35711260SMiao.Chen@Sun.COM 		DRM_DEBUG("memory refresh error\n");
35811260SMiao.Chen@Sun.COM 		DRM_DEBUG("PIPEASTAT: 0x%08x\n",
35911260SMiao.Chen@Sun.COM 		       pipea_stats);
36011260SMiao.Chen@Sun.COM 		DRM_DEBUG("PIPEBSTAT: 0x%08x\n",
36111260SMiao.Chen@Sun.COM 		       pipeb_stats);
36211260SMiao.Chen@Sun.COM 		/* pipestat has already been acked */
36311260SMiao.Chen@Sun.COM 	}
36411260SMiao.Chen@Sun.COM 	if (eir & I915_ERROR_INSTRUCTION) {
36511260SMiao.Chen@Sun.COM 		DRM_DEBUG("instruction error\n");
36611260SMiao.Chen@Sun.COM 		DRM_DEBUG("  INSTPM: 0x%08x\n",
36711260SMiao.Chen@Sun.COM 		       I915_READ(INSTPM));
36811260SMiao.Chen@Sun.COM 		if (!IS_I965G(dev)) {
36911260SMiao.Chen@Sun.COM 			u32 ipeir = I915_READ(IPEIR);
37011260SMiao.Chen@Sun.COM 
37111260SMiao.Chen@Sun.COM 			DRM_DEBUG("  IPEIR: 0x%08x\n",
37211260SMiao.Chen@Sun.COM 			       I915_READ(IPEIR));
37311260SMiao.Chen@Sun.COM 			DRM_DEBUG("  IPEHR: 0x%08x\n",
37411260SMiao.Chen@Sun.COM 			       I915_READ(IPEHR));
37511260SMiao.Chen@Sun.COM 			DRM_DEBUG("  INSTDONE: 0x%08x\n",
37611260SMiao.Chen@Sun.COM 			       I915_READ(INSTDONE));
37711260SMiao.Chen@Sun.COM 			DRM_DEBUG("  ACTHD: 0x%08x\n",
37811260SMiao.Chen@Sun.COM 			       I915_READ(ACTHD));
37911260SMiao.Chen@Sun.COM 			I915_WRITE(IPEIR, ipeir);
38011260SMiao.Chen@Sun.COM 			(void)I915_READ(IPEIR);
38111260SMiao.Chen@Sun.COM 		} else {
38211260SMiao.Chen@Sun.COM 			u32 ipeir = I915_READ(IPEIR_I965);
38311260SMiao.Chen@Sun.COM 
38411260SMiao.Chen@Sun.COM 			DRM_DEBUG("  IPEIR: 0x%08x\n",
38511260SMiao.Chen@Sun.COM 			       I915_READ(IPEIR_I965));
38611260SMiao.Chen@Sun.COM 			DRM_DEBUG("  IPEHR: 0x%08x\n",
38711260SMiao.Chen@Sun.COM 			       I915_READ(IPEHR_I965));
38811260SMiao.Chen@Sun.COM 			DRM_DEBUG("  INSTDONE: 0x%08x\n",
38911260SMiao.Chen@Sun.COM 			       I915_READ(INSTDONE_I965));
39011260SMiao.Chen@Sun.COM 			DRM_DEBUG("  INSTPS: 0x%08x\n",
39111260SMiao.Chen@Sun.COM 			       I915_READ(INSTPS));
39211260SMiao.Chen@Sun.COM 			DRM_DEBUG("  INSTDONE1: 0x%08x\n",
39311260SMiao.Chen@Sun.COM 			       I915_READ(INSTDONE1));
39411260SMiao.Chen@Sun.COM 			DRM_DEBUG("  ACTHD: 0x%08x\n",
39511260SMiao.Chen@Sun.COM 			       I915_READ(ACTHD_I965));
39611260SMiao.Chen@Sun.COM 			I915_WRITE(IPEIR_I965, ipeir);
39711260SMiao.Chen@Sun.COM 			(void)I915_READ(IPEIR_I965);
39811260SMiao.Chen@Sun.COM 		}
39911260SMiao.Chen@Sun.COM 	}
40011260SMiao.Chen@Sun.COM 
40111260SMiao.Chen@Sun.COM 	I915_WRITE(EIR, eir);
40211260SMiao.Chen@Sun.COM 	(void)I915_READ(EIR);
40311260SMiao.Chen@Sun.COM 	eir = I915_READ(EIR);
40411260SMiao.Chen@Sun.COM 	if (eir) {
40511260SMiao.Chen@Sun.COM 		/*
40611260SMiao.Chen@Sun.COM 		 * some errors might have become stuck,
40711260SMiao.Chen@Sun.COM 		 * mask them.
40811260SMiao.Chen@Sun.COM 		 */
40911260SMiao.Chen@Sun.COM 		DRM_DEBUG("EIR stuck: 0x%08x, masking\n", eir);
41011260SMiao.Chen@Sun.COM 		I915_WRITE(EMR, I915_READ(EMR) | eir);
41111260SMiao.Chen@Sun.COM 		I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
41211260SMiao.Chen@Sun.COM 	}
41311260SMiao.Chen@Sun.COM 
41411260SMiao.Chen@Sun.COM }
41511260SMiao.Chen@Sun.COM 
gm45_get_vblank_counter(struct drm_device * dev,int pipe)41611260SMiao.Chen@Sun.COM u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
41711260SMiao.Chen@Sun.COM {
41811260SMiao.Chen@Sun.COM        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
41911260SMiao.Chen@Sun.COM        int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
42011260SMiao.Chen@Sun.COM 
42111260SMiao.Chen@Sun.COM        if (!i915_pipe_enabled(dev, pipe)) {
42211260SMiao.Chen@Sun.COM 		DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
42311260SMiao.Chen@Sun.COM                return 0;
42411260SMiao.Chen@Sun.COM        }
42511260SMiao.Chen@Sun.COM 
42611260SMiao.Chen@Sun.COM        return I915_READ(reg);
42711260SMiao.Chen@Sun.COM }
42811260SMiao.Chen@Sun.COM 
igdng_irq_handler(struct drm_device * dev)429*11359SMiao.Chen@Sun.COM irqreturn_t igdng_irq_handler(struct drm_device *dev)
430*11359SMiao.Chen@Sun.COM {
431*11359SMiao.Chen@Sun.COM        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
432*11359SMiao.Chen@Sun.COM        int ret = IRQ_NONE;
433*11359SMiao.Chen@Sun.COM        u32 de_iir, gt_iir, de_ier;
434*11359SMiao.Chen@Sun.COM        u32 new_de_iir, new_gt_iir;
435*11359SMiao.Chen@Sun.COM        int vblank = 0;
436*11359SMiao.Chen@Sun.COM 
437*11359SMiao.Chen@Sun.COM 	/* disable master interrupt before clearing iir  */
438*11359SMiao.Chen@Sun.COM 	de_ier = I915_READ(DEIER);
439*11359SMiao.Chen@Sun.COM 	I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
440*11359SMiao.Chen@Sun.COM 	(void)I915_READ(DEIER);
441*11359SMiao.Chen@Sun.COM 
442*11359SMiao.Chen@Sun.COM        de_iir = I915_READ(DEIIR);
443*11359SMiao.Chen@Sun.COM        gt_iir = I915_READ(GTIIR);
444*11359SMiao.Chen@Sun.COM 
445*11359SMiao.Chen@Sun.COM        for (;;) {
446*11359SMiao.Chen@Sun.COM                if (de_iir == 0 && gt_iir == 0)
447*11359SMiao.Chen@Sun.COM                        break;
448*11359SMiao.Chen@Sun.COM 
449*11359SMiao.Chen@Sun.COM                ret = IRQ_HANDLED;
450*11359SMiao.Chen@Sun.COM 
451*11359SMiao.Chen@Sun.COM                I915_WRITE(DEIIR, de_iir);
452*11359SMiao.Chen@Sun.COM                new_de_iir = I915_READ(DEIIR);
453*11359SMiao.Chen@Sun.COM                I915_WRITE(GTIIR, gt_iir);
454*11359SMiao.Chen@Sun.COM                new_gt_iir = I915_READ(GTIIR);
455*11359SMiao.Chen@Sun.COM 
456*11359SMiao.Chen@Sun.COM         if (dev_priv->sarea_priv) {
457*11359SMiao.Chen@Sun.COM             dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
458*11359SMiao.Chen@Sun.COM 
459*11359SMiao.Chen@Sun.COM 	}
460*11359SMiao.Chen@Sun.COM 
461*11359SMiao.Chen@Sun.COM                if (gt_iir & GT_USER_INTERRUPT) {
462*11359SMiao.Chen@Sun.COM                        dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
463*11359SMiao.Chen@Sun.COM                        DRM_WAKEUP(&dev_priv->irq_queue);
464*11359SMiao.Chen@Sun.COM                }
465*11359SMiao.Chen@Sun.COM                if (de_iir & DE_PIPEA_VBLANK) {
466*11359SMiao.Chen@Sun.COM                        vblank++;
467*11359SMiao.Chen@Sun.COM                        drm_handle_vblank(dev, 0);
468*11359SMiao.Chen@Sun.COM                }
469*11359SMiao.Chen@Sun.COM 
470*11359SMiao.Chen@Sun.COM                if (de_iir & DE_PIPEB_VBLANK) {
471*11359SMiao.Chen@Sun.COM                        vblank++;
472*11359SMiao.Chen@Sun.COM                        drm_handle_vblank(dev, 1);
473*11359SMiao.Chen@Sun.COM                }
474*11359SMiao.Chen@Sun.COM 
475*11359SMiao.Chen@Sun.COM                de_iir = new_de_iir;
476*11359SMiao.Chen@Sun.COM                gt_iir = new_gt_iir;
477*11359SMiao.Chen@Sun.COM        }
478*11359SMiao.Chen@Sun.COM 
479*11359SMiao.Chen@Sun.COM 	I915_WRITE(DEIER, de_ier);
480*11359SMiao.Chen@Sun.COM 	(void)I915_READ(DEIER);
481*11359SMiao.Chen@Sun.COM 
482*11359SMiao.Chen@Sun.COM        return ret;
483*11359SMiao.Chen@Sun.COM }
484*11359SMiao.Chen@Sun.COM 
i915_driver_irq_handler(DRM_IRQ_ARGS)4853446Smrj irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
4863446Smrj {
4878832SMiao.Chen@Sun.COM         drm_device_t *dev = (drm_device_t *) (void *) arg;
4888832SMiao.Chen@Sun.COM         drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
4898832SMiao.Chen@Sun.COM         u32 iir;
4908832SMiao.Chen@Sun.COM         u32 pipea_stats = 0, pipeb_stats = 0;
4918832SMiao.Chen@Sun.COM 	int vblank = 0;
49211260SMiao.Chen@Sun.COM 
493*11359SMiao.Chen@Sun.COM 	if (IS_IGDNG(dev))
494*11359SMiao.Chen@Sun.COM 		return igdng_irq_handler(dev);
495*11359SMiao.Chen@Sun.COM 
4968832SMiao.Chen@Sun.COM 	iir = I915_READ(IIR);
4973446Smrj 
4988832SMiao.Chen@Sun.COM 	if (iir == 0) {
4998832SMiao.Chen@Sun.COM 		return IRQ_NONE;
5004194Szw161486 	}
50111260SMiao.Chen@Sun.COM start:
5028832SMiao.Chen@Sun.COM 
50311260SMiao.Chen@Sun.COM 	if (dev_priv->sarea_priv) {
50411260SMiao.Chen@Sun.COM 		if (dev_priv->hw_status_page)
50511260SMiao.Chen@Sun.COM 	    		dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
5068832SMiao.Chen@Sun.COM 	}
5078832SMiao.Chen@Sun.COM 
5088832SMiao.Chen@Sun.COM 	I915_WRITE(IIR, iir);
5098832SMiao.Chen@Sun.COM 
5108832SMiao.Chen@Sun.COM 	(void) I915_READ(IIR); /* Flush posted writes */
5114194Szw161486 
51211260SMiao.Chen@Sun.COM 
51311260SMiao.Chen@Sun.COM 	if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
51411260SMiao.Chen@Sun.COM 		i915_handle_error(dev);
51511260SMiao.Chen@Sun.COM 
5168832SMiao.Chen@Sun.COM         if (iir & I915_USER_INTERRUPT) {
51711260SMiao.Chen@Sun.COM 		dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
5188832SMiao.Chen@Sun.COM                 DRM_WAKEUP(&dev_priv->irq_queue);
5198832SMiao.Chen@Sun.COM         }
5208832SMiao.Chen@Sun.COM 
52111260SMiao.Chen@Sun.COM         if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
52211260SMiao.Chen@Sun.COM                 pipea_stats = I915_READ(PIPEASTAT);
52311260SMiao.Chen@Sun.COM 
52411260SMiao.Chen@Sun.COM                 /* The vblank interrupt gets enabled even if we didn't ask for
52511260SMiao.Chen@Sun.COM                    it, so make sure it's shut down again */
52611260SMiao.Chen@Sun.COM                 if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A))
52711260SMiao.Chen@Sun.COM                         pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
52811260SMiao.Chen@Sun.COM                                          PIPE_VBLANK_INTERRUPT_ENABLE);
52911260SMiao.Chen@Sun.COM                 else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
53011260SMiao.Chen@Sun.COM                                         PIPE_VBLANK_INTERRUPT_STATUS))
53111260SMiao.Chen@Sun.COM                 {
53211260SMiao.Chen@Sun.COM                         vblank++;
53311260SMiao.Chen@Sun.COM                         drm_handle_vblank(dev, 0);
53411260SMiao.Chen@Sun.COM                 }
5353446Smrj 
53611260SMiao.Chen@Sun.COM                 I915_WRITE(PIPEASTAT, pipea_stats);
53711260SMiao.Chen@Sun.COM         }
53811260SMiao.Chen@Sun.COM         if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
53911260SMiao.Chen@Sun.COM                 pipeb_stats = I915_READ(PIPEBSTAT);
54011260SMiao.Chen@Sun.COM 
54111260SMiao.Chen@Sun.COM                 /* The vblank interrupt gets enabled even if we didn't ask for
54211260SMiao.Chen@Sun.COM                    it, so make sure it's shut down again */
54311260SMiao.Chen@Sun.COM                 if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B))
54411260SMiao.Chen@Sun.COM                         pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
54511260SMiao.Chen@Sun.COM                                          PIPE_VBLANK_INTERRUPT_ENABLE);
54611260SMiao.Chen@Sun.COM                 else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
54711260SMiao.Chen@Sun.COM                                         PIPE_VBLANK_INTERRUPT_STATUS))
54811260SMiao.Chen@Sun.COM                 {
54911260SMiao.Chen@Sun.COM                         vblank++;
55011260SMiao.Chen@Sun.COM                         drm_handle_vblank(dev, 1);
55111260SMiao.Chen@Sun.COM                 }
55211260SMiao.Chen@Sun.COM 
55311260SMiao.Chen@Sun.COM                 I915_WRITE(PIPEBSTAT, pipeb_stats);
55411260SMiao.Chen@Sun.COM         }
55511260SMiao.Chen@Sun.COM        return IRQ_HANDLED;
5568832SMiao.Chen@Sun.COM 
5573446Smrj }
5583446Smrj 
i915_emit_irq(drm_device_t * dev)5594194Szw161486 int i915_emit_irq(drm_device_t * dev)
5603446Smrj {
5614194Szw161486 
5623446Smrj 	drm_i915_private_t *dev_priv = dev->dev_private;
5633446Smrj 	RING_LOCALS;
5643446Smrj 
5653446Smrj 	i915_kernel_lost_context(dev);
56611260SMiao.Chen@Sun.COM 
56711260SMiao.Chen@Sun.COM 	dev_priv->counter++;
56811260SMiao.Chen@Sun.COM 	if (dev_priv->counter > 0x7FFFFFFFUL)
56911260SMiao.Chen@Sun.COM 		dev_priv->counter = 1;
57011260SMiao.Chen@Sun.COM 	if (dev_priv->sarea_priv)
57111260SMiao.Chen@Sun.COM 		dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
5723446Smrj 
57311260SMiao.Chen@Sun.COM #if defined(__i386)
57411260SMiao.Chen@Sun.COM 	if (IS_GM45(dev)) {
57511260SMiao.Chen@Sun.COM 		BEGIN_LP_RING(3);
57611260SMiao.Chen@Sun.COM 		OUT_RING(MI_STORE_DWORD_INDEX);
57711260SMiao.Chen@Sun.COM 		OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
57811260SMiao.Chen@Sun.COM 		OUT_RING(dev_priv->counter);
57911260SMiao.Chen@Sun.COM 		ADVANCE_LP_RING();
58011260SMiao.Chen@Sun.COM 
58111260SMiao.Chen@Sun.COM 		(void) READ_BREADCRUMB(dev_priv);
58211260SMiao.Chen@Sun.COM 		BEGIN_LP_RING(2);
58311260SMiao.Chen@Sun.COM 		OUT_RING(0);
58411260SMiao.Chen@Sun.COM 		OUT_RING(MI_USER_INTERRUPT);
58511260SMiao.Chen@Sun.COM 		ADVANCE_LP_RING();
58611260SMiao.Chen@Sun.COM 	} else {
58711260SMiao.Chen@Sun.COM #endif  /* __i386 */
58811260SMiao.Chen@Sun.COM 	BEGIN_LP_RING(4);
58911260SMiao.Chen@Sun.COM 	OUT_RING(MI_STORE_DWORD_INDEX);
59011260SMiao.Chen@Sun.COM 	OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
59111260SMiao.Chen@Sun.COM 	OUT_RING(dev_priv->counter);
5928832SMiao.Chen@Sun.COM 	OUT_RING(MI_USER_INTERRUPT);
5933446Smrj 	ADVANCE_LP_RING();
59411260SMiao.Chen@Sun.COM #if defined(__i386)
59511260SMiao.Chen@Sun.COM 	}
59611260SMiao.Chen@Sun.COM #endif  /* __i386 */
59711260SMiao.Chen@Sun.COM 
59811260SMiao.Chen@Sun.COM #if defined(__i386)
599*11359SMiao.Chen@Sun.COM 	if (IS_I965GM(dev) || IS_IGDNG(dev) || IS_GM45(dev))
60011260SMiao.Chen@Sun.COM #else
601*11359SMiao.Chen@Sun.COM 	if (IS_I965GM(dev) || IS_IGDNG(dev))
60211260SMiao.Chen@Sun.COM #endif  /* __i386 */
60311260SMiao.Chen@Sun.COM 	{
60411260SMiao.Chen@Sun.COM 		(void) READ_BREADCRUMB(dev_priv);
60511260SMiao.Chen@Sun.COM 		BEGIN_LP_RING(2);
60611260SMiao.Chen@Sun.COM 		OUT_RING(0);
60711260SMiao.Chen@Sun.COM 		OUT_RING(0);
60811260SMiao.Chen@Sun.COM 		ADVANCE_LP_RING();
60911260SMiao.Chen@Sun.COM 		(void) READ_BREADCRUMB(dev_priv);
61011260SMiao.Chen@Sun.COM 	}
6114194Szw161486 
6124194Szw161486 	return dev_priv->counter;
6134194Szw161486 }
6143446Smrj 
i915_user_irq_on(struct drm_device * dev)61511260SMiao.Chen@Sun.COM void i915_user_irq_on(struct drm_device *dev)
6164194Szw161486 {
61711260SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
6184194Szw161486 	spin_lock(&dev_priv->user_irq_lock);
61911260SMiao.Chen@Sun.COM 	if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)){
620*11359SMiao.Chen@Sun.COM                if (IS_IGDNG(dev))
621*11359SMiao.Chen@Sun.COM                        igdng_enable_irq(dev_priv, GT_USER_INTERRUPT, 1);
622*11359SMiao.Chen@Sun.COM                else
623*11359SMiao.Chen@Sun.COM                        i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
6244194Szw161486 	}
6254194Szw161486 	spin_unlock(&dev_priv->user_irq_lock);
6264194Szw161486 
6273446Smrj }
6284194Szw161486 
i915_user_irq_off(struct drm_device * dev)62911260SMiao.Chen@Sun.COM void i915_user_irq_off(struct drm_device *dev)
6304194Szw161486 {
63111260SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
6324194Szw161486 	spin_lock(&dev_priv->user_irq_lock);
63311260SMiao.Chen@Sun.COM 	if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
634*11359SMiao.Chen@Sun.COM                if (IS_IGDNG(dev))
635*11359SMiao.Chen@Sun.COM                        igdng_disable_irq(dev_priv, GT_USER_INTERRUPT, 1);
636*11359SMiao.Chen@Sun.COM                else
637*11359SMiao.Chen@Sun.COM                        i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
6384194Szw161486 	}
6394194Szw161486 	spin_unlock(&dev_priv->user_irq_lock);
6404194Szw161486 }
6414194Szw161486 
6423446Smrj 
i915_wait_irq(drm_device_t * dev,int irq_nr)6433446Smrj static int i915_wait_irq(drm_device_t * dev, int irq_nr)
6443446Smrj {
6453446Smrj 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
6463446Smrj 	int ret = 0;
647*11359SMiao.Chen@Sun.COM 	int wait_time = 0;
6483446Smrj 
64911260SMiao.Chen@Sun.COM 	if (!dev_priv) {
65011260SMiao.Chen@Sun.COM 		DRM_ERROR("called with no initialization\n");
65111260SMiao.Chen@Sun.COM 		return -EINVAL;
65211260SMiao.Chen@Sun.COM 	}
6533446Smrj 
654*11359SMiao.Chen@Sun.COM waitmore:
655*11359SMiao.Chen@Sun.COM 	wait_time++;
6568832SMiao.Chen@Sun.COM 	if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
65711260SMiao.Chen@Sun.COM 		if (dev_priv->sarea_priv) {
6588832SMiao.Chen@Sun.COM 			dev_priv->sarea_priv->last_dispatch =
6598832SMiao.Chen@Sun.COM 				READ_BREADCRUMB(dev_priv);
66011260SMiao.Chen@Sun.COM 		}
6618832SMiao.Chen@Sun.COM 		return 0;
6628832SMiao.Chen@Sun.COM 	}
6638832SMiao.Chen@Sun.COM 	DRM_DEBUG("i915_wait_irq: irq_nr=%d breadcrumb=%d\n", irq_nr, READ_BREADCRUMB(dev_priv));
66411260SMiao.Chen@Sun.COM 	i915_user_irq_on(dev);
6655804Scg149915 	DRM_WAIT_ON(ret, &dev_priv->irq_queue, 3 * DRM_HZ,
6663446Smrj 		    READ_BREADCRUMB(dev_priv) >= irq_nr);
66711260SMiao.Chen@Sun.COM 	i915_user_irq_off(dev);
6683446Smrj 
66911260SMiao.Chen@Sun.COM 	if (ret == EBUSY) {
670*11359SMiao.Chen@Sun.COM 		if (wait_time > 5) {
6718832SMiao.Chen@Sun.COM 		DRM_DEBUG("%d: EBUSY -- rec: %d emitted: %d\n",
6728832SMiao.Chen@Sun.COM 			  ret,
6733446Smrj 			  READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
674*11359SMiao.Chen@Sun.COM 			return ret;
675*11359SMiao.Chen@Sun.COM 		}
676*11359SMiao.Chen@Sun.COM 		goto waitmore;
6773446Smrj 	}
6783446Smrj 
6798832SMiao.Chen@Sun.COM 	if (dev_priv->sarea_priv)
6808832SMiao.Chen@Sun.COM 		dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
6814194Szw161486 
682*11359SMiao.Chen@Sun.COM 	if (ret == EINTR) {
683*11359SMiao.Chen@Sun.COM 		if (wait_time > 5) {
684*11359SMiao.Chen@Sun.COM 			DRM_DEBUG("EINTR wait %d now %d", dev_priv->counter, READ_BREADCRUMB(dev_priv));
685*11359SMiao.Chen@Sun.COM 			return ret;
686*11359SMiao.Chen@Sun.COM 		}
687*11359SMiao.Chen@Sun.COM 		goto waitmore;
688*11359SMiao.Chen@Sun.COM 	}
689*11359SMiao.Chen@Sun.COM 
6904194Szw161486 	return ret;
6914194Szw161486 }
6924194Szw161486 
6934194Szw161486 
6943446Smrj /* Needs the lock as it touches the ring.
6953446Smrj  */
6963446Smrj /*ARGSUSED*/
i915_irq_emit(DRM_IOCTL_ARGS)6973446Smrj int i915_irq_emit(DRM_IOCTL_ARGS)
6983446Smrj {
6993446Smrj 	DRM_DEVICE;
7003446Smrj 	drm_i915_private_t *dev_priv = dev->dev_private;
7013446Smrj 	drm_i915_irq_emit_t emit;
7023446Smrj 	int result;
7033446Smrj 
7045804Scg149915 	LOCK_TEST_WITH_RETURN(dev, fpriv);
7053446Smrj 
7063446Smrj 	if (!dev_priv) {
7073446Smrj 		DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
7085804Scg149915 		return (EINVAL);
7093446Smrj 	}
7103446Smrj 
71111260SMiao.Chen@Sun.COM 
7123446Smrj 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
7133446Smrj 		drm_i915_irq_emit32_t irq_emit32;
7143446Smrj 
7155804Scg149915 		DRM_COPYFROM_WITH_RETURN(&irq_emit32,
7163446Smrj 			(drm_i915_irq_emit32_t __user *) data,
7173446Smrj 			sizeof (drm_i915_irq_emit32_t));
7183446Smrj 		emit.irq_seq = (int __user *)(uintptr_t)irq_emit32.irq_seq;
7193446Smrj 	} else
7205804Scg149915 		DRM_COPYFROM_WITH_RETURN(&emit,
7215804Scg149915 		    (drm_i915_irq_emit_t __user *) data, sizeof(emit));
7223446Smrj 
72311260SMiao.Chen@Sun.COM 	spin_lock(&dev->struct_mutex);
7243446Smrj 	result = i915_emit_irq(dev);
72511260SMiao.Chen@Sun.COM 	spin_unlock(&dev->struct_mutex);
7263446Smrj 
7273446Smrj 	if (DRM_COPY_TO_USER(emit.irq_seq, &result, sizeof(int))) {
7283446Smrj 		DRM_ERROR("copy_to_user\n");
7295804Scg149915 		return (EFAULT);
7303446Smrj 	}
7313446Smrj 
7323446Smrj 	return 0;
7333446Smrj }
7343446Smrj 
7353446Smrj /* Doesn't need the hardware lock.
7363446Smrj  */
7373446Smrj /*ARGSUSED*/
i915_irq_wait(DRM_IOCTL_ARGS)7383446Smrj int i915_irq_wait(DRM_IOCTL_ARGS)
7393446Smrj {
7403446Smrj 	DRM_DEVICE;
7413446Smrj 	drm_i915_private_t *dev_priv = dev->dev_private;
7423446Smrj 	drm_i915_irq_wait_t irqwait;
7433446Smrj 
7443446Smrj 	if (!dev_priv) {
7453446Smrj 		DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
7465804Scg149915 		return (EINVAL);
7473446Smrj 	}
7483446Smrj 
7495804Scg149915 	DRM_COPYFROM_WITH_RETURN(&irqwait,
7505804Scg149915 	    (drm_i915_irq_wait_t __user *) data, sizeof(irqwait));
7513446Smrj 
7523446Smrj 	return i915_wait_irq(dev, irqwait.irq_seq);
7533446Smrj }
7543446Smrj 
igdng_enable_vblank(struct drm_device * dev,int pipe)755*11359SMiao.Chen@Sun.COM static void igdng_enable_vblank(struct drm_device *dev, int pipe)
756*11359SMiao.Chen@Sun.COM {
757*11359SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
758*11359SMiao.Chen@Sun.COM 	u32 vblank;
759*11359SMiao.Chen@Sun.COM 
760*11359SMiao.Chen@Sun.COM 	if (pipe == 0)
761*11359SMiao.Chen@Sun.COM 		vblank = DE_PIPEA_VBLANK;
762*11359SMiao.Chen@Sun.COM 	else
763*11359SMiao.Chen@Sun.COM 		vblank = DE_PIPEB_VBLANK;
764*11359SMiao.Chen@Sun.COM 
765*11359SMiao.Chen@Sun.COM 	if ((dev_priv->de_irq_enable_reg & vblank) == 0) {
766*11359SMiao.Chen@Sun.COM 		igdng_enable_irq(dev_priv, vblank, 0);
767*11359SMiao.Chen@Sun.COM 		dev_priv->de_irq_enable_reg |= vblank;
768*11359SMiao.Chen@Sun.COM 		I915_WRITE(DEIER, dev_priv->de_irq_enable_reg);
769*11359SMiao.Chen@Sun.COM 		(void) I915_READ(DEIER);
770*11359SMiao.Chen@Sun.COM 	}
771*11359SMiao.Chen@Sun.COM }
772*11359SMiao.Chen@Sun.COM 
igdng_disable_vblank(struct drm_device * dev,int pipe)773*11359SMiao.Chen@Sun.COM static void igdng_disable_vblank(struct drm_device *dev, int pipe)
774*11359SMiao.Chen@Sun.COM {
775*11359SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
776*11359SMiao.Chen@Sun.COM 	u32 vblank;
777*11359SMiao.Chen@Sun.COM 
778*11359SMiao.Chen@Sun.COM 	if (pipe == 0)
779*11359SMiao.Chen@Sun.COM 		vblank = DE_PIPEA_VBLANK;
780*11359SMiao.Chen@Sun.COM 	else
781*11359SMiao.Chen@Sun.COM 		vblank = DE_PIPEB_VBLANK;
782*11359SMiao.Chen@Sun.COM 
783*11359SMiao.Chen@Sun.COM 	if ((dev_priv->de_irq_enable_reg & vblank) != 0) {
784*11359SMiao.Chen@Sun.COM 		igdng_disable_irq(dev_priv, vblank, 0);
785*11359SMiao.Chen@Sun.COM 		dev_priv->de_irq_enable_reg &= ~vblank;
786*11359SMiao.Chen@Sun.COM 		I915_WRITE(DEIER, dev_priv->de_irq_enable_reg);
787*11359SMiao.Chen@Sun.COM 		(void) I915_READ(DEIER);
788*11359SMiao.Chen@Sun.COM 	}
789*11359SMiao.Chen@Sun.COM }
790*11359SMiao.Chen@Sun.COM 
i915_enable_vblank(struct drm_device * dev,int pipe)79111260SMiao.Chen@Sun.COM int i915_enable_vblank(struct drm_device *dev, int pipe)
7928832SMiao.Chen@Sun.COM {
7938832SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
79411260SMiao.Chen@Sun.COM 	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
79511260SMiao.Chen@Sun.COM 	u32 pipeconf;
7968832SMiao.Chen@Sun.COM 
79711260SMiao.Chen@Sun.COM 	pipeconf = I915_READ(pipeconf_reg);
79811260SMiao.Chen@Sun.COM 	if (!(pipeconf & PIPEACONF_ENABLE))
79911260SMiao.Chen@Sun.COM 		return -EINVAL;
8008832SMiao.Chen@Sun.COM 
80111260SMiao.Chen@Sun.COM 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
802*11359SMiao.Chen@Sun.COM 	if (IS_IGDNG(dev))
803*11359SMiao.Chen@Sun.COM 		igdng_enable_vblank(dev, pipe);
804*11359SMiao.Chen@Sun.COM 	else if (IS_I965G(dev))
80511260SMiao.Chen@Sun.COM 		i915_enable_pipestat(dev_priv, pipe,
80611260SMiao.Chen@Sun.COM 				     PIPE_START_VBLANK_INTERRUPT_ENABLE);
80711260SMiao.Chen@Sun.COM 	else
80811260SMiao.Chen@Sun.COM 		i915_enable_pipestat(dev_priv, pipe,
80911260SMiao.Chen@Sun.COM 				     PIPE_VBLANK_INTERRUPT_ENABLE);
81011260SMiao.Chen@Sun.COM 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
8118832SMiao.Chen@Sun.COM 
8128832SMiao.Chen@Sun.COM 	return 0;
8138832SMiao.Chen@Sun.COM }
8148832SMiao.Chen@Sun.COM 
i915_disable_vblank(struct drm_device * dev,int pipe)81511260SMiao.Chen@Sun.COM void i915_disable_vblank(struct drm_device *dev, int pipe)
8164194Szw161486 {
8174194Szw161486 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
8188832SMiao.Chen@Sun.COM 
81911260SMiao.Chen@Sun.COM 	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
820*11359SMiao.Chen@Sun.COM 	if (IS_IGDNG(dev))
821*11359SMiao.Chen@Sun.COM 		igdng_disable_vblank(dev, pipe);
822*11359SMiao.Chen@Sun.COM 	else
82311260SMiao.Chen@Sun.COM 	i915_disable_pipestat(dev_priv, pipe,
82411260SMiao.Chen@Sun.COM 			      PIPE_VBLANK_INTERRUPT_ENABLE |
82511260SMiao.Chen@Sun.COM 			      PIPE_START_VBLANK_INTERRUPT_ENABLE);
82611260SMiao.Chen@Sun.COM 	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
8278832SMiao.Chen@Sun.COM }
8288832SMiao.Chen@Sun.COM 
8298832SMiao.Chen@Sun.COM /* Set the vblank monitor pipe
8308832SMiao.Chen@Sun.COM  */
8318832SMiao.Chen@Sun.COM /*ARGSUSED*/
i915_vblank_pipe_set(DRM_IOCTL_ARGS)8328832SMiao.Chen@Sun.COM int i915_vblank_pipe_set(DRM_IOCTL_ARGS)
8338832SMiao.Chen@Sun.COM {
8348832SMiao.Chen@Sun.COM 	DRM_DEVICE;
8358832SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = dev->dev_private;
8368832SMiao.Chen@Sun.COM 
8378832SMiao.Chen@Sun.COM 	if (!dev_priv) {
8388832SMiao.Chen@Sun.COM 		DRM_ERROR("called with no initialization\n");
8398832SMiao.Chen@Sun.COM 		return (-EINVAL);
8408832SMiao.Chen@Sun.COM 	}
8418832SMiao.Chen@Sun.COM 
8428832SMiao.Chen@Sun.COM 	return (0);
8438832SMiao.Chen@Sun.COM }
8448832SMiao.Chen@Sun.COM 
8458832SMiao.Chen@Sun.COM /*ARGSUSED*/
i915_vblank_pipe_get(DRM_IOCTL_ARGS)8468832SMiao.Chen@Sun.COM int i915_vblank_pipe_get(DRM_IOCTL_ARGS)
8478832SMiao.Chen@Sun.COM {
8488832SMiao.Chen@Sun.COM 	DRM_DEVICE;
8498832SMiao.Chen@Sun.COM 	drm_i915_private_t *dev_priv = dev->dev_private;
8508832SMiao.Chen@Sun.COM 	drm_i915_vblank_pipe_t pipe;
8518832SMiao.Chen@Sun.COM 
8528832SMiao.Chen@Sun.COM 	if (!dev_priv) {
8538832SMiao.Chen@Sun.COM 		DRM_ERROR("called with no initialization\n");
8548832SMiao.Chen@Sun.COM 		return -EINVAL;
8558832SMiao.Chen@Sun.COM 	}
8568832SMiao.Chen@Sun.COM 
8578832SMiao.Chen@Sun.COM 	DRM_COPYFROM_WITH_RETURN(&pipe, (drm_i915_vblank_pipe_t __user *)data, sizeof (pipe));
8588832SMiao.Chen@Sun.COM 
8598832SMiao.Chen@Sun.COM 	pipe.pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
8608832SMiao.Chen@Sun.COM 
8618832SMiao.Chen@Sun.COM 	return 0;
8628832SMiao.Chen@Sun.COM }
8638832SMiao.Chen@Sun.COM 
8648832SMiao.Chen@Sun.COM /**
8658832SMiao.Chen@Sun.COM  * Schedule buffer swap at given vertical blank.
8668832SMiao.Chen@Sun.COM  */
8678832SMiao.Chen@Sun.COM /*ARGSUSED*/
i915_vblank_swap(DRM_IOCTL_ARGS)8688832SMiao.Chen@Sun.COM int i915_vblank_swap(DRM_IOCTL_ARGS)
8698832SMiao.Chen@Sun.COM {
87011260SMiao.Chen@Sun.COM         /* The delayed swap mechanism was fundamentally racy, and has been
87111260SMiao.Chen@Sun.COM         * removed.  The model was that the client requested a delayed flip/swap
87211260SMiao.Chen@Sun.COM         * from the kernel, then waited for vblank before continuing to perform
87311260SMiao.Chen@Sun.COM         * rendering.  The problem was that the kernel might wake the client
87411260SMiao.Chen@Sun.COM         * up before it dispatched the vblank swap (since the lock has to be
87511260SMiao.Chen@Sun.COM         * held while touching the ringbuffer), in which case the client would
87611260SMiao.Chen@Sun.COM         * clear and start the next frame before the swap occurred, and
87711260SMiao.Chen@Sun.COM         * flicker would occur in addition to likely missing the vblank.
87811260SMiao.Chen@Sun.COM         *
87911260SMiao.Chen@Sun.COM         * In the absence of this ioctl, userland falls back to a correct path
88011260SMiao.Chen@Sun.COM         * of waiting for a vblank, then dispatching the swap on its own.
88111260SMiao.Chen@Sun.COM         * Context switching to userland and back is plenty fast enough for
88211260SMiao.Chen@Sun.COM         * meeting the requirements of vblank swapping.
88311260SMiao.Chen@Sun.COM         */
88411260SMiao.Chen@Sun.COM 	return -EINVAL;
8858832SMiao.Chen@Sun.COM 
8864194Szw161486 }
8874194Szw161486 
8883446Smrj /* drm_dma.h hooks
8893446Smrj */
890*11359SMiao.Chen@Sun.COM 
igdng_irq_preinstall(struct drm_device * dev)891*11359SMiao.Chen@Sun.COM static void igdng_irq_preinstall(struct drm_device *dev)
892*11359SMiao.Chen@Sun.COM {
893*11359SMiao.Chen@Sun.COM        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
894*11359SMiao.Chen@Sun.COM 
895*11359SMiao.Chen@Sun.COM        I915_WRITE(HWSTAM, 0xeffe);
896*11359SMiao.Chen@Sun.COM 
897*11359SMiao.Chen@Sun.COM       /* XXX hotplug from PCH */
898*11359SMiao.Chen@Sun.COM 
899*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIMR, 0xffffffff);
900*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIER, 0x0);
901*11359SMiao.Chen@Sun.COM        (void) I915_READ(DEIER);
902*11359SMiao.Chen@Sun.COM 
903*11359SMiao.Chen@Sun.COM        /* and GT */
904*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIMR, 0xffffffff);
905*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIER, 0x0);
906*11359SMiao.Chen@Sun.COM        (void) I915_READ(GTIER);
907*11359SMiao.Chen@Sun.COM }
908*11359SMiao.Chen@Sun.COM 
igdng_irq_postinstall(struct drm_device * dev)909*11359SMiao.Chen@Sun.COM static int igdng_irq_postinstall(struct drm_device *dev)
910*11359SMiao.Chen@Sun.COM {
911*11359SMiao.Chen@Sun.COM        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
912*11359SMiao.Chen@Sun.COM        /* enable kind of interrupts always enabled */
913*11359SMiao.Chen@Sun.COM        u32 display_mask = DE_MASTER_IRQ_CONTROL /*| DE_PCH_EVENT */;
914*11359SMiao.Chen@Sun.COM        u32 render_mask = GT_USER_INTERRUPT;
915*11359SMiao.Chen@Sun.COM 
916*11359SMiao.Chen@Sun.COM        dev_priv->irq_mask_reg = ~display_mask;
917*11359SMiao.Chen@Sun.COM        dev_priv->de_irq_enable_reg = display_mask;
918*11359SMiao.Chen@Sun.COM 
919*11359SMiao.Chen@Sun.COM        /* should always can generate irq */
920*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIIR, I915_READ(DEIIR));
921*11359SMiao.Chen@Sun.COM        (void) I915_READ(DEIIR);
922*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
923*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIER, dev_priv->de_irq_enable_reg);
924*11359SMiao.Chen@Sun.COM        (void) I915_READ(DEIER);
925*11359SMiao.Chen@Sun.COM 
926*11359SMiao.Chen@Sun.COM        /* user interrupt should be enabled, but masked initial */
927*11359SMiao.Chen@Sun.COM        dev_priv->gt_irq_mask_reg = 0xffffffff;
928*11359SMiao.Chen@Sun.COM        dev_priv->gt_irq_enable_reg = render_mask;
929*11359SMiao.Chen@Sun.COM 
930*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIIR, I915_READ(GTIIR));
931*11359SMiao.Chen@Sun.COM        (void) I915_READ(GTIIR);
932*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
933*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
934*11359SMiao.Chen@Sun.COM        (void) I915_READ(GTIER);
935*11359SMiao.Chen@Sun.COM 
936*11359SMiao.Chen@Sun.COM        return 0;
937*11359SMiao.Chen@Sun.COM }
938*11359SMiao.Chen@Sun.COM 
igdng_irq_uninstall(struct drm_device * dev)939*11359SMiao.Chen@Sun.COM static void igdng_irq_uninstall(struct drm_device *dev)
940*11359SMiao.Chen@Sun.COM {
941*11359SMiao.Chen@Sun.COM        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
942*11359SMiao.Chen@Sun.COM        I915_WRITE(HWSTAM, 0xffffffff);
943*11359SMiao.Chen@Sun.COM 
944*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIMR, 0xffffffff);
945*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIER, 0x0);
946*11359SMiao.Chen@Sun.COM        I915_WRITE(DEIIR, I915_READ(DEIIR));
947*11359SMiao.Chen@Sun.COM 
948*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIMR, 0xffffffff);
949*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIER, 0x0);
950*11359SMiao.Chen@Sun.COM        I915_WRITE(GTIIR, I915_READ(GTIIR));
951*11359SMiao.Chen@Sun.COM }
952*11359SMiao.Chen@Sun.COM 
i915_driver_irq_preinstall(drm_device_t * dev)9538959SMiao.Chen@Sun.COM int i915_driver_irq_preinstall(drm_device_t * dev)
9543446Smrj {
9553446Smrj 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
9563446Smrj 
9578959SMiao.Chen@Sun.COM 	if (!dev_priv->mmio_map)
9588959SMiao.Chen@Sun.COM 		return -EINVAL;
9598959SMiao.Chen@Sun.COM 
960*11359SMiao.Chen@Sun.COM 	if (IS_IGDNG(dev)) {
961*11359SMiao.Chen@Sun.COM                igdng_irq_preinstall(dev);
962*11359SMiao.Chen@Sun.COM                return 0;
963*11359SMiao.Chen@Sun.COM 	}
964*11359SMiao.Chen@Sun.COM 
96511260SMiao.Chen@Sun.COM 	I915_WRITE16(HWSTAM, 0xeffe);
96611260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEASTAT, 0);
96711260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEBSTAT, 0);
9688832SMiao.Chen@Sun.COM 	I915_WRITE(IMR, 0xffffffff);
96911260SMiao.Chen@Sun.COM 	I915_WRITE16(IER, 0x0);
97011260SMiao.Chen@Sun.COM 	(void) I915_READ(IER);
9718959SMiao.Chen@Sun.COM 
9728959SMiao.Chen@Sun.COM 	return 0;
9733446Smrj }
9743446Smrj 
i915_driver_irq_postinstall(drm_device_t * dev)9753446Smrj void i915_driver_irq_postinstall(drm_device_t * dev)
9763446Smrj {
97711260SMiao.Chen@Sun.COM 	int error_mask;
9783446Smrj 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
9793446Smrj 
98011260SMiao.Chen@Sun.COM 	dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
98111260SMiao.Chen@Sun.COM 
982*11359SMiao.Chen@Sun.COM 	if (IS_IGDNG(dev)) {
983*11359SMiao.Chen@Sun.COM 		(void) igdng_irq_postinstall(dev);
984*11359SMiao.Chen@Sun.COM 		DRM_INIT_WAITQUEUE(&dev_priv->irq_queue, DRM_INTR_PRI(dev));
985*11359SMiao.Chen@Sun.COM 		return;
986*11359SMiao.Chen@Sun.COM 	}
987*11359SMiao.Chen@Sun.COM 
98811260SMiao.Chen@Sun.COM 	/* Unmask the interrupts that we always want on. */
98911260SMiao.Chen@Sun.COM 	dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX;
99011260SMiao.Chen@Sun.COM 
99111260SMiao.Chen@Sun.COM 	dev_priv->pipestat[0] = 0;
99211260SMiao.Chen@Sun.COM 	dev_priv->pipestat[1] = 0;
9934194Szw161486 
99411260SMiao.Chen@Sun.COM 	/*
99511260SMiao.Chen@Sun.COM 	 * Enable some error detection, note the instruction error mask
99611260SMiao.Chen@Sun.COM 	 * bit is reserved, so we leave it masked.
99711260SMiao.Chen@Sun.COM 	 */
99811260SMiao.Chen@Sun.COM 	if (IS_G4X(dev)) {
99911260SMiao.Chen@Sun.COM 		error_mask = ~(GM45_ERROR_PAGE_TABLE |
100011260SMiao.Chen@Sun.COM 			       GM45_ERROR_MEM_PRIV |
100111260SMiao.Chen@Sun.COM 			       GM45_ERROR_CP_PRIV |
100211260SMiao.Chen@Sun.COM 			       I915_ERROR_MEMORY_REFRESH);
100311260SMiao.Chen@Sun.COM 	} else {
100411260SMiao.Chen@Sun.COM 		error_mask = ~(I915_ERROR_PAGE_TABLE |
100511260SMiao.Chen@Sun.COM 			       I915_ERROR_MEMORY_REFRESH);
100611260SMiao.Chen@Sun.COM 	}
100711260SMiao.Chen@Sun.COM 	I915_WRITE(EMR, error_mask);
10084194Szw161486 
100911260SMiao.Chen@Sun.COM 	/* Disable pipe interrupt enables, clear pending pipe status */
101011260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff);
101111260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff);
1012*11359SMiao.Chen@Sun.COM 	(void) I915_READ(PIPEASTAT);
1013*11359SMiao.Chen@Sun.COM         (void) I915_READ(PIPEBSTAT);
101411260SMiao.Chen@Sun.COM 	/* Clear pending interrupt status */
101511260SMiao.Chen@Sun.COM 	I915_WRITE(IIR, I915_READ(IIR));
10165804Scg149915 
1017*11359SMiao.Chen@Sun.COM 	(void) I915_READ(IIR);
101811260SMiao.Chen@Sun.COM 	I915_WRITE(IMR, dev_priv->irq_mask_reg);
101911260SMiao.Chen@Sun.COM 	I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
102011260SMiao.Chen@Sun.COM 	(void) I915_READ(IER);
10215804Scg149915 
10228832SMiao.Chen@Sun.COM 	DRM_INIT_WAITQUEUE(&dev_priv->irq_queue, DRM_INTR_PRI(dev));
10234194Szw161486 
10248832SMiao.Chen@Sun.COM 	return;
10253446Smrj }
10263446Smrj 
i915_driver_irq_uninstall(drm_device_t * dev)10273446Smrj void i915_driver_irq_uninstall(drm_device_t * dev)
10283446Smrj {
10293446Smrj 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
103011260SMiao.Chen@Sun.COM 	if ((!dev_priv) || (dev->irq_enabled == 0))
10313446Smrj 		return;
10323446Smrj 
10338832SMiao.Chen@Sun.COM 	dev_priv->vblank_pipe = 0;
10348832SMiao.Chen@Sun.COM 
1035*11359SMiao.Chen@Sun.COM 	if (IS_IGDNG(dev)) {
1036*11359SMiao.Chen@Sun.COM 		igdng_irq_uninstall(dev);
1037*11359SMiao.Chen@Sun.COM 		DRM_FINI_WAITQUEUE(&dev_priv->irq_queue);
1038*11359SMiao.Chen@Sun.COM 		return;
1039*11359SMiao.Chen@Sun.COM 	}
1040*11359SMiao.Chen@Sun.COM 
10418832SMiao.Chen@Sun.COM 	I915_WRITE(HWSTAM, 0xffffffff);
104211260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEASTAT, 0);
104311260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEBSTAT, 0);
10448832SMiao.Chen@Sun.COM 	I915_WRITE(IMR, 0xffffffff);
10458832SMiao.Chen@Sun.COM 	I915_WRITE(IER, 0x0);
10464194Szw161486 
104711260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff);
104811260SMiao.Chen@Sun.COM 	I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff);
104911260SMiao.Chen@Sun.COM 	I915_WRITE(IIR, I915_READ(IIR));
10505804Scg149915 
105111260SMiao.Chen@Sun.COM 	DRM_FINI_WAITQUEUE(&dev_priv->irq_queue);
10523446Smrj }
1053