vsprintf: give it some care to please checkpatch.pl
[safe/jmp/linux-2.6] / drivers / video / tdfxfb.c
index 6766425..9805483 100644 (file)
  * Created      : Thu Sep 23 18:17:43 1999, hmallat
  * Last modified: Tue Nov  2 21:19:47 1999, hmallat
  *
+ * I2C part copied from the i2c-voodoo3.c driver by:
+ * Frodo Looijaard <frodol@dds.nl>,
+ * Philip Edelbrock <phil@netroedge.com>,
+ * Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+ * Mark D. Studebaker <mdsxyz123@yahoo.com>
+ *
  * Lots of the information here comes from the Daryll Strauss' Banshee
  * patches to the XF86 server, and the rest comes from the 3dfx
  * Banshee specification. I'm very much indebted to Daryll for his
@@ -46,7 +52,7 @@
  *
  * 0.1.3 (released 1999-11-02) added Attila's panning support, code
  *                             reorg, hwcursor address page size alignment
- *                             (for mmaping both frame buffer and regs),
+ *                             (for mmapping both frame buffer and regs),
  *                             and my changes to get rid of hardcoded
  *                             VGA i/o register locations (uses PCI
  *                             configuration info now)
@@ -95,7 +101,6 @@ static inline int mtrr_del(int reg, unsigned long base,
 #define VOODOO5_MAX_PIXCLOCK 350000
 
 static struct fb_fix_screeninfo tdfx_fix __devinitdata = {
-       .id =           "3Dfx",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_PSEUDOCOLOR,
        .ypanstep =     1,
@@ -426,7 +431,7 @@ static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short dev_id)
        if (dev_id < PCI_DEVICE_ID_3DFX_VOODOO5) {
                /* Banshee/Voodoo3 */
                chip_size = 2;
-               if (has_sgram && (draminit0 & DRAMINIT0_SGRAM_TYPE))
+               if (has_sgram && !(draminit0 & DRAMINIT0_SGRAM_TYPE))
                        chip_size = 1;
        } else {
                /* Voodoo4/5 */
@@ -482,6 +487,12 @@ static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
                return -EINVAL;
        }
 
+       if (info->monspecs.hfmax && info->monspecs.vfmax &&
+           info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) {
+               DPRINTK("mode outside monitor's specs\n");
+               return -EINVAL;
+       }
+
        var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */
        lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3);
 
@@ -836,16 +847,12 @@ static int tdfxfb_pan_display(struct fb_var_screeninfo *var,
        struct tdfx_par *par = info->par;
        u32 addr = var->yoffset * info->fix.line_length;
 
-       if (nopan || var->xoffset || (var->yoffset > var->yres_virtual))
-               return -EINVAL;
-       if ((var->yoffset + var->yres > var->yres_virtual && nowrap))
+       if (nopan || var->xoffset)
                return -EINVAL;
 
        banshee_make_room(par, 1);
        tdfx_outl(par, VIDDESKSTART, addr);
 
-       info->var.xoffset = var->xoffset;
-       info->var.yoffset = var->yoffset;
        return 0;
 }
 
@@ -1172,6 +1179,206 @@ static struct fb_ops tdfxfb_ops = {
 #endif
 };
 
