📄 sis_main.c
字号:
/* * SiS 300/305/540/630(S)/730(S) * SiS 315(H/PRO)/55x/(M)65x/(M)661(F/M)X/740/741(GX)/330/(M)760 * frame buffer driver for Linux kernels >= 2.4.14 and >=2.6.3 * * Copyright (C) 2001-2004 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/spinlock.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/console.h>#include <linux/selection.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/vmalloc.h>#include <linux/vt_kern.h>#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#endif/* ------------------ 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_filter = -1; 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}static void __initsisfb_search_vesamode(unsigned int vesamode, BOOLEAN quiet){ int i = 0, j = 0; /* BEWARE: 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 voidsisfb_search_mode(char *name, BOOLEAN quiet){ int i = 0; unsigned int j = 0, xres = 0, yres = 0, depth = 0, rate = 0; char strbuf[16], strbuf1[20]; char *nameptr = name; /* BEWARE: 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; /* BEWARE: 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; /* BEWARE: 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; /* BEWARE: 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++; } } }}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 */ 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; if((xres >= 640) && (yres >= 480)) { for(j = 0; j < 8; j++) { if((xres == sisfb_ddcfmodes[j].x) && (yres == sisfb_ddcfmodes[j].y) && (refresh == sisfb_ddcfmodes[j].v)) { if(monitor->hmin > sisfb_ddcfmodes[j].h) monitor->hmin = sisfb_ddcfmodes[j].h; if(monitor->hmax < sisfb_ddcfmodes[j].h) monitor->hmax = sisfb_ddcfmodes[j].h + 1; if(monitor->vmin > sisfb_ddcsmodes[j].v) monitor->vmin = sisfb_ddcsmodes[j].v; if(monitor->vmax < sisfb_ddcsmodes[j].v) monitor->vmax = sisfb_ddcsmodes[j].v; if(monitor->dclockmax < sisfb_ddcsmodes[j].d) monitor->dclockmax = sisfb_ddcsmodes[i].d; } } } index += 2; } if((monitor->hmin <= monitor->hmax) && (monitor->vmin <= monitor->vmax)) { monitor->datavalid = TRUE; } } return(monitor->datavalid);}static void __devinit
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -