#include "tseng.h"
static Bool Tseng_ET4000ClockSelect(ScrnInfoPtr pScrn, int no);
static Bool Tseng_LegendClockSelect(ScrnInfoPtr pScrn, int no);
static SymTabRec TsengClockChips[] =
{
{CLOCKCHIP_ICD2061A, "icd2061a"},
{CLOCKCHIP_ET6000, "et6000"},
{CLOCKCHIP_ICS5341, "ics5341"},
{CLOCKCHIP_ICS5301, "ics5301"},
{CLOCKCHIP_CH8398, "ch8398"},
{CLOCKCHIP_STG1703, "stg1703"},
{-1, NULL}
};
Bool
Tseng_check_clockchip(ScrnInfoPtr pScrn)
{
MessageType from;
TsengPtr pTseng = TsengPTR(pScrn);
PDEBUG(" Tseng_check_clockchip\n");
if (pTseng->pEnt->device->clockchip && *pTseng->pEnt->device->clockchip) {
pScrn->clockchip = pTseng->pEnt->device->clockchip;
pTseng->ClockChip =
(t_clockchip_type)xf86StringToToken(TsengClockChips, pScrn->clockchip);
if (pTseng->ClockChip == CLOCKCHIP_DEFAULT) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unknown clockchip: \"%s\"\n",
pScrn->clockchip);
return FALSE;
}
from = X_CONFIG;
} else {
pScrn->clockchip = (char *)xf86TokenToString(TsengClockChips, pTseng->ClockChip);
from = X_PROBED;
}
xf86DrvMsg(pScrn->scrnIndex, from, "Clockchip: \"%s\"\n",
pScrn->clockchip);
return TRUE;
}
void tseng_clock_setup(ScrnInfoPtr pScrn)
{
TsengPtr pTseng = TsengPTR(pScrn);
int iobase = VGAHW_GET_IOBASE();
MessageType from;
int dacspeed, mem_bw;
Bool forceSpeed = FALSE;
int i;
PDEBUG(" tseng_clock_setup\n");
pTseng->MinClock = 12000;
if (pTseng->pEnt->device->dacSpeeds[0]) {
from = X_CONFIG;
forceSpeed = TRUE;
switch (pScrn->bitsPerPixel) {
default:
case 1:
case 4:
case 8:
dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP8];
break;
case 16:
dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP16];
break;
case 24:
dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP24];
break;
case 32:
dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP32];
break;
}
pTseng->max_vco_freq = pTseng->pEnt->device->dacSpeeds[0]*2+1;
if (dacspeed == 0)
dacspeed = pTseng->pEnt->device->dacSpeeds[0];
} else {
from = X_PROBED;
forceSpeed = FALSE;
dacspeed = MAX_TSENG_CLOCK;
if (Is_ET6100) {
dacspeed = 175000;
mem_bw = 280000;
} else if (Is_ET6000) {
dacspeed = 135000;
mem_bw = 225000;
} else {
if ( (pTseng->DacInfo.DacPort16) &&
(pScrn->bitsPerPixel == 8) &&
(!(DAC_is_GenDAC && pTseng->NoClockchip)) ) {
dacspeed = 135000;
}
mem_bw = 90000;
if (pScrn->videoRam > 1024)
mem_bw = 150000;
}
pTseng->max_vco_freq = dacspeed*2+1;
dacspeed = min(dacspeed, (mem_bw / pTseng->Bytesperpixel));
}
pTseng->clockRange[0] = xnfcalloc(sizeof(ClockRange), 1);
pTseng->clockRange[0]->next = NULL;
pTseng->clockRange[0]->minClock = pTseng->MinClock;
pTseng->clockRange[0]->maxClock = dacspeed;
pTseng->clockRange[0]->clockIndex = -1;
pTseng->clockRange[0]->interlaceAllowed = TRUE;
pTseng->clockRange[0]->doubleScanAllowed = TRUE;
pTseng->clockRange[0]->ClockMulFactor = 1;
pTseng->clockRange[0]->ClockDivFactor = 1;
pTseng->clockRange[0]->PrivFlags = TSENG_MODE_NORMAL;
if ( (pTseng->DacInfo.DacPort16) &&
(pScrn->bitsPerPixel == 8) &&
(!(DAC_is_GenDAC && pTseng->NoClockchip)) ) {
pTseng->clockRange[0]->maxClock = MAX_TSENG_CLOCK;
pTseng->clockRange[1] = xnfcalloc(sizeof(ClockRange), 1);
pTseng->clockRange[0]->next = pTseng->clockRange[1];
pTseng->clockRange[1]->next = NULL;
pTseng->clockRange[1]->minClock = 75000;
pTseng->clockRange[1]->maxClock = dacspeed;
pTseng->clockRange[1]->clockIndex = -1;
pTseng->clockRange[1]->interlaceAllowed = TRUE;
pTseng->clockRange[1]->doubleScanAllowed = TRUE;
pTseng->clockRange[1]->ClockMulFactor = 1;
pTseng->clockRange[1]->ClockDivFactor = 2;
pTseng->clockRange[1]->PrivFlags = TSENG_MODE_PIXMUX;
}
if ((pTseng->Bytesperpixel > 1) && (!Is_ET6K)) {
pTseng->clockRange[0]->maxClock = (forceSpeed) ? dacspeed :
min(MAX_TSENG_CLOCK / pTseng->Bytesperpixel, dacspeed);
pTseng->clockRange[0]->ClockMulFactor = pTseng->Bytesperpixel;
pTseng->clockRange[0]->ClockDivFactor = 1;
if (pTseng->DacInfo.DacPort16) {
pTseng->clockRange[1] = xnfcalloc(sizeof(ClockRange), 1);
pTseng->clockRange[0]->next = pTseng->clockRange[1];
pTseng->clockRange[1]->next = NULL;
pTseng->clockRange[1]->minClock = pTseng->MinClock;
pTseng->clockRange[1]->maxClock = (forceSpeed) ? dacspeed :
min((MAX_TSENG_CLOCK * 2) / pTseng->Bytesperpixel, dacspeed);
pTseng->clockRange[1]->clockIndex = -1;
pTseng->clockRange[1]->interlaceAllowed = TRUE;
pTseng->clockRange[1]->doubleScanAllowed = TRUE;
pTseng->clockRange[1]->ClockMulFactor = pTseng->Bytesperpixel;
pTseng->clockRange[1]->ClockDivFactor = 2;
pTseng->clockRange[1]->PrivFlags = TSENG_MODE_DACBUS16;
}
}
if (pTseng->clockRange[1])
pTseng->MaxClock = pTseng->clockRange[1]->maxClock;
else
pTseng->MaxClock = pTseng->clockRange[0]->maxClock;
xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "Min pixel clock is %d MHz\n",
pTseng->MinClock / 1000);
xf86DrvMsg(pScrn->scrnIndex, from, "Max pixel clock is %d MHz\n",
pTseng->MaxClock / 1000);
pTseng->MClkInfo.Set = FALSE;
if (pTseng->MClkInfo.Programmable) {
from = X_PROBED;
if (pTseng->MemClk > 0) {
if ((pTseng->MemClk < pTseng->MClkInfo.min)
|| (pTseng->MemClk > pTseng->MClkInfo.max)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"MCLK %d MHz out of range (=%d..%d); not changed!\n",
pTseng->MemClk / 1000,
pTseng->MClkInfo.min / 1000,
pTseng->MClkInfo.max / 1000);
} else {
pTseng->MClkInfo.MemClk = pTseng->MemClk;
pTseng->MClkInfo.Set = TRUE;
from = X_CONFIG;
}
}
xf86DrvMsg(pScrn->scrnIndex, from, "MCLK used is %d MHz\n",
pTseng->MClkInfo.MemClk / 1000);
}
if (!pScrn->progClock) {
int NoClocks;
Bool (*TsengClockSelect)(ScrnInfoPtr, int);
if (pTseng->Legend) {
TsengClockSelect = Tseng_LegendClockSelect;
NoClocks = 32;
} else {
TsengClockSelect = Tseng_ET4000ClockSelect;
if ( (!Is_stdET4K)
&& (!DAC_is_GenDAC) && (pTseng->DacInfo.DacType != CH8398_DAC) )
NoClocks = 32;
else
NoClocks = 16;
}
if (!pTseng->pEnt->device->numclocks) {
pScrn->numClocks = NoClocks;
xf86GetClocks(pScrn, NoClocks, TsengClockSelect,
TsengProtect, TsengBlankScreen,
iobase + 0x0A, 0x08, 1, 28322);
from = X_PROBED;
} else {
pScrn->numClocks = pTseng->pEnt->device->numclocks;
if (pScrn->numClocks > NoClocks) {
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
"Too many Clocks specified in configuration file.\n");
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
"\t\tAt most %d clocks may be specified\n", NoClocks);
pScrn->numClocks= NoClocks;
}
for (i = 0; i < pScrn->numClocks; i++)
pScrn->clock[i] = pTseng->pEnt->device->clock[i];
from = X_CONFIG;
}
#ifdef FIXME
for (i = 0; i < pScrn->numClocks; i++) {
pScrn->clock[i] *= pTseng->DacInfo.ClockDivFactor;
pScrn->clock[i] /= pTseng->DacInfo.ClockMulFactor;
}
#endif
xf86ShowClocks(pScrn, from);
}
}
static Bool
Tseng_ET4000ClockSelect(ScrnInfoPtr pScrn, int no)
{
TsengPtr pTseng = TsengPTR(pScrn);
unsigned char temp;
int iobase = VGAHW_GET_IOBASE();
switch (no) {
case CLK_REG_SAVE:
pTseng->save_clock.save1 = inb(0x3CC);
outb(iobase + 4, 0x34);
pTseng->save_clock.save2 = inb(iobase + 5);
outb(0x3C4, 7);
pTseng->save_clock.save3 = inb(0x3C5);
if (!Is_stdET4K) {
outb(iobase + 4, 0x31);
pTseng->save_clock.save4 = inb(iobase + 5);
}
break;
case CLK_REG_RESTORE:
outb(0x3C2, pTseng->save_clock.save1);
outw(iobase + 4, 0x34 | (pTseng->save_clock.save2 << 8));
outw(0x3C4, 7 | (pTseng->save_clock.save3 << 8));
if (!Is_stdET4K) {
outw(iobase + 4, 0x31 | (pTseng->save_clock.save4 << 8));
}
break;
default:
temp = inb(0x3CC);
outb(0x3C2, (temp & 0xf3) | ((no << 2) & 0x0C));
outb(iobase + 4, 0x34);
temp = inb(iobase + 5);
outw(iobase + 4, 0x34 | ((temp & 0xFD) << 8) | ((no & 0x04) << 7));
outb(0x3C4, 7);
temp = inb(0x3C5);
outb(0x3C5, (pTseng->save_divide ^ ((no & 0x8) << 3)) | (temp & 0xBF));
outb(iobase + 4, 0x31);
temp = inb(iobase + 5);
outb(iobase + 5, (temp & 0x3f) | ((no & 0x10) << 2));
}
return (TRUE);
}
static Bool
Tseng_LegendClockSelect(ScrnInfoPtr pScrn, int no)
{
TsengPtr pTseng = TsengPTR(pScrn);
unsigned char temp;
int iobase = VGAHW_GET_IOBASE();
switch (no) {
case CLK_REG_SAVE:
pTseng->save_clock.save1 = inb(0x3CC);
outb(iobase + 4, 0x34);
pTseng->save_clock.save2 = inb(iobase + 5);
break;
case CLK_REG_RESTORE:
outb(0x3C2, pTseng->save_clock.save1);
outw(iobase + 4, 0x34 | (pTseng->save_clock.save2 << 8));
break;
default:
temp = inb(0x3CC);
outb(0x3C2, (temp & 0xF3) | ((no & 0x10) >> 1) | (no & 0x04));
outw(iobase + 4, 0x0034);
outw(iobase + 4, 0x0234);
outw(iobase + 4, ((no & 0x08) << 6) | 0x34);
outb(0x3C2, (temp & 0xF3) | ((no << 2) & 0x0C));
}
return (TRUE);
}
#define BASE_FREQ 14.31818
void
TsengcommonCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2, int max_n2,
long freq_min, long freq_max,
unsigned char *mdiv, unsigned char *ndiv)
{
double ffreq, ffreq_min, ffreq_max;
double div, diff, best_diff;
unsigned int m;
unsigned char n1, n2;
unsigned char best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2;
PDEBUG(" commonCalcClock\n");
ffreq = freq / 1000.0 / BASE_FREQ;
ffreq_min = freq_min / 1000.0 / BASE_FREQ;
ffreq_max = freq_max / 1000.0 / BASE_FREQ;
if (ffreq < ffreq_min / (1 << max_n2)) {
ErrorF("invalid frequency %1.3f MHz [freq >= %1.3f MHz]\n",
ffreq * BASE_FREQ, ffreq_min * BASE_FREQ / (1 << max_n2));
ffreq = ffreq_min / (1 << max_n2);
}
if (ffreq > ffreq_max / (1 << min_n2)) {
ErrorF("invalid frequency %1.3f MHz [freq <= %1.3f MHz]\n",
ffreq * BASE_FREQ, ffreq_max * BASE_FREQ / (1 << min_n2));
ffreq = ffreq_max / (1 << min_n2);
}
best_diff = ffreq;
for (n2 = min_n2; n2 <= max_n2; n2++) {
for (n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) {
m = (int)(ffreq * n1 * (1 << n2) + 0.5);
if (m < min_m + 2 || m > 127 + 2)
continue;
div = (double)(m) / (double)(n1);
if ((div >= ffreq_min) &&
(div <= ffreq_max)) {
diff = ffreq - div / (1 << n2);
if (diff < 0.0)
diff = -diff;
if (diff < best_diff) {
best_diff = diff;
best_m = m;
best_n1 = n1;
best_n2 = n2;
}
}
}
}
#ifdef EXTENDED_DEBUG
ErrorF("Clock parameters for %1.6f MHz: m=%d, n1=%d, n2=%d\n",
((double)(best_m) / (double)(best_n1) / (1 << best_n2)) * BASE_FREQ,
best_m - 2, best_n1 - 2, best_n2);
#endif
if (max_n1 == 63)
*ndiv = (best_n1 - 2) | (best_n2 << 6);
else
*ndiv = (best_n1 - 2) | (best_n2 << 5);
*mdiv = best_m - 2;
}