OSDN Git Service

radeon: fix fence race condition hopefully
[android-x86/external-libdrm.git] / shared-core / i915_irq.c
index ea2c88a..f9fe5e2 100644 (file)
 #include "i915_drm.h"
 #include "i915_drv.h"
 #include "intel_drv.h"
+#include "drm_crtc_helper.h"
 
 #define MAX_NOPID ((u32)~0)
 
+/*
+ * These are the interrupts used by the driver
+ */
+#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \
+                                   I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
+                                   I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
+
+static inline void
+i915_enable_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != 0) {
+               dev_priv->irq_mask_reg &= ~mask;
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(IMR);
+       }
+}
+
+static inline void
+i915_disable_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != mask) {
+               dev_priv->irq_mask_reg |= mask;
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(IMR);
+       }
+}
+
 /**
  * i915_get_pipe - return the the pipe associated with a given plane
  * @dev: DRM device
@@ -164,11 +192,11 @@ static void i915_vblank_tasklet(struct drm_device *dev)
        u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24);
        RING_LOCALS;
        
-       if (sarea_priv->front_tiled) {
+       if (IS_I965G(dev) && sarea_priv->front_tiled) {
                cmd |= XY_SRC_COPY_BLT_DST_TILED;
                dst_pitch >>= 2;
        }
-       if (sarea_priv->back_tiled) {
+       if (IS_I965G(dev) && sarea_priv->back_tiled) {
                cmd |= XY_SRC_COPY_BLT_SRC_TILED;
                src_pitch >>= 2;
        }
@@ -198,6 +226,9 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                if ((counter[pipe] - vbl_swap->sequence) > (1<<23))
                        continue;
 
+               master_priv = vbl_swap->minor->master->driver_priv;
+               sarea_priv = master_priv->sarea_priv;
+               
                list_del(list);
                dev_priv->swaps_pending--;
                drm_vblank_put(dev, pipe);
@@ -246,6 +277,16 @@ static void i915_vblank_tasklet(struct drm_device *dev)
 
        i915_kernel_lost_context(dev);
 
+       upper[0] = upper[1] = 0;
+       slice[0] = max(sarea_priv->planeA_h / nhits, 1);
+       slice[1] = max(sarea_priv->planeB_h / nhits, 1);
+       lower[0] = sarea_priv->planeA_y + slice[0];
+       lower[1] = sarea_priv->planeB_y + slice[0];
+
+       offsets[0] = sarea_priv->front_offset;
+       offsets[1] = sarea_priv->back_offset;
+       offsets[2] = sarea_priv->third_offset;
+       num_pages = sarea_priv->third_handle ? 3 : 2;
 
        DRM_SPINLOCK(&dev->drw_lock);
 
@@ -259,6 +300,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)
             upper[1] = lower[1], lower[1] += slice[1]) {
                int init_drawrect = 1;
 
+               if (i == nhits)
+                       lower[0] = lower[1] = sarea_priv->height;
 
                list_for_each(hit, &hits) {
                        struct drm_i915_vbl_swap *swap_hit =
@@ -267,24 +310,6 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                        int num_rects, plane, front, back;
                        unsigned short top, bottom;
 
-                       sarea_priv = master_priv->sarea_priv;
-
-                       upper[0] = upper[1] = 0;
-                       slice[0] = max(sarea_priv->planeA_h / nhits, 1);
-                       slice[1] = max(sarea_priv->planeB_h / nhits, 1);
-                       lower[0] = sarea_priv->planeA_y + slice[0];
-                       lower[1] = sarea_priv->planeB_y + slice[0];
-
-                       offsets[0] = sarea_priv->front_offset;
-                       offsets[1] = sarea_priv->back_offset;
-                       offsets[2] = sarea_priv->third_offset;
-                       num_pages = sarea_priv->third_handle ? 3 : 2;
-                       if (i == nhits)
-                               lower[0] = lower[1] = sarea_priv->height;
-
-                       pitchropcpp = (sarea_priv->pitch * cpp) | (0xcc << 16) |
-                                       (cpp << 23) | (1 << 24);
-
                        drw = drm_get_drawable_info(dev, swap_hit->drw_id);
 
                        if (!drw)
@@ -297,8 +322,6 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                                continue;
                        }
 
-                       master_priv = swap_hit->minor->master->driver_priv;
-
                        if (init_drawrect) {
                                int width  = sarea_priv->width;
                                int height = sarea_priv->height;
@@ -337,8 +360,6 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                                 (2 * plane)) & 0x3;
                        back = (front + 1) % num_pages;
 
-               
-
                        for (num_rects = drw->num_rects; num_rects--; rect++) {
                                int y1 = max(rect->y1, top);
                                int y2 = min(rect->y2, bottom);
@@ -373,28 +394,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER);
        }
 }
-#if 0
-static int i915_in_vblank(struct drm_device *dev, int pipe)
-{
-       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
-       unsigned long pipedsl, vblank, vtotal;
-       unsigned long vbl_start, vbl_end, cur_line;
 
-       pipedsl = pipe ? PIPEBDSL : PIPEADSL;
-       vblank = pipe ? VBLANK_B : VBLANK_A;
-       vtotal = pipe ? VTOTAL_B : VTOTAL_A;
-
-       vbl_start = I915_READ(vblank) & VBLANK_START_MASK;
-       vbl_end = (I915_READ(vblank) >> VBLANK_END_SHIFT) & VBLANK_END_MASK;
-
-       cur_line = I915_READ(pipedsl);
-
-       if (cur_line >= vbl_start)
-               return 1;
-
-       return 0;
-}
-#endif
 u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
 {
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
@@ -408,8 +408,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
        low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
 
        if (!i915_pipe_enabled(dev, pipe)) {
-           printk(KERN_ERR "trying to get vblank count for disabled "
-                  "pipe %d\n", pipe);
+           DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
            return 0;
        }
 
@@ -429,116 +428,17 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
 
        count = (high1 << 8) | low;
 
-       /*
-        * If we're in the middle of the vblank period, the
-        * above regs won't have been updated yet, so return
-        * an incremented count to stay accurate
-        */
-#if 0
-       if (i915_in_vblank(dev, pipe))
-               count++;
-#endif
-       /* count may be reset by other driver(e.g. 2D driver), 
-          we have no way to know if it is wrapped or resetted 
-          when count is zero. do a rough guess.
-       */
-       if (count == 0 && dev->last_vblank[pipe] < dev->max_vblank_count/2)
-               dev->last_vblank[pipe] = 0; 
-       
        return count;
 }
 