+#ifdef CONFIG_FB_3DFX_I2C
+/* The voo GPIO registers don't have individual masks for each bit
+   so we always have to read before writing. */
+
+static void tdfxfb_i2c_setscl(void *data, int val)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+       unsigned int r;
+
+       r = tdfx_inl(par, VIDSERPARPORT);
+       if (val)
+               r |= I2C_SCL_OUT;
+       else
+               r &= ~I2C_SCL_OUT;
+       tdfx_outl(par, VIDSERPARPORT, r);
+       tdfx_inl(par, VIDSERPARPORT);   /* flush posted write */
+}
+
+static void tdfxfb_i2c_setsda(void *data, int val)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+       unsigned int r;
+
+       r = tdfx_inl(par, VIDSERPARPORT);
+       if (val)
+               r |= I2C_SDA_OUT;
+       else
+               r &= ~I2C_SDA_OUT;
+       tdfx_outl(par, VIDSERPARPORT, r);
+       tdfx_inl(par, VIDSERPARPORT);   /* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins always remain outputs.
+   We rely on the i2c-algo-bit routines to set the pins high before
+   reading the input from other chips. */
+
+static int tdfxfb_i2c_getscl(void *data)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+
+       return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SCL_IN));
+}
+
+static int tdfxfb_i2c_getsda(void *data)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+
+       return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SDA_IN));
+}
+
+static void tdfxfb_ddc_setscl(void *data, int val)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+       unsigned int r;
+
+       r = tdfx_inl(par, VIDSERPARPORT);
+       if (val)
+               r |= DDC_SCL_OUT;
+       else
+               r &= ~DDC_SCL_OUT;
+       tdfx_outl(par, VIDSERPARPORT, r);
+       tdfx_inl(par, VIDSERPARPORT);   /* flush posted write */
+}
+
+static void tdfxfb_ddc_setsda(void *data, int val)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+       unsigned int r;
+
+       r = tdfx_inl(par, VIDSERPARPORT);
+       if (val)
+               r |= DDC_SDA_OUT;
+       else
+               r &= ~DDC_SDA_OUT;
+       tdfx_outl(par, VIDSERPARPORT, r);
+       tdfx_inl(par, VIDSERPARPORT);   /* flush posted write */
+}
+
+static int tdfxfb_ddc_getscl(void *data)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+
+       return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SCL_IN));
+}
+
+static int tdfxfb_ddc_getsda(void *data)
+{
+       struct tdfxfb_i2c_chan  *chan = data;
+       struct tdfx_par         *par = chan->par;
+
+       return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SDA_IN));
+}
+
+static int __devinit tdfxfb_setup_ddc_bus(struct tdfxfb_i2c_chan *chan,
+                                         const char *name, struct device *dev)
+{
+       int rc;
+
+       strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name));
+       chan->adapter.owner             = THIS_MODULE;
+       chan->adapter.class             = I2C_CLASS_DDC;
+       chan->adapter.algo_data         = &chan->algo;
+       chan->adapter.dev.parent        = dev;
+       chan->algo.setsda               = tdfxfb_ddc_setsda;
+       chan->algo.setscl               = tdfxfb_ddc_setscl;
+       chan->algo.getsda               = tdfxfb_ddc_getsda;
+       chan->algo.getscl               = tdfxfb_ddc_getscl;
+       chan->algo.udelay               = 10;
+       chan->algo.timeout              = msecs_to_jiffies(500);
+       chan->algo.data                 = chan;
+
+       i2c_set_adapdata(&chan->adapter, chan);
+
+       rc = i2c_bit_add_bus(&chan->adapter);
+       if (rc == 0)
+               DPRINTK("I2C bus %s registered.\n", name);
+       else
+               chan->par = NULL;
+
+       return rc;
+}
+
+static int __devinit tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan,
+                                         const char *name, struct device *dev)
+{
+       int rc;
+
+       strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name));
+       chan->adapter.owner             = THIS_MODULE;
+       chan->adapter.algo_data         = &chan->algo;
+       chan->adapter.dev.parent        = dev;
+       chan->algo.setsda               = tdfxfb_i2c_setsda;
+       chan->algo.setscl               = tdfxfb_i2c_setscl;
+       chan->algo.getsda               = tdfxfb_i2c_getsda;
+       chan->algo.getscl               = tdfxfb_i2c_getscl;
+       chan->algo.udelay               = 10;
+       chan->algo.timeout              = msecs_to_jiffies(500);
+       chan->algo.data                 = chan;
+
+       i2c_set_adapdata(&chan->adapter, chan);
+
+       rc = i2c_bit_add_bus(&chan->adapter);
+       if (rc == 0)
+               DPRINTK("I2C bus %s registered.\n", name);
+       else
+               chan->par = NULL;
+
+       return rc;
+}
+
+static void __devinit tdfxfb_create_i2c_busses(struct fb_info *info)
+{
+       struct tdfx_par *par = info->par;
+
+       tdfx_outl(par, VIDINFORMAT, 0x8160);
+       tdfx_outl(par, VIDSERPARPORT, 0xcffc0020);
+
+       par->chan[0].par = par;
+       par->chan[1].par = par;
+
+       tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->dev);
+       tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->dev);
+}
+
+static void tdfxfb_delete_i2c_busses(struct tdfx_par *par)
+{
+       if (par->chan[0].par)
+               i2c_del_adapter(&par->chan[0].adapter);
+       par->chan[0].par = NULL;
+
+       if (par->chan[1].par)
+               i2c_del_adapter(&par->chan[1].adapter);
+       par->chan[1].par = NULL;
+}
+
+static int tdfxfb_probe_i2c_connector(struct tdfx_par *par,
+                                     struct fb_monspecs *specs)
+{
+       u8 *edid = NULL;
+
+       DPRINTK("Probe DDC Bus\n");
+       if (par->chan[0].par)
+               edid = fb_ddc_read(&par->chan[0].adapter);
+
+       if (edid) {
+               fb_edid_to_monspecs(edid, specs);
+               kfree(edid);
+               return 0;
+       }
+       return 1;
+}
+#endif /* CONFIG_FB_3DFX_I2C */
+
 /**
  *      tdfxfb_probe - Device Initializiation
  *
@@ -1187,6 +1394,8 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
        struct tdfx_par *default_par;
        struct fb_info *info;
        int err, lpitch;
+       struct fb_monspecs *specs;
+       bool found;
 
        err = pci_enable_device(pdev);
        if (err) {
@@ -1200,57 +1409,58 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
                return -ENOMEM;
 
        default_par = info->par;
+       info->fix = tdfx_fix;
 
        /* Configure the default fb_fix_screeninfo first */
        switch (pdev->device) {
        case PCI_DEVICE_ID_3DFX_BANSHEE:
-               strcat(tdfx_fix.id, " Banshee");
+               strcpy(info->fix.id, "3Dfx Banshee");
                default_par->max_pixclock = BANSHEE_MAX_PIXCLOCK;
                break;
        case PCI_DEVICE_ID_3DFX_VOODOO3:
-               strcat(tdfx_fix.id, " Voodoo3");
+               strcpy(info->fix.id, "3Dfx Voodoo3");
                default_par->max_pixclock = VOODOO3_MAX_PIXCLOCK;
                break;
        case PCI_DEVICE_ID_3DFX_VOODOO5:
-               strcat(tdfx_fix.id, " Voodoo5");
+               strcpy(info->fix.id, "3Dfx Voodoo5");
                default_par->max_pixclock = VOODOO5_MAX_PIXCLOCK;
                break;
        }
 
