/* 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 #include #include "backend.h" #include "nvclock.h" /* This list isn't used much for the speed ranges anymore */ /* Mainly mobile gpu speeds are missing */ const static struct pci_ids ids[] = { { 0x20, "nVidia Riva TnT", DESKTOP }, { 0x28, "nVidia Riva TnT 2 Pro", DESKTOP }, { 0x2a, "nVidia Riva TnT 2", DESKTOP }, { 0x2b, "nVidia Riva TnT 2", DESKTOP }, { 0xa0, "nVidia Riva TnT 2 Aladdin (Integrated)", DESKTOP }, { 0x2c, "nVidia Riva TnT 2 VANTA", DESKTOP }, { 0x2d, "nVidia Riva TnT 2 M64", DESKTOP }, { 0x2e, "nVidia Riva TnT 2 VANTA", DESKTOP }, { 0x2f, "nVidia Riva TnT 2 VANTA", DESKTOP }, { 0x29, "nVidia Riva TnT 2 Ultra", DESKTOP }, { 0x100, "nVidia Geforce 256 SDR", DESKTOP }, { 0x101, "nVidia Geforce 256 DDR", DESKTOP }, { 0x103, "nVidia Quadro", DESKTOP }, { 0x110, "nVidia Geforce 2 MX/MX400", DESKTOP }, { 0x111, "nVidia Geforce 2 MX 100/200", DESKTOP }, { 0x112, "nVidia Geforce 2 GO", MOBILE }, { 0x113, "nVidia Quadro 2 MXR/EX/GO", MOBILE }, { 0x1a0, "nVidia Geforce 2 MX integrated", NFORCE }, { 0x150, "nVidia Geforce 2 GTS/PRO", DESKTOP }, { 0x151, "nVidia Geforce 2 Ti", DESKTOP }, { 0x152, "nVidia Geforce 2 Ultra", DESKTOP }, { 0x153, "nVidia Quadro 2 Pro", DESKTOP }, { 0x170, "nVidia Geforce 4 MX460", DESKTOP }, { 0x171, "nVidia Geforce 4 MX440", DESKTOP }, { 0x172, "nVidia Geforce 4 MX420", DESKTOP }, { 0x173, "nVidia Geforce 4 MX440 SE", DESKTOP }, { 0x174, "nVidia Geforce 4 440 Go", MOBILE }, { 0x175, "nVidia Geforce 4 420 Go", MOBILE }, { 0x176, "nVidia Geforce 4 420 Go 32M", MOBILE }, { 0x177, "nVidia Geforce 4 460 Go", MOBILE }, { 0x178, "nVidia Quadro 4 550 XGL", DESKTOP }, { 0x179, "nVidia Geforce 4 440 Go 64M", MOBILE }, { 0x17a, "nVidia Quadro 4 200/400 NVS", DESKTOP }, { 0x17b, "nVidia Quadro 4 550 XGL", DESKTOP }, { 0x17c, "nVidia Quadro 4 500 GoGL", MOBILE }, { 0x17d, "nVidia Geforce 410 Go", MOBILE }, { 0x180, "nVidia Geforce 4 MX440 8X", DESKTOP }, { 0x181, "nVidia Geforce 4 MX440 8X", DESKTOP }, { 0x182, "nVidia Geforce 4 MX440SE 8X", DESKTOP }, { 0x185, "nVidia Geforce 4 MX4000", DESKTOP }, { 0x183, "nVidia Geforce 4 MX420 8X", DESKTOP }, { 0x186, "nVidia Geforce 4 Go", MOBILE }, { 0x187, "nVidia Geforce 4 488 Go", MOBILE }, { 0x188, "nVidia Quadro 4 580 XGL", DESKTOP }, { 0x18a, "nVidia Quadro 4 280 NVS", DESKTOP }, { 0x18b, "nVidia Quadro 4 380 XGL", DESKTOP }, { 0x18c, "nVidia Quadro NVS 50 PCI", DESKTOP }, { 0x18d, "nVidia Geforce 4 448 Go", MOBILE }, { 0x1f0, "nVidia Geforce 4 MX integrated", NFORCE }, { 0x200, "nVidia Geforce 3", DESKTOP }, { 0x201, "nVidia Geforce 3 Titanium 200", DESKTOP }, { 0x202, "nVidia Geforce 3 Titanium 500", DESKTOP }, { 0x203, "nVidia Quadro DCC", DESKTOP }, { 0x250, "nVidia Geforce 4 Ti 4600", DESKTOP }, { 0x251, "nVidia Geforce 4 Ti 4400", DESKTOP }, { 0x253, "nVidia Geforce 4 Ti 4200", DESKTOP }, { 0x258, "nVidia Quadro 4 900 XGL", DESKTOP }, { 0x259, "nVidia Quadro 4 750 XGL", DESKTOP }, { 0x25a, "nVidia Quadro 4 600 XGL", DESKTOP }, { 0x25b, "nVidia Quadro 4 700 XGL", DESKTOP }, { 0x280, "nVidia Geforce 4 Ti 4800", DESKTOP }, { 0x281, "nVidia Geforce 4 Ti 4200 8X", DESKTOP }, { 0x282, "nVidia Geforce 4 Ti 4800SE", DESKTOP }, { 0x286, "nVidia Geforce 4 4000 GO", MOBILE }, { 0x288, "nVidia Quadro 4 980 XGL", DESKTOP }, { 0x289, "nVidia Quadro 4 780 XGL", DESKTOP }, { 0x28c, "nVidia Quadro 4 700 GoGL", MOBILE }, { 0x300, "nVidia GeforceFX 5800", DESKTOP }, { 0x301, "nVidia GeforceFX 5800 Ultra", DESKTOP }, { 0x302, "nVidia GeforceFX 5800", DESKTOP }, { 0x308, "nVidia QuadroFX 2000", DESKTOP }, { 0x309, "nVidia QuadroFX 1000", DESKTOP }, { 0x311, "nVidia GeforceFX 5600 Ultra", DESKTOP }, { 0x312, "nVidia GeforceFX 5600", DESKTOP }, { 0x314, "nVidia GeforceFX 5600XT", DESKTOP }, { 0x316, "nVidia NV31", MOBILE }, { 0x317, "nVidia NV31", MOBILE }, { 0x318, "nVidia NV31GL", DESKTOP }, { 0x319, "nVidia NV31GL", DESKTOP }, { 0x31a, "nVidia GeforceFX Go 5600", MOBILE }, { 0x31b, "nVidia GeforceFX Go 5650", MOBILE }, { 0x31c, "nVidia QuadroFX Go700", MOBILE }, { 0x31d, "NV31", MOBILE }, { 0x31e, "NV31GL", MOBILE }, { 0x31f, "NV31GL", MOBILE }, { 0x321, "nVidia GeforceFX 5200 Ultra", DESKTOP }, { 0x322, "nVidia GeforceFX 5200", DESKTOP }, { 0x323, "nVidia GeforceFX 5200LE", DESKTOP }, { 0x324, "nVidia GeforceFX Go 5200", MOBILE }, { 0x325, "nVidia GeforceFX Go 5250", MOBILE }, { 0x326, "nVidia GeforceFX 5500", DESKTOP }, { 0x328, "nVidia GeForceFX Go5200 32M/64M", MOBILE }, { 0x329, "nVidia GeForce FX 5200 (Mac)", MOBILE }, { 0x32a, "nVidia Quadro NVS 280 PCI", DESKTOP }, { 0x32b, "nVidia QuadroFX 500", DESKTOP }, { 0x32c, "nVidia GeforceFX Go5300", MOBILE }, { 0x32d, "nVidia GeforceFX Go5100", MOBILE }, { 0x32f, "nVidia NV34GL", DESKTOP }, { 0x330, "nVidia GeforceFX 5900 Ultra", DESKTOP }, { 0x331, "nVidia GeforceFX 5900", DESKTOP }, { 0x332, "nVidia GeforceFX 5900XT", DESKTOP }, { 0x333, "nVidia GeforceFX 5950 Ultra", DESKTOP }, { 0x334, "nVidia GeforceFX 5900ZT", DESKTOP }, { 0x338, "nVidia QuadroFX 3000", DESKTOP }, { 0x33f, "nVidia GeforceFX 700", DESKTOP }, { 0x341, "nVidia GeforceFX 5700 Ultra", DESKTOP }, { 0x342, "nVidia GeforceFX 5700", DESKTOP }, { 0x343, "nVidia GeforceFX 5700LE", DESKTOP }, { 0x344, "nVidia GeforceFX 5700VE", DESKTOP }, { 0x345, "NV36", DESKTOP }, { 0x347, "nVidia GeforceFX Go5700", MOBILE }, { 0x348, "nVidia GeforceFX Go5700", MOBILE }, { 0x349, "NV36", MOBILE }, { 0x34b, "NV36", MOBILE }, { 0x34c, "nVidia Quadro FX Go1000", MOBILE }, { 0x34e, "nVidia QuadroFX 1100", DESKTOP }, { 0x34f, "NV36GL", DESKTOP }, { 0x2a0, "nVidia Xbox GPU", NFORCE }, { 0x40, "nVidia Geforce 6800 Ultra", DESKTOP }, { 0x41, "nVidia Geforce 6800", DESKTOP }, { 0x42, "nVidia Geforce 6800LE", DESKTOP }, { 0x43, "NV40", DESKTOP }, { 0x45, "nVidia Geforce 6800GT", DESKTOP }, { 0x46, "nVidia Geforce 6800GT", DESKTOP }, { 0x47, "nVidia Geforce 6800GS", DESKTOP }, { 0x48, "nVidia Geforce 6800XT", DESKTOP }, { 0x49, "NV40GL", DESKTOP }, { 0x4d, "nVidia QuadroFX 4400", DESKTOP }, { 0x4e, "nVidia QuadroFX 4000", DESKTOP }, { 0xc0, "nVidia Geforce 6800GS", DESKTOP }, { 0xc1, "nVidia Geforce 6800", DESKTOP }, { 0xc2, "nVidia Geforce 6800LE", DESKTOP }, { 0xc3, "nVidia Geforce 6800XT", DESKTOP }, { 0xc8, "nVidia Geforce Go 6800", MOBILE }, { 0xc9, "nVidia Geforce Go 6800Ultra", MOBILE }, { 0xcc, "nVidia QuadroFX Go 1400", MOBILE }, { 0xcd, "nVidia QuadroFX 3350/4000SDI", DESKTOP }, { 0xce, "nVidia QuadroFX 1400", DESKTOP }, { 0xf0, "nVidia Geforce 6800/Ultra", DESKTOP }, { 0xf1, "nVidia Geforce 6600/GT", DESKTOP }, { 0xf2, "nVidia Geforce 6600", DESKTOP }, { 0xf3, "nVidia Geforce 6200", DESKTOP }, { 0xf5, "nVidia Geforce 7800GS", DESKTOP }, { 0xf6, "nVidia Geforce 6800GS/XT", DESKTOP }, { 0xf8, "nVidia QuadroFX 3400", DESKTOP }, { 0xf9, "nVidia Geforce 6800 Ultra", DESKTOP }, { 0xfa, "nVidia GeforcePCX 5750", DESKTOP }, { 0xfb, "nVidia GeforcePCX 5900", DESKTOP }, { 0xfc, "nVidia GeforcePCX 5300 / Quadro FX330", DESKTOP }, { 0xfd, "nVidia Quadro NVS280/FX330", DESKTOP }, { 0xfe, "nVidia QuadroFX 1300", DESKTOP }, { 0xff, "nVidia GeforcePCX 4300", DESKTOP }, { 0x140, "nVidia Geforce 6600GT", DESKTOP }, { 0x141, "nVidia Geforce 6600", DESKTOP }, { 0x142, "nVidia Geforce 6600LE", DESKTOP }, { 0x143, "NV43", DESKTOP }, { 0x144, "nVidia Geforce Go 6600", MOBILE }, { 0x145, "nVidia Geforce 6610XL", DESKTOP }, { 0x146, "nVidia GeForce Go 6600TE/6200TE", MOBILE }, { 0x147, "nVidia Geforce 6700XL", DESKTOP }, { 0x148, "nVidia Geforce Go 6600", MOBILE }, { 0x149, "nVidia Geforce Go 6600GT", MOBILE }, { 0x14a, "NV43", DESKTOP }, { 0x14b, "NV43", DESKTOP }, { 0x14c, "NV43", DESKTOP }, { 0x14d, "NV43GL", DESKTOP }, { 0x14e, "nVidia QuadroFX 540", DESKTOP }, { 0x14f, "nVidia Geforce 6200", DESKTOP }, { 0x160, "nVidia NV44", DESKTOP }, { 0x161, "nVidia Geforce 6200 TurboCache", DESKTOP }, { 0x162, "nVidia Geforce 6200SE TurboCache", DESKTOP }, { 0x163, "NV44", DESKTOP }, { 0x164, "nVidia Geforce Go 6200", MOBILE }, { 0x165, "nVidia Quadro NVS 285", DESKTOP }, { 0x166, "nVidia Geforce Go 6400", MOBILE }, { 0x167, "nVidia Geforce Go 6200", MOBILE }, { 0x168, "nVidia Geforce Go 6400", MOBILE }, { 0x169, "NV44M", DESKTOP }, { 0x16b, "NV44GLM", DESKTOP }, { 0x16c, "NV44GLM", DESKTOP }, { 0x16d, "NV44GLM", DESKTOP }, { 0x16e, "NV44GLM", DESKTOP }, { 0x210, "NV48", DESKTOP }, { 0x211, "nVidia Geforce 6800", DESKTOP }, { 0x212, "nVidia Geforce 6800LE", DESKTOP }, { 0x215, "nVidia Geforce 6800GT", DESKTOP }, { 0x218, "nVidia Geforce 6800XT", DESKTOP }, { 0x220, "Unknown NV44", DESKTOP }, { 0x221, "nVidia Geforce 6200", DESKTOP }, { 0x222, "Unknown NV44", DESKTOP }, { 0x228, "NV44M", MOBILE }, { 0x240, "nVidia Geforce 6150", NFORCE }, { 0x241, "nVidia Geforce 6150 LE", NFORCE }, { 0x242, "nVidia Geforce 6100", NFORCE }, { 0x2dd, "nVidia Unknown NV4x", DESKTOP }, { 0x2de, "nVidia Unknown NV4x", DESKTOP }, { 0x90, "nVidia G70", DESKTOP }, { 0x91, "nVidia Geforce 7800GTX", DESKTOP }, { 0x92, "nVidia Geforce 7800GT", DESKTOP }, { 0x93, "nVidia Geforce 6800GS", DESKTOP }, { 0x94, "nVidia G70", DESKTOP }, { 0x98, "nVidia G70", DESKTOP }, { 0x99, "nVidia Geforce Go 7800GTX", MOBILE }, { 0x9c, "nVidia G70", DESKTOP }, { 0x9d, "nVidia QuadroFX 4500", DESKTOP }, { 0x9e, "nVidia G70GL", DESKTOP }, { 0x290, "nVidia Geforce 7900GTX", DESKTOP }, { 0x291, "nVidia Geforce 7900GT", DESKTOP }, { 0x391, "nVidia Geforce 7600GT", DESKTOP }, { 0x392, "nVidia Geforce 7600GS", DESKTOP }, { 0x398, "nVidia Geforce Go 7600", MOBILE }, { 0x1d1, "nVidia Geforce 7300LE", DESKTOP }, { 0x1d7, "nVidia Geforce Go 7300", MOBILE }, { 0x1d8, "nVidia Geforce Go 7400", MOBILE }, { 0x1da, "nVidia Quadro NVS 110M", MOBILE }, { 0x1de, "nVidia QuadroFX 350", DESKTOP }, { 0x1df, "nVidia Geforce 7300GS", DESKTOP }, { 0, NULL, UNKNOWN } }; const char *get_card_name(int device_id, gpu_type *gpu) { struct pci_ids *nv_ids = (struct pci_ids*)ids; while(nv_ids->id != 0) { if(nv_ids->id == device_id) { *gpu = nv_ids->gpu; return nv_ids->name; } nv_ids++; } /* if !found */ *gpu = UNKNOWN; return "Unknown Nvidia card"; } /* Internal gpu architecture function which sets / a device to a specific architecture. This architecture / doesn't have to be the real architecture. It is mainly / used to choose codepaths inside nvclock. */ int get_gpu_arch(int device_id) { int arch; switch(device_id & 0xff0) { case 0x20: arch = NV5; break; case 0x100: case 0x110: case 0x150: case 0x1a0: arch = NV10; break; case 0x170: case 0x180: case 0x1f0: arch = NV17; break; case 0x200: arch = NV20; break; case 0x250: case 0x280: case 0x320: /* We don't treat the FX5200/FX5500 as FX cards */ arch = NV25; break; case 0x300: arch = NV30; break; case 0x330: arch = NV35; /* Similar to NV30 but fanspeed stuff works differently */ break; /* Give a seperate arch to FX5600/FX5700 cards as they need different code than other FX cards */ case 0x310: case 0x340: arch = NV31; break; case 0x40: case 0x120: case 0x130: case 0x210: case 0x230: case 0x240: arch = NV40; break; case 0xc0: arch = NV41; break; case 0x140: arch = NV43; /* Similar to NV40 but with different fanspeed code */ break; case 0x160: case 0x220: arch = NV44; break; case 0x1d0: arch = NV46; break; case 0x90: arch = NV47; break; case 0x290: arch = NV49; /* 7900 */ break; case 0x390: arch = NV4B; /* 7600 */ break; case 0xf0: /* The code above doesn't work for pci-express cards as multiple architectures share one id-range */ switch(device_id) { case 0xf0: /* 6800 */ case 0xf9: /* 6800Ultra */ arch = NV40; break; case 0xf6: /* 6800GS/XT */ arch = NV41; break; case 0xf1: /* 6600/6600GT */ case 0xf2: /* 6600GT */ case 0xf3: /* 6200 */ arch = NV43; break; case 0xf5: /* 7800GS */ arch = NV47; break; case 0xfa: /* PCX5700 */ arch = NV31; break; case 0xf8: /* QuadroFX 3400 */ case 0xfb: /* PCX5900 */ arch = NV35; break; case 0xfc: /* PCX5300 */ case 0xfd: /* Quadro NVS280/FX330, FX5200 based? */ case 0xff: /* PCX4300 */ arch = NV25; break; case 0xfe: /* Quadro 1300, has the same id as a FX3000 */ arch = NV35; break; } break; default: arch = UNKNOWN; } return arch; } /* Receive the real gpu architecture */ static short get_gpu_architecture() { return (nv_card->PMC[0/4] >> 20) & 0xff; } /* Receive the gpu revision */ static short get_gpu_revision() { return nv_card->PMC[0/4] & 0xff; } static short get_gpu_pci_id() { return (nv_card->PMC[0x1800/4]>>16) & 0xffff; } static int set_gpu_pci_id(short id) { if(nv_card->arch & (NV10 | NV20)) { /* The first two bits of the pci id can be changed. They are stored in bit 13-12 of PEXTDEV_BOOT0 */ int pextdev_boot0 = nv_card->PEXTDEV[0x0/4] & ~(0x3 << 12); /* Only the first 2 bits can be changed on these GPUs */ if(id > 3) return 0; nv_card->PEXTDEV[0x0/4] = pextdev_boot0 | (id & 0x3 << 12); nv_card->device_id = get_gpu_pci_id(); nv_card->card_name = (char*)get_card_name(nv_card->device_id, &nv_card->gpu); return 1; } /* Don't allow modding on cards using bridges (0xf*)! */ else if((nv_card->arch & (NV17 | NV25 | NV3X | NV4X)) && ((nv_card->device_id & 0xfff0) != 0xf0)) { /* The first four bits of the pci id can be changed. The first two bits are stored in bit 13-12 of PEXTDEV_BOOT0, bit 3 and 4 are stored in bit 21-20 */ int pextdev_boot0 = nv_card->PEXTDEV[0x0/4] & ~((0x3 << 20) | (0x3 << 12)); /* The first 4 bits can be changed */ if(id > 16) return 0; /* On various NV4x cards the quadro capability bit in PBUS_DEBUG1 is locked. It can be unlocked by clearing a bit in 0xc020 */ if(nv_card->PMC[0xc020/4]) nv_card->PMC[0xc020/4] = 0; nv_card->PEXTDEV[0x0/4] = pextdev_boot0 | (((id>>2) & 0x3) << 20) | ((id & 0x3) << 12); nv_card->device_id = get_gpu_pci_id(); nv_card->card_name = (char*)get_card_name(nv_card->device_id, &nv_card->gpu); return 1; } return 0; } /* Function to read a single byte from pci configuration space */ static void read_byte(int offset, unsigned char *data) { /* The original plan was to read the PCI configuration directly from registers 0x1800 and upwards / from the card itself. Although this is a fully correct way, it doesn't work for some cards using / a PCI-Express -> AGP bridge. If I would read the registers from the card they would include PCI-Express / as one of the capabilities. Reading using the "normal" way results in AGP as one of the capabilities. / To correctly show that a card uses AGP we need to read the modded config space. */ *data = pciReadLong(nv_card->devbusfn,offset) & 0xff; } /* Function to read a single byte from some memory mapped register */ static void nv_read_byte(volatile unsigned int *reg, int offset, unsigned char *data) { int shift = (offset % 4)*8; *data = (reg[offset/4] >> shift) & 0xff; } /* Check the videocard for a certain PCI capability like AGP/PCI-Express/PowerManagement. / If a certain capability is supported return the position of the cap pointer. */ static int pci_find_capability(unsigned char cap) { unsigned char pos, id; read_byte(PCI_CAPABILITY_LIST, &pos); while(pos >= 0x40) { pos &= ~3; read_byte(pos + PCI_CAP_LIST_ID, &id); if(id == 0xff) break; if(id == cap) return pos; /* Return the position of the cap pointer */ read_byte(pos + PCI_CAP_LIST_NEXT, &pos); } return 0; } /* Check the videocard for a certain PCI capability like AGP/PCI-Express/PowerManagement. / If a certain capability is supported return the position of the cap pointer. */ static int nv_pci_find_capability(unsigned char cap) { unsigned char pos, id; nv_read_byte(nv_card->PMC, 0x1800 + PCI_CAPABILITY_LIST, &pos); while(pos >= 0x40) { pos &= ~3; nv_read_byte(nv_card->PMC, pos + 0x1800 + PCI_CAP_LIST_ID, &id); if(id == 0xff) break; if(id == cap) return pos; /* Return the position of the cap pointer */ nv_read_byte(nv_card->PMC, pos + 0x1800 + PCI_CAP_LIST_NEXT, &pos); } return 0; } static char* get_bus_type() { /* The pci header contains lots of information about a device like / what type of device it is, who the vendor is and so on. It also / contains a list of capabilities. Things like AGP, power management, / PCI-X and PCI-Express are considered capabilities. We could check / these capabilities to find out if the card is AGP or PCI-Express. / / Reading the bus type from the pci header would be a nice way but / unfortunately there are some issues. One way to do the reading / is to read the information directly from the card (from PMC). / This doesn't work as some PCI-Express boards (6600GT) actually use / PCI-Express GPUs connected to some bridge chip on AGP boards (same device id!). / If you read directly from the card it will advertise PCI-Express instead of AGP. / There is also another way to read the pci header for instance on Linux / using from files in /proc/bus/pci but non-root users can only read / a small part of the file. Most of the time the info we need isn't in / the readable part. Further there are also some early PCI-Express boards (GeforcePCX) / that contain bridge chips to turn AGP GPUs into PCI-Express ones. / / Currently we will return PCI-Express on GeforcePCX board under the valid / assumption that there are no AGP boards with the same device id. Further / it seems that 'low' device ids are for PCI-Express->AGP while the higher ones / are for AGP->PCI-Express, so for the lower ones (6200/6600/6800) we will return AGP / and for the higher ones PCI-Express. A nicer way would be to read all this stuff from / the pci header but as explained that can't be done at the moment. */ switch(nv_card->device_id) { case 0xf0: /* 6800 */ case 0xf1: /* 6600GT */ case 0xf2: /* 6600 */ case 0xf3: /* 6200 */ case 0xf5: /* 6800GS/XT */ case 0xf6: /* 7800GS */ return "AGP (BR02)"; /* We return something different from AGP for now as we don't want to show the AGP tab */ case 0xf8: /* Quadro FX3400 */ case 0xf9: /* Geforce 6800 series */ case 0xfa: /* PCX5500 */ case 0xfb: /* PCX5900 */ case 0xfc: /* Quadro FX330*/ case 0xfd: /* PCX5500 */ case 0xfe: /* Quadro 1300 */ case 0xff: /* PCX4300 */ return "PCI-Express (BR02)"; } if(nv_pci_find_capability(PCI_CAP_ID_EXP)) return "PCI-Express"; else if(nv_pci_find_capability(PCI_CAP_ID_AGP)) return "AGP"; else return "PCI"; } /* Needs better bus checks .. return a string ?*/ static short get_agp_bus_rate() { int agp_rate, agp_status; /* If the card is an AGP card */ if(((nv_card->PEXTDEV[0x0/4] >> 14) & 0x1) == 0x1) { agp_status = nv_card->PMC[0x1848/4]; agp_rate = nv_card->PMC[0x184c/4] & 0x7; /* If true, the user has AGP8x support */ if(agp_status & 0x8) { agp_rate <<= 2; } return agp_rate; } return 0; } static char* get_agp_fw_status() { /* Check if Fast Writes is supported by the hostbridge */ if(((nv_card->PMC[0x1848/4] >> 4) & 0x1) == 1) return ((nv_card->PMC[0x184c/4] >> 4) & 0x1) ? "Enabled" : "Disabled"; else return "Unsupported"; } static char* get_agp_sba_status() { /* Check if Fast Writes is supported by the hostbridge */ if(((nv_card->PMC[0x1848/4] >> 9) & 0x1) == 1) return ((nv_card->PMC[0x184c/4] >> 9) & 0x1) ? "Enabled" : "Disabled"; else return "Unsupported"; } static char* get_agp_status() { return ((nv_card->PMC[0x184c/4] >> 8) & 0x1) ? "Enabled" : "Disabled"; } static char* get_agp_supported_rates() { int agp_rates, agp_status, i; static char *rate; agp_status = nv_card->PMC[0x1848/4]; agp_rates = nv_card->PMC[0x1848/4] & 0x7; /* If true, the user has AGP8x support */ if(agp_status & 0x8) { agp_rates <<= 2; } rate = (char*)calloc(1, sizeof(char)); for(i=1; i <= 8; i*=2) { if(agp_rates & i) { char *temp = (char*)malloc(4 * sizeof(char)); sprintf(temp, "%dX ", i); rate = (char*)realloc(rate, strlen(rate)+4); rate = strcat(rate, temp); free(temp); } } return rate; } static short get_pcie_bus_rate() { int pcie_rate, pcie_status_reg; pcie_status_reg = nv_pci_find_capability(PCI_CAP_ID_EXP); if(pcie_status_reg != 0 ) { /* The current pcie rate is stored in PCIE_LINKSTATUS (+18) but this would lead to a non-aligned read, / so read from PCIE_LINKCONTROL (+16) and then shift 2 bytes to get to PCIE_LINKSTATUS. */ pcie_rate = ((nv_card->PMC[(0x1800 + pcie_status_reg + PCIE_LINKCONTROL)/4] >> 16) & PCIE_LINK_SPEED_MASK) >> PCIE_LINK_SPEED_SHIFT; return pcie_rate; } return 0; } static short get_pcie_max_bus_rate() { int pcie_rate, pcie_status_reg; pcie_status_reg = nv_pci_find_capability(PCI_CAP_ID_EXP); if(pcie_status_reg != 0 ) { pcie_rate = (nv_card->PMC[(0x1800 + pcie_status_reg + PCIE_LINKCAP)/4] & PCIE_LINK_SPEED_MASK) >> PCIE_LINK_SPEED_SHIFT; return pcie_rate; } return 0; } static short get_memory_width() { /* Nforce / Nforce2 */ if((nv_card->device_id == 0x1a0) || (nv_card->device_id == 0x1f0)) return 64; /* GeforceFX cards (except for FX5200) need a different check */ /* What to do with NV40 cards ? */ else if(nv_card->arch & NV3X) { /* I got this info from the rivatuner forum. On the forum * is a thread containing register dumps from lots of cards. * It might not be 100% correct but it is better than a pci id check */ switch(nv_card->PFB[0x200/4] & 0x7) { /* 64bit FX5600 */ case 0x1: return 64; /* 128bit FX5800 */ case 0x3: return 128; /* 128bit FX5600, FX5700 */ case 0x5: return 128; /* 256bit FX5900 */ case 0x7: return 256; } } else if(nv_card->arch == NV44) { return 64; /* For now return 64; (Turbocache cards) */ } else if(nv_card->arch & NV4X) { /* Memory bandwith detection for nv40 but not sure if it is correct, it is atleast better than nothing */ switch(nv_card->PFB[0x200/4] & 0x7) { /* 128bit 6600GT */ case 0x1: return 128; /* 256bit 6800 */ case 0x3: return 256; default: return 128; } } /* Generic algorithm for cards up to the Geforce4 */ return (nv_card->PEXTDEV[0x0/4] & 0x17) ? 128 : 64; } static char* get_memory_type() { /* Nforce / Nforce2 */ if((nv_card->device_id == 0x1a0) || (nv_card->device_id == 0x1f0)) return ((pciReadLong(0x1, 0x7c) >> 12) & 0x1) ? "DDR" : "SDR"; else if(nv_card->arch & (NV2X | NV3X)) { /* Based on statistics found on the rivatuner forum, the first two bytes of * register 0x1218 of NV2X/NV3X boards, contains "0x0001 or 0x0101" in case of DDR memory and "0x0301" for DDR2. */ return (((nv_card->PMC[0x1218/4] >> 8) & 0x3) == 0x3) ? "DDR2" : "DDR"; } else if(nv_card->arch & (NV4X)) { /* On Geforce6/7 cards 0x100474 (PFB 0x474) can be used to distinguish between DDR and DDR3. * Note these values are based on the bios and it was noted that for instance bits in this register differ. * In case of DDR3 the first byte contains 0x4 while in case of DDR it contains 0x1. */ return (nv_card->PFB[0x474/4] & 0x4) ? "DDR3" : "DDR"; } else /* TNT, Geforce1/2/4MX */ return (nv_card->PFB[0x200/4] & 0x01) ? "DDR" : "SDR"; } static short get_memory_size() { short memory_size; /* If the card is something TNT based the calculation of the memory is different. */ if(nv_card->arch == NV5) { if(nv_card->PFB[0x0/4] & 0x100) memory_size = ((nv_card->PFB[0x0/4] >> 12) & 0xf)*2+2; else { switch(nv_card->PFB[0x0/4] & 0x3) { case 0: memory_size = 32; break; case 1: memory_size = 4; break; case 2: memory_size = 8; break; case 3: memory_size = 16; break; default: memory_size = 16; break; } } } /* Nforce 1 */ else if(nv_card->device_id == 0x1a0) { int32_t temp = pciReadLong(0x1, 0x7c); memory_size = ((temp >> 6) & 0x31) + 1; } /* Nforce2 */ else if(nv_card->device_id == 0x1f0) { int32_t temp = pciReadLong(0x1, 0x84); memory_size = ((temp >> 4) & 0x127) + 1; } /* Memory calculation for geforce cards or better.*/ else { /* The code below is needed to show more than 256MB of memory / but I'm not sure if 0xfff is safe for pre-geforcefx cards. / There's no clean way right now to use 0xff for those old cards / as currently the FX5200/FX5500 (which support 256MB) use the / pre-geforcefx backend. */ memory_size = (nv_card->PFB[0x20c/4] >> 20) & 0xfff; } return memory_size; } void info_init(void) { nv_card->get_bus_type = get_bus_type; /* Set the pci id again as the detected id might not be accurate in case of pci id modding. The OS doesn't allways update the id while it really changed! Only do it for cards without bridges (device_id != 0xf* */ if((nv_card->device_id & 0xfff0) != 0xf0) { nv_card->device_id = get_gpu_pci_id(); nv_card->card_name = (char*)get_card_name(nv_card->device_id, &nv_card->gpu); } /* gpu arch/revision */ nv_card->get_gpu_architecture = get_gpu_architecture; nv_card->get_gpu_revision = get_gpu_revision; /* Allow modding on all Geforce cards except for ones using bridges */ if((nv_card->arch & (NV1X | NV2X | NV3X | NV4X)) && ((nv_card->device_id & 0xfff0) != 0xf0)) { nv_card->caps |= GPU_ID_MODDING; nv_card->set_gpu_pci_id = set_gpu_pci_id; } else nv_card->set_gpu_pci_id = NULL; /* Check if card is a native AGP one and not using a bridge chip else we can't use the code below */ if(strcmp(nv_card->get_bus_type(), "AGP") == 0) { nv_card->get_bus_rate = get_agp_bus_rate; nv_card->get_agp_status = get_agp_status; nv_card->get_agp_fw_status = get_agp_fw_status; nv_card->get_agp_sba_status = get_agp_sba_status; nv_card->get_agp_supported_rates = get_agp_supported_rates; } /* Check if card is a native PCI-Express one and not using a bridge chip else we can't use the code below */ else if(strcmp(nv_card->get_bus_type(), "PCI-Express") == 0) { nv_card->get_bus_rate = get_pcie_bus_rate; nv_card->get_pcie_max_bus_rate = get_pcie_max_bus_rate; nv_card->get_agp_status = NULL; nv_card->get_agp_fw_status = NULL; nv_card->get_agp_sba_status = NULL; nv_card->get_agp_supported_rates = NULL; } else { nv_card->get_bus_rate = NULL; nv_card->get_agp_status = NULL; nv_card->get_agp_fw_status = NULL; nv_card->get_agp_sba_status = NULL; nv_card->get_agp_supported_rates = NULL; } /* Find out what memory is being used */ nv_card->mem_type = (nv_card->PFB[0x200/4] & 0x01) ? DDR : SDR; nv_card->get_memory_size = get_memory_size; nv_card->get_memory_type = get_memory_type; nv_card->get_memory_width = get_memory_width; }