-#define HOTPLUG_CMD_CRT 1
-#define HOTPLUG_CMD_CRT_DIS 2
-#define HOTPLUG_CMD_SDVOB 4
-#define HOTPLUG_CMD_SDVOC 8
-#define HOTPLUG_CMD_TV 16
-
 static struct drm_device *hotplug_dev;
-static int hotplug_cmd = 0;
-static spinlock_t hotplug_lock = SPIN_LOCK_UNLOCKED;
-
-static void i915_hotplug_tv(struct drm_device *dev)
-{
-       struct drm_output *output;
-       struct intel_output *iout;
-       enum drm_output_status status;
-
-       mutex_lock(&dev->mode_config.mutex);
-
-       /* find the crt output */
-       list_for_each_entry(output, &dev->mode_config.output_list, head) {
-               iout = output->driver_private;
-               if (iout->type == INTEL_OUTPUT_TVOUT)
-                       break;
-               else
-                       iout = 0;
-       }
-
-       if (iout == 0)
-               goto unlock;
-
-       /* may need to I915_WRITE(TVDAC, 1<<31) to ack the interrupt */
-       status = output->funcs->detect(output);
-       drm_hotplug_stage_two(dev, output,
-                             status == output_status_connected ? 1 : 0);
 
-unlock:
-       mutex_unlock(&dev->mode_config.mutex);
-}
-
-static void i915_hotplug_crt(struct drm_device *dev, bool isconnected)
-{
-       struct drm_output *output;
-       struct intel_output *iout;
-
-       mutex_lock(&dev->mode_config.mutex);
-
-       /* find the crt output */
-       list_for_each_entry(output, &dev->mode_config.output_list, head) {
-               iout = output->driver_private;
-               if (iout->type == INTEL_OUTPUT_ANALOG)
-                       break;
-               else
-                       iout = 0;
-       }
-
-       if (iout == 0)
-               goto unlock;
-
-       drm_hotplug_stage_two(dev, output, isconnected);
-
-unlock:
-       mutex_unlock(&dev->mode_config.mutex);
-}
-
-static void i915_hotplug_sdvo(struct drm_device *dev, int sdvoB)
-{
-       struct drm_output *output = 0;
-       enum drm_output_status status;
-
-       mutex_lock(&dev->mode_config.mutex);
-
-       output = intel_sdvo_find(dev, sdvoB);
-
-       if (!output)
-               goto unlock;
-
-       status = output->funcs->detect(output);
-
-       if (status != output_status_connected)
-               drm_hotplug_stage_two(dev, output, false);
-       else
-               drm_hotplug_stage_two(dev, output, true);
-
-       intel_sdvo_set_hotplug(output, 1);
-
-unlock:
-       mutex_unlock(&dev->mode_config.mutex);
-}
-/*
- * This code is called in a more safe envirmoent to handle the hotplugs.
- * Add code here for hotplug love to userspace.
+/**
+ * Handler for user interrupts in process context (able to sleep, do VFS
+ * operations, etc.
+ *
+ * If another IRQ comes in while we're in this handler, it will still get put
+ * on the queue again to be rerun when we finish.
  */
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
 static void i915_hotplug_work_func(void *work)
