Merge commit 'origin/master' into modesetting-gem
[profile/ivi/libdrm.git] / shared-core / i915_irq.c
index d507d76..b4004a8 100644 (file)
 #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)
 
+/*
+ * 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
@@ -45,7 +74,7 @@
 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);
@@ -82,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)
@@ -100,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;
 
@@ -145,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,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);
@@ -213,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);
 
@@ -271,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;
@@ -323,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;
 
@@ -353,8 +386,8 @@ 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);
 
@@ -364,7 +397,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
 
 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;
@@ -398,17 +431,78 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
        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, pipeb_stats;
+       u32 pipea_stats = 0, pipeb_stats = 0, tvdac;
+       int hotplug = 0;
        int vblank = 0;
 
+       if (dev->pdev->msi_enabled)
+               I915_WRITE(IMR, ~0);
        iir = I915_READ(IIR);
-       if (iir == 0)
-               return IRQ_NONE;
+       atomic_inc(&dev_priv->irq_received);
+       if (iir == 0) {
+               if (dev->pdev->msi_enabled) {
+                       I915_WRITE(IMR, dev_priv->irq_mask_reg);
+                       (void) I915_READ(IMR);
+               }
+       }
 
        /*
         * Clear the PIPE(A|B)STAT regs before the IIR otherwise
@@ -416,72 +510,101 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
         */
        if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
                pipea_stats = I915_READ(PIPEASTAT);
-               if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
-                                  PIPE_VBLANK_INTERRUPT_STATUS))
-               {
-                       vblank++;
-                       drm_handle_vblank(dev, i915_get_plane(dev, 0));
-               }
-
                I915_WRITE(PIPEASTAT, pipea_stats);
        }
+
        if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
                pipeb_stats = I915_READ(PIPEBSTAT);
-               /* Ack the event */
                I915_WRITE(PIPEBSTAT, pipeb_stats);
+       }
 
-               /* 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));
-               }
+       I915_WRITE(IIR, iir);
+       if (dev->pdev->msi_enabled)
+               I915_WRITE(IMR, dev_priv->irq_mask_reg);
+       (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);
+       }
 
-#ifdef __linux__
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
-               if (pipeb_stats & I915_LEGACY_BLC_EVENT_ENABLE)
-                       opregion_asle_intr(dev);
-#endif
-#endif
-               I915_WRITE(PIPEBSTAT, pipeb_stats);
+       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 (dev_priv->sarea_priv)
-           dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
-
-       I915_WRITE(IIR, iir);
-       (void) I915_READ(IIR);
-
        if (iir & I915_USER_INTERRUPT) {
+               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
        }
 
+       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) {
                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);
@@ -498,52 +621,67 @@ int i915_emit_irq(struct drm_device *dev)
        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)){
-               dev_priv->irq_enable_reg |= I915_USER_INTERRUPT;
-               I915_WRITE(IER, 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(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);
-       if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
-               //              dev_priv->irq_enable_reg &= ~I915_USER_INTERRUPT;
-               //              I915_WRITE(IER, dev_priv->irq_enable_reg);
-       }
+       BUG_ON(dev_priv->irq_enabled && dev_priv->user_irq_refcount <= 0);
+       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)
 {
-       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) {
+               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_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;
 }
 
@@ -552,8 +690,8 @@ static 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);
@@ -578,8 +716,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");
@@ -591,19 +729,20 @@ 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;
        u32     pipestat;
 
        switch (pipe) {
        case 0:
                pipestat_reg = PIPEASTAT;
-               dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
                pipestat_reg = PIPEBSTAT;
-               dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
                DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
@@ -629,26 +768,30 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
                             PIPE_VBLANK_INTERRUPT_STATUS);
                I915_WRITE(pipestat_reg, pipestat);
        }
-       I915_WRITE(IER, 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;
 }
 
 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;
        u32     pipestat;
 
        switch (pipe) {
        case 0:
                pipestat_reg = PIPEASTAT;
-               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
                break;
        case 1:
                pipestat_reg = PIPEBSTAT;
-               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                break;
        default:
                DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
@@ -656,10 +799,11 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
                break;
        }
 
-       I915_WRITE(IER, 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 &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
                              PIPE_VBLANK_INTERRUPT_ENABLE);
@@ -669,14 +813,76 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
                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)
 {
-       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_enable_reg |= I915_USER_INTERRUPT;
+       dev_priv->irq_mask_reg &= ~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)
@@ -684,7 +890,10 @@ void i915_enable_interrupt (struct drm_device *dev)
 #endif
 #endif
 
-       I915_WRITE(IER, dev_priv->irq_enable_reg);
+       I915_WRITE(IMR, dev_priv->irq_mask_reg);
+       I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
+       (void) I915_READ (IER);
+
        dev_priv->irq_enabled = 1;
 }
 
@@ -693,7 +902,7 @@ void i915_enable_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;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (!dev_priv) {
                DRM_ERROR("called with no initialization\n");
@@ -706,15 +915,23 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
 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;
+       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;
        }
 
-       pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
+       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;
+       if (flag & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
+               pipe->pipe |= DRM_I915_VBLANK_PIPE_B;
 
        return 0;
 }
@@ -725,9 +942,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;
@@ -738,7 +956,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;
        }
@@ -830,7 +1053,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 &&
@@ -864,6 +1087,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
        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++;