/* NVClock 0.8 - Linux overclocker for NVIDIA cards * * site: http://nvclock.sourceforge.net * * Copyright(C) 2001-2005 Roderick Colenbrander * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* This source file uses some clock calculation code from nvidia's xfree86 driver. To keep Nvidia happy I have added their copyright. The way they interpret it (see linux kernel riva_hw.h) is that you need to add the disclaimer and copyright and when that's done you can basicly do what you want. */ /***************************************************************************\ |* *| |* Copyright 1993-2003 NVIDIA, Corporation. All rights reserved. *| |* *| |* NOTICE TO USER: The source code is copyrighted under U.S. and *| |* international laws. Users and possessors of this source code are *| |* hereby granted a nonexclusive, royalty-free copyright license to *| |* use this code in individual and commercial software. *| |* *| |* Any use of this source code must include, in the user documenta- *| |* tion and internal comments to the code, notices to the end user *| |* as follows: *| |* *| |* Copyright 1993-2003 NVIDIA, Corporation. All rights reserved. *| |* *| |* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *| |* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *| |* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *| |* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *| |* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *| |* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *| |* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *| |* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *| |* SULTING 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 SOURCE CODE. *| |* *| |* U.S. Government End Users. This source code is a "commercial *| |* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *| |* consisting of "commercial computer software" and "commercial *| |* computer software documentation," as such terms are used in *| |* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *| |* ment only as a commercial end item. Consistent with 48 C.F.R. *| |* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *| |* all U.S. Government End Users acquire the source code with only *| |* those rights set forth herein. *| |* *| \***************************************************************************/ #include #include "nvclock.h" #include "backend.h" static int CalcSpeed_nv31(int base_freq, int m1, int m2, int n1, int n2, int p) { return (int)((float)(n1*n2)/(m1*m2) * base_freq) >> p; } float GetClock_nv31(int base_freq, unsigned int pll, unsigned int pll2) { /* Geforce FX5600 cards and the FX5700?? (0x340) use a different algorithm */ int m1, m2, n1, n2, p; m1 = pll & 0xff; n1 = (pll >> 8) & 0xff; p = (pll >> 16) & 0x0f; /* For some reasons a speed consists of two plls */ if(pll2 & 0x80000000) { m2 = pll2 & 0xff; n2 = (pll2 >> 8) & 0xff; } else { m2 = 1; n2 = 1; } if(nv_card->debug) printf("m1=%d m2=%d n1=%d n2=%d p=%d\n", m1, m2, n1, n2, p); return (float)CalcSpeed_nv31(base_freq, m1, m2, n1, n2, p)/1000; } static void ClockSelect_nv31(int clockIn, int p_current, unsigned int *pllOut, unsigned int *pllBOut) { unsigned diff, diffOld; unsigned VClk, Freq; unsigned m, m2, n, n2, p = 0; int base_freq = 27000; unsigned plow = 0; diffOld = 0xFFFFFFFF; plow = p_current; VClk = (unsigned)clockIn; for (p = plow; p <= 1; p++) { Freq = VClk; if ((Freq >= 100000) && (Freq <= 1100000)) { for (m2 = 1; m2 <= 13; m2++) { for (m = 1; m <= 13; m++) { for(n2 = 2; n2 <= 10; n2++) { n = (int)((float)((VClk << p) * m * m2) / (float)(base_freq * n2)+.5); /* For now this line seems to create "good" speeds, it makes sure that n/m isn't much bigger than n2/m2 */ if(((n/m >= 1) && (n/m <= 8)) && ((n2/m2 >= 1) && (n2/m2 <= 8))) { Freq = ((base_freq * n * n2) / (m * m2)) >> p; if (Freq > VClk) diff = Freq - VClk; else diff = VClk - Freq; /* When the difference is 0 or less than .5% accept the speed */ if(((diff == 0) || ((float)diff/(float)clockIn <= 0.005))) { *pllOut = (p << 16) | (n << 8) | m; *pllBOut = (1<<31) | (n2 << 8) | m2; return; } if (diff < diffOld) { *pllOut = (p << 16) | (n << 8) | m; *pllBOut = (1<<31) | (n2 << 8) | m2; diffOld = diff; } } } } } } } } static float nv31_get_gpu_speed() { int pll = nv_card->PRAMDAC[0x500/4]; int pll2 = nv_card->PRAMDAC[0x570/4]; if(nv_card->debug == 1) { printf("NVPLL_COEFF=%08x\n", pll); printf("NVPLL_COEFF2=%08x\n", pll2); } return (float)GetClock_nv31(nv_card->base_freq, pll, pll2); } static void nv31_set_gpu_speed(unsigned int nvclk) { unsigned int PLL=0, PLL2=0; int p = (nv_card->PRAMDAC[0x504/4] >> 16) & 0x0f; nvclk *= 1000; /* Pass the current postdivider value to the clock calculation; Changing the postdivider can lead to stability problems. */ ClockSelect_nv31(nvclk, p, &PLL, &PLL2); /* Unlock the programmable NVPLL/MPLL */ nv_card->PRAMDAC[0x50c/4] |= 0x500; /* When no speed is found, don't change the PLL */ /* The algorithm doesn't allow too low speeds */ if(PLL) { if(nv_card->debug) { printf("NVPLL_COEFF: %08x\n", PLL); printf("NVPLL2_COEFF: %08x\n", PLL2); } nv_card->PRAMDAC[0x500/4] = PLL; nv_card->PRAMDAC[0x570/4] = PLL2; } } static float nv31_get_memory_speed() { int pll = nv_card->PRAMDAC[0x504/4]; int pll2 = nv_card->PRAMDAC[0x574/4]; if(nv_card->debug == 1) { printf("MPLL_COEFF=%08x\n", pll); printf("MPLL_COEFF2=%08x\n", pll2); } return (float)GetClock_nv31(nv_card->base_freq, pll, pll2); } static void nv31_set_memory_speed(unsigned int memclk) { unsigned int PLL=0, PLL2=0; int p = (nv_card->PRAMDAC[0x504/4] >> 16) & 0x0f; memclk *= 1000; ClockSelect_nv31(memclk, p, &PLL, &PLL2); /* Unlock the programmable NVPLL/MPLL */ nv_card->PRAMDAC[0x50c/4] |= 0x500; /* When no speed is found, don't change the PLL */ /* The algorithm doesn't allow too low speeds */ if(PLL) { if(nv_card->debug) { printf("MPLL_COEFF: %08x\n", PLL); printf("MPLL2_COEFF: %08x\n", PLL2); } nv_card->PRAMDAC[0x504/4] = PLL; nv_card->PRAMDAC[0x574/4] = PLL2; } } static void nv31_reset_gpu_speed() { /* FIXME: we need to use our bios info */ /* Unlock the programmable NVPLL/MPLL */ nv_card->PRAMDAC[0x50c/4] |= 0x500; /* Set the gpu speed */ nv_card->PRAMDAC[0x500/4] = nv_card->nvpll; nv_card->PRAMDAC[0x570/4] = nv_card->nvpll2; } static void nv31_reset_memory_speed() { /* FIXME: we need to use our bios info */ /* Unlock the programmable NVPLL/MPLL */ nv_card->PRAMDAC[0x50c/4] |= 0x500; /* Set the memory speed */ nv_card->PRAMDAC[0x504/4] = nv_card->mpll; nv_card->PRAMDAC[0x574/4] = nv_card->mpll2; } static void nv31_set_state(int state) { nv_card->state = state; #ifdef HAVE_NVCONTROL if(state & (STATE_2D | STATE_3D)) { nv_card->get_gpu_speed = nvcontrol_get_gpu_speed; nv_card->get_memory_speed = nvcontrol_get_memory_speed; nv_card->set_gpu_speed = nvcontrol_set_gpu_speed; nv_card->set_memory_speed = nvcontrol_set_memory_speed; nv_card->reset_gpu_speed = nvcontrol_reset_gpu_speed; nv_card->reset_memory_speed = nvcontrol_reset_memory_speed; } else #endif { nv_card->get_gpu_speed = nv31_get_gpu_speed; nv_card->get_memory_speed = nv31_get_memory_speed; nv_card->set_memory_speed = nv31_set_memory_speed; nv_card->set_gpu_speed = nv31_set_gpu_speed; nv_card->reset_gpu_speed = nv31_reset_gpu_speed; nv_card->reset_memory_speed = nv31_reset_memory_speed; } } void nv31_init(void) { nv_card->base_freq = 27000; nv_card->set_state = nv31_set_state; nv_card->set_state(nv_card->state); /* Set the clock function pointers */ nv_card->get_gpu_speed = nv31_get_gpu_speed; nv_card->get_memory_speed = nv31_get_memory_speed; nv_card->set_gpu_speed = nv31_set_gpu_speed; nv_card->set_memory_speed = nv31_set_memory_speed; nv_card->reset_gpu_speed = nv31_reset_gpu_speed; nv_card->reset_memory_speed = nv31_reset_memory_speed; /* Mobile GPU check; we don't want to overclock those unless the user wants it */ if(nv_card->gpu == MOBILE) { nv_card->caps = ~(~nv_card->caps | GPU_OVERCLOCKING | MEM_OVERCLOCKING); } else nv_card->caps |= (GPU_OVERCLOCKING | MEM_OVERCLOCKING); /* Set the speed range */ if(nv_card->bios) { /* GeforceFX models have different clocks in 2d and 3d; Useally the second entry is for 3D / but in case of the FX5600Ultra this is the second one. */ if(nv_card->device_id == 0x311) { nv_card->memclk_3d = (short)nv_card->bios->perf_lst[1].memclk; nv_card->nvclk_3d = (short)nv_card->bios->perf_lst[1].nvclk; } else { nv_card->memclk_3d = (short)nv_card->bios->perf_lst[2].memclk; nv_card->nvclk_3d = (short)nv_card->bios->perf_lst[2].nvclk; } nv_card->memclk_min = (short)(nv_card->bios->perf_lst[0].memclk * .75); nv_card->memclk_max = nv_card->memclk_3d * 1.25; nv_card->nvclk_min = (short)(nv_card->bios->perf_lst[0].nvclk * .75); nv_card->nvclk_max = nv_card->nvclk_3d * 1.25; } else { float memclk = GetClock_nv31(nv_card->base_freq, nv_card->mpll, nv_card->mpll2); float nvclk = GetClock_nv31(nv_card->base_freq, nv_card->nvpll, nv_card->nvpll2); /* Not great but better than nothing .. */ nv_card->memclk_min = (short)(memclk * .75); nv_card->memclk_max = (short)(memclk * 1.5); nv_card->nvclk_min = (short)(nvclk * .75); nv_card->nvclk_max = (short)(nvclk * 1.5); } }