@@ -547,34 +447,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
 #endif
 {
        struct drm_device *dev = hotplug_dev;
-       int crt;
-       int crtDis;
-       int sdvoB;
-       int sdvoC;
-       int tv;
-
-       spin_lock(&hotplug_lock);
-       tv = hotplug_cmd & HOTPLUG_CMD_TV;
-       crt = hotplug_cmd & HOTPLUG_CMD_CRT;
-       crtDis = hotplug_cmd & HOTPLUG_CMD_CRT_DIS;
-       sdvoB = hotplug_cmd & HOTPLUG_CMD_SDVOB;
-       sdvoC = hotplug_cmd & HOTPLUG_CMD_SDVOC;
-       hotplug_cmd = 0;
-       spin_unlock(&hotplug_lock);
-
-       if (tv)
-               i915_hotplug_tv(dev);
-       if (crt)
-               i915_hotplug_crt(dev, true);
-       if (crtDis)
-               i915_hotplug_crt(dev, false);
-
-       if (sdvoB)
-               i915_hotplug_sdvo(dev, 1);
-
-       if (sdvoC)
-               i915_hotplug_sdvo(dev, 0);
 
+       drm_helper_hotplug_stage_two(dev);
        drm_handle_hotplug(dev);
 }
 
@@ -591,42 +465,19 @@ static int i915_run_hotplug_tasklet(struct drm_device *dev, uint32_t stat)
 
        if (stat & TV_HOTPLUG_INT_STATUS) {
                DRM_DEBUG("TV event\n");
-
-               spin_lock(&hotplug_lock);
-               hotplug_cmd |= HOTPLUG_CMD_TV;
-               spin_unlock(&hotplug_lock);
        }
 
        if (stat & CRT_HOTPLUG_INT_STATUS) {
                DRM_DEBUG("CRT event\n");
-
-               if (stat & CRT_HOTPLUG_MONITOR_MASK) {
-                       spin_lock(&hotplug_lock);
-                       hotplug_cmd |= HOTPLUG_CMD_CRT;
-                       spin_unlock(&hotplug_lock);
-               } else {
-                       spin_lock(&hotplug_lock);
-                       hotplug_cmd |= HOTPLUG_CMD_CRT_DIS;
-                       spin_unlock(&hotplug_lock);
-               }
        }
 
        if (stat & SDVOB_HOTPLUG_INT_STATUS) {
                DRM_DEBUG("sDVOB event\n");
-
-               spin_lock(&hotplug_lock);
-               hotplug_cmd |= HOTPLUG_CMD_SDVOB;
-               spin_unlock(&hotplug_lock);
        }
 
        if (stat & SDVOC_HOTPLUG_INT_STATUS) {
                DRM_DEBUG("sDVOC event\n");
-
-               spin_lock(&hotplug_lock);
-               hotplug_cmd |= HOTPLUG_CMD_SDVOC;
-               spin_unlock(&hotplug_lock);
        }
-
        queue_work(dev_priv->wq, &hotplug);
 
        return 0;
