#include "rendition.h"
#include "vmodes.h"
#include "vos.h"
#include "vramdac.h"
#include "v1krisc.h"
#include "v1kregs.h"
#include "v2kregs.h"
#undef DEBUG
#define combineNMP(N, M, P) \
(((vu32)(M-2)<<10) | ((vu32)P<<8) | (vu32)(N-2))
#define v2kcombineNMP(N, M, P) (((vu32)N<<13) | ((vu32)P<<9) | (vu32)M)
#define CLK_DOUBLE 1
#define NO_CLK_DOUBLE 0
#define FIFOSIZE_THRESH 100000000
#define FIFOSIZE(vclk, Bpp) (((vclk * Bpp) > FIFOSIZE_THRESH) ? 128 : 64)
#define PALETTEDISABLE 0x10
#define HOLD_MEMREFRESHCYCLE 0x2000
#define DEFAULT_WREFRESH 0x330000
#define DISABLE_MEMREFRESHCYCLE 0x8000
#define CTL(ldbl, hsynchi, vsynchi) \
(((ldbl) ? CRTCCTL_LINEDOUBLE : 0) \
|((hsynchi) ? CRTCCTL_HSYNCHI : 0) \
|((vsynchi) ? CRTCCTL_VSYNCHI : 0) \
|(CRTCCTL_VSYNCENABLE | CRTCCTL_HSYNCENABLE))
#define HORZ(fp, sy, bp, ac) \
(((((vu32)(((fp)>>3)-1))&7)<<21)|((((vu32)(((sy)>>3)-1))&0x1F)<<16)|((((vu32)(((bp)>>3)-1))&0x3f)<<9)|((((vu32)(((ac)>>3)-1))&0xff)))
#define VERT(fp, sy, bp, ac) \
(((((vu32)(fp-1))&0x3f)<<20)|((((vu32)(sy-1))&0x7)<<17)|((((vu32)(bp-1))&0x3f)<<11)|((((vu32)(ac-1))&0x7ff)))
#define HORZAC(crtchorz) \
(((((vu32)crtchorz)&CRTCHORZ_ACTIVE_MASK)+1)<<3)
#define HORZBP(crtchorz) \
((((((vu32)crtchorz)&CRTCHORZ_BACKPORCH_MASK)>>9)+1)<<3)
#define HORZSY(crtchorz) \
((((((vu32)crtchorz)&CRTCHORZ_SYNC_MASK)>>16)+1)<<3)
#define HORZFP(crtchorz) \
((((((vu32)crtchorz)&CRTCHORZ_FRONTPORCH_MASK)>>21)+1)<<3)
#define VERTAC(crtcvert) \
((((vu32)crtcvert)&CRTCVERT_ACTIVE_MASK)+1)
#define VERTBP(crtcvert) \
(((((vu32)crtcvert)&CRTCVERT_BACKPORCH_MASK)>>11)+1)
#define VERTSY(crtcvert) \
(((((vu32)crtcvert)&CRTCVERT_SYNC_MASK)>>17)+1)
#define VERTFP(crtcvert) \
(((((vu32)crtcvert)&CRTCVERT_FRONTPORCH_MASK)>>20)+1)
#define PCLK(N, M, P) (N),(M),(P)
#define TIMING_MASK (CRTCCTL_VIDEOFIFOSIZE128|CRTCCTL_LINEDOUBLE|\
CRTCCTL_VSYNCHI|CRTCCTL_HSYNCHI|CRTCCTL_VSYNCENABLE|\
CRTCCTL_HSYNCENABLE)
#define BADADDR(x) (((x)&(((x)<<1)|((x)<<2)))&0x4)
#define V1_MIN_VCO_FREQ 25
#define V1_MAX_VCO_FREQ 135
#define V1_REF_FREQ 14.318
#define V1_MIN_PCF_FREQ 0.2
#define V1_MAX_PCF_FREQ 5
#define V2_MIN_VCO_FREQ 125
#define V2_MAX_VCO_FREQ 250
#define V2_REF_FREQ 14.31818
#define V2_MIN_PCF_FREQ 1
#define V2_MAX_PCF_FREQ 3
static struct width_to_stride_t {
vu32 width8bpp;
vu8 stride0;
vu8 stride1;
vu16 chip;
} width_to_stride_table[]={
{ 4, 4, 0, V1000_DEVICE },
{ 16, 0, 1, V1000_DEVICE },
{ 20, 4, 1, V1000_DEVICE },
{ 32, 0, 2, V1000_DEVICE },
{ 36, 4, 2, V1000_DEVICE },
{ 64, 0, 3, V1000_DEVICE },
{ 68, 4, 3, V1000_DEVICE },
{ 128, 0, 4, V1000_DEVICE },
{ 132, 4, 4, V1000_DEVICE },
{ 256, 1, 0, V1000_DEVICE },
{ 272, 1, 1, V1000_DEVICE },
{ 288, 1, 2, V1000_DEVICE },
{ 320, 1, 3, V1000_DEVICE },
{ 384, 1, 4, V1000_DEVICE },
{ 512, 2, 0, V1000_DEVICE },
{ 528, 2, 1, V1000_DEVICE },
{ 544, 2, 2, V1000_DEVICE },
{ 576, 2, 3, V1000_DEVICE },
{ 592, 6, 1, V2000_DEVICE },
{ 608, 6, 2, V2000_DEVICE },
{ 640, 2, 4, V1000_DEVICE },
{ 704, 6, 4, V2000_DEVICE },
{ 768, 5, 0, V2000_DEVICE },
{ 784, 5, 1, V2000_DEVICE },
{ 800, 5, 2, V2000_DEVICE },
{ 832, 5, 3, V2000_DEVICE },
{ 896, 5, 4, V2000_DEVICE },
{ 1024, 3, 0, V1000_DEVICE },
{ 1028, 4, 5, V1000_DEVICE },
{ 1040, 3, 1, V1000_DEVICE },
{ 1056, 3, 2, V1000_DEVICE },
{ 1088, 3, 3, V1000_DEVICE },
{ 1152, 3, 4, V1000_DEVICE },
{ 1168, 7, 1, V2000_DEVICE },
{ 1184, 7, 2, V2000_DEVICE },
{ 1216, 7, 3, V2000_DEVICE },
{ 1280, 1, 5, V1000_DEVICE },
{ 1536, 2, 5, V1000_DEVICE },
{ 1600, 6, 5, V2000_DEVICE },
{ 1792, 5, 5, V2000_DEVICE },
{ 2048, 0, 6, V1000_DEVICE },
{ 2052, 4, 6, V1000_DEVICE },
{ 2176, 7, 5, V2000_DEVICE },
{ 2304, 1, 6, V1000_DEVICE },
{ 2560, 2, 6, V1000_DEVICE },
{ 2624, 6, 6, V2000_DEVICE },
{ 2816, 5, 6, V2000_DEVICE },
{ 3072, 3, 6, V1000_DEVICE },
{ 3200, 7, 6, V2000_DEVICE },
{ 4096, 0, 7, V1000_DEVICE },
{ 4100, 4, 7, V1000_DEVICE },
{ 4352, 1, 7, V1000_DEVICE },
{ 4608, 2, 7, V1000_DEVICE },
{ 4672, 6, 7, V2000_DEVICE },
{ 4864, 5, 7, V2000_DEVICE },
{ 5120, 3, 7, V1000_DEVICE },
{ 5248, 7, 7, V2000_DEVICE },
{ 0, 0, 0, 0 }
};
struct V1000ClocksStr {
int h_size;
int pll_val;
} V1000Clocks [] = {
{ 72, 0x40000 },
{ 640, 0x2408 },
{ 800, 0x1583D },
{ 1024, 0x14823 },
{ 1280, 0x14414 },
{ 0, 0 }
};
static void set_PLL(IOADDRESS iob, vu32 value);
static double V1000CalcClock(double target, int *M, int *N, int *P);
static double V2200CalcClock(double target, int *m, int *n, int *p);
int
verite_setmode(ScrnInfoPtr pScreenInfo, struct verite_modeinfo_t *mode)
{
renditionPtr pRendition = RENDITIONPTR(pScreenInfo);
int tmp;
int doubleclock=0;
int M, N, P;
int iob=pRendition->board.io_base;
#ifdef DEBUG
ErrorF ("Rendition: Debug verite_setmode called\n");
#endif
verite_out8(iob+MODEREG, NATIVE_MODE|VESA_MODE);
switch (mode->bitsperpixel) {
case 32:
verite_out8(iob+MEMENDIAN, MEMENDIAN_NO);
break;
case 16:
verite_out8(iob+MEMENDIAN, MEMENDIAN_HW);
break;
case 8:
verite_out8(iob+MEMENDIAN, MEMENDIAN_END);
break;
}
if (pRendition->board.chip != V1000_DEVICE) {
if(!pRendition->board.overclock_mem){
verite_out32(iob+SCLKPLL, 0xa484d);
}
else{
xf86DrvMsg(pScreenInfo->scrnIndex, X_CONFIG,
(" *** OVERCLOCKING MEM/CLK mclk=125 sclk=60 ***\n"));
verite_out32(iob+SCLKPLL, 0xa4854);
}
usleep(500);
}
tmp=verite_in32(iob+DRAMCTL)&0xdfff;
verite_out32(iob+DRAMCTL, tmp|0x330000);
if (pRendition->board.chip == V1000_DEVICE) {
if (110.0 < V1000CalcClock(mode->clock/1000.0, &M, &N, &P)) {
P++;
doubleclock=1;
}
set_PLL(iob, combineNMP(N, M, P));
}
else {
tmp = (~0x1800) & verite_in32(iob+DRAMCTL);
verite_out32(iob+DRAMCTL, tmp);
V2200CalcClock(mode->clock/1000.0, &M, &N, &P);
verite_out32(iob+PCLKPLL, v2kcombineNMP(N, M, P));
}
usleep(500);
verite_initdac(pScreenInfo, mode->bitsperpixel, doubleclock);
verite_out32(iob+CRTCHORZ, HORZ(mode->hsyncstart - mode->hdisplay,
mode->hsyncend - mode->hsyncstart,
mode->htotal - mode->hsyncend,
mode->hdisplay));
verite_out32(iob+CRTCVERT, VERT(mode->vsyncstart-mode->vdisplay,
mode->vsyncend-mode->vsyncstart,
mode->vtotal-mode->vsyncend,
mode->vdisplay));
memcpy(&(pRendition->board.mode), mode, sizeof(struct verite_modeinfo_t));
pRendition->board.mode.fifosize=128;
pRendition->board.mode.pll_m=M;
pRendition->board.mode.pll_n=N;
pRendition->board.mode.pll_p=P;
pRendition->board.mode.doubleclock=doubleclock;
if (0 == pRendition->board.mode.virtualwidth)
pRendition->board.mode.virtualwidth=pRendition->board.mode.screenwidth;
pRendition->board.init=1;
(*pScreenInfo->AdjustFrame)(pScreenInfo->scrnIndex,
pScreenInfo->frameX0, pScreenInfo->frameY0, 0);
verite_out32(iob+CRTCCTL, CTL(0, mode->hsynchi, mode->vsynchi)
|mode->pixelformat
|CRTCCTL_VIDEOFIFOSIZE128
|CRTCCTL_HSYNCENABLE
|CRTCCTL_VSYNCENABLE
|CRTCCTL_VIDEOENABLE);
#ifdef DEBUG
ErrorF ("Interlace mode -> %d\n", mode->flags);
xf86sleep(10);ErrorF ("...Exit SetMode...\n");
#endif
return 0;
}
static double
verite_findtextclock(int width)
{
int i = 0, val;
while ((val = V1000Clocks[i].h_size) > 0) {
if (val > width)
break;
else
i++;
}
if (i > 0) i--;
return V1000Clocks[i].pll_val;
}
void
verite_save(ScrnInfoPtr pScreenInfo)
{
renditionPtr pRendition = RENDITIONPTR(pScreenInfo);
int iob=pRendition->board.io_base;
RenditionRegPtr reg = &pRendition->saveRegs;
reg->memendian = verite_in8(iob+MEMENDIAN);
reg->mode = verite_in8(iob+MODEREG);
reg->sclkpll = verite_in32(iob+SCLKPLL);
reg->dramctl = verite_in32(iob+DRAMCTL);
reg->crtch = verite_in32(iob+CRTCHORZ);
reg->crtcv = verite_in32(iob+CRTCVERT);
if (V1000_DEVICE ==pRendition->board.chip) {
int width;
width = ((reg->crtch & 0xff) + 1) << 3;
reg->pclkpll = verite_findtextclock(width);
reg->plldev= verite_in8(iob+PLLDEV);
} else {
reg->pclkpll = verite_in32(iob+PCLKPLL);
}
verite_out8(iob+MODEREG,NATIVE_MODE);
verite_savedac(pScreenInfo);
verite_out8(iob+MODEREG,reg->mode);
reg->vbasea = verite_in32(iob+FRAMEBASEA);
reg->crtcoff = verite_in32(iob+CRTCOFFSET);
reg->crtcctl = verite_in32(iob+CRTCCTL);
}
void
verite_restore(ScrnInfoPtr pScreenInfo, RenditionRegPtr reg)
{
renditionPtr pRendition = RENDITIONPTR(pScreenInfo);
int iob=pRendition->board.io_base;
verite_restoredac (pScreenInfo, reg);
if (pRendition->board.chip == V1000_DEVICE) {
verite_out32(iob+MODEREG,reg->mode);
}
verite_out8(iob+MEMENDIAN,reg->memendian);
verite_out32(iob+DRAMCTL,reg->dramctl);
verite_out32(iob+SCLKPLL,reg->sclkpll);
if (pRendition->board.chip == V1000_DEVICE) {
set_PLL(iob, reg->pclkpll);
xf86UDelay(10000);
} else {
verite_out32(iob+PCLKPLL,reg->pclkpll);
xf86UDelay(10000);
while ((verite_in32(iob+CRTCSTATUS)&CRTCSTATUS_VERT_MASK) !=
CRTCSTATUS_VERT_ACTIVE);
while ((verite_in32(iob+CRTCSTATUS)&CRTCSTATUS_VERT_MASK) ==
CRTCSTATUS_VERT_ACTIVE);
}
if (pRendition->board.chip != V1000_DEVICE) {
verite_out32(iob+MODEREG,reg->mode);
}
verite_out32(iob+CRTCHORZ,reg->crtch);
verite_out32(iob+CRTCVERT,reg->crtcv);
verite_out32(iob+FRAMEBASEA, reg->vbasea);
verite_out32(iob+CRTCOFFSET,reg->crtcoff);
verite_out32(iob+CRTCCTL,reg->crtcctl);
}
void
verite_setframebase(ScrnInfoPtr pScreenInfo, vu32 framebase)
{
renditionPtr pRendition = RENDITIONPTR(pScreenInfo);
vu32 offset;
int iob=pRendition->board.io_base;
int swidth=pRendition->board.mode.screenwidth;
int vwidth= pRendition->board.mode.virtualwidth;
int bytespp=pRendition->board.mode.bitsperpixel>>3;
int fifo_size=pRendition->board.mode.fifosize;
#ifdef DEBUG
ErrorF( "Rendition: Debug verite_setframebase w=%d v=%d b=%d f=%d\n",
swidth, vwidth, bytespp, fifo_size);
#endif
offset=vwidth*bytespp
-swidth*bytespp
+((swidth*bytespp)%fifo_size)
;
if (!( framebase&7
|| (swidth*bytespp)%128))
offset+=fifo_size;
#ifndef DEBUG
if (!pRendition->board.init) {
while ((verite_in32(iob+CRTCSTATUS) & CRTCSTATUS_VERT_MASK) !=
CRTCSTATUS_VERT_ACTIVE) ;
while ((verite_in32(iob+CRTCSTATUS) & CRTCSTATUS_VERT_MASK) ==
CRTCSTATUS_VERT_ACTIVE) ;
}
else
pRendition->board.init=0;
#endif
verite_out32(iob+FRAMEBASEA, framebase);
verite_out32(iob+CRTCOFFSET, offset&0xffff);
}
int
verite_getstride(ScrnInfoPtr pScreenInfo, int *width,
vu16 *stride0, vu16 *stride1)
{
renditionPtr pRendition = RENDITIONPTR(pScreenInfo);
int bytesperline;
int c = 0;
bytesperline
= pRendition->board.mode.virtualwidth
* (pRendition->board.mode.bitsperpixel >> 3);
#ifdef DEBUG
ErrorF("RENDITION: %d bytes per line\n", bytesperline);
#endif
while (0 != width_to_stride_table[c].width8bpp) {
if (width_to_stride_table[c].width8bpp == bytesperline
&& ((width_to_stride_table[c].chip == pRendition->board.chip)
|| (V2000_DEVICE == pRendition->board.chip))) {
*stride0 = width_to_stride_table[c].stride0;
*stride1 = width_to_stride_table[c].stride1;
return 1;
}
c++;
}
return 0;
}
void
set_PLL(IOADDRESS iob, vu32 value)
{
vu32 ulD;
int b;
for (b=19; b>=0; b--) {
ulD=(value>>b)&1;
verite_out8(iob+PLLDEV, (vu8)ulD);
}
(void)verite_in8(iob+PLLDEV);
}
static double
V1000CalcClock(double target, int *M, int *N, int *P)
{
double mindiff = 1e10;
double vco, pcf, diff, freq;
int mm, nn, pp;
for (pp=0; pp<4; pp++)
for (nn=1; nn<=129; nn++)
for (mm=1; mm<=129; mm++) {
vco=V1_REF_FREQ*2.0*mm/nn;
if ((vco<V1_MIN_VCO_FREQ) || (vco>V1_MAX_VCO_FREQ))
continue;
pcf = V1_REF_FREQ/nn;
if ((pcf<V1_MIN_PCF_FREQ) || (pcf>V1_MAX_PCF_FREQ))
continue;
freq=vco/(1<<pp);
diff=fabs(target-freq);
if (diff < mindiff) {
*M=mm;
*N=nn;
*P=pp;
mindiff=diff;
}
}
vco=V1_REF_FREQ*2*(*M)/(*N);
pcf=V1_REF_FREQ/(*N);
freq=vco/(1<<(*P));
#ifdef DEBUG
ErrorF(
"RENDITION: target=%f freq=%f vco=%f pcf=%f n=%d m=%d p=%d\n",
target, freq, vco, pcf, *N, *M, *P);
#endif
return freq;
}
static double
V2200CalcClock(double target, int *m, int *n, int *p)
{
double mindiff = 1e10;
double vco, pcf, diff, freq;
int mm, nn, pp;
for (pp=1; pp<=0x0f; pp++)
for (nn=1; nn<=0x3f; nn++)
for (mm=1; mm<=0xff; mm++) {
vco = V2_REF_FREQ*mm/nn;
if ((vco < V2_MIN_VCO_FREQ) || (vco > V2_MAX_VCO_FREQ))
continue;
pcf = V2_REF_FREQ*mm/nn;
if ((vco < V2_MIN_VCO_FREQ) || (vco > V2_MAX_VCO_FREQ))
continue;
pcf = V2_REF_FREQ/nn;
if ((pcf < V2_MIN_PCF_FREQ) || (pcf > V2_MAX_PCF_FREQ))
continue;
freq = vco/pp;
diff = fabs(target-freq);
if (diff < mindiff) {
*m = mm; *n = nn; *p = pp;
mindiff = diff;
}
}
vco = V2_REF_FREQ * *m / *n;
pcf = V2_REF_FREQ / *n;
freq = vco / *p;
#ifdef DEBUG
ErrorF(
"RENDITION: target=%f freq=%f vco=%f pcf=%f n=%d m=%d p=%d\n",
target, freq, vco, pcf, *n, *m, *p);
#endif
return freq;
}