|
@@ -1703,6 +1703,95 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static irqreturn_t cherryview_irq_handler(int irq, void *arg)
|
|
|
+{
|
|
|
+ struct drm_device *dev = (struct drm_device *) arg;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ u32 master_ctl, iir;
|
|
|
+ irqreturn_t ret = IRQ_NONE;
|
|
|
+ unsigned int pipes = 0;
|
|
|
+
|
|
|
+ master_ctl = I915_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
|
|
|
+
|
|
|
+ ret = gen8_gt_irq_handler(dev, dev_priv, master_ctl);
|
|
|
+
|
|
|
+ iir = I915_READ(VLV_IIR);
|
|
|
+
|
|
|
+ if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT))
|
|
|
+ pipes |= 1 << 0;
|
|
|
+ if (iir & (I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT))
|
|
|
+ pipes |= 1 << 1;
|
|
|
+ if (iir & (I915_DISPLAY_PIPE_C_VBLANK_INTERRUPT | I915_DISPLAY_PIPE_C_EVENT_INTERRUPT))
|
|
|
+ pipes |= 1 << 2;
|
|
|
+
|
|
|
+ if (pipes) {
|
|
|
+ u32 pipe_stats[I915_MAX_PIPES] = {};
|
|
|
+ unsigned long irqflags;
|
|
|
+ int pipe;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ unsigned int reg;
|
|
|
+
|
|
|
+ if (!(pipes & (1 << pipe)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ reg = PIPESTAT(pipe);
|
|
|
+ pipe_stats[pipe] = I915_READ(reg);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Clear the PIPE*STAT regs before the IIR
|
|
|
+ */
|
|
|
+ if (pipe_stats[pipe] & 0x8000ffff) {
|
|
|
+ if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
|
|
|
+ DRM_DEBUG_DRIVER("pipe %c underrun\n",
|
|
|
+ pipe_name(pipe));
|
|
|
+ I915_WRITE(reg, pipe_stats[pipe]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
+
|
|
|
+ for_each_pipe(pipe) {
|
|
|
+ if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
|
|
|
+ drm_handle_vblank(dev, pipe);
|
|
|
+
|
|
|
+ if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) {
|
|
|
+ intel_prepare_page_flip(dev, pipe);
|
|
|
+ intel_finish_page_flip(dev, pipe);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
|
|
|
+ gmbus_irq_handler(dev);
|
|
|
+
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Consume port. Then clear IIR or we'll miss events */
|
|
|
+ if (iir & I915_DISPLAY_PORT_INTERRUPT) {
|
|
|
+ u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
|
|
|
+
|
|
|
+ I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
|
|
|
+
|
|
|
+ DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
|
|
|
+ hotplug_status);
|
|
|
+ if (hotplug_status & HOTPLUG_INT_STATUS_I915)
|
|
|
+ queue_work(dev_priv->wq,
|
|
|
+ &dev_priv->hotplug_work);
|
|
|
+
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ I915_WRITE(VLV_IIR, iir);
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
|
|
|
+ POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
@@ -2974,6 +3063,37 @@ static void gen8_irq_preinstall(struct drm_device *dev)
|
|
|
gen8_irq_reset(dev);
|
|
|
}
|
|
|
|
|
|
+static void cherryview_irq_preinstall(struct drm_device *dev)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ int pipe;
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
|
|
|
+ POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+ GEN8_IRQ_RESET_NDX(GT, 0);
|
|
|
+ GEN8_IRQ_RESET_NDX(GT, 1);
|
|
|
+ GEN8_IRQ_RESET_NDX(GT, 2);
|
|
|
+ GEN8_IRQ_RESET_NDX(GT, 3);
|
|
|
+
|
|
|
+ GEN5_IRQ_RESET(GEN8_PCU_);
|
|
|
+
|
|
|
+ POSTING_READ(GEN8_PCU_IIR);
|
|
|
+
|
|
|
+ I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
|
|
|
+
|
|
|
+ I915_WRITE(PORT_HOTPLUG_EN, 0);
|
|
|
+ I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
|
|
|
+
|
|
|
+ for_each_pipe(pipe)
|
|
|
+ I915_WRITE(PIPESTAT(pipe), 0xffff);
|
|
|
+
|
|
|
+ I915_WRITE(VLV_IMR, 0xffffffff);
|
|
|
+ I915_WRITE(VLV_IER, 0x0);
|
|
|
+ I915_WRITE(VLV_IIR, 0xffffffff);
|
|
|
+ POSTING_READ(VLV_IIR);
|
|
|
+}
|
|
|
+
|
|
|
static void ibx_hpd_irq_setup(struct drm_device *dev)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
@@ -3291,6 +3411,50 @@ static int gen8_irq_postinstall(struct drm_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int cherryview_irq_postinstall(struct drm_device *dev)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ u32 enable_mask = I915_DISPLAY_PORT_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_C_EVENT_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_C_VBLANK_INTERRUPT;
|
|
|
+ u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
|
|
|
+ unsigned long irqflags;
|
|
|
+ int pipe;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Leave vblank interrupts masked initially. enable/disable will
|
|
|
+ * toggle them based on usage.
|
|
|
+ */
|
|
|
+ dev_priv->irq_mask = ~enable_mask |
|
|
|
+ I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT |
|
|
|
+ I915_DISPLAY_PIPE_C_VBLANK_INTERRUPT;
|
|
|
+
|
|
|
+ for_each_pipe(pipe)
|
|
|
+ I915_WRITE(PIPESTAT(pipe), 0xffff);
|
|
|
+
|
|
|
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
|
|
+ i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE);
|
|
|
+ for_each_pipe(pipe)
|
|
|
+ i915_enable_pipestat(dev_priv, pipe, pipestat_enable);
|
|
|
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
|
|
+
|
|
|
+ I915_WRITE(VLV_IIR, 0xffffffff);
|
|
|
+ I915_WRITE(VLV_IMR, dev_priv->irq_mask);
|
|
|
+ I915_WRITE(VLV_IER, enable_mask);
|
|
|
+
|
|
|
+ gen8_gt_irq_postinstall(dev_priv);
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, MASTER_INTERRUPT_ENABLE);
|
|
|
+ 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;
|
|
@@ -3336,6 +3500,57 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
|
|
|
POSTING_READ(VLV_IER);
|
|
|
}
|
|
|
|
|
|
+static void cherryview_irq_uninstall(struct drm_device *dev)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ int pipe;
|
|
|
+
|
|
|
+ if (!dev_priv)
|
|
|
+ return;
|
|
|
+
|
|
|
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
|
|
|
+ POSTING_READ(GEN8_MASTER_IRQ);
|
|
|
+
|
|
|
+#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); \
|
|
|
+ POSTING_READ(GEN8_##type##_IIR(which)); \
|
|
|
+ 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); \
|
|
|
+ POSTING_READ(GEN8_##type##_IIR); \
|
|
|
+ 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);
|
|
|
+
|
|
|
+ GEN8_IRQ_FINI(PCU);
|
|
|
+
|
|
|
+#undef GEN8_IRQ_FINI
|
|
|
+#undef GEN8_IRQ_FINI_NDX
|
|
|
+
|
|
|
+ I915_WRITE(PORT_HOTPLUG_EN, 0);
|
|
|
+ I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
|
|
|
+
|
|
|
+ for_each_pipe(pipe)
|
|
|
+ I915_WRITE(PIPESTAT(pipe), 0xffff);
|
|
|
+
|
|
|
+ I915_WRITE(VLV_IMR, 0xffffffff);
|
|
|
+ I915_WRITE(VLV_IER, 0x0);
|
|
|
+ I915_WRITE(VLV_IIR, 0xffffffff);
|
|
|
+ POSTING_READ(VLV_IIR);
|
|
|
+}
|
|
|
+
|
|
|
static void ironlake_irq_uninstall(struct drm_device *dev)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
@@ -4041,7 +4256,15 @@ void intel_irq_init(struct drm_device *dev)
|
|
|
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
|
|
|
}
|
|
|
|
|
|
- if (IS_VALLEYVIEW(dev)) {
|
|
|
+ if (IS_CHERRYVIEW(dev)) {
|
|
|
+ dev->driver->irq_handler = cherryview_irq_handler;
|
|
|
+ dev->driver->irq_preinstall = cherryview_irq_preinstall;
|
|
|
+ dev->driver->irq_postinstall = cherryview_irq_postinstall;
|
|
|
+ dev->driver->irq_uninstall = cherryview_irq_uninstall;
|
|
|
+ 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_VALLEYVIEW(dev)) {
|
|
|
dev->driver->irq_handler = valleyview_irq_handler;
|
|
|
dev->driver->irq_preinstall = valleyview_irq_preinstall;
|
|
|
dev->driver->irq_postinstall = valleyview_irq_postinstall;
|