@@ -638,29 +489,21 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
        struct drm_i915_master_private *master_priv;
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        u32 iir;
-       u32 pipea_stats, pipeb_stats;
+       u32 pipea_stats = 0, pipeb_stats = 0, tvdac;
        int hotplug = 0;
        int vblank = 0;
-
-       /* On i8xx/i915 hw the IIR and IER are 16bit on i9xx its 32bit */
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
-               iir = I915_READ(I915REG_INT_IDENTITY_R);
-       else
-               iir = I915_READ16(I915REG_INT_IDENTITY_R);
-
-       iir &= (dev_priv->irq_enable_reg | I915_USER_INTERRUPT);
-
-#if 0
-       DRM_DEBUG("flag=%08x\n", iir);
+#ifdef __linux__
+       if (dev->pdev->msi_enabled)
+               I915_WRITE(IMR, ~0);
 #endif
+       iir = I915_READ(IIR);
+       atomic_inc(&dev_priv->irq_received);
        if (iir == 0) {
-#if 0
-               DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n",
-                          iir,
-                          I915_READ(I915REG_INT_MASK_R),
-                          I915_READ(I915REG_INT_ENABLE_R),
-                          I915_READ(I915REG_PIPEASTAT),
-                          I915_READ(I915REG_PIPEBSTAT));
+#ifdef __linux__
+               if (dev->pdev->msi_enabled) {
+                       I915_WRITE(IMR, dev_priv->irq_mask_reg);
+                       (void) I915_READ(IMR);
+               }
 #endif
                return IRQ_NONE;
        }
@@ -670,40 +513,33 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
         * we may get extra interrupts.
         */
        if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
-               pipea_stats = I915_READ(I915REG_PIPEASTAT);
-               if (pipea_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
-                                  I915_VBLANK_INTERRUPT_STATUS))
-               {
-                       vblank++;
-                       drm_handle_vblank(dev, i915_get_plane(dev, 0));
-               }
-
-               /* This is a global event, and not a pipe A event */
-               if ((pipea_stats & I915_HOTPLUG_INTERRUPT_STATUS) ||
-                   (pipea_stats & I915_HOTPLUG_TV_INTERRUPT_STATUS))
-                       hotplug = 1;
-
-               I915_WRITE(I915REG_PIPEASTAT, pipea_stats);
+               pipea_stats = I915_READ(PIPEASTAT);
+               I915_WRITE(PIPEASTAT, pipea_stats);
        }
 
        if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
-               pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
-               if (pipeb_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
-                                  I915_VBLANK_INTERRUPT_STATUS))
-               {
-                       vblank++;
-                       drm_handle_vblank(dev, i915_get_plane(dev, 1));
-               }
-               I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats);
+               pipeb_stats = I915_READ(PIPEBSTAT);
+               I915_WRITE(PIPEBSTAT, pipeb_stats);
        }
 
-       /* Clear the generated interrupt */
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
-               I915_WRITE(I915REG_INT_IDENTITY_R, iir);
-               (void) I915_READ(I915REG_INT_IDENTITY_R);
-       } else {
-               I915_WRITE16(I915REG_INT_IDENTITY_R, iir);
-               (void) I915_READ16(I915REG_INT_IDENTITY_R);
+       I915_WRITE(IIR, iir);
+#ifdef __linux__
+       if (dev->pdev->msi_enabled)
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+#endif
+
+       (void) I915_READ(IIR); /* Flush posted writes */
+
+       /* This is a global event, and not a pipe A event */
+       if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS)
+               hotplug = 1;
+
+       if (pipea_stats & PIPE_HOTPLUG_TV_INTERRUPT_STATUS) {
+               hotplug = 1;
+               /* Toggle hotplug detection to clear hotplug status */
+               tvdac = I915_READ(TV_DAC);
+               I915_WRITE(TV_DAC, tvdac & ~TVDAC_STATE_CHG_EN);
+               I915_WRITE(TV_DAC, tvdac | TVDAC_STATE_CHG_EN);
        }
 
        if (dev->primary->master) {
@@ -711,11 +547,38 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
        }
 
