📄 sis_main.c
字号:
/* * SiS 300/630/730/540/315/550/650/740 frame buffer device * for Linux kernels 2.4.x and 2.5.x * * Partly based on the VBE 2.0 compliant graphic boards framebuffer driver, * which is (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> * * Authors: SiS (www.sis.com.tw) * (Various others) * Thomas Winischhofer <thomas@winischhofer.net>: * - many fixes and enhancements for all chipset series, * - extended bridge handling, TV output for Chrontel 7005 * - 650/LVDS support (for LCD panels up to 1400x1050) * - 650/Chrontel 7019 support * - 301B/301LV(x)/302B/302LV(x) LCD and TV support * - memory queue handling enhancements, * - 2D acceleration and y-panning, * - portation to 2.5 API (yet incomplete) * - everything marked with "TW" and more * (see http://www.winischhofer.net/ * for more information and updates) */#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/kernel.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/vt_kern.h>#include <linux/capability.h>#include <linux/fs.h>#include <linux/agp_backend.h>#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,33)#include <linux/spinlock.h>#endif#include "osdef.h"#include <linux/types.h>#include <linux/sisfb.h>#include <asm/io.h>#include <asm/mtrr.h>#include <video/fbcon.h>#include <video/fbcon-cfb8.h>#include <video/fbcon-cfb16.h>#include <video/fbcon-cfb24.h>#include <video/fbcon-cfb32.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,34)#include "../fbcon-accel.h"#endif#include "vgatypes.h"#include "sis_main.h"#include "sis.h"//#ifdef LINUXBIOS//#include "bios.h"//#endif/* -------------------- Macro definitions ---------------------------- */#undef SISFBDEBUG /* TW: no debugging */#ifdef SISFBDEBUG#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)#else#define DPRINTK(fmt, args...)#endif#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,33)#ifdef SISFBACCEL#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#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,34)/* TEMP */void my_cfb_imageblit(struct fb_info *info, struct fb_image *image);#endif/* --------------- Hardware Access Routines -------------------------- */void sisfb_set_reg4(u16 port, unsigned long data){ outl((u32) (data & 0xffffffff), port);}u32 sisfb_get_reg3(u16 port){ u32 data; data = inl(port); return (data);}/* -------------------- Interface to BIOS code -------------------- */BOOLEANsisfb_query_VGA_config_space(PSIS_HW_DEVICE_INFO psishw_ext, unsigned long offset, unsigned long set, unsigned long *value){ static struct pci_dev *pdev = NULL; static unsigned char init = 0, valid_pdev = 0; if (!set) DPRINTK("sisfb: Get VGA offset 0x%lx\n", offset); else DPRINTK("sisfb: Set offset 0x%lx to 0x%lx\n", offset, *value); if (!init) { init = TRUE; pci_for_each_dev(pdev) { DPRINTK("sisfb: Current: 0x%x, target: 0x%x\n", pdev->device, ivideo.chip_id); if ((pdev->vendor == PCI_VENDOR_ID_SI) && (pdev->device == ivideo.chip_id)) { valid_pdev = TRUE; break; } } } if (!valid_pdev) { printk(KERN_DEBUG "sisfb: Can't find SiS %d VGA device.\n", ivideo.chip_id); return FALSE; } if (set == 0) pci_read_config_dword(pdev, offset, (u32 *)value); else pci_write_config_dword(pdev, offset, (u32)(*value)); return TRUE;}BOOLEAN sisfb_query_north_bridge_space(PSIS_HW_DEVICE_INFO psishw_ext, unsigned long offset, unsigned long set, unsigned long *value){ static struct pci_dev *pdev = NULL; static unsigned char init = 0, valid_pdev = 0; u16 nbridge_id = 0; if (!init) { init = TRUE; switch (ivideo.chip) { case SIS_540: nbridge_id = PCI_DEVICE_ID_SI_540; break; case SIS_630: nbridge_id = PCI_DEVICE_ID_SI_630; break; case SIS_730: nbridge_id = PCI_DEVICE_ID_SI_730; break; case SIS_550: nbridge_id = PCI_DEVICE_ID_SI_550; break; case SIS_650: nbridge_id = PCI_DEVICE_ID_SI_650; break; default: nbridge_id = 0; break; } pci_for_each_dev(pdev) { DPRINTK("Current: 0x%x, target: 0x%x\n", pdev->device, ivideo.chip_id); if ((pdev->vendor == PCI_VENDOR_ID_SI) && (pdev->device == nbridge_id)) { valid_pdev = TRUE; break; } } } if (!valid_pdev) { printk(KERN_DEBUG "sisfb: Can't find SiS %d North Bridge device.\n", nbridge_id); return FALSE; } if (set == 0) pci_read_config_dword(pdev, offset, (u32 *)value); else pci_write_config_dword(pdev, offset, (u32)(*value)); return TRUE;}/* -------------------- Exported functions ----------------------------- */static void sis_get_glyph(struct fb_info *info, SIS_GLYINFO *gly){#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,23)#define currcon info->currcon#endif struct display *p = &fb_display[currcon]; u16 c; u8 *cdat; int widthb; u8 *gbuf = gly->gmask; int size; TWDEBUG("Inside get_glyph"); gly->fontheight = fontheight(p); gly->fontwidth = fontwidth(p); widthb = (fontwidth(p) + 7) / 8; c = gly->ch & p->charmask; if (fontwidth(p) <= 8) cdat = p->fontdata + c * fontheight(p); else cdat = p->fontdata + (c * fontheight(p) << 1); size = fontheight(p) * widthb; memcpy(gbuf, cdat, size); gly->ngmask = size; TWDEBUG("End of get_glyph");#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,23)#undef currcon #endif}void sis_dispinfo(struct ap_data *rec){ TWDEBUG("Inside dispinfo"); rec->minfo.bpp = ivideo.video_bpp; rec->minfo.xres = ivideo.video_width; rec->minfo.yres = ivideo.video_height; rec->minfo.v_xres = ivideo.video_vwidth; rec->minfo.v_yres = ivideo.video_vheight; rec->minfo.org_x = ivideo.org_x; rec->minfo.org_y = ivideo.org_y; rec->minfo.vrate = ivideo.refresh_rate; rec->iobase = ivideo.vga_base - 0x30; rec->mem_size = ivideo.video_size; rec->disp_state = ivideo.disp_state; rec->version = (VER_MAJOR << 24) | (VER_MINOR << 16) | VER_LEVEL; rec->hasVB = ivideo.hasVB; rec->TV_type = ivideo.TV_type; rec->TV_plug = ivideo.TV_plug; rec->chip = ivideo.chip; TWDEBUG("End of dispinfo");}/* ------------------ Internal Routines ------------------------------ */static void sisfb_search_mode(const char *name){ int i = 0, j = 0; if(name == NULL) return; while(sisbios_mode[i].mode_no != 0) { if (!strcmp(name, sisbios_mode[i].name)) { sisfb_mode_idx = i; j = 1; break; } i++; } if(!j) printk(KERN_INFO "sisfb: Invalid mode '%s'\n", name);}static void sisfb_search_vesamode(unsigned int vesamode){ int i = 0, j = 0; if(vesamode == 0) { sisfb_mode_idx = MODE_INDEX_NONE; return; } vesamode &= 0x1dff; /* Clean VESA mode number from other flags */ while(sisbios_mode[i].mode_no != 0) { if( (sisbios_mode[i].vesa_mode_no_1 == vesamode) || (sisbios_mode[i].vesa_mode_no_2 == vesamode) ) { sisfb_mode_idx = i; j = 1; break; } i++; } if(!j) printk(KERN_INFO "sisfb: Invalid VESA mode 0x%x'\n", vesamode);}static int sisfb_validate_mode(int myindex){ u16 xres, yres;#ifdef CONFIG_FB_SIS_300 if(sisvga_engine == SIS_300_VGA) { if(!(sisbios_mode[sisfb_mode_idx].chipset & MD_SIS300)) { return(-1); } }#endif#ifdef CONFIG_FB_SIS_315 if(sisvga_engine == SIS_315_VGA) { if(!(sisbios_mode[myindex].chipset & MD_SIS315)) { return(-1); } }#endif switch (ivideo.disp_state & DISPTYPE_DISP2) { case DISPTYPE_LCD: switch (sishw_ext.ulCRT2LCDType) { case LCD_1024x768: xres = 1024; yres = 768; break; case LCD_1280x1024: xres = 1280; yres = 1024; break; case LCD_1280x960: xres = 1280; yres = 960; break; case LCD_2048x1536: xres = 2048; yres = 1536; break; case LCD_1920x1440: xres = 1920; yres = 1440; break; case LCD_1600x1200: xres = 1600; yres = 1200; break; case LCD_800x600: xres = 800; yres = 600; break; case LCD_640x480: xres = 640; yres = 480; break; case LCD_320x480: /* TW: FSTN */ xres = 320; yres = 480; break; case LCD_1024x600: xres = 1024; yres = 600; break; case LCD_1152x864: xres = 1152; yres = 864; break; case LCD_1152x768: xres = 1152; yres = 768; break; case LCD_1280x768: xres = 1280; yres = 768; break; case LCD_1400x1050: xres = 1400; yres = 1050; break; default: xres = 0; yres = 0; break; } if(sisbios_mode[myindex].xres > xres) { return(-1); } if(sisbios_mode[myindex].yres > yres) { return(-1); } if (sisbios_mode[myindex].xres == 720) { return(-1); } break; case DISPTYPE_TV: switch (sisbios_mode[myindex].xres) { case 512: case 640: case 800: break; case 720: if (ivideo.TV_type == TVMODE_NTSC) { if (sisbios_mode[myindex].yres != 480) { return(-1); } } else if (ivideo.TV_type == TVMODE_PAL) { if (sisbios_mode[myindex].yres != 576) { return(-1); } } /* TW: LVDS/CHRONTEL does not support 720 */ if (ivideo.hasVB == HASVB_LVDS_CHRONTEL || ivideo.hasVB == HASVB_CHRONTEL) { return(-1); } break; case 1024: if (ivideo.TV_type == TVMODE_NTSC) { if(sisbios_mode[myindex].bpp == 32) { return(-1); } } /* TW: LVDS/CHRONTEL only supports < 800 (1024 on 650/Ch7019)*/ if (ivideo.hasVB == HASVB_LVDS_CHRONTEL || ivideo.hasVB == HASVB_CHRONTEL) { if(ivideo.chip < SIS_315H) { return(-1); } } break; default: return(-1); } break; } return(myindex);}static void sisfb_search_crt2type(const char *name){ int i = 0; if(name == NULL) return; while(sis_crt2type[i].type_no != -1) { if (!strcmp(name, sis_crt2type[i].name)) { sisfb_crt2type = sis_crt2type[i].type_no; sisfb_tvplug = sis_crt2type[i].tvplug_no; break; } i++; } if(sisfb_crt2type < 0) printk(KERN_INFO "sisfb: Invalid CRT2 type: %s\n", name);}static void sisfb_search_queuemode(const char *name){ int i = 0; if(name == NULL) return; while (sis_queuemode[i].type_no != -1) { if (!strcmp(name, sis_queuemode[i].name)) { sisfb_queuemode = sis_queuemode[i].type_no; break; } i++; } if (sisfb_queuemode < 0) printk(KERN_INFO "sisfb: Invalid queuemode type: %s\n", name);}static u8 sisfb_search_refresh_rate(unsigned int rate){ u16 xres, yres; int i = 0; xres = sisbios_mode[sisfb_mode_idx].xres; yres = sisbios_mode[sisfb_mode_idx].yres; sisfb_rate_idx = 0; while ((sisfb_vrate[i].idx != 0) && (sisfb_vrate[i].xres <= xres)) { if ((sisfb_vrate[i].xres == xres) && (sisfb_vrate[i].yres == yres)) { if (sisfb_vrate[i].refresh == rate) { sisfb_rate_idx = sisfb_vrate[i].idx; break; } else if (sisfb_vrate[i].refresh > rate) { if ((sisfb_vrate[i].refresh - rate) <= 2) { DPRINTK("sisfb: Adjusting rate from %d up to %d\n", rate, sisfb_vrate[i].refresh); sisfb_rate_idx = sisfb_vrate[i].idx; ivideo.refresh_rate = sisfb_vrate[i].refresh; } else if (((rate - sisfb_vrate[i-1].refresh) <= 2) && (sisfb_vrate[i].idx != 1)) { DPRINTK("sisfb: Adjusting rate from %d down to %d\n", rate, sisfb_vrate[i-1].refresh); sisfb_rate_idx = sisfb_vrate[i-1].idx; ivideo.refresh_rate = sisfb_vrate[i-1].refresh; } break; } } i++; } if (sisfb_rate_idx > 0) { return sisfb_rate_idx; } else { printk(KERN_INFO "sisfb: Unsupported rate %d for %dx%d\n", rate, xres, yres); return 0; }}#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,33)static int sis_getcolreg(unsigned regno, unsigned *red, unsigned *green, unsigned *blue, unsigned *transp, struct fb_info *fb_info){ if (regno >= ivideo.video_cmap_len) return 1; *red = sis_palette[regno].red; *green = sis_palette[regno].green; *blue = sis_palette[regno].blue; *transp = 0; return 0;}#endifstatic int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -