Rev 54 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* icd2061a.c
*
* support for the ICD 2061A programmable clockchip and compatibles
* outside of the DAC
*
* Rev history:
* Andreas Arens Dec 95: Created
*
* Andreas Arens Feb 15 1996: A few minor fixes
*/
#include "timing.h"
#include "libvga.h"
#include "ramdac.h"
#include "clockchip.h"
#include "driver.h"
#include "vga.h"
/*
* ATTENTION: The ICD 2061A does not support reading of the currently selected
* pixelclock. XFree86 also fails to restore this value correctly, but always
* estores a 45 MHz pixelclock. My standard text mode (132x25) uses 40 MHz,
* which is the value selected here.
* You can use the SVGATextMode-1.0 'clockprobe' tool right after boot to
* determine the value used with your card and modify here, but since 40 MHz
* is the VESA suggested pixelclock for a 70 Hz 132x25 mode, the value here
* seems fine. Note that 80xXX modes use 25 or 28 MHz clocks, which are fixed
* and not affected by this. This might not be true for Diamond boards using
* the DCS2824-0 clockchip, which is an ICD 2061A clone.
*/
#define I2061A_DEFAULT_TEXT_FREQUENCY (40000L) /* kHz */
/*
* Clockchip code is derived from I2051Aalt.c in XFree86/common_hw which
* in turn is derived from code available from the STB bulletin board.
* A number of modifications have been made to fit this into SVGAlib.
*/
#define I2061A_CRYSTAL_FREQUENCY (14.31818 * 2.0)
static double I2061A_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.0000001};
static long I2061A_SelectClock(long frequency)
/* in KHz */
{
unsigned int m;
int i;
double freq, fvco;
double dev, devx;
double delta, deltax;
double f0;
unsigned int p, q;
unsigned int bestp = 0, bestq = 0, bestm = 0, besti = 0;
freq = ((double) frequency) / 1000.0;
if (freq > I2061A_range[13])
freq = I2061A_range[13];
else if (freq < 7.0)
freq = 7.0;
/*
* Calculate values to load into ICD 2061A clock chip to set frequency
*/
delta = 999.0;
dev = 999.0;
for (m = 0; m < 8; m++) {
fvco = freq * (1 << m);
if (fvco < 50.0 || fvco > 120.0)
continue;
f0 = fvco / I2061A_CRYSTAL_FREQUENCY;
for (q = 14; q <= 71; q++) { /* q={15..71}:Constraint 2 on page 14 */
p = (int) (f0 * q + 0.5);
if (p < 4 || p > 130) /* p={4..130}:Constraint 5 on page 14 */
continue;
deltax = (double) (p) / (double) (q) - f0;
if (deltax < 0)
deltax = -deltax;
if (deltax <= delta) {
for (i = 13; i >= 0; i--)
if (fvco >= I2061A_range[i])
break;
devx = (fvco - (I2061A_range[i] + I2061A_range[i + 1]) / 2) / fvco;
if (devx < 0)
devx = -devx;
if (deltax < delta || devx < dev) {
delta = deltax;
dev = devx;
bestp = p;
bestq = q;
bestm = m;
besti = i;
}
}
}
}
return ((((((long) besti << 7) | (bestp - 3)) << 3) | bestm) << 7) | (bestq - 2);
}
static int I2061A_GetClock(long dwv)
{
int clock_q = (dwv & 0x7f) + 2;
int clock_m = (dwv >> 7) & 7;
int clock_p = ((dwv >> 10) & 0x7f) + 3;
double fvco;
fvco = I2061A_CRYSTAL_FREQUENCY / (1 << clock_m);
return (int) (((fvco * clock_p) / clock_q) * 1000);
}
/* needs some delay for really fast cpus */
#define wrt_clk_bit(v) outb(MIS_W, v), (void)inb(crtcaddr), (void)inb(crtcaddr)
/* ATTENTION: This assumes CRTC registers and S3 registers to be UNLOCKED! */
static void I2061A_init_clock(unsigned long setup)
{
unsigned char nclk[2], clk[2];
unsigned short restore42;
unsigned short oldclk;
unsigned short bitval;
int i;
unsigned char c;
unsigned short crtcaddr = (inb(MIS_R) & 0x01) ? CRT_IC : CRT_IM;
oldclk = inb(MIS_R);
outb(crtcaddr, 0x42);
restore42 = inb(crtcaddr + 1);
outw(SEQ_I, 0x0100);
outb(SEQ_I, 1);
c = inb(SEQ_D);
outb(SEQ_D, 0x20 | c);
outb(crtcaddr, 0x42);
outb(crtcaddr + 1, 0x03);
outw(SEQ_I, 0x0300);
nclk[0] = oldclk & 0xF3;
nclk[1] = nclk[0] | 0x08;
clk[0] = nclk[0] | 0x04;
clk[1] = nclk[0] | 0x0C;
outb(crtcaddr, 0x42);
(void) inb(crtcaddr + 1);
outw(SEQ_I, 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(SEQ_I, 1);
c = inb(SEQ_D);
outb(SEQ_D, 0xDF & c);
outb(crtcaddr, 0x42);
outb(crtcaddr + 1, restore42);
outb(MIS_W, oldclk);
outw(SEQ_I, 0x0300);
vga_waitretrace();
vga_waitretrace();
vga_waitretrace();
vga_waitretrace();
vga_waitretrace();
vga_waitretrace();
vga_waitretrace(); /* 0.10 second delay... */
}
static int I2061A_match_programmable_clock(int desiredclock)
{
long dvw;
dvw = I2061A_SelectClock((long) desiredclock);
if (dvw)
return I2061A_GetClock(dvw);
return 0;
}
static void I2061A_saveState(unsigned char *regs)
{
long *dvwp;
if (__svgalib_I2061A_clockchip_methods.DAC_saveState)
__svgalib_I2061A_clockchip_methods.DAC_saveState(regs);
dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize);
*dvwp = I2061A_SelectClock(__svgalib_I2061A_clockchip_methods.TextFrequency);
}
static void I2061A_restoreState(const unsigned char *regs)
{
unsigned int clknum = 2;
long *dvwp;
if (__svgalib_I2061A_clockchip_methods.DAC_restoreState)
__svgalib_I2061A_clockchip_methods.DAC_restoreState(regs);
dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize);
if (*dvwp) {
/*
* Write ICD 2061A clock chip - assumes S3 to be unlocked!
*/
I2061A_init_clock(((unsigned long) *dvwp) | (((long) clknum) << 21));
}
}
static void I2061A_initializeState(unsigned char *regs, int bpp, int colormode, int pixelclock)
{
long *dvwp;
if (__svgalib_I2061A_clockchip_methods.DAC_initializeState)
__svgalib_I2061A_clockchip_methods.DAC_initializeState(regs, bpp, colormode, pixelclock);
dvwp = (long *) (regs + __svgalib_I2061A_clockchip_methods.DAC_stateSize);
if (bpp > 16)
pixelclock *= 4;
else if (bpp > 8)
pixelclock *= 2;
*dvwp = I2061A_SelectClock((long) pixelclock);
}
/* This functions patches the DacMethod to route through the ClockChip Method */
static void I2061A_init(CardSpecs * cardspecs, DacMethods * DAC)
{
if (DAC && !__svgalib_I2061A_clockchip_methods.DAC_initializeState) {
if (__svgalib_driver_report)
cprintf("svgalib: Using ICD2061A or compatible clockchip.\n");
__svgalib_I2061A_clockchip_methods.DAC_initializeState = DAC->initializeState;
__svgalib_I2061A_clockchip_methods.DAC_saveState = DAC->saveState;
__svgalib_I2061A_clockchip_methods.DAC_restoreState = DAC->restoreState;
__svgalib_I2061A_clockchip_methods.DAC_stateSize = DAC->stateSize;
DAC->initializeState = I2061A_initializeState;
DAC->saveState = I2061A_saveState;
DAC->restoreState = I2061A_restoreState;
DAC->stateSize += sizeof(long);
cardspecs->matchProgrammableClock = I2061A_match_programmable_clock;
cardspecs->flags |= CLOCK_PROGRAMMABLE;
}
}
ClockChipMethods __svgalib_I2061A_clockchip_methods =
{
I2061A_init,
I2061A_saveState,
I2061A_restoreState,
I2061A_initializeState,
NULL, /* DAC function save area */
NULL,
NULL,
I2061A_DEFAULT_TEXT_FREQUENCY,
0,
};