+#ifdef __linux__
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
+       if ((iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) &&
+           (pipeb_stats & I915_LEGACY_BLC_EVENT_ENABLE))
+               opregion_asle_intr(dev);
+       if (iir & I915_ASLE_INTERRUPT)
+               opregion_asle_intr(dev);
+#endif
+#endif
+
        if (iir & I915_USER_INTERRUPT) {
-               DRM_WAKEUP(&dev_priv->irq_queue);
-#ifdef I915_HAVE_FENCE
-               i915_fence_handler(dev);
+#ifdef I915_HAVE_GEM
+               dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
 #endif
+               DRM_WAKEUP(&dev_priv->irq_queue);
+       }
+
+       if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
+                          PIPE_VBLANK_INTERRUPT_STATUS)) {
+               vblank++;
+               drm_handle_vblank(dev, i915_get_plane(dev, 0));
+       }
+
+       /* The vblank interrupt gets enabled even if we didn't ask for
+          it, so make sure it's shut down again */
+       if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B))
+               pipeb_stats &= ~(I915_VBLANK_INTERRUPT_ENABLE);
+
+       if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
+                          PIPE_VBLANK_INTERRUPT_STATUS)) {
+               vblank++;
+               drm_handle_vblank(dev, i915_get_plane(dev, 1));
        }
 
        if (vblank) {
@@ -729,10 +592,10 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                DRM_INFO("Hotplug event received\n");
 
                if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev)) {
-                       if (pipea_stats & I915_HOTPLUG_INTERRUPT_STATUS)
+                       if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS)
                                temp2 |= SDVOB_HOTPLUG_INT_STATUS |
                                        SDVOC_HOTPLUG_INT_STATUS;
-                       if (pipea_stats & I915_HOTPLUG_TV_INTERRUPT_STATUS)
+                       if (pipea_stats & PIPE_HOTPLUG_TV_INTERRUPT_STATUS)
                                temp2 |= TV_HOTPLUG_INT_STATUS;
                } else {
                        temp2 = I915_READ(PORT_HOTPLUG_STAT);
@@ -758,7 +621,7 @@ int i915_emit_irq(struct drm_device *dev)
 
        BEGIN_LP_RING(2);
        OUT_RING(0);
-       OUT_RING(GFX_OP_USER_INTERRUPT);
+       OUT_RING(MI_USER_INTERRUPT);
        ADVANCE_LP_RING();
 
        return dev_priv->counter;
@@ -769,15 +632,9 @@ void i915_user_irq_on(struct drm_device *dev)
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
 
        DRM_SPINLOCK(&dev_priv->user_irq_lock);
-       if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){
-               dev_priv->irq_enable_reg |= I915_USER_INTERRUPT;
-               if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
-                       I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-               else
-                       I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-       }
+       if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1))
+               i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
        DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
-
 }
                
 void i915_user_irq_off(struct drm_device *dev)
@@ -785,28 +642,40 @@ void i915_user_irq_off(struct drm_device *dev)
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
 
        DRM_SPINLOCK(&dev_priv->user_irq_lock);
-       if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
-               //              dev_priv->irq_enable_reg &= ~I915_USER_INTERRUPT;
-               //              if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
-               //                      I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-               //              else
-               //                      I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-       }
+#ifdef __linux__
+       BUG_ON(dev_priv->irq_enabled && dev_priv->user_irq_refcount <= 0);
+#endif
+       if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0))
+               i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
        DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 }
 
 
-static int i915_wait_irq(struct drm_device * dev, int irq_nr)
+int i915_wait_irq(struct drm_device * dev, int irq_nr)
 {
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        struct drm_i915_master_private *master_priv;
        int ret = 0;
 
+       if (!dev_priv) {
+               DRM_ERROR("called with no initialization\n");
+               return -EINVAL;
+       }
+
        DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
                  READ_BREADCRUMB(dev_priv));
 
-       if (READ_BREADCRUMB(dev_priv) >= irq_nr)
+       master_priv = dev->primary->master->driver_priv;
+
+       if (!master_priv) {
+               DRM_ERROR("no master priv?\n");
+               return -EINVAL;
+       }
+
+       if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
+               master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
                return 0;
+       }
 
        i915_user_irq_on(dev);
        DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
@@ -818,10 +687,8 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
                          READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
        }
        
-       if (dev->primary->master) {
-               master_priv = dev->primary->master->driver_priv;
+       if (READ_BREADCRUMB(dev_priv) >= irq_nr)
                master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
-       }
 
        return ret;
 }
