aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/i915_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c441
1 files changed, 393 insertions, 48 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 0aa2ef0d2ae..879c4ccb00d 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -112,6 +112,213 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
}
}
+static bool ivb_can_enable_err_int(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *crtc;
+ enum pipe pipe;
+
+ for_each_pipe(pipe) {
+ crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+ if (crtc->cpu_fifo_underrun_disabled)
+ return false;
+ }
+
+ return true;
+}
+
+static bool cpt_can_enable_serr_int(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum pipe pipe;
+ struct intel_crtc *crtc;
+
+ for_each_pipe(pipe) {
+ crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+ if (crtc->pch_fifo_underrun_disabled)
+ return false;
+ }
+
+ return true;
+}
+
+static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
+ enum pipe pipe, bool enable)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t bit = (pipe == PIPE_A) ? DE_PIPEA_FIFO_UNDERRUN :
+ DE_PIPEB_FIFO_UNDERRUN;
+
+ if (enable)
+ ironlake_enable_display_irq(dev_priv, bit);
+ else
+ ironlake_disable_display_irq(dev_priv, bit);
+}
+
+static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
+ bool enable)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (enable) {
+ if (!ivb_can_enable_err_int(dev))
+ return;
+
+ I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN_A |
+ ERR_INT_FIFO_UNDERRUN_B |
+ ERR_INT_FIFO_UNDERRUN_C);
+
+ ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
+ } else {
+ ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+ }
+}
+
+static void ibx_set_fifo_underrun_reporting(struct intel_crtc *crtc,
+ bool enable)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t bit = (crtc->pipe == PIPE_A) ? SDE_TRANSA_FIFO_UNDER :
+ SDE_TRANSB_FIFO_UNDER;
+
+ if (enable)
+ I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~bit);
+ else
+ I915_WRITE(SDEIMR, I915_READ(SDEIMR) | bit);
+
+ POSTING_READ(SDEIMR);
+}
+
+static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
+ enum transcoder pch_transcoder,
+ bool enable)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (enable) {
+ if (!cpt_can_enable_serr_int(dev))
+ return;
+
+ I915_WRITE(SERR_INT, SERR_INT_TRANS_A_FIFO_UNDERRUN |
+ SERR_INT_TRANS_B_FIFO_UNDERRUN |
+ SERR_INT_TRANS_C_FIFO_UNDERRUN);
+
+ I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~SDE_ERROR_CPT);
+ } else {
+ I915_WRITE(SDEIMR, I915_READ(SDEIMR) | SDE_ERROR_CPT);
+ }
+
+ POSTING_READ(SDEIMR);
+}
+
+/**
+ * intel_set_cpu_fifo_underrun_reporting - enable/disable FIFO underrun messages
+ * @dev: drm device
+ * @pipe: pipe
+ * @enable: true if we want to report FIFO underrun errors, false otherwise
+ *
+ * This function makes us disable or enable CPU fifo underruns for a specific
+ * pipe. Notice that on some Gens (e.g. IVB, HSW), disabling FIFO underrun
+ * reporting for one pipe may also disable all the other CPU error interruts for
+ * the other pipes, due to the fact that there's just one interrupt mask/enable
+ * bit for all the pipes.
+ *
+ * Returns the previous state of underrun reporting.
+ */
+bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+ enum pipe pipe, bool enable)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+ ret = !intel_crtc->cpu_fifo_underrun_disabled;
+
+ if (enable == ret)
+ goto done;
+
+ intel_crtc->cpu_fifo_underrun_disabled = !enable;
+
+ if (IS_GEN5(dev) || IS_GEN6(dev))
+ ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
+ else if (IS_GEN7(dev))
+ ivybridge_set_fifo_underrun_reporting(dev, enable);
+
+done:
+ spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+ return ret;
+}
+
+/**
+ * intel_set_pch_fifo_underrun_reporting - enable/disable FIFO underrun messages
+ * @dev: drm device
+ * @pch_transcoder: the PCH transcoder (same as pipe on IVB and older)
+ * @enable: true if we want to report FIFO underrun errors, false otherwise
+ *
+ * This function makes us disable or enable PCH fifo underruns for a specific
+ * PCH transcoder. Notice that on some PCHs (e.g. CPT/PPT), disabling FIFO
+ * underrun reporting for one transcoder may also disable all the other PCH
+ * error interruts for the other transcoders, due to the fact that there's just
+ * one interrupt mask/enable bit for all the transcoders.
+ *
+ * Returns the previous state of underrun reporting.
+ */
+bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
+ enum transcoder pch_transcoder,
+ bool enable)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum pipe p;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ unsigned long flags;
+ bool ret;
+
+ if (HAS_PCH_LPT(dev)) {
+ crtc = NULL;
+ for_each_pipe(p) {
+ struct drm_crtc *c = dev_priv->pipe_to_crtc_mapping[p];
+ if (intel_pipe_has_type(c, INTEL_OUTPUT_ANALOG)) {
+ crtc = c;
+ break;
+ }
+ }
+ if (!crtc) {
+ DRM_ERROR("PCH FIFO underrun, but no CRTC using the PCH found\n");
+ return false;
+ }
+ } else {
+ crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder];
+ }
+ intel_crtc = to_intel_crtc(crtc);
+
+ spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+ ret = !intel_crtc->pch_fifo_underrun_disabled;
+
+ if (enable == ret)
+ goto done;
+
+ intel_crtc->pch_fifo_underrun_disabled = !enable;
+
+ if (HAS_PCH_IBX(dev))
+ ibx_set_fifo_underrun_reporting(intel_crtc, enable);
+ else
+ cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
+
+done:
+ spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+ return ret;
+}
+
+
void
i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
{
@@ -142,28 +349,21 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
}
/**
- * intel_enable_asle - enable ASLE interrupt for OpRegion
+ * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion
*/
-void intel_enable_asle(struct drm_device *dev)
+static void i915_enable_asle_pipestat(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
unsigned long irqflags;
- /* FIXME: opregion/asle for VLV */
- if (IS_VALLEYVIEW(dev))
+ if (!dev_priv->opregion.asle || !IS_MOBILE(dev))
return;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- if (HAS_PCH_SPLIT(dev))
- ironlake_enable_display_irq(dev_priv, DE_GSE);
- else {
- i915_enable_pipestat(dev_priv, 1,
- PIPE_LEGACY_BLC_EVENT_ENABLE);
- if (INTEL_INFO(dev)->gen >= 4)
- i915_enable_pipestat(dev_priv, 0,
- PIPE_LEGACY_BLC_EVENT_ENABLE);
- }
+ i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
+ if (INTEL_INFO(dev)->gen >= 4)
+ i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
@@ -184,6 +384,10 @@ i915_pipe_enabled(struct drm_device *dev, int pipe)
enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
pipe);
+ if (!intel_display_power_enabled(dev,
+ POWER_DOMAIN_TRANSCODER(cpu_transcoder)))
+ return false;
+
return I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE;
}
@@ -334,6 +538,21 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
crtc);
}
+static int intel_hpd_irq_event(struct drm_device *dev, struct drm_connector *connector)
+{
+ enum drm_connector_status old_status;
+
+ WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+ old_status = connector->status;
+
+ connector->status = connector->funcs->detect(connector, false);
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
+ connector->base.id,
+ drm_get_connector_name(connector),
+ old_status, connector->status);
+ return (old_status != connector->status);
+}
+
/*
* Handle hotplug events outside the interrupt handler proper.
*/
@@ -350,6 +569,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
struct drm_connector *connector;
unsigned long irqflags;
bool hpd_disabled = false;
+ bool changed = false;
+ u32 hpd_event_bits;
/* HPD irq before everything is fully set up. */
if (!dev_priv->enable_hotplug_processing)
@@ -359,6 +580,9 @@ static void i915_hotplug_work_func(struct work_struct *work)
DRM_DEBUG_KMS("running encoder hotplug functions\n");
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+
+ hpd_event_bits = dev_priv->hpd_event_bits;
+ dev_priv->hpd_event_bits = 0;
list_for_each_entry(connector, &mode_config->connector_list, head) {
intel_connector = to_intel_connector(connector);
intel_encoder = intel_connector->encoder;
@@ -373,6 +597,10 @@ static void i915_hotplug_work_func(struct work_struct *work)
| DRM_CONNECTOR_POLL_DISCONNECT;
hpd_disabled = true;
}
+ if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
+ DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n",
+ drm_get_connector_name(connector), intel_encoder->hpd_pin);
+ }
}
/* if there were no outputs to poll, poll was disabled,
* therefore make sure it's enabled when disabling HPD on
@@ -385,14 +613,20 @@ static void i915_hotplug_work_func(struct work_struct *work)
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
- list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
- if (intel_encoder->hot_plug)
- intel_encoder->hot_plug(intel_encoder);
-
+ list_for_each_entry(connector, &mode_config->connector_list, head) {
+ intel_connector = to_intel_connector(connector);
+ intel_encoder = intel_connector->encoder;
+ if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
+ if (intel_encoder->hot_plug)
+ intel_encoder->hot_plug(intel_encoder);
+ if (intel_hpd_irq_event(dev, connector))
+ changed = true;
+ }
+ }
mutex_unlock(&mode_config->mutex);
- /* Just fire off a uevent and let userspace tell us what to do */
- drm_helper_hpd_irq_event(dev);
+ if (changed)
+ drm_kms_helper_hotplug_event(dev);
}
static void ironlake_handle_rps_change(struct drm_device *dev)
@@ -482,7 +716,21 @@ static void gen6_pm_rps_work(struct work_struct *work)
*/
if (!(new_delay > dev_priv->rps.max_delay ||
new_delay < dev_priv->rps.min_delay)) {
- gen6_set_rps(dev_priv->dev, new_delay);
+ if (IS_VALLEYVIEW(dev_priv->dev))
+ valleyview_set_rps(dev_priv->dev, new_delay);
+ else
+ gen6_set_rps(dev_priv->dev, new_delay);
+ }
+
+ if (IS_VALLEYVIEW(dev_priv->dev)) {
+ /*
+ * On VLV, when we enter RC6 we may not be at the minimum
+ * voltage level, so arm a timer to check. It should only
+ * fire when there's activity or once after we've entered
+ * RC6, and then won't be re-armed until the next RPS interrupt.
+ */
+ mod_delayed_work(dev_priv->wq, &dev_priv->rps.vlv_work,
+ msecs_to_jiffies(100));
}
mutex_unlock(&dev_priv->rps.hw_lock);
@@ -636,6 +884,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED)
continue;
+ dev_priv->hpd_event_bits |= (1 << i);
if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies,
dev_priv->hpd_stats[i].hpd_last_jiffies
+ msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) {
@@ -643,6 +892,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
dev_priv->hpd_stats[i].hpd_cnt = 0;
} else if (dev_priv->hpd_stats[i].hpd_cnt > HPD_STORM_THRESHOLD) {
dev_priv->hpd_stats[i].hpd_mark = HPD_MARK_DISABLED;
+ dev_priv->hpd_event_bits &= ~(1 << i);
DRM_DEBUG_KMS("HPD interrupt storm detected on PIN %d\n", i);
ret = true;
} else {
@@ -763,10 +1013,12 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
ibx_hpd_irq_setup(dev);
queue_work(dev_priv->wq, &dev_priv->hotplug_work);
}
- if (pch_iir & SDE_AUDIO_POWER_MASK)
+ if (pch_iir & SDE_AUDIO_POWER_MASK) {
+ int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
+ SDE_AUDIO_POWER_SHIFT);
DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
- (pch_iir & SDE_AUDIO_POWER_MASK) >>
- SDE_AUDIO_POWER_SHIFT);
+ port_name(port));
+ }
if (pch_iir & SDE_AUX_MASK)
dp_aux_irq_handler(dev);
@@ -795,10 +1047,64 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR))
DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n");
- if (pch_iir & SDE_TRANSB_FIFO_UNDER)
- DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n");
if (pch_iir & SDE_TRANSA_FIFO_UNDER)
- DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
+ if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
+ false))
+ DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+
+ if (pch_iir & SDE_TRANSB_FIFO_UNDER)
+ if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
+ false))
+ DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+}
+
+static void ivb_err_int_handler(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 err_int = I915_READ(GEN7_ERR_INT);
+
+ if (err_int & ERR_INT_POISON)
+ DRM_ERROR("Poison interrupt\n");
+
+ if (err_int & ERR_INT_FIFO_UNDERRUN_A)
+ if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
+ DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
+
+ if (err_int & ERR_INT_FIFO_UNDERRUN_B)
+ if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
+ DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
+
+ if (err_int & ERR_INT_FIFO_UNDERRUN_C)
+ if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false))
+ DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n");
+
+ I915_WRITE(GEN7_ERR_INT, err_int);
+}
+
+static void cpt_serr_int_handler(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 serr_int = I915_READ(SERR_INT);
+
+ if (serr_int & SERR_INT_POISON)
+ DRM_ERROR("PCH poison interrupt\n");
+
+ if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN)
+ if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
+ false))
+ DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+
+ if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN)
+ if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
+ false))
+ DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+
+ if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN)
+ if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_C,
+ false))
+ DRM_DEBUG_DRIVER("PCH transcoder C FIFO underrun\n");
+
+ I915_WRITE(SERR_INT, serr_int);
}
static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
@@ -812,10 +1118,12 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
ibx_hpd_irq_setup(dev);
queue_work(dev_priv->wq, &dev_priv->hotplug_work);
}
- if (pch_iir & SDE_AUDIO_POWER_MASK_CPT)
- DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
- (pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
- SDE_AUDIO_POWER_SHIFT_CPT);
+ if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
+ int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
+ SDE_AUDIO_POWER_SHIFT_CPT);
+ DRM_DEBUG_DRIVER("PCH audio power change on port %c\n",
+ port_name(port));
+ }
if (pch_iir & SDE_AUX_MASK_CPT)
dp_aux_irq_handler(dev);
@@ -834,6 +1142,9 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n",
pipe_name(pipe),
I915_READ(FDI_RX_IIR(pipe)));
+
+ if (pch_iir & SDE_ERROR_CPT)
+ cpt_serr_int_handler(dev);
}
static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
@@ -846,6 +1157,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
atomic_inc(&dev_priv->irq_received);
+ /* We get interrupts on unclaimed registers, so check for this before we
+ * do any I915_{READ,WRITE}. */
+ if (IS_HASWELL(dev) &&
+ (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
+ DRM_ERROR("Unclaimed register before interrupt\n");
+ I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
+ }
+
/* disable master interrupt before clearing iir */
de_ier = I915_READ(DEIER);
I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
@@ -861,6 +1180,12 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
POSTING_READ(SDEIER);
}
+ /* On Haswell, also mask ERR_INT because we don't want to risk
+ * generating "unclaimed register" interrupts from inside the interrupt
+ * handler. */
+ if (IS_HASWELL(dev))
+ ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+
gt_iir = I915_READ(GTIIR);
if (gt_iir) {
snb_gt_irq_handler(dev, dev_priv, gt_iir);
@@ -870,11 +1195,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
de_iir = I915_READ(DEIIR);
if (de_iir) {
+ if (de_iir & DE_ERR_INT_IVB)
+ ivb_err_int_handler(dev);
+
if (de_iir & DE_AUX_CHANNEL_A_IVB)
dp_aux_irq_handler(dev);
if (de_iir & DE_GSE_IVB)
- intel_opregion_gse_intr(dev);
+ intel_opregion_asle_intr(dev);
for (i = 0; i < 3; i++) {
if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
@@ -907,6 +1235,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
ret = IRQ_HANDLED;
}
+ if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev))
+ ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
+
I915_WRITE(DEIER, de_ier);
POSTING_READ(DEIER);
if (!HAS_PCH_NOP(dev)) {
@@ -968,7 +1299,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
dp_aux_irq_handler(dev);
if (de_iir & DE_GSE)
- intel_opregion_gse_intr(dev);
+ intel_opregion_asle_intr(dev);
if (de_iir & DE_PIPEA_VBLANK)
drm_handle_vblank(dev, 0);
@@ -976,6 +1307,17 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
if (de_iir & DE_PIPEB_VBLANK)
drm_handle_vblank(dev, 1);
+ if (de_iir & DE_POISON)
+ DRM_ERROR("Poison interrupt\n");
+
+ if (de_iir & DE_PIPEA_FIFO_UNDERRUN)
+ if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
+ DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
+
+ if (de_iir & DE_PIPEB_FIFO_UNDERRUN)
+ if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
+ DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
+
if (de_iir & DE_PLANEA_FLIP_DONE) {
intel_prepare_page_flip(dev, 0);
intel_finish_page_flip_plane(dev, 0);
@@ -2201,10 +2543,14 @@ static void ibx_irq_postinstall(struct drm_device *dev)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 mask;
- if (HAS_PCH_IBX(dev))
- mask = SDE_GMBUS | SDE_AUX_MASK;
- else
- mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
+ if (HAS_PCH_IBX(dev)) {
+ mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER |
+ SDE_TRANSA_FIFO_UNDER | SDE_POISON;
+ } else {
+ mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT;
+
+ I915_WRITE(SERR_INT, I915_READ(SERR_INT));
+ }
if (HAS_PCH_NOP(dev))
return;
@@ -2219,7 +2565,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
/* enable kind of interrupts always enabled */
u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
- DE_AUX_CHANNEL_A;
+ DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
+ DE_PIPEA_FIFO_UNDERRUN | DE_POISON;
u32 render_irqs;
dev_priv->irq_mask = ~display_mask;
@@ -2269,12 +2616,14 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
DE_PLANEC_FLIP_DONE_IVB |
DE_PLANEB_FLIP_DONE_IVB |
DE_PLANEA_FLIP_DONE_IVB |
- DE_AUX_CHANNEL_A_IVB;
+ DE_AUX_CHANNEL_A_IVB |
+ DE_ERR_INT_IVB;
u32 render_irqs;
dev_priv->irq_mask = ~display_mask;
/* should always can generate irq */
+ I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
I915_WRITE(DEIIR, I915_READ(DEIIR));
I915_WRITE(DEIMR, dev_priv->irq_mask);
I915_WRITE(DEIER,
@@ -2305,7 +2654,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
u32 enable_mask;
u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
u32 render_irqs;
- u16 msid;
enable_mask = I915_DISPLAY_PORT_INTERRUPT;
enable_mask |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
@@ -2321,13 +2669,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
- /* Hack for broken MSIs on VLV */
- pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000);
- pci_read_config_word(dev->pdev, 0x98, &msid);
- msid &= 0xff; /* mask out delivery bits */
- msid |= (1<<14);
- pci_write_config_word(dev_priv->dev->pdev, 0x98, msid);
-
I915_WRITE(PORT_HOTPLUG_EN, 0);
POSTING_READ(PORT_HOTPLUG_EN);
@@ -2402,6 +2743,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
I915_WRITE(DEIMR, 0xffffffff);
I915_WRITE(DEIER, 0x0);
I915_WRITE(DEIIR, I915_READ(DEIIR));
+ if (IS_GEN7(dev))
+ I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
I915_WRITE(GTIMR, 0xffffffff);
I915_WRITE(GTIER, 0x0);
@@ -2413,6 +2756,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
I915_WRITE(SDEIMR, 0xffffffff);
I915_WRITE(SDEIER, 0x0);
I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+ if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev))
+ I915_WRITE(SERR_INT, I915_READ(SERR_INT));
}
static void i8xx_irq_preinstall(struct drm_device * dev)
@@ -2626,7 +2971,7 @@ static int i915_irq_postinstall(struct drm_device *dev)
I915_WRITE(IER, enable_mask);
POSTING_READ(IER);
- intel_opregion_enable_asle(dev);
+ i915_enable_asle_pipestat(dev);
return 0;
}
@@ -2860,7 +3205,7 @@ static int i965_irq_postinstall(struct drm_device *dev)
I915_WRITE(PORT_HOTPLUG_EN, 0);
POSTING_READ(PORT_HOTPLUG_EN);
- intel_opregion_enable_asle(dev);
+ i915_enable_asle_pipestat(dev);
return 0;
}