1*f7018c21STomi Valkeinen /* 2*f7018c21STomi Valkeinen * linux/drivers/video/savagefb.c -- S3 Savage Framebuffer Driver 3*f7018c21STomi Valkeinen * 4*f7018c21STomi Valkeinen * Copyright (c) 2001-2002 Denis Oliver Kropp <dok@directfb.org> 5*f7018c21STomi Valkeinen * Sven Neumann <neo@directfb.org> 6*f7018c21STomi Valkeinen * 7*f7018c21STomi Valkeinen * 8*f7018c21STomi Valkeinen * Card specific code is based on XFree86's savage driver. 9*f7018c21STomi Valkeinen * Framebuffer framework code is based on code of cyber2000fb and tdfxfb. 10*f7018c21STomi Valkeinen * 11*f7018c21STomi Valkeinen * This file is subject to the terms and conditions of the GNU General 12*f7018c21STomi Valkeinen * Public License. See the file COPYING in the main directory of this 13*f7018c21STomi Valkeinen * archive for more details. 14*f7018c21STomi Valkeinen * 15*f7018c21STomi Valkeinen * 0.4.0 (neo) 16*f7018c21STomi Valkeinen * - hardware accelerated clear and move 17*f7018c21STomi Valkeinen * 18*f7018c21STomi Valkeinen * 0.3.2 (dok) 19*f7018c21STomi Valkeinen * - wait for vertical retrace before writing to cr67 20*f7018c21STomi Valkeinen * at the beginning of savagefb_set_par 21*f7018c21STomi Valkeinen * - use synchronization registers cr23 and cr26 22*f7018c21STomi Valkeinen * 23*f7018c21STomi Valkeinen * 0.3.1 (dok) 24*f7018c21STomi Valkeinen * - reset 3D engine 25*f7018c21STomi Valkeinen * - don't return alpha bits for 32bit format 26*f7018c21STomi Valkeinen * 27*f7018c21STomi Valkeinen * 0.3.0 (dok) 28*f7018c21STomi Valkeinen * - added WaitIdle functions for all Savage types 29*f7018c21STomi Valkeinen * - do WaitIdle before mode switching 30*f7018c21STomi Valkeinen * - code cleanup 31*f7018c21STomi Valkeinen * 32*f7018c21STomi Valkeinen * 0.2.0 (dok) 33*f7018c21STomi Valkeinen * - first working version 34*f7018c21STomi Valkeinen * 35*f7018c21STomi Valkeinen * 36*f7018c21STomi Valkeinen * TODO 37*f7018c21STomi Valkeinen * - clock validations in decode_var 38*f7018c21STomi Valkeinen * 39*f7018c21STomi Valkeinen * BUGS 40*f7018c21STomi Valkeinen * - white margin on bootup 41*f7018c21STomi Valkeinen * 42*f7018c21STomi Valkeinen */ 43*f7018c21STomi Valkeinen 44*f7018c21STomi Valkeinen #include <linux/module.h> 45*f7018c21STomi Valkeinen #include <linux/kernel.h> 46*f7018c21STomi Valkeinen #include <linux/errno.h> 47*f7018c21STomi Valkeinen #include <linux/string.h> 48*f7018c21STomi Valkeinen #include <linux/mm.h> 49*f7018c21STomi Valkeinen #include <linux/slab.h> 50*f7018c21STomi Valkeinen #include <linux/delay.h> 51*f7018c21STomi Valkeinen #include <linux/fb.h> 52*f7018c21STomi Valkeinen #include <linux/pci.h> 53*f7018c21STomi Valkeinen #include <linux/init.h> 54*f7018c21STomi Valkeinen #include <linux/console.h> 55*f7018c21STomi Valkeinen 56*f7018c21STomi Valkeinen #include <asm/io.h> 57*f7018c21STomi Valkeinen #include <asm/irq.h> 58*f7018c21STomi Valkeinen #include <asm/pgtable.h> 59*f7018c21STomi Valkeinen 60*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 61*f7018c21STomi Valkeinen #include <asm/mtrr.h> 62*f7018c21STomi Valkeinen #endif 63*f7018c21STomi Valkeinen 64*f7018c21STomi Valkeinen #include "savagefb.h" 65*f7018c21STomi Valkeinen 66*f7018c21STomi Valkeinen 67*f7018c21STomi Valkeinen #define SAVAGEFB_VERSION "0.4.0_2.6" 68*f7018c21STomi Valkeinen 69*f7018c21STomi Valkeinen /* --------------------------------------------------------------------- */ 70*f7018c21STomi Valkeinen 71*f7018c21STomi Valkeinen 72*f7018c21STomi Valkeinen static char *mode_option = NULL; 73*f7018c21STomi Valkeinen 74*f7018c21STomi Valkeinen #ifdef MODULE 75*f7018c21STomi Valkeinen 76*f7018c21STomi Valkeinen MODULE_AUTHOR("(c) 2001-2002 Denis Oliver Kropp <dok@directfb.org>"); 77*f7018c21STomi Valkeinen MODULE_LICENSE("GPL"); 78*f7018c21STomi Valkeinen MODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips"); 79*f7018c21STomi Valkeinen 80*f7018c21STomi Valkeinen #endif 81*f7018c21STomi Valkeinen 82*f7018c21STomi Valkeinen 83*f7018c21STomi Valkeinen /* --------------------------------------------------------------------- */ 84*f7018c21STomi Valkeinen 85*f7018c21STomi Valkeinen static void vgaHWSeqReset(struct savagefb_par *par, int start) 86*f7018c21STomi Valkeinen { 87*f7018c21STomi Valkeinen if (start) 88*f7018c21STomi Valkeinen VGAwSEQ(0x00, 0x01, par); /* Synchronous Reset */ 89*f7018c21STomi Valkeinen else 90*f7018c21STomi Valkeinen VGAwSEQ(0x00, 0x03, par); /* End Reset */ 91*f7018c21STomi Valkeinen } 92*f7018c21STomi Valkeinen 93*f7018c21STomi Valkeinen static void vgaHWProtect(struct savagefb_par *par, int on) 94*f7018c21STomi Valkeinen { 95*f7018c21STomi Valkeinen unsigned char tmp; 96*f7018c21STomi Valkeinen 97*f7018c21STomi Valkeinen if (on) { 98*f7018c21STomi Valkeinen /* 99*f7018c21STomi Valkeinen * Turn off screen and disable sequencer. 100*f7018c21STomi Valkeinen */ 101*f7018c21STomi Valkeinen tmp = VGArSEQ(0x01, par); 102*f7018c21STomi Valkeinen 103*f7018c21STomi Valkeinen vgaHWSeqReset(par, 1); /* start synchronous reset */ 104*f7018c21STomi Valkeinen VGAwSEQ(0x01, tmp | 0x20, par);/* disable the display */ 105*f7018c21STomi Valkeinen 106*f7018c21STomi Valkeinen VGAenablePalette(par); 107*f7018c21STomi Valkeinen } else { 108*f7018c21STomi Valkeinen /* 109*f7018c21STomi Valkeinen * Reenable sequencer, then turn on screen. 110*f7018c21STomi Valkeinen */ 111*f7018c21STomi Valkeinen 112*f7018c21STomi Valkeinen tmp = VGArSEQ(0x01, par); 113*f7018c21STomi Valkeinen 114*f7018c21STomi Valkeinen VGAwSEQ(0x01, tmp & ~0x20, par);/* reenable display */ 115*f7018c21STomi Valkeinen vgaHWSeqReset(par, 0); /* clear synchronous reset */ 116*f7018c21STomi Valkeinen 117*f7018c21STomi Valkeinen VGAdisablePalette(par); 118*f7018c21STomi Valkeinen } 119*f7018c21STomi Valkeinen } 120*f7018c21STomi Valkeinen 121*f7018c21STomi Valkeinen static void vgaHWRestore(struct savagefb_par *par, struct savage_reg *reg) 122*f7018c21STomi Valkeinen { 123*f7018c21STomi Valkeinen int i; 124*f7018c21STomi Valkeinen 125*f7018c21STomi Valkeinen VGAwMISC(reg->MiscOutReg, par); 126*f7018c21STomi Valkeinen 127*f7018c21STomi Valkeinen for (i = 1; i < 5; i++) 128*f7018c21STomi Valkeinen VGAwSEQ(i, reg->Sequencer[i], par); 129*f7018c21STomi Valkeinen 130*f7018c21STomi Valkeinen /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or 131*f7018c21STomi Valkeinen CRTC[17] */ 132*f7018c21STomi Valkeinen VGAwCR(17, reg->CRTC[17] & ~0x80, par); 133*f7018c21STomi Valkeinen 134*f7018c21STomi Valkeinen for (i = 0; i < 25; i++) 135*f7018c21STomi Valkeinen VGAwCR(i, reg->CRTC[i], par); 136*f7018c21STomi Valkeinen 137*f7018c21STomi Valkeinen for (i = 0; i < 9; i++) 138*f7018c21STomi Valkeinen VGAwGR(i, reg->Graphics[i], par); 139*f7018c21STomi Valkeinen 140*f7018c21STomi Valkeinen VGAenablePalette(par); 141*f7018c21STomi Valkeinen 142*f7018c21STomi Valkeinen for (i = 0; i < 21; i++) 143*f7018c21STomi Valkeinen VGAwATTR(i, reg->Attribute[i], par); 144*f7018c21STomi Valkeinen 145*f7018c21STomi Valkeinen VGAdisablePalette(par); 146*f7018c21STomi Valkeinen } 147*f7018c21STomi Valkeinen 148*f7018c21STomi Valkeinen static void vgaHWInit(struct fb_var_screeninfo *var, 149*f7018c21STomi Valkeinen struct savagefb_par *par, 150*f7018c21STomi Valkeinen struct xtimings *timings, 151*f7018c21STomi Valkeinen struct savage_reg *reg) 152*f7018c21STomi Valkeinen { 153*f7018c21STomi Valkeinen reg->MiscOutReg = 0x23; 154*f7018c21STomi Valkeinen 155*f7018c21STomi Valkeinen if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT)) 156*f7018c21STomi Valkeinen reg->MiscOutReg |= 0x40; 157*f7018c21STomi Valkeinen 158*f7018c21STomi Valkeinen if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT)) 159*f7018c21STomi Valkeinen reg->MiscOutReg |= 0x80; 160*f7018c21STomi Valkeinen 161*f7018c21STomi Valkeinen /* 162*f7018c21STomi Valkeinen * Time Sequencer 163*f7018c21STomi Valkeinen */ 164*f7018c21STomi Valkeinen reg->Sequencer[0x00] = 0x00; 165*f7018c21STomi Valkeinen reg->Sequencer[0x01] = 0x01; 166*f7018c21STomi Valkeinen reg->Sequencer[0x02] = 0x0F; 167*f7018c21STomi Valkeinen reg->Sequencer[0x03] = 0x00; /* Font select */ 168*f7018c21STomi Valkeinen reg->Sequencer[0x04] = 0x0E; /* Misc */ 169*f7018c21STomi Valkeinen 170*f7018c21STomi Valkeinen /* 171*f7018c21STomi Valkeinen * CRTC Controller 172*f7018c21STomi Valkeinen */ 173*f7018c21STomi Valkeinen reg->CRTC[0x00] = (timings->HTotal >> 3) - 5; 174*f7018c21STomi Valkeinen reg->CRTC[0x01] = (timings->HDisplay >> 3) - 1; 175*f7018c21STomi Valkeinen reg->CRTC[0x02] = (timings->HSyncStart >> 3) - 1; 176*f7018c21STomi Valkeinen reg->CRTC[0x03] = (((timings->HSyncEnd >> 3) - 1) & 0x1f) | 0x80; 177*f7018c21STomi Valkeinen reg->CRTC[0x04] = (timings->HSyncStart >> 3); 178*f7018c21STomi Valkeinen reg->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) | 179*f7018c21STomi Valkeinen (((timings->HSyncEnd >> 3)) & 0x1f); 180*f7018c21STomi Valkeinen reg->CRTC[0x06] = (timings->VTotal - 2) & 0xFF; 181*f7018c21STomi Valkeinen reg->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) | 182*f7018c21STomi Valkeinen (((timings->VDisplay - 1) & 0x100) >> 7) | 183*f7018c21STomi Valkeinen ((timings->VSyncStart & 0x100) >> 6) | 184*f7018c21STomi Valkeinen (((timings->VSyncStart - 1) & 0x100) >> 5) | 185*f7018c21STomi Valkeinen 0x10 | 186*f7018c21STomi Valkeinen (((timings->VTotal - 2) & 0x200) >> 4) | 187*f7018c21STomi Valkeinen (((timings->VDisplay - 1) & 0x200) >> 3) | 188*f7018c21STomi Valkeinen ((timings->VSyncStart & 0x200) >> 2); 189*f7018c21STomi Valkeinen reg->CRTC[0x08] = 0x00; 190*f7018c21STomi Valkeinen reg->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40; 191*f7018c21STomi Valkeinen 192*f7018c21STomi Valkeinen if (timings->dblscan) 193*f7018c21STomi Valkeinen reg->CRTC[0x09] |= 0x80; 194*f7018c21STomi Valkeinen 195*f7018c21STomi Valkeinen reg->CRTC[0x0a] = 0x00; 196*f7018c21STomi Valkeinen reg->CRTC[0x0b] = 0x00; 197*f7018c21STomi Valkeinen reg->CRTC[0x0c] = 0x00; 198*f7018c21STomi Valkeinen reg->CRTC[0x0d] = 0x00; 199*f7018c21STomi Valkeinen reg->CRTC[0x0e] = 0x00; 200*f7018c21STomi Valkeinen reg->CRTC[0x0f] = 0x00; 201*f7018c21STomi Valkeinen reg->CRTC[0x10] = timings->VSyncStart & 0xff; 202*f7018c21STomi Valkeinen reg->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20; 203*f7018c21STomi Valkeinen reg->CRTC[0x12] = (timings->VDisplay - 1) & 0xff; 204*f7018c21STomi Valkeinen reg->CRTC[0x13] = var->xres_virtual >> 4; 205*f7018c21STomi Valkeinen reg->CRTC[0x14] = 0x00; 206*f7018c21STomi Valkeinen reg->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff; 207*f7018c21STomi Valkeinen reg->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff; 208*f7018c21STomi Valkeinen reg->CRTC[0x17] = 0xc3; 209*f7018c21STomi Valkeinen reg->CRTC[0x18] = 0xff; 210*f7018c21STomi Valkeinen 211*f7018c21STomi Valkeinen /* 212*f7018c21STomi Valkeinen * are these unnecessary? 213*f7018c21STomi Valkeinen * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO); 214*f7018c21STomi Valkeinen * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO); 215*f7018c21STomi Valkeinen */ 216*f7018c21STomi Valkeinen 217*f7018c21STomi Valkeinen /* 218*f7018c21STomi Valkeinen * Graphics Display Controller 219*f7018c21STomi Valkeinen */ 220*f7018c21STomi Valkeinen reg->Graphics[0x00] = 0x00; 221*f7018c21STomi Valkeinen reg->Graphics[0x01] = 0x00; 222*f7018c21STomi Valkeinen reg->Graphics[0x02] = 0x00; 223*f7018c21STomi Valkeinen reg->Graphics[0x03] = 0x00; 224*f7018c21STomi Valkeinen reg->Graphics[0x04] = 0x00; 225*f7018c21STomi Valkeinen reg->Graphics[0x05] = 0x40; 226*f7018c21STomi Valkeinen reg->Graphics[0x06] = 0x05; /* only map 64k VGA memory !!!! */ 227*f7018c21STomi Valkeinen reg->Graphics[0x07] = 0x0F; 228*f7018c21STomi Valkeinen reg->Graphics[0x08] = 0xFF; 229*f7018c21STomi Valkeinen 230*f7018c21STomi Valkeinen 231*f7018c21STomi Valkeinen reg->Attribute[0x00] = 0x00; /* standard colormap translation */ 232*f7018c21STomi Valkeinen reg->Attribute[0x01] = 0x01; 233*f7018c21STomi Valkeinen reg->Attribute[0x02] = 0x02; 234*f7018c21STomi Valkeinen reg->Attribute[0x03] = 0x03; 235*f7018c21STomi Valkeinen reg->Attribute[0x04] = 0x04; 236*f7018c21STomi Valkeinen reg->Attribute[0x05] = 0x05; 237*f7018c21STomi Valkeinen reg->Attribute[0x06] = 0x06; 238*f7018c21STomi Valkeinen reg->Attribute[0x07] = 0x07; 239*f7018c21STomi Valkeinen reg->Attribute[0x08] = 0x08; 240*f7018c21STomi Valkeinen reg->Attribute[0x09] = 0x09; 241*f7018c21STomi Valkeinen reg->Attribute[0x0a] = 0x0A; 242*f7018c21STomi Valkeinen reg->Attribute[0x0b] = 0x0B; 243*f7018c21STomi Valkeinen reg->Attribute[0x0c] = 0x0C; 244*f7018c21STomi Valkeinen reg->Attribute[0x0d] = 0x0D; 245*f7018c21STomi Valkeinen reg->Attribute[0x0e] = 0x0E; 246*f7018c21STomi Valkeinen reg->Attribute[0x0f] = 0x0F; 247*f7018c21STomi Valkeinen reg->Attribute[0x10] = 0x41; 248*f7018c21STomi Valkeinen reg->Attribute[0x11] = 0xFF; 249*f7018c21STomi Valkeinen reg->Attribute[0x12] = 0x0F; 250*f7018c21STomi Valkeinen reg->Attribute[0x13] = 0x00; 251*f7018c21STomi Valkeinen reg->Attribute[0x14] = 0x00; 252*f7018c21STomi Valkeinen } 253*f7018c21STomi Valkeinen 254*f7018c21STomi Valkeinen /* -------------------- Hardware specific routines ------------------------- */ 255*f7018c21STomi Valkeinen 256*f7018c21STomi Valkeinen /* 257*f7018c21STomi Valkeinen * Hardware Acceleration for SavageFB 258*f7018c21STomi Valkeinen */ 259*f7018c21STomi Valkeinen 260*f7018c21STomi Valkeinen /* Wait for fifo space */ 261*f7018c21STomi Valkeinen static void 262*f7018c21STomi Valkeinen savage3D_waitfifo(struct savagefb_par *par, int space) 263*f7018c21STomi Valkeinen { 264*f7018c21STomi Valkeinen int slots = MAXFIFO - space; 265*f7018c21STomi Valkeinen 266*f7018c21STomi Valkeinen while ((savage_in32(0x48C00, par) & 0x0000ffff) > slots); 267*f7018c21STomi Valkeinen } 268*f7018c21STomi Valkeinen 269*f7018c21STomi Valkeinen static void 270*f7018c21STomi Valkeinen savage4_waitfifo(struct savagefb_par *par, int space) 271*f7018c21STomi Valkeinen { 272*f7018c21STomi Valkeinen int slots = MAXFIFO - space; 273*f7018c21STomi Valkeinen 274*f7018c21STomi Valkeinen while ((savage_in32(0x48C60, par) & 0x001fffff) > slots); 275*f7018c21STomi Valkeinen } 276*f7018c21STomi Valkeinen 277*f7018c21STomi Valkeinen static void 278*f7018c21STomi Valkeinen savage2000_waitfifo(struct savagefb_par *par, int space) 279*f7018c21STomi Valkeinen { 280*f7018c21STomi Valkeinen int slots = MAXFIFO - space; 281*f7018c21STomi Valkeinen 282*f7018c21STomi Valkeinen while ((savage_in32(0x48C60, par) & 0x0000ffff) > slots); 283*f7018c21STomi Valkeinen } 284*f7018c21STomi Valkeinen 285*f7018c21STomi Valkeinen /* Wait for idle accelerator */ 286*f7018c21STomi Valkeinen static void 287*f7018c21STomi Valkeinen savage3D_waitidle(struct savagefb_par *par) 288*f7018c21STomi Valkeinen { 289*f7018c21STomi Valkeinen while ((savage_in32(0x48C00, par) & 0x0008ffff) != 0x80000); 290*f7018c21STomi Valkeinen } 291*f7018c21STomi Valkeinen 292*f7018c21STomi Valkeinen static void 293*f7018c21STomi Valkeinen savage4_waitidle(struct savagefb_par *par) 294*f7018c21STomi Valkeinen { 295*f7018c21STomi Valkeinen while ((savage_in32(0x48C60, par) & 0x00a00000) != 0x00a00000); 296*f7018c21STomi Valkeinen } 297*f7018c21STomi Valkeinen 298*f7018c21STomi Valkeinen static void 299*f7018c21STomi Valkeinen savage2000_waitidle(struct savagefb_par *par) 300*f7018c21STomi Valkeinen { 301*f7018c21STomi Valkeinen while ((savage_in32(0x48C60, par) & 0x009fffff)); 302*f7018c21STomi Valkeinen } 303*f7018c21STomi Valkeinen 304*f7018c21STomi Valkeinen #ifdef CONFIG_FB_SAVAGE_ACCEL 305*f7018c21STomi Valkeinen static void 306*f7018c21STomi Valkeinen SavageSetup2DEngine(struct savagefb_par *par) 307*f7018c21STomi Valkeinen { 308*f7018c21STomi Valkeinen unsigned long GlobalBitmapDescriptor; 309*f7018c21STomi Valkeinen 310*f7018c21STomi Valkeinen GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE; 311*f7018c21STomi Valkeinen BCI_BD_SET_BPP(GlobalBitmapDescriptor, par->depth); 312*f7018c21STomi Valkeinen BCI_BD_SET_STRIDE(GlobalBitmapDescriptor, par->vwidth); 313*f7018c21STomi Valkeinen 314*f7018c21STomi Valkeinen switch(par->chip) { 315*f7018c21STomi Valkeinen case S3_SAVAGE3D: 316*f7018c21STomi Valkeinen case S3_SAVAGE_MX: 317*f7018c21STomi Valkeinen /* Disable BCI */ 318*f7018c21STomi Valkeinen savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par); 319*f7018c21STomi Valkeinen /* Setup BCI command overflow buffer */ 320*f7018c21STomi Valkeinen savage_out32(0x48C14, 321*f7018c21STomi Valkeinen (par->cob_offset >> 11) | (par->cob_index << 29), 322*f7018c21STomi Valkeinen par); 323*f7018c21STomi Valkeinen /* Program shadow status update. */ 324*f7018c21STomi Valkeinen savage_out32(0x48C10, 0x78207220, par); 325*f7018c21STomi Valkeinen savage_out32(0x48C0C, 0, par); 326*f7018c21STomi Valkeinen /* Enable BCI and command overflow buffer */ 327*f7018c21STomi Valkeinen savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x0C, par); 328*f7018c21STomi Valkeinen break; 329*f7018c21STomi Valkeinen case S3_SAVAGE4: 330*f7018c21STomi Valkeinen case S3_TWISTER: 331*f7018c21STomi Valkeinen case S3_PROSAVAGE: 332*f7018c21STomi Valkeinen case S3_PROSAVAGEDDR: 333*f7018c21STomi Valkeinen case S3_SUPERSAVAGE: 334*f7018c21STomi Valkeinen /* Disable BCI */ 335*f7018c21STomi Valkeinen savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par); 336*f7018c21STomi Valkeinen /* Program shadow status update */ 337*f7018c21STomi Valkeinen savage_out32(0x48C10, 0x00700040, par); 338*f7018c21STomi Valkeinen savage_out32(0x48C0C, 0, par); 339*f7018c21STomi Valkeinen /* Enable BCI without the COB */ 340*f7018c21STomi Valkeinen savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x08, par); 341*f7018c21STomi Valkeinen break; 342*f7018c21STomi Valkeinen case S3_SAVAGE2000: 343*f7018c21STomi Valkeinen /* Disable BCI */ 344*f7018c21STomi Valkeinen savage_out32(0x48C18, 0, par); 345*f7018c21STomi Valkeinen /* Setup BCI command overflow buffer */ 346*f7018c21STomi Valkeinen savage_out32(0x48C18, 347*f7018c21STomi Valkeinen (par->cob_offset >> 7) | (par->cob_index), 348*f7018c21STomi Valkeinen par); 349*f7018c21STomi Valkeinen /* Disable shadow status update */ 350*f7018c21STomi Valkeinen savage_out32(0x48A30, 0, par); 351*f7018c21STomi Valkeinen /* Enable BCI and command overflow buffer */ 352*f7018c21STomi Valkeinen savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x00280000, 353*f7018c21STomi Valkeinen par); 354*f7018c21STomi Valkeinen break; 355*f7018c21STomi Valkeinen default: 356*f7018c21STomi Valkeinen break; 357*f7018c21STomi Valkeinen } 358*f7018c21STomi Valkeinen /* Turn on 16-bit register access. */ 359*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x31, par); 360*f7018c21STomi Valkeinen vga_out8(0x3d5, 0x0c, par); 361*f7018c21STomi Valkeinen 362*f7018c21STomi Valkeinen /* Set stride to use GBD. */ 363*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x50, par); 364*f7018c21STomi Valkeinen vga_out8(0x3d5, vga_in8(0x3d5, par) | 0xC1, par); 365*f7018c21STomi Valkeinen 366*f7018c21STomi Valkeinen /* Enable 2D engine. */ 367*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 368*f7018c21STomi Valkeinen vga_out8(0x3d5, 0x01, par); 369*f7018c21STomi Valkeinen 370*f7018c21STomi Valkeinen savage_out32(MONO_PAT_0, ~0, par); 371*f7018c21STomi Valkeinen savage_out32(MONO_PAT_1, ~0, par); 372*f7018c21STomi Valkeinen 373*f7018c21STomi Valkeinen /* Setup plane masks */ 374*f7018c21STomi Valkeinen savage_out32(0x8128, ~0, par); /* enable all write planes */ 375*f7018c21STomi Valkeinen savage_out32(0x812C, ~0, par); /* enable all read planes */ 376*f7018c21STomi Valkeinen savage_out16(0x8134, 0x27, par); 377*f7018c21STomi Valkeinen savage_out16(0x8136, 0x07, par); 378*f7018c21STomi Valkeinen 379*f7018c21STomi Valkeinen /* Now set the GBD */ 380*f7018c21STomi Valkeinen par->bci_ptr = 0; 381*f7018c21STomi Valkeinen par->SavageWaitFifo(par, 4); 382*f7018c21STomi Valkeinen 383*f7018c21STomi Valkeinen BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1); 384*f7018c21STomi Valkeinen BCI_SEND(0); 385*f7018c21STomi Valkeinen BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2); 386*f7018c21STomi Valkeinen BCI_SEND(GlobalBitmapDescriptor); 387*f7018c21STomi Valkeinen 388*f7018c21STomi Valkeinen /* 389*f7018c21STomi Valkeinen * I don't know why, sending this twice fixes the initial black screen, 390*f7018c21STomi Valkeinen * prevents X from crashing at least in Toshiba laptops with SavageIX. 391*f7018c21STomi Valkeinen * --Tony 392*f7018c21STomi Valkeinen */ 393*f7018c21STomi Valkeinen par->bci_ptr = 0; 394*f7018c21STomi Valkeinen par->SavageWaitFifo(par, 4); 395*f7018c21STomi Valkeinen 396*f7018c21STomi Valkeinen BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1); 397*f7018c21STomi Valkeinen BCI_SEND(0); 398*f7018c21STomi Valkeinen BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2); 399*f7018c21STomi Valkeinen BCI_SEND(GlobalBitmapDescriptor); 400*f7018c21STomi Valkeinen } 401*f7018c21STomi Valkeinen 402*f7018c21STomi Valkeinen static void savagefb_set_clip(struct fb_info *info) 403*f7018c21STomi Valkeinen { 404*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 405*f7018c21STomi Valkeinen int cmd; 406*f7018c21STomi Valkeinen 407*f7018c21STomi Valkeinen cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW; 408*f7018c21STomi Valkeinen par->bci_ptr = 0; 409*f7018c21STomi Valkeinen par->SavageWaitFifo(par,3); 410*f7018c21STomi Valkeinen BCI_SEND(cmd); 411*f7018c21STomi Valkeinen BCI_SEND(BCI_CLIP_TL(0, 0)); 412*f7018c21STomi Valkeinen BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff)); 413*f7018c21STomi Valkeinen } 414*f7018c21STomi Valkeinen #else 415*f7018c21STomi Valkeinen static void SavageSetup2DEngine(struct savagefb_par *par) {} 416*f7018c21STomi Valkeinen 417*f7018c21STomi Valkeinen #endif 418*f7018c21STomi Valkeinen 419*f7018c21STomi Valkeinen static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1, 420*f7018c21STomi Valkeinen int min_n2, int max_n2, long freq_min, 421*f7018c21STomi Valkeinen long freq_max, unsigned int *mdiv, 422*f7018c21STomi Valkeinen unsigned int *ndiv, unsigned int *r) 423*f7018c21STomi Valkeinen { 424*f7018c21STomi Valkeinen long diff, best_diff; 425*f7018c21STomi Valkeinen unsigned int m; 426*f7018c21STomi Valkeinen unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2; 427*f7018c21STomi Valkeinen 428*f7018c21STomi Valkeinen if (freq < freq_min / (1 << max_n2)) { 429*f7018c21STomi Valkeinen printk(KERN_ERR "invalid frequency %ld Khz\n", freq); 430*f7018c21STomi Valkeinen freq = freq_min / (1 << max_n2); 431*f7018c21STomi Valkeinen } 432*f7018c21STomi Valkeinen if (freq > freq_max / (1 << min_n2)) { 433*f7018c21STomi Valkeinen printk(KERN_ERR "invalid frequency %ld Khz\n", freq); 434*f7018c21STomi Valkeinen freq = freq_max / (1 << min_n2); 435*f7018c21STomi Valkeinen } 436*f7018c21STomi Valkeinen 437*f7018c21STomi Valkeinen /* work out suitable timings */ 438*f7018c21STomi Valkeinen best_diff = freq; 439*f7018c21STomi Valkeinen 440*f7018c21STomi Valkeinen for (n2=min_n2; n2<=max_n2; n2++) { 441*f7018c21STomi Valkeinen for (n1=min_n1+2; n1<=max_n1+2; n1++) { 442*f7018c21STomi Valkeinen m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) / 443*f7018c21STomi Valkeinen BASE_FREQ; 444*f7018c21STomi Valkeinen if (m < min_m+2 || m > 127+2) 445*f7018c21STomi Valkeinen continue; 446*f7018c21STomi Valkeinen if ((m * BASE_FREQ >= freq_min * n1) && 447*f7018c21STomi Valkeinen (m * BASE_FREQ <= freq_max * n1)) { 448*f7018c21STomi Valkeinen diff = freq * (1 << n2) * n1 - BASE_FREQ * m; 449*f7018c21STomi Valkeinen if (diff < 0) 450*f7018c21STomi Valkeinen diff = -diff; 451*f7018c21STomi Valkeinen if (diff < best_diff) { 452*f7018c21STomi Valkeinen best_diff = diff; 453*f7018c21STomi Valkeinen best_m = m; 454*f7018c21STomi Valkeinen best_n1 = n1; 455*f7018c21STomi Valkeinen best_n2 = n2; 456*f7018c21STomi Valkeinen } 457*f7018c21STomi Valkeinen } 458*f7018c21STomi Valkeinen } 459*f7018c21STomi Valkeinen } 460*f7018c21STomi Valkeinen 461*f7018c21STomi Valkeinen *ndiv = best_n1 - 2; 462*f7018c21STomi Valkeinen *r = best_n2; 463*f7018c21STomi Valkeinen *mdiv = best_m - 2; 464*f7018c21STomi Valkeinen } 465*f7018c21STomi Valkeinen 466*f7018c21STomi Valkeinen static int common_calc_clock(long freq, int min_m, int min_n1, int max_n1, 467*f7018c21STomi Valkeinen int min_n2, int max_n2, long freq_min, 468*f7018c21STomi Valkeinen long freq_max, unsigned char *mdiv, 469*f7018c21STomi Valkeinen unsigned char *ndiv) 470*f7018c21STomi Valkeinen { 471*f7018c21STomi Valkeinen long diff, best_diff; 472*f7018c21STomi Valkeinen unsigned int m; 473*f7018c21STomi Valkeinen unsigned char n1, n2; 474*f7018c21STomi Valkeinen unsigned char best_n1 = 16+2, best_n2 = 2, best_m = 125+2; 475*f7018c21STomi Valkeinen 476*f7018c21STomi Valkeinen best_diff = freq; 477*f7018c21STomi Valkeinen 478*f7018c21STomi Valkeinen for (n2 = min_n2; n2 <= max_n2; n2++) { 479*f7018c21STomi Valkeinen for (n1 = min_n1+2; n1 <= max_n1+2; n1++) { 480*f7018c21STomi Valkeinen m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) / 481*f7018c21STomi Valkeinen BASE_FREQ; 482*f7018c21STomi Valkeinen if (m < min_m + 2 || m > 127+2) 483*f7018c21STomi Valkeinen continue; 484*f7018c21STomi Valkeinen if ((m * BASE_FREQ >= freq_min * n1) && 485*f7018c21STomi Valkeinen (m * BASE_FREQ <= freq_max * n1)) { 486*f7018c21STomi Valkeinen diff = freq * (1 << n2) * n1 - BASE_FREQ * m; 487*f7018c21STomi Valkeinen if (diff < 0) 488*f7018c21STomi Valkeinen diff = -diff; 489*f7018c21STomi Valkeinen if (diff < best_diff) { 490*f7018c21STomi Valkeinen best_diff = diff; 491*f7018c21STomi Valkeinen best_m = m; 492*f7018c21STomi Valkeinen best_n1 = n1; 493*f7018c21STomi Valkeinen best_n2 = n2; 494*f7018c21STomi Valkeinen } 495*f7018c21STomi Valkeinen } 496*f7018c21STomi Valkeinen } 497*f7018c21STomi Valkeinen } 498*f7018c21STomi Valkeinen 499*f7018c21STomi Valkeinen if (max_n1 == 63) 500*f7018c21STomi Valkeinen *ndiv = (best_n1 - 2) | (best_n2 << 6); 501*f7018c21STomi Valkeinen else 502*f7018c21STomi Valkeinen *ndiv = (best_n1 - 2) | (best_n2 << 5); 503*f7018c21STomi Valkeinen 504*f7018c21STomi Valkeinen *mdiv = best_m - 2; 505*f7018c21STomi Valkeinen 506*f7018c21STomi Valkeinen return 0; 507*f7018c21STomi Valkeinen } 508*f7018c21STomi Valkeinen 509*f7018c21STomi Valkeinen #ifdef SAVAGEFB_DEBUG 510*f7018c21STomi Valkeinen /* This function is used to debug, it prints out the contents of s3 regs */ 511*f7018c21STomi Valkeinen 512*f7018c21STomi Valkeinen static void SavagePrintRegs(struct savagefb_par *par) 513*f7018c21STomi Valkeinen { 514*f7018c21STomi Valkeinen unsigned char i; 515*f7018c21STomi Valkeinen int vgaCRIndex = 0x3d4; 516*f7018c21STomi Valkeinen int vgaCRReg = 0x3d5; 517*f7018c21STomi Valkeinen 518*f7018c21STomi Valkeinen printk(KERN_DEBUG "SR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE " 519*f7018c21STomi Valkeinen "xF"); 520*f7018c21STomi Valkeinen 521*f7018c21STomi Valkeinen for (i = 0; i < 0x70; i++) { 522*f7018c21STomi Valkeinen if (!(i % 16)) 523*f7018c21STomi Valkeinen printk(KERN_DEBUG "\nSR%xx ", i >> 4); 524*f7018c21STomi Valkeinen vga_out8(0x3c4, i, par); 525*f7018c21STomi Valkeinen printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par)); 526*f7018c21STomi Valkeinen } 527*f7018c21STomi Valkeinen 528*f7018c21STomi Valkeinen printk(KERN_DEBUG "\n\nCR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC " 529*f7018c21STomi Valkeinen "xD xE xF"); 530*f7018c21STomi Valkeinen 531*f7018c21STomi Valkeinen for (i = 0; i < 0xB7; i++) { 532*f7018c21STomi Valkeinen if (!(i % 16)) 533*f7018c21STomi Valkeinen printk(KERN_DEBUG "\nCR%xx ", i >> 4); 534*f7018c21STomi Valkeinen vga_out8(vgaCRIndex, i, par); 535*f7018c21STomi Valkeinen printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par)); 536*f7018c21STomi Valkeinen } 537*f7018c21STomi Valkeinen 538*f7018c21STomi Valkeinen printk(KERN_DEBUG "\n\n"); 539*f7018c21STomi Valkeinen } 540*f7018c21STomi Valkeinen #endif 541*f7018c21STomi Valkeinen 542*f7018c21STomi Valkeinen /* --------------------------------------------------------------------- */ 543*f7018c21STomi Valkeinen 544*f7018c21STomi Valkeinen static void savage_get_default_par(struct savagefb_par *par, struct savage_reg *reg) 545*f7018c21STomi Valkeinen { 546*f7018c21STomi Valkeinen unsigned char cr3a, cr53, cr66; 547*f7018c21STomi Valkeinen 548*f7018c21STomi Valkeinen vga_out16(0x3d4, 0x4838, par); 549*f7018c21STomi Valkeinen vga_out16(0x3d4, 0xa039, par); 550*f7018c21STomi Valkeinen vga_out16(0x3c4, 0x0608, par); 551*f7018c21STomi Valkeinen 552*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 553*f7018c21STomi Valkeinen cr66 = vga_in8(0x3d5, par); 554*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66 | 0x80, par); 555*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 556*f7018c21STomi Valkeinen cr3a = vga_in8(0x3d5, par); 557*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a | 0x80, par); 558*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x53, par); 559*f7018c21STomi Valkeinen cr53 = vga_in8(0x3d5, par); 560*f7018c21STomi Valkeinen vga_out8(0x3d5, cr53 & 0x7f, par); 561*f7018c21STomi Valkeinen 562*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 563*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66, par); 564*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 565*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a, par); 566*f7018c21STomi Valkeinen 567*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 568*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66, par); 569*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 570*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a, par); 571*f7018c21STomi Valkeinen 572*f7018c21STomi Valkeinen /* unlock extended seq regs */ 573*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x08, par); 574*f7018c21STomi Valkeinen reg->SR08 = vga_in8(0x3c5, par); 575*f7018c21STomi Valkeinen vga_out8(0x3c5, 0x06, par); 576*f7018c21STomi Valkeinen 577*f7018c21STomi Valkeinen /* now save all the extended regs we need */ 578*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x31, par); 579*f7018c21STomi Valkeinen reg->CR31 = vga_in8(0x3d5, par); 580*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x32, par); 581*f7018c21STomi Valkeinen reg->CR32 = vga_in8(0x3d5, par); 582*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x34, par); 583*f7018c21STomi Valkeinen reg->CR34 = vga_in8(0x3d5, par); 584*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x36, par); 585*f7018c21STomi Valkeinen reg->CR36 = vga_in8(0x3d5, par); 586*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 587*f7018c21STomi Valkeinen reg->CR3A = vga_in8(0x3d5, par); 588*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 589*f7018c21STomi Valkeinen reg->CR40 = vga_in8(0x3d5, par); 590*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x42, par); 591*f7018c21STomi Valkeinen reg->CR42 = vga_in8(0x3d5, par); 592*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x45, par); 593*f7018c21STomi Valkeinen reg->CR45 = vga_in8(0x3d5, par); 594*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x50, par); 595*f7018c21STomi Valkeinen reg->CR50 = vga_in8(0x3d5, par); 596*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x51, par); 597*f7018c21STomi Valkeinen reg->CR51 = vga_in8(0x3d5, par); 598*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x53, par); 599*f7018c21STomi Valkeinen reg->CR53 = vga_in8(0x3d5, par); 600*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x58, par); 601*f7018c21STomi Valkeinen reg->CR58 = vga_in8(0x3d5, par); 602*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x60, par); 603*f7018c21STomi Valkeinen reg->CR60 = vga_in8(0x3d5, par); 604*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 605*f7018c21STomi Valkeinen reg->CR66 = vga_in8(0x3d5, par); 606*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x67, par); 607*f7018c21STomi Valkeinen reg->CR67 = vga_in8(0x3d5, par); 608*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x68, par); 609*f7018c21STomi Valkeinen reg->CR68 = vga_in8(0x3d5, par); 610*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x69, par); 611*f7018c21STomi Valkeinen reg->CR69 = vga_in8(0x3d5, par); 612*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x6f, par); 613*f7018c21STomi Valkeinen reg->CR6F = vga_in8(0x3d5, par); 614*f7018c21STomi Valkeinen 615*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x33, par); 616*f7018c21STomi Valkeinen reg->CR33 = vga_in8(0x3d5, par); 617*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x86, par); 618*f7018c21STomi Valkeinen reg->CR86 = vga_in8(0x3d5, par); 619*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x88, par); 620*f7018c21STomi Valkeinen reg->CR88 = vga_in8(0x3d5, par); 621*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x90, par); 622*f7018c21STomi Valkeinen reg->CR90 = vga_in8(0x3d5, par); 623*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x91, par); 624*f7018c21STomi Valkeinen reg->CR91 = vga_in8(0x3d5, par); 625*f7018c21STomi Valkeinen vga_out8(0x3d4, 0xb0, par); 626*f7018c21STomi Valkeinen reg->CRB0 = vga_in8(0x3d5, par) | 0x80; 627*f7018c21STomi Valkeinen 628*f7018c21STomi Valkeinen /* extended mode timing regs */ 629*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3b, par); 630*f7018c21STomi Valkeinen reg->CR3B = vga_in8(0x3d5, par); 631*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3c, par); 632*f7018c21STomi Valkeinen reg->CR3C = vga_in8(0x3d5, par); 633*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x43, par); 634*f7018c21STomi Valkeinen reg->CR43 = vga_in8(0x3d5, par); 635*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x5d, par); 636*f7018c21STomi Valkeinen reg->CR5D = vga_in8(0x3d5, par); 637*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x5e, par); 638*f7018c21STomi Valkeinen reg->CR5E = vga_in8(0x3d5, par); 639*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x65, par); 640*f7018c21STomi Valkeinen reg->CR65 = vga_in8(0x3d5, par); 641*f7018c21STomi Valkeinen 642*f7018c21STomi Valkeinen /* save seq extended regs for DCLK PLL programming */ 643*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0e, par); 644*f7018c21STomi Valkeinen reg->SR0E = vga_in8(0x3c5, par); 645*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0f, par); 646*f7018c21STomi Valkeinen reg->SR0F = vga_in8(0x3c5, par); 647*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x10, par); 648*f7018c21STomi Valkeinen reg->SR10 = vga_in8(0x3c5, par); 649*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x11, par); 650*f7018c21STomi Valkeinen reg->SR11 = vga_in8(0x3c5, par); 651*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x12, par); 652*f7018c21STomi Valkeinen reg->SR12 = vga_in8(0x3c5, par); 653*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x13, par); 654*f7018c21STomi Valkeinen reg->SR13 = vga_in8(0x3c5, par); 655*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x29, par); 656*f7018c21STomi Valkeinen reg->SR29 = vga_in8(0x3c5, par); 657*f7018c21STomi Valkeinen 658*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x15, par); 659*f7018c21STomi Valkeinen reg->SR15 = vga_in8(0x3c5, par); 660*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x30, par); 661*f7018c21STomi Valkeinen reg->SR30 = vga_in8(0x3c5, par); 662*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x18, par); 663*f7018c21STomi Valkeinen reg->SR18 = vga_in8(0x3c5, par); 664*f7018c21STomi Valkeinen 665*f7018c21STomi Valkeinen /* Save flat panel expansion registers. */ 666*f7018c21STomi Valkeinen if (par->chip == S3_SAVAGE_MX) { 667*f7018c21STomi Valkeinen int i; 668*f7018c21STomi Valkeinen 669*f7018c21STomi Valkeinen for (i = 0; i < 8; i++) { 670*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x54+i, par); 671*f7018c21STomi Valkeinen reg->SR54[i] = vga_in8(0x3c5, par); 672*f7018c21STomi Valkeinen } 673*f7018c21STomi Valkeinen } 674*f7018c21STomi Valkeinen 675*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 676*f7018c21STomi Valkeinen cr66 = vga_in8(0x3d5, par); 677*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66 | 0x80, par); 678*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 679*f7018c21STomi Valkeinen cr3a = vga_in8(0x3d5, par); 680*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a | 0x80, par); 681*f7018c21STomi Valkeinen 682*f7018c21STomi Valkeinen /* now save MIU regs */ 683*f7018c21STomi Valkeinen if (par->chip != S3_SAVAGE_MX) { 684*f7018c21STomi Valkeinen reg->MMPR0 = savage_in32(FIFO_CONTROL_REG, par); 685*f7018c21STomi Valkeinen reg->MMPR1 = savage_in32(MIU_CONTROL_REG, par); 686*f7018c21STomi Valkeinen reg->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par); 687*f7018c21STomi Valkeinen reg->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par); 688*f7018c21STomi Valkeinen } 689*f7018c21STomi Valkeinen 690*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 691*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a, par); 692*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 693*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66, par); 694*f7018c21STomi Valkeinen } 695*f7018c21STomi Valkeinen 696*f7018c21STomi Valkeinen static void savage_set_default_par(struct savagefb_par *par, 697*f7018c21STomi Valkeinen struct savage_reg *reg) 698*f7018c21STomi Valkeinen { 699*f7018c21STomi Valkeinen unsigned char cr3a, cr53, cr66; 700*f7018c21STomi Valkeinen 701*f7018c21STomi Valkeinen vga_out16(0x3d4, 0x4838, par); 702*f7018c21STomi Valkeinen vga_out16(0x3d4, 0xa039, par); 703*f7018c21STomi Valkeinen vga_out16(0x3c4, 0x0608, par); 704*f7018c21STomi Valkeinen 705*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 706*f7018c21STomi Valkeinen cr66 = vga_in8(0x3d5, par); 707*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66 | 0x80, par); 708*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 709*f7018c21STomi Valkeinen cr3a = vga_in8(0x3d5, par); 710*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a | 0x80, par); 711*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x53, par); 712*f7018c21STomi Valkeinen cr53 = vga_in8(0x3d5, par); 713*f7018c21STomi Valkeinen vga_out8(0x3d5, cr53 & 0x7f, par); 714*f7018c21STomi Valkeinen 715*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 716*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66, par); 717*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 718*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a, par); 719*f7018c21STomi Valkeinen 720*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 721*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66, par); 722*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 723*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a, par); 724*f7018c21STomi Valkeinen 725*f7018c21STomi Valkeinen /* unlock extended seq regs */ 726*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x08, par); 727*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR08, par); 728*f7018c21STomi Valkeinen vga_out8(0x3c5, 0x06, par); 729*f7018c21STomi Valkeinen 730*f7018c21STomi Valkeinen /* now restore all the extended regs we need */ 731*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x31, par); 732*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR31, par); 733*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x32, par); 734*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR32, par); 735*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x34, par); 736*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR34, par); 737*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x36, par); 738*f7018c21STomi Valkeinen vga_out8(0x3d5,reg->CR36, par); 739*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 740*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR3A, par); 741*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 742*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR40, par); 743*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x42, par); 744*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR42, par); 745*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x45, par); 746*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR45, par); 747*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x50, par); 748*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR50, par); 749*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x51, par); 750*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR51, par); 751*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x53, par); 752*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR53, par); 753*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x58, par); 754*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR58, par); 755*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x60, par); 756*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR60, par); 757*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 758*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR66, par); 759*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x67, par); 760*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR67, par); 761*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x68, par); 762*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR68, par); 763*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x69, par); 764*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR69, par); 765*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x6f, par); 766*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR6F, par); 767*f7018c21STomi Valkeinen 768*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x33, par); 769*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR33, par); 770*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x86, par); 771*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR86, par); 772*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x88, par); 773*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR88, par); 774*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x90, par); 775*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR90, par); 776*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x91, par); 777*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR91, par); 778*f7018c21STomi Valkeinen vga_out8(0x3d4, 0xb0, par); 779*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CRB0, par); 780*f7018c21STomi Valkeinen 781*f7018c21STomi Valkeinen /* extended mode timing regs */ 782*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3b, par); 783*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR3B, par); 784*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3c, par); 785*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR3C, par); 786*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x43, par); 787*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR43, par); 788*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x5d, par); 789*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR5D, par); 790*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x5e, par); 791*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR5E, par); 792*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x65, par); 793*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR65, par); 794*f7018c21STomi Valkeinen 795*f7018c21STomi Valkeinen /* save seq extended regs for DCLK PLL programming */ 796*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0e, par); 797*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR0E, par); 798*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0f, par); 799*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR0F, par); 800*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x10, par); 801*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR10, par); 802*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x11, par); 803*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR11, par); 804*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x12, par); 805*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR12, par); 806*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x13, par); 807*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR13, par); 808*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x29, par); 809*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR29, par); 810*f7018c21STomi Valkeinen 811*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x15, par); 812*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR15, par); 813*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x30, par); 814*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR30, par); 815*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x18, par); 816*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR18, par); 817*f7018c21STomi Valkeinen 818*f7018c21STomi Valkeinen /* Save flat panel expansion registers. */ 819*f7018c21STomi Valkeinen if (par->chip == S3_SAVAGE_MX) { 820*f7018c21STomi Valkeinen int i; 821*f7018c21STomi Valkeinen 822*f7018c21STomi Valkeinen for (i = 0; i < 8; i++) { 823*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x54+i, par); 824*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR54[i], par); 825*f7018c21STomi Valkeinen } 826*f7018c21STomi Valkeinen } 827*f7018c21STomi Valkeinen 828*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 829*f7018c21STomi Valkeinen cr66 = vga_in8(0x3d5, par); 830*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66 | 0x80, par); 831*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 832*f7018c21STomi Valkeinen cr3a = vga_in8(0x3d5, par); 833*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a | 0x80, par); 834*f7018c21STomi Valkeinen 835*f7018c21STomi Valkeinen /* now save MIU regs */ 836*f7018c21STomi Valkeinen if (par->chip != S3_SAVAGE_MX) { 837*f7018c21STomi Valkeinen savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par); 838*f7018c21STomi Valkeinen savage_out32(MIU_CONTROL_REG, reg->MMPR1, par); 839*f7018c21STomi Valkeinen savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par); 840*f7018c21STomi Valkeinen savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par); 841*f7018c21STomi Valkeinen } 842*f7018c21STomi Valkeinen 843*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 844*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a, par); 845*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 846*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66, par); 847*f7018c21STomi Valkeinen } 848*f7018c21STomi Valkeinen 849*f7018c21STomi Valkeinen static void savage_update_var(struct fb_var_screeninfo *var, 850*f7018c21STomi Valkeinen const struct fb_videomode *modedb) 851*f7018c21STomi Valkeinen { 852*f7018c21STomi Valkeinen var->xres = var->xres_virtual = modedb->xres; 853*f7018c21STomi Valkeinen var->yres = modedb->yres; 854*f7018c21STomi Valkeinen if (var->yres_virtual < var->yres) 855*f7018c21STomi Valkeinen var->yres_virtual = var->yres; 856*f7018c21STomi Valkeinen var->xoffset = var->yoffset = 0; 857*f7018c21STomi Valkeinen var->pixclock = modedb->pixclock; 858*f7018c21STomi Valkeinen var->left_margin = modedb->left_margin; 859*f7018c21STomi Valkeinen var->right_margin = modedb->right_margin; 860*f7018c21STomi Valkeinen var->upper_margin = modedb->upper_margin; 861*f7018c21STomi Valkeinen var->lower_margin = modedb->lower_margin; 862*f7018c21STomi Valkeinen var->hsync_len = modedb->hsync_len; 863*f7018c21STomi Valkeinen var->vsync_len = modedb->vsync_len; 864*f7018c21STomi Valkeinen var->sync = modedb->sync; 865*f7018c21STomi Valkeinen var->vmode = modedb->vmode; 866*f7018c21STomi Valkeinen } 867*f7018c21STomi Valkeinen 868*f7018c21STomi Valkeinen static int savagefb_check_var(struct fb_var_screeninfo *var, 869*f7018c21STomi Valkeinen struct fb_info *info) 870*f7018c21STomi Valkeinen { 871*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 872*f7018c21STomi Valkeinen int memlen, vramlen, mode_valid = 0; 873*f7018c21STomi Valkeinen 874*f7018c21STomi Valkeinen DBG("savagefb_check_var"); 875*f7018c21STomi Valkeinen 876*f7018c21STomi Valkeinen var->transp.offset = 0; 877*f7018c21STomi Valkeinen var->transp.length = 0; 878*f7018c21STomi Valkeinen switch (var->bits_per_pixel) { 879*f7018c21STomi Valkeinen case 8: 880*f7018c21STomi Valkeinen var->red.offset = var->green.offset = 881*f7018c21STomi Valkeinen var->blue.offset = 0; 882*f7018c21STomi Valkeinen var->red.length = var->green.length = 883*f7018c21STomi Valkeinen var->blue.length = var->bits_per_pixel; 884*f7018c21STomi Valkeinen break; 885*f7018c21STomi Valkeinen case 16: 886*f7018c21STomi Valkeinen var->red.offset = 11; 887*f7018c21STomi Valkeinen var->red.length = 5; 888*f7018c21STomi Valkeinen var->green.offset = 5; 889*f7018c21STomi Valkeinen var->green.length = 6; 890*f7018c21STomi Valkeinen var->blue.offset = 0; 891*f7018c21STomi Valkeinen var->blue.length = 5; 892*f7018c21STomi Valkeinen break; 893*f7018c21STomi Valkeinen case 32: 894*f7018c21STomi Valkeinen var->transp.offset = 24; 895*f7018c21STomi Valkeinen var->transp.length = 8; 896*f7018c21STomi Valkeinen var->red.offset = 16; 897*f7018c21STomi Valkeinen var->red.length = 8; 898*f7018c21STomi Valkeinen var->green.offset = 8; 899*f7018c21STomi Valkeinen var->green.length = 8; 900*f7018c21STomi Valkeinen var->blue.offset = 0; 901*f7018c21STomi Valkeinen var->blue.length = 8; 902*f7018c21STomi Valkeinen break; 903*f7018c21STomi Valkeinen 904*f7018c21STomi Valkeinen default: 905*f7018c21STomi Valkeinen return -EINVAL; 906*f7018c21STomi Valkeinen } 907*f7018c21STomi Valkeinen 908*f7018c21STomi Valkeinen if (!info->monspecs.hfmax || !info->monspecs.vfmax || 909*f7018c21STomi Valkeinen !info->monspecs.dclkmax || !fb_validate_mode(var, info)) 910*f7018c21STomi Valkeinen mode_valid = 1; 911*f7018c21STomi Valkeinen 912*f7018c21STomi Valkeinen /* calculate modeline if supported by monitor */ 913*f7018c21STomi Valkeinen if (!mode_valid && info->monspecs.gtf) { 914*f7018c21STomi Valkeinen if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info)) 915*f7018c21STomi Valkeinen mode_valid = 1; 916*f7018c21STomi Valkeinen } 917*f7018c21STomi Valkeinen 918*f7018c21STomi Valkeinen if (!mode_valid) { 919*f7018c21STomi Valkeinen const struct fb_videomode *mode; 920*f7018c21STomi Valkeinen 921*f7018c21STomi Valkeinen mode = fb_find_best_mode(var, &info->modelist); 922*f7018c21STomi Valkeinen if (mode) { 923*f7018c21STomi Valkeinen savage_update_var(var, mode); 924*f7018c21STomi Valkeinen mode_valid = 1; 925*f7018c21STomi Valkeinen } 926*f7018c21STomi Valkeinen } 927*f7018c21STomi Valkeinen 928*f7018c21STomi Valkeinen if (!mode_valid && info->monspecs.modedb_len) 929*f7018c21STomi Valkeinen return -EINVAL; 930*f7018c21STomi Valkeinen 931*f7018c21STomi Valkeinen /* Is the mode larger than the LCD panel? */ 932*f7018c21STomi Valkeinen if (par->SavagePanelWidth && 933*f7018c21STomi Valkeinen (var->xres > par->SavagePanelWidth || 934*f7018c21STomi Valkeinen var->yres > par->SavagePanelHeight)) { 935*f7018c21STomi Valkeinen printk(KERN_INFO "Mode (%dx%d) larger than the LCD panel " 936*f7018c21STomi Valkeinen "(%dx%d)\n", var->xres, var->yres, 937*f7018c21STomi Valkeinen par->SavagePanelWidth, 938*f7018c21STomi Valkeinen par->SavagePanelHeight); 939*f7018c21STomi Valkeinen return -1; 940*f7018c21STomi Valkeinen } 941*f7018c21STomi Valkeinen 942*f7018c21STomi Valkeinen if (var->yres_virtual < var->yres) 943*f7018c21STomi Valkeinen var->yres_virtual = var->yres; 944*f7018c21STomi Valkeinen if (var->xres_virtual < var->xres) 945*f7018c21STomi Valkeinen var->xres_virtual = var->xres; 946*f7018c21STomi Valkeinen 947*f7018c21STomi Valkeinen vramlen = info->fix.smem_len; 948*f7018c21STomi Valkeinen 949*f7018c21STomi Valkeinen memlen = var->xres_virtual * var->bits_per_pixel * 950*f7018c21STomi Valkeinen var->yres_virtual / 8; 951*f7018c21STomi Valkeinen if (memlen > vramlen) { 952*f7018c21STomi Valkeinen var->yres_virtual = vramlen * 8 / 953*f7018c21STomi Valkeinen (var->xres_virtual * var->bits_per_pixel); 954*f7018c21STomi Valkeinen memlen = var->xres_virtual * var->bits_per_pixel * 955*f7018c21STomi Valkeinen var->yres_virtual / 8; 956*f7018c21STomi Valkeinen } 957*f7018c21STomi Valkeinen 958*f7018c21STomi Valkeinen /* we must round yres/xres down, we already rounded y/xres_virtual up 959*f7018c21STomi Valkeinen if it was possible. We should return -EINVAL, but I disagree */ 960*f7018c21STomi Valkeinen if (var->yres_virtual < var->yres) 961*f7018c21STomi Valkeinen var->yres = var->yres_virtual; 962*f7018c21STomi Valkeinen if (var->xres_virtual < var->xres) 963*f7018c21STomi Valkeinen var->xres = var->xres_virtual; 964*f7018c21STomi Valkeinen if (var->xoffset + var->xres > var->xres_virtual) 965*f7018c21STomi Valkeinen var->xoffset = var->xres_virtual - var->xres; 966*f7018c21STomi Valkeinen if (var->yoffset + var->yres > var->yres_virtual) 967*f7018c21STomi Valkeinen var->yoffset = var->yres_virtual - var->yres; 968*f7018c21STomi Valkeinen 969*f7018c21STomi Valkeinen return 0; 970*f7018c21STomi Valkeinen } 971*f7018c21STomi Valkeinen 972*f7018c21STomi Valkeinen 973*f7018c21STomi Valkeinen static int savagefb_decode_var(struct fb_var_screeninfo *var, 974*f7018c21STomi Valkeinen struct savagefb_par *par, 975*f7018c21STomi Valkeinen struct savage_reg *reg) 976*f7018c21STomi Valkeinen { 977*f7018c21STomi Valkeinen struct xtimings timings; 978*f7018c21STomi Valkeinen int width, dclk, i, j; /*, refresh; */ 979*f7018c21STomi Valkeinen unsigned int m, n, r; 980*f7018c21STomi Valkeinen unsigned char tmp = 0; 981*f7018c21STomi Valkeinen unsigned int pixclock = var->pixclock; 982*f7018c21STomi Valkeinen 983*f7018c21STomi Valkeinen DBG("savagefb_decode_var"); 984*f7018c21STomi Valkeinen 985*f7018c21STomi Valkeinen memset(&timings, 0, sizeof(timings)); 986*f7018c21STomi Valkeinen 987*f7018c21STomi Valkeinen if (!pixclock) pixclock = 10000; /* 10ns = 100MHz */ 988*f7018c21STomi Valkeinen timings.Clock = 1000000000 / pixclock; 989*f7018c21STomi Valkeinen if (timings.Clock < 1) timings.Clock = 1; 990*f7018c21STomi Valkeinen timings.dblscan = var->vmode & FB_VMODE_DOUBLE; 991*f7018c21STomi Valkeinen timings.interlaced = var->vmode & FB_VMODE_INTERLACED; 992*f7018c21STomi Valkeinen timings.HDisplay = var->xres; 993*f7018c21STomi Valkeinen timings.HSyncStart = timings.HDisplay + var->right_margin; 994*f7018c21STomi Valkeinen timings.HSyncEnd = timings.HSyncStart + var->hsync_len; 995*f7018c21STomi Valkeinen timings.HTotal = timings.HSyncEnd + var->left_margin; 996*f7018c21STomi Valkeinen timings.VDisplay = var->yres; 997*f7018c21STomi Valkeinen timings.VSyncStart = timings.VDisplay + var->lower_margin; 998*f7018c21STomi Valkeinen timings.VSyncEnd = timings.VSyncStart + var->vsync_len; 999*f7018c21STomi Valkeinen timings.VTotal = timings.VSyncEnd + var->upper_margin; 1000*f7018c21STomi Valkeinen timings.sync = var->sync; 1001*f7018c21STomi Valkeinen 1002*f7018c21STomi Valkeinen 1003*f7018c21STomi Valkeinen par->depth = var->bits_per_pixel; 1004*f7018c21STomi Valkeinen par->vwidth = var->xres_virtual; 1005*f7018c21STomi Valkeinen 1006*f7018c21STomi Valkeinen if (var->bits_per_pixel == 16 && par->chip == S3_SAVAGE3D) { 1007*f7018c21STomi Valkeinen timings.HDisplay *= 2; 1008*f7018c21STomi Valkeinen timings.HSyncStart *= 2; 1009*f7018c21STomi Valkeinen timings.HSyncEnd *= 2; 1010*f7018c21STomi Valkeinen timings.HTotal *= 2; 1011*f7018c21STomi Valkeinen } 1012*f7018c21STomi Valkeinen 1013*f7018c21STomi Valkeinen /* 1014*f7018c21STomi Valkeinen * This will allocate the datastructure and initialize all of the 1015*f7018c21STomi Valkeinen * generic VGA registers. 1016*f7018c21STomi Valkeinen */ 1017*f7018c21STomi Valkeinen vgaHWInit(var, par, &timings, reg); 1018*f7018c21STomi Valkeinen 1019*f7018c21STomi Valkeinen /* We need to set CR67 whether or not we use the BIOS. */ 1020*f7018c21STomi Valkeinen 1021*f7018c21STomi Valkeinen dclk = timings.Clock; 1022*f7018c21STomi Valkeinen reg->CR67 = 0x00; 1023*f7018c21STomi Valkeinen 1024*f7018c21STomi Valkeinen switch(var->bits_per_pixel) { 1025*f7018c21STomi Valkeinen case 8: 1026*f7018c21STomi Valkeinen if ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) 1027*f7018c21STomi Valkeinen reg->CR67 = 0x10; /* 8bpp, 2 pixels/clock */ 1028*f7018c21STomi Valkeinen else 1029*f7018c21STomi Valkeinen reg->CR67 = 0x00; /* 8bpp, 1 pixel/clock */ 1030*f7018c21STomi Valkeinen break; 1031*f7018c21STomi Valkeinen case 15: 1032*f7018c21STomi Valkeinen if (S3_SAVAGE_MOBILE_SERIES(par->chip) || 1033*f7018c21STomi Valkeinen ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))) 1034*f7018c21STomi Valkeinen reg->CR67 = 0x30; /* 15bpp, 2 pixel/clock */ 1035*f7018c21STomi Valkeinen else 1036*f7018c21STomi Valkeinen reg->CR67 = 0x20; /* 15bpp, 1 pixels/clock */ 1037*f7018c21STomi Valkeinen break; 1038*f7018c21STomi Valkeinen case 16: 1039*f7018c21STomi Valkeinen if (S3_SAVAGE_MOBILE_SERIES(par->chip) || 1040*f7018c21STomi Valkeinen ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))) 1041*f7018c21STomi Valkeinen reg->CR67 = 0x50; /* 16bpp, 2 pixel/clock */ 1042*f7018c21STomi Valkeinen else 1043*f7018c21STomi Valkeinen reg->CR67 = 0x40; /* 16bpp, 1 pixels/clock */ 1044*f7018c21STomi Valkeinen break; 1045*f7018c21STomi Valkeinen case 24: 1046*f7018c21STomi Valkeinen reg->CR67 = 0x70; 1047*f7018c21STomi Valkeinen break; 1048*f7018c21STomi Valkeinen case 32: 1049*f7018c21STomi Valkeinen reg->CR67 = 0xd0; 1050*f7018c21STomi Valkeinen break; 1051*f7018c21STomi Valkeinen } 1052*f7018c21STomi Valkeinen 1053*f7018c21STomi Valkeinen /* 1054*f7018c21STomi Valkeinen * Either BIOS use is disabled, or we failed to find a suitable 1055*f7018c21STomi Valkeinen * match. Fall back to traditional register-crunching. 1056*f7018c21STomi Valkeinen */ 1057*f7018c21STomi Valkeinen 1058*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 1059*f7018c21STomi Valkeinen tmp = vga_in8(0x3d5, par); 1060*f7018c21STomi Valkeinen if (1 /*FIXME:psav->pci_burst*/) 1061*f7018c21STomi Valkeinen reg->CR3A = (tmp & 0x7f) | 0x15; 1062*f7018c21STomi Valkeinen else 1063*f7018c21STomi Valkeinen reg->CR3A = tmp | 0x95; 1064*f7018c21STomi Valkeinen 1065*f7018c21STomi Valkeinen reg->CR53 = 0x00; 1066*f7018c21STomi Valkeinen reg->CR31 = 0x8c; 1067*f7018c21STomi Valkeinen reg->CR66 = 0x89; 1068*f7018c21STomi Valkeinen 1069*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x58, par); 1070*f7018c21STomi Valkeinen reg->CR58 = vga_in8(0x3d5, par) & 0x80; 1071*f7018c21STomi Valkeinen reg->CR58 |= 0x13; 1072*f7018c21STomi Valkeinen 1073*f7018c21STomi Valkeinen reg->SR15 = 0x03 | 0x80; 1074*f7018c21STomi Valkeinen reg->SR18 = 0x00; 1075*f7018c21STomi Valkeinen reg->CR43 = reg->CR45 = reg->CR65 = 0x00; 1076*f7018c21STomi Valkeinen 1077*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 1078*f7018c21STomi Valkeinen reg->CR40 = vga_in8(0x3d5, par) & ~0x01; 1079*f7018c21STomi Valkeinen 1080*f7018c21STomi Valkeinen reg->MMPR0 = 0x010400; 1081*f7018c21STomi Valkeinen reg->MMPR1 = 0x00; 1082*f7018c21STomi Valkeinen reg->MMPR2 = 0x0808; 1083*f7018c21STomi Valkeinen reg->MMPR3 = 0x08080810; 1084*f7018c21STomi Valkeinen 1085*f7018c21STomi Valkeinen SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r); 1086*f7018c21STomi Valkeinen /* m = 107; n = 4; r = 2; */ 1087*f7018c21STomi Valkeinen 1088*f7018c21STomi Valkeinen if (par->MCLK <= 0) { 1089*f7018c21STomi Valkeinen reg->SR10 = 255; 1090*f7018c21STomi Valkeinen reg->SR11 = 255; 1091*f7018c21STomi Valkeinen } else { 1092*f7018c21STomi Valkeinen common_calc_clock(par->MCLK, 1, 1, 31, 0, 3, 135000, 270000, 1093*f7018c21STomi Valkeinen ®->SR11, ®->SR10); 1094*f7018c21STomi Valkeinen /* reg->SR10 = 80; // MCLK == 286000 */ 1095*f7018c21STomi Valkeinen /* reg->SR11 = 125; */ 1096*f7018c21STomi Valkeinen } 1097*f7018c21STomi Valkeinen 1098*f7018c21STomi Valkeinen reg->SR12 = (r << 6) | (n & 0x3f); 1099*f7018c21STomi Valkeinen reg->SR13 = m & 0xff; 1100*f7018c21STomi Valkeinen reg->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2; 1101*f7018c21STomi Valkeinen 1102*f7018c21STomi Valkeinen if (var->bits_per_pixel < 24) 1103*f7018c21STomi Valkeinen reg->MMPR0 -= 0x8000; 1104*f7018c21STomi Valkeinen else 1105*f7018c21STomi Valkeinen reg->MMPR0 -= 0x4000; 1106*f7018c21STomi Valkeinen 1107*f7018c21STomi Valkeinen if (timings.interlaced) 1108*f7018c21STomi Valkeinen reg->CR42 = 0x20; 1109*f7018c21STomi Valkeinen else 1110*f7018c21STomi Valkeinen reg->CR42 = 0x00; 1111*f7018c21STomi Valkeinen 1112*f7018c21STomi Valkeinen reg->CR34 = 0x10; /* display fifo */ 1113*f7018c21STomi Valkeinen 1114*f7018c21STomi Valkeinen i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) | 1115*f7018c21STomi Valkeinen ((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) | 1116*f7018c21STomi Valkeinen ((((timings.HSyncStart >> 3) - 1) & 0x100) >> 6) | 1117*f7018c21STomi Valkeinen ((timings.HSyncStart & 0x800) >> 7); 1118*f7018c21STomi Valkeinen 1119*f7018c21STomi Valkeinen if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 64) 1120*f7018c21STomi Valkeinen i |= 0x08; 1121*f7018c21STomi Valkeinen if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32) 1122*f7018c21STomi Valkeinen i |= 0x20; 1123*f7018c21STomi Valkeinen 1124*f7018c21STomi Valkeinen j = (reg->CRTC[0] + ((i & 0x01) << 8) + 1125*f7018c21STomi Valkeinen reg->CRTC[4] + ((i & 0x10) << 4) + 1) / 2; 1126*f7018c21STomi Valkeinen 1127*f7018c21STomi Valkeinen if (j - (reg->CRTC[4] + ((i & 0x10) << 4)) < 4) { 1128*f7018c21STomi Valkeinen if (reg->CRTC[4] + ((i & 0x10) << 4) + 4 <= 1129*f7018c21STomi Valkeinen reg->CRTC[0] + ((i & 0x01) << 8)) 1130*f7018c21STomi Valkeinen j = reg->CRTC[4] + ((i & 0x10) << 4) + 4; 1131*f7018c21STomi Valkeinen else 1132*f7018c21STomi Valkeinen j = reg->CRTC[0] + ((i & 0x01) << 8) + 1; 1133*f7018c21STomi Valkeinen } 1134*f7018c21STomi Valkeinen 1135*f7018c21STomi Valkeinen reg->CR3B = j & 0xff; 1136*f7018c21STomi Valkeinen i |= (j & 0x100) >> 2; 1137*f7018c21STomi Valkeinen reg->CR3C = (reg->CRTC[0] + ((i & 0x01) << 8)) / 2; 1138*f7018c21STomi Valkeinen reg->CR5D = i; 1139*f7018c21STomi Valkeinen reg->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) | 1140*f7018c21STomi Valkeinen (((timings.VDisplay - 1) & 0x400) >> 9) | 1141*f7018c21STomi Valkeinen (((timings.VSyncStart) & 0x400) >> 8) | 1142*f7018c21STomi Valkeinen (((timings.VSyncStart) & 0x400) >> 6) | 0x40; 1143*f7018c21STomi Valkeinen width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3; 1144*f7018c21STomi Valkeinen reg->CR91 = reg->CRTC[19] = 0xff & width; 1145*f7018c21STomi Valkeinen reg->CR51 = (0x300 & width) >> 4; 1146*f7018c21STomi Valkeinen reg->CR90 = 0x80 | (width >> 8); 1147*f7018c21STomi Valkeinen reg->MiscOutReg |= 0x0c; 1148*f7018c21STomi Valkeinen 1149*f7018c21STomi Valkeinen /* Set frame buffer description. */ 1150*f7018c21STomi Valkeinen 1151*f7018c21STomi Valkeinen if (var->bits_per_pixel <= 8) 1152*f7018c21STomi Valkeinen reg->CR50 = 0; 1153*f7018c21STomi Valkeinen else if (var->bits_per_pixel <= 16) 1154*f7018c21STomi Valkeinen reg->CR50 = 0x10; 1155*f7018c21STomi Valkeinen else 1156*f7018c21STomi Valkeinen reg->CR50 = 0x30; 1157*f7018c21STomi Valkeinen 1158*f7018c21STomi Valkeinen if (var->xres_virtual <= 640) 1159*f7018c21STomi Valkeinen reg->CR50 |= 0x40; 1160*f7018c21STomi Valkeinen else if (var->xres_virtual == 800) 1161*f7018c21STomi Valkeinen reg->CR50 |= 0x80; 1162*f7018c21STomi Valkeinen else if (var->xres_virtual == 1024) 1163*f7018c21STomi Valkeinen reg->CR50 |= 0x00; 1164*f7018c21STomi Valkeinen else if (var->xres_virtual == 1152) 1165*f7018c21STomi Valkeinen reg->CR50 |= 0x01; 1166*f7018c21STomi Valkeinen else if (var->xres_virtual == 1280) 1167*f7018c21STomi Valkeinen reg->CR50 |= 0xc0; 1168*f7018c21STomi Valkeinen else if (var->xres_virtual == 1600) 1169*f7018c21STomi Valkeinen reg->CR50 |= 0x81; 1170*f7018c21STomi Valkeinen else 1171*f7018c21STomi Valkeinen reg->CR50 |= 0xc1; /* Use GBD */ 1172*f7018c21STomi Valkeinen 1173*f7018c21STomi Valkeinen if (par->chip == S3_SAVAGE2000) 1174*f7018c21STomi Valkeinen reg->CR33 = 0x08; 1175*f7018c21STomi Valkeinen else 1176*f7018c21STomi Valkeinen reg->CR33 = 0x20; 1177*f7018c21STomi Valkeinen 1178*f7018c21STomi Valkeinen reg->CRTC[0x17] = 0xeb; 1179*f7018c21STomi Valkeinen 1180*f7018c21STomi Valkeinen reg->CR67 |= 1; 1181*f7018c21STomi Valkeinen 1182*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x36, par); 1183*f7018c21STomi Valkeinen reg->CR36 = vga_in8(0x3d5, par); 1184*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x68, par); 1185*f7018c21STomi Valkeinen reg->CR68 = vga_in8(0x3d5, par); 1186*f7018c21STomi Valkeinen reg->CR69 = 0; 1187*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x6f, par); 1188*f7018c21STomi Valkeinen reg->CR6F = vga_in8(0x3d5, par); 1189*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x86, par); 1190*f7018c21STomi Valkeinen reg->CR86 = vga_in8(0x3d5, par); 1191*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x88, par); 1192*f7018c21STomi Valkeinen reg->CR88 = vga_in8(0x3d5, par) | 0x08; 1193*f7018c21STomi Valkeinen vga_out8(0x3d4, 0xb0, par); 1194*f7018c21STomi Valkeinen reg->CRB0 = vga_in8(0x3d5, par) | 0x80; 1195*f7018c21STomi Valkeinen 1196*f7018c21STomi Valkeinen return 0; 1197*f7018c21STomi Valkeinen } 1198*f7018c21STomi Valkeinen 1199*f7018c21STomi Valkeinen /* --------------------------------------------------------------------- */ 1200*f7018c21STomi Valkeinen 1201*f7018c21STomi Valkeinen /* 1202*f7018c21STomi Valkeinen * Set a single color register. Return != 0 for invalid regno. 1203*f7018c21STomi Valkeinen */ 1204*f7018c21STomi Valkeinen static int savagefb_setcolreg(unsigned regno, 1205*f7018c21STomi Valkeinen unsigned red, 1206*f7018c21STomi Valkeinen unsigned green, 1207*f7018c21STomi Valkeinen unsigned blue, 1208*f7018c21STomi Valkeinen unsigned transp, 1209*f7018c21STomi Valkeinen struct fb_info *info) 1210*f7018c21STomi Valkeinen { 1211*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1212*f7018c21STomi Valkeinen 1213*f7018c21STomi Valkeinen if (regno >= NR_PALETTE) 1214*f7018c21STomi Valkeinen return -EINVAL; 1215*f7018c21STomi Valkeinen 1216*f7018c21STomi Valkeinen par->palette[regno].red = red; 1217*f7018c21STomi Valkeinen par->palette[regno].green = green; 1218*f7018c21STomi Valkeinen par->palette[regno].blue = blue; 1219*f7018c21STomi Valkeinen par->palette[regno].transp = transp; 1220*f7018c21STomi Valkeinen 1221*f7018c21STomi Valkeinen switch (info->var.bits_per_pixel) { 1222*f7018c21STomi Valkeinen case 8: 1223*f7018c21STomi Valkeinen vga_out8(0x3c8, regno, par); 1224*f7018c21STomi Valkeinen 1225*f7018c21STomi Valkeinen vga_out8(0x3c9, red >> 10, par); 1226*f7018c21STomi Valkeinen vga_out8(0x3c9, green >> 10, par); 1227*f7018c21STomi Valkeinen vga_out8(0x3c9, blue >> 10, par); 1228*f7018c21STomi Valkeinen break; 1229*f7018c21STomi Valkeinen 1230*f7018c21STomi Valkeinen case 16: 1231*f7018c21STomi Valkeinen if (regno < 16) 1232*f7018c21STomi Valkeinen ((u32 *)info->pseudo_palette)[regno] = 1233*f7018c21STomi Valkeinen ((red & 0xf800) ) | 1234*f7018c21STomi Valkeinen ((green & 0xfc00) >> 5) | 1235*f7018c21STomi Valkeinen ((blue & 0xf800) >> 11); 1236*f7018c21STomi Valkeinen break; 1237*f7018c21STomi Valkeinen 1238*f7018c21STomi Valkeinen case 24: 1239*f7018c21STomi Valkeinen if (regno < 16) 1240*f7018c21STomi Valkeinen ((u32 *)info->pseudo_palette)[regno] = 1241*f7018c21STomi Valkeinen ((red & 0xff00) << 8) | 1242*f7018c21STomi Valkeinen ((green & 0xff00) ) | 1243*f7018c21STomi Valkeinen ((blue & 0xff00) >> 8); 1244*f7018c21STomi Valkeinen break; 1245*f7018c21STomi Valkeinen case 32: 1246*f7018c21STomi Valkeinen if (regno < 16) 1247*f7018c21STomi Valkeinen ((u32 *)info->pseudo_palette)[regno] = 1248*f7018c21STomi Valkeinen ((transp & 0xff00) << 16) | 1249*f7018c21STomi Valkeinen ((red & 0xff00) << 8) | 1250*f7018c21STomi Valkeinen ((green & 0xff00) ) | 1251*f7018c21STomi Valkeinen ((blue & 0xff00) >> 8); 1252*f7018c21STomi Valkeinen break; 1253*f7018c21STomi Valkeinen 1254*f7018c21STomi Valkeinen default: 1255*f7018c21STomi Valkeinen return 1; 1256*f7018c21STomi Valkeinen } 1257*f7018c21STomi Valkeinen 1258*f7018c21STomi Valkeinen return 0; 1259*f7018c21STomi Valkeinen } 1260*f7018c21STomi Valkeinen 1261*f7018c21STomi Valkeinen static void savagefb_set_par_int(struct savagefb_par *par, struct savage_reg *reg) 1262*f7018c21STomi Valkeinen { 1263*f7018c21STomi Valkeinen unsigned char tmp, cr3a, cr66, cr67; 1264*f7018c21STomi Valkeinen 1265*f7018c21STomi Valkeinen DBG("savagefb_set_par_int"); 1266*f7018c21STomi Valkeinen 1267*f7018c21STomi Valkeinen par->SavageWaitIdle(par); 1268*f7018c21STomi Valkeinen 1269*f7018c21STomi Valkeinen vga_out8(0x3c2, 0x23, par); 1270*f7018c21STomi Valkeinen 1271*f7018c21STomi Valkeinen vga_out16(0x3d4, 0x4838, par); 1272*f7018c21STomi Valkeinen vga_out16(0x3d4, 0xa539, par); 1273*f7018c21STomi Valkeinen vga_out16(0x3c4, 0x0608, par); 1274*f7018c21STomi Valkeinen 1275*f7018c21STomi Valkeinen vgaHWProtect(par, 1); 1276*f7018c21STomi Valkeinen 1277*f7018c21STomi Valkeinen /* 1278*f7018c21STomi Valkeinen * Some Savage/MX and /IX systems go nuts when trying to exit the 1279*f7018c21STomi Valkeinen * server after WindowMaker has displayed a gradient background. I 1280*f7018c21STomi Valkeinen * haven't been able to find what causes it, but a non-destructive 1281*f7018c21STomi Valkeinen * switch to mode 3 here seems to eliminate the issue. 1282*f7018c21STomi Valkeinen */ 1283*f7018c21STomi Valkeinen 1284*f7018c21STomi Valkeinen VerticalRetraceWait(par); 1285*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x67, par); 1286*f7018c21STomi Valkeinen cr67 = vga_in8(0x3d5, par); 1287*f7018c21STomi Valkeinen vga_out8(0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */ 1288*f7018c21STomi Valkeinen 1289*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x23, par); 1290*f7018c21STomi Valkeinen vga_out8(0x3d5, 0x00, par); 1291*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x26, par); 1292*f7018c21STomi Valkeinen vga_out8(0x3d5, 0x00, par); 1293*f7018c21STomi Valkeinen 1294*f7018c21STomi Valkeinen /* restore extended regs */ 1295*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 1296*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR66, par); 1297*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 1298*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR3A, par); 1299*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x31, par); 1300*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR31, par); 1301*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x32, par); 1302*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR32, par); 1303*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x58, par); 1304*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR58, par); 1305*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x53, par); 1306*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR53 & 0x7f, par); 1307*f7018c21STomi Valkeinen 1308*f7018c21STomi Valkeinen vga_out16(0x3c4, 0x0608, par); 1309*f7018c21STomi Valkeinen 1310*f7018c21STomi Valkeinen /* Restore DCLK registers. */ 1311*f7018c21STomi Valkeinen 1312*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0e, par); 1313*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR0E, par); 1314*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0f, par); 1315*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR0F, par); 1316*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x29, par); 1317*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR29, par); 1318*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x15, par); 1319*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR15, par); 1320*f7018c21STomi Valkeinen 1321*f7018c21STomi Valkeinen /* Restore flat panel expansion registers. */ 1322*f7018c21STomi Valkeinen if (par->chip == S3_SAVAGE_MX) { 1323*f7018c21STomi Valkeinen int i; 1324*f7018c21STomi Valkeinen 1325*f7018c21STomi Valkeinen for (i = 0; i < 8; i++) { 1326*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x54+i, par); 1327*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR54[i], par); 1328*f7018c21STomi Valkeinen } 1329*f7018c21STomi Valkeinen } 1330*f7018c21STomi Valkeinen 1331*f7018c21STomi Valkeinen vgaHWRestore (par, reg); 1332*f7018c21STomi Valkeinen 1333*f7018c21STomi Valkeinen /* extended mode timing registers */ 1334*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x53, par); 1335*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR53, par); 1336*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x5d, par); 1337*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR5D, par); 1338*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x5e, par); 1339*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR5E, par); 1340*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3b, par); 1341*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR3B, par); 1342*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3c, par); 1343*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR3C, par); 1344*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x43, par); 1345*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR43, par); 1346*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x65, par); 1347*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR65, par); 1348*f7018c21STomi Valkeinen 1349*f7018c21STomi Valkeinen /* restore the desired video mode with cr67 */ 1350*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x67, par); 1351*f7018c21STomi Valkeinen /* following part not present in X11 driver */ 1352*f7018c21STomi Valkeinen cr67 = vga_in8(0x3d5, par) & 0xf; 1353*f7018c21STomi Valkeinen vga_out8(0x3d5, 0x50 | cr67, par); 1354*f7018c21STomi Valkeinen mdelay(10); 1355*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x67, par); 1356*f7018c21STomi Valkeinen /* end of part */ 1357*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR67 & ~0x0c, par); 1358*f7018c21STomi Valkeinen 1359*f7018c21STomi Valkeinen /* other mode timing and extended regs */ 1360*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x34, par); 1361*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR34, par); 1362*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 1363*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR40, par); 1364*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x42, par); 1365*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR42, par); 1366*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x45, par); 1367*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR45, par); 1368*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x50, par); 1369*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR50, par); 1370*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x51, par); 1371*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR51, par); 1372*f7018c21STomi Valkeinen 1373*f7018c21STomi Valkeinen /* memory timings */ 1374*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x36, par); 1375*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR36, par); 1376*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x60, par); 1377*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR60, par); 1378*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x68, par); 1379*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR68, par); 1380*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x69, par); 1381*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR69, par); 1382*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x6f, par); 1383*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR6F, par); 1384*f7018c21STomi Valkeinen 1385*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x33, par); 1386*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR33, par); 1387*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x86, par); 1388*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR86, par); 1389*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x88, par); 1390*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR88, par); 1391*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x90, par); 1392*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR90, par); 1393*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x91, par); 1394*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR91, par); 1395*f7018c21STomi Valkeinen 1396*f7018c21STomi Valkeinen if (par->chip == S3_SAVAGE4) { 1397*f7018c21STomi Valkeinen vga_out8(0x3d4, 0xb0, par); 1398*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CRB0, par); 1399*f7018c21STomi Valkeinen } 1400*f7018c21STomi Valkeinen 1401*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x32, par); 1402*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR32, par); 1403*f7018c21STomi Valkeinen 1404*f7018c21STomi Valkeinen /* unlock extended seq regs */ 1405*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x08, par); 1406*f7018c21STomi Valkeinen vga_out8(0x3c5, 0x06, par); 1407*f7018c21STomi Valkeinen 1408*f7018c21STomi Valkeinen /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates 1409*f7018c21STomi Valkeinen * that we should leave the default SR10 and SR11 values there. 1410*f7018c21STomi Valkeinen */ 1411*f7018c21STomi Valkeinen if (reg->SR10 != 255) { 1412*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x10, par); 1413*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR10, par); 1414*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x11, par); 1415*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR11, par); 1416*f7018c21STomi Valkeinen } 1417*f7018c21STomi Valkeinen 1418*f7018c21STomi Valkeinen /* restore extended seq regs for dclk */ 1419*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0e, par); 1420*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR0E, par); 1421*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0f, par); 1422*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR0F, par); 1423*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x12, par); 1424*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR12, par); 1425*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x13, par); 1426*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR13, par); 1427*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x29, par); 1428*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR29, par); 1429*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x18, par); 1430*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR18, par); 1431*f7018c21STomi Valkeinen 1432*f7018c21STomi Valkeinen /* load new m, n pll values for dclk & mclk */ 1433*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x15, par); 1434*f7018c21STomi Valkeinen tmp = vga_in8(0x3c5, par) & ~0x21; 1435*f7018c21STomi Valkeinen 1436*f7018c21STomi Valkeinen vga_out8(0x3c5, tmp | 0x03, par); 1437*f7018c21STomi Valkeinen vga_out8(0x3c5, tmp | 0x23, par); 1438*f7018c21STomi Valkeinen vga_out8(0x3c5, tmp | 0x03, par); 1439*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR15, par); 1440*f7018c21STomi Valkeinen udelay(100); 1441*f7018c21STomi Valkeinen 1442*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x30, par); 1443*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR30, par); 1444*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x08, par); 1445*f7018c21STomi Valkeinen vga_out8(0x3c5, reg->SR08, par); 1446*f7018c21STomi Valkeinen 1447*f7018c21STomi Valkeinen /* now write out cr67 in full, possibly starting STREAMS */ 1448*f7018c21STomi Valkeinen VerticalRetraceWait(par); 1449*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x67, par); 1450*f7018c21STomi Valkeinen vga_out8(0x3d5, reg->CR67, par); 1451*f7018c21STomi Valkeinen 1452*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 1453*f7018c21STomi Valkeinen cr66 = vga_in8(0x3d5, par); 1454*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66 | 0x80, par); 1455*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 1456*f7018c21STomi Valkeinen cr3a = vga_in8(0x3d5, par); 1457*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a | 0x80, par); 1458*f7018c21STomi Valkeinen 1459*f7018c21STomi Valkeinen if (par->chip != S3_SAVAGE_MX) { 1460*f7018c21STomi Valkeinen VerticalRetraceWait(par); 1461*f7018c21STomi Valkeinen savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par); 1462*f7018c21STomi Valkeinen par->SavageWaitIdle(par); 1463*f7018c21STomi Valkeinen savage_out32(MIU_CONTROL_REG, reg->MMPR1, par); 1464*f7018c21STomi Valkeinen par->SavageWaitIdle(par); 1465*f7018c21STomi Valkeinen savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par); 1466*f7018c21STomi Valkeinen par->SavageWaitIdle(par); 1467*f7018c21STomi Valkeinen savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par); 1468*f7018c21STomi Valkeinen } 1469*f7018c21STomi Valkeinen 1470*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 1471*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66, par); 1472*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3a, par); 1473*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3a, par); 1474*f7018c21STomi Valkeinen 1475*f7018c21STomi Valkeinen SavageSetup2DEngine(par); 1476*f7018c21STomi Valkeinen vgaHWProtect(par, 0); 1477*f7018c21STomi Valkeinen } 1478*f7018c21STomi Valkeinen 1479*f7018c21STomi Valkeinen static void savagefb_update_start(struct savagefb_par *par, int base) 1480*f7018c21STomi Valkeinen { 1481*f7018c21STomi Valkeinen /* program the start address registers */ 1482*f7018c21STomi Valkeinen vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par); 1483*f7018c21STomi Valkeinen vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par); 1484*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x69, par); 1485*f7018c21STomi Valkeinen vga_out8(0x3d5, (base & 0x7f0000) >> 16, par); 1486*f7018c21STomi Valkeinen } 1487*f7018c21STomi Valkeinen 1488*f7018c21STomi Valkeinen 1489*f7018c21STomi Valkeinen static void savagefb_set_fix(struct fb_info *info) 1490*f7018c21STomi Valkeinen { 1491*f7018c21STomi Valkeinen info->fix.line_length = info->var.xres_virtual * 1492*f7018c21STomi Valkeinen info->var.bits_per_pixel / 8; 1493*f7018c21STomi Valkeinen 1494*f7018c21STomi Valkeinen if (info->var.bits_per_pixel == 8) { 1495*f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 1496*f7018c21STomi Valkeinen info->fix.xpanstep = 4; 1497*f7018c21STomi Valkeinen } else { 1498*f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_TRUECOLOR; 1499*f7018c21STomi Valkeinen info->fix.xpanstep = 2; 1500*f7018c21STomi Valkeinen } 1501*f7018c21STomi Valkeinen 1502*f7018c21STomi Valkeinen } 1503*f7018c21STomi Valkeinen 1504*f7018c21STomi Valkeinen static int savagefb_set_par(struct fb_info *info) 1505*f7018c21STomi Valkeinen { 1506*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1507*f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var; 1508*f7018c21STomi Valkeinen int err; 1509*f7018c21STomi Valkeinen 1510*f7018c21STomi Valkeinen DBG("savagefb_set_par"); 1511*f7018c21STomi Valkeinen err = savagefb_decode_var(var, par, &par->state); 1512*f7018c21STomi Valkeinen if (err) 1513*f7018c21STomi Valkeinen return err; 1514*f7018c21STomi Valkeinen 1515*f7018c21STomi Valkeinen if (par->dacSpeedBpp <= 0) { 1516*f7018c21STomi Valkeinen if (var->bits_per_pixel > 24) 1517*f7018c21STomi Valkeinen par->dacSpeedBpp = par->clock[3]; 1518*f7018c21STomi Valkeinen else if (var->bits_per_pixel >= 24) 1519*f7018c21STomi Valkeinen par->dacSpeedBpp = par->clock[2]; 1520*f7018c21STomi Valkeinen else if ((var->bits_per_pixel > 8) && (var->bits_per_pixel < 24)) 1521*f7018c21STomi Valkeinen par->dacSpeedBpp = par->clock[1]; 1522*f7018c21STomi Valkeinen else if (var->bits_per_pixel <= 8) 1523*f7018c21STomi Valkeinen par->dacSpeedBpp = par->clock[0]; 1524*f7018c21STomi Valkeinen } 1525*f7018c21STomi Valkeinen 1526*f7018c21STomi Valkeinen /* Set ramdac limits */ 1527*f7018c21STomi Valkeinen par->maxClock = par->dacSpeedBpp; 1528*f7018c21STomi Valkeinen par->minClock = 10000; 1529*f7018c21STomi Valkeinen 1530*f7018c21STomi Valkeinen savagefb_set_par_int(par, &par->state); 1531*f7018c21STomi Valkeinen fb_set_cmap(&info->cmap, info); 1532*f7018c21STomi Valkeinen savagefb_set_fix(info); 1533*f7018c21STomi Valkeinen savagefb_set_clip(info); 1534*f7018c21STomi Valkeinen 1535*f7018c21STomi Valkeinen SavagePrintRegs(par); 1536*f7018c21STomi Valkeinen return 0; 1537*f7018c21STomi Valkeinen } 1538*f7018c21STomi Valkeinen 1539*f7018c21STomi Valkeinen /* 1540*f7018c21STomi Valkeinen * Pan or Wrap the Display 1541*f7018c21STomi Valkeinen */ 1542*f7018c21STomi Valkeinen static int savagefb_pan_display(struct fb_var_screeninfo *var, 1543*f7018c21STomi Valkeinen struct fb_info *info) 1544*f7018c21STomi Valkeinen { 1545*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1546*f7018c21STomi Valkeinen int base; 1547*f7018c21STomi Valkeinen 1548*f7018c21STomi Valkeinen base = (var->yoffset * info->fix.line_length 1549*f7018c21STomi Valkeinen + (var->xoffset & ~1) * ((info->var.bits_per_pixel+7) / 8)) >> 2; 1550*f7018c21STomi Valkeinen 1551*f7018c21STomi Valkeinen savagefb_update_start(par, base); 1552*f7018c21STomi Valkeinen return 0; 1553*f7018c21STomi Valkeinen } 1554*f7018c21STomi Valkeinen 1555*f7018c21STomi Valkeinen static int savagefb_blank(int blank, struct fb_info *info) 1556*f7018c21STomi Valkeinen { 1557*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1558*f7018c21STomi Valkeinen u8 sr8 = 0, srd = 0; 1559*f7018c21STomi Valkeinen 1560*f7018c21STomi Valkeinen if (par->display_type == DISP_CRT) { 1561*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x08, par); 1562*f7018c21STomi Valkeinen sr8 = vga_in8(0x3c5, par); 1563*f7018c21STomi Valkeinen sr8 |= 0x06; 1564*f7018c21STomi Valkeinen vga_out8(0x3c5, sr8, par); 1565*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0d, par); 1566*f7018c21STomi Valkeinen srd = vga_in8(0x3c5, par); 1567*f7018c21STomi Valkeinen srd &= 0x50; 1568*f7018c21STomi Valkeinen 1569*f7018c21STomi Valkeinen switch (blank) { 1570*f7018c21STomi Valkeinen case FB_BLANK_UNBLANK: 1571*f7018c21STomi Valkeinen case FB_BLANK_NORMAL: 1572*f7018c21STomi Valkeinen break; 1573*f7018c21STomi Valkeinen case FB_BLANK_VSYNC_SUSPEND: 1574*f7018c21STomi Valkeinen srd |= 0x10; 1575*f7018c21STomi Valkeinen break; 1576*f7018c21STomi Valkeinen case FB_BLANK_HSYNC_SUSPEND: 1577*f7018c21STomi Valkeinen srd |= 0x40; 1578*f7018c21STomi Valkeinen break; 1579*f7018c21STomi Valkeinen case FB_BLANK_POWERDOWN: 1580*f7018c21STomi Valkeinen srd |= 0x50; 1581*f7018c21STomi Valkeinen break; 1582*f7018c21STomi Valkeinen } 1583*f7018c21STomi Valkeinen 1584*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x0d, par); 1585*f7018c21STomi Valkeinen vga_out8(0x3c5, srd, par); 1586*f7018c21STomi Valkeinen } 1587*f7018c21STomi Valkeinen 1588*f7018c21STomi Valkeinen if (par->display_type == DISP_LCD || 1589*f7018c21STomi Valkeinen par->display_type == DISP_DFP) { 1590*f7018c21STomi Valkeinen switch(blank) { 1591*f7018c21STomi Valkeinen case FB_BLANK_UNBLANK: 1592*f7018c21STomi Valkeinen case FB_BLANK_NORMAL: 1593*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */ 1594*f7018c21STomi Valkeinen vga_out8(0x3c5, vga_in8(0x3c5, par) | 0x10, par); 1595*f7018c21STomi Valkeinen break; 1596*f7018c21STomi Valkeinen case FB_BLANK_VSYNC_SUSPEND: 1597*f7018c21STomi Valkeinen case FB_BLANK_HSYNC_SUSPEND: 1598*f7018c21STomi Valkeinen case FB_BLANK_POWERDOWN: 1599*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */ 1600*f7018c21STomi Valkeinen vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x10, par); 1601*f7018c21STomi Valkeinen break; 1602*f7018c21STomi Valkeinen } 1603*f7018c21STomi Valkeinen } 1604*f7018c21STomi Valkeinen 1605*f7018c21STomi Valkeinen return (blank == FB_BLANK_NORMAL) ? 1 : 0; 1606*f7018c21STomi Valkeinen } 1607*f7018c21STomi Valkeinen 1608*f7018c21STomi Valkeinen static int savagefb_open(struct fb_info *info, int user) 1609*f7018c21STomi Valkeinen { 1610*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1611*f7018c21STomi Valkeinen 1612*f7018c21STomi Valkeinen mutex_lock(&par->open_lock); 1613*f7018c21STomi Valkeinen 1614*f7018c21STomi Valkeinen if (!par->open_count) { 1615*f7018c21STomi Valkeinen memset(&par->vgastate, 0, sizeof(par->vgastate)); 1616*f7018c21STomi Valkeinen par->vgastate.flags = VGA_SAVE_CMAP | VGA_SAVE_FONTS | 1617*f7018c21STomi Valkeinen VGA_SAVE_MODE; 1618*f7018c21STomi Valkeinen par->vgastate.vgabase = par->mmio.vbase + 0x8000; 1619*f7018c21STomi Valkeinen save_vga(&par->vgastate); 1620*f7018c21STomi Valkeinen savage_get_default_par(par, &par->initial); 1621*f7018c21STomi Valkeinen } 1622*f7018c21STomi Valkeinen 1623*f7018c21STomi Valkeinen par->open_count++; 1624*f7018c21STomi Valkeinen mutex_unlock(&par->open_lock); 1625*f7018c21STomi Valkeinen return 0; 1626*f7018c21STomi Valkeinen } 1627*f7018c21STomi Valkeinen 1628*f7018c21STomi Valkeinen static int savagefb_release(struct fb_info *info, int user) 1629*f7018c21STomi Valkeinen { 1630*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1631*f7018c21STomi Valkeinen 1632*f7018c21STomi Valkeinen mutex_lock(&par->open_lock); 1633*f7018c21STomi Valkeinen 1634*f7018c21STomi Valkeinen if (par->open_count == 1) { 1635*f7018c21STomi Valkeinen savage_set_default_par(par, &par->initial); 1636*f7018c21STomi Valkeinen restore_vga(&par->vgastate); 1637*f7018c21STomi Valkeinen } 1638*f7018c21STomi Valkeinen 1639*f7018c21STomi Valkeinen par->open_count--; 1640*f7018c21STomi Valkeinen mutex_unlock(&par->open_lock); 1641*f7018c21STomi Valkeinen return 0; 1642*f7018c21STomi Valkeinen } 1643*f7018c21STomi Valkeinen 1644*f7018c21STomi Valkeinen static struct fb_ops savagefb_ops = { 1645*f7018c21STomi Valkeinen .owner = THIS_MODULE, 1646*f7018c21STomi Valkeinen .fb_open = savagefb_open, 1647*f7018c21STomi Valkeinen .fb_release = savagefb_release, 1648*f7018c21STomi Valkeinen .fb_check_var = savagefb_check_var, 1649*f7018c21STomi Valkeinen .fb_set_par = savagefb_set_par, 1650*f7018c21STomi Valkeinen .fb_setcolreg = savagefb_setcolreg, 1651*f7018c21STomi Valkeinen .fb_pan_display = savagefb_pan_display, 1652*f7018c21STomi Valkeinen .fb_blank = savagefb_blank, 1653*f7018c21STomi Valkeinen #if defined(CONFIG_FB_SAVAGE_ACCEL) 1654*f7018c21STomi Valkeinen .fb_fillrect = savagefb_fillrect, 1655*f7018c21STomi Valkeinen .fb_copyarea = savagefb_copyarea, 1656*f7018c21STomi Valkeinen .fb_imageblit = savagefb_imageblit, 1657*f7018c21STomi Valkeinen .fb_sync = savagefb_sync, 1658*f7018c21STomi Valkeinen #else 1659*f7018c21STomi Valkeinen .fb_fillrect = cfb_fillrect, 1660*f7018c21STomi Valkeinen .fb_copyarea = cfb_copyarea, 1661*f7018c21STomi Valkeinen .fb_imageblit = cfb_imageblit, 1662*f7018c21STomi Valkeinen #endif 1663*f7018c21STomi Valkeinen }; 1664*f7018c21STomi Valkeinen 1665*f7018c21STomi Valkeinen /* --------------------------------------------------------------------- */ 1666*f7018c21STomi Valkeinen 1667*f7018c21STomi Valkeinen static struct fb_var_screeninfo savagefb_var800x600x8 = { 1668*f7018c21STomi Valkeinen .accel_flags = FB_ACCELF_TEXT, 1669*f7018c21STomi Valkeinen .xres = 800, 1670*f7018c21STomi Valkeinen .yres = 600, 1671*f7018c21STomi Valkeinen .xres_virtual = 800, 1672*f7018c21STomi Valkeinen .yres_virtual = 600, 1673*f7018c21STomi Valkeinen .bits_per_pixel = 8, 1674*f7018c21STomi Valkeinen .pixclock = 25000, 1675*f7018c21STomi Valkeinen .left_margin = 88, 1676*f7018c21STomi Valkeinen .right_margin = 40, 1677*f7018c21STomi Valkeinen .upper_margin = 23, 1678*f7018c21STomi Valkeinen .lower_margin = 1, 1679*f7018c21STomi Valkeinen .hsync_len = 128, 1680*f7018c21STomi Valkeinen .vsync_len = 4, 1681*f7018c21STomi Valkeinen .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1682*f7018c21STomi Valkeinen .vmode = FB_VMODE_NONINTERLACED 1683*f7018c21STomi Valkeinen }; 1684*f7018c21STomi Valkeinen 1685*f7018c21STomi Valkeinen static void savage_enable_mmio(struct savagefb_par *par) 1686*f7018c21STomi Valkeinen { 1687*f7018c21STomi Valkeinen unsigned char val; 1688*f7018c21STomi Valkeinen 1689*f7018c21STomi Valkeinen DBG("savage_enable_mmio\n"); 1690*f7018c21STomi Valkeinen 1691*f7018c21STomi Valkeinen val = vga_in8(0x3c3, par); 1692*f7018c21STomi Valkeinen vga_out8(0x3c3, val | 0x01, par); 1693*f7018c21STomi Valkeinen val = vga_in8(0x3cc, par); 1694*f7018c21STomi Valkeinen vga_out8(0x3c2, val | 0x01, par); 1695*f7018c21STomi Valkeinen 1696*f7018c21STomi Valkeinen if (par->chip >= S3_SAVAGE4) { 1697*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 1698*f7018c21STomi Valkeinen val = vga_in8(0x3d5, par); 1699*f7018c21STomi Valkeinen vga_out8(0x3d5, val | 1, par); 1700*f7018c21STomi Valkeinen } 1701*f7018c21STomi Valkeinen } 1702*f7018c21STomi Valkeinen 1703*f7018c21STomi Valkeinen 1704*f7018c21STomi Valkeinen static void savage_disable_mmio(struct savagefb_par *par) 1705*f7018c21STomi Valkeinen { 1706*f7018c21STomi Valkeinen unsigned char val; 1707*f7018c21STomi Valkeinen 1708*f7018c21STomi Valkeinen DBG("savage_disable_mmio\n"); 1709*f7018c21STomi Valkeinen 1710*f7018c21STomi Valkeinen if (par->chip >= S3_SAVAGE4) { 1711*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 1712*f7018c21STomi Valkeinen val = vga_in8(0x3d5, par); 1713*f7018c21STomi Valkeinen vga_out8(0x3d5, val | 1, par); 1714*f7018c21STomi Valkeinen } 1715*f7018c21STomi Valkeinen } 1716*f7018c21STomi Valkeinen 1717*f7018c21STomi Valkeinen 1718*f7018c21STomi Valkeinen static int savage_map_mmio(struct fb_info *info) 1719*f7018c21STomi Valkeinen { 1720*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1721*f7018c21STomi Valkeinen DBG("savage_map_mmio"); 1722*f7018c21STomi Valkeinen 1723*f7018c21STomi Valkeinen if (S3_SAVAGE3D_SERIES(par->chip)) 1724*f7018c21STomi Valkeinen par->mmio.pbase = pci_resource_start(par->pcidev, 0) + 1725*f7018c21STomi Valkeinen SAVAGE_NEWMMIO_REGBASE_S3; 1726*f7018c21STomi Valkeinen else 1727*f7018c21STomi Valkeinen par->mmio.pbase = pci_resource_start(par->pcidev, 0) + 1728*f7018c21STomi Valkeinen SAVAGE_NEWMMIO_REGBASE_S4; 1729*f7018c21STomi Valkeinen 1730*f7018c21STomi Valkeinen par->mmio.len = SAVAGE_NEWMMIO_REGSIZE; 1731*f7018c21STomi Valkeinen 1732*f7018c21STomi Valkeinen par->mmio.vbase = ioremap(par->mmio.pbase, par->mmio.len); 1733*f7018c21STomi Valkeinen if (!par->mmio.vbase) { 1734*f7018c21STomi Valkeinen printk("savagefb: unable to map memory mapped IO\n"); 1735*f7018c21STomi Valkeinen return -ENOMEM; 1736*f7018c21STomi Valkeinen } else 1737*f7018c21STomi Valkeinen printk(KERN_INFO "savagefb: mapped io at %p\n", 1738*f7018c21STomi Valkeinen par->mmio.vbase); 1739*f7018c21STomi Valkeinen 1740*f7018c21STomi Valkeinen info->fix.mmio_start = par->mmio.pbase; 1741*f7018c21STomi Valkeinen info->fix.mmio_len = par->mmio.len; 1742*f7018c21STomi Valkeinen 1743*f7018c21STomi Valkeinen par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET); 1744*f7018c21STomi Valkeinen par->bci_ptr = 0; 1745*f7018c21STomi Valkeinen 1746*f7018c21STomi Valkeinen savage_enable_mmio(par); 1747*f7018c21STomi Valkeinen 1748*f7018c21STomi Valkeinen return 0; 1749*f7018c21STomi Valkeinen } 1750*f7018c21STomi Valkeinen 1751*f7018c21STomi Valkeinen static void savage_unmap_mmio(struct fb_info *info) 1752*f7018c21STomi Valkeinen { 1753*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1754*f7018c21STomi Valkeinen DBG("savage_unmap_mmio"); 1755*f7018c21STomi Valkeinen 1756*f7018c21STomi Valkeinen savage_disable_mmio(par); 1757*f7018c21STomi Valkeinen 1758*f7018c21STomi Valkeinen if (par->mmio.vbase) { 1759*f7018c21STomi Valkeinen iounmap(par->mmio.vbase); 1760*f7018c21STomi Valkeinen par->mmio.vbase = NULL; 1761*f7018c21STomi Valkeinen } 1762*f7018c21STomi Valkeinen } 1763*f7018c21STomi Valkeinen 1764*f7018c21STomi Valkeinen static int savage_map_video(struct fb_info *info, int video_len) 1765*f7018c21STomi Valkeinen { 1766*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1767*f7018c21STomi Valkeinen int resource; 1768*f7018c21STomi Valkeinen 1769*f7018c21STomi Valkeinen DBG("savage_map_video"); 1770*f7018c21STomi Valkeinen 1771*f7018c21STomi Valkeinen if (S3_SAVAGE3D_SERIES(par->chip)) 1772*f7018c21STomi Valkeinen resource = 0; 1773*f7018c21STomi Valkeinen else 1774*f7018c21STomi Valkeinen resource = 1; 1775*f7018c21STomi Valkeinen 1776*f7018c21STomi Valkeinen par->video.pbase = pci_resource_start(par->pcidev, resource); 1777*f7018c21STomi Valkeinen par->video.len = video_len; 1778*f7018c21STomi Valkeinen par->video.vbase = ioremap(par->video.pbase, par->video.len); 1779*f7018c21STomi Valkeinen 1780*f7018c21STomi Valkeinen if (!par->video.vbase) { 1781*f7018c21STomi Valkeinen printk("savagefb: unable to map screen memory\n"); 1782*f7018c21STomi Valkeinen return -ENOMEM; 1783*f7018c21STomi Valkeinen } else 1784*f7018c21STomi Valkeinen printk(KERN_INFO "savagefb: mapped framebuffer at %p, " 1785*f7018c21STomi Valkeinen "pbase == %x\n", par->video.vbase, par->video.pbase); 1786*f7018c21STomi Valkeinen 1787*f7018c21STomi Valkeinen info->fix.smem_start = par->video.pbase; 1788*f7018c21STomi Valkeinen info->fix.smem_len = par->video.len - par->cob_size; 1789*f7018c21STomi Valkeinen info->screen_base = par->video.vbase; 1790*f7018c21STomi Valkeinen 1791*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 1792*f7018c21STomi Valkeinen par->video.mtrr = mtrr_add(par->video.pbase, video_len, 1793*f7018c21STomi Valkeinen MTRR_TYPE_WRCOMB, 1); 1794*f7018c21STomi Valkeinen #endif 1795*f7018c21STomi Valkeinen 1796*f7018c21STomi Valkeinen /* Clear framebuffer, it's all white in memory after boot */ 1797*f7018c21STomi Valkeinen memset_io(par->video.vbase, 0, par->video.len); 1798*f7018c21STomi Valkeinen 1799*f7018c21STomi Valkeinen return 0; 1800*f7018c21STomi Valkeinen } 1801*f7018c21STomi Valkeinen 1802*f7018c21STomi Valkeinen static void savage_unmap_video(struct fb_info *info) 1803*f7018c21STomi Valkeinen { 1804*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 1805*f7018c21STomi Valkeinen 1806*f7018c21STomi Valkeinen DBG("savage_unmap_video"); 1807*f7018c21STomi Valkeinen 1808*f7018c21STomi Valkeinen if (par->video.vbase) { 1809*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 1810*f7018c21STomi Valkeinen mtrr_del(par->video.mtrr, par->video.pbase, par->video.len); 1811*f7018c21STomi Valkeinen #endif 1812*f7018c21STomi Valkeinen 1813*f7018c21STomi Valkeinen iounmap(par->video.vbase); 1814*f7018c21STomi Valkeinen par->video.vbase = NULL; 1815*f7018c21STomi Valkeinen info->screen_base = NULL; 1816*f7018c21STomi Valkeinen } 1817*f7018c21STomi Valkeinen } 1818*f7018c21STomi Valkeinen 1819*f7018c21STomi Valkeinen static int savage_init_hw(struct savagefb_par *par) 1820*f7018c21STomi Valkeinen { 1821*f7018c21STomi Valkeinen unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp; 1822*f7018c21STomi Valkeinen 1823*f7018c21STomi Valkeinen static unsigned char RamSavage3D[] = { 8, 4, 4, 2 }; 1824*f7018c21STomi Valkeinen static unsigned char RamSavage4[] = { 2, 4, 8, 12, 16, 32, 64, 32 }; 1825*f7018c21STomi Valkeinen static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 }; 1826*f7018c21STomi Valkeinen static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 }; 1827*f7018c21STomi Valkeinen int videoRam, videoRambytes, dvi; 1828*f7018c21STomi Valkeinen 1829*f7018c21STomi Valkeinen DBG("savage_init_hw"); 1830*f7018c21STomi Valkeinen 1831*f7018c21STomi Valkeinen /* unprotect CRTC[0-7] */ 1832*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x11, par); 1833*f7018c21STomi Valkeinen tmp = vga_in8(0x3d5, par); 1834*f7018c21STomi Valkeinen vga_out8(0x3d5, tmp & 0x7f, par); 1835*f7018c21STomi Valkeinen 1836*f7018c21STomi Valkeinen /* unlock extended regs */ 1837*f7018c21STomi Valkeinen vga_out16(0x3d4, 0x4838, par); 1838*f7018c21STomi Valkeinen vga_out16(0x3d4, 0xa039, par); 1839*f7018c21STomi Valkeinen vga_out16(0x3c4, 0x0608, par); 1840*f7018c21STomi Valkeinen 1841*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x40, par); 1842*f7018c21STomi Valkeinen tmp = vga_in8(0x3d5, par); 1843*f7018c21STomi Valkeinen vga_out8(0x3d5, tmp & ~0x01, par); 1844*f7018c21STomi Valkeinen 1845*f7018c21STomi Valkeinen /* unlock sys regs */ 1846*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x38, par); 1847*f7018c21STomi Valkeinen vga_out8(0x3d5, 0x48, par); 1848*f7018c21STomi Valkeinen 1849*f7018c21STomi Valkeinen /* Unlock system registers. */ 1850*f7018c21STomi Valkeinen vga_out16(0x3d4, 0x4838, par); 1851*f7018c21STomi Valkeinen 1852*f7018c21STomi Valkeinen /* Next go on to detect amount of installed ram */ 1853*f7018c21STomi Valkeinen 1854*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x36, par); /* for register CR36 (CONFG_REG1), */ 1855*f7018c21STomi Valkeinen config1 = vga_in8(0x3d5, par); /* get amount of vram installed */ 1856*f7018c21STomi Valkeinen 1857*f7018c21STomi Valkeinen /* Compute the amount of video memory and offscreen memory. */ 1858*f7018c21STomi Valkeinen 1859*f7018c21STomi Valkeinen switch (par->chip) { 1860*f7018c21STomi Valkeinen case S3_SAVAGE3D: 1861*f7018c21STomi Valkeinen videoRam = RamSavage3D[(config1 & 0xC0) >> 6 ] * 1024; 1862*f7018c21STomi Valkeinen break; 1863*f7018c21STomi Valkeinen 1864*f7018c21STomi Valkeinen case S3_SAVAGE4: 1865*f7018c21STomi Valkeinen /* 1866*f7018c21STomi Valkeinen * The Savage4 has one ugly special case to consider. On 1867*f7018c21STomi Valkeinen * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB 1868*f7018c21STomi Valkeinen * when it really means 8MB. Why do it the same when you 1869*f7018c21STomi Valkeinen * can do it different... 1870*f7018c21STomi Valkeinen */ 1871*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x68, par); /* memory control 1 */ 1872*f7018c21STomi Valkeinen if ((vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6)) 1873*f7018c21STomi Valkeinen RamSavage4[1] = 8; 1874*f7018c21STomi Valkeinen 1875*f7018c21STomi Valkeinen /*FALLTHROUGH*/ 1876*f7018c21STomi Valkeinen 1877*f7018c21STomi Valkeinen case S3_SAVAGE2000: 1878*f7018c21STomi Valkeinen videoRam = RamSavage4[(config1 & 0xE0) >> 5] * 1024; 1879*f7018c21STomi Valkeinen break; 1880*f7018c21STomi Valkeinen 1881*f7018c21STomi Valkeinen case S3_SAVAGE_MX: 1882*f7018c21STomi Valkeinen case S3_SUPERSAVAGE: 1883*f7018c21STomi Valkeinen videoRam = RamSavageMX[(config1 & 0x0E) >> 1] * 1024; 1884*f7018c21STomi Valkeinen break; 1885*f7018c21STomi Valkeinen 1886*f7018c21STomi Valkeinen case S3_PROSAVAGE: 1887*f7018c21STomi Valkeinen case S3_PROSAVAGEDDR: 1888*f7018c21STomi Valkeinen case S3_TWISTER: 1889*f7018c21STomi Valkeinen videoRam = RamSavageNB[(config1 & 0xE0) >> 5] * 1024; 1890*f7018c21STomi Valkeinen break; 1891*f7018c21STomi Valkeinen 1892*f7018c21STomi Valkeinen default: 1893*f7018c21STomi Valkeinen /* How did we get here? */ 1894*f7018c21STomi Valkeinen videoRam = 0; 1895*f7018c21STomi Valkeinen break; 1896*f7018c21STomi Valkeinen } 1897*f7018c21STomi Valkeinen 1898*f7018c21STomi Valkeinen videoRambytes = videoRam * 1024; 1899*f7018c21STomi Valkeinen 1900*f7018c21STomi Valkeinen printk(KERN_INFO "savagefb: probed videoram: %dk\n", videoRam); 1901*f7018c21STomi Valkeinen 1902*f7018c21STomi Valkeinen /* reset graphics engine to avoid memory corruption */ 1903*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 1904*f7018c21STomi Valkeinen cr66 = vga_in8(0x3d5, par); 1905*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66 | 0x02, par); 1906*f7018c21STomi Valkeinen mdelay(10); 1907*f7018c21STomi Valkeinen 1908*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x66, par); 1909*f7018c21STomi Valkeinen vga_out8(0x3d5, cr66 & ~0x02, par); /* clear reset flag */ 1910*f7018c21STomi Valkeinen mdelay(10); 1911*f7018c21STomi Valkeinen 1912*f7018c21STomi Valkeinen 1913*f7018c21STomi Valkeinen /* 1914*f7018c21STomi Valkeinen * reset memory interface, 3D engine, AGP master, PCI master, 1915*f7018c21STomi Valkeinen * master engine unit, motion compensation/LPB 1916*f7018c21STomi Valkeinen */ 1917*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3f, par); 1918*f7018c21STomi Valkeinen cr3f = vga_in8(0x3d5, par); 1919*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3f | 0x08, par); 1920*f7018c21STomi Valkeinen mdelay(10); 1921*f7018c21STomi Valkeinen 1922*f7018c21STomi Valkeinen vga_out8(0x3d4, 0x3f, par); 1923*f7018c21STomi Valkeinen vga_out8(0x3d5, cr3f & ~0x08, par); /* clear reset flags */ 1924*f7018c21STomi Valkeinen mdelay(10); 1925*f7018c21STomi Valkeinen 1926*f7018c21STomi Valkeinen /* Savage ramdac speeds */ 1927*f7018c21STomi Valkeinen par->numClocks = 4; 1928*f7018c21STomi Valkeinen par->clock[0] = 250000; 1929*f7018c21STomi Valkeinen par->clock[1] = 250000; 1930*f7018c21STomi Valkeinen par->clock[2] = 220000; 1931*f7018c21STomi Valkeinen par->clock[3] = 220000; 1932*f7018c21STomi Valkeinen 1933*f7018c21STomi Valkeinen /* detect current mclk */ 1934*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x08, par); 1935*f7018c21STomi Valkeinen sr8 = vga_in8(0x3c5, par); 1936*f7018c21STomi Valkeinen vga_out8(0x3c5, 0x06, par); 1937*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x10, par); 1938*f7018c21STomi Valkeinen n = vga_in8(0x3c5, par); 1939*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x11, par); 1940*f7018c21STomi Valkeinen m = vga_in8(0x3c5, par); 1941*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x08, par); 1942*f7018c21STomi Valkeinen vga_out8(0x3c5, sr8, par); 1943*f7018c21STomi Valkeinen m &= 0x7f; 1944*f7018c21STomi Valkeinen n1 = n & 0x1f; 1945*f7018c21STomi Valkeinen n2 = (n >> 5) & 0x03; 1946*f7018c21STomi Valkeinen par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100; 1947*f7018c21STomi Valkeinen printk(KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n", 1948*f7018c21STomi Valkeinen par->MCLK); 1949*f7018c21STomi Valkeinen 1950*f7018c21STomi Valkeinen /* check for DVI/flat panel */ 1951*f7018c21STomi Valkeinen dvi = 0; 1952*f7018c21STomi Valkeinen 1953*f7018c21STomi Valkeinen if (par->chip == S3_SAVAGE4) { 1954*f7018c21STomi Valkeinen unsigned char sr30 = 0x00; 1955*f7018c21STomi Valkeinen 1956*f7018c21STomi Valkeinen vga_out8(0x3c4, 0x30, par); 1957*f7018c21STomi Valkeinen /* clear bit 1 */ 1958*f7018c21STomi Valkeinen vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x02, par); 1959*f7018c21STomi Valkeinen sr30 = vga_in8(0x3c5, par); 1960*f7018c21STomi Valkeinen if (sr30 & 0x02 /*0x04 */) { 1961*f7018c21STomi Valkeinen dvi = 1; 1962*f7018c21STomi Valkeinen printk("savagefb: Digital Flat Panel Detected\n"); 1963*f7018c21STomi Valkeinen } 1964*f7018c21STomi Valkeinen } 1965*f7018c21STomi Valkeinen 1966*f7018c21STomi Valkeinen if ((S3_SAVAGE_MOBILE_SERIES(par->chip) || 1967*f7018c21STomi Valkeinen S3_MOBILE_TWISTER_SERIES(par->chip)) && !par->crtonly) 1968*f7018c21STomi Valkeinen par->display_type = DISP_LCD; 1969*f7018c21STomi Valkeinen else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi)) 1970*f7018c21STomi Valkeinen par->display_type = DISP_DFP; 1971*f7018c21STomi Valkeinen else 1972*f7018c21STomi Valkeinen par->display_type = DISP_CRT; 1973*f7018c21STomi Valkeinen 1974*f7018c21STomi Valkeinen /* Check LCD panel parrmation */ 1975*f7018c21STomi Valkeinen 1976*f7018c21STomi Valkeinen if (par->display_type == DISP_LCD) { 1977*f7018c21STomi Valkeinen unsigned char cr6b = VGArCR(0x6b, par); 1978*f7018c21STomi Valkeinen 1979*f7018c21STomi Valkeinen int panelX = (VGArSEQ(0x61, par) + 1980*f7018c21STomi Valkeinen ((VGArSEQ(0x66, par) & 0x02) << 7) + 1) * 8; 1981*f7018c21STomi Valkeinen int panelY = (VGArSEQ(0x69, par) + 1982*f7018c21STomi Valkeinen ((VGArSEQ(0x6e, par) & 0x70) << 4) + 1); 1983*f7018c21STomi Valkeinen 1984*f7018c21STomi Valkeinen char * sTechnology = "Unknown"; 1985*f7018c21STomi Valkeinen 1986*f7018c21STomi Valkeinen /* OK, I admit it. I don't know how to limit the max dot clock 1987*f7018c21STomi Valkeinen * for LCD panels of various sizes. I thought I copied the 1988*f7018c21STomi Valkeinen * formula from the BIOS, but many users have parrmed me of 1989*f7018c21STomi Valkeinen * my folly. 1990*f7018c21STomi Valkeinen * 1991*f7018c21STomi Valkeinen * Instead, I'll abandon any attempt to automatically limit the 1992*f7018c21STomi Valkeinen * clock, and add an LCDClock option to XF86Config. Some day, 1993*f7018c21STomi Valkeinen * I should come back to this. 1994*f7018c21STomi Valkeinen */ 1995*f7018c21STomi Valkeinen 1996*f7018c21STomi Valkeinen enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */ 1997*f7018c21STomi Valkeinen ActiveCRT = 0x01, 1998*f7018c21STomi Valkeinen ActiveLCD = 0x02, 1999*f7018c21STomi Valkeinen ActiveTV = 0x04, 2000*f7018c21STomi Valkeinen ActiveCRT2 = 0x20, 2001*f7018c21STomi Valkeinen ActiveDUO = 0x80 2002*f7018c21STomi Valkeinen }; 2003*f7018c21STomi Valkeinen 2004*f7018c21STomi Valkeinen if ((VGArSEQ(0x39, par) & 0x03) == 0) { 2005*f7018c21STomi Valkeinen sTechnology = "TFT"; 2006*f7018c21STomi Valkeinen } else if ((VGArSEQ(0x30, par) & 0x01) == 0) { 2007*f7018c21STomi Valkeinen sTechnology = "DSTN"; 2008*f7018c21STomi Valkeinen } else { 2009*f7018c21STomi Valkeinen sTechnology = "STN"; 2010*f7018c21STomi Valkeinen } 2011*f7018c21STomi Valkeinen 2012*f7018c21STomi Valkeinen printk(KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n", 2013*f7018c21STomi Valkeinen panelX, panelY, sTechnology, 2014*f7018c21STomi Valkeinen cr6b & ActiveLCD ? "and active" : "but not active"); 2015*f7018c21STomi Valkeinen 2016*f7018c21STomi Valkeinen if (cr6b & ActiveLCD) { 2017*f7018c21STomi Valkeinen /* 2018*f7018c21STomi Valkeinen * If the LCD is active and panel expansion is enabled, 2019*f7018c21STomi Valkeinen * we probably want to kill the HW cursor. 2020*f7018c21STomi Valkeinen */ 2021*f7018c21STomi Valkeinen 2022*f7018c21STomi Valkeinen printk(KERN_INFO "savagefb: Limiting video mode to " 2023*f7018c21STomi Valkeinen "%dx%d\n", panelX, panelY); 2024*f7018c21STomi Valkeinen 2025*f7018c21STomi Valkeinen par->SavagePanelWidth = panelX; 2026*f7018c21STomi Valkeinen par->SavagePanelHeight = panelY; 2027*f7018c21STomi Valkeinen 2028*f7018c21STomi Valkeinen } else 2029*f7018c21STomi Valkeinen par->display_type = DISP_CRT; 2030*f7018c21STomi Valkeinen } 2031*f7018c21STomi Valkeinen 2032*f7018c21STomi Valkeinen savage_get_default_par(par, &par->state); 2033*f7018c21STomi Valkeinen par->save = par->state; 2034*f7018c21STomi Valkeinen 2035*f7018c21STomi Valkeinen if (S3_SAVAGE4_SERIES(par->chip)) { 2036*f7018c21STomi Valkeinen /* 2037*f7018c21STomi Valkeinen * The Savage4 and ProSavage have COB coherency bugs which 2038*f7018c21STomi Valkeinen * render the buffer useless. We disable it. 2039*f7018c21STomi Valkeinen */ 2040*f7018c21STomi Valkeinen par->cob_index = 2; 2041*f7018c21STomi Valkeinen par->cob_size = 0x8000 << par->cob_index; 2042*f7018c21STomi Valkeinen par->cob_offset = videoRambytes; 2043*f7018c21STomi Valkeinen } else { 2044*f7018c21STomi Valkeinen /* We use 128kB for the COB on all chips. */ 2045*f7018c21STomi Valkeinen 2046*f7018c21STomi Valkeinen par->cob_index = 7; 2047*f7018c21STomi Valkeinen par->cob_size = 0x400 << par->cob_index; 2048*f7018c21STomi Valkeinen par->cob_offset = videoRambytes - par->cob_size; 2049*f7018c21STomi Valkeinen } 2050*f7018c21STomi Valkeinen 2051*f7018c21STomi Valkeinen return videoRambytes; 2052*f7018c21STomi Valkeinen } 2053*f7018c21STomi Valkeinen 2054*f7018c21STomi Valkeinen static int savage_init_fb_info(struct fb_info *info, struct pci_dev *dev, 2055*f7018c21STomi Valkeinen const struct pci_device_id *id) 2056*f7018c21STomi Valkeinen { 2057*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 2058*f7018c21STomi Valkeinen int err = 0; 2059*f7018c21STomi Valkeinen 2060*f7018c21STomi Valkeinen par->pcidev = dev; 2061*f7018c21STomi Valkeinen 2062*f7018c21STomi Valkeinen info->fix.type = FB_TYPE_PACKED_PIXELS; 2063*f7018c21STomi Valkeinen info->fix.type_aux = 0; 2064*f7018c21STomi Valkeinen info->fix.ypanstep = 1; 2065*f7018c21STomi Valkeinen info->fix.ywrapstep = 0; 2066*f7018c21STomi Valkeinen info->fix.accel = id->driver_data; 2067*f7018c21STomi Valkeinen 2068*f7018c21STomi Valkeinen switch (info->fix.accel) { 2069*f7018c21STomi Valkeinen case FB_ACCEL_SUPERSAVAGE: 2070*f7018c21STomi Valkeinen par->chip = S3_SUPERSAVAGE; 2071*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "SuperSavage"); 2072*f7018c21STomi Valkeinen break; 2073*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE4: 2074*f7018c21STomi Valkeinen par->chip = S3_SAVAGE4; 2075*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage4"); 2076*f7018c21STomi Valkeinen break; 2077*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE3D: 2078*f7018c21STomi Valkeinen par->chip = S3_SAVAGE3D; 2079*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage3D"); 2080*f7018c21STomi Valkeinen break; 2081*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE3D_MV: 2082*f7018c21STomi Valkeinen par->chip = S3_SAVAGE3D; 2083*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage3D-MV"); 2084*f7018c21STomi Valkeinen break; 2085*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE2000: 2086*f7018c21STomi Valkeinen par->chip = S3_SAVAGE2000; 2087*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage2000"); 2088*f7018c21STomi Valkeinen break; 2089*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE_MX_MV: 2090*f7018c21STomi Valkeinen par->chip = S3_SAVAGE_MX; 2091*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage/MX-MV"); 2092*f7018c21STomi Valkeinen break; 2093*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE_MX: 2094*f7018c21STomi Valkeinen par->chip = S3_SAVAGE_MX; 2095*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage/MX"); 2096*f7018c21STomi Valkeinen break; 2097*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE_IX_MV: 2098*f7018c21STomi Valkeinen par->chip = S3_SAVAGE_MX; 2099*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage/IX-MV"); 2100*f7018c21STomi Valkeinen break; 2101*f7018c21STomi Valkeinen case FB_ACCEL_SAVAGE_IX: 2102*f7018c21STomi Valkeinen par->chip = S3_SAVAGE_MX; 2103*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "Savage/IX"); 2104*f7018c21STomi Valkeinen break; 2105*f7018c21STomi Valkeinen case FB_ACCEL_PROSAVAGE_PM: 2106*f7018c21STomi Valkeinen par->chip = S3_PROSAVAGE; 2107*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "ProSavagePM"); 2108*f7018c21STomi Valkeinen break; 2109*f7018c21STomi Valkeinen case FB_ACCEL_PROSAVAGE_KM: 2110*f7018c21STomi Valkeinen par->chip = S3_PROSAVAGE; 2111*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "ProSavageKM"); 2112*f7018c21STomi Valkeinen break; 2113*f7018c21STomi Valkeinen case FB_ACCEL_S3TWISTER_P: 2114*f7018c21STomi Valkeinen par->chip = S3_TWISTER; 2115*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "TwisterP"); 2116*f7018c21STomi Valkeinen break; 2117*f7018c21STomi Valkeinen case FB_ACCEL_S3TWISTER_K: 2118*f7018c21STomi Valkeinen par->chip = S3_TWISTER; 2119*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "TwisterK"); 2120*f7018c21STomi Valkeinen break; 2121*f7018c21STomi Valkeinen case FB_ACCEL_PROSAVAGE_DDR: 2122*f7018c21STomi Valkeinen par->chip = S3_PROSAVAGEDDR; 2123*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "ProSavageDDR"); 2124*f7018c21STomi Valkeinen break; 2125*f7018c21STomi Valkeinen case FB_ACCEL_PROSAVAGE_DDRK: 2126*f7018c21STomi Valkeinen par->chip = S3_PROSAVAGEDDR; 2127*f7018c21STomi Valkeinen snprintf(info->fix.id, 16, "ProSavage8"); 2128*f7018c21STomi Valkeinen break; 2129*f7018c21STomi Valkeinen } 2130*f7018c21STomi Valkeinen 2131*f7018c21STomi Valkeinen if (S3_SAVAGE3D_SERIES(par->chip)) { 2132*f7018c21STomi Valkeinen par->SavageWaitIdle = savage3D_waitidle; 2133*f7018c21STomi Valkeinen par->SavageWaitFifo = savage3D_waitfifo; 2134*f7018c21STomi Valkeinen } else if (S3_SAVAGE4_SERIES(par->chip) || 2135*f7018c21STomi Valkeinen S3_SUPERSAVAGE == par->chip) { 2136*f7018c21STomi Valkeinen par->SavageWaitIdle = savage4_waitidle; 2137*f7018c21STomi Valkeinen par->SavageWaitFifo = savage4_waitfifo; 2138*f7018c21STomi Valkeinen } else { 2139*f7018c21STomi Valkeinen par->SavageWaitIdle = savage2000_waitidle; 2140*f7018c21STomi Valkeinen par->SavageWaitFifo = savage2000_waitfifo; 2141*f7018c21STomi Valkeinen } 2142*f7018c21STomi Valkeinen 2143*f7018c21STomi Valkeinen info->var.nonstd = 0; 2144*f7018c21STomi Valkeinen info->var.activate = FB_ACTIVATE_NOW; 2145*f7018c21STomi Valkeinen info->var.width = -1; 2146*f7018c21STomi Valkeinen info->var.height = -1; 2147*f7018c21STomi Valkeinen info->var.accel_flags = 0; 2148*f7018c21STomi Valkeinen 2149*f7018c21STomi Valkeinen info->fbops = &savagefb_ops; 2150*f7018c21STomi Valkeinen info->flags = FBINFO_DEFAULT | 2151*f7018c21STomi Valkeinen FBINFO_HWACCEL_YPAN | 2152*f7018c21STomi Valkeinen FBINFO_HWACCEL_XPAN; 2153*f7018c21STomi Valkeinen 2154*f7018c21STomi Valkeinen info->pseudo_palette = par->pseudo_palette; 2155*f7018c21STomi Valkeinen 2156*f7018c21STomi Valkeinen #if defined(CONFIG_FB_SAVAGE_ACCEL) 2157*f7018c21STomi Valkeinen /* FIFO size + padding for commands */ 2158*f7018c21STomi Valkeinen info->pixmap.addr = kcalloc(8, 1024, GFP_KERNEL); 2159*f7018c21STomi Valkeinen 2160*f7018c21STomi Valkeinen err = -ENOMEM; 2161*f7018c21STomi Valkeinen if (info->pixmap.addr) { 2162*f7018c21STomi Valkeinen info->pixmap.size = 8*1024; 2163*f7018c21STomi Valkeinen info->pixmap.scan_align = 4; 2164*f7018c21STomi Valkeinen info->pixmap.buf_align = 4; 2165*f7018c21STomi Valkeinen info->pixmap.access_align = 32; 2166*f7018c21STomi Valkeinen 2167*f7018c21STomi Valkeinen err = fb_alloc_cmap(&info->cmap, NR_PALETTE, 0); 2168*f7018c21STomi Valkeinen if (!err) 2169*f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_COPYAREA | 2170*f7018c21STomi Valkeinen FBINFO_HWACCEL_FILLRECT | 2171*f7018c21STomi Valkeinen FBINFO_HWACCEL_IMAGEBLIT; 2172*f7018c21STomi Valkeinen } 2173*f7018c21STomi Valkeinen #endif 2174*f7018c21STomi Valkeinen return err; 2175*f7018c21STomi Valkeinen } 2176*f7018c21STomi Valkeinen 2177*f7018c21STomi Valkeinen /* --------------------------------------------------------------------- */ 2178*f7018c21STomi Valkeinen 2179*f7018c21STomi Valkeinen static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id) 2180*f7018c21STomi Valkeinen { 2181*f7018c21STomi Valkeinen struct fb_info *info; 2182*f7018c21STomi Valkeinen struct savagefb_par *par; 2183*f7018c21STomi Valkeinen u_int h_sync, v_sync; 2184*f7018c21STomi Valkeinen int err, lpitch; 2185*f7018c21STomi Valkeinen int video_len; 2186*f7018c21STomi Valkeinen 2187*f7018c21STomi Valkeinen DBG("savagefb_probe"); 2188*f7018c21STomi Valkeinen 2189*f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev); 2190*f7018c21STomi Valkeinen if (!info) 2191*f7018c21STomi Valkeinen return -ENOMEM; 2192*f7018c21STomi Valkeinen par = info->par; 2193*f7018c21STomi Valkeinen mutex_init(&par->open_lock); 2194*f7018c21STomi Valkeinen err = pci_enable_device(dev); 2195*f7018c21STomi Valkeinen if (err) 2196*f7018c21STomi Valkeinen goto failed_enable; 2197*f7018c21STomi Valkeinen 2198*f7018c21STomi Valkeinen if ((err = pci_request_regions(dev, "savagefb"))) { 2199*f7018c21STomi Valkeinen printk(KERN_ERR "cannot request PCI regions\n"); 2200*f7018c21STomi Valkeinen goto failed_enable; 2201*f7018c21STomi Valkeinen } 2202*f7018c21STomi Valkeinen 2203*f7018c21STomi Valkeinen err = -ENOMEM; 2204*f7018c21STomi Valkeinen 2205*f7018c21STomi Valkeinen if ((err = savage_init_fb_info(info, dev, id))) 2206*f7018c21STomi Valkeinen goto failed_init; 2207*f7018c21STomi Valkeinen 2208*f7018c21STomi Valkeinen err = savage_map_mmio(info); 2209*f7018c21STomi Valkeinen if (err) 2210*f7018c21STomi Valkeinen goto failed_mmio; 2211*f7018c21STomi Valkeinen 2212*f7018c21STomi Valkeinen video_len = savage_init_hw(par); 2213*f7018c21STomi Valkeinen /* FIXME: can't be negative */ 2214*f7018c21STomi Valkeinen if (video_len < 0) { 2215*f7018c21STomi Valkeinen err = video_len; 2216*f7018c21STomi Valkeinen goto failed_mmio; 2217*f7018c21STomi Valkeinen } 2218*f7018c21STomi Valkeinen 2219*f7018c21STomi Valkeinen err = savage_map_video(info, video_len); 2220*f7018c21STomi Valkeinen if (err) 2221*f7018c21STomi Valkeinen goto failed_video; 2222*f7018c21STomi Valkeinen 2223*f7018c21STomi Valkeinen INIT_LIST_HEAD(&info->modelist); 2224*f7018c21STomi Valkeinen #if defined(CONFIG_FB_SAVAGE_I2C) 2225*f7018c21STomi Valkeinen savagefb_create_i2c_busses(info); 2226*f7018c21STomi Valkeinen savagefb_probe_i2c_connector(info, &par->edid); 2227*f7018c21STomi Valkeinen fb_edid_to_monspecs(par->edid, &info->monspecs); 2228*f7018c21STomi Valkeinen kfree(par->edid); 2229*f7018c21STomi Valkeinen fb_videomode_to_modelist(info->monspecs.modedb, 2230*f7018c21STomi Valkeinen info->monspecs.modedb_len, 2231*f7018c21STomi Valkeinen &info->modelist); 2232*f7018c21STomi Valkeinen #endif 2233*f7018c21STomi Valkeinen info->var = savagefb_var800x600x8; 2234*f7018c21STomi Valkeinen /* if a panel was detected, default to a CVT mode instead */ 2235*f7018c21STomi Valkeinen if (par->SavagePanelWidth) { 2236*f7018c21STomi Valkeinen struct fb_videomode cvt_mode; 2237*f7018c21STomi Valkeinen 2238*f7018c21STomi Valkeinen memset(&cvt_mode, 0, sizeof(cvt_mode)); 2239*f7018c21STomi Valkeinen cvt_mode.xres = par->SavagePanelWidth; 2240*f7018c21STomi Valkeinen cvt_mode.yres = par->SavagePanelHeight; 2241*f7018c21STomi Valkeinen cvt_mode.refresh = 60; 2242*f7018c21STomi Valkeinen /* FIXME: if we know there is only the panel 2243*f7018c21STomi Valkeinen * we can enable reduced blanking as well */ 2244*f7018c21STomi Valkeinen if (fb_find_mode_cvt(&cvt_mode, 0, 0)) 2245*f7018c21STomi Valkeinen printk(KERN_WARNING "No CVT mode found for panel\n"); 2246*f7018c21STomi Valkeinen else if (fb_find_mode(&info->var, info, NULL, NULL, 0, 2247*f7018c21STomi Valkeinen &cvt_mode, 0) != 3) 2248*f7018c21STomi Valkeinen info->var = savagefb_var800x600x8; 2249*f7018c21STomi Valkeinen } 2250*f7018c21STomi Valkeinen 2251*f7018c21STomi Valkeinen if (mode_option) { 2252*f7018c21STomi Valkeinen fb_find_mode(&info->var, info, mode_option, 2253*f7018c21STomi Valkeinen info->monspecs.modedb, info->monspecs.modedb_len, 2254*f7018c21STomi Valkeinen NULL, 8); 2255*f7018c21STomi Valkeinen } else if (info->monspecs.modedb != NULL) { 2256*f7018c21STomi Valkeinen const struct fb_videomode *mode; 2257*f7018c21STomi Valkeinen 2258*f7018c21STomi Valkeinen mode = fb_find_best_display(&info->monspecs, &info->modelist); 2259*f7018c21STomi Valkeinen savage_update_var(&info->var, mode); 2260*f7018c21STomi Valkeinen } 2261*f7018c21STomi Valkeinen 2262*f7018c21STomi Valkeinen /* maximize virtual vertical length */ 2263*f7018c21STomi Valkeinen lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3); 2264*f7018c21STomi Valkeinen info->var.yres_virtual = info->fix.smem_len/lpitch; 2265*f7018c21STomi Valkeinen 2266*f7018c21STomi Valkeinen if (info->var.yres_virtual < info->var.yres) { 2267*f7018c21STomi Valkeinen err = -ENOMEM; 2268*f7018c21STomi Valkeinen goto failed; 2269*f7018c21STomi Valkeinen } 2270*f7018c21STomi Valkeinen 2271*f7018c21STomi Valkeinen #if defined(CONFIG_FB_SAVAGE_ACCEL) 2272*f7018c21STomi Valkeinen /* 2273*f7018c21STomi Valkeinen * The clipping coordinates are masked with 0xFFF, so limit our 2274*f7018c21STomi Valkeinen * virtual resolutions to these sizes. 2275*f7018c21STomi Valkeinen */ 2276*f7018c21STomi Valkeinen if (info->var.yres_virtual > 0x1000) 2277*f7018c21STomi Valkeinen info->var.yres_virtual = 0x1000; 2278*f7018c21STomi Valkeinen 2279*f7018c21STomi Valkeinen if (info->var.xres_virtual > 0x1000) 2280*f7018c21STomi Valkeinen info->var.xres_virtual = 0x1000; 2281*f7018c21STomi Valkeinen #endif 2282*f7018c21STomi Valkeinen savagefb_check_var(&info->var, info); 2283*f7018c21STomi Valkeinen savagefb_set_fix(info); 2284*f7018c21STomi Valkeinen 2285*f7018c21STomi Valkeinen /* 2286*f7018c21STomi Valkeinen * Calculate the hsync and vsync frequencies. Note that 2287*f7018c21STomi Valkeinen * we split the 1e12 constant up so that we can preserve 2288*f7018c21STomi Valkeinen * the precision and fit the results into 32-bit registers. 2289*f7018c21STomi Valkeinen * (1953125000 * 512 = 1e12) 2290*f7018c21STomi Valkeinen */ 2291*f7018c21STomi Valkeinen h_sync = 1953125000 / info->var.pixclock; 2292*f7018c21STomi Valkeinen h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin + 2293*f7018c21STomi Valkeinen info->var.right_margin + 2294*f7018c21STomi Valkeinen info->var.hsync_len); 2295*f7018c21STomi Valkeinen v_sync = h_sync / (info->var.yres + info->var.upper_margin + 2296*f7018c21STomi Valkeinen info->var.lower_margin + info->var.vsync_len); 2297*f7018c21STomi Valkeinen 2298*f7018c21STomi Valkeinen printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": " 2299*f7018c21STomi Valkeinen "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n", 2300*f7018c21STomi Valkeinen info->fix.smem_len >> 10, 2301*f7018c21STomi Valkeinen info->var.xres, info->var.yres, 2302*f7018c21STomi Valkeinen h_sync / 1000, h_sync % 1000, v_sync); 2303*f7018c21STomi Valkeinen 2304*f7018c21STomi Valkeinen 2305*f7018c21STomi Valkeinen fb_destroy_modedb(info->monspecs.modedb); 2306*f7018c21STomi Valkeinen info->monspecs.modedb = NULL; 2307*f7018c21STomi Valkeinen 2308*f7018c21STomi Valkeinen err = register_framebuffer(info); 2309*f7018c21STomi Valkeinen if (err < 0) 2310*f7018c21STomi Valkeinen goto failed; 2311*f7018c21STomi Valkeinen 2312*f7018c21STomi Valkeinen printk(KERN_INFO "fb: S3 %s frame buffer device\n", 2313*f7018c21STomi Valkeinen info->fix.id); 2314*f7018c21STomi Valkeinen 2315*f7018c21STomi Valkeinen /* 2316*f7018c21STomi Valkeinen * Our driver data 2317*f7018c21STomi Valkeinen */ 2318*f7018c21STomi Valkeinen pci_set_drvdata(dev, info); 2319*f7018c21STomi Valkeinen 2320*f7018c21STomi Valkeinen return 0; 2321*f7018c21STomi Valkeinen 2322*f7018c21STomi Valkeinen failed: 2323*f7018c21STomi Valkeinen #ifdef CONFIG_FB_SAVAGE_I2C 2324*f7018c21STomi Valkeinen savagefb_delete_i2c_busses(info); 2325*f7018c21STomi Valkeinen #endif 2326*f7018c21STomi Valkeinen fb_alloc_cmap(&info->cmap, 0, 0); 2327*f7018c21STomi Valkeinen savage_unmap_video(info); 2328*f7018c21STomi Valkeinen failed_video: 2329*f7018c21STomi Valkeinen savage_unmap_mmio(info); 2330*f7018c21STomi Valkeinen failed_mmio: 2331*f7018c21STomi Valkeinen kfree(info->pixmap.addr); 2332*f7018c21STomi Valkeinen failed_init: 2333*f7018c21STomi Valkeinen pci_release_regions(dev); 2334*f7018c21STomi Valkeinen failed_enable: 2335*f7018c21STomi Valkeinen framebuffer_release(info); 2336*f7018c21STomi Valkeinen 2337*f7018c21STomi Valkeinen return err; 2338*f7018c21STomi Valkeinen } 2339*f7018c21STomi Valkeinen 2340*f7018c21STomi Valkeinen static void savagefb_remove(struct pci_dev *dev) 2341*f7018c21STomi Valkeinen { 2342*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(dev); 2343*f7018c21STomi Valkeinen 2344*f7018c21STomi Valkeinen DBG("savagefb_remove"); 2345*f7018c21STomi Valkeinen 2346*f7018c21STomi Valkeinen if (info) { 2347*f7018c21STomi Valkeinen /* 2348*f7018c21STomi Valkeinen * If unregister_framebuffer fails, then 2349*f7018c21STomi Valkeinen * we will be leaving hooks that could cause 2350*f7018c21STomi Valkeinen * oopsen laying around. 2351*f7018c21STomi Valkeinen */ 2352*f7018c21STomi Valkeinen if (unregister_framebuffer(info)) 2353*f7018c21STomi Valkeinen printk(KERN_WARNING "savagefb: danger danger! " 2354*f7018c21STomi Valkeinen "Oopsen imminent!\n"); 2355*f7018c21STomi Valkeinen 2356*f7018c21STomi Valkeinen #ifdef CONFIG_FB_SAVAGE_I2C 2357*f7018c21STomi Valkeinen savagefb_delete_i2c_busses(info); 2358*f7018c21STomi Valkeinen #endif 2359*f7018c21STomi Valkeinen fb_alloc_cmap(&info->cmap, 0, 0); 2360*f7018c21STomi Valkeinen savage_unmap_video(info); 2361*f7018c21STomi Valkeinen savage_unmap_mmio(info); 2362*f7018c21STomi Valkeinen kfree(info->pixmap.addr); 2363*f7018c21STomi Valkeinen pci_release_regions(dev); 2364*f7018c21STomi Valkeinen framebuffer_release(info); 2365*f7018c21STomi Valkeinen } 2366*f7018c21STomi Valkeinen } 2367*f7018c21STomi Valkeinen 2368*f7018c21STomi Valkeinen static int savagefb_suspend(struct pci_dev *dev, pm_message_t mesg) 2369*f7018c21STomi Valkeinen { 2370*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(dev); 2371*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 2372*f7018c21STomi Valkeinen 2373*f7018c21STomi Valkeinen DBG("savagefb_suspend"); 2374*f7018c21STomi Valkeinen 2375*f7018c21STomi Valkeinen if (mesg.event == PM_EVENT_PRETHAW) 2376*f7018c21STomi Valkeinen mesg.event = PM_EVENT_FREEZE; 2377*f7018c21STomi Valkeinen par->pm_state = mesg.event; 2378*f7018c21STomi Valkeinen dev->dev.power.power_state = mesg; 2379*f7018c21STomi Valkeinen 2380*f7018c21STomi Valkeinen /* 2381*f7018c21STomi Valkeinen * For PM_EVENT_FREEZE, do not power down so the console 2382*f7018c21STomi Valkeinen * can remain active. 2383*f7018c21STomi Valkeinen */ 2384*f7018c21STomi Valkeinen if (mesg.event == PM_EVENT_FREEZE) 2385*f7018c21STomi Valkeinen return 0; 2386*f7018c21STomi Valkeinen 2387*f7018c21STomi Valkeinen console_lock(); 2388*f7018c21STomi Valkeinen fb_set_suspend(info, 1); 2389*f7018c21STomi Valkeinen 2390*f7018c21STomi Valkeinen if (info->fbops->fb_sync) 2391*f7018c21STomi Valkeinen info->fbops->fb_sync(info); 2392*f7018c21STomi Valkeinen 2393*f7018c21STomi Valkeinen savagefb_blank(FB_BLANK_POWERDOWN, info); 2394*f7018c21STomi Valkeinen savage_set_default_par(par, &par->save); 2395*f7018c21STomi Valkeinen savage_disable_mmio(par); 2396*f7018c21STomi Valkeinen pci_save_state(dev); 2397*f7018c21STomi Valkeinen pci_disable_device(dev); 2398*f7018c21STomi Valkeinen pci_set_power_state(dev, pci_choose_state(dev, mesg)); 2399*f7018c21STomi Valkeinen console_unlock(); 2400*f7018c21STomi Valkeinen 2401*f7018c21STomi Valkeinen return 0; 2402*f7018c21STomi Valkeinen } 2403*f7018c21STomi Valkeinen 2404*f7018c21STomi Valkeinen static int savagefb_resume(struct pci_dev* dev) 2405*f7018c21STomi Valkeinen { 2406*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(dev); 2407*f7018c21STomi Valkeinen struct savagefb_par *par = info->par; 2408*f7018c21STomi Valkeinen int cur_state = par->pm_state; 2409*f7018c21STomi Valkeinen 2410*f7018c21STomi Valkeinen DBG("savage_resume"); 2411*f7018c21STomi Valkeinen 2412*f7018c21STomi Valkeinen par->pm_state = PM_EVENT_ON; 2413*f7018c21STomi Valkeinen 2414*f7018c21STomi Valkeinen /* 2415*f7018c21STomi Valkeinen * The adapter was not powered down coming back from a 2416*f7018c21STomi Valkeinen * PM_EVENT_FREEZE. 2417*f7018c21STomi Valkeinen */ 2418*f7018c21STomi Valkeinen if (cur_state == PM_EVENT_FREEZE) { 2419*f7018c21STomi Valkeinen pci_set_power_state(dev, PCI_D0); 2420*f7018c21STomi Valkeinen return 0; 2421*f7018c21STomi Valkeinen } 2422*f7018c21STomi Valkeinen 2423*f7018c21STomi Valkeinen console_lock(); 2424*f7018c21STomi Valkeinen 2425*f7018c21STomi Valkeinen pci_set_power_state(dev, PCI_D0); 2426*f7018c21STomi Valkeinen pci_restore_state(dev); 2427*f7018c21STomi Valkeinen 2428*f7018c21STomi Valkeinen if (pci_enable_device(dev)) 2429*f7018c21STomi Valkeinen DBG("err"); 2430*f7018c21STomi Valkeinen 2431*f7018c21STomi Valkeinen pci_set_master(dev); 2432*f7018c21STomi Valkeinen savage_enable_mmio(par); 2433*f7018c21STomi Valkeinen savage_init_hw(par); 2434*f7018c21STomi Valkeinen savagefb_set_par(info); 2435*f7018c21STomi Valkeinen fb_set_suspend(info, 0); 2436*f7018c21STomi Valkeinen savagefb_blank(FB_BLANK_UNBLANK, info); 2437*f7018c21STomi Valkeinen console_unlock(); 2438*f7018c21STomi Valkeinen 2439*f7018c21STomi Valkeinen return 0; 2440*f7018c21STomi Valkeinen } 2441*f7018c21STomi Valkeinen 2442*f7018c21STomi Valkeinen 2443*f7018c21STomi Valkeinen static struct pci_device_id savagefb_devices[] = { 2444*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128, 2445*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2446*f7018c21STomi Valkeinen 2447*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64, 2448*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2449*f7018c21STomi Valkeinen 2450*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C, 2451*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2452*f7018c21STomi Valkeinen 2453*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR, 2454*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2455*f7018c21STomi Valkeinen 2456*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR, 2457*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2458*f7018c21STomi Valkeinen 2459*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR, 2460*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2461*f7018c21STomi Valkeinen 2462*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR, 2463*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2464*f7018c21STomi Valkeinen 2465*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR, 2466*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2467*f7018c21STomi Valkeinen 2468*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR, 2469*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 2470*f7018c21STomi Valkeinen 2471*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4, 2472*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4}, 2473*f7018c21STomi Valkeinen 2474*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D, 2475*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D}, 2476*f7018c21STomi Valkeinen 2477*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV, 2478*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV}, 2479*f7018c21STomi Valkeinen 2480*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000, 2481*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000}, 2482*f7018c21STomi Valkeinen 2483*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV, 2484*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV}, 2485*f7018c21STomi Valkeinen 2486*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX, 2487*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX}, 2488*f7018c21STomi Valkeinen 2489*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV, 2490*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV}, 2491*f7018c21STomi Valkeinen 2492*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX, 2493*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX}, 2494*f7018c21STomi Valkeinen 2495*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM, 2496*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM}, 2497*f7018c21STomi Valkeinen 2498*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM, 2499*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM}, 2500*f7018c21STomi Valkeinen 2501*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P, 2502*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P}, 2503*f7018c21STomi Valkeinen 2504*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K, 2505*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K}, 2506*f7018c21STomi Valkeinen 2507*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR, 2508*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR}, 2509*f7018c21STomi Valkeinen 2510*f7018c21STomi Valkeinen {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK, 2511*f7018c21STomi Valkeinen PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK}, 2512*f7018c21STomi Valkeinen 2513*f7018c21STomi Valkeinen {0, 0, 0, 0, 0, 0, 0} 2514*f7018c21STomi Valkeinen }; 2515*f7018c21STomi Valkeinen 2516*f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, savagefb_devices); 2517*f7018c21STomi Valkeinen 2518*f7018c21STomi Valkeinen static struct pci_driver savagefb_driver = { 2519*f7018c21STomi Valkeinen .name = "savagefb", 2520*f7018c21STomi Valkeinen .id_table = savagefb_devices, 2521*f7018c21STomi Valkeinen .probe = savagefb_probe, 2522*f7018c21STomi Valkeinen .suspend = savagefb_suspend, 2523*f7018c21STomi Valkeinen .resume = savagefb_resume, 2524*f7018c21STomi Valkeinen .remove = savagefb_remove, 2525*f7018c21STomi Valkeinen }; 2526*f7018c21STomi Valkeinen 2527*f7018c21STomi Valkeinen /* **************************** exit-time only **************************** */ 2528*f7018c21STomi Valkeinen 2529*f7018c21STomi Valkeinen static void __exit savage_done(void) 2530*f7018c21STomi Valkeinen { 2531*f7018c21STomi Valkeinen DBG("savage_done"); 2532*f7018c21STomi Valkeinen pci_unregister_driver(&savagefb_driver); 2533*f7018c21STomi Valkeinen } 2534*f7018c21STomi Valkeinen 2535*f7018c21STomi Valkeinen 2536*f7018c21STomi Valkeinen /* ************************* init in-kernel code ************************** */ 2537*f7018c21STomi Valkeinen 2538*f7018c21STomi Valkeinen static int __init savagefb_setup(char *options) 2539*f7018c21STomi Valkeinen { 2540*f7018c21STomi Valkeinen #ifndef MODULE 2541*f7018c21STomi Valkeinen char *this_opt; 2542*f7018c21STomi Valkeinen 2543*f7018c21STomi Valkeinen if (!options || !*options) 2544*f7018c21STomi Valkeinen return 0; 2545*f7018c21STomi Valkeinen 2546*f7018c21STomi Valkeinen while ((this_opt = strsep(&options, ",")) != NULL) { 2547*f7018c21STomi Valkeinen mode_option = this_opt; 2548*f7018c21STomi Valkeinen } 2549*f7018c21STomi Valkeinen #endif /* !MODULE */ 2550*f7018c21STomi Valkeinen return 0; 2551*f7018c21STomi Valkeinen } 2552*f7018c21STomi Valkeinen 2553*f7018c21STomi Valkeinen static int __init savagefb_init(void) 2554*f7018c21STomi Valkeinen { 2555*f7018c21STomi Valkeinen char *option; 2556*f7018c21STomi Valkeinen 2557*f7018c21STomi Valkeinen DBG("savagefb_init"); 2558*f7018c21STomi Valkeinen 2559*f7018c21STomi Valkeinen if (fb_get_options("savagefb", &option)) 2560*f7018c21STomi Valkeinen return -ENODEV; 2561*f7018c21STomi Valkeinen 2562*f7018c21STomi Valkeinen savagefb_setup(option); 2563*f7018c21STomi Valkeinen return pci_register_driver(&savagefb_driver); 2564*f7018c21STomi Valkeinen 2565*f7018c21STomi Valkeinen } 2566*f7018c21STomi Valkeinen 2567*f7018c21STomi Valkeinen module_init(savagefb_init); 2568*f7018c21STomi Valkeinen module_exit(savage_done); 2569*f7018c21STomi Valkeinen 2570*f7018c21STomi Valkeinen module_param(mode_option, charp, 0); 2571*f7018c21STomi Valkeinen MODULE_PARM_DESC(mode_option, "Specify initial video mode"); 2572