@@ -873,16 +740,17 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        int pipe = i915_get_pipe(dev, plane);
        u32     pipestat_reg = 0;
+       u32     mask_reg = 0;
        u32     pipestat;
 
        switch (pipe) {
        case 0:
-               pipestat_reg = I915REG_PIPEASTAT;
-               dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               pipestat_reg = PIPEASTAT;
+               mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
-               pipestat_reg = I915REG_PIPEBSTAT;
-               dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               pipestat_reg = PIPEBSTAT;
+               mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
                DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
@@ -898,22 +766,19 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
                 * but 
                 */
                if (IS_I965G (dev))
-                       pipestat |= I915_START_VBLANK_INTERRUPT_ENABLE;
+                       pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE;
                else
-                       pipestat |= I915_VBLANK_INTERRUPT_ENABLE;
+                       pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE;
                /*
                 * Clear any pending status
                 */
-               pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS |
-                            I915_VBLANK_INTERRUPT_STATUS);
+               pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
+                            PIPE_VBLANK_INTERRUPT_STATUS);
                I915_WRITE(pipestat_reg, pipestat);
        }
-
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
-               I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-       else
-               I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-
+       DRM_SPINLOCK(&dev_priv->user_irq_lock);
+       i915_enable_irq(dev_priv, mask_reg);
+       DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 
        return 0;
 }
@@ -923,16 +788,17 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        int pipe = i915_get_pipe(dev, plane);
        u32     pipestat_reg = 0;
+       u32     mask_reg = 0;
        u32     pipestat;
 
        switch (pipe) {
        case 0:
-               pipestat_reg = I915REG_PIPEASTAT;
-               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               pipestat_reg = PIPEASTAT;
+               mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
-               pipestat_reg = I915REG_PIPEBSTAT;
-               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               pipestat_reg = PIPEBSTAT;
+               mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
                DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
@@ -940,48 +806,48 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
                break;
        }
 
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
-               I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-       else
-               I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+       DRM_SPINLOCK(&dev_priv->user_irq_lock);
+       i915_disable_irq(dev_priv, mask_reg);
+       DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 
-       if (pipestat_reg)
-       {
+       if (pipestat_reg) {
                pipestat = I915_READ (pipestat_reg);
-               pipestat &= ~(I915_START_VBLANK_INTERRUPT_ENABLE |
-                             I915_VBLANK_INTERRUPT_ENABLE);
+               pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
+                             PIPE_VBLANK_INTERRUPT_ENABLE);
                /*
                 * Clear any pending status
                 */
-               pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS |
-                            I915_VBLANK_INTERRUPT_STATUS);
+               pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
+                            PIPE_VBLANK_INTERRUPT_STATUS);
                I915_WRITE(pipestat_reg, pipestat);
+               (void) I915_READ(pipestat_reg);
        }
 }
 
 void i915_enable_interrupt (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
-       struct drm_output *o;
-
-       dev_priv->irq_enable_reg |= I915_USER_INTERRUPT;
+       struct drm_connector *o;
+       
+       dev_priv->irq_mask_reg &= ~0;
 
        if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
-               if (dev->mode_config.num_output)
-                       dev_priv->irq_enable_reg |= I915_DISPLAY_PORT_INTERRUPT;
+               if (dev->mode_config.num_connector)
+                       dev_priv->irq_mask_reg &= ~I915_DISPLAY_PORT_INTERRUPT;
        } else {
-               if (dev->mode_config.num_output)
-                       dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               if (dev->mode_config.num_connector)
+                       dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
 
                /* Enable global interrupts for hotplug - not a pipeA event */
-               I915_WRITE(I915REG_PIPEASTAT, I915_READ(I915REG_PIPEASTAT) |
-                          I915_HOTPLUG_INTERRUPT_ENABLE |
-                          I915_HOTPLUG_TV_INTERRUPT_ENABLE |
-                          I915_HOTPLUG_TV_CLEAR |
-                          I915_HOTPLUG_CLEAR);
+               I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) |
+                          PIPE_HOTPLUG_INTERRUPT_ENABLE |
+                          PIPE_HOTPLUG_TV_INTERRUPT_ENABLE |
+                          PIPE_HOTPLUG_TV_INTERRUPT_STATUS |
+                          PIPE_HOTPLUG_INTERRUPT_STATUS);
        }
 
