/* * This code is derived from code available from the STB bulletin board */ /* $XFree86: mit/server/ddx/x386/common_hw/ICD2061Aalt.c,v 2.6 1994/04/15 05:10:30 dawes Exp $ */ #ifndef KERNEL #include "compiler.h" #else #define GCCUSESGAS #define PCVT_STANDALONE 1 #endif #define SEQREG 0x03C4 #define MISCREG 0x03C2 #define MISCREAD 0x03CC double fref = 14.31818 * 2.0; char ascclk[] = "VIDEO CLOCK ?"; unsigned short clknum; unsigned short vlbus_flag; unsigned short card; unsigned short crtcaddr; unsigned short clockreg; static double range[15] = {50.0, 51.0, 53.2, 58.5, 60.7, 64.4, 66.8, 73.5, 75.6, 80.9, 83.2, 91.5, 100.0, 120.0, 120.0}; #ifdef __STDC__ static double genratio(unsigned int *p, unsigned int *q, double tgt); static double f(unsigned int p, unsigned int q, double basefreq); #if 0 static void prtbinary(unsigned int size, unsigned int val); #endif static void wait_vb(); static void wrt_clk_bit(unsigned int value); static void init_clock(unsigned long setup, unsigned short crtcport); #else static double genratio(); static double f(); #if 0 static void prtbinary(); #endif static void wait_vb(); static void wrt_clk_bit(); static void init_clock(); #endif void AltICD2061SetClock(frequency, select) register long frequency; /* in Hz */ int select; { unsigned int m, mval, ival; int i; long dwv; double realval; double freq, fvco; double dev, devx; double delta, deltax; unsigned int p, q; unsigned int bestp, bestq; unsigned char tmp; crtcaddr=(inb(0x3CC) & 0x01) ? 0x3D4 : 0x3B4; outb(crtcaddr, 0x11); /* Unlock CRTC registers */ tmp = inb(crtcaddr + 1); outb(crtcaddr + 1, tmp & ~0x80); outw(crtcaddr, 0x4838); /* Unlock S3 register set */ outw(crtcaddr, 0xA039); clknum = select; freq = ((double)frequency)/1000000.0; if (freq > range[14]) freq =range[14]; else if (freq <= 6.99) freq = 7.0; /* * Calculate values to load into ICD 2061A clock chip to set frequency */ delta = 999.0; dev = 999.0; ival = 99; mval = 99; fvco = freq / 2; for (m = 0; m < 8; m++) { fvco *= 2.0; for (i = 14; i >= 0; i--) if (fvco >= range[i]) break; if (i < 0) continue; if (i == 14) break; devx = (fvco - (range[i] + range[i+1])/2)/fvco; if (devx < 0) devx = -devx; deltax = genratio(&p, &q, fvco); if (delta < deltax) continue; if (deltax < delta || devx < dev) { bestp = p; bestq = q; delta = deltax; dev = devx; ival = i; mval = m; } } fvco = fref; for (m=0; m 128) mmax = 128; while (m < mmax) { test = f(m, k, fref) - tgt; if (test < 0) test = -test; if (mindiff > test) { mindiff = test; *p = m; *q = k; } m++; } } return (mindiff); } #if 0 static void prtbinary(size, val) unsigned int size; unsigned int val; { unsigned int mask; int k; mask = 1; for (k=size; --k > 0 || mask <= val/2;) mask <<= 1; while (mask) { fputc((mask&val)? '1': '0' , stderr); mask >>= 1; } } #endif static void wait_vb() { while ((inb(crtcaddr+6) & 0x08) == 0) ; while (inb(crtcaddr+6) & 0x08) ; } #ifdef __STDC__ static void init_clock(unsigned long setup, unsigned short crtcport) #else static void init_clock(setup, crtcport) unsigned long setup; unsigned short crtcport; #endif { unsigned char nclk[2], clk[2]; unsigned short restore42; unsigned short oldclk; unsigned short bitval; int i; unsigned char c; #ifndef PCVT_STANDALONE (void)xf86DisableInterrupts(); #endif oldclk = inb(0x3CC); outb(crtcport, 0x42); restore42 = inb(crtcport+1); outw(0x3C4, 0x0100); outb(0x3C4, 1); c = inb(0x3C5); outb(0x3C5, 0x20 | c); outb(crtcport, 0x42); outb(crtcport+1, 0x03); outw(0x3C4, 0x0300); nclk[0] = oldclk & 0xF3; nclk[1] = nclk[0] | 0x08; clk[0] = nclk[0] | 0x04; clk[1] = nclk[0] | 0x0C; outb(crtcport, 0x42); i = inw(crtcport); outw(0x3C4, 0x0100); wrt_clk_bit(oldclk | 0x08); wrt_clk_bit(oldclk | 0x0C); for (i=0; i<5; i++) { wrt_clk_bit(nclk[1]); wrt_clk_bit(clk[1]); } wrt_clk_bit(nclk[1]); wrt_clk_bit(nclk[0]); wrt_clk_bit(clk[0]); wrt_clk_bit(nclk[0]); wrt_clk_bit(clk[0]); for (i=0; i<24; i++) { bitval = setup & 0x01; setup >>= 1; wrt_clk_bit(clk[1-bitval]); wrt_clk_bit(nclk[1-bitval]); wrt_clk_bit(nclk[bitval]); wrt_clk_bit(clk[bitval]); } wrt_clk_bit(clk[1]); wrt_clk_bit(nclk[1]); wrt_clk_bit(clk[1]); outb(0x3C4, 1); c = inb(0x3C5); outb(0x3C5, 0xDF & c); outb(crtcport, 0x42); outb(crtcport+1, restore42); outb(0x3C2, oldclk); outw(0x3C4, 0x0300); #ifndef PCVT_STANDALONE xf86EnableInterrupts(); #endif } static void wrt_clk_bit(value) unsigned int value; { int j; outb(0x3C2, value); for (j=2; --j; ) inb(0x200); }