jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
}
-static void gen6_pm_irq_handler(struct drm_device *dev)
+static void gen6_pm_rps_work(struct work_struct *work)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+ rps_work);
u8 new_delay = dev_priv->cur_delay;
- u32 pm_iir;
+ u32 pm_iir, pm_imr;
+
+ spin_lock_irq(&dev_priv->rps_lock);
+ pm_iir = dev_priv->pm_iir;
+ dev_priv->pm_iir = 0;
+ pm_imr = I915_READ(GEN6_PMIMR);
+ spin_unlock_irq(&dev_priv->rps_lock);
- pm_iir = I915_READ(GEN6_PMIIR);
if (!pm_iir)
return;
+ mutex_lock(&dev_priv->dev->struct_mutex);
if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
if (dev_priv->cur_delay != dev_priv->max_delay)
new_delay = dev_priv->cur_delay + 1;
if (new_delay > dev_priv->max_delay)
new_delay = dev_priv->max_delay;
} else if (pm_iir & (GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT)) {
+ gen6_gt_force_wake_get(dev_priv);
if (dev_priv->cur_delay != dev_priv->min_delay)
new_delay = dev_priv->cur_delay - 1;
if (new_delay < dev_priv->min_delay) {
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
I915_READ(GEN6_RP_INTERRUPT_LIMITS) & ~0x3f0000);
}
-
+ gen6_gt_force_wake_put(dev_priv);
}
- gen6_set_rps(dev, new_delay);
+ gen6_set_rps(dev_priv->dev, new_delay);
dev_priv->cur_delay = new_delay;
- I915_WRITE(GEN6_PMIIR, pm_iir);
+ /*
+ * rps_lock not held here because clearing is non-destructive. There is
+ * an *extremely* unlikely race with gen6_rps_enable() that is prevented
+ * by holding struct_mutex for the duration of the write.
+ */
+ I915_WRITE(GEN6_PMIMR, pm_imr & ~pm_iir);
+ mutex_unlock(&dev_priv->dev->struct_mutex);
}
static void pch_irq_handler(struct drm_device *dev)
DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
}
-static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
+irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
{
+ struct drm_device *dev = (struct drm_device *) arg;
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
int ret = IRQ_NONE;
u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir;
struct drm_i915_master_private *master_priv;
u32 bsd_usr_interrupt = GT_BSD_USER_INTERRUPT;
+ atomic_inc(&dev_priv->irq_received);
+
if (IS_GEN6(dev))
bsd_usr_interrupt = GT_GEN6_BSD_USER_INTERRUPT;
i915_handle_rps_change(dev);
}
- if (IS_GEN6(dev))
- gen6_pm_irq_handler(dev);
+ if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) {
+ /*
+ * IIR bits should never already be set because IMR should
+ * prevent an interrupt from being shown in IIR. The warning
+ * displays a case where we've unsafely cleared
+ * dev_priv->pm_iir. Although missing an interrupt of the same
+ * type is not a problem, it displays a problem in the logic.
+ *
+ * The mask bit in IMR is cleared by rps_work.
+ */
+ unsigned long flags;
+ spin_lock_irqsave(&dev_priv->rps_lock, flags);
+ WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
+ I915_WRITE(GEN6_PMIMR, pm_iir);
+ dev_priv->pm_iir |= pm_iir;
+ spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
+ queue_work(dev_priv->wq, &dev_priv->rps_work);
+ }
/* should clear PCH hotplug event before clear CPU irq */
I915_WRITE(SDEIIR, pch_iir);
I915_WRITE(GTIIR, gt_iir);
I915_WRITE(DEIIR, de_iir);
+ I915_WRITE(GEN6_PMIIR, pm_iir);
done:
I915_WRITE(DEIER, de_ier);
err->dirty = obj->dirty;
err->purgeable = obj->madv != I915_MADV_WILLNEED;
err->ring = obj->ring ? obj->ring->id : 0;
- err->agp_type = obj->agp_type == AGP_USER_CACHED_MEMORY;
+ err->cache_level = obj->cache_level;
if (++i == count)
break;
atomic_inc(&dev_priv->irq_received);
- if (HAS_PCH_SPLIT(dev))
- return ironlake_irq_handler(dev);
-
iir = I915_READ(IIR);
if (INTEL_INFO(dev)->gen >= 4)
/* drm_dma.h hooks
*/
-static void ironlake_irq_preinstall(struct drm_device *dev)
+void ironlake_irq_preinstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ atomic_set(&dev_priv->irq_received, 0);
+
+ INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+ INIT_WORK(&dev_priv->error_work, i915_error_work_func);
+
I915_WRITE(HWSTAM, 0xeffe);
/* XXX hotplug from PCH */
POSTING_READ(SDEIER);
}
-static int ironlake_irq_postinstall(struct drm_device *dev)
+int ironlake_irq_postinstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
/* enable kind of interrupts always enabled */
u32 render_irqs;
u32 hotplug_mask;
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue);
+ if (HAS_BSD(dev))
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue);
+ if (HAS_BLT(dev))
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue);
+
+ dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
dev_priv->irq_mask = ~display_mask;
/* should always can generate irq */
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
INIT_WORK(&dev_priv->error_work, i915_error_work_func);
-
- if (HAS_PCH_SPLIT(dev)) {
- ironlake_irq_preinstall(dev);
- return;
- }
+ INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work);
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
u32 error_mask;
- DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue);
- if (HAS_BSD(dev))
- DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue);
- if (HAS_BLT(dev))
- DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue);
-
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
- if (HAS_PCH_SPLIT(dev))
- return ironlake_irq_postinstall(dev);
-
/* Unmask the interrupts that we always want on. */
dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX;
return 0;
}
-static void ironlake_irq_uninstall(struct drm_device *dev)
+void ironlake_irq_uninstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+ if (!dev_priv)
+ return;
+
+ dev_priv->vblank_pipe = 0;
+
I915_WRITE(HWSTAM, 0xffffffff);
I915_WRITE(DEIMR, 0xffffffff);
dev_priv->vblank_pipe = 0;
- if (HAS_PCH_SPLIT(dev)) {
- ironlake_irq_uninstall(dev);
- return;
- }
-
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));