diff --git a/debian/changelog b/debian/changelog index e6f917ad6..318dfbd8b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -110,6 +110,7 @@ linux (3.2.28-1) UNRELEASED; urgency=low (Closes: #686528) * [i386/686-pae] video: Disable Geode framebuffer drivers, not used with any chips that support PAE + * [x86] drm/i915: Fix i8xx interrupt handling (Closes: #655152) [ Bastian Blank ] * Make xen-linux-system meta-packages depend on xen-system. This allows diff --git a/debian/patches/bugfix/x86/drm-i915-i8xx-interrupt-handler.patch b/debian/patches/bugfix/x86/drm-i915-i8xx-interrupt-handler.patch new file mode 100644 index 000000000..a5d93e5db --- /dev/null +++ b/debian/patches/bugfix/x86/drm-i915-i8xx-interrupt-handler.patch @@ -0,0 +1,250 @@ +From: Chris Wilson +Date: Sun, 22 Apr 2012 21:13:57 +0100 +Subject: drm/i915: i8xx interrupt handler +Bug-Debian: http://bugs.debian.org/655152 + +commit c2798b19bac2538393fc932bfbe59807a4734b3e upstream. + +gen2 hardware has some significant differences from the other interrupt +routines that were glossed over and then forgotten about in the +transition to KMS. Such as + +- 16bit IIR +- PendingFlip status bit + +This patch reintroduces a handler specifically for gen2 for the purpose +of handling pageflips correctly, simplifying code in the process. + +v2: Also fixup ring get/put irq to only access 16bit registers (Daniel) + +Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=24202 +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=41793 +Signed-off-by: Chris Wilson +[danvet: use posting_read16 in intel_ringbuffer.c and kill _driver +from the function names.] +Signed-off-by: Daniel Vetter +[jn: check for gen2 directly in i915_enable_irq and i915_disable_irq + to simplify backport] +Signed-off-by: Jonathan Nieder +--- + drivers/gpu/drm/i915/i915_irq.c | 161 ++++++++++++++++++++++++++++++- + drivers/gpu/drm/i915/intel_ringbuffer.c | 18 +++- + 2 files changed, 171 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c +index 578ddfc..ac5a50d 100644 +--- a/drivers/gpu/drm/i915/i915_irq.c ++++ b/drivers/gpu/drm/i915/i915_irq.c +@@ -2083,6 +2083,152 @@ static void i915_driver_irq_uninstall(struct drm_device * dev) + I915_WRITE(IIR, I915_READ(IIR)); + } + ++static void i8xx_irq_preinstall(struct drm_device * dev) ++{ ++ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; ++ int pipe; ++ ++ atomic_set(&dev_priv->irq_received, 0); ++ ++ for_each_pipe(pipe) ++ I915_WRITE(PIPESTAT(pipe), 0); ++ I915_WRITE16(IMR, 0xffff); ++ I915_WRITE16(IER, 0x0); ++ POSTING_READ16(IER); ++} ++ ++static int i8xx_irq_postinstall(struct drm_device *dev) ++{ ++ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; ++ ++ dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; ++ ++ dev_priv->pipestat[0] = 0; ++ dev_priv->pipestat[1] = 0; ++ ++ I915_WRITE16(EMR, ++ ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); ++ ++ /* Unmask the interrupts that we always want on. */ ++ dev_priv->irq_mask = ++ ~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | ++ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | ++ I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | ++ I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | ++ I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); ++ I915_WRITE16(IMR, dev_priv->irq_mask); ++ ++ I915_WRITE16(IER, ++ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | ++ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | ++ I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT | ++ I915_USER_INTERRUPT); ++ POSTING_READ16(IER); ++ ++ return 0; ++} ++ ++static irqreturn_t i8xx_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; ++ struct drm_i915_master_private *master_priv; ++ u16 iir, new_iir; ++ u32 pipe_stats[2]; ++ unsigned long irqflags; ++ int irq_received; ++ int pipe; ++ u16 flip_mask = ++ I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | ++ I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; ++ ++ atomic_inc(&dev_priv->irq_received); ++ ++ iir = I915_READ16(IIR); ++ if (iir == 0) ++ return IRQ_NONE; ++ ++ while (iir & ~flip_mask) { ++ /* Can't rely on pipestat interrupt bit in iir as it might ++ * have been cleared after the pipestat interrupt was received. ++ * It doesn't set the bit in iir again, but it still produces ++ * interrupts (for non-MSI). ++ */ ++ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ++ if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) ++ i915_handle_error(dev, false); ++ ++ for_each_pipe(pipe) { ++ int 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]); ++ irq_received = 1; ++ } ++ } ++ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); ++ ++ I915_WRITE16(IIR, iir & ~flip_mask); ++ new_iir = I915_READ16(IIR); /* Flush posted writes */ ++ ++ if (dev->primary->master) { ++ master_priv = dev->primary->master->driver_priv; ++ if (master_priv->sarea_priv) ++ master_priv->sarea_priv->last_dispatch = ++ READ_BREADCRUMB(dev_priv); ++ } ++ ++ if (iir & I915_USER_INTERRUPT) ++ notify_ring(dev, &dev_priv->ring[RCS]); ++ ++ if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS && ++ drm_handle_vblank(dev, 0)) { ++ if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { ++ intel_prepare_page_flip(dev, 0); ++ intel_finish_page_flip(dev, 0); ++ flip_mask &= ~I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT; ++ } ++ } ++ ++ if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS && ++ drm_handle_vblank(dev, 1)) { ++ if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { ++ intel_prepare_page_flip(dev, 1); ++ intel_finish_page_flip(dev, 1); ++ flip_mask &= ~I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; ++ } ++ } ++ ++ iir = new_iir; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void i8xx_irq_uninstall(struct drm_device * dev) ++{ ++ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; ++ int pipe; ++ ++ dev_priv->vblank_pipe = 0; ++ ++ for_each_pipe(pipe) { ++ /* Clear enable bits; then clear status bits */ ++ I915_WRITE(PIPESTAT(pipe), 0); ++ I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); ++ } ++ I915_WRITE16(IMR, 0xffff); ++ I915_WRITE16(IER, 0x0); ++ I915_WRITE16(IIR, I915_READ16(IIR)); ++} ++ + void intel_irq_init(struct drm_device *dev) + { + dev->driver->get_vblank_counter = i915_get_vblank_counter; +@@ -2114,10 +2260,17 @@ void intel_irq_init(struct drm_device *dev) + dev->driver->enable_vblank = ironlake_enable_vblank; + dev->driver->disable_vblank = ironlake_disable_vblank; + } else { +- dev->driver->irq_preinstall = i915_driver_irq_preinstall; +- dev->driver->irq_postinstall = i915_driver_irq_postinstall; +- dev->driver->irq_uninstall = i915_driver_irq_uninstall; +- dev->driver->irq_handler = i915_driver_irq_handler; ++ if (INTEL_INFO(dev)->gen == 2) { ++ dev->driver->irq_preinstall = i8xx_irq_preinstall; ++ dev->driver->irq_postinstall = i8xx_irq_postinstall; ++ dev->driver->irq_handler = i8xx_irq_handler; ++ dev->driver->irq_uninstall = i8xx_irq_uninstall; ++ } else { ++ dev->driver->irq_preinstall = i915_driver_irq_preinstall; ++ dev->driver->irq_postinstall = i915_driver_irq_postinstall; ++ dev->driver->irq_uninstall = i915_driver_irq_uninstall; ++ dev->driver->irq_handler = i915_driver_irq_handler; ++ } + dev->driver->enable_vblank = i915_enable_vblank; + dev->driver->disable_vblank = i915_disable_vblank; + } +diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c +index 19085c0..57a7363 100644 +--- a/drivers/gpu/drm/i915/intel_ringbuffer.c ++++ b/drivers/gpu/drm/i915/intel_ringbuffer.c +@@ -689,16 +689,26 @@ static void + i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) + { + dev_priv->irq_mask &= ~mask; +- I915_WRITE(IMR, dev_priv->irq_mask); +- POSTING_READ(IMR); ++ if (IS_GEN2(dev_priv->dev)) { ++ I915_WRITE16(IMR, dev_priv->irq_mask); ++ POSTING_READ16(IMR); ++ } else { ++ I915_WRITE(IMR, dev_priv->irq_mask); ++ POSTING_READ(IMR); ++ } + } + + static void + i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) + { + dev_priv->irq_mask |= mask; +- I915_WRITE(IMR, dev_priv->irq_mask); +- POSTING_READ(IMR); ++ if (IS_GEN2(dev_priv->dev)) { ++ I915_WRITE16(IMR, dev_priv->irq_mask); ++ POSTING_READ16(IMR); ++ } else { ++ I915_WRITE(IMR, dev_priv->irq_mask); ++ POSTING_READ(IMR); ++ } + } + + static bool +-- +1.7.10.4 + diff --git a/debian/patches/series b/debian/patches/series index fa7273e43..f6753da56 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -393,3 +393,4 @@ features/all/USB-add-USB_VENDOR_AND_INTERFACE_INFO-macro.patch bugfix/all/usb-Add-quirk-detection-based-on-interface-informati.patch bugfix/all/usb-Add-USB_QUIRK_RESET_RESUME-for-all-Logitech-UVC-.patch bugfix/alpha/alpha-use-large-data-model.diff +bugfix/x86/drm-i915-i8xx-interrupt-handler.patch