-       tdfx_fix.mmio_start = pci_resource_start(pdev, 0);
-       tdfx_fix.mmio_len = pci_resource_len(pdev, 0);
-       if (!request_mem_region(tdfx_fix.mmio_start, tdfx_fix.mmio_len,
+       info->fix.mmio_start = pci_resource_start(pdev, 0);
+       info->fix.mmio_len = pci_resource_len(pdev, 0);
+       if (!request_mem_region(info->fix.mmio_start, info->fix.mmio_len,
                                "tdfx regbase")) {
                printk(KERN_ERR "tdfxfb: Can't reserve regbase\n");
                goto out_err;
        }
 
        default_par->regbase_virt =
-               ioremap_nocache(tdfx_fix.mmio_start, tdfx_fix.mmio_len);
+               ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
        if (!default_par->regbase_virt) {
                printk(KERN_ERR "fb: Can't remap %s register area.\n",
-                               tdfx_fix.id);
+                               info->fix.id);
                goto out_err_regbase;
        }
 
-       tdfx_fix.smem_start = pci_resource_start(pdev, 1);
-       tdfx_fix.smem_len = do_lfb_size(default_par, pdev->device);
-       if (!tdfx_fix.smem_len) {
-               printk(KERN_ERR "fb: Can't count %s memory.\n", tdfx_fix.id);
+       info->fix.smem_start = pci_resource_start(pdev, 1);
+       info->fix.smem_len = do_lfb_size(default_par, pdev->device);
+       if (!info->fix.smem_len) {
+               printk(KERN_ERR "fb: Can't count %s memory.\n", info->fix.id);
                goto out_err_regbase;
        }
 
-       if (!request_mem_region(tdfx_fix.smem_start,
+       if (!request_mem_region(info->fix.smem_start,
                                pci_resource_len(pdev, 1), "tdfx smem")) {
                printk(KERN_ERR "tdfxfb: Can't reserve smem\n");
                goto out_err_regbase;
        }
 
-       info->screen_base = ioremap_nocache(tdfx_fix.smem_start,
-                                           tdfx_fix.smem_len);
+       info->screen_base = ioremap_nocache(info->fix.smem_start,
+                                           info->fix.smem_len);
        if (!info->screen_base) {
                printk(KERN_ERR "fb: Can't remap %s framebuffer.\n",
-                               tdfx_fix.id);
+                               info->fix.id);
                goto out_err_screenbase;
        }
 
@@ -1262,20 +1472,19 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
                goto out_err_screenbase;
        }
 
-       printk(KERN_INFO "fb: %s memory = %dK\n", tdfx_fix.id,
-                       tdfx_fix.smem_len >> 10);
+       printk(KERN_INFO "fb: %s memory = %dK\n", info->fix.id,
+                       info->fix.smem_len >> 10);
 
        default_par->mtrr_handle = -1;
        if (!nomtrr)
                default_par->mtrr_handle =
-                       mtrr_add(tdfx_fix.smem_start, tdfx_fix.smem_len,
+                       mtrr_add(info->fix.smem_start, info->fix.smem_len,
                                 MTRR_TYPE_WRCOMB, 1);
 
-       tdfx_fix.ypanstep       = nopan ? 0 : 1;
-       tdfx_fix.ywrapstep      = nowrap ? 0 : 1;
+       info->fix.ypanstep      = nopan ? 0 : 1;
+       info->fix.ywrapstep     = nowrap ? 0 : 1;
 
        info->fbops             = &tdfxfb_ops;
-       info->fix               = tdfx_fix;
        info->pseudo_palette    = default_par->palette;
        info->flags             = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
 #ifdef CONFIG_FB_3DFX_ACCEL
@@ -1289,13 +1498,49 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
        if (hwcursor)
                info->fix.smem_len = (info->fix.smem_len - 1024) &
                                        (PAGE_MASK << 1);
-
-       if (!mode_option)
+       specs = &info->monspecs;
+       found = false;
+       info->var.bits_per_pixel = 8;
+#ifdef CONFIG_FB_3DFX_I2C
+       tdfxfb_create_i2c_busses(info);
+       err = tdfxfb_probe_i2c_connector(default_par, specs);
+
+       if (!err) {
+               if (specs->modedb == NULL)
+                       DPRINTK("Unable to get Mode Database\n");
+               else {
+                       const struct fb_videomode *m;
+
+                       fb_videomode_to_modelist(specs->modedb,
+                                                specs->modedb_len,
+                                                &info->modelist);
+                       m = fb_find_best_display(specs, &info->modelist);
+                       if (m) {
+                               fb_videomode_to_var(&info->var, m);
+                               /* fill all other info->var's fields */
+                               if (tdfxfb_check_var(&info->var, info) < 0)
+                                       info->var = tdfx_var;
+                               else
+                                       found = true;
+                       }
+               }
+       }
+#endif
+       if (!mode_option && !found)
                mode_option = "640x480@60";
 
-       err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
-       if (!err || err == 4)
-               info->var = tdfx_var;
+       if (mode_option) {
+               err = fb_find_mode(&info->var, info, mode_option,
+                                  specs->modedb, specs->modedb_len,
+                                  NULL, info->var.bits_per_pixel);
+               if (!err || err == 4)
+                       info->var = tdfx_var;
+       }
+
+       if (found) {
+               fb_destroy_modedb(specs->modedb);
+               specs->modedb = NULL;
+       }
 
        /* maximize virtual vertical length */
        lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3);
@@ -1320,6 +1565,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
        return 0;
 
 out_err_iobase:
+#ifdef CONFIG_FB_3DFX_I2C
+       tdfxfb_delete_i2c_busses(default_par);
+#endif
        if (default_par->mtrr_handle >= 0)
                mtrr_del(default_par->mtrr_handle, info->fix.smem_start,
                         info->fix.smem_len);
@@ -1328,14 +1576,14 @@ out_err_iobase:
 out_err_screenbase:
        if (info->screen_base)
                iounmap(info->screen_base);
-       release_mem_region(tdfx_fix.smem_start, pci_resource_len(pdev, 1));
+       release_mem_region(info->fix.smem_start, pci_resource_len(pdev, 1));
 out_err_regbase:
        /*
         * Cleanup after anything that was remapped/allocated.
         */
        if (default_par->regbase_virt)
                iounmap(default_par->regbase_virt);
-       release_mem_region(tdfx_fix.mmio_start, tdfx_fix.mmio_len);
+       release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
 out_err:
        framebuffer_release(info);
        return -ENXIO;
@@ -1384,6 +1632,9 @@ static void __devexit tdfxfb_remove(struct pci_dev *pdev)
        struct tdfx_par *par = info->par;
 
        unregister_framebuffer(info);
+#ifdef CONFIG_FB_3DFX_I2C
+       tdfxfb_delete_i2c_busses(par);
+#endif
        if (par->mtrr_handle >= 0)
                mtrr_del(par->mtrr_handle, info->fix.smem_start,
                         info->fix.smem_len);
@@ -1398,6 +1649,7 @@ static void __devexit tdfxfb_remove(struct pci_dev *pdev)
        release_mem_region(pci_resource_start(pdev, 0),
                           pci_resource_len(pdev, 0));
        pci_set_drvdata(pdev, NULL);
+       fb_dealloc_cmap(&info->cmap);
        framebuffer_release(info);
 }