📄 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/config.h>#include <linux/version.h>#include <linux/module.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)#include <linux/moduleparam.h>#endif#include <linux/kernel.h>#include <linux/smp_lock.h>#include <linux/spinlock.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#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>#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)#include <linux/vt_kern.h>#endif#include <linux/capability.h>#include <linux/fs.h>#include <linux/types.h>#include <asm/uaccess.h>#include <asm/io.h>#ifdef CONFIG_MTRR#include <asm/mtrr.h>#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)#include <video/fbcon.h>#include <video/fbcon-cfb8.h>#include <video/fbcon-cfb16.h>#include <video/fbcon-cfb24.h>#include <video/fbcon-cfb32.h>#endif#include "sis.h"#include "sis_main.h"#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3)#error "This version of sisfb requires at least 2.6.3"#endif#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)#ifdef FBCON_HAS_CFB8extern struct display_switch fbcon_sis8;#endif#ifdef FBCON_HAS_CFB16extern struct display_switch fbcon_sis16;#endif#ifdef FBCON_HAS_CFB32extern struct display_switch fbcon_sis32;#endif#endifstatic 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;#ifdef MODULE /* Module: "None" for 2.4, default mode for 2.5+ */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) sisfb_mode_idx = -1;#else sisfb_mode_idx = MODE_INDEX_NONE;#endif#else /* Static: Default mode */ sisfb_mode_idx = -1;#endif 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 LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) sisfb_inverse = 0; sisfb_fontname[0] = 0;#endif#if !defined(__i386__) && !defined(__x86_64__) sisfb_resetcard = 0; sisfb_videoram = 0;#endif}/* ------------- Parameter parsing -------------- */static void __devinitsisfb_search_vesamode(unsigned int vesamode, BOOLEAN quiet){ int i = 0, j = 0; /* We don't know the hardware specs yet and there is no ivideo */ if(vesamode == 0) {#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) sisfb_mode_idx = MODE_INDEX_NONE;#else if(!quiet) printk(KERN_ERR "sisfb: Invalid mode. Using default.\n"); sisfb_mode_idx = DEFAULT_MODE;#endif 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, BOOLEAN 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) 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; }#endif 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){#if (defined(__i386__) || defined(__x86_64__)) && defined(CONFIG_VIDEO_SELECT) 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; BOOLEAN 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; BOOLEAN 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 BOOLEAN __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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -