OSDN Git Service

radeon: fix fence race condition hopefully
[android-x86/external-libdrm.git] / shared-core / i915_irq.c
index 710b289..f9fe5e2 100644 (file)
@@ -30,6 +30,8 @@
 #include "drm.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
+#include "intel_drv.h"
+#include "drm_crtc_helper.h"
 
 #define MAX_NOPID ((u32)~0)
 
                                    I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
 
 static inline void
-i915_enable_irq(drm_i915_private_t *dev_priv, uint32_t mask)
+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(I915REG_INT_MASK_R, dev_priv->irq_mask_reg);
-               (void) I915_READ(I915REG_INT_MASK_R);
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(IMR);
        }
 }
 
 static inline void
-i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask)
+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(I915REG_INT_MASK_R, dev_priv->irq_mask_reg);
-               (void) I915_READ(I915REG_INT_MASK_R);
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(IMR);
        }
 }
 
@@ -72,7 +74,7 @@ i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask)
 static int
 i915_get_pipe(struct drm_device *dev, int plane)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        u32 dspcntr;
 
        dspcntr = plane ? I915_READ(DSPBCNTR) : I915_READ(DSPACNTR);
@@ -109,7 +111,7 @@ i915_get_plane(struct drm_device *dev, int pipe)
 static int
 i915_pipe_enabled(struct drm_device *dev, int pipe)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF;
 
        if (I915_READ(pipeconf) & PIPEACONF_ENABLE)
@@ -127,8 +129,8 @@ static void
 i915_dispatch_vsync_flip(struct drm_device *dev, struct drm_drawable_info *drw,
                         int plane)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
+       struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
+       struct drm_i915_sarea *sarea_priv = master_priv->sarea_priv;
        u16 x1, y1, x2, y2;
        int pf_planes = 1 << plane;
 
@@ -172,12 +174,13 @@ i915_dispatch_vsync_flip(struct drm_device *dev, struct drm_drawable_info *drw,
  */
 static void i915_vblank_tasklet(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
+       struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
        struct list_head *list, *tmp, hits, *hit;
        int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;
        unsigned counter[2];
        struct drm_drawable_info *drw;
-       drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
+       struct drm_i915_sarea *sarea_priv = master_priv->sarea_priv;
        u32 cpp = dev_priv->cpp,  offsets[3];
        u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |
                                XY_SRC_COPY_BLT_WRITE_ALPHA |
@@ -189,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;
        }
@@ -216,13 +219,16 @@ static void i915_vblank_tasklet(struct drm_device *dev)
 
        /* Find buffer swaps scheduled for this vertical blank */
        list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {
-               drm_i915_vbl_swap_t *vbl_swap =
-                       list_entry(list, drm_i915_vbl_swap_t, head);
+               struct drm_i915_vbl_swap *vbl_swap =
+                       list_entry(list, struct drm_i915_vbl_swap, head);
                int pipe = i915_get_pipe(dev, vbl_swap->plane);
 
                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);
@@ -240,8 +246,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                }
 
                list_for_each(hit, &hits) {
-                       drm_i915_vbl_swap_t *swap_cmp =
-                               list_entry(hit, drm_i915_vbl_swap_t, head);
+                       struct drm_i915_vbl_swap *swap_cmp =
+                               list_entry(hit, struct drm_i915_vbl_swap, head);
                        struct drm_drawable_info *drw_cmp =
                                drm_get_drawable_info(dev, swap_cmp->drw_id);
 
@@ -298,8 +304,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                        lower[0] = lower[1] = sarea_priv->height;
 
                list_for_each(hit, &hits) {
-                       drm_i915_vbl_swap_t *swap_hit =
-                               list_entry(hit, drm_i915_vbl_swap_t, head);
+                       struct drm_i915_vbl_swap *swap_hit =
+                               list_entry(hit, struct drm_i915_vbl_swap, head);
                        struct drm_clip_rect *rect;
                        int num_rects, plane, front, back;
                        unsigned short top, bottom;
@@ -350,7 +356,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                        top = upper[plane];
                        bottom = lower[plane];
 
-                       front = (dev_priv->sarea_priv->pf_current_page >>
+                       front = (master_priv->sarea_priv->pf_current_page >>
                                 (2 * plane)) & 0x3;
                        back = (front + 1) % num_pages;
 
@@ -380,39 +386,18 @@ static void i915_vblank_tasklet(struct drm_device *dev)
        DRM_SPINUNLOCK(&dev->drw_lock);
 
        list_for_each_safe(hit, tmp, &hits) {
-               drm_i915_vbl_swap_t *swap_hit =
-                       list_entry(hit, drm_i915_vbl_swap_t, head);
+               struct drm_i915_vbl_swap *swap_hit =
+                       list_entry(hit, struct drm_i915_vbl_swap, head);
 
                list_del(hit);
 
                drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER);
        }
 }
-#if 0
-static int i915_in_vblank(struct drm_device *dev, int pipe)
-{
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) 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)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        unsigned long high_frame;
        unsigned long low_frame;
        u32 high1, high2, low, count;
