/*
* ics_gendac.c:
*
* This works only with ARK, since it has ARK 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 */
#define SDAC_COMMAND 0
#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 GENDAC_STATESIZE 6
static void GENDAC_init
(void)
{
}
/*
* 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
fprintf(stderr
,"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
fprintf(stderr
,"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
fprintf(stderr
,"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
fprintf(stderr
,"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;
}
static int GENDAC_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;
}
static void GENDAC_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
)) {
fprintf(stderr
,"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 0
if (__svgalib_driver_report
)
fprintf(stderr
,"Initializing DAC PLL values; 0x%02X, 0x%02X.\n", m
, n
);
#endif
}
static void GENDAC_savestate
(unsigned char *regs
)
{
unsigned char tmp
;
tmp
= __svgalib_inSR
(0x1c);
__svgalib_outSR
(0x1c, tmp
| 0x80);
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_outSR
(0x1c, tmp
& ~
0x80);
}
static void GENDAC_restorestate
(const unsigned char *regs
)
{
unsigned char tmp
;
tmp
= __svgalib_inseq
(0x1c);
__svgalib_outseq
(0x1c, tmp
| 0x80);
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_outseq
(0x1c, tmp
);
}
/*
* 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.)
*/
static int GENDAC_probe
(void)
{
int i
;
inb
(0x3c6);
inb
(0x3c6);
inb
(0x3c6);
i
=inb
(0x3c6);
if(i
==177) return 1;
return 0;
}
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_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_match_programmable_clock
;
cardspecs
->mapHorizontalCrtc
= GENDAC_map_horizontal_crtc
;
cardspecs
->flags
|= CLOCK_PROGRAMMABLE
;
}
DacMethods __svgalib_ICS_GENDAC_methods
=
{
S3_GENDAC
,
"ICS-GENDAC (5342)",
DAC_HAS_PROGRAMMABLE_CLOCKS
,
GENDAC_probe
,
GENDAC_init
,
GENDAC_qualify_cardspecs
,
GENDAC_savestate
,
GENDAC_restorestate
,
GENDAC_initializestate
,
GENDAC_STATESIZE
};