| /* |
| * 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/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> |
| #include <linux/screen_info.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> |
| #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" |
| |
| #if !defined(CONFIG_FB_SIS_300) && !defined(CONFIG_FB_SIS_315) |
| #warning Neither CONFIG_FB_SIS_300 nor CONFIG_FB_SIS_315 is set |
| #warning sisfb will not work! |
| #endif |
| |
| static void sisfb_handle_command(struct sis_video_info *ivideo, |
| struct sisfb_cmd *sisfb_command); |
| |
| /* ------------------ Internal helper routines ----------------- */ |
| |
| static void __init |
| sisfb_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 __devinit |
| sisfb_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 __devinit |
| sisfb_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 MODULE |
| static void __devinit |
| sisfb_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; |
| } |
| #endif |
| |
| static void __init |
| sisfb_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 __init |
| sisfb_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 __init |
| sisfb_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 __devinit |
| sisfb_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 __devinit |
| sisfb_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[j].d; |
| } |
| } |
| } |
| index += 2; |
| } |
| if((monitor->hmin <= monitor->hmax) && (monitor->vmin <= monitor->vmax)) { |
| monitor->datavalid = true; |
| } |
| } |
| |
| return monitor->datavalid; |
| } |
| |
| static void __devinit |
| sisfb_handle_ddc(struct sis_video_info *ivideo, struct sisfb_monitor *monitor, int crtno) |
| { |
| unsigned short temp, i, realcrtno = crtno; |
| unsigned char buffer[256]; |
| |
| monitor->datavalid = false; |
| |
| if(crtno) { |
| if(ivideo->vbflags & CRT2_LCD) realcrtno = 1; |
| else if(ivideo->vbflags & CRT2_VGA) realcrtno = 2; |
| else return; |
| } |
| |
| if((ivideo->sisfb_crt1off) && (!crtno)) |
| return; |
| |
| temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine, |
| realcrtno, 0, &buffer[0], ivideo->vbflags2); |
| if((!temp) || (temp == 0xffff)) { |
| printk(KERN_INFO "sisfb: CRT%d DDC probing failed\n", crtno + 1); |
| return; |
| } else { |
| printk(KERN_INFO "sisfb: CRT%d DDC supported\n", crtno + 1); |
| printk(KERN_INFO "sisfb: CRT%d DDC level: %s%s%s%s\n", |
| crtno + 1, |
| (temp & 0x1a) ? "" : "[none of the supported]", |
| (temp & 0x02) ? "2 " : "", |
| (temp & 0x08) ? "D&P" : "", |
| (temp & 0x10) ? "FPDI-2" : ""); |
| if(temp & 0x02) { |
| i = 3; /* Number of retrys */ |
| do { |
| temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine, |
| realcrtno, 1, &buffer[0], ivideo->vbflags2); |
| } while((temp) && i--); |
| if(!temp) { |
| if(sisfb_interpret_edid(monitor, &buffer[0])) { |
| printk(KERN_INFO "sisfb: Monitor range H %d-%dKHz, V %d-%dHz, Max. dotclock %dMHz\n", |
| monitor->hmin, monitor->hmax, monitor->vmin, monitor->vmax, |
| monitor->dclockmax / 1000); |
| } else { |
| printk(KERN_INFO "sisfb: CRT%d DDC EDID corrupt\n", crtno + 1); |
| } |
| } else { |
| printk(KERN_INFO "sisfb: CRT%d DDC reading failed\n", crtno + 1); |
| } |
| } else { |
| printk(KERN_INFO "sisfb: VESA D&P and FPDI-2 not supported yet\n"); |
| } |
| } |
| } |
| |
| /* -------------- Mode validation --------------- */ |
| |
| static bool |
| sisfb_verify_rate(struct sis_video_info *ivideo, struct sisfb_monitor *monitor, |
| int mode_idx, int rate_idx, int rate) |
| { |
| int htotal, vtotal; |
| unsigned int dclock, hsync; |
| |
| if(!monitor->datavalid) |
| return true; |
| |
| if(mode_idx < 0) |
| return false; |
| |
| /* Skip for 320x200, 320x240, 640x400 */ |
| switch(sisbios_mode[mode_idx].mode_no[ivideo->mni]) { |
| case 0x59: |
| case 0x41: |
| case 0x4f: |
| case 0x50: |
| case 0x56: |
| case 0x53: |
| case 0x2f: |
| case 0x5d: |
| case 0x5e: |
| return true; |
| #ifdef CONFIG_FB_SIS_315 |
| case 0x5a: |
| case 0x5b: |
| if(ivideo->sisvga_engine == SIS_315_VGA) return true; |
| #endif |
| } |
| |
| if(rate < (monitor->vmin - 1)) |
| return false; |
| if(rate > (monitor->vmax + 1)) |
| return false; |
| |
| if(sisfb_gettotalfrommode(&ivideo->SiS_Pr, |
| sisbios_mode[mode_idx].mode_no[ivideo->mni], |
| &htotal, &vtotal, rate_idx)) { |
| dclock = (htotal * vtotal * rate) / 1000; |
| if(dclock > (monitor->dclockmax + 1000)) |
| return false; |
| hsync = dclock / htotal; |
| if(hsync < (monitor->hmin - 1)) |
| return false; |
| if(hsync > (monitor->hmax + 1)) |
| return false; |
| } else { |
| return false; |
| } |
| return true; |
| } |
| |
| static int |
| sisfb_validate_mode(struct sis_video_info *ivideo, int myindex, u32 vbflags) |
| { |
| u16 xres=0, yres, myres; |
| |
| #ifdef CONFIG_FB_SIS_300 |
| if(ivideo->sisvga_engine == SIS_300_VGA) { |
| if(!(sisbios_mode[myindex].chipset & MD_SIS300)) |
| return -1 ; |
| } |
| #endif |
| #ifdef CONFIG_FB_SIS_315 |
| if(ivideo->sisvga_engine == SIS_315_VGA) { |
| if(!(sisbios_mode[myindex].chipset & MD_SIS315)) |
| return -1; |
| } |
| #endif |
| |
| myres = sisbios_mode[myindex].yres; |
| |
| switch(vbflags & VB_DISPTYPE_DISP2) { |
| |
| case CRT2_LCD: |
| xres = ivideo->lcdxres; yres = ivideo->lcdyres; |
| |
| if((ivideo->SiS_Pr.SiS_CustomT != CUT_PANEL848) && |
| (ivideo->SiS_Pr.SiS_CustomT != CUT_PANEL856)) { |
| if(sisbios_mode[myindex].xres > xres) |
| return -1; |
| if(myres > yres) |
| return -1; |
| } |
| |
| if(ivideo->sisfb_fstn) { |
| if(sisbios_mode[myindex].xres == 320) { |
| if(myres == 240) { |
| switch(sisbios_mode[myindex].mode_no[1]) { |
| case 0x50: myindex = MODE_FSTN_8; break; |
| case 0x56: myindex = MODE_FSTN_16; break; |
| case 0x53: return -1; |
| } |
| } |
| } |
| } |
| |
| if(SiS_GetModeID_LCD(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres, |
| sisbios_mode[myindex].yres, 0, ivideo->sisfb_fstn, |
| ivideo->SiS_Pr.SiS_CustomT, xres, yres, ivideo->vbflags2) < 0x14) { |
| return -1; |
| } |
| break; |
| |
| case CRT2_TV: |
| if(SiS_GetModeID_TV(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres, |
| sisbios_mode[myindex].yres, 0, ivideo->vbflags2) < 0x14) { |
| return -1; |
| } |
| break; |
| |
| case CRT2_VGA: |
| if(SiS_GetModeID_VGA2(ivideo->sisvga_engine, vbflags, sisbios_mode[myindex].xres, |
| sisbios_mode[myindex].yres, 0, ivideo->vbflags2) < 0x14) { |
| return -1; |
| } |
| break; |
| } |
| |
| return myindex; |
| } |
| |
| static u8 |
| sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate, int mode_idx) |
| { |
| int i = 0; |
| u16 xres = sisbios_mode[mode_idx].xres; |
| u16 yres = sisbios_mode[mode_idx].yres; |
| |
| ivideo->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) { |
| ivideo->rate_idx = sisfb_vrate[i].idx; |
| break; |
| } else if(sisfb_vrate[i].refresh > rate) { |
| if((sisfb_vrate[i].refresh - rate) <= 3) { |
| DPRINTK("sisfb: Adjusting rate from %d up to %d\n", |
| rate, sisfb_vrate[i].refresh); |
| ivideo->rate_idx = sisfb_vrate[i].idx; |
| ivideo->refresh_rate = sisfb_vrate[i].refresh; |
| } else if((sisfb_vrate[i].idx != 1) && |
| ((rate - sisfb_vrate[i-1].refresh) <= 2)) { |
| DPRINTK("sisfb: Adjusting rate from %d down to %d\n", |
| rate, sisfb_vrate[i-1].refresh); |
| ivideo->rate_idx = sisfb_vrate[i-1].idx; |
| ivideo->refresh_rate = sisfb_vrate[i-1].refresh; |
| } |
| break; |
| } else if((rate - sisfb_vrate[i].refresh) <= 2) { |
| DPRINTK("sisfb: Adjusting rate from %d down to %d\n", |
| rate, sisfb_vrate[i].refresh); |
| ivideo->rate_idx = sisfb_vrate[i].idx; |
| break; |
| } |
| } |
| i++; |
| } |
| if(ivideo->rate_idx > 0) { |
| return ivideo->rate_idx; |
| } else { |
| printk(KERN_INFO "sisfb: Unsupported rate %d for %dx%d\n", |
| rate, xres, yres); |
| return 0; |
| } |
| } |
| |
| static bool |
| sisfb_bridgeisslave(struct sis_video_info *ivideo) |
| { |
| unsigned char P1_00; |
| |
| if(!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) |
| return false; |
| |
| P1_00 = SiS_GetReg(SISPART1, 0x00); |
| if( ((ivideo->sisvga_engine == SIS_300_VGA) && (P1_00 & 0xa0) == 0x20) || |
| ((ivideo->sisvga_engine == SIS_315_VGA) && (P1_00 & 0x50) == 0x10) ) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static bool |
| sisfballowretracecrt1(struct sis_video_info *ivideo) |
| { |
| u8 temp; |
| |
| temp = SiS_GetReg(SISCR, 0x17); |
| if(!(temp & 0x80)) |
| return false; |
| |
| temp = SiS_GetReg(SISSR, 0x1f); |
| if(temp & 0xc0) |
| return false; |
| |
| return true; |
| } |
| |
| static bool |
| sisfbcheckvretracecrt1(struct sis_video_info *ivideo) |
| { |
| if(!sisfballowretracecrt1(ivideo)) |
| return false; |
| |
| if (SiS_GetRegByte(SISINPSTAT) & 0x08) |
| return true; |
| else |
| return false; |
| } |
| |
| static void |
| sisfbwaitretracecrt1(struct sis_video_info *ivideo) |
| { |
| int watchdog; |
| |
| if(!sisfballowretracecrt1(ivideo)) |
| return; |
| |
| watchdog = 65536; |
| while ((!(SiS_GetRegByte(SISINPSTAT) & 0x08)) && --watchdog); |
| watchdog = 65536; |
| while ((SiS_GetRegByte(SISINPSTAT) & 0x08) && --watchdog); |
| } |
| |
| static bool |
| sisfbcheckvretracecrt2(struct sis_video_info *ivideo) |
| { |
| unsigned char temp, reg; |
| |
| switch(ivideo->sisvga_engine) { |
| case SIS_300_VGA: reg = 0x25; break; |
| case SIS_315_VGA: reg = 0x30; break; |
| default: return false; |
| } |
| |
| temp = SiS_GetReg(SISPART1, reg); |
| if(temp & 0x02) |
| return true; |
| else |
| return false; |
| } |
| |
| static bool |
| sisfb_CheckVBRetrace(struct sis_video_info *ivideo) |
| { |
| if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) { |
| if(!sisfb_bridgeisslave(ivideo)) { |
| return sisfbcheckvretracecrt2(ivideo); |
| } |
| } |
| return sisfbcheckvretracecrt1(ivideo); |
| } |
| |
| static u32 |
| sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount) |
| { |
| u8 idx, reg1, reg2, reg3, reg4; |
| u32 ret = 0; |
| |
| (*vcount) = (*hcount) = 0; |
| |
| if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!(sisfb_bridgeisslave(ivideo)))) { |
| |
| ret |= (FB_VBLANK_HAVE_VSYNC | |
| FB_VBLANK_HAVE_HBLANK | |
| FB_VBLANK_HAVE_VBLANK | |
| FB_VBLANK_HAVE_VCOUNT | |
| FB_VBLANK_HAVE_HCOUNT); |
| switch(ivideo->sisvga_engine) { |
| case SIS_300_VGA: idx = 0x25; break; |
| default: |
| case SIS_315_VGA: idx = 0x30; break; |
| } |
| reg1 = SiS_GetReg(SISPART1, (idx+0)); /* 30 */ |
| reg2 = SiS_GetReg(SISPART1, (idx+1)); /* 31 */ |
| reg3 = SiS_GetReg(SISPART1, (idx+2)); /* 32 */ |
| reg4 = SiS_GetReg(SISPART1, (idx+3)); /* 33 */ |
| if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING; |
| if(reg1 & 0x02) ret |= FB_VBLANK_VSYNCING; |
| if(reg4 & 0x80) ret |= FB_VBLANK_HBLANKING; |
| (*vcount) = reg3 | ((reg4 & 0x70) << 4); |
| (*hcount) = reg2 | ((reg4 & 0x0f) << 8); |
| |
| } else if(sisfballowretracecrt1(ivideo)) { |
| |
| ret |= (FB_VBLANK_HAVE_VSYNC | |
| FB_VBLANK_HAVE_VBLANK | |
| FB_VBLANK_HAVE_VCOUNT | |
| FB_VBLANK_HAVE_HCOUNT); |
| reg1 = SiS_GetRegByte(SISINPSTAT); |
| if(reg1 & 0x08) ret |= FB_VBLANK_VSYNCING; |
| if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING; |
| reg1 = SiS_GetReg(SISCR, 0x20); |
| reg1 = SiS_GetReg(SISCR, 0x1b); |
| reg2 = SiS_GetReg(SISCR, 0x1c); |
| reg3 = SiS_GetReg(SISCR, 0x1d); |
| (*vcount) = reg2 | ((reg3 & 0x07) << 8); |
| (*hcount) = (reg1 | ((reg3 & 0x10) << 4)) << 3; |
| } |
| |
| return ret; |
| } |
| |
| static int |
| sisfb_myblank(struct sis_video_info *ivideo, int blank) |
| { |
| u8 sr01, sr11, sr1f, cr63=0, p2_0, p1_13; |
| bool backlight = true; |
| |
| switch(blank) { |
| case FB_BLANK_UNBLANK: /* on */ |
| sr01 = 0x00; |
| sr11 = 0x00; |
| sr1f = 0x00; |
| cr63 = 0x00; |
| p2_0 = 0x20; |
| p1_13 = 0x00; |
| backlight = true; |
| break; |
| case FB_BLANK_NORMAL: /* blank */ |
| sr01 = 0x20; |
| sr11 = 0x00; |
| sr1f = 0x00; |
| cr63 = 0x00; |
| p2_0 = 0x20; |
| p1_13 = 0x00; |
| backlight = true; |
| break; |
| case FB_BLANK_VSYNC_SUSPEND: /* no vsync */ |
| sr01 = 0x20; |
| sr11 = 0x08; |
| sr1f = 0x80; |
| cr63 = 0x40; |
| p2_0 = 0x40; |
| p1_13 = 0x80; |
| backlight = false; |
| break; |
| case FB_BLANK_HSYNC_SUSPEND: /* no hsync */ |
| sr01 = 0x20; |
| sr11 = 0x08; |
| sr1f = 0x40; |
| cr63 = 0x40; |
| p2_0 = 0x80; |
| p1_13 = 0x40; |
| backlight = false; |
| break; |
| case FB_BLANK_POWERDOWN: /* off */ |
| sr01 = 0x20; |
| sr11 = 0x08; |
| sr1f = 0xc0; |
| cr63 = 0x40; |
| p2_0 = 0xc0; |
| p1_13 = 0xc0; |
| backlight = false; |
| break; |
| default: |
| return 1; |
| } |
| |
| if(ivideo->currentvbflags & VB_DISPTYPE_CRT1) { |
| |
| if( (!ivideo->sisfb_thismonitor.datavalid) || |
| ((ivideo->sisfb_thismonitor.datavalid) && |
| (ivideo->sisfb_thismonitor.feature & 0xe0))) { |
| |
| if(ivideo->sisvga_engine == SIS_315_VGA) { |
| SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63); |
| } |
| |
| if(!(sisfb_bridgeisslave(ivideo))) { |
| SiS_SetRegANDOR(SISSR, 0x01, ~0x20, sr01); |
| SiS_SetRegANDOR(SISSR, 0x1f, 0x3f, sr1f); |
| } |
| } |
| |
| } |
| |
| if(ivideo->currentvbflags & CRT2_LCD) { |
| |
| if(ivideo->vbflags2 & VB2_SISLVDSBRIDGE) { |
| if(backlight) { |
| SiS_SiS30xBLOn(&ivideo->SiS_Pr); |
| } else { |
| SiS_SiS30xBLOff(&ivideo->SiS_Pr); |
| } |
| } else if(ivideo->sisvga_engine == SIS_315_VGA) { |
| #ifdef CONFIG_FB_SIS_315 |
| if(ivideo->vbflags2 & VB2_CHRONTEL) { |
| if(backlight) { |
| SiS_Chrontel701xBLOn(&ivideo->SiS_Pr); |
| } else { |
| SiS_Chrontel701xBLOff(&ivideo->SiS_Pr); |
| } |
| } |
| #endif |
| } |
| |
| if(((ivideo->sisvga_engine == SIS_300_VGA) && |
| (ivideo->vbflags2 & (VB2_301|VB2_30xBDH|VB2_LVDS))) || |
| ((ivideo->sisvga_engine == SIS_315_VGA) && |
| ((ivideo->vbflags2 & (VB2_LVDS | VB2_CHRONTEL)) == VB2_LVDS))) { |
| SiS_SetRegANDOR(SISSR, 0x11, ~0x0c, sr11); |
| } |
| |
| if(ivideo->sisvga_engine == SIS_300_VGA) { |
| if((ivideo->vbflags2 & VB2_30xB) && |
| (!(ivideo->vbflags2 & VB2_30xBDH))) { |
| SiS_SetRegANDOR(SISPART1, 0x13, 0x3f, p1_13); |
| } |
| } else if(ivideo->sisvga_engine == SIS_315_VGA) { |
| if((ivideo->vbflags2 & VB2_30xB) && |
| (!(ivideo->vbflags2 & VB2_30xBDH))) { |
| SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0); |
| } |
| } |
| |
| } else if(ivideo->currentvbflags & CRT2_VGA) { |
| |
| if(ivideo->vbflags2 & VB2_30xB) { |
| SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0); |
| } |
| |
| } |
| |
| return 0; |
| } |
| |
| /* ------------- Callbacks from init.c/init301.c -------------- */ |
| |
| #ifdef CONFIG_FB_SIS_300 |
| unsigned int |
| sisfb_read_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo; |
| u32 val = 0; |
| |
| pci_read_config_dword(ivideo->nbridge, reg, &val); |
| return (unsigned int)val; |
| } |
| |
| void |
| sisfb_write_nbridge_pci_dword(struct SiS_Private *SiS_Pr, int reg, unsigned int val) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo; |
| |
| pci_write_config_dword(ivideo->nbridge, reg, (u32)val); |
| } |
| |
| unsigned int |
| sisfb_read_lpc_pci_dword(struct SiS_Private *SiS_Pr, int reg) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo; |
| u32 val = 0; |
| |
| if(!ivideo->lpcdev) return 0; |
| |
| pci_read_config_dword(ivideo->lpcdev, reg, &val); |
| return (unsigned int)val; |
| } |
| #endif |
| |
| #ifdef CONFIG_FB_SIS_315 |
| void |
| sisfb_write_nbridge_pci_byte(struct SiS_Private *SiS_Pr, int reg, unsigned char val) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo; |
| |
| pci_write_config_byte(ivideo->nbridge, reg, (u8)val); |
| } |
| |
| unsigned int |
| sisfb_read_mio_pci_word(struct SiS_Private *SiS_Pr, int reg) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)SiS_Pr->ivideo; |
| u16 val = 0; |
| |
| if(!ivideo->lpcdev) return 0; |
| |
| pci_read_config_word(ivideo->lpcdev, reg, &val); |
| return (unsigned int)val; |
| } |
| #endif |
| |
| /* ----------- FBDev related routines for all series ----------- */ |
| |
| static int |
| sisfb_get_cmap_len(const struct fb_var_screeninfo *var) |
| { |
| return (var->bits_per_pixel == 8) ? 256 : 16; |
| } |
| |
| static void |
| sisfb_set_vparms(struct sis_video_info *ivideo) |
| { |
| switch(ivideo->video_bpp) { |
| case 8: |
| ivideo->DstColor = 0x0000; |
| ivideo->SiS310_AccelDepth = 0x00000000; |
| ivideo->video_cmap_len = 256; |
| break; |
| case 16: |
| ivideo->DstColor = 0x8000; |
| ivideo->SiS310_AccelDepth = 0x00010000; |
| ivideo->video_cmap_len = 16; |
| break; |
| case 32: |
| ivideo->DstColor = 0xC000; |
| ivideo->SiS310_AccelDepth = 0x00020000; |
| ivideo->video_cmap_len = 16; |
| break; |
| default: |
| ivideo->video_cmap_len = 16; |
| printk(KERN_ERR "sisfb: Unsupported depth %d", ivideo->video_bpp); |
| ivideo->accel = 0; |
| } |
| } |
| |
| static int |
| sisfb_calc_maxyres(struct sis_video_info *ivideo, struct fb_var_screeninfo *var) |
| { |
| int maxyres = ivideo->sisfb_mem / (var->xres_virtual * (var->bits_per_pixel >> 3)); |
| |
| if(maxyres > 32767) maxyres = 32767; |
| |
| return maxyres; |
| } |
| |
| static void |
| sisfb_calc_pitch(struct sis_video_info *ivideo, struct fb_var_screeninfo *var) |
| { |
| ivideo->video_linelength = var->xres_virtual * (var->bits_per_pixel >> 3); |
| ivideo->scrnpitchCRT1 = ivideo->video_linelength; |
| if(!(ivideo->currentvbflags & CRT1_LCDA)) { |
| if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { |
| ivideo->scrnpitchCRT1 <<= 1; |
| } |
| } |
| } |
| |
| static void |
| sisfb_set_pitch(struct sis_video_info *ivideo) |
| { |
| bool isslavemode = false; |
| unsigned short HDisplay1 = ivideo->scrnpitchCRT1 >> 3; |
| unsigned short HDisplay2 = ivideo->video_linelength >> 3; |
| |
| if(sisfb_bridgeisslave(ivideo)) isslavemode = true; |
| |
| /* We need to set pitch for CRT1 if bridge is in slave mode, too */ |
| if((ivideo->currentvbflags & VB_DISPTYPE_DISP1) || (isslavemode)) { |
| SiS_SetReg(SISCR, 0x13, (HDisplay1 & 0xFF)); |
| SiS_SetRegANDOR(SISSR, 0x0E, 0xF0, (HDisplay1 >> 8)); |
| } |
| |
| /* We must not set the pitch for CRT2 if bridge is in slave mode */ |
| if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!isslavemode)) { |
| SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01); |
| SiS_SetReg(SISPART1, 0x07, (HDisplay2 & 0xFF)); |
| SiS_SetRegANDOR(SISPART1, 0x09, 0xF0, (HDisplay2 >> 8)); |
| } |
| } |
| |
| static void |
| sisfb_bpp_to_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var) |
| { |
| ivideo->video_cmap_len = sisfb_get_cmap_len(var); |
| |
| switch(var->bits_per_pixel) { |
| case 8: |
| var->red.offset = var->green.offset = var->blue.offset = 0; |
| var->red.length = var->green.length = var->blue.length = 8; |
| break; |
| case 16: |
| var->red.offset = 11; |
| var->red.length = 5; |
| var->green.offset = 5; |
| var->green.length = 6; |
| var->blue.offset = 0; |
| var->blue.length = 5; |
| var->transp.offset = 0; |
| var->transp.length = 0; |
| break; |
| case 32: |
| var->red.offset = 16; |
| var->red.length = 8; |
| var->green.offset = 8; |
| var->green.length = 8; |
| var->blue.offset = 0; |
| var->blue.length = 8; |
| var->transp.offset = 24; |
| var->transp.length = 8; |
| break; |
| } |
| } |
| |
| static int |
| sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn) |
| { |
| unsigned short modeno = ivideo->mode_no; |
| |
| /* >=2.6.12's fbcon clears the screen anyway */ |
| modeno |= 0x80; |
| |
| SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); |
| |
| sisfb_pre_setmode(ivideo); |
| |
| if(!SiSSetMode(&ivideo->SiS_Pr, modeno)) { |
| printk(KERN_ERR "sisfb: Setting mode[0x%x] failed\n", ivideo->mode_no); |
| return -EINVAL; |
| } |
| |
| SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); |
| |
| sisfb_post_setmode(ivideo); |
| |
| return 0; |
| } |
| |
| |
| static int |
| sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive, struct fb_info *info) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)info->par; |
| unsigned int htotal = 0, vtotal = 0; |
| unsigned int drate = 0, hrate = 0; |
| int found_mode = 0, ret; |
| int old_mode; |
| u32 pixclock; |
| |
| htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len; |
| |
| vtotal = var->upper_margin + var->lower_margin + var->vsync_len; |
| |
| pixclock = var->pixclock; |
| |
| if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) { |
| vtotal += var->yres; |
| vtotal <<= 1; |
| } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { |
| vtotal += var->yres; |
| vtotal <<= 2; |
| } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { |
| vtotal += var->yres; |
| vtotal <<= 1; |
| } else vtotal += var->yres; |
| |
| if(!(htotal) || !(vtotal)) { |
| DPRINTK("sisfb: Invalid 'var' information\n"); |
| return -EINVAL; |
| } |
| |
| if(pixclock && htotal && vtotal) { |
| drate = 1000000000 / pixclock; |
| hrate = (drate * 1000) / htotal; |
| ivideo->refresh_rate = (unsigned int) (hrate * 2 / vtotal); |
| } else { |
| ivideo->refresh_rate = 60; |
| } |
| |
| old_mode = ivideo->sisfb_mode_idx; |
| ivideo->sisfb_mode_idx = 0; |
| |
| while( (sisbios_mode[ivideo->sisfb_mode_idx].mode_no[0] != 0) && |
| (sisbios_mode[ivideo->sisfb_mode_idx].xres <= var->xres) ) { |
| if( (sisbios_mode[ivideo->sisfb_mode_idx].xres == var->xres) && |
| (sisbios_mode[ivideo->sisfb_mode_idx].yres == var->yres) && |
| (sisbios_mode[ivideo->sisfb_mode_idx].bpp == var->bits_per_pixel)) { |
| ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]; |
| found_mode = 1; |
| break; |
| } |
| ivideo->sisfb_mode_idx++; |
| } |
| |
| if(found_mode) { |
| ivideo->sisfb_mode_idx = sisfb_validate_mode(ivideo, |
| ivideo->sisfb_mode_idx, ivideo->currentvbflags); |
| } else { |
| ivideo->sisfb_mode_idx = -1; |
| } |
| |
| if(ivideo->sisfb_mode_idx < 0) { |
| printk(KERN_ERR "sisfb: Mode %dx%dx%d not supported\n", var->xres, |
| var->yres, var->bits_per_pixel); |
| ivideo->sisfb_mode_idx = old_mode; |
| return -EINVAL; |
| } |
| |
| ivideo->mode_no = sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]; |
| |
| if(sisfb_search_refresh_rate(ivideo, ivideo->refresh_rate, ivideo->sisfb_mode_idx) == 0) { |
| ivideo->rate_idx = sisbios_mode[ivideo->sisfb_mode_idx].rate_idx; |
| ivideo->refresh_rate = 60; |
| } |
| |
| if(isactive) { |
| /* If acceleration to be used? Need to know |
| * before pre/post_set_mode() |
| */ |
| ivideo->accel = 0; |
| #if defined(FBINFO_HWACCEL_DISABLED) && defined(FBINFO_HWACCEL_XPAN) |
| #ifdef STUPID_ACCELF_TEXT_SHIT |
| if(var->accel_flags & FB_ACCELF_TEXT) { |
| info->flags &= ~FBINFO_HWACCEL_DISABLED; |
| } else { |
| info->flags |= FBINFO_HWACCEL_DISABLED; |
| } |
| #endif |
| if(!(info->flags & FBINFO_HWACCEL_DISABLED)) ivideo->accel = -1; |
| #else |
| if(var->accel_flags & FB_ACCELF_TEXT) ivideo->accel = -1; |
| #endif |
| |
| if((ret = sisfb_set_mode(ivideo, 1))) { |
| return ret; |
| } |
| |
| ivideo->video_bpp = sisbios_mode[ivideo->sisfb_mode_idx].bpp; |
| ivideo->video_width = sisbios_mode[ivideo->sisfb_mode_idx].xres; |
| ivideo->video_height = sisbios_mode[ivideo->sisfb_mode_idx].yres; |
| |
| sisfb_calc_pitch(ivideo, var); |
| sisfb_set_pitch(ivideo); |
| |
| sisfb_set_vparms(ivideo); |
| |
| ivideo->current_width = ivideo->video_width; |
| ivideo->current_height = ivideo->video_height; |
| ivideo->current_bpp = ivideo->video_bpp; |
| ivideo->current_htotal = htotal; |
| ivideo->current_vtotal = vtotal; |
| ivideo->current_linelength = ivideo->video_linelength; |
| ivideo->current_pixclock = var->pixclock; |
| ivideo->current_refresh_rate = ivideo->refresh_rate; |
| ivideo->sisfb_lastrates[ivideo->mode_no] = ivideo->refresh_rate; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| sisfb_set_base_CRT1(struct sis_video_info *ivideo, unsigned int base) |
| { |
| SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); |
| |
| SiS_SetReg(SISCR, 0x0D, base & 0xFF); |
| SiS_SetReg(SISCR, 0x0C, (base >> 8) & 0xFF); |
| SiS_SetReg(SISSR, 0x0D, (base >> 16) & 0xFF); |
| if(ivideo->sisvga_engine == SIS_315_VGA) { |
| SiS_SetRegANDOR(SISSR, 0x37, 0xFE, (base >> 24) & 0x01); |
| } |
| } |
| |
| static void |
| sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base) |
| { |
| if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) { |
| SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01); |
| SiS_SetReg(SISPART1, 0x06, (base & 0xFF)); |
| SiS_SetReg(SISPART1, 0x05, ((base >> 8) & 0xFF)); |
| SiS_SetReg(SISPART1, 0x04, ((base >> 16) & 0xFF)); |
| if(ivideo->sisvga_engine == SIS_315_VGA) { |
| SiS_SetRegANDOR(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7); |
| } |
| } |
| } |
| |
| static int |
| sisfb_pan_var(struct sis_video_info *ivideo, struct fb_info *info, |
| struct fb_var_screeninfo *var) |
| { |
| ivideo->current_base = var->yoffset * info->var.xres_virtual |
| + var->xoffset; |
| |
| /* calculate base bpp dep. */ |
| switch (info->var.bits_per_pixel) { |
| case 32: |
| break; |
| case 16: |
| ivideo->current_base >>= 1; |
| break; |
| case 8: |
| default: |
| ivideo->current_base >>= 2; |
| break; |
| } |
| |
| ivideo->current_base += (ivideo->video_offset >> 2); |
| |
| sisfb_set_base_CRT1(ivideo, ivideo->current_base); |
| sisfb_set_base_CRT2(ivideo, ivideo->current_base); |
| |
| return 0; |
| } |
| |
| static int |
| sisfb_open(struct fb_info *info, int user) |
| { |
| return 0; |
| } |
| |
| static int |
| sisfb_release(struct fb_info *info, int user) |
| { |
| return 0; |
| } |
| |
| static int |
| sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, |
| unsigned transp, struct fb_info *info) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)info->par; |
| |
| if(regno >= sisfb_get_cmap_len(&info->var)) |
| return 1; |
| |
| switch(info->var.bits_per_pixel) { |
| case 8: |
| SiS_SetRegByte(SISDACA, regno); |
| SiS_SetRegByte(SISDACD, (red >> 10)); |
| SiS_SetRegByte(SISDACD, (green >> 10)); |
| SiS_SetRegByte(SISDACD, (blue >> 10)); |
| if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) { |
| SiS_SetRegByte(SISDAC2A, regno); |
| SiS_SetRegByte(SISDAC2D, (red >> 8)); |
| SiS_SetRegByte(SISDAC2D, (green >> 8)); |
| SiS_SetRegByte(SISDAC2D, (blue >> 8)); |
| } |
| break; |
| case 16: |
| if (regno >= 16) |
| break; |
| |
| ((u32 *)(info->pseudo_palette))[regno] = |
| (red & 0xf800) | |
| ((green & 0xfc00) >> 5) | |
| ((blue & 0xf800) >> 11); |
| break; |
| case 32: |
| if (regno >= 16) |
| break; |
| |
| red >>= 8; |
| green >>= 8; |
| blue >>= 8; |
| ((u32 *)(info->pseudo_palette))[regno] = |
| (red << 16) | (green << 8) | (blue); |
| break; |
| } |
| return 0; |
| } |
| |
| static int |
| sisfb_set_par(struct fb_info *info) |
| { |
| int err; |
| |
| if((err = sisfb_do_set_var(&info->var, 1, info))) |
| return err; |
| |
| sisfb_get_fix(&info->fix, -1, info); |
| |
| return 0; |
| } |
| |
| static int |
| sisfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)info->par; |
| unsigned int htotal = 0, vtotal = 0, myrateindex = 0; |
| unsigned int drate = 0, hrate = 0, maxyres; |
| int found_mode = 0; |
| int refresh_rate, search_idx, tidx; |
| bool recalc_clock = false; |
| u32 pixclock; |
| |
| htotal = var->left_margin + var->xres + var->right_margin + var->hsync_len; |
| |
| vtotal = var->upper_margin + var->lower_margin + var->vsync_len; |
| |
| pixclock = var->pixclock; |
| |
| if((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) { |
| vtotal += var->yres; |
| vtotal <<= 1; |
| } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { |
| vtotal += var->yres; |
| vtotal <<= 2; |
| } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { |
| vtotal += var->yres; |
| vtotal <<= 1; |
| } else |
| vtotal += var->yres; |
| |
| if(!(htotal) || !(vtotal)) { |
| SISFAIL("sisfb: no valid timing data"); |
| } |
| |
| search_idx = 0; |
| while( (sisbios_mode[search_idx].mode_no[0] != 0) && |
| (sisbios_mode[search_idx].xres <= var->xres) ) { |
| if( (sisbios_mode[search_idx].xres == var->xres) && |
| (sisbios_mode[search_idx].yres == var->yres) && |
| (sisbios_mode[search_idx].bpp == var->bits_per_pixel)) { |
| if((tidx = sisfb_validate_mode(ivideo, search_idx, |
| ivideo->currentvbflags)) > 0) { |
| found_mode = 1; |
| search_idx = tidx; |
| break; |
| } |
| } |
| search_idx++; |
| } |
| |
| if(!found_mode) { |
| search_idx = 0; |
| while(sisbios_mode[search_idx].mode_no[0] != 0) { |
| if( (var->xres <= sisbios_mode[search_idx].xres) && |
| (var->yres <= sisbios_mode[search_idx].yres) && |
| (var->bits_per_pixel == sisbios_mode[search_idx].bpp) ) { |
| if((tidx = sisfb_validate_mode(ivideo,search_idx, |
| ivideo->currentvbflags)) > 0) { |
| found_mode = 1; |
| search_idx = tidx; |
| break; |
| } |
| } |
| search_idx++; |
| } |
| if(found_mode) { |
| printk(KERN_DEBUG |
| "sisfb: Adapted from %dx%dx%d to %dx%dx%d\n", |
| var->xres, var->yres, var->bits_per_pixel, |
| sisbios_mode[search_idx].xres, |
| sisbios_mode[search_idx].yres, |
| var->bits_per_pixel); |
| var->xres = sisbios_mode[search_idx].xres; |
| var->yres = sisbios_mode[search_idx].yres; |
| } else { |
| printk(KERN_ERR |
| "sisfb: Failed to find supported mode near %dx%dx%d\n", |
| var->xres, var->yres, var->bits_per_pixel); |
| return -EINVAL; |
| } |
| } |
| |
| if( ((ivideo->vbflags2 & VB2_LVDS) || |
| ((ivideo->vbflags2 & VB2_30xBDH) && (ivideo->currentvbflags & CRT2_LCD))) && |
| (var->bits_per_pixel == 8) ) { |
| /* Slave modes on LVDS and 301B-DH */ |
| refresh_rate = 60; |
| recalc_clock = true; |
| } else if( (ivideo->current_htotal == htotal) && |
| (ivideo->current_vtotal == vtotal) && |
| (ivideo->current_pixclock == pixclock) ) { |
| /* x=x & y=y & c=c -> assume depth change */ |
| drate = 1000000000 / pixclock; |
| hrate = (drate * 1000) / htotal; |
| refresh_rate = (unsigned int) (hrate * 2 / vtotal); |
| } else if( ( (ivideo->current_htotal != htotal) || |
| (ivideo->current_vtotal != vtotal) ) && |
| (ivideo->current_pixclock == var->pixclock) ) { |
| /* x!=x | y!=y & c=c -> invalid pixclock */ |
| if(ivideo->sisfb_lastrates[sisbios_mode[search_idx].mode_no[ivideo->mni]]) { |
| refresh_rate = |
| ivideo->sisfb_lastrates[sisbios_mode[search_idx].mode_no[ivideo->mni]]; |
| } else if(ivideo->sisfb_parm_rate != -1) { |
| /* Sic, sisfb_parm_rate - want to know originally desired rate here */ |
| refresh_rate = ivideo->sisfb_parm_rate; |
| } else { |
| refresh_rate = 60; |
| } |
| recalc_clock = true; |
| } else if((pixclock) && (htotal) && (vtotal)) { |
| drate = 1000000000 / pixclock; |
| hrate = (drate * 1000) / htotal; |
| refresh_rate = (unsigned int) (hrate * 2 / vtotal); |
| } else if(ivideo->current_refresh_rate) { |
| refresh_rate = ivideo->current_refresh_rate; |
| recalc_clock = true; |
| } else { |
| refresh_rate = 60; |
| recalc_clock = true; |
| } |
| |
| myrateindex = sisfb_search_refresh_rate(ivideo, refresh_rate, search_idx); |
| |
| /* Eventually recalculate timing and clock */ |
| if(recalc_clock) { |
| if(!myrateindex) myrateindex = sisbios_mode[search_idx].rate_idx; |
| var->pixclock = (u32) (1000000000 / sisfb_mode_rate_to_dclock(&ivideo->SiS_Pr, |
| sisbios_mode[search_idx].mode_no[ivideo->mni], |
| myrateindex)); |
| sisfb_mode_rate_to_ddata(&ivideo->SiS_Pr, |
| sisbios_mode[search_idx].mode_no[ivideo->mni], |
| myrateindex, var); |
| if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { |
| var->pixclock <<= 1; |
| } |
| } |
| |
| if(ivideo->sisfb_thismonitor.datavalid) { |
| if(!sisfb_verify_rate(ivideo, &ivideo->sisfb_thismonitor, search_idx, |
| myrateindex, refresh_rate)) { |
| printk(KERN_INFO |
| "sisfb: WARNING: Refresh rate exceeds monitor specs!\n"); |
| } |
| } |
| |
| /* Adapt RGB settings */ |
| sisfb_bpp_to_var(ivideo, var); |
| |
| /* Sanity check for offsets */ |
| if(var->xoffset < 0) var->xoffset = 0; |
| if(var->yoffset < 0) var->yoffset = 0; |
| |
| if(var->xres > var->xres_virtual) |
| var->xres_virtual = var->xres; |
| |
| if(ivideo->sisfb_ypan) { |
| maxyres = sisfb_calc_maxyres(ivideo, var); |
| if(ivideo->sisfb_max) { |
| var->yres_virtual = maxyres; |
| } else { |
| if(var->yres_virtual > maxyres) { |
| var->yres_virtual = maxyres; |
| } |
| } |
| if(var->yres_virtual <= var->yres) { |
| var->yres_virtual = var->yres; |
| } |
| } else { |
| if(var->yres != var->yres_virtual) { |
| var->yres_virtual = var->yres; |
| } |
| var->xoffset = 0; |
| var->yoffset = 0; |
| } |
| |
| /* Truncate offsets to maximum if too high */ |
| if(var->xoffset > var->xres_virtual - var->xres) { |
| var->xoffset = var->xres_virtual - var->xres - 1; |
| } |
| |
| if(var->yoffset > var->yres_virtual - var->yres) { |
| var->yoffset = var->yres_virtual - var->yres - 1; |
| } |
| |
| /* Set everything else to 0 */ |
| var->red.msb_right = |
| var->green.msb_right = |
| var->blue.msb_right = |
| var->transp.offset = |
| var->transp.length = |
| var->transp.msb_right = 0; |
| |
| return 0; |
| } |
| |
| static int |
| sisfb_pan_display(struct fb_var_screeninfo *var, struct fb_info* info) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)info->par; |
| int err; |
| |
| if (var->vmode & FB_VMODE_YWRAP) |
| return -EINVAL; |
| |
| if (var->xoffset + info->var.xres > info->var.xres_virtual || |
| var->yoffset + info->var.yres > info->var.yres_virtual) |
| return -EINVAL; |
| |
| err = sisfb_pan_var(ivideo, info, var); |
| if (err < 0) |
| return err; |
| |
| info->var.xoffset = var->xoffset; |
| info->var.yoffset = var->yoffset; |
| |
| return 0; |
| } |
| |
| static int |
| sisfb_blank(int blank, struct fb_info *info) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)info->par; |
| |
| return sisfb_myblank(ivideo, blank); |
| } |
| |
| /* ----------- FBDev related routines for all series ---------- */ |
| |
| static int sisfb_ioctl(struct fb_info *info, unsigned int cmd, |
| unsigned long arg) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)info->par; |
| struct sis_memreq sismemreq; |
| struct fb_vblank sisvbblank; |
| u32 gpu32 = 0; |
| #ifndef __user |
| #define __user |
| #endif |
| u32 __user *argp = (u32 __user *)arg; |
| |
| switch(cmd) { |
| case FBIO_ALLOC: |
| if(!capable(CAP_SYS_RAWIO)) |
| return -EPERM; |
| |
| if(copy_from_user(&sismemreq, (void __user *)arg, sizeof(sismemreq))) |
| return -EFAULT; |
| |
| sis_malloc(&sismemreq); |
| |
| if(copy_to_user((void __user *)arg, &sismemreq, sizeof(sismemreq))) { |
| sis_free((u32)sismemreq.offset); |
| return -EFAULT; |
| } |
| break; |
| |
| case FBIO_FREE: |
| if(!capable(CAP_SYS_RAWIO)) |
| return -EPERM; |
| |
| if(get_user(gpu32, argp)) |
| return -EFAULT; |
| |
| sis_free(gpu32); |
| break; |
| |
| case FBIOGET_VBLANK: |
| |
| memset(&sisvbblank, 0, sizeof(struct fb_vblank)); |
| |
| sisvbblank.count = 0; |
| sisvbblank.flags = sisfb_setupvbblankflags(ivideo, &sisvbblank.vcount, &sisvbblank.hcount); |
| |
| if(copy_to_user((void __user *)arg, &sisvbblank, sizeof(sisvbblank))) |
| return -EFAULT; |
| |
| break; |
| |
| case SISFB_GET_INFO_SIZE: |
| return put_user(sizeof(struct sisfb_info), argp); |
| |
| case SISFB_GET_INFO_OLD: |
| if(ivideo->warncount++ < 10) |
| printk(KERN_INFO |
| "sisfb: Deprecated ioctl call received - update your application!\n"); |
| case SISFB_GET_INFO: /* For communication with X driver */ |
| ivideo->sisfb_infoblock.sisfb_id = SISFB_ID; |
| ivideo->sisfb_infoblock.sisfb_version = VER_MAJOR; |
| ivideo->sisfb_infoblock.sisfb_revision = VER_MINOR; |
| ivideo->sisfb_infoblock.sisfb_patchlevel = VER_LEVEL; |
| ivideo->sisfb_infoblock.chip_id = ivideo->chip_id; |
| ivideo->sisfb_infoblock.sisfb_pci_vendor = ivideo->chip_vendor; |
| ivideo->sisfb_infoblock.memory = ivideo->video_size / 1024; |
| ivideo->sisfb_infoblock.heapstart = ivideo->heapstart / 1024; |
| if(ivideo->modechanged) { |
| ivideo->sisfb_infoblock.fbvidmode = ivideo->mode_no; |
| } else { |
| ivideo->sisfb_infoblock.fbvidmode = ivideo->modeprechange; |
| } |
| ivideo->sisfb_infoblock.sisfb_caps = ivideo->caps; |
| ivideo->sisfb_infoblock.sisfb_tqlen = ivideo->cmdQueueSize / 1024; |
| ivideo->sisfb_infoblock.sisfb_pcibus = ivideo->pcibus; |
| ivideo->sisfb_infoblock.sisfb_pcislot = ivideo->pcislot; |
| ivideo->sisfb_infoblock.sisfb_pcifunc = ivideo->pcifunc; |
| ivideo->sisfb_infoblock.sisfb_lcdpdc = ivideo->detectedpdc; |
| ivideo->sisfb_infoblock.sisfb_lcdpdca = ivideo->detectedpdca; |
| ivideo->sisfb_infoblock.sisfb_lcda = ivideo->detectedlcda; |
| ivideo->sisfb_infoblock.sisfb_vbflags = ivideo->vbflags; |
| ivideo->sisfb_infoblock.sisfb_currentvbflags = ivideo->currentvbflags; |
| ivideo->sisfb_infoblock.sisfb_scalelcd = ivideo->SiS_Pr.UsePanelScaler; |
| ivideo->sisfb_infoblock.sisfb_specialtiming = ivideo->SiS_Pr.SiS_CustomT; |
| ivideo->sisfb_infoblock.sisfb_haveemi = ivideo->SiS_Pr.HaveEMI ? 1 : 0; |
| ivideo->sisfb_infoblock.sisfb_haveemilcd = ivideo->SiS_Pr.HaveEMILCD ? 1 : 0; |
| ivideo->sisfb_infoblock.sisfb_emi30 = ivideo->SiS_Pr.EMI_30; |
| ivideo->sisfb_infoblock.sisfb_emi31 = ivideo->SiS_Pr.EMI_31; |
| ivideo->sisfb_infoblock.sisfb_emi32 = ivideo->SiS_Pr.EMI_32; |
| ivideo->sisfb_infoblock.sisfb_emi33 = ivideo->SiS_Pr.EMI_33; |
| ivideo->sisfb_infoblock.sisfb_tvxpos = (u16)(ivideo->tvxpos + 32); |
| ivideo->sisfb_infoblock.sisfb_tvypos = (u16)(ivideo->tvypos + 32); |
| ivideo->sisfb_infoblock.sisfb_heapsize = ivideo->sisfb_heap_size / 1024; |
| ivideo->sisfb_infoblock.sisfb_videooffset = ivideo->video_offset; |
| ivideo->sisfb_infoblock.sisfb_curfstn = ivideo->curFSTN; |
| ivideo->sisfb_infoblock.sisfb_curdstn = ivideo->curDSTN; |
| ivideo->sisfb_infoblock.sisfb_vbflags2 = ivideo->vbflags2; |
| ivideo->sisfb_infoblock.sisfb_can_post = ivideo->sisfb_can_post ? 1 : 0; |
| ivideo->sisfb_infoblock.sisfb_card_posted = ivideo->sisfb_card_posted ? 1 : 0; |
| ivideo->sisfb_infoblock.sisfb_was_boot_device = ivideo->sisfb_was_boot_device ? 1 : 0; |
| |
| if(copy_to_user((void __user *)arg, &ivideo->sisfb_infoblock, |
| sizeof(ivideo->sisfb_infoblock))) |
| return -EFAULT; |
| |
| break; |
| |
| case SISFB_GET_VBRSTATUS_OLD: |
| if(ivideo->warncount++ < 10) |
| printk(KERN_INFO |
| "sisfb: Deprecated ioctl call received - update your application!\n"); |
| case SISFB_GET_VBRSTATUS: |
| if(sisfb_CheckVBRetrace(ivideo)) |
| return put_user((u32)1, argp); |
| else |
| return put_user((u32)0, argp); |
| |
| case SISFB_GET_AUTOMAXIMIZE_OLD: |
| if(ivideo->warncount++ < 10) |
| printk(KERN_INFO |
| "sisfb: Deprecated ioctl call received - update your application!\n"); |
| case SISFB_GET_AUTOMAXIMIZE: |
| if(ivideo->sisfb_max) |
| return put_user((u32)1, argp); |
| else |
| return put_user((u32)0, argp); |
| |
| case SISFB_SET_AUTOMAXIMIZE_OLD: |
| if(ivideo->warncount++ < 10) |
| printk(KERN_INFO |
| "sisfb: Deprecated ioctl call received - update your application!\n"); |
| case SISFB_SET_AUTOMAXIMIZE: |
| if(get_user(gpu32, argp)) |
| return -EFAULT; |
| |
| ivideo->sisfb_max = (gpu32) ? 1 : 0; |
| break; |
| |
| case SISFB_SET_TVPOSOFFSET: |
| if(get_user(gpu32, argp)) |
| return -EFAULT; |
| |
| sisfb_set_TVxposoffset(ivideo, ((int)(gpu32 >> 16)) - 32); |
| sisfb_set_TVyposoffset(ivideo, ((int)(gpu32 & 0xffff)) - 32); |
| break; |
| |
| case SISFB_GET_TVPOSOFFSET: |
| return put_user((u32)(((ivideo->tvxpos+32)<<16)|((ivideo->tvypos+32)&0xffff)), |
| argp); |
| |
| case SISFB_COMMAND: |
| if(copy_from_user(&ivideo->sisfb_command, (void __user *)arg, |
| sizeof(struct sisfb_cmd))) |
| return -EFAULT; |
| |
| sisfb_handle_command(ivideo, &ivideo->sisfb_command); |
| |
| if(copy_to_user((void __user *)arg, &ivideo->sisfb_command, |
| sizeof(struct sisfb_cmd))) |
| return -EFAULT; |
| |
| break; |
| |
| case SISFB_SET_LOCK: |
| if(get_user(gpu32, argp)) |
| return -EFAULT; |
| |
| ivideo->sisfblocked = (gpu32) ? 1 : 0; |
| break; |
| |
| default: |
| #ifdef SIS_NEW_CONFIG_COMPAT |
| return -ENOIOCTLCMD; |
| #else |
| return -EINVAL; |
| #endif |
| } |
| return 0; |
| } |
| |
| static int |
| sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info) |
| { |
| struct sis_video_info *ivideo = (struct sis_video_info *)info->par; |
| |
| memset(fix, 0, sizeof(struct fb_fix_screeninfo)); |
| |
| strlcpy(fix->id, ivideo->myid, sizeof(fix->id)); |
| |
| mutex_lock(&info->mm_lock); |
| fix->smem_start = ivideo->video_base + ivideo->video_offset; |
| fix->smem_len = ivideo->sisfb_mem; |
| mutex_unlock(&info->mm_lock); |
| fix->type = FB_TYPE_PACKED_PIXELS; |
| fix->type_aux = 0; |
| fix->visual = (ivideo->video_bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; |
| fix->xpanstep = 1; |
| fix->ypanstep = (ivideo->sisfb_ypan) ? 1 : 0; |
| fix->ywrapstep = 0; |
| fix->line_length = ivideo->video_linelength; |
| fix->mmio_start = ivideo->mmio_base; |
| fix->mmio_len = ivideo->mmio_size; |
| if(ivideo->sisvga_engine == SIS_300_VGA) { |
| fix->accel = FB_ACCEL_SIS_GLAMOUR; |
| } else if((ivideo->chip == SIS_330) || |
| (ivideo->chip == SIS_760) || |
| (ivideo->chip == SIS_761)) { |
| fix->accel = FB_ACCEL_SIS_XABRE; |
| } else if(ivideo->chip == XGI_20) { |
| fix->accel = FB_ACCEL_XGI_VOLARI_Z; |
| } else if(ivideo->chip >= XGI_40) { |
| fix->accel = FB_ACCEL_XGI_VOLARI_V; |
| } else { |
| fix->accel = FB_ACCEL_SIS_GLAMOUR_2; |
| } |
| |
| return 0; |
| } |
| |
| /* ---------------- fb_ops structures ----------------- */ |
| |
| static struct fb_ops sisfb_ops = { |
| .owner = THIS_MODULE, |
| .fb_open = sisfb_open, |
| .fb_release = sisfb_release, |
| .fb_check_var = sisfb_check_var, |
| .fb_set_par = sisfb_set_par, |
| .fb_setcolreg = sisfb_setcolreg, |
| .fb_pan_display = sisfb_pan_display, |
| .fb_blank = sisfb_blank, |
| .fb_fillrect = fbcon_sis_fillrect, |
| .fb_copyarea = fbcon_sis_copyarea, |
| .fb_imageblit = cfb_imageblit, |
| .fb_sync = fbcon_sis_sync, |
| #ifdef SIS_NEW_CONFIG_COMPAT |
| .fb_compat_ioctl= sisfb_ioctl, |
| #endif |
| .fb_ioctl = sisfb_ioctl |
| }; |
| |
| /* ---------------- Chip generation dependent routines ---------------- */ |
| |
| static struct pci_dev * __devinit |
| sisfb_get_northbridge(int basechipid) |
| { |
| struct pci_dev *pdev = NULL; |
| int nbridgenum, nbridgeidx, i; |
| static const unsigned short nbridgeids[] = { |
| PCI_DEVICE_ID_SI_540, /* for SiS 540 VGA */ |
| PCI_DEVICE_ID_SI_630, /* for SiS 630/730 VGA */ |
| PCI_DEVICE_ID_SI_730, |
| PCI_DEVICE_ID_SI_550, /* for SiS 550 VGA */ |
| PCI_DEVICE_ID_SI_650, /* for SiS 650/651/740 VGA */ |
| PCI_DEVICE_ID_SI_651, |
| PCI_DEVICE_ID_SI_740, |
| PCI_DEVICE_ID_SI_661, /* for SiS 661/741/660/760/761 VGA */ |
| PCI_DEVICE_ID_SI_741, |
| PCI_DEVICE_ID_SI_660, |
| PCI_DEVICE_ID_SI_760, |
| PCI_DEVICE_ID_SI_761 |
| }; |
| |
| switch(basechipid) { |
| #ifdef CONFIG_FB_SIS_300 |
| case SIS_540: nbridgeidx = 0; nbridgenum = 1; break; |
| case SIS_630: nbridgeidx = 1; nbridgenum = 2; break; |
| #endif |
| #ifdef CONFIG_FB_SIS_315 |
| case SIS_550: nbridgeidx = 3; nbridgenum = 1; break; |
| case SIS_650: nbridgeidx = 4; nbridgenum = 3; break; |
| case SIS_660: nbridgeidx = 7; nbridgenum = 5; break; |
| #endif |
| default: return NULL; |
| } |
| for(i = 0; i < nbridgenum; i++) { |
| if((pdev = pci_get_device(PCI_VENDOR_ID_SI, |
| nbridgeids[nbridgeidx+i], NULL))) |
| break; |
| } |
| return pdev; |
| } |
| |
| static int __devinit |
| sisfb_get_dram_size(struct sis_video_info *ivideo) |
| { |
| #if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315) |
| u8 reg; |
| #endif |
| |
| ivideo->video_size = 0; |
| ivideo->UMAsize = ivideo->LFBsize = 0; |
| |
| switch(ivideo->chip) { |
| #ifdef CONFIG_FB_SIS_300 |
| case SIS_300: |
| reg = SiS_GetReg(SISSR, 0x14); |
| ivideo->video_size = ((reg & 0x3F) + 1) << 20; |
| break; |
| case SIS_540: |
| case SIS_630: |
| case SIS_730: |
| if(!ivideo->nbridge) |
| return -1; |
| pci_read_config_byte(ivideo->nbridge, 0x63, ®); |
| ivideo->video_size = 1 << (((reg & 0x70) >> 4) + 21); |
| break; |
| #endif |
| #ifdef CONFIG_FB_SIS_315 |
| case SIS_315H: |
| case SIS_315PRO: |
| case SIS_315: |
| reg = SiS_GetReg(SISSR, 0x14); |
| ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; |
| switch((reg >> 2) & 0x03) { |
| case 0x01: |
| case 0x03: |
| ivideo->video_size <<= 1; |
| break; |
| case 0x02: |
| ivideo->video_size += (ivideo->video_size/2); |
| } |
| break; |
| case SIS_330: |
| reg = SiS_GetReg(SISSR, 0x14); |
| ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; |
| if(reg & 0x0c) ivideo->video_size <<= 1; |
| break; |
| case SIS_550: |
| case SIS_650: |
| case SIS_740: |
| reg = SiS_GetReg(SISSR, 0x14); |
| ivideo->video_size = (((reg & 0x3f) + 1) << 2) << 20; |
| break; |
| case SIS_661: |
| case SIS_741: |
| reg = SiS_GetReg(SISCR, 0x79); |
| ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; |
| break; |
| case SIS_660: |
| case SIS_760: |
| case SIS_761: |
| reg = SiS_GetReg(SISCR, 0x79); |
| reg = (reg & 0xf0) >> 4; |
| if(reg) { |
| ivideo->video_size = (1 << reg) << 20; |
| ivideo->UMAsize = ivideo->video_size; |
| } |
| reg = SiS_GetReg(SISCR, 0x78); |
| reg &= 0x30; |
| if(reg) { |
| if(reg == 0x10) { |
| ivideo->LFBsize = (32 << 20); |
| } else { |
| ivideo->LFBsize = (64 << 20); |
| } |
| ivideo->video_size += ivideo->LFBsize; |
| } |
| break; |
| case SIS_340: |
| case XGI_20: |
| case XGI_40: |
| reg = SiS_GetReg(SISSR, 0x14); |
| ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; |
| if(ivideo->chip != XGI_20) { |
| reg = (reg & 0x0c) >> 2; |
| if(ivideo->revision_id == 2) { |
| if(reg & 0x01) reg = 0x02; |
| else reg = 0x00; |
| } |
| if(reg == 0x02) ivideo->video_size <<= 1; |
| else if(reg == 0x03) ivideo->video_size <<= 2; |
| } |
| break; |
| #endif |
| default: |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* -------------- video bridge device detection --------------- */ |
| |
| static void __devinit |
| sisfb_detect_VB_connect(struct sis_video_info *ivideo) |
| { |
| u8 cr32, temp; |
| |
| /* No CRT2 on XGI Z7 */ |
| if(ivideo->chip == XGI_20) { |
| ivideo->sisfb_crt1off = 0; |
| return; |
| } |
| |
| #ifdef CONFIG_FB_SIS_300 |
| if(ivideo->sisvga_engine == SIS_300_VGA) { |
| temp = SiS_GetReg(SISSR, 0x17); |
| if((temp & 0x0F) && (ivideo->chip != SIS_300)) { |
| /* PAL/NTSC is stored on SR16 on such machines */ |
| if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN))) { |
| temp = SiS_GetReg(SISSR, 0x16); |
| if(temp & 0x20) |
| ivideo->vbflags |= TV_PAL; |
| else |
| ivideo->vbflags |= TV_NTSC; |
| } |
| } |
| } |
| #endif |
| |
| cr32 = SiS_GetReg(SISCR, 0x32); |
| |
| if(cr32 & SIS_CRT1) { |
| ivideo->sisfb_crt1off = 0; |
| } else { |
| ivideo->sisfb_crt1off = (cr32 & 0xDF) ? 1 : 0; |
| } |
| |
| ivideo->vbflags &= ~(CRT2_TV | CRT2_LCD | CRT2_VGA); |
| |
| if(cr32 & SIS_VB_TV) ivideo->vbflags |= CRT2_TV; |
| if(cr32 & SIS_VB_LCD) ivideo->vbflags |= CRT2_LCD; |
| if(cr32 & SIS_VB_CRT2) ivideo->vbflags |= CRT2_VGA; |
| |
| /* Check given parms for hardware compatibility. |
| * (Cannot do this in the search_xx routines since we don't |
| * know what hardware we are running on then) |
| */ |
| |
| if(ivideo->chip != SIS_550) { |
| ivideo->sisfb_dstn = ivideo->sisfb_fstn = 0; |
| } |
| |
| if(ivideo->sisfb_tvplug != -1) { |
| if( (ivideo->sisvga_engine != SIS_315_VGA) || |
| (!(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) ) { |
| if(ivideo->sisfb_tvplug & TV_YPBPR) { |
| ivideo->sisfb_tvplug = -1; |
| printk(KERN_ERR "sisfb: YPbPr not supported\n"); |
| } |
| } |
| } |
| if(ivideo->sisfb_tvplug != -1) { |
| if( (ivideo->sisvga_engine != SIS_315_VGA) || |
| (!(ivideo->vbflags2 & VB2_SISHIVISIONBRIDGE)) ) { |
| if(ivideo->sisfb_tvplug & TV_HIVISION) { |
| ivideo->sisfb_tvplug = -1; |
| printk(KERN_ERR "sisfb: HiVision not supported\n"); |
| } |
| } |
| } |
| if(ivideo->sisfb_tvstd != -1) { |
| if( (!(ivideo->vbflags2 & VB2_SISBRIDGE)) && |
| (!((ivideo->sisvga_engine == SIS_315_VGA) && |
| (ivideo->vbflags2 & VB2_CHRONTEL))) ) { |
| if(ivideo->sisfb_tvstd & (TV_PALM | TV_PALN | TV_NTSCJ)) { |
| ivideo->sisfb_tvstd = -1; |
| printk(KERN_ERR "sisfb: PALM/PALN/NTSCJ not supported\n"); |
| } |
| } |
| } |
| |
| /* Detect/set TV plug & type */ |
| if(ivideo->sisfb_tvplug != -1) { |
| ivideo->vbflags |= ivideo->sisfb_tvplug; |
| } else { |
| if(cr32 & SIS_VB_YPBPR) ivideo->vbflags |= (TV_YPBPR|TV_YPBPR525I); /* default: 480i */ |
| else if(cr32 & SIS_VB_HIVISION) ivideo->vbflags |= TV_HIVISION; |
| else if(cr32 & SIS_VB_SCART) ivideo->vbflags |= TV_SCART; |
| else { |
| if(cr32 & SIS_VB_SVIDEO) ivideo->vbflags |= TV_SVIDEO; |
| if(cr32 & SIS_VB_COMPOSITE) ivideo->vbflags |= TV_AVIDEO; |
| } |
| } |
| |
| if(!(ivideo->vbflags & (TV_YPBPR | TV_HIVISION))) { |
| if(ivideo->sisfb_tvstd != -1) { |
| ivideo->vbflags &= ~(TV_NTSC | TV_PAL | TV_PALM | TV_PALN | TV_NTSCJ); |
| ivideo->vbflags |= ivideo->sisfb_tvstd; |
| } |
| if(ivideo->vbflags & TV_SCART) { |
| ivideo->vbflags &= ~(TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ); |
| ivideo->vbflags |= TV_PAL; |
| } |
| if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ))) { |
| if(ivideo->sisvga_engine == SIS_300_VGA) { |
| temp = SiS_GetReg(SISSR, 0x38); |
| if(temp & 0x01) ivideo->vbflags |= TV_PAL; |
| else ivideo->vbflags |= TV_NTSC; |
| } else if((ivideo->chip <= SIS_315PRO) || (ivideo->chip >= SIS_330)) { |
| temp = SiS_GetReg(SISSR, 0x38); |
| if(temp & 0x01) ivideo->vbflags |= TV_PAL; |
| else ivideo->vbflags |= TV_NTSC; |
| } else { |
| temp = SiS_GetReg(SISCR, 0x79); |
| if(temp & 0x20) ivideo->vbflags |= TV_PAL; |
| else ivideo->vbflags |= TV_NTSC; |
| } |
| } |
| } |
| |
| /* Copy forceCRT1 option to CRT1off if option is given */ |
| if(ivideo->sisfb_forcecrt1 != -1) { |
| ivideo->sisfb_crt1off = (ivideo->sisfb_forcecrt1) ? 0 : 1; |
| } |
| } |
| |
| /* ------------------ Sensing routines ------------------ */ |
| |
| static bool __devinit |
| sisfb_test_DDC1(struct sis_video_info *ivideo) |
| { |
| unsigned short old; |
| int count = 48; |
| |
| old = SiS_ReadDDC1Bit(&ivideo->SiS_Pr); |
| do { |
| if(old != SiS_ReadDDC1Bit(&ivideo->SiS_Pr)) break; |
| } while(count--); |
| return (count != -1); |
| } |
| |
| static void __devinit |
| sisfb_sense_crt1(struct sis_video_info *ivideo) |
| { |
| bool mustwait = false; |
| u8 sr1F, cr17; |
| #ifdef CONFIG_FB_SIS_315 |
| u8 cr63=0; |
| #endif |
| u16 temp = 0xffff; |
| int i; |
| |
| sr1F = SiS_GetReg(SISSR, 0x1F); |
| SiS_SetRegOR(SISSR, 0x1F, 0x04); |
| SiS_SetRegAND(SISSR, 0x1F, 0x3F); |
| if(sr1F & 0xc0) mustwait = true; |
| |
| #ifdef CONFIG_FB_SIS_315 |
| if(ivideo->sisvga_engine == SIS_315_VGA) { |
| cr63 = SiS_GetReg(SISCR, ivideo->SiS_Pr.SiS_MyCR63); |
| cr63 &= 0x40; |
| SiS_SetRegAND(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF); |
| } |
| #endif |
| |
| cr17 = SiS_GetReg(SISCR, 0x17); |
| cr17 &= 0x80; |
| if(!cr17) { |
| SiS_SetRegOR(SISCR, 0x17, 0x80); |
| mustwait = true; |
| SiS_SetReg(SISSR, 0x00, 0x01); |
| SiS_SetReg(SISSR, 0x00, 0x03); |
| } |
| |
| if(mustwait) { |
| for(i=0; i < 10; i++) sisfbwaitretracecrt1(ivideo); |
| } |
| |
| #ifdef CONFIG_FB_SIS_315 |
| if(ivideo->chip >= SIS_330) { |
| SiS_SetRegAND(SISCR, 0x32, ~0x20); |
| if(ivideo->chip >= SIS_340) { |
| SiS_SetReg(SISCR, 0x57, 0x4a); |
| } else { |
| SiS_SetReg(SISCR, 0x57, 0x5f); |
| } |
| SiS_SetRegOR(SISCR, 0x53, 0x02); |
| while ((SiS_GetRegByte(SISINPSTAT)) & 0x01) break; |
| while (!((SiS_GetRegByte(SISINPSTAT)) & 0x01)) break; |
| if ((SiS_GetRegByte(SISMISCW)) & 0x10) temp = 1; |
| SiS_SetRegAND(SISCR, 0x53, 0xfd); |
| SiS_SetRegAND(SISCR, 0x57, 0x00); |
| } |
| #endif |
| |
| if(temp == 0xffff) { |
| i = 3; |
| do { |
| temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, |
| ivideo->sisvga_engine, 0, 0, NULL, ivideo->vbflags2); |
| } while(((temp == 0) || (temp == 0xffff)) && i--); |
| |
| if((temp == 0) || (temp == 0xffff)) { |
| if(sisfb_test_DDC1(ivideo)) temp = 1; |
| } |
| } |
| |
| if((temp) && (temp != 0xffff)) { |
| SiS_SetRegOR(SISCR, 0x32, 0x20); |
| } |
| |
| #ifdef CONFIG_FB_SIS_315 |
| if(ivideo->sisvga_engine == SIS_315_VGA) { |
| SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF, cr63); |
| } |
| #endif |
| |
| SiS_SetRegANDOR(SISCR, 0x17, 0x7F, cr17); |
| |
| SiS_SetReg(SISSR, 0x1F, sr1F); |
| } |
| |
| /* Determine and detect attached devices on SiS30x */ |
| static void __devinit |
| SiS_SenseLCD(struct sis_video_info *ivideo) |
| { |
| unsigned char buffer[256]; |
| unsigned short temp, realcrtno, i; |
| u8 reg, cr37 = 0, paneltype = 0; |
| u16 xres, yres; |
| |
| ivideo->SiS_Pr.PanelSelfDetected = false; |
| |
| /* LCD detection only for TMDS bridges */ |
| if(!(ivideo->vbflags2 & VB2_SISTMDSBRIDGE)) |
| return; |
| if(ivideo->vbflags2 & VB2_30xBDH) |
| return; |
| |
| /* If LCD already set up by BIOS, skip it */ |
| reg = SiS_GetReg(SISCR, 0x32); |
| if(reg & 0x08) |
| return; |
| |
| realcrtno = 1; |
| if(ivideo->SiS_Pr.DDCPortMixup) |
| realcrtno = 0; |
| |
| /* Check DDC capabilities */ |
| temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, ivideo->sisvga_engine, |
| realcrtno, 0, &buffer[0], ivideo->vbflags2); |
| |
| if((!temp) || (temp == 0xffff) || (!(temp & 0x02))) |
| return; |
| |
| /* Read DDC data */ |
| i = 3; /* Number of retrys */ |
| do { |
| temp = SiS_HandleDDC(&ivideo->SiS_Pr, ivideo->vbflags, |
| ivideo->sisvga_engine, realcrtno, 1, |
| &buffer[0], ivideo->vbflags2); |
| } while((temp) && i--); |
| |
| if(temp) |
| return; |
| |
| /* No digital device */ |
| if(!(buffer[0x14] & 0x80)) |
| return; |
| |
| /* First detailed timing preferred timing? */ |
| if(!(buffer[0x18] & 0x02)) |
| return; |
| |
| xres = buffer[0x38] | ((buffer[0x3a] & 0xf0) << 4); |
| yres = buffer[0x3b] | ((buffer[0x3d] & 0xf0) << 4); |
| |
| switch(xres) { |
| case 1024: |
| if(yres == 768) |
| paneltype = 0x02; |
| break; |
| case 1280: |
| if(yres == 1024) |
| paneltype = 0x03; |
| break; |
| case 1600: |
| if((yres == 1200) && (ivideo->vbflags2 & VB2_30xC)) |
| paneltype = 0x0b; |
| break; |
| } |
| |
| if(!paneltype) |
| return; |
| |
| if(buffer[0x23]) |
| cr37 |= 0x10; |
| |
| if((buffer[0x47] & 0x18) == 0x18) |
| cr37 |= ((((buffer[0x47] & 0x06) ^ 0x06) << 5) | 0x20); |
| else |
| cr37 |= 0xc0; |
| |
| SiS_SetReg(SISCR, 0x36, paneltype); |
| cr37 &= 0xf1; |
| SiS_SetRegANDOR(SISCR, 0x37, 0x0c, cr37); |
| SiS_SetRegOR(SISCR, 0x32, 0x08); |
| |
| ivideo->SiS_Pr.PanelSelfDetected = true; |
| } |
| |
| static int __devinit |
| SISDoSense(struct sis_video_info *ivideo, u16 type, u16 test) |
| { |
| int temp, mytest, result, i, j; |
| |
| for(j = 0; j < 10; j++) { |
| result = 0; |
| for(i = 0; i < 3; i++) { |
| mytest = test; |
| SiS_SetReg(SISPART4, 0x11, (type & 0x00ff)); |
| temp = (type >> 8) | (mytest & 0x00ff); |
| SiS_SetRegANDOR(SISPART4, 0x10, 0xe0, temp); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1500); |
| mytest >>= 8; |
| mytest &= 0x7f; |
| temp = SiS_GetReg(SISPART4, 0x03); |
| temp ^= 0x0e; |
| temp &= mytest; |
| if(temp == mytest) result++; |
| #if 1 |
| SiS_SetReg(SISPART4, 0x11, 0x00); |
| SiS_SetRegAND(SISPART4, 0x10, 0xe0); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1000); |
| #endif |
| } |
| if((result == 0) || (result >= 2)) break; |
| } |
| return result; |
| } |
| |
| static void __devinit |
| SiS_Sense30x(struct sis_video_info *ivideo) |
| { |
| u8 backupP4_0d,backupP2_00,backupP2_4d,backupSR_1e,biosflag=0; |
| u16 svhs=0, svhs_c=0; |
| u16 cvbs=0, cvbs_c=0; |
| u16 vga2=0, vga2_c=0; |
| int myflag, result; |
| char stdstr[] = "sisfb: Detected"; |
| char tvstr[] = "TV connected to"; |
| |
| if(ivideo->vbflags2 & VB2_301) { |
| svhs = 0x00b9; cvbs = 0x00b3; vga2 = 0x00d1; |
| myflag = SiS_GetReg(SISPART4, 0x01); |
| if(myflag & 0x04) { |
| svhs = 0x00dd; cvbs = 0x00ee; vga2 = 0x00fd; |
| } |
| } else if(ivideo->vbflags2 & (VB2_301B | VB2_302B)) { |
| svhs = 0x016b; cvbs = 0x0174; vga2 = 0x0190; |
| } else if(ivideo->vbflags2 & (VB2_301LV | VB2_302LV)) { |
| svhs = 0x0200; cvbs = 0x0100; |
| } else if(ivideo->vbflags2 & (VB2_301C | VB2_302ELV | VB2_307T | VB2_307LV)) { |
| svhs = 0x016b; cvbs = 0x0110; vga2 = 0x0190; |
| } else |
| return; |
| |
| vga2_c = 0x0e08; svhs_c = 0x0404; cvbs_c = 0x0804; |
| if(ivideo->vbflags & (VB2_301LV|VB2_302LV|VB2_302ELV|VB2_307LV)) { |
| svhs_c = 0x0408; cvbs_c = 0x0808; |
| } |
| |
| biosflag = 2; |
| if(ivideo->haveXGIROM) { |
| biosflag = ivideo->bios_abase[0x58] & 0x03; |
| } else if(ivideo->newrom) { |
| if(ivideo->bios_abase[0x5d] & 0x04) biosflag |= 0x01; |
| } else if(ivideo->sisvga_engine == SIS_300_VGA) { |
| if(ivideo->bios_abase) { |
| biosflag = ivideo->bios_abase[0xfe] & 0x03; |
| } |
| } |
| |
| if(ivideo->chip == SIS_300) { |
| myflag = SiS_GetReg(SISSR, 0x3b); |
| if(!(myflag & 0x01)) vga2 = vga2_c = 0; |
| } |
| |
| if(!(ivideo->vbflags2 & VB2_SISVGA2BRIDGE)) { |
| vga2 = vga2_c = 0; |
| } |
| |
| backupSR_1e = SiS_GetReg(SISSR, 0x1e); |
| SiS_SetRegOR(SISSR, 0x1e, 0x20); |
| |
| backupP4_0d = SiS_GetReg(SISPART4, 0x0d); |
| if(ivideo->vbflags2 & VB2_30xC) { |
| SiS_SetRegANDOR(SISPART4, 0x0d, ~0x07, 0x01); |
| } else { |
| SiS_SetRegOR(SISPART4, 0x0d, 0x04); |
| } |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000); |
| |
| backupP2_00 = SiS_GetReg(SISPART2, 0x00); |
| SiS_SetReg(SISPART2, 0x00, ((backupP2_00 | 0x1c) & 0xfc)); |
| |
| backupP2_4d = SiS_GetReg(SISPART2, 0x4d); |
| if(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE) { |
| SiS_SetReg(SISPART2, 0x4d, (backupP2_4d & ~0x10)); |
| } |
| |
| if(!(ivideo->vbflags2 & VB2_30xCLV)) { |
| SISDoSense(ivideo, 0, 0); |
| } |
| |
| SiS_SetRegAND(SISCR, 0x32, ~0x14); |
| |
| if(vga2_c || vga2) { |
| if(SISDoSense(ivideo, vga2, vga2_c)) { |
| if(biosflag & 0x01) { |
| printk(KERN_INFO "%s %s SCART output\n", stdstr, tvstr); |
| SiS_SetRegOR(SISCR, 0x32, 0x04); |
| } else { |
| printk(KERN_INFO "%s secondary VGA connection\n", stdstr); |
| SiS_SetRegOR(SISCR, 0x32, 0x10); |
| } |
| } |
| } |
| |
| SiS_SetRegAND(SISCR, 0x32, 0x3f); |
| |
| if(ivideo->vbflags2 & VB2_30xCLV) { |
| SiS_SetRegOR(SISPART4, 0x0d, 0x04); |
| } |
| |
| if((ivideo->sisvga_engine == SIS_315_VGA) && (ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) { |
| SiS_SetReg(SISPART2, 0x4d, (backupP2_4d | 0x10)); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000); |
| if((result = SISDoSense(ivideo, svhs, 0x0604))) { |
| if((result = SISDoSense(ivideo, cvbs, 0x0804))) { |
| printk(KERN_INFO "%s %s YPbPr component output\n", stdstr, tvstr); |
| SiS_SetRegOR(SISCR, 0x32, 0x80); |
| } |
| } |
| SiS_SetReg(SISPART2, 0x4d, backupP2_4d); |
| } |
| |
| SiS_SetRegAND(SISCR, 0x32, ~0x03); |
| |
| if(!(ivideo->vbflags & TV_YPBPR)) { |
| if((result = SISDoSense(ivideo, svhs, svhs_c))) { |
| printk(KERN_INFO "%s %s SVIDEO output\n", stdstr, tvstr); |
| SiS_SetRegOR(SISCR, 0x32, 0x02); |
| } |
| if((biosflag & 0x02) || (!result)) { |
| if(SISDoSense(ivideo, cvbs, cvbs_c)) { |
| printk(KERN_INFO "%s %s COMPOSITE output\n", stdstr, tvstr); |
| SiS_SetRegOR(SISCR, 0x32, 0x01); |
| } |
| } |
| } |
| |
| SISDoSense(ivideo, 0, 0); |
| |
| SiS_SetReg(SISPART2, 0x00, backupP2_00); |
| SiS_SetReg(SISPART4, 0x0d, backupP4_0d); |
| SiS_SetReg(SISSR, 0x1e, backupSR_1e); |
| |
| if(ivideo->vbflags2 & VB2_30xCLV) { |
| biosflag = SiS_GetReg(SISPART2, 0x00); |
| if(biosflag & 0x20) { |
| for(myflag = 2; myflag > 0; myflag--) { |
| biosflag ^= 0x20; |
| SiS_SetReg(SISPART2, 0x00, biosflag); |
| } |
| } |
| } |
| |
| SiS_SetReg(SISPART2, 0x00, backupP2_00); |
| } |
| |
| /* Determine and detect attached TV's on Chrontel */ |
| static void __devinit |
| SiS_SenseCh(struct sis_video_info *ivideo) |
| { |
| #if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315) |
| u8 temp1, temp2; |
| char stdstr[] = "sisfb: Chrontel: Detected TV connected to"; |
| #endif |
| #ifdef CONFIG_FB_SIS_300 |
| unsigned char test[3]; |
| int i; |
| #endif |
| |
| if(ivideo->chip < SIS_315H) { |
| |
| #ifdef CONFIG_FB_SIS_300 |
| ivideo->SiS_Pr.SiS_IF_DEF_CH70xx = 1; /* Chrontel 700x */ |
| SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x9c); /* Set general purpose IO for Chrontel communication */ |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 1000); |
| temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x25); |
| /* See Chrontel TB31 for explanation */ |
| temp2 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0e); |
| if(((temp2 & 0x07) == 0x01) || (temp2 & 0x04)) { |
| SiS_SetCH700x(&ivideo->SiS_Pr, 0x0e, 0x0b); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 300); |
| } |
| temp2 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x25); |
| if(temp2 != temp1) temp1 = temp2; |
| |
| if((temp1 >= 0x22) && (temp1 <= 0x50)) { |
| /* Read power status */ |
| temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0e); |
| if((temp1 & 0x03) != 0x03) { |
| /* Power all outputs */ |
| SiS_SetCH700x(&ivideo->SiS_Pr, 0x0e,0x0b); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 300); |
| } |
| /* Sense connected TV devices */ |
| for(i = 0; i < 3; i++) { |
| SiS_SetCH700x(&ivideo->SiS_Pr, 0x10, 0x01); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96); |
| SiS_SetCH700x(&ivideo->SiS_Pr, 0x10, 0x00); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96); |
| temp1 = SiS_GetCH700x(&ivideo->SiS_Pr, 0x10); |
| if(!(temp1 & 0x08)) test[i] = 0x02; |
| else if(!(temp1 & 0x02)) test[i] = 0x01; |
| else test[i] = 0; |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96); |
| } |
| |
| if(test[0] == test[1]) temp1 = test[0]; |
| else if(test[0] == test[2]) temp1 = test[0]; |
| else if(test[1] == test[2]) temp1 = test[1]; |
| else { |
| printk(KERN_INFO |
| "sisfb: TV detection unreliable - test results varied\n"); |
| temp1 = test[2]; |
| } |
| if(temp1 == 0x02) { |
| printk(KERN_INFO "%s SVIDEO output\n", stdstr); |
| ivideo->vbflags |= TV_SVIDEO; |
| SiS_SetRegOR(SISCR, 0x32, 0x02); |
| SiS_SetRegAND(SISCR, 0x32, ~0x05); |
| } else if (temp1 == 0x01) { |
| printk(KERN_INFO "%s CVBS output\n", stdstr); |
| ivideo->vbflags |= TV_AVIDEO; |
| SiS_SetRegOR(SISCR, 0x32, 0x01); |
| SiS_SetRegAND(SISCR, 0x32, ~0x06); |
| } else { |
| SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8); |
| SiS_SetRegAND(SISCR, 0x32, ~0x07); |
| } |
| } else if(temp1 == 0) { |
| SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8); |
| SiS_SetRegAND(SISCR, 0x32, ~0x07); |
| } |
| /* Set general purpose IO for Chrontel communication */ |
| SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x00); |
| #endif |
| |
| } else { |
| |
| #ifdef CONFIG_FB_SIS_315 |
| ivideo->SiS_Pr.SiS_IF_DEF_CH70xx = 2; /* Chrontel 7019 */ |
| temp1 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x49); |
| SiS_SetCH701x(&ivideo->SiS_Pr, 0x49, 0x20); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96); |
| temp2 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x20); |
| temp2 |= 0x01; |
| SiS_SetCH701x(&ivideo->SiS_Pr, 0x20, temp2); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96); |
| temp2 ^= 0x01; |
| SiS_SetCH701x(&ivideo->SiS_Pr, 0x20, temp2); |
| SiS_DDC2Delay(&ivideo->SiS_Pr, 0x96); |
| temp2 = SiS_GetCH701x(&ivideo->SiS_Pr, 0x20); |
| SiS_SetCH701x(&ivideo->SiS_Pr, 0x49, temp1); |
| temp1 = 0; |
| if(temp2 & 0x02) temp1 |= 0x01; |
| if(temp2 & 0x10) temp1 |= 0x01; |
| if(temp2 & 0x04) temp1 |= 0x02; |
| if( (temp1 & 0x01) && (temp1 & 0x02) ) temp1 = 0x04; |
| switch(temp1) { |
| case 0x01: |
| printk(KERN_INFO "%s CVBS output\n", stdstr); |
| ivideo->vbflags |= TV_AVIDEO; |
| SiS_SetRegOR(SISCR, 0x32, 0x01); |
| SiS_SetRegAND(SISCR, 0x32, ~0x06); |
| break; |
| case 0x02: |
| printk(KERN_INFO "%s SVIDEO output\n", stdstr); |
| ivideo->vbflags |= TV_SVIDEO; |
| SiS_SetRegOR(SISCR, 0x32, 0x02); |
| SiS_SetRegAND(SISCR, 0x32, ~0x05); |
| break; |
| case 0x04: |
| printk(KERN_INFO "%s SCART output\n", stdstr); |
| SiS_SetRegOR(SISCR, 0x32, 0x04); |
| SiS_SetRegAND(SISCR, 0x32, ~0x03); |
| break; |
| default: |
| SiS_SetRegAND(SISCR, 0x32, ~0x07); |
| } |
| #endif |
| } |
| } |
| |
| static void __devinit |
| sisfb_get_VB_type(struct sis_video_info *ivideo) |
| { |
| char stdstr[] = "sisfb: Detected"; |
| char bridgestr[] = "video bridge"; |
| u8 vb_chipid; |
| u8 reg; |
| |
| /* No CRT2 on XGI Z7 */ |
| if(ivideo->chip == XGI_20) |
| return; |
| |
| vb_chipid = SiS_GetReg(SISPART4, 0x00); |
| switch(vb_chipid) { |
| case 0x01: |
| reg = SiS_GetReg(SISPART4, 0x01); |
| if(reg < 0xb0) { |
| ivideo->vbflags |= VB_301; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_301; |
| printk(KERN_INFO "%s SiS301 %s\n", stdstr, bridgestr); |
| } else if(reg < 0xc0) { |
| ivideo->vbflags |= VB_301B; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_301B; |
| reg = SiS_GetReg(SISPART4, 0x23); |
| if(!(reg & 0x02)) { |
| ivideo->vbflags |= VB_30xBDH; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_30xBDH; |
| printk(KERN_INFO "%s SiS301B-DH %s\n", stdstr, bridgestr); |
| } else { |
| printk(KERN_INFO "%s SiS301B %s\n", stdstr, bridgestr); |
| } |
| } else if(reg < 0xd0) { |
| ivideo->vbflags |= VB_301C; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_301C; |
| printk(KERN_INFO "%s SiS301C %s\n", stdstr, bridgestr); |
| } else if(reg < 0xe0) { |
| ivideo->vbflags |= VB_301LV; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_301LV; |
| printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr); |
| } else if(reg <= 0xe1) { |
| reg = SiS_GetReg(SISPART4, 0x39); |
| if(reg == 0xff) { |
| ivideo->vbflags |= VB_302LV; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_302LV; |
| printk(KERN_INFO "%s SiS302LV %s\n", stdstr, bridgestr); |
| } else { |
| ivideo->vbflags |= VB_301C; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_301C; |
| printk(KERN_INFO "%s SiS301C(P4) %s\n", stdstr, bridgestr); |
| #if 0 |
| ivideo->vbflags |= VB_302ELV; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_302ELV; |
| printk(KERN_INFO "%s SiS302ELV %s\n", stdstr, bridgestr); |
| #endif |
| } |
| } |
| break; |
| case 0x02: |
| ivideo->vbflags |= VB_302B; /* Deprecated */ |
| ivideo->vbflags2 |= VB2_302B; |
| printk(KERN_INFO "%s SiS302B %s\n", stdstr, bridgestr); |
| break; |
| } |
| |
| if((!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) && (ivideo->chip != SIS_300)) { |
| reg = SiS_GetReg(SISCR, 0x37); |
| reg &= SIS_EXTERNAL_CHIP_MASK; |
| reg >>= 1; |
| if(ivideo->sisvga_engine == SIS_300_VGA) { |
| #ifdef CONFIG_FB_SIS_300 |
|