/* * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of Marc Aurele La France not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Marc Aurele La France makes no representations * about the suitability of this software for any purpose. It is provided * "as-is" without express or implied warranty. * * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "ati.h" #include "atichip.h" #include "atidsp.h" #include "atimach64io.h" #include "atividmem.h" /* * ATIDSPPreInit -- * * This function initialises global variables used to set DSP registers on a * VT-B or later. */ Bool ATIDSPPreInit ( int iScreen, ATIPtr pATI ) { CARD32 IOValue, dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off; int trp; /* * VT-B's and later have additional post-dividers that are not powers of * two. */ pATI->ClockDescriptor.NumD = 8; /* Retrieve XCLK settings */ IOValue = ATIMach64GetPLLReg(PLL_XCLK_CNTL); pATI->XCLKPostDivider = GetBits(IOValue, PLL_XCLK_SRC_SEL); pATI->XCLKReferenceDivider = 1; switch (pATI->XCLKPostDivider) { case 0: case 1: case 2: case 3: break; case 4: pATI->XCLKReferenceDivider = 3; pATI->XCLKPostDivider = 0; break; default: xf86DrvMsg(iScreen, X_ERROR, "Unsupported XCLK source: %d.\n", pATI->XCLKPostDivider); return FALSE; } pATI->XCLKPostDivider -= GetBits(IOValue, PLL_MFB_TIMES_4_2B); pATI->XCLKFeedbackDivider = ATIMach64GetPLLReg(PLL_MCLK_FB_DIV); xf86DrvMsgVerb(iScreen, X_INFO, 2, "Engine XCLK %.3f MHz; Refresh rate code %ld.\n", ATIDivide(pATI->XCLKFeedbackDivider * pATI->ReferenceNumerator, pATI->XCLKReferenceDivider * pATI->ClockDescriptor.MaxM * pATI->ReferenceDenominator, 1 - pATI->XCLKPostDivider, 0) / (double)1000.0, GetBits(pATI->LockData.mem_cntl, CTL_MEM_REFRESH_RATE_B)); /* Compute maximum RAS delay and friends */ trp = GetBits(pATI->LockData.mem_cntl, CTL_MEM_TRP); pATI->XCLKPageFaultDelay = GetBits(pATI->LockData.mem_cntl, CTL_MEM_TRCD) + GetBits(pATI->LockData.mem_cntl, CTL_MEM_TCRD) + trp + 2; pATI->XCLKMaxRASDelay = GetBits(pATI->LockData.mem_cntl, CTL_MEM_TRAS) + trp + 2; pATI->DisplayFIFODepth = 32; if (pATI->Chip < ATI_CHIP_264VT4) { pATI->XCLKPageFaultDelay += 2; pATI->XCLKMaxRASDelay += 3; pATI->DisplayFIFODepth = 24; } switch (pATI->MemoryType) { case MEM_264_DRAM: if (pATI->VideoRAM <= 1024) { pATI->DisplayLoopLatency = 10; } else { pATI->DisplayLoopLatency = 8; pATI->XCLKPageFaultDelay += 2; } break; case MEM_264_EDO: case MEM_264_PSEUDO_EDO: if (pATI->VideoRAM <= 1024) { pATI->DisplayLoopLatency = 9; } else { pATI->DisplayLoopLatency = 8; pATI->XCLKPageFaultDelay++; } break; case MEM_264_SDRAM: if (pATI->VideoRAM <= 1024) { pATI->DisplayLoopLatency = 11; } else { pATI->DisplayLoopLatency = 10; pATI->XCLKPageFaultDelay++; } break; case MEM_264_SGRAM: pATI->DisplayLoopLatency = 8; pATI->XCLKPageFaultDelay += 3; break; default: /* Set maximums */ pATI->DisplayLoopLatency = 11; pATI->XCLKPageFaultDelay += 3; break; } if (pATI->XCLKMaxRASDelay <= pATI->XCLKPageFaultDelay) pATI->XCLKMaxRASDelay = pATI->XCLKPageFaultDelay + 1; /* Allow BIOS to override */ dsp_config = inr(DSP_CONFIG); dsp_on_off = inr(DSP_ON_OFF); vga_dsp_config = inr(VGA_DSP_CONFIG); vga_dsp_on_off = inr(VGA_DSP_ON_OFF); if (dsp_config) pATI->DisplayLoopLatency = GetBits(dsp_config, DSP_LOOP_LATENCY); if ((!dsp_on_off && (pATI->Chip < ATI_CHIP_264GTPRO)) || ((dsp_on_off == vga_dsp_on_off) && (!dsp_config || !((dsp_config ^ vga_dsp_config) & DSP_XCLKS_PER_QW)))) { if (ATIDivide(GetBits(vga_dsp_on_off, VGA_DSP_OFF), GetBits(vga_dsp_config, VGA_DSP_XCLKS_PER_QW), 5, 1) > 24) pATI->DisplayFIFODepth = 32; else pATI->DisplayFIFODepth = 24; } return TRUE; } /* * ATIDSPSave -- * * This function is called to remember DSP register values on VT-B and later * controllers. */ void ATIDSPSave ( ATIPtr pATI, ATIHWPtr pATIHW ) { pATIHW->dsp_on_off = inr(DSP_ON_OFF); pATIHW->dsp_config = inr(DSP_CONFIG); } /* * ATIDSPCalculate -- * * This function sets up DSP register values for a VTB or later. Note that * this would be slightly different if VCLK 0 or 1 were used for the mode * instead. In that case, this function would set VGA_DSP_CONFIG and * VGA_DSP_ON_OFF, would have to zero out DSP_CONFIG and DSP_ON_OFF, and would * have to consider that VGA_DSP_CONFIG is partitioned slightly differently * than DSP_CONFIG. */ void ATIDSPCalculate ( ATIPtr pATI, ATIHWPtr pATIHW, DisplayModePtr pMode ) { int Multiplier, Divider; int RASMultiplier = pATI->XCLKMaxRASDelay, RASDivider = 1; int dsp_precision, dsp_on, dsp_off, dsp_xclks; int tmp, vshift, xshift; # define Maximum_DSP_PRECISION ((int)MaxBits(DSP_PRECISION)) /* Compute a memory-to-screen bandwidth ratio */ Multiplier = pATI->XCLKFeedbackDivider * pATI->ClockDescriptor.PostDividers[pATIHW->PostDivider]; Divider = pATIHW->FeedbackDivider * pATI->XCLKReferenceDivider; { Divider *= pATI->bitsPerPixel / 4; } /* Start by assuming a display FIFO width of 64 bits */ vshift = (6 - 2) - pATI->XCLKPostDivider; if (pATI->OptionPanelDisplay && (pATI->LCDPanelID >= 0)) { /* Compensate for horizontal stretching */ Multiplier *= pATI->LCDHorizontal; Divider *= pMode->HDisplay & ~7; RASMultiplier *= pATI->LCDHorizontal; RASDivider *= pMode->HDisplay & ~7; } /* Determine dsp_precision first */ tmp = ATIDivide(Multiplier * pATI->DisplayFIFODepth, Divider, vshift, -1); for (dsp_precision = -5; tmp; dsp_precision++) tmp >>= 1; if (dsp_precision < 0) dsp_precision = 0; else if (dsp_precision > Maximum_DSP_PRECISION) dsp_precision = Maximum_DSP_PRECISION; xshift = 6 - dsp_precision; vshift += xshift; /* Move on to dsp_off */ dsp_off = ATIDivide(Multiplier * (pATI->DisplayFIFODepth - 1), Divider, vshift, -1) - ATIDivide(1, 1, vshift - xshift, 1); /* Next is dsp_on */ { dsp_on = ATIDivide(Multiplier, Divider, vshift, 1); tmp = ATIDivide(RASMultiplier, RASDivider, xshift, 1); if (dsp_on < tmp) dsp_on = tmp; dsp_on += (tmp * 2) + ATIDivide(pATI->XCLKPageFaultDelay, 1, xshift, 1); } /* Calculate rounding factor and apply it to dsp_on */ tmp = ((1 << (Maximum_DSP_PRECISION - dsp_precision)) - 1) >> 1; dsp_on = ((dsp_on + tmp) / (tmp + 1)) * (tmp + 1); if (dsp_on >= ((dsp_off / (tmp + 1)) * (tmp + 1))) { dsp_on = dsp_off - ATIDivide(Multiplier, Divider, vshift, -1); dsp_on = (dsp_on / (tmp + 1)) * (tmp + 1); } /* Last but not least: dsp_xclks */ dsp_xclks = ATIDivide(Multiplier, Divider, vshift + 5, 1); /* Build DSP register contents */ pATIHW->dsp_on_off = SetBits(dsp_on, DSP_ON) | SetBits(dsp_off, DSP_OFF); pATIHW->dsp_config = SetBits(dsp_precision, DSP_PRECISION) | SetBits(dsp_xclks, DSP_XCLKS_PER_QW) | SetBits(pATI->DisplayLoopLatency, DSP_LOOP_LATENCY); } /* * ATIDSPSet -- * * This function is called to set DSP registers on VT-B and later controllers. */ void ATIDSPSet ( ATIPtr pATI, ATIHWPtr pATIHW ) { outr(DSP_ON_OFF, pATIHW->dsp_on_off); outr(DSP_CONFIG, pATIHW->dsp_config); }