📄 sis_main.c
字号:
/* * SiS 300/540/630[S]/730[S], * SiS 315[E|PRO]/550/[M]65x/[M]66x[F|M|G]X/[M]74x[GX]/330/[M]76x[GX], * XGI V3XT/V5/V8, Z7 * frame buffer driver for Linux kernels >= 2.4.14 and >=2.6.3 * * Copyright (C) 2001-2005 Thomas Winischhofer, Vienna, Austria. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the named License, * or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA * * Author: Thomas Winischhofer <thomas@winischhofer.net> * * Author of (practically wiped) code base: * SiS (www.sis.com) * Copyright (C) 1999 Silicon Integrated Systems, Inc. * * See http://www.winischhofer.net/ for more information and updates * * Originally based on the VBE 2.0 compliant graphic boards framebuffer driver, * which is (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> * */#include <linux/version.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/spinlock.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)#include <linux/tty.h>#else#include <linux/screen_info.h>#endif#include <linux/slab.h>#include <linux/fb.h>#include <linux/selection.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/vmalloc.h>#include <linux/capability.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/uaccess.h>#include <asm/io.h>#ifdef CONFIG_MTRR#include <asm/mtrr.h>#endif#include "sis.h"#include "sis_main.h"static void sisfb_handle_command(struct sis_video_info *ivideo, struct sisfb_cmd *sisfb_command);/* ------------------ Internal helper routines ----------------- */static void __initsisfb_setdefaultparms(void){ sisfb_off = 0; sisfb_parm_mem = 0; sisfb_accel = -1; sisfb_ypan = -1; sisfb_max = -1; sisfb_userom = -1; sisfb_useoem = -1; sisfb_mode_idx = -1; sisfb_parm_rate = -1; sisfb_crt1off = 0; sisfb_forcecrt1 = -1; sisfb_crt2type = -1; sisfb_crt2flags = 0; sisfb_pdc = 0xff; sisfb_pdca = 0xff; sisfb_scalelcd = -1; sisfb_specialtiming = CUT_NONE; sisfb_lvdshl = -1; sisfb_dstn = 0; sisfb_fstn = 0; sisfb_tvplug = -1; sisfb_tvstd = -1; sisfb_tvxposoffset = 0; sisfb_tvyposoffset = 0; sisfb_nocrt2rate = 0;#if !defined(__i386__) && !defined(__x86_64__) sisfb_resetcard = 0; sisfb_videoram = 0;#endif}/* ------------- Parameter parsing -------------- */static void __devinitsisfb_search_vesamode(unsigned int vesamode, bool quiet){ int i = 0, j = 0; /* We don't know the hardware specs yet and there is no ivideo */ if(vesamode == 0) { if(!quiet) printk(KERN_ERR "sisfb: Invalid mode. Using default.\n"); sisfb_mode_idx = DEFAULT_MODE; return; } vesamode &= 0x1dff; /* Clean VESA mode number from other flags */ while(sisbios_mode[i++].mode_no[0] != 0) { if( (sisbios_mode[i-1].vesa_mode_no_1 == vesamode) || (sisbios_mode[i-1].vesa_mode_no_2 == vesamode) ) { if(sisfb_fstn) { if(sisbios_mode[i-1].mode_no[1] == 0x50 || sisbios_mode[i-1].mode_no[1] == 0x56 || sisbios_mode[i-1].mode_no[1] == 0x53) continue; } else { if(sisbios_mode[i-1].mode_no[1] == 0x5a || sisbios_mode[i-1].mode_no[1] == 0x5b) continue; } sisfb_mode_idx = i - 1; j = 1; break; } } if((!j) && !quiet) printk(KERN_ERR "sisfb: Invalid VESA mode 0x%x'\n", vesamode);}static void __devinitsisfb_search_mode(char *name, bool quiet){ unsigned int j = 0, xres = 0, yres = 0, depth = 0, rate = 0; int i = 0; char strbuf[16], strbuf1[20]; char *nameptr = name; /* We don't know the hardware specs yet and there is no ivideo */ if(name == NULL) { if(!quiet) printk(KERN_ERR "sisfb: Internal error, using default mode.\n"); sisfb_mode_idx = DEFAULT_MODE; return; } if(!strnicmp(name, sisbios_mode[MODE_INDEX_NONE].name, strlen(name))) { if(!quiet) printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n"); sisfb_mode_idx = DEFAULT_MODE; return; } if(strlen(name) <= 19) { strcpy(strbuf1, name); for(i = 0; i < strlen(strbuf1); i++) { if(strbuf1[i] < '0' || strbuf1[i] > '9') strbuf1[i] = ' '; } /* This does some fuzzy mode naming detection */ if(sscanf(strbuf1, "%u %u %u %u", &xres, &yres, &depth, &rate) == 4) { if((rate <= 32) || (depth > 32)) { j = rate; rate = depth; depth = j; } sprintf(strbuf, "%ux%ux%u", xres, yres, depth); nameptr = strbuf; sisfb_parm_rate = rate; } else if(sscanf(strbuf1, "%u %u %u", &xres, &yres, &depth) == 3) { sprintf(strbuf, "%ux%ux%u", xres, yres, depth); nameptr = strbuf; } else { xres = 0; if((sscanf(strbuf1, "%u %u", &xres, &yres) == 2) && (xres != 0)) { sprintf(strbuf, "%ux%ux8", xres, yres); nameptr = strbuf; } else { sisfb_search_vesamode(simple_strtoul(name, NULL, 0), quiet); return; } } } i = 0; j = 0; while(sisbios_mode[i].mode_no[0] != 0) { if(!strnicmp(nameptr, sisbios_mode[i++].name, strlen(nameptr))) { if(sisfb_fstn) { if(sisbios_mode[i-1].mode_no[1] == 0x50 || sisbios_mode[i-1].mode_no[1] == 0x56 || sisbios_mode[i-1].mode_no[1] == 0x53) continue; } else { if(sisbios_mode[i-1].mode_no[1] == 0x5a || sisbios_mode[i-1].mode_no[1] == 0x5b) continue; } sisfb_mode_idx = i - 1; j = 1; break; } } if((!j) && !quiet) printk(KERN_ERR "sisfb: Invalid mode '%s'\n", nameptr);}#ifndef MODULEstatic void __devinitsisfb_get_vga_mode_from_kernel(void){#ifdef CONFIG_X86 char mymode[32]; int mydepth = screen_info.lfb_depth; if(screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB) return; if( (screen_info.lfb_width >= 320) && (screen_info.lfb_width <= 2048) && (screen_info.lfb_height >= 200) && (screen_info.lfb_height <= 1536) && (mydepth >= 8) && (mydepth <= 32) ) { if(mydepth == 24) mydepth = 32; sprintf(mymode, "%ux%ux%u", screen_info.lfb_width, screen_info.lfb_height, mydepth); printk(KERN_DEBUG "sisfb: Using vga mode %s pre-set by kernel as default\n", mymode); sisfb_search_mode(mymode, true); }#endif return;}#endifstatic void __initsisfb_search_crt2type(const char *name){ int i = 0; /* We don't know the hardware specs yet and there is no ivideo */ if(name == NULL) return; while(sis_crt2type[i].type_no != -1) { if(!strnicmp(name, sis_crt2type[i].name, strlen(sis_crt2type[i].name))) { sisfb_crt2type = sis_crt2type[i].type_no; sisfb_tvplug = sis_crt2type[i].tvplug_no; sisfb_crt2flags = sis_crt2type[i].flags; break; } i++; } sisfb_dstn = (sisfb_crt2flags & FL_550_DSTN) ? 1 : 0; sisfb_fstn = (sisfb_crt2flags & FL_550_FSTN) ? 1 : 0; if(sisfb_crt2type < 0) printk(KERN_ERR "sisfb: Invalid CRT2 type: %s\n", name);}static void __initsisfb_search_tvstd(const char *name){ int i = 0; /* We don't know the hardware specs yet and there is no ivideo */ if(name == NULL) return; while(sis_tvtype[i].type_no != -1) { if(!strnicmp(name, sis_tvtype[i].name, strlen(sis_tvtype[i].name))) { sisfb_tvstd = sis_tvtype[i].type_no; break; } i++; }}static void __initsisfb_search_specialtiming(const char *name){ int i = 0; bool found = false; /* We don't know the hardware specs yet and there is no ivideo */ if(name == NULL) return; if(!strnicmp(name, "none", 4)) { sisfb_specialtiming = CUT_FORCENONE; printk(KERN_DEBUG "sisfb: Special timing disabled\n"); } else { while(mycustomttable[i].chipID != 0) { if(!strnicmp(name,mycustomttable[i].optionName, strlen(mycustomttable[i].optionName))) { sisfb_specialtiming = mycustomttable[i].SpecialID; found = true; printk(KERN_INFO "sisfb: Special timing for %s %s forced (\"%s\")\n", mycustomttable[i].vendorName, mycustomttable[i].cardName, mycustomttable[i].optionName); break; } i++; } if(!found) { printk(KERN_WARNING "sisfb: Invalid SpecialTiming parameter, valid are:"); printk(KERN_WARNING "\t\"none\" (to disable special timings)\n"); i = 0; while(mycustomttable[i].chipID != 0) { printk(KERN_WARNING "\t\"%s\" (for %s %s)\n", mycustomttable[i].optionName, mycustomttable[i].vendorName, mycustomttable[i].cardName); i++; } } }}/* ----------- Various detection routines ----------- */static void __devinitsisfb_detect_custom_timing(struct sis_video_info *ivideo){ unsigned char *biosver = NULL; unsigned char *biosdate = NULL; bool footprint; u32 chksum = 0; int i, j; if(ivideo->SiS_Pr.UseROM) { biosver = ivideo->SiS_Pr.VirtualRomBase + 0x06; biosdate = ivideo->SiS_Pr.VirtualRomBase + 0x2c; for(i = 0; i < 32768; i++) chksum += ivideo->SiS_Pr.VirtualRomBase[i]; } i = 0; do { if( (mycustomttable[i].chipID == ivideo->chip) && ((!strlen(mycustomttable[i].biosversion)) || (ivideo->SiS_Pr.UseROM && (!strncmp(mycustomttable[i].biosversion, biosver, strlen(mycustomttable[i].biosversion))))) && ((!strlen(mycustomttable[i].biosdate)) || (ivideo->SiS_Pr.UseROM && (!strncmp(mycustomttable[i].biosdate, biosdate, strlen(mycustomttable[i].biosdate))))) && ((!mycustomttable[i].bioschksum) || (ivideo->SiS_Pr.UseROM && (mycustomttable[i].bioschksum == chksum))) && (mycustomttable[i].pcisubsysvendor == ivideo->subsysvendor) && (mycustomttable[i].pcisubsyscard == ivideo->subsysdevice) ) { footprint = true; for(j = 0; j < 5; j++) { if(mycustomttable[i].biosFootprintAddr[j]) { if(ivideo->SiS_Pr.UseROM) { if(ivideo->SiS_Pr.VirtualRomBase[mycustomttable[i].biosFootprintAddr[j]] != mycustomttable[i].biosFootprintData[j]) { footprint = false; } } else footprint = false; } } if(footprint) { ivideo->SiS_Pr.SiS_CustomT = mycustomttable[i].SpecialID; printk(KERN_DEBUG "sisfb: Identified [%s %s], special timing applies\n", mycustomttable[i].vendorName, mycustomttable[i].cardName); printk(KERN_DEBUG "sisfb: [specialtiming parameter name: %s]\n", mycustomttable[i].optionName); break; } } i++; } while(mycustomttable[i].chipID);}static bool __devinitsisfb_interpret_edid(struct sisfb_monitor *monitor, u8 *buffer){ int i, j, xres, yres, refresh, index; u32 emodes; if(buffer[0] != 0x00 || buffer[1] != 0xff || buffer[2] != 0xff || buffer[3] != 0xff || buffer[4] != 0xff || buffer[5] != 0xff || buffer[6] != 0xff || buffer[7] != 0x00) { printk(KERN_DEBUG "sisfb: Bad EDID header\n"); return false; } if(buffer[0x12] != 0x01) { printk(KERN_INFO "sisfb: EDID version %d not supported\n", buffer[0x12]); return false; } monitor->feature = buffer[0x18]; if(!buffer[0x14] & 0x80) { if(!(buffer[0x14] & 0x08)) { printk(KERN_INFO "sisfb: WARNING: Monitor does not support separate syncs\n"); } } if(buffer[0x13] >= 0x01) { /* EDID V1 rev 1 and 2: Search for monitor descriptor * to extract ranges */ j = 0x36; for(i=0; i<4; i++) { if(buffer[j] == 0x00 && buffer[j + 1] == 0x00 && buffer[j + 2] == 0x00 && buffer[j + 3] == 0xfd && buffer[j + 4] == 0x00) { monitor->hmin = buffer[j + 7]; monitor->hmax = buffer[j + 8]; monitor->vmin = buffer[j + 5]; monitor->vmax = buffer[j + 6]; monitor->dclockmax = buffer[j + 9] * 10 * 1000; monitor->datavalid = true; break; } j += 18; } } if(!monitor->datavalid) { /* Otherwise: Get a range from the list of supported * Estabished Timings. This is not entirely accurate, * because fixed frequency monitors are not supported * that way. */ monitor->hmin = 65535; monitor->hmax = 0; monitor->vmin = 65535; monitor->vmax = 0; monitor->dclockmax = 0; emodes = buffer[0x23] | (buffer[0x24] << 8) | (buffer[0x25] << 16); for(i = 0; i < 13; i++) { if(emodes & sisfb_ddcsmodes[i].mask) { if(monitor->hmin > sisfb_ddcsmodes[i].h) monitor->hmin = sisfb_ddcsmodes[i].h; if(monitor->hmax < sisfb_ddcsmodes[i].h) monitor->hmax = sisfb_ddcsmodes[i].h + 1; if(monitor->vmin > sisfb_ddcsmodes[i].v) monitor->vmin = sisfb_ddcsmodes[i].v; if(monitor->vmax < sisfb_ddcsmodes[i].v) monitor->vmax = sisfb_ddcsmodes[i].v; if(monitor->dclockmax < sisfb_ddcsmodes[i].d) monitor->dclockmax = sisfb_ddcsmodes[i].d; } } index = 0x26; for(i = 0; i < 8; i++) { xres = (buffer[index] + 31) * 8; switch(buffer[index + 1] & 0xc0) { case 0xc0: yres = (xres * 9) / 16; break; case 0x80: yres = (xres * 4) / 5; break; case 0x40: yres = (xres * 3) / 4; break; default: yres = xres; break; } refresh = (buffer[index + 1] & 0x3f) + 60;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -