/*
* 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
)
fprintf(stderr
,"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,
};