+
+static irqreturn_t
+intelfbhw_irq(int irq, void *dev_id) {
+ 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;
+}