+
+static irqreturn_t intelfbhw_irq(int irq, void *dev_id)
+{
+ u16 tmp;
+ struct intelfb_info *dinfo = dev_id;
+
+ spin_lock(&dinfo->int_lock);
+
+ tmp = INREG16(IIR);
+ if (dinfo->info->var.vmode & FB_VMODE_INTERLACED)
+ tmp &= PIPE_A_EVENT_INTERRUPT;
+ else
+ tmp &= VSYNC_PIPE_A_INTERRUPT; /* non-interlaced */
+
+ if (tmp == 0) {
+ spin_unlock(&dinfo->int_lock);
+ return IRQ_RETVAL(0); /* not us */
+ }
+
+ /* clear status bits 0-15 ASAP and don't touch bits 16-31 */
+ OUTREG(PIPEASTAT, INREG(PIPEASTAT));
+
+ OUTREG16(IIR, tmp);
+ if (dinfo->vsync.pan_display) {
+ dinfo->vsync.pan_display = 0;
+ OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+ }
+
+ dinfo->vsync.count++;
+ wake_up_interruptible(&dinfo->vsync.wait);
+
+ spin_unlock(&dinfo->int_lock);
+
+ return IRQ_RETVAL(1);
+}
+
+int intelfbhw_enable_irq(struct intelfb_info *dinfo)
+{
+ u16 tmp;
+ if (!test_and_set_bit(0, &dinfo->irq_flags)) {
+ if (request_irq(dinfo->pdev->irq, intelfbhw_irq, IRQF_SHARED,
+ "intelfb", dinfo)) {
+ clear_bit(0, &dinfo->irq_flags);
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&dinfo->int_lock);
+ OUTREG16(HWSTAM, 0xfffe); /* i830 DRM uses ffff */
+ OUTREG16(IMR, 0);
+ } else
+ spin_lock_irq(&dinfo->int_lock);
+
+ if (dinfo->info->var.vmode & FB_VMODE_INTERLACED)
+ tmp = PIPE_A_EVENT_INTERRUPT;
+ else
+ tmp = VSYNC_PIPE_A_INTERRUPT; /* non-interlaced */
+ if (tmp != INREG16(IER)) {
+ DBG_MSG("changing IER to 0x%X\n", tmp);
+ OUTREG16(IER, tmp);
+ }
+
+ spin_unlock_irq(&dinfo->int_lock);
+ return 0;
+}
+
+void intelfbhw_disable_irq(struct intelfb_info *dinfo)
+{
+ 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);
+
+ OUTREG16(IIR, INREG16(IIR)); /* clear IRQ requests */
+ 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);
+ 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) {
+ DBG_MSG("wait_for_vsync timed out!\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}