|
@@ -30,6 +30,7 @@
|
|
|
|
|
|
#include <linux/sysrq.h>
|
|
|
#include <linux/slab.h>
|
|
|
+#include <linux/circ_buf.h>
|
|
|
#include <drm/drmP.h>
|
|
|
#include <drm/i915_drm.h>
|
|
|
#include "i915_drv.h"
|
|
@@ -269,6 +270,21 @@ static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void broadwell_set_fifo_underrun_reporting(struct drm_device *dev,
|
|
|
+ enum pipe pipe, bool enable)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+
|
|
|
+ assert_spin_locked(&dev_priv->irq_lock);
|
|
|
+
|
|
|
+ if (enable)
|
|
|
+ dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_FIFO_UNDERRUN;
|
|
|
+ else
|
|
|
+ dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_FIFO_UNDERRUN;
|
|
|
+ I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
|
|
|
+ POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ibx_display_interrupt_update - update SDEIMR
|
|
|
* @dev_priv: driver private
|
|
@@ -381,6 +397,8 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
|
|
|
ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
|
|
|
else if (IS_GEN7(dev))
|
|
|
ivybridge_set_fifo_underrun_reporting(dev, pipe, enable);
|
|
|
+ else if (IS_GEN8(dev))
|
|
|
+ broadwell_set_fifo_underrun_reporting(dev, pipe, enable);
|
|
|
|
|
|
done:
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
|
|
@@ -441,7 +459,7 @@ done:
|
|
|
|
|
|
|
|
|
void
|
|
|
-i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
|
|
|
+i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
|
|
|
{
|
|
|
u32 reg = PIPESTAT(pipe);
|
|
|
u32 pipestat = I915_READ(reg) & 0x7fff0000;
|
|
@@ -458,7 +476,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
|
|
|
}
|
|
|
|
|
|
void
|
|
|
-i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
|
|
|
+i915_disable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask)
|
|
|
{
|
|
|
u32 reg = PIPESTAT(pipe);
|
|
|
u32 pipestat = I915_READ(reg) & 0x7fff0000;
|
|
@@ -486,9 +504,10 @@ static void i915_enable_asle_pipestat(struct drm_device *dev)
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
|
|
|
- i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_B, 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, PIPE_A,
|
|
|
+ PIPE_LEGACY_BLC_EVENT_ENABLE);
|
|
|
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
}
|
|
@@ -518,6 +537,12 @@ i915_pipe_enabled(struct drm_device *dev, int pipe)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static u32 i8xx_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
|
+{
|
|
|
+ /* Gen2 doesn't have a hardware frame counter */
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* Called from drm generic code, passed a 'crtc', which
|
|
|
* we use as a pipe index
|
|
|
*/
|
|
@@ -526,7 +551,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
unsigned long high_frame;
|
|
|
unsigned long low_frame;
|
|
|
- u32 high1, high2, low;
|
|
|
+ u32 high1, high2, low, pixel, vbl_start;
|
|
|
|
|
|
if (!i915_pipe_enabled(dev, pipe)) {
|
|
|
DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
|
|
@@ -534,6 +559,24 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
|
+ struct intel_crtc *intel_crtc =
|
|
|
+ to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
|
|
|
+ const struct drm_display_mode *mode =
|
|
|
+ &intel_crtc->config.adjusted_mode;
|
|
|
+
|
|
|
+ vbl_start = mode->crtc_vblank_start * mode->crtc_htotal;
|
|
|
+ } else {
|
|
|
+ enum transcoder cpu_transcoder =
|
|
|
+ intel_pipe_to_cpu_transcoder(dev_priv, pipe);
|
|
|
+ u32 htotal;
|
|
|
+
|
|
|
+ htotal = ((I915_READ(HTOTAL(cpu_transcoder)) >> 16) & 0x1fff) + 1;
|
|
|
+ vbl_start = (I915_READ(VBLANK(cpu_transcoder)) & 0x1fff) + 1;
|
|
|
+
|
|
|
+ vbl_start *= htotal;
|
|
|
+ }
|
|
|
+
|
|
|
high_frame = PIPEFRAME(pipe);
|
|
|
low_frame = PIPEFRAMEPIXEL(pipe);
|
|
|
|
|
@@ -544,13 +587,20 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
|
*/
|
|
|
do {
|
|
|
high1 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK;
|
|
|
- low = I915_READ(low_frame) & PIPE_FRAME_LOW_MASK;
|
|
|
+ low = I915_READ(low_frame);
|
|
|
high2 = I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK;
|
|
|
} while (high1 != high2);
|
|
|
|
|
|
high1 >>= PIPE_FRAME_HIGH_SHIFT;
|
|
|
+ pixel = low & PIPE_PIXEL_MASK;
|
|
|
low >>= PIPE_FRAME_LOW_SHIFT;
|
|
|
- return (high1 << 8) | low;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The frame counter increments at beginning of active.
|
|
|
+ * Cook up a vblank counter by also checking the pixel
|
|
|
+ * counter against vblank start.
|
|
|
+ */
|
|
|
+ return ((high1 << 8) | low) + (pixel >= vbl_start);
|
|
|
}
|
|
|
|
|
|
static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
@@ -567,66 +617,163 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
|
|
|
return I915_READ(reg);
|
|
|
}
|
|
|
|
|
|
+/* raw reads, only for fast reads of display block, no need for forcewake etc. */
|
|
|
+#define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
|
|
|
+#define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__))
|
|
|
+
|
|
|
+static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ uint32_t status;
|
|
|
+ int reg;
|
|
|
+
|
|
|
+ if (IS_VALLEYVIEW(dev)) {
|
|
|
+ status = pipe == PIPE_A ?
|
|
|
+ I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
|
|
|
+ I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
|
|
|
+
|
|
|
+ reg = VLV_ISR;
|
|
|
+ } else if (IS_GEN2(dev)) {
|
|
|
+ status = pipe == PIPE_A ?
|
|
|
+ I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
|
|
|
+ I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
|
|
|
+
|
|
|
+ reg = ISR;
|
|
|
+ } else if (INTEL_INFO(dev)->gen < 5) {
|
|
|
+ status = pipe == PIPE_A ?
|
|
|
+ I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
|
|
|
+ I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
|
|
|
+
|
|
|
+ reg = ISR;
|
|
|
+ } else if (INTEL_INFO(dev)->gen < 7) {
|
|
|
+ status = pipe == PIPE_A ?
|
|
|
+ DE_PIPEA_VBLANK :
|
|
|
+ DE_PIPEB_VBLANK;
|
|
|
+
|
|
|
+ reg = DEISR;
|
|
|
+ } else {
|
|
|
+ switch (pipe) {
|
|
|
+ default:
|
|
|
+ case PIPE_A:
|
|
|
+ status = DE_PIPEA_VBLANK_IVB;
|
|
|
+ break;
|
|
|
+ case PIPE_B:
|
|
|
+ status = DE_PIPEB_VBLANK_IVB;
|
|
|
+ break;
|
|
|
+ case PIPE_C:
|
|
|
+ status = DE_PIPEC_VBLANK_IVB;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ reg = DEISR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (IS_GEN2(dev))
|
|
|
+ return __raw_i915_read16(dev_priv, reg) & status;
|
|
|
+ else
|
|
|
+ return __raw_i915_read32(dev_priv, reg) & status;
|
|
|
+}
|
|
|
+
|
|
|
static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
|
|
|
- int *vpos, int *hpos)
|
|
|
+ int *vpos, int *hpos, ktime_t *stime, ktime_t *etime)
|
|
|
{
|
|
|
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
- u32 vbl = 0, position = 0;
|
|
|
+ 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);
|
|
|
+ const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
|
|
|
+ int position;
|
|
|
int vbl_start, vbl_end, htotal, vtotal;
|
|
|
bool in_vbl = true;
|
|
|
int ret = 0;
|
|
|
- enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
|
|
|
- pipe);
|
|
|
+ unsigned long irqflags;
|
|
|
|
|
|
- if (!i915_pipe_enabled(dev, pipe)) {
|
|
|
+ if (!intel_crtc->active) {
|
|
|
DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
|
|
|
"pipe %c\n", pipe_name(pipe));
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- /* Get vtotal. */
|
|
|
- vtotal = 1 + ((I915_READ(VTOTAL(cpu_transcoder)) >> 16) & 0x1fff);
|
|
|
+ htotal = mode->crtc_htotal;
|
|
|
+ vtotal = mode->crtc_vtotal;
|
|
|
+ vbl_start = mode->crtc_vblank_start;
|
|
|
+ vbl_end = mode->crtc_vblank_end;
|
|
|
|
|
|
- if (INTEL_INFO(dev)->gen >= 4) {
|
|
|
+ ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Lock uncore.lock, as we will do multiple timing critical raw
|
|
|
+ * register reads, potentially with preemption disabled, so the
|
|
|
+ * following code must not block on uncore.lock.
|
|
|
+ */
|
|
|
+ spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
|
|
|
+
|
|
|
+ /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
|
|
|
+
|
|
|
+ /* Get optional system timestamp before query. */
|
|
|
+ if (stime)
|
|
|
+ *stime = ktime_get();
|
|
|
+
|
|
|
+ if (IS_GEN2(dev) || IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
|
|
|
/* No obvious pixelcount register. Only query vertical
|
|
|
* scanout position from Display scan line register.
|
|
|
*/
|
|
|
- position = I915_READ(PIPEDSL(pipe));
|
|
|
+ if (IS_GEN2(dev))
|
|
|
+ position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
|
|
|
+ else
|
|
|
+ position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
|
|
|
|
|
|
- /* Decode into vertical scanout position. Don't have
|
|
|
- * horizontal scanout position.
|
|
|
+ /*
|
|
|
+ * The scanline counter increments at the leading edge
|
|
|
+ * of hsync, ie. it completely misses the active portion
|
|
|
+ * of the line. Fix up the counter at both edges of vblank
|
|
|
+ * to get a more accurate picture whether we're in vblank
|
|
|
+ * or not.
|
|
|
*/
|
|
|
- *vpos = position & 0x1fff;
|
|
|
- *hpos = 0;
|
|
|
+ in_vbl = intel_pipe_in_vblank_locked(dev, pipe);
|
|
|
+ if ((in_vbl && position == vbl_start - 1) ||
|
|
|
+ (!in_vbl && position == vbl_end - 1))
|
|
|
+ position = (position + 1) % vtotal;
|
|
|
} else {
|
|
|
/* Have access to pixelcount since start of frame.
|
|
|
* We can split this into vertical and horizontal
|
|
|
* scanout position.
|
|
|
*/
|
|
|
- position = (I915_READ(PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT;
|
|
|
+ position = (__raw_i915_read32(dev_priv, PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT;
|
|
|
|
|
|
- htotal = 1 + ((I915_READ(HTOTAL(cpu_transcoder)) >> 16) & 0x1fff);
|
|
|
- *vpos = position / htotal;
|
|
|
- *hpos = position - (*vpos * htotal);
|
|
|
+ /* convert to pixel counts */
|
|
|
+ vbl_start *= htotal;
|
|
|
+ vbl_end *= htotal;
|
|
|
+ vtotal *= htotal;
|
|
|
}
|
|
|
|
|
|
- /* Query vblank area. */
|
|
|
- vbl = I915_READ(VBLANK(cpu_transcoder));
|
|
|
+ /* Get optional system timestamp after query. */
|
|
|
+ if (etime)
|
|
|
+ *etime = ktime_get();
|
|
|
|
|
|
- /* Test position against vblank region. */
|
|
|
- vbl_start = vbl & 0x1fff;
|
|
|
- vbl_end = (vbl >> 16) & 0x1fff;
|
|
|
+ /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
|
|
|
|
|
|
- if ((*vpos < vbl_start) || (*vpos > vbl_end))
|
|
|
- in_vbl = false;
|
|
|
+ spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
|
|
|
|
|
|
- /* Inside "upper part" of vblank area? Apply corrective offset: */
|
|
|
- if (in_vbl && (*vpos >= vbl_start))
|
|
|
- *vpos = *vpos - vtotal;
|
|
|
+ in_vbl = position >= vbl_start && position < vbl_end;
|
|
|
|
|
|
- /* Readouts valid? */
|
|
|
- if (vbl > 0)
|
|
|
- ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
|
|
|
+ /*
|
|
|
+ * While in vblank, position will be negative
|
|
|
+ * counting up towards 0 at vbl_end. And outside
|
|
|
+ * vblank, position will be positive counting
|
|
|
+ * up since vbl_end.
|
|
|
+ */
|
|
|
+ if (position >= vbl_start)
|
|
|
+ position -= vbl_end;
|
|
|
+ else
|
|
|
+ position += vtotal - vbl_end;
|
|
|
+
|
|
|
+ if (IS_GEN2(dev) || IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
|
|
|
+ *vpos = position;
|
|
|
+ *hpos = 0;
|
|
|
+ } else {
|
|
|
+ *vpos = position / htotal;
|
|
|
+ *hpos = position - (*vpos * htotal);
|
|
|
+ }
|
|
|
|
|
|
/* In vblank? */
|
|
|
if (in_vbl)
|
|
@@ -665,7 +812,8 @@ 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)
|
|
|
+static bool intel_hpd_irq_event(struct drm_device *dev,
|
|
|
+ struct drm_connector *connector)
|
|
|
{
|
|
|
enum drm_connector_status old_status;
|
|
|
|
|
@@ -673,11 +821,16 @@ static int intel_hpd_irq_event(struct drm_device *dev, struct drm_connector *con
|
|
|
old_status = connector->status;
|
|
|
|
|
|
connector->status = connector->funcs->detect(connector, false);
|
|
|
- DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
|
|
|
+ if (old_status == connector->status)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
|
|
|
connector->base.id,
|
|
|
drm_get_connector_name(connector),
|
|
|
- old_status, connector->status);
|
|
|
- return (old_status != connector->status);
|
|
|
+ drm_get_connector_status_name(old_status),
|
|
|
+ drm_get_connector_status_name(connector->status));
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -801,7 +954,7 @@ static void notify_ring(struct drm_device *dev,
|
|
|
if (ring->obj == NULL)
|
|
|
return;
|
|
|
|
|
|
- trace_i915_gem_request_complete(ring, ring->get_seqno(ring, false));
|
|
|
+ trace_i915_gem_request_complete(ring);
|
|
|
|
|
|
wake_up_all(&ring->irq_queue);
|
|
|
i915_queue_hangcheck(dev);
|
|
@@ -812,7 +965,7 @@ static void gen6_pm_rps_work(struct work_struct *work)
|
|
|
drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
|
|
|
rps.work);
|
|
|
u32 pm_iir;
|
|
|
- u8 new_delay;
|
|
|
+ int new_delay, adj;
|
|
|
|
|
|
spin_lock_irq(&dev_priv->irq_lock);
|
|
|
pm_iir = dev_priv->rps.pm_iir;
|
|
@@ -829,40 +982,49 @@ static void gen6_pm_rps_work(struct work_struct *work)
|
|
|
|
|
|
mutex_lock(&dev_priv->rps.hw_lock);
|
|
|
|
|
|
+ adj = dev_priv->rps.last_adj;
|
|
|
if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
|
|
|
- new_delay = dev_priv->rps.cur_delay + 1;
|
|
|
+ if (adj > 0)
|
|
|
+ adj *= 2;
|
|
|
+ else
|
|
|
+ adj = 1;
|
|
|
+ new_delay = dev_priv->rps.cur_delay + adj;
|
|
|
|
|
|
/*
|
|
|
* For better performance, jump directly
|
|
|
* to RPe if we're below it.
|
|
|
*/
|
|
|
- if (IS_VALLEYVIEW(dev_priv->dev) &&
|
|
|
- dev_priv->rps.cur_delay < dev_priv->rps.rpe_delay)
|
|
|
+ if (new_delay < dev_priv->rps.rpe_delay)
|
|
|
+ new_delay = dev_priv->rps.rpe_delay;
|
|
|
+ } else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
|
|
|
+ if (dev_priv->rps.cur_delay > dev_priv->rps.rpe_delay)
|
|
|
new_delay = dev_priv->rps.rpe_delay;
|
|
|
- } else
|
|
|
- new_delay = dev_priv->rps.cur_delay - 1;
|
|
|
+ else
|
|
|
+ new_delay = dev_priv->rps.min_delay;
|
|
|
+ adj = 0;
|
|
|
+ } else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) {
|
|
|
+ if (adj < 0)
|
|
|
+ adj *= 2;
|
|
|
+ else
|
|
|
+ adj = -1;
|
|
|
+ new_delay = dev_priv->rps.cur_delay + adj;
|
|
|
+ } else { /* unknown event */
|
|
|
+ new_delay = dev_priv->rps.cur_delay;
|
|
|
+ }
|
|
|
|
|
|
/* sysfs frequency interfaces may have snuck in while servicing the
|
|
|
* interrupt
|
|
|
*/
|
|
|
- if (new_delay >= dev_priv->rps.min_delay &&
|
|
|
- new_delay <= dev_priv->rps.max_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));
|
|
|
- }
|
|
|
+ if (new_delay < (int)dev_priv->rps.min_delay)
|
|
|
+ new_delay = dev_priv->rps.min_delay;
|
|
|
+ if (new_delay > (int)dev_priv->rps.max_delay)
|
|
|
+ new_delay = dev_priv->rps.max_delay;
|
|
|
+ dev_priv->rps.last_adj = new_delay - dev_priv->rps.cur_delay;
|
|
|
+
|
|
|
+ if (IS_VALLEYVIEW(dev_priv->dev))
|
|
|
+ valleyview_set_rps(dev_priv->dev, new_delay);
|
|
|
+ else
|
|
|
+ gen6_set_rps(dev_priv->dev, new_delay);
|
|
|
|
|
|
mutex_unlock(&dev_priv->rps.hw_lock);
|
|
|
}
|
|
@@ -882,9 +1044,10 @@ static void ivybridge_parity_work(struct work_struct *work)
|
|
|
drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
|
|
|
l3_parity.error_work);
|
|
|
u32 error_status, row, bank, subbank;
|
|
|
- char *parity_event[5];
|
|
|
+ char *parity_event[6];
|
|
|
uint32_t misccpctl;
|
|
|
unsigned long flags;
|
|
|
+ uint8_t slice = 0;
|
|
|
|
|
|
/* We must turn off DOP level clock gating to access the L3 registers.
|
|
|
* In order to prevent a get/put style interface, acquire struct mutex
|
|
@@ -892,55 +1055,81 @@ static void ivybridge_parity_work(struct work_struct *work)
|
|
|
*/
|
|
|
mutex_lock(&dev_priv->dev->struct_mutex);
|
|
|
|
|
|
+ /* If we've screwed up tracking, just let the interrupt fire again */
|
|
|
+ if (WARN_ON(!dev_priv->l3_parity.which_slice))
|
|
|
+ goto out;
|
|
|
+
|
|
|
misccpctl = I915_READ(GEN7_MISCCPCTL);
|
|
|
I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
|
|
|
POSTING_READ(GEN7_MISCCPCTL);
|
|
|
|
|
|
- error_status = I915_READ(GEN7_L3CDERRST1);
|
|
|
- row = GEN7_PARITY_ERROR_ROW(error_status);
|
|
|
- bank = GEN7_PARITY_ERROR_BANK(error_status);
|
|
|
- subbank = GEN7_PARITY_ERROR_SUBBANK(error_status);
|
|
|
+ while ((slice = ffs(dev_priv->l3_parity.which_slice)) != 0) {
|
|
|
+ u32 reg;
|
|
|
|
|
|
- I915_WRITE(GEN7_L3CDERRST1, GEN7_PARITY_ERROR_VALID |
|
|
|
- GEN7_L3CDERRST1_ENABLE);
|
|
|
- POSTING_READ(GEN7_L3CDERRST1);
|
|
|
+ slice--;
|
|
|
+ if (WARN_ON_ONCE(slice >= NUM_L3_SLICES(dev_priv->dev)))
|
|
|
+ break;
|
|
|
|
|
|
- I915_WRITE(GEN7_MISCCPCTL, misccpctl);
|
|
|
+ dev_priv->l3_parity.which_slice &= ~(1<<slice);
|
|
|
|
|
|
- spin_lock_irqsave(&dev_priv->irq_lock, flags);
|
|
|
- ilk_enable_gt_irq(dev_priv, GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
|
|
|
- spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
|
|
|
+ reg = GEN7_L3CDERRST1 + (slice * 0x200);
|
|
|
|
|
|
- mutex_unlock(&dev_priv->dev->struct_mutex);
|
|
|
+ error_status = I915_READ(reg);
|
|
|
+ row = GEN7_PARITY_ERROR_ROW(error_status);
|
|
|
+ bank = GEN7_PARITY_ERROR_BANK(error_status);
|
|
|
+ subbank = GEN7_PARITY_ERROR_SUBBANK(error_status);
|
|
|
|
|
|
- parity_event[0] = I915_L3_PARITY_UEVENT "=1";
|
|
|
- parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
|
|
|
- parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
|
|
|
- parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
|
|
|
- parity_event[4] = NULL;
|
|
|
+ I915_WRITE(reg, GEN7_PARITY_ERROR_VALID | GEN7_L3CDERRST1_ENABLE);
|
|
|
+ POSTING_READ(reg);
|
|
|
+
|
|
|
+ parity_event[0] = I915_L3_PARITY_UEVENT "=1";
|
|
|
+ parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row);
|
|
|
+ parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank);
|
|
|
+ parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank);
|
|
|
+ parity_event[4] = kasprintf(GFP_KERNEL, "SLICE=%d", slice);
|
|
|
+ parity_event[5] = NULL;
|
|
|
+
|
|
|
+ kobject_uevent_env(&dev_priv->dev->primary->kdev->kobj,
|
|
|
+ KOBJ_CHANGE, parity_event);
|
|
|
+
|
|
|
+ DRM_DEBUG("Parity error: Slice = %d, Row = %d, Bank = %d, Sub bank = %d.\n",
|
|
|
+ slice, row, bank, subbank);
|
|
|
+
|
|
|
+ kfree(parity_event[4]);
|
|
|
+ kfree(parity_event[3]);
|
|
|
+ kfree(parity_event[2]);
|
|
|
+ kfree(parity_event[1]);
|
|
|
+ }
|
|
|
|
|
|
- kobject_uevent_env(&dev_priv->dev->primary->kdev.kobj,
|
|
|
- KOBJ_CHANGE, parity_event);
|
|
|
+ I915_WRITE(GEN7_MISCCPCTL, misccpctl);
|
|
|
|
|
|
- DRM_DEBUG("Parity error: Row = %d, Bank = %d, Sub bank = %d.\n",
|
|
|
- row, bank, subbank);
|
|
|
+out:
|
|
|
+ WARN_ON(dev_priv->l3_parity.which_slice);
|
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, flags);
|
|
|
+ ilk_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv->dev));
|
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
|
|
|
|
|
|
- kfree(parity_event[3]);
|
|
|
- kfree(parity_event[2]);
|
|
|
- kfree(parity_event[1]);
|
|
|
+ mutex_unlock(&dev_priv->dev->struct_mutex);
|
|
|
}
|
|
|
|
|
|
-static void ivybridge_parity_error_irq_handler(struct drm_device *dev)
|
|
|
+static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
|
|
|
{
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
|
|
|
- if (!HAS_L3_GPU_CACHE(dev))
|
|
|
+ if (!HAS_L3_DPF(dev))
|
|
|
return;
|
|
|
|
|
|
spin_lock(&dev_priv->irq_lock);
|
|
|
- ilk_disable_gt_irq(dev_priv, GT_RENDER_L3_PARITY_ERROR_INTERRUPT);
|
|
|
+ ilk_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev));
|
|
|
spin_unlock(&dev_priv->irq_lock);
|
|
|
|
|
|
+ iir &= GT_PARITY_ERROR(dev);
|
|
|
+ if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1)
|
|
|
+ dev_priv->l3_parity.which_slice |= 1 << 1;
|
|
|
+
|
|
|
+ if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT)
|
|
|
+ dev_priv->l3_parity.which_slice |= 1 << 0;
|
|
|
+
|
|
|
queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
|
|
|
}
|
|
|
|
|
@@ -975,8 +1164,58 @@ static void snb_gt_irq_handler(struct drm_device *dev,
|
|
|
i915_handle_error(dev, false);
|
|
|
}
|
|
|
|
|
|
- if (gt_iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT)
|
|
|
- ivybridge_parity_error_irq_handler(dev);
|
|
|
+ if (gt_iir & GT_PARITY_ERROR(dev))
|
|
|
+ ivybridge_parity_error_irq_handler(dev, gt_iir);
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
|
|
|
+ struct drm_i915_private *dev_priv,
|
|
|
+ u32 master_ctl)
|
|
|
+{
|
|
|
+ u32 rcs, bcs, vcs;
|
|
|
+ uint32_t tmp = 0;
|
|
|
+ irqreturn_t ret = IRQ_NONE;
|
|
|
+
|
|
|
+ if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
|
|
|
+ tmp = I915_READ(GEN8_GT_IIR(0));
|
|
|
+ if (tmp) {
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ rcs = tmp >> GEN8_RCS_IRQ_SHIFT;
|
|
|
+ bcs = tmp >> GEN8_BCS_IRQ_SHIFT;
|
|
|
+ if (rcs & GT_RENDER_USER_INTERRUPT)
|
|
|
+ notify_ring(dev, &dev_priv->ring[RCS]);
|
|
|
+ if (bcs & GT_RENDER_USER_INTERRUPT)
|
|
|
+ notify_ring(dev, &dev_priv->ring[BCS]);
|
|
|
+ I915_WRITE(GEN8_GT_IIR(0), tmp);
|
|
|
+ } else
|
|
|
+ DRM_ERROR("The master control interrupt lied (GT0)!\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (master_ctl & GEN8_GT_VCS1_IRQ) {
|
|
|
+ tmp = I915_READ(GEN8_GT_IIR(1));
|
|
|
+ if (tmp) {
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ vcs = tmp >> GEN8_VCS1_IRQ_SHIFT;
|
|
|
+ if (vcs & GT_RENDER_USER_INTERRUPT)
|
|
|
+ notify_ring(dev, &dev_priv->ring[VCS]);
|
|
|
+ I915_WRITE(GEN8_GT_IIR(1), tmp);
|
|
|
+ } else
|
|
|
+ DRM_ERROR("The master control interrupt lied (GT1)!\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (master_ctl & GEN8_GT_VECS_IRQ) {
|
|
|
+ tmp = I915_READ(GEN8_GT_IIR(3));
|
|
|
+ if (tmp) {
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ vcs = tmp >> GEN8_VECS_IRQ_SHIFT;
|
|
|
+ if (vcs & GT_RENDER_USER_INTERRUPT)
|
|
|
+ notify_ring(dev, &dev_priv->ring[VECS]);
|
|
|
+ I915_WRITE(GEN8_GT_IIR(3), tmp);
|
|
|
+ } else
|
|
|
+ DRM_ERROR("The master control interrupt lied (GT3)!\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
#define HPD_STORM_DETECT_PERIOD 1000
|
|
@@ -1050,6 +1289,102 @@ static void dp_aux_irq_handler(struct drm_device *dev)
|
|
|
wake_up_all(&dev_priv->gmbus_wait_queue);
|
|
|
}
|
|
|
|
|
|
+#if defined(CONFIG_DEBUG_FS)
|
|
|
+static void display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe,
|
|
|
+ uint32_t crc0, uint32_t crc1,
|
|
|
+ uint32_t crc2, uint32_t crc3,
|
|
|
+ uint32_t crc4)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
|
|
|
+ struct intel_pipe_crc_entry *entry;
|
|
|
+ int head, tail;
|
|
|
+
|
|
|
+ spin_lock(&pipe_crc->lock);
|
|
|
+
|
|
|
+ if (!pipe_crc->entries) {
|
|
|
+ spin_unlock(&pipe_crc->lock);
|
|
|
+ DRM_ERROR("spurious interrupt\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ head = pipe_crc->head;
|
|
|
+ tail = pipe_crc->tail;
|
|
|
+
|
|
|
+ if (CIRC_SPACE(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) {
|
|
|
+ spin_unlock(&pipe_crc->lock);
|
|
|
+ DRM_ERROR("CRC buffer overflowing\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ entry = &pipe_crc->entries[head];
|
|
|
+
|
|
|
+ entry->frame = dev->driver->get_vblank_counter(dev, pipe);
|
|
|
+ entry->crc[0] = crc0;
|
|
|
+ entry->crc[1] = crc1;
|
|
|
+ entry->crc[2] = crc2;
|
|
|
+ entry->crc[3] = crc3;
|
|
|
+ entry->crc[4] = crc4;
|
|
|
+
|
|
|
+ head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1);
|
|
|
+ pipe_crc->head = head;
|
|
|
+
|
|
|
+ spin_unlock(&pipe_crc->lock);
|
|
|
+
|
|
|
+ wake_up_interruptible(&pipe_crc->wq);
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline void
|
|
|
+display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe,
|
|
|
+ uint32_t crc0, uint32_t crc1,
|
|
|
+ uint32_t crc2, uint32_t crc3,
|
|
|
+ uint32_t crc4) {}
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+static void hsw_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+
|
|
|
+ display_pipe_crc_irq_handler(dev, pipe,
|
|
|
+ I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
|
|
|
+ 0, 0, 0, 0);
|
|
|
+}
|
|
|
+
|
|
|
+static void ivb_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+
|
|
|
+ display_pipe_crc_irq_handler(dev, pipe,
|
|
|
+ I915_READ(PIPE_CRC_RES_1_IVB(pipe)),
|
|
|
+ I915_READ(PIPE_CRC_RES_2_IVB(pipe)),
|
|
|
+ I915_READ(PIPE_CRC_RES_3_IVB(pipe)),
|
|
|
+ I915_READ(PIPE_CRC_RES_4_IVB(pipe)),
|
|
|
+ I915_READ(PIPE_CRC_RES_5_IVB(pipe)));
|
|
|
+}
|
|
|
+
|
|
|
+static void i9xx_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ uint32_t res1, res2;
|
|
|
+
|
|
|
+ if (INTEL_INFO(dev)->gen >= 3)
|
|
|
+ res1 = I915_READ(PIPE_CRC_RES_RES1_I915(pipe));
|
|
|
+ else
|
|
|
+ res1 = 0;
|
|
|
+
|
|
|
+ if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
|
|
|
+ res2 = I915_READ(PIPE_CRC_RES_RES2_G4X(pipe));
|
|
|
+ else
|
|
|
+ res2 = 0;
|
|
|
+
|
|
|
+ display_pipe_crc_irq_handler(dev, pipe,
|
|
|
+ I915_READ(PIPE_CRC_RES_RED(pipe)),
|
|
|
+ I915_READ(PIPE_CRC_RES_GREEN(pipe)),
|
|
|
+ I915_READ(PIPE_CRC_RES_BLUE(pipe)),
|
|
|
+ res1, res2);
|
|
|
+}
|
|
|
+
|
|
|
/* The RPS events need forcewake, so we add them to a work queue and mask their
|
|
|
* IMR bits until the work is done. Other interrupts can be processed without
|
|
|
* the work queue. */
|
|
@@ -1117,13 +1452,16 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
|
|
for_each_pipe(pipe) {
|
|
|
- if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
|
|
|
+ if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
|
|
|
drm_handle_vblank(dev, pipe);
|
|
|
|
|
|
if (pipe_stats[pipe] & PLANE_FLIPDONE_INT_STATUS_VLV) {
|
|
|
intel_prepare_page_flip(dev, pipe);
|
|
|
intel_finish_page_flip(dev, pipe);
|
|
|
}
|
|
|
+
|
|
|
+ if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
|
|
+ i9xx_pipe_crc_irq_handler(dev, pipe);
|
|
|
}
|
|
|
|
|
|
/* Consume port. Then clear IIR or we'll miss events */
|
|
@@ -1212,21 +1550,26 @@ 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);
|
|
|
+ enum pipe pipe;
|
|
|
|
|
|
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");
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) {
|
|
|
+ if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
|
|
|
+ false))
|
|
|
+ DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
|
|
|
+ pipe_name(pipe));
|
|
|
+ }
|
|
|
|
|
|
- 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");
|
|
|
+ if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) {
|
|
|
+ if (IS_IVYBRIDGE(dev))
|
|
|
+ ivb_pipe_crc_irq_handler(dev, pipe);
|
|
|
+ else
|
|
|
+ hsw_pipe_crc_irq_handler(dev, pipe);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
I915_WRITE(GEN7_ERR_INT, err_int);
|
|
|
}
|
|
@@ -1297,6 +1640,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
|
|
|
static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ enum pipe pipe;
|
|
|
|
|
|
if (de_iir & DE_AUX_CHANNEL_A)
|
|
|
dp_aux_irq_handler(dev);
|
|
@@ -1304,31 +1648,26 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
|
|
|
if (de_iir & DE_GSE)
|
|
|
intel_opregion_asle_intr(dev);
|
|
|
|
|
|
- if (de_iir & DE_PIPEA_VBLANK)
|
|
|
- drm_handle_vblank(dev, 0);
|
|
|
-
|
|
|
- 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");
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ if (de_iir & DE_PIPE_VBLANK(pipe))
|
|
|
+ drm_handle_vblank(dev, pipe);
|
|
|
|
|
|
- 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_PIPE_FIFO_UNDERRUN(pipe))
|
|
|
+ if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
|
|
|
+ DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
|
|
|
+ pipe_name(pipe));
|
|
|
|
|
|
- if (de_iir & DE_PLANEA_FLIP_DONE) {
|
|
|
- intel_prepare_page_flip(dev, 0);
|
|
|
- intel_finish_page_flip_plane(dev, 0);
|
|
|
- }
|
|
|
+ if (de_iir & DE_PIPE_CRC_DONE(pipe))
|
|
|
+ i9xx_pipe_crc_irq_handler(dev, pipe);
|
|
|
|
|
|
- if (de_iir & DE_PLANEB_FLIP_DONE) {
|
|
|
- intel_prepare_page_flip(dev, 1);
|
|
|
- intel_finish_page_flip_plane(dev, 1);
|
|
|
+ /* plane/pipes map 1:1 on ilk+ */
|
|
|
+ if (de_iir & DE_PLANE_FLIP_DONE(pipe)) {
|
|
|
+ intel_prepare_page_flip(dev, pipe);
|
|
|
+ intel_finish_page_flip_plane(dev, pipe);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* check event from PCH */
|
|
@@ -1351,7 +1690,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
|
|
|
static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
- int i;
|
|
|
+ enum pipe i;
|
|
|
|
|
|
if (de_iir & DE_ERR_INT_IVB)
|
|
|
ivb_err_int_handler(dev);
|
|
@@ -1362,10 +1701,12 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
|
|
|
if (de_iir & DE_GSE_IVB)
|
|
|
intel_opregion_asle_intr(dev);
|
|
|
|
|
|
- for (i = 0; i < 3; i++) {
|
|
|
- if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
|
|
|
+ for_each_pipe(i) {
|
|
|
+ if (de_iir & (DE_PIPE_VBLANK_IVB(i)))
|
|
|
drm_handle_vblank(dev, i);
|
|
|
- if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
|
|
|
+
|
|
|
+ /* plane/pipes map 1:1 on ilk+ */
|
|
|
+ if (de_iir & DE_PLANE_FLIP_DONE_IVB(i)) {
|
|
|
intel_prepare_page_flip(dev, i);
|
|
|
intel_finish_page_flip_plane(dev, i);
|
|
|
}
|
|
@@ -1388,7 +1729,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
u32 de_iir, gt_iir, de_ier, sde_ier = 0;
|
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
- bool err_int_reenable = false;
|
|
|
|
|
|
atomic_inc(&dev_priv->irq_received);
|
|
|
|
|
@@ -1412,17 +1752,6 @@ static irqreturn_t ironlake_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)) {
|
|
|
- spin_lock(&dev_priv->irq_lock);
|
|
|
- err_int_reenable = ~dev_priv->irq_mask & DE_ERR_INT_IVB;
|
|
|
- if (err_int_reenable)
|
|
|
- ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
|
|
|
- spin_unlock(&dev_priv->irq_lock);
|
|
|
- }
|
|
|
-
|
|
|
gt_iir = I915_READ(GTIIR);
|
|
|
if (gt_iir) {
|
|
|
if (INTEL_INFO(dev)->gen >= 6)
|
|
@@ -1452,13 +1781,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (err_int_reenable) {
|
|
|
- spin_lock(&dev_priv->irq_lock);
|
|
|
- if (ivb_can_enable_err_int(dev))
|
|
|
- ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
|
|
|
- spin_unlock(&dev_priv->irq_lock);
|
|
|
- }
|
|
|
-
|
|
|
I915_WRITE(DEIER, de_ier);
|
|
|
POSTING_READ(DEIER);
|
|
|
if (!HAS_PCH_NOP(dev)) {
|
|
@@ -1469,6 +1791,117 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static irqreturn_t gen8_irq_handler(int irq, void *arg)
|
|
|
+{
|
|
|
+ struct drm_device *dev = arg;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ u32 master_ctl;
|
|
|
+ irqreturn_t ret = IRQ_NONE;
|
|
|
+ uint32_t tmp = 0;
|
|
|
+ enum pipe pipe;
|
|
|
+
|
|
|
+ atomic_inc(&dev_priv->irq_received);
|
|
|
+
|
|
|
+ master_ctl = I915_READ(GEN8_MASTER_IRQ);
|
|
|
+ master_ctl &= ~GEN8_MASTER_IRQ_CONTROL;
|
|
|
+ if (!master_ctl)
|
|
|
+ return IRQ_NONE;
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
|
|
|
+ POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+ ret = gen8_gt_irq_handler(dev, dev_priv, master_ctl);
|
|
|
+
|
|
|
+ if (master_ctl & GEN8_DE_MISC_IRQ) {
|
|
|
+ tmp = I915_READ(GEN8_DE_MISC_IIR);
|
|
|
+ if (tmp & GEN8_DE_MISC_GSE)
|
|
|
+ intel_opregion_asle_intr(dev);
|
|
|
+ else if (tmp)
|
|
|
+ DRM_ERROR("Unexpected DE Misc interrupt\n");
|
|
|
+ else
|
|
|
+ DRM_ERROR("The master control interrupt lied (DE MISC)!\n");
|
|
|
+
|
|
|
+ if (tmp) {
|
|
|
+ I915_WRITE(GEN8_DE_MISC_IIR, tmp);
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (master_ctl & GEN8_DE_PORT_IRQ) {
|
|
|
+ tmp = I915_READ(GEN8_DE_PORT_IIR);
|
|
|
+ if (tmp & GEN8_AUX_CHANNEL_A)
|
|
|
+ dp_aux_irq_handler(dev);
|
|
|
+ else if (tmp)
|
|
|
+ DRM_ERROR("Unexpected DE Port interrupt\n");
|
|
|
+ else
|
|
|
+ DRM_ERROR("The master control interrupt lied (DE PORT)!\n");
|
|
|
+
|
|
|
+ if (tmp) {
|
|
|
+ I915_WRITE(GEN8_DE_PORT_IIR, tmp);
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ uint32_t pipe_iir;
|
|
|
+
|
|
|
+ if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
|
|
|
+ if (pipe_iir & GEN8_PIPE_VBLANK)
|
|
|
+ drm_handle_vblank(dev, pipe);
|
|
|
+
|
|
|
+ if (pipe_iir & GEN8_PIPE_FLIP_DONE) {
|
|
|
+ intel_prepare_page_flip(dev, pipe);
|
|
|
+ intel_finish_page_flip_plane(dev, pipe);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pipe_iir & GEN8_PIPE_CDCLK_CRC_DONE)
|
|
|
+ hsw_pipe_crc_irq_handler(dev, pipe);
|
|
|
+
|
|
|
+ if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) {
|
|
|
+ if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
|
|
|
+ false))
|
|
|
+ DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n",
|
|
|
+ pipe_name(pipe));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) {
|
|
|
+ DRM_ERROR("Fault errors on pipe %c\n: 0x%08x",
|
|
|
+ pipe_name(pipe),
|
|
|
+ pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pipe_iir) {
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir);
|
|
|
+ } else
|
|
|
+ DRM_ERROR("The master control interrupt lied (DE PIPE)!\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!HAS_PCH_NOP(dev) && master_ctl & GEN8_DE_PCH_IRQ) {
|
|
|
+ /*
|
|
|
+ * FIXME(BDW): Assume for now that the new interrupt handling
|
|
|
+ * scheme also closed the SDE interrupt handling race we've seen
|
|
|
+ * on older pch-split platforms. But this needs testing.
|
|
|
+ */
|
|
|
+ u32 pch_iir = I915_READ(SDEIIR);
|
|
|
+
|
|
|
+ cpt_irq_handler(dev, pch_iir);
|
|
|
+
|
|
|
+ if (pch_iir) {
|
|
|
+ I915_WRITE(SDEIIR, pch_iir);
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
|
|
|
+ POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static void i915_error_wake_up(struct drm_i915_private *dev_priv,
|
|
|
bool reset_completed)
|
|
|
{
|
|
@@ -1516,7 +1949,7 @@ static void i915_error_work_func(struct work_struct *work)
|
|
|
char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
|
|
|
int ret;
|
|
|
|
|
|
- kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
|
|
|
+ kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, error_event);
|
|
|
|
|
|
/*
|
|
|
* Note that there's only one work item which does gpu resets, so we
|
|
@@ -1530,7 +1963,7 @@ static void i915_error_work_func(struct work_struct *work)
|
|
|
*/
|
|
|
if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) {
|
|
|
DRM_DEBUG_DRIVER("resetting chip\n");
|
|
|
- kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE,
|
|
|
+ kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
|
|
|
reset_event);
|
|
|
|
|
|
/*
|
|
@@ -1557,7 +1990,7 @@ static void i915_error_work_func(struct work_struct *work)
|
|
|
smp_mb__before_atomic_inc();
|
|
|
atomic_inc(&dev_priv->gpu_error.reset_counter);
|
|
|
|
|
|
- kobject_uevent_env(&dev->primary->kdev.kobj,
|
|
|
+ kobject_uevent_env(&dev->primary->kdev->kobj,
|
|
|
KOBJ_CHANGE, reset_done_event);
|
|
|
} else {
|
|
|
atomic_set(&error->reset_counter, I915_WEDGED);
|
|
@@ -1787,7 +2220,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
unsigned long irqflags;
|
|
|
uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
|
|
|
- DE_PIPE_VBLANK_ILK(pipe);
|
|
|
+ DE_PIPE_VBLANK(pipe);
|
|
|
|
|
|
if (!i915_pipe_enabled(dev, pipe))
|
|
|
return -EINVAL;
|
|
@@ -1810,7 +2243,7 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe)
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
imr = I915_READ(VLV_IMR);
|
|
|
- if (pipe == 0)
|
|
|
+ if (pipe == PIPE_A)
|
|
|
imr &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
|
|
|
else
|
|
|
imr &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
|
|
@@ -1822,6 +2255,22 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int gen8_enable_vblank(struct drm_device *dev, int pipe)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ unsigned long irqflags;
|
|
|
+
|
|
|
+ if (!i915_pipe_enabled(dev, pipe))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
+ dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_VBLANK;
|
|
|
+ I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
|
|
|
+ POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
|
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* Called from drm generic code, passed 'crtc' which
|
|
|
* we use as a pipe index
|
|
|
*/
|
|
@@ -1845,7 +2294,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
unsigned long irqflags;
|
|
|
uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) :
|
|
|
- DE_PIPE_VBLANK_ILK(pipe);
|
|
|
+ DE_PIPE_VBLANK(pipe);
|
|
|
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
ironlake_disable_display_irq(dev_priv, bit);
|
|
@@ -1862,7 +2311,7 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe)
|
|
|
i915_disable_pipestat(dev_priv, pipe,
|
|
|
PIPE_START_VBLANK_INTERRUPT_ENABLE);
|
|
|
imr = I915_READ(VLV_IMR);
|
|
|
- if (pipe == 0)
|
|
|
+ if (pipe == PIPE_A)
|
|
|
imr |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
|
|
|
else
|
|
|
imr |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
|
|
@@ -1870,6 +2319,21 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe)
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
}
|
|
|
|
|
|
+static void gen8_disable_vblank(struct drm_device *dev, int pipe)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ unsigned long irqflags;
|
|
|
+
|
|
|
+ if (!i915_pipe_enabled(dev, pipe))
|
|
|
+ return;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
+ dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_VBLANK;
|
|
|
+ I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
|
|
|
+ POSTING_READ(GEN8_DE_PIPE_IMR(pipe));
|
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
+}
|
|
|
+
|
|
|
static u32
|
|
|
ring_last_seqno(struct intel_ring_buffer *ring)
|
|
|
{
|
|
@@ -1965,6 +2429,7 @@ ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
|
|
|
if (tmp & RING_WAIT) {
|
|
|
DRM_ERROR("Kicking stuck wait on %s\n",
|
|
|
ring->name);
|
|
|
+ i915_handle_error(dev, false);
|
|
|
I915_WRITE_CTL(ring, tmp);
|
|
|
return HANGCHECK_KICK;
|
|
|
}
|
|
@@ -1976,6 +2441,7 @@ ring_stuck(struct intel_ring_buffer *ring, u32 acthd)
|
|
|
case 1:
|
|
|
DRM_ERROR("Kicking stuck semaphore on %s\n",
|
|
|
ring->name);
|
|
|
+ i915_handle_error(dev, false);
|
|
|
I915_WRITE_CTL(ring, tmp);
|
|
|
return HANGCHECK_KICK;
|
|
|
case 0:
|
|
@@ -2021,12 +2487,21 @@ static void i915_hangcheck_elapsed(unsigned long data)
|
|
|
|
|
|
if (ring->hangcheck.seqno == seqno) {
|
|
|
if (ring_idle(ring, seqno)) {
|
|
|
+ ring->hangcheck.action = HANGCHECK_IDLE;
|
|
|
+
|
|
|
if (waitqueue_active(&ring->irq_queue)) {
|
|
|
/* Issue a wake-up to catch stuck h/w. */
|
|
|
- DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
|
|
|
- ring->name);
|
|
|
- wake_up_all(&ring->irq_queue);
|
|
|
- ring->hangcheck.score += HUNG;
|
|
|
+ if (!test_and_set_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings)) {
|
|
|
+ if (!(dev_priv->gpu_error.test_irq_rings & intel_ring_flag(ring)))
|
|
|
+ DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
|
|
|
+ ring->name);
|
|
|
+ else
|
|
|
+ DRM_INFO("Fake missed irq on %s\n",
|
|
|
+ ring->name);
|
|
|
+ wake_up_all(&ring->irq_queue);
|
|
|
+ }
|
|
|
+ /* Safeguard against driver failure */
|
|
|
+ ring->hangcheck.score += BUSY;
|
|
|
} else
|
|
|
busy = false;
|
|
|
} else {
|
|
@@ -2049,6 +2524,7 @@ static void i915_hangcheck_elapsed(unsigned long data)
|
|
|
acthd);
|
|
|
|
|
|
switch (ring->hangcheck.action) {
|
|
|
+ case HANGCHECK_IDLE:
|
|
|
case HANGCHECK_WAIT:
|
|
|
break;
|
|
|
case HANGCHECK_ACTIVE:
|
|
@@ -2064,6 +2540,8 @@ static void i915_hangcheck_elapsed(unsigned long data)
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
+ ring->hangcheck.action = HANGCHECK_ACTIVE;
|
|
|
+
|
|
|
/* Gradually reduce the count so that we catch DoS
|
|
|
* attempts across multiple batches.
|
|
|
*/
|
|
@@ -2190,6 +2668,53 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
|
|
|
POSTING_READ(VLV_IER);
|
|
|
}
|
|
|
|
|
|
+static void gen8_irq_preinstall(struct drm_device *dev)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ int pipe;
|
|
|
+
|
|
|
+ atomic_set(&dev_priv->irq_received, 0);
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
|
|
|
+ POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+ /* IIR can theoretically queue up two events. Be paranoid */
|
|
|
+#define GEN8_IRQ_INIT_NDX(type, which) do { \
|
|
|
+ I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
|
|
|
+ POSTING_READ(GEN8_##type##_IMR(which)); \
|
|
|
+ I915_WRITE(GEN8_##type##_IER(which), 0); \
|
|
|
+ I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
|
|
|
+ POSTING_READ(GEN8_##type##_IIR(which)); \
|
|
|
+ I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+#define GEN8_IRQ_INIT(type) do { \
|
|
|
+ I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
|
|
|
+ POSTING_READ(GEN8_##type##_IMR); \
|
|
|
+ I915_WRITE(GEN8_##type##_IER, 0); \
|
|
|
+ I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
|
|
|
+ POSTING_READ(GEN8_##type##_IIR); \
|
|
|
+ I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+ GEN8_IRQ_INIT_NDX(GT, 0);
|
|
|
+ GEN8_IRQ_INIT_NDX(GT, 1);
|
|
|
+ GEN8_IRQ_INIT_NDX(GT, 2);
|
|
|
+ GEN8_IRQ_INIT_NDX(GT, 3);
|
|
|
+
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ GEN8_IRQ_INIT_NDX(DE_PIPE, pipe);
|
|
|
+ }
|
|
|
+
|
|
|
+ GEN8_IRQ_INIT(DE_PORT);
|
|
|
+ GEN8_IRQ_INIT(DE_MISC);
|
|
|
+ GEN8_IRQ_INIT(PCU);
|
|
|
+#undef GEN8_IRQ_INIT
|
|
|
+#undef GEN8_IRQ_INIT_NDX
|
|
|
+
|
|
|
+ POSTING_READ(GEN8_PCU_IIR);
|
|
|
+}
|
|
|
+
|
|
|
static void ibx_hpd_irq_setup(struct drm_device *dev)
|
|
|
{
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
@@ -2254,10 +2779,10 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
|
|
|
pm_irqs = gt_irqs = 0;
|
|
|
|
|
|
dev_priv->gt_irq_mask = ~0;
|
|
|
- if (HAS_L3_GPU_CACHE(dev)) {
|
|
|
+ if (HAS_L3_DPF(dev)) {
|
|
|
/* L3 parity interrupt is always unmasked. */
|
|
|
- dev_priv->gt_irq_mask = ~GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
|
|
|
- gt_irqs |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
|
|
|
+ dev_priv->gt_irq_mask = ~GT_PARITY_ERROR(dev);
|
|
|
+ gt_irqs |= GT_PARITY_ERROR(dev);
|
|
|
}
|
|
|
|
|
|
gt_irqs |= GT_RENDER_USER_INTERRUPT;
|
|
@@ -2306,8 +2831,10 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
|
|
|
} else {
|
|
|
display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
|
|
|
DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
|
|
|
- DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
|
|
|
- DE_PIPEA_FIFO_UNDERRUN | DE_POISON);
|
|
|
+ DE_AUX_CHANNEL_A |
|
|
|
+ DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
|
|
|
+ DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE |
|
|
|
+ DE_POISON);
|
|
|
extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT;
|
|
|
}
|
|
|
|
|
@@ -2341,7 +2868,8 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
|
|
|
{
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
u32 enable_mask;
|
|
|
- u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
|
|
|
+ u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV |
|
|
|
+ PIPE_CRC_DONE_ENABLE;
|
|
|
unsigned long irqflags;
|
|
|
|
|
|
enable_mask = I915_DISPLAY_PORT_INTERRUPT;
|
|
@@ -2371,9 +2899,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
|
|
|
/* Interrupt setup is already guaranteed to be single-threaded, this is
|
|
|
* just to make the assert_spin_locked check happy. */
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
- i915_enable_pipestat(dev_priv, 0, pipestat_enable);
|
|
|
- i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
|
|
|
- i915_enable_pipestat(dev_priv, 1, pipestat_enable);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_A, pipestat_enable);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_B, pipestat_enable);
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
|
|
I915_WRITE(VLV_IIR, 0xffffffff);
|
|
@@ -2392,6 +2920,117 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* These are interrupts we'll toggle with the ring mask register */
|
|
|
+ uint32_t gt_interrupts[] = {
|
|
|
+ GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
|
|
|
+ GT_RENDER_L3_PARITY_ERROR_INTERRUPT |
|
|
|
+ GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
|
|
|
+ GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
|
|
|
+ GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT,
|
|
|
+ 0,
|
|
|
+ GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT
|
|
|
+ };
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) {
|
|
|
+ u32 tmp = I915_READ(GEN8_GT_IIR(i));
|
|
|
+ if (tmp)
|
|
|
+ DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
|
|
|
+ i, tmp);
|
|
|
+ I915_WRITE(GEN8_GT_IMR(i), ~gt_interrupts[i]);
|
|
|
+ I915_WRITE(GEN8_GT_IER(i), gt_interrupts[i]);
|
|
|
+ }
|
|
|
+ POSTING_READ(GEN8_GT_IER(0));
|
|
|
+}
|
|
|
+
|
|
|
+static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
|
|
|
+{
|
|
|
+ struct drm_device *dev = dev_priv->dev;
|
|
|
+ uint32_t de_pipe_masked = GEN8_PIPE_FLIP_DONE |
|
|
|
+ GEN8_PIPE_CDCLK_CRC_DONE |
|
|
|
+ GEN8_PIPE_FIFO_UNDERRUN |
|
|
|
+ GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
|
|
|
+ uint32_t de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK;
|
|
|
+ int pipe;
|
|
|
+ dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_masked;
|
|
|
+ dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked;
|
|
|
+ dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked;
|
|
|
+
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ u32 tmp = I915_READ(GEN8_DE_PIPE_IIR(pipe));
|
|
|
+ if (tmp)
|
|
|
+ DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
|
|
|
+ pipe, tmp);
|
|
|
+ I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
|
|
|
+ I915_WRITE(GEN8_DE_PIPE_IER(pipe), de_pipe_enables);
|
|
|
+ }
|
|
|
+ POSTING_READ(GEN8_DE_PIPE_ISR(0));
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_DE_PORT_IMR, ~GEN8_AUX_CHANNEL_A);
|
|
|
+ I915_WRITE(GEN8_DE_PORT_IER, GEN8_AUX_CHANNEL_A);
|
|
|
+ POSTING_READ(GEN8_DE_PORT_IER);
|
|
|
+}
|
|
|
+
|
|
|
+static int gen8_irq_postinstall(struct drm_device *dev)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+
|
|
|
+ gen8_gt_irq_postinstall(dev_priv);
|
|
|
+ gen8_de_irq_postinstall(dev_priv);
|
|
|
+
|
|
|
+ ibx_irq_postinstall(dev);
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
|
|
|
+ POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void gen8_irq_uninstall(struct drm_device *dev)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ int pipe;
|
|
|
+
|
|
|
+ if (!dev_priv)
|
|
|
+ return;
|
|
|
+
|
|
|
+ atomic_set(&dev_priv->irq_received, 0);
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
|
|
|
+
|
|
|
+#define GEN8_IRQ_FINI_NDX(type, which) do { \
|
|
|
+ I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
|
|
|
+ I915_WRITE(GEN8_##type##_IER(which), 0); \
|
|
|
+ I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+#define GEN8_IRQ_FINI(type) do { \
|
|
|
+ I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
|
|
|
+ I915_WRITE(GEN8_##type##_IER, 0); \
|
|
|
+ I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+ GEN8_IRQ_FINI_NDX(GT, 0);
|
|
|
+ GEN8_IRQ_FINI_NDX(GT, 1);
|
|
|
+ GEN8_IRQ_FINI_NDX(GT, 2);
|
|
|
+ GEN8_IRQ_FINI_NDX(GT, 3);
|
|
|
+
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ GEN8_IRQ_FINI_NDX(DE_PIPE, pipe);
|
|
|
+ }
|
|
|
+
|
|
|
+ GEN8_IRQ_FINI(DE_PORT);
|
|
|
+ GEN8_IRQ_FINI(DE_MISC);
|
|
|
+ GEN8_IRQ_FINI(PCU);
|
|
|
+#undef GEN8_IRQ_FINI
|
|
|
+#undef GEN8_IRQ_FINI_NDX
|
|
|
+
|
|
|
+ POSTING_READ(GEN8_PCU_IIR);
|
|
|
+}
|
|
|
+
|
|
|
static void valleyview_irq_uninstall(struct drm_device *dev)
|
|
|
{
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
@@ -2464,6 +3103,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev)
|
|
|
static int i8xx_irq_postinstall(struct drm_device *dev)
|
|
|
{
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
+ unsigned long irqflags;
|
|
|
|
|
|
I915_WRITE16(EMR,
|
|
|
~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
|
|
@@ -2484,6 +3124,13 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
|
|
|
I915_USER_INTERRUPT);
|
|
|
POSTING_READ16(IER);
|
|
|
|
|
|
+ /* Interrupt setup is already guaranteed to be single-threaded, this is
|
|
|
+ * just to make the assert_spin_locked check happy. */
|
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
|
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -2570,13 +3217,14 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
|
|
|
if (iir & I915_USER_INTERRUPT)
|
|
|
notify_ring(dev, &dev_priv->ring[RCS]);
|
|
|
|
|
|
- if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS &&
|
|
|
- i8xx_handle_vblank(dev, 0, iir))
|
|
|
- flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(0);
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS &&
|
|
|
+ i8xx_handle_vblank(dev, pipe, iir))
|
|
|
+ flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe);
|
|
|
|
|
|
- if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS &&
|
|
|
- i8xx_handle_vblank(dev, 1, iir))
|
|
|
- flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(1);
|
|
|
+ if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
|
|
+ i9xx_pipe_crc_irq_handler(dev, pipe);
|
|
|
+ }
|
|
|
|
|
|
iir = new_iir;
|
|
|
}
|
|
@@ -2623,6 +3271,7 @@ static int i915_irq_postinstall(struct drm_device *dev)
|
|
|
{
|
|
|
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
|
|
u32 enable_mask;
|
|
|
+ unsigned long irqflags;
|
|
|
|
|
|
I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
|
|
|
|
|
@@ -2658,6 +3307,13 @@ static int i915_irq_postinstall(struct drm_device *dev)
|
|
|
|
|
|
i915_enable_asle_pipestat(dev);
|
|
|
|
|
|
+ /* Interrupt setup is already guaranteed to be single-threaded, this is
|
|
|
+ * just to make the assert_spin_locked check happy. */
|
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
|
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -2769,6 +3425,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
|
|
|
blc_event = true;
|
|
|
+
|
|
|
+ if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
|
|
+ i9xx_pipe_crc_irq_handler(dev, pipe);
|
|
|
}
|
|
|
|
|
|
if (blc_event || (iir & I915_ASLE_INTERRUPT))
|
|
@@ -2867,7 +3526,9 @@ static int i965_irq_postinstall(struct drm_device *dev)
|
|
|
/* Interrupt setup is already guaranteed to be single-threaded, this is
|
|
|
* just to make the assert_spin_locked check happy. */
|
|
|
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
- i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE);
|
|
|
+ i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE);
|
|
|
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
|
|
|
/*
|
|
@@ -3013,6 +3674,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
|
|
|
|
|
|
if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
|
|
|
blc_event = true;
|
|
|
+
|
|
|
+ if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
|
|
|
+ i9xx_pipe_crc_irq_handler(dev, pipe);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -3122,18 +3786,21 @@ void intel_irq_init(struct drm_device *dev)
|
|
|
|
|
|
pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
|
|
|
|
|
|
- dev->driver->get_vblank_counter = i915_get_vblank_counter;
|
|
|
- dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
|
|
|
- if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
|
|
|
+ if (IS_GEN2(dev)) {
|
|
|
+ dev->max_vblank_count = 0;
|
|
|
+ dev->driver->get_vblank_counter = i8xx_get_vblank_counter;
|
|
|
+ } else if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
|
|
|
dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
|
|
|
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
|
|
|
+ } else {
|
|
|
+ dev->driver->get_vblank_counter = i915_get_vblank_counter;
|
|
|
+ dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
|
|
|
}
|
|
|
|
|
|
- if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
|
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
|
|
dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
|
|
|
- else
|
|
|
- dev->driver->get_vblank_timestamp = NULL;
|
|
|
- dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
|
|
|
+ dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
|
|
|
+ }
|
|
|
|
|
|
if (IS_VALLEYVIEW(dev)) {
|
|
|
dev->driver->irq_handler = valleyview_irq_handler;
|
|
@@ -3143,6 +3810,14 @@ void intel_irq_init(struct drm_device *dev)
|
|
|
dev->driver->enable_vblank = valleyview_enable_vblank;
|
|
|
dev->driver->disable_vblank = valleyview_disable_vblank;
|
|
|
dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
|
|
|
+ } else if (IS_GEN8(dev)) {
|
|
|
+ dev->driver->irq_handler = gen8_irq_handler;
|
|
|
+ dev->driver->irq_preinstall = gen8_irq_preinstall;
|
|
|
+ dev->driver->irq_postinstall = gen8_irq_postinstall;
|
|
|
+ dev->driver->irq_uninstall = gen8_irq_uninstall;
|
|
|
+ dev->driver->enable_vblank = gen8_enable_vblank;
|
|
|
+ dev->driver->disable_vblank = gen8_disable_vblank;
|
|
|
+ dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
|
|
|
} else if (HAS_PCH_SPLIT(dev)) {
|
|
|
dev->driver->irq_handler = ironlake_irq_handler;
|
|
|
dev->driver->irq_preinstall = ironlake_irq_preinstall;
|