1667 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *4 * tdfxfb.c5 *6 * Author: Hannu Mallat <hmallat@cc.hut.fi>7 *8 * Copyright © 1999 Hannu Mallat9 * All rights reserved10 *11 * Created : Thu Sep 23 18:17:43 1999, hmallat12 * Last modified: Tue Nov 2 21:19:47 1999, hmallat13 *14 * I2C part copied from the i2c-voodoo3.c driver by:15 * Frodo Looijaard <frodol@dds.nl>,16 * Philip Edelbrock <phil@netroedge.com>,17 * Ralph Metzler <rjkm@thp.uni-koeln.de>, and18 * Mark D. Studebaker <mdsxyz123@yahoo.com>19 *20 * Lots of the information here comes from the Daryll Strauss' Banshee21 * patches to the XF86 server, and the rest comes from the 3dfx22 * Banshee specification. I'm very much indebted to Daryll for his23 * work on the X server.24 *25 * Voodoo3 support was contributed Harold Oga. Lots of additions26 * (proper acceleration, 24 bpp, hardware cursor) and bug fixes by Attila27 * Kesmarki. Thanks guys!28 *29 * Voodoo1 and Voodoo2 support aren't relevant to this driver as they30 * behave very differently from the Voodoo3/4/5. For anyone wanting to31 * use frame buffer on the Voodoo1/2, see the sstfb driver (which is32 * located at http://www.sourceforge.net/projects/sstfb).33 *34 * While I _am_ grateful to 3Dfx for releasing the specs for Banshee,35 * I do wish the next version is a bit more complete. Without the XF8636 * patches I couldn't have gotten even this far... for instance, the37 * extensions to the VGA register set go completely unmentioned in the38 * spec! Also, lots of references are made to the 'SST core', but no39 * spec is publicly available, AFAIK.40 *41 * The structure of this driver comes pretty much from the Permedia42 * driver by Ilario Nardinocchi, which in turn is based on skeletonfb.43 *44 * TODO:45 * - multihead support (basically need to support an array of fb_infos)46 * - support other architectures (PPC, Alpha); does the fact that the VGA47 * core can be accessed only thru I/O (not memory mapped) complicate48 * things?49 *50 * Version history:51 *52 * 0.1.4 (released 2002-05-28) ported over to new fbdev api by James Simmons53 *54 * 0.1.3 (released 1999-11-02) added Attila's panning support, code55 * reorg, hwcursor address page size alignment56 * (for mmapping both frame buffer and regs),57 * and my changes to get rid of hardcoded58 * VGA i/o register locations (uses PCI59 * configuration info now)60 * 0.1.2 (released 1999-10-19) added Attila Kesmarki's bug fixes and61 * improvements62 * 0.1.1 (released 1999-10-07) added Voodoo3 support by Harold Oga.63 * 0.1.0 (released 1999-10-06) initial version64 *65 */66 67#include <linux/aperture.h>68#include <linux/module.h>69#include <linux/kernel.h>70#include <linux/errno.h>71#include <linux/string.h>72#include <linux/mm.h>73#include <linux/slab.h>74#include <linux/fb.h>75#include <linux/init.h>76#include <linux/pci.h>77#include <asm/io.h>78 79#include <video/tdfx.h>80 81#define DPRINTK(a, b...) pr_debug("fb: %s: " a, __func__ , ## b)82 83#define BANSHEE_MAX_PIXCLOCK 27000084#define VOODOO3_MAX_PIXCLOCK 30000085#define VOODOO5_MAX_PIXCLOCK 35000086 87static const struct fb_fix_screeninfo tdfx_fix = {88 .type = FB_TYPE_PACKED_PIXELS,89 .visual = FB_VISUAL_PSEUDOCOLOR,90 .ypanstep = 1,91 .ywrapstep = 1,92 .accel = FB_ACCEL_3DFX_BANSHEE93};94 95static const struct fb_var_screeninfo tdfx_var = {96 /* "640x480, 8 bpp @ 60 Hz */97 .xres = 640,98 .yres = 480,99 .xres_virtual = 640,100 .yres_virtual = 1024,101 .bits_per_pixel = 8,102 .red = {0, 8, 0},103 .blue = {0, 8, 0},104 .green = {0, 8, 0},105 .activate = FB_ACTIVATE_NOW,106 .height = -1,107 .width = -1,108 .accel_flags = FB_ACCELF_TEXT,109 .pixclock = 39722,110 .left_margin = 40,111 .right_margin = 24,112 .upper_margin = 32,113 .lower_margin = 11,114 .hsync_len = 96,115 .vsync_len = 2,116 .vmode = FB_VMODE_NONINTERLACED117};118 119/*120 * PCI driver prototypes121 */122static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id);123static void tdfxfb_remove(struct pci_dev *pdev);124 125static const struct pci_device_id tdfxfb_id_table[] = {126 { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE,127 PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,128 0xff0000, 0 },129 { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3,130 PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,131 0xff0000, 0 },132 { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO5,133 PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,134 0xff0000, 0 },135 { 0, }136};137 138static struct pci_driver tdfxfb_driver = {139 .name = "tdfxfb",140 .id_table = tdfxfb_id_table,141 .probe = tdfxfb_probe,142 .remove = tdfxfb_remove,143};144 145MODULE_DEVICE_TABLE(pci, tdfxfb_id_table);146 147/*148 * Driver data149 */150static int nopan;151static int nowrap = 1; /* not implemented (yet) */152static int hwcursor = 1;153static char *mode_option;154static bool nomtrr;155 156/* -------------------------------------------------------------------------157 * Hardware-specific funcions158 * ------------------------------------------------------------------------- */159 160static inline u8 vga_inb(struct tdfx_par *par, u32 reg)161{162 return inb(par->iobase + reg - 0x300);163}164 165static inline void vga_outb(struct tdfx_par *par, u32 reg, u8 val)166{167 outb(val, par->iobase + reg - 0x300);168}169 170static inline void gra_outb(struct tdfx_par *par, u32 idx, u8 val)171{172 vga_outb(par, GRA_I, idx);173 wmb();174 vga_outb(par, GRA_D, val);175 wmb();176}177 178static inline void seq_outb(struct tdfx_par *par, u32 idx, u8 val)179{180 vga_outb(par, SEQ_I, idx);181 wmb();182 vga_outb(par, SEQ_D, val);183 wmb();184}185 186static inline u8 seq_inb(struct tdfx_par *par, u32 idx)187{188 vga_outb(par, SEQ_I, idx);189 mb();190 return vga_inb(par, SEQ_D);191}192 193static inline void crt_outb(struct tdfx_par *par, u32 idx, u8 val)194{195 vga_outb(par, CRT_I, idx);196 wmb();197 vga_outb(par, CRT_D, val);198 wmb();199}200 201static inline u8 crt_inb(struct tdfx_par *par, u32 idx)202{203 vga_outb(par, CRT_I, idx);204 mb();205 return vga_inb(par, CRT_D);206}207 208static inline void att_outb(struct tdfx_par *par, u32 idx, u8 val)209{210 vga_inb(par, IS1_R);211 vga_outb(par, ATT_IW, idx);212 vga_outb(par, ATT_IW, val);213}214 215static inline void vga_disable_video(struct tdfx_par *par)216{217 unsigned char s;218 219 s = seq_inb(par, 0x01) | 0x20;220 seq_outb(par, 0x00, 0x01);221 seq_outb(par, 0x01, s);222 seq_outb(par, 0x00, 0x03);223}224 225static inline void vga_enable_video(struct tdfx_par *par)226{227 unsigned char s;228 229 s = seq_inb(par, 0x01) & 0xdf;230 seq_outb(par, 0x00, 0x01);231 seq_outb(par, 0x01, s);232 seq_outb(par, 0x00, 0x03);233}234 235static inline void vga_enable_palette(struct tdfx_par *par)236{237 vga_inb(par, IS1_R);238 mb();239 vga_outb(par, ATT_IW, 0x20);240}241 242static inline u32 tdfx_inl(struct tdfx_par *par, unsigned int reg)243{244 return readl(par->regbase_virt + reg);245}246 247static inline void tdfx_outl(struct tdfx_par *par, unsigned int reg, u32 val)248{249 writel(val, par->regbase_virt + reg);250}251 252static inline void banshee_make_room(struct tdfx_par *par, int size)253{254 /* Note: The Voodoo3's onboard FIFO has 32 slots. This loop255 * won't quit if you ask for more. */256 while ((tdfx_inl(par, STATUS) & 0x1f) < size - 1)257 cpu_relax();258}259 260static int banshee_wait_idle(struct fb_info *info)261{262 struct tdfx_par *par = info->par;263 int i = 0;264 265 banshee_make_room(par, 1);266 tdfx_outl(par, COMMAND_3D, COMMAND_3D_NOP);267 268 do {269 if ((tdfx_inl(par, STATUS) & STATUS_BUSY) == 0)270 i++;271 } while (i < 3);272 273 return 0;274}275 276/*277 * Set the color of a palette entry in 8bpp mode278 */279static inline void do_setpalentry(struct tdfx_par *par, unsigned regno, u32 c)280{281 banshee_make_room(par, 2);282 tdfx_outl(par, DACADDR, regno);283 /* read after write makes it working */284 tdfx_inl(par, DACADDR);285 tdfx_outl(par, DACDATA, c);286}287 288static u32 do_calc_pll(int freq, int *freq_out)289{290 int m, n, k, best_m, best_n, best_k, best_error;291 int fref = 14318;292 293 best_error = freq;294 best_n = best_m = best_k = 0;295 296 for (k = 3; k >= 0; k--) {297 for (m = 63; m >= 0; m--) {298 /*299 * Estimate value of n that produces target frequency300 * with current m and k301 */302 int n_estimated = ((freq * (m + 2) << k) / fref) - 2;303 304 /* Search neighborhood of estimated n */305 for (n = max(0, n_estimated);306 n <= min(255, n_estimated + 1);307 n++) {308 /*309 * Calculate PLL freqency with current m, k and310 * estimated n311 */312 int f = (fref * (n + 2) / (m + 2)) >> k;313 int error = abs(f - freq);314 315 /*316 * If this is the closest we've come to the317 * target frequency then remember n, m and k318 */319 if (error < best_error) {320 best_error = error;321 best_n = n;322 best_m = m;323 best_k = k;324 }325 }326 }327 }328 329 n = best_n;330 m = best_m;331 k = best_k;332 *freq_out = (fref * (n + 2) / (m + 2)) >> k;333 334 return (n << 8) | (m << 2) | k;335}336 337static void do_write_regs(struct fb_info *info, struct banshee_reg *reg)338{339 struct tdfx_par *par = info->par;340 int i;341 342 banshee_wait_idle(info);343 344 tdfx_outl(par, MISCINIT1, tdfx_inl(par, MISCINIT1) | 0x01);345 346 crt_outb(par, 0x11, crt_inb(par, 0x11) & 0x7f); /* CRT unprotect */347 348 banshee_make_room(par, 3);349 tdfx_outl(par, VGAINIT1, reg->vgainit1 & 0x001FFFFF);350 tdfx_outl(par, VIDPROCCFG, reg->vidcfg & ~0x00000001);351#if 0352 tdfx_outl(par, PLLCTRL1, reg->mempll);353 tdfx_outl(par, PLLCTRL2, reg->gfxpll);354#endif355 tdfx_outl(par, PLLCTRL0, reg->vidpll);356 357 vga_outb(par, MISC_W, reg->misc[0x00] | 0x01);358 359 for (i = 0; i < 5; i++)360 seq_outb(par, i, reg->seq[i]);361 362 for (i = 0; i < 25; i++)363 crt_outb(par, i, reg->crt[i]);364 365 for (i = 0; i < 9; i++)366 gra_outb(par, i, reg->gra[i]);367 368 for (i = 0; i < 21; i++)369 att_outb(par, i, reg->att[i]);370 371 crt_outb(par, 0x1a, reg->ext[0]);372 crt_outb(par, 0x1b, reg->ext[1]);373 374 vga_enable_palette(par);375 vga_enable_video(par);376 377 banshee_make_room(par, 9);378 tdfx_outl(par, VGAINIT0, reg->vgainit0);379 tdfx_outl(par, DACMODE, reg->dacmode);380 tdfx_outl(par, VIDDESKSTRIDE, reg->stride);381 tdfx_outl(par, HWCURPATADDR, reg->curspataddr);382 383 tdfx_outl(par, VIDSCREENSIZE, reg->screensize);384 tdfx_outl(par, VIDDESKSTART, reg->startaddr);385 tdfx_outl(par, VIDPROCCFG, reg->vidcfg);386 tdfx_outl(par, VGAINIT1, reg->vgainit1);387 tdfx_outl(par, MISCINIT0, reg->miscinit0);388 389 banshee_make_room(par, 8);390 tdfx_outl(par, SRCBASE, reg->startaddr);391 tdfx_outl(par, DSTBASE, reg->startaddr);392 tdfx_outl(par, COMMANDEXTRA_2D, 0);393 tdfx_outl(par, CLIP0MIN, 0);394 tdfx_outl(par, CLIP0MAX, 0x0fff0fff);395 tdfx_outl(par, CLIP1MIN, 0);396 tdfx_outl(par, CLIP1MAX, 0x0fff0fff);397 tdfx_outl(par, SRCXY, 0);398 399 banshee_wait_idle(info);400}401 402static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short dev_id)403{404 u32 draminit0 = tdfx_inl(par, DRAMINIT0);405 u32 draminit1 = tdfx_inl(par, DRAMINIT1);406 u32 miscinit1;407 int num_chips = (draminit0 & DRAMINIT0_SGRAM_NUM) ? 8 : 4;408 int chip_size; /* in MB */409 int has_sgram = draminit1 & DRAMINIT1_MEM_SDRAM;410 411 if (dev_id < PCI_DEVICE_ID_3DFX_VOODOO5) {412 /* Banshee/Voodoo3 */413 chip_size = 2;414 if (has_sgram && !(draminit0 & DRAMINIT0_SGRAM_TYPE))415 chip_size = 1;416 } else {417 /* Voodoo4/5 */418 has_sgram = 0;419 chip_size = draminit0 & DRAMINIT0_SGRAM_TYPE_MASK;420 chip_size = 1 << (chip_size >> DRAMINIT0_SGRAM_TYPE_SHIFT);421 }422 423 /* disable block writes for SDRAM */424 miscinit1 = tdfx_inl(par, MISCINIT1);425 miscinit1 |= has_sgram ? 0 : MISCINIT1_2DBLOCK_DIS;426 miscinit1 |= MISCINIT1_CLUT_INV;427 428 banshee_make_room(par, 1);429 tdfx_outl(par, MISCINIT1, miscinit1);430 return num_chips * chip_size * 1024l * 1024;431}432 433/* ------------------------------------------------------------------------- */434 435static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)436{437 struct tdfx_par *par = info->par;438 u32 lpitch;439 440 if (var->bits_per_pixel != 8 && var->bits_per_pixel != 16 &&441 var->bits_per_pixel != 24 && var->bits_per_pixel != 32) {442 DPRINTK("depth not supported: %u\n", var->bits_per_pixel);443 return -EINVAL;444 }445 446 if (var->xres != var->xres_virtual)447 var->xres_virtual = var->xres;448 449 if (var->yres > var->yres_virtual)450 var->yres_virtual = var->yres;451 452 if (var->xoffset) {453 DPRINTK("xoffset not supported\n");454 return -EINVAL;455 }456 var->yoffset = 0;457 458 /*459 * Banshee doesn't support interlace, but Voodoo4/5 and probably460 * Voodoo3 do.461 * no direct information about device id now?462 * use max_pixclock for this...463 */464 if (((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) &&465 (par->max_pixclock < VOODOO3_MAX_PIXCLOCK)) {466 DPRINTK("interlace not supported\n");467 return -EINVAL;468 }469 470 if (info->monspecs.hfmax && info->monspecs.vfmax &&471 info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) {472 DPRINTK("mode outside monitor's specs\n");473 return -EINVAL;474 }475 476 var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */477 lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3);478 479 if (var->xres < 320 || var->xres > 2048) {480 DPRINTK("width not supported: %u\n", var->xres);481 return -EINVAL;482 }483 484 if (var->yres < 200 || var->yres > 2048) {485 DPRINTK("height not supported: %u\n", var->yres);486 return -EINVAL;487 }488 489 if (lpitch * var->yres_virtual > info->fix.smem_len) {490 var->yres_virtual = info->fix.smem_len / lpitch;491 if (var->yres_virtual < var->yres) {492 DPRINTK("no memory for screen (%ux%ux%u)\n",493 var->xres, var->yres_virtual,494 var->bits_per_pixel);495 return -EINVAL;496 }497 }498 499 if (PICOS2KHZ(var->pixclock) > par->max_pixclock) {500 DPRINTK("pixclock too high (%ldKHz)\n",501 PICOS2KHZ(var->pixclock));502 return -EINVAL;503 }504 505 var->transp.offset = 0;506 var->transp.length = 0;507 switch (var->bits_per_pixel) {508 case 8:509 var->red.length = 8;510 var->red.offset = 0;511 var->green = var->red;512 var->blue = var->red;513 break;514 case 16:515 var->red.offset = 11;516 var->red.length = 5;517 var->green.offset = 5;518 var->green.length = 6;519 var->blue.offset = 0;520 var->blue.length = 5;521 break;522 case 32:523 var->transp.offset = 24;524 var->transp.length = 8;525 fallthrough;526 case 24:527 var->red.offset = 16;528 var->green.offset = 8;529 var->blue.offset = 0;530 var->red.length = var->green.length = var->blue.length = 8;531 break;532 }533 var->width = -1;534 var->height = -1;535 536 var->accel_flags = FB_ACCELF_TEXT;537 538 DPRINTK("Checking graphics mode at %dx%d depth %d\n",539 var->xres, var->yres, var->bits_per_pixel);540 return 0;541}542 543static int tdfxfb_set_par(struct fb_info *info)544{545 struct tdfx_par *par = info->par;546 u32 hdispend = info->var.xres;547 u32 hsyncsta = hdispend + info->var.right_margin;548 u32 hsyncend = hsyncsta + info->var.hsync_len;549 u32 htotal = hsyncend + info->var.left_margin;550 u32 hd, hs, he, ht, hbs, hbe;551 u32 vd, vs, ve, vt, vbs, vbe;552 struct banshee_reg reg;553 int fout, freq;554 u32 wd;555 u32 cpp = (info->var.bits_per_pixel + 7) >> 3;556 557 memset(®, 0, sizeof(reg));558 559 reg.vidcfg = VIDCFG_VIDPROC_ENABLE | VIDCFG_DESK_ENABLE |560 VIDCFG_CURS_X11 |561 ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) |562 (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0);563 564 /* PLL settings */565 freq = PICOS2KHZ(info->var.pixclock);566 567 reg.vidcfg &= ~VIDCFG_2X;568 569 if (freq > par->max_pixclock / 2) {570 freq = freq > par->max_pixclock ? par->max_pixclock : freq;571 reg.dacmode |= DACMODE_2X;572 reg.vidcfg |= VIDCFG_2X;573 hdispend >>= 1;574 hsyncsta >>= 1;575 hsyncend >>= 1;576 htotal >>= 1;577 }578 579 wd = (hdispend >> 3) - 1;580 hd = wd;581 hs = (hsyncsta >> 3) - 1;582 he = (hsyncend >> 3) - 1;583 ht = (htotal >> 3) - 1;584 hbs = hd;585 hbe = ht;586 587 if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {588 vd = (info->var.yres << 1) - 1;589 vs = vd + (info->var.lower_margin << 1);590 ve = vs + (info->var.vsync_len << 1);591 vt = ve + (info->var.upper_margin << 1) - 1;592 reg.screensize = info->var.xres | (info->var.yres << 13);593 reg.vidcfg |= VIDCFG_HALF_MODE;594 reg.crt[0x09] = 0x80;595 } else {596 vd = info->var.yres - 1;597 vs = vd + info->var.lower_margin;598 ve = vs + info->var.vsync_len;599 vt = ve + info->var.upper_margin - 1;600 reg.screensize = info->var.xres | (info->var.yres << 12);601 reg.vidcfg &= ~VIDCFG_HALF_MODE;602 }603 vbs = vd;604 vbe = vt;605 606 /* this is all pretty standard VGA register stuffing */607 reg.misc[0x00] = 0x0f |608 (info->var.xres < 400 ? 0xa0 :609 info->var.xres < 480 ? 0x60 :610 info->var.xres < 768 ? 0xe0 : 0x20);611 612 reg.gra[0x05] = 0x40;613 reg.gra[0x06] = 0x05;614 reg.gra[0x07] = 0x0f;615 reg.gra[0x08] = 0xff;616 617 reg.att[0x00] = 0x00;618 reg.att[0x01] = 0x01;619 reg.att[0x02] = 0x02;620 reg.att[0x03] = 0x03;621 reg.att[0x04] = 0x04;622 reg.att[0x05] = 0x05;623 reg.att[0x06] = 0x06;624 reg.att[0x07] = 0x07;625 reg.att[0x08] = 0x08;626 reg.att[0x09] = 0x09;627 reg.att[0x0a] = 0x0a;628 reg.att[0x0b] = 0x0b;629 reg.att[0x0c] = 0x0c;630 reg.att[0x0d] = 0x0d;631 reg.att[0x0e] = 0x0e;632 reg.att[0x0f] = 0x0f;633 reg.att[0x10] = 0x41;634 reg.att[0x12] = 0x0f;635 636 reg.seq[0x00] = 0x03;637 reg.seq[0x01] = 0x01; /* fixme: clkdiv2? */638 reg.seq[0x02] = 0x0f;639 reg.seq[0x03] = 0x00;640 reg.seq[0x04] = 0x0e;641 642 reg.crt[0x00] = ht - 4;643 reg.crt[0x01] = hd;644 reg.crt[0x02] = hbs;645 reg.crt[0x03] = 0x80 | (hbe & 0x1f);646 reg.crt[0x04] = hs;647 reg.crt[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f);648 reg.crt[0x06] = vt;649 reg.crt[0x07] = ((vs & 0x200) >> 2) |650 ((vd & 0x200) >> 3) |651 ((vt & 0x200) >> 4) | 0x10 |652 ((vbs & 0x100) >> 5) |653 ((vs & 0x100) >> 6) |654 ((vd & 0x100) >> 7) |655 ((vt & 0x100) >> 8);656 reg.crt[0x09] |= 0x40 | ((vbs & 0x200) >> 4);657 reg.crt[0x10] = vs;658 reg.crt[0x11] = (ve & 0x0f) | 0x20;659 reg.crt[0x12] = vd;660 reg.crt[0x13] = wd;661 reg.crt[0x15] = vbs;662 reg.crt[0x16] = vbe + 1;663 reg.crt[0x17] = 0xc3;664 reg.crt[0x18] = 0xff;665 666 /* Banshee's nonvga stuff */667 reg.ext[0x00] = (((ht & 0x100) >> 8) |668 ((hd & 0x100) >> 6) |669 ((hbs & 0x100) >> 4) |670 ((hbe & 0x40) >> 1) |671 ((hs & 0x100) >> 2) |672 ((he & 0x20) << 2));673 reg.ext[0x01] = (((vt & 0x400) >> 10) |674 ((vd & 0x400) >> 8) |675 ((vbs & 0x400) >> 6) |676 ((vbe & 0x400) >> 4));677 678 reg.vgainit0 = VGAINIT0_8BIT_DAC |679 VGAINIT0_EXT_ENABLE |680 VGAINIT0_WAKEUP_3C3 |681 VGAINIT0_ALT_READBACK |682 VGAINIT0_EXTSHIFTOUT;683 reg.vgainit1 = tdfx_inl(par, VGAINIT1) & 0x1fffff;684 685 if (hwcursor)686 reg.curspataddr = info->fix.smem_len;687 688 reg.cursloc = 0;689 690 reg.cursc0 = 0;691 reg.cursc1 = 0xffffff;692 693 reg.stride = info->var.xres * cpp;694 reg.startaddr = info->var.yoffset * reg.stride695 + info->var.xoffset * cpp;696 697 reg.vidpll = do_calc_pll(freq, &fout);698#if 0699 reg.mempll = do_calc_pll(..., &fout);700 reg.gfxpll = do_calc_pll(..., &fout);701#endif702 703 if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)704 reg.vidcfg |= VIDCFG_INTERLACE;705 reg.miscinit0 = tdfx_inl(par, MISCINIT0);706 707#if defined(__BIG_ENDIAN)708 switch (info->var.bits_per_pixel) {709 case 8:710 case 24:711 reg.miscinit0 &= ~(1 << 30);712 reg.miscinit0 &= ~(1 << 31);713 break;714 case 16:715 reg.miscinit0 |= (1 << 30);716 reg.miscinit0 |= (1 << 31);717 break;718 case 32:719 reg.miscinit0 |= (1 << 30);720 reg.miscinit0 &= ~(1 << 31);721 break;722 }723#endif724 do_write_regs(info, ®);725 726 /* Now change fb_fix_screeninfo according to changes in par */727 info->fix.line_length = reg.stride;728 info->fix.visual = (info->var.bits_per_pixel == 8)729 ? FB_VISUAL_PSEUDOCOLOR730 : FB_VISUAL_TRUECOLOR;731 DPRINTK("Graphics mode is now set at %dx%d depth %d\n",732 info->var.xres, info->var.yres, info->var.bits_per_pixel);733 return 0;734}735 736/* A handy macro shamelessly pinched from matroxfb */737#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)738 739static int tdfxfb_setcolreg(unsigned regno, unsigned red, unsigned green,740 unsigned blue, unsigned transp,741 struct fb_info *info)742{743 struct tdfx_par *par = info->par;744 u32 rgbcol;745 746 if (regno >= info->cmap.len || regno > 255)747 return 1;748 749 /* grayscale works only partially under directcolor */750 if (info->var.grayscale) {751 /* grayscale = 0.30*R + 0.59*G + 0.11*B */752 blue = (red * 77 + green * 151 + blue * 28) >> 8;753 green = blue;754 red = blue;755 }756 757 switch (info->fix.visual) {758 case FB_VISUAL_PSEUDOCOLOR:759 rgbcol = (((u32)red & 0xff00) << 8) |760 (((u32)green & 0xff00) << 0) |761 (((u32)blue & 0xff00) >> 8);762 do_setpalentry(par, regno, rgbcol);763 break;764 /* Truecolor has no hardware color palettes. */765 case FB_VISUAL_TRUECOLOR:766 if (regno < 16) {767 rgbcol = (CNVT_TOHW(red, info->var.red.length) <<768 info->var.red.offset) |769 (CNVT_TOHW(green, info->var.green.length) <<770 info->var.green.offset) |771 (CNVT_TOHW(blue, info->var.blue.length) <<772 info->var.blue.offset) |773 (CNVT_TOHW(transp, info->var.transp.length) <<774 info->var.transp.offset);775 par->palette[regno] = rgbcol;776 }777 778 break;779 default:780 DPRINTK("bad depth %u\n", info->var.bits_per_pixel);781 break;782 }783 784 return 0;785}786 787/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */788static int tdfxfb_blank(int blank, struct fb_info *info)789{790 struct tdfx_par *par = info->par;791 int vgablank = 1;792 u32 dacmode = tdfx_inl(par, DACMODE);793 794 dacmode &= ~(BIT(1) | BIT(3));795 796 switch (blank) {797 case FB_BLANK_UNBLANK: /* Screen: On; HSync: On, VSync: On */798 vgablank = 0;799 break;800 case FB_BLANK_NORMAL: /* Screen: Off; HSync: On, VSync: On */801 break;802 case FB_BLANK_VSYNC_SUSPEND: /* Screen: Off; HSync: On, VSync: Off */803 dacmode |= BIT(3);804 break;805 case FB_BLANK_HSYNC_SUSPEND: /* Screen: Off; HSync: Off, VSync: On */806 dacmode |= BIT(1);807 break;808 case FB_BLANK_POWERDOWN: /* Screen: Off; HSync: Off, VSync: Off */809 dacmode |= BIT(1) | BIT(3);810 break;811 }812 813 banshee_make_room(par, 1);814 tdfx_outl(par, DACMODE, dacmode);815 if (vgablank)816 vga_disable_video(par);817 else818 vga_enable_video(par);819 return 0;820}821 822/*823 * Set the starting position of the visible screen to var->yoffset824 */825static int tdfxfb_pan_display(struct fb_var_screeninfo *var,826 struct fb_info *info)827{828 struct tdfx_par *par = info->par;829 u32 addr = var->yoffset * info->fix.line_length;830 831 if (nopan || var->xoffset)832 return -EINVAL;833 834 banshee_make_room(par, 1);835 tdfx_outl(par, VIDDESKSTART, addr);836 837 return 0;838}839 840#ifdef CONFIG_FB_3DFX_ACCEL841/*842 * FillRect 2D command (solidfill or invert (via ROP_XOR))843 */844static void tdfxfb_fillrect(struct fb_info *info,845 const struct fb_fillrect *rect)846{847 struct tdfx_par *par = info->par;848 u32 bpp = info->var.bits_per_pixel;849 u32 stride = info->fix.line_length;850 u32 fmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13);851 int tdfx_rop;852 u32 dx = rect->dx;853 u32 dy = rect->dy;854 u32 dstbase = 0;855 856 if (rect->rop == ROP_COPY)857 tdfx_rop = TDFX_ROP_COPY;858 else859 tdfx_rop = TDFX_ROP_XOR;860 861 /* assume always rect->height < 4096 */862 if (dy + rect->height > 4095) {863 dstbase = stride * dy;864 dy = 0;865 }866 /* assume always rect->width < 4096 */867 if (dx + rect->width > 4095) {868 dstbase += dx * bpp >> 3;869 dx = 0;870 }871 banshee_make_room(par, 6);872 tdfx_outl(par, DSTFORMAT, fmt);873 if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {874 tdfx_outl(par, COLORFORE, rect->color);875 } else { /* FB_VISUAL_TRUECOLOR */876 tdfx_outl(par, COLORFORE, par->palette[rect->color]);877 }878 tdfx_outl(par, COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24));879 tdfx_outl(par, DSTBASE, dstbase);880 tdfx_outl(par, DSTSIZE, rect->width | (rect->height << 16));881 tdfx_outl(par, LAUNCH_2D, dx | (dy << 16));882}883 884/*885 * Screen-to-Screen BitBlt 2D command (for the bmove fb op.)886 */887static void tdfxfb_copyarea(struct fb_info *info,888 const struct fb_copyarea *area)889{890 struct tdfx_par *par = info->par;891 u32 sx = area->sx, sy = area->sy, dx = area->dx, dy = area->dy;892 u32 bpp = info->var.bits_per_pixel;893 u32 stride = info->fix.line_length;894 u32 blitcmd = COMMAND_2D_S2S_BITBLT | (TDFX_ROP_COPY << 24);895 u32 fmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13);896 u32 dstbase = 0;897 u32 srcbase = 0;898 899 /* assume always area->height < 4096 */900 if (sy + area->height > 4095) {901 srcbase = stride * sy;902 sy = 0;903 }904 /* assume always area->width < 4096 */905 if (sx + area->width > 4095) {906 srcbase += sx * bpp >> 3;907 sx = 0;908 }909 /* assume always area->height < 4096 */910 if (dy + area->height > 4095) {911 dstbase = stride * dy;912 dy = 0;913 }914 /* assume always area->width < 4096 */915 if (dx + area->width > 4095) {916 dstbase += dx * bpp >> 3;917 dx = 0;918 }919 920 if (area->sx <= area->dx) {921 /* -X */922 blitcmd |= BIT(14);923 sx += area->width - 1;924 dx += area->width - 1;925 }926 if (area->sy <= area->dy) {927 /* -Y */928 blitcmd |= BIT(15);929 sy += area->height - 1;930 dy += area->height - 1;931 }932 933 banshee_make_room(par, 8);934 935 tdfx_outl(par, SRCFORMAT, fmt);936 tdfx_outl(par, DSTFORMAT, fmt);937 tdfx_outl(par, COMMAND_2D, blitcmd);938 tdfx_outl(par, DSTSIZE, area->width | (area->height << 16));939 tdfx_outl(par, DSTXY, dx | (dy << 16));940 tdfx_outl(par, SRCBASE, srcbase);941 tdfx_outl(par, DSTBASE, dstbase);942 tdfx_outl(par, LAUNCH_2D, sx | (sy << 16));943}944 945static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image)946{947 struct tdfx_par *par = info->par;948 int size = image->height * ((image->width * image->depth + 7) >> 3);949 int fifo_free;950 int i, stride = info->fix.line_length;951 u32 bpp = info->var.bits_per_pixel;952 u32 dstfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13);953 u8 *chardata = (u8 *) image->data;954 u32 srcfmt;955 u32 dx = image->dx;956 u32 dy = image->dy;957 u32 dstbase = 0;958 959 if (image->depth != 1) {960#ifdef BROKEN_CODE961 banshee_make_room(par, 6 + ((size + 3) >> 2));962 srcfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13) |963 0x400000;964#else965 cfb_imageblit(info, image);966#endif967 return;968 }969 banshee_make_room(par, 9);970 switch (info->fix.visual) {971 case FB_VISUAL_PSEUDOCOLOR:972 tdfx_outl(par, COLORFORE, image->fg_color);973 tdfx_outl(par, COLORBACK, image->bg_color);974 break;975 case FB_VISUAL_TRUECOLOR:976 default:977 tdfx_outl(par, COLORFORE,978 par->palette[image->fg_color]);979 tdfx_outl(par, COLORBACK,980 par->palette[image->bg_color]);981 }982#ifdef __BIG_ENDIAN983 srcfmt = 0x400000 | BIT(20);984#else985 srcfmt = 0x400000;986#endif987 /* assume always image->height < 4096 */988 if (dy + image->height > 4095) {989 dstbase = stride * dy;990 dy = 0;991 }992 /* assume always image->width < 4096 */993 if (dx + image->width > 4095) {994 dstbase += dx * bpp >> 3;995 dx = 0;996 }997 998 tdfx_outl(par, DSTBASE, dstbase);999 tdfx_outl(par, SRCXY, 0);1000 tdfx_outl(par, DSTXY, dx | (dy << 16));1001 tdfx_outl(par, COMMAND_2D,1002 COMMAND_2D_H2S_BITBLT | (TDFX_ROP_COPY << 24));1003 tdfx_outl(par, SRCFORMAT, srcfmt);1004 tdfx_outl(par, DSTFORMAT, dstfmt);1005 tdfx_outl(par, DSTSIZE, image->width | (image->height << 16));1006 1007 /* A count of how many free FIFO entries we've requested.1008 * When this goes negative, we need to request more. */1009 fifo_free = 0;1010 1011 /* Send four bytes at a time of data */1012 for (i = (size >> 2); i > 0; i--) {1013 if (--fifo_free < 0) {1014 fifo_free = 31;1015 banshee_make_room(par, fifo_free);1016 }1017 tdfx_outl(par, LAUNCH_2D, *(u32 *)chardata);1018 chardata += 4;1019 }1020 1021 /* Send the leftovers now */1022 banshee_make_room(par, 3);1023 switch (size % 4) {1024 case 0:1025 break;1026 case 1:1027 tdfx_outl(par, LAUNCH_2D, *chardata);1028 break;1029 case 2:1030 tdfx_outl(par, LAUNCH_2D, *(u16 *)chardata);1031 break;1032 case 3:1033 tdfx_outl(par, LAUNCH_2D,1034 *(u16 *)chardata | (chardata[3] << 24));1035 break;1036 }1037}1038#endif /* CONFIG_FB_3DFX_ACCEL */1039 1040static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor)1041{1042 struct tdfx_par *par = info->par;1043 u32 vidcfg;1044 1045 if (!hwcursor)1046 return -EINVAL; /* just to force soft_cursor() call */1047 1048 /* Too large of a cursor or wrong bpp :-( */1049 if (cursor->image.width > 64 ||1050 cursor->image.height > 64 ||1051 cursor->image.depth > 1)1052 return -EINVAL;1053 1054 vidcfg = tdfx_inl(par, VIDPROCCFG);1055 if (cursor->enable)1056 tdfx_outl(par, VIDPROCCFG, vidcfg | VIDCFG_HWCURSOR_ENABLE);1057 else1058 tdfx_outl(par, VIDPROCCFG, vidcfg & ~VIDCFG_HWCURSOR_ENABLE);1059 1060 /*1061 * If the cursor is not be changed this means either we want the1062 * current cursor state (if enable is set) or we want to query what1063 * we can do with the cursor (if enable is not set)1064 */1065 if (!cursor->set)1066 return 0;1067 1068 /* fix cursor color - XFree86 forgets to restore it properly */1069 if (cursor->set & FB_CUR_SETCMAP) {1070 struct fb_cmap cmap = info->cmap;1071 u32 bg_idx = cursor->image.bg_color;1072 u32 fg_idx = cursor->image.fg_color;1073 unsigned long bg_color, fg_color;1074 1075 fg_color = (((u32)cmap.red[fg_idx] & 0xff00) << 8) |1076 (((u32)cmap.green[fg_idx] & 0xff00) << 0) |1077 (((u32)cmap.blue[fg_idx] & 0xff00) >> 8);1078 bg_color = (((u32)cmap.red[bg_idx] & 0xff00) << 8) |1079 (((u32)cmap.green[bg_idx] & 0xff00) << 0) |1080 (((u32)cmap.blue[bg_idx] & 0xff00) >> 8);1081 banshee_make_room(par, 2);1082 tdfx_outl(par, HWCURC0, bg_color);1083 tdfx_outl(par, HWCURC1, fg_color);1084 }1085 1086 if (cursor->set & FB_CUR_SETPOS) {1087 int x = cursor->image.dx;1088 int y = cursor->image.dy - info->var.yoffset;1089 1090 x += 63;1091 y += 63;1092 banshee_make_room(par, 1);1093 tdfx_outl(par, HWCURLOC, (y << 16) + x);1094 }1095 if (cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) {1096 /*1097 * Voodoo 3 and above cards use 2 monochrome cursor patterns.1098 * The reason is so the card can fetch 8 words at a time1099 * and are stored on chip for use for the next 8 scanlines.1100 * This reduces the number of times for access to draw the1101 * cursor for each screen refresh.1102 * Each pattern is a bitmap of 64 bit wide and 64 bit high1103 * (total of 8192 bits or 1024 bytes). The two patterns are1104 * stored in such a way that pattern 0 always resides in the1105 * lower half (least significant 64 bits) of a 128 bit word1106 * and pattern 1 the upper half. If you examine the data of1107 * the cursor image the graphics card uses then from the1108 * beginning you see line one of pattern 0, line one of1109 * pattern 1, line two of pattern 0, line two of pattern 1,1110 * etc etc. The linear stride for the cursor is always 16 bytes1111 * (128 bits) which is the maximum cursor width times two for1112 * the two monochrome patterns.1113 */1114 u8 __iomem *cursorbase = info->screen_base + info->fix.smem_len;1115 u8 *bitmap = (u8 *)cursor->image.data;1116 u8 *mask = (u8 *)cursor->mask;1117 int i;1118 1119 fb_memset_io(cursorbase, 0, 1024);1120 1121 for (i = 0; i < cursor->image.height; i++) {1122 int h = 0;1123 int j = (cursor->image.width + 7) >> 3;1124 1125 for (; j > 0; j--) {1126 u8 data = *mask ^ *bitmap;1127 if (cursor->rop == ROP_COPY)1128 data = *mask & *bitmap;1129 /* Pattern 0. Copy the cursor mask to it */1130 fb_writeb(*mask, cursorbase + h);1131 mask++;1132 /* Pattern 1. Copy the cursor bitmap to it */1133 fb_writeb(data, cursorbase + h + 8);1134 bitmap++;1135 h++;1136 }1137 cursorbase += 16;1138 }1139 }1140 return 0;1141}1142 1143static const struct fb_ops tdfxfb_ops = {1144 .owner = THIS_MODULE,1145 __FB_DEFAULT_IOMEM_OPS_RDWR,1146 .fb_check_var = tdfxfb_check_var,1147 .fb_set_par = tdfxfb_set_par,1148 .fb_setcolreg = tdfxfb_setcolreg,1149 .fb_blank = tdfxfb_blank,1150 .fb_pan_display = tdfxfb_pan_display,1151 .fb_sync = banshee_wait_idle,1152 .fb_cursor = tdfxfb_cursor,1153#ifdef CONFIG_FB_3DFX_ACCEL1154 .fb_fillrect = tdfxfb_fillrect,1155 .fb_copyarea = tdfxfb_copyarea,1156 .fb_imageblit = tdfxfb_imageblit,1157#else1158 __FB_DEFAULT_IOMEM_OPS_DRAW,1159#endif1160 __FB_DEFAULT_IOMEM_OPS_MMAP,1161};1162 1163#ifdef CONFIG_FB_3DFX_I2C1164/* The voo GPIO registers don't have individual masks for each bit1165 so we always have to read before writing. */1166 1167static void tdfxfb_i2c_setscl(void *data, int val)1168{1169 struct tdfxfb_i2c_chan *chan = data;1170 struct tdfx_par *par = chan->par;1171 unsigned int r;1172 1173 r = tdfx_inl(par, VIDSERPARPORT);1174 if (val)1175 r |= I2C_SCL_OUT;1176 else1177 r &= ~I2C_SCL_OUT;1178 tdfx_outl(par, VIDSERPARPORT, r);1179 tdfx_inl(par, VIDSERPARPORT); /* flush posted write */1180}1181 1182static void tdfxfb_i2c_setsda(void *data, int val)1183{1184 struct tdfxfb_i2c_chan *chan = data;1185 struct tdfx_par *par = chan->par;1186 unsigned int r;1187 1188 r = tdfx_inl(par, VIDSERPARPORT);1189 if (val)1190 r |= I2C_SDA_OUT;1191 else1192 r &= ~I2C_SDA_OUT;1193 tdfx_outl(par, VIDSERPARPORT, r);1194 tdfx_inl(par, VIDSERPARPORT); /* flush posted write */1195}1196 1197/* The GPIO pins are open drain, so the pins always remain outputs.1198 We rely on the i2c-algo-bit routines to set the pins high before1199 reading the input from other chips. */1200 1201static int tdfxfb_i2c_getscl(void *data)1202{1203 struct tdfxfb_i2c_chan *chan = data;1204 struct tdfx_par *par = chan->par;1205 1206 return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SCL_IN));1207}1208 1209static int tdfxfb_i2c_getsda(void *data)1210{1211 struct tdfxfb_i2c_chan *chan = data;1212 struct tdfx_par *par = chan->par;1213 1214 return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SDA_IN));1215}1216 1217static void tdfxfb_ddc_setscl(void *data, int val)1218{1219 struct tdfxfb_i2c_chan *chan = data;1220 struct tdfx_par *par = chan->par;1221 unsigned int r;1222 1223 r = tdfx_inl(par, VIDSERPARPORT);1224 if (val)1225 r |= DDC_SCL_OUT;1226 else1227 r &= ~DDC_SCL_OUT;1228 tdfx_outl(par, VIDSERPARPORT, r);1229 tdfx_inl(par, VIDSERPARPORT); /* flush posted write */1230}1231 1232static void tdfxfb_ddc_setsda(void *data, int val)1233{1234 struct tdfxfb_i2c_chan *chan = data;1235 struct tdfx_par *par = chan->par;1236 unsigned int r;1237 1238 r = tdfx_inl(par, VIDSERPARPORT);1239 if (val)1240 r |= DDC_SDA_OUT;1241 else1242 r &= ~DDC_SDA_OUT;1243 tdfx_outl(par, VIDSERPARPORT, r);1244 tdfx_inl(par, VIDSERPARPORT); /* flush posted write */1245}1246 1247static int tdfxfb_ddc_getscl(void *data)1248{1249 struct tdfxfb_i2c_chan *chan = data;1250 struct tdfx_par *par = chan->par;1251 1252 return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SCL_IN));1253}1254 1255static int tdfxfb_ddc_getsda(void *data)1256{1257 struct tdfxfb_i2c_chan *chan = data;1258 struct tdfx_par *par = chan->par;1259 1260 return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SDA_IN));1261}1262 1263static int tdfxfb_setup_ddc_bus(struct tdfxfb_i2c_chan *chan, const char *name,1264 struct device *dev)1265{1266 int rc;1267 1268 strscpy(chan->adapter.name, name, sizeof(chan->adapter.name));1269 chan->adapter.owner = THIS_MODULE;1270 chan->adapter.algo_data = &chan->algo;1271 chan->adapter.dev.parent = dev;1272 chan->algo.setsda = tdfxfb_ddc_setsda;1273 chan->algo.setscl = tdfxfb_ddc_setscl;1274 chan->algo.getsda = tdfxfb_ddc_getsda;1275 chan->algo.getscl = tdfxfb_ddc_getscl;1276 chan->algo.udelay = 10;1277 chan->algo.timeout = msecs_to_jiffies(500);1278 chan->algo.data = chan;1279 1280 i2c_set_adapdata(&chan->adapter, chan);1281 1282 rc = i2c_bit_add_bus(&chan->adapter);1283 if (rc == 0)1284 DPRINTK("I2C bus %s registered.\n", name);1285 else1286 chan->par = NULL;1287 1288 return rc;1289}1290 1291static int tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan, const char *name,1292 struct device *dev)1293{1294 int rc;1295 1296 strscpy(chan->adapter.name, name, sizeof(chan->adapter.name));1297 chan->adapter.owner = THIS_MODULE;1298 chan->adapter.algo_data = &chan->algo;1299 chan->adapter.dev.parent = dev;1300 chan->algo.setsda = tdfxfb_i2c_setsda;1301 chan->algo.setscl = tdfxfb_i2c_setscl;1302 chan->algo.getsda = tdfxfb_i2c_getsda;1303 chan->algo.getscl = tdfxfb_i2c_getscl;1304 chan->algo.udelay = 10;1305 chan->algo.timeout = msecs_to_jiffies(500);1306 chan->algo.data = chan;1307 1308 i2c_set_adapdata(&chan->adapter, chan);1309 1310 rc = i2c_bit_add_bus(&chan->adapter);1311 if (rc == 0)1312 DPRINTK("I2C bus %s registered.\n", name);1313 else1314 chan->par = NULL;1315 1316 return rc;1317}1318 1319static void tdfxfb_create_i2c_busses(struct fb_info *info)1320{1321 struct tdfx_par *par = info->par;1322 1323 tdfx_outl(par, VIDINFORMAT, 0x8160);1324 tdfx_outl(par, VIDSERPARPORT, 0xcffc0020);1325 1326 par->chan[0].par = par;1327 par->chan[1].par = par;1328 1329 tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->device);1330 tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->device);1331}1332 1333static void tdfxfb_delete_i2c_busses(struct tdfx_par *par)1334{1335 if (par->chan[0].par)1336 i2c_del_adapter(&par->chan[0].adapter);1337 par->chan[0].par = NULL;1338 1339 if (par->chan[1].par)1340 i2c_del_adapter(&par->chan[1].adapter);1341 par->chan[1].par = NULL;1342}1343 1344static int tdfxfb_probe_i2c_connector(struct tdfx_par *par,1345 struct fb_monspecs *specs)1346{1347 u8 *edid = NULL;1348 1349 DPRINTK("Probe DDC Bus\n");1350 if (par->chan[0].par)1351 edid = fb_ddc_read(&par->chan[0].adapter);1352 1353 if (edid) {1354 fb_edid_to_monspecs(edid, specs);1355 kfree(edid);1356 return 0;1357 }1358 return 1;1359}1360#endif /* CONFIG_FB_3DFX_I2C */1361 1362/**1363 * tdfxfb_probe - Device Initializiation1364 *1365 * @pdev: PCI Device to initialize1366 * @id: PCI Device ID1367 *1368 * Initializes and allocates resources for PCI device @pdev.1369 *1370 */1371static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)1372{1373 struct tdfx_par *default_par;1374 struct fb_info *info;1375 int err, lpitch;1376 struct fb_monspecs *specs;1377 bool found;1378 1379 err = aperture_remove_conflicting_pci_devices(pdev, "tdfxfb");1380 if (err)1381 return err;1382 1383 err = pci_enable_device(pdev);1384 if (err) {1385 printk(KERN_ERR "tdfxfb: Can't enable pdev: %d\n", err);1386 return err;1387 }1388 1389 info = framebuffer_alloc(sizeof(struct tdfx_par), &pdev->dev);1390 1391 if (!info)1392 return -ENOMEM;1393 1394 default_par = info->par;1395 info->fix = tdfx_fix;1396 1397 /* Configure the default fb_fix_screeninfo first */1398 switch (pdev->device) {1399 case PCI_DEVICE_ID_3DFX_BANSHEE:1400 strcpy(info->fix.id, "3Dfx Banshee");1401 default_par->max_pixclock = BANSHEE_MAX_PIXCLOCK;1402 break;1403 case PCI_DEVICE_ID_3DFX_VOODOO3:1404 strcpy(info->fix.id, "3Dfx Voodoo3");1405 default_par->max_pixclock = VOODOO3_MAX_PIXCLOCK;1406 break;1407 case PCI_DEVICE_ID_3DFX_VOODOO5:1408 strcpy(info->fix.id, "3Dfx Voodoo5");1409 default_par->max_pixclock = VOODOO5_MAX_PIXCLOCK;1410 break;1411 }1412 1413 info->fix.mmio_start = pci_resource_start(pdev, 0);1414 info->fix.mmio_len = pci_resource_len(pdev, 0);1415 if (!request_mem_region(info->fix.mmio_start, info->fix.mmio_len,1416 "tdfx regbase")) {1417 printk(KERN_ERR "tdfxfb: Can't reserve regbase\n");1418 goto out_err;1419 }1420 1421 default_par->regbase_virt =1422 ioremap(info->fix.mmio_start, info->fix.mmio_len);1423 if (!default_par->regbase_virt) {1424 printk(KERN_ERR "fb: Can't remap %s register area.\n",1425 info->fix.id);1426 goto out_err_regbase;1427 }1428 1429 info->fix.smem_start = pci_resource_start(pdev, 1);1430 info->fix.smem_len = do_lfb_size(default_par, pdev->device);1431 if (!info->fix.smem_len) {1432 printk(KERN_ERR "fb: Can't count %s memory.\n", info->fix.id);1433 goto out_err_regbase;1434 }1435 1436 if (!request_mem_region(info->fix.smem_start,1437 pci_resource_len(pdev, 1), "tdfx smem")) {1438 printk(KERN_ERR "tdfxfb: Can't reserve smem\n");1439 goto out_err_regbase;1440 }1441 1442 info->screen_base = ioremap_wc(info->fix.smem_start,1443 info->fix.smem_len);1444 if (!info->screen_base) {1445 printk(KERN_ERR "fb: Can't remap %s framebuffer.\n",1446 info->fix.id);1447 goto out_err_screenbase;1448 }1449 1450 default_par->iobase = pci_resource_start(pdev, 2);1451 1452 if (!request_region(pci_resource_start(pdev, 2),1453 pci_resource_len(pdev, 2), "tdfx iobase")) {1454 printk(KERN_ERR "tdfxfb: Can't reserve iobase\n");1455 goto out_err_screenbase;1456 }1457 1458 printk(KERN_INFO "fb: %s memory = %dK\n", info->fix.id,1459 info->fix.smem_len >> 10);1460 1461 if (!nomtrr)1462 default_par->wc_cookie= arch_phys_wc_add(info->fix.smem_start,1463 info->fix.smem_len);1464 1465 info->fix.ypanstep = nopan ? 0 : 1;1466 info->fix.ywrapstep = nowrap ? 0 : 1;1467 1468 info->fbops = &tdfxfb_ops;1469 info->pseudo_palette = default_par->palette;1470 info->flags = FBINFO_HWACCEL_YPAN;1471#ifdef CONFIG_FB_3DFX_ACCEL1472 info->flags |= FBINFO_HWACCEL_FILLRECT |1473 FBINFO_HWACCEL_COPYAREA |1474 FBINFO_HWACCEL_IMAGEBLIT |1475 FBINFO_READS_FAST;1476#endif1477 /* reserve 8192 bits for cursor */1478 /* the 2.4 driver says PAGE_MASK boundary is not enough for Voodoo4 */1479 if (hwcursor)1480 info->fix.smem_len = (info->fix.smem_len - 1024) &1481 (PAGE_MASK << 1);1482 specs = &info->monspecs;1483 found = false;1484 info->var.bits_per_pixel = 8;1485#ifdef CONFIG_FB_3DFX_I2C1486 tdfxfb_create_i2c_busses(info);1487 err = tdfxfb_probe_i2c_connector(default_par, specs);1488 1489 if (!err) {1490 if (specs->modedb == NULL)1491 DPRINTK("Unable to get Mode Database\n");1492 else {1493 const struct fb_videomode *m;1494 1495 fb_videomode_to_modelist(specs->modedb,1496 specs->modedb_len,1497 &info->modelist);1498 m = fb_find_best_display(specs, &info->modelist);1499 if (m) {1500 fb_videomode_to_var(&info->var, m);1501 /* fill all other info->var's fields */1502 if (tdfxfb_check_var(&info->var, info) < 0)1503 info->var = tdfx_var;1504 else1505 found = true;1506 }1507 }1508 }1509#endif1510 if (!mode_option && !found)1511 mode_option = "640x480@60";1512 1513 if (mode_option) {1514 err = fb_find_mode(&info->var, info, mode_option,1515 specs->modedb, specs->modedb_len,1516 NULL, info->var.bits_per_pixel);1517 if (!err || err == 4)1518 info->var = tdfx_var;1519 }1520 1521 if (found) {1522 fb_destroy_modedb(specs->modedb);1523 specs->modedb = NULL;1524 }1525 1526 /* maximize virtual vertical length */1527 lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3);1528 info->var.yres_virtual = info->fix.smem_len / lpitch;1529 if (info->var.yres_virtual < info->var.yres)1530 goto out_err_iobase;1531 1532 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {1533 printk(KERN_ERR "tdfxfb: Can't allocate color map\n");1534 goto out_err_iobase;1535 }1536 1537 if (register_framebuffer(info) < 0) {1538 printk(KERN_ERR "tdfxfb: can't register framebuffer\n");1539 fb_dealloc_cmap(&info->cmap);1540 goto out_err_iobase;1541 }1542 /*1543 * Our driver data1544 */1545 pci_set_drvdata(pdev, info);1546 return 0;1547 1548out_err_iobase:1549#ifdef CONFIG_FB_3DFX_I2C1550 tdfxfb_delete_i2c_busses(default_par);1551#endif1552 arch_phys_wc_del(default_par->wc_cookie);1553 release_region(pci_resource_start(pdev, 2),1554 pci_resource_len(pdev, 2));1555out_err_screenbase:1556 if (info->screen_base)1557 iounmap(info->screen_base);1558 release_mem_region(info->fix.smem_start, pci_resource_len(pdev, 1));1559out_err_regbase:1560 /*1561 * Cleanup after anything that was remapped/allocated.1562 */1563 if (default_par->regbase_virt)1564 iounmap(default_par->regbase_virt);1565 release_mem_region(info->fix.mmio_start, info->fix.mmio_len);1566out_err:1567 framebuffer_release(info);1568 return -ENXIO;1569}1570 1571#ifndef MODULE1572static void __init tdfxfb_setup(char *options)1573{1574 char *this_opt;1575 1576 if (!options || !*options)1577 return;1578 1579 while ((this_opt = strsep(&options, ",")) != NULL) {1580 if (!*this_opt)1581 continue;1582 if (!strcmp(this_opt, "nopan")) {1583 nopan = 1;1584 } else if (!strcmp(this_opt, "nowrap")) {1585 nowrap = 1;1586 } else if (!strncmp(this_opt, "hwcursor=", 9)) {1587 hwcursor = simple_strtoul(this_opt + 9, NULL, 0);1588 } else if (!strncmp(this_opt, "nomtrr", 6)) {1589 nomtrr = 1;1590 } else {1591 mode_option = this_opt;1592 }1593 }1594}1595#endif1596 1597/**1598 * tdfxfb_remove - Device removal1599 *1600 * @pdev: PCI Device to cleanup1601 *1602 * Releases all resources allocated during the course of the driver's1603 * lifetime for the PCI device @pdev.1604 *1605 */1606static void tdfxfb_remove(struct pci_dev *pdev)1607{1608 struct fb_info *info = pci_get_drvdata(pdev);1609 struct tdfx_par *par = info->par;1610 1611 unregister_framebuffer(info);1612#ifdef CONFIG_FB_3DFX_I2C1613 tdfxfb_delete_i2c_busses(par);1614#endif1615 arch_phys_wc_del(par->wc_cookie);1616 iounmap(par->regbase_virt);1617 iounmap(info->screen_base);1618 1619 /* Clean up after reserved regions */1620 release_region(pci_resource_start(pdev, 2),1621 pci_resource_len(pdev, 2));1622 release_mem_region(pci_resource_start(pdev, 1),1623 pci_resource_len(pdev, 1));1624 release_mem_region(pci_resource_start(pdev, 0),1625 pci_resource_len(pdev, 0));1626 fb_dealloc_cmap(&info->cmap);1627 framebuffer_release(info);1628}1629 1630static int __init tdfxfb_init(void)1631{1632#ifndef MODULE1633 char *option = NULL;1634#endif1635 1636 if (fb_modesetting_disabled("tdfxfb"))1637 return -ENODEV;1638 1639#ifndef MODULE1640 if (fb_get_options("tdfxfb", &option))1641 return -ENODEV;1642 1643 tdfxfb_setup(option);1644#endif1645 return pci_register_driver(&tdfxfb_driver);1646}1647 1648static void __exit tdfxfb_exit(void)1649{1650 pci_unregister_driver(&tdfxfb_driver);1651}1652 1653MODULE_AUTHOR("Hannu Mallat <hmallat@cc.hut.fi>");1654MODULE_DESCRIPTION("3Dfx framebuffer device driver");1655MODULE_LICENSE("GPL");1656 1657module_param(hwcursor, int, 0644);1658MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "1659 "(1=enable, 0=disable, default=1)");1660module_param(mode_option, charp, 0);1661MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");1662module_param(nomtrr, bool, 0);1663MODULE_PARM_DESC(nomtrr, "Disable MTRR support (default: enabled)");1664 1665module_init(tdfxfb_init);1666module_exit(tdfxfb_exit);1667