summaryrefslogtreecommitdiff
path: root/src/mga_g450pll.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mga_g450pll.c')
-rw-r--r--src/mga_g450pll.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/src/mga_g450pll.c b/src/mga_g450pll.c
new file mode 100644
index 0000000..6176549
--- /dev/null
+++ b/src/mga_g450pll.c
@@ -0,0 +1,523 @@
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/mga/mga_g450pll.c,v 1.8 2002/09/16 18:05:56 eich Exp $ */
+
+/* All drivers should typically include these */
+#include "xf86.h"
+#include "xf86_OSproc.h"
+#include "xf86_ansic.h"
+
+/* Drivers for PCI hardware need this */
+#include "xf86PciInfo.h"
+
+/* Drivers that need to access the PCI config space directly need this */
+#include "xf86Pci.h"
+
+#include "mga_bios.h"
+#include "mga_reg.h"
+#include "mga.h"
+
+#define MNP_TABLE_SIZE 64
+#define CLKSEL_MGA 0x0c
+#define PLLLOCK 0x40
+
+static CARD32 G450ApplyPFactor(ScrnInfoPtr pScrn, CARD8 ucP, CARD32 *pulFIn)
+{
+ if(!(ucP & 0x40))
+ {
+ *pulFIn = *pulFIn / (2L << (ucP & 3));
+ }
+
+ return TRUE;
+}
+
+
+static CARD32 G450RemovePFactor(ScrnInfoPtr pScrn, CARD8 ucP, CARD32 *pulFIn)
+{
+ if(!(ucP & 0x40))
+ {
+ *pulFIn = *pulFIn * (2L << (ucP & 3));
+ }
+
+ return TRUE;
+}
+
+
+static CARD32 G450CalculVCO(ScrnInfoPtr pScrn, CARD32 ulMNP, CARD32 *pulF)
+{
+ CARD8 ucM, ucN, ucP;
+
+ ucM = (CARD8)((ulMNP >> 16) & 0xff);
+ ucN = (CARD8)((ulMNP >> 8) & 0xff);
+ ucP = (CARD8)(ulMNP & 0x03);
+
+ *pulF = (27000 * (2 * (ucN + 2)) + ((ucM + 1) >> 1)) / (ucM + 1);
+
+ return TRUE;
+}
+
+
+static CARD32 G450CalculDeltaFreq(ScrnInfoPtr pScrn, CARD32 ulF1,
+ CARD32 ulF2, CARD32 *pulDelta)
+{
+ if(ulF2 < ulF1)
+ {
+ *pulDelta = ((ulF1 - ulF2) * 1000) / ulF1;
+ }
+ else
+ {
+ *pulDelta = ((ulF2 - ulF1) * 1000) / ulF1;
+ }
+
+ return TRUE;
+}
+
+
+
+
+static CARD32 G450FindNextPLLParam(ScrnInfoPtr pScrn, CARD32 ulFout,
+ CARD32 *pulPLLMNP)
+{
+ CARD8 ucM, ucN, ucP, ucS;
+ CARD32 ulVCO, ulVCOMin;
+
+ ucM = (CARD8)((*pulPLLMNP >> 16) & 0xff);
+ ucN = (CARD8)((*pulPLLMNP >> 8) & 0xff);
+ ucP = (CARD8)(*pulPLLMNP & 0x43);
+
+ ulVCOMin = 256000;
+
+ if(ulVCOMin >= (255L * 8000))
+ {
+ ulVCOMin = 230000;
+ }
+
+ if((ucM == 9) && (ucP & 0x40))
+ {
+ *pulPLLMNP = 0xffffffff;
+ } else if (ucM == 9)
+ {
+ if(ucP)
+ {
+ ucP--;
+ }
+ else
+ {
+ ucP = 0x40;
+ }
+ ucM = 0;
+ }
+ else
+ {
+ ucM++;
+ }
+
+ ulVCO = ulFout;
+
+ G450RemovePFactor(pScrn, ucP, &ulVCO);
+
+ if(ulVCO < ulVCOMin)
+ {
+ *pulPLLMNP = 0xffffffff;
+ }
+
+ if(*pulPLLMNP != 0xffffffff)
+ {
+ ucN = (CARD8)(((ulVCO * (ucM+1) + 27000)/(27000 * 2)) - 2);
+
+ ucS = 5;
+ if(ulVCO < 1300000) ucS = 4;
+ if(ulVCO < 1100000) ucS = 3;
+ if(ulVCO < 900000) ucS = 2;
+ if(ulVCO < 700000) ucS = 1;
+ if(ulVCO < 550000) ucS = 0;
+
+ ucP |= (CARD8)(ucS << 3);
+
+ *pulPLLMNP &= 0xff000000;
+ *pulPLLMNP |= (CARD32)ucM << 16;
+ *pulPLLMNP |= (CARD32)ucN << 8;
+ *pulPLLMNP |= (CARD32)ucP;
+
+#ifdef DEBUG
+ ErrorF("FINS_S: VCO = %d, S = %02X, *pulPLLMNP = %08X\n", ulVCO, (ULONG)ucS, *pulPLLMNP);
+#endif
+ }
+
+ return TRUE;
+}
+
+
+static CARD32 G450FindFirstPLLParam(ScrnInfoPtr pScrn, CARD32 ulFout,
+ CARD32 *pulPLLMNP)
+{
+ CARD8 ucP;
+ CARD32 ulVCO;
+ CARD32 ulVCOMax;
+
+ /* Default value */
+ ulVCOMax = 1300000;
+
+ if(ulFout > (ulVCOMax/2))
+ {
+ ucP = 0x40;
+ ulVCO = ulFout;
+ }
+ else
+ {
+ ucP = 3;
+ ulVCO = ulFout;
+ G450RemovePFactor(pScrn, ucP, &ulVCO);
+ while(ucP && (ulVCO > ulVCOMax))
+ {
+ ucP--;
+ ulVCO = ulFout;
+ G450RemovePFactor(pScrn, ucP, &ulVCO);
+ }
+ }
+
+ if(ulVCO > ulVCOMax)
+ {
+ *pulPLLMNP = 0xffffffff;
+ }
+ else
+ {
+ /* Pixel clock: 1 */
+ *pulPLLMNP = (1 << 24) + 0xff0000 + ucP;
+ G450FindNextPLLParam(pScrn, ulFout, pulPLLMNP);
+ }
+
+ return TRUE;
+
+}
+
+
+static CARD32 G450WriteMNP(ScrnInfoPtr pScrn, CARD32 ulMNP)
+{
+ MGAPtr pMga = MGAPTR(pScrn);
+ MGARegPtr pReg;
+
+ pReg = &pMga->ModeReg;
+
+ if (!pMga->SecondCrtc) {
+ outMGAdac(MGA1064_PIX_PLLC_M, (CARD8)(ulMNP >> 16));
+ outMGAdac(MGA1064_PIX_PLLC_N, (CARD8)(ulMNP >> 8));
+ outMGAdac(MGA1064_PIX_PLLC_P, (CARD8) ulMNP);
+ } else {
+ outMGAdac(MGA1064_VID_PLL_M, (CARD8)(ulMNP >> 16));
+ outMGAdac(MGA1064_VID_PLL_N, (CARD8)(ulMNP >> 8));
+ outMGAdac(MGA1064_VID_PLL_P, (CARD8) ulMNP);
+ }
+ return TRUE;
+}
+
+static CARD32 G450ReadMNP(ScrnInfoPtr pScrn)
+{
+ MGAPtr pMga = MGAPTR(pScrn);
+ MGARegPtr pReg;
+ CARD32 ret = 0;
+
+ pReg = &pMga->ModeReg;
+
+ if (!pMga->SecondCrtc) {
+ ret = (CARD8)inMGAdac(MGA1064_PIX_PLLC_M) << 16;
+ ret |= (CARD8)inMGAdac(MGA1064_PIX_PLLC_N) << 8;
+ ret |= (CARD8)inMGAdac(MGA1064_PIX_PLLC_P);
+ } else {
+ ret = (CARD8)inMGAdac(MGA1064_VID_PLL_M) << 16;
+ ret |= (CARD8)inMGAdac(MGA1064_VID_PLL_N) << 8;
+ ret |= (CARD8)inMGAdac(MGA1064_VID_PLL_P);
+ }
+ return ret;
+}
+
+
+static CARD32 G450CompareMNP(ScrnInfoPtr pScrn, CARD32 ulFout, CARD32 ulMNP1,
+ CARD32 ulMNP2, long *pulResult)
+{
+ CARD32 ulFreq, ulDelta1, ulDelta2;
+
+ G450CalculVCO(pScrn, ulMNP1, &ulFreq);
+ G450ApplyPFactor(pScrn, (CARD8) ulMNP1, &ulFreq);
+ G450CalculDeltaFreq(pScrn, ulFout, ulFreq, &ulDelta1);
+
+ G450CalculVCO(pScrn, ulMNP2, &ulFreq);
+ G450ApplyPFactor(pScrn, (CARD8) ulMNP2, &ulFreq);
+ G450CalculDeltaFreq(pScrn, ulFout, ulFreq, &ulDelta2);
+
+ if(ulDelta1 < ulDelta2)
+ {
+ *pulResult = -1;
+ }
+ else if(ulDelta1 > ulDelta2)
+ {
+ *pulResult = 1;
+ }
+ else
+ {
+ *pulResult = 0;
+ }
+
+ if((ulDelta1 <= 5) && (ulDelta2 <= 5))
+ {
+ if((ulMNP1 & 0xff0000) < (ulMNP2 & 0xff0000))
+ {
+ *pulResult = -1;
+ }
+ else if((ulMNP1 & 0xff0000) > (ulMNP2 & 0xff0000))
+ {
+ *pulResult = 1;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static CARD32 G450IsPllLocked(ScrnInfoPtr pScrn, Bool *lpbLocked)
+{
+ CARD32 ulFallBackCounter, ulLockCount, ulCount;
+ CARD8 ucPLLStatus;
+
+ MGAPtr pMga = MGAPTR(pScrn);
+
+ /* Pixel PLL */
+ if (!pMga->SecondCrtc)
+ OUTREG8(0x3c00, 0x4f); /* Pixel PLL */
+ else
+ OUTREG8(0x3c00, 0x8c); /* Video PLL */
+
+ ulFallBackCounter = 0;
+
+ do
+ {
+ ucPLLStatus = INREG8(0x3c0a);
+ ulFallBackCounter++;
+ } while(!(ucPLLStatus & PLLLOCK) && (ulFallBackCounter < 1000));
+
+ ulLockCount = 0;
+ if(ulFallBackCounter < 1000)
+ {
+ for(ulCount = 0; ulCount < 100; ulCount++)
+ {
+ ucPLLStatus = INREG8(0x3c0a);
+ if(ucPLLStatus & PLLLOCK)
+ {
+ ulLockCount++;
+ }
+ }
+ }
+
+ *lpbLocked = ulLockCount >= 90;
+
+ return TRUE;
+}
+
+
+double MGAG450SetPLLFreq(ScrnInfoPtr pScrn, long f_out)
+{
+ Bool bFoundValidPLL;
+ Bool bLocked;
+ CARD8 ucMisc, ucSIndex, ucSTable[4];
+ CARD32 ulMaxIndex;
+ CARD32 ulMNP;
+ CARD32 ulMNPTable[MNP_TABLE_SIZE];
+ CARD32 ulIndex;
+ CARD32 ulTryMNP;
+ long lCompareResult;
+ MGAPtr pMga = MGAPTR(pScrn);
+
+#ifdef DEBUG
+ xf86DrvMsg(pScrn->scrnIndex,X_INFO, "Restoring PLLClk = %d\n",f_out);
+#endif
+ G450FindFirstPLLParam(pScrn, f_out, &ulMNP);
+ ulMNPTable[0] = ulMNP;
+ G450FindNextPLLParam(pScrn, f_out, &ulMNP);
+ ulMaxIndex = 1;
+ while(ulMNP != 0xffffffff)
+ {
+ int ulIndex;
+ Bool bSkipValue;
+
+ bSkipValue = FALSE;
+ if(ulMaxIndex == MNP_TABLE_SIZE)
+ {
+ G450CompareMNP(pScrn, f_out, ulMNP, ulMNPTable[MNP_TABLE_SIZE - 1],
+ &lCompareResult);
+
+ if(lCompareResult > 0)
+ {
+ bSkipValue = TRUE;
+ }
+ else
+ {
+ ulMaxIndex--;
+ }
+ }
+
+ if(!bSkipValue)
+ {
+ for(ulIndex = ulMaxIndex; !bSkipValue && (ulIndex > 0); ulIndex--)
+ {
+ G450CompareMNP(pScrn, f_out, ulMNP, ulMNPTable[ulIndex - 1],
+ &lCompareResult);
+
+ if(lCompareResult < 0)
+ {
+ ulMNPTable[ulIndex] = ulMNPTable[ulIndex - 1];
+ }
+ else
+ {
+ break;
+ }
+ }
+ ulMNPTable[ulIndex] = ulMNP;
+ ulMaxIndex++;
+ }
+
+ G450FindNextPLLParam(pScrn, f_out, &ulMNP);
+ }
+
+ bFoundValidPLL = FALSE;
+ ulMNP = 0;
+
+ /* For pixel pll */
+ if (!pMga->SecondCrtc) {
+ ucMisc = INREG8(0x1FCC);
+ OUTREG8(0x1fc2, (CARD8)(ucMisc | CLKSEL_MGA));
+ }
+
+ for(ulIndex = 0; !bFoundValidPLL && (ulIndex < ulMaxIndex); ulIndex++)
+ {
+ ulTryMNP = ulMNPTable[ulIndex];
+
+ ucSTable[3] = 0xff;
+ ucSTable[2] = 0xff;
+ ucSTable[0] = (CARD8) (ulTryMNP & 0x38);
+
+ if (ucSTable[0] != 0) {
+ ucSTable[1] = ucSTable[0] - 8;
+ if (ucSTable[0] != 0x38) {
+ ucSTable[2] = ucSTable[0] + 8;
+ }
+ } else {
+ ucSTable[1] = 8;
+ }
+
+ for(ucSIndex = 0; !bFoundValidPLL && (ucSTable[ucSIndex] != 0xff);
+ ucSIndex++) {
+ ulTryMNP &= 0xffffffc7;
+ ulTryMNP |= (CARD32)ucSTable[ucSIndex];
+
+ bLocked = TRUE;
+ if((ulMNPTable[ulIndex] & 0xff00) < 0x300 ||
+ (ulMNPTable[ulIndex] & 0xff00) > 0x7a00)
+ {
+ bLocked = FALSE;
+ }
+
+ if(bLocked)
+ {
+ G450WriteMNP(pScrn, ulTryMNP - 0x300);
+ G450IsPllLocked(pScrn, &bLocked);
+ }
+
+ if(bLocked)
+ {
+ G450WriteMNP(pScrn, ulTryMNP + 0x300);
+ G450IsPllLocked(pScrn, &bLocked);
+ }
+
+ if(bLocked)
+ {
+ G450WriteMNP(pScrn, ulTryMNP - 0x200);
+ G450IsPllLocked(pScrn, &bLocked);
+ }
+
+ if(bLocked)
+ {
+ G450WriteMNP(pScrn, ulTryMNP + 0x200);
+ G450IsPllLocked(pScrn, &bLocked);
+ }
+
+ if(bLocked)
+ {
+ G450WriteMNP(pScrn, ulTryMNP - 0x100);
+ G450IsPllLocked(pScrn, &bLocked);
+ }
+
+ if(bLocked)
+ {
+ G450WriteMNP(pScrn, ulTryMNP + 0x100);
+ G450IsPllLocked(pScrn, &bLocked);
+ }
+
+ if(bLocked)
+ {
+ G450WriteMNP(pScrn, ulTryMNP);
+ G450IsPllLocked(pScrn, &bLocked);
+ }
+ else if(!ulMNP)
+ {
+ G450WriteMNP(pScrn, ulTryMNP);
+ G450IsPllLocked(pScrn, &bLocked);
+ if(bLocked)
+ {
+ ulMNP = ulMNPTable[ulIndex];
+ }
+ bLocked = FALSE;
+ }
+
+ if(bLocked)
+ {
+ bFoundValidPLL = TRUE;
+ }
+ }
+ }
+
+ if(!bFoundValidPLL)
+ {
+ if(ulMNP)
+ {
+ G450WriteMNP(pScrn, ulMNP);
+ }
+ else
+ {
+ G450WriteMNP(pScrn, ulMNPTable[0]);
+ }
+ }
+
+ return TRUE;
+}
+
+long
+MGAG450SavePLLFreq(ScrnInfoPtr pScrn)
+{
+ CARD32 ulMNP = G450ReadMNP(pScrn);
+ CARD8 ucP;
+ CARD32 freq;
+
+ G450CalculVCO(pScrn, ulMNP, &freq);
+ ucP = (CARD8)(ulMNP & 0x03);
+ G450ApplyPFactor(pScrn, ucP, &freq);
+
+#ifdef DEBUG
+ xf86DrvMsg(pScrn->scrnIndex,X_INFO,"Saved PLLClk = %d\n",freq);
+#endif
+ return freq;
+}
+
+#ifdef DEBUG
+void
+MGAG450PrintPLL(ScrnInfoPtr pScrn)
+{
+ CARD32 ulMNP = G450ReadMNP(pScrn);
+ CARD8 ucP;
+ CARD32 freq;
+
+ G450CalculVCO(pScrn, ulMNP, &freq);
+ ucP = (CARD8)(ulMNP & 0x03);
+ G450ApplyPFactor(pScrn, ucP, &freq);
+
+ xf86DrvMsg(pScrn->scrnIndex,X_INFO,"MGAGClock = %d -- MNP = 0x%x\n",
+ freq,ulMNP);
+}
+#endif