/*
* s3dacs.c:
*
* RAMDAC definitions for the S3-SDAC (86C716), S3-GENDAC, and Trio64.
*
* These contain S3-specific code.
*/
//#include <stdio.h>
#include "libvga.h"
#include "timing.h"
#include "vgaregs.h"
#include "driver.h" /* for __svgalib_driver_report */
#include "ramdac.h"
/* SDAC/GENDAC registers */
#if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC)
#define SDAC_COMMAND 0 /* Register offsets into state. */
#define GENDAC_COMMAND 0
#define SDAC_PLL_WRITEINDEX 1
#define SDAC_PLL_READINDEX 2
#define SDAC_PLL_M 3 /* f2 programmed clock */
#define SDAC_PLL_N1_N2 4
#define SDAC_PLL_CONTROL 5
#define SDAC_STATESIZE 6 /* 6 registers. */
#define GENDAC_STATESIZE 6
#endif
#if defined(INCLUDE_S3_SDAC_DAC_TEST) || defined(INCLUDE_S3_GENDAC_DAC_TEST)
static int GENDAC_SDAC_probe
(void)
{
/* Taken from XFree86, accel/s3.c. */
/* Return 1 if GENDAC found, 2 if SDAC, 0 otherwise. */
/* probe for S3 GENDAC or SDAC */
/*
* S3 GENDAC and SDAC have two fixed read only PLL clocks
* CLK0 f0: 25.255MHz M-byte 0x28 N-byte 0x61
* CLK0 f1: 28.311MHz M-byte 0x3d N-byte 0x62
* which can be used to detect GENDAC and SDAC since there is no chip-id
* for the GENDAC.
*
* NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values
* for CLK0 f0 and f1 always returns 0x7f (but is documented "read only")
*/
unsigned char saveCR55
, savelut
[6];
int i
;
long clock01
, clock23
;
saveCR55
= __svgalib_inCR
(0x55);
__svgalib_outbCR
(0x55, saveCR55
& ~
1);
outb
(0x3c7, 0);
for (i
= 0; i
< 2 * 3; i
++) /* save first two LUT entries */
savelut
[i
] = inb
(0x3c9);
outb
(0x3c8, 0);
for (i
= 0; i
< 2 * 3; i
++) /* set first two LUT entries to zero */
outb
(0x3c9, 0);
__svgalib_outbCR
(0x55, saveCR55
| 1);
outb
(0x3c7, 0);
for (i
= clock01
= 0; i
< 4; i
++)
clock01
= (clock01
<< 8) | (inb
(0x3c9) & 0xff);
for (i
= clock23
= 0; i
< 4; i
++)
clock23
= (clock23
<< 8) | (inb
(0x3c9) & 0xff);
__svgalib_outbCR
(0x55, saveCR55
& ~
1);
outb
(0x3c8, 0);
for (i
= 0; i
< 2 * 3; i
++) /* restore first two LUT entries */
outb
(0x3c9, savelut
[i
]);
__svgalib_outbCR
(0x55, saveCR55
);
if (clock01
== 0x28613d62 ||
(clock01
== 0x7f7f7f7f && clock23
!= 0x7f7f7f7f)) {
inb
(0x3c8); /* dactopel */
inb
(0x3c6);
inb
(0x3c6);
inb
(0x3c6);
/* the forth read will show the SDAC chip ID and revision */
if (((i
= inb
(0x3c6)) & 0xf0) == 0x70) {
return 2; /* SDAC found. */
} else {
return 1; /* GENDAC found. */
}
inb
(0x3c8); /* dactopel */
}
return 0;
}
#endif
#if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC)
static void GENDAC_SDAC_init
(void)
{
unsigned char val
;
int m
, n
, n1
, n2
, MCLK
;
val
= __svgalib_inCR
(0x55);
__svgalib_outbCR
(0x55, val
| 0x01);
outb
(0x3C7, 10); /* Read MCLK. */
m
= inb
(0x3C9);
n
= inb
(0x3C9);
__svgalib_outbCR
(0x55, val
); /* Restore CR55. */
m
&= 0x7f;
n1
= n
& 0x1f;
n2
= (n
>> 5) & 0x03;
/* Calculate MCLK in kHz. */
MCLK
= 14318 * (m
+ 2) / (n1
+ 2) / (1 << n2
);
if (__svgalib_driver_report
)
printk
(KERN_INFO
"svgalib: S3-GENDAC/SDAC: MCLK = %d.%03d MHz\n",
MCLK
/ 1000, MCLK
% 1000);
}
#endif
#if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) || defined(INCLUDE_S3_TRIO64_DAC)
/*
* From XFree86 common_hw/S3gendac.c and S3gendac.h.
*
* Progaming of the S3 gendac programable clocks, from the S3 Gendac
* programing documentation by S3 Inc.
* Jon Tombs <jon@esix2.us.es>
*
* Returns nonzero if success, 0 if failure.
*/
#define BASE_FREQ 14.31818 /* MHz */
#define DEBUG_FINDCLOCK 0
static int S3dacsFindClock
(int freq_in
, int min_n2
, int freq_min
, int freq_max
,
int *best_m_out
, int *best_n1_out
, int *best_n2_out
)
{
double ffreq_in
, ffreq_min
, ffreq_max
;
double ffreq_out
, diff
, best_diff
;
unsigned int m
;
unsigned char n1
, n2
;
unsigned char best_n1
= 16 + 2, best_n2
= 2, best_m
= 125 + 2;
#if DEBUG_FINDCLOCK
printk
(KERN_INFO
"S3dacsFindClock: Trying to match clock of %0.3f MHz\n", freq_in
/ 1000.0);
#endif
ffreq_in
= freq_in
/ 1000.0 / BASE_FREQ
;
ffreq_min
= freq_min
/ 1000.0 / BASE_FREQ
;
ffreq_max
= freq_max
/ 1000.0 / BASE_FREQ
;
/* Check if getting freq_in is possible at all */
if (freq_in
< freq_min
/ 8) {
#if DEBUG_FINDCLOCK
printk
(KERN_INFO
"S3dacsFindClock: %0.3f MHz is too low (lowest is %0.3f MHz)\n",
freq_in
/ 1000.0, freq_min
/ 1000.0 / 8);
#endif
return 0;
}
if (freq_in
> freq_max
/ (1 << min_n2
)) {
#if DEBUG_FINDCLOCK
printk
(KERN_INFO
"S3dacsFindClock: %0.3f MHz is too high (highest is %0.3f MHz)\n",
freq_in
/ 1000.0, freq_max
/ 1000.0 / (1 << min_n2
));
#endif
return 0;
}
/* work out suitable timings */
best_diff
= ffreq_in
;
for (n2
= min_n2
; n2
<= 3; n2
++) {
for (n1
= 1 + 2; n1
<= 31 + 2; n1
++) {
m
= (int) (ffreq_in
* n1
* (1 << n2
) + 0.5);
if (m
< 1 + 2 || m
> 127 + 2)
continue;
ffreq_out
= (double) (m
) / (double) (n1
);
if ((ffreq_out
>= ffreq_min
) && (ffreq_out
<= ffreq_max
)) {
diff
= ffreq_in
- ffreq_out
/ (1 << n2
);
if (diff
< 0.0)
diff
= -diff
;
if (diff
< best_diff
) {
best_diff
= diff
;
best_m
= m
;
best_n1
= n1
;
best_n2
= n2
;
}
}
}
}
#if DEBUG_FINDCLOCK
printk
(KERN_INFO
"S3dacsFindClock: clock wanted %1.6f MHz, found %1.6f MHz (m %d, n1 %d, n2 %d)\n",
freq_in
/ 1000.0,
best_m
/ ((double) best_n1
* (1 << best_n2
)) * BASE_FREQ
,
best_m
, best_n1
, best_n2
);
#endif
*best_m_out
= best_m
;
*best_n1_out
= best_n1
;
*best_n2_out
= best_n2
;
return 1;
}
#endif
#if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC)
static int GENDAC_SDAC_match_programmable_clock
(int desiredclock
)
{
int min_m
, min_n1
, n2
;
/* Note: For ICS5342, min_n2 parameter should be one. */
if (!S3dacsFindClock
(desiredclock
, 0, 100000, 250000, &min_m
, &min_n1
, &n2
))
return 0;
return ((float) (min_m
) / (float) (min_n1
) / (1 << n2
)) * BASE_FREQ
* 1000;
}
#if 0 /* Retained for reference. */
static void setdacpll
(reg
, data1
, data2
)
int reg
;
unsigned char data1
;
unsigned char data2
;
{
unsigned char tmp
, tmp1
;
int vgaCRIndex
= vgaIOBase
+ 4;
int vgaCRReg
= vgaIOBase
+ 5;
/* set RS2 via CR55, yuck */
tmp
= __svgalib_inCR
(0x55) & 0xFC;
__svgalib_outCR
(tmp
| 0x01);
tmp1
= inb
(GENDAC_INDEX
);
outb
(GENDAC_INDEX
, reg
);
outb
(GENDAC_DATA
, data1
);
outb
(GENDAC_DATA
, data2
);
/* Now clean up our mess */
outb
(GENDAC_INDEX
, tmp1
);
__svgalib_outbCR
(0x55, tmp
);
}
#endif
static void GENDAC_SDAC_initialize_clock_state
(unsigned char *regs
, int freq
)
{
int min_m
, min_n1
, n2
;
int n
, m
;
if (!S3dacsFindClock
(freq
, 0, 100000, 250000, &min_m
, &min_n1
, &n2
)) {
printk
(KERN_INFO
"Bad dot clock %0.3f MHz.\n", freq
/ 1000.0);
return;
}
n
= (min_n1
- 2) | (n2
<< 5);
m
= min_m
- 2;
regs
[SDAC_PLL_M
] = m
;
regs
[SDAC_PLL_N1_N2
] = n
;
if (__svgalib_driver_report
)
printk
(KERN_INFO
"Initializing DAC PLL values; 0x%02X, 0x%02X.\n", m
, n
);
}
static void GENDAC_SDAC_savestate
(unsigned char *regs
)
{
unsigned char tmp
;
tmp
= __svgalib_inCR
(0x55);
__svgalib_outbCR
(0x55, tmp
| 1);
regs
[SDAC_COMMAND
] = inb
(0x3c6);
regs
[SDAC_PLL_WRITEINDEX
] = inb
(0x3c8); /* PLL write index */
regs
[SDAC_PLL_READINDEX
] = inb
(0x3c7); /* PLL read index */
outb
(0x3c7, 2); /* index to f2 reg */
regs
[SDAC_PLL_M
] = inb
(0x3c9); /* f2 PLL M divider */
regs
[SDAC_PLL_N1_N2
] = inb
(0x3c9); /* f2 PLL N1/N2 divider */
outb
(0x3c7, 0x0e); /* index to PLL control */
regs
[SDAC_PLL_CONTROL
] = inb
(0x3c9); /* PLL control */
__svgalib_outbCR
(0x55, tmp
& ~
1);
}
static void GENDAC_SDAC_restorestate
(const unsigned char *regs
)
{
unsigned char tmp
;
/* set RS2 via CR55, yuck */
tmp
= __svgalib_inCR
(0x55) & 0xFC;
__svgalib_outbCR
(0x55, tmp
| 0x01);
#ifdef DEBUG
do {
int m
, n1
, n2
, clk
;
m
= regs
[SDAC_PLL_M
] & 0x7f;
n1
= regs
[SDAC_PLL_N1_N2
] & 0x1f;
n2
= (regs
[SDAC_PLL_N1_N2
] & 0x60) >> 5;
clk
= 14318 * (m
+ 2) / (n1
+ 2) / (1 << n2
);
printk
(KERN_INFO
"SDAC.restorestate, setting clock 0x%02X 0x%02X (%d.%3dMHz)\n",
regs
[SDAC_PLL_M
],
regs
[SDAC_PLL_N1_N2
], clk
/ 1000, clk
% 1000);
} while (0);
#endif
outb
(0x3c6, regs
[SDAC_COMMAND
]);
outb
(0x3c8, 2); /* index to f2 reg */
outb
(0x3c9, regs
[SDAC_PLL_M
]); /* f2 PLL M divider */
outb
(0x3c9, regs
[SDAC_PLL_N1_N2
]); /* f2 PLL N1/N2 divider */
outb
(0x3c8, 0x0e); /* index to PLL control */
outb
(0x3c9, regs
[SDAC_PLL_CONTROL
]); /* PLL control */
outb
(0x3c8, regs
[SDAC_PLL_WRITEINDEX
]); /* PLL write index */
outb
(0x3c7, regs
[SDAC_PLL_READINDEX
]); /* PLL read index */
__svgalib_outbCR
(0x55, tmp
);
}
#endif /* defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) */
/*
* SDAC: 16-bit DAC, 110 MHz raw clock limit.
*
* The 135 MHz version supports pixel multiplexing in 8bpp modes with a
* halved raw clock. (SL: at least mine doesn't.)
*/
#ifdef INCLUDE_S3_SDAC_DAC_TEST
static int SDAC_probe
(void)
{
return GENDAC_SDAC_probe
() == 2;
}
#else
#define SDAC_probe 0
#endif
#ifdef INCLUDE_S3_SDAC_DAC
static int SDAC_map_clock
(int bpp
, int pixelclock
)
{
switch (bpp
) {
case 4:
case 8:
#ifdef SDAC_8BPP_PIXMUX /* SL: AFAIK it doesn't work */
if (pixelclock
>= 67500)
/* Use pixel multiplexing. */
return pixelclock
/ 2;
#endif
break;
case 24:
return pixelclock
* 3 / 2;
case 32:
return pixelclock
* 2;
}
return pixelclock
;
}
static int SDAC_map_horizontal_crtc
(int bpp
, int pixelclock
, int htiming
)
{
switch (bpp
) {
case 16:
return htiming
* 2;
case 24:
return htiming
* 3;
case 32:
return htiming
* 4;
}
return htiming
;
}
static void SDAC_initializestate
(unsigned char *regs
, int bpp
, int colormode
,
int pixelclock
)
{
int pixmux
; /* SDAC command register. */
pixmux
= 0;
switch (colormode
) {
case CLUT8_6
:
#ifdef SDAC_8BPP_PIXMUX
if (pixelclock
>= 67500)
pixmux
= 0x10;
#endif
break;
case RGB16_555
:
pixmux
= 0x30;
break;
case RGB16_565
:
pixmux
= 0x50;
break;
case RGB24_888_B
:
/* Use 0x40 for 3 VCLK/pixel. Change SDAC_map_clock and CR67 as well. */
pixmux
= 0x90;
break;
case RGB32_888_B
:
pixmux
= 0x70;
break;
}
regs
[SDAC_COMMAND
] = pixmux
;
GENDAC_SDAC_initialize_clock_state
(regs
,
SDAC_map_clock
(bpp
, pixelclock
));
}
static void SDAC_qualify_cardspecs
(CardSpecs
* cardspecs
, int dacspeed
)
{
dacspeed
= __svgalib_setDacSpeed
(dacspeed
, 110000); /* most can do 135MHz. */
cardspecs
->maxPixelClock4bpp
= dacspeed
;
cardspecs
->maxPixelClock8bpp
= dacspeed
;
cardspecs
->maxPixelClock16bpp
= dacspeed
;
cardspecs
->maxPixelClock24bpp
= dacspeed
* 2 / 3;
cardspecs
->maxPixelClock32bpp
= dacspeed
/ 2;
cardspecs
->mapClock
= SDAC_map_clock
;
cardspecs
->matchProgrammableClock
= GENDAC_SDAC_match_programmable_clock
;
cardspecs
->mapHorizontalCrtc
= SDAC_map_horizontal_crtc
;
cardspecs
->flags
|= CLOCK_PROGRAMMABLE
;
}
DacMethods __svgalib_S3_SDAC_methods
=
{
S3_SDAC
,
"S3-SDAC (86C716)",
DAC_HAS_PROGRAMMABLE_CLOCKS
,
SDAC_probe
,
GENDAC_SDAC_init
,
SDAC_qualify_cardspecs
,
GENDAC_SDAC_savestate
,
GENDAC_SDAC_restorestate
,
SDAC_initializestate
,
SDAC_STATESIZE
};
#endif
/* S3-GENDAC, 8-bit DAC. */
#ifdef INCLUDE_S3_GENDAC_DAC_TEST
static int GENDAC_probe
(void)
{
return GENDAC_SDAC_probe
() == 1;
}
#else
#define GENDAC_probe 0
#endif
#ifdef INCLUDE_S3_GENDAC_DAC
static int GENDAC_map_clock
(int bpp
, int pixelclock
)
{
if (bpp
== 16)
return pixelclock
* 2;
if (bpp
== 24)
return pixelclock
* 3;
if (bpp
== 32)
return pixelclock
* 4;
return pixelclock
;
}
static int GENDAC_map_horizontal_crtc
(int bpp
, int pixelclock
, int htiming
)
{
/* XXXX Not sure. */
if (bpp
== 24)
return htiming
* 3;
if (bpp
== 16)
return htiming
* 2;
return htiming
;
}
static void GENDAC_initializestate
(unsigned char *regs
, int bpp
, int colormode
,
int pixelclock
)
{
int daccomm
; /* DAC command register. */
daccomm
= 0;
if (colormode
== RGB16_555
)
daccomm
= 0x20;
else if (colormode
== RGB16_565
)
daccomm
= 0x60;
else if (colormode
== RGB24_888_B
)
daccomm
= 0x40;
regs
[GENDAC_COMMAND
] = daccomm
;
GENDAC_SDAC_initialize_clock_state
(regs
,
GENDAC_map_clock
(bpp
, pixelclock
));
}
static void GENDAC_qualify_cardspecs
(CardSpecs
* cardspecs
, int dacspeed
)
{
dacspeed
= __svgalib_setDacSpeed
(dacspeed
, 110000);
cardspecs
->maxPixelClock4bpp
= dacspeed
;
cardspecs
->maxPixelClock8bpp
= dacspeed
;
cardspecs
->maxPixelClock16bpp
= dacspeed
/ 2;
cardspecs
->maxPixelClock24bpp
= dacspeed
/ 3;
cardspecs
->maxPixelClock32bpp
= 0;
cardspecs
->mapClock
= GENDAC_map_clock
;
cardspecs
->matchProgrammableClock
= GENDAC_SDAC_match_programmable_clock
;
cardspecs
->mapHorizontalCrtc
= GENDAC_map_horizontal_crtc
;
cardspecs
->flags
|= CLOCK_PROGRAMMABLE
;
}
DacMethods __svgalib_S3_GENDAC_methods
=
{
S3_GENDAC
,
"S3-GENDAC (86C708)",
DAC_HAS_PROGRAMMABLE_CLOCKS
,
GENDAC_probe
,
GENDAC_SDAC_init
,
GENDAC_qualify_cardspecs
,
GENDAC_SDAC_savestate
,
GENDAC_SDAC_restorestate
,
GENDAC_initializestate
,
GENDAC_STATESIZE
};
#endif
#ifdef INCLUDE_S3_TRIO64_DAC
/* S3-Trio64, 16-bit integrated DAC. */
#define TRIO64_SR15 0
#define TRIO64_SR18 1
#define TRIO64_PLL_N1_N2 2
#define TRIO64_PLL_M 3
#define TRIO64_CR67 4
#define TRIO64_SRB 5
#define TRIO64_STATESIZE 6
/* Note: s3.c also defines CR67, but doesn't use it for the Trio64. */
extern int __svgalib_s3_s3Mclk
;
static int Trio64_get_mclk
(void)
{
unsigned char sr8
;
int m
, n
, n1
, n2
;
outb
(0x3c4, 0x08);
sr8
= inb
(0x3c5);
outb
(0x3c5, 0x06);
outb
(0x3c4, 0x11);
m
= inb
(0x3c5);
outb
(0x3c4, 0x10);
n
= inb
(0x3c5);
outb
(0x3c4, 0x08);
outb
(0x3c5, sr8
);
m
&= 0x7f;
n1
= n
& 0x1f;
n2
= (n
>> 5) & 0x03;
/* Calculate MCLK in kHz. */
return ((1431818 * (m
+ 2)) / (n1
+ 2) / (1 << n2
) + 50) / 100;
}
#if 0
static void Trio64_set_mclk
(int khz
)
/* Doesn't work. Locks computer up. Why? */
{
int sr8
;
int min_m
, min_n1
, n2
;
if (!S3dacsFindClock
(khz
, 0, 40000, 70000, &min_m
, &min_n1
, &n2
)) {
fprintf(stderr
,"Bad MCLK %0.3f MHz.\n", khz
/ 1000.0);
return;
}
fprintf(stderr
,"%0.3f MHz MCLK, m = %d, n = %d, r = %d\n", khz
/ 1000.0, min_m
- 2, min_n1
- 2, n2
);
outb
(0x3C4, 0x08);
sr8
= inb
(0x3C5);
outb
(0x3C5, 0x06); /* Unlock. */
outb
(0x3c4, 0x15);
outb
(0x3c5, inb
(0x3c5) & ~
0x20);
/* MCLK. */
__svgalib_outSR
(0x10, (min_n1
- 2) | (n2
<< 5));
__svgalib_outSR
(0x11, min_m
- 2);
outb
(0x3c4, 0x15);
outb
(0x3c5, inb
(0x3c5) | 0x20);
outb
(0x3c5, inb
(0x3c5) & ~
0x20);
__svgalib_outSR
(0x08, sr8
);
}
#endif
static void Trio64_init
(void)
{
int mclk
;
mclk
= Trio64_get_mclk
();
if (__svgalib_driver_report
)
printk
(KERN_INFO
"svgalib: RAMDAC: Trio64: MCLK = %0.3f MHz\n",
mclk
/ 1000.0);
__svgalib_s3_s3Mclk
= mclk
;
}
static int Trio64_map_clock
(int bpp
, int pixelclock
)
{
if (bpp
== 8 && pixelclock
>= 67500) /* use pixel doubling */
return pixelclock
/ 2;
if (bpp
== 24)
return pixelclock
* 3;
return pixelclock
;
}
static int Trio64_map_horizontal_crtc
(int bpp
, int pixelclock
, int htiming
)
{
if (bpp
== 16)
return htiming
* 2;
/* Normal mapping for 8bpp and 32bpp. */
return htiming
;
}
static void Trio64_initialize_clock_state
(unsigned char *regs
, int freq
)
{
int min_m
, min_n1
, n2
;
int n
, m
;
if (!S3dacsFindClock
(freq
, 0, 130000, 270000, &min_m
, &min_n1
, &n2
)) {
printk
(KERN_INFO
"Bad dot clock %0.3f MHz.\n", freq
/ 1000.0);
return;
}
n
= (min_n1
- 2) | (n2
<< 5);
m
= min_m
- 2;
regs
[TRIO64_PLL_M
] = m
;
regs
[TRIO64_PLL_N1_N2
] = n
;
}
static int Trio64_match_programmable_clock
(int desiredclock
)
{
int min_m
, min_n1
, n2
;
if (!S3dacsFindClock
(desiredclock
, 0, 130000, 270000, &min_m
, &min_n1
, &n2
))
return 0;
return ((float) (min_m
) / (float) (min_n1
) / (1 << n2
)) * BASE_FREQ
* 1000;
}
static void Trio64_initializestate
(unsigned char *regs
, int bpp
, int colormode
,
int pixelclock
)
{
int pixmux
, reserved_CR67_1
;
regs
[TRIO64_SR15
] &= ~
0x50;
regs
[TRIO64_SR18
] &= ~
0x80;
pixmux
= 0;
reserved_CR67_1
= 0;
if (bpp
== 8 && pixelclock
>= 67500) {
pixmux
= 0x10;
reserved_CR67_1
= 2;
regs
[TRIO64_SR15
] |= 0x50;
regs
[TRIO64_SR18
] |= 0x80;
} else if (bpp
== 16) {
/* moderegs[S3_CR33] |= 0x08; *//* done in s3.c. */
if (colormode
== RGB16_555
)
pixmux
= 0x30;
else
pixmux
= 0x50;
reserved_CR67_1
= 2;
} else if (colormode
== RGB24_888_B
) {
/* remember to adjust SRB as well. */
pixmux
= 0x00;
} else if (colormode
== RGB32_888_B
) {
pixmux
= 0xD0; /* 32-bit color, 2 VCLKs/pixel. */
reserved_CR67_1
= 2;
}
regs
[TRIO64_CR67
] = pixmux
| reserved_CR67_1
;
Trio64_initialize_clock_state
(regs
, pixelclock
);
}
static void Trio64_savestate
(unsigned char *regs
)
{
unsigned char sr8
;
outb
(0x3C4, 0x08);
sr8
= inb
(0x3C5);
outb
(0x3C5, 0x06); /* Unlock. */
regs
[TRIO64_SR15
] = __svgalib_inSR
(0x15);
regs
[TRIO64_SR18
] = __svgalib_inSR
(0x18);
regs
[TRIO64_PLL_N1_N2
] = __svgalib_inSR
(0x12);
regs
[TRIO64_PLL_M
] = __svgalib_inSR
(0x13);
regs
[TRIO64_CR67
] = __svgalib_inCR
(0x67);
__svgalib_outSR
(0x08, sr8
);
}
static void Trio64_restorestate
(const unsigned char *regs
)
{
unsigned char sr8
, tmp
;
outb
(0x3C4, 0x08);
sr8
= inb
(0x3C5);
outb
(0x3C5, 0x06); /* Unlock. */
__svgalib_outCR
(0x67, regs
[TRIO64_CR67
]);
__svgalib_outSR
(0x15, regs
[TRIO64_SR15
]);
__svgalib_outSR
(0x18, regs
[TRIO64_SR18
]);
/* Clock. */
__svgalib_outSR
(0x12, regs
[TRIO64_PLL_N1_N2
]);
__svgalib_outSR
(0x13, regs
[TRIO64_PLL_M
]);
#if 0
/*
* XFree86 XF86_S3 (common_hw/gendac.c) has this, but it looks
* incorrect, it should flip the bit by writing to 0x3c5, not
* 0x3c4.
*/
outb
(0x3c4, 0x15);
tmp
= inb
(0x3c5);
outb
(0x3c4, tmp
& ~
0x20);
outb
(0x3c4, tmp
| 0x20);
outb
(0x3c4, tmp
& ~
0x20);
#else
outb
(0x3c4, 0x15);
tmp
= inb
(0x3c5);
outb
(0x3c5, tmp
& ~
0x20);
outb
(0x3c5, tmp
| 0x20);
outb
(0x3c5, tmp
& ~
0x20);
#endif
__svgalib_outSR
(0x08, sr8
);
}
static void Trio64_qualify_cardspecs
(CardSpecs
* cardspecs
, int dacspeed
)
{
if (dacspeed
) {
if (__svgalib_driver_report
)
printk
(KERN_INFO
"svgalib: using 'dacspeed' not recommended for this RAMDAC.\n");
cardspecs
->maxPixelClock4bpp
= dacspeed
;
cardspecs
->maxPixelClock8bpp
= 135000;
cardspecs
->maxPixelClock16bpp
= dacspeed
;
cardspecs
->maxPixelClock24bpp
= 0; /* dacspeed / 3; *//* How to program? */
cardspecs
->maxPixelClock32bpp
= 50000;
} else {
cardspecs
->maxPixelClock4bpp
= 80000;
cardspecs
->maxPixelClock8bpp
= 135000;
cardspecs
->maxPixelClock16bpp
= 80000;
cardspecs
->maxPixelClock24bpp
= 0; /* 25000; *//* How to program? */
cardspecs
->maxPixelClock32bpp
= 50000;
}
cardspecs
->mapClock
= Trio64_map_clock
;
cardspecs
->matchProgrammableClock
= Trio64_match_programmable_clock
;
cardspecs
->mapHorizontalCrtc
= Trio64_map_horizontal_crtc
;
cardspecs
->flags
|= CLOCK_PROGRAMMABLE
;
}
DacMethods __svgalib_Trio64_methods
=
{
TRIO64
,
"S3-Trio64 internal DAC",
DAC_HAS_PROGRAMMABLE_CLOCKS
,
NULL
, /* probe */
Trio64_init
,
Trio64_qualify_cardspecs
,
Trio64_savestate
,
Trio64_restorestate
,
Trio64_initializestate
,
TRIO64_STATESIZE
};
#endif