/* * X.org/XFree86 specific extensions to init.c/init301.c * * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1) Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3) The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Author: Thomas Winischhofer * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "initextx.h" static void SiS_MakeClockRegs(ScrnInfoPtr pScrn, int clock, unsigned char *p2b, unsigned char *p2c) { int out_n, out_dn, out_div, out_sbit, out_scale; unsigned int vclk[5]; #define Midx 0 #define Nidx 1 #define VLDidx 2 #define Pidx 3 #define PSNidx 4 if(SiS_compute_vclk(clock, &out_n, &out_dn, &out_div, &out_sbit, &out_scale)) { (*p2b) = (out_div == 2) ? 0x80 : 0x00; (*p2b) |= ((out_n - 1) & 0x7f); (*p2c) = (out_dn - 1) & 0x1f; (*p2c) |= (((out_scale - 1) & 3) << 5); (*p2c) |= ((out_sbit & 0x01) << 7); #ifdef TWDEBUG xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Clock %d: n %d dn %d div %d sb %d sc %d\n", clock, out_n, out_dn, out_div, out_sbit, out_scale); #endif } else { SiSCalcClock(pScrn, clock, 2, vclk); (*p2b) = (vclk[VLDidx] == 2) ? 0x80 : 0x00; (*p2b) |= (vclk[Midx] - 1) & 0x7f; (*p2c) = (vclk[Nidx] - 1) & 0x1f; if(vclk[Pidx] <= 4) { /* postscale 1,2,3,4 */ (*p2c) |= ((vclk[Pidx] - 1) & 3) << 5; } else { /* postscale 6,8 */ (*p2c) |= (((vclk[Pidx] / 2) - 1) & 3) << 5; (*p2c) |= 0x80; } #ifdef TWDEBUG xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Clock %d: n %d dn %d div %d sc %d\n", clock, vclk[Midx], vclk[Nidx], vclk[VLDidx], vclk[Pidx]); #endif } } unsigned short SiS_CheckBuildCustomMode(ScrnInfoPtr pScrn, DisplayModePtr mode, unsigned int VBFlags) { SISPtr pSiS = SISPTR(pScrn); int depth = pSiS->CurrentLayout.bitsPerPixel; pSiS->SiS_Pr->CModeFlag = 0; pSiS->SiS_Pr->CDClock = mode->Clock; pSiS->SiS_Pr->CHDisplay = mode->HDisplay; pSiS->SiS_Pr->CHSyncStart = mode->HSyncStart; pSiS->SiS_Pr->CHSyncEnd = mode->HSyncEnd; pSiS->SiS_Pr->CHTotal = mode->HTotal; pSiS->SiS_Pr->CVDisplay = mode->VDisplay; pSiS->SiS_Pr->CVSyncStart = mode->VSyncStart; pSiS->SiS_Pr->CVSyncEnd = mode->VSyncEnd; pSiS->SiS_Pr->CVTotal = mode->VTotal; pSiS->SiS_Pr->CFlags = mode->Flags; if(pSiS->SiS_Pr->CFlags & V_INTERLACE) { pSiS->SiS_Pr->CVDisplay >>= 1; pSiS->SiS_Pr->CVSyncStart >>= 1; pSiS->SiS_Pr->CVSyncEnd >>= 1; pSiS->SiS_Pr->CVTotal >>= 1; } else if(pSiS->SiS_Pr->CFlags & V_DBLSCAN) { pSiS->SiS_Pr->CVDisplay <<= 1; pSiS->SiS_Pr->CVSyncStart <<= 1; pSiS->SiS_Pr->CVSyncEnd <<= 1; pSiS->SiS_Pr->CVTotal <<= 1; } pSiS->SiS_Pr->CHBlankStart = pSiS->SiS_Pr->CHDisplay; pSiS->SiS_Pr->CHBlankEnd = pSiS->SiS_Pr->CHTotal; pSiS->SiS_Pr->CVBlankStart = pSiS->SiS_Pr->CVSyncStart - 1; pSiS->SiS_Pr->CVBlankEnd = pSiS->SiS_Pr->CVTotal; if((!(mode->type & M_T_BUILTIN)) && (mode->HDisplay <= 512)) { pSiS->SiS_Pr->CModeFlag |= HalfDCLK; pSiS->SiS_Pr->CDClock <<= 1; } /* Note: For CRT2, HDisplay, HSync* and HTotal must be shifted left * in HalfDCLK mode. */ SiS_MakeClockRegs(pScrn, pSiS->SiS_Pr->CDClock, &pSiS->SiS_Pr->CSR2B, &pSiS->SiS_Pr->CSR2C); pSiS->SiS_Pr->CSRClock = (pSiS->SiS_Pr->CDClock / 1000) + 1; SiS_CalcCRRegisters(pSiS->SiS_Pr, depth); switch(depth) { case 8: pSiS->SiS_Pr->CModeFlag |= 0x223b; break; case 16: pSiS->SiS_Pr->CModeFlag |= 0x227d; break; case 32: pSiS->SiS_Pr->CModeFlag |= 0x22ff; break; default: return 0; } if(pSiS->SiS_Pr->CFlags & V_DBLSCAN) pSiS->SiS_Pr->CModeFlag |= DoubleScanMode; if((pSiS->SiS_Pr->CVDisplay >= 1024) || (pSiS->SiS_Pr->CVTotal >= 1024) || (pSiS->SiS_Pr->CHDisplay >= 1024)) pSiS->SiS_Pr->CModeFlag |= LineCompareOff; pSiS->SiS_Pr->CInfoFlag = 0x0007; if(pSiS->SiS_Pr->CFlags & V_NHSYNC) pSiS->SiS_Pr->CInfoFlag |= 0x4000; if(pSiS->SiS_Pr->CFlags & V_NVSYNC) pSiS->SiS_Pr->CInfoFlag |= 0x8000; if(pSiS->SiS_Pr->CFlags & V_INTERLACE) pSiS->SiS_Pr->CInfoFlag |= InterlaceMode; pSiS->SiS_Pr->UseCustomMode = TRUE; #ifdef TWDEBUG xf86DrvMsg(0, X_INFO, "Custom mode %dx%d:\n", pSiS->SiS_Pr->CHDisplay,pSiS->SiS_Pr->CVDisplay); xf86DrvMsg(0, X_INFO, "Modeflag %04x, Infoflag %04x\n", pSiS->SiS_Pr->CModeFlag, pSiS->SiS_Pr->CInfoFlag); xf86DrvMsg(0, X_INFO, " {{0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,\n", pSiS->SiS_Pr->CCRT1CRTC[0], pSiS->SiS_Pr->CCRT1CRTC[1], pSiS->SiS_Pr->CCRT1CRTC[2], pSiS->SiS_Pr->CCRT1CRTC[3], pSiS->SiS_Pr->CCRT1CRTC[4], pSiS->SiS_Pr->CCRT1CRTC[5], pSiS->SiS_Pr->CCRT1CRTC[6], pSiS->SiS_Pr->CCRT1CRTC[7]); xf86DrvMsg(0, X_INFO, " 0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,\n", pSiS->SiS_Pr->CCRT1CRTC[8], pSiS->SiS_Pr->CCRT1CRTC[9], pSiS->SiS_Pr->CCRT1CRTC[10], pSiS->SiS_Pr->CCRT1CRTC[11], pSiS->SiS_Pr->CCRT1CRTC[12], pSiS->SiS_Pr->CCRT1CRTC[13], pSiS->SiS_Pr->CCRT1CRTC[14], pSiS->SiS_Pr->CCRT1CRTC[15]); xf86DrvMsg(0, X_INFO, " 0x%02x}},\n", pSiS->SiS_Pr->CCRT1CRTC[16]); xf86DrvMsg(0, X_INFO, "Clock: 0x%02x, 0x%02x, %d\n", pSiS->SiS_Pr->CSR2B, pSiS->SiS_Pr->CSR2C, pSiS->SiS_Pr->CSRClock); #endif return 1; } /* Build a list of supported modes: * Built-in modes for which we have all data are M_T_DEFAULT, * modes derived from DDC or database data are M_T_BUILTIN */ DisplayModePtr SiSBuildBuiltInModeList(ScrnInfoPtr pScrn, BOOLEAN includelcdmodes, BOOLEAN isfordvi, BOOLEAN fakecrt2modes, BOOLEAN IsForCRT2) { SISPtr pSiS = SISPTR(pScrn); unsigned char sr2b, sr2c; float num, denum, postscalar, divider; int i, j, k, l, index, vclkindex, UseWide; DisplayModePtr new = NULL, current = NULL, first = NULL; BOOLEAN done = FALSE, IsHDCLK; #if 0 DisplayModePtr backup = NULL; #endif pSiS->backupmodelist = NULL; pSiS->AddedPlasmaModes = FALSE; UseWide = pSiS->SiS_Pr->SiS_UseWide; if(IsForCRT2) UseWide = pSiS->SiS_Pr->SiS_UseWideCRT2; if(!SiSInitPtr(pSiS->SiS_Pr)) return NULL; i = 0; while(pSiS->SiS_Pr->SiS_RefIndex[i].Ext_InfoFlag != 0xFFFF) { if(pSiS->SiS_Pr->SiS_RefIndex[i].Ext_InfoFlag & HaveWideTiming) { if(UseWide == 1) { if((pSiS->SiS_Pr->SiS_RefIndex[i].Ext_CRT1CRTC_WIDE == 0xff) && (pSiS->SiS_Pr->SiS_RefIndex[i].Ext_CRTVCLK_WIDE == 0xff)) { i++; continue; } } else { if((pSiS->SiS_Pr->SiS_RefIndex[i].Ext_CRT1CRTC_NORM == 0xff) && (pSiS->SiS_Pr->SiS_RefIndex[i].Ext_CRTVCLK_NORM == 0xff)) { i++; continue; } } } index = SiS_GetRefCRT1CRTC(pSiS->SiS_Pr, i, UseWide); if(fakecrt2modes) { if(pSiS->SiS_Pr->SiS_RefIndex[i].Ext_FakeCRT2CRTC) { index = pSiS->SiS_Pr->SiS_RefIndex[i].Ext_FakeCRT2CRTC; } } /* 0x5a (320x240) for FTSN - skip, is bad for CRT1 */ if(pSiS->SiS_Pr->SiS_RefIndex[i].ModeID == 0x5a) { i++; continue; } if(!(new = malloc(sizeof(DisplayModeRec)))) return first; memset(new, 0, sizeof(DisplayModeRec)); if(!(new->name = malloc(10))) { free(new); return first; } if(!first) first = new; if(current) { current->next = new; new->prev = current; } current = new; sprintf(current->name, "%dx%d", pSiS->SiS_Pr->SiS_RefIndex[i].XRes, pSiS->SiS_Pr->SiS_RefIndex[i].YRes); current->status = MODE_OK; current->type = M_T_DEFAULT; vclkindex = SiS_GetRefCRTVCLK(pSiS->SiS_Pr, i, UseWide); if(fakecrt2modes) { if(pSiS->SiS_Pr->SiS_RefIndex[i].Ext_FakeCRT2Clk) { vclkindex = pSiS->SiS_Pr->SiS_RefIndex[i].Ext_FakeCRT2Clk; } } sr2b = pSiS->SiS_Pr->SiS_VCLKData[vclkindex].SR2B; sr2c = pSiS->SiS_Pr->SiS_VCLKData[vclkindex].SR2C; divider = (sr2b & 0x80) ? 2.0 : 1.0; postscalar = (sr2c & 0x80) ? ( (((sr2c >> 5) & 0x03) == 0x02) ? 6.0 : 8.0) : (((sr2c >> 5) & 0x03) + 1.0); num = (sr2b & 0x7f) + 1.0; denum = (sr2c & 0x1f) + 1.0; #ifdef TWDEBUG xf86DrvMsg(0, X_INFO, "------------\n"); xf86DrvMsg(0, X_INFO, "sr2b: %x sr2c %x div %f ps %f num %f denum %f\n", sr2b, sr2c, divider, postscalar, num, denum); #endif current->Clock = (int)(14318 * (divider / postscalar) * (num / denum)); SiS_Generic_ConvertCRData(pSiS->SiS_Pr, (unsigned char *)&pSiS->SiS_Pr->SiS_CRT1Table[index].CR[0], pSiS->SiS_Pr->SiS_RefIndex[i].XRes, pSiS->SiS_Pr->SiS_RefIndex[i].YRes, current); if(pSiS->SiS_Pr->SiS_RefIndex[i].Ext_InfoFlag & 0x4000) current->Flags |= V_NHSYNC; else current->Flags |= V_PHSYNC; if(pSiS->SiS_Pr->SiS_RefIndex[i].Ext_InfoFlag & 0x8000) current->Flags |= V_NVSYNC; else current->Flags |= V_PVSYNC; if(pSiS->SiS_Pr->SiS_RefIndex[i].Ext_InfoFlag & 0x0080) current->Flags |= V_INTERLACE; j = 0; IsHDCLK = FALSE; while(pSiS->SiS_Pr->SiS_EModeIDTable[j].Ext_ModeID != 0xff) { if(pSiS->SiS_Pr->SiS_EModeIDTable[j].Ext_ModeID == pSiS->SiS_Pr->SiS_RefIndex[i].ModeID) { if(pSiS->SiS_Pr->SiS_EModeIDTable[j].Ext_ModeFlag & DoubleScanMode) { current->Flags |= V_DBLSCAN; } if(pSiS->SiS_Pr->SiS_EModeIDTable[j].Ext_ModeFlag & HalfDCLK) { IsHDCLK = TRUE; } break; } j++; } if(current->Flags & V_INTERLACE) { current->VDisplay <<= 1; current->VSyncStart <<= 1; current->VSyncEnd <<= 1; current->VTotal <<= 1; current->VTotal |= 1; } if(IsHDCLK) { current->Clock >>= 1; } if(current->Flags & V_DBLSCAN) { current->VDisplay >>= 1; current->VSyncStart >>= 1; current->VSyncEnd >>= 1; current->VTotal >>= 1; } #ifdef TWDEBUG xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Built-in: %s %.2f %d %d %d %d %d %d %d %d\n", current->name, (float)current->Clock / 1000, current->HDisplay, current->HSyncStart, current->HSyncEnd, current->HTotal, current->VDisplay, current->VSyncStart, current->VSyncEnd, current->VTotal); #endif i++; } /* Add non-standard LCD modes for panel's detailed timings */ if(!includelcdmodes) return first; if(pSiS->SiS_Pr->CP_Vendor) { xf86DrvMsg(0, X_INFO, "Checking database for vendor %x, product %x\n", pSiS->SiS_Pr->CP_Vendor, pSiS->SiS_Pr->CP_Product); } i = 0; while((!done) && (SiS_PlasmaTable[i].vendor) && (pSiS->SiS_Pr->CP_Vendor)) { if(SiS_PlasmaTable[i].vendor == pSiS->SiS_Pr->CP_Vendor) { for(j=0; jSiS_Pr->CP_Product) { xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Identified %s panel, adding specific modes\n", SiS_PlasmaTable[i].plasmaname); for(k=0; kVBFlags2 & VB2_LCDOVER1280BRIDGE)) { if(isfordvi) { if(SiS_PlasmaMode[l].VDisplay > 1024) continue; } } if(!(new = malloc(sizeof(DisplayModeRec)))) return first; memset(new, 0, sizeof(DisplayModeRec)); if(!(new->name = malloc(12))) { free(new); return first; } if(!first) first = new; if(current) { current->next = new; new->prev = current; } current = new; pSiS->AddedPlasmaModes = TRUE; strcpy(current->name, SiS_PlasmaMode[l].name); current->status = MODE_OK; current->type = M_T_BUILTIN; current->Clock = SiS_PlasmaMode[l].clock; current->SynthClock = current->Clock; current->HDisplay = SiS_PlasmaMode[l].HDisplay; current->HSyncStart = current->HDisplay + SiS_PlasmaMode[l].HFrontPorch; current->HSyncEnd = current->HSyncStart + SiS_PlasmaMode[l].HSyncWidth; current->HTotal = SiS_PlasmaMode[l].HTotal; current->VDisplay = SiS_PlasmaMode[l].VDisplay; current->VSyncStart = current->VDisplay + SiS_PlasmaMode[l].VFrontPorch; current->VSyncEnd = current->VSyncStart + SiS_PlasmaMode[l].VSyncWidth; current->VTotal = SiS_PlasmaMode[l].VTotal; current->CrtcHDisplay = current->HDisplay; current->CrtcHBlankStart = current->HSyncStart; current->CrtcHSyncStart = current->HSyncStart; current->CrtcHSyncEnd = current->HSyncEnd; current->CrtcHBlankEnd = current->HSyncEnd; current->CrtcHTotal = current->HTotal; current->CrtcVDisplay = current->VDisplay; current->CrtcVBlankStart = current->VSyncStart; current->CrtcVSyncStart = current->VSyncStart; current->CrtcVSyncEnd = current->VSyncEnd; current->CrtcVBlankEnd = current->VSyncEnd; current->CrtcVTotal = current->VTotal; if(SiS_PlasmaMode[l].SyncFlags & SIS_PL_HSYNCP) current->Flags |= V_PHSYNC; else current->Flags |= V_NHSYNC; if(SiS_PlasmaMode[l].SyncFlags & SIS_PL_VSYNCP) current->Flags |= V_PVSYNC; else current->Flags |= V_NVSYNC; if(current->HDisplay > pSiS->LCDwidth) pSiS->LCDwidth = pSiS->SiS_Pr->CP_MaxX = current->HDisplay; if(current->VDisplay > pSiS->LCDheight) pSiS->LCDheight = pSiS->SiS_Pr->CP_MaxY = current->VDisplay; xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "\tAdding \"%s\" to list of built-in modes\n", current->name); } done = TRUE; break; } } } i++; } if(pSiS->SiS_Pr->CP_HaveCustomData) { for(i=0; i<7; i++) { if(pSiS->SiS_Pr->CP_DataValid[i]) { if(!(new = malloc(sizeof(DisplayModeRec)))) return first; memset(new, 0, sizeof(DisplayModeRec)); if(!(new->name = malloc(10))) { free(new); return first; } if(!first) first = new; if(current) { current->next = new; new->prev = current; } current = new; sprintf(current->name, "%dx%d", pSiS->SiS_Pr->CP_HDisplay[i], pSiS->SiS_Pr->CP_VDisplay[i]); current->status = MODE_OK; current->type = M_T_BUILTIN; current->Clock = pSiS->SiS_Pr->CP_Clock[i]; current->SynthClock = current->Clock; current->HDisplay = pSiS->SiS_Pr->CP_HDisplay[i]; current->HSyncStart = pSiS->SiS_Pr->CP_HSyncStart[i]; current->HSyncEnd = pSiS->SiS_Pr->CP_HSyncEnd[i]; current->HTotal = pSiS->SiS_Pr->CP_HTotal[i]; current->VDisplay = pSiS->SiS_Pr->CP_VDisplay[i]; current->VSyncStart = pSiS->SiS_Pr->CP_VSyncStart[i]; current->VSyncEnd = pSiS->SiS_Pr->CP_VSyncEnd[i]; current->VTotal = pSiS->SiS_Pr->CP_VTotal[i]; current->CrtcHDisplay = current->HDisplay; current->CrtcHBlankStart = pSiS->SiS_Pr->CP_HBlankStart[i]; current->CrtcHSyncStart = current->HSyncStart; current->CrtcHSyncEnd = current->HSyncEnd; current->CrtcHBlankEnd = pSiS->SiS_Pr->CP_HBlankEnd[i]; current->CrtcHTotal = current->HTotal; current->CrtcVDisplay = current->VDisplay; current->CrtcVBlankStart = pSiS->SiS_Pr->CP_VBlankStart[i]; current->CrtcVSyncStart = current->VSyncStart; current->CrtcVSyncEnd = current->VSyncEnd; current->CrtcVBlankEnd = pSiS->SiS_Pr->CP_VBlankEnd[i]; current->CrtcVTotal = current->VTotal; if(pSiS->SiS_Pr->CP_SyncValid[i]) { if(pSiS->SiS_Pr->CP_HSync_P[i]) current->Flags |= V_PHSYNC; else current->Flags |= V_NHSYNC; if(pSiS->SiS_Pr->CP_VSync_P[i]) current->Flags |= V_PVSYNC; else current->Flags |= V_NVSYNC; } else { /* No sync data? Use positive sync... */ current->Flags |= V_PHSYNC; current->Flags |= V_PVSYNC; } } } } return first; } /* Translate a mode number into the VESA pendant */ int SiSTranslateToVESA(ScrnInfoPtr pScrn, int modenumber) { SISPtr pSiS = SISPTR(pScrn); int i = 0; if(!SiSInitPtr(pSiS->SiS_Pr)) return -1; if(modenumber <= 0x13) return modenumber; #ifdef SIS315H if(pSiS->ROM661New) { /* Not XGI! */ while(SiS_EModeIDTable661[i].Ext_ModeID != 0xff) { if(SiS_EModeIDTable661[i].Ext_ModeID == modenumber) { return (int)SiS_EModeIDTable661[i].Ext_VESAID; } i++; } } else { #endif while(pSiS->SiS_Pr->SiS_EModeIDTable[i].Ext_ModeID != 0xff) { if(pSiS->SiS_Pr->SiS_EModeIDTable[i].Ext_ModeID == modenumber) { return (int)pSiS->SiS_Pr->SiS_EModeIDTable[i].Ext_VESAID; } i++; } #ifdef SIS315H } #endif return -1; } /* Translate a new (SiS or XGI) BIOS mode number into the driver's pendant */ int SiSTranslateToOldMode(int modenumber) { #ifdef SIS315H int i = 0; while(SiS_EModeIDTable661[i].Ext_ModeID != 0xff) { if(SiS_EModeIDTable661[i].Ext_ModeID == modenumber) { if(SiS_EModeIDTable661[i].Ext_MyModeID) return (int)SiS_EModeIDTable661[i].Ext_MyModeID; else return modenumber; } i++; } #endif return modenumber; } BOOLEAN SiS_GetPanelID(struct SiS_Private *SiS_Pr) { unsigned short tempax, tempbx, temp; static const unsigned short PanelTypeTable300[16] = { 0xc101, 0xc117, 0x0121, 0xc135, 0xc142, 0xc152, 0xc162, 0xc072, 0xc181, 0xc192, 0xc1a1, 0xc1b6, 0xc1c2, 0xc0d2, 0xc1e2, 0xc1f2 }; static const unsigned short PanelTypeTable31030x[16] = { 0xc102, 0xc112, 0x0122, 0xc132, 0xc142, 0xc152, 0xc169, 0xc179, 0x0189, 0xc192, 0xc1a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; static const unsigned short PanelTypeTable310LVDS[16] = { 0xc111, 0xc122, 0xc133, 0xc144, 0xc155, 0xc166, 0xc177, 0xc188, 0xc199, 0xc0aa, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; if(SiS_Pr->ChipType < SIS_315H) { tempbx = SiS_GetReg(SiS_Pr->SiS_P3c4,0x18); if(!(tempbx & 0x10)) { if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { tempbx = 0; temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x38); if(temp & 0x40) tempbx |= 0x08; if(temp & 0x20) tempbx |= 0x02; if(temp & 0x01) tempbx |= 0x01; temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x39); if(temp & 0x80) tempbx |= 0x04; } else { return FALSE; } } tempbx = PanelTypeTable300[(tempbx & 0x0f)] | LCDSync; SiS_SetReg(SiS_Pr->SiS_P3d4,0x36,tempbx); SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x37,~(LCDSyncBit|LCDRGB18Bit),(tempbx >> 8)); } else { if(SiS_Pr->ChipType >= SIS_661) return FALSE; tempax = (SiS_GetReg(SiS_Pr->SiS_P3c4,0x1a) & 0x1e) >> 1; if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { if(tempax == 0) return FALSE; tempbx = PanelTypeTable310LVDS[tempax - 1]; temp = tempax & 0xff; } else { tempbx = PanelTypeTable31030x[tempax]; temp = tempbx & 0xff; } SiS_SetReg(SiS_Pr->SiS_P3d4,0x36,temp); tempbx >>= 8; SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x37,~(LCDSyncBit|LCDRGB18Bit),(tempbx & 0xc1)); if(SiS_Pr->SiS_VBType & VB_SISVB) { SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x39,0xfb,(tempbx & 0x04)); } } return TRUE; } /* LCD and VGA2 detection */ static BOOLEAN checkedid1(unsigned char *buffer) { /* Check header */ 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)) return FALSE; /* Check EDID version and revision */ if((buffer[0x12] != 1) || (buffer[0x13] > 4)) return FALSE; /* Check week of manufacture for sanity */ if(buffer[0x10] > 54) return FALSE; /* Check year of manufacture for sanity */ if(buffer[0x11] > 40) return FALSE; return TRUE; } static BOOLEAN checkedid2(unsigned char *buffer) { unsigned short year = buffer[6] | (buffer[7] << 8); /* Check EDID version */ if((buffer[0] & 0xf0) != 0x20) return FALSE; /* Check week of manufacture for sanity */ if(buffer[5] > 54) return FALSE; /* Check year of manufacture for sanity */ if((year != 0) && ((year < 1990) || (year > 2030))) return FALSE; return TRUE; } static int SiS_FindPanelFromDB(SISPtr pSiS, unsigned short panelvendor, unsigned short panelproduct, int *maxx, int *maxy, int *prefx, int *prefy) { int i, j; BOOLEAN done = FALSE; i = 0; while((!done) && (SiS_PlasmaTable[i].vendor) && panelvendor) { if(SiS_PlasmaTable[i].vendor == panelvendor) { for(j=0; jpScrn->scrnIndex, X_PROBED, "Identified %s, correcting max X res %d, max Y res %d\n", SiS_PlasmaTable[i].plasmaname, SiS_PlasmaTable[i].maxx, SiS_PlasmaTable[i].maxy); break; } } } } i++; } return (done) ? 1 : 0; } /* Sense the LCD parameters (CR36, CR37) via DDC */ /* SiS TMDS bridges only */ unsigned short SiS_SenseLCDDDC(struct SiS_Private *SiS_Pr, SISPtr pSiS) { unsigned short DDCdatatype, paneltype, adapternum, flag, xres=0, yres=0; unsigned short index, myindex, lumsize, numcodes, panelvendor, panelproduct; int maxx=0, maxy=0, prefx=0, prefy=0; unsigned char cr37=0, seekcode; BOOLEAN checkexpand = FALSE; BOOLEAN havesync = FALSE; BOOLEAN indb = FALSE; int retry, i; int panel1280x960 = (pSiS->VGAEngine == SIS_315_VGA) ? Panel310_1280x960 : Panel300_1280x960; unsigned char buffer[256]; for(i=0; i<7; i++) SiS_Pr->CP_DataValid[i] = FALSE; SiS_Pr->CP_HaveCustomData = FALSE; SiS_Pr->CP_MaxX = SiS_Pr->CP_MaxY = SiS_Pr->CP_MaxClock = 0; SiS_Pr->CP_PreferredX = SiS_Pr->CP_PreferredY = 0; SiS_Pr->CP_PreferredIndex = -1; SiS_Pr->CP_PrefClock = 0; SiS_Pr->PanelSelfDetected = FALSE; if(!(pSiS->VBFlags2 & VB2_SISTMDSBRIDGE)) return 0; if(pSiS->VBFlags2 & VB2_30xBDH) return 0; /* Specific for XGI_40/Rev 2/A01 (XGI V3XT A01): This card has CRT1's * and CRT2's DDC ports physically connected to each other. There * is no connection to the video bridge's DDC port, both DDC * channels are routed to the GPU. Smart. If both CRT1 (CRT) and * CRT2 (VGA or LCD) are connected, DDC will fail. Hence, no * reliable panel detection here... */ adapternum = 1; if(SiS_Pr->DDCPortMixup) adapternum = 0; if(SiS_InitDDCRegs(SiS_Pr, pSiS->VBFlags, pSiS->VGAEngine, adapternum, 0, FALSE, pSiS->VBFlags2) == 0xFFFF) return 0; SiS_Pr->SiS_DDC_SecAddr = 0x00; /* Probe supported DA's */ flag = SiS_ProbeDDC(SiS_Pr); #ifdef TWDEBUG xf86DrvMsg(pSiS->pScrn->scrnIndex, X_INFO, "CRT2 DDC capabilities 0x%x\n", flag); #endif if(flag & 0x10) { SiS_Pr->SiS_DDC_DeviceAddr = 0xa6; /* EDID V2 (FP) */ DDCdatatype = 4; } else if(flag & 0x08) { SiS_Pr->SiS_DDC_DeviceAddr = 0xa2; /* EDID V2 (P&D-D Monitor) */ DDCdatatype = 3; } else if(flag & 0x02) { SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; /* EDID V1 */ DDCdatatype = 1; } else return 0; /* no DDC support (or no device attached) */ /* Read the entire EDID */ retry = 2; do { if(SiS_ReadDDC(SiS_Pr, DDCdatatype, buffer)) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "CRT2: DDC read failed (attempt %d), %s\n", (3-retry), (retry == 1) ? "giving up" : "retrying"); retry--; if(retry == 0) return 0xFFFF; } else break; } while(1); #ifdef TWDEBUG for(i=0; i<256; i+=16) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7], buffer[i+8], buffer[i+9], buffer[i+10], buffer[i+11], buffer[i+12], buffer[i+13], buffer[i+14], buffer[i+15]); } #endif /* Analyze EDID and retrieve LCD panel information */ paneltype = 0; switch(DDCdatatype) { case 1: /* Analyze EDID V1 */ /* Catch a few clear cases: */ if(!(checkedid1(buffer))) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "LCD sense: EDID corrupt\n"); return 0; } if(!(buffer[0x14] & 0x80)) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "LCD sense: Attached display expects analog input (0x%02x)\n", buffer[0x14]); return 0; } /* Save given gamma */ pSiS->CRT2LCDMonitorGamma = (buffer[0x17] + 100) * 10; /* Now analyze the first Detailed Timing Block and see * if the preferred timing mode is stored there. If so, * check if this is a standard panel for which we already * know the timing. */ paneltype = Panel_Custom; checkexpand = FALSE; panelvendor = buffer[9] | (buffer[8] << 8); panelproduct = buffer[10] | (buffer[11] << 8); /* Overrule bogus preferred modes from database */ if((indb = SiS_FindPanelFromDB(pSiS, panelvendor, panelproduct, &maxx, &maxy, &prefx, &prefy))) { if(prefx) SiS_Pr->CP_PreferredX = xres = prefx; if(prefy) SiS_Pr->CP_PreferredY = yres = prefy; } if(buffer[0x18] & 0x02) { unsigned short pclk = (buffer[0x36] | (buffer[0x37] << 8)); unsigned short phb = (buffer[0x39] | ((buffer[0x3a] & 0x0f) << 8)); unsigned short pvb = (buffer[0x3c] | ((buffer[0x3d] & 0x0f) << 8)); if(!xres) SiS_Pr->CP_PreferredX = xres = buffer[0x38] | ((buffer[0x3a] & 0xf0) << 4); if(!yres) SiS_Pr->CP_PreferredY = yres = buffer[0x3b] | ((buffer[0x3d] & 0xf0) << 4); switch(xres) { case 1024: if(yres == 768) { paneltype = Panel_1024x768; checkexpand = TRUE; } break; case 1280: if(yres == 1024) { paneltype = Panel_1280x1024; checkexpand = TRUE; } else if(yres == 960) { paneltype = panel1280x960; } else if(yres == 768) { if( (pclk == 8100) && (phb == (1688 - 1280)) && (pvb == (802 - 768)) ) { paneltype = Panel_1280x768; checkexpand = FALSE; cr37 |= 0x10; } } else if(yres == 800) { if( (pclk == 6900) && (phb == (1408 - 1280)) && (pvb == (816 - 800)) ) { paneltype = Panel_1280x800; } } break; case 1400: if(pSiS->VGAEngine == SIS_315_VGA) { if(yres == 1050) { paneltype = Panel310_1400x1050; checkexpand = TRUE; } } break; case 1600: if((pSiS->VGAEngine == SIS_315_VGA) && (pSiS->VBFlags2 & VB2_30xC)) { if(yres == 1200) { if( (pclk == 16200) && (phb == (2160 - 1600)) && (pvb == (1250 - 1200)) ) { paneltype = Panel310_1600x1200; checkexpand = TRUE; } } } break; } /* Save sync: This is used if "Pass 1:1" is off; in this case * we always use the panel's native mode = this "preferred mode" * we just have been analysing. Hence, we also need its sync. */ if((buffer[0x47] & 0x18) == 0x18) { cr37 |= ((((buffer[0x47] & 0x06) ^ 0x06) << 5) | 0x20); havesync = TRUE; } else { /* What now? There is no digital separate output timing... */ xf86DrvMsg(pSiS->pScrn->scrnIndex, X_WARNING, "LCD sense: Unable to retrieve Sync polarity information\n"); cr37 |= 0xc0; /* Default */ } } /* Check against our database; eg. Sanyo Z2 projector reports * 1024x768 as preferred mode, although it supports 1280x720 * natively in non-HDCP mode. Treat such wrongly reporting * panels as custom and fixup actual maximum resolutions. */ if(paneltype != Panel_Custom) { if(indb) { paneltype = Panel_Custom; SiS_Pr->CP_MaxX = maxx; SiS_Pr->CP_MaxY = maxy; /* Leave preferred unchanged (MUST contain a valid mode!) */ } } /* If we still don't know what panel this is, we take it * as a custom panel and derive the timing data from the * detailed timing blocks */ if(paneltype == Panel_Custom) { int i, temp, base = 0x36; unsigned long estpack; static const unsigned short estx[] = { 720, 720, 640, 640, 640, 640, 800, 800, 800, 800, 832,1024,1024,1024,1024,1280, 1152 }; static const unsigned short esty[] = { 400, 400, 480, 480, 480, 480, 600, 600, 600, 600, 624, 768, 768, 768, 768,1024, 870 }; static const int estclk[] = { 0, 0, 25100, 0, 31500, 31500, 36100, 40000, 50100, 49500, 0, 0, 65100, 75200, 78700,135200, 0 }; paneltype = 0; SiS_Pr->CP_Supports64048075 = TRUE; /* Find the maximum resolution */ /* 1. From Established timings */ estpack = (buffer[0x23] << 9) | (buffer[0x24] << 1) | ((buffer[0x25] >> 7) & 0x01); for(i=16; i>=0; i--) { if(estpack & (1 << i)) { if(estx[16 - i] > SiS_Pr->CP_MaxX) SiS_Pr->CP_MaxX = estx[16 - i]; if(esty[16 - i] > SiS_Pr->CP_MaxY) SiS_Pr->CP_MaxY = esty[16 - i]; if(estclk[16 - i] > SiS_Pr->CP_MaxClock) SiS_Pr->CP_MaxClock = estclk[16 - i]; } } /* By default we drive the LCD at 75Hz in 640x480 mode; if * the panel does not provide this mode, use 60hz */ if(!(buffer[0x23] & 0x04)) SiS_Pr->CP_Supports64048075 = FALSE; /* 2. From Standard Timings */ for(i=0x26; i < 0x36; i+=2) { if((buffer[i] != 0x01) && (buffer[i+1] != 0x01)) { temp = (buffer[i] + 31) * 8; if(temp > SiS_Pr->CP_MaxX) SiS_Pr->CP_MaxX = temp; switch((buffer[i+1] & 0xc0) >> 6) { case 0x03: temp = temp * 9 / 16; break; case 0x02: temp = temp * 4 / 5; break; case 0x01: temp = temp * 3 / 4; break; } if(temp > SiS_Pr->CP_MaxY) SiS_Pr->CP_MaxY = temp; } } /* Now extract the Detailed Timings and convert them into modes */ for(i = 0; i < 4; i++, base += 18) { /* Is this a detailed timing block or a monitor descriptor? */ if(buffer[base] || buffer[base+1] || buffer[base+2]) { xres = buffer[base+2] | ((buffer[base+4] & 0xf0) << 4); yres = buffer[base+5] | ((buffer[base+7] & 0xf0) << 4); SiS_Pr->CP_HDisplay[i] = xres; SiS_Pr->CP_HSyncStart[i] = xres + (buffer[base+8] | ((buffer[base+11] & 0xc0) << 2)); SiS_Pr->CP_HSyncEnd[i] = SiS_Pr->CP_HSyncStart[i] + (buffer[base+9] | ((buffer[base+11] & 0x30) << 4)); SiS_Pr->CP_HTotal[i] = xres + (buffer[base+3] | ((buffer[base+4] & 0x0f) << 8)); SiS_Pr->CP_HBlankStart[i] = xres + 1; SiS_Pr->CP_HBlankEnd[i] = SiS_Pr->CP_HTotal[i]; SiS_Pr->CP_VDisplay[i] = yres; SiS_Pr->CP_VSyncStart[i] = yres + (((buffer[base+10] & 0xf0) >> 4) | ((buffer[base+11] & 0x0c) << 2)); SiS_Pr->CP_VSyncEnd[i] = SiS_Pr->CP_VSyncStart[i] + ((buffer[base+10] & 0x0f) | ((buffer[base+11] & 0x03) << 4)); SiS_Pr->CP_VTotal[i] = yres + (buffer[base+6] | ((buffer[base+7] & 0x0f) << 8)); SiS_Pr->CP_VBlankStart[i] = yres + 1; SiS_Pr->CP_VBlankEnd[i] = SiS_Pr->CP_VTotal[i]; SiS_Pr->CP_Clock[i] = (buffer[base] | (buffer[base+1] << 8)) * 10; SiS_Pr->CP_DataValid[i] = TRUE; /* Sort out invalid timings, interlace and too high clocks */ if((SiS_Pr->CP_HDisplay[i] & 7) || (SiS_Pr->CP_HDisplay[i] > SiS_Pr->CP_HSyncStart[i]) || (SiS_Pr->CP_HDisplay[i] >= SiS_Pr->CP_HSyncEnd[i]) || (SiS_Pr->CP_HDisplay[i] >= SiS_Pr->CP_HTotal[i]) || (SiS_Pr->CP_HSyncStart[i] >= SiS_Pr->CP_HSyncEnd[i]) || (SiS_Pr->CP_HSyncStart[i] > SiS_Pr->CP_HTotal[i]) || (SiS_Pr->CP_HSyncEnd[i] > SiS_Pr->CP_HTotal[i]) || (SiS_Pr->CP_VDisplay[i] > SiS_Pr->CP_VSyncStart[i]) || (SiS_Pr->CP_VDisplay[i] >= SiS_Pr->CP_VSyncEnd[i]) || (SiS_Pr->CP_VDisplay[i] >= SiS_Pr->CP_VTotal[i]) || (SiS_Pr->CP_VSyncStart[i] > SiS_Pr->CP_VSyncEnd[i]) || (SiS_Pr->CP_VSyncStart[i] > SiS_Pr->CP_VTotal[i]) || (SiS_Pr->CP_VSyncEnd[i] > SiS_Pr->CP_VTotal[i]) || (((pSiS->VBFlags2 & VB2_30xC) && (SiS_Pr->CP_Clock[i] > 162500)) || ((!(pSiS->VBFlags2 & VB2_30xC)) && ( (SiS_Pr->CP_Clock[i] > 110500) || /* TODO for 307 */ (SiS_Pr->CP_VDisplay[i] > 1024) || (SiS_Pr->CP_HDisplay[i] > 1600) ))) || (buffer[base+17] & 0x80)) { SiS_Pr->CP_DataValid[i] = FALSE; } else { SiS_Pr->CP_HaveCustomData = TRUE; if(xres > SiS_Pr->CP_MaxX) SiS_Pr->CP_MaxX = xres; if(yres > SiS_Pr->CP_MaxY) SiS_Pr->CP_MaxY = yres; if(SiS_Pr->CP_Clock[i] > SiS_Pr->CP_MaxClock) SiS_Pr->CP_MaxClock = SiS_Pr->CP_Clock[i]; if((SiS_Pr->CP_PreferredX == xres) && (SiS_Pr->CP_PreferredY == yres)) { SiS_Pr->CP_PreferredIndex = i; SiS_MakeClockRegs(pSiS->pScrn, SiS_Pr->CP_Clock[i], &SiS_Pr->CP_PrefSR2B, &SiS_Pr->CP_PrefSR2C); SiS_Pr->CP_PrefClock = (SiS_Pr->CP_Clock[i] / 1000) + 1; } /* Extract the sync polarisation information. This only works * if the Flags indicate a digital separate output. */ if((buffer[base+17] & 0x18) == 0x18) { SiS_Pr->CP_HSync_P[i] = (buffer[base+17] & 0x02) ? TRUE : FALSE; SiS_Pr->CP_VSync_P[i] = (buffer[base+17] & 0x04) ? TRUE : FALSE; SiS_Pr->CP_SyncValid[i] = TRUE; if((i == SiS_Pr->CP_PreferredIndex) && (!havesync)) { cr37 |= ((((buffer[base+17] & 0x06) ^ 0x06) << 5) | 0x20); havesync = TRUE; } } else { SiS_Pr->CP_SyncValid[i] = FALSE; } } } else if((!buffer[base]) && (!buffer[base+1]) && (!buffer[base+2]) && (!buffer[base+4])) { /* Maximum pixclock from Monitor Range Limits */ if((buffer[base+3] == 0xfd) && (buffer[base+9] != 0xff)) { int maxclk = buffer[base+9] * 10; /* More than 170 is not supported anyway */ if(maxclk <= 170) SiS_Pr->CP_MaxClock = maxclk * 1000; } } } if(SiS_Pr->CP_MaxX && SiS_Pr->CP_MaxY) { paneltype = Panel_Custom; checkexpand = FALSE; cr37 |= 0x10; SiS_Pr->CP_Vendor = panelvendor; SiS_Pr->CP_Product = panelproduct; } } if(paneltype && checkexpand) { /* If any of the Established low-res modes is supported, the * panel can scale automatically. For 800x600 panels, we only * check the even lower ones. */ if(paneltype == Panel_800x600) { if(buffer[0x23] & 0xfc) cr37 |= 0x10; } else { if(buffer[0x23]) cr37 |= 0x10; } } break; case 3: /* Analyze EDID V2 */ case 4: index = 0; if(!(checkedid2(buffer))) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "LCD sense: EDID corrupt\n"); return 0; } if((buffer[0x41] & 0x0f) == 0x03) { index = 0x42 + 3; xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "LCD sense: Display supports TMDS input on primary interface\n"); } else if((buffer[0x41] & 0xf0) == 0x30) { index = 0x46 + 3; xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "LCD sense: Display supports TMDS input on secondary interface\n"); } else { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "LCD sense: Display does not support TMDS video interface (0x%02x)\n", buffer[0x41]); return 0; } /* Save given gamma */ pSiS->CRT2LCDMonitorGamma = (buffer[0x56] + 100) * 10; SiS_Pr->CP_Vendor = panelvendor = buffer[2] | (buffer[1] << 8); SiS_Pr->CP_Product = panelproduct = buffer[3] | (buffer[4] << 8); paneltype = Panel_Custom; SiS_Pr->CP_MaxX = SiS_Pr->CP_PreferredX = xres = buffer[0x76] | (buffer[0x77] << 8); SiS_Pr->CP_MaxY = SiS_Pr->CP_PreferredY = yres = buffer[0x78] | (buffer[0x79] << 8); switch(xres) { case 1024: if(yres == 768) { paneltype = Panel_1024x768; checkexpand = TRUE; } break; case 1280: if(yres == 960) { paneltype = panel1280x960; } else if(yres == 1024) { paneltype = Panel_1280x1024; checkexpand = TRUE; } /* 1280x768, 1280x800 treated as custom here */ break; case 1400: if(pSiS->VGAEngine == SIS_315_VGA) { if(yres == 1050) { paneltype = Panel310_1400x1050; checkexpand = TRUE; } } break; /* 1600x1200 treated as custom */ } /* Determine if RGB18 or RGB24 */ if(index) { if((buffer[index] == 0x20) || (buffer[index] == 0x34)) { cr37 |= 0x01; } } if(checkexpand) { /* TODO - for now, we let the panel scale */ cr37 |= 0x10; } /* Now seek 4-Byte Timing codes and extract sync pol info */ index = 0x80; if(buffer[0x7e] & 0x20) { /* skip Luminance Table (if provided) */ lumsize = buffer[0x80] & 0x1f; if(buffer[0x80] & 0x80) lumsize *= 3; lumsize++; /* luminance header byte */ index += lumsize; } #if 0 /* "pixel rate" = pixel clock? */ if(buffer[0x7e] & 0x1c) { for(i=0; i<((buffer[0x7e] & 0x1c) >> 2); i++) { if(buffer[index + (i*8) + 6] && (buffer[index + (i*8) + 7] & 0x0f)) { int clk = (buffer[index + (i*8) + 6] | ((buffer[index + (i*8) + 7] & 0x0f) << 4)) * 1000; if(clk > SiS_Pr->CP_MaxClock) SiS_Pr->CP_MaxClock = clk; } } } #endif index += (((buffer[0x7e] & 0x1c) >> 2) * 8); /* skip Frequency Ranges */ if(buffer[0x7e] & 0x03) { for(i=0; i<(buffer[0x7e] & 0x03); i++) { if((buffer[index + (i*27) + 9]) || (buffer[index + (i*27) + 10])) { int clk = ((buffer[index + (i*27) + 9]) | ((buffer[index + (i*27) + 9]) << 8)) * 10; if(clk > SiS_Pr->CP_MaxClock) SiS_Pr->CP_MaxClock = clk; } } } index += ((buffer[0x7e] & 0x03) * 27); /* skip Detailed Range Limits */ numcodes = (buffer[0x7f] & 0xf8) >> 3; if(numcodes) { myindex = index; seekcode = (xres - 256) / 16; for(i=0; ipScrn->scrnIndex, X_WARNING, "LCD sense: Unable to retrieve Sync polarity information\n"); } } else { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_WARNING, "LCD sense: Unable to retrieve Sync polarity information\n"); } /* Check against our database; Eg. Sanyo projector reports * 1024x768 in non-HDPC mode, although it supports 1280x720. * Treat such wrongly reporting panels as custom. */ if(paneltype != Panel_Custom) { int maxx, maxy, prefx, prefy; if((SiS_FindPanelFromDB(pSiS, panelvendor, panelproduct, &maxx, &maxy, &prefx, &prefy))) { paneltype = Panel_Custom; SiS_Pr->CP_MaxX = maxx; SiS_Pr->CP_MaxY = maxy; cr37 |= 0x10; /* Leave preferred unchanged (MUST be a valid mode!) */ } } /* Now seek the detailed timing descriptions for custom panels */ if(paneltype == Panel_Custom) { SiS_Pr->CP_Supports64048075 = TRUE; index += (numcodes * 4); numcodes = buffer[0x7f] & 0x07; for(i=0; iCP_HDisplay[i] = xres; SiS_Pr->CP_HSyncStart[i] = xres + (buffer[index+8] | ((buffer[index+11] & 0xc0) << 2)); SiS_Pr->CP_HSyncEnd[i] = SiS_Pr->CP_HSyncStart[i] + (buffer[index+9] | ((buffer[index+11] & 0x30) << 4)); SiS_Pr->CP_HTotal[i] = xres + (buffer[index+3] | ((buffer[index+4] & 0x0f) << 8)); SiS_Pr->CP_HBlankStart[i] = xres + 1; SiS_Pr->CP_HBlankEnd[i] = SiS_Pr->CP_HTotal[i]; SiS_Pr->CP_VDisplay[i] = yres; SiS_Pr->CP_VSyncStart[i] = yres + (((buffer[index+10] & 0xf0) >> 4) | ((buffer[index+11] & 0x0c) << 2)); SiS_Pr->CP_VSyncEnd[i] = SiS_Pr->CP_VSyncStart[i] + ((buffer[index+10] & 0x0f) | ((buffer[index+11] & 0x03) << 4)); SiS_Pr->CP_VTotal[i] = yres + (buffer[index+6] | ((buffer[index+7] & 0x0f) << 8)); SiS_Pr->CP_VBlankStart[i] = yres + 1; SiS_Pr->CP_VBlankEnd[i] = SiS_Pr->CP_VTotal[i]; SiS_Pr->CP_Clock[i] = (buffer[index] | (buffer[index+1] << 8)) * 10; SiS_Pr->CP_DataValid[i] = TRUE; if((SiS_Pr->CP_HDisplay[i] & 7) || (SiS_Pr->CP_HDisplay[i] > SiS_Pr->CP_HSyncStart[i]) || (SiS_Pr->CP_HDisplay[i] >= SiS_Pr->CP_HSyncEnd[i]) || (SiS_Pr->CP_HDisplay[i] >= SiS_Pr->CP_HTotal[i]) || (SiS_Pr->CP_HSyncStart[i] >= SiS_Pr->CP_HSyncEnd[i]) || (SiS_Pr->CP_HSyncStart[i] > SiS_Pr->CP_HTotal[i]) || (SiS_Pr->CP_HSyncEnd[i] > SiS_Pr->CP_HTotal[i]) || (SiS_Pr->CP_VDisplay[i] > SiS_Pr->CP_VSyncStart[i]) || (SiS_Pr->CP_VDisplay[i] >= SiS_Pr->CP_VSyncEnd[i]) || (SiS_Pr->CP_VDisplay[i] >= SiS_Pr->CP_VTotal[i]) || (SiS_Pr->CP_VSyncStart[i] > SiS_Pr->CP_VSyncEnd[i]) || (SiS_Pr->CP_VSyncStart[i] > SiS_Pr->CP_VTotal[i]) || (SiS_Pr->CP_VSyncEnd[i] > SiS_Pr->CP_VTotal[i]) || (((pSiS->VBFlags2 & VB2_30xC) && (SiS_Pr->CP_Clock[i] > 162500)) || ((!(pSiS->VBFlags2 & VB2_30xC)) && ( (SiS_Pr->CP_Clock[i] > 110500) || (SiS_Pr->CP_VDisplay[i] > 1024) || (SiS_Pr->CP_HDisplay[i] > 1600) ))) || (buffer[index + 17] & 0x80)) { SiS_Pr->CP_DataValid[i] = FALSE; } else { SiS_Pr->CP_HaveCustomData = TRUE; if(SiS_Pr->CP_Clock[i] > SiS_Pr->CP_MaxClock) SiS_Pr->CP_MaxClock = SiS_Pr->CP_Clock[i]; if((SiS_Pr->CP_PreferredX == xres) && (SiS_Pr->CP_PreferredY == yres)) { SiS_Pr->CP_PreferredIndex = i; SiS_MakeClockRegs(pSiS->pScrn, SiS_Pr->CP_Clock[i], &SiS_Pr->CP_PrefSR2B, &SiS_Pr->CP_PrefSR2C); SiS_Pr->CP_PrefClock = (SiS_Pr->CP_Clock[i] / 1000) + 1; if(!havesync) { cr37 |= ((((buffer[index + 17] & 0x06) ^ 0x06) << 5) | 0x20); havesync = TRUE; } } SiS_Pr->CP_HSync_P[i] = (buffer[index + 17] & 0x02) ? TRUE : FALSE; SiS_Pr->CP_VSync_P[i] = (buffer[index + 17] & 0x04) ? TRUE : FALSE; SiS_Pr->CP_SyncValid[i] = TRUE; } } cr37 |= 0x10; } break; } /* 1280x960 panels are always RGB24, unable to scale and use * high active sync polarity. (Check is save, other panel types * for other chipset series not being set up) */ if(paneltype == panel1280x960) cr37 &= 0x0e; for(i = 0; i < 7; i++) { if(SiS_Pr->CP_DataValid[i]) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "Non-standard LCD/DVI-D timing data no. %d:\n", i); xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, " HDisplay %d HSync %d HSyncEnd %d HTotal %d\n", SiS_Pr->CP_HDisplay[i], SiS_Pr->CP_HSyncStart[i], SiS_Pr->CP_HSyncEnd[i], SiS_Pr->CP_HTotal[i]); xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, " VDisplay %d VSync %d VSyncEnd %d VTotal %d\n", SiS_Pr->CP_VDisplay[i], SiS_Pr->CP_VSyncStart[i], SiS_Pr->CP_VSyncEnd[i], SiS_Pr->CP_VTotal[i]); xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, " Pixel clock: %3.3fMhz\n", (float)SiS_Pr->CP_Clock[i] / 1000); xf86DrvMsg(pSiS->pScrn->scrnIndex, X_INFO, " To use this, add \"%dx%d\" to the Modes list in the Screen section\n", SiS_Pr->CP_HDisplay[i], SiS_Pr->CP_VDisplay[i]); } } if(paneltype) { if(!SiS_Pr->CP_PreferredX) SiS_Pr->CP_PreferredX = SiS_Pr->CP_MaxX; if(!SiS_Pr->CP_PreferredY) SiS_Pr->CP_PreferredY = SiS_Pr->CP_MaxY; SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x32,0x08); SiS_SetReg(SiS_Pr->SiS_P3d4,0x36,paneltype); cr37 &= 0xf1; SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x37,0x0c,cr37); SiS_Pr->PanelSelfDetected = TRUE; #ifdef TWDEBUG xf86DrvMsgVerb(pSiS->pScrn->scrnIndex, X_PROBED, 3, "LCD sense: [DDC LCD results: 0x%02x, 0x%02x]\n", paneltype, cr37); #endif } else { SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x32,~0x08); SiS_SetReg(SiS_Pr->SiS_P3d4,0x36,0x00); } return 0; } unsigned short SiS_SenseVGA2DDC(struct SiS_Private *SiS_Pr, SISPtr pSiS) { unsigned short DDCdatatype, flag; BOOLEAN foundcrt = FALSE; int retry; unsigned char buffer[256]; if(!(pSiS->VBFlags2 & VB2_SISVGA2BRIDGE)) return 0; /* Specific for XGI_40/Rev 2/A01 (XGI V3XT A01): This card has CRT1's * and CRT2's DDC ports physically connected to each other. There * is no connection to the video bridge's DDC port, both DDC * channels are routed to the GPU. Smart. If both CRT1 (CRT) and * CRT2 (VGA or LCD) are connected, DDC will fail. If a CRT is * connected to the DVI-I port, it will report "analog" as well, * so we never know if the monitor is connected to CRT1 or CRT2. * Hence, no reliable CRT detection here... we need to fall back to * the sensing stuff in sis_vb.c. */ if(SiS_Pr->DDCPortMixup) return 0; if(SiS_InitDDCRegs(SiS_Pr, pSiS->VBFlags, pSiS->VGAEngine, 2, 0, FALSE, pSiS->VBFlags2) == 0xFFFF) return 0; SiS_Pr->SiS_DDC_SecAddr = 0x00; /* Probe supported DA's */ flag = SiS_ProbeDDC(SiS_Pr); if(flag & 0x10) { SiS_Pr->SiS_DDC_DeviceAddr = 0xa6; /* EDID V2 (FP) */ DDCdatatype = 4; } else if(flag & 0x08) { SiS_Pr->SiS_DDC_DeviceAddr = 0xa2; /* EDID V2 (P&D-D Monitor) */ DDCdatatype = 3; } else if(flag & 0x02) { SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; /* EDID V1 */ DDCdatatype = 1; } else { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "VGA2 sense: Do DDC answer\n"); return 0; /* no DDC support (or no device attached) */ } /* Read the entire EDID */ retry = 2; do { if(SiS_ReadDDC(SiS_Pr, DDCdatatype, buffer)) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, "VGA2 sense: DDC read failed (attempt %d), %s\n", (3-retry), (retry == 1) ? "giving up" : "retrying"); retry--; if(retry == 0) return 0xFFFF; } else break; } while(1); /* Analyze EDID. We don't have many chances to * distinguish a flat panel from a CRT... */ switch(DDCdatatype) { case 1: if(!(checkedid1(buffer))) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_ERROR, "VGA2 sense: EDID corrupt\n"); return 0; } if(buffer[0x14] & 0x80) { /* Display uses digital input */ xf86DrvMsg(pSiS->pScrn->scrnIndex, X_ERROR, "VGA2 sense: Attached display expects digital input\n"); return 0; } SiS_Pr->CP_Vendor = buffer[9] | (buffer[8] << 8); SiS_Pr->CP_Product = buffer[10] | (buffer[11] << 8); foundcrt = TRUE; /* Save given gamma */ pSiS->CRT2VGAMonitorGamma = (buffer[0x17] + 100) * 10; break; case 3: case 4: if(!(checkedid2(buffer))) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_ERROR, "VGA2 sense: EDID corrupt\n"); return 0; } if( ((buffer[0x41] & 0x0f) != 0x01) && /* Display does not support analog input */ ((buffer[0x41] & 0x0f) != 0x02) && ((buffer[0x41] & 0xf0) != 0x10) && ((buffer[0x41] & 0xf0) != 0x20) ) { xf86DrvMsg(pSiS->pScrn->scrnIndex, X_ERROR, "VGA2 sense: Attached display does not support analog input (0x%02x)\n", buffer[0x41]); return 0; } SiS_Pr->CP_Vendor = buffer[2] | (buffer[1] << 8); SiS_Pr->CP_Product = buffer[3] | (buffer[4] << 8); foundcrt = TRUE; /* Save given gamma */ pSiS->CRT2VGAMonitorGamma = (buffer[0x56] + 100) * 10; break; } if(foundcrt) { SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x32,0x10); } return(0); } /* 4-tap scaler for 301C and later */ static float rcos(float x) { double pi = 3.14159265358979; float r = 0.5, y; if(x == 0.0) { y = 1.0; } else if(x == -1.0 || x == 1.0) { y = 0.0; } else { y = sin(pi * x) / (pi * x) * cos(r * pi * x) / (1 - x * x); } return y; } static int roundandconv(float in) { int a = ((int)(in)) * 10; int b = (int)(in * 10.0); if (in >= 0) { if((b - a) < 5) return (a / 10); else return (a / 10) + 1; } else { if((b - a) > -5) return (a / 10); else return (a / 10) -1; } } void SiS_CalcXTapScaler(struct SiS_Private *SiS_Pr, int srcsize, int destsize, int taps, Bool ishoriz) { float scale = (float)srcsize / (float)destsize; int coe_bit_number = 6; float fixnumber = (float)(1 << (coe_bit_number - 1)); float ops, WW, W[8]; int WeightMat[16][8]; int i, j, index; /* For now: */ if(taps != 4) taps = 4; if(scale < 1.0) scale = 1.0; else if(scale > 1.0) scale *= 1.1; for(i = 0; i < 16; i++) { ops = (float)i / (16.0 * scale); switch(taps) { case 4: W[0] = rcos( 1.0 / scale + ops); W[1] = rcos( 0.0 / scale + ops); W[2] = rcos(-1.0 / scale + ops); W[3] = rcos(-2.0 / scale + ops); WW = W[0] + W[1] + W[2] + W[3]; WeightMat[i][0] = roundandconv(W[0] / WW * fixnumber); WeightMat[i][1] = roundandconv(W[1] / WW * fixnumber); WeightMat[i][2] = roundandconv(W[2] / WW * fixnumber); WeightMat[i][3] = (int)fixnumber - WeightMat[i][0] - WeightMat[i][1] - WeightMat[i][2]; break; #if 0 /* For future use */ case 8: W[0] = rcos( 3.0/scale + ops); W[1] = rcos( 2.0/scale + ops); W[2] = rcos( 1.0/scale + ops); W[3] = rcos( 0.0/scale + ops); W[4] = rcos(-1.0/scale + ops); W[5] = rcos(-2.0/scale + ops); W[6] = rcos(-3.0/scale + ops); W[7] = rcos(-4.0/scale + ops); WW = W[0] + W[1] + W[2] + W[3] + W[4] + W[5] + W[6] + W[7]; WeightMat[i][0] = roundandconv(W[0]/WW * fixnumber); WeightMat[i][1] = roundandconv(W[1]/WW * fixnumber); WeightMat[i][2] = roundandconv(W[2]/WW * fixnumber); WeightMat[i][3] = roundandconv(W[3]/WW * fixnumber); WeightMat[i][4] = roundandconv(W[4]/WW * fixnumber); WeightMat[i][5] = roundandconv(W[5]/WW * fixnumber); WeightMat[i][6] = roundandconv(W[6]/WW * fixnumber); WeightMat[i][7] = (int)fixnumber - WeightMat[i][0] - WeightMat[i][1] - WeightMat[i][2] - WeightMat[i][3] - WeightMat[i][4] - WeightMat[i][5] - WeightMat[i][6]; break; #endif } } index = ishoriz ? 0x80 : 0xc0; for(i = 0; i < 16; i++) { for(j = 0; j < 4 /* taps! */; j++) { if(WeightMat[i][j] < 0) { WeightMat[i][j] = ((~(-WeightMat[i][j])) + 1) & 0x7f; } SiS_SetReg(SiS_Pr->SiS_Part2Port, index++, WeightMat[i][j]); } } } void SiS_SetGroup2_C_ELV(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned short ModeIdIndex, unsigned short RefreshRateTableIndex) { unsigned char temp; if(!(SiS_Pr->SiS_VBType & VB_SISTAP4SCALER)) return; SiS_CalcXTapScaler(SiS_Pr, SiS_Pr->SiS_VGAHDE, SiS_Pr->SiS_HDE, 4, TRUE); if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { SiS_CalcXTapScaler(SiS_Pr, SiS_Pr->SiS_VGAVDE, SiS_Pr->SiS_VDE, 4, FALSE); } temp = 0x10; if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) temp |= 0x04; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x4e,0xeb,temp); }