/* NVClock 0.8 - Linux overclocker for NVIDIA cards
 *
 * site: http://nvclock.sourceforge.net
 *
 * Copyright(C) 2001-2007 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 <stdio.h>
#include "nvclock.h"

/* Convert the gpu architecture to a string using NVxx/Gxx naming */
int convert_gpu_architecture(short arch, char *buf)
{
	if(!buf)
		return 0;

	switch(arch)
	{
		case 0x46:
			sprintf(buf, "NV46/G72"); /* 7300 */
			break;
		case 0x47:
			sprintf(buf, "NV47/G70"); /* 7800 */
			break;
		case 0x49:
			sprintf(buf, "NV49/G71"); /* 7900 */
			break;
		case 0x4b:
			sprintf(buf, "NV4B/G73"); /* 7600 */
			break;
		case 0x4c: /* is this correct also a C51? */
		case 0x4e:
			sprintf(buf, "C51"); /* Geforce 6x00 nForce */
			break;
		// sprintf(buf, "C68"); /* Geforce 70xx nForce */ 
		case 0x50:
			sprintf(buf, "NV50/G80"); /* 8800 */
			break;
		case 0xa0:
			sprintf(buf, "GT200"); /* Geforce GTX260/280 */
			break;
		default:
			if(arch <= 0x44) /* The NV44/6200TC is the last card with only an NV name */
				sprintf(buf, "NV%X", arch);
			else /* Use Gxx for anything else */
				sprintf(buf, "G%X", arch);
	}
	return 1;
}

/* Convert a mask containing enabled/disabled pipelines for nv4x cards
/  to a binary string.
*/
void convert_unit_mask_to_binary(char mask, char hw_default, char *buf)
{
	int i, len;

	/* Count the number of pipelines on the card */
	for(i=0, len=0; i<8; i++)
		len += (hw_default & (1<<i)) ? 1 : 0;

	for(i=0; i<len; i++)
	{
		buf[i] = (mask & (1<<(len-i-1))) ? '1' : '0';
	}
	buf[len] = 0;
}

/* 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:
			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 0x190:
			arch = NV50; /* 8800 'NV50 / G80' */
			break;
		case 0x400: /* 8600 'G84' */
			arch = G84;
			break;
		case 0x420: /* 8500 'G86' */
			arch = G86;
			break;
		case 0x5e0: /* GT2x0 */
		case 0x5f0: /* GT2x0 */
		case 0xa20: /* GT216 */
		case 0xca0: /* GT215 */
		case 0xcb0: /* GT215 */
			arch = GT200;
			break;
		case 0x6e0: /* G98 */
		case 0x6f0: /* G98 */
		case 0x840: /* C7x */
		case 0x850: /* C7x */
		case 0x860: /* C79 */
		case 0x870: /* C7x */
			arch = G86;
			break;
		case 0x600: /* G92 */
		case 0x610: /* G92 */
			arch = G92;
			break;
		case 0x620: /* 9600GT 'G94' */
		case 0x630: /* GTS 150M / 9600GSO 'G94B' */
			arch = G94;
			break;
		case 0x640: /* 9500GT */
		case 0x650: /* G110M/G120M/G130M 'G96B' */
			arch = G96;
			break;
		case 0x240:
		case 0x3d0: /* not sure if this is a C51 too */
		case 0x530: /* not sure if the 70xx is C51 too */
		case 0x7e0: /* what architecture is the 71xx? */
			arch = C51;
			break;
		case 0x2e0:
		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 */
				case 0xf4: /* 6600LE */
					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;
				case 0x2e0: /* Geforce 7600GT AGP (at least Leadtek uses this id) */
				case 0x2e1: /* Geforce 7600GS AGP (at least BFG uses this id) */
				case 0x2e2: /* Geforce 7300GT AGP (at least a Galaxy 7300GT uses this id) */
					arch = NV4B;
					break;
				case 0x2e4: /* Geforce 7950 GT AGP */
					arch = NV49;
					break;
			}
			break;
		default:
			arch = UNKNOWN;
	}
	return arch;
}