-       if (dev_priv->irq_enable_reg & (I915_DISPLAY_PORT_INTERRUPT | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)) {
+       if (!(dev_priv->irq_mask_reg & I915_DISPLAY_PORT_INTERRUPT) ||
+           !(dev_priv->irq_mask_reg & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)) {
                u32 temp = 0;
 
                if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
@@ -1008,6 +874,9 @@ void i915_enable_interrupt (struct drm_device *dev)
 
                        I915_WRITE(SDVOB, I915_READ(SDVOB) | SDVO_INTERRUPT_ENABLE);
                        I915_WRITE(SDVOC, I915_READ(SDVOC) | SDVO_INTERRUPT_ENABLE);
+
+                       /* TV */
+                       I915_WRITE(TV_DAC, I915_READ(TV_DAC) | TVDAC_STATE_CHG_EN);
                } else {
                        /* DVO ???? */
                }
@@ -1022,11 +891,11 @@ void i915_enable_interrupt (struct drm_device *dev)
                }
        }
 
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
-               I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-       else
-               I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
-
+#ifdef __linux__
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
+       opregion_enable_asle(dev);
+#endif
+#endif
        dev_priv->irq_enabled = 1;
 }
 
@@ -1036,20 +905,12 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
                         struct drm_file *file_priv)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_i915_vblank_pipe *pipe = data;
 
        if (!dev_priv) {
                DRM_ERROR("called with no initialization\n");
                return -EINVAL;
        }
 
-       if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) {
-               DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe);
-               return -EINVAL;
-       }
-
-       dev_priv->vblank_pipe = pipe->pipe;
-
        return 0;
 }
 
@@ -1058,17 +919,15 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_vblank_pipe *pipe = data;
-       u16 flag;
+       u32 flag = 0;
 
        if (!dev_priv) {
                DRM_ERROR("called with no initialization\n");
                return -EINVAL;
        }
 
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
-               flag = I915_READ(I915REG_INT_ENABLE_R);
-       else
-               flag = I915_READ16(I915REG_INT_ENABLE_R);
+       if (dev_priv->irq_enabled)
+           flag = ~dev_priv->irq_mask_reg;
 
        pipe->pipe = 0;
        if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)
@@ -1141,7 +1000,13 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
        DRM_SPINUNLOCK_IRQRESTORE(&dev->drw_lock, irqflags);
 
-       drm_update_vblank_count(dev, pipe);
+       /*
+        * We take the ref here and put it when the swap actually completes
+        * in the tasklet.
+        */
+       ret = drm_vblank_get(dev, pipe);
+       if (ret)
+               return ret;
        curseq = drm_vblank_count(dev, pipe);
 
        if (seqtype == _DRM_VBLANK_RELATIVE)
@@ -1152,6 +1017,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
                        swap->sequence = curseq + 1;
                } else {
                        DRM_DEBUG("Missed target sequence\n");
+                       drm_vblank_put(dev, pipe);
                        return -EINVAL;
                }
        }
@@ -1173,6 +1039,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
                                    irqflags);
                                DRM_DEBUG("Invalid drawable ID %d\n",
                                          swap->drawable);
+                               drm_vblank_put(dev, pipe);
                                return -EINVAL;
                        }
 
@@ -1180,6 +1047,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
                        DRM_SPINUNLOCK_IRQRESTORE(&dev->drw_lock, irqflags);
 
+                       drm_vblank_put(dev, pipe);
                        return 0;
                }
        }
@@ -1203,6 +1071,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
        if (dev_priv->swaps_pending >= 100) {
                DRM_DEBUG("Too many swaps queued\n");
+               drm_vblank_put(dev, pipe);
                return -EBUSY;
        }
 
@@ -1210,17 +1079,12 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
        if (!vbl_swap) {
                DRM_ERROR("Failed to allocate memory to queue swap\n");
+               drm_vblank_put(dev, pipe);
                return -ENOMEM;
        }
 
        DRM_DEBUG("\n");
 
-       ret = drm_vblank_get(dev, pipe);
-       if (ret) {
-               drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
-               return ret;
-       }
-
        vbl_swap->drw_id = swap->drawable;
        vbl_swap->plane = plane;
        vbl_swap->sequence = swap->sequence;
