diff options
author | Eric Hustvedt <ehustvedt@cecropia.com> | 2006-06-20 14:36:41 -0400 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2006-07-03 18:59:46 +1000 |
commit | 7649757bd900bc900adcd95ab08903cdc28342fa (patch) | |
tree | 4c710d9e458ff3c6731180aca738123886f7adec /drivers/video/intelfb/intelfbhw.c | |
parent | 9a5f019b1a9ea6a75ba36d7c312ff069006ed479 (diff) |
intelfb: add vsync interrupt support
[03/05] intelfb: Implement basic interrupt handling
Functions have been added to enable and disable interrupts using the MMIO registers. Currently only pipe A vsync interrupts are enabled.
A generalized vsync accounting struct is defined, with the intent that it can encapsulate per-pipe vsync related info in the future. Currently a single instance is hard-coded.
The interrupt service routine currently only looks for vsync interrupts on pipe A, and increments a counter and wakes up anyone waiting on it.
This implementation is heavily influenced by similar implementations in the atyfb and matroxfb drivers.
Signed-off-by: Eric Hustvedt <ehustvedt@cecropia.com>
Diffstat (limited to 'drivers/video/intelfb/intelfbhw.c')
-rw-r--r-- | drivers/video/intelfb/intelfbhw.c | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 05aded669cd..1a698a7230e 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -34,6 +34,7 @@ #include <linux/pci.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> +#include <linux/interrupt.h> #include <asm/io.h> @@ -1943,3 +1944,78 @@ 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++; + 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)) { + 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); + } +} |