@@ -423,7 +408,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
        low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
 
        if (!i915_pipe_enabled(dev, pipe)) {
-           DRM_ERROR("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;
        }
 
@@ -443,52 +428,83 @@ 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;
 }
 
+static struct drm_device *hotplug_dev;
+
+/**
+ * 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)
+#else
+static void i915_hotplug_work_func(struct work_struct *work)
+#endif
+{
+       struct drm_device *dev = hotplug_dev;
+
+       drm_helper_hotplug_stage_two(dev);
+       drm_handle_hotplug(dev);
+}
+
+static int i915_run_hotplug_tasklet(struct drm_device *dev, uint32_t stat)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       static DECLARE_WORK(hotplug, i915_hotplug_work_func, NULL);
+#else
+       static DECLARE_WORK(hotplug, i915_hotplug_work_func);
+#endif
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       hotplug_dev = dev;
+
+       if (stat & TV_HOTPLUG_INT_STATUS) {
+               DRM_DEBUG("TV event\n");
+       }
+
+       if (stat & CRT_HOTPLUG_INT_STATUS) {
+               DRM_DEBUG("CRT event\n");
+       }
+
+       if (stat & SDVOB_HOTPLUG_INT_STATUS) {
+               DRM_DEBUG("sDVOB event\n");
+       }
+
+       if (stat & SDVOC_HOTPLUG_INT_STATUS) {
+               DRM_DEBUG("sDVOC event\n");
+       }
+       queue_work(dev_priv->wq, &hotplug);
+
+       return 0;
+}
+
 irqreturn_t i915_driver_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;
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
        u32 iir;
-       u32 pipea_stats = 0, pipeb_stats = 0;
+       u32 pipea_stats = 0, pipeb_stats = 0, tvdac;
+       int hotplug = 0;
        int vblank = 0;
-
+#ifdef __linux__
        if (dev->pdev->msi_enabled)
-               I915_WRITE(I915REG_INT_MASK_R, ~0);
-       iir = I915_READ(I915REG_INT_IDENTITY_R);
-#if 0
-       DRM_DEBUG("flag=%08x\n", iir);
+               I915_WRITE(IMR, ~0);
 #endif
+       iir = I915_READ(IIR);
        atomic_inc(&dev_priv->irq_received);
        if (iir == 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(I915REG_INT_MASK_R,
-                                  dev_priv->irq_mask_reg);
-                       (void) I915_READ(I915REG_INT_MASK_R);
+                       I915_WRITE(IMR, dev_priv->irq_mask_reg);
+                       (void) I915_READ(IMR);
                }
+#endif
                return IRQ_NONE;
        }
 
@@ -497,51 +513,104 @@ 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);
-               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);
-               I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats);
+               pipeb_stats = I915_READ(PIPEBSTAT);
+               I915_WRITE(PIPEBSTAT, pipeb_stats);
        }
 
-       I915_WRITE(I915REG_INT_IDENTITY_R, iir);
+       I915_WRITE(IIR, iir);
+#ifdef __linux__
        if (dev->pdev->msi_enabled)
-               I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg);
-       (void) I915_READ(I915REG_INT_IDENTITY_R); /* Flush posted writes */
+               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 (dev_priv->sarea_priv)
-           dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
+       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) {
+               master_priv = dev->primary->master->driver_priv;
+               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) {
+#ifdef I915_HAVE_GEM
                dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
-               DRM_WAKEUP(&dev_priv->irq_queue);
-#ifdef I915_HAVE_FENCE
-               i915_fence_handler(dev);
 #endif
+               DRM_WAKEUP(&dev_priv->irq_queue);
        }
 
