xref: /dragonfly/sys/dev/drm/i915/i915_irq.c (revision a85cb24f)
1c4a9e910SFrançois Tigeot /* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
2c4a9e910SFrançois Tigeot  */
300640ec9SFrançois Tigeot /*
4c4a9e910SFrançois Tigeot  * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
5c4a9e910SFrançois Tigeot  * All Rights Reserved.
6c4a9e910SFrançois Tigeot  *
7c4a9e910SFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
8c4a9e910SFrançois Tigeot  * copy of this software and associated documentation files (the
9c4a9e910SFrançois Tigeot  * "Software"), to deal in the Software without restriction, including
10c4a9e910SFrançois Tigeot  * without limitation the rights to use, copy, modify, merge, publish,
11c4a9e910SFrançois Tigeot  * distribute, sub license, and/or sell copies of the Software, and to
12c4a9e910SFrançois Tigeot  * permit persons to whom the Software is furnished to do so, subject to
13c4a9e910SFrançois Tigeot  * the following conditions:
14c4a9e910SFrançois Tigeot  *
15c4a9e910SFrançois Tigeot  * The above copyright notice and this permission notice (including the
16c4a9e910SFrançois Tigeot  * next paragraph) shall be included in all copies or substantial portions
17c4a9e910SFrançois Tigeot  * of the Software.
18c4a9e910SFrançois Tigeot  *
19c4a9e910SFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20c4a9e910SFrançois Tigeot  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21c4a9e910SFrançois Tigeot  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22c4a9e910SFrançois Tigeot  * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23c4a9e910SFrançois Tigeot  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24c4a9e910SFrançois Tigeot  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25c4a9e910SFrançois Tigeot  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26c4a9e910SFrançois Tigeot  *
27c4a9e910SFrançois Tigeot  */
28c4a9e910SFrançois Tigeot 
29183e2373SFrançois Tigeot #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30183e2373SFrançois Tigeot 
31183e2373SFrançois Tigeot #include <linux/sysrq.h>
321487f786SFrançois Tigeot #include <linux/slab.h>
338621f407SFrançois Tigeot #include <linux/circ_buf.h>
3418e26a6dSFrançois Tigeot #include <drm/drmP.h>
355c6c6f23SFrançois Tigeot #include <drm/i915_drm.h>
36c4a9e910SFrançois Tigeot #include "i915_drv.h"
379edbd4a0SFrançois Tigeot #include "i915_trace.h"
38e3adcf8fSFrançois Tigeot #include "intel_drv.h"
39c4a9e910SFrançois Tigeot 
402c9916cdSFrançois Tigeot /**
412c9916cdSFrançois Tigeot  * DOC: interrupt handling
422c9916cdSFrançois Tigeot  *
432c9916cdSFrançois Tigeot  * These functions provide the basic support for enabling and disabling the
442c9916cdSFrançois Tigeot  * interrupt handling support. There's a lot more functionality in i915_irq.c
452c9916cdSFrançois Tigeot  * and related files, but that will be described in separate chapters.
462c9916cdSFrançois Tigeot  */
472c9916cdSFrançois Tigeot 
48352ff8bdSFrançois Tigeot static const u32 hpd_ilk[HPD_NUM_PINS] = {
49352ff8bdSFrançois Tigeot 	[HPD_PORT_A] = DE_DP_A_HOTPLUG,
50352ff8bdSFrançois Tigeot };
51352ff8bdSFrançois Tigeot 
52352ff8bdSFrançois Tigeot static const u32 hpd_ivb[HPD_NUM_PINS] = {
53352ff8bdSFrançois Tigeot 	[HPD_PORT_A] = DE_DP_A_HOTPLUG_IVB,
54352ff8bdSFrançois Tigeot };
55352ff8bdSFrançois Tigeot 
56352ff8bdSFrançois Tigeot static const u32 hpd_bdw[HPD_NUM_PINS] = {
57352ff8bdSFrançois Tigeot 	[HPD_PORT_A] = GEN8_PORT_DP_A_HOTPLUG,
58352ff8bdSFrançois Tigeot };
59352ff8bdSFrançois Tigeot 
602c9916cdSFrançois Tigeot static const u32 hpd_ibx[HPD_NUM_PINS] = {
618e26cdf6SFrançois Tigeot 	[HPD_CRT] = SDE_CRT_HOTPLUG,
628e26cdf6SFrançois Tigeot 	[HPD_SDVO_B] = SDE_SDVOB_HOTPLUG,
638e26cdf6SFrançois Tigeot 	[HPD_PORT_B] = SDE_PORTB_HOTPLUG,
648e26cdf6SFrançois Tigeot 	[HPD_PORT_C] = SDE_PORTC_HOTPLUG,
658e26cdf6SFrançois Tigeot 	[HPD_PORT_D] = SDE_PORTD_HOTPLUG
668e26cdf6SFrançois Tigeot };
678e26cdf6SFrançois Tigeot 
682c9916cdSFrançois Tigeot static const u32 hpd_cpt[HPD_NUM_PINS] = {
698e26cdf6SFrançois Tigeot 	[HPD_CRT] = SDE_CRT_HOTPLUG_CPT,
708e26cdf6SFrançois Tigeot 	[HPD_SDVO_B] = SDE_SDVOB_HOTPLUG_CPT,
718e26cdf6SFrançois Tigeot 	[HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT,
728e26cdf6SFrançois Tigeot 	[HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT,
738e26cdf6SFrançois Tigeot 	[HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT
748e26cdf6SFrançois Tigeot };
758e26cdf6SFrançois Tigeot 
76a05eeebfSFrançois Tigeot static const u32 hpd_spt[HPD_NUM_PINS] = {
77352ff8bdSFrançois Tigeot 	[HPD_PORT_A] = SDE_PORTA_HOTPLUG_SPT,
78a05eeebfSFrançois Tigeot 	[HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT,
79a05eeebfSFrançois Tigeot 	[HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT,
80a05eeebfSFrançois Tigeot 	[HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT,
81a05eeebfSFrançois Tigeot 	[HPD_PORT_E] = SDE_PORTE_HOTPLUG_SPT
82a05eeebfSFrançois Tigeot };
83a05eeebfSFrançois Tigeot 
842c9916cdSFrançois Tigeot static const u32 hpd_mask_i915[HPD_NUM_PINS] = {
858e26cdf6SFrançois Tigeot 	[HPD_CRT] = CRT_HOTPLUG_INT_EN,
868e26cdf6SFrançois Tigeot 	[HPD_SDVO_B] = SDVOB_HOTPLUG_INT_EN,
878e26cdf6SFrançois Tigeot 	[HPD_SDVO_C] = SDVOC_HOTPLUG_INT_EN,
888e26cdf6SFrançois Tigeot 	[HPD_PORT_B] = PORTB_HOTPLUG_INT_EN,
898e26cdf6SFrançois Tigeot 	[HPD_PORT_C] = PORTC_HOTPLUG_INT_EN,
908e26cdf6SFrançois Tigeot 	[HPD_PORT_D] = PORTD_HOTPLUG_INT_EN
918e26cdf6SFrançois Tigeot };
928e26cdf6SFrançois Tigeot 
932c9916cdSFrançois Tigeot static const u32 hpd_status_g4x[HPD_NUM_PINS] = {
948e26cdf6SFrançois Tigeot 	[HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
958e26cdf6SFrançois Tigeot 	[HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_G4X,
968e26cdf6SFrançois Tigeot 	[HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_G4X,
978e26cdf6SFrançois Tigeot 	[HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
988e26cdf6SFrançois Tigeot 	[HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
998e26cdf6SFrançois Tigeot 	[HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
1008e26cdf6SFrançois Tigeot };
1018e26cdf6SFrançois Tigeot 
10219c468b4SFrançois Tigeot static const u32 hpd_status_i915[HPD_NUM_PINS] = {
1038e26cdf6SFrançois Tigeot 	[HPD_CRT] = CRT_HOTPLUG_INT_STATUS,
1048e26cdf6SFrançois Tigeot 	[HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915,
1058e26cdf6SFrançois Tigeot 	[HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I915,
1068e26cdf6SFrançois Tigeot 	[HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS,
1078e26cdf6SFrançois Tigeot 	[HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS,
1088e26cdf6SFrançois Tigeot 	[HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
1098e26cdf6SFrançois Tigeot };
1108e26cdf6SFrançois Tigeot 
11119c468b4SFrançois Tigeot /* BXT hpd list */
11219c468b4SFrançois Tigeot static const u32 hpd_bxt[HPD_NUM_PINS] = {
113352ff8bdSFrançois Tigeot 	[HPD_PORT_A] = BXT_DE_PORT_HP_DDIA,
11419c468b4SFrançois Tigeot 	[HPD_PORT_B] = BXT_DE_PORT_HP_DDIB,
11519c468b4SFrançois Tigeot 	[HPD_PORT_C] = BXT_DE_PORT_HP_DDIC
11619c468b4SFrançois Tigeot };
11719c468b4SFrançois Tigeot 
118ba55f2f5SFrançois Tigeot /* IIR can theoretically queue up two events. Be paranoid. */
119ba55f2f5SFrançois Tigeot #define GEN8_IRQ_RESET_NDX(type, which) do { \
120ba55f2f5SFrançois Tigeot 	I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
121ba55f2f5SFrançois Tigeot 	POSTING_READ(GEN8_##type##_IMR(which)); \
122ba55f2f5SFrançois Tigeot 	I915_WRITE(GEN8_##type##_IER(which), 0); \
123ba55f2f5SFrançois Tigeot 	I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
124ba55f2f5SFrançois Tigeot 	POSTING_READ(GEN8_##type##_IIR(which)); \
125ba55f2f5SFrançois Tigeot 	I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
126ba55f2f5SFrançois Tigeot 	POSTING_READ(GEN8_##type##_IIR(which)); \
127ba55f2f5SFrançois Tigeot } while (0)
128ba55f2f5SFrançois Tigeot 
129ba55f2f5SFrançois Tigeot #define GEN5_IRQ_RESET(type) do { \
130ba55f2f5SFrançois Tigeot 	I915_WRITE(type##IMR, 0xffffffff); \
131ba55f2f5SFrançois Tigeot 	POSTING_READ(type##IMR); \
132ba55f2f5SFrançois Tigeot 	I915_WRITE(type##IER, 0); \
133ba55f2f5SFrançois Tigeot 	I915_WRITE(type##IIR, 0xffffffff); \
134ba55f2f5SFrançois Tigeot 	POSTING_READ(type##IIR); \
135ba55f2f5SFrançois Tigeot 	I915_WRITE(type##IIR, 0xffffffff); \
136ba55f2f5SFrançois Tigeot 	POSTING_READ(type##IIR); \
137ba55f2f5SFrançois Tigeot } while (0)
138ba55f2f5SFrançois Tigeot 
139ba55f2f5SFrançois Tigeot /*
140ba55f2f5SFrançois Tigeot  * We should clear IMR at preinstall/uninstall, and just check at postinstall.
141ba55f2f5SFrançois Tigeot  */
142aee94f86SFrançois Tigeot static void gen5_assert_iir_is_zero(struct drm_i915_private *dev_priv,
143aee94f86SFrançois Tigeot 				    i915_reg_t reg)
144352ff8bdSFrançois Tigeot {
145352ff8bdSFrançois Tigeot 	u32 val = I915_READ(reg);
146352ff8bdSFrançois Tigeot 
147352ff8bdSFrançois Tigeot 	if (val == 0)
148352ff8bdSFrançois Tigeot 		return;
149352ff8bdSFrançois Tigeot 
150352ff8bdSFrançois Tigeot 	WARN(1, "Interrupt register 0x%x is not zero: 0x%08x\n",
151aee94f86SFrançois Tigeot 	     i915_mmio_reg_offset(reg), val);
152352ff8bdSFrançois Tigeot 	I915_WRITE(reg, 0xffffffff);
153352ff8bdSFrançois Tigeot 	POSTING_READ(reg);
154352ff8bdSFrançois Tigeot 	I915_WRITE(reg, 0xffffffff);
155352ff8bdSFrançois Tigeot 	POSTING_READ(reg);
156352ff8bdSFrançois Tigeot }
157ba55f2f5SFrançois Tigeot 
158ba55f2f5SFrançois Tigeot #define GEN8_IRQ_INIT_NDX(type, which, imr_val, ier_val) do { \
159352ff8bdSFrançois Tigeot 	gen5_assert_iir_is_zero(dev_priv, GEN8_##type##_IIR(which)); \
160ba55f2f5SFrançois Tigeot 	I915_WRITE(GEN8_##type##_IER(which), (ier_val)); \
1612c9916cdSFrançois Tigeot 	I915_WRITE(GEN8_##type##_IMR(which), (imr_val)); \
1622c9916cdSFrançois Tigeot 	POSTING_READ(GEN8_##type##_IMR(which)); \
163ba55f2f5SFrançois Tigeot } while (0)
164ba55f2f5SFrançois Tigeot 
165ba55f2f5SFrançois Tigeot #define GEN5_IRQ_INIT(type, imr_val, ier_val) do { \
166352ff8bdSFrançois Tigeot 	gen5_assert_iir_is_zero(dev_priv, type##IIR); \
167ba55f2f5SFrançois Tigeot 	I915_WRITE(type##IER, (ier_val)); \
1682c9916cdSFrançois Tigeot 	I915_WRITE(type##IMR, (imr_val)); \
1692c9916cdSFrançois Tigeot 	POSTING_READ(type##IMR); \
170ba55f2f5SFrançois Tigeot } while (0)
171ba55f2f5SFrançois Tigeot 
1722c9916cdSFrançois Tigeot static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
1734be47400SFrançois Tigeot static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
1742c9916cdSFrançois Tigeot 
175e3adcf8fSFrançois Tigeot /* For display hotplug interrupt */
176352ff8bdSFrançois Tigeot static inline void
177352ff8bdSFrançois Tigeot i915_hotplug_interrupt_update_locked(struct drm_i915_private *dev_priv,
178352ff8bdSFrançois Tigeot 				     uint32_t mask,
179352ff8bdSFrançois Tigeot 				     uint32_t bits)
180c4a9e910SFrançois Tigeot {
181352ff8bdSFrançois Tigeot 	uint32_t val;
182352ff8bdSFrançois Tigeot 
183*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
184352ff8bdSFrançois Tigeot 	WARN_ON(bits & ~mask);
185352ff8bdSFrançois Tigeot 
186352ff8bdSFrançois Tigeot 	val = I915_READ(PORT_HOTPLUG_EN);
187352ff8bdSFrançois Tigeot 	val &= ~mask;
188352ff8bdSFrançois Tigeot 	val |= bits;
189352ff8bdSFrançois Tigeot 	I915_WRITE(PORT_HOTPLUG_EN, val);
190352ff8bdSFrançois Tigeot }
191352ff8bdSFrançois Tigeot 
192352ff8bdSFrançois Tigeot /**
193352ff8bdSFrançois Tigeot  * i915_hotplug_interrupt_update - update hotplug interrupt enable
194352ff8bdSFrançois Tigeot  * @dev_priv: driver private
195352ff8bdSFrançois Tigeot  * @mask: bits to update
196352ff8bdSFrançois Tigeot  * @bits: bits to enable
197352ff8bdSFrançois Tigeot  * NOTE: the HPD enable bits are modified both inside and outside
198352ff8bdSFrançois Tigeot  * of an interrupt context. To avoid that read-modify-write cycles
199352ff8bdSFrançois Tigeot  * interfer, these bits are protected by a spinlock. Since this
200352ff8bdSFrançois Tigeot  * function is usually not called from a context where the lock is
201352ff8bdSFrançois Tigeot  * held already, this function acquires the lock itself. A non-locking
202352ff8bdSFrançois Tigeot  * version is also available.
203352ff8bdSFrançois Tigeot  */
204352ff8bdSFrançois Tigeot void i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv,
205352ff8bdSFrançois Tigeot 				   uint32_t mask,
206352ff8bdSFrançois Tigeot 				   uint32_t bits)
207352ff8bdSFrançois Tigeot {
208352ff8bdSFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
209352ff8bdSFrançois Tigeot 	i915_hotplug_interrupt_update_locked(dev_priv, mask, bits);
210352ff8bdSFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
211352ff8bdSFrançois Tigeot }
212352ff8bdSFrançois Tigeot 
213352ff8bdSFrançois Tigeot /**
214352ff8bdSFrançois Tigeot  * ilk_update_display_irq - update DEIMR
215352ff8bdSFrançois Tigeot  * @dev_priv: driver private
216352ff8bdSFrançois Tigeot  * @interrupt_mask: mask of interrupt bits to update
217352ff8bdSFrançois Tigeot  * @enabled_irq_mask: mask of interrupt bits to enable
218352ff8bdSFrançois Tigeot  */
219aee94f86SFrançois Tigeot void ilk_update_display_irq(struct drm_i915_private *dev_priv,
220352ff8bdSFrançois Tigeot 			    uint32_t interrupt_mask,
221352ff8bdSFrançois Tigeot 			    uint32_t enabled_irq_mask)
222352ff8bdSFrançois Tigeot {
223352ff8bdSFrançois Tigeot 	uint32_t new_val;
224352ff8bdSFrançois Tigeot 
225*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
226352ff8bdSFrançois Tigeot 
227352ff8bdSFrançois Tigeot 	WARN_ON(enabled_irq_mask & ~interrupt_mask);
2289edbd4a0SFrançois Tigeot 
22924edb884SFrançois Tigeot 	if (WARN_ON(!intel_irqs_enabled(dev_priv)))
2309edbd4a0SFrançois Tigeot 		return;
2319edbd4a0SFrançois Tigeot 
232352ff8bdSFrançois Tigeot 	new_val = dev_priv->irq_mask;
233352ff8bdSFrançois Tigeot 	new_val &= ~interrupt_mask;
234352ff8bdSFrançois Tigeot 	new_val |= (~enabled_irq_mask & interrupt_mask);
235352ff8bdSFrançois Tigeot 
236352ff8bdSFrançois Tigeot 	if (new_val != dev_priv->irq_mask) {
237352ff8bdSFrançois Tigeot 		dev_priv->irq_mask = new_val;
238e3adcf8fSFrançois Tigeot 		I915_WRITE(DEIMR, dev_priv->irq_mask);
239e3adcf8fSFrançois Tigeot 		POSTING_READ(DEIMR);
240c4a9e910SFrançois Tigeot 	}
241c4a9e910SFrançois Tigeot }
242c4a9e910SFrançois Tigeot 
2439edbd4a0SFrançois Tigeot /**
2449edbd4a0SFrançois Tigeot  * ilk_update_gt_irq - update GTIMR
2459edbd4a0SFrançois Tigeot  * @dev_priv: driver private
2469edbd4a0SFrançois Tigeot  * @interrupt_mask: mask of interrupt bits to update
2479edbd4a0SFrançois Tigeot  * @enabled_irq_mask: mask of interrupt bits to enable
2489edbd4a0SFrançois Tigeot  */
2499edbd4a0SFrançois Tigeot static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
2509edbd4a0SFrançois Tigeot 			      uint32_t interrupt_mask,
2519edbd4a0SFrançois Tigeot 			      uint32_t enabled_irq_mask)
2529edbd4a0SFrançois Tigeot {
253*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
2549edbd4a0SFrançois Tigeot 
2552c9916cdSFrançois Tigeot 	WARN_ON(enabled_irq_mask & ~interrupt_mask);
2562c9916cdSFrançois Tigeot 
25724edb884SFrançois Tigeot 	if (WARN_ON(!intel_irqs_enabled(dev_priv)))
2589edbd4a0SFrançois Tigeot 		return;
2599edbd4a0SFrançois Tigeot 
2609edbd4a0SFrançois Tigeot 	dev_priv->gt_irq_mask &= ~interrupt_mask;
2619edbd4a0SFrançois Tigeot 	dev_priv->gt_irq_mask |= (~enabled_irq_mask & interrupt_mask);
2629edbd4a0SFrançois Tigeot 	I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
2639edbd4a0SFrançois Tigeot }
2649edbd4a0SFrançois Tigeot 
26524edb884SFrançois Tigeot void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
2669edbd4a0SFrançois Tigeot {
2679edbd4a0SFrançois Tigeot 	ilk_update_gt_irq(dev_priv, mask, mask);
268bf017597SFrançois Tigeot 	POSTING_READ_FW(GTIMR);
2699edbd4a0SFrançois Tigeot }
2709edbd4a0SFrançois Tigeot 
27124edb884SFrançois Tigeot void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
2729edbd4a0SFrançois Tigeot {
2739edbd4a0SFrançois Tigeot 	ilk_update_gt_irq(dev_priv, mask, 0);
2749edbd4a0SFrançois Tigeot }
2759edbd4a0SFrançois Tigeot 
276aee94f86SFrançois Tigeot static i915_reg_t gen6_pm_iir(struct drm_i915_private *dev_priv)
2772c9916cdSFrançois Tigeot {
2782c9916cdSFrançois Tigeot 	return INTEL_INFO(dev_priv)->gen >= 8 ? GEN8_GT_IIR(2) : GEN6_PMIIR;
2792c9916cdSFrançois Tigeot }
2802c9916cdSFrançois Tigeot 
281aee94f86SFrançois Tigeot static i915_reg_t gen6_pm_imr(struct drm_i915_private *dev_priv)
2822c9916cdSFrançois Tigeot {
2832c9916cdSFrançois Tigeot 	return INTEL_INFO(dev_priv)->gen >= 8 ? GEN8_GT_IMR(2) : GEN6_PMIMR;
2842c9916cdSFrançois Tigeot }
2852c9916cdSFrançois Tigeot 
286aee94f86SFrançois Tigeot static i915_reg_t gen6_pm_ier(struct drm_i915_private *dev_priv)
2872c9916cdSFrançois Tigeot {
2882c9916cdSFrançois Tigeot 	return INTEL_INFO(dev_priv)->gen >= 8 ? GEN8_GT_IER(2) : GEN6_PMIER;
2892c9916cdSFrançois Tigeot }
2902c9916cdSFrançois Tigeot 
2919edbd4a0SFrançois Tigeot /**
2929edbd4a0SFrançois Tigeot  * snb_update_pm_irq - update GEN6_PMIMR
2939edbd4a0SFrançois Tigeot  * @dev_priv: driver private
2949edbd4a0SFrançois Tigeot  * @interrupt_mask: mask of interrupt bits to update
2959edbd4a0SFrançois Tigeot  * @enabled_irq_mask: mask of interrupt bits to enable
2969edbd4a0SFrançois Tigeot  */
2979edbd4a0SFrançois Tigeot static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
2989edbd4a0SFrançois Tigeot 			      uint32_t interrupt_mask,
2999edbd4a0SFrançois Tigeot 			      uint32_t enabled_irq_mask)
3009edbd4a0SFrançois Tigeot {
3019edbd4a0SFrançois Tigeot 	uint32_t new_val;
3029edbd4a0SFrançois Tigeot 
3032c9916cdSFrançois Tigeot 	WARN_ON(enabled_irq_mask & ~interrupt_mask);
3049edbd4a0SFrançois Tigeot 
305*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
3069edbd4a0SFrançois Tigeot 
3074be47400SFrançois Tigeot 	new_val = dev_priv->pm_imr;
3089edbd4a0SFrançois Tigeot 	new_val &= ~interrupt_mask;
3099edbd4a0SFrançois Tigeot 	new_val |= (~enabled_irq_mask & interrupt_mask);
3109edbd4a0SFrançois Tigeot 
3114be47400SFrançois Tigeot 	if (new_val != dev_priv->pm_imr) {
3124be47400SFrançois Tigeot 		dev_priv->pm_imr = new_val;
3134be47400SFrançois Tigeot 		I915_WRITE(gen6_pm_imr(dev_priv), dev_priv->pm_imr);
3142c9916cdSFrançois Tigeot 		POSTING_READ(gen6_pm_imr(dev_priv));
3159edbd4a0SFrançois Tigeot 	}
3169edbd4a0SFrançois Tigeot }
3179edbd4a0SFrançois Tigeot 
3184be47400SFrançois Tigeot void gen6_unmask_pm_irq(struct drm_i915_private *dev_priv, u32 mask)
3199edbd4a0SFrançois Tigeot {
3202c9916cdSFrançois Tigeot 	if (WARN_ON(!intel_irqs_enabled(dev_priv)))
3212c9916cdSFrançois Tigeot 		return;
3222c9916cdSFrançois Tigeot 
3239edbd4a0SFrançois Tigeot 	snb_update_pm_irq(dev_priv, mask, mask);
3249edbd4a0SFrançois Tigeot }
3259edbd4a0SFrançois Tigeot 
3264be47400SFrançois Tigeot static void __gen6_mask_pm_irq(struct drm_i915_private *dev_priv, u32 mask)
3279edbd4a0SFrançois Tigeot {
3289edbd4a0SFrançois Tigeot 	snb_update_pm_irq(dev_priv, mask, 0);
3299edbd4a0SFrançois Tigeot }
3309edbd4a0SFrançois Tigeot 
3314be47400SFrançois Tigeot void gen6_mask_pm_irq(struct drm_i915_private *dev_priv, u32 mask)
3325d0b1887SFrançois Tigeot {
33324edb884SFrançois Tigeot 	if (WARN_ON(!intel_irqs_enabled(dev_priv)))
334ba55f2f5SFrançois Tigeot 		return;
335ba55f2f5SFrançois Tigeot 
3364be47400SFrançois Tigeot 	__gen6_mask_pm_irq(dev_priv, mask);
3374be47400SFrançois Tigeot }
3384be47400SFrançois Tigeot 
3394be47400SFrançois Tigeot void gen6_reset_pm_iir(struct drm_i915_private *dev_priv, u32 reset_mask)
3404be47400SFrançois Tigeot {
3414be47400SFrançois Tigeot 	i915_reg_t reg = gen6_pm_iir(dev_priv);
3424be47400SFrançois Tigeot 
343*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
3444be47400SFrançois Tigeot 
3454be47400SFrançois Tigeot 	I915_WRITE(reg, reset_mask);
3464be47400SFrançois Tigeot 	I915_WRITE(reg, reset_mask);
3474be47400SFrançois Tigeot 	POSTING_READ(reg);
3484be47400SFrançois Tigeot }
3494be47400SFrançois Tigeot 
3504be47400SFrançois Tigeot void gen6_enable_pm_irq(struct drm_i915_private *dev_priv, u32 enable_mask)
3514be47400SFrançois Tigeot {
352*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
3534be47400SFrançois Tigeot 
3544be47400SFrançois Tigeot 	dev_priv->pm_ier |= enable_mask;
3554be47400SFrançois Tigeot 	I915_WRITE(gen6_pm_ier(dev_priv), dev_priv->pm_ier);
3564be47400SFrançois Tigeot 	gen6_unmask_pm_irq(dev_priv, enable_mask);
3574be47400SFrançois Tigeot 	/* unmask_pm_irq provides an implicit barrier (POSTING_READ) */
3584be47400SFrançois Tigeot }
3594be47400SFrançois Tigeot 
3604be47400SFrançois Tigeot void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, u32 disable_mask)
3614be47400SFrançois Tigeot {
362*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
3634be47400SFrançois Tigeot 
3644be47400SFrançois Tigeot 	dev_priv->pm_ier &= ~disable_mask;
3654be47400SFrançois Tigeot 	__gen6_mask_pm_irq(dev_priv, disable_mask);
3664be47400SFrançois Tigeot 	I915_WRITE(gen6_pm_ier(dev_priv), dev_priv->pm_ier);
3674be47400SFrançois Tigeot 	/* though a barrier is missing here, but don't really need a one */
368ba55f2f5SFrançois Tigeot }
369ba55f2f5SFrançois Tigeot 
3701487f786SFrançois Tigeot void gen6_reset_rps_interrupts(struct drm_i915_private *dev_priv)
3715d0b1887SFrançois Tigeot {
3725e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
3734be47400SFrançois Tigeot 	gen6_reset_pm_iir(dev_priv, dev_priv->pm_rps_events);
374477eb7f9SFrançois Tigeot 	dev_priv->rps.pm_iir = 0;
3755e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
3765d0b1887SFrançois Tigeot }
3775d0b1887SFrançois Tigeot 
3781487f786SFrançois Tigeot void gen6_enable_rps_interrupts(struct drm_i915_private *dev_priv)
379ba55f2f5SFrançois Tigeot {
3801e12ee3bSFrançois Tigeot 	if (READ_ONCE(dev_priv->rps.interrupts_enabled))
3811e12ee3bSFrançois Tigeot 		return;
3821e12ee3bSFrançois Tigeot 
3835e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
384303bf270SFrançois Tigeot 	WARN_ON_ONCE(dev_priv->rps.pm_iir);
385303bf270SFrançois Tigeot 	WARN_ON_ONCE(I915_READ(gen6_pm_iir(dev_priv)) & dev_priv->pm_rps_events);
3862c9916cdSFrançois Tigeot 	dev_priv->rps.interrupts_enabled = true;
3872c9916cdSFrançois Tigeot 	gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
388ba55f2f5SFrançois Tigeot 
3895e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
390ba55f2f5SFrançois Tigeot }
391ba55f2f5SFrançois Tigeot 
3921487f786SFrançois Tigeot void gen6_disable_rps_interrupts(struct drm_i915_private *dev_priv)
3935d0b1887SFrançois Tigeot {
3941e12ee3bSFrançois Tigeot 	if (!READ_ONCE(dev_priv->rps.interrupts_enabled))
3951e12ee3bSFrançois Tigeot 		return;
3961e12ee3bSFrançois Tigeot 
3975e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
3982c9916cdSFrançois Tigeot 	dev_priv->rps.interrupts_enabled = false;
3992c9916cdSFrançois Tigeot 
4001e12ee3bSFrançois Tigeot 	I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0u));
4012c9916cdSFrançois Tigeot 
4024be47400SFrançois Tigeot 	gen6_disable_pm_irq(dev_priv, dev_priv->pm_rps_events);
4032c9916cdSFrançois Tigeot 
4045e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
405303bf270SFrançois Tigeot 	synchronize_irq(dev_priv->drm.irq);
406477eb7f9SFrançois Tigeot 
407303bf270SFrançois Tigeot 	/* Now that we will not be generating any more work, flush any
408303bf270SFrançois Tigeot 	 * outsanding tasks. As we are called on the RPS idle path,
409303bf270SFrançois Tigeot 	 * we will reset the GPU to minimum frequencies, so the current
410303bf270SFrançois Tigeot 	 * state of the worker can be discarded.
411303bf270SFrançois Tigeot 	 */
412303bf270SFrançois Tigeot 	cancel_work_sync(&dev_priv->rps.work);
413303bf270SFrançois Tigeot 	gen6_reset_rps_interrupts(dev_priv);
4149edbd4a0SFrançois Tigeot }
4155d0b1887SFrançois Tigeot 
4164be47400SFrançois Tigeot void gen9_reset_guc_interrupts(struct drm_i915_private *dev_priv)
4174be47400SFrançois Tigeot {
4184be47400SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
4194be47400SFrançois Tigeot 	gen6_reset_pm_iir(dev_priv, dev_priv->pm_guc_events);
4204be47400SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
4214be47400SFrançois Tigeot }
4224be47400SFrançois Tigeot 
4234be47400SFrançois Tigeot void gen9_enable_guc_interrupts(struct drm_i915_private *dev_priv)
4244be47400SFrançois Tigeot {
4254be47400SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
4264be47400SFrançois Tigeot 	if (!dev_priv->guc.interrupts_enabled) {
4274be47400SFrançois Tigeot 		WARN_ON_ONCE(I915_READ(gen6_pm_iir(dev_priv)) &
4284be47400SFrançois Tigeot 				       dev_priv->pm_guc_events);
4294be47400SFrançois Tigeot 		dev_priv->guc.interrupts_enabled = true;
4304be47400SFrançois Tigeot 		gen6_enable_pm_irq(dev_priv, dev_priv->pm_guc_events);
4314be47400SFrançois Tigeot 	}
4324be47400SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
4334be47400SFrançois Tigeot }
4344be47400SFrançois Tigeot 
4354be47400SFrançois Tigeot void gen9_disable_guc_interrupts(struct drm_i915_private *dev_priv)
4364be47400SFrançois Tigeot {
4374be47400SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
4384be47400SFrançois Tigeot 	dev_priv->guc.interrupts_enabled = false;
4394be47400SFrançois Tigeot 
4404be47400SFrançois Tigeot 	gen6_disable_pm_irq(dev_priv, dev_priv->pm_guc_events);
4414be47400SFrançois Tigeot 
4424be47400SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
4434be47400SFrançois Tigeot 	synchronize_irq(dev_priv->drm.irq);
4444be47400SFrançois Tigeot 
4454be47400SFrançois Tigeot 	gen9_reset_guc_interrupts(dev_priv);
4464be47400SFrançois Tigeot }
4474be47400SFrançois Tigeot 
4489edbd4a0SFrançois Tigeot /**
449352ff8bdSFrançois Tigeot  * bdw_update_port_irq - update DE port interrupt
450352ff8bdSFrançois Tigeot  * @dev_priv: driver private
451352ff8bdSFrançois Tigeot  * @interrupt_mask: mask of interrupt bits to update
452352ff8bdSFrançois Tigeot  * @enabled_irq_mask: mask of interrupt bits to enable
453352ff8bdSFrançois Tigeot  */
454352ff8bdSFrançois Tigeot static void bdw_update_port_irq(struct drm_i915_private *dev_priv,
455352ff8bdSFrançois Tigeot 				uint32_t interrupt_mask,
456352ff8bdSFrançois Tigeot 				uint32_t enabled_irq_mask)
457352ff8bdSFrançois Tigeot {
458352ff8bdSFrançois Tigeot 	uint32_t new_val;
459352ff8bdSFrançois Tigeot 	uint32_t old_val;
460352ff8bdSFrançois Tigeot 
461*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
462352ff8bdSFrançois Tigeot 
463352ff8bdSFrançois Tigeot 	WARN_ON(enabled_irq_mask & ~interrupt_mask);
464352ff8bdSFrançois Tigeot 
465352ff8bdSFrançois Tigeot 	if (WARN_ON(!intel_irqs_enabled(dev_priv)))
466352ff8bdSFrançois Tigeot 		return;
467352ff8bdSFrançois Tigeot 
468352ff8bdSFrançois Tigeot 	old_val = I915_READ(GEN8_DE_PORT_IMR);
469352ff8bdSFrançois Tigeot 
470352ff8bdSFrançois Tigeot 	new_val = old_val;
471352ff8bdSFrançois Tigeot 	new_val &= ~interrupt_mask;
472352ff8bdSFrançois Tigeot 	new_val |= (~enabled_irq_mask & interrupt_mask);
473352ff8bdSFrançois Tigeot 
474352ff8bdSFrançois Tigeot 	if (new_val != old_val) {
475352ff8bdSFrançois Tigeot 		I915_WRITE(GEN8_DE_PORT_IMR, new_val);
476352ff8bdSFrançois Tigeot 		POSTING_READ(GEN8_DE_PORT_IMR);
477352ff8bdSFrançois Tigeot 	}
478352ff8bdSFrançois Tigeot }
479352ff8bdSFrançois Tigeot 
480352ff8bdSFrançois Tigeot /**
481aee94f86SFrançois Tigeot  * bdw_update_pipe_irq - update DE pipe interrupt
482aee94f86SFrançois Tigeot  * @dev_priv: driver private
483aee94f86SFrançois Tigeot  * @pipe: pipe whose interrupt to update
484aee94f86SFrançois Tigeot  * @interrupt_mask: mask of interrupt bits to update
485aee94f86SFrançois Tigeot  * @enabled_irq_mask: mask of interrupt bits to enable
486aee94f86SFrançois Tigeot  */
487aee94f86SFrançois Tigeot void bdw_update_pipe_irq(struct drm_i915_private *dev_priv,
488aee94f86SFrançois Tigeot 			 enum i915_pipe pipe,
489aee94f86SFrançois Tigeot 			 uint32_t interrupt_mask,
490aee94f86SFrançois Tigeot 			 uint32_t enabled_irq_mask)
491aee94f86SFrançois Tigeot {
492aee94f86SFrançois Tigeot 	uint32_t new_val;
493aee94f86SFrançois Tigeot 
494*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
495aee94f86SFrançois Tigeot 
496aee94f86SFrançois Tigeot 	WARN_ON(enabled_irq_mask & ~interrupt_mask);
497aee94f86SFrançois Tigeot 
498aee94f86SFrançois Tigeot 	if (WARN_ON(!intel_irqs_enabled(dev_priv)))
499aee94f86SFrançois Tigeot 		return;
500aee94f86SFrançois Tigeot 
501aee94f86SFrançois Tigeot 	new_val = dev_priv->de_irq_mask[pipe];
502aee94f86SFrançois Tigeot 	new_val &= ~interrupt_mask;
503aee94f86SFrançois Tigeot 	new_val |= (~enabled_irq_mask & interrupt_mask);
504aee94f86SFrançois Tigeot 
505aee94f86SFrançois Tigeot 	if (new_val != dev_priv->de_irq_mask[pipe]) {
506aee94f86SFrançois Tigeot 		dev_priv->de_irq_mask[pipe] = new_val;
507aee94f86SFrançois Tigeot 		I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
508aee94f86SFrançois Tigeot 		POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
509aee94f86SFrançois Tigeot 	}
510aee94f86SFrançois Tigeot }
511aee94f86SFrançois Tigeot 
512aee94f86SFrançois Tigeot /**
5139edbd4a0SFrançois Tigeot  * ibx_display_interrupt_update - update SDEIMR
5149edbd4a0SFrançois Tigeot  * @dev_priv: driver private
5159edbd4a0SFrançois Tigeot  * @interrupt_mask: mask of interrupt bits to update
5169edbd4a0SFrançois Tigeot  * @enabled_irq_mask: mask of interrupt bits to enable
5179edbd4a0SFrançois Tigeot  */
5182c9916cdSFrançois Tigeot void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
5199edbd4a0SFrançois Tigeot 				  uint32_t interrupt_mask,
5209edbd4a0SFrançois Tigeot 				  uint32_t enabled_irq_mask)
5219edbd4a0SFrançois Tigeot {
5229edbd4a0SFrançois Tigeot 	uint32_t sdeimr = I915_READ(SDEIMR);
5239edbd4a0SFrançois Tigeot 	sdeimr &= ~interrupt_mask;
5249edbd4a0SFrançois Tigeot 	sdeimr |= (~enabled_irq_mask & interrupt_mask);
5259edbd4a0SFrançois Tigeot 
5262c9916cdSFrançois Tigeot 	WARN_ON(enabled_irq_mask & ~interrupt_mask);
5272c9916cdSFrançois Tigeot 
528*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
5299edbd4a0SFrançois Tigeot 
53024edb884SFrançois Tigeot 	if (WARN_ON(!intel_irqs_enabled(dev_priv)))
5319edbd4a0SFrançois Tigeot 		return;
5329edbd4a0SFrançois Tigeot 
5339edbd4a0SFrançois Tigeot 	I915_WRITE(SDEIMR, sdeimr);
5345d0b1887SFrançois Tigeot 	POSTING_READ(SDEIMR);
5355d0b1887SFrançois Tigeot }
5365d0b1887SFrançois Tigeot 
537ba55f2f5SFrançois Tigeot static void
538ba55f2f5SFrançois Tigeot __i915_enable_pipestat(struct drm_i915_private *dev_priv, enum i915_pipe pipe,
539ba55f2f5SFrançois Tigeot 		       u32 enable_mask, u32 status_mask)
540c4a9e910SFrançois Tigeot {
541aee94f86SFrançois Tigeot 	i915_reg_t reg = PIPESTAT(pipe);
542ba55f2f5SFrançois Tigeot 	u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK;
543c4a9e910SFrançois Tigeot 
544*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
5452c9916cdSFrançois Tigeot 	WARN_ON(!intel_irqs_enabled(dev_priv));
5469edbd4a0SFrançois Tigeot 
547ba55f2f5SFrançois Tigeot 	if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
548ba55f2f5SFrançois Tigeot 		      status_mask & ~PIPESTAT_INT_STATUS_MASK,
549ba55f2f5SFrançois Tigeot 		      "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
550ba55f2f5SFrançois Tigeot 		      pipe_name(pipe), enable_mask, status_mask))
5518e26cdf6SFrançois Tigeot 		return;
5528e26cdf6SFrançois Tigeot 
553ba55f2f5SFrançois Tigeot 	if ((pipestat & enable_mask) == enable_mask)
554ba55f2f5SFrançois Tigeot 		return;
555ba55f2f5SFrançois Tigeot 
556ba55f2f5SFrançois Tigeot 	dev_priv->pipestat_irq_mask[pipe] |= status_mask;
557ba55f2f5SFrançois Tigeot 
558c4a9e910SFrançois Tigeot 	/* Enable the interrupt, clear any pending status */
559ba55f2f5SFrançois Tigeot 	pipestat |= enable_mask | status_mask;
5608e26cdf6SFrançois Tigeot 	I915_WRITE(reg, pipestat);
561e3adcf8fSFrançois Tigeot 	POSTING_READ(reg);
562c4a9e910SFrançois Tigeot }
563c4a9e910SFrançois Tigeot 
564ba55f2f5SFrançois Tigeot static void
565ba55f2f5SFrançois Tigeot __i915_disable_pipestat(struct drm_i915_private *dev_priv, enum i915_pipe pipe,
566ba55f2f5SFrançois Tigeot 		        u32 enable_mask, u32 status_mask)
567c4a9e910SFrançois Tigeot {
568aee94f86SFrançois Tigeot 	i915_reg_t reg = PIPESTAT(pipe);
569ba55f2f5SFrançois Tigeot 	u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK;
570c4a9e910SFrançois Tigeot 
571*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
5722c9916cdSFrançois Tigeot 	WARN_ON(!intel_irqs_enabled(dev_priv));
5739edbd4a0SFrançois Tigeot 
574ba55f2f5SFrançois Tigeot 	if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
575ba55f2f5SFrançois Tigeot 		      status_mask & ~PIPESTAT_INT_STATUS_MASK,
576ba55f2f5SFrançois Tigeot 		      "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
577ba55f2f5SFrançois Tigeot 		      pipe_name(pipe), enable_mask, status_mask))
5788e26cdf6SFrançois Tigeot 		return;
5798e26cdf6SFrançois Tigeot 
580ba55f2f5SFrançois Tigeot 	if ((pipestat & enable_mask) == 0)
581ba55f2f5SFrançois Tigeot 		return;
582ba55f2f5SFrançois Tigeot 
583ba55f2f5SFrançois Tigeot 	dev_priv->pipestat_irq_mask[pipe] &= ~status_mask;
584ba55f2f5SFrançois Tigeot 
585ba55f2f5SFrançois Tigeot 	pipestat &= ~enable_mask;
5868e26cdf6SFrançois Tigeot 	I915_WRITE(reg, pipestat);
587e3adcf8fSFrançois Tigeot 	POSTING_READ(reg);
588c4a9e910SFrançois Tigeot }
589c4a9e910SFrançois Tigeot 
590ba55f2f5SFrançois Tigeot static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask)
591ba55f2f5SFrançois Tigeot {
592ba55f2f5SFrançois Tigeot 	u32 enable_mask = status_mask << 16;
593ba55f2f5SFrançois Tigeot 
594ba55f2f5SFrançois Tigeot 	/*
595ba55f2f5SFrançois Tigeot 	 * On pipe A we don't support the PSR interrupt yet,
596ba55f2f5SFrançois Tigeot 	 * on pipe B and C the same bit MBZ.
597ba55f2f5SFrançois Tigeot 	 */
598ba55f2f5SFrançois Tigeot 	if (WARN_ON_ONCE(status_mask & PIPE_A_PSR_STATUS_VLV))
599ba55f2f5SFrançois Tigeot 		return 0;
600ba55f2f5SFrançois Tigeot 	/*
601ba55f2f5SFrançois Tigeot 	 * On pipe B and C we don't support the PSR interrupt yet, on pipe
602ba55f2f5SFrançois Tigeot 	 * A the same bit is for perf counters which we don't use either.
603ba55f2f5SFrançois Tigeot 	 */
604ba55f2f5SFrançois Tigeot 	if (WARN_ON_ONCE(status_mask & PIPE_B_PSR_STATUS_VLV))
605ba55f2f5SFrançois Tigeot 		return 0;
606ba55f2f5SFrançois Tigeot 
607ba55f2f5SFrançois Tigeot 	enable_mask &= ~(PIPE_FIFO_UNDERRUN_STATUS |
608ba55f2f5SFrançois Tigeot 			 SPRITE0_FLIP_DONE_INT_EN_VLV |
609ba55f2f5SFrançois Tigeot 			 SPRITE1_FLIP_DONE_INT_EN_VLV);
610ba55f2f5SFrançois Tigeot 	if (status_mask & SPRITE0_FLIP_DONE_INT_STATUS_VLV)
611ba55f2f5SFrançois Tigeot 		enable_mask |= SPRITE0_FLIP_DONE_INT_EN_VLV;
612ba55f2f5SFrançois Tigeot 	if (status_mask & SPRITE1_FLIP_DONE_INT_STATUS_VLV)
613ba55f2f5SFrançois Tigeot 		enable_mask |= SPRITE1_FLIP_DONE_INT_EN_VLV;
614ba55f2f5SFrançois Tigeot 
615ba55f2f5SFrançois Tigeot 	return enable_mask;
616ba55f2f5SFrançois Tigeot }
617ba55f2f5SFrançois Tigeot 
618ba55f2f5SFrançois Tigeot void
619ba55f2f5SFrançois Tigeot i915_enable_pipestat(struct drm_i915_private *dev_priv, enum i915_pipe pipe,
620ba55f2f5SFrançois Tigeot 		     u32 status_mask)
621ba55f2f5SFrançois Tigeot {
622ba55f2f5SFrançois Tigeot 	u32 enable_mask;
623ba55f2f5SFrançois Tigeot 
624aee94f86SFrançois Tigeot 	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
625303bf270SFrançois Tigeot 		enable_mask = vlv_get_pipestat_enable_mask(&dev_priv->drm,
626ba55f2f5SFrançois Tigeot 							   status_mask);
627ba55f2f5SFrançois Tigeot 	else
628ba55f2f5SFrançois Tigeot 		enable_mask = status_mask << 16;
629ba55f2f5SFrançois Tigeot 	__i915_enable_pipestat(dev_priv, pipe, enable_mask, status_mask);
630ba55f2f5SFrançois Tigeot }
631ba55f2f5SFrançois Tigeot 
632ba55f2f5SFrançois Tigeot void
633ba55f2f5SFrançois Tigeot i915_disable_pipestat(struct drm_i915_private *dev_priv, enum i915_pipe pipe,
634ba55f2f5SFrançois Tigeot 		      u32 status_mask)
635ba55f2f5SFrançois Tigeot {
636ba55f2f5SFrançois Tigeot 	u32 enable_mask;
637ba55f2f5SFrançois Tigeot 
638aee94f86SFrançois Tigeot 	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
639303bf270SFrançois Tigeot 		enable_mask = vlv_get_pipestat_enable_mask(&dev_priv->drm,
640ba55f2f5SFrançois Tigeot 							   status_mask);
641ba55f2f5SFrançois Tigeot 	else
642ba55f2f5SFrançois Tigeot 		enable_mask = status_mask << 16;
643ba55f2f5SFrançois Tigeot 	__i915_disable_pipestat(dev_priv, pipe, enable_mask, status_mask);
644ba55f2f5SFrançois Tigeot }
645ba55f2f5SFrançois Tigeot 
646c4a9e910SFrançois Tigeot /**
6475d0b1887SFrançois Tigeot  * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion
6481487f786SFrançois Tigeot  * @dev_priv: i915 device private
649e3adcf8fSFrançois Tigeot  */
6501487f786SFrançois Tigeot static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv)
651e3adcf8fSFrançois Tigeot {
6521487f786SFrançois Tigeot 	if (!dev_priv->opregion.asle || !IS_MOBILE(dev_priv))
65300640ec9SFrançois Tigeot 		return;
65400640ec9SFrançois Tigeot 
6555e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
656e3adcf8fSFrançois Tigeot 
657ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_STATUS);
6581487f786SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 4)
6599edbd4a0SFrançois Tigeot 		i915_enable_pipestat(dev_priv, PIPE_A,
660ba55f2f5SFrançois Tigeot 				     PIPE_LEGACY_BLC_EVENT_STATUS);
661e3adcf8fSFrançois Tigeot 
6625e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
663e3adcf8fSFrançois Tigeot }
664e3adcf8fSFrançois Tigeot 
665ba55f2f5SFrançois Tigeot /*
666ba55f2f5SFrançois Tigeot  * This timing diagram depicts the video signal in and
667ba55f2f5SFrançois Tigeot  * around the vertical blanking period.
668ba55f2f5SFrançois Tigeot  *
669ba55f2f5SFrançois Tigeot  * Assumptions about the fictitious mode used in this example:
670ba55f2f5SFrançois Tigeot  *  vblank_start >= 3
671ba55f2f5SFrançois Tigeot  *  vsync_start = vblank_start + 1
672ba55f2f5SFrançois Tigeot  *  vsync_end = vblank_start + 2
673ba55f2f5SFrançois Tigeot  *  vtotal = vblank_start + 3
674ba55f2f5SFrançois Tigeot  *
675ba55f2f5SFrançois Tigeot  *           start of vblank:
676ba55f2f5SFrançois Tigeot  *           latch double buffered registers
677ba55f2f5SFrançois Tigeot  *           increment frame counter (ctg+)
678ba55f2f5SFrançois Tigeot  *           generate start of vblank interrupt (gen4+)
679ba55f2f5SFrançois Tigeot  *           |
680ba55f2f5SFrançois Tigeot  *           |          frame start:
681ba55f2f5SFrançois Tigeot  *           |          generate frame start interrupt (aka. vblank interrupt) (gmch)
682ba55f2f5SFrançois Tigeot  *           |          may be shifted forward 1-3 extra lines via PIPECONF
683ba55f2f5SFrançois Tigeot  *           |          |
684ba55f2f5SFrançois Tigeot  *           |          |  start of vsync:
685ba55f2f5SFrançois Tigeot  *           |          |  generate vsync interrupt
686ba55f2f5SFrançois Tigeot  *           |          |  |
687ba55f2f5SFrançois Tigeot  * ___xxxx___    ___xxxx___    ___xxxx___    ___xxxx___    ___xxxx___    ___xxxx
688ba55f2f5SFrançois Tigeot  *       .   \hs/   .      \hs/          \hs/          \hs/   .      \hs/
689ba55f2f5SFrançois Tigeot  * ----va---> <-----------------vb--------------------> <--------va-------------
690ba55f2f5SFrançois Tigeot  *       |          |       <----vs----->                     |
691ba55f2f5SFrançois Tigeot  * -vbs-----> <---vbs+1---> <---vbs+2---> <-----0-----> <-----1-----> <-----2--- (scanline counter gen2)
692ba55f2f5SFrançois Tigeot  * -vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2---> <-----0--- (scanline counter gen3+)
693ba55f2f5SFrançois Tigeot  * -vbs-2---> <---vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2- (scanline counter hsw+ hdmi)
694ba55f2f5SFrançois Tigeot  *       |          |                                         |
695ba55f2f5SFrançois Tigeot  *       last visible pixel                                   first visible pixel
696ba55f2f5SFrançois Tigeot  *                  |                                         increment frame counter (gen3/4)
697ba55f2f5SFrançois Tigeot  *                  pixel counter = vblank_start * htotal     pixel counter = 0 (gen3/4)
698ba55f2f5SFrançois Tigeot  *
699ba55f2f5SFrançois Tigeot  * x  = horizontal active
700ba55f2f5SFrançois Tigeot  * _  = horizontal blanking
701ba55f2f5SFrançois Tigeot  * hs = horizontal sync
702ba55f2f5SFrançois Tigeot  * va = vertical active
703ba55f2f5SFrançois Tigeot  * vb = vertical blanking
704ba55f2f5SFrançois Tigeot  * vs = vertical sync
705ba55f2f5SFrançois Tigeot  * vbs = vblank_start (number)
706ba55f2f5SFrançois Tigeot  *
707ba55f2f5SFrançois Tigeot  * Summary:
708ba55f2f5SFrançois Tigeot  * - most events happen at the start of horizontal sync
709ba55f2f5SFrançois Tigeot  * - frame start happens at the start of horizontal blank, 1-4 lines
710ba55f2f5SFrançois Tigeot  *   (depending on PIPECONF settings) after the start of vblank
711ba55f2f5SFrançois Tigeot  * - gen3/4 pixel and frame counter are synchronized with the start
712ba55f2f5SFrançois Tigeot  *   of horizontal active on the first line of vertical active
713ba55f2f5SFrançois Tigeot  */
714ba55f2f5SFrançois Tigeot 
715c4a9e910SFrançois Tigeot /* Called from drm generic code, passed a 'crtc', which
716c4a9e910SFrançois Tigeot  * we use as a pipe index
717c4a9e910SFrançois Tigeot  */
718352ff8bdSFrançois Tigeot static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
719c4a9e910SFrançois Tigeot {
720303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
721aee94f86SFrançois Tigeot 	i915_reg_t high_frame, low_frame;
722ba55f2f5SFrançois Tigeot 	u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
7234be47400SFrançois Tigeot 	struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
7244be47400SFrançois Tigeot 								pipe);
725a05eeebfSFrançois Tigeot 	const struct drm_display_mode *mode = &intel_crtc->base.hwmode;
726*a85cb24fSFrançois Tigeot 	unsigned long irqflags;
7279edbd4a0SFrançois Tigeot 
728ba55f2f5SFrançois Tigeot 	htotal = mode->crtc_htotal;
729ba55f2f5SFrançois Tigeot 	hsync_start = mode->crtc_hsync_start;
730ba55f2f5SFrançois Tigeot 	vbl_start = mode->crtc_vblank_start;
731ba55f2f5SFrançois Tigeot 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
732ba55f2f5SFrançois Tigeot 		vbl_start = DIV_ROUND_UP(vbl_start, 2);
7339edbd4a0SFrançois Tigeot 
734ba55f2f5SFrançois Tigeot 	/* Convert to pixel count */
735ba55f2f5SFrançois Tigeot 	vbl_start *= htotal;
736ba55f2f5SFrançois Tigeot 
737ba55f2f5SFrançois Tigeot 	/* Start of vblank event occurs at start of hsync */
738ba55f2f5SFrançois Tigeot 	vbl_start -= htotal - hsync_start;
739ba55f2f5SFrançois Tigeot 
740e3adcf8fSFrançois Tigeot 	high_frame = PIPEFRAME(pipe);
741e3adcf8fSFrançois Tigeot 	low_frame = PIPEFRAMEPIXEL(pipe);
742e3adcf8fSFrançois Tigeot 
743*a85cb24fSFrançois Tigeot 	spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
744*a85cb24fSFrançois Tigeot 
745c4a9e910SFrançois Tigeot 	/*
746c4a9e910SFrançois Tigeot 	 * High & low register fields aren't synchronized, so make sure
747c4a9e910SFrançois Tigeot 	 * we get a low value that's stable across two reads of the high
748c4a9e910SFrançois Tigeot 	 * register.
749c4a9e910SFrançois Tigeot 	 */
750c4a9e910SFrançois Tigeot 	do {
751*a85cb24fSFrançois Tigeot 		high1 = I915_READ_FW(high_frame) & PIPE_FRAME_HIGH_MASK;
752*a85cb24fSFrançois Tigeot 		low   = I915_READ_FW(low_frame);
753*a85cb24fSFrançois Tigeot 		high2 = I915_READ_FW(high_frame) & PIPE_FRAME_HIGH_MASK;
754c4a9e910SFrançois Tigeot 	} while (high1 != high2);
755c4a9e910SFrançois Tigeot 
756*a85cb24fSFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
757*a85cb24fSFrançois Tigeot 
758e3adcf8fSFrançois Tigeot 	high1 >>= PIPE_FRAME_HIGH_SHIFT;
7599edbd4a0SFrançois Tigeot 	pixel = low & PIPE_PIXEL_MASK;
760e3adcf8fSFrançois Tigeot 	low >>= PIPE_FRAME_LOW_SHIFT;
7619edbd4a0SFrançois Tigeot 
7629edbd4a0SFrançois Tigeot 	/*
7639edbd4a0SFrançois Tigeot 	 * The frame counter increments at beginning of active.
7649edbd4a0SFrançois Tigeot 	 * Cook up a vblank counter by also checking the pixel
7659edbd4a0SFrançois Tigeot 	 * counter against vblank start.
7669edbd4a0SFrançois Tigeot 	 */
7679edbd4a0SFrançois Tigeot 	return (((high1 << 8) | low) + (pixel >= vbl_start)) & 0xffffff;
768c4a9e910SFrançois Tigeot }
769c4a9e910SFrançois Tigeot 
770352ff8bdSFrançois Tigeot static u32 g4x_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
771c4a9e910SFrançois Tigeot {
772303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
773c4a9e910SFrançois Tigeot 
774352ff8bdSFrançois Tigeot 	return I915_READ(PIPE_FRMCOUNT_G4X(pipe));
775c4a9e910SFrançois Tigeot }
776c4a9e910SFrançois Tigeot 
777aee94f86SFrançois Tigeot /* I915_READ_FW, only for fast reads of display block, no need for forcewake etc. */
778ba55f2f5SFrançois Tigeot static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
7799edbd4a0SFrançois Tigeot {
780ba55f2f5SFrançois Tigeot 	struct drm_device *dev = crtc->base.dev;
781303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
782a05eeebfSFrançois Tigeot 	const struct drm_display_mode *mode = &crtc->base.hwmode;
783ba55f2f5SFrançois Tigeot 	enum i915_pipe pipe = crtc->pipe;
784ba55f2f5SFrançois Tigeot 	int position, vtotal;
7859edbd4a0SFrançois Tigeot 
786*a85cb24fSFrançois Tigeot 	if (!crtc->active)
787*a85cb24fSFrançois Tigeot 		return -1;
788*a85cb24fSFrançois Tigeot 
789ba55f2f5SFrançois Tigeot 	vtotal = mode->crtc_vtotal;
790ba55f2f5SFrançois Tigeot 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
791ba55f2f5SFrançois Tigeot 		vtotal /= 2;
7929edbd4a0SFrançois Tigeot 
7931487f786SFrançois Tigeot 	if (IS_GEN2(dev_priv))
794aee94f86SFrançois Tigeot 		position = I915_READ_FW(PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
795ba55f2f5SFrançois Tigeot 	else
796aee94f86SFrançois Tigeot 		position = I915_READ_FW(PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
797ba55f2f5SFrançois Tigeot 
798ba55f2f5SFrançois Tigeot 	/*
799a05eeebfSFrançois Tigeot 	 * On HSW, the DSL reg (0x70000) appears to return 0 if we
800a05eeebfSFrançois Tigeot 	 * read it just before the start of vblank.  So try it again
801a05eeebfSFrançois Tigeot 	 * so we don't accidentally end up spanning a vblank frame
802a05eeebfSFrançois Tigeot 	 * increment, causing the pipe_update_end() code to squak at us.
803a05eeebfSFrançois Tigeot 	 *
804a05eeebfSFrançois Tigeot 	 * The nature of this problem means we can't simply check the ISR
805a05eeebfSFrançois Tigeot 	 * bit and return the vblank start value; nor can we use the scanline
806a05eeebfSFrançois Tigeot 	 * debug register in the transcoder as it appears to have the same
807a05eeebfSFrançois Tigeot 	 * problem.  We may need to extend this to include other platforms,
808a05eeebfSFrançois Tigeot 	 * but so far testing only shows the problem on HSW.
809a05eeebfSFrançois Tigeot 	 */
8101487f786SFrançois Tigeot 	if (HAS_DDI(dev_priv) && !position) {
811a05eeebfSFrançois Tigeot 		int i, temp;
812a05eeebfSFrançois Tigeot 
813a05eeebfSFrançois Tigeot 		for (i = 0; i < 100; i++) {
814a05eeebfSFrançois Tigeot 			udelay(1);
815*a85cb24fSFrançois Tigeot 			temp = I915_READ_FW(PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
816a05eeebfSFrançois Tigeot 			if (temp != position) {
817a05eeebfSFrançois Tigeot 				position = temp;
818a05eeebfSFrançois Tigeot 				break;
819a05eeebfSFrançois Tigeot 			}
820a05eeebfSFrançois Tigeot 		}
821a05eeebfSFrançois Tigeot 	}
822a05eeebfSFrançois Tigeot 
823a05eeebfSFrançois Tigeot 	/*
824ba55f2f5SFrançois Tigeot 	 * See update_scanline_offset() for the details on the
825ba55f2f5SFrançois Tigeot 	 * scanline_offset adjustment.
826ba55f2f5SFrançois Tigeot 	 */
827ba55f2f5SFrançois Tigeot 	return (position + crtc->scanline_offset) % vtotal;
8289edbd4a0SFrançois Tigeot }
8299edbd4a0SFrançois Tigeot 
830352ff8bdSFrançois Tigeot static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
831782e40d3SFrançois Tigeot 				    unsigned int flags, int *vpos, int *hpos,
832352ff8bdSFrançois Tigeot 				    ktime_t *stime, ktime_t *etime,
833352ff8bdSFrançois Tigeot 				    const struct drm_display_mode *mode)
834e3adcf8fSFrançois Tigeot {
835303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
8364be47400SFrançois Tigeot 	struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
8374be47400SFrançois Tigeot 								pipe);
8389edbd4a0SFrançois Tigeot 	int position;
839ba55f2f5SFrançois Tigeot 	int vbl_start, vbl_end, hsync_start, htotal, vtotal;
840e3adcf8fSFrançois Tigeot 	bool in_vbl = true;
841e3adcf8fSFrançois Tigeot 	int ret = 0;
8425e269720SFrançois Tigeot 	unsigned long irqflags;
843e3adcf8fSFrançois Tigeot 
844a05eeebfSFrançois Tigeot 	if (WARN_ON(!mode->crtc_clock)) {
84500640ec9SFrançois Tigeot 		DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
846e3adcf8fSFrançois Tigeot 				 "pipe %c\n", pipe_name(pipe));
847e3adcf8fSFrançois Tigeot 		return 0;
848e3adcf8fSFrançois Tigeot 	}
849e3adcf8fSFrançois Tigeot 
8509edbd4a0SFrançois Tigeot 	htotal = mode->crtc_htotal;
851ba55f2f5SFrançois Tigeot 	hsync_start = mode->crtc_hsync_start;
8529edbd4a0SFrançois Tigeot 	vtotal = mode->crtc_vtotal;
8539edbd4a0SFrançois Tigeot 	vbl_start = mode->crtc_vblank_start;
8549edbd4a0SFrançois Tigeot 	vbl_end = mode->crtc_vblank_end;
855e3adcf8fSFrançois Tigeot 
8569edbd4a0SFrançois Tigeot 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
8579edbd4a0SFrançois Tigeot 		vbl_start = DIV_ROUND_UP(vbl_start, 2);
8589edbd4a0SFrançois Tigeot 		vbl_end /= 2;
8599edbd4a0SFrançois Tigeot 		vtotal /= 2;
8609edbd4a0SFrançois Tigeot 	}
8619edbd4a0SFrançois Tigeot 
8629edbd4a0SFrançois Tigeot 	ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
8639edbd4a0SFrançois Tigeot 
8649edbd4a0SFrançois Tigeot 	/*
8659edbd4a0SFrançois Tigeot 	 * Lock uncore.lock, as we will do multiple timing critical raw
8669edbd4a0SFrançois Tigeot 	 * register reads, potentially with preemption disabled, so the
8679edbd4a0SFrançois Tigeot 	 * following code must not block on uncore.lock.
8689edbd4a0SFrançois Tigeot 	 */
8695e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
8709edbd4a0SFrançois Tigeot 
8719edbd4a0SFrançois Tigeot 	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
8729edbd4a0SFrançois Tigeot 
8739edbd4a0SFrançois Tigeot 	/* Get optional system timestamp before query. */
8749edbd4a0SFrançois Tigeot 	if (stime)
8759edbd4a0SFrançois Tigeot 		*stime = ktime_get();
8769edbd4a0SFrançois Tigeot 
8771487f786SFrançois Tigeot 	if (IS_GEN2(dev_priv) || IS_G4X(dev_priv) || INTEL_GEN(dev_priv) >= 5) {
878e3adcf8fSFrançois Tigeot 		/* No obvious pixelcount register. Only query vertical
879e3adcf8fSFrançois Tigeot 		 * scanout position from Display scan line register.
880e3adcf8fSFrançois Tigeot 		 */
881ba55f2f5SFrançois Tigeot 		position = __intel_get_crtc_scanline(intel_crtc);
882e3adcf8fSFrançois Tigeot 	} else {
883e3adcf8fSFrançois Tigeot 		/* Have access to pixelcount since start of frame.
884e3adcf8fSFrançois Tigeot 		 * We can split this into vertical and horizontal
885e3adcf8fSFrançois Tigeot 		 * scanout position.
886e3adcf8fSFrançois Tigeot 		 */
887aee94f86SFrançois Tigeot 		position = (I915_READ_FW(PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT;
888e3adcf8fSFrançois Tigeot 
8899edbd4a0SFrançois Tigeot 		/* convert to pixel counts */
8909edbd4a0SFrançois Tigeot 		vbl_start *= htotal;
8919edbd4a0SFrançois Tigeot 		vbl_end *= htotal;
8929edbd4a0SFrançois Tigeot 		vtotal *= htotal;
893ba55f2f5SFrançois Tigeot 
894ba55f2f5SFrançois Tigeot 		/*
895ba55f2f5SFrançois Tigeot 		 * In interlaced modes, the pixel counter counts all pixels,
896ba55f2f5SFrançois Tigeot 		 * so one field will have htotal more pixels. In order to avoid
897ba55f2f5SFrançois Tigeot 		 * the reported position from jumping backwards when the pixel
898ba55f2f5SFrançois Tigeot 		 * counter is beyond the length of the shorter field, just
899ba55f2f5SFrançois Tigeot 		 * clamp the position the length of the shorter field. This
900ba55f2f5SFrançois Tigeot 		 * matches how the scanline counter based position works since
901ba55f2f5SFrançois Tigeot 		 * the scanline counter doesn't count the two half lines.
902ba55f2f5SFrançois Tigeot 		 */
903ba55f2f5SFrançois Tigeot 		if (position >= vtotal)
904ba55f2f5SFrançois Tigeot 			position = vtotal - 1;
905ba55f2f5SFrançois Tigeot 
906ba55f2f5SFrançois Tigeot 		/*
907ba55f2f5SFrançois Tigeot 		 * Start of vblank interrupt is triggered at start of hsync,
908ba55f2f5SFrançois Tigeot 		 * just prior to the first active line of vblank. However we
909ba55f2f5SFrançois Tigeot 		 * consider lines to start at the leading edge of horizontal
910ba55f2f5SFrançois Tigeot 		 * active. So, should we get here before we've crossed into
911ba55f2f5SFrançois Tigeot 		 * the horizontal active of the first line in vblank, we would
912ba55f2f5SFrançois Tigeot 		 * not set the DRM_SCANOUTPOS_INVBL flag. In order to fix that,
913ba55f2f5SFrançois Tigeot 		 * always add htotal-hsync_start to the current pixel position.
914ba55f2f5SFrançois Tigeot 		 */
915ba55f2f5SFrançois Tigeot 		position = (position + htotal - hsync_start) % vtotal;
9169edbd4a0SFrançois Tigeot 	}
9179edbd4a0SFrançois Tigeot 
9189edbd4a0SFrançois Tigeot 	/* Get optional system timestamp after query. */
9199edbd4a0SFrançois Tigeot 	if (etime)
9209edbd4a0SFrançois Tigeot 		*etime = ktime_get();
9219edbd4a0SFrançois Tigeot 
9229edbd4a0SFrançois Tigeot 	/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
9239edbd4a0SFrançois Tigeot 
9245e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
9259edbd4a0SFrançois Tigeot 
9269edbd4a0SFrançois Tigeot 	in_vbl = position >= vbl_start && position < vbl_end;
9279edbd4a0SFrançois Tigeot 
9289edbd4a0SFrançois Tigeot 	/*
9299edbd4a0SFrançois Tigeot 	 * While in vblank, position will be negative
9309edbd4a0SFrançois Tigeot 	 * counting up towards 0 at vbl_end. And outside
9319edbd4a0SFrançois Tigeot 	 * vblank, position will be positive counting
9329edbd4a0SFrançois Tigeot 	 * up since vbl_end.
9339edbd4a0SFrançois Tigeot 	 */
9349edbd4a0SFrançois Tigeot 	if (position >= vbl_start)
9359edbd4a0SFrançois Tigeot 		position -= vbl_end;
9369edbd4a0SFrançois Tigeot 	else
9379edbd4a0SFrançois Tigeot 		position += vtotal - vbl_end;
9389edbd4a0SFrançois Tigeot 
9391487f786SFrançois Tigeot 	if (IS_GEN2(dev_priv) || IS_G4X(dev_priv) || INTEL_GEN(dev_priv) >= 5) {
9409edbd4a0SFrançois Tigeot 		*vpos = position;
9419edbd4a0SFrançois Tigeot 		*hpos = 0;
9429edbd4a0SFrançois Tigeot 	} else {
943e3adcf8fSFrançois Tigeot 		*vpos = position / htotal;
944e3adcf8fSFrançois Tigeot 		*hpos = position - (*vpos * htotal);
945e3adcf8fSFrançois Tigeot 	}
946e3adcf8fSFrançois Tigeot 
947e3adcf8fSFrançois Tigeot 	/* In vblank? */
948e3adcf8fSFrançois Tigeot 	if (in_vbl)
9491b13d190SFrançois Tigeot 		ret |= DRM_SCANOUTPOS_IN_VBLANK;
950e3adcf8fSFrançois Tigeot 
951e3adcf8fSFrançois Tigeot 	return ret;
952e3adcf8fSFrançois Tigeot }
953e3adcf8fSFrançois Tigeot 
954ba55f2f5SFrançois Tigeot int intel_get_crtc_scanline(struct intel_crtc *crtc)
955ba55f2f5SFrançois Tigeot {
956303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
9575e269720SFrançois Tigeot 	unsigned long irqflags;
958ba55f2f5SFrançois Tigeot 	int position;
959ba55f2f5SFrançois Tigeot 
9605e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
961ba55f2f5SFrançois Tigeot 	position = __intel_get_crtc_scanline(crtc);
9625e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
963ba55f2f5SFrançois Tigeot 
964ba55f2f5SFrançois Tigeot 	return position;
965ba55f2f5SFrançois Tigeot }
966ba55f2f5SFrançois Tigeot 
967352ff8bdSFrançois Tigeot static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
96800640ec9SFrançois Tigeot 			      int *max_error,
96900640ec9SFrançois Tigeot 			      struct timeval *vblank_time,
97000640ec9SFrançois Tigeot 			      unsigned flags)
971e3adcf8fSFrançois Tigeot {
9724be47400SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
9734be47400SFrançois Tigeot 	struct intel_crtc *crtc;
974e3adcf8fSFrançois Tigeot 
9754be47400SFrançois Tigeot 	if (pipe >= INTEL_INFO(dev_priv)->num_pipes) {
976352ff8bdSFrançois Tigeot 		DRM_ERROR("Invalid crtc %u\n", pipe);
977e3adcf8fSFrançois Tigeot 		return -EINVAL;
978e3adcf8fSFrançois Tigeot 	}
979e3adcf8fSFrançois Tigeot 
980e3adcf8fSFrançois Tigeot 	/* Get drm_crtc to timestamp: */
9814be47400SFrançois Tigeot 	crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
982e3adcf8fSFrançois Tigeot 	if (crtc == NULL) {
983352ff8bdSFrançois Tigeot 		DRM_ERROR("Invalid crtc %u\n", pipe);
984e3adcf8fSFrançois Tigeot 		return -EINVAL;
985e3adcf8fSFrançois Tigeot 	}
986e3adcf8fSFrançois Tigeot 
9874be47400SFrançois Tigeot 	if (!crtc->base.hwmode.crtc_clock) {
988352ff8bdSFrançois Tigeot 		DRM_DEBUG_KMS("crtc %u is disabled\n", pipe);
989e3adcf8fSFrançois Tigeot 		return -EBUSY;
990e3adcf8fSFrançois Tigeot 	}
991e3adcf8fSFrançois Tigeot 
992e3adcf8fSFrançois Tigeot 	/* Helper routine in DRM core does all the work: */
993e3adcf8fSFrançois Tigeot 	return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
994e3adcf8fSFrançois Tigeot 						     vblank_time, flags,
9954be47400SFrançois Tigeot 						     &crtc->base.hwmode);
996e3adcf8fSFrançois Tigeot }
997e3adcf8fSFrançois Tigeot 
9981487f786SFrançois Tigeot static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
999e3adcf8fSFrançois Tigeot {
1000e3adcf8fSFrançois Tigeot 	u32 busy_up, busy_down, max_avg, min_avg;
1001a2296444SFrançois Tigeot 	u8 new_delay;
1002a2296444SFrançois Tigeot 
1003a2296444SFrançois Tigeot 	lockmgr(&mchdev_lock, LK_EXCLUSIVE);
1004a2296444SFrançois Tigeot 
1005a2296444SFrançois Tigeot 	I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
1006a2296444SFrançois Tigeot 
100700640ec9SFrançois Tigeot 	new_delay = dev_priv->ips.cur_delay;
1008e3adcf8fSFrançois Tigeot 
1009e3adcf8fSFrançois Tigeot 	I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG);
1010e3adcf8fSFrançois Tigeot 	busy_up = I915_READ(RCPREVBSYTUPAVG);
1011e3adcf8fSFrançois Tigeot 	busy_down = I915_READ(RCPREVBSYTDNAVG);
1012e3adcf8fSFrançois Tigeot 	max_avg = I915_READ(RCBMAXAVG);
1013e3adcf8fSFrançois Tigeot 	min_avg = I915_READ(RCBMINAVG);
1014e3adcf8fSFrançois Tigeot 
1015e3adcf8fSFrançois Tigeot 	/* Handle RCS change request from hw */
1016e3adcf8fSFrançois Tigeot 	if (busy_up > max_avg) {
101700640ec9SFrançois Tigeot 		if (dev_priv->ips.cur_delay != dev_priv->ips.max_delay)
101800640ec9SFrançois Tigeot 			new_delay = dev_priv->ips.cur_delay - 1;
101900640ec9SFrançois Tigeot 		if (new_delay < dev_priv->ips.max_delay)
102000640ec9SFrançois Tigeot 			new_delay = dev_priv->ips.max_delay;
1021e3adcf8fSFrançois Tigeot 	} else if (busy_down < min_avg) {
102200640ec9SFrançois Tigeot 		if (dev_priv->ips.cur_delay != dev_priv->ips.min_delay)
102300640ec9SFrançois Tigeot 			new_delay = dev_priv->ips.cur_delay + 1;
102400640ec9SFrançois Tigeot 		if (new_delay > dev_priv->ips.min_delay)
102500640ec9SFrançois Tigeot 			new_delay = dev_priv->ips.min_delay;
1026e3adcf8fSFrançois Tigeot 	}
1027e3adcf8fSFrançois Tigeot 
10281487f786SFrançois Tigeot 	if (ironlake_set_drps(dev_priv, new_delay))
102900640ec9SFrançois Tigeot 		dev_priv->ips.cur_delay = new_delay;
1030e3adcf8fSFrançois Tigeot 
1031a2296444SFrançois Tigeot 	lockmgr(&mchdev_lock, LK_RELEASE);
1032a2296444SFrançois Tigeot 
1033e3adcf8fSFrançois Tigeot 	return;
1034e3adcf8fSFrançois Tigeot }
1035e3adcf8fSFrançois Tigeot 
10368621f407SFrançois Tigeot static void notify_ring(struct intel_engine_cs *engine)
1037e3adcf8fSFrançois Tigeot {
1038*a85cb24fSFrançois Tigeot 	struct drm_i915_gem_request *rq = NULL;
1039*a85cb24fSFrançois Tigeot 	struct intel_wait *wait;
1040*a85cb24fSFrançois Tigeot 
1041*a85cb24fSFrançois Tigeot 	atomic_inc(&engine->irq_count);
1042*a85cb24fSFrançois Tigeot 	set_bit(ENGINE_IRQ_BREADCRUMB, &engine->irq_posted);
1043*a85cb24fSFrançois Tigeot 
1044*a85cb24fSFrançois Tigeot 	lockmgr(&engine->breadcrumbs.irq_lock, LK_EXCLUSIVE);
1045*a85cb24fSFrançois Tigeot 	wait = engine->breadcrumbs.irq_wait;
1046*a85cb24fSFrançois Tigeot 	if (wait) {
1047*a85cb24fSFrançois Tigeot 		/* We use a callback from the dma-fence to submit
1048*a85cb24fSFrançois Tigeot 		 * requests after waiting on our own requests. To
1049*a85cb24fSFrançois Tigeot 		 * ensure minimum delay in queuing the next request to
1050*a85cb24fSFrançois Tigeot 		 * hardware, signal the fence now rather than wait for
1051*a85cb24fSFrançois Tigeot 		 * the signaler to be woken up. We still wake up the
1052*a85cb24fSFrançois Tigeot 		 * waiter in order to handle the irq-seqno coherency
1053*a85cb24fSFrançois Tigeot 		 * issues (we may receive the interrupt before the
1054*a85cb24fSFrançois Tigeot 		 * seqno is written, see __i915_request_irq_complete())
1055*a85cb24fSFrançois Tigeot 		 * and to handle coalescing of multiple seqno updates
1056*a85cb24fSFrançois Tigeot 		 * and many waiters.
1057*a85cb24fSFrançois Tigeot 		 */
1058*a85cb24fSFrançois Tigeot 		if (i915_seqno_passed(intel_engine_get_seqno(engine),
1059*a85cb24fSFrançois Tigeot 				      wait->seqno) &&
1060*a85cb24fSFrançois Tigeot 		    !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
1061*a85cb24fSFrançois Tigeot 			      &wait->request->fence.flags))
1062*a85cb24fSFrançois Tigeot 			rq = i915_gem_request_get(wait->request);
1063*a85cb24fSFrançois Tigeot 
1064*a85cb24fSFrançois Tigeot 		wake_up_process(wait->tsk);
1065*a85cb24fSFrançois Tigeot 	} else {
1066*a85cb24fSFrançois Tigeot 		__intel_engine_disarm_breadcrumbs(engine);
1067*a85cb24fSFrançois Tigeot 	}
1068*a85cb24fSFrançois Tigeot 	lockmgr(&engine->breadcrumbs.irq_lock, LK_RELEASE);
1069*a85cb24fSFrançois Tigeot 
1070*a85cb24fSFrançois Tigeot 	if (rq) {
1071*a85cb24fSFrançois Tigeot 		dma_fence_signal(&rq->fence);
1072*a85cb24fSFrançois Tigeot 		i915_gem_request_put(rq);
1073*a85cb24fSFrançois Tigeot 	}
1074*a85cb24fSFrançois Tigeot 
1075*a85cb24fSFrançois Tigeot 	trace_intel_engine_notify(engine, wait);
1076e3adcf8fSFrançois Tigeot }
1077e3adcf8fSFrançois Tigeot 
1078477eb7f9SFrançois Tigeot static void vlv_c0_read(struct drm_i915_private *dev_priv,
1079477eb7f9SFrançois Tigeot 			struct intel_rps_ei *ei)
108024edb884SFrançois Tigeot {
1081*a85cb24fSFrançois Tigeot 	ei->ktime = ktime_get_raw();
1082477eb7f9SFrançois Tigeot 	ei->render_c0 = I915_READ(VLV_RENDER_C0_COUNT);
1083477eb7f9SFrançois Tigeot 	ei->media_c0 = I915_READ(VLV_MEDIA_C0_COUNT);
108424edb884SFrançois Tigeot }
108524edb884SFrançois Tigeot 
1086477eb7f9SFrançois Tigeot void gen6_rps_reset_ei(struct drm_i915_private *dev_priv)
1087477eb7f9SFrançois Tigeot {
10884be47400SFrançois Tigeot 	memset(&dev_priv->rps.ei, 0, sizeof(dev_priv->rps.ei));
108924edb884SFrançois Tigeot }
109024edb884SFrançois Tigeot 
1091477eb7f9SFrançois Tigeot static u32 vlv_wa_c0_ei(struct drm_i915_private *dev_priv, u32 pm_iir)
1092477eb7f9SFrançois Tigeot {
10934be47400SFrançois Tigeot 	const struct intel_rps_ei *prev = &dev_priv->rps.ei;
1094477eb7f9SFrançois Tigeot 	struct intel_rps_ei now;
1095477eb7f9SFrançois Tigeot 	u32 events = 0;
109624edb884SFrançois Tigeot 
10974be47400SFrançois Tigeot 	if ((pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) == 0)
1098477eb7f9SFrançois Tigeot 		return 0;
109924edb884SFrançois Tigeot 
1100477eb7f9SFrançois Tigeot 	vlv_c0_read(dev_priv, &now);
110124edb884SFrançois Tigeot 
1102*a85cb24fSFrançois Tigeot 	if (prev->ktime) {
11034be47400SFrançois Tigeot 		u64 time, c0;
1104*a85cb24fSFrançois Tigeot 		u32 render, media;
11054be47400SFrançois Tigeot 
1106*a85cb24fSFrançois Tigeot 		time = ktime_us_delta(now.ktime, prev->ktime);
11074be47400SFrançois Tigeot 
11084be47400SFrançois Tigeot 		time *= dev_priv->czclk_freq;
11094be47400SFrançois Tigeot 
11104be47400SFrançois Tigeot 		/* Workload can be split between render + media,
11114be47400SFrançois Tigeot 		 * e.g. SwapBuffers being blitted in X after being rendered in
11124be47400SFrançois Tigeot 		 * mesa. To account for this we need to combine both engines
11134be47400SFrançois Tigeot 		 * into our activity counter.
11144be47400SFrançois Tigeot 		 */
1115*a85cb24fSFrançois Tigeot 		render = now.render_c0 - prev->render_c0;
1116*a85cb24fSFrançois Tigeot 		media = now.media_c0 - prev->media_c0;
1117*a85cb24fSFrançois Tigeot 		c0 = max(render, media);
1118*a85cb24fSFrançois Tigeot 		c0 *= 1000 * 100 << 8; /* to usecs and scale to threshold% */
11194be47400SFrançois Tigeot 
11204be47400SFrançois Tigeot 		if (c0 > time * dev_priv->rps.up_threshold)
11214be47400SFrançois Tigeot 			events = GEN6_PM_RP_UP_THRESHOLD;
11224be47400SFrançois Tigeot 		else if (c0 < time * dev_priv->rps.down_threshold)
11234be47400SFrançois Tigeot 			events = GEN6_PM_RP_DOWN_THRESHOLD;
112424edb884SFrançois Tigeot 	}
112524edb884SFrançois Tigeot 
11264be47400SFrançois Tigeot 	dev_priv->rps.ei = now;
1127477eb7f9SFrançois Tigeot 	return events;
112824edb884SFrançois Tigeot }
112924edb884SFrançois Tigeot 
113019c468b4SFrançois Tigeot static bool any_waiters(struct drm_i915_private *dev_priv)
113119c468b4SFrançois Tigeot {
11328621f407SFrançois Tigeot 	struct intel_engine_cs *engine;
11331e12ee3bSFrançois Tigeot 	enum intel_engine_id id;
113419c468b4SFrançois Tigeot 
11351e12ee3bSFrançois Tigeot 	for_each_engine(engine, dev_priv, id)
1136303bf270SFrançois Tigeot 		if (intel_engine_has_waiter(engine))
113719c468b4SFrançois Tigeot 			return true;
113819c468b4SFrançois Tigeot 
113919c468b4SFrançois Tigeot 	return false;
114019c468b4SFrançois Tigeot }
114119c468b4SFrançois Tigeot 
1142abf1f4f4SFrançois Tigeot static void gen6_pm_rps_work(struct work_struct *work)
1143e3adcf8fSFrançois Tigeot {
1144ba55f2f5SFrançois Tigeot 	struct drm_i915_private *dev_priv =
1145ba55f2f5SFrançois Tigeot 		container_of(work, struct drm_i915_private, rps.work);
1146*a85cb24fSFrançois Tigeot 	bool client_boost = false;
114719c468b4SFrançois Tigeot 	int new_delay, adj, min, max;
1148*a85cb24fSFrançois Tigeot 	u32 pm_iir = 0;
1149e3adcf8fSFrançois Tigeot 
11505e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
1151*a85cb24fSFrançois Tigeot 	if (dev_priv->rps.interrupts_enabled) {
1152*a85cb24fSFrançois Tigeot 		pm_iir = fetch_and_zero(&dev_priv->rps.pm_iir);
1153*a85cb24fSFrançois Tigeot 		client_boost = fetch_and_zero(&dev_priv->rps.client_boost);
11542c9916cdSFrançois Tigeot 	}
11555e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
11569edbd4a0SFrançois Tigeot 
11579edbd4a0SFrançois Tigeot 	/* Make sure we didn't queue anything we're not going to process. */
1158ba55f2f5SFrançois Tigeot 	WARN_ON(pm_iir & ~dev_priv->pm_rps_events);
115919c468b4SFrançois Tigeot 	if ((pm_iir & dev_priv->pm_rps_events) == 0 && !client_boost)
1160*a85cb24fSFrançois Tigeot 		goto out;
1161e3adcf8fSFrançois Tigeot 
1162a2fdbec6SFrançois Tigeot 	mutex_lock(&dev_priv->rps.hw_lock);
1163e3adcf8fSFrançois Tigeot 
1164477eb7f9SFrançois Tigeot 	pm_iir |= vlv_wa_c0_ei(dev_priv, pm_iir);
1165477eb7f9SFrançois Tigeot 
11669edbd4a0SFrançois Tigeot 	adj = dev_priv->rps.last_adj;
116719c468b4SFrançois Tigeot 	new_delay = dev_priv->rps.cur_freq;
116819c468b4SFrançois Tigeot 	min = dev_priv->rps.min_freq_softlimit;
116919c468b4SFrançois Tigeot 	max = dev_priv->rps.max_freq_softlimit;
117087df8fc6SFrançois Tigeot 	if (client_boost || any_waiters(dev_priv))
117187df8fc6SFrançois Tigeot 		max = dev_priv->rps.max_freq;
117287df8fc6SFrançois Tigeot 	if (client_boost && new_delay < dev_priv->rps.boost_freq) {
117387df8fc6SFrançois Tigeot 		new_delay = dev_priv->rps.boost_freq;
117419c468b4SFrançois Tigeot 		adj = 0;
117519c468b4SFrançois Tigeot 	} else if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
11769edbd4a0SFrançois Tigeot 		if (adj > 0)
11779edbd4a0SFrançois Tigeot 			adj *= 2;
117819c468b4SFrançois Tigeot 		else /* CHV needs even encode values */
117919c468b4SFrançois Tigeot 			adj = IS_CHERRYVIEW(dev_priv) ? 2 : 1;
1180*a85cb24fSFrançois Tigeot 
1181*a85cb24fSFrançois Tigeot 		if (new_delay >= dev_priv->rps.max_freq_softlimit)
118219c468b4SFrançois Tigeot 			adj = 0;
118387df8fc6SFrançois Tigeot 	} else if (client_boost || any_waiters(dev_priv)) {
118419c468b4SFrançois Tigeot 		adj = 0;
11859edbd4a0SFrançois Tigeot 	} else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
1186ba55f2f5SFrançois Tigeot 		if (dev_priv->rps.cur_freq > dev_priv->rps.efficient_freq)
1187ba55f2f5SFrançois Tigeot 			new_delay = dev_priv->rps.efficient_freq;
1188*a85cb24fSFrançois Tigeot 		else if (dev_priv->rps.cur_freq > dev_priv->rps.min_freq_softlimit)
1189ba55f2f5SFrançois Tigeot 			new_delay = dev_priv->rps.min_freq_softlimit;
11909edbd4a0SFrançois Tigeot 		adj = 0;
11919edbd4a0SFrançois Tigeot 	} else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) {
11929edbd4a0SFrançois Tigeot 		if (adj < 0)
11939edbd4a0SFrançois Tigeot 			adj *= 2;
119419c468b4SFrançois Tigeot 		else /* CHV needs even encode values */
119519c468b4SFrançois Tigeot 			adj = IS_CHERRYVIEW(dev_priv) ? -2 : -1;
1196*a85cb24fSFrançois Tigeot 
1197*a85cb24fSFrançois Tigeot 		if (new_delay <= dev_priv->rps.min_freq_softlimit)
1198*a85cb24fSFrançois Tigeot 			adj = 0;
11999edbd4a0SFrançois Tigeot 	} else { /* unknown event */
120019c468b4SFrançois Tigeot 		adj = 0;
12019edbd4a0SFrançois Tigeot 	}
1202e3adcf8fSFrançois Tigeot 
120319c468b4SFrançois Tigeot 	dev_priv->rps.last_adj = adj;
120419c468b4SFrançois Tigeot 
1205abf1f4f4SFrançois Tigeot 	/* sysfs frequency interfaces may have snuck in while servicing the
1206abf1f4f4SFrançois Tigeot 	 * interrupt
1207e3adcf8fSFrançois Tigeot 	 */
120819c468b4SFrançois Tigeot 	new_delay += adj;
120919c468b4SFrançois Tigeot 	new_delay = clamp_t(int, new_delay, min, max);
12109edbd4a0SFrançois Tigeot 
1211*a85cb24fSFrançois Tigeot 	if (intel_set_rps(dev_priv, new_delay)) {
1212*a85cb24fSFrançois Tigeot 		DRM_DEBUG_DRIVER("Failed to set new GPU frequency\n");
1213*a85cb24fSFrançois Tigeot 		dev_priv->rps.last_adj = 0;
1214*a85cb24fSFrançois Tigeot 	}
12155d0b1887SFrançois Tigeot 
1216a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev_priv->rps.hw_lock);
1217*a85cb24fSFrançois Tigeot 
1218*a85cb24fSFrançois Tigeot out:
1219*a85cb24fSFrançois Tigeot 	/* Make sure not to corrupt PMIMR state used by ringbuffer on GEN6 */
1220*a85cb24fSFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
1221*a85cb24fSFrançois Tigeot 	if (dev_priv->rps.interrupts_enabled)
1222*a85cb24fSFrançois Tigeot 		gen6_unmask_pm_irq(dev_priv, dev_priv->pm_rps_events);
1223*a85cb24fSFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
1224e3adcf8fSFrançois Tigeot }
1225e3adcf8fSFrançois Tigeot 
12265e269720SFrançois Tigeot 
122700640ec9SFrançois Tigeot /**
122800640ec9SFrançois Tigeot  * ivybridge_parity_work - Workqueue called when a parity error interrupt
122900640ec9SFrançois Tigeot  * occurred.
123000640ec9SFrançois Tigeot  * @work: workqueue struct
123100640ec9SFrançois Tigeot  *
123200640ec9SFrançois Tigeot  * Doesn't actually do anything except notify userspace. As a consequence of
123300640ec9SFrançois Tigeot  * this event, userspace should try to remap the bad rows since statistically
123400640ec9SFrançois Tigeot  * it is likely the same row is more likely to go bad again.
123500640ec9SFrançois Tigeot  */
123600640ec9SFrançois Tigeot static void ivybridge_parity_work(struct work_struct *work)
123700640ec9SFrançois Tigeot {
1238ba55f2f5SFrançois Tigeot 	struct drm_i915_private *dev_priv =
1239ba55f2f5SFrançois Tigeot 		container_of(work, struct drm_i915_private, l3_parity.error_work);
124000640ec9SFrançois Tigeot 	u32 error_status, row, bank, subbank;
12419edbd4a0SFrançois Tigeot 	char *parity_event[6];
124200640ec9SFrançois Tigeot 	uint32_t misccpctl;
12439edbd4a0SFrançois Tigeot 	uint8_t slice = 0;
124400640ec9SFrançois Tigeot 
124500640ec9SFrançois Tigeot 	/* We must turn off DOP level clock gating to access the L3 registers.
124600640ec9SFrançois Tigeot 	 * In order to prevent a get/put style interface, acquire struct mutex
124700640ec9SFrançois Tigeot 	 * any time we access those registers.
124800640ec9SFrançois Tigeot 	 */
1249303bf270SFrançois Tigeot 	mutex_lock(&dev_priv->drm.struct_mutex);
125000640ec9SFrançois Tigeot 
12519edbd4a0SFrançois Tigeot 	/* If we've screwed up tracking, just let the interrupt fire again */
12529edbd4a0SFrançois Tigeot 	if (WARN_ON(!dev_priv->l3_parity.which_slice))
12539edbd4a0SFrançois Tigeot 		goto out;
12549edbd4a0SFrançois Tigeot 
125500640ec9SFrançois Tigeot 	misccpctl = I915_READ(GEN7_MISCCPCTL);
125600640ec9SFrançois Tigeot 	I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
125700640ec9SFrançois Tigeot 	POSTING_READ(GEN7_MISCCPCTL);
125800640ec9SFrançois Tigeot 
12599edbd4a0SFrançois Tigeot 	while ((slice = ffs(dev_priv->l3_parity.which_slice)) != 0) {
1260aee94f86SFrançois Tigeot 		i915_reg_t reg;
12619edbd4a0SFrançois Tigeot 
12629edbd4a0SFrançois Tigeot 		slice--;
12638621f407SFrançois Tigeot 		if (WARN_ON_ONCE(slice >= NUM_L3_SLICES(dev_priv)))
12649edbd4a0SFrançois Tigeot 			break;
12659edbd4a0SFrançois Tigeot 
12669edbd4a0SFrançois Tigeot 		dev_priv->l3_parity.which_slice &= ~(1<<slice);
12679edbd4a0SFrançois Tigeot 
1268aee94f86SFrançois Tigeot 		reg = GEN7_L3CDERRST1(slice);
12699edbd4a0SFrançois Tigeot 
12709edbd4a0SFrançois Tigeot 		error_status = I915_READ(reg);
127100640ec9SFrançois Tigeot 		row = GEN7_PARITY_ERROR_ROW(error_status);
127200640ec9SFrançois Tigeot 		bank = GEN7_PARITY_ERROR_BANK(error_status);
127300640ec9SFrançois Tigeot 		subbank = GEN7_PARITY_ERROR_SUBBANK(error_status);
127400640ec9SFrançois Tigeot 
12759edbd4a0SFrançois Tigeot 		I915_WRITE(reg, GEN7_PARITY_ERROR_VALID | GEN7_L3CDERRST1_ENABLE);
12769edbd4a0SFrançois Tigeot 		POSTING_READ(reg);
12779edbd4a0SFrançois Tigeot 
12789edbd4a0SFrançois Tigeot 		parity_event[0] = I915_L3_PARITY_UEVENT "=1";
1279c0e85e96SFrançois Tigeot 		parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
1280c0e85e96SFrançois Tigeot 		parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
1281c0e85e96SFrançois Tigeot 		parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
1282c0e85e96SFrançois Tigeot 		parity_event[4] = kasprintf(GFP_KERNEL, "SLICE=%d", slice);
12839edbd4a0SFrançois Tigeot 		parity_event[5] = NULL;
12849edbd4a0SFrançois Tigeot 
1285303bf270SFrançois Tigeot 		kobject_uevent_env(&dev_priv->drm.primary->kdev->kobj,
12869edbd4a0SFrançois Tigeot 				   KOBJ_CHANGE, parity_event);
12879edbd4a0SFrançois Tigeot 
12889edbd4a0SFrançois Tigeot 		DRM_DEBUG("Parity error: Slice = %d, Row = %d, Bank = %d, Sub bank = %d.\n",
12899edbd4a0SFrançois Tigeot 			  slice, row, bank, subbank);
12909edbd4a0SFrançois Tigeot 
12919edbd4a0SFrançois Tigeot 		kfree(parity_event[4]);
12929edbd4a0SFrançois Tigeot 		kfree(parity_event[3]);
12939edbd4a0SFrançois Tigeot 		kfree(parity_event[2]);
12949edbd4a0SFrançois Tigeot 		kfree(parity_event[1]);
12959edbd4a0SFrançois Tigeot 	}
129600640ec9SFrançois Tigeot 
129700640ec9SFrançois Tigeot 	I915_WRITE(GEN7_MISCCPCTL, misccpctl);
129800640ec9SFrançois Tigeot 
12999edbd4a0SFrançois Tigeot out:
13009edbd4a0SFrançois Tigeot 	WARN_ON(dev_priv->l3_parity.which_slice);
13015e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
13028621f407SFrançois Tigeot 	gen5_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv));
13035e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
130400640ec9SFrançois Tigeot 
1305303bf270SFrançois Tigeot 	mutex_unlock(&dev_priv->drm.struct_mutex);
130600640ec9SFrançois Tigeot }
130700640ec9SFrançois Tigeot 
13088621f407SFrançois Tigeot static void ivybridge_parity_error_irq_handler(struct drm_i915_private *dev_priv,
13098621f407SFrançois Tigeot 					       u32 iir)
131000640ec9SFrançois Tigeot {
13118621f407SFrançois Tigeot 	if (!HAS_L3_DPF(dev_priv))
131200640ec9SFrançois Tigeot 		return;
131300640ec9SFrançois Tigeot 
131400640ec9SFrançois Tigeot 	lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
13158621f407SFrançois Tigeot 	gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv));
131600640ec9SFrançois Tigeot 	lockmgr(&dev_priv->irq_lock, LK_RELEASE);
131700640ec9SFrançois Tigeot 
13188621f407SFrançois Tigeot 	iir &= GT_PARITY_ERROR(dev_priv);
13199edbd4a0SFrançois Tigeot 	if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1)
13209edbd4a0SFrançois Tigeot 		dev_priv->l3_parity.which_slice |= 1 << 1;
13219edbd4a0SFrançois Tigeot 
13229edbd4a0SFrançois Tigeot 	if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT)
13239edbd4a0SFrançois Tigeot 		dev_priv->l3_parity.which_slice |= 1 << 0;
13249edbd4a0SFrançois Tigeot 
132500640ec9SFrançois Tigeot 	queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
132600640ec9SFrançois Tigeot }
132700640ec9SFrançois Tigeot 
13288621f407SFrançois Tigeot static void ilk_gt_irq_handler(struct drm_i915_private *dev_priv,
13299edbd4a0SFrançois Tigeot 			       u32 gt_iir)
13309edbd4a0SFrançois Tigeot {
1331303bf270SFrançois Tigeot 	if (gt_iir & GT_RENDER_USER_INTERRUPT)
13321e12ee3bSFrançois Tigeot 		notify_ring(dev_priv->engine[RCS]);
13339edbd4a0SFrançois Tigeot 	if (gt_iir & ILK_BSD_USER_INTERRUPT)
13341e12ee3bSFrançois Tigeot 		notify_ring(dev_priv->engine[VCS]);
13359edbd4a0SFrançois Tigeot }
13369edbd4a0SFrançois Tigeot 
13378621f407SFrançois Tigeot static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
1338a2296444SFrançois Tigeot 			       u32 gt_iir)
1339a2296444SFrançois Tigeot {
1340303bf270SFrançois Tigeot 	if (gt_iir & GT_RENDER_USER_INTERRUPT)
13411e12ee3bSFrançois Tigeot 		notify_ring(dev_priv->engine[RCS]);
13425d0b1887SFrançois Tigeot 	if (gt_iir & GT_BSD_USER_INTERRUPT)
13431e12ee3bSFrançois Tigeot 		notify_ring(dev_priv->engine[VCS]);
13445d0b1887SFrançois Tigeot 	if (gt_iir & GT_BLT_USER_INTERRUPT)
13451e12ee3bSFrançois Tigeot 		notify_ring(dev_priv->engine[BCS]);
1346a2296444SFrançois Tigeot 
13475d0b1887SFrançois Tigeot 	if (gt_iir & (GT_BLT_CS_ERROR_INTERRUPT |
13485d0b1887SFrançois Tigeot 		      GT_BSD_CS_ERROR_INTERRUPT |
13492c9916cdSFrançois Tigeot 		      GT_RENDER_CS_MASTER_ERROR_INTERRUPT))
13502c9916cdSFrançois Tigeot 		DRM_DEBUG("Command parser error, gt_iir 0x%08x\n", gt_iir);
1351a2296444SFrançois Tigeot 
13528621f407SFrançois Tigeot 	if (gt_iir & GT_PARITY_ERROR(dev_priv))
13538621f407SFrançois Tigeot 		ivybridge_parity_error_irq_handler(dev_priv, gt_iir);
1354a2296444SFrançois Tigeot }
1355a2296444SFrançois Tigeot 
1356aee94f86SFrançois Tigeot static __always_inline void
13578621f407SFrançois Tigeot gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
1358aee94f86SFrançois Tigeot {
1359*a85cb24fSFrançois Tigeot 	bool tasklet = false;
1360*a85cb24fSFrançois Tigeot 
1361*a85cb24fSFrançois Tigeot 	if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) {
1362*a85cb24fSFrançois Tigeot 		set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
1363*a85cb24fSFrançois Tigeot 		tasklet = true;
1364*a85cb24fSFrançois Tigeot 	}
1365*a85cb24fSFrançois Tigeot 
1366*a85cb24fSFrançois Tigeot 	if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) {
13678621f407SFrançois Tigeot 		notify_ring(engine);
1368*a85cb24fSFrançois Tigeot 		tasklet |= i915.enable_guc_submission;
1369*a85cb24fSFrançois Tigeot 	}
1370*a85cb24fSFrançois Tigeot 
1371*a85cb24fSFrançois Tigeot 	if (tasklet)
1372*a85cb24fSFrançois Tigeot 		tasklet_hi_schedule(&engine->irq_tasklet);
1373aee94f86SFrançois Tigeot }
1374aee94f86SFrançois Tigeot 
13758621f407SFrançois Tigeot static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv,
13768621f407SFrançois Tigeot 				   u32 master_ctl,
13778621f407SFrançois Tigeot 				   u32 gt_iir[4])
1378a2296444SFrançois Tigeot {
1379183e2373SFrançois Tigeot 	irqreturn_t ret = IRQ_NONE;
1380a2296444SFrançois Tigeot 
13819edbd4a0SFrançois Tigeot 	if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
13828621f407SFrançois Tigeot 		gt_iir[0] = I915_READ_FW(GEN8_GT_IIR(0));
13838621f407SFrançois Tigeot 		if (gt_iir[0]) {
13848621f407SFrançois Tigeot 			I915_WRITE_FW(GEN8_GT_IIR(0), gt_iir[0]);
1385183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
13869edbd4a0SFrançois Tigeot 		} else
13879edbd4a0SFrançois Tigeot 			DRM_ERROR("The master control interrupt lied (GT0)!\n");
13889edbd4a0SFrançois Tigeot 	}
1389a2296444SFrançois Tigeot 
1390ba55f2f5SFrançois Tigeot 	if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) {
13918621f407SFrançois Tigeot 		gt_iir[1] = I915_READ_FW(GEN8_GT_IIR(1));
13928621f407SFrançois Tigeot 		if (gt_iir[1]) {
13938621f407SFrançois Tigeot 			I915_WRITE_FW(GEN8_GT_IIR(1), gt_iir[1]);
1394183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
13959edbd4a0SFrançois Tigeot 		} else
13969edbd4a0SFrançois Tigeot 			DRM_ERROR("The master control interrupt lied (GT1)!\n");
13979edbd4a0SFrançois Tigeot 	}
1398a2296444SFrançois Tigeot 
139919c468b4SFrançois Tigeot 	if (master_ctl & GEN8_GT_VECS_IRQ) {
14008621f407SFrançois Tigeot 		gt_iir[3] = I915_READ_FW(GEN8_GT_IIR(3));
14018621f407SFrançois Tigeot 		if (gt_iir[3]) {
14028621f407SFrançois Tigeot 			I915_WRITE_FW(GEN8_GT_IIR(3), gt_iir[3]);
1403183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
140419c468b4SFrançois Tigeot 		} else
140519c468b4SFrançois Tigeot 			DRM_ERROR("The master control interrupt lied (GT3)!\n");
140619c468b4SFrançois Tigeot 	}
140719c468b4SFrançois Tigeot 
14084be47400SFrançois Tigeot 	if (master_ctl & (GEN8_GT_PM_IRQ | GEN8_GT_GUC_IRQ)) {
14098621f407SFrançois Tigeot 		gt_iir[2] = I915_READ_FW(GEN8_GT_IIR(2));
14104be47400SFrançois Tigeot 		if (gt_iir[2] & (dev_priv->pm_rps_events |
14114be47400SFrançois Tigeot 				 dev_priv->pm_guc_events)) {
141219c468b4SFrançois Tigeot 			I915_WRITE_FW(GEN8_GT_IIR(2),
14134be47400SFrançois Tigeot 				      gt_iir[2] & (dev_priv->pm_rps_events |
14144be47400SFrançois Tigeot 						   dev_priv->pm_guc_events));
1415183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
1416ba55f2f5SFrançois Tigeot 		} else
1417ba55f2f5SFrançois Tigeot 			DRM_ERROR("The master control interrupt lied (PM)!\n");
1418ba55f2f5SFrançois Tigeot 	}
1419ba55f2f5SFrançois Tigeot 
1420183e2373SFrançois Tigeot 	return ret;
1421a2296444SFrançois Tigeot }
1422a2296444SFrançois Tigeot 
14238621f407SFrançois Tigeot static void gen8_gt_irq_handler(struct drm_i915_private *dev_priv,
14248621f407SFrançois Tigeot 				u32 gt_iir[4])
14258621f407SFrançois Tigeot {
14268621f407SFrançois Tigeot 	if (gt_iir[0]) {
14271e12ee3bSFrançois Tigeot 		gen8_cs_irq_handler(dev_priv->engine[RCS],
14288621f407SFrançois Tigeot 				    gt_iir[0], GEN8_RCS_IRQ_SHIFT);
14291e12ee3bSFrançois Tigeot 		gen8_cs_irq_handler(dev_priv->engine[BCS],
14308621f407SFrançois Tigeot 				    gt_iir[0], GEN8_BCS_IRQ_SHIFT);
14318621f407SFrançois Tigeot 	}
14328621f407SFrançois Tigeot 
14338621f407SFrançois Tigeot 	if (gt_iir[1]) {
14341e12ee3bSFrançois Tigeot 		gen8_cs_irq_handler(dev_priv->engine[VCS],
14358621f407SFrançois Tigeot 				    gt_iir[1], GEN8_VCS1_IRQ_SHIFT);
14361e12ee3bSFrançois Tigeot 		gen8_cs_irq_handler(dev_priv->engine[VCS2],
14378621f407SFrançois Tigeot 				    gt_iir[1], GEN8_VCS2_IRQ_SHIFT);
14388621f407SFrançois Tigeot 	}
14398621f407SFrançois Tigeot 
14408621f407SFrançois Tigeot 	if (gt_iir[3])
14411e12ee3bSFrançois Tigeot 		gen8_cs_irq_handler(dev_priv->engine[VECS],
14428621f407SFrançois Tigeot 				    gt_iir[3], GEN8_VECS_IRQ_SHIFT);
14438621f407SFrançois Tigeot 
14448621f407SFrançois Tigeot 	if (gt_iir[2] & dev_priv->pm_rps_events)
14458621f407SFrançois Tigeot 		gen6_rps_irq_handler(dev_priv, gt_iir[2]);
14464be47400SFrançois Tigeot 
14474be47400SFrançois Tigeot 	if (gt_iir[2] & dev_priv->pm_guc_events)
14484be47400SFrançois Tigeot 		gen9_guc_irq_handler(dev_priv, gt_iir[2]);
14498621f407SFrançois Tigeot }
14508621f407SFrançois Tigeot 
1451a05eeebfSFrançois Tigeot static bool bxt_port_hotplug_long_detect(enum port port, u32 val)
145224edb884SFrançois Tigeot {
145324edb884SFrançois Tigeot 	switch (port) {
145424edb884SFrançois Tigeot 	case PORT_A:
1455352ff8bdSFrançois Tigeot 		return val & PORTA_HOTPLUG_LONG_DETECT;
1456352ff8bdSFrançois Tigeot 	case PORT_B:
1457352ff8bdSFrançois Tigeot 		return val & PORTB_HOTPLUG_LONG_DETECT;
1458352ff8bdSFrançois Tigeot 	case PORT_C:
1459352ff8bdSFrançois Tigeot 		return val & PORTC_HOTPLUG_LONG_DETECT;
1460352ff8bdSFrançois Tigeot 	default:
1461352ff8bdSFrançois Tigeot 		return false;
1462352ff8bdSFrançois Tigeot 	}
1463352ff8bdSFrançois Tigeot }
1464352ff8bdSFrançois Tigeot 
1465352ff8bdSFrançois Tigeot static bool spt_port_hotplug2_long_detect(enum port port, u32 val)
1466352ff8bdSFrançois Tigeot {
1467352ff8bdSFrançois Tigeot 	switch (port) {
1468352ff8bdSFrançois Tigeot 	case PORT_E:
1469352ff8bdSFrançois Tigeot 		return val & PORTE_HOTPLUG_LONG_DETECT;
1470352ff8bdSFrançois Tigeot 	default:
1471352ff8bdSFrançois Tigeot 		return false;
1472352ff8bdSFrançois Tigeot 	}
1473352ff8bdSFrançois Tigeot }
1474352ff8bdSFrançois Tigeot 
1475352ff8bdSFrançois Tigeot static bool spt_port_hotplug_long_detect(enum port port, u32 val)
1476352ff8bdSFrançois Tigeot {
1477352ff8bdSFrançois Tigeot 	switch (port) {
1478352ff8bdSFrançois Tigeot 	case PORT_A:
1479352ff8bdSFrançois Tigeot 		return val & PORTA_HOTPLUG_LONG_DETECT;
148024edb884SFrançois Tigeot 	case PORT_B:
1481a05eeebfSFrançois Tigeot 		return val & PORTB_HOTPLUG_LONG_DETECT;
148224edb884SFrançois Tigeot 	case PORT_C:
1483a05eeebfSFrançois Tigeot 		return val & PORTC_HOTPLUG_LONG_DETECT;
148424edb884SFrançois Tigeot 	case PORT_D:
1485a05eeebfSFrançois Tigeot 		return val & PORTD_HOTPLUG_LONG_DETECT;
1486a05eeebfSFrançois Tigeot 	default:
1487a05eeebfSFrançois Tigeot 		return false;
148824edb884SFrançois Tigeot 	}
148924edb884SFrançois Tigeot }
149024edb884SFrançois Tigeot 
1491352ff8bdSFrançois Tigeot static bool ilk_port_hotplug_long_detect(enum port port, u32 val)
1492352ff8bdSFrançois Tigeot {
1493352ff8bdSFrançois Tigeot 	switch (port) {
1494352ff8bdSFrançois Tigeot 	case PORT_A:
1495352ff8bdSFrançois Tigeot 		return val & DIGITAL_PORTA_HOTPLUG_LONG_DETECT;
1496352ff8bdSFrançois Tigeot 	default:
1497352ff8bdSFrançois Tigeot 		return false;
1498352ff8bdSFrançois Tigeot 	}
1499352ff8bdSFrançois Tigeot }
1500352ff8bdSFrançois Tigeot 
1501a05eeebfSFrançois Tigeot static bool pch_port_hotplug_long_detect(enum port port, u32 val)
150224edb884SFrançois Tigeot {
150324edb884SFrançois Tigeot 	switch (port) {
150424edb884SFrançois Tigeot 	case PORT_B:
1505a05eeebfSFrançois Tigeot 		return val & PORTB_HOTPLUG_LONG_DETECT;
150624edb884SFrançois Tigeot 	case PORT_C:
1507a05eeebfSFrançois Tigeot 		return val & PORTC_HOTPLUG_LONG_DETECT;
150824edb884SFrançois Tigeot 	case PORT_D:
1509a05eeebfSFrançois Tigeot 		return val & PORTD_HOTPLUG_LONG_DETECT;
151024edb884SFrançois Tigeot 	default:
1511a05eeebfSFrançois Tigeot 		return false;
151224edb884SFrançois Tigeot 	}
151324edb884SFrançois Tigeot }
151424edb884SFrançois Tigeot 
1515a05eeebfSFrançois Tigeot static bool i9xx_port_hotplug_long_detect(enum port port, u32 val)
15168e26cdf6SFrançois Tigeot {
1517a05eeebfSFrançois Tigeot 	switch (port) {
1518a05eeebfSFrançois Tigeot 	case PORT_B:
1519a05eeebfSFrançois Tigeot 		return val & PORTB_HOTPLUG_INT_LONG_PULSE;
1520a05eeebfSFrançois Tigeot 	case PORT_C:
1521a05eeebfSFrançois Tigeot 		return val & PORTC_HOTPLUG_INT_LONG_PULSE;
1522a05eeebfSFrançois Tigeot 	case PORT_D:
1523a05eeebfSFrançois Tigeot 		return val & PORTD_HOTPLUG_INT_LONG_PULSE;
1524a05eeebfSFrançois Tigeot 	default:
1525a05eeebfSFrançois Tigeot 		return false;
1526a05eeebfSFrançois Tigeot 	}
1527a05eeebfSFrançois Tigeot }
1528a05eeebfSFrançois Tigeot 
1529352ff8bdSFrançois Tigeot /*
1530352ff8bdSFrançois Tigeot  * Get a bit mask of pins that have triggered, and which ones may be long.
1531352ff8bdSFrançois Tigeot  * This can be called multiple times with the same masks to accumulate
1532352ff8bdSFrançois Tigeot  * hotplug detection results from several registers.
1533352ff8bdSFrançois Tigeot  *
1534352ff8bdSFrançois Tigeot  * Note that the caller is expected to zero out the masks initially.
1535352ff8bdSFrançois Tigeot  */
1536a05eeebfSFrançois Tigeot static void intel_get_hpd_pins(u32 *pin_mask, u32 *long_mask,
1537a05eeebfSFrançois Tigeot 			     u32 hotplug_trigger, u32 dig_hotplug_reg,
1538a05eeebfSFrançois Tigeot 			     const u32 hpd[HPD_NUM_PINS],
1539a05eeebfSFrançois Tigeot 			     bool long_pulse_detect(enum port port, u32 val))
1540a05eeebfSFrançois Tigeot {
154124edb884SFrançois Tigeot 	enum port port;
1542a05eeebfSFrançois Tigeot 	int i;
15435d0b1887SFrançois Tigeot 
1544a05eeebfSFrançois Tigeot 	for_each_hpd_pin(i) {
1545a05eeebfSFrançois Tigeot 		if ((hpd[i] & hotplug_trigger) == 0)
154624edb884SFrançois Tigeot 			continue;
15478e26cdf6SFrançois Tigeot 
1548a05eeebfSFrançois Tigeot 		*pin_mask |= BIT(i);
154924edb884SFrançois Tigeot 
1550a05eeebfSFrançois Tigeot 		if (!intel_hpd_pin_to_port(i, &port))
15518e26cdf6SFrançois Tigeot 			continue;
15528e26cdf6SFrançois Tigeot 
1553a05eeebfSFrançois Tigeot 		if (long_pulse_detect(port, dig_hotplug_reg))
1554a05eeebfSFrançois Tigeot 			*long_mask |= BIT(i);
155524edb884SFrançois Tigeot 	}
155624edb884SFrançois Tigeot 
1557a05eeebfSFrançois Tigeot 	DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x, dig 0x%08x, pins 0x%08x\n",
1558a05eeebfSFrançois Tigeot 			 hotplug_trigger, dig_hotplug_reg, *pin_mask);
15598e26cdf6SFrançois Tigeot 
15608e26cdf6SFrançois Tigeot }
15618e26cdf6SFrançois Tigeot 
15621487f786SFrançois Tigeot static void gmbus_irq_handler(struct drm_i915_private *dev_priv)
1563a2fdbec6SFrançois Tigeot {
1564a2fdbec6SFrançois Tigeot 	wake_up_all(&dev_priv->gmbus_wait_queue);
1565a2fdbec6SFrançois Tigeot }
1566a2fdbec6SFrançois Tigeot 
15671487f786SFrançois Tigeot static void dp_aux_irq_handler(struct drm_i915_private *dev_priv)
1568a2fdbec6SFrançois Tigeot {
1569a2fdbec6SFrançois Tigeot 	wake_up_all(&dev_priv->gmbus_wait_queue);
1570a2fdbec6SFrançois Tigeot }
1571a2fdbec6SFrançois Tigeot 
15729edbd4a0SFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
15731487f786SFrançois Tigeot static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
15741487f786SFrançois Tigeot 					 enum i915_pipe pipe,
15759edbd4a0SFrançois Tigeot 					 uint32_t crc0, uint32_t crc1,
15769edbd4a0SFrançois Tigeot 					 uint32_t crc2, uint32_t crc3,
15779edbd4a0SFrançois Tigeot 					 uint32_t crc4)
15785d0b1887SFrançois Tigeot {
15799edbd4a0SFrançois Tigeot 	struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
15809edbd4a0SFrançois Tigeot 	struct intel_pipe_crc_entry *entry;
1581*a85cb24fSFrançois Tigeot 	struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
1582*a85cb24fSFrançois Tigeot 	struct drm_driver *driver = dev_priv->drm.driver;
1583*a85cb24fSFrançois Tigeot 	uint32_t crcs[5];
15849edbd4a0SFrançois Tigeot 	int head, tail;
15859edbd4a0SFrançois Tigeot 
15861e12ee3bSFrançois Tigeot 	lockmgr(&pipe_crc->lock, LK_EXCLUSIVE);
1587*a85cb24fSFrançois Tigeot 	if (pipe_crc->source) {
15889edbd4a0SFrançois Tigeot 		if (!pipe_crc->entries) {
15891e12ee3bSFrançois Tigeot 			lockmgr(&pipe_crc->lock, LK_RELEASE);
15902c9916cdSFrançois Tigeot 			DRM_DEBUG_KMS("spurious interrupt\n");
15919edbd4a0SFrançois Tigeot 			return;
15929edbd4a0SFrançois Tigeot 		}
15939edbd4a0SFrançois Tigeot 
15949edbd4a0SFrançois Tigeot 		head = pipe_crc->head;
15959edbd4a0SFrançois Tigeot 		tail = pipe_crc->tail;
15969edbd4a0SFrançois Tigeot 
15979edbd4a0SFrançois Tigeot 		if (CIRC_SPACE(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) {
15981e12ee3bSFrançois Tigeot 			lockmgr(&pipe_crc->lock, LK_RELEASE);
15999edbd4a0SFrançois Tigeot 			DRM_ERROR("CRC buffer overflowing\n");
16009edbd4a0SFrançois Tigeot 			return;
16019edbd4a0SFrançois Tigeot 		}
16029edbd4a0SFrançois Tigeot 
16039edbd4a0SFrançois Tigeot 		entry = &pipe_crc->entries[head];
16049edbd4a0SFrançois Tigeot 
1605*a85cb24fSFrançois Tigeot 		entry->frame = driver->get_vblank_counter(&dev_priv->drm, pipe);
16069edbd4a0SFrançois Tigeot 		entry->crc[0] = crc0;
16079edbd4a0SFrançois Tigeot 		entry->crc[1] = crc1;
16089edbd4a0SFrançois Tigeot 		entry->crc[2] = crc2;
16099edbd4a0SFrançois Tigeot 		entry->crc[3] = crc3;
16109edbd4a0SFrançois Tigeot 		entry->crc[4] = crc4;
16119edbd4a0SFrançois Tigeot 
16129edbd4a0SFrançois Tigeot 		head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1);
16139edbd4a0SFrançois Tigeot 		pipe_crc->head = head;
16149edbd4a0SFrançois Tigeot 
16151e12ee3bSFrançois Tigeot 		lockmgr(&pipe_crc->lock, LK_RELEASE);
16169edbd4a0SFrançois Tigeot 
16179edbd4a0SFrançois Tigeot 		wake_up_interruptible(&pipe_crc->wq);
1618*a85cb24fSFrançois Tigeot 	} else {
1619*a85cb24fSFrançois Tigeot 		/*
1620*a85cb24fSFrançois Tigeot 		 * For some not yet identified reason, the first CRC is
1621*a85cb24fSFrançois Tigeot 		 * bonkers. So let's just wait for the next vblank and read
1622*a85cb24fSFrançois Tigeot 		 * out the buggy result.
1623*a85cb24fSFrançois Tigeot 		 *
1624*a85cb24fSFrançois Tigeot 		 * On CHV sometimes the second CRC is bonkers as well, so
1625*a85cb24fSFrançois Tigeot 		 * don't trust that one either.
1626*a85cb24fSFrançois Tigeot 		 */
1627*a85cb24fSFrançois Tigeot 		if (pipe_crc->skipped == 0 ||
1628*a85cb24fSFrançois Tigeot 		    (IS_CHERRYVIEW(dev_priv) && pipe_crc->skipped == 1)) {
1629*a85cb24fSFrançois Tigeot 			pipe_crc->skipped++;
1630*a85cb24fSFrançois Tigeot 			lockmgr(&pipe_crc->lock, LK_RELEASE);
1631*a85cb24fSFrançois Tigeot 			return;
1632*a85cb24fSFrançois Tigeot 		}
1633*a85cb24fSFrançois Tigeot 		lockmgr(&pipe_crc->lock, LK_RELEASE);
1634*a85cb24fSFrançois Tigeot 		crcs[0] = crc0;
1635*a85cb24fSFrançois Tigeot 		crcs[1] = crc1;
1636*a85cb24fSFrançois Tigeot 		crcs[2] = crc2;
1637*a85cb24fSFrançois Tigeot 		crcs[3] = crc3;
1638*a85cb24fSFrançois Tigeot 		crcs[4] = crc4;
1639*a85cb24fSFrançois Tigeot 		drm_crtc_add_crc_entry(&crtc->base, true,
1640*a85cb24fSFrançois Tigeot 				       drm_accurate_vblank_count(&crtc->base),
1641*a85cb24fSFrançois Tigeot 				       crcs);
1642*a85cb24fSFrançois Tigeot 	}
16439edbd4a0SFrançois Tigeot }
16449edbd4a0SFrançois Tigeot #else
16459edbd4a0SFrançois Tigeot static inline void
16461487f786SFrançois Tigeot display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
16471487f786SFrançois Tigeot 			     enum i915_pipe pipe,
16489edbd4a0SFrançois Tigeot 			     uint32_t crc0, uint32_t crc1,
16499edbd4a0SFrançois Tigeot 			     uint32_t crc2, uint32_t crc3,
16509edbd4a0SFrançois Tigeot 			     uint32_t crc4) {}
16519edbd4a0SFrançois Tigeot #endif
16529edbd4a0SFrançois Tigeot 
16539edbd4a0SFrançois Tigeot 
16541487f786SFrançois Tigeot static void hsw_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
16551487f786SFrançois Tigeot 				     enum i915_pipe pipe)
16569edbd4a0SFrançois Tigeot {
16571487f786SFrançois Tigeot 	display_pipe_crc_irq_handler(dev_priv, pipe,
16589edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
16599edbd4a0SFrançois Tigeot 				     0, 0, 0, 0);
16609edbd4a0SFrançois Tigeot }
16619edbd4a0SFrançois Tigeot 
16621487f786SFrançois Tigeot static void ivb_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
16631487f786SFrançois Tigeot 				     enum i915_pipe pipe)
16649edbd4a0SFrançois Tigeot {
16651487f786SFrançois Tigeot 	display_pipe_crc_irq_handler(dev_priv, pipe,
16669edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
16679edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_2_IVB(pipe)),
16689edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_3_IVB(pipe)),
16699edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_4_IVB(pipe)),
16709edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_5_IVB(pipe)));
16719edbd4a0SFrançois Tigeot }
16729edbd4a0SFrançois Tigeot 
16731487f786SFrançois Tigeot static void i9xx_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
16741487f786SFrançois Tigeot 				      enum i915_pipe pipe)
16759edbd4a0SFrançois Tigeot {
16769edbd4a0SFrançois Tigeot 	uint32_t res1, res2;
16779edbd4a0SFrançois Tigeot 
16781487f786SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 3)
16799edbd4a0SFrançois Tigeot 		res1 = I915_READ(PIPE_CRC_RES_RES1_I915(pipe));
16809edbd4a0SFrançois Tigeot 	else
16819edbd4a0SFrançois Tigeot 		res1 = 0;
16829edbd4a0SFrançois Tigeot 
16831487f786SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv))
16849edbd4a0SFrançois Tigeot 		res2 = I915_READ(PIPE_CRC_RES_RES2_G4X(pipe));
16859edbd4a0SFrançois Tigeot 	else
16869edbd4a0SFrançois Tigeot 		res2 = 0;
16879edbd4a0SFrançois Tigeot 
16881487f786SFrançois Tigeot 	display_pipe_crc_irq_handler(dev_priv, pipe,
16899edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_RED(pipe)),
16909edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_GREEN(pipe)),
16919edbd4a0SFrançois Tigeot 				     I915_READ(PIPE_CRC_RES_BLUE(pipe)),
16929edbd4a0SFrançois Tigeot 				     res1, res2);
16939edbd4a0SFrançois Tigeot }
16949edbd4a0SFrançois Tigeot 
16959edbd4a0SFrançois Tigeot /* The RPS events need forcewake, so we add them to a work queue and mask their
16969edbd4a0SFrançois Tigeot  * IMR bits until the work is done. Other interrupts can be processed without
16979edbd4a0SFrançois Tigeot  * the work queue. */
16989edbd4a0SFrançois Tigeot static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
16999edbd4a0SFrançois Tigeot {
1700ba55f2f5SFrançois Tigeot 	if (pm_iir & dev_priv->pm_rps_events) {
17019edbd4a0SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
17024be47400SFrançois Tigeot 		gen6_mask_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
17032c9916cdSFrançois Tigeot 		if (dev_priv->rps.interrupts_enabled) {
17042c9916cdSFrançois Tigeot 			dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events;
1705303bf270SFrançois Tigeot 			schedule_work(&dev_priv->rps.work);
17065d0b1887SFrançois Tigeot 		}
17072c9916cdSFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_RELEASE);
17082c9916cdSFrançois Tigeot 	}
17092c9916cdSFrançois Tigeot 
17102c9916cdSFrançois Tigeot 	if (INTEL_INFO(dev_priv)->gen >= 8)
17112c9916cdSFrançois Tigeot 		return;
17125d0b1887SFrançois Tigeot 
17138621f407SFrançois Tigeot 	if (HAS_VEBOX(dev_priv)) {
17145d0b1887SFrançois Tigeot 		if (pm_iir & PM_VEBOX_USER_INTERRUPT)
17151e12ee3bSFrançois Tigeot 			notify_ring(dev_priv->engine[VECS]);
17165d0b1887SFrançois Tigeot 
17172c9916cdSFrançois Tigeot 		if (pm_iir & PM_VEBOX_CS_ERROR_INTERRUPT)
17182c9916cdSFrançois Tigeot 			DRM_DEBUG("Command parser error, pm_iir 0x%08x\n", pm_iir);
17195d0b1887SFrançois Tigeot 	}
17205d0b1887SFrançois Tigeot }
17215d0b1887SFrançois Tigeot 
17224be47400SFrançois Tigeot static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 gt_iir)
17234be47400SFrançois Tigeot {
17244be47400SFrançois Tigeot 	if (gt_iir & GEN9_GUC_TO_HOST_INT_EVENT) {
17254be47400SFrançois Tigeot 		/* Sample the log buffer flush related bits & clear them out now
17264be47400SFrançois Tigeot 		 * itself from the message identity register to minimize the
17274be47400SFrançois Tigeot 		 * probability of losing a flush interrupt, when there are back
17284be47400SFrançois Tigeot 		 * to back flush interrupts.
17294be47400SFrançois Tigeot 		 * There can be a new flush interrupt, for different log buffer
17304be47400SFrançois Tigeot 		 * type (like for ISR), whilst Host is handling one (for DPC).
17314be47400SFrançois Tigeot 		 * Since same bit is used in message register for ISR & DPC, it
17324be47400SFrançois Tigeot 		 * could happen that GuC sets the bit for 2nd interrupt but Host
17334be47400SFrançois Tigeot 		 * clears out the bit on handling the 1st interrupt.
17344be47400SFrançois Tigeot 		 */
17354be47400SFrançois Tigeot 		u32 msg, flush;
17364be47400SFrançois Tigeot 
17374be47400SFrançois Tigeot 		msg = I915_READ(SOFT_SCRATCH(15));
1738*a85cb24fSFrançois Tigeot 		flush = msg & (INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED |
1739*a85cb24fSFrançois Tigeot 			       INTEL_GUC_RECV_MSG_FLUSH_LOG_BUFFER);
17404be47400SFrançois Tigeot 		if (flush) {
17414be47400SFrançois Tigeot 			/* Clear the message bits that are handled */
17424be47400SFrançois Tigeot 			I915_WRITE(SOFT_SCRATCH(15), msg & ~flush);
17434be47400SFrançois Tigeot 
17444be47400SFrançois Tigeot 			/* Handle flush interrupt in bottom half */
1745*a85cb24fSFrançois Tigeot 			queue_work(dev_priv->guc.log.runtime.flush_wq,
1746*a85cb24fSFrançois Tigeot 				   &dev_priv->guc.log.runtime.flush_work);
17474be47400SFrançois Tigeot 
17484be47400SFrançois Tigeot 			dev_priv->guc.log.flush_interrupt_count++;
17494be47400SFrançois Tigeot 		} else {
17504be47400SFrançois Tigeot 			/* Not clearing of unhandled event bits won't result in
17514be47400SFrançois Tigeot 			 * re-triggering of the interrupt.
17524be47400SFrançois Tigeot 			 */
17534be47400SFrançois Tigeot 		}
17544be47400SFrançois Tigeot 	}
17554be47400SFrançois Tigeot }
17564be47400SFrançois Tigeot 
17571487f786SFrançois Tigeot static bool intel_pipe_handle_vblank(struct drm_i915_private *dev_priv,
17581487f786SFrançois Tigeot 				     enum i915_pipe pipe)
1759ba55f2f5SFrançois Tigeot {
17601487f786SFrançois Tigeot 	bool ret;
1761ba55f2f5SFrançois Tigeot 
1762303bf270SFrançois Tigeot 	ret = drm_handle_vblank(&dev_priv->drm, pipe);
17631487f786SFrançois Tigeot 	if (ret)
17641487f786SFrançois Tigeot 		intel_finish_page_flip_mmio(dev_priv, pipe);
17651487f786SFrançois Tigeot 
17661487f786SFrançois Tigeot 	return ret;
1767ba55f2f5SFrançois Tigeot }
1768ba55f2f5SFrançois Tigeot 
17691487f786SFrançois Tigeot static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv,
17701487f786SFrançois Tigeot 					u32 iir, u32 pipe_stats[I915_MAX_PIPES])
1771ba55f2f5SFrançois Tigeot {
1772ba55f2f5SFrançois Tigeot 	int pipe;
1773ba55f2f5SFrançois Tigeot 
1774ba55f2f5SFrançois Tigeot 	lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
1775c0e85e96SFrançois Tigeot 
1776c0e85e96SFrançois Tigeot 	if (!dev_priv->display_irqs_enabled) {
1777c0e85e96SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_RELEASE);
1778c0e85e96SFrançois Tigeot 		return;
1779c0e85e96SFrançois Tigeot 	}
1780c0e85e96SFrançois Tigeot 
17811b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
1782aee94f86SFrançois Tigeot 		i915_reg_t reg;
1783ba55f2f5SFrançois Tigeot 		u32 mask, iir_bit = 0;
1784ba55f2f5SFrançois Tigeot 
1785ba55f2f5SFrançois Tigeot 		/*
1786ba55f2f5SFrançois Tigeot 		 * PIPESTAT bits get signalled even when the interrupt is
1787ba55f2f5SFrançois Tigeot 		 * disabled with the mask bits, and some of the status bits do
1788ba55f2f5SFrançois Tigeot 		 * not generate interrupts at all (like the underrun bit). Hence
1789ba55f2f5SFrançois Tigeot 		 * we need to be careful that we only handle what we want to
1790ba55f2f5SFrançois Tigeot 		 * handle.
1791ba55f2f5SFrançois Tigeot 		 */
17922c9916cdSFrançois Tigeot 
17932c9916cdSFrançois Tigeot 		/* fifo underruns are filterered in the underrun handler. */
17942c9916cdSFrançois Tigeot 		mask = PIPE_FIFO_UNDERRUN_STATUS;
1795ba55f2f5SFrançois Tigeot 
1796ba55f2f5SFrançois Tigeot 		switch (pipe) {
1797ba55f2f5SFrançois Tigeot 		case PIPE_A:
1798ba55f2f5SFrançois Tigeot 			iir_bit = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
1799ba55f2f5SFrançois Tigeot 			break;
1800ba55f2f5SFrançois Tigeot 		case PIPE_B:
1801ba55f2f5SFrançois Tigeot 			iir_bit = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
1802ba55f2f5SFrançois Tigeot 			break;
1803ba55f2f5SFrançois Tigeot 		case PIPE_C:
1804ba55f2f5SFrançois Tigeot 			iir_bit = I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
1805ba55f2f5SFrançois Tigeot 			break;
1806ba55f2f5SFrançois Tigeot 		}
1807ba55f2f5SFrançois Tigeot 		if (iir & iir_bit)
1808ba55f2f5SFrançois Tigeot 			mask |= dev_priv->pipestat_irq_mask[pipe];
1809ba55f2f5SFrançois Tigeot 
1810ba55f2f5SFrançois Tigeot 		if (!mask)
1811ba55f2f5SFrançois Tigeot 			continue;
1812ba55f2f5SFrançois Tigeot 
1813ba55f2f5SFrançois Tigeot 		reg = PIPESTAT(pipe);
1814ba55f2f5SFrançois Tigeot 		mask |= PIPESTAT_INT_ENABLE_MASK;
1815ba55f2f5SFrançois Tigeot 		pipe_stats[pipe] = I915_READ(reg) & mask;
1816ba55f2f5SFrançois Tigeot 
1817ba55f2f5SFrançois Tigeot 		/*
1818ba55f2f5SFrançois Tigeot 		 * Clear the PIPE*STAT regs before the IIR
1819ba55f2f5SFrançois Tigeot 		 */
1820ba55f2f5SFrançois Tigeot 		if (pipe_stats[pipe] & (PIPE_FIFO_UNDERRUN_STATUS |
1821ba55f2f5SFrançois Tigeot 					PIPESTAT_INT_STATUS_MASK))
1822ba55f2f5SFrançois Tigeot 			I915_WRITE(reg, pipe_stats[pipe]);
1823ba55f2f5SFrançois Tigeot 	}
1824ba55f2f5SFrançois Tigeot 	lockmgr(&dev_priv->irq_lock, LK_RELEASE);
18258621f407SFrançois Tigeot }
18268621f407SFrançois Tigeot 
18271487f786SFrançois Tigeot static void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv,
18288621f407SFrançois Tigeot 					    u32 pipe_stats[I915_MAX_PIPES])
18298621f407SFrançois Tigeot {
18308621f407SFrançois Tigeot 	enum i915_pipe pipe;
1831ba55f2f5SFrançois Tigeot 
18321b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
18331b13d190SFrançois Tigeot 		if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS &&
18341487f786SFrançois Tigeot 		    intel_pipe_handle_vblank(dev_priv, pipe))
18351487f786SFrançois Tigeot 			intel_check_page_flip(dev_priv, pipe);
1836ba55f2f5SFrançois Tigeot 
18371487f786SFrançois Tigeot 		if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV)
18381487f786SFrançois Tigeot 			intel_finish_page_flip_cs(dev_priv, pipe);
1839ba55f2f5SFrançois Tigeot 
1840ba55f2f5SFrançois Tigeot 		if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
18411487f786SFrançois Tigeot 			i9xx_pipe_crc_irq_handler(dev_priv, pipe);
1842ba55f2f5SFrançois Tigeot 
18432c9916cdSFrançois Tigeot 		if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
18442c9916cdSFrançois Tigeot 			intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
1845ba55f2f5SFrançois Tigeot 	}
1846ba55f2f5SFrançois Tigeot 
1847ba55f2f5SFrançois Tigeot 	if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
18481487f786SFrançois Tigeot 		gmbus_irq_handler(dev_priv);
1849ba55f2f5SFrançois Tigeot }
1850ba55f2f5SFrançois Tigeot 
18518621f407SFrançois Tigeot static u32 i9xx_hpd_irq_ack(struct drm_i915_private *dev_priv)
1852ba55f2f5SFrançois Tigeot {
1853ba55f2f5SFrançois Tigeot 	u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
1854ba55f2f5SFrançois Tigeot 
18558621f407SFrançois Tigeot 	if (hotplug_status)
1856ba55f2f5SFrançois Tigeot 		I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
18578621f407SFrançois Tigeot 
18588621f407SFrançois Tigeot 	return hotplug_status;
18598621f407SFrançois Tigeot }
18608621f407SFrançois Tigeot 
18611487f786SFrançois Tigeot static void i9xx_hpd_irq_handler(struct drm_i915_private *dev_priv,
18628621f407SFrançois Tigeot 				 u32 hotplug_status)
18638621f407SFrançois Tigeot {
18648621f407SFrançois Tigeot 	u32 pin_mask = 0, long_mask = 0;
186524edb884SFrançois Tigeot 
18661487f786SFrançois Tigeot 	if (IS_G4X(dev_priv) || IS_VALLEYVIEW(dev_priv) ||
18671487f786SFrançois Tigeot 	    IS_CHERRYVIEW(dev_priv)) {
186824edb884SFrançois Tigeot 		u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
186924edb884SFrançois Tigeot 
1870352ff8bdSFrançois Tigeot 		if (hotplug_trigger) {
1871a05eeebfSFrançois Tigeot 			intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
1872a05eeebfSFrançois Tigeot 					   hotplug_trigger, hpd_status_g4x,
1873a05eeebfSFrançois Tigeot 					   i9xx_port_hotplug_long_detect);
1874352ff8bdSFrançois Tigeot 
18751487f786SFrançois Tigeot 			intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
1876352ff8bdSFrançois Tigeot 		}
1877a05eeebfSFrançois Tigeot 
1878a05eeebfSFrançois Tigeot 		if (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
18791487f786SFrançois Tigeot 			dp_aux_irq_handler(dev_priv);
188024edb884SFrançois Tigeot 	} else {
188124edb884SFrançois Tigeot 		u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
188224edb884SFrançois Tigeot 
1883352ff8bdSFrançois Tigeot 		if (hotplug_trigger) {
1884a05eeebfSFrançois Tigeot 			intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
1885a05eeebfSFrançois Tigeot 					   hotplug_trigger, hpd_status_i915,
1886a05eeebfSFrançois Tigeot 					   i9xx_port_hotplug_long_detect);
18871487f786SFrançois Tigeot 			intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
188824edb884SFrançois Tigeot 		}
1889ba55f2f5SFrançois Tigeot 	}
1890352ff8bdSFrançois Tigeot }
1891ba55f2f5SFrançois Tigeot 
1892183e2373SFrançois Tigeot static irqreturn_t valleyview_irq_handler(int irq, void *arg)
1893e9243325SFrançois Tigeot {
1894ba55f2f5SFrançois Tigeot 	struct drm_device *dev = arg;
1895303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
1896183e2373SFrançois Tigeot 	irqreturn_t ret = IRQ_NONE;
1897ba55f2f5SFrançois Tigeot 
18982c9916cdSFrançois Tigeot 	if (!intel_irqs_enabled(dev_priv))
18992c9916cdSFrançois Tigeot 		return IRQ_NONE;
19002c9916cdSFrançois Tigeot 
1901aee94f86SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
1902aee94f86SFrançois Tigeot 	disable_rpm_wakeref_asserts(dev_priv);
1903aee94f86SFrançois Tigeot 
1904c0e85e96SFrançois Tigeot 	do {
19058621f407SFrançois Tigeot 		u32 iir, gt_iir, pm_iir;
19068621f407SFrançois Tigeot 		u32 pipe_stats[I915_MAX_PIPES] = {};
19078621f407SFrançois Tigeot 		u32 hotplug_status = 0;
19088621f407SFrançois Tigeot 		u32 ier = 0;
19098621f407SFrançois Tigeot 
19108621f407SFrançois Tigeot 		gt_iir = I915_READ(GTIIR);
19118621f407SFrançois Tigeot 		pm_iir = I915_READ(GEN6_PMIIR);
19128621f407SFrançois Tigeot 		iir = I915_READ(VLV_IIR);
19138621f407SFrançois Tigeot 
19148621f407SFrançois Tigeot 		if (gt_iir == 0 && pm_iir == 0 && iir == 0)
19158621f407SFrançois Tigeot 			break;
19168621f407SFrançois Tigeot 
1917183e2373SFrançois Tigeot 		ret = IRQ_HANDLED;
19188621f407SFrançois Tigeot 
19198621f407SFrançois Tigeot 		/*
19208621f407SFrançois Tigeot 		 * Theory on interrupt generation, based on empirical evidence:
19218621f407SFrançois Tigeot 		 *
19228621f407SFrançois Tigeot 		 * x = ((VLV_IIR & VLV_IER) ||
19238621f407SFrançois Tigeot 		 *      (((GT_IIR & GT_IER) || (GEN6_PMIIR & GEN6_PMIER)) &&
19248621f407SFrançois Tigeot 		 *       (VLV_MASTER_IER & MASTER_INTERRUPT_ENABLE)));
19258621f407SFrançois Tigeot 		 *
19268621f407SFrançois Tigeot 		 * A CPU interrupt will only be raised when 'x' has a 0->1 edge.
19278621f407SFrançois Tigeot 		 * Hence we clear MASTER_INTERRUPT_ENABLE and VLV_IER to
19288621f407SFrançois Tigeot 		 * guarantee the CPU interrupt will be raised again even if we
19298621f407SFrançois Tigeot 		 * don't end up clearing all the VLV_IIR, GT_IIR, GEN6_PMIIR
19308621f407SFrançois Tigeot 		 * bits this time around.
19318621f407SFrançois Tigeot 		 */
19328621f407SFrançois Tigeot 		I915_WRITE(VLV_MASTER_IER, 0);
19338621f407SFrançois Tigeot 		ier = I915_READ(VLV_IER);
19348621f407SFrançois Tigeot 		I915_WRITE(VLV_IER, 0);
19358621f407SFrançois Tigeot 
19368621f407SFrançois Tigeot 		if (gt_iir)
19378621f407SFrançois Tigeot 			I915_WRITE(GTIIR, gt_iir);
19388621f407SFrançois Tigeot 		if (pm_iir)
19398621f407SFrançois Tigeot 			I915_WRITE(GEN6_PMIIR, pm_iir);
19408621f407SFrançois Tigeot 
19418621f407SFrançois Tigeot 		if (iir & I915_DISPLAY_PORT_INTERRUPT)
19428621f407SFrançois Tigeot 			hotplug_status = i9xx_hpd_irq_ack(dev_priv);
19438621f407SFrançois Tigeot 
19448621f407SFrançois Tigeot 		/* Call regardless, as some status bits might not be
19458621f407SFrançois Tigeot 		 * signalled in iir */
19461487f786SFrançois Tigeot 		valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats);
19478621f407SFrançois Tigeot 
1948*a85cb24fSFrançois Tigeot 		if (iir & (I915_LPE_PIPE_A_INTERRUPT |
1949*a85cb24fSFrançois Tigeot 			   I915_LPE_PIPE_B_INTERRUPT))
1950*a85cb24fSFrançois Tigeot 			intel_lpe_audio_irq_handler(dev_priv);
1951*a85cb24fSFrançois Tigeot 
19528621f407SFrançois Tigeot 		/*
19538621f407SFrançois Tigeot 		 * VLV_IIR is single buffered, and reflects the level
19548621f407SFrançois Tigeot 		 * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
19558621f407SFrançois Tigeot 		 */
19568621f407SFrançois Tigeot 		if (iir)
19578621f407SFrançois Tigeot 			I915_WRITE(VLV_IIR, iir);
19588621f407SFrançois Tigeot 
19598621f407SFrançois Tigeot 		I915_WRITE(VLV_IER, ier);
19608621f407SFrançois Tigeot 		I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
19618621f407SFrançois Tigeot 		POSTING_READ(VLV_MASTER_IER);
19628621f407SFrançois Tigeot 
19638621f407SFrançois Tigeot 		if (gt_iir)
19648621f407SFrançois Tigeot 			snb_gt_irq_handler(dev_priv, gt_iir);
19658621f407SFrançois Tigeot 		if (pm_iir)
19668621f407SFrançois Tigeot 			gen6_rps_irq_handler(dev_priv, pm_iir);
19678621f407SFrançois Tigeot 
19688621f407SFrançois Tigeot 		if (hotplug_status)
19691487f786SFrançois Tigeot 			i9xx_hpd_irq_handler(dev_priv, hotplug_status);
19708621f407SFrançois Tigeot 
19711487f786SFrançois Tigeot 		valleyview_pipestat_irq_handler(dev_priv, pipe_stats);
19728621f407SFrançois Tigeot 	} while (0);
19738621f407SFrançois Tigeot 
19748621f407SFrançois Tigeot 	enable_rpm_wakeref_asserts(dev_priv);
19758621f407SFrançois Tigeot 
1976183e2373SFrançois Tigeot 	return ret;
19778621f407SFrançois Tigeot }
19788621f407SFrançois Tigeot 
1979183e2373SFrançois Tigeot static irqreturn_t cherryview_irq_handler(int irq, void *arg)
19808621f407SFrançois Tigeot {
19818621f407SFrançois Tigeot 	struct drm_device *dev = arg;
1982303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
1983183e2373SFrançois Tigeot 	irqreturn_t ret = IRQ_NONE;
19848621f407SFrançois Tigeot 
19858621f407SFrançois Tigeot 	if (!intel_irqs_enabled(dev_priv))
19868621f407SFrançois Tigeot 		return IRQ_NONE;
19878621f407SFrançois Tigeot 
19888621f407SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
19898621f407SFrançois Tigeot 	disable_rpm_wakeref_asserts(dev_priv);
19908621f407SFrançois Tigeot 
19918621f407SFrançois Tigeot 	do {
19928621f407SFrançois Tigeot 		u32 master_ctl, iir;
19938621f407SFrançois Tigeot 		u32 gt_iir[4] = {};
19948621f407SFrançois Tigeot 		u32 pipe_stats[I915_MAX_PIPES] = {};
19958621f407SFrançois Tigeot 		u32 hotplug_status = 0;
19968621f407SFrançois Tigeot 		u32 ier = 0;
19978621f407SFrançois Tigeot 
1998ba55f2f5SFrançois Tigeot 		master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL;
1999ba55f2f5SFrançois Tigeot 		iir = I915_READ(VLV_IIR);
2000ba55f2f5SFrançois Tigeot 
2001ba55f2f5SFrançois Tigeot 		if (master_ctl == 0 && iir == 0)
2002ba55f2f5SFrançois Tigeot 			break;
2003ba55f2f5SFrançois Tigeot 
2004183e2373SFrançois Tigeot 		ret = IRQ_HANDLED;
200524edb884SFrançois Tigeot 
20068621f407SFrançois Tigeot 		/*
20078621f407SFrançois Tigeot 		 * Theory on interrupt generation, based on empirical evidence:
20088621f407SFrançois Tigeot 		 *
20098621f407SFrançois Tigeot 		 * x = ((VLV_IIR & VLV_IER) ||
20108621f407SFrançois Tigeot 		 *      ((GEN8_MASTER_IRQ & ~GEN8_MASTER_IRQ_CONTROL) &&
20118621f407SFrançois Tigeot 		 *       (GEN8_MASTER_IRQ & GEN8_MASTER_IRQ_CONTROL)));
20128621f407SFrançois Tigeot 		 *
20138621f407SFrançois Tigeot 		 * A CPU interrupt will only be raised when 'x' has a 0->1 edge.
20148621f407SFrançois Tigeot 		 * Hence we clear GEN8_MASTER_IRQ_CONTROL and VLV_IER to
20158621f407SFrançois Tigeot 		 * guarantee the CPU interrupt will be raised again even if we
20168621f407SFrançois Tigeot 		 * don't end up clearing all the VLV_IIR and GEN8_MASTER_IRQ_CONTROL
20178621f407SFrançois Tigeot 		 * bits this time around.
20188621f407SFrançois Tigeot 		 */
2019ba55f2f5SFrançois Tigeot 		I915_WRITE(GEN8_MASTER_IRQ, 0);
20208621f407SFrançois Tigeot 		ier = I915_READ(VLV_IER);
20218621f407SFrançois Tigeot 		I915_WRITE(VLV_IER, 0);
2022ba55f2f5SFrançois Tigeot 
20238621f407SFrançois Tigeot 		gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
202424edb884SFrançois Tigeot 
202524edb884SFrançois Tigeot 		if (iir & I915_DISPLAY_PORT_INTERRUPT)
20268621f407SFrançois Tigeot 			hotplug_status = i9xx_hpd_irq_ack(dev_priv);
2027ba55f2f5SFrançois Tigeot 
202824edb884SFrançois Tigeot 		/* Call regardless, as some status bits might not be
202924edb884SFrançois Tigeot 		 * signalled in iir */
20301487f786SFrançois Tigeot 		valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats);
2031ba55f2f5SFrançois Tigeot 
2032*a85cb24fSFrançois Tigeot 		if (iir & (I915_LPE_PIPE_A_INTERRUPT |
2033*a85cb24fSFrançois Tigeot 			   I915_LPE_PIPE_B_INTERRUPT |
2034*a85cb24fSFrançois Tigeot 			   I915_LPE_PIPE_C_INTERRUPT))
2035*a85cb24fSFrançois Tigeot 			intel_lpe_audio_irq_handler(dev_priv);
2036*a85cb24fSFrançois Tigeot 
20378621f407SFrançois Tigeot 		/*
20388621f407SFrançois Tigeot 		 * VLV_IIR is single buffered, and reflects the level
20398621f407SFrançois Tigeot 		 * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last.
20408621f407SFrançois Tigeot 		 */
20418621f407SFrançois Tigeot 		if (iir)
20428621f407SFrançois Tigeot 			I915_WRITE(VLV_IIR, iir);
20438621f407SFrançois Tigeot 
20448621f407SFrançois Tigeot 		I915_WRITE(VLV_IER, ier);
20458621f407SFrançois Tigeot 		I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
2046ba55f2f5SFrançois Tigeot 		POSTING_READ(GEN8_MASTER_IRQ);
20478621f407SFrançois Tigeot 
20488621f407SFrançois Tigeot 		gen8_gt_irq_handler(dev_priv, gt_iir);
20498621f407SFrançois Tigeot 
20508621f407SFrançois Tigeot 		if (hotplug_status)
20511487f786SFrançois Tigeot 			i9xx_hpd_irq_handler(dev_priv, hotplug_status);
20528621f407SFrançois Tigeot 
20531487f786SFrançois Tigeot 		valleyview_pipestat_irq_handler(dev_priv, pipe_stats);
2054c0e85e96SFrançois Tigeot 	} while (0);
205524edb884SFrançois Tigeot 
2056aee94f86SFrançois Tigeot 	enable_rpm_wakeref_asserts(dev_priv);
2057c0e85e96SFrançois Tigeot 
2058183e2373SFrançois Tigeot 	return ret;
2059ba55f2f5SFrançois Tigeot }
2060ba55f2f5SFrançois Tigeot 
20611487f786SFrançois Tigeot static void ibx_hpd_irq_handler(struct drm_i915_private *dev_priv,
20621487f786SFrançois Tigeot 				u32 hotplug_trigger,
2063352ff8bdSFrançois Tigeot 				const u32 hpd[HPD_NUM_PINS])
2064352ff8bdSFrançois Tigeot {
2065352ff8bdSFrançois Tigeot 	u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
2066352ff8bdSFrançois Tigeot 
2067aee94f86SFrançois Tigeot 	/*
2068aee94f86SFrançois Tigeot 	 * Somehow the PCH doesn't seem to really ack the interrupt to the CPU
2069aee94f86SFrançois Tigeot 	 * unless we touch the hotplug register, even if hotplug_trigger is
2070aee94f86SFrançois Tigeot 	 * zero. Not acking leads to "The master control interrupt lied (SDE)!"
2071aee94f86SFrançois Tigeot 	 * errors.
2072aee94f86SFrançois Tigeot 	 */
2073352ff8bdSFrançois Tigeot 	dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
2074aee94f86SFrançois Tigeot 	if (!hotplug_trigger) {
2075aee94f86SFrançois Tigeot 		u32 mask = PORTA_HOTPLUG_STATUS_MASK |
2076aee94f86SFrançois Tigeot 			PORTD_HOTPLUG_STATUS_MASK |
2077aee94f86SFrançois Tigeot 			PORTC_HOTPLUG_STATUS_MASK |
2078aee94f86SFrançois Tigeot 			PORTB_HOTPLUG_STATUS_MASK;
2079aee94f86SFrançois Tigeot 		dig_hotplug_reg &= ~mask;
2080aee94f86SFrançois Tigeot 	}
2081aee94f86SFrançois Tigeot 
2082352ff8bdSFrançois Tigeot 	I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
2083aee94f86SFrançois Tigeot 	if (!hotplug_trigger)
2084aee94f86SFrançois Tigeot 		return;
2085352ff8bdSFrançois Tigeot 
2086352ff8bdSFrançois Tigeot 	intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
2087352ff8bdSFrançois Tigeot 			   dig_hotplug_reg, hpd,
2088352ff8bdSFrançois Tigeot 			   pch_port_hotplug_long_detect);
2089352ff8bdSFrançois Tigeot 
20901487f786SFrançois Tigeot 	intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
2091352ff8bdSFrançois Tigeot }
2092352ff8bdSFrançois Tigeot 
20931487f786SFrançois Tigeot static void ibx_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
2094e3adcf8fSFrançois Tigeot {
2095e3adcf8fSFrançois Tigeot 	int pipe;
20968e26cdf6SFrançois Tigeot 	u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK;
2097a05eeebfSFrançois Tigeot 
20981487f786SFrançois Tigeot 	ibx_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_ibx);
20995d0b1887SFrançois Tigeot 
21005d0b1887SFrançois Tigeot 	if (pch_iir & SDE_AUDIO_POWER_MASK) {
21015d0b1887SFrançois Tigeot 		int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
2102e3adcf8fSFrançois Tigeot 			       SDE_AUDIO_POWER_SHIFT);
21035d0b1887SFrançois Tigeot 		DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
21045d0b1887SFrançois Tigeot 				 port_name(port));
21055d0b1887SFrançois Tigeot 	}
2106e3adcf8fSFrançois Tigeot 
2107a2fdbec6SFrançois Tigeot 	if (pch_iir & SDE_AUX_MASK)
21081487f786SFrançois Tigeot 		dp_aux_irq_handler(dev_priv);
2109a2fdbec6SFrançois Tigeot 
2110e3adcf8fSFrançois Tigeot 	if (pch_iir & SDE_GMBUS)
21111487f786SFrançois Tigeot 		gmbus_irq_handler(dev_priv);
2112e3adcf8fSFrançois Tigeot 
2113e3adcf8fSFrançois Tigeot 	if (pch_iir & SDE_AUDIO_HDCP_MASK)
2114a2296444SFrançois Tigeot 		DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n");
2115e3adcf8fSFrançois Tigeot 
2116e3adcf8fSFrançois Tigeot 	if (pch_iir & SDE_AUDIO_TRANS_MASK)
2117a2296444SFrançois Tigeot 		DRM_DEBUG_DRIVER("PCH transcoder audio interrupt\n");
2118e3adcf8fSFrançois Tigeot 
2119e3adcf8fSFrançois Tigeot 	if (pch_iir & SDE_POISON)
2120a2296444SFrançois Tigeot 		DRM_ERROR("PCH poison interrupt\n");
2121e3adcf8fSFrançois Tigeot 
2122e3adcf8fSFrançois Tigeot 	if (pch_iir & SDE_FDI_MASK)
21231b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe)
2124a2296444SFrançois Tigeot 			DRM_DEBUG_DRIVER("  pipe %c FDI IIR: 0x%08x\n",
2125e3adcf8fSFrançois Tigeot 					 pipe_name(pipe),
2126e3adcf8fSFrançois Tigeot 					 I915_READ(FDI_RX_IIR(pipe)));
2127e3adcf8fSFrançois Tigeot 
2128e3adcf8fSFrançois Tigeot 	if (pch_iir & (SDE_TRANSB_CRC_DONE | SDE_TRANSA_CRC_DONE))
2129a2296444SFrançois Tigeot 		DRM_DEBUG_DRIVER("PCH transcoder CRC done interrupt\n");
2130e3adcf8fSFrançois Tigeot 
2131e3adcf8fSFrançois Tigeot 	if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR))
2132a2296444SFrançois Tigeot 		DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n");
2133e3adcf8fSFrançois Tigeot 
2134e3adcf8fSFrançois Tigeot 	if (pch_iir & SDE_TRANSA_FIFO_UNDER)
21352c9916cdSFrançois Tigeot 		intel_pch_fifo_underrun_irq_handler(dev_priv, TRANSCODER_A);
21365d0b1887SFrançois Tigeot 
21375d0b1887SFrançois Tigeot 	if (pch_iir & SDE_TRANSB_FIFO_UNDER)
21382c9916cdSFrançois Tigeot 		intel_pch_fifo_underrun_irq_handler(dev_priv, TRANSCODER_B);
21395d0b1887SFrançois Tigeot }
21405d0b1887SFrançois Tigeot 
21411487f786SFrançois Tigeot static void ivb_err_int_handler(struct drm_i915_private *dev_priv)
21425d0b1887SFrançois Tigeot {
21435d0b1887SFrançois Tigeot 	u32 err_int = I915_READ(GEN7_ERR_INT);
21449edbd4a0SFrançois Tigeot 	enum i915_pipe pipe;
21455d0b1887SFrançois Tigeot 
21465d0b1887SFrançois Tigeot 	if (err_int & ERR_INT_POISON)
21475d0b1887SFrançois Tigeot 		DRM_ERROR("Poison interrupt\n");
21485d0b1887SFrançois Tigeot 
21491b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
21502c9916cdSFrançois Tigeot 		if (err_int & ERR_INT_FIFO_UNDERRUN(pipe))
21512c9916cdSFrançois Tigeot 			intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
21525d0b1887SFrançois Tigeot 
21539edbd4a0SFrançois Tigeot 		if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) {
21541487f786SFrançois Tigeot 			if (IS_IVYBRIDGE(dev_priv))
21551487f786SFrançois Tigeot 				ivb_pipe_crc_irq_handler(dev_priv, pipe);
21569edbd4a0SFrançois Tigeot 			else
21571487f786SFrançois Tigeot 				hsw_pipe_crc_irq_handler(dev_priv, pipe);
21589edbd4a0SFrançois Tigeot 		}
21599edbd4a0SFrançois Tigeot 	}
21605d0b1887SFrançois Tigeot 
21615d0b1887SFrançois Tigeot 	I915_WRITE(GEN7_ERR_INT, err_int);
21625d0b1887SFrançois Tigeot }
21635d0b1887SFrançois Tigeot 
21641487f786SFrançois Tigeot static void cpt_serr_int_handler(struct drm_i915_private *dev_priv)
21655d0b1887SFrançois Tigeot {
21665d0b1887SFrançois Tigeot 	u32 serr_int = I915_READ(SERR_INT);
21675d0b1887SFrançois Tigeot 
21685d0b1887SFrançois Tigeot 	if (serr_int & SERR_INT_POISON)
21695d0b1887SFrançois Tigeot 		DRM_ERROR("PCH poison interrupt\n");
21705d0b1887SFrançois Tigeot 
21715d0b1887SFrançois Tigeot 	if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN)
21722c9916cdSFrançois Tigeot 		intel_pch_fifo_underrun_irq_handler(dev_priv, TRANSCODER_A);
21735d0b1887SFrançois Tigeot 
21745d0b1887SFrançois Tigeot 	if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN)
21752c9916cdSFrançois Tigeot 		intel_pch_fifo_underrun_irq_handler(dev_priv, TRANSCODER_B);
21765d0b1887SFrançois Tigeot 
21775d0b1887SFrançois Tigeot 	if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN)
21782c9916cdSFrançois Tigeot 		intel_pch_fifo_underrun_irq_handler(dev_priv, TRANSCODER_C);
21795d0b1887SFrançois Tigeot 
21805d0b1887SFrançois Tigeot 	I915_WRITE(SERR_INT, serr_int);
2181a2296444SFrançois Tigeot }
2182a2296444SFrançois Tigeot 
21831487f786SFrançois Tigeot static void cpt_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
2184a2296444SFrançois Tigeot {
2185a2296444SFrançois Tigeot 	int pipe;
2186352ff8bdSFrançois Tigeot 	u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
2187a05eeebfSFrançois Tigeot 
21881487f786SFrançois Tigeot 	ibx_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_cpt);
21895d0b1887SFrançois Tigeot 
21905d0b1887SFrançois Tigeot 	if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
21915d0b1887SFrançois Tigeot 		int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
2192a2296444SFrançois Tigeot 			       SDE_AUDIO_POWER_SHIFT_CPT);
21935d0b1887SFrançois Tigeot 		DRM_DEBUG_DRIVER("PCH audio power change on port %c\n",
21945d0b1887SFrançois Tigeot 				 port_name(port));
21955d0b1887SFrançois Tigeot 	}
2196a2296444SFrançois Tigeot 
2197a2296444SFrançois Tigeot 	if (pch_iir & SDE_AUX_MASK_CPT)
21981487f786SFrançois Tigeot 		dp_aux_irq_handler(dev_priv);
2199a2296444SFrançois Tigeot 
2200a2296444SFrançois Tigeot 	if (pch_iir & SDE_GMBUS_CPT)
22011487f786SFrançois Tigeot 		gmbus_irq_handler(dev_priv);
2202a2296444SFrançois Tigeot 
2203a2296444SFrançois Tigeot 	if (pch_iir & SDE_AUDIO_CP_REQ_CPT)
2204a2296444SFrançois Tigeot 		DRM_DEBUG_DRIVER("Audio CP request interrupt\n");
2205a2296444SFrançois Tigeot 
2206a2296444SFrançois Tigeot 	if (pch_iir & SDE_AUDIO_CP_CHG_CPT)
2207a2296444SFrançois Tigeot 		DRM_DEBUG_DRIVER("Audio CP change interrupt\n");
2208a2296444SFrançois Tigeot 
2209a2296444SFrançois Tigeot 	if (pch_iir & SDE_FDI_MASK_CPT)
22101b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe)
2211a2296444SFrançois Tigeot 			DRM_DEBUG_DRIVER("  pipe %c FDI IIR: 0x%08x\n",
2212a2296444SFrançois Tigeot 					 pipe_name(pipe),
2213a2296444SFrançois Tigeot 					 I915_READ(FDI_RX_IIR(pipe)));
22145d0b1887SFrançois Tigeot 
22155d0b1887SFrançois Tigeot 	if (pch_iir & SDE_ERROR_CPT)
22161487f786SFrançois Tigeot 		cpt_serr_int_handler(dev_priv);
2217e3adcf8fSFrançois Tigeot }
2218e3adcf8fSFrançois Tigeot 
22191487f786SFrançois Tigeot static void spt_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
2220352ff8bdSFrançois Tigeot {
2221352ff8bdSFrançois Tigeot 	u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT &
2222352ff8bdSFrançois Tigeot 		~SDE_PORTE_HOTPLUG_SPT;
2223352ff8bdSFrançois Tigeot 	u32 hotplug2_trigger = pch_iir & SDE_PORTE_HOTPLUG_SPT;
2224352ff8bdSFrançois Tigeot 	u32 pin_mask = 0, long_mask = 0;
2225352ff8bdSFrançois Tigeot 
2226352ff8bdSFrançois Tigeot 	if (hotplug_trigger) {
2227352ff8bdSFrançois Tigeot 		u32 dig_hotplug_reg;
2228352ff8bdSFrançois Tigeot 
2229352ff8bdSFrançois Tigeot 		dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
2230352ff8bdSFrançois Tigeot 		I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
2231352ff8bdSFrançois Tigeot 
2232352ff8bdSFrançois Tigeot 		intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
2233352ff8bdSFrançois Tigeot 				   dig_hotplug_reg, hpd_spt,
2234352ff8bdSFrançois Tigeot 				   spt_port_hotplug_long_detect);
2235352ff8bdSFrançois Tigeot 	}
2236352ff8bdSFrançois Tigeot 
2237352ff8bdSFrançois Tigeot 	if (hotplug2_trigger) {
2238352ff8bdSFrançois Tigeot 		u32 dig_hotplug_reg;
2239352ff8bdSFrançois Tigeot 
2240352ff8bdSFrançois Tigeot 		dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG2);
2241352ff8bdSFrançois Tigeot 		I915_WRITE(PCH_PORT_HOTPLUG2, dig_hotplug_reg);
2242352ff8bdSFrançois Tigeot 
2243352ff8bdSFrançois Tigeot 		intel_get_hpd_pins(&pin_mask, &long_mask, hotplug2_trigger,
2244352ff8bdSFrançois Tigeot 				   dig_hotplug_reg, hpd_spt,
2245352ff8bdSFrançois Tigeot 				   spt_port_hotplug2_long_detect);
2246352ff8bdSFrançois Tigeot 	}
2247352ff8bdSFrançois Tigeot 
2248352ff8bdSFrançois Tigeot 	if (pin_mask)
22491487f786SFrançois Tigeot 		intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
2250352ff8bdSFrançois Tigeot 
2251352ff8bdSFrançois Tigeot 	if (pch_iir & SDE_GMBUS_CPT)
22521487f786SFrançois Tigeot 		gmbus_irq_handler(dev_priv);
2253352ff8bdSFrançois Tigeot }
2254352ff8bdSFrançois Tigeot 
22551487f786SFrançois Tigeot static void ilk_hpd_irq_handler(struct drm_i915_private *dev_priv,
22561487f786SFrançois Tigeot 				u32 hotplug_trigger,
2257352ff8bdSFrançois Tigeot 				const u32 hpd[HPD_NUM_PINS])
2258352ff8bdSFrançois Tigeot {
2259352ff8bdSFrançois Tigeot 	u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
2260352ff8bdSFrançois Tigeot 
2261352ff8bdSFrançois Tigeot 	dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL);
2262352ff8bdSFrançois Tigeot 	I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg);
2263352ff8bdSFrançois Tigeot 
2264352ff8bdSFrançois Tigeot 	intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
2265352ff8bdSFrançois Tigeot 			   dig_hotplug_reg, hpd,
2266352ff8bdSFrançois Tigeot 			   ilk_port_hotplug_long_detect);
2267352ff8bdSFrançois Tigeot 
22681487f786SFrançois Tigeot 	intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
2269352ff8bdSFrançois Tigeot }
2270352ff8bdSFrançois Tigeot 
22711487f786SFrançois Tigeot static void ilk_display_irq_handler(struct drm_i915_private *dev_priv,
22721487f786SFrançois Tigeot 				    u32 de_iir)
2273c4a9e910SFrançois Tigeot {
22749edbd4a0SFrançois Tigeot 	enum i915_pipe pipe;
2275352ff8bdSFrançois Tigeot 	u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG;
2276352ff8bdSFrançois Tigeot 
2277352ff8bdSFrançois Tigeot 	if (hotplug_trigger)
22781487f786SFrançois Tigeot 		ilk_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_ilk);
2279e3adcf8fSFrançois Tigeot 
2280a2fdbec6SFrançois Tigeot 	if (de_iir & DE_AUX_CHANNEL_A)
22811487f786SFrançois Tigeot 		dp_aux_irq_handler(dev_priv);
2282a2fdbec6SFrançois Tigeot 
228300640ec9SFrançois Tigeot 	if (de_iir & DE_GSE)
22841487f786SFrançois Tigeot 		intel_opregion_asle_intr(dev_priv);
2285e3adcf8fSFrançois Tigeot 
22865d0b1887SFrançois Tigeot 	if (de_iir & DE_POISON)
22875d0b1887SFrançois Tigeot 		DRM_ERROR("Poison interrupt\n");
22885d0b1887SFrançois Tigeot 
22891b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
22901b13d190SFrançois Tigeot 		if (de_iir & DE_PIPE_VBLANK(pipe) &&
22911487f786SFrançois Tigeot 		    intel_pipe_handle_vblank(dev_priv, pipe))
22921487f786SFrançois Tigeot 			intel_check_page_flip(dev_priv, pipe);
22935d0b1887SFrançois Tigeot 
22949edbd4a0SFrançois Tigeot 		if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
22952c9916cdSFrançois Tigeot 			intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
22965d0b1887SFrançois Tigeot 
22979edbd4a0SFrançois Tigeot 		if (de_iir & DE_PIPE_CRC_DONE(pipe))
22981487f786SFrançois Tigeot 			i9xx_pipe_crc_irq_handler(dev_priv, pipe);
22999edbd4a0SFrançois Tigeot 
23009edbd4a0SFrançois Tigeot 		/* plane/pipes map 1:1 on ilk+ */
23011487f786SFrançois Tigeot 		if (de_iir & DE_PLANE_FLIP_DONE(pipe))
23021487f786SFrançois Tigeot 			intel_finish_page_flip_cs(dev_priv, pipe);
2303e3adcf8fSFrançois Tigeot 	}
2304e3adcf8fSFrançois Tigeot 
2305e3adcf8fSFrançois Tigeot 	/* check event from PCH */
2306e3adcf8fSFrançois Tigeot 	if (de_iir & DE_PCH_EVENT) {
2307a2fdbec6SFrançois Tigeot 		u32 pch_iir = I915_READ(SDEIIR);
2308a2fdbec6SFrançois Tigeot 
23091487f786SFrançois Tigeot 		if (HAS_PCH_CPT(dev_priv))
23101487f786SFrançois Tigeot 			cpt_irq_handler(dev_priv, pch_iir);
2311a2296444SFrançois Tigeot 		else
23121487f786SFrançois Tigeot 			ibx_irq_handler(dev_priv, pch_iir);
2313a2fdbec6SFrançois Tigeot 
2314a2fdbec6SFrançois Tigeot 		/* should clear PCH hotplug event before clear CPU irq */
2315a2fdbec6SFrançois Tigeot 		I915_WRITE(SDEIIR, pch_iir);
2316e3adcf8fSFrançois Tigeot 	}
2317e3adcf8fSFrançois Tigeot 
23181487f786SFrançois Tigeot 	if (IS_GEN5(dev_priv) && de_iir & DE_PCU_EVENT)
23191487f786SFrançois Tigeot 		ironlake_rps_change_irq_handler(dev_priv);
23209edbd4a0SFrançois Tigeot }
2321e3adcf8fSFrançois Tigeot 
23221487f786SFrançois Tigeot static void ivb_display_irq_handler(struct drm_i915_private *dev_priv,
23231487f786SFrançois Tigeot 				    u32 de_iir)
23249edbd4a0SFrançois Tigeot {
2325ba55f2f5SFrançois Tigeot 	enum i915_pipe pipe;
2326352ff8bdSFrançois Tigeot 	u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB;
2327352ff8bdSFrançois Tigeot 
2328352ff8bdSFrançois Tigeot 	if (hotplug_trigger)
23291487f786SFrançois Tigeot 		ilk_hpd_irq_handler(dev_priv, hotplug_trigger, hpd_ivb);
2330e3adcf8fSFrançois Tigeot 
23319edbd4a0SFrançois Tigeot 	if (de_iir & DE_ERR_INT_IVB)
23321487f786SFrançois Tigeot 		ivb_err_int_handler(dev_priv);
23339edbd4a0SFrançois Tigeot 
23349edbd4a0SFrançois Tigeot 	if (de_iir & DE_AUX_CHANNEL_A_IVB)
23351487f786SFrançois Tigeot 		dp_aux_irq_handler(dev_priv);
23369edbd4a0SFrançois Tigeot 
23379edbd4a0SFrançois Tigeot 	if (de_iir & DE_GSE_IVB)
23381487f786SFrançois Tigeot 		intel_opregion_asle_intr(dev_priv);
23399edbd4a0SFrançois Tigeot 
23401b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
23411b13d190SFrançois Tigeot 		if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)) &&
23421487f786SFrançois Tigeot 		    intel_pipe_handle_vblank(dev_priv, pipe))
23431487f786SFrançois Tigeot 			intel_check_page_flip(dev_priv, pipe);
23449edbd4a0SFrançois Tigeot 
23459edbd4a0SFrançois Tigeot 		/* plane/pipes map 1:1 on ilk+ */
23461487f786SFrançois Tigeot 		if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe))
23471487f786SFrançois Tigeot 			intel_finish_page_flip_cs(dev_priv, pipe);
23489edbd4a0SFrançois Tigeot 	}
23499edbd4a0SFrançois Tigeot 
23509edbd4a0SFrançois Tigeot 	/* check event from PCH */
23511487f786SFrançois Tigeot 	if (!HAS_PCH_NOP(dev_priv) && (de_iir & DE_PCH_EVENT_IVB)) {
23529edbd4a0SFrançois Tigeot 		u32 pch_iir = I915_READ(SDEIIR);
23539edbd4a0SFrançois Tigeot 
23541487f786SFrançois Tigeot 		cpt_irq_handler(dev_priv, pch_iir);
23559edbd4a0SFrançois Tigeot 
23569edbd4a0SFrançois Tigeot 		/* clear PCH hotplug event before clear CPU irq */
23579edbd4a0SFrançois Tigeot 		I915_WRITE(SDEIIR, pch_iir);
23589edbd4a0SFrançois Tigeot 	}
23599edbd4a0SFrançois Tigeot }
23609edbd4a0SFrançois Tigeot 
236124edb884SFrançois Tigeot /*
236224edb884SFrançois Tigeot  * To handle irqs with the minimum potential races with fresh interrupts, we:
236324edb884SFrançois Tigeot  * 1 - Disable Master Interrupt Control.
236424edb884SFrançois Tigeot  * 2 - Find the source(s) of the interrupt.
236524edb884SFrançois Tigeot  * 3 - Clear the Interrupt Identity bits (IIR).
236624edb884SFrançois Tigeot  * 4 - Process the interrupt(s) that had bits set in the IIRs.
236724edb884SFrançois Tigeot  * 5 - Re-enable Master Interrupt Control.
236824edb884SFrançois Tigeot  */
2369183e2373SFrançois Tigeot static irqreturn_t ironlake_irq_handler(int irq, void *arg)
23709edbd4a0SFrançois Tigeot {
2371ba55f2f5SFrançois Tigeot 	struct drm_device *dev = arg;
2372303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
23739edbd4a0SFrançois Tigeot 	u32 de_iir, gt_iir, de_ier, sde_ier = 0;
2374183e2373SFrançois Tigeot 	irqreturn_t ret = IRQ_NONE;
23759edbd4a0SFrançois Tigeot 
23762c9916cdSFrançois Tigeot 	if (!intel_irqs_enabled(dev_priv))
23772c9916cdSFrançois Tigeot 		return IRQ_NONE;
23782c9916cdSFrançois Tigeot 
2379aee94f86SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
2380aee94f86SFrançois Tigeot 	disable_rpm_wakeref_asserts(dev_priv);
2381aee94f86SFrançois Tigeot 
23829edbd4a0SFrançois Tigeot 	/* disable master interrupt before clearing iir  */
23839edbd4a0SFrançois Tigeot 	de_ier = I915_READ(DEIER);
23849edbd4a0SFrançois Tigeot 	I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
23859edbd4a0SFrançois Tigeot 	POSTING_READ(DEIER);
23869edbd4a0SFrançois Tigeot 
23879edbd4a0SFrançois Tigeot 	/* Disable south interrupts. We'll only write to SDEIIR once, so further
23889edbd4a0SFrançois Tigeot 	 * interrupts will will be stored on its back queue, and then we'll be
23899edbd4a0SFrançois Tigeot 	 * able to process them after we restore SDEIER (as soon as we restore
23909edbd4a0SFrançois Tigeot 	 * it, we'll get an interrupt if SDEIIR still has something to process
23919edbd4a0SFrançois Tigeot 	 * due to its back queue). */
23921487f786SFrançois Tigeot 	if (!HAS_PCH_NOP(dev_priv)) {
23939edbd4a0SFrançois Tigeot 		sde_ier = I915_READ(SDEIER);
23949edbd4a0SFrançois Tigeot 		I915_WRITE(SDEIER, 0);
23959edbd4a0SFrançois Tigeot 		POSTING_READ(SDEIER);
23969edbd4a0SFrançois Tigeot 	}
23979edbd4a0SFrançois Tigeot 
239824edb884SFrançois Tigeot 	/* Find, clear, then process each source of interrupt */
239924edb884SFrançois Tigeot 
24009edbd4a0SFrançois Tigeot 	gt_iir = I915_READ(GTIIR);
24019edbd4a0SFrançois Tigeot 	if (gt_iir) {
240224edb884SFrançois Tigeot 		I915_WRITE(GTIIR, gt_iir);
2403183e2373SFrançois Tigeot 		ret = IRQ_HANDLED;
24041487f786SFrançois Tigeot 		if (INTEL_GEN(dev_priv) >= 6)
24058621f407SFrançois Tigeot 			snb_gt_irq_handler(dev_priv, gt_iir);
24069edbd4a0SFrançois Tigeot 		else
24078621f407SFrançois Tigeot 			ilk_gt_irq_handler(dev_priv, gt_iir);
24089edbd4a0SFrançois Tigeot 	}
2409e3adcf8fSFrançois Tigeot 
24109edbd4a0SFrançois Tigeot 	de_iir = I915_READ(DEIIR);
24119edbd4a0SFrançois Tigeot 	if (de_iir) {
241224edb884SFrançois Tigeot 		I915_WRITE(DEIIR, de_iir);
2413183e2373SFrançois Tigeot 		ret = IRQ_HANDLED;
24141487f786SFrançois Tigeot 		if (INTEL_GEN(dev_priv) >= 7)
24151487f786SFrançois Tigeot 			ivb_display_irq_handler(dev_priv, de_iir);
24169edbd4a0SFrançois Tigeot 		else
24171487f786SFrançois Tigeot 			ilk_display_irq_handler(dev_priv, de_iir);
24189edbd4a0SFrançois Tigeot 	}
24199edbd4a0SFrançois Tigeot 
24201487f786SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 6) {
24219edbd4a0SFrançois Tigeot 		u32 pm_iir = I915_READ(GEN6_PMIIR);
24229edbd4a0SFrançois Tigeot 		if (pm_iir) {
24239edbd4a0SFrançois Tigeot 			I915_WRITE(GEN6_PMIIR, pm_iir);
2424183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
242524edb884SFrançois Tigeot 			gen6_rps_irq_handler(dev_priv, pm_iir);
24269edbd4a0SFrançois Tigeot 		}
24279edbd4a0SFrançois Tigeot 	}
24289edbd4a0SFrançois Tigeot 
2429e3adcf8fSFrançois Tigeot 	I915_WRITE(DEIER, de_ier);
2430e3adcf8fSFrançois Tigeot 	POSTING_READ(DEIER);
24311487f786SFrançois Tigeot 	if (!HAS_PCH_NOP(dev_priv)) {
2432a2fdbec6SFrançois Tigeot 		I915_WRITE(SDEIER, sde_ier);
2433a2fdbec6SFrançois Tigeot 		POSTING_READ(SDEIER);
2434e3adcf8fSFrançois Tigeot 	}
2435e3adcf8fSFrançois Tigeot 
2436aee94f86SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
2437aee94f86SFrançois Tigeot 	enable_rpm_wakeref_asserts(dev_priv);
2438aee94f86SFrançois Tigeot 
2439183e2373SFrançois Tigeot 	return ret;
24409edbd4a0SFrançois Tigeot }
24419edbd4a0SFrançois Tigeot 
24421487f786SFrançois Tigeot static void bxt_hpd_irq_handler(struct drm_i915_private *dev_priv,
24431487f786SFrançois Tigeot 				u32 hotplug_trigger,
2444352ff8bdSFrançois Tigeot 				const u32 hpd[HPD_NUM_PINS])
244519c468b4SFrançois Tigeot {
2446352ff8bdSFrançois Tigeot 	u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
244719c468b4SFrançois Tigeot 
2448352ff8bdSFrançois Tigeot 	dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
2449352ff8bdSFrançois Tigeot 	I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
245019c468b4SFrançois Tigeot 
2451352ff8bdSFrançois Tigeot 	intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
2452352ff8bdSFrançois Tigeot 			   dig_hotplug_reg, hpd,
2453352ff8bdSFrançois Tigeot 			   bxt_port_hotplug_long_detect);
245419c468b4SFrançois Tigeot 
24551487f786SFrançois Tigeot 	intel_hpd_irq_handler(dev_priv, pin_mask, long_mask);
245619c468b4SFrançois Tigeot }
245719c468b4SFrançois Tigeot 
2458c0e85e96SFrançois Tigeot static irqreturn_t
2459c0e85e96SFrançois Tigeot gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl)
24609edbd4a0SFrançois Tigeot {
2461183e2373SFrançois Tigeot 	irqreturn_t ret = IRQ_NONE;
2462c0e85e96SFrançois Tigeot 	u32 iir;
24639edbd4a0SFrançois Tigeot 	enum i915_pipe pipe;
24649edbd4a0SFrançois Tigeot 
24659edbd4a0SFrançois Tigeot 	if (master_ctl & GEN8_DE_MISC_IRQ) {
2466c0e85e96SFrançois Tigeot 		iir = I915_READ(GEN8_DE_MISC_IIR);
2467c0e85e96SFrançois Tigeot 		if (iir) {
2468c0e85e96SFrançois Tigeot 			I915_WRITE(GEN8_DE_MISC_IIR, iir);
2469183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
2470c0e85e96SFrançois Tigeot 			if (iir & GEN8_DE_MISC_GSE)
24711487f786SFrançois Tigeot 				intel_opregion_asle_intr(dev_priv);
247224edb884SFrançois Tigeot 			else
247324edb884SFrançois Tigeot 				DRM_ERROR("Unexpected DE Misc interrupt\n");
24749edbd4a0SFrançois Tigeot 		}
247524edb884SFrançois Tigeot 		else
247624edb884SFrançois Tigeot 			DRM_ERROR("The master control interrupt lied (DE MISC)!\n");
24779edbd4a0SFrançois Tigeot 	}
24789edbd4a0SFrançois Tigeot 
24799edbd4a0SFrançois Tigeot 	if (master_ctl & GEN8_DE_PORT_IRQ) {
2480c0e85e96SFrançois Tigeot 		iir = I915_READ(GEN8_DE_PORT_IIR);
2481c0e85e96SFrançois Tigeot 		if (iir) {
2482c0e85e96SFrançois Tigeot 			u32 tmp_mask;
248319c468b4SFrançois Tigeot 			bool found = false;
2484352ff8bdSFrançois Tigeot 
2485c0e85e96SFrançois Tigeot 			I915_WRITE(GEN8_DE_PORT_IIR, iir);
2486183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
248719c468b4SFrançois Tigeot 
2488c0e85e96SFrançois Tigeot 			tmp_mask = GEN8_AUX_CHANNEL_A;
2489c0e85e96SFrançois Tigeot 			if (INTEL_INFO(dev_priv)->gen >= 9)
2490c0e85e96SFrançois Tigeot 				tmp_mask |= GEN9_AUX_CHANNEL_B |
2491c0e85e96SFrançois Tigeot 					    GEN9_AUX_CHANNEL_C |
2492c0e85e96SFrançois Tigeot 					    GEN9_AUX_CHANNEL_D;
249319c468b4SFrançois Tigeot 
2494c0e85e96SFrançois Tigeot 			if (iir & tmp_mask) {
24951487f786SFrançois Tigeot 				dp_aux_irq_handler(dev_priv);
249619c468b4SFrançois Tigeot 				found = true;
249719c468b4SFrançois Tigeot 			}
249819c468b4SFrançois Tigeot 
2499*a85cb24fSFrançois Tigeot 			if (IS_GEN9_LP(dev_priv)) {
2500c0e85e96SFrançois Tigeot 				tmp_mask = iir & BXT_DE_PORT_HOTPLUG_MASK;
2501c0e85e96SFrançois Tigeot 				if (tmp_mask) {
25021487f786SFrançois Tigeot 					bxt_hpd_irq_handler(dev_priv, tmp_mask,
25031487f786SFrançois Tigeot 							    hpd_bxt);
250419c468b4SFrançois Tigeot 					found = true;
250519c468b4SFrançois Tigeot 				}
2506c0e85e96SFrançois Tigeot 			} else if (IS_BROADWELL(dev_priv)) {
2507c0e85e96SFrançois Tigeot 				tmp_mask = iir & GEN8_PORT_DP_A_HOTPLUG;
2508c0e85e96SFrançois Tigeot 				if (tmp_mask) {
25091487f786SFrançois Tigeot 					ilk_hpd_irq_handler(dev_priv,
25101487f786SFrançois Tigeot 							    tmp_mask, hpd_bdw);
2511c0e85e96SFrançois Tigeot 					found = true;
2512c0e85e96SFrançois Tigeot 				}
2513c0e85e96SFrançois Tigeot 			}
251419c468b4SFrançois Tigeot 
2515*a85cb24fSFrançois Tigeot 			if (IS_GEN9_LP(dev_priv) && (iir & BXT_DE_PORT_GMBUS)) {
25161487f786SFrançois Tigeot 				gmbus_irq_handler(dev_priv);
251719c468b4SFrançois Tigeot 				found = true;
251819c468b4SFrançois Tigeot 			}
251919c468b4SFrançois Tigeot 
252019c468b4SFrançois Tigeot 			if (!found)
252124edb884SFrançois Tigeot 				DRM_ERROR("Unexpected DE Port interrupt\n");
25229edbd4a0SFrançois Tigeot 		}
252324edb884SFrançois Tigeot 		else
252424edb884SFrançois Tigeot 			DRM_ERROR("The master control interrupt lied (DE PORT)!\n");
25259edbd4a0SFrançois Tigeot 	}
25269edbd4a0SFrançois Tigeot 
25271b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
2528c0e85e96SFrançois Tigeot 		u32 flip_done, fault_errors;
25299edbd4a0SFrançois Tigeot 
25309edbd4a0SFrançois Tigeot 		if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe)))
25319edbd4a0SFrançois Tigeot 			continue;
25329edbd4a0SFrançois Tigeot 
2533c0e85e96SFrançois Tigeot 		iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
2534c0e85e96SFrançois Tigeot 		if (!iir) {
2535c0e85e96SFrançois Tigeot 			DRM_ERROR("The master control interrupt lied (DE PIPE)!\n");
2536c0e85e96SFrançois Tigeot 			continue;
2537c0e85e96SFrançois Tigeot 		}
25382c9916cdSFrançois Tigeot 
2539183e2373SFrançois Tigeot 		ret = IRQ_HANDLED;
2540c0e85e96SFrançois Tigeot 		I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir);
2541c0e85e96SFrançois Tigeot 
2542c0e85e96SFrançois Tigeot 		if (iir & GEN8_PIPE_VBLANK &&
25431487f786SFrançois Tigeot 		    intel_pipe_handle_vblank(dev_priv, pipe))
25441487f786SFrançois Tigeot 			intel_check_page_flip(dev_priv, pipe);
25459edbd4a0SFrançois Tigeot 
2546c0e85e96SFrançois Tigeot 		flip_done = iir;
2547352ff8bdSFrançois Tigeot 		if (INTEL_INFO(dev_priv)->gen >= 9)
2548c0e85e96SFrançois Tigeot 			flip_done &= GEN9_PIPE_PLANE1_FLIP_DONE;
25492c9916cdSFrançois Tigeot 		else
2550c0e85e96SFrançois Tigeot 			flip_done &= GEN8_PIPE_PRIMARY_FLIP_DONE;
25512c9916cdSFrançois Tigeot 
25521487f786SFrançois Tigeot 		if (flip_done)
25531487f786SFrançois Tigeot 			intel_finish_page_flip_cs(dev_priv, pipe);
25549edbd4a0SFrançois Tigeot 
2555c0e85e96SFrançois Tigeot 		if (iir & GEN8_PIPE_CDCLK_CRC_DONE)
25561487f786SFrançois Tigeot 			hsw_pipe_crc_irq_handler(dev_priv, pipe);
25579edbd4a0SFrançois Tigeot 
2558c0e85e96SFrançois Tigeot 		if (iir & GEN8_PIPE_FIFO_UNDERRUN)
2559c0e85e96SFrançois Tigeot 			intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
25609edbd4a0SFrançois Tigeot 
2561c0e85e96SFrançois Tigeot 		fault_errors = iir;
2562352ff8bdSFrançois Tigeot 		if (INTEL_INFO(dev_priv)->gen >= 9)
2563c0e85e96SFrançois Tigeot 			fault_errors &= GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
25642c9916cdSFrançois Tigeot 		else
2565c0e85e96SFrançois Tigeot 			fault_errors &= GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
25662c9916cdSFrançois Tigeot 
25672c9916cdSFrançois Tigeot 		if (fault_errors)
25684be47400SFrançois Tigeot 			DRM_ERROR("Fault errors on pipe %c: 0x%08x\n",
25699edbd4a0SFrançois Tigeot 				  pipe_name(pipe),
2570c0e85e96SFrançois Tigeot 				  fault_errors);
25719edbd4a0SFrançois Tigeot 	}
25729edbd4a0SFrançois Tigeot 
25731487f786SFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv) && !HAS_PCH_NOP(dev_priv) &&
257419c468b4SFrançois Tigeot 	    master_ctl & GEN8_DE_PCH_IRQ) {
25759edbd4a0SFrançois Tigeot 		/*
25769edbd4a0SFrançois Tigeot 		 * FIXME(BDW): Assume for now that the new interrupt handling
25779edbd4a0SFrançois Tigeot 		 * scheme also closed the SDE interrupt handling race we've seen
25789edbd4a0SFrançois Tigeot 		 * on older pch-split platforms. But this needs testing.
25799edbd4a0SFrançois Tigeot 		 */
2580c0e85e96SFrançois Tigeot 		iir = I915_READ(SDEIIR);
2581c0e85e96SFrançois Tigeot 		if (iir) {
2582c0e85e96SFrançois Tigeot 			I915_WRITE(SDEIIR, iir);
2583183e2373SFrançois Tigeot 			ret = IRQ_HANDLED;
2584352ff8bdSFrançois Tigeot 
2585303bf270SFrançois Tigeot 			if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv))
25861487f786SFrançois Tigeot 				spt_irq_handler(dev_priv, iir);
2587352ff8bdSFrançois Tigeot 			else
25881487f786SFrançois Tigeot 				cpt_irq_handler(dev_priv, iir);
2589aee94f86SFrançois Tigeot 		} else {
2590aee94f86SFrançois Tigeot 			/*
2591aee94f86SFrançois Tigeot 			 * Like on previous PCH there seems to be something
2592aee94f86SFrançois Tigeot 			 * fishy going on with forwarding PCH interrupts.
2593aee94f86SFrançois Tigeot 			 */
2594aee94f86SFrançois Tigeot 			DRM_DEBUG_DRIVER("The master control interrupt lied (SDE)!\n");
2595aee94f86SFrançois Tigeot 		}
25969edbd4a0SFrançois Tigeot 	}
25979edbd4a0SFrançois Tigeot 
2598183e2373SFrançois Tigeot 	return ret;
2599c0e85e96SFrançois Tigeot }
2600c0e85e96SFrançois Tigeot 
2601183e2373SFrançois Tigeot static irqreturn_t gen8_irq_handler(int irq, void *arg)
2602c0e85e96SFrançois Tigeot {
2603c0e85e96SFrançois Tigeot 	struct drm_device *dev = arg;
2604303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
2605c0e85e96SFrançois Tigeot 	u32 master_ctl;
26068621f407SFrançois Tigeot 	u32 gt_iir[4] = {};
2607183e2373SFrançois Tigeot 	irqreturn_t ret;
2608c0e85e96SFrançois Tigeot 
2609c0e85e96SFrançois Tigeot 	if (!intel_irqs_enabled(dev_priv))
2610c0e85e96SFrançois Tigeot 		return IRQ_NONE;
2611c0e85e96SFrançois Tigeot 
2612c0e85e96SFrançois Tigeot 	master_ctl = I915_READ_FW(GEN8_MASTER_IRQ);
2613c0e85e96SFrançois Tigeot 	master_ctl &= ~GEN8_MASTER_IRQ_CONTROL;
2614c0e85e96SFrançois Tigeot 	if (!master_ctl)
2615c0e85e96SFrançois Tigeot 		return IRQ_NONE;
2616c0e85e96SFrançois Tigeot 
2617c0e85e96SFrançois Tigeot 	I915_WRITE_FW(GEN8_MASTER_IRQ, 0);
2618c0e85e96SFrançois Tigeot 
2619c0e85e96SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
2620c0e85e96SFrançois Tigeot 	disable_rpm_wakeref_asserts(dev_priv);
2621c0e85e96SFrançois Tigeot 
2622c0e85e96SFrançois Tigeot 	/* Find, clear, then process each source of interrupt */
2623183e2373SFrançois Tigeot 	ret = gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
26248621f407SFrançois Tigeot 	gen8_gt_irq_handler(dev_priv, gt_iir);
2625183e2373SFrançois Tigeot 	ret |= gen8_de_irq_handler(dev_priv, master_ctl);
2626c0e85e96SFrançois Tigeot 
262719c468b4SFrançois Tigeot 	I915_WRITE_FW(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
262819c468b4SFrançois Tigeot 	POSTING_READ_FW(GEN8_MASTER_IRQ);
26299edbd4a0SFrançois Tigeot 
2630aee94f86SFrançois Tigeot 	enable_rpm_wakeref_asserts(dev_priv);
2631aee94f86SFrançois Tigeot 
2632183e2373SFrançois Tigeot 	return ret;
26339edbd4a0SFrançois Tigeot }
26349edbd4a0SFrançois Tigeot 
2635e3adcf8fSFrançois Tigeot /**
26362c9916cdSFrançois Tigeot  * i915_reset_and_wakeup - do process context error handling work
26371487f786SFrançois Tigeot  * @dev_priv: i915 device private
2638e3adcf8fSFrançois Tigeot  *
2639e3adcf8fSFrançois Tigeot  * Fire an error uevent so userspace can see that a hang or error
2640e3adcf8fSFrançois Tigeot  * was detected.
2641e3adcf8fSFrançois Tigeot  */
26421487f786SFrançois Tigeot static void i915_reset_and_wakeup(struct drm_i915_private *dev_priv)
2643e3adcf8fSFrançois Tigeot {
2644303bf270SFrançois Tigeot 	struct kobject *kobj = &dev_priv->drm.primary->kdev->kobj;
26459edbd4a0SFrançois Tigeot 	char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
26469edbd4a0SFrançois Tigeot 	char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
26479edbd4a0SFrançois Tigeot 	char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
2648e3adcf8fSFrançois Tigeot 
26491487f786SFrançois Tigeot 	kobject_uevent_env(kobj, KOBJ_CHANGE, error_event);
2650e3adcf8fSFrançois Tigeot 
2651abf1f4f4SFrançois Tigeot 	DRM_DEBUG_DRIVER("resetting chip\n");
26521487f786SFrançois Tigeot 	kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event);
2653a2fdbec6SFrançois Tigeot 
26541487f786SFrançois Tigeot 	intel_prepare_reset(dev_priv);
26552c9916cdSFrançois Tigeot 
2656*a85cb24fSFrançois Tigeot 	set_bit(I915_RESET_HANDOFF, &dev_priv->gpu_error.flags);
2657*a85cb24fSFrançois Tigeot 	wake_up_all(&dev_priv->gpu_error.wait_queue);
2658*a85cb24fSFrançois Tigeot 
26591e12ee3bSFrançois Tigeot 	do {
2660ba55f2f5SFrançois Tigeot 		/*
26619edbd4a0SFrançois Tigeot 		 * All state reset _must_ be completed before we update the
26629edbd4a0SFrançois Tigeot 		 * reset counter, for otherwise waiters might miss the reset
26639edbd4a0SFrançois Tigeot 		 * pending state and not properly drop locks, resulting in
26649edbd4a0SFrançois Tigeot 		 * deadlocks with the reset work.
26659edbd4a0SFrançois Tigeot 		 */
26661e12ee3bSFrançois Tigeot 		if (mutex_trylock(&dev_priv->drm.struct_mutex)) {
26671e12ee3bSFrançois Tigeot 			i915_reset(dev_priv);
26681e12ee3bSFrançois Tigeot 			mutex_unlock(&dev_priv->drm.struct_mutex);
26691e12ee3bSFrançois Tigeot 		}
26701e12ee3bSFrançois Tigeot 
26711e12ee3bSFrançois Tigeot 		/* We need to wait for anyone holding the lock to wakeup */
26721e12ee3bSFrançois Tigeot 	} while (wait_on_bit_timeout(&dev_priv->gpu_error.flags,
2673*a85cb24fSFrançois Tigeot 				     I915_RESET_HANDOFF,
26741e12ee3bSFrançois Tigeot 				     TASK_UNINTERRUPTIBLE,
26751e12ee3bSFrançois Tigeot 				     HZ));
2676a2fdbec6SFrançois Tigeot 
26771487f786SFrançois Tigeot 	intel_finish_reset(dev_priv);
2678ba55f2f5SFrançois Tigeot 
26791e12ee3bSFrançois Tigeot 	if (!test_bit(I915_WEDGED, &dev_priv->gpu_error.flags))
26801487f786SFrançois Tigeot 		kobject_uevent_env(kobj,
2681a2fdbec6SFrançois Tigeot 				   KOBJ_CHANGE, reset_done_event);
2682a2fdbec6SFrançois Tigeot 
26839edbd4a0SFrançois Tigeot 	/*
26849edbd4a0SFrançois Tigeot 	 * Note: The wake_up also serves as a memory barrier so that
26851e12ee3bSFrançois Tigeot 	 * waiters see the updated value of the dev_priv->gpu_error.
2686e3adcf8fSFrançois Tigeot 	 */
2687*a85cb24fSFrançois Tigeot 	clear_bit(I915_RESET_BACKOFF, &dev_priv->gpu_error.flags);
26881487f786SFrançois Tigeot 	wake_up_all(&dev_priv->gpu_error.reset_queue);
2689e3adcf8fSFrançois Tigeot }
2690e3adcf8fSFrançois Tigeot 
26911e12ee3bSFrançois Tigeot static inline void
26921e12ee3bSFrançois Tigeot i915_err_print_instdone(struct drm_i915_private *dev_priv,
26931e12ee3bSFrançois Tigeot 			struct intel_instdone *instdone)
2694e9243325SFrançois Tigeot {
26951e12ee3bSFrançois Tigeot 	int slice;
26961e12ee3bSFrançois Tigeot 	int subslice;
2697e9243325SFrançois Tigeot 
26981e12ee3bSFrançois Tigeot 	pr_err("  INSTDONE: 0x%08x\n", instdone->instdone);
26991e12ee3bSFrançois Tigeot 
27001e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) <= 3)
2701e9243325SFrançois Tigeot 		return;
2702e9243325SFrançois Tigeot 
27031e12ee3bSFrançois Tigeot 	pr_err("  SC_INSTDONE: 0x%08x\n", instdone->slice_common);
270400640ec9SFrançois Tigeot 
27051e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) <= 6)
27061e12ee3bSFrançois Tigeot 		return;
2707e9243325SFrançois Tigeot 
27081e12ee3bSFrançois Tigeot 	for_each_instdone_slice_subslice(dev_priv, slice, subslice)
27091e12ee3bSFrançois Tigeot 		pr_err("  SAMPLER_INSTDONE[%d][%d]: 0x%08x\n",
27101e12ee3bSFrançois Tigeot 		       slice, subslice, instdone->sampler[slice][subslice]);
2711e9243325SFrançois Tigeot 
27121e12ee3bSFrançois Tigeot 	for_each_instdone_slice_subslice(dev_priv, slice, subslice)
27131e12ee3bSFrançois Tigeot 		pr_err("  ROW_INSTDONE[%d][%d]: 0x%08x\n",
27141e12ee3bSFrançois Tigeot 		       slice, subslice, instdone->row[slice][subslice]);
2715e9243325SFrançois Tigeot }
2716e9243325SFrançois Tigeot 
27171e12ee3bSFrançois Tigeot static void i915_clear_error_registers(struct drm_i915_private *dev_priv)
27181e12ee3bSFrançois Tigeot {
27191e12ee3bSFrançois Tigeot 	u32 eir;
2720e9243325SFrançois Tigeot 
27211e12ee3bSFrançois Tigeot 	if (!IS_GEN2(dev_priv))
27221e12ee3bSFrançois Tigeot 		I915_WRITE(PGTBL_ER, I915_READ(PGTBL_ER));
2723e9243325SFrançois Tigeot 
27241e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 4)
27251e12ee3bSFrançois Tigeot 		I915_WRITE(IPEIR, I915_READ(IPEIR));
27261e12ee3bSFrançois Tigeot 	else
27271e12ee3bSFrançois Tigeot 		I915_WRITE(IPEIR_I965, I915_READ(IPEIR_I965));
2728e9243325SFrançois Tigeot 
27291e12ee3bSFrançois Tigeot 	I915_WRITE(EIR, I915_READ(EIR));
2730e9243325SFrançois Tigeot 	eir = I915_READ(EIR);
2731e9243325SFrançois Tigeot 	if (eir) {
2732e9243325SFrançois Tigeot 		/*
2733e9243325SFrançois Tigeot 		 * some errors might have become stuck,
2734e9243325SFrançois Tigeot 		 * mask them.
2735e9243325SFrançois Tigeot 		 */
27361e12ee3bSFrançois Tigeot 		DRM_DEBUG_DRIVER("EIR stuck: 0x%08x, masking\n", eir);
2737e9243325SFrançois Tigeot 		I915_WRITE(EMR, I915_READ(EMR) | eir);
2738e9243325SFrançois Tigeot 		I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
2739e9243325SFrançois Tigeot 	}
2740e9243325SFrançois Tigeot }
2741e9243325SFrançois Tigeot 
2742e9243325SFrançois Tigeot /**
27432c9916cdSFrançois Tigeot  * i915_handle_error - handle a gpu error
27441487f786SFrançois Tigeot  * @dev_priv: i915 device private
27458621f407SFrançois Tigeot  * @engine_mask: mask representing engines that are hung
2746*a85cb24fSFrançois Tigeot  * @fmt: Error message format string
2747*a85cb24fSFrançois Tigeot  *
2748352ff8bdSFrançois Tigeot  * Do some basic checking of register state at error time and
2749e9243325SFrançois Tigeot  * dump it to the syslog.  Also call i915_capture_error_state() to make
2750e9243325SFrançois Tigeot  * sure we get a record and make it available in debugfs.  Fire a uevent
2751e9243325SFrançois Tigeot  * so userspace knows something bad happened (should trigger collection
2752e9243325SFrançois Tigeot  * of a ring dump etc.).
2753e9243325SFrançois Tigeot  */
27541487f786SFrançois Tigeot void i915_handle_error(struct drm_i915_private *dev_priv,
27551487f786SFrançois Tigeot 		       u32 engine_mask,
2756ba55f2f5SFrançois Tigeot 		       const char *fmt, ...)
2757e9243325SFrançois Tigeot {
2758ba55f2f5SFrançois Tigeot 	va_list args;
2759ba55f2f5SFrançois Tigeot 	char error_msg[80];
2760ba55f2f5SFrançois Tigeot 
2761ba55f2f5SFrançois Tigeot 	va_start(args, fmt);
2762ba55f2f5SFrançois Tigeot 	vscnprintf(error_msg, sizeof(error_msg), fmt, args);
2763ba55f2f5SFrançois Tigeot 	va_end(args);
2764ba55f2f5SFrançois Tigeot 
2765*a85cb24fSFrançois Tigeot 	/*
2766*a85cb24fSFrançois Tigeot 	 * In most cases it's guaranteed that we get here with an RPM
2767*a85cb24fSFrançois Tigeot 	 * reference held, for example because there is a pending GPU
2768*a85cb24fSFrançois Tigeot 	 * request that won't finish until the reset is done. This
2769*a85cb24fSFrançois Tigeot 	 * isn't the case at least when we get here by doing a
2770*a85cb24fSFrançois Tigeot 	 * simulated reset via debugfs, so get an RPM reference.
2771*a85cb24fSFrançois Tigeot 	 */
2772*a85cb24fSFrançois Tigeot 	intel_runtime_pm_get(dev_priv);
2773*a85cb24fSFrançois Tigeot 
27741487f786SFrançois Tigeot 	i915_capture_error_state(dev_priv, engine_mask, error_msg);
27751e12ee3bSFrançois Tigeot 	i915_clear_error_registers(dev_priv);
2776e9243325SFrançois Tigeot 
27771e12ee3bSFrançois Tigeot 	if (!engine_mask)
2778*a85cb24fSFrançois Tigeot 		goto out;
27791e12ee3bSFrançois Tigeot 
2780*a85cb24fSFrançois Tigeot 	if (test_and_set_bit(I915_RESET_BACKOFF,
27811e12ee3bSFrançois Tigeot 			     &dev_priv->gpu_error.flags))
2782*a85cb24fSFrançois Tigeot 		goto out;
2783e9243325SFrançois Tigeot 
27841487f786SFrançois Tigeot 	i915_reset_and_wakeup(dev_priv);
2785*a85cb24fSFrançois Tigeot 
2786*a85cb24fSFrançois Tigeot out:
2787*a85cb24fSFrançois Tigeot 	intel_runtime_pm_put(dev_priv);
2788e9243325SFrançois Tigeot }
2789e9243325SFrançois Tigeot 
2790e9243325SFrançois Tigeot /* Called from drm generic code, passed 'crtc' which
2791e9243325SFrançois Tigeot  * we use as a pipe index
2792e9243325SFrançois Tigeot  */
27931e12ee3bSFrançois Tigeot static int i8xx_enable_vblank(struct drm_device *dev, unsigned int pipe)
2794e9243325SFrançois Tigeot {
2795303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
27965e269720SFrançois Tigeot 	unsigned long irqflags;
2797e9243325SFrançois Tigeot 
27985e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
27991e12ee3bSFrançois Tigeot 	i915_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS);
28001e12ee3bSFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
28011e12ee3bSFrançois Tigeot 
28021e12ee3bSFrançois Tigeot 	return 0;
28031e12ee3bSFrançois Tigeot }
28041e12ee3bSFrançois Tigeot 
28051e12ee3bSFrançois Tigeot static int i965_enable_vblank(struct drm_device *dev, unsigned int pipe)
28061e12ee3bSFrançois Tigeot {
28071e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
28081e12ee3bSFrançois Tigeot 	unsigned long irqflags;
28091e12ee3bSFrançois Tigeot 
28101e12ee3bSFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
2811e9243325SFrançois Tigeot 	i915_enable_pipestat(dev_priv, pipe,
2812ba55f2f5SFrançois Tigeot 			     PIPE_START_VBLANK_INTERRUPT_STATUS);
28135e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
2814e9243325SFrançois Tigeot 
2815e9243325SFrançois Tigeot 	return 0;
2816e9243325SFrançois Tigeot }
2817e9243325SFrançois Tigeot 
2818352ff8bdSFrançois Tigeot static int ironlake_enable_vblank(struct drm_device *dev, unsigned int pipe)
2819e9243325SFrançois Tigeot {
2820303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
28215e269720SFrançois Tigeot 	unsigned long irqflags;
28221e12ee3bSFrançois Tigeot 	uint32_t bit = INTEL_GEN(dev_priv) >= 7 ?
28231e12ee3bSFrançois Tigeot 		DE_PIPE_VBLANK_IVB(pipe) : DE_PIPE_VBLANK(pipe);
2824e9243325SFrançois Tigeot 
28255e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
2826aee94f86SFrançois Tigeot 	ilk_enable_display_irq(dev_priv, bit);
28275e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
2828e9243325SFrançois Tigeot 
2829e9243325SFrançois Tigeot 	return 0;
2830e9243325SFrançois Tigeot }
2831e9243325SFrançois Tigeot 
2832352ff8bdSFrançois Tigeot static int gen8_enable_vblank(struct drm_device *dev, unsigned int pipe)
28339edbd4a0SFrançois Tigeot {
2834303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
28355e269720SFrançois Tigeot 	unsigned long irqflags;
28369edbd4a0SFrançois Tigeot 
28375e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
2838aee94f86SFrançois Tigeot 	bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
28395e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
2840aee94f86SFrançois Tigeot 
28419edbd4a0SFrançois Tigeot 	return 0;
28429edbd4a0SFrançois Tigeot }
28439edbd4a0SFrançois Tigeot 
2844e9243325SFrançois Tigeot /* Called from drm generic code, passed 'crtc' which
2845e9243325SFrançois Tigeot  * we use as a pipe index
2846e9243325SFrançois Tigeot  */
28471e12ee3bSFrançois Tigeot static void i8xx_disable_vblank(struct drm_device *dev, unsigned int pipe)
28481e12ee3bSFrançois Tigeot {
28491e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
28501e12ee3bSFrançois Tigeot 	unsigned long irqflags;
28511e12ee3bSFrançois Tigeot 
28521e12ee3bSFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
28531e12ee3bSFrançois Tigeot 	i915_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS);
28541e12ee3bSFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
28551e12ee3bSFrançois Tigeot }
28561e12ee3bSFrançois Tigeot 
28571e12ee3bSFrançois Tigeot static void i965_disable_vblank(struct drm_device *dev, unsigned int pipe)
2858e9243325SFrançois Tigeot {
2859303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
28605e269720SFrançois Tigeot 	unsigned long irqflags;
2861e9243325SFrançois Tigeot 
28625e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
2863e9243325SFrançois Tigeot 	i915_disable_pipestat(dev_priv, pipe,
2864ba55f2f5SFrançois Tigeot 			      PIPE_START_VBLANK_INTERRUPT_STATUS);
28655e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
2866e9243325SFrançois Tigeot }
2867e9243325SFrançois Tigeot 
2868352ff8bdSFrançois Tigeot static void ironlake_disable_vblank(struct drm_device *dev, unsigned int pipe)
2869e9243325SFrançois Tigeot {
2870303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
28715e269720SFrançois Tigeot 	unsigned long irqflags;
28721e12ee3bSFrançois Tigeot 	uint32_t bit = INTEL_GEN(dev_priv) >= 7 ?
28731e12ee3bSFrançois Tigeot 		DE_PIPE_VBLANK_IVB(pipe) : DE_PIPE_VBLANK(pipe);
2874e9243325SFrançois Tigeot 
28755e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
2876aee94f86SFrançois Tigeot 	ilk_disable_display_irq(dev_priv, bit);
28775e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
2878e9243325SFrançois Tigeot }
2879e9243325SFrançois Tigeot 
2880352ff8bdSFrançois Tigeot static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe)
28819edbd4a0SFrançois Tigeot {
2882303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
28835e269720SFrançois Tigeot 	unsigned long irqflags;
28849edbd4a0SFrançois Tigeot 
28855e269720SFrançois Tigeot 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
2886aee94f86SFrançois Tigeot 	bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
28875e269720SFrançois Tigeot 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
28889edbd4a0SFrançois Tigeot }
28899edbd4a0SFrançois Tigeot 
28904be47400SFrançois Tigeot static void ibx_irq_reset(struct drm_i915_private *dev_priv)
2891ba55f2f5SFrançois Tigeot {
28921e12ee3bSFrançois Tigeot 	if (HAS_PCH_NOP(dev_priv))
28935d0b1887SFrançois Tigeot 		return;
28945d0b1887SFrançois Tigeot 
2895ba55f2f5SFrançois Tigeot 	GEN5_IRQ_RESET(SDE);
2896ba55f2f5SFrançois Tigeot 
28971e12ee3bSFrançois Tigeot 	if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv))
2898ba55f2f5SFrançois Tigeot 		I915_WRITE(SERR_INT, 0xffffffff);
2899ba55f2f5SFrançois Tigeot }
2900ba55f2f5SFrançois Tigeot 
29015d0b1887SFrançois Tigeot /*
2902ba55f2f5SFrançois Tigeot  * SDEIER is also touched by the interrupt handler to work around missed PCH
2903ba55f2f5SFrançois Tigeot  * interrupts. Hence we can't update it after the interrupt handler is enabled -
2904ba55f2f5SFrançois Tigeot  * instead we unconditionally enable all PCH interrupt sources here, but then
2905ba55f2f5SFrançois Tigeot  * only unmask them as needed with SDEIMR.
2906ba55f2f5SFrançois Tigeot  *
2907ba55f2f5SFrançois Tigeot  * This function needs to be called before interrupts are enabled.
29085d0b1887SFrançois Tigeot  */
2909ba55f2f5SFrançois Tigeot static void ibx_irq_pre_postinstall(struct drm_device *dev)
2910ba55f2f5SFrançois Tigeot {
2911303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
2912ba55f2f5SFrançois Tigeot 
29131e12ee3bSFrançois Tigeot 	if (HAS_PCH_NOP(dev_priv))
2914ba55f2f5SFrançois Tigeot 		return;
2915ba55f2f5SFrançois Tigeot 
2916ba55f2f5SFrançois Tigeot 	WARN_ON(I915_READ(SDEIER) != 0);
29175d0b1887SFrançois Tigeot 	I915_WRITE(SDEIER, 0xffffffff);
29185d0b1887SFrançois Tigeot 	POSTING_READ(SDEIER);
2919e9243325SFrançois Tigeot }
2920e9243325SFrançois Tigeot 
29214be47400SFrançois Tigeot static void gen5_gt_irq_reset(struct drm_i915_private *dev_priv)
29229edbd4a0SFrançois Tigeot {
2923ba55f2f5SFrançois Tigeot 	GEN5_IRQ_RESET(GT);
29244be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 6)
2925ba55f2f5SFrançois Tigeot 		GEN5_IRQ_RESET(GEN6_PM);
29269edbd4a0SFrançois Tigeot }
29279edbd4a0SFrançois Tigeot 
29288621f407SFrançois Tigeot static void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
29298621f407SFrançois Tigeot {
29308621f407SFrançois Tigeot 	enum i915_pipe pipe;
29318621f407SFrançois Tigeot 
29328621f407SFrançois Tigeot 	if (IS_CHERRYVIEW(dev_priv))
29338621f407SFrançois Tigeot 		I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
29348621f407SFrançois Tigeot 	else
29358621f407SFrançois Tigeot 		I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK);
29368621f407SFrançois Tigeot 
29378621f407SFrançois Tigeot 	i915_hotplug_interrupt_update_locked(dev_priv, 0xffffffff, 0);
29388621f407SFrançois Tigeot 	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
29398621f407SFrançois Tigeot 
29408621f407SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
29418621f407SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe),
29428621f407SFrançois Tigeot 			   PIPE_FIFO_UNDERRUN_STATUS |
29438621f407SFrançois Tigeot 			   PIPESTAT_INT_STATUS_MASK);
29448621f407SFrançois Tigeot 		dev_priv->pipestat_irq_mask[pipe] = 0;
29458621f407SFrançois Tigeot 	}
29468621f407SFrançois Tigeot 
29478621f407SFrançois Tigeot 	GEN5_IRQ_RESET(VLV_);
29488621f407SFrançois Tigeot 	dev_priv->irq_mask = ~0;
29498621f407SFrançois Tigeot }
29508621f407SFrançois Tigeot 
29518621f407SFrançois Tigeot static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
29528621f407SFrançois Tigeot {
29538621f407SFrançois Tigeot 	u32 pipestat_mask;
29548621f407SFrançois Tigeot 	u32 enable_mask;
29558621f407SFrançois Tigeot 	enum i915_pipe pipe;
29568621f407SFrançois Tigeot 
29578621f407SFrançois Tigeot 	pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV |
29588621f407SFrançois Tigeot 			PIPE_CRC_DONE_INTERRUPT_STATUS;
29598621f407SFrançois Tigeot 
29608621f407SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
29618621f407SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
29628621f407SFrançois Tigeot 		i915_enable_pipestat(dev_priv, pipe, pipestat_mask);
29638621f407SFrançois Tigeot 
29648621f407SFrançois Tigeot 	enable_mask = I915_DISPLAY_PORT_INTERRUPT |
29658621f407SFrançois Tigeot 		I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
2966*a85cb24fSFrançois Tigeot 		I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
2967*a85cb24fSFrançois Tigeot 		I915_LPE_PIPE_A_INTERRUPT |
2968*a85cb24fSFrançois Tigeot 		I915_LPE_PIPE_B_INTERRUPT;
2969*a85cb24fSFrançois Tigeot 
29708621f407SFrançois Tigeot 	if (IS_CHERRYVIEW(dev_priv))
2971*a85cb24fSFrançois Tigeot 		enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT |
2972*a85cb24fSFrançois Tigeot 			I915_LPE_PIPE_C_INTERRUPT;
29738621f407SFrançois Tigeot 
29748621f407SFrançois Tigeot 	WARN_ON(dev_priv->irq_mask != ~0);
29758621f407SFrançois Tigeot 
29768621f407SFrançois Tigeot 	dev_priv->irq_mask = ~enable_mask;
29778621f407SFrançois Tigeot 
29788621f407SFrançois Tigeot 	GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask);
29798621f407SFrançois Tigeot }
29808621f407SFrançois Tigeot 
2981e9243325SFrançois Tigeot /* drm_dma.h hooks
2982e9243325SFrançois Tigeot */
2983ba55f2f5SFrançois Tigeot static void ironlake_irq_reset(struct drm_device *dev)
2984e9243325SFrançois Tigeot {
2985303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
2986e9243325SFrançois Tigeot 
2987ba55f2f5SFrançois Tigeot 	I915_WRITE(HWSTAM, 0xffffffff);
2988e9243325SFrançois Tigeot 
2989ba55f2f5SFrançois Tigeot 	GEN5_IRQ_RESET(DE);
29901e12ee3bSFrançois Tigeot 	if (IS_GEN7(dev_priv))
2991ba55f2f5SFrançois Tigeot 		I915_WRITE(GEN7_ERR_INT, 0xffffffff);
2992e9243325SFrançois Tigeot 
29934be47400SFrançois Tigeot 	gen5_gt_irq_reset(dev_priv);
2994e9243325SFrançois Tigeot 
29954be47400SFrançois Tigeot 	ibx_irq_reset(dev_priv);
2996e9243325SFrançois Tigeot }
2997e9243325SFrançois Tigeot 
2998e9243325SFrançois Tigeot static void valleyview_irq_preinstall(struct drm_device *dev)
2999e9243325SFrançois Tigeot {
3000303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3001e9243325SFrançois Tigeot 
30028621f407SFrançois Tigeot 	I915_WRITE(VLV_MASTER_IER, 0);
30038621f407SFrançois Tigeot 	POSTING_READ(VLV_MASTER_IER);
3004e9243325SFrançois Tigeot 
30054be47400SFrançois Tigeot 	gen5_gt_irq_reset(dev_priv);
3006e9243325SFrançois Tigeot 
30078621f407SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
30088621f407SFrançois Tigeot 	if (dev_priv->display_irqs_enabled)
30092c9916cdSFrançois Tigeot 		vlv_display_irq_reset(dev_priv);
30108621f407SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
3011e9243325SFrançois Tigeot }
3012e9243325SFrançois Tigeot 
3013ba55f2f5SFrançois Tigeot static void gen8_gt_irq_reset(struct drm_i915_private *dev_priv)
3014ba55f2f5SFrançois Tigeot {
3015ba55f2f5SFrançois Tigeot 	GEN8_IRQ_RESET_NDX(GT, 0);
3016ba55f2f5SFrançois Tigeot 	GEN8_IRQ_RESET_NDX(GT, 1);
3017ba55f2f5SFrançois Tigeot 	GEN8_IRQ_RESET_NDX(GT, 2);
3018ba55f2f5SFrançois Tigeot 	GEN8_IRQ_RESET_NDX(GT, 3);
3019ba55f2f5SFrançois Tigeot }
3020ba55f2f5SFrançois Tigeot 
3021ba55f2f5SFrançois Tigeot static void gen8_irq_reset(struct drm_device *dev)
30229edbd4a0SFrançois Tigeot {
3023303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
30249edbd4a0SFrançois Tigeot 	int pipe;
30259edbd4a0SFrançois Tigeot 
3026ba55f2f5SFrançois Tigeot 	I915_WRITE(GEN8_MASTER_IRQ, 0);
3027ba55f2f5SFrançois Tigeot 	POSTING_READ(GEN8_MASTER_IRQ);
3028ba55f2f5SFrançois Tigeot 
3029ba55f2f5SFrançois Tigeot 	gen8_gt_irq_reset(dev_priv);
3030ba55f2f5SFrançois Tigeot 
30311b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
30322c9916cdSFrançois Tigeot 		if (intel_display_power_is_enabled(dev_priv,
303324edb884SFrançois Tigeot 						   POWER_DOMAIN_PIPE(pipe)))
3034ba55f2f5SFrançois Tigeot 			GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
3035ba55f2f5SFrançois Tigeot 
3036ba55f2f5SFrançois Tigeot 	GEN5_IRQ_RESET(GEN8_DE_PORT_);
3037ba55f2f5SFrançois Tigeot 	GEN5_IRQ_RESET(GEN8_DE_MISC_);
3038ba55f2f5SFrançois Tigeot 	GEN5_IRQ_RESET(GEN8_PCU_);
3039ba55f2f5SFrançois Tigeot 
30401e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
30414be47400SFrançois Tigeot 		ibx_irq_reset(dev_priv);
3042ba55f2f5SFrançois Tigeot }
3043ba55f2f5SFrançois Tigeot 
3044477eb7f9SFrançois Tigeot void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv,
3045477eb7f9SFrançois Tigeot 				     unsigned int pipe_mask)
304624edb884SFrançois Tigeot {
3047acf467cbSFrançois Tigeot 	uint32_t extra_ier = GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN;
3048c0e85e96SFrançois Tigeot 	enum i915_pipe pipe;
304924edb884SFrançois Tigeot 
30505e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
3051c0e85e96SFrançois Tigeot 	for_each_pipe_masked(dev_priv, pipe, pipe_mask)
3052c0e85e96SFrançois Tigeot 		GEN8_IRQ_INIT_NDX(DE_PIPE, pipe,
3053c0e85e96SFrançois Tigeot 				  dev_priv->de_irq_mask[pipe],
3054c0e85e96SFrançois Tigeot 				  ~dev_priv->de_irq_mask[pipe] | extra_ier);
30555e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
305624edb884SFrançois Tigeot }
305724edb884SFrançois Tigeot 
3058c0e85e96SFrançois Tigeot void gen8_irq_power_well_pre_disable(struct drm_i915_private *dev_priv,
3059c0e85e96SFrançois Tigeot 				     unsigned int pipe_mask)
3060c0e85e96SFrançois Tigeot {
3061c0e85e96SFrançois Tigeot 	enum i915_pipe pipe;
3062c0e85e96SFrançois Tigeot 
3063c0e85e96SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
3064c0e85e96SFrançois Tigeot 	for_each_pipe_masked(dev_priv, pipe, pipe_mask)
3065c0e85e96SFrançois Tigeot 		GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
3066c0e85e96SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
3067c0e85e96SFrançois Tigeot 
3068c0e85e96SFrançois Tigeot 	/* make sure we're done processing display irqs */
3069303bf270SFrançois Tigeot 	synchronize_irq(dev_priv->drm.irq);
3070c0e85e96SFrançois Tigeot }
3071c0e85e96SFrançois Tigeot 
3072ba55f2f5SFrançois Tigeot static void cherryview_irq_preinstall(struct drm_device *dev)
3073ba55f2f5SFrançois Tigeot {
3074303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
30759edbd4a0SFrançois Tigeot 
30769edbd4a0SFrançois Tigeot 	I915_WRITE(GEN8_MASTER_IRQ, 0);
30779edbd4a0SFrançois Tigeot 	POSTING_READ(GEN8_MASTER_IRQ);
30789edbd4a0SFrançois Tigeot 
3079ba55f2f5SFrançois Tigeot 	gen8_gt_irq_reset(dev_priv);
30809edbd4a0SFrançois Tigeot 
3081ba55f2f5SFrançois Tigeot 	GEN5_IRQ_RESET(GEN8_PCU_);
30829edbd4a0SFrançois Tigeot 
30838621f407SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
30848621f407SFrançois Tigeot 	if (dev_priv->display_irqs_enabled)
30852c9916cdSFrançois Tigeot 		vlv_display_irq_reset(dev_priv);
30868621f407SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
30879edbd4a0SFrançois Tigeot }
30889edbd4a0SFrançois Tigeot 
30891487f786SFrançois Tigeot static u32 intel_hpd_enabled_irqs(struct drm_i915_private *dev_priv,
3090352ff8bdSFrançois Tigeot 				  const u32 hpd[HPD_NUM_PINS])
3091352ff8bdSFrançois Tigeot {
3092352ff8bdSFrançois Tigeot 	struct intel_encoder *encoder;
3093352ff8bdSFrançois Tigeot 	u32 enabled_irqs = 0;
3094352ff8bdSFrançois Tigeot 
3095303bf270SFrançois Tigeot 	for_each_intel_encoder(&dev_priv->drm, encoder)
3096352ff8bdSFrançois Tigeot 		if (dev_priv->hotplug.stats[encoder->hpd_pin].state == HPD_ENABLED)
3097352ff8bdSFrançois Tigeot 			enabled_irqs |= hpd[encoder->hpd_pin];
3098352ff8bdSFrançois Tigeot 
3099352ff8bdSFrançois Tigeot 	return enabled_irqs;
3100352ff8bdSFrançois Tigeot }
3101352ff8bdSFrançois Tigeot 
3102*a85cb24fSFrançois Tigeot static void ibx_hpd_detection_setup(struct drm_i915_private *dev_priv)
3103*a85cb24fSFrançois Tigeot {
3104*a85cb24fSFrançois Tigeot 	u32 hotplug;
3105*a85cb24fSFrançois Tigeot 
3106*a85cb24fSFrançois Tigeot 	/*
3107*a85cb24fSFrançois Tigeot 	 * Enable digital hotplug on the PCH, and configure the DP short pulse
3108*a85cb24fSFrançois Tigeot 	 * duration to 2ms (which is the minimum in the Display Port spec).
3109*a85cb24fSFrançois Tigeot 	 * The pulse duration bits are reserved on LPT+.
3110*a85cb24fSFrançois Tigeot 	 */
3111*a85cb24fSFrançois Tigeot 	hotplug = I915_READ(PCH_PORT_HOTPLUG);
3112*a85cb24fSFrançois Tigeot 	hotplug &= ~(PORTB_PULSE_DURATION_MASK |
3113*a85cb24fSFrançois Tigeot 		     PORTC_PULSE_DURATION_MASK |
3114*a85cb24fSFrançois Tigeot 		     PORTD_PULSE_DURATION_MASK);
3115*a85cb24fSFrançois Tigeot 	hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms;
3116*a85cb24fSFrançois Tigeot 	hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms;
3117*a85cb24fSFrançois Tigeot 	hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms;
3118*a85cb24fSFrançois Tigeot 	/*
3119*a85cb24fSFrançois Tigeot 	 * When CPU and PCH are on the same package, port A
3120*a85cb24fSFrançois Tigeot 	 * HPD must be enabled in both north and south.
3121*a85cb24fSFrançois Tigeot 	 */
3122*a85cb24fSFrançois Tigeot 	if (HAS_PCH_LPT_LP(dev_priv))
3123*a85cb24fSFrançois Tigeot 		hotplug |= PORTA_HOTPLUG_ENABLE;
3124*a85cb24fSFrançois Tigeot 	I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
3125*a85cb24fSFrançois Tigeot }
3126*a85cb24fSFrançois Tigeot 
31271487f786SFrançois Tigeot static void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv)
31288e26cdf6SFrançois Tigeot {
3129*a85cb24fSFrançois Tigeot 	u32 hotplug_irqs, enabled_irqs;
31308e26cdf6SFrançois Tigeot 
31311487f786SFrançois Tigeot 	if (HAS_PCH_IBX(dev_priv)) {
31329edbd4a0SFrançois Tigeot 		hotplug_irqs = SDE_HOTPLUG_MASK;
31331487f786SFrançois Tigeot 		enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_ibx);
31348e26cdf6SFrançois Tigeot 	} else {
31359edbd4a0SFrançois Tigeot 		hotplug_irqs = SDE_HOTPLUG_MASK_CPT;
31361487f786SFrançois Tigeot 		enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_cpt);
31378e26cdf6SFrançois Tigeot 	}
31388e26cdf6SFrançois Tigeot 
31399edbd4a0SFrançois Tigeot 	ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
31408e26cdf6SFrançois Tigeot 
3141*a85cb24fSFrançois Tigeot 	ibx_hpd_detection_setup(dev_priv);
3142352ff8bdSFrançois Tigeot }
3143352ff8bdSFrançois Tigeot 
31444be47400SFrançois Tigeot static void spt_hpd_detection_setup(struct drm_i915_private *dev_priv)
31454be47400SFrançois Tigeot {
31464be47400SFrançois Tigeot 	u32 hotplug;
31474be47400SFrançois Tigeot 
31484be47400SFrançois Tigeot 	/* Enable digital hotplug on the PCH */
31494be47400SFrançois Tigeot 	hotplug = I915_READ(PCH_PORT_HOTPLUG);
31504be47400SFrançois Tigeot 	hotplug |= PORTA_HOTPLUG_ENABLE |
31514be47400SFrançois Tigeot 		   PORTB_HOTPLUG_ENABLE |
31524be47400SFrançois Tigeot 		   PORTC_HOTPLUG_ENABLE |
31534be47400SFrançois Tigeot 		   PORTD_HOTPLUG_ENABLE;
31544be47400SFrançois Tigeot 	I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
31554be47400SFrançois Tigeot 
31564be47400SFrançois Tigeot 	hotplug = I915_READ(PCH_PORT_HOTPLUG2);
31574be47400SFrançois Tigeot 	hotplug |= PORTE_HOTPLUG_ENABLE;
31584be47400SFrançois Tigeot 	I915_WRITE(PCH_PORT_HOTPLUG2, hotplug);
31594be47400SFrançois Tigeot }
31604be47400SFrançois Tigeot 
31611487f786SFrançois Tigeot static void spt_hpd_irq_setup(struct drm_i915_private *dev_priv)
3162352ff8bdSFrançois Tigeot {
31634be47400SFrançois Tigeot 	u32 hotplug_irqs, enabled_irqs;
3164352ff8bdSFrançois Tigeot 
3165352ff8bdSFrançois Tigeot 	hotplug_irqs = SDE_HOTPLUG_MASK_SPT;
31661487f786SFrançois Tigeot 	enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_spt);
3167352ff8bdSFrançois Tigeot 
3168352ff8bdSFrançois Tigeot 	ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
3169352ff8bdSFrançois Tigeot 
31704be47400SFrançois Tigeot 	spt_hpd_detection_setup(dev_priv);
3171a05eeebfSFrançois Tigeot }
3172352ff8bdSFrançois Tigeot 
3173*a85cb24fSFrançois Tigeot static void ilk_hpd_detection_setup(struct drm_i915_private *dev_priv)
3174*a85cb24fSFrançois Tigeot {
3175*a85cb24fSFrançois Tigeot 	u32 hotplug;
3176*a85cb24fSFrançois Tigeot 
3177*a85cb24fSFrançois Tigeot 	/*
3178*a85cb24fSFrançois Tigeot 	 * Enable digital hotplug on the CPU, and configure the DP short pulse
3179*a85cb24fSFrançois Tigeot 	 * duration to 2ms (which is the minimum in the Display Port spec)
3180*a85cb24fSFrançois Tigeot 	 * The pulse duration bits are reserved on HSW+.
3181*a85cb24fSFrançois Tigeot 	 */
3182*a85cb24fSFrançois Tigeot 	hotplug = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL);
3183*a85cb24fSFrançois Tigeot 	hotplug &= ~DIGITAL_PORTA_PULSE_DURATION_MASK;
3184*a85cb24fSFrançois Tigeot 	hotplug |= DIGITAL_PORTA_HOTPLUG_ENABLE |
3185*a85cb24fSFrançois Tigeot 		   DIGITAL_PORTA_PULSE_DURATION_2ms;
3186*a85cb24fSFrançois Tigeot 	I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, hotplug);
3187*a85cb24fSFrançois Tigeot }
3188*a85cb24fSFrançois Tigeot 
31891487f786SFrançois Tigeot static void ilk_hpd_irq_setup(struct drm_i915_private *dev_priv)
3190352ff8bdSFrançois Tigeot {
3191*a85cb24fSFrançois Tigeot 	u32 hotplug_irqs, enabled_irqs;
3192352ff8bdSFrançois Tigeot 
31931487f786SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 8) {
3194352ff8bdSFrançois Tigeot 		hotplug_irqs = GEN8_PORT_DP_A_HOTPLUG;
31951487f786SFrançois Tigeot 		enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_bdw);
3196352ff8bdSFrançois Tigeot 
3197352ff8bdSFrançois Tigeot 		bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
31981487f786SFrançois Tigeot 	} else if (INTEL_GEN(dev_priv) >= 7) {
3199352ff8bdSFrançois Tigeot 		hotplug_irqs = DE_DP_A_HOTPLUG_IVB;
32001487f786SFrançois Tigeot 		enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_ivb);
3201352ff8bdSFrançois Tigeot 
3202352ff8bdSFrançois Tigeot 		ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
3203352ff8bdSFrançois Tigeot 	} else {
3204352ff8bdSFrançois Tigeot 		hotplug_irqs = DE_DP_A_HOTPLUG;
32051487f786SFrançois Tigeot 		enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_ilk);
3206352ff8bdSFrançois Tigeot 
3207352ff8bdSFrançois Tigeot 		ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
3208352ff8bdSFrançois Tigeot 	}
3209352ff8bdSFrançois Tigeot 
3210*a85cb24fSFrançois Tigeot 	ilk_hpd_detection_setup(dev_priv);
3211352ff8bdSFrançois Tigeot 
32121487f786SFrançois Tigeot 	ibx_hpd_irq_setup(dev_priv);
3213e9243325SFrançois Tigeot }
3214e9243325SFrançois Tigeot 
32154be47400SFrançois Tigeot static void __bxt_hpd_detection_setup(struct drm_i915_private *dev_priv,
32164be47400SFrançois Tigeot 				      u32 enabled_irqs)
321719c468b4SFrançois Tigeot {
32184be47400SFrançois Tigeot 	u32 hotplug;
321919c468b4SFrançois Tigeot 
3220352ff8bdSFrançois Tigeot 	hotplug = I915_READ(PCH_PORT_HOTPLUG);
32214be47400SFrançois Tigeot 	hotplug |= PORTA_HOTPLUG_ENABLE |
32224be47400SFrançois Tigeot 		   PORTB_HOTPLUG_ENABLE |
32234be47400SFrançois Tigeot 		   PORTC_HOTPLUG_ENABLE;
32248621f407SFrançois Tigeot 
32258621f407SFrançois Tigeot 	DRM_DEBUG_KMS("Invert bit setting: hp_ctl:%x hp_port:%x\n",
32268621f407SFrançois Tigeot 		      hotplug, enabled_irqs);
32278621f407SFrançois Tigeot 	hotplug &= ~BXT_DDI_HPD_INVERT_MASK;
32288621f407SFrançois Tigeot 
32298621f407SFrançois Tigeot 	/*
32308621f407SFrançois Tigeot 	 * For BXT invert bit has to be set based on AOB design
32318621f407SFrançois Tigeot 	 * for HPD detection logic, update it based on VBT fields.
32328621f407SFrançois Tigeot 	 */
32338621f407SFrançois Tigeot 	if ((enabled_irqs & BXT_DE_PORT_HP_DDIA) &&
32348621f407SFrançois Tigeot 	    intel_bios_is_port_hpd_inverted(dev_priv, PORT_A))
32358621f407SFrançois Tigeot 		hotplug |= BXT_DDIA_HPD_INVERT;
32368621f407SFrançois Tigeot 	if ((enabled_irqs & BXT_DE_PORT_HP_DDIB) &&
32378621f407SFrançois Tigeot 	    intel_bios_is_port_hpd_inverted(dev_priv, PORT_B))
32388621f407SFrançois Tigeot 		hotplug |= BXT_DDIB_HPD_INVERT;
32398621f407SFrançois Tigeot 	if ((enabled_irqs & BXT_DE_PORT_HP_DDIC) &&
32408621f407SFrançois Tigeot 	    intel_bios_is_port_hpd_inverted(dev_priv, PORT_C))
32418621f407SFrançois Tigeot 		hotplug |= BXT_DDIC_HPD_INVERT;
32428621f407SFrançois Tigeot 
3243352ff8bdSFrançois Tigeot 	I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
324419c468b4SFrançois Tigeot }
324519c468b4SFrançois Tigeot 
32464be47400SFrançois Tigeot static void bxt_hpd_detection_setup(struct drm_i915_private *dev_priv)
32474be47400SFrançois Tigeot {
32484be47400SFrançois Tigeot 	__bxt_hpd_detection_setup(dev_priv, BXT_DE_PORT_HOTPLUG_MASK);
32494be47400SFrançois Tigeot }
32504be47400SFrançois Tigeot 
32514be47400SFrançois Tigeot static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv)
32524be47400SFrançois Tigeot {
32534be47400SFrançois Tigeot 	u32 hotplug_irqs, enabled_irqs;
32544be47400SFrançois Tigeot 
32554be47400SFrançois Tigeot 	enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_bxt);
32564be47400SFrançois Tigeot 	hotplug_irqs = BXT_DE_PORT_HOTPLUG_MASK;
32574be47400SFrançois Tigeot 
32584be47400SFrançois Tigeot 	bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
32594be47400SFrançois Tigeot 
32604be47400SFrançois Tigeot 	__bxt_hpd_detection_setup(dev_priv, enabled_irqs);
32614be47400SFrançois Tigeot }
32624be47400SFrançois Tigeot 
3263a2fdbec6SFrançois Tigeot static void ibx_irq_postinstall(struct drm_device *dev)
3264a2fdbec6SFrançois Tigeot {
3265303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3266a2fdbec6SFrançois Tigeot 	u32 mask;
3267a2fdbec6SFrançois Tigeot 
32681e12ee3bSFrançois Tigeot 	if (HAS_PCH_NOP(dev_priv))
32698e26cdf6SFrançois Tigeot 		return;
3270a2fdbec6SFrançois Tigeot 
32711e12ee3bSFrançois Tigeot 	if (HAS_PCH_IBX(dev_priv))
32729edbd4a0SFrançois Tigeot 		mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON;
3273ba55f2f5SFrançois Tigeot 	else
32749edbd4a0SFrançois Tigeot 		mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
32755d0b1887SFrançois Tigeot 
3276352ff8bdSFrançois Tigeot 	gen5_assert_iir_is_zero(dev_priv, SDEIIR);
3277a2fdbec6SFrançois Tigeot 	I915_WRITE(SDEIMR, ~mask);
32784be47400SFrançois Tigeot 
32794be47400SFrançois Tigeot 	if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) ||
32804be47400SFrançois Tigeot 	    HAS_PCH_LPT(dev_priv))
3281*a85cb24fSFrançois Tigeot 		ibx_hpd_detection_setup(dev_priv);
32824be47400SFrançois Tigeot 	else
32834be47400SFrançois Tigeot 		spt_hpd_detection_setup(dev_priv);
3284a2fdbec6SFrançois Tigeot }
3285a2fdbec6SFrançois Tigeot 
32869edbd4a0SFrançois Tigeot static void gen5_gt_irq_postinstall(struct drm_device *dev)
32879edbd4a0SFrançois Tigeot {
3288303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
32899edbd4a0SFrançois Tigeot 	u32 pm_irqs, gt_irqs;
32909edbd4a0SFrançois Tigeot 
32919edbd4a0SFrançois Tigeot 	pm_irqs = gt_irqs = 0;
32929edbd4a0SFrançois Tigeot 
32939edbd4a0SFrançois Tigeot 	dev_priv->gt_irq_mask = ~0;
32941e12ee3bSFrançois Tigeot 	if (HAS_L3_DPF(dev_priv)) {
32959edbd4a0SFrançois Tigeot 		/* L3 parity interrupt is always unmasked. */
32961e12ee3bSFrançois Tigeot 		dev_priv->gt_irq_mask = ~GT_PARITY_ERROR(dev_priv);
32971e12ee3bSFrançois Tigeot 		gt_irqs |= GT_PARITY_ERROR(dev_priv);
32989edbd4a0SFrançois Tigeot 	}
32999edbd4a0SFrançois Tigeot 
33009edbd4a0SFrançois Tigeot 	gt_irqs |= GT_RENDER_USER_INTERRUPT;
33011e12ee3bSFrançois Tigeot 	if (IS_GEN5(dev_priv)) {
3302303bf270SFrançois Tigeot 		gt_irqs |= ILK_BSD_USER_INTERRUPT;
33039edbd4a0SFrançois Tigeot 	} else {
33049edbd4a0SFrançois Tigeot 		gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
33059edbd4a0SFrançois Tigeot 	}
33069edbd4a0SFrançois Tigeot 
3307ba55f2f5SFrançois Tigeot 	GEN5_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs);
33089edbd4a0SFrançois Tigeot 
33094be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 6) {
33102c9916cdSFrançois Tigeot 		/*
33112c9916cdSFrançois Tigeot 		 * RPS interrupts will get enabled/disabled on demand when RPS
33122c9916cdSFrançois Tigeot 		 * itself is enabled/disabled.
33132c9916cdSFrançois Tigeot 		 */
33144be47400SFrançois Tigeot 		if (HAS_VEBOX(dev_priv)) {
33159edbd4a0SFrançois Tigeot 			pm_irqs |= PM_VEBOX_USER_INTERRUPT;
33164be47400SFrançois Tigeot 			dev_priv->pm_ier |= PM_VEBOX_USER_INTERRUPT;
33174be47400SFrançois Tigeot 		}
33189edbd4a0SFrançois Tigeot 
33194be47400SFrançois Tigeot 		dev_priv->pm_imr = 0xffffffff;
33204be47400SFrançois Tigeot 		GEN5_IRQ_INIT(GEN6_PM, dev_priv->pm_imr, pm_irqs);
33219edbd4a0SFrançois Tigeot 	}
33229edbd4a0SFrançois Tigeot }
33239edbd4a0SFrançois Tigeot 
3324e9243325SFrançois Tigeot static int ironlake_irq_postinstall(struct drm_device *dev)
3325e9243325SFrançois Tigeot {
3326303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
33279edbd4a0SFrançois Tigeot 	u32 display_mask, extra_mask;
33289edbd4a0SFrançois Tigeot 
33294be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 7) {
33309edbd4a0SFrançois Tigeot 		display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB |
33319edbd4a0SFrançois Tigeot 				DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB |
33329edbd4a0SFrançois Tigeot 				DE_PLANEB_FLIP_DONE_IVB |
33339edbd4a0SFrançois Tigeot 				DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB);
33349edbd4a0SFrançois Tigeot 		extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB |
3335352ff8bdSFrançois Tigeot 			      DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB |
3336352ff8bdSFrançois Tigeot 			      DE_DP_A_HOTPLUG_IVB);
33379edbd4a0SFrançois Tigeot 	} else {
33389edbd4a0SFrançois Tigeot 		display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
3339a2fdbec6SFrançois Tigeot 				DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
33409edbd4a0SFrançois Tigeot 				DE_AUX_CHANNEL_A |
33419edbd4a0SFrançois Tigeot 				DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE |
33429edbd4a0SFrançois Tigeot 				DE_POISON);
3343352ff8bdSFrançois Tigeot 		extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT |
3344352ff8bdSFrançois Tigeot 			      DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
3345352ff8bdSFrançois Tigeot 			      DE_DP_A_HOTPLUG);
33469edbd4a0SFrançois Tigeot 	}
3347e9243325SFrançois Tigeot 
3348e9243325SFrançois Tigeot 	dev_priv->irq_mask = ~display_mask;
3349e9243325SFrançois Tigeot 
3350ba55f2f5SFrançois Tigeot 	I915_WRITE(HWSTAM, 0xeffe);
3351ba55f2f5SFrançois Tigeot 
3352ba55f2f5SFrançois Tigeot 	ibx_irq_pre_postinstall(dev);
3353ba55f2f5SFrançois Tigeot 
3354ba55f2f5SFrançois Tigeot 	GEN5_IRQ_INIT(DE, dev_priv->irq_mask, display_mask | extra_mask);
3355e9243325SFrançois Tigeot 
33569edbd4a0SFrançois Tigeot 	gen5_gt_irq_postinstall(dev);
3357e9243325SFrançois Tigeot 
3358*a85cb24fSFrançois Tigeot 	ilk_hpd_detection_setup(dev_priv);
3359*a85cb24fSFrançois Tigeot 
3360a2fdbec6SFrançois Tigeot 	ibx_irq_postinstall(dev);
3361e9243325SFrançois Tigeot 
33621e12ee3bSFrançois Tigeot 	if (IS_IRONLAKE_M(dev_priv)) {
33635d0b1887SFrançois Tigeot 		/* Enable PCU event interrupts
33645d0b1887SFrançois Tigeot 		 *
33655d0b1887SFrançois Tigeot 		 * spinlocking not required here for correctness since interrupt
33665d0b1887SFrançois Tigeot 		 * setup is guaranteed to run in single-threaded context. But we
33675d0b1887SFrançois Tigeot 		 * need it to make the assert_spin_locked happy. */
33685e269720SFrançois Tigeot 		spin_lock_irq(&dev_priv->irq_lock);
3369aee94f86SFrançois Tigeot 		ilk_enable_display_irq(dev_priv, DE_PCU_EVENT);
33705e269720SFrançois Tigeot 		spin_unlock_irq(&dev_priv->irq_lock);
3371e9243325SFrançois Tigeot 	}
3372e9243325SFrançois Tigeot 
3373e9243325SFrançois Tigeot 	return 0;
3374e9243325SFrançois Tigeot }
3375e9243325SFrançois Tigeot 
3376ba55f2f5SFrançois Tigeot void valleyview_enable_display_irqs(struct drm_i915_private *dev_priv)
3377ba55f2f5SFrançois Tigeot {
3378*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
3379ba55f2f5SFrançois Tigeot 
3380ba55f2f5SFrançois Tigeot 	if (dev_priv->display_irqs_enabled)
3381ba55f2f5SFrançois Tigeot 		return;
3382ba55f2f5SFrançois Tigeot 
3383ba55f2f5SFrançois Tigeot 	dev_priv->display_irqs_enabled = true;
3384ba55f2f5SFrançois Tigeot 
33858621f407SFrançois Tigeot 	if (intel_irqs_enabled(dev_priv)) {
33868621f407SFrançois Tigeot 		vlv_display_irq_reset(dev_priv);
33878621f407SFrançois Tigeot 		vlv_display_irq_postinstall(dev_priv);
33888621f407SFrançois Tigeot 	}
3389ba55f2f5SFrançois Tigeot }
3390ba55f2f5SFrançois Tigeot 
3391ba55f2f5SFrançois Tigeot void valleyview_disable_display_irqs(struct drm_i915_private *dev_priv)
3392ba55f2f5SFrançois Tigeot {
3393*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
3394ba55f2f5SFrançois Tigeot 
3395ba55f2f5SFrançois Tigeot 	if (!dev_priv->display_irqs_enabled)
3396ba55f2f5SFrançois Tigeot 		return;
3397ba55f2f5SFrançois Tigeot 
3398ba55f2f5SFrançois Tigeot 	dev_priv->display_irqs_enabled = false;
3399ba55f2f5SFrançois Tigeot 
34002c9916cdSFrançois Tigeot 	if (intel_irqs_enabled(dev_priv))
34018621f407SFrançois Tigeot 		vlv_display_irq_reset(dev_priv);
3402ba55f2f5SFrançois Tigeot }
3403ba55f2f5SFrançois Tigeot 
3404e9243325SFrançois Tigeot 
34052c9916cdSFrançois Tigeot static int valleyview_irq_postinstall(struct drm_device *dev)
34062c9916cdSFrançois Tigeot {
3407303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
34082c9916cdSFrançois Tigeot 
34099edbd4a0SFrançois Tigeot 	gen5_gt_irq_postinstall(dev);
3410e9243325SFrançois Tigeot 
34118621f407SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
34128621f407SFrançois Tigeot 	if (dev_priv->display_irqs_enabled)
34138621f407SFrançois Tigeot 		vlv_display_irq_postinstall(dev_priv);
34148621f407SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
3415e9243325SFrançois Tigeot 
3416e9243325SFrançois Tigeot 	I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
34178621f407SFrançois Tigeot 	POSTING_READ(VLV_MASTER_IER);
3418a2fdbec6SFrançois Tigeot 
3419a2fdbec6SFrançois Tigeot 	return 0;
3420a2fdbec6SFrançois Tigeot }
3421a2fdbec6SFrançois Tigeot 
34229edbd4a0SFrançois Tigeot static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
34239edbd4a0SFrançois Tigeot {
34249edbd4a0SFrançois Tigeot 	/* These are interrupts we'll toggle with the ring mask register */
34259edbd4a0SFrançois Tigeot 	uint32_t gt_interrupts[] = {
34269edbd4a0SFrançois Tigeot 		GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
34271b13d190SFrançois Tigeot 			GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
34281b13d190SFrançois Tigeot 			GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT |
34291b13d190SFrançois Tigeot 			GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
34309edbd4a0SFrançois Tigeot 		GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
34311b13d190SFrançois Tigeot 			GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
34321b13d190SFrançois Tigeot 			GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT |
34331b13d190SFrançois Tigeot 			GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS2_IRQ_SHIFT,
34349edbd4a0SFrançois Tigeot 		0,
34351b13d190SFrançois Tigeot 		GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT |
34361b13d190SFrançois Tigeot 			GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT
34379edbd4a0SFrançois Tigeot 		};
34389edbd4a0SFrançois Tigeot 
34398621f407SFrançois Tigeot 	if (HAS_L3_DPF(dev_priv))
34408621f407SFrançois Tigeot 		gt_interrupts[0] |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
34418621f407SFrançois Tigeot 
34424be47400SFrançois Tigeot 	dev_priv->pm_ier = 0x0;
34434be47400SFrançois Tigeot 	dev_priv->pm_imr = ~dev_priv->pm_ier;
34441b13d190SFrançois Tigeot 	GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]);
34451b13d190SFrançois Tigeot 	GEN8_IRQ_INIT_NDX(GT, 1, ~gt_interrupts[1], gt_interrupts[1]);
34462c9916cdSFrançois Tigeot 	/*
34472c9916cdSFrançois Tigeot 	 * RPS interrupts will get enabled/disabled on demand when RPS itself
34484be47400SFrançois Tigeot 	 * is enabled/disabled. Same wil be the case for GuC interrupts.
34492c9916cdSFrançois Tigeot 	 */
34504be47400SFrançois Tigeot 	GEN8_IRQ_INIT_NDX(GT, 2, dev_priv->pm_imr, dev_priv->pm_ier);
34511b13d190SFrançois Tigeot 	GEN8_IRQ_INIT_NDX(GT, 3, ~gt_interrupts[3], gt_interrupts[3]);
34529edbd4a0SFrançois Tigeot }
34539edbd4a0SFrançois Tigeot 
34549edbd4a0SFrançois Tigeot static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
34559edbd4a0SFrançois Tigeot {
34562c9916cdSFrançois Tigeot 	uint32_t de_pipe_masked = GEN8_PIPE_CDCLK_CRC_DONE;
34572c9916cdSFrançois Tigeot 	uint32_t de_pipe_enables;
3458352ff8bdSFrançois Tigeot 	u32 de_port_masked = GEN8_AUX_CHANNEL_A;
3459352ff8bdSFrançois Tigeot 	u32 de_port_enables;
34601487f786SFrançois Tigeot 	u32 de_misc_masked = GEN8_DE_MISC_GSE;
3461352ff8bdSFrançois Tigeot 	enum i915_pipe pipe;
34622c9916cdSFrançois Tigeot 
3463352ff8bdSFrançois Tigeot 	if (INTEL_INFO(dev_priv)->gen >= 9) {
34642c9916cdSFrançois Tigeot 		de_pipe_masked |= GEN9_PIPE_PLANE1_FLIP_DONE |
34652c9916cdSFrançois Tigeot 				  GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
3466352ff8bdSFrançois Tigeot 		de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
34672c9916cdSFrançois Tigeot 				  GEN9_AUX_CHANNEL_D;
3468*a85cb24fSFrançois Tigeot 		if (IS_GEN9_LP(dev_priv))
3469352ff8bdSFrançois Tigeot 			de_port_masked |= BXT_DE_PORT_GMBUS;
3470352ff8bdSFrançois Tigeot 	} else {
34712c9916cdSFrançois Tigeot 		de_pipe_masked |= GEN8_PIPE_PRIMARY_FLIP_DONE |
34722c9916cdSFrançois Tigeot 				  GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
3473352ff8bdSFrançois Tigeot 	}
34742c9916cdSFrançois Tigeot 
34752c9916cdSFrançois Tigeot 	de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK |
34762c9916cdSFrançois Tigeot 					   GEN8_PIPE_FIFO_UNDERRUN;
34772c9916cdSFrançois Tigeot 
3478352ff8bdSFrançois Tigeot 	de_port_enables = de_port_masked;
3479*a85cb24fSFrançois Tigeot 	if (IS_GEN9_LP(dev_priv))
3480352ff8bdSFrançois Tigeot 		de_port_enables |= BXT_DE_PORT_HOTPLUG_MASK;
3481352ff8bdSFrançois Tigeot 	else if (IS_BROADWELL(dev_priv))
3482352ff8bdSFrançois Tigeot 		de_port_enables |= GEN8_PORT_DP_A_HOTPLUG;
3483352ff8bdSFrançois Tigeot 
34849edbd4a0SFrançois Tigeot 	dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_masked;
34859edbd4a0SFrançois Tigeot 	dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked;
34869edbd4a0SFrançois Tigeot 	dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked;
34879edbd4a0SFrançois Tigeot 
34881b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
34892c9916cdSFrançois Tigeot 		if (intel_display_power_is_enabled(dev_priv,
349024edb884SFrançois Tigeot 				POWER_DOMAIN_PIPE(pipe)))
349124edb884SFrançois Tigeot 			GEN8_IRQ_INIT_NDX(DE_PIPE, pipe,
349224edb884SFrançois Tigeot 					  dev_priv->de_irq_mask[pipe],
3493ba55f2f5SFrançois Tigeot 					  de_pipe_enables);
34949edbd4a0SFrançois Tigeot 
3495352ff8bdSFrançois Tigeot 	GEN5_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables);
34961487f786SFrançois Tigeot 	GEN5_IRQ_INIT(GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked);
34974be47400SFrançois Tigeot 
3498*a85cb24fSFrançois Tigeot 	if (IS_GEN9_LP(dev_priv))
34994be47400SFrançois Tigeot 		bxt_hpd_detection_setup(dev_priv);
3500*a85cb24fSFrançois Tigeot 	else if (IS_BROADWELL(dev_priv))
3501*a85cb24fSFrançois Tigeot 		ilk_hpd_detection_setup(dev_priv);
35029edbd4a0SFrançois Tigeot }
35039edbd4a0SFrançois Tigeot 
35049edbd4a0SFrançois Tigeot static int gen8_irq_postinstall(struct drm_device *dev)
35059edbd4a0SFrançois Tigeot {
3506303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
35079edbd4a0SFrançois Tigeot 
35081e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
3509ba55f2f5SFrançois Tigeot 		ibx_irq_pre_postinstall(dev);
3510ba55f2f5SFrançois Tigeot 
35119edbd4a0SFrançois Tigeot 	gen8_gt_irq_postinstall(dev_priv);
35129edbd4a0SFrançois Tigeot 	gen8_de_irq_postinstall(dev_priv);
35139edbd4a0SFrançois Tigeot 
35141e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
35159edbd4a0SFrançois Tigeot 		ibx_irq_postinstall(dev);
35169edbd4a0SFrançois Tigeot 
35178621f407SFrançois Tigeot 	I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
35189edbd4a0SFrançois Tigeot 	POSTING_READ(GEN8_MASTER_IRQ);
35199edbd4a0SFrançois Tigeot 
35209edbd4a0SFrançois Tigeot 	return 0;
35219edbd4a0SFrançois Tigeot }
35229edbd4a0SFrançois Tigeot 
3523ba55f2f5SFrançois Tigeot static int cherryview_irq_postinstall(struct drm_device *dev)
3524ba55f2f5SFrançois Tigeot {
3525303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3526ba55f2f5SFrançois Tigeot 
3527ba55f2f5SFrançois Tigeot 	gen8_gt_irq_postinstall(dev_priv);
3528ba55f2f5SFrançois Tigeot 
35298621f407SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
35308621f407SFrançois Tigeot 	if (dev_priv->display_irqs_enabled)
35318621f407SFrançois Tigeot 		vlv_display_irq_postinstall(dev_priv);
35328621f407SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
35338621f407SFrançois Tigeot 
35348621f407SFrançois Tigeot 	I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
3535ba55f2f5SFrançois Tigeot 	POSTING_READ(GEN8_MASTER_IRQ);
3536ba55f2f5SFrançois Tigeot 
3537ba55f2f5SFrançois Tigeot 	return 0;
3538ba55f2f5SFrançois Tigeot }
3539ba55f2f5SFrançois Tigeot 
35409edbd4a0SFrançois Tigeot static void gen8_irq_uninstall(struct drm_device *dev)
35419edbd4a0SFrançois Tigeot {
3542303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3543ba55f2f5SFrançois Tigeot 
3544ba55f2f5SFrançois Tigeot 	if (!dev_priv)
3545ba55f2f5SFrançois Tigeot 		return;
3546ba55f2f5SFrançois Tigeot 
3547ba55f2f5SFrançois Tigeot 	gen8_irq_reset(dev);
3548ba55f2f5SFrançois Tigeot }
3549ba55f2f5SFrançois Tigeot 
3550ba55f2f5SFrançois Tigeot static void valleyview_irq_uninstall(struct drm_device *dev)
3551ba55f2f5SFrançois Tigeot {
3552303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
35539edbd4a0SFrançois Tigeot 
35549edbd4a0SFrançois Tigeot 	if (!dev_priv)
35559edbd4a0SFrançois Tigeot 		return;
35569edbd4a0SFrançois Tigeot 
3557ba55f2f5SFrançois Tigeot 	I915_WRITE(VLV_MASTER_IER, 0);
35588621f407SFrançois Tigeot 	POSTING_READ(VLV_MASTER_IER);
3559ba55f2f5SFrançois Tigeot 
35604be47400SFrançois Tigeot 	gen5_gt_irq_reset(dev_priv);
3561ba55f2f5SFrançois Tigeot 
3562ba55f2f5SFrançois Tigeot 	I915_WRITE(HWSTAM, 0xffffffff);
3563ba55f2f5SFrançois Tigeot 
35648621f407SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
35658621f407SFrançois Tigeot 	if (dev_priv->display_irqs_enabled)
35668621f407SFrançois Tigeot 		vlv_display_irq_reset(dev_priv);
35678621f407SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
3568ba55f2f5SFrançois Tigeot }
3569ba55f2f5SFrançois Tigeot 
3570ba55f2f5SFrançois Tigeot static void cherryview_irq_uninstall(struct drm_device *dev)
3571ba55f2f5SFrançois Tigeot {
3572303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3573ba55f2f5SFrançois Tigeot 
3574ba55f2f5SFrançois Tigeot 	if (!dev_priv)
3575ba55f2f5SFrançois Tigeot 		return;
35769edbd4a0SFrançois Tigeot 
35779edbd4a0SFrançois Tigeot 	I915_WRITE(GEN8_MASTER_IRQ, 0);
3578ba55f2f5SFrançois Tigeot 	POSTING_READ(GEN8_MASTER_IRQ);
35799edbd4a0SFrançois Tigeot 
35802c9916cdSFrançois Tigeot 	gen8_gt_irq_reset(dev_priv);
35819edbd4a0SFrançois Tigeot 
35822c9916cdSFrançois Tigeot 	GEN5_IRQ_RESET(GEN8_PCU_);
35839edbd4a0SFrançois Tigeot 
35848621f407SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
35858621f407SFrançois Tigeot 	if (dev_priv->display_irqs_enabled)
35868621f407SFrançois Tigeot 		vlv_display_irq_reset(dev_priv);
35878621f407SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
3588e9243325SFrançois Tigeot }
3589e9243325SFrançois Tigeot 
3590e9243325SFrançois Tigeot static void ironlake_irq_uninstall(struct drm_device *dev)
3591e9243325SFrançois Tigeot {
3592303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3593e9243325SFrançois Tigeot 
3594e9243325SFrançois Tigeot 	if (!dev_priv)
3595e9243325SFrançois Tigeot 		return;
3596e9243325SFrançois Tigeot 
3597ba55f2f5SFrançois Tigeot 	ironlake_irq_reset(dev);
3598e9243325SFrançois Tigeot }
3599e9243325SFrançois Tigeot 
3600e9243325SFrançois Tigeot static void i8xx_irq_preinstall(struct drm_device * dev)
3601e9243325SFrançois Tigeot {
3602303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3603e9243325SFrançois Tigeot 	int pipe;
3604e9243325SFrançois Tigeot 
36051b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
3606e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), 0);
3607e9243325SFrançois Tigeot 	I915_WRITE16(IMR, 0xffff);
3608e9243325SFrançois Tigeot 	I915_WRITE16(IER, 0x0);
3609e9243325SFrançois Tigeot 	POSTING_READ16(IER);
3610e9243325SFrançois Tigeot }
3611e9243325SFrançois Tigeot 
3612e9243325SFrançois Tigeot static int i8xx_irq_postinstall(struct drm_device *dev)
3613e9243325SFrançois Tigeot {
3614303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3615e9243325SFrançois Tigeot 
3616e9243325SFrançois Tigeot 	I915_WRITE16(EMR,
3617e9243325SFrançois Tigeot 		     ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
3618e9243325SFrançois Tigeot 
3619e9243325SFrançois Tigeot 	/* Unmask the interrupts that we always want on. */
3620e9243325SFrançois Tigeot 	dev_priv->irq_mask =
3621e9243325SFrançois Tigeot 		~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
3622e9243325SFrançois Tigeot 		  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
3623e9243325SFrançois Tigeot 		  I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
3624477eb7f9SFrançois Tigeot 		  I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT);
3625e9243325SFrançois Tigeot 	I915_WRITE16(IMR, dev_priv->irq_mask);
3626e9243325SFrançois Tigeot 
3627e9243325SFrançois Tigeot 	I915_WRITE16(IER,
3628e9243325SFrançois Tigeot 		     I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
3629e9243325SFrançois Tigeot 		     I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
3630e9243325SFrançois Tigeot 		     I915_USER_INTERRUPT);
3631e9243325SFrançois Tigeot 	POSTING_READ16(IER);
3632e9243325SFrançois Tigeot 
36339edbd4a0SFrançois Tigeot 	/* Interrupt setup is already guaranteed to be single-threaded, this is
36349edbd4a0SFrançois Tigeot 	 * just to make the assert_spin_locked check happy. */
36355e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
3636ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
3637ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
36385e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
36399edbd4a0SFrançois Tigeot 
3640e9243325SFrançois Tigeot 	return 0;
3641e9243325SFrançois Tigeot }
3642e9243325SFrançois Tigeot 
36438e26cdf6SFrançois Tigeot /*
36448e26cdf6SFrançois Tigeot  * Returns true when a page flip has completed.
36458e26cdf6SFrançois Tigeot  */
36461487f786SFrançois Tigeot static bool i8xx_handle_vblank(struct drm_i915_private *dev_priv,
36479edbd4a0SFrançois Tigeot 			       int plane, int pipe, u32 iir)
36488e26cdf6SFrançois Tigeot {
36499edbd4a0SFrançois Tigeot 	u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
36508e26cdf6SFrançois Tigeot 
36511487f786SFrançois Tigeot 	if (!intel_pipe_handle_vblank(dev_priv, pipe))
36528e26cdf6SFrançois Tigeot 		return false;
36538e26cdf6SFrançois Tigeot 
36548e26cdf6SFrançois Tigeot 	if ((iir & flip_pending) == 0)
36551b13d190SFrançois Tigeot 		goto check_page_flip;
36568e26cdf6SFrançois Tigeot 
36578e26cdf6SFrançois Tigeot 	/* We detect FlipDone by looking for the change in PendingFlip from '1'
36588e26cdf6SFrançois Tigeot 	 * to '0' on the following vblank, i.e. IIR has the Pendingflip
36598e26cdf6SFrançois Tigeot 	 * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence
36608e26cdf6SFrançois Tigeot 	 * the flip is completed (no longer pending). Since this doesn't raise
36618e26cdf6SFrançois Tigeot 	 * an interrupt per se, we watch for the change at vblank.
36628e26cdf6SFrançois Tigeot 	 */
36638e26cdf6SFrançois Tigeot 	if (I915_READ16(ISR) & flip_pending)
36641b13d190SFrançois Tigeot 		goto check_page_flip;
36658e26cdf6SFrançois Tigeot 
36661487f786SFrançois Tigeot 	intel_finish_page_flip_cs(dev_priv, pipe);
36678e26cdf6SFrançois Tigeot 	return true;
36681b13d190SFrançois Tigeot 
36691b13d190SFrançois Tigeot check_page_flip:
36701487f786SFrançois Tigeot 	intel_check_page_flip(dev_priv, pipe);
36711b13d190SFrançois Tigeot 	return false;
36728e26cdf6SFrançois Tigeot }
36738e26cdf6SFrançois Tigeot 
3674183e2373SFrançois Tigeot static irqreturn_t i8xx_irq_handler(int irq, void *arg)
3675e9243325SFrançois Tigeot {
3676ba55f2f5SFrançois Tigeot 	struct drm_device *dev = arg;
3677303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3678e9243325SFrançois Tigeot 	u16 iir, new_iir;
3679e9243325SFrançois Tigeot 	u32 pipe_stats[2];
3680e9243325SFrançois Tigeot 	int pipe;
3681e9243325SFrançois Tigeot 	u16 flip_mask =
3682e9243325SFrançois Tigeot 		I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
3683e9243325SFrançois Tigeot 		I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
3684183e2373SFrançois Tigeot 	irqreturn_t ret;
3685e9243325SFrançois Tigeot 
36862c9916cdSFrançois Tigeot 	if (!intel_irqs_enabled(dev_priv))
36872c9916cdSFrançois Tigeot 		return IRQ_NONE;
36882c9916cdSFrançois Tigeot 
3689aee94f86SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
3690aee94f86SFrançois Tigeot 	disable_rpm_wakeref_asserts(dev_priv);
3691aee94f86SFrançois Tigeot 
3692183e2373SFrançois Tigeot 	ret = IRQ_NONE;
3693e9243325SFrançois Tigeot 	iir = I915_READ16(IIR);
3694e9243325SFrançois Tigeot 	if (iir == 0)
3695aee94f86SFrançois Tigeot 		goto out;
3696e9243325SFrançois Tigeot 
3697e9243325SFrançois Tigeot 	while (iir & ~flip_mask) {
3698e9243325SFrançois Tigeot 		/* Can't rely on pipestat interrupt bit in iir as it might
3699e9243325SFrançois Tigeot 		 * have been cleared after the pipestat interrupt was received.
3700e9243325SFrançois Tigeot 		 * It doesn't set the bit in iir again, but it still produces
3701e9243325SFrançois Tigeot 		 * interrupts (for non-MSI).
3702e9243325SFrançois Tigeot 		 */
3703e9243325SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
3704e9243325SFrançois Tigeot 		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
37052c9916cdSFrançois Tigeot 			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
3706e9243325SFrançois Tigeot 
37071b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe) {
3708aee94f86SFrançois Tigeot 			i915_reg_t reg = PIPESTAT(pipe);
3709e9243325SFrançois Tigeot 			pipe_stats[pipe] = I915_READ(reg);
3710e9243325SFrançois Tigeot 
3711e9243325SFrançois Tigeot 			/*
3712e9243325SFrançois Tigeot 			 * Clear the PIPE*STAT regs before the IIR
3713e9243325SFrançois Tigeot 			 */
3714ba55f2f5SFrançois Tigeot 			if (pipe_stats[pipe] & 0x8000ffff)
3715e9243325SFrançois Tigeot 				I915_WRITE(reg, pipe_stats[pipe]);
3716e9243325SFrançois Tigeot 		}
3717e9243325SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_RELEASE);
3718e9243325SFrançois Tigeot 
3719e9243325SFrançois Tigeot 		I915_WRITE16(IIR, iir & ~flip_mask);
3720e9243325SFrançois Tigeot 		new_iir = I915_READ16(IIR); /* Flush posted writes */
3721e9243325SFrançois Tigeot 
3722e9243325SFrançois Tigeot 		if (iir & I915_USER_INTERRUPT)
37231e12ee3bSFrançois Tigeot 			notify_ring(dev_priv->engine[RCS]);
3724e9243325SFrançois Tigeot 
37251b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe) {
37269edbd4a0SFrançois Tigeot 			int plane = pipe;
37271487f786SFrançois Tigeot 			if (HAS_FBC(dev_priv))
37289edbd4a0SFrançois Tigeot 				plane = !plane;
3729e9243325SFrançois Tigeot 
37309edbd4a0SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS &&
37311487f786SFrançois Tigeot 			    i8xx_handle_vblank(dev_priv, plane, pipe, iir))
37329edbd4a0SFrançois Tigeot 				flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(plane);
37339edbd4a0SFrançois Tigeot 
37349edbd4a0SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
37351487f786SFrançois Tigeot 				i9xx_pipe_crc_irq_handler(dev_priv, pipe);
3736ba55f2f5SFrançois Tigeot 
37372c9916cdSFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
37382c9916cdSFrançois Tigeot 				intel_cpu_fifo_underrun_irq_handler(dev_priv,
37392c9916cdSFrançois Tigeot 								    pipe);
37409edbd4a0SFrançois Tigeot 		}
3741e9243325SFrançois Tigeot 
3742e9243325SFrançois Tigeot 		iir = new_iir;
3743e9243325SFrançois Tigeot 	}
3744183e2373SFrançois Tigeot 	ret = IRQ_HANDLED;
3745e9243325SFrançois Tigeot 
3746aee94f86SFrançois Tigeot out:
3747aee94f86SFrançois Tigeot 	enable_rpm_wakeref_asserts(dev_priv);
3748aee94f86SFrançois Tigeot 
3749183e2373SFrançois Tigeot 	return ret;
3750e9243325SFrançois Tigeot }
3751e9243325SFrançois Tigeot 
3752e9243325SFrançois Tigeot static void i8xx_irq_uninstall(struct drm_device * dev)
3753e9243325SFrançois Tigeot {
3754303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3755e9243325SFrançois Tigeot 	int pipe;
3756e9243325SFrançois Tigeot 
37571b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
3758e9243325SFrançois Tigeot 		/* Clear enable bits; then clear status bits */
3759e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), 0);
3760e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)));
3761e9243325SFrançois Tigeot 	}
3762e9243325SFrançois Tigeot 	I915_WRITE16(IMR, 0xffff);
3763e9243325SFrançois Tigeot 	I915_WRITE16(IER, 0x0);
3764e9243325SFrançois Tigeot 	I915_WRITE16(IIR, I915_READ16(IIR));
3765e9243325SFrançois Tigeot }
3766e9243325SFrançois Tigeot 
3767e9243325SFrançois Tigeot static void i915_irq_preinstall(struct drm_device * dev)
3768e9243325SFrançois Tigeot {
3769303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3770e9243325SFrançois Tigeot 	int pipe;
3771e9243325SFrançois Tigeot 
37724be47400SFrançois Tigeot 	if (I915_HAS_HOTPLUG(dev_priv)) {
3773352ff8bdSFrançois Tigeot 		i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
3774e9243325SFrançois Tigeot 		I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
3775e9243325SFrançois Tigeot 	}
3776e9243325SFrançois Tigeot 
3777e9243325SFrançois Tigeot 	I915_WRITE16(HWSTAM, 0xeffe);
37781b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
3779e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), 0);
3780e9243325SFrançois Tigeot 	I915_WRITE(IMR, 0xffffffff);
3781e9243325SFrançois Tigeot 	I915_WRITE(IER, 0x0);
3782e9243325SFrançois Tigeot 	POSTING_READ(IER);
3783e9243325SFrançois Tigeot }
3784e9243325SFrançois Tigeot 
3785e9243325SFrançois Tigeot static int i915_irq_postinstall(struct drm_device *dev)
3786e9243325SFrançois Tigeot {
3787303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3788e9243325SFrançois Tigeot 	u32 enable_mask;
3789e9243325SFrançois Tigeot 
3790e9243325SFrançois Tigeot 	I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
3791e9243325SFrançois Tigeot 
3792e9243325SFrançois Tigeot 	/* Unmask the interrupts that we always want on. */
3793e9243325SFrançois Tigeot 	dev_priv->irq_mask =
3794e9243325SFrançois Tigeot 		~(I915_ASLE_INTERRUPT |
3795e9243325SFrançois Tigeot 		  I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
3796e9243325SFrançois Tigeot 		  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
3797e9243325SFrançois Tigeot 		  I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
3798477eb7f9SFrançois Tigeot 		  I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT);
3799e9243325SFrançois Tigeot 
3800e9243325SFrançois Tigeot 	enable_mask =
3801e9243325SFrançois Tigeot 		I915_ASLE_INTERRUPT |
3802e9243325SFrançois Tigeot 		I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
3803e9243325SFrançois Tigeot 		I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
3804e9243325SFrançois Tigeot 		I915_USER_INTERRUPT;
3805e9243325SFrançois Tigeot 
38064be47400SFrançois Tigeot 	if (I915_HAS_HOTPLUG(dev_priv)) {
3807352ff8bdSFrançois Tigeot 		i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
3808a2fdbec6SFrançois Tigeot 		POSTING_READ(PORT_HOTPLUG_EN);
3809a2fdbec6SFrançois Tigeot 
3810e9243325SFrançois Tigeot 		/* Enable in IER... */
3811e9243325SFrançois Tigeot 		enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
3812e9243325SFrançois Tigeot 		/* and unmask in IMR */
3813e9243325SFrançois Tigeot 		dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT;
3814e9243325SFrançois Tigeot 	}
3815e9243325SFrançois Tigeot 
3816e9243325SFrançois Tigeot 	I915_WRITE(IMR, dev_priv->irq_mask);
3817e9243325SFrançois Tigeot 	I915_WRITE(IER, enable_mask);
3818e9243325SFrançois Tigeot 	POSTING_READ(IER);
3819e9243325SFrançois Tigeot 
38201487f786SFrançois Tigeot 	i915_enable_asle_pipestat(dev_priv);
3821e9243325SFrançois Tigeot 
38229edbd4a0SFrançois Tigeot 	/* Interrupt setup is already guaranteed to be single-threaded, this is
38239edbd4a0SFrançois Tigeot 	 * just to make the assert_spin_locked check happy. */
38245e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
3825ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
3826ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
38275e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
38289edbd4a0SFrançois Tigeot 
3829a2fdbec6SFrançois Tigeot 	return 0;
3830a2fdbec6SFrançois Tigeot }
3831a2fdbec6SFrançois Tigeot 
38328e26cdf6SFrançois Tigeot /*
38338e26cdf6SFrançois Tigeot  * Returns true when a page flip has completed.
38348e26cdf6SFrançois Tigeot  */
38351487f786SFrançois Tigeot static bool i915_handle_vblank(struct drm_i915_private *dev_priv,
38368e26cdf6SFrançois Tigeot 			       int plane, int pipe, u32 iir)
3837a2fdbec6SFrançois Tigeot {
38388e26cdf6SFrançois Tigeot 	u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
3839a2fdbec6SFrançois Tigeot 
38401487f786SFrançois Tigeot 	if (!intel_pipe_handle_vblank(dev_priv, pipe))
38418e26cdf6SFrançois Tigeot 		return false;
3842a2fdbec6SFrançois Tigeot 
38438e26cdf6SFrançois Tigeot 	if ((iir & flip_pending) == 0)
38441b13d190SFrançois Tigeot 		goto check_page_flip;
3845e9243325SFrançois Tigeot 
38468e26cdf6SFrançois Tigeot 	/* We detect FlipDone by looking for the change in PendingFlip from '1'
38478e26cdf6SFrançois Tigeot 	 * to '0' on the following vblank, i.e. IIR has the Pendingflip
38488e26cdf6SFrançois Tigeot 	 * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence
38498e26cdf6SFrançois Tigeot 	 * the flip is completed (no longer pending). Since this doesn't raise
38508e26cdf6SFrançois Tigeot 	 * an interrupt per se, we watch for the change at vblank.
38518e26cdf6SFrançois Tigeot 	 */
38528e26cdf6SFrançois Tigeot 	if (I915_READ(ISR) & flip_pending)
38531b13d190SFrançois Tigeot 		goto check_page_flip;
38548e26cdf6SFrançois Tigeot 
38551487f786SFrançois Tigeot 	intel_finish_page_flip_cs(dev_priv, pipe);
38568e26cdf6SFrançois Tigeot 	return true;
38571b13d190SFrançois Tigeot 
38581b13d190SFrançois Tigeot check_page_flip:
38591487f786SFrançois Tigeot 	intel_check_page_flip(dev_priv, pipe);
38601b13d190SFrançois Tigeot 	return false;
3861e9243325SFrançois Tigeot }
3862e9243325SFrançois Tigeot 
3863183e2373SFrançois Tigeot static irqreturn_t i915_irq_handler(int irq, void *arg)
3864e9243325SFrançois Tigeot {
3865ba55f2f5SFrançois Tigeot 	struct drm_device *dev = arg;
3866303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3867e9243325SFrançois Tigeot 	u32 iir, new_iir, pipe_stats[I915_MAX_PIPES];
3868e9243325SFrançois Tigeot 	u32 flip_mask =
3869e9243325SFrançois Tigeot 		I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
3870e9243325SFrançois Tigeot 		I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
3871183e2373SFrançois Tigeot 	int pipe, ret = IRQ_NONE;
3872e9243325SFrançois Tigeot 
38732c9916cdSFrançois Tigeot 	if (!intel_irqs_enabled(dev_priv))
38742c9916cdSFrançois Tigeot 		return IRQ_NONE;
38752c9916cdSFrançois Tigeot 
3876aee94f86SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
3877aee94f86SFrançois Tigeot 	disable_rpm_wakeref_asserts(dev_priv);
3878aee94f86SFrançois Tigeot 
3879e9243325SFrançois Tigeot 	iir = I915_READ(IIR);
3880e9243325SFrançois Tigeot 	do {
3881e9243325SFrançois Tigeot 		bool irq_received = (iir & ~flip_mask) != 0;
3882e9243325SFrançois Tigeot 		bool blc_event = false;
3883e9243325SFrançois Tigeot 
3884e9243325SFrançois Tigeot 		/* Can't rely on pipestat interrupt bit in iir as it might
3885e9243325SFrançois Tigeot 		 * have been cleared after the pipestat interrupt was received.
3886e9243325SFrançois Tigeot 		 * It doesn't set the bit in iir again, but it still produces
3887e9243325SFrançois Tigeot 		 * interrupts (for non-MSI).
3888e9243325SFrançois Tigeot 		 */
3889e9243325SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
3890e9243325SFrançois Tigeot 		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
38912c9916cdSFrançois Tigeot 			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
3892e9243325SFrançois Tigeot 
38931b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe) {
3894aee94f86SFrançois Tigeot 			i915_reg_t reg = PIPESTAT(pipe);
3895e9243325SFrançois Tigeot 			pipe_stats[pipe] = I915_READ(reg);
3896e9243325SFrançois Tigeot 
3897e9243325SFrançois Tigeot 			/* Clear the PIPE*STAT regs before the IIR */
3898e9243325SFrançois Tigeot 			if (pipe_stats[pipe] & 0x8000ffff) {
3899e9243325SFrançois Tigeot 				I915_WRITE(reg, pipe_stats[pipe]);
3900e9243325SFrançois Tigeot 				irq_received = true;
3901e9243325SFrançois Tigeot 			}
3902e9243325SFrançois Tigeot 		}
3903e9243325SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_RELEASE);
3904e9243325SFrançois Tigeot 
3905e9243325SFrançois Tigeot 		if (!irq_received)
3906e9243325SFrançois Tigeot 			break;
3907e9243325SFrançois Tigeot 
3908e9243325SFrançois Tigeot 		/* Consume port.  Then clear IIR or we'll miss events */
39091487f786SFrançois Tigeot 		if (I915_HAS_HOTPLUG(dev_priv) &&
39108621f407SFrançois Tigeot 		    iir & I915_DISPLAY_PORT_INTERRUPT) {
39118621f407SFrançois Tigeot 			u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
39128621f407SFrançois Tigeot 			if (hotplug_status)
39131487f786SFrançois Tigeot 				i9xx_hpd_irq_handler(dev_priv, hotplug_status);
39148621f407SFrançois Tigeot 		}
3915e9243325SFrançois Tigeot 
3916e9243325SFrançois Tigeot 		I915_WRITE(IIR, iir & ~flip_mask);
3917e9243325SFrançois Tigeot 		new_iir = I915_READ(IIR); /* Flush posted writes */
3918e9243325SFrançois Tigeot 
3919e9243325SFrançois Tigeot 		if (iir & I915_USER_INTERRUPT)
39201e12ee3bSFrançois Tigeot 			notify_ring(dev_priv->engine[RCS]);
3921e9243325SFrançois Tigeot 
39221b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe) {
3923e9243325SFrançois Tigeot 			int plane = pipe;
39241487f786SFrançois Tigeot 			if (HAS_FBC(dev_priv))
3925e9243325SFrançois Tigeot 				plane = !plane;
39268e26cdf6SFrançois Tigeot 
3927e9243325SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS &&
39281487f786SFrançois Tigeot 			    i915_handle_vblank(dev_priv, plane, pipe, iir))
39298e26cdf6SFrançois Tigeot 				flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(plane);
3930e9243325SFrançois Tigeot 
3931e9243325SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
3932e9243325SFrançois Tigeot 				blc_event = true;
39339edbd4a0SFrançois Tigeot 
39349edbd4a0SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
39351487f786SFrançois Tigeot 				i9xx_pipe_crc_irq_handler(dev_priv, pipe);
3936ba55f2f5SFrançois Tigeot 
39372c9916cdSFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
39382c9916cdSFrançois Tigeot 				intel_cpu_fifo_underrun_irq_handler(dev_priv,
39392c9916cdSFrançois Tigeot 								    pipe);
3940e9243325SFrançois Tigeot 		}
3941e9243325SFrançois Tigeot 
3942e9243325SFrançois Tigeot 		if (blc_event || (iir & I915_ASLE_INTERRUPT))
39431487f786SFrançois Tigeot 			intel_opregion_asle_intr(dev_priv);
3944e9243325SFrançois Tigeot 
3945e9243325SFrançois Tigeot 		/* With MSI, interrupts are only generated when iir
3946e9243325SFrançois Tigeot 		 * transitions from zero to nonzero.  If another bit got
3947e9243325SFrançois Tigeot 		 * set while we were handling the existing iir bits, then
3948e9243325SFrançois Tigeot 		 * we would never get another interrupt.
3949e9243325SFrançois Tigeot 		 *
3950e9243325SFrançois Tigeot 		 * This is fine on non-MSI as well, as if we hit this path
3951e9243325SFrançois Tigeot 		 * we avoid exiting the interrupt handler only to generate
3952e9243325SFrançois Tigeot 		 * another one.
3953e9243325SFrançois Tigeot 		 *
3954e9243325SFrançois Tigeot 		 * Note that for MSI this could cause a stray interrupt report
3955e9243325SFrançois Tigeot 		 * if an interrupt landed in the time between writing IIR and
3956e9243325SFrançois Tigeot 		 * the posting read.  This should be rare enough to never
3957e9243325SFrançois Tigeot 		 * trigger the 99% of 100,000 interrupts test for disabling
3958e9243325SFrançois Tigeot 		 * stray interrupts.
3959e9243325SFrançois Tigeot 		 */
3960183e2373SFrançois Tigeot 		ret = IRQ_HANDLED;
3961e9243325SFrançois Tigeot 		iir = new_iir;
3962e9243325SFrançois Tigeot 	} while (iir & ~flip_mask);
3963e9243325SFrançois Tigeot 
3964aee94f86SFrançois Tigeot 	enable_rpm_wakeref_asserts(dev_priv);
3965aee94f86SFrançois Tigeot 
3966183e2373SFrançois Tigeot 	return ret;
3967e9243325SFrançois Tigeot }
3968e9243325SFrançois Tigeot 
3969e9243325SFrançois Tigeot static void i915_irq_uninstall(struct drm_device * dev)
3970e9243325SFrançois Tigeot {
3971303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3972e9243325SFrançois Tigeot 	int pipe;
3973e9243325SFrançois Tigeot 
39744be47400SFrançois Tigeot 	if (I915_HAS_HOTPLUG(dev_priv)) {
3975352ff8bdSFrançois Tigeot 		i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
3976e9243325SFrançois Tigeot 		I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
3977e9243325SFrançois Tigeot 	}
3978e9243325SFrançois Tigeot 
3979e9243325SFrançois Tigeot 	I915_WRITE16(HWSTAM, 0xffff);
39801b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe) {
3981e9243325SFrançois Tigeot 		/* Clear enable bits; then clear status bits */
3982e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), 0);
3983e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)));
3984e9243325SFrançois Tigeot 	}
3985e9243325SFrançois Tigeot 	I915_WRITE(IMR, 0xffffffff);
3986e9243325SFrançois Tigeot 	I915_WRITE(IER, 0x0);
3987e9243325SFrançois Tigeot 
3988e9243325SFrançois Tigeot 	I915_WRITE(IIR, I915_READ(IIR));
3989e9243325SFrançois Tigeot }
3990e9243325SFrançois Tigeot 
3991e9243325SFrançois Tigeot static void i965_irq_preinstall(struct drm_device * dev)
3992e9243325SFrançois Tigeot {
3993303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
3994e9243325SFrançois Tigeot 	int pipe;
3995e9243325SFrançois Tigeot 
3996352ff8bdSFrançois Tigeot 	i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
3997e9243325SFrançois Tigeot 	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
3998e9243325SFrançois Tigeot 
3999e9243325SFrançois Tigeot 	I915_WRITE(HWSTAM, 0xeffe);
40001b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
4001e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), 0);
4002e9243325SFrançois Tigeot 	I915_WRITE(IMR, 0xffffffff);
4003e9243325SFrançois Tigeot 	I915_WRITE(IER, 0x0);
4004e9243325SFrançois Tigeot 	POSTING_READ(IER);
4005e9243325SFrançois Tigeot }
4006e9243325SFrançois Tigeot 
4007e9243325SFrançois Tigeot static int i965_irq_postinstall(struct drm_device *dev)
4008e9243325SFrançois Tigeot {
4009303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
4010e9243325SFrançois Tigeot 	u32 enable_mask;
4011e9243325SFrançois Tigeot 	u32 error_mask;
4012e9243325SFrançois Tigeot 
4013e9243325SFrançois Tigeot 	/* Unmask the interrupts that we always want on. */
4014e9243325SFrançois Tigeot 	dev_priv->irq_mask = ~(I915_ASLE_INTERRUPT |
4015e9243325SFrançois Tigeot 			       I915_DISPLAY_PORT_INTERRUPT |
4016e9243325SFrançois Tigeot 			       I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
4017e9243325SFrançois Tigeot 			       I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
4018e9243325SFrançois Tigeot 			       I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
4019e9243325SFrançois Tigeot 			       I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT |
4020e9243325SFrançois Tigeot 			       I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
4021e9243325SFrançois Tigeot 
4022e9243325SFrançois Tigeot 	enable_mask = ~dev_priv->irq_mask;
40238e26cdf6SFrançois Tigeot 	enable_mask &= ~(I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
40248e26cdf6SFrançois Tigeot 			 I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT);
4025e9243325SFrançois Tigeot 	enable_mask |= I915_USER_INTERRUPT;
4026e9243325SFrançois Tigeot 
40271487f786SFrançois Tigeot 	if (IS_G4X(dev_priv))
4028e9243325SFrançois Tigeot 		enable_mask |= I915_BSD_USER_INTERRUPT;
4029e9243325SFrançois Tigeot 
40309edbd4a0SFrançois Tigeot 	/* Interrupt setup is already guaranteed to be single-threaded, this is
40319edbd4a0SFrançois Tigeot 	 * just to make the assert_spin_locked check happy. */
40325e269720SFrançois Tigeot 	spin_lock_irq(&dev_priv->irq_lock);
4033ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
4034ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
4035ba55f2f5SFrançois Tigeot 	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
40365e269720SFrançois Tigeot 	spin_unlock_irq(&dev_priv->irq_lock);
4037e9243325SFrançois Tigeot 
4038e9243325SFrançois Tigeot 	/*
4039e9243325SFrançois Tigeot 	 * Enable some error detection, note the instruction error mask
4040e9243325SFrançois Tigeot 	 * bit is reserved, so we leave it masked.
4041e9243325SFrançois Tigeot 	 */
40421487f786SFrançois Tigeot 	if (IS_G4X(dev_priv)) {
4043e9243325SFrançois Tigeot 		error_mask = ~(GM45_ERROR_PAGE_TABLE |
4044e9243325SFrançois Tigeot 			       GM45_ERROR_MEM_PRIV |
4045e9243325SFrançois Tigeot 			       GM45_ERROR_CP_PRIV |
4046e9243325SFrançois Tigeot 			       I915_ERROR_MEMORY_REFRESH);
4047e9243325SFrançois Tigeot 	} else {
4048e9243325SFrançois Tigeot 		error_mask = ~(I915_ERROR_PAGE_TABLE |
4049e9243325SFrançois Tigeot 			       I915_ERROR_MEMORY_REFRESH);
4050e9243325SFrançois Tigeot 	}
4051e9243325SFrançois Tigeot 	I915_WRITE(EMR, error_mask);
4052e9243325SFrançois Tigeot 
4053e9243325SFrançois Tigeot 	I915_WRITE(IMR, dev_priv->irq_mask);
4054e9243325SFrançois Tigeot 	I915_WRITE(IER, enable_mask);
4055e9243325SFrançois Tigeot 	POSTING_READ(IER);
4056e9243325SFrançois Tigeot 
4057352ff8bdSFrançois Tigeot 	i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
4058a2fdbec6SFrançois Tigeot 	POSTING_READ(PORT_HOTPLUG_EN);
4059a2fdbec6SFrançois Tigeot 
40601487f786SFrançois Tigeot 	i915_enable_asle_pipestat(dev_priv);
4061a2fdbec6SFrançois Tigeot 
4062a2fdbec6SFrançois Tigeot 	return 0;
4063a2fdbec6SFrançois Tigeot }
4064a2fdbec6SFrançois Tigeot 
40651487f786SFrançois Tigeot static void i915_hpd_irq_setup(struct drm_i915_private *dev_priv)
4066a2fdbec6SFrançois Tigeot {
4067a2fdbec6SFrançois Tigeot 	u32 hotplug_en;
4068a2fdbec6SFrançois Tigeot 
4069*a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->irq_lock);
40709edbd4a0SFrançois Tigeot 
4071e9243325SFrançois Tigeot 	/* Note HDMI and DP share hotplug bits */
40728e26cdf6SFrançois Tigeot 	/* enable bits are the same for all generations */
40731487f786SFrançois Tigeot 	hotplug_en = intel_hpd_enabled_irqs(dev_priv, hpd_mask_i915);
4074e9243325SFrançois Tigeot 	/* Programming the CRT detection parameters tends
4075e9243325SFrançois Tigeot 	   to generate a spurious hotplug event about three
4076e9243325SFrançois Tigeot 	   seconds later.  So just do it once.
4077e9243325SFrançois Tigeot 	*/
40781487f786SFrançois Tigeot 	if (IS_G4X(dev_priv))
4079e9243325SFrançois Tigeot 		hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;
4080e9243325SFrançois Tigeot 	hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
4081e9243325SFrançois Tigeot 
4082e9243325SFrançois Tigeot 	/* Ignore TV since it's buggy */
4083352ff8bdSFrançois Tigeot 	i915_hotplug_interrupt_update_locked(dev_priv,
4084352ff8bdSFrançois Tigeot 					     HOTPLUG_INT_EN_MASK |
4085352ff8bdSFrançois Tigeot 					     CRT_HOTPLUG_VOLTAGE_COMPARE_MASK |
4086352ff8bdSFrançois Tigeot 					     CRT_HOTPLUG_ACTIVATION_PERIOD_64,
4087352ff8bdSFrançois Tigeot 					     hotplug_en);
4088e9243325SFrançois Tigeot }
4089e9243325SFrançois Tigeot 
4090183e2373SFrançois Tigeot static irqreturn_t i965_irq_handler(int irq, void *arg)
4091e9243325SFrançois Tigeot {
4092ba55f2f5SFrançois Tigeot 	struct drm_device *dev = arg;
4093303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
4094e9243325SFrançois Tigeot 	u32 iir, new_iir;
4095e9243325SFrançois Tigeot 	u32 pipe_stats[I915_MAX_PIPES];
4096183e2373SFrançois Tigeot 	int ret = IRQ_NONE, pipe;
40978e26cdf6SFrançois Tigeot 	u32 flip_mask =
40988e26cdf6SFrançois Tigeot 		I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
40998e26cdf6SFrançois Tigeot 		I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
4100e9243325SFrançois Tigeot 
41012c9916cdSFrançois Tigeot 	if (!intel_irqs_enabled(dev_priv))
41022c9916cdSFrançois Tigeot 		return IRQ_NONE;
41032c9916cdSFrançois Tigeot 
4104aee94f86SFrançois Tigeot 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
4105aee94f86SFrançois Tigeot 	disable_rpm_wakeref_asserts(dev_priv);
4106aee94f86SFrançois Tigeot 
4107e9243325SFrançois Tigeot 	iir = I915_READ(IIR);
4108e9243325SFrançois Tigeot 
4109e9243325SFrançois Tigeot 	for (;;) {
4110ba55f2f5SFrançois Tigeot 		bool irq_received = (iir & ~flip_mask) != 0;
4111e9243325SFrançois Tigeot 		bool blc_event = false;
4112e9243325SFrançois Tigeot 
4113e9243325SFrançois Tigeot 		/* Can't rely on pipestat interrupt bit in iir as it might
4114e9243325SFrançois Tigeot 		 * have been cleared after the pipestat interrupt was received.
4115e9243325SFrançois Tigeot 		 * It doesn't set the bit in iir again, but it still produces
4116e9243325SFrançois Tigeot 		 * interrupts (for non-MSI).
4117e9243325SFrançois Tigeot 		 */
4118e9243325SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_EXCLUSIVE);
4119e9243325SFrançois Tigeot 		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
41202c9916cdSFrançois Tigeot 			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
4121e9243325SFrançois Tigeot 
41221b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe) {
4123aee94f86SFrançois Tigeot 			i915_reg_t reg = PIPESTAT(pipe);
4124e9243325SFrançois Tigeot 			pipe_stats[pipe] = I915_READ(reg);
4125e9243325SFrançois Tigeot 
4126e9243325SFrançois Tigeot 			/*
4127e9243325SFrançois Tigeot 			 * Clear the PIPE*STAT regs before the IIR
4128e9243325SFrançois Tigeot 			 */
4129e9243325SFrançois Tigeot 			if (pipe_stats[pipe] & 0x8000ffff) {
4130e9243325SFrançois Tigeot 				I915_WRITE(reg, pipe_stats[pipe]);
4131ba55f2f5SFrançois Tigeot 				irq_received = true;
4132e9243325SFrançois Tigeot 			}
4133e9243325SFrançois Tigeot 		}
4134e9243325SFrançois Tigeot 		lockmgr(&dev_priv->irq_lock, LK_RELEASE);
4135e9243325SFrançois Tigeot 
4136e9243325SFrançois Tigeot 		if (!irq_received)
4137e9243325SFrançois Tigeot 			break;
4138e9243325SFrançois Tigeot 
4139183e2373SFrançois Tigeot 		ret = IRQ_HANDLED;
4140c0e85e96SFrançois Tigeot 
4141e9243325SFrançois Tigeot 		/* Consume port.  Then clear IIR or we'll miss events */
41428621f407SFrançois Tigeot 		if (iir & I915_DISPLAY_PORT_INTERRUPT) {
41438621f407SFrançois Tigeot 			u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
41448621f407SFrançois Tigeot 			if (hotplug_status)
41451487f786SFrançois Tigeot 				i9xx_hpd_irq_handler(dev_priv, hotplug_status);
41468621f407SFrançois Tigeot 		}
4147e9243325SFrançois Tigeot 
41488e26cdf6SFrançois Tigeot 		I915_WRITE(IIR, iir & ~flip_mask);
4149e9243325SFrançois Tigeot 		new_iir = I915_READ(IIR); /* Flush posted writes */
4150e9243325SFrançois Tigeot 
4151e9243325SFrançois Tigeot 		if (iir & I915_USER_INTERRUPT)
41521e12ee3bSFrançois Tigeot 			notify_ring(dev_priv->engine[RCS]);
4153e9243325SFrançois Tigeot 		if (iir & I915_BSD_USER_INTERRUPT)
41541e12ee3bSFrançois Tigeot 			notify_ring(dev_priv->engine[VCS]);
4155e9243325SFrançois Tigeot 
41561b13d190SFrançois Tigeot 		for_each_pipe(dev_priv, pipe) {
4157e9243325SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS &&
41581487f786SFrançois Tigeot 			    i915_handle_vblank(dev_priv, pipe, pipe, iir))
41598e26cdf6SFrançois Tigeot 				flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe);
4160e9243325SFrançois Tigeot 
4161e9243325SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
4162e9243325SFrançois Tigeot 				blc_event = true;
41639edbd4a0SFrançois Tigeot 
41649edbd4a0SFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
41651487f786SFrançois Tigeot 				i9xx_pipe_crc_irq_handler(dev_priv, pipe);
4166e9243325SFrançois Tigeot 
41672c9916cdSFrançois Tigeot 			if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
41682c9916cdSFrançois Tigeot 				intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
4169ba55f2f5SFrançois Tigeot 		}
41709edbd4a0SFrançois Tigeot 
4171e9243325SFrançois Tigeot 		if (blc_event || (iir & I915_ASLE_INTERRUPT))
41721487f786SFrançois Tigeot 			intel_opregion_asle_intr(dev_priv);
4173e9243325SFrançois Tigeot 
4174a2fdbec6SFrançois Tigeot 		if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
41751487f786SFrançois Tigeot 			gmbus_irq_handler(dev_priv);
4176a2fdbec6SFrançois Tigeot 
4177e9243325SFrançois Tigeot 		/* With MSI, interrupts are only generated when iir
4178e9243325SFrançois Tigeot 		 * transitions from zero to nonzero.  If another bit got
4179e9243325SFrançois Tigeot 		 * set while we were handling the existing iir bits, then
4180e9243325SFrançois Tigeot 		 * we would never get another interrupt.
4181e9243325SFrançois Tigeot 		 *
4182e9243325SFrançois Tigeot 		 * This is fine on non-MSI as well, as if we hit this path
4183e9243325SFrançois Tigeot 		 * we avoid exiting the interrupt handler only to generate
4184e9243325SFrançois Tigeot 		 * another one.
4185e9243325SFrançois Tigeot 		 *
4186e9243325SFrançois Tigeot 		 * Note that for MSI this could cause a stray interrupt report
4187e9243325SFrançois Tigeot 		 * if an interrupt landed in the time between writing IIR and
4188e9243325SFrançois Tigeot 		 * the posting read.  This should be rare enough to never
4189e9243325SFrançois Tigeot 		 * trigger the 99% of 100,000 interrupts test for disabling
4190e9243325SFrançois Tigeot 		 * stray interrupts.
4191e9243325SFrançois Tigeot 		 */
4192e9243325SFrançois Tigeot 		iir = new_iir;
4193e9243325SFrançois Tigeot 	}
4194e9243325SFrançois Tigeot 
4195aee94f86SFrançois Tigeot 	enable_rpm_wakeref_asserts(dev_priv);
4196aee94f86SFrançois Tigeot 
4197183e2373SFrançois Tigeot 	return ret;
4198e9243325SFrançois Tigeot }
4199e9243325SFrançois Tigeot 
4200e9243325SFrançois Tigeot static void i965_irq_uninstall(struct drm_device * dev)
4201e9243325SFrançois Tigeot {
4202303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
4203e9243325SFrançois Tigeot 	int pipe;
4204e9243325SFrançois Tigeot 
4205e9243325SFrançois Tigeot 	if (!dev_priv)
4206e9243325SFrançois Tigeot 		return;
4207e9243325SFrançois Tigeot 
4208352ff8bdSFrançois Tigeot 	i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
4209e9243325SFrançois Tigeot 	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
4210e9243325SFrançois Tigeot 
4211e9243325SFrançois Tigeot 	I915_WRITE(HWSTAM, 0xffffffff);
42121b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
4213e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe), 0);
4214e9243325SFrançois Tigeot 	I915_WRITE(IMR, 0xffffffff);
4215e9243325SFrançois Tigeot 	I915_WRITE(IER, 0x0);
4216e9243325SFrançois Tigeot 
42171b13d190SFrançois Tigeot 	for_each_pipe(dev_priv, pipe)
4218e9243325SFrançois Tigeot 		I915_WRITE(PIPESTAT(pipe),
4219e9243325SFrançois Tigeot 			   I915_READ(PIPESTAT(pipe)) & 0x8000ffff);
4220e9243325SFrançois Tigeot 	I915_WRITE(IIR, I915_READ(IIR));
4221e9243325SFrançois Tigeot }
4222e9243325SFrançois Tigeot 
42232c9916cdSFrançois Tigeot /**
42242c9916cdSFrançois Tigeot  * intel_irq_init - initializes irq support
42252c9916cdSFrançois Tigeot  * @dev_priv: i915 device instance
42262c9916cdSFrançois Tigeot  *
42272c9916cdSFrançois Tigeot  * This function initializes all the irq support including work items, timers
42282c9916cdSFrançois Tigeot  * and all the vtables. It does not setup the interrupt itself though.
42292c9916cdSFrançois Tigeot  */
42302c9916cdSFrançois Tigeot void intel_irq_init(struct drm_i915_private *dev_priv)
4231e9243325SFrançois Tigeot {
4232303bf270SFrançois Tigeot 	struct drm_device *dev = &dev_priv->drm;
4233e9243325SFrançois Tigeot 
4234a05eeebfSFrançois Tigeot 	intel_hpd_init_work(dev_priv);
4235a05eeebfSFrançois Tigeot 
4236e9243325SFrançois Tigeot 	INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
423700640ec9SFrançois Tigeot 	INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
4238e9243325SFrançois Tigeot 
42394be47400SFrançois Tigeot 	if (HAS_GUC_SCHED(dev_priv))
42404be47400SFrançois Tigeot 		dev_priv->pm_guc_events = GEN9_GUC_TO_HOST_INT_EVENT;
42414be47400SFrançois Tigeot 
4242ba55f2f5SFrançois Tigeot 	/* Let's track the enabled rps events */
4243aee94f86SFrançois Tigeot 	if (IS_VALLEYVIEW(dev_priv))
42441b13d190SFrançois Tigeot 		/* WaGsvRC0ResidencyMethod:vlv */
42454be47400SFrançois Tigeot 		dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED;
424624edb884SFrançois Tigeot 	else
4247ba55f2f5SFrançois Tigeot 		dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS;
4248ba55f2f5SFrançois Tigeot 
4249*a85cb24fSFrançois Tigeot 	dev_priv->rps.pm_intrmsk_mbz = 0;
42501487f786SFrançois Tigeot 
42511487f786SFrançois Tigeot 	/*
4252*a85cb24fSFrançois Tigeot 	 * SNB,IVB,HSW can while VLV,CHV may hard hang on looping batchbuffer
42531487f786SFrançois Tigeot 	 * if GEN6_PM_UP_EI_EXPIRED is masked.
42541487f786SFrançois Tigeot 	 *
42551487f786SFrançois Tigeot 	 * TODO: verify if this can be reproduced on VLV,CHV.
42561487f786SFrançois Tigeot 	 */
4257*a85cb24fSFrançois Tigeot 	if (INTEL_INFO(dev_priv)->gen <= 7)
4258*a85cb24fSFrançois Tigeot 		dev_priv->rps.pm_intrmsk_mbz |= GEN6_PM_RP_UP_EI_EXPIRED;
42591487f786SFrançois Tigeot 
42601487f786SFrançois Tigeot 	if (INTEL_INFO(dev_priv)->gen >= 8)
4261*a85cb24fSFrançois Tigeot 		dev_priv->rps.pm_intrmsk_mbz |= GEN8_PMINTR_DISABLE_REDIRECT_TO_GUC;
42621487f786SFrançois Tigeot 
42632c9916cdSFrançois Tigeot 	if (IS_GEN2(dev_priv)) {
426471f41f3eSFrançois Tigeot 		/* Gen2 doesn't have a hardware frame counter */
42659edbd4a0SFrançois Tigeot 		dev->max_vblank_count = 0;
42662c9916cdSFrançois Tigeot 	} else if (IS_G4X(dev_priv) || INTEL_INFO(dev_priv)->gen >= 5) {
4267e9243325SFrançois Tigeot 		dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
4268352ff8bdSFrançois Tigeot 		dev->driver->get_vblank_counter = g4x_get_vblank_counter;
42699edbd4a0SFrançois Tigeot 	} else {
42709edbd4a0SFrançois Tigeot 		dev->driver->get_vblank_counter = i915_get_vblank_counter;
42719edbd4a0SFrançois Tigeot 		dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
4272e9243325SFrançois Tigeot 	}
4273e9243325SFrançois Tigeot 
42741b13d190SFrançois Tigeot 	/*
42751b13d190SFrançois Tigeot 	 * Opt out of the vblank disable timer on everything except gen2.
42761b13d190SFrançois Tigeot 	 * Gen2 doesn't have a hardware frame counter and so depends on
42771b13d190SFrançois Tigeot 	 * vblank interrupts to produce sane vblank seuquence numbers.
42781b13d190SFrançois Tigeot 	 */
42792c9916cdSFrançois Tigeot 	if (!IS_GEN2(dev_priv))
42801b13d190SFrançois Tigeot 		dev->vblank_disable_immediate = true;
42811b13d190SFrançois Tigeot 
42824be47400SFrançois Tigeot 	/* Most platforms treat the display irq block as an always-on
42834be47400SFrançois Tigeot 	 * power domain. vlv/chv can disable it at runtime and need
42844be47400SFrançois Tigeot 	 * special care to avoid writing any of the display block registers
42854be47400SFrançois Tigeot 	 * outside of the power domain. We defer setting up the display irqs
42864be47400SFrançois Tigeot 	 * in this case to the runtime pm.
42874be47400SFrançois Tigeot 	 */
42884be47400SFrançois Tigeot 	dev_priv->display_irqs_enabled = true;
42894be47400SFrançois Tigeot 	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
42904be47400SFrançois Tigeot 		dev_priv->display_irqs_enabled = false;
42914be47400SFrançois Tigeot 
4292*a85cb24fSFrançois Tigeot 	dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
4293*a85cb24fSFrançois Tigeot 
4294e9243325SFrançois Tigeot 	dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
4295e9243325SFrançois Tigeot 	dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
4296e9243325SFrançois Tigeot 
42972c9916cdSFrançois Tigeot 	if (IS_CHERRYVIEW(dev_priv)) {
4298ba55f2f5SFrançois Tigeot 		dev->driver->irq_handler = cherryview_irq_handler;
4299ba55f2f5SFrançois Tigeot 		dev->driver->irq_preinstall = cherryview_irq_preinstall;
4300ba55f2f5SFrançois Tigeot 		dev->driver->irq_postinstall = cherryview_irq_postinstall;
4301ba55f2f5SFrançois Tigeot 		dev->driver->irq_uninstall = cherryview_irq_uninstall;
43021e12ee3bSFrançois Tigeot 		dev->driver->enable_vblank = i965_enable_vblank;
43031e12ee3bSFrançois Tigeot 		dev->driver->disable_vblank = i965_disable_vblank;
4304ba55f2f5SFrançois Tigeot 		dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
43052c9916cdSFrançois Tigeot 	} else if (IS_VALLEYVIEW(dev_priv)) {
4306e9243325SFrançois Tigeot 		dev->driver->irq_handler = valleyview_irq_handler;
4307e9243325SFrançois Tigeot 		dev->driver->irq_preinstall = valleyview_irq_preinstall;
4308e9243325SFrançois Tigeot 		dev->driver->irq_postinstall = valleyview_irq_postinstall;
4309e9243325SFrançois Tigeot 		dev->driver->irq_uninstall = valleyview_irq_uninstall;
43101e12ee3bSFrançois Tigeot 		dev->driver->enable_vblank = i965_enable_vblank;
43111e12ee3bSFrançois Tigeot 		dev->driver->disable_vblank = i965_disable_vblank;
43128e26cdf6SFrançois Tigeot 		dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
43132c9916cdSFrançois Tigeot 	} else if (INTEL_INFO(dev_priv)->gen >= 8) {
43149edbd4a0SFrançois Tigeot 		dev->driver->irq_handler = gen8_irq_handler;
4315ba55f2f5SFrançois Tigeot 		dev->driver->irq_preinstall = gen8_irq_reset;
43169edbd4a0SFrançois Tigeot 		dev->driver->irq_postinstall = gen8_irq_postinstall;
43179edbd4a0SFrançois Tigeot 		dev->driver->irq_uninstall = gen8_irq_uninstall;
43189edbd4a0SFrançois Tigeot 		dev->driver->enable_vblank = gen8_enable_vblank;
43199edbd4a0SFrançois Tigeot 		dev->driver->disable_vblank = gen8_disable_vblank;
4320*a85cb24fSFrançois Tigeot 		if (IS_GEN9_LP(dev_priv))
432119c468b4SFrançois Tigeot 			dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup;
43221e12ee3bSFrançois Tigeot 		else if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv))
4323352ff8bdSFrançois Tigeot 			dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup;
4324352ff8bdSFrançois Tigeot 		else
4325352ff8bdSFrançois Tigeot 			dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
43261e12ee3bSFrançois Tigeot 	} else if (HAS_PCH_SPLIT(dev_priv)) {
4327e9243325SFrançois Tigeot 		dev->driver->irq_handler = ironlake_irq_handler;
4328ba55f2f5SFrançois Tigeot 		dev->driver->irq_preinstall = ironlake_irq_reset;
4329e9243325SFrançois Tigeot 		dev->driver->irq_postinstall = ironlake_irq_postinstall;
4330e9243325SFrançois Tigeot 		dev->driver->irq_uninstall = ironlake_irq_uninstall;
4331e9243325SFrançois Tigeot 		dev->driver->enable_vblank = ironlake_enable_vblank;
4332e9243325SFrançois Tigeot 		dev->driver->disable_vblank = ironlake_disable_vblank;
4333352ff8bdSFrançois Tigeot 		dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
4334e9243325SFrançois Tigeot 	} else {
43351487f786SFrançois Tigeot 		if (IS_GEN2(dev_priv)) {
4336e9243325SFrançois Tigeot 			dev->driver->irq_preinstall = i8xx_irq_preinstall;
4337e9243325SFrançois Tigeot 			dev->driver->irq_postinstall = i8xx_irq_postinstall;
4338e9243325SFrançois Tigeot 			dev->driver->irq_handler = i8xx_irq_handler;
4339e9243325SFrançois Tigeot 			dev->driver->irq_uninstall = i8xx_irq_uninstall;
43401e12ee3bSFrançois Tigeot 			dev->driver->enable_vblank = i8xx_enable_vblank;
43411e12ee3bSFrançois Tigeot 			dev->driver->disable_vblank = i8xx_disable_vblank;
43421487f786SFrançois Tigeot 		} else if (IS_GEN3(dev_priv)) {
4343e9243325SFrançois Tigeot 			dev->driver->irq_preinstall = i915_irq_preinstall;
4344e9243325SFrançois Tigeot 			dev->driver->irq_postinstall = i915_irq_postinstall;
4345e9243325SFrançois Tigeot 			dev->driver->irq_uninstall = i915_irq_uninstall;
4346e9243325SFrançois Tigeot 			dev->driver->irq_handler = i915_irq_handler;
43471e12ee3bSFrançois Tigeot 			dev->driver->enable_vblank = i8xx_enable_vblank;
43481e12ee3bSFrançois Tigeot 			dev->driver->disable_vblank = i8xx_disable_vblank;
4349e9243325SFrançois Tigeot 		} else {
4350e9243325SFrançois Tigeot 			dev->driver->irq_preinstall = i965_irq_preinstall;
4351e9243325SFrançois Tigeot 			dev->driver->irq_postinstall = i965_irq_postinstall;
4352e9243325SFrançois Tigeot 			dev->driver->irq_uninstall = i965_irq_uninstall;
4353e9243325SFrançois Tigeot 			dev->driver->irq_handler = i965_irq_handler;
43541e12ee3bSFrançois Tigeot 			dev->driver->enable_vblank = i965_enable_vblank;
43551e12ee3bSFrançois Tigeot 			dev->driver->disable_vblank = i965_disable_vblank;
4356e9243325SFrançois Tigeot 		}
43572c9916cdSFrançois Tigeot 		if (I915_HAS_HOTPLUG(dev_priv))
43582c9916cdSFrançois Tigeot 			dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
4359e9243325SFrançois Tigeot 	}
4360e9243325SFrançois Tigeot }
4361a2fdbec6SFrançois Tigeot 
43622c9916cdSFrançois Tigeot /**
43632c9916cdSFrançois Tigeot  * intel_irq_install - enables the hardware interrupt
43642c9916cdSFrançois Tigeot  * @dev_priv: i915 device instance
43652c9916cdSFrançois Tigeot  *
43662c9916cdSFrançois Tigeot  * This function enables the hardware interrupt handling, but leaves the hotplug
43672c9916cdSFrançois Tigeot  * handling still disabled. It is called after intel_irq_init().
43682c9916cdSFrançois Tigeot  *
43692c9916cdSFrançois Tigeot  * In the driver load and resume code we need working interrupts in a few places
43702c9916cdSFrançois Tigeot  * but don't want to deal with the hassle of concurrent probe and hotplug
43712c9916cdSFrançois Tigeot  * workers. Hence the split into this two-stage approach.
43722c9916cdSFrançois Tigeot  */
43732c9916cdSFrançois Tigeot int intel_irq_install(struct drm_i915_private *dev_priv)
43749edbd4a0SFrançois Tigeot {
43752c9916cdSFrançois Tigeot 	/*
43762c9916cdSFrançois Tigeot 	 * We enable some interrupt sources in our postinstall hooks, so mark
43772c9916cdSFrançois Tigeot 	 * interrupts as enabled _before_ actually enabling them to avoid
43782c9916cdSFrançois Tigeot 	 * special cases in our ordering checks.
43792c9916cdSFrançois Tigeot 	 */
43802c9916cdSFrançois Tigeot 	dev_priv->pm.irqs_enabled = true;
43819edbd4a0SFrançois Tigeot 
4382303bf270SFrançois Tigeot 	return drm_irq_install(&dev_priv->drm, dev_priv->drm.pdev->irq);
43839edbd4a0SFrançois Tigeot }
43849edbd4a0SFrançois Tigeot 
43852c9916cdSFrançois Tigeot /**
43862c9916cdSFrançois Tigeot  * intel_irq_uninstall - finilizes all irq handling
43872c9916cdSFrançois Tigeot  * @dev_priv: i915 device instance
43882c9916cdSFrançois Tigeot  *
43892c9916cdSFrançois Tigeot  * This stops interrupt and hotplug handling and unregisters and frees all
43902c9916cdSFrançois Tigeot  * resources acquired in the init functions.
43912c9916cdSFrançois Tigeot  */
43922c9916cdSFrançois Tigeot void intel_irq_uninstall(struct drm_i915_private *dev_priv)
43939edbd4a0SFrançois Tigeot {
4394303bf270SFrançois Tigeot 	drm_irq_uninstall(&dev_priv->drm);
43952c9916cdSFrançois Tigeot 	intel_hpd_cancel_work(dev_priv);
43962c9916cdSFrançois Tigeot 	dev_priv->pm.irqs_enabled = false;
43972c9916cdSFrançois Tigeot }
43989edbd4a0SFrançois Tigeot 
43992c9916cdSFrançois Tigeot /**
44002c9916cdSFrançois Tigeot  * intel_runtime_pm_disable_interrupts - runtime interrupt disabling
44012c9916cdSFrançois Tigeot  * @dev_priv: i915 device instance
44022c9916cdSFrançois Tigeot  *
44032c9916cdSFrançois Tigeot  * This function is used to disable interrupts at runtime, both in the runtime
44042c9916cdSFrançois Tigeot  * pm and the system suspend/resume code.
44052c9916cdSFrançois Tigeot  */
44062c9916cdSFrançois Tigeot void intel_runtime_pm_disable_interrupts(struct drm_i915_private *dev_priv)
44072c9916cdSFrançois Tigeot {
4408303bf270SFrançois Tigeot 	dev_priv->drm.driver->irq_uninstall(&dev_priv->drm);
44092c9916cdSFrançois Tigeot 	dev_priv->pm.irqs_enabled = false;
4410303bf270SFrançois Tigeot 	synchronize_irq(dev_priv->drm.irq);
44112c9916cdSFrançois Tigeot }
44122c9916cdSFrançois Tigeot 
44132c9916cdSFrançois Tigeot /**
44142c9916cdSFrançois Tigeot  * intel_runtime_pm_enable_interrupts - runtime interrupt enabling
44152c9916cdSFrançois Tigeot  * @dev_priv: i915 device instance
44162c9916cdSFrançois Tigeot  *
44172c9916cdSFrançois Tigeot  * This function is used to enable interrupts at runtime, both in the runtime
44182c9916cdSFrançois Tigeot  * pm and the system suspend/resume code.
44192c9916cdSFrançois Tigeot  */
44202c9916cdSFrançois Tigeot void intel_runtime_pm_enable_interrupts(struct drm_i915_private *dev_priv)
44212c9916cdSFrançois Tigeot {
44222c9916cdSFrançois Tigeot 	dev_priv->pm.irqs_enabled = true;
4423303bf270SFrançois Tigeot 	dev_priv->drm.driver->irq_preinstall(&dev_priv->drm);
4424303bf270SFrançois Tigeot 	dev_priv->drm.driver->irq_postinstall(&dev_priv->drm);
44259edbd4a0SFrançois Tigeot }
4426