@@ -1244,51 +1108,29 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 */
 void i915_driver_irq_preinstall(struct drm_device * dev)
 {
-       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
-       u32 tmp;
-
-       tmp = I915_READ(I915REG_PIPEASTAT);
-       I915_WRITE(I915REG_PIPEASTAT, tmp);
-       tmp = I915_READ(I915REG_PIPEBSTAT);
-       I915_WRITE(I915REG_PIPEBSTAT, tmp);
-
-
-       I915_WRITE16(I915REG_HWSTAM, 0xeffe);
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
-               I915_WRITE(I915REG_INT_MASK_R, 0x0);
-               I915_WRITE(I915REG_INT_ENABLE_R, 0x0);
-               tmp = I915_READ(I915REG_INT_IDENTITY_R);
-               I915_WRITE(I915REG_INT_IDENTITY_R, tmp);
-       } else {
-               I915_WRITE16(I915REG_INT_MASK_R, 0x0);
-               I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);
-               tmp = I915_READ16(I915REG_INT_IDENTITY_R);
-               I915_WRITE16(I915REG_INT_IDENTITY_R, tmp);
-       }
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
+       I915_WRITE16(HWSTAM, 0xeffe);
+       I915_WRITE16(IMR, 0x0);
+       I915_WRITE16(IER, 0x0);
 }
 
 int i915_driver_irq_postinstall(struct drm_device * dev)
 {
-       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        int ret, num_pipes = 2;
 
-       DRM_SPININIT(&dev_priv->swaps_lock, "swap");
        INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
        dev_priv->swaps_pending = 0;
 
-       DRM_SPININIT(&dev_priv->user_irq_lock, "userirq");
        dev_priv->user_irq_refcount = 0;
-       dev_priv->irq_enable_reg = 0;
+       dev_priv->irq_mask_reg = ~0;
 
        ret = drm_vblank_init(dev, num_pipes);
        if (ret)
                return ret;
 
-       ret = drm_hotplug_init(dev);
-       if (ret)
-               return ret;
-
+       dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
 
        i915_enable_interrupt(dev);
@@ -1298,37 +1140,29 @@ int i915_driver_irq_postinstall(struct drm_device * dev)
         * Initialize the hardware status page IRQ location.
         */
 
-       I915_WRITE(I915REG_INSTPM, (1 << 5) | (1 << 21));
+       I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
        return 0;
 }
 
 void i915_driver_irq_uninstall(struct drm_device * dev)
 {
-       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        u32 temp;
 
        if (!dev_priv)
                return;
 
-       dev_priv->irq_enabled = 0;
-
-       temp = I915_READ(I915REG_PIPEASTAT);
-       I915_WRITE(I915REG_PIPEASTAT, temp);
-       temp = I915_READ(I915REG_PIPEBSTAT);
-       I915_WRITE(I915REG_PIPEBSTAT, temp);
-       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
-               I915_WRITE(I915REG_HWSTAM, 0xffffffff);
-               I915_WRITE(I915REG_INT_MASK_R, 0xffffffff);
-               I915_WRITE(I915REG_INT_ENABLE_R, 0x0);
-
-               temp = I915_READ(I915REG_INT_IDENTITY_R);
-               I915_WRITE(I915REG_INT_IDENTITY_R, temp);
-       } else {
-               I915_WRITE16(I915REG_HWSTAM, 0xffff);
-               I915_WRITE16(I915REG_INT_MASK_R, 0xffff);
-               I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);
+       dev_priv->vblank_pipe = 0;
 
-               temp = I915_READ16(I915REG_INT_IDENTITY_R);
-               I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
-       }
+       dev_priv->irq_enabled = 0;
+       I915_WRITE(HWSTAM, 0xffffffff);
+       I915_WRITE(IMR, 0xffffffff);
+       I915_WRITE(IER, 0x0);
+
+       temp = I915_READ(PIPEASTAT);
+       I915_WRITE(PIPEASTAT, temp);
+       temp = I915_READ(PIPEBSTAT);
+       I915_WRITE(PIPEBSTAT, temp);
+       temp = I915_READ(IIR);
+       I915_WRITE(IIR, temp);
 }