-       if (pipea_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
-                          I915_VBLANK_INTERRUPT_STATUS)) {
-               vblank = 1;
+       if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
+                          PIPE_VBLANK_INTERRUPT_STATUS)) {
+               vblank++;
                drm_handle_vblank(dev, i915_get_plane(dev, 0));
        }
-       if (pipeb_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
-                          I915_VBLANK_INTERRUPT_STATUS)) {
-               vblank = 1;
+
+       /* 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) {
                if (dev_priv->swaps_pending > 0)
                        drm_locked_tasklet(dev, i915_vblank_tasklet);
        }
 
+       if ((iir & I915_DISPLAY_PORT_INTERRUPT) || hotplug) {
+               u32 temp2 = 0;
+
+               DRM_INFO("Hotplug event received\n");
+
+               if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev)) {
+                       if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS)
+                               temp2 |= SDVOB_HOTPLUG_INT_STATUS |
+                                       SDVOC_HOTPLUG_INT_STATUS;
+                       if (pipea_stats & PIPE_HOTPLUG_TV_INTERRUPT_STATUS)
+                               temp2 |= TV_HOTPLUG_INT_STATUS;
+               } else {
+                       temp2 = I915_READ(PORT_HOTPLUG_STAT);
+
+                       I915_WRITE(PORT_HOTPLUG_STAT, temp2);
+               }
+               i915_run_hotplug_tasklet(dev, temp2);
+       }
+
        return IRQ_HANDLED;
 }
 
 int i915_emit_irq(struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        RING_LOCALS;
 
        i915_kernel_lost_context(dev);
@@ -552,24 +621,30 @@ 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;
 }
 
-void i915_user_irq_on(drm_i915_private_t *dev_priv)
+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))
                i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
        DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
 }
-
-void i915_user_irq_off(drm_i915_private_t *dev_priv)
+               
+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);
+#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);
@@ -578,7 +653,8 @@ void i915_user_irq_off(drm_i915_private_t *dev_priv)
 
 int i915_wait_irq(struct drm_device * dev, int irq_nr)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       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) {
@@ -589,26 +665,31 @@ int i915_wait_irq(struct drm_device * dev, int irq_nr)
        DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
                  READ_BREADCRUMB(dev_priv));
 
+       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) {
-               if (dev_priv->sarea_priv)
-                       dev_priv->sarea_priv->last_dispatch =
-                               READ_BREADCRUMB(dev_priv);
+               master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
                return 0;
        }
 
-       i915_user_irq_on(dev_priv);
+       i915_user_irq_on(dev);
        DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
                    READ_BREADCRUMB(dev_priv) >= irq_nr);
-       i915_user_irq_off(dev_priv);
+       i915_user_irq_off(dev);
 
        if (ret == -EBUSY) {
                DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
                          READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
        }
+       
+       if (READ_BREADCRUMB(dev_priv) >= irq_nr)
+               master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
 
-       if (dev_priv->sarea_priv)
-               dev_priv->sarea_priv->last_dispatch =
-                       READ_BREADCRUMB(dev_priv);
        return ret;
 }
 
@@ -617,8 +698,8 @@ int i915_wait_irq(struct drm_device * dev, int irq_nr)
 int i915_irq_emit(struct drm_device *dev, void *data,
                         struct drm_file *file_priv)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       drm_i915_irq_emit_t *emit = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_irq_emit *emit = data;
        int result;
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
@@ -643,8 +724,8 @@ int i915_irq_emit(struct drm_device *dev, void *data,
 int i915_irq_wait(struct drm_device *dev, void *data,
                  struct drm_file *file_priv)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       drm_i915_irq_wait_t *irqwait = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_irq_wait *irqwait = data;
 
        if (!dev_priv) {
                DRM_ERROR("called with no initialization\n");
@@ -656,7 +737,7 @@ int i915_irq_wait(struct drm_device *dev, void *data,
 
 int i915_enable_vblank(struct drm_device *dev, int plane)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       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;
@@ -664,11 +745,11 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
 
        switch (pipe) {
        case 0:
-               pipestat_reg = I915REG_PIPEASTAT;
+               pipestat_reg = PIPEASTAT;
                mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
-               pipestat_reg = I915REG_PIPEBSTAT;
+               pipestat_reg = PIPEBSTAT;
                mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
@@ -685,14 +766,14 @@ 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);
        }
        DRM_SPINLOCK(&dev_priv->user_irq_lock);
@@ -704,7 +785,7 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
 
 void i915_disable_vblank(struct drm_device *dev, int plane)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       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;
@@ -712,11 +793,11 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
 
        switch (pipe) {
        case 0:
-               pipestat_reg = I915REG_PIPEASTAT;
+               pipestat_reg = PIPEASTAT;
                mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
-               pipestat_reg = I915REG_PIPEBSTAT;
+               pipestat_reg = PIPEBSTAT;
                mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
@@ -728,42 +809,94 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
        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);
        }
 }
 
-static void i915_enable_interrupt (struct drm_device *dev)
+void i915_enable_interrupt (struct drm_device *dev)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
+       struct drm_connector *o;
        
-       dev_priv->irq_mask_reg = ~0;
-       I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg);
-       I915_WRITE(I915REG_INT_ENABLE_R, I915_INTERRUPT_ENABLE_MASK);
-       (void) I915_READ (I915REG_INT_ENABLE_R);
-       dev_priv->irq_enabled = 1;
-}
+       dev_priv->irq_mask_reg &= ~0;
 
-static void i915_disable_interrupt (struct drm_device *dev)
-{
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       
-       I915_WRITE(I915REG_HWSTAM, 0xffffffff);
-       I915_WRITE(I915REG_INT_MASK_R, 0xffffffff);
-       I915_WRITE(I915REG_INT_ENABLE_R, 0);
-       I915_WRITE(I915REG_INT_IDENTITY_R, 0xffffffff);
-       (void) I915_READ (I915REG_INT_IDENTITY_R);
-       dev_priv->irq_enabled = 0;
+       if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+               if (dev->mode_config.num_connector)
+                       dev_priv->irq_mask_reg &= ~I915_DISPLAY_PORT_INTERRUPT;
+       } else {
+               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(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_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)) {
+                       temp = I915_READ(PORT_HOTPLUG_EN);
+
+                       /* Activate the CRT */
+                       temp |= CRT_HOTPLUG_INT_EN;
+               }
+
+               if (IS_I9XX(dev)) {
+                       /* SDVOB */
+                       o = intel_sdvo_find(dev, 1);
+                       if (o && intel_sdvo_supports_hotplug(o)) {
+                               intel_sdvo_set_hotplug(o, 1);
+                               temp |= SDVOB_HOTPLUG_INT_EN;
+                       }
+
+                       /* SDVOC */
+                       o = intel_sdvo_find(dev, 0);
+                       if (o && intel_sdvo_supports_hotplug(o)) {
+                               intel_sdvo_set_hotplug(o, 1);
+                               temp |= SDVOC_HOTPLUG_INT_EN;
+                       }
+
+                       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 ???? */
+               }
+
+               if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+                       I915_WRITE(PORT_HOTPLUG_EN, temp);
+
+                       DRM_DEBUG("HEN %08x\n",I915_READ(PORT_HOTPLUG_EN));
+                       DRM_DEBUG("HST %08x\n",I915_READ(PORT_HOTPLUG_STAT));
+
+                       I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+               }
+       }
+
+#ifdef __linux__
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
+       opregion_enable_asle(dev);
+#endif
+#endif
+       dev_priv->irq_enabled = 1;
 }
 
 /* Set the vblank monitor pipe
@@ -771,37 +904,31 @@ static void i915_disable_interrupt (struct drm_device *dev)
 int i915_vblank_pipe_set(struct drm_device *dev, void *data,
                         struct drm_file *file_priv)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       drm_i915_vblank_pipe_t *pipe = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
        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;
 }
 
 int i915_vblank_pipe_get(struct drm_device *dev, void *data,
                         struct drm_file *file_priv)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       drm_i915_vblank_pipe_t *pipe = data;
-       u16 flag;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_vblank_pipe *pipe = data;
+       u32 flag = 0;
 
        if (!dev_priv) {
                DRM_ERROR("called with no initialization\n");
                return -EINVAL;
        }
 
-       flag = I915_READ(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)
                pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
@@ -817,9 +944,10 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
 int i915_vblank_swap(struct drm_device *dev, void *data,
                     struct drm_file *file_priv)
 {
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       drm_i915_vblank_swap_t *swap = data;
-       drm_i915_vbl_swap_t *vbl_swap;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_master_private *master_priv;
+       struct drm_i915_vblank_swap *swap = data;
+       struct drm_i915_vbl_swap *vbl_swap;
        unsigned int pipe, seqtype, curseq, plane;
        unsigned long irqflags;
        struct list_head *list;
@@ -830,7 +958,12 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       if (!dev_priv->sarea_priv || dev_priv->sarea_priv->rotation) {
+       if (!dev->primary->master)
+               return -EINVAL;
+
+       master_priv = dev->primary->master->driver_priv;
+
+       if (master_priv->sarea_priv->rotation) {
                DRM_DEBUG("Rotation not supported\n");
                return -EINVAL;
        }
@@ -867,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)
@@ -878,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;
                }
        }
@@ -899,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;
                        }
 
@@ -906,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;
                }
        }
@@ -913,7 +1055,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
        DRM_SPINLOCK_IRQSAVE(&dev_priv->swaps_lock, irqflags);
 
        list_for_each(list, &dev_priv->vbl_swaps.head) {
-               vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head);
+               vbl_swap = list_entry(list, struct drm_i915_vbl_swap, head);
 
                if (vbl_swap->drw_id == swap->drawable &&
                    vbl_swap->plane == plane &&
@@ -929,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;
        }
 
@@ -936,21 +1079,17 @@ 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;
        vbl_swap->flip = (swap->seqtype & _DRM_VBLANK_FLIP);
+       vbl_swap->minor = file_priv->minor;
 
        if (vbl_swap->flip)
                swap->sequence++;
@@ -969,33 +1108,29 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 */
 void i915_driver_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);
-       I915_WRITE(I915REG_HWSTAM, 0xffff);
-       I915_WRITE(I915REG_INT_ENABLE_R, 0x0);
-       I915_WRITE(I915REG_INT_MASK_R, 0xffffffff);
-       I915_WRITE(I915REG_INT_IDENTITY_R, 0xffffffff);
-       (void) I915_READ(I915REG_INT_IDENTITY_R);
+       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)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) 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_mask_reg = 0;
+       dev_priv->irq_mask_reg = ~0;
 
        ret = drm_vblank_init(dev, num_pipes);
        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);
@@ -1005,22 +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)
 {
-       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        u32 temp;
 
        if (!dev_priv)
                return;
 
-       i915_disable_interrupt (dev);
+       dev_priv->vblank_pipe = 0;
 
-       temp = I915_READ(I915REG_PIPEASTAT);
-       I915_WRITE(I915REG_PIPEASTAT, temp);
-       temp = I915_READ(I915REG_PIPEBSTAT);
-       I915_WRITE(I915REG_PIPEBSTAT, 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);
 }