Merge branch 'release-2.6.27' of git://git.kernel.org/pub/scm/linux/kernel/git/ak...
[pandora-kernel.git] / drivers / video / tridentfb.c
index 2794115..479b2e7 100644 (file)
@@ -24,8 +24,6 @@
 #include <video/vga.h>
 #include <video/trident.h>
 
-#define VERSION                "0.7.9-NEWAPI"
-
 struct tridentfb_par {
        void __iomem *io_virt;  /* iospace virtual memory address */
        u32 pseudo_pal[16];
@@ -37,11 +35,12 @@ struct tridentfb_par {
                (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
        void (*copy_rect)
                (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
+       void (*image_blit)
+               (struct tridentfb_par *par, const char*,
+                u32, u32, u32, u32, u32, u32);
+       unsigned char eng_oper; /* engine operation... */
 };
 
-static unsigned char eng_oper; /* engine operation... */
-static struct fb_ops tridentfb_ops;
-
 static struct fb_fix_screeninfo tridentfb_fix = {
        .id = "Trident",
        .type = FB_TYPE_PACKED_PIXELS,
@@ -53,7 +52,7 @@ static struct fb_fix_screeninfo tridentfb_fix = {
 /* defaults which are normally overriden by user values */
 
 /* video mode */
-static char *mode_option __devinitdata = "640x480";
+static char *mode_option __devinitdata = "640x480-8@60";
 static int bpp __devinitdata = 8;
 
 static int noaccel __devinitdata;
@@ -84,24 +83,22 @@ MODULE_PARM_DESC(fp, "Define if flatpanel is connected");
 module_param(crt, int, 0);
 MODULE_PARM_DESC(crt, "Define if CRT is connected");
 
-static int is_oldclock(int id)
+static inline int is_oldclock(int id)
 {
        return  (id == TGUI9440) ||
                (id == TGUI9660) ||
                (id == CYBER9320);
 }
 
-static int is_oldprotect(int id)
+static inline int is_oldprotect(int id)
 {
-       return  (id == TGUI9440) ||
-               (id == TGUI9660) ||
+       return  is_oldclock(id) ||
                (id == PROVIDIA9685) ||
-               (id == CYBER9320) ||
                (id == CYBER9382) ||
                (id == CYBER9385);
 }
 
-static int is_blade(int id)
+static inline int is_blade(int id)
 {
        return  (id == BLADE3D) ||
                (id == CYBERBLADEE4) ||
@@ -113,27 +110,22 @@ static int is_blade(int id)
                (id == CYBERBLADEAi1D);
 }
 
-static int is_xp(int id)
+static inline int is_xp(int id)
 {
        return  (id == CYBERBLADEXPAi1) ||
                (id == CYBERBLADEXPm8) ||
                (id == CYBERBLADEXPm16);
 }
 
-static int is3Dchip(int id)
+static inline int is3Dchip(int id)
 {
-       return ((id == BLADE3D) || (id == CYBERBLADEE4) ||
-               (id == CYBERBLADEi7) || (id == CYBERBLADEi7D) ||
+       return  is_blade(id) || is_xp(id) ||
                (id == CYBER9397) || (id == CYBER9397DVD) ||
                (id == CYBER9520) || (id == CYBER9525DVD) ||
-               (id == IMAGE975) || (id == IMAGE985) ||
-               (id == CYBERBLADEi1) || (id == CYBERBLADEi1D) ||
-               (id == CYBERBLADEAi1) || (id == CYBERBLADEAi1D) ||
-               (id == CYBERBLADEXPm8) || (id == CYBERBLADEXPm16) ||
-               (id == CYBERBLADEXPAi1));
+               (id == IMAGE975) || (id == IMAGE985);
 }
 
-static int iscyber(int id)
+static inline int iscyber(int id)
 {
        switch (id) {
        case CYBER9388:
@@ -153,13 +145,7 @@ static int iscyber(int id)
                return 1;
 
        case CYBER9320:
-       case TGUI9660:
-       case PROVIDIA9685:
-       case IMAGE975:
-       case IMAGE985:
-       case BLADE3D:
        case CYBERBLADEi7:      /* VIA MPV4 integrated version */
-
        default:
                /* case CYBERBLDAEXPm8:  Strange */
                /* case CYBERBLDAEXPm16: Strange */
@@ -227,6 +213,21 @@ static void blade_fill_rect(struct tridentfb_par *par,
        writemmr(par, DST2, point(x + w - 1, y + h - 1));
 }
 
+static void blade_image_blit(struct tridentfb_par *par, const char *data,
+                            u32 x, u32 y, u32 w, u32 h, u32 c, u32 b)
+{
+       unsigned size = ((w + 31) >> 5) * h;
+
+       writemmr(par, COLOR, c);
+       writemmr(par, BGCOLOR, b);
+       writemmr(par, CMD, 0xa0000000 | 3 << 19);
+
+       writemmr(par, DST1, point(x, y));
+       writemmr(par, DST2, point(x + w - 1, y + h - 1));
+
+       memcpy(par->io_virt + 0x10000, data, 4 * size);
+}
+
 static void blade_copy_rect(struct tridentfb_par *par,
                            u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
 {
@@ -275,7 +276,7 @@ static void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp)
 
        t_outb(par, x, 0x2125);
 
-       eng_oper = x | 0x40;
+       par->eng_oper = x | 0x40;
 
        writemmr(par, 0x2154, v1);
        writemmr(par, 0x2150, v1);
@@ -284,10 +285,9 @@ static void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp)
 
 static void xp_wait_engine(struct tridentfb_par *par)
 {
-       int count, timeout;
+       int count = 0;
+       int timeout = 0;
 
-       count = 0;
-       timeout = 0;
        while (t_inb(par, STATUS) & 0x80) {
                count++;
                if (count == 10000000) {
@@ -313,16 +313,14 @@ static void xp_fill_rect(struct tridentfb_par *par,
        writemmr(par, OLDDIM, point(h, w));
        writemmr(par, OLDDST, point(y, x));
        t_outb(par, 0x01, OLDCMD);
-       t_outb(par, eng_oper, 0x2125);
+       t_outb(par, par->eng_oper, 0x2125);
 }
 
 static void xp_copy_rect(struct tridentfb_par *par,
                         u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
 {
-       int direction;
        u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp;
-
-       direction = 0x0004;
+       int direction = 0x0004;
 
        if ((x1 < x2) && (y1 == y2)) {
                direction |= 0x0200;
@@ -515,6 +513,36 @@ static void tridentfb_fillrect(struct fb_info *info,
                       fr->height, col, fr->rop);
 }
 
+static void tridentfb_imageblit(struct fb_info *info,
+                               const struct fb_image *img)
+{
+       struct tridentfb_par *par = info->par;
+       int col, bgcol;
+
+       if ((info->flags & FBINFO_HWACCEL_DISABLED) || img->depth != 1) {
+               cfb_imageblit(info, img);
+               return;
+       }
+       if (info->var.bits_per_pixel == 8) {
+               col = img->fg_color;
+               col |= col << 8;
+               col |= col << 16;
+               bgcol = img->bg_color;
+               bgcol |= bgcol << 8;
+               bgcol |= bgcol << 16;
+       } else {
+               col = ((u32 *)(info->pseudo_palette))[img->fg_color];
+               bgcol = ((u32 *)(info->pseudo_palette))[img->bg_color];
+       }
+
+       par->wait_engine(par);
+       if (par->image_blit)
+               par->image_blit(par, img->data, img->dx, img->dy,
+                               img->width, img->height, col, bgcol);
+       else
+               cfb_imageblit(info, img);
+}
+
 static void tridentfb_copyarea(struct fb_info *info,
                               const struct fb_copyarea *ca)
 {
@@ -540,6 +568,7 @@ static int tridentfb_sync(struct fb_info *info)
 #else
 #define tridentfb_fillrect cfb_fillrect
 #define tridentfb_copyarea cfb_copyarea
+#define tridentfb_imageblit cfb_imageblit
 #endif /* CONFIG_FB_TRIDENT_ACCEL */
 
 /*
@@ -576,13 +605,15 @@ static inline void write3CE(struct tridentfb_par *par, int reg,
        vga_mm_wgfx(par->io_virt, reg, val);
 }
 
-static void enable_mmio(void)
+static void enable_mmio(struct tridentfb_par *par)
 {
        /* Goto New Mode */
        vga_io_rseq(0x0B);
 
        /* Unprotect registers */
        vga_io_wseq(NewMode1, 0x80);
+       if (!is_oldprotect(par->chip_id))
+               vga_io_wseq(Protection, 0x92);
 
        /* Enable MMIO */
        outb(PCIReg, 0x3D4);
@@ -596,13 +627,15 @@ static void disable_mmio(struct tridentfb_par *par)
 
        /* Unprotect registers */
        vga_mm_wseq(par->io_virt, NewMode1, 0x80);
+       if (!is_oldprotect(par->chip_id))
+               vga_mm_wseq(par->io_virt, Protection, 0x92);
 
        /* Disable MMIO */
        t_outb(par, PCIReg, 0x3D4);
        t_outb(par, t_inb(par, 0x3D5) & ~0x01, 0x3D5);
 }
 
-static void crtc_unlock(struct tridentfb_par *par)
+static inline void crtc_unlock(struct tridentfb_par *par)
 {
        write3X4(par, VGA_CRTC_V_SYNC_END,
                 read3X4(par, VGA_CRTC_V_SYNC_END) & 0x7F);
@@ -642,7 +675,7 @@ static int __devinit get_nativex(struct tridentfb_par *par)
 }
 
 /* Set pitch */
-static void set_lwidth(struct tridentfb_par *par, int width)
+static inline void set_lwidth(struct tridentfb_par *par, int width)
 {
        write3X4(par, VGA_CRTC_OFFSET, width & 0xFF);
        write3X4(par, AddColReg,
@@ -661,7 +694,7 @@ static void screen_stretch(struct tridentfb_par *par)
 }
 
 /* For resolutions smaller than FP resolution center */
-static void screen_center(struct tridentfb_par *par)
+static inline void screen_center(struct tridentfb_par *par)
 {
        write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 0x80);
        write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 0x80);
@@ -686,15 +719,16 @@ static void set_vclk(struct tridentfb_par *par, unsigned long freq)
        unsigned long fi, d, di;
        unsigned char best_m = 0, best_n = 0, best_k = 0;
        unsigned char hi, lo;
+       unsigned char shift = !is_oldclock(par->chip_id) ? 2 : 1;
 
        d = 20000;
-       for (k = 1; k >= 0; k--)
-               for (m = 0; m < 32; m++) {
-                       n = 2 * (m + 2) - 8;
+       for (k = shift; k >= 0; k--)
+               for (m = 1; m < 32; m++) {
+                       n = ((m + 2) << shift) - 8;
                        for (n = (n < 0 ? 0 : n); n < 122; n++) {
                                fi = ((14318l * (n + 8)) / (m + 2)) >> k;
                                di = abs(fi - freq);
-                               if (di <= d) {
+                               if (di < d || (di == d && k == best_k)) {
                                        d = di;
                                        best_n = n;
                                        best_m = m;
@@ -884,19 +918,19 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var,
                line_length = var->xres_virtual * bpp / 8;
        }
 
-       if (var->yres > var->yres_virtual)
-               var->yres_virtual = var->yres;
+       /* datasheet specifies how to set panning only up to 4 MB */
+       if (line_length * (var->yres_virtual - var->yres) > (4 << 20))
+               var->yres_virtual = ((4 << 20) / line_length) + var->yres;
+
        if (line_length * var->yres_virtual > info->fix.smem_len)
                return -EINVAL;
 
        switch (bpp) {
        case 8:
                var->red.offset = 0;
-               var->green.offset = 0;
-               var->blue.offset = 0;
-               var->red.length = 6;
-               var->green.length = 6;
-               var->blue.length = 6;
+               var->red.length = 8;
+               var->green = var->red;
+               var->blue = var->red;
                break;
        case 16:
                var->red.offset = 11;
@@ -960,19 +994,17 @@ static int tridentfb_pan_display(struct fb_var_screeninfo *var,
        debug("enter\n");
        offset = (var->xoffset + (var->yoffset * var->xres_virtual))
                * var->bits_per_pixel / 32;
-       info->var.xoffset = var->xoffset;
-       info->var.yoffset = var->yoffset;
        set_screen_start(par, offset);
        debug("exit\n");
        return 0;
 }
 
-static void shadowmode_on(struct tridentfb_par *par)
+static inline void shadowmode_on(struct tridentfb_par *par)
 {
        write3CE(par, CyberControl, read3CE(par, CyberControl) | 0x81);
 }
 
-static void shadowmode_off(struct tridentfb_par *par)
+static inline void shadowmode_off(struct tridentfb_par *par)
 {
        write3CE(par, CyberControl, read3CE(par, CyberControl) & 0x7E);
 }
@@ -980,7 +1012,7 @@ static void shadowmode_off(struct tridentfb_par *par)
 /* Set the hardware to the requested video mode */
 static int tridentfb_set_par(struct fb_info *info)
 {
-       struct tridentfb_par *par = (struct tridentfb_par *)(info->par);
+       struct tridentfb_par *par = info->par;
        u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend;
        u32 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend;
        struct fb_var_screeninfo *var = &info->var;
@@ -1013,6 +1045,7 @@ static int tridentfb_set_par(struct fb_info *info)
                vblankend /= 2;
        }
 
+       enable_mmio(par);
        crtc_unlock(par);
        write3CE(par, CyberControl, 8);
        tmp = 0xEB;
@@ -1134,7 +1167,7 @@ static int tridentfb_set_par(struct fb_info *info)
        if (!is_xp(par->chip_id))
                write3X4(par, Performance, read3X4(par, Performance) | 0x10);
        /* MMIO & PCI read and write burst enable */
-       if (par->chip_id != TGUI9440)
+       if (par->chip_id != TGUI9440 && par->chip_id != IMAGE975)
                write3X4(par, PCIReg, read3X4(par, PCIReg) | 0x06);
 
        vga_mm_wseq(par->io_virt, 0, 3);
@@ -1159,11 +1192,6 @@ static int tridentfb_set_par(struct fb_info *info)
        write3CE(par, 0x6, 0x05);       /* graphics mode */
        write3CE(par, 0x7, 0x0F);       /* planes? */
 
-       if (par->chip_id == CYBERBLADEXPAi1) {
-               /* This fixes snow-effect in 32 bpp */
-               write3X4(par, VGA_CRTC_H_SYNC_START, 0x84);
-       }
-
        /* graphics mode and support 256 color modes */
        writeAttr(par, 0x10, 0x41);
        writeAttr(par, 0x12, 0x0F);     /* planes */
@@ -1238,18 +1266,17 @@ static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green,
                        col |= col << 16;
                        ((u32 *)(info->pseudo_palette))[regno] = col;
                } else if (bpp == 32)           /* ARGB 8888 */
-                       ((u32*)info->pseudo_palette)[regno] =
+                       ((u32 *)info->pseudo_palette)[regno] =
                                ((transp & 0xFF00) << 16)       |
                                ((red & 0xFF00) << 8)           |
                                ((green & 0xFF00))              |
                                ((blue & 0xFF00) >> 8);
        }
 
-/*     debug("exit\n"); */
        return 0;
 }
 
-/* Try blanking the screen.For flat panels it does nothing */
+/* Try blanking the screen. For flat panels it does nothing */
 static int tridentfb_blank(int blank_mode, struct fb_info *info)
 {
        unsigned char PMCont, DPMSCont;
@@ -1305,7 +1332,7 @@ static struct fb_ops tridentfb_ops = {
        .fb_set_par = tridentfb_set_par,
        .fb_fillrect = tridentfb_fillrect,
        .fb_copyarea = tridentfb_copyarea,
-       .fb_imageblit = cfb_imageblit,
+       .fb_imageblit = tridentfb_imageblit,
 #ifdef CONFIG_FB_TRIDENT_ACCEL
        .fb_sync = tridentfb_sync,
 #endif
@@ -1332,10 +1359,6 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
 
        chip_id = id->device;
 
-       if (chip_id == CYBERBLADEi1)
-               output("*** Please do use cyblafb, Cyberblade/i1 support "
-                      "will soon be removed from tridentfb!\n");
-
 #ifndef CONFIG_FB_TRIDENT_ACCEL
        noaccel = 1;
 #endif
@@ -1389,6 +1412,7 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
                default_par->wait_engine = blade_wait_engine;
                default_par->fill_rect = blade_fill_rect;
                default_par->copy_rect = blade_copy_rect;
+               default_par->image_blit = blade_image_blit;
                tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D;
        } else if (chip3D) {                    /* 3DImage family left */
                default_par->init_accel = image_init_accel;
@@ -1408,9 +1432,10 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
 
        /* setup MMIO region */
        tridentfb_fix.mmio_start = pci_resource_start(dev, 1);
-       tridentfb_fix.mmio_len = chip3D ? 0x20000 : 0x10000;
+       tridentfb_fix.mmio_len = pci_resource_len(dev, 1);
 
-       if (!request_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len, "tridentfb")) {
+       if (!request_mem_region(tridentfb_fix.mmio_start,
+                               tridentfb_fix.mmio_len, "tridentfb")) {
                debug("request_region failed!\n");
                framebuffer_release(info);
                return -1;
@@ -1425,13 +1450,14 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
                goto out_unmap1;
        }
 
-       enable_mmio();
+       enable_mmio(default_par);
 
        /* setup framebuffer memory */
        tridentfb_fix.smem_start = pci_resource_start(dev, 0);
        tridentfb_fix.smem_len = get_memsize(default_par);
 
-       if (!request_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len, "tridentfb")) {
+       if (!request_mem_region(tridentfb_fix.smem_start,
+                               tridentfb_fix.smem_len, "tridentfb")) {
                debug("request_mem_region failed!\n");
                disable_mmio(info->par);
                err = -1;
@@ -1447,7 +1473,6 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
                goto out_unmap2;
        }
 
-       output("%s board found\n", pci_name(dev));
        default_par->flatpanel = is_flatpanel(default_par);
 
        if (default_par->flatpanel)
@@ -1465,6 +1490,29 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
        } else
                info->flags |= FBINFO_HWACCEL_DISABLED;
 
+       info->pixmap.addr = kmalloc(4096, GFP_KERNEL);
+       if (!info->pixmap.addr) {
+               err = -ENOMEM;
+               goto out_unmap2;
+       }
+
+       info->pixmap.size = 4096;
+       info->pixmap.buf_align = 4;
+       info->pixmap.scan_align = 1;
+       info->pixmap.access_align = 32;
+       info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+       if (default_par->image_blit) {
+               info->flags |= FBINFO_HWACCEL_IMAGEBLIT;
+               info->pixmap.scan_align = 4;
+       }
+
+       if (noaccel) {
+               printk(KERN_DEBUG "disabling acceleration\n");
+               info->flags |= FBINFO_HWACCEL_DISABLED;
+               info->pixmap.scan_align = 1;
+       }
+
        if (!fb_find_mode(&info->var, info,
                          mode_option, NULL, 0, NULL, bpp)) {
                err = -EINVAL;
@@ -1477,7 +1525,7 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
        info->var.activate |= FB_ACTIVATE_NOW;
        info->device = &dev->dev;
        if (register_framebuffer(info) < 0) {
-               printk(KERN_ERR "tridentfb: could not register Trident framebuffer\n");
+               printk(KERN_ERR "tridentfb: could not register framebuffer\n");
                fb_dealloc_cmap(&info->cmap);
                err = -EINVAL;
                goto out_unmap2;
@@ -1490,6 +1538,7 @@ static int __devinit trident_pci_probe(struct pci_dev *dev,
        return 0;
 
 out_unmap2:
+       kfree(info->pixmap.addr);
        if (info->screen_base)
                iounmap(info->screen_base);
        release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
@@ -1513,6 +1562,7 @@ static void __devexit trident_pci_remove(struct pci_dev *dev)
        release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
        release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
        pci_set_drvdata(dev, NULL);
+       kfree(info->pixmap.addr);
        framebuffer_release(info);
 }
 
@@ -1599,7 +1649,6 @@ static int __init tridentfb_init(void)
                return -ENODEV;
        tridentfb_setup(option);
 #endif
-       output("Trident framebuffer %s initializing\n", VERSION);
        return pci_register_driver(&tridentfb_pci_driver);
 }