162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/savagefb.c -- S3 Savage Framebuffer Driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2001-2002 Denis Oliver Kropp <dok@directfb.org> 562306a36Sopenharmony_ci * Sven Neumann <neo@directfb.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Card specific code is based on XFree86's savage driver. 962306a36Sopenharmony_ci * Framebuffer framework code is based on code of cyber2000fb and tdfxfb. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 1262306a36Sopenharmony_ci * Public License. See the file COPYING in the main directory of this 1362306a36Sopenharmony_ci * archive for more details. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * 0.4.0 (neo) 1662306a36Sopenharmony_ci * - hardware accelerated clear and move 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * 0.3.2 (dok) 1962306a36Sopenharmony_ci * - wait for vertical retrace before writing to cr67 2062306a36Sopenharmony_ci * at the beginning of savagefb_set_par 2162306a36Sopenharmony_ci * - use synchronization registers cr23 and cr26 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * 0.3.1 (dok) 2462306a36Sopenharmony_ci * - reset 3D engine 2562306a36Sopenharmony_ci * - don't return alpha bits for 32bit format 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * 0.3.0 (dok) 2862306a36Sopenharmony_ci * - added WaitIdle functions for all Savage types 2962306a36Sopenharmony_ci * - do WaitIdle before mode switching 3062306a36Sopenharmony_ci * - code cleanup 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * 0.2.0 (dok) 3362306a36Sopenharmony_ci * - first working version 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * TODO 3762306a36Sopenharmony_ci * - clock validations in decode_var 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * BUGS 4062306a36Sopenharmony_ci * - white margin on bootup 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <linux/aperture.h> 4562306a36Sopenharmony_ci#include <linux/module.h> 4662306a36Sopenharmony_ci#include <linux/kernel.h> 4762306a36Sopenharmony_ci#include <linux/errno.h> 4862306a36Sopenharmony_ci#include <linux/string.h> 4962306a36Sopenharmony_ci#include <linux/mm.h> 5062306a36Sopenharmony_ci#include <linux/slab.h> 5162306a36Sopenharmony_ci#include <linux/delay.h> 5262306a36Sopenharmony_ci#include <linux/fb.h> 5362306a36Sopenharmony_ci#include <linux/pci.h> 5462306a36Sopenharmony_ci#include <linux/init.h> 5562306a36Sopenharmony_ci#include <linux/console.h> 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#include <asm/io.h> 5862306a36Sopenharmony_ci#include <asm/irq.h> 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#include "savagefb.h" 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define SAVAGEFB_VERSION "0.4.0_2.6" 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic char *mode_option = NULL; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#ifdef MODULE 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciMODULE_AUTHOR("(c) 2001-2002 Denis Oliver Kropp <dok@directfb.org>"); 7362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 7462306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#endif 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void vgaHWSeqReset(struct savagefb_par *par, int start) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci if (start) 8462306a36Sopenharmony_ci VGAwSEQ(0x00, 0x01, par); /* Synchronous Reset */ 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci VGAwSEQ(0x00, 0x03, par); /* End Reset */ 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void vgaHWProtect(struct savagefb_par *par, int on) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci unsigned char tmp; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (on) { 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * Turn off screen and disable sequencer. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci tmp = VGArSEQ(0x01, par); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci vgaHWSeqReset(par, 1); /* start synchronous reset */ 10062306a36Sopenharmony_ci VGAwSEQ(0x01, tmp | 0x20, par);/* disable the display */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci VGAenablePalette(par); 10362306a36Sopenharmony_ci } else { 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * Reenable sequencer, then turn on screen. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci tmp = VGArSEQ(0x01, par); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci VGAwSEQ(0x01, tmp & ~0x20, par);/* reenable display */ 11162306a36Sopenharmony_ci vgaHWSeqReset(par, 0); /* clear synchronous reset */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci VGAdisablePalette(par); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void vgaHWRestore(struct savagefb_par *par, struct savage_reg *reg) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int i; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci VGAwMISC(reg->MiscOutReg, par); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 1; i < 5; i++) 12462306a36Sopenharmony_ci VGAwSEQ(i, reg->Sequencer[i], par); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or 12762306a36Sopenharmony_ci CRTC[17] */ 12862306a36Sopenharmony_ci VGAwCR(17, reg->CRTC[17] & ~0x80, par); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci for (i = 0; i < 25; i++) 13162306a36Sopenharmony_ci VGAwCR(i, reg->CRTC[i], par); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < 9; i++) 13462306a36Sopenharmony_ci VGAwGR(i, reg->Graphics[i], par); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci VGAenablePalette(par); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; i < 21; i++) 13962306a36Sopenharmony_ci VGAwATTR(i, reg->Attribute[i], par); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci VGAdisablePalette(par); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void vgaHWInit(struct fb_var_screeninfo *var, 14562306a36Sopenharmony_ci struct savagefb_par *par, 14662306a36Sopenharmony_ci struct xtimings *timings, 14762306a36Sopenharmony_ci struct savage_reg *reg) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci reg->MiscOutReg = 0x23; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT)) 15262306a36Sopenharmony_ci reg->MiscOutReg |= 0x40; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT)) 15562306a36Sopenharmony_ci reg->MiscOutReg |= 0x80; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * Time Sequencer 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci reg->Sequencer[0x00] = 0x00; 16162306a36Sopenharmony_ci reg->Sequencer[0x01] = 0x01; 16262306a36Sopenharmony_ci reg->Sequencer[0x02] = 0x0F; 16362306a36Sopenharmony_ci reg->Sequencer[0x03] = 0x00; /* Font select */ 16462306a36Sopenharmony_ci reg->Sequencer[0x04] = 0x0E; /* Misc */ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * CRTC Controller 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci reg->CRTC[0x00] = (timings->HTotal >> 3) - 5; 17062306a36Sopenharmony_ci reg->CRTC[0x01] = (timings->HDisplay >> 3) - 1; 17162306a36Sopenharmony_ci reg->CRTC[0x02] = (timings->HSyncStart >> 3) - 1; 17262306a36Sopenharmony_ci reg->CRTC[0x03] = (((timings->HSyncEnd >> 3) - 1) & 0x1f) | 0x80; 17362306a36Sopenharmony_ci reg->CRTC[0x04] = (timings->HSyncStart >> 3); 17462306a36Sopenharmony_ci reg->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) | 17562306a36Sopenharmony_ci (((timings->HSyncEnd >> 3)) & 0x1f); 17662306a36Sopenharmony_ci reg->CRTC[0x06] = (timings->VTotal - 2) & 0xFF; 17762306a36Sopenharmony_ci reg->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) | 17862306a36Sopenharmony_ci (((timings->VDisplay - 1) & 0x100) >> 7) | 17962306a36Sopenharmony_ci ((timings->VSyncStart & 0x100) >> 6) | 18062306a36Sopenharmony_ci (((timings->VSyncStart - 1) & 0x100) >> 5) | 18162306a36Sopenharmony_ci 0x10 | 18262306a36Sopenharmony_ci (((timings->VTotal - 2) & 0x200) >> 4) | 18362306a36Sopenharmony_ci (((timings->VDisplay - 1) & 0x200) >> 3) | 18462306a36Sopenharmony_ci ((timings->VSyncStart & 0x200) >> 2); 18562306a36Sopenharmony_ci reg->CRTC[0x08] = 0x00; 18662306a36Sopenharmony_ci reg->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (timings->dblscan) 18962306a36Sopenharmony_ci reg->CRTC[0x09] |= 0x80; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci reg->CRTC[0x0a] = 0x00; 19262306a36Sopenharmony_ci reg->CRTC[0x0b] = 0x00; 19362306a36Sopenharmony_ci reg->CRTC[0x0c] = 0x00; 19462306a36Sopenharmony_ci reg->CRTC[0x0d] = 0x00; 19562306a36Sopenharmony_ci reg->CRTC[0x0e] = 0x00; 19662306a36Sopenharmony_ci reg->CRTC[0x0f] = 0x00; 19762306a36Sopenharmony_ci reg->CRTC[0x10] = timings->VSyncStart & 0xff; 19862306a36Sopenharmony_ci reg->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20; 19962306a36Sopenharmony_ci reg->CRTC[0x12] = (timings->VDisplay - 1) & 0xff; 20062306a36Sopenharmony_ci reg->CRTC[0x13] = var->xres_virtual >> 4; 20162306a36Sopenharmony_ci reg->CRTC[0x14] = 0x00; 20262306a36Sopenharmony_ci reg->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff; 20362306a36Sopenharmony_ci reg->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff; 20462306a36Sopenharmony_ci reg->CRTC[0x17] = 0xc3; 20562306a36Sopenharmony_ci reg->CRTC[0x18] = 0xff; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * are these unnecessary? 20962306a36Sopenharmony_ci * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO); 21062306a36Sopenharmony_ci * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO); 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Graphics Display Controller 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci reg->Graphics[0x00] = 0x00; 21762306a36Sopenharmony_ci reg->Graphics[0x01] = 0x00; 21862306a36Sopenharmony_ci reg->Graphics[0x02] = 0x00; 21962306a36Sopenharmony_ci reg->Graphics[0x03] = 0x00; 22062306a36Sopenharmony_ci reg->Graphics[0x04] = 0x00; 22162306a36Sopenharmony_ci reg->Graphics[0x05] = 0x40; 22262306a36Sopenharmony_ci reg->Graphics[0x06] = 0x05; /* only map 64k VGA memory !!!! */ 22362306a36Sopenharmony_ci reg->Graphics[0x07] = 0x0F; 22462306a36Sopenharmony_ci reg->Graphics[0x08] = 0xFF; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci reg->Attribute[0x00] = 0x00; /* standard colormap translation */ 22862306a36Sopenharmony_ci reg->Attribute[0x01] = 0x01; 22962306a36Sopenharmony_ci reg->Attribute[0x02] = 0x02; 23062306a36Sopenharmony_ci reg->Attribute[0x03] = 0x03; 23162306a36Sopenharmony_ci reg->Attribute[0x04] = 0x04; 23262306a36Sopenharmony_ci reg->Attribute[0x05] = 0x05; 23362306a36Sopenharmony_ci reg->Attribute[0x06] = 0x06; 23462306a36Sopenharmony_ci reg->Attribute[0x07] = 0x07; 23562306a36Sopenharmony_ci reg->Attribute[0x08] = 0x08; 23662306a36Sopenharmony_ci reg->Attribute[0x09] = 0x09; 23762306a36Sopenharmony_ci reg->Attribute[0x0a] = 0x0A; 23862306a36Sopenharmony_ci reg->Attribute[0x0b] = 0x0B; 23962306a36Sopenharmony_ci reg->Attribute[0x0c] = 0x0C; 24062306a36Sopenharmony_ci reg->Attribute[0x0d] = 0x0D; 24162306a36Sopenharmony_ci reg->Attribute[0x0e] = 0x0E; 24262306a36Sopenharmony_ci reg->Attribute[0x0f] = 0x0F; 24362306a36Sopenharmony_ci reg->Attribute[0x10] = 0x41; 24462306a36Sopenharmony_ci reg->Attribute[0x11] = 0xFF; 24562306a36Sopenharmony_ci reg->Attribute[0x12] = 0x0F; 24662306a36Sopenharmony_ci reg->Attribute[0x13] = 0x00; 24762306a36Sopenharmony_ci reg->Attribute[0x14] = 0x00; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* -------------------- Hardware specific routines ------------------------- */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Hardware Acceleration for SavageFB 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* Wait for fifo space */ 25762306a36Sopenharmony_cistatic void 25862306a36Sopenharmony_cisavage3D_waitfifo(struct savagefb_par *par, int space) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int slots = MAXFIFO - space; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci while ((savage_in32(0x48C00, par) & 0x0000ffff) > slots); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void 26662306a36Sopenharmony_cisavage4_waitfifo(struct savagefb_par *par, int space) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int slots = MAXFIFO - space; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci while ((savage_in32(0x48C60, par) & 0x001fffff) > slots); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void 27462306a36Sopenharmony_cisavage2000_waitfifo(struct savagefb_par *par, int space) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int slots = MAXFIFO - space; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci while ((savage_in32(0x48C60, par) & 0x0000ffff) > slots); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* Wait for idle accelerator */ 28262306a36Sopenharmony_cistatic void 28362306a36Sopenharmony_cisavage3D_waitidle(struct savagefb_par *par) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci while ((savage_in32(0x48C00, par) & 0x0008ffff) != 0x80000); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void 28962306a36Sopenharmony_cisavage4_waitidle(struct savagefb_par *par) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci while ((savage_in32(0x48C60, par) & 0x00a00000) != 0x00a00000); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void 29562306a36Sopenharmony_cisavage2000_waitidle(struct savagefb_par *par) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci while ((savage_in32(0x48C60, par) & 0x009fffff)); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_ACCEL 30162306a36Sopenharmony_cistatic void 30262306a36Sopenharmony_ciSavageSetup2DEngine(struct savagefb_par *par) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci unsigned long GlobalBitmapDescriptor; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE; 30762306a36Sopenharmony_ci BCI_BD_SET_BPP(GlobalBitmapDescriptor, par->depth); 30862306a36Sopenharmony_ci BCI_BD_SET_STRIDE(GlobalBitmapDescriptor, par->vwidth); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci switch(par->chip) { 31162306a36Sopenharmony_ci case S3_SAVAGE3D: 31262306a36Sopenharmony_ci case S3_SAVAGE_MX: 31362306a36Sopenharmony_ci /* Disable BCI */ 31462306a36Sopenharmony_ci savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par); 31562306a36Sopenharmony_ci /* Setup BCI command overflow buffer */ 31662306a36Sopenharmony_ci savage_out32(0x48C14, 31762306a36Sopenharmony_ci (par->cob_offset >> 11) | (par->cob_index << 29), 31862306a36Sopenharmony_ci par); 31962306a36Sopenharmony_ci /* Program shadow status update. */ 32062306a36Sopenharmony_ci savage_out32(0x48C10, 0x78207220, par); 32162306a36Sopenharmony_ci savage_out32(0x48C0C, 0, par); 32262306a36Sopenharmony_ci /* Enable BCI and command overflow buffer */ 32362306a36Sopenharmony_ci savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x0C, par); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci case S3_SAVAGE4: 32662306a36Sopenharmony_ci case S3_TWISTER: 32762306a36Sopenharmony_ci case S3_PROSAVAGE: 32862306a36Sopenharmony_ci case S3_PROSAVAGEDDR: 32962306a36Sopenharmony_ci case S3_SUPERSAVAGE: 33062306a36Sopenharmony_ci /* Disable BCI */ 33162306a36Sopenharmony_ci savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par); 33262306a36Sopenharmony_ci /* Program shadow status update */ 33362306a36Sopenharmony_ci savage_out32(0x48C10, 0x00700040, par); 33462306a36Sopenharmony_ci savage_out32(0x48C0C, 0, par); 33562306a36Sopenharmony_ci /* Enable BCI without the COB */ 33662306a36Sopenharmony_ci savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x08, par); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case S3_SAVAGE2000: 33962306a36Sopenharmony_ci /* Disable BCI */ 34062306a36Sopenharmony_ci savage_out32(0x48C18, 0, par); 34162306a36Sopenharmony_ci /* Setup BCI command overflow buffer */ 34262306a36Sopenharmony_ci savage_out32(0x48C18, 34362306a36Sopenharmony_ci (par->cob_offset >> 7) | (par->cob_index), 34462306a36Sopenharmony_ci par); 34562306a36Sopenharmony_ci /* Disable shadow status update */ 34662306a36Sopenharmony_ci savage_out32(0x48A30, 0, par); 34762306a36Sopenharmony_ci /* Enable BCI and command overflow buffer */ 34862306a36Sopenharmony_ci savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x00280000, 34962306a36Sopenharmony_ci par); 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci default: 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci /* Turn on 16-bit register access. */ 35562306a36Sopenharmony_ci vga_out8(0x3d4, 0x31, par); 35662306a36Sopenharmony_ci vga_out8(0x3d5, 0x0c, par); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Set stride to use GBD. */ 35962306a36Sopenharmony_ci vga_out8(0x3d4, 0x50, par); 36062306a36Sopenharmony_ci vga_out8(0x3d5, vga_in8(0x3d5, par) | 0xC1, par); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Enable 2D engine. */ 36362306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 36462306a36Sopenharmony_ci vga_out8(0x3d5, 0x01, par); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci savage_out32(MONO_PAT_0, ~0, par); 36762306a36Sopenharmony_ci savage_out32(MONO_PAT_1, ~0, par); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Setup plane masks */ 37062306a36Sopenharmony_ci savage_out32(0x8128, ~0, par); /* enable all write planes */ 37162306a36Sopenharmony_ci savage_out32(0x812C, ~0, par); /* enable all read planes */ 37262306a36Sopenharmony_ci savage_out16(0x8134, 0x27, par); 37362306a36Sopenharmony_ci savage_out16(0x8136, 0x07, par); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Now set the GBD */ 37662306a36Sopenharmony_ci par->bci_ptr = 0; 37762306a36Sopenharmony_ci par->SavageWaitFifo(par, 4); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1); 38062306a36Sopenharmony_ci BCI_SEND(0); 38162306a36Sopenharmony_ci BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2); 38262306a36Sopenharmony_ci BCI_SEND(GlobalBitmapDescriptor); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* 38562306a36Sopenharmony_ci * I don't know why, sending this twice fixes the initial black screen, 38662306a36Sopenharmony_ci * prevents X from crashing at least in Toshiba laptops with SavageIX. 38762306a36Sopenharmony_ci * --Tony 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci par->bci_ptr = 0; 39062306a36Sopenharmony_ci par->SavageWaitFifo(par, 4); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1); 39362306a36Sopenharmony_ci BCI_SEND(0); 39462306a36Sopenharmony_ci BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2); 39562306a36Sopenharmony_ci BCI_SEND(GlobalBitmapDescriptor); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic void savagefb_set_clip(struct fb_info *info) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct savagefb_par *par = info->par; 40162306a36Sopenharmony_ci int cmd; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW; 40462306a36Sopenharmony_ci par->bci_ptr = 0; 40562306a36Sopenharmony_ci par->SavageWaitFifo(par,3); 40662306a36Sopenharmony_ci BCI_SEND(cmd); 40762306a36Sopenharmony_ci BCI_SEND(BCI_CLIP_TL(0, 0)); 40862306a36Sopenharmony_ci BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff)); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci#else 41162306a36Sopenharmony_cistatic void SavageSetup2DEngine(struct savagefb_par *par) {} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci#endif 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1, 41662306a36Sopenharmony_ci int min_n2, int max_n2, long freq_min, 41762306a36Sopenharmony_ci long freq_max, unsigned int *mdiv, 41862306a36Sopenharmony_ci unsigned int *ndiv, unsigned int *r) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci long diff, best_diff; 42162306a36Sopenharmony_ci unsigned int m; 42262306a36Sopenharmony_ci unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (freq < freq_min / (1 << max_n2)) { 42562306a36Sopenharmony_ci printk(KERN_ERR "invalid frequency %ld Khz\n", freq); 42662306a36Sopenharmony_ci freq = freq_min / (1 << max_n2); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (freq > freq_max / (1 << min_n2)) { 42962306a36Sopenharmony_ci printk(KERN_ERR "invalid frequency %ld Khz\n", freq); 43062306a36Sopenharmony_ci freq = freq_max / (1 << min_n2); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* work out suitable timings */ 43462306a36Sopenharmony_ci best_diff = freq; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci for (n2=min_n2; n2<=max_n2; n2++) { 43762306a36Sopenharmony_ci for (n1=min_n1+2; n1<=max_n1+2; n1++) { 43862306a36Sopenharmony_ci m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) / 43962306a36Sopenharmony_ci BASE_FREQ; 44062306a36Sopenharmony_ci if (m < min_m+2 || m > 127+2) 44162306a36Sopenharmony_ci continue; 44262306a36Sopenharmony_ci if ((m * BASE_FREQ >= freq_min * n1) && 44362306a36Sopenharmony_ci (m * BASE_FREQ <= freq_max * n1)) { 44462306a36Sopenharmony_ci diff = freq * (1 << n2) * n1 - BASE_FREQ * m; 44562306a36Sopenharmony_ci if (diff < 0) 44662306a36Sopenharmony_ci diff = -diff; 44762306a36Sopenharmony_ci if (diff < best_diff) { 44862306a36Sopenharmony_ci best_diff = diff; 44962306a36Sopenharmony_ci best_m = m; 45062306a36Sopenharmony_ci best_n1 = n1; 45162306a36Sopenharmony_ci best_n2 = n2; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci *ndiv = best_n1 - 2; 45862306a36Sopenharmony_ci *r = best_n2; 45962306a36Sopenharmony_ci *mdiv = best_m - 2; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic int common_calc_clock(long freq, int min_m, int min_n1, int max_n1, 46362306a36Sopenharmony_ci int min_n2, int max_n2, long freq_min, 46462306a36Sopenharmony_ci long freq_max, unsigned char *mdiv, 46562306a36Sopenharmony_ci unsigned char *ndiv) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci long diff, best_diff; 46862306a36Sopenharmony_ci unsigned int m; 46962306a36Sopenharmony_ci unsigned char n1, n2; 47062306a36Sopenharmony_ci unsigned char best_n1 = 16+2, best_n2 = 2, best_m = 125+2; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci best_diff = freq; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci for (n2 = min_n2; n2 <= max_n2; n2++) { 47562306a36Sopenharmony_ci for (n1 = min_n1+2; n1 <= max_n1+2; n1++) { 47662306a36Sopenharmony_ci m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) / 47762306a36Sopenharmony_ci BASE_FREQ; 47862306a36Sopenharmony_ci if (m < min_m + 2 || m > 127+2) 47962306a36Sopenharmony_ci continue; 48062306a36Sopenharmony_ci if ((m * BASE_FREQ >= freq_min * n1) && 48162306a36Sopenharmony_ci (m * BASE_FREQ <= freq_max * n1)) { 48262306a36Sopenharmony_ci diff = freq * (1 << n2) * n1 - BASE_FREQ * m; 48362306a36Sopenharmony_ci if (diff < 0) 48462306a36Sopenharmony_ci diff = -diff; 48562306a36Sopenharmony_ci if (diff < best_diff) { 48662306a36Sopenharmony_ci best_diff = diff; 48762306a36Sopenharmony_ci best_m = m; 48862306a36Sopenharmony_ci best_n1 = n1; 48962306a36Sopenharmony_ci best_n2 = n2; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (max_n1 == 63) 49662306a36Sopenharmony_ci *ndiv = (best_n1 - 2) | (best_n2 << 6); 49762306a36Sopenharmony_ci else 49862306a36Sopenharmony_ci *ndiv = (best_n1 - 2) | (best_n2 << 5); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci *mdiv = best_m - 2; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci#ifdef SAVAGEFB_DEBUG 50662306a36Sopenharmony_ci/* This function is used to debug, it prints out the contents of s3 regs */ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic void SavagePrintRegs(struct savagefb_par *par) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci unsigned char i; 51162306a36Sopenharmony_ci int vgaCRIndex = 0x3d4; 51262306a36Sopenharmony_ci int vgaCRReg = 0x3d5; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci printk(KERN_DEBUG "SR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE " 51562306a36Sopenharmony_ci "xF"); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci for (i = 0; i < 0x70; i++) { 51862306a36Sopenharmony_ci if (!(i % 16)) 51962306a36Sopenharmony_ci printk(KERN_DEBUG "\nSR%xx ", i >> 4); 52062306a36Sopenharmony_ci vga_out8(0x3c4, i, par); 52162306a36Sopenharmony_ci printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par)); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci printk(KERN_DEBUG "\n\nCR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC " 52562306a36Sopenharmony_ci "xD xE xF"); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci for (i = 0; i < 0xB7; i++) { 52862306a36Sopenharmony_ci if (!(i % 16)) 52962306a36Sopenharmony_ci printk(KERN_DEBUG "\nCR%xx ", i >> 4); 53062306a36Sopenharmony_ci vga_out8(vgaCRIndex, i, par); 53162306a36Sopenharmony_ci printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par)); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci printk(KERN_DEBUG "\n\n"); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci#endif 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void savage_get_default_par(struct savagefb_par *par, struct savage_reg *reg) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci unsigned char cr3a, cr53, cr66; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci vga_out16(0x3d4, 0x4838, par); 54562306a36Sopenharmony_ci vga_out16(0x3d4, 0xa039, par); 54662306a36Sopenharmony_ci vga_out16(0x3c4, 0x0608, par); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 54962306a36Sopenharmony_ci cr66 = vga_in8(0x3d5, par); 55062306a36Sopenharmony_ci vga_out8(0x3d5, cr66 | 0x80, par); 55162306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 55262306a36Sopenharmony_ci cr3a = vga_in8(0x3d5, par); 55362306a36Sopenharmony_ci vga_out8(0x3d5, cr3a | 0x80, par); 55462306a36Sopenharmony_ci vga_out8(0x3d4, 0x53, par); 55562306a36Sopenharmony_ci cr53 = vga_in8(0x3d5, par); 55662306a36Sopenharmony_ci vga_out8(0x3d5, cr53 & 0x7f, par); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 55962306a36Sopenharmony_ci vga_out8(0x3d5, cr66, par); 56062306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 56162306a36Sopenharmony_ci vga_out8(0x3d5, cr3a, par); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 56462306a36Sopenharmony_ci vga_out8(0x3d5, cr66, par); 56562306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 56662306a36Sopenharmony_ci vga_out8(0x3d5, cr3a, par); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* unlock extended seq regs */ 56962306a36Sopenharmony_ci vga_out8(0x3c4, 0x08, par); 57062306a36Sopenharmony_ci reg->SR08 = vga_in8(0x3c5, par); 57162306a36Sopenharmony_ci vga_out8(0x3c5, 0x06, par); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* now save all the extended regs we need */ 57462306a36Sopenharmony_ci vga_out8(0x3d4, 0x31, par); 57562306a36Sopenharmony_ci reg->CR31 = vga_in8(0x3d5, par); 57662306a36Sopenharmony_ci vga_out8(0x3d4, 0x32, par); 57762306a36Sopenharmony_ci reg->CR32 = vga_in8(0x3d5, par); 57862306a36Sopenharmony_ci vga_out8(0x3d4, 0x34, par); 57962306a36Sopenharmony_ci reg->CR34 = vga_in8(0x3d5, par); 58062306a36Sopenharmony_ci vga_out8(0x3d4, 0x36, par); 58162306a36Sopenharmony_ci reg->CR36 = vga_in8(0x3d5, par); 58262306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 58362306a36Sopenharmony_ci reg->CR3A = vga_in8(0x3d5, par); 58462306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 58562306a36Sopenharmony_ci reg->CR40 = vga_in8(0x3d5, par); 58662306a36Sopenharmony_ci vga_out8(0x3d4, 0x42, par); 58762306a36Sopenharmony_ci reg->CR42 = vga_in8(0x3d5, par); 58862306a36Sopenharmony_ci vga_out8(0x3d4, 0x45, par); 58962306a36Sopenharmony_ci reg->CR45 = vga_in8(0x3d5, par); 59062306a36Sopenharmony_ci vga_out8(0x3d4, 0x50, par); 59162306a36Sopenharmony_ci reg->CR50 = vga_in8(0x3d5, par); 59262306a36Sopenharmony_ci vga_out8(0x3d4, 0x51, par); 59362306a36Sopenharmony_ci reg->CR51 = vga_in8(0x3d5, par); 59462306a36Sopenharmony_ci vga_out8(0x3d4, 0x53, par); 59562306a36Sopenharmony_ci reg->CR53 = vga_in8(0x3d5, par); 59662306a36Sopenharmony_ci vga_out8(0x3d4, 0x58, par); 59762306a36Sopenharmony_ci reg->CR58 = vga_in8(0x3d5, par); 59862306a36Sopenharmony_ci vga_out8(0x3d4, 0x60, par); 59962306a36Sopenharmony_ci reg->CR60 = vga_in8(0x3d5, par); 60062306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 60162306a36Sopenharmony_ci reg->CR66 = vga_in8(0x3d5, par); 60262306a36Sopenharmony_ci vga_out8(0x3d4, 0x67, par); 60362306a36Sopenharmony_ci reg->CR67 = vga_in8(0x3d5, par); 60462306a36Sopenharmony_ci vga_out8(0x3d4, 0x68, par); 60562306a36Sopenharmony_ci reg->CR68 = vga_in8(0x3d5, par); 60662306a36Sopenharmony_ci vga_out8(0x3d4, 0x69, par); 60762306a36Sopenharmony_ci reg->CR69 = vga_in8(0x3d5, par); 60862306a36Sopenharmony_ci vga_out8(0x3d4, 0x6f, par); 60962306a36Sopenharmony_ci reg->CR6F = vga_in8(0x3d5, par); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci vga_out8(0x3d4, 0x33, par); 61262306a36Sopenharmony_ci reg->CR33 = vga_in8(0x3d5, par); 61362306a36Sopenharmony_ci vga_out8(0x3d4, 0x86, par); 61462306a36Sopenharmony_ci reg->CR86 = vga_in8(0x3d5, par); 61562306a36Sopenharmony_ci vga_out8(0x3d4, 0x88, par); 61662306a36Sopenharmony_ci reg->CR88 = vga_in8(0x3d5, par); 61762306a36Sopenharmony_ci vga_out8(0x3d4, 0x90, par); 61862306a36Sopenharmony_ci reg->CR90 = vga_in8(0x3d5, par); 61962306a36Sopenharmony_ci vga_out8(0x3d4, 0x91, par); 62062306a36Sopenharmony_ci reg->CR91 = vga_in8(0x3d5, par); 62162306a36Sopenharmony_ci vga_out8(0x3d4, 0xb0, par); 62262306a36Sopenharmony_ci reg->CRB0 = vga_in8(0x3d5, par) | 0x80; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* extended mode timing regs */ 62562306a36Sopenharmony_ci vga_out8(0x3d4, 0x3b, par); 62662306a36Sopenharmony_ci reg->CR3B = vga_in8(0x3d5, par); 62762306a36Sopenharmony_ci vga_out8(0x3d4, 0x3c, par); 62862306a36Sopenharmony_ci reg->CR3C = vga_in8(0x3d5, par); 62962306a36Sopenharmony_ci vga_out8(0x3d4, 0x43, par); 63062306a36Sopenharmony_ci reg->CR43 = vga_in8(0x3d5, par); 63162306a36Sopenharmony_ci vga_out8(0x3d4, 0x5d, par); 63262306a36Sopenharmony_ci reg->CR5D = vga_in8(0x3d5, par); 63362306a36Sopenharmony_ci vga_out8(0x3d4, 0x5e, par); 63462306a36Sopenharmony_ci reg->CR5E = vga_in8(0x3d5, par); 63562306a36Sopenharmony_ci vga_out8(0x3d4, 0x65, par); 63662306a36Sopenharmony_ci reg->CR65 = vga_in8(0x3d5, par); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* save seq extended regs for DCLK PLL programming */ 63962306a36Sopenharmony_ci vga_out8(0x3c4, 0x0e, par); 64062306a36Sopenharmony_ci reg->SR0E = vga_in8(0x3c5, par); 64162306a36Sopenharmony_ci vga_out8(0x3c4, 0x0f, par); 64262306a36Sopenharmony_ci reg->SR0F = vga_in8(0x3c5, par); 64362306a36Sopenharmony_ci vga_out8(0x3c4, 0x10, par); 64462306a36Sopenharmony_ci reg->SR10 = vga_in8(0x3c5, par); 64562306a36Sopenharmony_ci vga_out8(0x3c4, 0x11, par); 64662306a36Sopenharmony_ci reg->SR11 = vga_in8(0x3c5, par); 64762306a36Sopenharmony_ci vga_out8(0x3c4, 0x12, par); 64862306a36Sopenharmony_ci reg->SR12 = vga_in8(0x3c5, par); 64962306a36Sopenharmony_ci vga_out8(0x3c4, 0x13, par); 65062306a36Sopenharmony_ci reg->SR13 = vga_in8(0x3c5, par); 65162306a36Sopenharmony_ci vga_out8(0x3c4, 0x29, par); 65262306a36Sopenharmony_ci reg->SR29 = vga_in8(0x3c5, par); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci vga_out8(0x3c4, 0x15, par); 65562306a36Sopenharmony_ci reg->SR15 = vga_in8(0x3c5, par); 65662306a36Sopenharmony_ci vga_out8(0x3c4, 0x30, par); 65762306a36Sopenharmony_ci reg->SR30 = vga_in8(0x3c5, par); 65862306a36Sopenharmony_ci vga_out8(0x3c4, 0x18, par); 65962306a36Sopenharmony_ci reg->SR18 = vga_in8(0x3c5, par); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* Save flat panel expansion registers. */ 66262306a36Sopenharmony_ci if (par->chip == S3_SAVAGE_MX) { 66362306a36Sopenharmony_ci int i; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 66662306a36Sopenharmony_ci vga_out8(0x3c4, 0x54+i, par); 66762306a36Sopenharmony_ci reg->SR54[i] = vga_in8(0x3c5, par); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 67262306a36Sopenharmony_ci cr66 = vga_in8(0x3d5, par); 67362306a36Sopenharmony_ci vga_out8(0x3d5, cr66 | 0x80, par); 67462306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 67562306a36Sopenharmony_ci cr3a = vga_in8(0x3d5, par); 67662306a36Sopenharmony_ci vga_out8(0x3d5, cr3a | 0x80, par); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* now save MIU regs */ 67962306a36Sopenharmony_ci if (par->chip != S3_SAVAGE_MX) { 68062306a36Sopenharmony_ci reg->MMPR0 = savage_in32(FIFO_CONTROL_REG, par); 68162306a36Sopenharmony_ci reg->MMPR1 = savage_in32(MIU_CONTROL_REG, par); 68262306a36Sopenharmony_ci reg->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par); 68362306a36Sopenharmony_ci reg->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 68762306a36Sopenharmony_ci vga_out8(0x3d5, cr3a, par); 68862306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 68962306a36Sopenharmony_ci vga_out8(0x3d5, cr66, par); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic void savage_set_default_par(struct savagefb_par *par, 69362306a36Sopenharmony_ci struct savage_reg *reg) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci unsigned char cr3a, cr53, cr66; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci vga_out16(0x3d4, 0x4838, par); 69862306a36Sopenharmony_ci vga_out16(0x3d4, 0xa039, par); 69962306a36Sopenharmony_ci vga_out16(0x3c4, 0x0608, par); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 70262306a36Sopenharmony_ci cr66 = vga_in8(0x3d5, par); 70362306a36Sopenharmony_ci vga_out8(0x3d5, cr66 | 0x80, par); 70462306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 70562306a36Sopenharmony_ci cr3a = vga_in8(0x3d5, par); 70662306a36Sopenharmony_ci vga_out8(0x3d5, cr3a | 0x80, par); 70762306a36Sopenharmony_ci vga_out8(0x3d4, 0x53, par); 70862306a36Sopenharmony_ci cr53 = vga_in8(0x3d5, par); 70962306a36Sopenharmony_ci vga_out8(0x3d5, cr53 & 0x7f, par); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 71262306a36Sopenharmony_ci vga_out8(0x3d5, cr66, par); 71362306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 71462306a36Sopenharmony_ci vga_out8(0x3d5, cr3a, par); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 71762306a36Sopenharmony_ci vga_out8(0x3d5, cr66, par); 71862306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 71962306a36Sopenharmony_ci vga_out8(0x3d5, cr3a, par); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* unlock extended seq regs */ 72262306a36Sopenharmony_ci vga_out8(0x3c4, 0x08, par); 72362306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR08, par); 72462306a36Sopenharmony_ci vga_out8(0x3c5, 0x06, par); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* now restore all the extended regs we need */ 72762306a36Sopenharmony_ci vga_out8(0x3d4, 0x31, par); 72862306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR31, par); 72962306a36Sopenharmony_ci vga_out8(0x3d4, 0x32, par); 73062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR32, par); 73162306a36Sopenharmony_ci vga_out8(0x3d4, 0x34, par); 73262306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR34, par); 73362306a36Sopenharmony_ci vga_out8(0x3d4, 0x36, par); 73462306a36Sopenharmony_ci vga_out8(0x3d5,reg->CR36, par); 73562306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 73662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR3A, par); 73762306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 73862306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR40, par); 73962306a36Sopenharmony_ci vga_out8(0x3d4, 0x42, par); 74062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR42, par); 74162306a36Sopenharmony_ci vga_out8(0x3d4, 0x45, par); 74262306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR45, par); 74362306a36Sopenharmony_ci vga_out8(0x3d4, 0x50, par); 74462306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR50, par); 74562306a36Sopenharmony_ci vga_out8(0x3d4, 0x51, par); 74662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR51, par); 74762306a36Sopenharmony_ci vga_out8(0x3d4, 0x53, par); 74862306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR53, par); 74962306a36Sopenharmony_ci vga_out8(0x3d4, 0x58, par); 75062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR58, par); 75162306a36Sopenharmony_ci vga_out8(0x3d4, 0x60, par); 75262306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR60, par); 75362306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 75462306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR66, par); 75562306a36Sopenharmony_ci vga_out8(0x3d4, 0x67, par); 75662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR67, par); 75762306a36Sopenharmony_ci vga_out8(0x3d4, 0x68, par); 75862306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR68, par); 75962306a36Sopenharmony_ci vga_out8(0x3d4, 0x69, par); 76062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR69, par); 76162306a36Sopenharmony_ci vga_out8(0x3d4, 0x6f, par); 76262306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR6F, par); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci vga_out8(0x3d4, 0x33, par); 76562306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR33, par); 76662306a36Sopenharmony_ci vga_out8(0x3d4, 0x86, par); 76762306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR86, par); 76862306a36Sopenharmony_ci vga_out8(0x3d4, 0x88, par); 76962306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR88, par); 77062306a36Sopenharmony_ci vga_out8(0x3d4, 0x90, par); 77162306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR90, par); 77262306a36Sopenharmony_ci vga_out8(0x3d4, 0x91, par); 77362306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR91, par); 77462306a36Sopenharmony_ci vga_out8(0x3d4, 0xb0, par); 77562306a36Sopenharmony_ci vga_out8(0x3d5, reg->CRB0, par); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* extended mode timing regs */ 77862306a36Sopenharmony_ci vga_out8(0x3d4, 0x3b, par); 77962306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR3B, par); 78062306a36Sopenharmony_ci vga_out8(0x3d4, 0x3c, par); 78162306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR3C, par); 78262306a36Sopenharmony_ci vga_out8(0x3d4, 0x43, par); 78362306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR43, par); 78462306a36Sopenharmony_ci vga_out8(0x3d4, 0x5d, par); 78562306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR5D, par); 78662306a36Sopenharmony_ci vga_out8(0x3d4, 0x5e, par); 78762306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR5E, par); 78862306a36Sopenharmony_ci vga_out8(0x3d4, 0x65, par); 78962306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR65, par); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* save seq extended regs for DCLK PLL programming */ 79262306a36Sopenharmony_ci vga_out8(0x3c4, 0x0e, par); 79362306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR0E, par); 79462306a36Sopenharmony_ci vga_out8(0x3c4, 0x0f, par); 79562306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR0F, par); 79662306a36Sopenharmony_ci vga_out8(0x3c4, 0x10, par); 79762306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR10, par); 79862306a36Sopenharmony_ci vga_out8(0x3c4, 0x11, par); 79962306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR11, par); 80062306a36Sopenharmony_ci vga_out8(0x3c4, 0x12, par); 80162306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR12, par); 80262306a36Sopenharmony_ci vga_out8(0x3c4, 0x13, par); 80362306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR13, par); 80462306a36Sopenharmony_ci vga_out8(0x3c4, 0x29, par); 80562306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR29, par); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci vga_out8(0x3c4, 0x15, par); 80862306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR15, par); 80962306a36Sopenharmony_ci vga_out8(0x3c4, 0x30, par); 81062306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR30, par); 81162306a36Sopenharmony_ci vga_out8(0x3c4, 0x18, par); 81262306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR18, par); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* Save flat panel expansion registers. */ 81562306a36Sopenharmony_ci if (par->chip == S3_SAVAGE_MX) { 81662306a36Sopenharmony_ci int i; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 81962306a36Sopenharmony_ci vga_out8(0x3c4, 0x54+i, par); 82062306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR54[i], par); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 82562306a36Sopenharmony_ci cr66 = vga_in8(0x3d5, par); 82662306a36Sopenharmony_ci vga_out8(0x3d5, cr66 | 0x80, par); 82762306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 82862306a36Sopenharmony_ci cr3a = vga_in8(0x3d5, par); 82962306a36Sopenharmony_ci vga_out8(0x3d5, cr3a | 0x80, par); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* now save MIU regs */ 83262306a36Sopenharmony_ci if (par->chip != S3_SAVAGE_MX) { 83362306a36Sopenharmony_ci savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par); 83462306a36Sopenharmony_ci savage_out32(MIU_CONTROL_REG, reg->MMPR1, par); 83562306a36Sopenharmony_ci savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par); 83662306a36Sopenharmony_ci savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par); 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 84062306a36Sopenharmony_ci vga_out8(0x3d5, cr3a, par); 84162306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 84262306a36Sopenharmony_ci vga_out8(0x3d5, cr66, par); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic void savage_update_var(struct fb_var_screeninfo *var, 84662306a36Sopenharmony_ci const struct fb_videomode *modedb) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci var->xres = var->xres_virtual = modedb->xres; 84962306a36Sopenharmony_ci var->yres = modedb->yres; 85062306a36Sopenharmony_ci if (var->yres_virtual < var->yres) 85162306a36Sopenharmony_ci var->yres_virtual = var->yres; 85262306a36Sopenharmony_ci var->xoffset = var->yoffset = 0; 85362306a36Sopenharmony_ci var->pixclock = modedb->pixclock; 85462306a36Sopenharmony_ci var->left_margin = modedb->left_margin; 85562306a36Sopenharmony_ci var->right_margin = modedb->right_margin; 85662306a36Sopenharmony_ci var->upper_margin = modedb->upper_margin; 85762306a36Sopenharmony_ci var->lower_margin = modedb->lower_margin; 85862306a36Sopenharmony_ci var->hsync_len = modedb->hsync_len; 85962306a36Sopenharmony_ci var->vsync_len = modedb->vsync_len; 86062306a36Sopenharmony_ci var->sync = modedb->sync; 86162306a36Sopenharmony_ci var->vmode = modedb->vmode; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int savagefb_check_var(struct fb_var_screeninfo *var, 86562306a36Sopenharmony_ci struct fb_info *info) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct savagefb_par *par = info->par; 86862306a36Sopenharmony_ci int memlen, vramlen, mode_valid = 0; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci DBG("savagefb_check_var"); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (!var->pixclock) 87362306a36Sopenharmony_ci return -EINVAL; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci var->transp.offset = 0; 87662306a36Sopenharmony_ci var->transp.length = 0; 87762306a36Sopenharmony_ci switch (var->bits_per_pixel) { 87862306a36Sopenharmony_ci case 8: 87962306a36Sopenharmony_ci var->red.offset = var->green.offset = 88062306a36Sopenharmony_ci var->blue.offset = 0; 88162306a36Sopenharmony_ci var->red.length = var->green.length = 88262306a36Sopenharmony_ci var->blue.length = var->bits_per_pixel; 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci case 16: 88562306a36Sopenharmony_ci var->red.offset = 11; 88662306a36Sopenharmony_ci var->red.length = 5; 88762306a36Sopenharmony_ci var->green.offset = 5; 88862306a36Sopenharmony_ci var->green.length = 6; 88962306a36Sopenharmony_ci var->blue.offset = 0; 89062306a36Sopenharmony_ci var->blue.length = 5; 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci case 32: 89362306a36Sopenharmony_ci var->transp.offset = 24; 89462306a36Sopenharmony_ci var->transp.length = 8; 89562306a36Sopenharmony_ci var->red.offset = 16; 89662306a36Sopenharmony_ci var->red.length = 8; 89762306a36Sopenharmony_ci var->green.offset = 8; 89862306a36Sopenharmony_ci var->green.length = 8; 89962306a36Sopenharmony_ci var->blue.offset = 0; 90062306a36Sopenharmony_ci var->blue.length = 8; 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci default: 90462306a36Sopenharmony_ci return -EINVAL; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (!info->monspecs.hfmax || !info->monspecs.vfmax || 90862306a36Sopenharmony_ci !info->monspecs.dclkmax || !fb_validate_mode(var, info)) 90962306a36Sopenharmony_ci mode_valid = 1; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* calculate modeline if supported by monitor */ 91262306a36Sopenharmony_ci if (!mode_valid && info->monspecs.gtf) { 91362306a36Sopenharmony_ci if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info)) 91462306a36Sopenharmony_ci mode_valid = 1; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (!mode_valid) { 91862306a36Sopenharmony_ci const struct fb_videomode *mode; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci mode = fb_find_best_mode(var, &info->modelist); 92162306a36Sopenharmony_ci if (mode) { 92262306a36Sopenharmony_ci savage_update_var(var, mode); 92362306a36Sopenharmony_ci mode_valid = 1; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (!mode_valid && info->monspecs.modedb_len) 92862306a36Sopenharmony_ci return -EINVAL; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Is the mode larger than the LCD panel? */ 93162306a36Sopenharmony_ci if (par->SavagePanelWidth && 93262306a36Sopenharmony_ci (var->xres > par->SavagePanelWidth || 93362306a36Sopenharmony_ci var->yres > par->SavagePanelHeight)) { 93462306a36Sopenharmony_ci printk(KERN_INFO "Mode (%dx%d) larger than the LCD panel " 93562306a36Sopenharmony_ci "(%dx%d)\n", var->xres, var->yres, 93662306a36Sopenharmony_ci par->SavagePanelWidth, 93762306a36Sopenharmony_ci par->SavagePanelHeight); 93862306a36Sopenharmony_ci return -1; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (var->yres_virtual < var->yres) 94262306a36Sopenharmony_ci var->yres_virtual = var->yres; 94362306a36Sopenharmony_ci if (var->xres_virtual < var->xres) 94462306a36Sopenharmony_ci var->xres_virtual = var->xres; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci vramlen = info->fix.smem_len; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci memlen = var->xres_virtual * var->bits_per_pixel * 94962306a36Sopenharmony_ci var->yres_virtual / 8; 95062306a36Sopenharmony_ci if (memlen > vramlen) { 95162306a36Sopenharmony_ci var->yres_virtual = vramlen * 8 / 95262306a36Sopenharmony_ci (var->xres_virtual * var->bits_per_pixel); 95362306a36Sopenharmony_ci memlen = var->xres_virtual * var->bits_per_pixel * 95462306a36Sopenharmony_ci var->yres_virtual / 8; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* we must round yres/xres down, we already rounded y/xres_virtual up 95862306a36Sopenharmony_ci if it was possible. We should return -EINVAL, but I disagree */ 95962306a36Sopenharmony_ci if (var->yres_virtual < var->yres) 96062306a36Sopenharmony_ci var->yres = var->yres_virtual; 96162306a36Sopenharmony_ci if (var->xres_virtual < var->xres) 96262306a36Sopenharmony_ci var->xres = var->xres_virtual; 96362306a36Sopenharmony_ci if (var->xoffset + var->xres > var->xres_virtual) 96462306a36Sopenharmony_ci var->xoffset = var->xres_virtual - var->xres; 96562306a36Sopenharmony_ci if (var->yoffset + var->yres > var->yres_virtual) 96662306a36Sopenharmony_ci var->yoffset = var->yres_virtual - var->yres; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int savagefb_decode_var(struct fb_var_screeninfo *var, 97362306a36Sopenharmony_ci struct savagefb_par *par, 97462306a36Sopenharmony_ci struct savage_reg *reg) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct xtimings timings; 97762306a36Sopenharmony_ci int width, dclk, i, j; /*, refresh; */ 97862306a36Sopenharmony_ci unsigned int m, n, r; 97962306a36Sopenharmony_ci unsigned char tmp = 0; 98062306a36Sopenharmony_ci unsigned int pixclock = var->pixclock; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci DBG("savagefb_decode_var"); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci memset(&timings, 0, sizeof(timings)); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (!pixclock) pixclock = 10000; /* 10ns = 100MHz */ 98762306a36Sopenharmony_ci timings.Clock = 1000000000 / pixclock; 98862306a36Sopenharmony_ci if (timings.Clock < 1) timings.Clock = 1; 98962306a36Sopenharmony_ci timings.dblscan = var->vmode & FB_VMODE_DOUBLE; 99062306a36Sopenharmony_ci timings.interlaced = var->vmode & FB_VMODE_INTERLACED; 99162306a36Sopenharmony_ci timings.HDisplay = var->xres; 99262306a36Sopenharmony_ci timings.HSyncStart = timings.HDisplay + var->right_margin; 99362306a36Sopenharmony_ci timings.HSyncEnd = timings.HSyncStart + var->hsync_len; 99462306a36Sopenharmony_ci timings.HTotal = timings.HSyncEnd + var->left_margin; 99562306a36Sopenharmony_ci timings.VDisplay = var->yres; 99662306a36Sopenharmony_ci timings.VSyncStart = timings.VDisplay + var->lower_margin; 99762306a36Sopenharmony_ci timings.VSyncEnd = timings.VSyncStart + var->vsync_len; 99862306a36Sopenharmony_ci timings.VTotal = timings.VSyncEnd + var->upper_margin; 99962306a36Sopenharmony_ci timings.sync = var->sync; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci par->depth = var->bits_per_pixel; 100362306a36Sopenharmony_ci par->vwidth = var->xres_virtual; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (var->bits_per_pixel == 16 && par->chip == S3_SAVAGE3D) { 100662306a36Sopenharmony_ci timings.HDisplay *= 2; 100762306a36Sopenharmony_ci timings.HSyncStart *= 2; 100862306a36Sopenharmony_ci timings.HSyncEnd *= 2; 100962306a36Sopenharmony_ci timings.HTotal *= 2; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* 101362306a36Sopenharmony_ci * This will allocate the datastructure and initialize all of the 101462306a36Sopenharmony_ci * generic VGA registers. 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_ci vgaHWInit(var, par, &timings, reg); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci /* We need to set CR67 whether or not we use the BIOS. */ 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci dclk = timings.Clock; 102162306a36Sopenharmony_ci reg->CR67 = 0x00; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci switch(var->bits_per_pixel) { 102462306a36Sopenharmony_ci case 8: 102562306a36Sopenharmony_ci if ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) 102662306a36Sopenharmony_ci reg->CR67 = 0x10; /* 8bpp, 2 pixels/clock */ 102762306a36Sopenharmony_ci else 102862306a36Sopenharmony_ci reg->CR67 = 0x00; /* 8bpp, 1 pixel/clock */ 102962306a36Sopenharmony_ci break; 103062306a36Sopenharmony_ci case 15: 103162306a36Sopenharmony_ci if (S3_SAVAGE_MOBILE_SERIES(par->chip) || 103262306a36Sopenharmony_ci ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))) 103362306a36Sopenharmony_ci reg->CR67 = 0x30; /* 15bpp, 2 pixel/clock */ 103462306a36Sopenharmony_ci else 103562306a36Sopenharmony_ci reg->CR67 = 0x20; /* 15bpp, 1 pixels/clock */ 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci case 16: 103862306a36Sopenharmony_ci if (S3_SAVAGE_MOBILE_SERIES(par->chip) || 103962306a36Sopenharmony_ci ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))) 104062306a36Sopenharmony_ci reg->CR67 = 0x50; /* 16bpp, 2 pixel/clock */ 104162306a36Sopenharmony_ci else 104262306a36Sopenharmony_ci reg->CR67 = 0x40; /* 16bpp, 1 pixels/clock */ 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci case 24: 104562306a36Sopenharmony_ci reg->CR67 = 0x70; 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci case 32: 104862306a36Sopenharmony_ci reg->CR67 = 0xd0; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* 105362306a36Sopenharmony_ci * Either BIOS use is disabled, or we failed to find a suitable 105462306a36Sopenharmony_ci * match. Fall back to traditional register-crunching. 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 105862306a36Sopenharmony_ci tmp = vga_in8(0x3d5, par); 105962306a36Sopenharmony_ci if (1 /*FIXME:psav->pci_burst*/) 106062306a36Sopenharmony_ci reg->CR3A = (tmp & 0x7f) | 0x15; 106162306a36Sopenharmony_ci else 106262306a36Sopenharmony_ci reg->CR3A = tmp | 0x95; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci reg->CR53 = 0x00; 106562306a36Sopenharmony_ci reg->CR31 = 0x8c; 106662306a36Sopenharmony_ci reg->CR66 = 0x89; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci vga_out8(0x3d4, 0x58, par); 106962306a36Sopenharmony_ci reg->CR58 = vga_in8(0x3d5, par) & 0x80; 107062306a36Sopenharmony_ci reg->CR58 |= 0x13; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci reg->SR15 = 0x03 | 0x80; 107362306a36Sopenharmony_ci reg->SR18 = 0x00; 107462306a36Sopenharmony_ci reg->CR43 = reg->CR45 = reg->CR65 = 0x00; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 107762306a36Sopenharmony_ci reg->CR40 = vga_in8(0x3d5, par) & ~0x01; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci reg->MMPR0 = 0x010400; 108062306a36Sopenharmony_ci reg->MMPR1 = 0x00; 108162306a36Sopenharmony_ci reg->MMPR2 = 0x0808; 108262306a36Sopenharmony_ci reg->MMPR3 = 0x08080810; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r); 108562306a36Sopenharmony_ci /* m = 107; n = 4; r = 2; */ 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (par->MCLK <= 0) { 108862306a36Sopenharmony_ci reg->SR10 = 255; 108962306a36Sopenharmony_ci reg->SR11 = 255; 109062306a36Sopenharmony_ci } else { 109162306a36Sopenharmony_ci common_calc_clock(par->MCLK, 1, 1, 31, 0, 3, 135000, 270000, 109262306a36Sopenharmony_ci ®->SR11, ®->SR10); 109362306a36Sopenharmony_ci /* reg->SR10 = 80; // MCLK == 286000 */ 109462306a36Sopenharmony_ci /* reg->SR11 = 125; */ 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci reg->SR12 = (r << 6) | (n & 0x3f); 109862306a36Sopenharmony_ci reg->SR13 = m & 0xff; 109962306a36Sopenharmony_ci reg->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (var->bits_per_pixel < 24) 110262306a36Sopenharmony_ci reg->MMPR0 -= 0x8000; 110362306a36Sopenharmony_ci else 110462306a36Sopenharmony_ci reg->MMPR0 -= 0x4000; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (timings.interlaced) 110762306a36Sopenharmony_ci reg->CR42 = 0x20; 110862306a36Sopenharmony_ci else 110962306a36Sopenharmony_ci reg->CR42 = 0x00; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci reg->CR34 = 0x10; /* display fifo */ 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) | 111462306a36Sopenharmony_ci ((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) | 111562306a36Sopenharmony_ci ((((timings.HSyncStart >> 3) - 1) & 0x100) >> 6) | 111662306a36Sopenharmony_ci ((timings.HSyncStart & 0x800) >> 7); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 64) 111962306a36Sopenharmony_ci i |= 0x08; 112062306a36Sopenharmony_ci if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32) 112162306a36Sopenharmony_ci i |= 0x20; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci j = (reg->CRTC[0] + ((i & 0x01) << 8) + 112462306a36Sopenharmony_ci reg->CRTC[4] + ((i & 0x10) << 4) + 1) / 2; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (j - (reg->CRTC[4] + ((i & 0x10) << 4)) < 4) { 112762306a36Sopenharmony_ci if (reg->CRTC[4] + ((i & 0x10) << 4) + 4 <= 112862306a36Sopenharmony_ci reg->CRTC[0] + ((i & 0x01) << 8)) 112962306a36Sopenharmony_ci j = reg->CRTC[4] + ((i & 0x10) << 4) + 4; 113062306a36Sopenharmony_ci else 113162306a36Sopenharmony_ci j = reg->CRTC[0] + ((i & 0x01) << 8) + 1; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci reg->CR3B = j & 0xff; 113562306a36Sopenharmony_ci i |= (j & 0x100) >> 2; 113662306a36Sopenharmony_ci reg->CR3C = (reg->CRTC[0] + ((i & 0x01) << 8)) / 2; 113762306a36Sopenharmony_ci reg->CR5D = i; 113862306a36Sopenharmony_ci reg->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) | 113962306a36Sopenharmony_ci (((timings.VDisplay - 1) & 0x400) >> 9) | 114062306a36Sopenharmony_ci (((timings.VSyncStart) & 0x400) >> 8) | 114162306a36Sopenharmony_ci (((timings.VSyncStart) & 0x400) >> 6) | 0x40; 114262306a36Sopenharmony_ci width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3; 114362306a36Sopenharmony_ci reg->CR91 = reg->CRTC[19] = 0xff & width; 114462306a36Sopenharmony_ci reg->CR51 = (0x300 & width) >> 4; 114562306a36Sopenharmony_ci reg->CR90 = 0x80 | (width >> 8); 114662306a36Sopenharmony_ci reg->MiscOutReg |= 0x0c; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci /* Set frame buffer description. */ 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (var->bits_per_pixel <= 8) 115162306a36Sopenharmony_ci reg->CR50 = 0; 115262306a36Sopenharmony_ci else if (var->bits_per_pixel <= 16) 115362306a36Sopenharmony_ci reg->CR50 = 0x10; 115462306a36Sopenharmony_ci else 115562306a36Sopenharmony_ci reg->CR50 = 0x30; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (var->xres_virtual <= 640) 115862306a36Sopenharmony_ci reg->CR50 |= 0x40; 115962306a36Sopenharmony_ci else if (var->xres_virtual == 800) 116062306a36Sopenharmony_ci reg->CR50 |= 0x80; 116162306a36Sopenharmony_ci else if (var->xres_virtual == 1024) 116262306a36Sopenharmony_ci reg->CR50 |= 0x00; 116362306a36Sopenharmony_ci else if (var->xres_virtual == 1152) 116462306a36Sopenharmony_ci reg->CR50 |= 0x01; 116562306a36Sopenharmony_ci else if (var->xres_virtual == 1280) 116662306a36Sopenharmony_ci reg->CR50 |= 0xc0; 116762306a36Sopenharmony_ci else if (var->xres_virtual == 1600) 116862306a36Sopenharmony_ci reg->CR50 |= 0x81; 116962306a36Sopenharmony_ci else 117062306a36Sopenharmony_ci reg->CR50 |= 0xc1; /* Use GBD */ 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (par->chip == S3_SAVAGE2000) 117362306a36Sopenharmony_ci reg->CR33 = 0x08; 117462306a36Sopenharmony_ci else 117562306a36Sopenharmony_ci reg->CR33 = 0x20; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci reg->CRTC[0x17] = 0xeb; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci reg->CR67 |= 1; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci vga_out8(0x3d4, 0x36, par); 118262306a36Sopenharmony_ci reg->CR36 = vga_in8(0x3d5, par); 118362306a36Sopenharmony_ci vga_out8(0x3d4, 0x68, par); 118462306a36Sopenharmony_ci reg->CR68 = vga_in8(0x3d5, par); 118562306a36Sopenharmony_ci reg->CR69 = 0; 118662306a36Sopenharmony_ci vga_out8(0x3d4, 0x6f, par); 118762306a36Sopenharmony_ci reg->CR6F = vga_in8(0x3d5, par); 118862306a36Sopenharmony_ci vga_out8(0x3d4, 0x86, par); 118962306a36Sopenharmony_ci reg->CR86 = vga_in8(0x3d5, par); 119062306a36Sopenharmony_ci vga_out8(0x3d4, 0x88, par); 119162306a36Sopenharmony_ci reg->CR88 = vga_in8(0x3d5, par) | 0x08; 119262306a36Sopenharmony_ci vga_out8(0x3d4, 0xb0, par); 119362306a36Sopenharmony_ci reg->CRB0 = vga_in8(0x3d5, par) | 0x80; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci return 0; 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci/* 120162306a36Sopenharmony_ci * Set a single color register. Return != 0 for invalid regno. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_cistatic int savagefb_setcolreg(unsigned regno, 120462306a36Sopenharmony_ci unsigned red, 120562306a36Sopenharmony_ci unsigned green, 120662306a36Sopenharmony_ci unsigned blue, 120762306a36Sopenharmony_ci unsigned transp, 120862306a36Sopenharmony_ci struct fb_info *info) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct savagefb_par *par = info->par; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (regno >= NR_PALETTE) 121362306a36Sopenharmony_ci return -EINVAL; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci par->palette[regno].red = red; 121662306a36Sopenharmony_ci par->palette[regno].green = green; 121762306a36Sopenharmony_ci par->palette[regno].blue = blue; 121862306a36Sopenharmony_ci par->palette[regno].transp = transp; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 122162306a36Sopenharmony_ci case 8: 122262306a36Sopenharmony_ci vga_out8(0x3c8, regno, par); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci vga_out8(0x3c9, red >> 10, par); 122562306a36Sopenharmony_ci vga_out8(0x3c9, green >> 10, par); 122662306a36Sopenharmony_ci vga_out8(0x3c9, blue >> 10, par); 122762306a36Sopenharmony_ci break; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci case 16: 123062306a36Sopenharmony_ci if (regno < 16) 123162306a36Sopenharmony_ci ((u32 *)info->pseudo_palette)[regno] = 123262306a36Sopenharmony_ci ((red & 0xf800) ) | 123362306a36Sopenharmony_ci ((green & 0xfc00) >> 5) | 123462306a36Sopenharmony_ci ((blue & 0xf800) >> 11); 123562306a36Sopenharmony_ci break; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci case 24: 123862306a36Sopenharmony_ci if (regno < 16) 123962306a36Sopenharmony_ci ((u32 *)info->pseudo_palette)[regno] = 124062306a36Sopenharmony_ci ((red & 0xff00) << 8) | 124162306a36Sopenharmony_ci ((green & 0xff00) ) | 124262306a36Sopenharmony_ci ((blue & 0xff00) >> 8); 124362306a36Sopenharmony_ci break; 124462306a36Sopenharmony_ci case 32: 124562306a36Sopenharmony_ci if (regno < 16) 124662306a36Sopenharmony_ci ((u32 *)info->pseudo_palette)[regno] = 124762306a36Sopenharmony_ci ((transp & 0xff00) << 16) | 124862306a36Sopenharmony_ci ((red & 0xff00) << 8) | 124962306a36Sopenharmony_ci ((green & 0xff00) ) | 125062306a36Sopenharmony_ci ((blue & 0xff00) >> 8); 125162306a36Sopenharmony_ci break; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci default: 125462306a36Sopenharmony_ci return 1; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci return 0; 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic void savagefb_set_par_int(struct savagefb_par *par, struct savage_reg *reg) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci unsigned char tmp, cr3a, cr66, cr67; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci DBG("savagefb_set_par_int"); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci par->SavageWaitIdle(par); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci vga_out8(0x3c2, 0x23, par); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci vga_out16(0x3d4, 0x4838, par); 127162306a36Sopenharmony_ci vga_out16(0x3d4, 0xa539, par); 127262306a36Sopenharmony_ci vga_out16(0x3c4, 0x0608, par); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci vgaHWProtect(par, 1); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci /* 127762306a36Sopenharmony_ci * Some Savage/MX and /IX systems go nuts when trying to exit the 127862306a36Sopenharmony_ci * server after WindowMaker has displayed a gradient background. I 127962306a36Sopenharmony_ci * haven't been able to find what causes it, but a non-destructive 128062306a36Sopenharmony_ci * switch to mode 3 here seems to eliminate the issue. 128162306a36Sopenharmony_ci */ 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci VerticalRetraceWait(par); 128462306a36Sopenharmony_ci vga_out8(0x3d4, 0x67, par); 128562306a36Sopenharmony_ci cr67 = vga_in8(0x3d5, par); 128662306a36Sopenharmony_ci vga_out8(0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */ 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci vga_out8(0x3d4, 0x23, par); 128962306a36Sopenharmony_ci vga_out8(0x3d5, 0x00, par); 129062306a36Sopenharmony_ci vga_out8(0x3d4, 0x26, par); 129162306a36Sopenharmony_ci vga_out8(0x3d5, 0x00, par); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* restore extended regs */ 129462306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 129562306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR66, par); 129662306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 129762306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR3A, par); 129862306a36Sopenharmony_ci vga_out8(0x3d4, 0x31, par); 129962306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR31, par); 130062306a36Sopenharmony_ci vga_out8(0x3d4, 0x32, par); 130162306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR32, par); 130262306a36Sopenharmony_ci vga_out8(0x3d4, 0x58, par); 130362306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR58, par); 130462306a36Sopenharmony_ci vga_out8(0x3d4, 0x53, par); 130562306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR53 & 0x7f, par); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci vga_out16(0x3c4, 0x0608, par); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci /* Restore DCLK registers. */ 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci vga_out8(0x3c4, 0x0e, par); 131262306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR0E, par); 131362306a36Sopenharmony_ci vga_out8(0x3c4, 0x0f, par); 131462306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR0F, par); 131562306a36Sopenharmony_ci vga_out8(0x3c4, 0x29, par); 131662306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR29, par); 131762306a36Sopenharmony_ci vga_out8(0x3c4, 0x15, par); 131862306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR15, par); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* Restore flat panel expansion registers. */ 132162306a36Sopenharmony_ci if (par->chip == S3_SAVAGE_MX) { 132262306a36Sopenharmony_ci int i; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 132562306a36Sopenharmony_ci vga_out8(0x3c4, 0x54+i, par); 132662306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR54[i], par); 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci vgaHWRestore (par, reg); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci /* extended mode timing registers */ 133362306a36Sopenharmony_ci vga_out8(0x3d4, 0x53, par); 133462306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR53, par); 133562306a36Sopenharmony_ci vga_out8(0x3d4, 0x5d, par); 133662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR5D, par); 133762306a36Sopenharmony_ci vga_out8(0x3d4, 0x5e, par); 133862306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR5E, par); 133962306a36Sopenharmony_ci vga_out8(0x3d4, 0x3b, par); 134062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR3B, par); 134162306a36Sopenharmony_ci vga_out8(0x3d4, 0x3c, par); 134262306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR3C, par); 134362306a36Sopenharmony_ci vga_out8(0x3d4, 0x43, par); 134462306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR43, par); 134562306a36Sopenharmony_ci vga_out8(0x3d4, 0x65, par); 134662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR65, par); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* restore the desired video mode with cr67 */ 134962306a36Sopenharmony_ci vga_out8(0x3d4, 0x67, par); 135062306a36Sopenharmony_ci /* following part not present in X11 driver */ 135162306a36Sopenharmony_ci cr67 = vga_in8(0x3d5, par) & 0xf; 135262306a36Sopenharmony_ci vga_out8(0x3d5, 0x50 | cr67, par); 135362306a36Sopenharmony_ci mdelay(10); 135462306a36Sopenharmony_ci vga_out8(0x3d4, 0x67, par); 135562306a36Sopenharmony_ci /* end of part */ 135662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR67 & ~0x0c, par); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* other mode timing and extended regs */ 135962306a36Sopenharmony_ci vga_out8(0x3d4, 0x34, par); 136062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR34, par); 136162306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 136262306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR40, par); 136362306a36Sopenharmony_ci vga_out8(0x3d4, 0x42, par); 136462306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR42, par); 136562306a36Sopenharmony_ci vga_out8(0x3d4, 0x45, par); 136662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR45, par); 136762306a36Sopenharmony_ci vga_out8(0x3d4, 0x50, par); 136862306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR50, par); 136962306a36Sopenharmony_ci vga_out8(0x3d4, 0x51, par); 137062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR51, par); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* memory timings */ 137362306a36Sopenharmony_ci vga_out8(0x3d4, 0x36, par); 137462306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR36, par); 137562306a36Sopenharmony_ci vga_out8(0x3d4, 0x60, par); 137662306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR60, par); 137762306a36Sopenharmony_ci vga_out8(0x3d4, 0x68, par); 137862306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR68, par); 137962306a36Sopenharmony_ci vga_out8(0x3d4, 0x69, par); 138062306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR69, par); 138162306a36Sopenharmony_ci vga_out8(0x3d4, 0x6f, par); 138262306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR6F, par); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci vga_out8(0x3d4, 0x33, par); 138562306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR33, par); 138662306a36Sopenharmony_ci vga_out8(0x3d4, 0x86, par); 138762306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR86, par); 138862306a36Sopenharmony_ci vga_out8(0x3d4, 0x88, par); 138962306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR88, par); 139062306a36Sopenharmony_ci vga_out8(0x3d4, 0x90, par); 139162306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR90, par); 139262306a36Sopenharmony_ci vga_out8(0x3d4, 0x91, par); 139362306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR91, par); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (par->chip == S3_SAVAGE4) { 139662306a36Sopenharmony_ci vga_out8(0x3d4, 0xb0, par); 139762306a36Sopenharmony_ci vga_out8(0x3d5, reg->CRB0, par); 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci vga_out8(0x3d4, 0x32, par); 140162306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR32, par); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* unlock extended seq regs */ 140462306a36Sopenharmony_ci vga_out8(0x3c4, 0x08, par); 140562306a36Sopenharmony_ci vga_out8(0x3c5, 0x06, par); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates 140862306a36Sopenharmony_ci * that we should leave the default SR10 and SR11 values there. 140962306a36Sopenharmony_ci */ 141062306a36Sopenharmony_ci if (reg->SR10 != 255) { 141162306a36Sopenharmony_ci vga_out8(0x3c4, 0x10, par); 141262306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR10, par); 141362306a36Sopenharmony_ci vga_out8(0x3c4, 0x11, par); 141462306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR11, par); 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci /* restore extended seq regs for dclk */ 141862306a36Sopenharmony_ci vga_out8(0x3c4, 0x0e, par); 141962306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR0E, par); 142062306a36Sopenharmony_ci vga_out8(0x3c4, 0x0f, par); 142162306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR0F, par); 142262306a36Sopenharmony_ci vga_out8(0x3c4, 0x12, par); 142362306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR12, par); 142462306a36Sopenharmony_ci vga_out8(0x3c4, 0x13, par); 142562306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR13, par); 142662306a36Sopenharmony_ci vga_out8(0x3c4, 0x29, par); 142762306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR29, par); 142862306a36Sopenharmony_ci vga_out8(0x3c4, 0x18, par); 142962306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR18, par); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci /* load new m, n pll values for dclk & mclk */ 143262306a36Sopenharmony_ci vga_out8(0x3c4, 0x15, par); 143362306a36Sopenharmony_ci tmp = vga_in8(0x3c5, par) & ~0x21; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci vga_out8(0x3c5, tmp | 0x03, par); 143662306a36Sopenharmony_ci vga_out8(0x3c5, tmp | 0x23, par); 143762306a36Sopenharmony_ci vga_out8(0x3c5, tmp | 0x03, par); 143862306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR15, par); 143962306a36Sopenharmony_ci udelay(100); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci vga_out8(0x3c4, 0x30, par); 144262306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR30, par); 144362306a36Sopenharmony_ci vga_out8(0x3c4, 0x08, par); 144462306a36Sopenharmony_ci vga_out8(0x3c5, reg->SR08, par); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci /* now write out cr67 in full, possibly starting STREAMS */ 144762306a36Sopenharmony_ci VerticalRetraceWait(par); 144862306a36Sopenharmony_ci vga_out8(0x3d4, 0x67, par); 144962306a36Sopenharmony_ci vga_out8(0x3d5, reg->CR67, par); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 145262306a36Sopenharmony_ci cr66 = vga_in8(0x3d5, par); 145362306a36Sopenharmony_ci vga_out8(0x3d5, cr66 | 0x80, par); 145462306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 145562306a36Sopenharmony_ci cr3a = vga_in8(0x3d5, par); 145662306a36Sopenharmony_ci vga_out8(0x3d5, cr3a | 0x80, par); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if (par->chip != S3_SAVAGE_MX) { 145962306a36Sopenharmony_ci VerticalRetraceWait(par); 146062306a36Sopenharmony_ci savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par); 146162306a36Sopenharmony_ci par->SavageWaitIdle(par); 146262306a36Sopenharmony_ci savage_out32(MIU_CONTROL_REG, reg->MMPR1, par); 146362306a36Sopenharmony_ci par->SavageWaitIdle(par); 146462306a36Sopenharmony_ci savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par); 146562306a36Sopenharmony_ci par->SavageWaitIdle(par); 146662306a36Sopenharmony_ci savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par); 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 147062306a36Sopenharmony_ci vga_out8(0x3d5, cr66, par); 147162306a36Sopenharmony_ci vga_out8(0x3d4, 0x3a, par); 147262306a36Sopenharmony_ci vga_out8(0x3d5, cr3a, par); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci SavageSetup2DEngine(par); 147562306a36Sopenharmony_ci vgaHWProtect(par, 0); 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic void savagefb_update_start(struct savagefb_par *par, int base) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci /* program the start address registers */ 148162306a36Sopenharmony_ci vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par); 148262306a36Sopenharmony_ci vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par); 148362306a36Sopenharmony_ci vga_out8(0x3d4, 0x69, par); 148462306a36Sopenharmony_ci vga_out8(0x3d5, (base & 0x7f0000) >> 16, par); 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic void savagefb_set_fix(struct fb_info *info) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci info->fix.line_length = info->var.xres_virtual * 149162306a36Sopenharmony_ci info->var.bits_per_pixel / 8; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (info->var.bits_per_pixel == 8) { 149462306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 149562306a36Sopenharmony_ci info->fix.xpanstep = 4; 149662306a36Sopenharmony_ci } else { 149762306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 149862306a36Sopenharmony_ci info->fix.xpanstep = 2; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic int savagefb_set_par(struct fb_info *info) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct savagefb_par *par = info->par; 150662306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 150762306a36Sopenharmony_ci int err; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci DBG("savagefb_set_par"); 151062306a36Sopenharmony_ci err = savagefb_decode_var(var, par, &par->state); 151162306a36Sopenharmony_ci if (err) 151262306a36Sopenharmony_ci return err; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci if (par->dacSpeedBpp <= 0) { 151562306a36Sopenharmony_ci if (var->bits_per_pixel > 24) 151662306a36Sopenharmony_ci par->dacSpeedBpp = par->clock[3]; 151762306a36Sopenharmony_ci else if (var->bits_per_pixel >= 24) 151862306a36Sopenharmony_ci par->dacSpeedBpp = par->clock[2]; 151962306a36Sopenharmony_ci else if ((var->bits_per_pixel > 8) && (var->bits_per_pixel < 24)) 152062306a36Sopenharmony_ci par->dacSpeedBpp = par->clock[1]; 152162306a36Sopenharmony_ci else if (var->bits_per_pixel <= 8) 152262306a36Sopenharmony_ci par->dacSpeedBpp = par->clock[0]; 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci /* Set ramdac limits */ 152662306a36Sopenharmony_ci par->maxClock = par->dacSpeedBpp; 152762306a36Sopenharmony_ci par->minClock = 10000; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci savagefb_set_par_int(par, &par->state); 153062306a36Sopenharmony_ci fb_set_cmap(&info->cmap, info); 153162306a36Sopenharmony_ci savagefb_set_fix(info); 153262306a36Sopenharmony_ci savagefb_set_clip(info); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci SavagePrintRegs(par); 153562306a36Sopenharmony_ci return 0; 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci/* 153962306a36Sopenharmony_ci * Pan or Wrap the Display 154062306a36Sopenharmony_ci */ 154162306a36Sopenharmony_cistatic int savagefb_pan_display(struct fb_var_screeninfo *var, 154262306a36Sopenharmony_ci struct fb_info *info) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct savagefb_par *par = info->par; 154562306a36Sopenharmony_ci int base; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci base = (var->yoffset * info->fix.line_length 154862306a36Sopenharmony_ci + (var->xoffset & ~1) * ((info->var.bits_per_pixel+7) / 8)) >> 2; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci savagefb_update_start(par, base); 155162306a36Sopenharmony_ci return 0; 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic int savagefb_blank(int blank, struct fb_info *info) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci struct savagefb_par *par = info->par; 155762306a36Sopenharmony_ci u8 sr8 = 0, srd = 0; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (par->display_type == DISP_CRT) { 156062306a36Sopenharmony_ci vga_out8(0x3c4, 0x08, par); 156162306a36Sopenharmony_ci sr8 = vga_in8(0x3c5, par); 156262306a36Sopenharmony_ci sr8 |= 0x06; 156362306a36Sopenharmony_ci vga_out8(0x3c5, sr8, par); 156462306a36Sopenharmony_ci vga_out8(0x3c4, 0x0d, par); 156562306a36Sopenharmony_ci srd = vga_in8(0x3c5, par); 156662306a36Sopenharmony_ci srd &= 0x50; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci switch (blank) { 156962306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 157062306a36Sopenharmony_ci case FB_BLANK_NORMAL: 157162306a36Sopenharmony_ci break; 157262306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 157362306a36Sopenharmony_ci srd |= 0x10; 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 157662306a36Sopenharmony_ci srd |= 0x40; 157762306a36Sopenharmony_ci break; 157862306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 157962306a36Sopenharmony_ci srd |= 0x50; 158062306a36Sopenharmony_ci break; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci vga_out8(0x3c4, 0x0d, par); 158462306a36Sopenharmony_ci vga_out8(0x3c5, srd, par); 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (par->display_type == DISP_LCD || 158862306a36Sopenharmony_ci par->display_type == DISP_DFP) { 158962306a36Sopenharmony_ci switch(blank) { 159062306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 159162306a36Sopenharmony_ci case FB_BLANK_NORMAL: 159262306a36Sopenharmony_ci vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */ 159362306a36Sopenharmony_ci vga_out8(0x3c5, vga_in8(0x3c5, par) | 0x10, par); 159462306a36Sopenharmony_ci break; 159562306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 159662306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 159762306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 159862306a36Sopenharmony_ci vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */ 159962306a36Sopenharmony_ci vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x10, par); 160062306a36Sopenharmony_ci break; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci return (blank == FB_BLANK_NORMAL) ? 1 : 0; 160562306a36Sopenharmony_ci} 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_cistatic int savagefb_open(struct fb_info *info, int user) 160862306a36Sopenharmony_ci{ 160962306a36Sopenharmony_ci struct savagefb_par *par = info->par; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci mutex_lock(&par->open_lock); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci if (!par->open_count) { 161462306a36Sopenharmony_ci memset(&par->vgastate, 0, sizeof(par->vgastate)); 161562306a36Sopenharmony_ci par->vgastate.flags = VGA_SAVE_CMAP | VGA_SAVE_FONTS | 161662306a36Sopenharmony_ci VGA_SAVE_MODE; 161762306a36Sopenharmony_ci par->vgastate.vgabase = par->mmio.vbase + 0x8000; 161862306a36Sopenharmony_ci save_vga(&par->vgastate); 161962306a36Sopenharmony_ci savage_get_default_par(par, &par->initial); 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci par->open_count++; 162362306a36Sopenharmony_ci mutex_unlock(&par->open_lock); 162462306a36Sopenharmony_ci return 0; 162562306a36Sopenharmony_ci} 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_cistatic int savagefb_release(struct fb_info *info, int user) 162862306a36Sopenharmony_ci{ 162962306a36Sopenharmony_ci struct savagefb_par *par = info->par; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci mutex_lock(&par->open_lock); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (par->open_count == 1) { 163462306a36Sopenharmony_ci savage_set_default_par(par, &par->initial); 163562306a36Sopenharmony_ci restore_vga(&par->vgastate); 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci par->open_count--; 163962306a36Sopenharmony_ci mutex_unlock(&par->open_lock); 164062306a36Sopenharmony_ci return 0; 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistatic const struct fb_ops savagefb_ops = { 164462306a36Sopenharmony_ci .owner = THIS_MODULE, 164562306a36Sopenharmony_ci .fb_open = savagefb_open, 164662306a36Sopenharmony_ci .fb_release = savagefb_release, 164762306a36Sopenharmony_ci .fb_check_var = savagefb_check_var, 164862306a36Sopenharmony_ci .fb_set_par = savagefb_set_par, 164962306a36Sopenharmony_ci .fb_setcolreg = savagefb_setcolreg, 165062306a36Sopenharmony_ci .fb_pan_display = savagefb_pan_display, 165162306a36Sopenharmony_ci .fb_blank = savagefb_blank, 165262306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL) 165362306a36Sopenharmony_ci .fb_fillrect = savagefb_fillrect, 165462306a36Sopenharmony_ci .fb_copyarea = savagefb_copyarea, 165562306a36Sopenharmony_ci .fb_imageblit = savagefb_imageblit, 165662306a36Sopenharmony_ci .fb_sync = savagefb_sync, 165762306a36Sopenharmony_ci#else 165862306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 165962306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 166062306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 166162306a36Sopenharmony_ci#endif 166262306a36Sopenharmony_ci}; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_cistatic const struct fb_var_screeninfo savagefb_var800x600x8 = { 166762306a36Sopenharmony_ci .accel_flags = FB_ACCELF_TEXT, 166862306a36Sopenharmony_ci .xres = 800, 166962306a36Sopenharmony_ci .yres = 600, 167062306a36Sopenharmony_ci .xres_virtual = 800, 167162306a36Sopenharmony_ci .yres_virtual = 600, 167262306a36Sopenharmony_ci .bits_per_pixel = 8, 167362306a36Sopenharmony_ci .pixclock = 25000, 167462306a36Sopenharmony_ci .left_margin = 88, 167562306a36Sopenharmony_ci .right_margin = 40, 167662306a36Sopenharmony_ci .upper_margin = 23, 167762306a36Sopenharmony_ci .lower_margin = 1, 167862306a36Sopenharmony_ci .hsync_len = 128, 167962306a36Sopenharmony_ci .vsync_len = 4, 168062306a36Sopenharmony_ci .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 168162306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 168262306a36Sopenharmony_ci}; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic void savage_enable_mmio(struct savagefb_par *par) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci unsigned char val; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci DBG("savage_enable_mmio\n"); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci val = vga_in8(0x3c3, par); 169162306a36Sopenharmony_ci vga_out8(0x3c3, val | 0x01, par); 169262306a36Sopenharmony_ci val = vga_in8(0x3cc, par); 169362306a36Sopenharmony_ci vga_out8(0x3c2, val | 0x01, par); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (par->chip >= S3_SAVAGE4) { 169662306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 169762306a36Sopenharmony_ci val = vga_in8(0x3d5, par); 169862306a36Sopenharmony_ci vga_out8(0x3d5, val | 1, par); 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci} 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_cistatic void savage_disable_mmio(struct savagefb_par *par) 170462306a36Sopenharmony_ci{ 170562306a36Sopenharmony_ci unsigned char val; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci DBG("savage_disable_mmio\n"); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci if (par->chip >= S3_SAVAGE4) { 171062306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 171162306a36Sopenharmony_ci val = vga_in8(0x3d5, par); 171262306a36Sopenharmony_ci vga_out8(0x3d5, val | 1, par); 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci} 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_cistatic int savage_map_mmio(struct fb_info *info) 171862306a36Sopenharmony_ci{ 171962306a36Sopenharmony_ci struct savagefb_par *par = info->par; 172062306a36Sopenharmony_ci DBG("savage_map_mmio"); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (S3_SAVAGE3D_SERIES(par->chip)) 172362306a36Sopenharmony_ci par->mmio.pbase = pci_resource_start(par->pcidev, 0) + 172462306a36Sopenharmony_ci SAVAGE_NEWMMIO_REGBASE_S3; 172562306a36Sopenharmony_ci else 172662306a36Sopenharmony_ci par->mmio.pbase = pci_resource_start(par->pcidev, 0) + 172762306a36Sopenharmony_ci SAVAGE_NEWMMIO_REGBASE_S4; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci par->mmio.len = SAVAGE_NEWMMIO_REGSIZE; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci par->mmio.vbase = ioremap(par->mmio.pbase, par->mmio.len); 173262306a36Sopenharmony_ci if (!par->mmio.vbase) { 173362306a36Sopenharmony_ci printk("savagefb: unable to map memory mapped IO\n"); 173462306a36Sopenharmony_ci return -ENOMEM; 173562306a36Sopenharmony_ci } else 173662306a36Sopenharmony_ci printk(KERN_INFO "savagefb: mapped io at %p\n", 173762306a36Sopenharmony_ci par->mmio.vbase); 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci info->fix.mmio_start = par->mmio.pbase; 174062306a36Sopenharmony_ci info->fix.mmio_len = par->mmio.len; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET); 174362306a36Sopenharmony_ci par->bci_ptr = 0; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci savage_enable_mmio(par); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci return 0; 174862306a36Sopenharmony_ci} 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistatic void savage_unmap_mmio(struct fb_info *info) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci struct savagefb_par *par = info->par; 175362306a36Sopenharmony_ci DBG("savage_unmap_mmio"); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci savage_disable_mmio(par); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (par->mmio.vbase) { 175862306a36Sopenharmony_ci iounmap(par->mmio.vbase); 175962306a36Sopenharmony_ci par->mmio.vbase = NULL; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci} 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_cistatic int savage_map_video(struct fb_info *info, int video_len) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci struct savagefb_par *par = info->par; 176662306a36Sopenharmony_ci int resource; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci DBG("savage_map_video"); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (S3_SAVAGE3D_SERIES(par->chip)) 177162306a36Sopenharmony_ci resource = 0; 177262306a36Sopenharmony_ci else 177362306a36Sopenharmony_ci resource = 1; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci par->video.pbase = pci_resource_start(par->pcidev, resource); 177662306a36Sopenharmony_ci par->video.len = video_len; 177762306a36Sopenharmony_ci par->video.vbase = ioremap_wc(par->video.pbase, par->video.len); 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci if (!par->video.vbase) { 178062306a36Sopenharmony_ci printk("savagefb: unable to map screen memory\n"); 178162306a36Sopenharmony_ci return -ENOMEM; 178262306a36Sopenharmony_ci } else 178362306a36Sopenharmony_ci printk(KERN_INFO "savagefb: mapped framebuffer at %p, " 178462306a36Sopenharmony_ci "pbase == %x\n", par->video.vbase, par->video.pbase); 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci info->fix.smem_start = par->video.pbase; 178762306a36Sopenharmony_ci info->fix.smem_len = par->video.len - par->cob_size; 178862306a36Sopenharmony_ci info->screen_base = par->video.vbase; 178962306a36Sopenharmony_ci par->video.wc_cookie = arch_phys_wc_add(par->video.pbase, video_len); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci /* Clear framebuffer, it's all white in memory after boot */ 179262306a36Sopenharmony_ci memset_io(par->video.vbase, 0, par->video.len); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci return 0; 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_cistatic void savage_unmap_video(struct fb_info *info) 179862306a36Sopenharmony_ci{ 179962306a36Sopenharmony_ci struct savagefb_par *par = info->par; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci DBG("savage_unmap_video"); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (par->video.vbase) { 180462306a36Sopenharmony_ci arch_phys_wc_del(par->video.wc_cookie); 180562306a36Sopenharmony_ci iounmap(par->video.vbase); 180662306a36Sopenharmony_ci par->video.vbase = NULL; 180762306a36Sopenharmony_ci info->screen_base = NULL; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_cistatic int savage_init_hw(struct savagefb_par *par) 181262306a36Sopenharmony_ci{ 181362306a36Sopenharmony_ci unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci static unsigned char RamSavage3D[] = { 8, 4, 4, 2 }; 181662306a36Sopenharmony_ci static unsigned char RamSavage4[] = { 2, 4, 8, 12, 16, 32, 64, 32 }; 181762306a36Sopenharmony_ci static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 }; 181862306a36Sopenharmony_ci static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 }; 181962306a36Sopenharmony_ci int videoRam, videoRambytes, dvi; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci DBG("savage_init_hw"); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci /* unprotect CRTC[0-7] */ 182462306a36Sopenharmony_ci vga_out8(0x3d4, 0x11, par); 182562306a36Sopenharmony_ci tmp = vga_in8(0x3d5, par); 182662306a36Sopenharmony_ci vga_out8(0x3d5, tmp & 0x7f, par); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* unlock extended regs */ 182962306a36Sopenharmony_ci vga_out16(0x3d4, 0x4838, par); 183062306a36Sopenharmony_ci vga_out16(0x3d4, 0xa039, par); 183162306a36Sopenharmony_ci vga_out16(0x3c4, 0x0608, par); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci vga_out8(0x3d4, 0x40, par); 183462306a36Sopenharmony_ci tmp = vga_in8(0x3d5, par); 183562306a36Sopenharmony_ci vga_out8(0x3d5, tmp & ~0x01, par); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* unlock sys regs */ 183862306a36Sopenharmony_ci vga_out8(0x3d4, 0x38, par); 183962306a36Sopenharmony_ci vga_out8(0x3d5, 0x48, par); 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* Unlock system registers. */ 184262306a36Sopenharmony_ci vga_out16(0x3d4, 0x4838, par); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci /* Next go on to detect amount of installed ram */ 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci vga_out8(0x3d4, 0x36, par); /* for register CR36 (CONFG_REG1), */ 184762306a36Sopenharmony_ci config1 = vga_in8(0x3d5, par); /* get amount of vram installed */ 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* Compute the amount of video memory and offscreen memory. */ 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci switch (par->chip) { 185262306a36Sopenharmony_ci case S3_SAVAGE3D: 185362306a36Sopenharmony_ci videoRam = RamSavage3D[(config1 & 0xC0) >> 6 ] * 1024; 185462306a36Sopenharmony_ci break; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci case S3_SAVAGE4: 185762306a36Sopenharmony_ci /* 185862306a36Sopenharmony_ci * The Savage4 has one ugly special case to consider. On 185962306a36Sopenharmony_ci * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB 186062306a36Sopenharmony_ci * when it really means 8MB. Why do it the same when you 186162306a36Sopenharmony_ci * can do it different... 186262306a36Sopenharmony_ci */ 186362306a36Sopenharmony_ci vga_out8(0x3d4, 0x68, par); /* memory control 1 */ 186462306a36Sopenharmony_ci if ((vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6)) 186562306a36Sopenharmony_ci RamSavage4[1] = 8; 186662306a36Sopenharmony_ci fallthrough; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci case S3_SAVAGE2000: 186962306a36Sopenharmony_ci videoRam = RamSavage4[(config1 & 0xE0) >> 5] * 1024; 187062306a36Sopenharmony_ci break; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci case S3_SAVAGE_MX: 187362306a36Sopenharmony_ci case S3_SUPERSAVAGE: 187462306a36Sopenharmony_ci videoRam = RamSavageMX[(config1 & 0x0E) >> 1] * 1024; 187562306a36Sopenharmony_ci break; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci case S3_PROSAVAGE: 187862306a36Sopenharmony_ci case S3_PROSAVAGEDDR: 187962306a36Sopenharmony_ci case S3_TWISTER: 188062306a36Sopenharmony_ci videoRam = RamSavageNB[(config1 & 0xE0) >> 5] * 1024; 188162306a36Sopenharmony_ci break; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci default: 188462306a36Sopenharmony_ci /* How did we get here? */ 188562306a36Sopenharmony_ci videoRam = 0; 188662306a36Sopenharmony_ci break; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci videoRambytes = videoRam * 1024; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci printk(KERN_INFO "savagefb: probed videoram: %dk\n", videoRam); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* reset graphics engine to avoid memory corruption */ 189462306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 189562306a36Sopenharmony_ci cr66 = vga_in8(0x3d5, par); 189662306a36Sopenharmony_ci vga_out8(0x3d5, cr66 | 0x02, par); 189762306a36Sopenharmony_ci usleep_range(10000, 11000); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci vga_out8(0x3d4, 0x66, par); 190062306a36Sopenharmony_ci vga_out8(0x3d5, cr66 & ~0x02, par); /* clear reset flag */ 190162306a36Sopenharmony_ci usleep_range(10000, 11000); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci /* 190562306a36Sopenharmony_ci * reset memory interface, 3D engine, AGP master, PCI master, 190662306a36Sopenharmony_ci * master engine unit, motion compensation/LPB 190762306a36Sopenharmony_ci */ 190862306a36Sopenharmony_ci vga_out8(0x3d4, 0x3f, par); 190962306a36Sopenharmony_ci cr3f = vga_in8(0x3d5, par); 191062306a36Sopenharmony_ci vga_out8(0x3d5, cr3f | 0x08, par); 191162306a36Sopenharmony_ci usleep_range(10000, 11000); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci vga_out8(0x3d4, 0x3f, par); 191462306a36Sopenharmony_ci vga_out8(0x3d5, cr3f & ~0x08, par); /* clear reset flags */ 191562306a36Sopenharmony_ci usleep_range(10000, 11000); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci /* Savage ramdac speeds */ 191862306a36Sopenharmony_ci par->numClocks = 4; 191962306a36Sopenharmony_ci par->clock[0] = 250000; 192062306a36Sopenharmony_ci par->clock[1] = 250000; 192162306a36Sopenharmony_ci par->clock[2] = 220000; 192262306a36Sopenharmony_ci par->clock[3] = 220000; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci /* detect current mclk */ 192562306a36Sopenharmony_ci vga_out8(0x3c4, 0x08, par); 192662306a36Sopenharmony_ci sr8 = vga_in8(0x3c5, par); 192762306a36Sopenharmony_ci vga_out8(0x3c5, 0x06, par); 192862306a36Sopenharmony_ci vga_out8(0x3c4, 0x10, par); 192962306a36Sopenharmony_ci n = vga_in8(0x3c5, par); 193062306a36Sopenharmony_ci vga_out8(0x3c4, 0x11, par); 193162306a36Sopenharmony_ci m = vga_in8(0x3c5, par); 193262306a36Sopenharmony_ci vga_out8(0x3c4, 0x08, par); 193362306a36Sopenharmony_ci vga_out8(0x3c5, sr8, par); 193462306a36Sopenharmony_ci m &= 0x7f; 193562306a36Sopenharmony_ci n1 = n & 0x1f; 193662306a36Sopenharmony_ci n2 = (n >> 5) & 0x03; 193762306a36Sopenharmony_ci par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100; 193862306a36Sopenharmony_ci printk(KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n", 193962306a36Sopenharmony_ci par->MCLK); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci /* check for DVI/flat panel */ 194262306a36Sopenharmony_ci dvi = 0; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci if (par->chip == S3_SAVAGE4) { 194562306a36Sopenharmony_ci unsigned char sr30 = 0x00; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci vga_out8(0x3c4, 0x30, par); 194862306a36Sopenharmony_ci /* clear bit 1 */ 194962306a36Sopenharmony_ci vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x02, par); 195062306a36Sopenharmony_ci sr30 = vga_in8(0x3c5, par); 195162306a36Sopenharmony_ci if (sr30 & 0x02 /*0x04 */) { 195262306a36Sopenharmony_ci dvi = 1; 195362306a36Sopenharmony_ci printk("savagefb: Digital Flat Panel Detected\n"); 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci if ((S3_SAVAGE_MOBILE_SERIES(par->chip) || 195862306a36Sopenharmony_ci S3_MOBILE_TWISTER_SERIES(par->chip)) && !par->crtonly) 195962306a36Sopenharmony_ci par->display_type = DISP_LCD; 196062306a36Sopenharmony_ci else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi)) 196162306a36Sopenharmony_ci par->display_type = DISP_DFP; 196262306a36Sopenharmony_ci else 196362306a36Sopenharmony_ci par->display_type = DISP_CRT; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci /* Check LCD panel parrmation */ 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (par->display_type == DISP_LCD) { 196862306a36Sopenharmony_ci unsigned char cr6b = VGArCR(0x6b, par); 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci int panelX = (VGArSEQ(0x61, par) + 197162306a36Sopenharmony_ci ((VGArSEQ(0x66, par) & 0x02) << 7) + 1) * 8; 197262306a36Sopenharmony_ci int panelY = (VGArSEQ(0x69, par) + 197362306a36Sopenharmony_ci ((VGArSEQ(0x6e, par) & 0x70) << 4) + 1); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci char * sTechnology = "Unknown"; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci /* OK, I admit it. I don't know how to limit the max dot clock 197862306a36Sopenharmony_ci * for LCD panels of various sizes. I thought I copied the 197962306a36Sopenharmony_ci * formula from the BIOS, but many users have parrmed me of 198062306a36Sopenharmony_ci * my folly. 198162306a36Sopenharmony_ci * 198262306a36Sopenharmony_ci * Instead, I'll abandon any attempt to automatically limit the 198362306a36Sopenharmony_ci * clock, and add an LCDClock option to XF86Config. Some day, 198462306a36Sopenharmony_ci * I should come back to this. 198562306a36Sopenharmony_ci */ 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */ 198862306a36Sopenharmony_ci ActiveCRT = 0x01, 198962306a36Sopenharmony_ci ActiveLCD = 0x02, 199062306a36Sopenharmony_ci ActiveTV = 0x04, 199162306a36Sopenharmony_ci ActiveCRT2 = 0x20, 199262306a36Sopenharmony_ci ActiveDUO = 0x80 199362306a36Sopenharmony_ci }; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci if ((VGArSEQ(0x39, par) & 0x03) == 0) { 199662306a36Sopenharmony_ci sTechnology = "TFT"; 199762306a36Sopenharmony_ci } else if ((VGArSEQ(0x30, par) & 0x01) == 0) { 199862306a36Sopenharmony_ci sTechnology = "DSTN"; 199962306a36Sopenharmony_ci } else { 200062306a36Sopenharmony_ci sTechnology = "STN"; 200162306a36Sopenharmony_ci } 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci printk(KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n", 200462306a36Sopenharmony_ci panelX, panelY, sTechnology, 200562306a36Sopenharmony_ci cr6b & ActiveLCD ? "and active" : "but not active"); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci if (cr6b & ActiveLCD) { 200862306a36Sopenharmony_ci /* 200962306a36Sopenharmony_ci * If the LCD is active and panel expansion is enabled, 201062306a36Sopenharmony_ci * we probably want to kill the HW cursor. 201162306a36Sopenharmony_ci */ 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci printk(KERN_INFO "savagefb: Limiting video mode to " 201462306a36Sopenharmony_ci "%dx%d\n", panelX, panelY); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci par->SavagePanelWidth = panelX; 201762306a36Sopenharmony_ci par->SavagePanelHeight = panelY; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci } else 202062306a36Sopenharmony_ci par->display_type = DISP_CRT; 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci savage_get_default_par(par, &par->state); 202462306a36Sopenharmony_ci par->save = par->state; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci if (S3_SAVAGE4_SERIES(par->chip)) { 202762306a36Sopenharmony_ci /* 202862306a36Sopenharmony_ci * The Savage4 and ProSavage have COB coherency bugs which 202962306a36Sopenharmony_ci * render the buffer useless. We disable it. 203062306a36Sopenharmony_ci */ 203162306a36Sopenharmony_ci par->cob_index = 2; 203262306a36Sopenharmony_ci par->cob_size = 0x8000 << par->cob_index; 203362306a36Sopenharmony_ci par->cob_offset = videoRambytes; 203462306a36Sopenharmony_ci } else { 203562306a36Sopenharmony_ci /* We use 128kB for the COB on all chips. */ 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci par->cob_index = 7; 203862306a36Sopenharmony_ci par->cob_size = 0x400 << par->cob_index; 203962306a36Sopenharmony_ci par->cob_offset = videoRambytes - par->cob_size; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci return videoRambytes; 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_cistatic int savage_init_fb_info(struct fb_info *info, struct pci_dev *dev, 204662306a36Sopenharmony_ci const struct pci_device_id *id) 204762306a36Sopenharmony_ci{ 204862306a36Sopenharmony_ci struct savagefb_par *par = info->par; 204962306a36Sopenharmony_ci int err = 0; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci par->pcidev = dev; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 205462306a36Sopenharmony_ci info->fix.type_aux = 0; 205562306a36Sopenharmony_ci info->fix.ypanstep = 1; 205662306a36Sopenharmony_ci info->fix.ywrapstep = 0; 205762306a36Sopenharmony_ci info->fix.accel = id->driver_data; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci switch (info->fix.accel) { 206062306a36Sopenharmony_ci case FB_ACCEL_SUPERSAVAGE: 206162306a36Sopenharmony_ci par->chip = S3_SUPERSAVAGE; 206262306a36Sopenharmony_ci snprintf(info->fix.id, 16, "SuperSavage"); 206362306a36Sopenharmony_ci break; 206462306a36Sopenharmony_ci case FB_ACCEL_SAVAGE4: 206562306a36Sopenharmony_ci par->chip = S3_SAVAGE4; 206662306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage4"); 206762306a36Sopenharmony_ci break; 206862306a36Sopenharmony_ci case FB_ACCEL_SAVAGE3D: 206962306a36Sopenharmony_ci par->chip = S3_SAVAGE3D; 207062306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage3D"); 207162306a36Sopenharmony_ci break; 207262306a36Sopenharmony_ci case FB_ACCEL_SAVAGE3D_MV: 207362306a36Sopenharmony_ci par->chip = S3_SAVAGE3D; 207462306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage3D-MV"); 207562306a36Sopenharmony_ci break; 207662306a36Sopenharmony_ci case FB_ACCEL_SAVAGE2000: 207762306a36Sopenharmony_ci par->chip = S3_SAVAGE2000; 207862306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage2000"); 207962306a36Sopenharmony_ci break; 208062306a36Sopenharmony_ci case FB_ACCEL_SAVAGE_MX_MV: 208162306a36Sopenharmony_ci par->chip = S3_SAVAGE_MX; 208262306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage/MX-MV"); 208362306a36Sopenharmony_ci break; 208462306a36Sopenharmony_ci case FB_ACCEL_SAVAGE_MX: 208562306a36Sopenharmony_ci par->chip = S3_SAVAGE_MX; 208662306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage/MX"); 208762306a36Sopenharmony_ci break; 208862306a36Sopenharmony_ci case FB_ACCEL_SAVAGE_IX_MV: 208962306a36Sopenharmony_ci par->chip = S3_SAVAGE_MX; 209062306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage/IX-MV"); 209162306a36Sopenharmony_ci break; 209262306a36Sopenharmony_ci case FB_ACCEL_SAVAGE_IX: 209362306a36Sopenharmony_ci par->chip = S3_SAVAGE_MX; 209462306a36Sopenharmony_ci snprintf(info->fix.id, 16, "Savage/IX"); 209562306a36Sopenharmony_ci break; 209662306a36Sopenharmony_ci case FB_ACCEL_PROSAVAGE_PM: 209762306a36Sopenharmony_ci par->chip = S3_PROSAVAGE; 209862306a36Sopenharmony_ci snprintf(info->fix.id, 16, "ProSavagePM"); 209962306a36Sopenharmony_ci break; 210062306a36Sopenharmony_ci case FB_ACCEL_PROSAVAGE_KM: 210162306a36Sopenharmony_ci par->chip = S3_PROSAVAGE; 210262306a36Sopenharmony_ci snprintf(info->fix.id, 16, "ProSavageKM"); 210362306a36Sopenharmony_ci break; 210462306a36Sopenharmony_ci case FB_ACCEL_S3TWISTER_P: 210562306a36Sopenharmony_ci par->chip = S3_TWISTER; 210662306a36Sopenharmony_ci snprintf(info->fix.id, 16, "TwisterP"); 210762306a36Sopenharmony_ci break; 210862306a36Sopenharmony_ci case FB_ACCEL_S3TWISTER_K: 210962306a36Sopenharmony_ci par->chip = S3_TWISTER; 211062306a36Sopenharmony_ci snprintf(info->fix.id, 16, "TwisterK"); 211162306a36Sopenharmony_ci break; 211262306a36Sopenharmony_ci case FB_ACCEL_PROSAVAGE_DDR: 211362306a36Sopenharmony_ci par->chip = S3_PROSAVAGEDDR; 211462306a36Sopenharmony_ci snprintf(info->fix.id, 16, "ProSavageDDR"); 211562306a36Sopenharmony_ci break; 211662306a36Sopenharmony_ci case FB_ACCEL_PROSAVAGE_DDRK: 211762306a36Sopenharmony_ci par->chip = S3_PROSAVAGEDDR; 211862306a36Sopenharmony_ci snprintf(info->fix.id, 16, "ProSavage8"); 211962306a36Sopenharmony_ci break; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci if (S3_SAVAGE3D_SERIES(par->chip)) { 212362306a36Sopenharmony_ci par->SavageWaitIdle = savage3D_waitidle; 212462306a36Sopenharmony_ci par->SavageWaitFifo = savage3D_waitfifo; 212562306a36Sopenharmony_ci } else if (S3_SAVAGE4_SERIES(par->chip) || 212662306a36Sopenharmony_ci S3_SUPERSAVAGE == par->chip) { 212762306a36Sopenharmony_ci par->SavageWaitIdle = savage4_waitidle; 212862306a36Sopenharmony_ci par->SavageWaitFifo = savage4_waitfifo; 212962306a36Sopenharmony_ci } else { 213062306a36Sopenharmony_ci par->SavageWaitIdle = savage2000_waitidle; 213162306a36Sopenharmony_ci par->SavageWaitFifo = savage2000_waitfifo; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci info->var.nonstd = 0; 213562306a36Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 213662306a36Sopenharmony_ci info->var.width = -1; 213762306a36Sopenharmony_ci info->var.height = -1; 213862306a36Sopenharmony_ci info->var.accel_flags = 0; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci info->fbops = &savagefb_ops; 214162306a36Sopenharmony_ci info->flags = FBINFO_HWACCEL_YPAN | 214262306a36Sopenharmony_ci FBINFO_HWACCEL_XPAN; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL) 214762306a36Sopenharmony_ci /* FIFO size + padding for commands */ 214862306a36Sopenharmony_ci info->pixmap.addr = kcalloc(8, 1024, GFP_KERNEL); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci err = -ENOMEM; 215162306a36Sopenharmony_ci if (info->pixmap.addr) { 215262306a36Sopenharmony_ci info->pixmap.size = 8*1024; 215362306a36Sopenharmony_ci info->pixmap.scan_align = 4; 215462306a36Sopenharmony_ci info->pixmap.buf_align = 4; 215562306a36Sopenharmony_ci info->pixmap.access_align = 32; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci err = fb_alloc_cmap(&info->cmap, NR_PALETTE, 0); 215862306a36Sopenharmony_ci if (!err) 215962306a36Sopenharmony_ci info->flags |= FBINFO_HWACCEL_COPYAREA | 216062306a36Sopenharmony_ci FBINFO_HWACCEL_FILLRECT | 216162306a36Sopenharmony_ci FBINFO_HWACCEL_IMAGEBLIT; 216262306a36Sopenharmony_ci else 216362306a36Sopenharmony_ci kfree(info->pixmap.addr); 216462306a36Sopenharmony_ci } 216562306a36Sopenharmony_ci#endif 216662306a36Sopenharmony_ci return err; 216762306a36Sopenharmony_ci} 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_cistatic int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id) 217262306a36Sopenharmony_ci{ 217362306a36Sopenharmony_ci struct fb_info *info; 217462306a36Sopenharmony_ci struct savagefb_par *par; 217562306a36Sopenharmony_ci u_int h_sync, v_sync; 217662306a36Sopenharmony_ci unsigned char __maybe_unused *edid; 217762306a36Sopenharmony_ci int err, lpitch; 217862306a36Sopenharmony_ci int video_len; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci DBG("savagefb_probe"); 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci err = aperture_remove_conflicting_pci_devices(dev, "savagefb"); 218362306a36Sopenharmony_ci if (err) 218462306a36Sopenharmony_ci return err; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev); 218762306a36Sopenharmony_ci if (!info) 218862306a36Sopenharmony_ci return -ENOMEM; 218962306a36Sopenharmony_ci par = info->par; 219062306a36Sopenharmony_ci mutex_init(&par->open_lock); 219162306a36Sopenharmony_ci err = pci_enable_device(dev); 219262306a36Sopenharmony_ci if (err) 219362306a36Sopenharmony_ci goto failed_enable; 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci if ((err = pci_request_regions(dev, "savagefb"))) { 219662306a36Sopenharmony_ci printk(KERN_ERR "cannot request PCI regions\n"); 219762306a36Sopenharmony_ci goto failed_enable; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci err = -ENOMEM; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci if ((err = savage_init_fb_info(info, dev, id))) 220362306a36Sopenharmony_ci goto failed_init; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci err = savage_map_mmio(info); 220662306a36Sopenharmony_ci if (err) 220762306a36Sopenharmony_ci goto failed_mmio; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci video_len = savage_init_hw(par); 221062306a36Sopenharmony_ci /* FIXME: can't be negative */ 221162306a36Sopenharmony_ci if (video_len < 0) { 221262306a36Sopenharmony_ci err = video_len; 221362306a36Sopenharmony_ci goto failed_mmio; 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci err = savage_map_video(info, video_len); 221762306a36Sopenharmony_ci if (err) 221862306a36Sopenharmony_ci goto failed_video; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci INIT_LIST_HEAD(&info->modelist); 222162306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_I2C) 222262306a36Sopenharmony_ci savagefb_create_i2c_busses(info); 222362306a36Sopenharmony_ci savagefb_probe_i2c_connector(info, &edid); 222462306a36Sopenharmony_ci fb_edid_to_monspecs(edid, &info->monspecs); 222562306a36Sopenharmony_ci kfree(edid); 222662306a36Sopenharmony_ci fb_videomode_to_modelist(info->monspecs.modedb, 222762306a36Sopenharmony_ci info->monspecs.modedb_len, 222862306a36Sopenharmony_ci &info->modelist); 222962306a36Sopenharmony_ci#endif 223062306a36Sopenharmony_ci info->var = savagefb_var800x600x8; 223162306a36Sopenharmony_ci /* if a panel was detected, default to a CVT mode instead */ 223262306a36Sopenharmony_ci if (par->SavagePanelWidth) { 223362306a36Sopenharmony_ci struct fb_videomode cvt_mode; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci memset(&cvt_mode, 0, sizeof(cvt_mode)); 223662306a36Sopenharmony_ci cvt_mode.xres = par->SavagePanelWidth; 223762306a36Sopenharmony_ci cvt_mode.yres = par->SavagePanelHeight; 223862306a36Sopenharmony_ci cvt_mode.refresh = 60; 223962306a36Sopenharmony_ci /* FIXME: if we know there is only the panel 224062306a36Sopenharmony_ci * we can enable reduced blanking as well */ 224162306a36Sopenharmony_ci if (fb_find_mode_cvt(&cvt_mode, 0, 0)) 224262306a36Sopenharmony_ci printk(KERN_WARNING "No CVT mode found for panel\n"); 224362306a36Sopenharmony_ci else if (fb_find_mode(&info->var, info, NULL, NULL, 0, 224462306a36Sopenharmony_ci &cvt_mode, 0) != 3) 224562306a36Sopenharmony_ci info->var = savagefb_var800x600x8; 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci if (mode_option) { 224962306a36Sopenharmony_ci fb_find_mode(&info->var, info, mode_option, 225062306a36Sopenharmony_ci info->monspecs.modedb, info->monspecs.modedb_len, 225162306a36Sopenharmony_ci NULL, 8); 225262306a36Sopenharmony_ci } else if (info->monspecs.modedb != NULL) { 225362306a36Sopenharmony_ci const struct fb_videomode *mode; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci mode = fb_find_best_display(&info->monspecs, &info->modelist); 225662306a36Sopenharmony_ci savage_update_var(&info->var, mode); 225762306a36Sopenharmony_ci } 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci /* maximize virtual vertical length */ 226062306a36Sopenharmony_ci lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3); 226162306a36Sopenharmony_ci info->var.yres_virtual = info->fix.smem_len/lpitch; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci if (info->var.yres_virtual < info->var.yres) { 226462306a36Sopenharmony_ci err = -ENOMEM; 226562306a36Sopenharmony_ci goto failed; 226662306a36Sopenharmony_ci } 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL) 226962306a36Sopenharmony_ci /* 227062306a36Sopenharmony_ci * The clipping coordinates are masked with 0xFFF, so limit our 227162306a36Sopenharmony_ci * virtual resolutions to these sizes. 227262306a36Sopenharmony_ci */ 227362306a36Sopenharmony_ci if (info->var.yres_virtual > 0x1000) 227462306a36Sopenharmony_ci info->var.yres_virtual = 0x1000; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci if (info->var.xres_virtual > 0x1000) 227762306a36Sopenharmony_ci info->var.xres_virtual = 0x1000; 227862306a36Sopenharmony_ci#endif 227962306a36Sopenharmony_ci savagefb_check_var(&info->var, info); 228062306a36Sopenharmony_ci savagefb_set_fix(info); 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci /* 228362306a36Sopenharmony_ci * Calculate the hsync and vsync frequencies. Note that 228462306a36Sopenharmony_ci * we split the 1e12 constant up so that we can preserve 228562306a36Sopenharmony_ci * the precision and fit the results into 32-bit registers. 228662306a36Sopenharmony_ci * (1953125000 * 512 = 1e12) 228762306a36Sopenharmony_ci */ 228862306a36Sopenharmony_ci h_sync = 1953125000 / info->var.pixclock; 228962306a36Sopenharmony_ci h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin + 229062306a36Sopenharmony_ci info->var.right_margin + 229162306a36Sopenharmony_ci info->var.hsync_len); 229262306a36Sopenharmony_ci v_sync = h_sync / (info->var.yres + info->var.upper_margin + 229362306a36Sopenharmony_ci info->var.lower_margin + info->var.vsync_len); 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": " 229662306a36Sopenharmony_ci "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n", 229762306a36Sopenharmony_ci info->fix.smem_len >> 10, 229862306a36Sopenharmony_ci info->var.xres, info->var.yres, 229962306a36Sopenharmony_ci h_sync / 1000, h_sync % 1000, v_sync); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 230362306a36Sopenharmony_ci info->monspecs.modedb = NULL; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci err = register_framebuffer(info); 230662306a36Sopenharmony_ci if (err < 0) 230762306a36Sopenharmony_ci goto failed; 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci printk(KERN_INFO "fb: S3 %s frame buffer device\n", 231062306a36Sopenharmony_ci info->fix.id); 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci /* 231362306a36Sopenharmony_ci * Our driver data 231462306a36Sopenharmony_ci */ 231562306a36Sopenharmony_ci pci_set_drvdata(dev, info); 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci return 0; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci failed: 232062306a36Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_I2C 232162306a36Sopenharmony_ci savagefb_delete_i2c_busses(info); 232262306a36Sopenharmony_ci#endif 232362306a36Sopenharmony_ci fb_alloc_cmap(&info->cmap, 0, 0); 232462306a36Sopenharmony_ci savage_unmap_video(info); 232562306a36Sopenharmony_ci failed_video: 232662306a36Sopenharmony_ci savage_unmap_mmio(info); 232762306a36Sopenharmony_ci failed_mmio: 232862306a36Sopenharmony_ci kfree(info->pixmap.addr); 232962306a36Sopenharmony_ci failed_init: 233062306a36Sopenharmony_ci pci_release_regions(dev); 233162306a36Sopenharmony_ci failed_enable: 233262306a36Sopenharmony_ci framebuffer_release(info); 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci return err; 233562306a36Sopenharmony_ci} 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_cistatic void savagefb_remove(struct pci_dev *dev) 233862306a36Sopenharmony_ci{ 233962306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(dev); 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci DBG("savagefb_remove"); 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci if (info) { 234462306a36Sopenharmony_ci unregister_framebuffer(info); 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_I2C 234762306a36Sopenharmony_ci savagefb_delete_i2c_busses(info); 234862306a36Sopenharmony_ci#endif 234962306a36Sopenharmony_ci fb_alloc_cmap(&info->cmap, 0, 0); 235062306a36Sopenharmony_ci savage_unmap_video(info); 235162306a36Sopenharmony_ci savage_unmap_mmio(info); 235262306a36Sopenharmony_ci kfree(info->pixmap.addr); 235362306a36Sopenharmony_ci pci_release_regions(dev); 235462306a36Sopenharmony_ci framebuffer_release(info); 235562306a36Sopenharmony_ci } 235662306a36Sopenharmony_ci} 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_cistatic int savagefb_suspend_late(struct device *dev, pm_message_t mesg) 235962306a36Sopenharmony_ci{ 236062306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 236162306a36Sopenharmony_ci struct savagefb_par *par = info->par; 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci DBG("savagefb_suspend"); 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci if (mesg.event == PM_EVENT_PRETHAW) 236662306a36Sopenharmony_ci mesg.event = PM_EVENT_FREEZE; 236762306a36Sopenharmony_ci par->pm_state = mesg.event; 236862306a36Sopenharmony_ci dev->power.power_state = mesg; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci /* 237162306a36Sopenharmony_ci * For PM_EVENT_FREEZE, do not power down so the console 237262306a36Sopenharmony_ci * can remain active. 237362306a36Sopenharmony_ci */ 237462306a36Sopenharmony_ci if (mesg.event == PM_EVENT_FREEZE) 237562306a36Sopenharmony_ci return 0; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci console_lock(); 237862306a36Sopenharmony_ci fb_set_suspend(info, 1); 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci if (info->fbops->fb_sync) 238162306a36Sopenharmony_ci info->fbops->fb_sync(info); 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci savagefb_blank(FB_BLANK_POWERDOWN, info); 238462306a36Sopenharmony_ci savage_set_default_par(par, &par->save); 238562306a36Sopenharmony_ci savage_disable_mmio(par); 238662306a36Sopenharmony_ci console_unlock(); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci return 0; 238962306a36Sopenharmony_ci} 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_cistatic int __maybe_unused savagefb_suspend(struct device *dev) 239262306a36Sopenharmony_ci{ 239362306a36Sopenharmony_ci return savagefb_suspend_late(dev, PMSG_SUSPEND); 239462306a36Sopenharmony_ci} 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_cistatic int __maybe_unused savagefb_hibernate(struct device *dev) 239762306a36Sopenharmony_ci{ 239862306a36Sopenharmony_ci return savagefb_suspend_late(dev, PMSG_HIBERNATE); 239962306a36Sopenharmony_ci} 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_cistatic int __maybe_unused savagefb_freeze(struct device *dev) 240262306a36Sopenharmony_ci{ 240362306a36Sopenharmony_ci return savagefb_suspend_late(dev, PMSG_FREEZE); 240462306a36Sopenharmony_ci} 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_cistatic int __maybe_unused savagefb_resume(struct device *dev) 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 240962306a36Sopenharmony_ci struct savagefb_par *par = info->par; 241062306a36Sopenharmony_ci int cur_state = par->pm_state; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci DBG("savage_resume"); 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci par->pm_state = PM_EVENT_ON; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci /* 241762306a36Sopenharmony_ci * The adapter was not powered down coming back from a 241862306a36Sopenharmony_ci * PM_EVENT_FREEZE. 241962306a36Sopenharmony_ci */ 242062306a36Sopenharmony_ci if (cur_state == PM_EVENT_FREEZE) 242162306a36Sopenharmony_ci return 0; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci console_lock(); 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci savage_enable_mmio(par); 242662306a36Sopenharmony_ci savage_init_hw(par); 242762306a36Sopenharmony_ci savagefb_set_par(info); 242862306a36Sopenharmony_ci fb_set_suspend(info, 0); 242962306a36Sopenharmony_ci savagefb_blank(FB_BLANK_UNBLANK, info); 243062306a36Sopenharmony_ci console_unlock(); 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci return 0; 243362306a36Sopenharmony_ci} 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_cistatic const struct dev_pm_ops savagefb_pm_ops = { 243662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 243762306a36Sopenharmony_ci .suspend = savagefb_suspend, 243862306a36Sopenharmony_ci .resume = savagefb_resume, 243962306a36Sopenharmony_ci .freeze = savagefb_freeze, 244062306a36Sopenharmony_ci .thaw = savagefb_resume, 244162306a36Sopenharmony_ci .poweroff = savagefb_hibernate, 244262306a36Sopenharmony_ci .restore = savagefb_resume, 244362306a36Sopenharmony_ci#endif 244462306a36Sopenharmony_ci}; 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_cistatic const struct pci_device_id savagefb_devices[] = { 244762306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128, 244862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64, 245162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C, 245462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR, 245762306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR, 246062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR, 246362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR, 246662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR, 246962306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR, 247262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE}, 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4, 247562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4}, 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D, 247862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D}, 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV, 248162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV}, 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000, 248462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000}, 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV, 248762306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV}, 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX, 249062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX}, 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV, 249362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV}, 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX, 249662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX}, 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM, 249962306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM}, 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM, 250262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM}, 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P, 250562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P}, 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K, 250862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K}, 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR, 251162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR}, 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK, 251462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK}, 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0, 0} 251762306a36Sopenharmony_ci}; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, savagefb_devices); 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_cistatic struct pci_driver savagefb_driver = { 252262306a36Sopenharmony_ci .name = "savagefb", 252362306a36Sopenharmony_ci .id_table = savagefb_devices, 252462306a36Sopenharmony_ci .probe = savagefb_probe, 252562306a36Sopenharmony_ci .driver.pm = &savagefb_pm_ops, 252662306a36Sopenharmony_ci .remove = savagefb_remove, 252762306a36Sopenharmony_ci}; 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci/* **************************** exit-time only **************************** */ 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_cistatic void __exit savage_done(void) 253262306a36Sopenharmony_ci{ 253362306a36Sopenharmony_ci DBG("savage_done"); 253462306a36Sopenharmony_ci pci_unregister_driver(&savagefb_driver); 253562306a36Sopenharmony_ci} 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci/* ************************* init in-kernel code ************************** */ 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_cistatic int __init savagefb_setup(char *options) 254162306a36Sopenharmony_ci{ 254262306a36Sopenharmony_ci#ifndef MODULE 254362306a36Sopenharmony_ci char *this_opt; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci if (!options || !*options) 254662306a36Sopenharmony_ci return 0; 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 254962306a36Sopenharmony_ci mode_option = this_opt; 255062306a36Sopenharmony_ci } 255162306a36Sopenharmony_ci#endif /* !MODULE */ 255262306a36Sopenharmony_ci return 0; 255362306a36Sopenharmony_ci} 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_cistatic int __init savagefb_init(void) 255662306a36Sopenharmony_ci{ 255762306a36Sopenharmony_ci char *option; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci DBG("savagefb_init"); 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci if (fb_modesetting_disabled("savagefb")) 256262306a36Sopenharmony_ci return -ENODEV; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci if (fb_get_options("savagefb", &option)) 256562306a36Sopenharmony_ci return -ENODEV; 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ci savagefb_setup(option); 256862306a36Sopenharmony_ci return pci_register_driver(&savagefb_driver); 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci} 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_cimodule_init(savagefb_init); 257362306a36Sopenharmony_cimodule_exit(savage_done); 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_cimodule_param(mode_option, charp, 0); 257662306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Specify initial video mode"); 2577