fbdev: move FBIO_WAITFORVSYNC to linux/fb.h
[safe/jmp/linux-2.6] / drivers / video / intelfb / intelfbhw.c
index a02ee24..38065cf 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Linux framebuffer driver for Intel(R) 865G integrated graphics chips.
  *
- * Copyright © 2002, 2003 David Dawes <dawes@xfree86.org>
+ * Copyright Â© 2002, 2003 David Dawes <dawes@xfree86.org>
  *                   2004 Sylvain Meyer
  *
  * This driver consists of two parts.  The first part (intelfbdrv.c) provides
@@ -24,7 +24,6 @@
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/mm.h>
-#include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/fb.h>
 #include <linux/ioport.h>
@@ -84,6 +83,11 @@ int intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo)
                dinfo->mobile = 0;
                dinfo->pll_index = PLLS_I8xx;
                return 0;
+       case PCI_DEVICE_ID_INTEL_854:
+               dinfo->mobile = 1;
+               dinfo->name = "Intel(R) 854";
+               dinfo->chipset = INTEL_854;
+               return 0;
        case PCI_DEVICE_ID_INTEL_85XGM:
                tmp = 0;
                dinfo->mobile = 1;
@@ -143,6 +147,24 @@ int intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo)
                dinfo->mobile = 1;
                dinfo->pll_index = PLLS_I9xx;
                return 0;
+       case PCI_DEVICE_ID_INTEL_945GME:
+               dinfo->name = "Intel(R) 945GME";
+               dinfo->chipset = INTEL_945GME;
+               dinfo->mobile = 1;
+               dinfo->pll_index = PLLS_I9xx;
+               return 0;
+       case PCI_DEVICE_ID_INTEL_965G:
+               dinfo->name = "Intel(R) 965G";
+               dinfo->chipset = INTEL_965G;
+               dinfo->mobile = 0;
+               dinfo->pll_index = PLLS_I9xx;
+               return 0;
+       case PCI_DEVICE_ID_INTEL_965GM:
+               dinfo->name = "Intel(R) 965GM";
+               dinfo->chipset = INTEL_965GM;
+               dinfo->mobile = 1;
+               dinfo->pll_index = PLLS_I9xx;
+               return 0;
        default:
                return 1;
        }
@@ -174,7 +196,10 @@ int intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size,
        case PCI_DEVICE_ID_INTEL_915GM:
        case PCI_DEVICE_ID_INTEL_945G:
        case PCI_DEVICE_ID_INTEL_945GM:
-               /* 915 and 945 chipsets support a 256MB aperture.
+       case PCI_DEVICE_ID_INTEL_945GME:
+       case PCI_DEVICE_ID_INTEL_965G:
+       case PCI_DEVICE_ID_INTEL_965GM:
+               /* 915, 945 and 965 chipsets support a 256MB aperture.
                   Aperture size is determined by inspected the
                   base address of the aperture. */
                if (pci_resource_start(pdev, 2) & 0x08000000)
@@ -376,7 +401,7 @@ int intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
 
        dinfo->vsync.pan_offset = offset;
        if ((var->activate & FB_ACTIVATE_VBL) &&
-           !intelfbhw_enable_irq(dinfo, 0))
+           !intelfbhw_enable_irq(dinfo))
                dinfo->vsync.pan_display = 1;
        else {
                dinfo->vsync.pan_display = 0;
@@ -443,6 +468,32 @@ void intelfbhw_do_blank(int blank, struct fb_info *info)
 }
 
 
+/* Check which pipe is connected to an active display plane. */
+int intelfbhw_active_pipe(const struct intelfb_hwstate *hw)
+{
+       int pipe = -1;
+
+       /* keep old default behaviour - prefer PIPE_A */
+       if (hw->disp_b_ctrl & DISPPLANE_PLANE_ENABLE) {
+               pipe = (hw->disp_b_ctrl >> DISPPLANE_SEL_PIPE_SHIFT);
+               pipe &= PIPE_MASK;
+               if (unlikely(pipe == PIPE_A))
+                       return PIPE_A;
+       }
+       if (hw->disp_a_ctrl & DISPPLANE_PLANE_ENABLE) {
+               pipe = (hw->disp_a_ctrl >> DISPPLANE_SEL_PIPE_SHIFT);
+               pipe &= PIPE_MASK;
+               if (likely(pipe == PIPE_A))
+                       return PIPE_A;
+       }
+       /* Impossible that no pipe is selected - return PIPE_A */
+       WARN_ON(pipe == -1);
+       if (unlikely(pipe == -1))
+               pipe = PIPE_A;
+
+       return pipe;
+}
+
 void intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno,
                         unsigned red, unsigned green, unsigned blue,
                         unsigned transp)
