vserver 1.9.3
[linux-2.6.git] / drivers / video / tdfxfb.c
index 2873877..503ecc3 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  *
  * tdfxfb.c
@@ -126,8 +125,9 @@ static struct fb_var_screeninfo tdfx_var __initdata = {
 /*
  * PCI driver prototypes
  */
-static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id);
-static void tdfxfb_remove(struct pci_dev *pdev);
+static int __devinit tdfxfb_probe(struct pci_dev *pdev,
+                                 const struct pci_device_id *id);
+static void __devexit tdfxfb_remove(struct pci_dev *pdev);
 
 static struct pci_device_id tdfxfb_id_table[] = {
        { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE,
@@ -155,7 +155,7 @@ MODULE_DEVICE_TABLE(pci, tdfxfb_id_table);
  *  Frame buffer device API
  */
 int tdfxfb_init(void);
-void tdfxfb_setup(char *options, int *ints); 
+void tdfxfb_setup(char *options);
 
 static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb); 
 static int tdfxfb_set_par(struct fb_info *info); 
@@ -163,15 +163,12 @@ static int tdfxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
                            u_int transp, struct fb_info *info); 
 static int tdfxfb_blank(int blank, struct fb_info *info); 
 static int tdfxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
+static int banshee_wait_idle(struct fb_info *info);
+#ifdef CONFIG_FB_3DFX_ACCEL
 static void tdfxfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
 static void tdfxfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);  
 static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image); 
-#ifdef CONFIG_FB_3DFX_ACCEL
-static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor);
-#else /* !CONFIG_FB_3DFX_ACCEL */
-#define tdfxfb_cursor soft_cursor
 #endif /* CONFIG_FB_3DFX_ACCEL */
-static int banshee_wait_idle(struct fb_info *info);
 
 static struct fb_ops tdfxfb_ops = {
        .owner          = THIS_MODULE,
@@ -180,11 +177,17 @@ static struct fb_ops tdfxfb_ops = {
        .fb_setcolreg   = tdfxfb_setcolreg,
        .fb_blank       = tdfxfb_blank,
        .fb_pan_display = tdfxfb_pan_display,
+       .fb_sync        = banshee_wait_idle,
+#ifdef CONFIG_FB_3DFX_ACCEL
        .fb_fillrect    = tdfxfb_fillrect,
        .fb_copyarea    = tdfxfb_copyarea,
        .fb_imageblit   = tdfxfb_imageblit,
-       .fb_sync        = banshee_wait_idle,
-       .fb_cursor      = tdfxfb_cursor,
+#else
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+#endif
+       .fb_cursor      = soft_cursor,
 };
 
 /*
@@ -497,23 +500,21 @@ static int tdfxfb_check_var(struct fb_var_screeninfo *var,struct fb_info *info)
                return -EINVAL;
        }
 
-       if (var->xres != var->xres_virtual) {
-               DPRINTK("virtual x resolution != physical x resolution not supported\n");
-               return -EINVAL;
-       }
+       if (var->xres != var->xres_virtual)
+               var->xres_virtual = var->xres;
 
-       if (var->yres > var->yres_virtual) {
-               DPRINTK("virtual y resolution < physical y resolution not possible\n");
-               return -EINVAL;
-       }
+       if (var->yres > var->yres_virtual)
+               var->yres_virtual = var->yres;
 
        if (var->xoffset) {
                DPRINTK("xoffset not supported\n");
                return -EINVAL;
        }
 
-       /* fixme: does Voodoo3 support interlace? Banshee doesn't */
-       if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
+       /* Banshee doesn't support interlace, but Voodoo4/5 and probably Voodoo3 do. */
+       /* no direct information about device id now? use max_pixclock for this... */
+       if (((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) &&
+                       (par->max_pixclock < VOODOO3_MAX_PIXCLOCK)) {
                DPRINTK("interlace not supported\n");
                return -EINVAL;
        }
@@ -532,9 +533,12 @@ static int tdfxfb_check_var(struct fb_var_screeninfo *var,struct fb_info *info)
        }
   
        if (lpitch * var->yres_virtual > info->fix.smem_len) {
-               DPRINTK("no memory for screen (%ux%ux%u)\n",
-               var->xres, var->yres_virtual, var->bits_per_pixel);
-               return -EINVAL;
+               var->yres_virtual = info->fix.smem_len/lpitch;
+               if (var->yres_virtual < var->yres) {
+                       DPRINTK("no memory for screen (%ux%ux%u)\n",
+                       var->xres, var->yres_virtual, var->bits_per_pixel);
+                       return -EINVAL;
+               }
        }
   
        if (PICOS2KHZ(var->pixclock) > par->max_pixclock) {
@@ -584,7 +588,6 @@ static int tdfxfb_set_par(struct fb_info *info)
        int fout, freq;
        u32 wd, cpp;
   
-       info->cmap.len = (info->var.bits_per_pixel == 8) ? 256 : 16;
        par->baseline  = 0;
  
        memset(&reg, 0, sizeof(reg));
@@ -620,10 +623,17 @@ static int tdfxfb_set_par(struct fb_info *info)
        hbs = hd;
        hbe = ht;
 
-       vbs = vd = info->var.yres - 1;
-       vs  = vd + info->var.lower_margin;
-       ve  = vs + info->var.vsync_len;
-       vbe = vt = ve + info->var.upper_margin - 1;
+       if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
+               vbs = vd = (info->var.yres << 1) - 1;
+               vs  = vd + (info->var.lower_margin << 1);
+               ve  = vs + (info->var.vsync_len << 1);
+               vbe = vt = ve + (info->var.upper_margin << 1) - 1;
+       } else {
+               vbs = vd = info->var.yres - 1;
+               vs  = vd + info->var.lower_margin;
+               ve  = vs + info->var.vsync_len;
+               vbe = vt = ve + info->var.upper_margin - 1;
+       }
   
        /* this is all pretty standard VGA register stuffing */
        reg.misc[0x00] = 0x0f | 
@@ -746,8 +756,16 @@ static int tdfxfb_set_par(struct fb_info *info)
        reg.gfxpll = do_calc_pll(..., &fout);
 #endif
 
-       reg.screensize = info->var.xres | (info->var.yres << 12);
-       reg.vidcfg &= ~VIDCFG_HALF_MODE;
+       if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
+               reg.screensize = info->var.xres | (info->var.yres << 13);
+               reg.vidcfg |= VIDCFG_HALF_MODE;
+               reg.crt[0x09] |= 0x80;
+       } else {
+               reg.screensize = info->var.xres | (info->var.yres << 12);
+               reg.vidcfg &= ~VIDCFG_HALF_MODE;
+       }
+       if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
+               reg.vidcfg |= VIDCFG_INTERLACE;
        reg.miscinit0 = tdfx_inl(par, MISCINIT0);
 
 #if defined(__BIG_ENDIAN)
