/* * 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 * * 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MTRR #include #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 =