diff -u --recursive --new-file linux.2.4.4.orig/drivers/video/Makefile linux/drivers/video/Makefile --- linux.2.4.4.orig/drivers/video/Makefile Sat Apr 14 05:31:32 2001 +++ linux/drivers/video/Makefile Tue May 1 20:05:33 2001 @@ -56,7 +56,6 @@ obj-$(CONFIG_FB_CYBER) += cyberfb.o obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o obj-$(CONFIG_FB_SGIVW) += sgivwfb.o -obj-$(CONFIG_FB_3DFX) += tdfxfb.o obj-$(CONFIG_FB_MAC) += macfb.o macmodes.o obj-$(CONFIG_FB_HP300) += hpfb.o obj-$(CONFIG_FB_OF) += offb.o @@ -92,6 +91,11 @@ subdir-$(CONFIG_FB_SIS) += sis ifeq ($(CONFIG_FB_SIS),y) obj-y += sis/sisfb.o +endif + +subdir-$(CONFIG_FB_3DFX) += tdfx +ifeq ($(CONFIG_FB_3DFX),y) +obj-y += tdfx/tdfxfb.o endif obj-$(CONFIG_FB_SUN3) += sun3fb.o diff -u --recursive --new-file linux.2.4.4.orig/drivers/video/tdfx/Makefile linux/drivers/video/tdfx/Makefile --- linux.2.4.4.orig/drivers/video/tdfx/Makefile Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/tdfx/Makefile Tue May 1 20:05:39 2001 @@ -0,0 +1,7 @@ + +O_TARGET := tdfxfb.o + +obj-y := tdfx_base.o tdfx_hw.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file linux.2.4.4.orig/drivers/video/tdfx/tdfx_base.c linux/drivers/video/tdfx/tdfx_base.c --- linux.2.4.4.orig/drivers/video/tdfx/tdfx_base.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/tdfx/tdfx_base.c Tue May 1 22:08:05 2001 @@ -0,0 +1,1699 @@ +/* + * + * tdfx_base.c + * + * Framebuffer driver for video cards with 3dfx chipsets + * (Currently supports Voodoo Banshee and Voodoo 3 (and maybe Voodoo 2?)) + * + * Authors: + * Hannu Mallat + * Attila Kesmarki + * + * Copyright (C) 1999,2000 Hannu Mallat & Attila Kesmarki + * + * 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. We are very much indebted to Daryll for his + * work on the X server. + * + * Many important contributions have come to the driver after the + * first release; see the version history for credits. + * + * TODO: + * - Support other 3dfx boards + * + * History: + * + * 0.2.2 (released 2000-10-07) Fixes in the multihead handling code + * 3dfx board initialization + * added MIPS support + * (Thanks to Jun Sun for the testing) + * testing, testing, testing... + * + * 0.2.1 (released 2000-07-25) fixes in initialization & unloading part + * restructuring the driver code + * cleanups + * 0.2.0 (released 2000-07-17) multihead support + * fixed XServer VTSwitch bug + * fixed 2X-mode bug (pixclock > max_pixclock/2) + * better kernel module support + * support for non-8 dot wide fonts (like SUN12x22) + * full support for 8, 16, 24, and 32 bit color depths + * and many other minor bugfixes + * 0.1.4 (not released) added Jeff Garzik's modularization patches, + * and Alexander Lukyanov's flicker fix on + * console switch + * 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), + * and my changes to get rid of hardcoded + * VGA i/o register locations (uses PCI + * configuration info now) + * 0.1.2 (released 1999-10-19) added Attila Kesmarki's bug fixes and + * improvements + * 0.1.1 (released 1999-10-07) added Voodoo3 support by Harold Oga. + * 0.1.0 (released 1999-10-06) initial version + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include "tdfx_base.h" +#include + +/* + * Interface used by the world + */ +#ifndef MODULE +static const char *mode_option __initdata = NULL; +#endif + +static struct fb_ops tdfxfb_ops = { + owner: THIS_MODULE, + fb_open: tdfxfb_open, + fb_release: tdfxfb_release, + fb_get_fix: tdfxfb_get_fix, + fb_get_var: tdfxfb_get_var, + fb_set_var: tdfxfb_set_var, + fb_get_cmap: tdfxfb_get_cmap, + fb_set_cmap: tdfxfb_set_cmap, + fb_pan_display: tdfxfb_pan_display, + fb_ioctl: tdfxfb_ioctl, +}; + +struct mode { + char *name; + struct fb_var_screeninfo var; +} mode; + +/* 2.[34].x kernels have a fb mode database, so supply only one backup default */ +struct mode default_mode[] = { + {"640x480-8@60", /* @ 60 Hz */ + { + 640, 480, 640, 1024, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, + 39722, 40, 24, 32, 11, 96, 2, + 0, FB_VMODE_NONINTERLACED} + } +}; + +static struct board board_list[] = { + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE, + -1, + 270000, "Voodoo Banshee",1}, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO, + -1, + 300000, "Voodoo Graphics",2}, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO2, + -1, + 300000, "Voodoo 2",3}, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3, + PCI_SUBDEVICE_ID_VOODOO3_2000, 300000, "Voodoo 3 2000",4}, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3, + PCI_SUBDEVICE_ID_VOODOO3_3000, 350000, "Voodoo 3 3000",4}, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO5, + -1, + 350000, "Voodoo 5",5}, + {0, 0, + 0, 0, NULL} +}; + +static struct pci_device_id tdfxfb_devices[] __devinitdata = { + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO5, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + {0, 0, + 0, 0, 0, 0, 0} +}; + +MODULE_DEVICE_TABLE(pci, tdfxfb_devices); + +static int noinit = 1; +static int noaccel = 0; +static int nopan = 0; +static int inverse = 0; +#ifdef CONFIG_MTRR +static int nomtrr = 0; +#endif + +LIST_HEAD(fb_list); + +static char *fontname = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +#define tdfx_cfb24_putc tdfx_cfb32_putc +#define tdfx_cfb24_putcs tdfx_cfb32_putcs +#define tdfx_cfb24_clear tdfx_cfb32_clear + +void tdfx_cfbX_clear_margins(struct vc_data *conp, struct display *p, + int bottom_only) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + void *mmiobase = info->regbase_virt; + unsigned int cw = fontwidth(p); + unsigned int ch = fontheight(p); + unsigned int rw = p->var.xres % cw; + unsigned int bh = p->var.yres % ch; + unsigned int rs = p->var.xres - rw; + unsigned int bs = p->var.yres - bh; + unsigned int remy = p->var.yres_virtual; + unsigned int nowy; + + if (!bottom_only && rw) { + while (remy) { + nowy = (remy >= 2047) ? 2047 : remy; + do_fillrect(mmiobase, p->var.xoffset + rs, + p->var.yres_virtual - remy, rw, nowy, 0, + info->current_par.lpitch, + info->current_par.bpp, ROP_COPY); + remy -= nowy; + } + } + + if (bh) { + do_fillrect(mmiobase, p->var.xoffset, bs + p->var.yoffset, + rs, bh, 0, + info->current_par.lpitch, + info->current_par.bpp, ROP_COPY); + } +} +void tdfx_cfbX_bmove(struct display *p, + int sy, int sx, int dy, int dx, int height, int width) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + + do_bitblt(info->regbase_virt, + fontwidth(p) * sx, + fontheight(p) * sy, + fontwidth(p) * dx, + fontheight(p) * dy, + fontwidth(p) * width, + fontheight(p) * height, + info->current_par.lpitch, info->current_par.bpp); +} +void tdfx_cfb8_putc(struct vc_data *conp, + struct display *p, int c, int yy, int xx) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + u32 fgx, bgx; + fgx = attr_fgcol(p, c); + bgx = attr_bgcol(p, c); + info->current_par.putc(fgx, bgx, p, c, yy, xx); +} + +void tdfx_cfb16_putc(struct vc_data *conp, + struct display *p, int c, int yy, int xx) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + u32 fgx, bgx; + if (p->dispsw_data==NULL) { + DPRINTK("OOps! dispsw_data=NULL"); + return; + } + fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, c)]; + bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, c)]; + info->current_par.putc(fgx, bgx, p, c, yy, xx); +} +void tdfx_cfb32_putc(struct vc_data *conp, + struct display *p, int c, int yy, int xx) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + u32 fgx, bgx; + + if (p->dispsw_data==NULL) { + DPRINTK("OOps! dispsw_data=NULL"); + return; + } + + fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, c)]; + bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, c)]; + + info->current_par.putc(fgx, bgx, p, c, yy, xx); +} +void tdfx_cfb8_putcs(struct vc_data *conp, + struct display *p, + const unsigned short *s, int c, int yy, int xx) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + u32 fgx, bgx; + fgx = attr_fgcol(p, scr_readw(s)); + bgx = attr_bgcol(p, scr_readw(s)); + info->current_par.putcs(fgx, bgx, p, s, c, yy, xx); +} +void tdfx_cfb16_putcs(struct vc_data *conp, + struct display *p, + const unsigned short *s, int c, int yy, int xx) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + u32 fgx, bgx; + if (p->dispsw_data==NULL) { + DPRINTK("OOps! can't do cfb16_putcs. dispsw_data=NULL"); + return; + } + fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, scr_readw(s))]; + bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, scr_readw(s))]; + info->current_par.putcs(fgx, bgx, p, s, c, yy, xx); +} +void tdfx_cfb32_putcs(struct vc_data *conp, + struct display *p, + const unsigned short *s, int c, int yy, int xx) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + u32 fgx, bgx; + if (p->dispsw_data==NULL) { + DPRINTK("OOps! can't do putcs. dispsw_data=NULL"); + return; + } + fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, scr_readw(s))]; + bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, scr_readw(s))]; + info->current_par.putcs(fgx, bgx, p, s, c, yy, xx); +} + + +void tdfx_cfb8_clear(struct vc_data *conp, + struct display *p, int sy, int sx, int height, int width) +{ + u32 bg; + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + + bg = attr_bgcol_ec(p, conp); + do_fillrect(info->regbase_virt, + fontwidth(p) * sx, + fontheight(p) * sy, + fontwidth(p) * width, + fontheight(p) * height, + bg, + info->current_par.lpitch, + info->current_par.bpp, ROP_COPY); +} + +void tdfx_cfb16_clear(struct vc_data *conp, + struct display *p, + int sy, int sx, int height, int width) +{ + u32 bg; + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + + if (p->dispsw_data==NULL) { + DPRINTK("OOps! dispsw_data=NULL"); + return; + } + + bg = ((u16 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; + do_fillrect(info->regbase_virt, + fontwidth(p) * sx, + fontheight(p) * sy, + fontwidth(p) * width, + fontheight(p) * height, + bg, + info->current_par.lpitch, + info->current_par.bpp, ROP_COPY); +} + +void tdfx_cfb32_clear(struct vc_data *conp, + struct display *p, + int sy, int sx, int height, int width) +{ + u32 bg; + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + if (p->dispsw_data==NULL) { + DPRINTK("OOps! dispsw_data=NULL"); + return; + } + + bg = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; + do_fillrect(info->regbase_virt, + fontwidth(p) * sx, + fontheight(p) * sy, + fontwidth(p) * width, + fontheight(p) * height, + bg, + info->current_par.lpitch, + info->current_par.bpp, ROP_COPY); +} +void tdfx_cfbX_revc(struct display *p, int xx, int yy) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + int bpp = info->current_par.bpp; + + do_fillrect(info->regbase_virt, + xx * fontwidth(p), yy * fontheight(p), + fontwidth(p), fontheight(p), + (bpp == 8) ? 0x0f : 0xffffffff, + info->current_par.lpitch, bpp, ROP_XOR); + +} +void tdfx_cfbX_cursor(struct display *p, int mode, int x, int y) +{ + unsigned long flags; + int tip; + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + + tip = p->conp->vc_cursor_type & CUR_HWMASK; + if (mode == CM_ERASE) { + if (info->cursor.state != CM_ERASE) { + spin_lock_irqsave(&info->DAClock, flags); + info->cursor.state = CM_ERASE; + del_timer_sync(&(info->cursor.timer)); + do_hwcursor_disable(info); + spin_unlock_irqrestore(&info->DAClock, flags); + } + return; + } + if ((p->conp->vc_cursor_type & CUR_HWMASK) != info->cursor.type) + tdfxfb_createcursor(p); + x *= fontwidth(p); + y *= fontheight(p); + y -= p->var.yoffset; + spin_lock_irqsave(&info->DAClock, flags); + if ((x != info->cursor.x) || + (y != info->cursor.y) || (info->cursor.redraw)) { + info->cursor.x = x; + info->cursor.y = y; + info->cursor.redraw = 0; + x += 63; + y += 63; + do_hwcursor_pos(info, x, y); + } + info->cursor.state = CM_DRAW; + mod_timer(&info->cursor.timer, jiffies + HZ / 2); + do_hwcursor_enable(info); + spin_unlock_irqrestore(&info->DAClock, flags); + return; +} + +#ifdef FBCON_HAS_CFB8 +struct display_switch fbcon_banshee8 = { + setup: fbcon_cfb8_setup, + bmove: tdfx_cfbX_bmove, + clear: tdfx_cfb8_clear, + putc: tdfx_cfb8_putc, + putcs: tdfx_cfb8_putcs, + revc: tdfx_cfbX_revc, + cursor: tdfx_cfbX_cursor, + clear_margins: tdfx_cfbX_clear_margins, + fontwidthmask: ~1 +}; +#endif +#ifdef FBCON_HAS_CFB16 +struct display_switch fbcon_banshee16 = { + setup: fbcon_cfb16_setup, + bmove: tdfx_cfbX_bmove, + clear: tdfx_cfb16_clear, + putc: tdfx_cfb16_putc, + putcs: tdfx_cfb16_putcs, + revc: tdfx_cfbX_revc, + cursor: tdfx_cfbX_cursor, + clear_margins: tdfx_cfbX_clear_margins, + fontwidthmask: ~1 +}; +#endif +#ifdef FBCON_HAS_CFB24 +struct display_switch fbcon_banshee24 = { + setup: fbcon_cfb24_setup, + bmove: tdfx_cfbX_bmove, + clear: tdfx_cfb24_clear, + putc: tdfx_cfb24_putc, + putcs: tdfx_cfb24_putcs, + revc: tdfx_cfbX_revc, + cursor: tdfx_cfbX_cursor, + clear_margins: tdfx_cfbX_clear_margins, + fontwidthmask: ~1 +}; +#endif +#ifdef FBCON_HAS_CFB32 +struct display_switch fbcon_banshee32 = { + setup: fbcon_cfb32_setup, + bmove: tdfx_cfbX_bmove, + clear: tdfx_cfb32_clear, + putc: tdfx_cfb32_putc, + putcs: tdfx_cfb32_putcs, + revc: tdfx_cfbX_revc, + cursor: tdfx_cfbX_cursor, + clear_margins: tdfx_cfbX_clear_margins, + fontwidthmask: ~1 +}; +#endif + +int tdfxfb_decode_var(const struct fb_var_screeninfo *var, + struct tdfxfb_par *par, const struct fb_info_tdfx *info) +{ + struct fb_info_tdfx *i = (struct fb_info_tdfx *) info; + + if (var->bits_per_pixel != 8 && + var->bits_per_pixel != 16 && + var->bits_per_pixel != 24 && var->bits_per_pixel != 32) { + DPRINTK("depth not supported: %u\n", var->bits_per_pixel); + return -EINVAL; + } + + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + DPRINTK("interlace not supported\n"); + return -EINVAL; + } + + if (var->xoffset) { + DPRINTK("xoffset not supported\n"); + return -EINVAL; + } + + if (var->xres != var->xres_virtual) { + DPRINTK + ("virtual x resolution != physical x resolution not supported\n"); + return -EINVAL; + } + + if (var->yres > var->yres_virtual) { + DPRINTK + ("virtual y resolution < physical y resolution not possible\n"); + return -EINVAL; + } + + /* fixme: does Voodoo3 support interlace? Banshee doesn't */ + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + DPRINTK("interlace not supported\n"); + return -EINVAL; + } + + memset(par, 0, sizeof(struct tdfxfb_par)); + + switch (i->dev) { + case PCI_DEVICE_ID_3DFX_BANSHEE: + case PCI_DEVICE_ID_3DFX_VOODOO3: + par->width = (var->xres + 15) & ~15; /* could sometimes be 8 */ + par->width_virt = par->width; + par->height = var->yres; + par->height_virt = var->yres_virtual; + par->bpp = var->bits_per_pixel; + par->ppitch = var->bits_per_pixel; + par->lpitch = par->width * ((par->ppitch + 7) >> 3); + par->cmap_len = (par->bpp == 8) ? 256 : 16; + + par->baseline = 0; //var->yoffset; + + if (par->width < 320 || par->width > 2048) { + DPRINTK("width not supported: %u\n", par->width); + return -EINVAL; + } + if (par->height < 200 || par->height > 2048) { + DPRINTK("height not supported: %u\n", par->height); + return -EINVAL; + } + if (par->lpitch * par->height_virt > i->bufbase_size) { + DPRINTK("no memory for screen (%ux%ux%u)\n", + par->width, par->height_virt, par->bpp); + return -EINVAL; + } + par->pixclock = PICOS2KHZ(var->pixclock); + if (par->pixclock > i->max_pixclock) { + DPRINTK("pixclock too high (%uKHz)\n", par->pixclock); + return -EINVAL; + } + + par->hdispend = var->xres; + par->hsyncsta = par->hdispend + var->right_margin; + par->hsyncend = par->hsyncsta + var->hsync_len; + par->htotal = par->hsyncend + var->left_margin; + + par->vdispend = var->yres; + par->vsyncsta = par->vdispend + var->lower_margin; + par->vsyncend = par->vsyncsta + var->vsync_len; + par->vtotal = par->vsyncend + var->upper_margin; + + if (var->sync & FB_SYNC_HOR_HIGH_ACT) { + par->video |= TDFXF_HSYNC_ACT_HIGH; + } else { + par->video |= TDFXF_HSYNC_ACT_LOW; + } + + if (var->sync & FB_SYNC_VERT_HIGH_ACT) { + par->video |= TDFXF_VSYNC_ACT_HIGH; + } else { + par->video |= TDFXF_VSYNC_ACT_LOW; + } + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { + par->video |= TDFXF_LINE_DOUBLE; + } + if (var->activate == FB_ACTIVATE_NOW) + par->video |= TDFXF_VIDEO_ENABLE; + } + + if (var->accel_flags & FB_ACCELF_TEXT) + par->accel_flags = FB_ACCELF_TEXT; + else + par->accel_flags = 0; + + return 0; +} + +int tdfxfb_encode_var(struct fb_var_screeninfo *var, + const struct tdfxfb_par *par, + const struct fb_info_tdfx *info) +{ + struct fb_var_screeninfo v; + + memset(&v, 0, sizeof(struct fb_var_screeninfo)); + v.xres_virtual = par->width_virt; + v.yres_virtual = par->height_virt; + v.xres = par->width; + v.yres = par->height; + v.right_margin = par->hsyncsta - par->hdispend; + v.hsync_len = par->hsyncend - par->hsyncsta; + v.left_margin = par->htotal - par->hsyncend; + v.lower_margin = par->vsyncsta - par->vdispend; + v.vsync_len = par->vsyncend - par->vsyncsta; + v.upper_margin = par->vtotal - par->vsyncend; + v.bits_per_pixel = par->bpp; + switch (par->bpp) { + case 8: + v.red.length = v.green.length = v.blue.length = 8; + break; + case 16: +#ifdef CONFIG_PPC + v.red.offset = 10; + v.red.length = 5; + v.green.offset = 5; + v.green.length = 5; + v.blue.offset = 0; + v.blue.length = 5; +#else + v.red.offset = 11; + v.red.length = 5; + v.green.offset = 5; + v.green.length = 6; + v.blue.offset = 0; + v.blue.length = 5; +#endif + v.transp.offset = 0; + v.transp.length = 0; + break; + case 24: + case 32: + v.red.offset = 16; + v.green.offset = 8; + v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 8; + v.transp.offset = 24; + v.transp.length = 8; + break; + } + v.height = v.width = -1; + v.pixclock = KHZ2PICOS(par->pixclock); + if ((par->video & TDFXF_HSYNC_MASK) == TDFXF_HSYNC_ACT_HIGH) + v.sync |= FB_SYNC_HOR_HIGH_ACT; + if ((par->video & TDFXF_VSYNC_MASK) == TDFXF_VSYNC_ACT_HIGH) + v.sync |= FB_SYNC_VERT_HIGH_ACT; + if (par->video & TDFXF_LINE_DOUBLE) + v.vmode = FB_VMODE_DOUBLE; + *var = v; + return 0; +} + +int tdfxfb_encode_fix(struct fb_fix_screeninfo *fix, + const struct tdfxfb_par *par, + const struct fb_info_tdfx *info) +{ + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + + switch (info->dev) { + case PCI_DEVICE_ID_3DFX_BANSHEE: + case PCI_DEVICE_ID_3DFX_VOODOO3: + strcpy(fix->id, + info->dev == PCI_DEVICE_ID_3DFX_BANSHEE + ? "3Dfx Banshee" : "3Dfx Voodoo3"); + fix->smem_start = info->bufbase_phys; + fix->smem_len = info->bufbase_size; + fix->mmio_start = info->regbase_phys; + fix->mmio_len = info->regbase_size; + + fix->accel = FB_ACCEL_3DFX_BANSHEE; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + fix->line_length = par->lpitch; + fix->visual = (par->bpp == 8) + ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + fix->xpanstep = 0; + fix->ypanstep = nopan ? 0 : 1; + fix->ywrapstep = 0; + + break; + default: + return -EINVAL; + } + + return 0; +} + +int tdfxfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *fb) +{ + const struct fb_info_tdfx *info = (struct fb_info_tdfx *) fb; + struct tdfxfb_par par; + + if (con == -1) + par = info->default_par; + else +{ + tdfxfb_decode_var(&fb_display[con].var, &par, info); +//DPRINTK("get_fix %s display=%p\n",info->card->name,&fb_display[con]); +} + tdfxfb_encode_fix(fix, &par, info); + return 0; +} + +int tdfxfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb) +{ + const struct fb_info_tdfx *info = (struct fb_info_tdfx *) fb; + + if (con == -1) + tdfxfb_encode_var(var, &info->default_par, info); + else +{ *var = fb_display[con].var; +//DPRINTK("get_var %s display=%p\n",info->card->name,&fb_display[con]); +} + return 0; +} + +void tdfxfb_set_disp(struct display *disp, + struct fb_info_tdfx *info, int bpp, int accel) +{ + + //DPRINTK("set_disp depth %u\n", bpp); + if (disp->dispsw && disp->conp) + fb_con.con_cursor(disp->conp, CM_ERASE); + switch (bpp) { +#ifdef FBCON_HAS_CFB8 + case 8: + info->dispsw = noaccel ? fbcon_cfb8 : fbcon_banshee8; + disp->dispsw = &info->dispsw; + if (nohwcursor) + fbcon_banshee8.cursor = NULL; + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 16: + info->dispsw = noaccel ? fbcon_cfb16 : fbcon_banshee16; + disp->dispsw = &info->dispsw; + disp->dispsw_data = info->fbcon_cmap.cfb16; + if (nohwcursor) + fbcon_banshee16.cursor = NULL; + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + info->dispsw = noaccel ? fbcon_cfb24 : fbcon_banshee24; + disp->dispsw = &info->dispsw; + disp->dispsw_data = info->fbcon_cmap.cfb24; + if (nohwcursor) + fbcon_banshee24.cursor = NULL; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + info->dispsw = noaccel ? fbcon_cfb32 : fbcon_banshee32; + disp->dispsw = &info->dispsw; + disp->dispsw_data = info->fbcon_cmap.cfb32; + if (nohwcursor) + fbcon_banshee32.cursor = NULL; + break; +#endif + default: + info->dispsw = fbcon_dummy; + disp->dispsw = &info->dispsw; + } + +} + +// con==-2: First call, without hardware initialization +// con==-1: If switch_con wasn't called in the register_framebuffer, then +// initialize the tdfx board +// con>=0: the set_var is now only called from ioctl (example by "fbset") +// (Future plan: Must be called from switch_con. (ToDo)) + +int tdfxfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) fb; + struct tdfxfb_par par; + struct display *display; + int oldaccel, accel, oldbpp, err; + int activate = var->activate; + int j, k; + int chgvar; + + if (con >= 0) + display = &fb_display[con]; + else + display = fb->disp; /* used during initialization */ + + if ((err = tdfxfb_decode_var(var, &par, info))) + return err; + + tdfxfb_encode_var(var, &par, info); + + if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { +// DPRINTK("set_var called on fb %s con=%d\n", +// info->card->name,con); + + oldaccel = display->var.accel_flags; + oldbpp = display->var.bits_per_pixel; + + if (con >= 0) { + chgvar = ((display->var.xres != var->xres) || + (display->var.yres != var->yres) || + (display->var.xres_virtual != + var->xres_virtual) + || (display->var.yres_virtual != + var->yres_virtual) + || (display->var.bits_per_pixel != + var->bits_per_pixel) + || memcmp(&display->var.red, &var->red, + sizeof(var->red)) + || memcmp(&display->var.green, &var->green, + sizeof(var->green)) + || memcmp(&display->var.blue, &var->blue, + sizeof(var->blue))); + } else { + chgvar = 0; + } + display->var = *var; + + if (con < 0 || chgvar || oldaccel != var->accel_flags) { + struct fb_fix_screeninfo fix; + + tdfxfb_encode_fix(&fix, &par, info); + display->screen_base = info->bufbase_virt; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->line_length = fix.line_length; + display->next_line = fix.line_length; + display->can_soft_blank = 1; + display->inverse = inverse; + accel = var->accel_flags & FB_ACCELF_TEXT; + tdfxfb_set_disp(display, info, par.bpp, accel); + + if (nopan) + display->scrollmode = SCROLL_YREDRAW; + + if (chgvar && info && info->fb_info.changevar) + (*info->fb_info.changevar) (con); + } + if (con != -2) { + if (var->bits_per_pixel == 8) + for (j = 0; j < 16; j++) { + k = color_table[j]; + info->palette[j].red = default_red[k]; + info->palette[j].green = + default_grn[k]; + info->palette[j].blue = + default_blu[k]; + } + + del_timer_sync(&(info->cursor.timer)); + info->cursor.state = CM_ERASE; + if (!info->fb_info.display_fg || + info->fb_info.display_fg->vc_num == con || + con < 0) do_set_par(&par, info); + if (display && display->conp) { + if (!nohwcursor) + tdfxfb_createcursor(display); + info->current_par.putc = do_putc; + info->current_par.putcs = do_putcs; + } + info->cursor.redraw = 1; + if (oldbpp != var->bits_per_pixel || con < 0) { + if ( + (err = + fb_alloc_cmap(&display->cmap, 0, + 0))) return err; + tdfxfb_install_cmap(display, + &(info->fb_info)); + } + } +// DPRINTK("set_var ended\n"); + + } + return 0; +} + +int tdfxfb_pan_display(struct fb_var_screeninfo *var, + int con, struct fb_info *fb) +{ + struct fb_info_tdfx *i = (struct fb_info_tdfx *) fb; + + if (nopan) + return -EINVAL; + if (var->xoffset) + return -EINVAL; + + if (var->yoffset + var->yres > var->yres_virtual) + return -EINVAL; + + if (var->vmode & FB_VMODE_YWRAP) { + if (var->yoffset + var->yres > var->yres_virtual) + return -EINVAL; + } else { + if (var->yoffset > var->yres_virtual) + return -EINVAL; + } + + if (con == i->currcon) + do_pan_var(var, i); + + fb_display[con].var.xoffset = var->xoffset; + fb_display[con].var.yoffset = var->yoffset; + if (var->vmode & FB_VMODE_YWRAP) + fb_display[con].var.vmode |= FB_VMODE_YWRAP; + else + fb_display[con].var.vmode &= ~FB_VMODE_YWRAP; + return 0; +} + +int tdfxfb_get_cmap(struct fb_cmap *cmap, + int kspc, int con, struct fb_info *fb) +{ + + struct fb_info_tdfx *i = (struct fb_info_tdfx *) fb; + struct display *d = (con < 0) ? fb->disp : fb_display + con; + + if (con == i->currcon) { + /* current console? */ + return fb_get_cmap(cmap, kspc, tdfxfb_getcolreg, fb); + } else if (d->cmap.len) { + /* non default colormap? */ + fb_copy_cmap(&d->cmap, cmap, kspc ? 0 : 2); + } else { + fb_copy_cmap(fb_default_cmap(i->current_par.cmap_len), cmap, + kspc ? 0 : 2); + } + return 0; +} + +int tdfxfb_set_cmap(struct fb_cmap *cmap, + int kspc, int con, struct fb_info *fb) +{ + struct display *d = (con < 0) ? fb->disp : fb_display + con; + struct fb_info_tdfx *i = (struct fb_info_tdfx *) fb; + int cmap_len = (i->current_par.bpp == 8) ? 256 : 16; + + if (d->cmap.len != cmap_len) { + int err; + if ((err = fb_alloc_cmap(&d->cmap, cmap_len, 0))) + return err; + } + if (con == i->currcon) { + /* current console? */ + return fb_set_cmap(cmap, kspc, tdfxfb_setcolreg, fb); + } else { + fb_copy_cmap(cmap, &d->cmap, kspc ? 0 : 1); + } + return 0; +} + +int tdfxfb_ioctl(struct inode *inode, + struct file *file, + u_int cmd, u_long arg, int con, struct fb_info *fb) +{ +#ifdef TDFXFB_DEBUG + int ret; + int reg; + int outpar[2]; + + switch (cmd) { + case 0x4680: + DPRINTK("Ioctl: get_monitor_sense\n"); + ret = do_get_monitor_sense((struct fb_info_tdfx *) fb); + return copy_to_user((void *) arg, &ret, 1) ? -EFAULT : 0; + // inb + case 0x4681: + if (copy_from_user ( ®, (void *)arg, sizeof(reg) )) + return -EFAULT; + ret = do_inb((struct fb_info_tdfx *)fb,reg); + if (copy_to_user( (void *)arg, &ret, sizeof(ret))) + return -EFAULT; + return 0; + // outb + case 0x4682: + if (copy_from_user ( &outpar[0], (void *)arg, 8 )) + return -EFAULT; + do_outb((struct fb_info_tdfx *)fb,outpar[0],outpar[1]); + return 0; + } +#endif + return -EINVAL; +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ +void tdfxfb_blank(int blank, struct fb_info *fb) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) fb; + u32 state = 0, vgablank = 0; + + switch (blank) { + case 0: /* Screen: On; HSync: On, VSync: On */ + state = 0; + vgablank = 0; + break; + case 1: /* Screen: Off; HSync: On, VSync: On */ + state = 0; + vgablank = 1; + break; + case 2: /* Screen: Off; HSync: On, VSync: Off */ + state = 8; + vgablank = 1; + break; + case 3: /* Screen: Off; HSync: Off, VSync: On */ + state = 2; + vgablank = 1; + break; + case 4: /* Screen: Off; HSync: Off, VSync: Off */ + state = 8 | 2; + vgablank = 1; + break; + } + + do_blank(info, state, vgablank); + return; +} + +int tdfxfb_updatevar(int con, struct fb_info *fb) +{ + + struct fb_info_tdfx *i = (struct fb_info_tdfx *) fb; + if ((con == i->currcon) && (!nopan)) + do_pan_var(&fb_display[con].var, i); + return 0; +} + +int tdfxfb_getcolreg(unsigned regno, + unsigned *red, + unsigned *green, + unsigned *blue, unsigned *transp, struct fb_info *fb) +{ + struct fb_info_tdfx *i = (struct fb_info_tdfx *) fb; + +#ifdef CONFIG_PPC + unsigned tmp; +#endif + if (regno > i->current_par.cmap_len) + return 1; + +#ifdef CONFIG_PPC + tmp = i->palette[regno].red; + *red = (((tmp >> 8) & 0xff) | ((tmp & 0xff) << 8)); + tmp = i->palette[regno].green; + *green = (((tmp >> 8) & 0xff) | ((tmp & 0xff) << 8)); + tmp = i->palette[regno].blue; + *blue = (((tmp >> 8) & 0xff) | ((tmp & 0xff) << 8)); +#else + *red = i->palette[regno].red; + *green = i->palette[regno].green; + *blue = i->palette[regno].blue; +#endif + *transp = 0; + + return 0; +} + +int tdfxfb_setcolreg(unsigned regno, + unsigned red, + unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + struct fb_info_tdfx *i = (struct fb_info_tdfx *) info; + void *mmiobase = i->regbase_virt; + u32 rgbcol; + +#ifdef CONFIG_PPC + int x; +#endif + + assert(i->currcon_display != NULL); + + if (regno >= i->current_par.cmap_len) + return 1; + + i->palette[regno].red = red; + i->palette[regno].green = green; + i->palette[regno].blue = blue; + + switch (i->currcon_display->var.bits_per_pixel) { +#ifdef FBCON_HAS_CFB8 + case 8: + rgbcol = (((u32) red & 0xff00) << 8) | + (((u32) green & 0xff00) << 0) | + (((u32) blue & 0xff00) >> 8); + do_setpalentry(mmiobase, regno, rgbcol); + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 16: +#ifdef CONFIG_PPC + i->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) | regno; +#else + i->fbcon_cmap.cfb16[regno] = + (((u32) red & 0xf800) >> 0) | + (((u32) green & 0xfc00) >> 5) | + (((u32) blue & 0xf800) >> 11); +#endif + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + i->fbcon_cmap.cfb24[regno] = + (((u32) red & 0xff00) << 8) | + (((u32) green & 0xff00) << 0) | + (((u32) blue & 0xff00) >> 8); + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: +#ifdef CONFIG_PPC + x = (regno << 8) | regno; + i->fbcon_cmap.cfb32[regno] = (x << 16) | x; +#else + i->fbcon_cmap.cfb32[regno] = + (((u32) red & 0xff00) << 8) | + (((u32) green & 0xff00) << 0) | + (((u32) blue & 0xff00) >> 8); +#endif + break; +#endif + default: + DPRINTK("bad depth %u\n", i->current_par.bpp); + break; + } + return 0; +} + +void tdfxfb_install_cmap(struct display *d, struct fb_info *info) +{ + struct fb_info_tdfx *i = (struct fb_info_tdfx *) info; + + if (d->cmap.len) { + fb_set_cmap(&(d->cmap), 1, tdfxfb_setcolreg, info); + } else { + fb_set_cmap(fb_default_cmap(i->current_par.cmap_len), 1, + tdfxfb_setcolreg, info); + } +} +int tdfxfb_switch_con(int con, struct fb_info *fb) +{ + struct fb_info_tdfx *info = (struct fb_info_tdfx *) fb; + struct tdfxfb_par par; + struct tdfxfb_par old_par; + int prev_con; + struct display *prev_dsp; + struct display *dsp; + +// DPRINTK("tdfxfb: switch_con...old: %d new: %d\n",old_con,con); + + dsp = (con<0) ? info->disp : &fb_display[con]; + prev_dsp = info->currcon_display; + prev_con=info->currcon; + + DPRINTK("start with fb %s pdsp=%p dsp=%p prevcon=%d con=%d\n", + info->card->name,prev_dsp,dsp,prev_con,con); + + /* Do we have to save the colormap? */ + if (info->currcon >= 0) + if (dsp->cmap.len) + fb_get_cmap(&(dsp->cmap), 1, + tdfxfb_getcolreg, fb); + + info->currcon = con; + info->currcon_display = dsp; + + dsp->var.activate = FB_ACTIVATE_NOW; + tdfxfb_decode_var(&(dsp->var), &par, info); + +// if (prev_con >= 0 && (prev_con != con)) { + if( (prev_con>=0) && + (prev_con!=con) && + (vt_cons[prev_con]->vc_mode != KD_GRAPHICS) ) { + /* check if we have to change video registers */ + tdfxfb_decode_var(&(prev_dsp->var), &old_par, info); + if (memcmp(&par, &old_par, sizeof(par))) { /* avoid flicker */ + do_set_par(&par, info); + } else +#ifdef XServerVTSwitchBug + { + if (dsp->conp && !nohwcursor) { + do_hwcursor_reset(info); + } + } +#endif + } else { + do_set_par(&par, info); + } + + if (dsp->conp) { + if (!nohwcursor) + tdfxfb_createcursor(dsp); + info->current_par.putc = do_putc; + info->current_par.putcs = do_putcs; + } + + tdfxfb_set_disp(dsp, + info, par.bpp, par.accel_flags & FB_ACCELF_TEXT); + + del_timer_sync(&(info->cursor.timer)); + info->cursor.state = CM_ERASE; + info->cursor.redraw = 1; + + tdfxfb_install_cmap(dsp, fb); + tdfxfb_updatevar(con, fb); + + return 1; +} + +void tdfxfb_createcursorshape(struct display *p) +{ + unsigned int h, cu, cd; + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + + h = fontheight(p); + cd = h; + if (cd >= 10) + cd--; + info->cursor.type = p->conp->vc_cursor_type & CUR_HWMASK; + switch (info->cursor.type) { + case CUR_NONE: + cu = cd; + break; + case CUR_UNDERLINE: + cu = cd - 2; + break; + case CUR_LOWER_THIRD: + cu = (h * 2) / 3; + break; + case CUR_LOWER_HALF: + cu = h / 2; + break; + case CUR_TWO_THIRDS: + cu = h / 3; + break; + case CUR_BLOCK: + default: + cu = 0; + cd = h; + break; + } + info->cursor.w = fontwidth(p); + info->cursor.u = cu; + info->cursor.d = cd; +} + +void tdfxfb_createcursor(struct display *p) +{ + u8 *cursorbase; + u32 xline; + unsigned int i; + unsigned int h, to; + struct fb_info_tdfx *info = (struct fb_info_tdfx *) p->fb_info; + + tdfxfb_createcursorshape(p); + xline = (~0) << (32 - info->cursor.w); + +#ifdef __BIG_ENDIAN + switch (p->var.bits_per_pixel) { + case 16: + xline=((xline & 0xff00ff00) >> 8) | ((xline & 0xff00ff) << 8); + break; + case 32: + xline=((xline & 0xff000000) >> 24) + | ((xline & 0xff0000) >> 8) + | ((xline & 0xff00) << 8) + | ((xline & 0xff) << 24); + break; + } +#endif + + cursorbase = (u8 *) info->bufbase_virt; + h = info->cursor.cursorimage; + + to = info->cursor.u; + for (i = 0; i < to; i++) { + writel(0, cursorbase + h); + writel(0, cursorbase + h + 4); + writel(~0, cursorbase + h + 8); + writel(~0, cursorbase + h + 12); + h += 16; + } + + to = info->cursor.d; + + for (; i < to; i++) { + writeb(xline >> 24, cursorbase + h); + writeb(xline >> 16, cursorbase + h + 1); + writeb(xline >> 8, cursorbase + h + 2); + writeb(xline, cursorbase + h + 3); + writel(0, cursorbase + h + 4); + writel(~0, cursorbase + h + 8); + writel(~0, cursorbase + h + 12); + h += 16; + } + + for (; i < 64; i++) { + writel(0, cursorbase + h); + writel(0, cursorbase + h + 4); + writel(~0, cursorbase + h + 8); + writel(~0, cursorbase + h + 12); + h += 16; + } +} + +/***************************** + * load,unload,open,close + *****************************/ + +void tdfxfb_hwcursor_init(struct fb_info_tdfx *info) +{ + unsigned int start; + start = (info->bufbase_size - 1024) & PAGE_MASK; + info->bufbase_size = start; + info->cursor.cursorimage = info->bufbase_size; + printk + ("tdfxfb: allocating 1024 bytes from the framebuffer (for hwcursor image)\n"); +} +int tdfxfb_check_regions(struct pci_dev *pdev) +{ + int unavail=0; + unavail = check_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + unavail |= check_mem_region(pci_resource_start(pdev, 1), + pci_resource_len(pdev, 1)); + unavail |= check_region(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + return (unavail) ? -1 : 0; +} + +void tdfxfb_request_regions(struct pci_dev *pdev) +{ + request_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), "tdfxfb mmiobase"); + request_mem_region(pci_resource_start(pdev, 1), + pci_resource_len(pdev, 1), "tdfxfb framebuffer"); + request_region(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2), "tdfxfb iobase"); +} + +void tdfxfb_release_regions(struct pci_dev *pdev) +{ + if (!pdev) return; + + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + release_mem_region(pci_resource_start(pdev, 1), + pci_resource_len(pdev, 1)); + release_region(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + +} + +static int tdfxfb_probe(struct pci_dev *pdev, + const struct pci_device_id *dummy) +{ + struct fb_var_screeninfo var; + u32 cmd,initcmd; + int maxpixclock; + struct fb_info_tdfx *info; + short vendor = pdev->vendor; + short device = pdev->device; + short sdid = pdev->subsystem_device; + const char *name = ""; + struct board *b = NULL; + struct display *d; + int i; + + i = 0; + while (board_list[i].vendor) { + b = &(board_list[i]); + if ((b->vendor == vendor) && (b->device == device)) { + if (b->sdid == -1) + break; + if (b->sdid == sdid) + break; + } + i++; + } + + if (!board_list[i].vendor) + return -1; + + pci_read_config_dword(pdev, PCI_COMMAND, &initcmd); + + name = b->name; + maxpixclock = b->maxclk; + + if (pci_enable_device(pdev)) { + printk(KERN_ERR "tdfxfb: Unable to enable PCI device.\n"); + return -1; + } + pci_read_config_dword(pdev, PCI_COMMAND, &cmd); + cmd = cmd & ~PCI_COMMAND_VGA_PALETTE; + pci_write_config_dword(pdev, PCI_COMMAND, cmd); + + info = (struct fb_info_tdfx *) kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -1; + d= (struct display *) kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) goto err1; + + memset(info, 0, sizeof(*info)); + memset(d, 0, sizeof(*d)); + +#if defined(__mips__) +// something is buggy with mips, because the memory is always enabled. +// (Maybe PCI initialization bug in the kernel?) + noinit=0; +#endif + +// info->devflags.inverse=0; +// info->devflags.hwcursor=0; +// info->devflags.accel=0; + if (initcmd & PCI_COMMAND_MEMORY) { + DPRINTK("PCI memory enabled. name=%s noinit=%d\n", + b->name,noinit); + info->devflags.noinit = noinit; + noinit=0; + } else { + DPRINTK("PCI memory disabled. Initialize it. name=%s\n", + b->name); + info->devflags.noinit = 0; + } + + + pci_set_drvdata(pdev,info); + info->pdev = pdev; + info->card = b; + info->dev = pdev->device; + info->max_pixclock = maxpixclock; + info->usecount = 0; + info->dead = 0; + info->disp = d; + info->regbase_phys = pci_resource_start(pdev, 0); + info->bufbase_phys = pci_resource_start(pdev, 1); + info->iobase = pci_resource_start(pdev, 2); + + if (!info->regbase_phys) { + printk(KERN_ERR "tdfxfb: membase0 unavailable.\n"); + goto err2; + } + if (!info->bufbase_phys) { + printk(KERN_ERR "tdfxfb: membase1 unavailable.\n"); + goto err2; + } + if (!info->iobase) { + printk(KERN_ERR "tdfxfb: iobase0 unavailable.\n"); + goto err2; + } + if (tdfxfb_check_regions(pdev)) { + printk(KERN_ERR "tdfxfb: PCI regions unavailable\n"); + goto err2; + } + tdfxfb_request_regions(pdev); + + info->regbase_size = 1 << 24; + info->regbase_virt = ioremap_nocache(info->regbase_phys, 1 << 24); + if (!info->regbase_virt) { + printk("fb: Can't remap %s register area.\n", name); + goto err3; + } + if (do_card_preinit(info)) { + printk(KERN_ERR "tdfxfb: can't initialize card.\n"); + goto err3; + } + + if (!(info->bufbase_size = do_lfb_size(info) * 1024)) { + printk("fb: Can't count %s memory.\n", name); + iounmap(info->regbase_virt); + goto err3; + } + info->bufbase_virt = + ioremap_nocache(info->bufbase_phys, info->bufbase_size); + + if (!info->bufbase_virt) { + printk("fb: Can't remap %s framebuffer.\n", name); + iounmap(info->bufbase_virt); + iounmap(info->regbase_virt); + goto err3; + } + + DPRINTK("fb: %s memory = %ldK\n", name, info->bufbase_size >> 10); + +#ifdef CONFIG_MTRR + if (!nomtrr) { + info->mtrr_idx = + mtrr_add(info->bufbase_phys, info->bufbase_size, + MTRR_TYPE_WRCOMB, 1); + printk("fb: MTRR's turned on\n"); + } +#endif + + info->currcon = -1; + info->currcon_display=info->disp; + + if (!nohwcursor) + tdfxfb_hwcursor_init(info); + init_timer(&info->cursor.timer); + info->cursor.timer.function = do_flashcursor; + info->cursor.state = CM_ERASE; + info->cursor.timer.data = (unsigned long) info; + spin_lock_init(&info->DAClock); + strcpy(info->fb_info.modename, "3Dfx "); + strcat(info->fb_info.modename, name); + info->fb_info.changevar = NULL; + info->fb_info.node = -1; + info->fb_info.fbops = &tdfxfb_ops; + info->fb_info.disp = info->disp; + strcpy(info->fb_info.fontname, fontname); + info->fb_info.switch_con = &tdfxfb_switch_con; + info->fb_info.updatevar = &tdfxfb_updatevar; + info->fb_info.blank = &tdfxfb_blank; + info->fb_info.flags = FBINFO_FLAG_DEFAULT; + + memset(&var, 0, sizeof(var)); + + fb_memset(info->bufbase_virt, 0, info->bufbase_size); + +#ifndef MODULE + if (!mode_option || + !fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0, NULL, + 8)) +#endif + var = default_mode[0].var; + + if (noaccel) + var.accel_flags &= ~FB_ACCELF_TEXT; + else + var.accel_flags |= FB_ACCELF_TEXT; + + if (tdfxfb_decode_var(&var, &info->default_par, info)) { + /* ugh -- can't use the mode from the mode db. (or command line), + so try the default */ + + printk("tdfxfb: " + "can't decode the supplied video mode, using default\n"); + + var = default_mode[0].var; + if (noaccel) + var.accel_flags &= ~FB_ACCELF_TEXT; + else + var.accel_flags |= FB_ACCELF_TEXT; + + if (tdfxfb_decode_var(&var, &info->default_par, info)) { + /* this is getting really bad!... */ + printk("tdfxfb: can't decode default video mode\n"); + goto err3; + } + } + + if (tdfxfb_set_var(&var, -2, &info->fb_info)) { + printk("tdfxfb: can't set default video mode\n"); + goto err3; + } + + if (register_framebuffer(&info->fb_info) < 0) { + printk("tdfxfb: can't register framebuffer\n"); + goto err3; + } + + printk("fb%d: %s frame buffer device, %ldK @ 0x%lX\n", + GET_FB_IDX(info->fb_info.node), info->fb_info.modename, + info->bufbase_size >> 10, info->bufbase_phys); + if ((info->currcon < 0) || !(info->devflags.noinit)) { + // OOps! The fbcon didn't called our switch_con(). + // Maybe there is no console on this fb... So, initialize hw. + // Or, secund case: if we are not on the first head (multiheaded), + // then set the mode on it. + tdfxfb_set_var(&var, -1, &info->fb_info); + } + list_add(&info->next_fb, &fb_list); + return 0; + + err3: + tdfxfb_release_regions(pdev); + err2: + kfree(d); + err1: + kfree(info); + return -1; +} + +static void tdfxfb_remove(struct fb_info_tdfx *info) +{ + info->dead = 1; + if (info->usecount) { + // Can't unregister, it's still in use. + // FIXME: What should we do now? + return; + } + list_del(&info->next_fb); + unregister_framebuffer(&info->fb_info); + del_timer_sync(&(info->cursor.timer)); +#ifdef CONFIG_MTRR + if (!nomtrr) { + mtrr_del(info->mtrr_idx, info->bufbase_phys, + info->bufbase_size); + printk("fb: MTRR's turned off\n"); + } +#endif + iounmap(info->regbase_virt); + iounmap(info->bufbase_virt); + tdfxfb_release_regions(info->pdev); + printk("tdfxfb: %s unregistered successfully\n",info->card->name); + kfree(info->disp); + kfree(info); +} +static void tdfxfb_remove_pci(struct pci_dev *pdev) +{ + struct fb_info_tdfx *info = pci_get_drvdata(pdev); + tdfxfb_remove(info); +} +static struct pci_driver tdfxfb_driver = { + name: "tdfxfb", + id_table: tdfxfb_devices, + probe: tdfxfb_probe, + remove: tdfxfb_remove_pci +}; + +int tdfxfb_open(struct fb_info *info, int user) +{ + struct fb_info_tdfx *i = (struct fb_info_tdfx *) info; + if (i->dead) { + return -ENXIO; + } + i->usecount++; + return (0); +} + +int tdfxfb_release(struct fb_info *info, int user) +{ + struct fb_info_tdfx *i = (struct fb_info_tdfx *) info; + + i->usecount--; + if ((i->usecount == 0) && (i->dead == 1)) { + tdfxfb_remove(i); + } + return (0); +} + +void __exit tdfxfb_exit(void) +{ + pci_unregister_driver(&tdfxfb_driver); +} +static int __init initialized = 0; + +int tdfxfb_init(void) +{ + if (!initialized) { + initialized = 1; + pci_register_driver(&tdfxfb_driver); + } + return 0; +} + +#ifndef MODULE +int tdfxfb_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + for (this_opt = strtok(options, ","); + this_opt; this_opt = strtok(NULL, ",")) { + if (!strcmp(this_opt, "inverse")) { + inverse = 1; + fb_invert_cmaps(); + } else if (!strcmp(this_opt, "noaccel")) + noaccel = nopan = nohwcursor = 1; + else if (!strcmp(this_opt, "nopan")) + nopan = 1; + else if (!strcmp(this_opt, "nohwcursor")) + nohwcursor = 1; +#ifdef CONFIG_MTRR + else if (!strcmp(this_opt, "nomtrr")) + nomtrr = 1; +#endif + else if (!strncmp(this_opt, "font:", 5)) + strncpy(fontname, this_opt + 5, 40); + else { + mode_option = this_opt; + } + } + return 0; +} +#else +int __init tdfxfb_module_init(void) +{ + tdfxfb_init(); + return 0; +} +#endif + +MODULE_AUTHOR + ("(c) 1999,2000 Attila Kesmarki & Hannu Mallat "); +MODULE_DESCRIPTION("FBDev driver for 3dfx Voodoo Banshee and Voodoo3 cards"); + +MODULE_PARM(inverse, "i"); +MODULE_PARM_DESC(inverse, "Invert colors if 1. Default is 0"); + +MODULE_PARM(noaccel, "i"); +MODULE_PARM_DESC(noaccel, + "Disables all the acceleration supports, including hardware cursor and panning too"); + +MODULE_PARM(nopan, "i"); +MODULE_PARM_DESC(nopan, "Disables panning if 1 (defaults to 0)"); + +MODULE_PARM(nohwcursor, "i"); +MODULE_PARM_DESC(nohwcursor, + "Disables the usage of the hardware cursor if set to 1 (default is 0)"); + +#ifdef CONFIG_MTRR +MODULE_PARM(nomtrr, "i"); +MODULE_PARM_DESC(nomtrr, + "1 disables the use of the mtrr reg. /x86 only/ (default is 0)"); +#endif + +MODULE_PARM(fontname, "1-40s"); +MODULE_PARM_DESC(fontname, "Name of the startup font"); + +#ifdef MODULE +module_init(tdfxfb_module_init); +#endif + +module_exit(tdfxfb_exit); + diff -u --recursive --new-file linux.2.4.4.orig/drivers/video/tdfx/tdfx_base.h linux/drivers/video/tdfx/tdfx_base.h --- linux.2.4.4.orig/drivers/video/tdfx/tdfx_base.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/tdfx/tdfx_base.h Tue May 1 21:56:47 2001 @@ -0,0 +1,294 @@ +/* + * + * tdfx_base.h + * + * Framebuffer driver for video cards with 3dfx chipsets + * + * Authors: + * Hannu Mallat + * Attila Kesmarki + * + * Copyright (C) 1999,2000 Hannu Mallat & Attila Kesmarki + * All rights reserved + * + */ + +#ifndef __TDFXFB_H__ +#define __TDFXFB_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif + +#include