@@ -993,7 +1044,7 @@ int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
                         struct intelfb_hwstate *hw,
                         struct fb_var_screeninfo *var)
 {
-       int pipe = PIPE_A;
+       int pipe = intelfbhw_active_pipe(hw);
        u32 *dpll, *fp0, *fp1;
        u32 m1, m2, n, p1, p2, clock_target, clock;
        u32 hsync_start, hsync_end, hblank_start, hblank_end, htotal, hactive;
@@ -1007,12 +1058,6 @@ int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
        /* Disable VGA */
        hw->vgacntrl |= VGA_DISABLE;
 
-       /* Check whether pipe A or pipe B is enabled. */
-       if (hw->pipe_a_conf & PIPECONF_ENABLE)
-               pipe = PIPE_A;
-       else if (hw->pipe_b_conf & PIPECONF_ENABLE)
-               pipe = PIPE_B;
-
        /* Set which pipe's registers will be set. */
        if (pipe == PIPE_B) {
                dpll = &hw->dpll_b;
@@ -1236,17 +1281,16 @@ int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
 int intelfbhw_program_mode(struct intelfb_info *dinfo,
                           const struct intelfb_hwstate *hw, int blank)
 {
-       int pipe = PIPE_A;
        u32 tmp;
        const u32 *dpll, *fp0, *fp1, *pipe_conf;
        const u32 *hs, *ht, *hb, *vs, *vt, *vb, *ss;
-       u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg;
+       u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg, pipe_stat_reg;
        u32 hsync_reg, htotal_reg, hblank_reg;
        u32 vsync_reg, vtotal_reg, vblank_reg;
        u32 src_size_reg;
        u32 count, tmp_val[3];
 
-       /* Assume single pipe, display plane A, analog CRT. */
+       /* Assume single pipe */
 
 #if VERBOSE > 0
        DBG_MSG("intelfbhw_program_mode\n");
@@ -1257,15 +1301,9 @@ int intelfbhw_program_mode(struct intelfb_info *dinfo,
        tmp |= VGA_DISABLE;
        OUTREG(VGACNTRL, tmp);
 
-       /* Check whether pipe A or pipe B is enabled. */
-       if (hw->pipe_a_conf & PIPECONF_ENABLE)
-               pipe = PIPE_A;
-       else if (hw->pipe_b_conf & PIPECONF_ENABLE)
-               pipe = PIPE_B;
-
-       dinfo->pipe = pipe;
+       dinfo->pipe = intelfbhw_active_pipe(hw);
 
-       if (pipe == PIPE_B) {
+       if (dinfo->pipe == PIPE_B) {
                dpll = &hw->dpll_b;
                fp0 = &hw->fpb0;
                fp1 = &hw->fpb1;
@@ -1281,6 +1319,7 @@ int intelfbhw_program_mode(struct intelfb_info *dinfo,
                fp0_reg = FPB0;
                fp1_reg = FPB1;
                pipe_conf_reg = PIPEBCONF;
+               pipe_stat_reg = PIPEBSTAT;
                hsync_reg = HSYNC_B;
                htotal_reg = HTOTAL_B;
                hblank_reg = HBLANK_B;
@@ -1304,6 +1343,7 @@ int intelfbhw_program_mode(struct intelfb_info *dinfo,
                fp0_reg = FPA0;
                fp1_reg = FPA1;
                pipe_conf_reg = PIPEACONF;
+               pipe_stat_reg = PIPEASTAT;
                hsync_reg = HSYNC_A;
                htotal_reg = HTOTAL_A;
                hblank_reg = HBLANK_A;
@@ -1390,6 +1430,17 @@ int intelfbhw_program_mode(struct intelfb_info *dinfo,
        OUTREG(vtotal_reg, *vt);
        OUTREG(src_size_reg, *ss);
 
+       switch (dinfo->info->var.vmode & (FB_VMODE_INTERLACED |
+                                         FB_VMODE_ODD_FLD_FIRST)) {
+       case FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST:
+               OUTREG(pipe_stat_reg, 0xFFFF | PIPESTAT_FLD_EVT_ODD_EN);
+               break;
+       case FB_VMODE_INTERLACED: /* even lines first */
+               OUTREG(pipe_stat_reg, 0xFFFF | PIPESTAT_FLD_EVT_EVEN_EN);
+               break;
+       default:                /* non-interlaced */
+               OUTREG(pipe_stat_reg, 0xFFFF); /* clear all status bits only */
+       }
        /* Enable pipe */
        OUTREG(pipe_conf_reg, *pipe_conf | PIPECONF_ENABLE);
 
@@ -1955,71 +2006,72 @@ void intelfbhw_cursor_reset(struct intelfb_info *dinfo)
        }
 }
 
-static irqreturn_t
-intelfbhw_irq(int irq, void *dev_id) {
-       int handled = 0;
+static irqreturn_t intelfbhw_irq(int irq, void *dev_id)
+{
        u16 tmp;
-       struct intelfb_info *dinfo = (struct intelfb_info *)dev_id;
+       struct intelfb_info *dinfo = dev_id;
 
        spin_lock(&dinfo->int_lock);
 
        tmp = INREG16(IIR);
-       tmp &= VSYNC_PIPE_A_INTERRUPT;
+       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(handled);
+               return IRQ_RETVAL(0); /* not us */
        }
 
-       OUTREG16(IIR, tmp);
+       /* clear status bits 0-15 ASAP and don't touch bits 16-31 */
+       OUTREG(PIPEASTAT, INREG(PIPEASTAT));
 
-       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;
+       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(handled);
+       return IRQ_RETVAL(1);
 }
 
-int
-intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) {
-
+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)) {
+                               "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;
-
+               OUTREG16(HWSTAM, 0xfffe); /* i830 DRM uses ffff */
+               OUTREG16(IMR, 0);
+       } else
                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);
+
+       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) {
-
+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;
@@ -2051,7 +2103,7 @@ int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe)
                        return -ENODEV;
        }
 
-       ret = intelfbhw_enable_irq(dinfo, 0);
+       ret = intelfbhw_enable_irq(dinfo);
        if (ret)
                return ret;
 
@@ -2061,7 +2113,6 @@ int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe)
        if (ret < 0)
                return ret;
        if (ret == 0) {
-               intelfbhw_enable_irq(dinfo, 1);
                DBG_MSG("wait_for_vsync timed out!\n");
                return -ETIMEDOUT;
        }