/* NVClock 0.8 - Linux overclocker for NVIDIA cards * * site: http://nvclock.sourceforge.net * * Copyright(C) 2001-2006 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 */ #include #include "nvclock.h" #include "backend.h" static float nv30_get_fanspeed() { /* Bit 30-16 of register 0x10f0 are used to store the fanspeed and bit 14-0 contain the pwm divider */ int pwm_divider = nv_card->PMC[0x10f0/4] & 0x7fff; return ((nv_card->PMC[0x10f0/4] >> 16) & 0x7fff) * 100.0/pwm_divider; } static void nv30_set_fanspeed(float speed) { int value; int pwm_divider = nv_card->PMC[0x10f0/4] & 0x7fff; /* Don't set the fan lower than 10% for safety reasons */ if(speed < 10 || speed > 100) return; value = 0x80000000 + (((int)(speed * pwm_divider/100) & 0x7fff) << 16) + (nv_card->PMC[0x10f0/4] & 0xffff); nv_card->PMC[0x10f0/4] = value; } static int CalcSpeed(int base_freq, int m, int n, int p) { return (int)((float)n/(float)m * base_freq) >> p; } static int CalcSpeed_nv30(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_nv30(int base_freq, unsigned int pll) { /* All GeforceFX cards except the FX5200/FX5600/FX5700 use this algorithm */ /* If bit 7 is set use the new algorithm */ if(pll & 0x80) { int m1, m2, n1, n2, p; m1 = NV30_PLL_M1(pll); m2 = NV30_PLL_M2(pll); n1 = NV30_PLL_N1(pll); n2 = NV30_PLL_N2(pll); p = NV30_PLL_P(pll); /* Perhaps the 0x1f for n2 is wrong .. ? */ if(nv_card->debug) printf("m1=%d m2=%d n1=%d n2=%d p=%d\n", m1, m2, n1, n2, p); return (float)CalcSpeed_nv30(base_freq, m1, m2, n1, n2, p)/1000; } else { int m, n, p; m = NV30_PLL_M1(pll); n = NV30_PLL_N1(pll); p = NV30_PLL_P(pll); if(nv_card->debug) printf("m=%d n=%d p=%d\n", m, n, p); return (float)CalcSpeed(base_freq, m, n, p)/1000; } } static void ClockSelect_nv30(int clockIn, int p_current, unsigned int *pllOut) { unsigned diff, diffOld; unsigned VClk, Freq; unsigned m, m2, n, n2, p = 0; int base_freq = 27000; diffOld = 0xFFFFFFFF; if(clockIn < 125) p = 3; else if(clockIn < 250) p = 2; else if(clockIn < 500) p = 1; else p = 0; VClk = (unsigned)clockIn; Freq = VClk; if ((Freq >= 75000) && (Freq <= 1100000)) { for(m = 1; m <= 4; m++) { for (m2 = 1; m2 <= 4; m2++) { for(n = 1; n <= 31; n++) { n2 = (int)((float)((VClk << p) * m * m2) / (float)(base_freq * n)+.5); if((n2 < 24) && (n >= n2) && (m >= m2)) { 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( (float)diff/(float)clockIn <= 0.005) { *pllOut = m + (m2<<4) + (n<<8) + ((n2 & 0x7) << 19) + ((n2 & 0x18)<<21) + (p<<16) + (1<<7); return; } if(diff < diffOld) { *pllOut = m + (m2<<4) + (n<<8) + ((n2 & 0x7) << 19) + ((n2 & 0x18)<<21) + (p<<16) + (1<<7); diffOld = diff; } } } } } } } static float nv30_get_gpu_speed() { int nvpll = nv_card->PRAMDAC[0x500/4]; if(nv_card->debug == 1) { printf("NVPLL_COEFF=%08x\n", nvpll); } return (float)GetClock_nv30(nv_card->base_freq, nvpll); } static void nv30_set_gpu_speed(unsigned int nvclk) { unsigned int PLL=0; int p; nvclk *= 1000; p = NV30_PLL_P(nv_card->PRAMDAC[0x500/4]); ClockSelect_nv30(nvclk, p, &PLL); if(nv_card->debug) printf("NVPLL_COEFF: %08x\n", PLL); /* Unlock the programmable NVPLL/MPLL */ nv_card->PRAMDAC[0x50c/4] |= 0x500; if(PLL) nv_card->PRAMDAC[0x500/4] = PLL; } static float nv30_get_memory_speed() { int mpll = nv_card->PRAMDAC[0x504/4]; if(nv_card->debug == 1) { printf("MPLL_COEFF=%08x\n", mpll); } return (float)GetClock_nv30(nv_card->base_freq, mpll); } static void nv30_set_memory_speed(unsigned int memclk) { unsigned int PLL=0; int p; memclk *= 1000; p = NV30_PLL_P(nv_card->PRAMDAC[0x504/4]); ClockSelect_nv30(memclk, p, &PLL); if(nv_card->debug) printf("MPLL_COEFF: %08x\n", PLL); /* Unlock the programmable NVPLL/MPLL */ nv_card->PRAMDAC[0x50c/4] |= 0x500; if(PLL) nv_card->PRAMDAC[0x504/4] = PLL; } static void nv30_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; } static void nv30_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; } static void nv30_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 = nv30_get_gpu_speed; nv_card->get_memory_speed = nv30_get_memory_speed; nv_card->set_memory_speed = nv30_set_memory_speed; nv_card->set_gpu_speed = nv30_set_gpu_speed; nv_card->reset_gpu_speed = nv30_reset_gpu_speed; nv_card->reset_memory_speed = nv30_reset_memory_speed; } } void nv30_init(void) { nv_card->base_freq = 27000; nv_card->set_state = nv30_set_state; nv_card->set_state(nv_card->state); /* Set the clock function pointers */ /* HW monitoring; bit 31 is an indication if fanspeed monitoring is available / Note this bit isn't very reliable as it is set on cards with advanced sensors too. / / Only support this on NV30/NV35/NV38 hardware for now as it works differently on other NV3x boards */ if((nv_card->PMC[0x10f0/4] & 0x80000000) && (nv_card->arch & (NV30 | NV35)) && !(nv_card->caps & I2C_FANSPEED_MONITORING)) { nv_card->caps |= GPU_FANSPEED_MONITORING; nv_card->get_fanspeed = nv30_get_fanspeed; nv_card->set_fanspeed = nv30_set_fanspeed; } /* 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; above hack doesn't work for those */ nv_card->memclk_min = (short)(nv_card->bios->perf_lst[0].memclk * .75); nv_card->memclk_max = (short)(nv_card->bios->perf_lst[2].memclk * 1.25); nv_card->nvclk_min = (short)(nv_card->bios->perf_lst[0].nvclk * .75); nv_card->nvclk_max = (short)(nv_card->bios->perf_lst[2].nvclk * 1.25); } else { float memclk = GetClock_nv30(nv_card->base_freq, nv_card->mpll); float nvclk = GetClock_nv30(nv_card->base_freq, nv_card->nvpll); /* 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); } }