/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/rendition/vmodes.c,v 1.13 2002/12/11 17:23:33 dawes Exp $ */ /* * file vmodes.c * * Routines that handle mode setting. */ /* * includes */ #include "rendition.h" #include "vmodes.h" #include "vos.h" #include "vramdac.h" #include "v1krisc.h" #include "v1kregs.h" #include "v2kregs.h" #undef DEBUG /* * defines */ #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) /* defines for doubled clock */ #define CLK_DOUBLE 1 #define NO_CLK_DOUBLE 0 /* video FIFO is set to 64 or 128 entries based on req.d bandwidth in bytes/s */ #define FIFOSIZE_THRESH 100000000 /* set thresh to 100MB/s */ /* compute desired video FIFO size given total bandwidth in bytes/s. */ #define FIFOSIZE(vclk, Bpp) (((vclk * Bpp) > FIFOSIZE_THRESH) ? 128 : 64) /* Bt485A RAMDAC control bits */ #define PALETTEDISABLE 0x10 /* Hold memory refresh cycles until video blank */ #define HOLD_MEMREFRESHCYCLE 0x2000 /* memCtl bits [16..23] */ #define DEFAULT_WREFRESH 0x330000 /* Disable memory refresh cycles */ #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) /* addr&7 == 5, 6, 7 are bad */ #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 /* Eh, is this right? */ #define V2_MIN_PCF_FREQ 1 #define V2_MAX_PCF_FREQ 3 /* * global data */ static struct width_to_stride_t { vu32 width8bpp; vu8 stride0; vu8 stride1; vu16 chip; } width_to_stride_table[]={ /* { 0, 0, 0, V1000_DEVICE }, */ { 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 }, /* For Textmode */ { 640, 0x2408 }, /* 31500 MHz */ { 800, 0x1583D }, /* 40000 MHz */ { 1024, 0x14823 }, /* 65000 MHz */ { 1280, 0x14414 }, /* 108000 MHz */ { 0, 0 } }; /* * local function prototypes */ 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); /* * functions */ 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 /* switching to native mode */ verite_out8(iob+MODEREG, NATIVE_MODE|VESA_MODE); /* flipping some bytes */ /* Must be something better to do than this -- FIX */ 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); /* mclk=110 sclk=50 */ /* M/N/P/P = 77/5/2/4 */ } else{ xf86DrvMsg(pScreenInfo->scrnIndex, X_CONFIG, (" *** OVERCLOCKING MEM/CLK mclk=125 sclk=60 ***\n")); /* increase Mem/Sys clock on request */ verite_out32(iob+SCLKPLL, 0xa4854); /* mclk=125 sclk=60 */ /* M/N/P/P = 84/5/2/4 */ } usleep(500); } /* this has something to do with memory */ tmp=verite_in32(iob+DRAMCTL)&0xdfff; /* reset bit 13 */ verite_out32(iob+DRAMCTL, tmp|0x330000); /* program pixel clock */ 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); /* init the ramdac */ 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)); /* fill in the mode parameters */ 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); /* Need to fix up syncs */ /* enable the display */ 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); /* clock */ if (V1000_DEVICE ==pRendition->board.chip) { int width; /* * I have no idea how to read back the clock from * V1000. Therefore we pick a VESA Mode clock from * a list for the width found. */ width = ((reg->crtch & 0xff) + 1) << 3; reg->pclkpll = verite_findtextclock(width); reg->plldev= verite_in8(iob+PLLDEV); } else { reg->pclkpll = verite_in32(iob+PCLKPLL); } /* initdac */ 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); 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) { /* fixme */ set_PLL(iob, reg->pclkpll); xf86UDelay(10000); } else { verite_out32(iob+PCLKPLL,reg->pclkpll); /* * Need to wait 200uS for PLL to stabilize -- * let's play it safe with 500 */ xf86UDelay(10000); /* wait until VBLANK */ while ((verite_in32(iob+CRTCSTATUS)&CRTCSTATUS_VERT_MASK) != CRTCSTATUS_VERT_ACTIVE); while ((verite_in32(iob+CRTCSTATUS)&CRTCSTATUS_VERT_MASK) == CRTCSTATUS_VERT_ACTIVE); } 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 /* CRTCOFFSET */ offset=vwidth*bytespp /* virtual width in bytes */ -swidth*bytespp /* screen width in bytes */ +((swidth*bytespp)%fifo_size) /* width in bytes modulo fifo size */ ; if (!( framebase&7 /* framebase multiple of 8? */ || (swidth*bytespp)%128)) /* screenwidth multiple of fifo size */ offset+=fifo_size; /* increment offset by fifosize */ /* wait for vertical retrace */ #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 /* framebase */ verite_out32(iob+FRAMEBASEA, framebase); /* crtc offset */ 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 /* for now, I implemented a linear search only, should be fixed */ 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; } /* * local functions */ /* * void set_PLL(IOADDRESS iob, vu32 value) * * Set PLL clock to desired frequency for the V1000. */ void set_PLL(IOADDRESS iob, vu32 value) { vu32 ulD; int b; /* shift out the 20 serial bits */ for (b=19; b>=0; b--) { ulD=(value>>b)&1; verite_out8(iob+PLLDEV, (vu8)ulD); } /* read PLL device so the latch is filled with the previously * written value */ (void)verite_in8(iob+PLLDEV); } /* Vxx00CalcClock -- finds PLL parameters to match target * frequency (in megahertz) * * Brute force, but takes less than a tenth * of a second and the function is only called * O(1) times during program execution. */ 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 ((vcoV1_MAX_VCO_FREQ)) continue; pcf = V1_REF_FREQ/nn; if ((pcfV1_MAX_PCF_FREQ)) continue; freq=vco/(1< 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; } /* * end of file vmodes.c */