Merge branch 'intelfb-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
[linux-2.6.git] / drivers / video / intelfb / intelfbhw.c
index 2a9322f..f887f1e 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
+#include <linux/interrupt.h>
 
 #include <asm/io.h>
 
@@ -368,7 +369,13 @@ intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
 
        offset += dinfo->fb.offset << 12;
 
-       OUTREG(DSPABASE, offset);
+       dinfo->vsync.pan_offset = offset;
+       if ((var->activate & FB_ACTIVATE_VBL) && !intelfbhw_enable_irq(dinfo, 0)) {
+               dinfo->vsync.pan_display = 1;
+       } else {
+               dinfo->vsync.pan_display = 0;
+               OUTREG(DSPABASE, offset);
+       }
 
        return 0;
 }
@@ -585,6 +592,11 @@ intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
        hw->fw_blc_0 = INREG(FW_BLC_0);
        hw->fw_blc_1 = INREG(FW_BLC_1);
 
+       hw->hwstam = INREG16(HWSTAM);
+       hw->ier = INREG16(IER);
+       hw->iir = INREG16(IIR);
+       hw->imr = INREG16(IMR);
+
        return 0;
 }
 
@@ -613,6 +625,7 @@ static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, int lvd
        return vco / p;
 }
 
+#if REGDUMP
 static void
 intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
 {
@@ -638,6 +651,7 @@ intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
        *o_p1 = p1;
        *o_p2 = p2;
 }
+#endif
 
 
 void
@@ -794,6 +808,10 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw)
        printk("        FW_BLC_0                0x%08x\n", hw->fw_blc_0);
        printk("        FW_BLC_1                0x%08x\n", hw->fw_blc_1);
 
+       printk("        HWSTAM                  0x%04x\n", hw->hwstam);
+       printk("        IER                     0x%04x\n", hw->ier);
+       printk("        IIR                     0x%04x\n", hw->iir);
+       printk("        IMR                     0x%04x\n", hw->imr);
        printk("hw state dump end\n");
 #endif
 }
@@ -1932,3 +1950,119 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) {
                addr += 16;
        }
 }
+
+static irqreturn_t
+intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) {
+       int handled = 0;
+       u16 tmp;
+       struct intelfb_info *dinfo = (struct intelfb_info *)dev_id;
+
+       spin_lock(&dinfo->int_lock);
+
+       tmp = INREG16(IIR);
+       tmp &= VSYNC_PIPE_A_INTERRUPT;
+
+       if (tmp == 0) {
+               spin_unlock(&dinfo->int_lock);
+               return IRQ_RETVAL(handled);
+       }
+
+       OUTREG16(IIR, tmp);
+
+       if (tmp & VSYNC_PIPE_A_INTERRUPT) {
+               dinfo->vsync.count++;
+               if (dinfo->vsync.pan_display) {
+                       dinfo->vsync.pan_display = 0;
+                       OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+               }
+               wake_up_interruptible(&dinfo->vsync.wait);
+               handled = 1;
+       }
+
+       spin_unlock(&dinfo->int_lock);
+
+       return IRQ_RETVAL(handled);
+}
+
+int
+intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) {
+
+       if (!test_and_set_bit(0, &dinfo->irq_flags)) {
+               if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "intelfb", dinfo)) {
+                       clear_bit(0, &dinfo->irq_flags);
+                       return -EINVAL;
+               }
+
+               spin_lock_irq(&dinfo->int_lock);
+               OUTREG16(HWSTAM, 0xfffe);
+               OUTREG16(IMR, 0x0);
+               OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT);
+               spin_unlock_irq(&dinfo->int_lock);
+       } else if (reenable) {
+               u16 ier;
+
+               spin_lock_irq(&dinfo->int_lock);
+               ier = INREG16(IER);
+               if ((ier & VSYNC_PIPE_A_INTERRUPT)) {
+                       DBG_MSG("someone disabled the IRQ [%08X]\n", ier);
+                       OUTREG(IER, VSYNC_PIPE_A_INTERRUPT);
+               }
+               spin_unlock_irq(&dinfo->int_lock);
+       }
+       return 0;
+}
+
+void
+intelfbhw_disable_irq(struct intelfb_info *dinfo) {
+       u16 tmp;
+
+       if (test_and_clear_bit(0, &dinfo->irq_flags)) {
+               if (dinfo->vsync.pan_display) {
+                       dinfo->vsync.pan_display = 0;
+                       OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+               }
+               spin_lock_irq(&dinfo->int_lock);
+               OUTREG16(HWSTAM, 0xffff);
+               OUTREG16(IMR, 0xffff);
+               OUTREG16(IER, 0x0);
+
+               tmp = INREG16(IIR);
+               OUTREG16(IIR, tmp);
+               spin_unlock_irq(&dinfo->int_lock);
+
+               free_irq(dinfo->pdev->irq, dinfo);
+       }
+}
+
+int
+intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) {
+       struct intelfb_vsync *vsync;
+       unsigned int count;
+       int ret;
+
+       switch (pipe) {
+               case 0:
+                       vsync = &dinfo->vsync;
+                       break;
+               default:
+                       return -ENODEV;
+       }
+
+       ret = intelfbhw_enable_irq(dinfo, 0);
+       if (ret) {
+               return ret;
+       }
+
+       count = vsync->count;
+       ret = wait_event_interruptible_timeout(vsync->wait, count != vsync->count, HZ/10);
+       if (ret < 0) {
+               return ret;
+       }
+       if (ret == 0) {
+               intelfbhw_enable_irq(dinfo, 1);
+               DBG_MSG("wait_for_vsync timed out!\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}