Rev 54 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* 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
cprintf("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
cprintf("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
cprintf("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
cprintf("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)) {
cprintf("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)
cprintf("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
};