@@ -787,7 +805,7 @@ static int tdfxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
        struct tdfx_par *par = (struct tdfx_par *) info->par;
        u32 rgbcol;
    
-       if (regno >= info->cmap.len) return 1;
+       if (regno >= info->cmap.len || regno > 255) return 1;
    
        switch (info->fix.visual) {
                case FB_VISUAL_PSEUDOCOLOR:
@@ -876,6 +894,7 @@ static int tdfxfb_pan_display(struct fb_var_screeninfo *var,
        return 0;
 }
 
+#ifdef CONFIG_FB_3DFX_ACCEL
 /*
  * FillRect 2D command (solidfill or invert (via ROP_XOR))   
  */
@@ -894,7 +913,11 @@ static void tdfxfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect
 
        banshee_make_room(par, 5);
        tdfx_outl(par,  DSTFORMAT, fmt);
-       tdfx_outl(par,  COLORFORE, rect->color);
+       if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
+               tdfx_outl(par,  COLORFORE, rect->color);
+       } else { /* FB_VISUAL_TRUECOLOR */
+               tdfx_outl(par, COLORFORE, ((u32*)(info->pseudo_palette))[rect->color]);
+       }
        tdfx_outl(par,  COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24));
        tdfx_outl(par,  DSTSIZE,    rect->width | (rect->height << 16));
        tdfx_outl(par,  LAUNCH_2D,  rect->dx | (rect->dy << 16));
@@ -1004,8 +1027,9 @@ static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image)
        }
        banshee_wait_idle(info);
 }
+#endif /* CONFIG_FB_3DFX_ACCEL */
 
-#ifdef CONFIG_FB_3DFX_ACCEL
+#ifdef TDFX_HARDWARE_CURSOR
 static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
 {
        struct tdfx_par *par = (struct tdfx_par *) info->par;
@@ -1043,7 +1067,7 @@ static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
                struct fb_cmap cmap = cursor->image.cmap;
                unsigned long bg_color, fg_color;
 
-               cmap.len = 2;/* Voodoo 3+ only support 2 color cursors*/
+               cmap.len = 2; /* Voodoo 3+ only support 2 color cursors */
                fg_color = ((cmap.red[cmap.start] << 16) |
                            (cmap.green[cmap.start] << 8)  |
                            (cmap.blue[cmap.start]));
@@ -1101,7 +1125,7 @@ static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
                 */
                u8 *cursorbase = (u8 *) info->cursor.image.data;
                char *bitmap = (char *)cursor->image.data;
-               const char *mask = cursor->mask;
+               char *mask = (char *) cursor->mask;
                int i, j, k, h = 0;
 
                for (i = 0; i < 64; i++) {
@@ -1142,7 +1166,7 @@ static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
        spin_unlock_irqrestore(&par->DAClock, flags);
        return 0;
 }
-#endif /* CONFIG_FB_3DFX_ACCEL */
+#endif
 
 /**
  *      tdfxfb_probe - Device Initializiation
@@ -1158,22 +1182,20 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
 {
        struct tdfx_par *default_par;
        struct fb_info *info;
-       int size, err;
+       int size, err, lpitch;
 
        if ((err = pci_enable_device(pdev))) {
                printk(KERN_WARNING "tdfxfb: Can't enable pdev: %d\n", err);
                return err;
        }
 
-       size = sizeof(struct fb_info)+sizeof(struct tdfx_par)+16*sizeof(u32);
+       size = sizeof(struct tdfx_par)+256*sizeof(u32);
 
-       info = kmalloc(size, GFP_KERNEL);
+       info = framebuffer_alloc(size, &pdev->dev);
 
        if (!info)      return -ENOMEM;
                
-       memset(info, 0, size);
-    
-       default_par = (struct tdfx_par *) (info + 1);
+       default_par = info->par;
  
        /* Configure the default fb_fix_screeninfo first */
        switch (pdev->device) {
@@ -1251,24 +1273,44 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
    
        info->fbops             = &tdfxfb_ops;
        info->fix               = tdfx_fix;     
-       info->par               = default_par;
        info->pseudo_palette    = (void *)(default_par + 1); 
-       info->flags             = FBINFO_FLAG_DEFAULT;
+       info->flags             = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
+#ifdef CONFIG_FB_3DFX_ACCEL
+       info->flags             |= FBINFO_HWACCEL_FILLRECT |
+               FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_IMAGEBLIT;
+#endif
 
-#ifndef MODULE
        if (!mode_option)
                mode_option = "640x480@60";
         
        err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); 
        if (!err || err == 4)
-#endif
                info->var = tdfx_var;
 
-       size = (info->var.bits_per_pixel == 8) ? 256 : 16;
-       fb_alloc_cmap(&info->cmap, size, 0);  
+       /* maximize virtual vertical length */
+       lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3);
+       info->var.yres_virtual = info->fix.smem_len/lpitch;
+       if (info->var.yres_virtual < info->var.yres)
+               goto out_err;
+
+#ifdef CONFIG_FB_3DFX_ACCEL
+       /*
+        * FIXME: Limit var->yres_virtual to 4096 because of screen artifacts
+        * during scrolling. This is only present if 2D acceleration is
+        * enabled.
+        */
+       if (info->var.yres_virtual > 4096)
+               info->var.yres_virtual = 4096;
+#endif /* CONFIG_FB_3DFX_ACCEL */
+
+       if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
+               printk(KERN_WARNING "tdfxfb: Can't allocate color map\n");
+               goto out_err;
+       }
 
        if (register_framebuffer(info) < 0) {
                printk("tdfxfb: can't register framebuffer\n");
+               fb_dealloc_cmap(&info->cmap);
                goto out_err;
        }
        /*
@@ -1285,7 +1327,7 @@ out_err:
                iounmap(default_par->regbase_virt);
        if (info->screen_base)
                iounmap(info->screen_base);
-       kfree(info);
+       framebuffer_release(info);
        return -ENXIO;
 }
 
@@ -1315,11 +1357,19 @@ 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);
-       kfree(info);
+       framebuffer_release(info);
 }
 
 int __init tdfxfb_init(void)
 {
+#ifndef MODULE
+       char *option = NULL;
+
+       if (fb_get_options("tdfxfb", &option))
+               return -ENODEV;
+
+       tdfxfb_setup(option);
+#endif
         return pci_module_init(&tdfxfb_driver);
 }
 
@@ -1332,14 +1382,12 @@ MODULE_AUTHOR("Hannu Mallat <hmallat@cc.hut.fi>");
 MODULE_DESCRIPTION("3Dfx framebuffer device driver");
 MODULE_LICENSE("GPL");
  
-#ifdef MODULE
 module_init(tdfxfb_init);
-#endif
 module_exit(tdfxfb_exit);
 
 
 #ifndef MODULE
-void tdfxfb_setup(char *options, int *ints)
+void tdfxfb_setup(char *options)
 { 
        char* this_opt;