From 20ba898719877e6e99e72cd224ec0cf940123e65 Mon Sep 17 00:00:00 2001 From: Rudolf Cornelissen Date: Sun, 10 Jul 2005 17:58:46 +0000 Subject: [PATCH] added via accelerant, a copy of skeleton driver yet. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13605 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/accelerants/via/engine/info.c | 3126 +++++++++++++++++++++ 1 file changed, 3126 insertions(+) create mode 100644 src/add-ons/accelerants/via/engine/info.c diff --git a/src/add-ons/accelerants/via/engine/info.c b/src/add-ons/accelerants/via/engine/info.c new file mode 100644 index 0000000000..fe34991feb --- /dev/null +++ b/src/add-ons/accelerants/via/engine/info.c @@ -0,0 +1,3126 @@ +/* Read initialisation information from card */ +/* some bits are hacks, where PINS is not known */ +/* Author: + Rudolf Cornelissen 7/2003-10/2004 +*/ + +#define MODULE_BIT 0x00002000 + +#include "std.h" + +/* pins V5.16 and up ROM infoblock stuff */ +typedef struct { + uint16 InitScriptTablePtr; /* ptr to list of ptrs to scripts to exec */ + uint16 MacroIndexTablePtr; /* ptr to list with indexes and sizes of items in MacroTable */ + uint16 MacroTablePtr; /* ptr to list with items containing multiple 32bit reg writes */ + uint16 ConditionTablePtr; /* ptr to list of PCI regs and bits to tst for exec mode */ + uint16 IOConditionTablePtr; /* ptr to list of ISA regs and bits to tst for exec mode */ + uint16 IOFlagConditionTablePtr;/* ptr to list of ISA regs and bits to tst, ref'd to a matrix, for exec mode */ + uint16 InitFunctionTablePtr; /* ptr to list of startadresses of fixed ROM init routines */ +} PinsTables; + +static void detect_panels(void); +static void setup_output_matrix(void); +static void pinsnv4_fake(void); +static void pinsnv5_nv5m64_fake(void); +static void pinsnv6_fake(void); +static void pinsnv10_arch_fake(void); +static void pinsnv20_arch_fake(void); +static void pinsnv30_arch_fake(void); +static void getRAMsize_arch_nv4(void); +static void getstrap_arch_nv4(void); +static void getRAMsize_arch_nv10_20_30_40(void); +static void getstrap_arch_nv10_20_30_40(void); +static status_t pins2_read(uint8 *rom, uint32 offset); +static status_t pins3_5_read(uint8 *rom, uint32 offset); +static status_t coldstart_card(uint8* rom, uint16 init1, uint16 init2, uint16 init_size, uint16 ram_tab); +static status_t coldstart_card_516_up(uint8* rom, PinsTables tabs, uint16 ram_tab); +static status_t exec_type1_script(uint8* rom, uint16 adress, int16* size, uint16 ram_tab); +static status_t exec_type2_script(uint8* rom, uint16 adress, int16* size, PinsTables tabs, uint16 ram_tab); +static status_t exec_type2_script_mode(uint8* rom, uint16* adress, int16* size, PinsTables tabs, uint16 ram_tab, bool* exec); +static void exec_cmd_39_type2(uint8* rom, uint32 data, PinsTables tabs, bool* exec); +static void log_pll(uint32 reg, uint32 freq); +static void setup_ram_config(uint8* rom, uint16 ram_tab); +static void setup_ram_config_nv10_up(uint8* rom); +static void setup_ram_config_nv28(uint8* rom); +static status_t translate_ISA_PCI(uint32* reg); +static status_t eng_crtc_setup_fifo(void); + +/* Parse the BIOS PINS structure if there */ +status_t parse_pins () +{ + uint8 *rom; + uint8 chksum = 0; + int i; + uint32 offset; + status_t result = B_ERROR; + + /* preset PINS read status to failed */ + si->ps.pins_status = B_ERROR; + + /* check the validity of PINS */ + LOG(2,("INFO: Reading PINS info\n")); + rom = (uint8 *) si->rom_mirror; + /* check BIOS signature - this is defined in the PCI standard */ + if (rom[0]!=0x55 || rom[1]!=0xaa) + { + LOG(8,("INFO: BIOS signature not found\n")); + return B_ERROR; + } + LOG(2,("INFO: BIOS signature $AA55 found OK\n")); + + /* find the PINS struct adress */ + for (offset = 0; offset < 65536; offset++) + { + if (rom[offset ] != 0xff) continue; + if (rom[offset + 1] != 0x7f) continue; + if (rom[offset + 2] != 0x4e) continue; /* N */ + if (rom[offset + 3] != 0x56) continue; /* V */ + if (rom[offset + 4] != 0x00) continue; + + LOG(8,("INFO: PINS signature found\n")); + break; + } + + if (offset > 65535) + { + LOG(8,("INFO: PINS signature not found\n")); + return B_ERROR; + } + + /* verify PINS checksum */ + for (i = 0; i < 8; i++) + { + chksum += rom[offset + i]; + } + if (chksum) + { + LOG(8,("INFO: PINS checksum error\n")); + return B_ERROR; + } + + /* checkout PINS struct version */ + LOG(2,("INFO: PINS checksum is OK; PINS version is %d.%d\n", + rom[offset + 5], rom[offset + 6])); + + /* update the si->ps struct as far as is possible and coldstart card */ + //fixme: NV40 and up(?) nolonger use this system... + switch (rom[offset + 5]) + { + case 2: + result = pins2_read(rom, offset); + break; + case 3: + case 4: + case 5: + result = pins3_5_read(rom, offset); + break; + default: + LOG(8,("INFO: unknown PINS version\n")); + return B_ERROR; + break; + } + + /* check PINS read result */ + if (result == B_ERROR) + { + LOG(8,("INFO: PINS read/decode/execute error\n")); + return B_ERROR; + } + /* PINS scan succeeded */ + si->ps.pins_status = B_OK; + LOG(2,("INFO: PINS scan completed succesfully\n")); + return B_OK; +} + +static status_t pins2_read(uint8 *rom, uint32 offset) +{ + uint16 init1 = rom[offset + 18] + (rom[offset + 19] * 256); + uint16 init2 = rom[offset + 20] + (rom[offset + 21] * 256); + uint16 init_size = rom[offset + 22] + (rom[offset + 23] * 256) + 1; + /* confirmed by comparing cards */ + uint16 ram_tab = init1 - 0x0010; + /* fixme: PPC BIOSes (might) return NULL pointers for messages here */ + char* signon_msg = &(rom[(rom[offset + 24] + (rom[offset + 25] * 256))]); + char* vendor_name = &(rom[(rom[offset + 40] + (rom[offset + 41] * 256))]); + char* product_name = &(rom[(rom[offset + 42] + (rom[offset + 43] * 256))]); + char* product_rev = &(rom[(rom[offset + 44] + (rom[offset + 45] * 256))]); + + LOG(8,("INFO: cmdlist 1: $%04x, 2: $%04x, max. size $%04x\n", init1, init2, init_size)); + LOG(8,("INFO: signon msg:\n%s\n", signon_msg)); + LOG(8,("INFO: vendor name: %s\n", vendor_name)); + LOG(8,("INFO: product name: %s\n", product_name)); + LOG(8,("INFO: product rev: %s\n", product_rev)); + + return coldstart_card(rom, init1, init2, init_size, ram_tab); +} + +static status_t pins3_5_read(uint8 *rom, uint32 offset) +{ + uint16 init1 = rom[offset + 18] + (rom[offset + 19] * 256); + uint16 init2 = rom[offset + 20] + (rom[offset + 21] * 256); + uint16 init_size = rom[offset + 22] + (rom[offset + 23] * 256) + 1; + /* still confirm!! */ + uint16 ram_tab = init1 - 0x0010; + /* fixme: PPC BIOSes (might) return NULL pointers for messages here */ + char* signon_msg = &(rom[(rom[offset + 30] + (rom[offset + 31] * 256))]); + char* vendor_name = &(rom[(rom[offset + 46] + (rom[offset + 47] * 256))]); + char* product_name = &(rom[(rom[offset + 48] + (rom[offset + 49] * 256))]); + char* product_rev = &(rom[(rom[offset + 50] + (rom[offset + 51] * 256))]); + + LOG(8,("INFO: pre PINS 5.16 cmdlist 1: $%04x, 2: $%04x, max. size $%04x\n", init1, init2, init_size)); + LOG(8,("INFO: signon msg:\n%s\n", signon_msg)); + LOG(8,("INFO: vendor name: %s\n", vendor_name)); + LOG(8,("INFO: product name: %s\n", product_name)); + LOG(8,("INFO: product rev: %s\n", product_rev)); + + /* pins 5.06 and higher has VCO range info */ + if (((rom[offset + 5]) == 5) && ((rom[offset + 6]) >= 0x06)) + { + /* get PLL VCO range info */ + uint32 fvco_max = *((uint32*)(&(rom[offset + 67]))); + uint32 fvco_min = *((uint32*)(&(rom[offset + 71]))); + + LOG(8,("INFO: PLL VCO range is %dkHz - %dkHz\n", fvco_min, fvco_max)); + + /* modify presets to reflect card capability */ + si->ps.min_system_vco = fvco_min / 1000; + si->ps.max_system_vco = fvco_max / 1000; + //fixme: enable and modify PLL code... + //si->ps.min_pixel_vco = fvco_min / 1000; + //si->ps.max_pixel_vco = fvco_max / 1000; + //si->ps.min_video_vco = fvco_min / 1000; + //si->ps.max_video_vco = fvco_max / 1000; + } + + //fixme: add 'parsing scripts while not actually executing' as warmstart method, + // instead of not parsing at all: this will update the driver's speeds + // as below, while logging the scripts as well (for our learning pleasure :) + + /* pins 5.16 and higher is more extensive, and works differently from before */ + if (((rom[offset + 5]) == 5) && ((rom[offset + 6]) >= 0x10)) + { + /* pins 5.16 and up have a more extensive command list table, and have more + * commands to choose from as well. */ + PinsTables tabs; + tabs.InitScriptTablePtr = rom[offset + 75] + (rom[offset + 76] * 256); + tabs.MacroIndexTablePtr = rom[offset + 77] + (rom[offset + 78] * 256); + tabs.MacroTablePtr = rom[offset + 79] + (rom[offset + 80] * 256); + tabs.ConditionTablePtr = rom[offset + 81] + (rom[offset + 82] * 256); + tabs.IOConditionTablePtr = rom[offset + 83] + (rom[offset + 84] * 256); + tabs.IOFlagConditionTablePtr = rom[offset + 85] + (rom[offset + 86] * 256); + tabs.InitFunctionTablePtr = rom[offset + 87] + (rom[offset + 88] * 256); + + LOG(8,("INFO: PINS 5.16 and later cmdlist pointers:\n")); + LOG(8,("INFO: InitScriptTablePtr: $%04x\n", tabs.InitScriptTablePtr)); + LOG(8,("INFO: MacroIndexTablePtr: $%04x\n", tabs.MacroIndexTablePtr)); + LOG(8,("INFO: MacroTablePtr: $%04x\n", tabs.MacroTablePtr)); + LOG(8,("INFO: ConditionTablePtr: $%04x\n", tabs.ConditionTablePtr)); + LOG(8,("INFO: IOConditionTablePtr: $%04x\n", tabs.IOConditionTablePtr)); + LOG(8,("INFO: IOFlagConditionTablePtr: $%04x\n", tabs.IOFlagConditionTablePtr)); + LOG(8,("INFO: InitFunctionTablePtr: $%04x\n", tabs.InitFunctionTablePtr)); + + return coldstart_card_516_up(rom, tabs, ram_tab); + } + else + { + /* pre 'pins 5.16' still uses the 'old' method in which the command list + * table always has two entries. */ + return coldstart_card(rom, init1, init2, init_size, ram_tab); + } +} + +static status_t coldstart_card(uint8* rom, uint16 init1, uint16 init2, uint16 init_size, uint16 ram_tab) +{ + status_t result = B_OK; + int16 size = init_size; + + LOG(8,("INFO: now executing coldstart...\n")); + + /* select colormode CRTC registers base adresses */ + ENG_REG8(RG8_MISCW) = 0xcb; + + /* unknown.. */ + ENG_REG8(RG8_VSE2) = 0x01; + + /* enable access to primary head */ + set_crtc_owner(0); + /* unlock head's registers for R/W access */ + CRTCW(LOCK, 0x57); + CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + /* disable RMA as it's not used */ + /* (RMA is the cmd register for the 32bit port in the GPU to access 32bit registers + * and framebuffer via legacy ISA I/O space.) */ + CRTCW(RMA, 0x00); + + if (si->ps.secondary_head) + { + /* enable access to secondary head */ + set_crtc_owner(1); + /* unlock head's registers for R/W access */ + CRTC2W(LOCK, 0x57); + CRTC2W(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + } + + /* turn off both displays and the hardcursors (also disables transfers) */ + eng_crtc_dpms(false, false, false); + eng_crtc_cursor_hide(); + if (si->ps.secondary_head) + { + eng_crtc2_dpms(false, false, false); + eng_crtc2_cursor_hide(); + } + + /* execute BIOS coldstart script(s) */ + if (init1 || init2) + { + if (init1) + if (exec_type1_script(rom, init1, &size, ram_tab) != B_OK) result = B_ERROR; + if (init2 && (result == B_OK)) + if (exec_type1_script(rom, init2, &size, ram_tab) != B_OK) result = B_ERROR; + + /* now enable ROM shadow or the card will remain shut-off! */ + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) |= 0x00000001)); + + //temporary: should be called from setmode probably.. + eng_crtc_setup_fifo(); + } + else + { + result = B_ERROR; + } + + if (result != B_OK) + LOG(8,("INFO: coldstart failed.\n")); + else + LOG(8,("INFO: coldstart execution completed OK.\n")); + + return result; +} + +static status_t coldstart_card_516_up(uint8* rom, PinsTables tabs, uint16 ram_tab) +{ + status_t result = B_OK; + uint16 adress; + uint32 fb_mrs1 = 0; + uint32 fb_mrs2 = 0; + + LOG(8,("INFO: now executing coldstart...\n")); + + /* get some strapinfo(?) for NV28 framebuffer access */ + //fixme?: works on at least one NV28... how about other cards? + if (si->ps.card_type == NV28) + { + fb_mrs2 = ENG_RG32(RG32_FB_MRS2); + fb_mrs1 = ENG_RG32(RG32_FB_MRS1); + } + + /* select colormode CRTC registers base adresses */ + ENG_REG8(RG8_MISCW) = 0xcb; + + /* unknown.. */ + ENG_REG8(RG8_VSE2) = 0x01; + + /* enable access to primary head */ + set_crtc_owner(0); + /* unlock head's registers for R/W access */ + CRTCW(LOCK, 0x57); + CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + /* disable RMA as it's not used */ + /* (RMA is the cmd register for the 32bit port in the GPU to access 32bit registers + * and framebuffer via legacy ISA I/O space.) */ + CRTCW(RMA, 0x00); + + if (si->ps.secondary_head) + { + /* enable access to secondary head */ + set_crtc_owner(1); + /* unlock head's registers for R/W access */ + CRTC2W(LOCK, 0x57); + CRTC2W(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + } + + /* turn off both displays and the hardcursors (also disables transfers) */ + eng_crtc_dpms(false, false, false); + eng_crtc_cursor_hide(); + if (si->ps.secondary_head) + { + eng_crtc2_dpms(false, false, false); + eng_crtc2_cursor_hide(); + } + + /* execute all BIOS coldstart script(s) */ + if (tabs.InitScriptTablePtr) + { + /* size is nolonger used, keeping it anyway for testing purposes :) */ + int16 size = 32767; + uint16 index = tabs.InitScriptTablePtr; + + adress = *((uint16*)(&(rom[index]))); + if (!adress) + { + LOG(8,("INFO: no cmdlist found!\n")); + result = B_ERROR; + } + + while (adress && (result == B_OK)) + { + result = exec_type2_script(rom, adress, &size, tabs, ram_tab); + /* next command script, please */ + index += 2; + adress = *((uint16*)(&(rom[index]))); + } + + /* do some NV28 specific extra stuff */ + //fixme: NV28 only?? + if (si->ps.card_type == NV28) + { + /* setup PTIMER */ + ACCW(PT_NUMERATOR, (si->ps.std_engine_clock * 20)); + ACCW(PT_DENOMINATR, 0x00000271); + + /* get NV28 RAM access up and running */ + //fixme?: works on at least one NV28... how about other cards? + ENG_RG32(RG32_FB_MRS2) = fb_mrs2; + ENG_RG32(RG32_FB_MRS1) = fb_mrs1; + } + + /* now enable ROM shadow or the card will remain shut-off! */ + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) |= 0x00000001)); + + //temporary: should be called from setmode probably.. + eng_crtc_setup_fifo(); + } + else + { + result = B_ERROR; + } + + if (result != B_OK) + LOG(8,("INFO: coldstart failed.\n")); + else + LOG(8,("INFO: coldstart execution completed OK.\n")); + + return result; +} + +/* This routine is complete, and is used for pre-NV10 cards. It's tested on a Elsa + * Erazor III with TNT2 (NV05) and on a no-name TNT2-M64. Both cards coldstart + * perfectly. */ +static status_t exec_type1_script(uint8* rom, uint16 adress, int16* size, uint16 ram_tab) +{ + status_t result = B_OK; + bool end = false; + bool exec = true; + uint8 index, byte; + uint32 reg, data, data2, and_out, or_in; + + LOG(8,("\nINFO: executing type1 script at adress $%04x...\n", adress)); + LOG(8,("INFO: ---Executing following command(s):\n")); + + while (!end) + { + LOG(8,("INFO: $%04x ($%02x); ", adress, rom[adress])); + + switch (rom[adress]) + { + case 0x59: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + data2 = *((uint16*)(&(rom[data]))); + LOG(8,("cmd 'calculate indirect and set PLL 32bit reg $%08x for %.3fMHz'\n", + reg, ((float)data2))); + if (exec) + { + float calced_clk; + uint8 m, n, p; + eng_dac_sys_pll_find(((float)data2), &calced_clk, &m, &n, &p, 0); + ENG_RG32(reg) = ((p << 16) | (n << 8) | m); + } + log_pll(reg, data2); + break; + case 0x5a: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + data2 = *((uint32*)(&(rom[data]))); + LOG(8,("cmd 'WR indirect 32bit reg' $%08x = $%08x\n", reg, data2)); + if (exec) ENG_RG32(reg) = data2; + break; + case 0x63: + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + LOG(8,("cmd 'setup RAM config' (always done)\n")); + /* always done */ + setup_ram_config(rom, ram_tab); + break; + case 0x65: + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint32*)(&(rom[adress]))); + adress += 4; + data2 = *((uint32*)(&(rom[adress]))); + adress += 4; + LOG(8,("cmd 'WR 32bit reg $%08x = $%08x, then = $%08x' (always done)\n", + reg, data, data2)); + /* always done */ + ENG_RG32(reg) = data; + ENG_RG32(reg) = data2; + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) & 0xfffffffe)); + break; + case 0x69: + *size -= 5; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint16*)(&(rom[adress]))); + adress += 2; + and_out = *((uint8*)(&(rom[adress]))); + adress += 1; + or_in = *((uint8*)(&(rom[adress]))); + adress += 1; + LOG(8,("cmd 'RD 8bit ISA reg $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + reg, and_out, or_in)); + if (exec) + { + translate_ISA_PCI(®); + byte = ENG_REG8(reg); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + ENG_REG8(reg) = byte; + } + break; + case 0x6d: + *size -= 3; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + data = ENG_RG32(RG32_NV4STRAPINFO); + and_out = *((uint8*)(&(rom[adress]))); + adress += 1; + byte = *((uint8*)(&(rom[adress]))); + adress += 1; + data &= (uint32)and_out; + LOG(8,("cmd 'CHK bits AND-out $%02x RAMCFG for $%02x'\n", + and_out, byte)); + if (((uint8)data) != byte) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x6e: + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + and_out = *((uint32*)(&(rom[adress]))); + adress += 4; + or_in = *((uint32*)(&(rom[adress]))); + adress += 4; + LOG(8,("cmd 'RD 32bit reg $%08x, AND-out = $%08x, OR-in = $%08x, WR-bk'\n", + reg, and_out, or_in)); + if (exec) + { + data = ENG_RG32(reg); + data &= and_out; + data |= or_in; + ENG_RG32(reg) = data; + } + break; + case 0x71: + LOG(8,("cmd 'END', execution completed.\n\n")); + end = true; + + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + break; + case 0x72: + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + adress += 1; + LOG(8,("cmd 'PGM commands'\n")); + LOG(8,("INFO: ---Executing following command(s):\n")); + exec = true; + break; + case 0x73: + *size -= 9; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + data = ENG_RG32(RG32_NVSTRAPINFO2); + and_out = *((uint32*)(&(rom[adress]))); + adress += 4; + data2 = *((uint32*)(&(rom[adress]))); + adress += 4; + data &= and_out; + LOG(8,("cmd 'CHK bits AND-out $%08x STRAPCFG2 for $%08x'\n", + and_out, data2)); + if (data != data2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x74: + *size -= 3; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + LOG(8,("cmd 'SNOOZE for %d ($%04x) microSeconds' (always done)\n", data, data)); + /* always done */ + snooze(data); + break; + case 0x77: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + LOG(8,("cmd 'WR 32bit reg' $%08x = $%08x (b31-16 = '0', b15-0 = data)\n", + reg, data)); + if (exec) ENG_RG32(reg) = data; + break; + case 0x78: + *size -= 6; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint16*)(&(rom[adress]))); + adress += 2; + index = *((uint8*)(&(rom[adress]))); + adress += 1; + and_out = *((uint8*)(&(rom[adress]))); + adress += 1; + or_in = *((uint8*)(&(rom[adress]))); + adress += 1; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + index, reg, and_out, or_in)); + if (exec) + { + translate_ISA_PCI(®); + ENG_REG8(reg) = index; + byte = ENG_REG8(reg + 1); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + ENG_REG8(reg + 1) = byte; + } + break; + case 0x79: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint16*)(&(rom[adress]))); + adress += 2; + LOG(8,("cmd 'calculate and set PLL 32bit reg $%08x for %.3fMHz'\n", reg, (data / 100.0))); + if (exec) + { + float calced_clk; + uint8 m, n, p; + eng_dac_sys_pll_find((data / 100.0), &calced_clk, &m, &n, &p, 0); + ENG_RG32(reg) = ((p << 16) | (n << 8) | m); + } + log_pll(reg, (data / 100)); + break; + case 0x7a: + *size -= 9; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + adress += 1; + reg = *((uint32*)(&(rom[adress]))); + adress += 4; + data = *((uint32*)(&(rom[adress]))); + adress += 4; + LOG(8,("cmd 'WR 32bit reg' $%08x = $%08x\n", reg, data)); + if (exec) ENG_RG32(reg) = data; + break; + default: + LOG(8,("unknown cmd, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + } + + return result; +} + +static void log_pll(uint32 reg, uint32 freq) +{ + if ((si->ps.card_type == NV31) || (si->ps.card_type == NV36)) + LOG(8,("INFO: ---WARNING: check/update PLL programming script code!!!\n")); + switch (reg) + { + case RG32_MEMPLL: + LOG(8,("INFO: ---Memory PLL accessed.\n")); + /* update the card's specs */ + si->ps.std_memory_clock = freq; + break; + case RG32_COREPLL: + LOG(8,("INFO: ---Core PLL accessed.\n")); + /* update the card's specs */ + si->ps.std_engine_clock = freq; + break; + case ENDAC_PIXPLLC: + LOG(8,("INFO: ---DAC1 PLL accessed.\n")); + break; + case ENDAC2_PIXPLLC: + LOG(8,("INFO: ---DAC2 PLL accessed.\n")); + break; + /* unexpected cases, here for learning goals... */ + case RG32_MEMPLL2: + LOG(8,("INFO: ---NV31/NV36 extension to memory PLL accessed only!\n")); + break; + case RG32_COREPLL2: + LOG(8,("INFO: ---NV31/NV36 extension to core PLL accessed only!\n")); + break; + case ENDAC_PIXPLLC2: + LOG(8,("INFO: ---NV31/NV36 extension to DAC1 PLL accessed only!\n")); + break; + case ENDAC2_PIXPLLC2: + LOG(8,("INFO: ---NV31/NV36 extension to DAC2 PLL accessed only!\n")); + break; + default: + LOG(8,("INFO: ---Unknown PLL accessed!\n")); + break; + } +} + +static void setup_ram_config(uint8* rom, uint16 ram_tab) +{ + uint32 ram_cfg, data; + uint8 cnt; + + /* set MRS = 256 */ + ENG_RG32(RG32_PFB_DEBUG_0) &= 0xffffffef; + /* read RAM config hardware(?) strap */ + ram_cfg = ((ENG_RG32(RG32_NVSTRAPINFO2) >> 2) & 0x0000000f); + LOG(8,("INFO: ---RAM config strap is $%01x\n", ram_cfg)); + /* use it as a pointer in a BIOS table for prerecorded RAM configurations */ + ram_cfg = *((uint16*)(&(rom[(ram_tab + (ram_cfg * 2))]))); + /* log info */ + switch (ram_cfg & 0x00000003) + { + case 0: + LOG(8,("INFO: ---32Mb RAM should be connected\n")); + break; + case 1: + LOG(8,("INFO: ---4Mb RAM should be connected\n")); + break; + case 2: + LOG(8,("INFO: ---8Mb RAM should be connected\n")); + break; + case 3: + LOG(8,("INFO: ---16Mb RAM should be connected\n")); + break; + } + if (ram_cfg & 0x00000004) + LOG(8,("INFO: ---RAM should be 128bits wide\n")); + else + LOG(8,("INFO: ---RAM should be 64bits wide\n")); + switch ((ram_cfg & 0x00000038) >> 3) + { + case 0: + LOG(8,("INFO: ---RAM type: 8Mbit SGRAM\n")); + break; + case 1: + LOG(8,("INFO: ---RAM type: 16Mbit SGRAM\n")); + break; + case 2: + LOG(8,("INFO: ---RAM type: 4 banks of 16Mbit SGRAM\n")); + break; + case 3: + LOG(8,("INFO: ---RAM type: 16Mbit SDRAM\n")); + break; + case 4: + LOG(8,("INFO: ---RAM type: 64Mbit SDRAM\n")); + break; + case 5: + LOG(8,("INFO: ---RAM type: 64Mbit x16 SDRAM\n")); + break; + } + /* set RAM amount, width and type */ + data = (ENG_RG32(RG32_NV4STRAPINFO) & 0xffffffc0); + ENG_RG32(RG32_NV4STRAPINFO) = (data | (ram_cfg & 0x0000003f)); + /* setup write to read delay (?) */ + data = (ENG_RG32(RG32_PFB_CONFIG_1) & 0xff8ffffe); + data |= ((ram_cfg & 0x00000700) << 12); + /* force update via b0 = 0... */ + ENG_RG32(RG32_PFB_CONFIG_1) = data; + /* ... followed by b0 = 1(?) */ + ENG_RG32(RG32_PFB_CONFIG_1) = (data | 0x00000001); + + /* do RAM width test to confirm RAM width set to be correct */ + /* write testpattern to first 128 bits of graphics memory... */ + data = 0x4e563541; + for (cnt = 0; cnt < 4; cnt++) + ((uint32 *)si->framebuffer)[cnt] = data; + /* ... if second 64 bits does not contain the testpattern we are apparantly + * set to 128bits width while we should be set to 64bits width, so correct. */ + if (((uint32 *)si->framebuffer)[3] != data) + { + LOG(8,("INFO: ---RAM width tested: width is 64bits, correcting settings.\n")); + ENG_RG32(RG32_NV4STRAPINFO) &= ~0x00000004; + } + else + { + LOG(8,("INFO: ---RAM width tested: access is OK.\n")); + } + + //fixme?: do RAM size test +} + +/* this routine is used for NV10 and later */ +static status_t exec_type2_script(uint8* rom, uint16 adress, int16* size, PinsTables tabs, uint16 ram_tab) +{ + bool exec = true; + + LOG(8,("\nINFO: executing type2 script at adress $%04x...\n", adress)); + LOG(8,("INFO: ---Executing following command(s):\n")); + + return exec_type2_script_mode(rom, &adress, size, tabs, ram_tab, &exec); +} + +/* this routine is used for NV10 and later. It's tested on a GeForce2 MX400 (NV11), + * GeForce4 MX440 (NV18), GeForce4 Ti4200 (NV28) and a GeForceFX 5200 (NV34). + * These cards coldstart perfectly. */ +static status_t exec_type2_script_mode(uint8* rom, uint16* adress, int16* size, PinsTables tabs, uint16 ram_tab, bool* exec) +{ + status_t result = B_OK; + bool end = false; + uint8 index, byte, byte2, shift; + uint32 reg, reg2, data, data2, and_out, and_out2, or_in, or_in2, safe32, offset32, size32; + + while (!end) + { + LOG(8,("INFO: $%04x ($%02x); ", *adress, rom[*adress])); + + /* all commands are here (verified NV11 and NV28) */ + switch (rom[*adress]) + { + case 0x31: /* new */ + *size -= (15 + ((*((uint8*)(&(rom[(*adress + 10)])))) << 2)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + and_out = *((uint32*)(&(rom[*adress]))); + *adress += 4; + shift = *((uint8*)(&(rom[*adress]))); + *adress += 1; + size32 = ((*((uint8*)(&(rom[*adress])))) << 2); + *adress += 1; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD 32bit reg $%08x, AND-out = $%08x, shift-right = $%02x,\n", + reg, and_out, shift)); + LOG(8,("INFO: (cont.) RD 32bit data from subtable with size $%04x, at offset (result << 2),\n", + size32)); + LOG(8,("INFO: (cont.) then WR result data to 32bit reg $%08x'\n", reg2)); + if (*exec && reg2) + { + data = ENG_RG32(reg); + data &= and_out; + data >>= shift; + data2 = *((uint32*)(&(rom[(*adress + (data << 2))]))); + ENG_RG32(reg2) = data2; + } + *adress += size32; + break; + case 0x32: /* new */ + *size -= (11 + ((*((uint8*)(&(rom[(*adress + 6)])))) << 2)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + byte2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + size32 = ((*((uint8*)(&(rom[*adress])))) << 2); + *adress += 1; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, shift-right = $%02x,\n", + index, reg, and_out, byte2)); + LOG(8,("INFO: (cont.) RD 32bit data from subtable with size $%04x, at offset (result << 2),\n", + size32)); + LOG(8,("INFO: (cont.) then WR result data to 32bit reg $%08x'\n", reg2)); + if (*exec && reg2) + { + translate_ISA_PCI(®); + ENG_REG8(reg) = index; + byte = ENG_REG8(reg + 1); + byte &= (uint8)and_out; + byte >>= byte2; + offset32 = (byte << 2); + data = *((uint32*)(&(rom[(*adress + offset32)]))); + ENG_RG32(reg2) = data; + } + *adress += size32; + break; + case 0x33: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + size32 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + /* executed 1-256 times */ + if (!size32) size32 = 256; + /* remember where to start each time */ + safe32 = *adress; + LOG(8,("cmd 'execute following part of this script $%03x times' (always done)\n", size32)); + for (offset32 = 0; offset32 < size32; offset32++) + { + LOG(8,("\nINFO: (#$%02x) executing part of type2 script at adress $%04x...\n", + offset32, *adress)); + LOG(8,("INFO: ---Not touching 'execution' mode at this time:\n")); + *adress = safe32; + result = exec_type2_script_mode(rom, adress, size, tabs, ram_tab, exec); + } + LOG(8,("INFO: ---Continuing script:\n")); + break; + case 0x34: /* new */ + *size -= (12 + ((*((uint8*)(&(rom[(*adress + 7)])))) << 1)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + shift = *((uint8*)(&(rom[*adress]))); + *adress += 1; + offset32 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + size32 = ((*((uint8*)(&(rom[*adress])))) << 1); + *adress += 1; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, shift-right = $%02x,\n", + index, reg, and_out, shift)); + LOG(8,("INFO: (cont.) RD 16bit PLL frequency to pgm from subtable with size $%04x, at offset (result << 1),\n", + size32)); + LOG(8,("INFO: (cont.) RD table-index ($%02x) for cmd $39'\n", + offset32)); + translate_ISA_PCI(®); + ENG_REG8(reg) = index; + byte = ENG_REG8(reg + 1); + byte &= (uint8)and_out; + data = (byte >> shift); + data <<= 1; + data2 = *((uint16*)(&(rom[(*adress + data)]))); + if (offset32 < 0x80) + { + bool double_f = true; + LOG(8,("INFO: Do subcmd ($39); ")); + exec_cmd_39_type2(rom, offset32, tabs, &double_f); + LOG(8,("INFO: (cont. cmd $34) Doubling PLL frequency to be set for cmd $34.\n")); + if (double_f) data2 <<= 1; + LOG(8,("INFO: ---Reverting to pre-subcmd ($39) 'execution' mode.\n")); + } + else + { + LOG(8,("INFO: table index is negative, not executing subcmd ($39).\n")); + } + LOG(8,("INFO: (cont.) 'calc and set PLL 32bit reg $%08x for %.3fMHz'\n", + reg2, (data2 / 100.0))); + if (*exec && reg2) + { + float calced_clk; + uint8 m, n, p; + eng_dac_sys_pll_find((data2 / 100.0), &calced_clk, &m, &n, &p, 0); + /* programming the PLL needs to be done in steps! (confirmed NV28) */ + data = ENG_RG32(reg2); + ENG_RG32(reg2) = ((data & 0xffff0000) | (n << 8) | m); + data = ENG_RG32(reg2); + ENG_RG32(reg2) = ((p << 16) | (n << 8) | m); +//fixme? + /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ +// if ((si->ps.card_type == NV31) || (si->ps.card_type == NV36)) +// DACW(PIXPLLC2, 0x80000401); + } + log_pll(reg2, (data2 / 100)); + *adress += size32; + break; + case 0x35: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + offset32 = (byte << 1); + offset32 += tabs.InitFunctionTablePtr; + LOG(8,("cmd 'execute fixed VGA BIOS routine #$%02x at adress $%04x'\n", + byte, offset32)); + /* note: + * This command is BIOS/'pins' version specific. Confirmed a NV28 having NO + * entries at all in InitFunctionTable! + * (BIOS version 4.28.20.05.11; 'pins' version 5.21) */ + //fixme: impl. if it turns out this cmd is used.. (didn't see that yet) + if (*exec) + { + //fixme: add BIOS/'pins' version dependancy... + switch(byte) + { + default: + LOG(8,("\n\nINFO: WARNING: function not implemented, skipping!\n\n")); + break; + } + } + break; + case 0x37: /* new */ + *size -= 11; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + byte2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + reg2 = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'RD 32bit reg $%08x, shift-right = $%02x, AND-out lsb = $%02x,\n", + reg, byte2, and_out)); + LOG(8,("INFO: (cont.) RD 8bit ISA reg $%02x via $%04x, AND-out = $%02x, OR-in lsb result 32bit, WR-bk'\n", + index, reg2, and_out2)); + if (*exec) + { + data = ENG_RG32(reg); + if (byte2 < 0x80) + { + data >>= byte2; + } + else + { + data <<= (0x0100 - byte2); + } + data &= and_out; + translate_ISA_PCI(®2); + ENG_REG8(reg2) = index; + byte = ENG_REG8(reg2 + 1); + byte &= (uint8)and_out2; + byte |= (uint8)data; + ENG_REG8(reg2 + 1) = byte; + } + break; + case 0x38: /* new */ + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + LOG(8,("cmd 'invert current mode'\n")); + *exec = !(*exec); + if (*exec) + LOG(8,("INFO: ---Executing following command(s):\n")); + else + LOG(8,("INFO: ---Not executing following command(s):\n")); + break; + case 0x39: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + exec_cmd_39_type2(rom, data, tabs, exec); + break; + case 0x49: /* new */ + size32 = *((uint8*)(&(rom[*adress + 17]))); + if (!size32) size32 = 256; + *size -= (18 + (size32 << 1)); + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + reg2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + and_out = *((uint32*)(&(rom[*adress]))); + *adress += 4; + or_in = *((uint32*)(&(rom[*adress]))); + *adress += 4; + size32 = *((uint8*)(&(rom[*adress]))); + if (!size32) size32 = 256; + *adress += 1; + LOG(8,("cmd 'do following cmd structure $%03x time(s)':\n", size32)); + for (offset32 = 0; offset32 < size32; offset32++) + { + or_in2 = *((uint8*)(&(rom[(*adress + (offset32 << 1))]))); + data2 = *((uint8*)(&(rom[(*adress + (offset32 << 1) + 1)]))); + LOG(8,("INFO (cont.) (#$%02x) cmd 'WR 32bit reg $%08x = $%08x, RD 32bit reg $%08x,\n", + offset32, reg2, data2, reg)); + LOG(8,("INFO (cont.) AND-out $%08x, OR-in $%08x, OR-in $%08x, WR-bk'\n", + and_out, or_in, or_in2)); + } + if (*exec) + { + for (index = 0; index < size32; index++) + { + or_in2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data2 = *((uint8*)(&(rom[*adress]))); + *adress += 1; + ENG_RG32(reg2) = data2; + data = ENG_RG32(reg); + data &= and_out; + data |= or_in; + data |= or_in2; + ENG_RG32(reg) = data; + } + } + else + { + *adress += (size32 << 1); + } + break; + case 0x61: /* new */ + *size -= 4; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'WR ISA reg $%04x = $%02x'\n", reg, byte)); + if (*exec) + { + translate_ISA_PCI(®); + ENG_REG8(reg) = byte; + } + break; + case 0x62: /* new */ + *size -= 5; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'WR idx ISA reg $%02x via $%04x = $%02x'\n", index, reg, byte)); + if (*exec) + { + translate_ISA_PCI(®); + ENG_REG16(reg) = ((((uint16)byte) << 8) | index); + } + break; + case 0x63: /* new setup compared to pre-NV10 version */ + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + LOG(8,("cmd 'setup RAM config' (always done)\n")); + /* always done */ + switch (si->ps.card_type) + { + case NV28: + setup_ram_config_nv28(rom); + break; + default: + setup_ram_config_nv10_up(rom); + break; + } + break; + case 0x65: /* identical to type1 */ + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data2 = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'WR 32bit reg $%08x = $%08x, then = $%08x' (always done)\n", + reg, data, data2)); + /* always done */ + ENG_RG32(reg) = data; + ENG_RG32(reg) = data2; + CFGW(ROMSHADOW, (CFGR(ROMSHADOW) & 0xfffffffe)); + break; + case 0x69: /* identical to type1 */ + *size -= 5; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + or_in = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'RD 8bit ISA reg $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + reg, and_out, or_in)); + if (*exec) + { + translate_ISA_PCI(®); + byte = ENG_REG8(reg); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + ENG_REG8(reg) = byte; + } + break; + case 0x6a: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data2 = *((uint16*)(&(rom[(tabs.InitScriptTablePtr + (data << 1))]))); + LOG(8,("cmd 'jump to script #$%02x at adress $%04x'\n", data, data2)); + if (*exec) + { + *adress = data2; + LOG(8,("INFO: ---Jumping; not touching 'execution' mode.\n")); + } + break; + case 0x6b: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data2 = *((uint16*)(&(rom[(tabs.InitScriptTablePtr + (data << 1))]))); + LOG(8,("cmd 'gosub script #$%02x at adress $%04x'\n", data, data2)); + if (*exec && data2) + { + result = exec_type2_script(rom, data2, size, tabs, ram_tab); + LOG(8,("INFO: ---Reverting to pre-gosub 'execution' mode.\n")); + } + break; + case 0x6e: /* identical to type1 */ + *size -= 13; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + and_out = *((uint32*)(&(rom[*adress]))); + *adress += 4; + or_in = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'RD 32bit reg $%08x, AND-out = $%08x, OR-in = $%08x, WR-bk'\n", + reg, and_out, or_in)); + if (*exec) + { + data = ENG_RG32(reg); + data &= and_out; + data |= or_in; + ENG_RG32(reg) = data; + } + break; + case 0x6f: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + byte = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data = tabs.MacroIndexTablePtr + (byte << 1); + offset32 = (*((uint8*)(&(rom[data]))) << 3); + size32 = *((uint8*)(&(rom[(data + 1)]))); + offset32 += tabs.MacroTablePtr; + /* note: min 1, max 255 commands can be requested */ + LOG(8,("cmd 'do $%02x time(s) a 32bit reg WR with 32bit data' (MacroIndexTable idx = $%02x):\n", + size32, byte)); + safe32 = 0; + while (safe32 < size32) + { + reg2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3))]))); + data2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3) + 4)]))); + LOG(8,("INFO: (cont.) (#$%02x) cmd 'WR 32bit reg' $%08x = $%08x\n", + safe32, reg2, data2)); + safe32++; + } + if (*exec) + { + safe32 = 0; + while (safe32 < size32) + { + reg2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3))]))); + data2 = *((uint32*)(&(rom[(offset32 + (safe32 << 3) + 4)]))); + ENG_RG32(reg2) = data2; + safe32++; + } + } + break; + case 0x36: /* new */ + case 0x66: /* new */ + case 0x67: /* new */ + case 0x68: /* new */ + case 0x6c: /* new */ + case 0x71: /* identical to type1 */ + LOG(8,("cmd 'END', execution completed.\n\n")); + end = true; + + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; /* needed to make cmd #$33 work correctly! */ + break; + case 0x72: /* identical to type1 */ + *size -= 1; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; + LOG(8,("cmd 'PGM commands'\n")); + LOG(8,("INFO: ---Executing following command(s):\n")); + *exec = true; + break; + case 0x74: /* identical to type1 */ + //fixme? on at least NV28 this cmd hammers the CRTC PCI-timeout register + //'data' number of times instead of snoozing. + //Couldn't see any diff in behaviour though! + *size -= 3; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + data = *((uint16*)(&(rom[*adress]))); + *adress += 2; + LOG(8,("cmd 'SNOOZE for %d ($%04x) microSeconds' (always done)\n", data, data)); + /* always done */ + snooze(data); + break; + case 0x75: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data *= 12; + data += tabs.ConditionTablePtr; + reg = *((uint32*)(&(rom[data]))); + and_out = *((uint32*)(&(rom[(data + 4)]))); + data2 = *((uint32*)(&(rom[(data + 8)]))); + data = ENG_RG32(reg); + data &= and_out; + LOG(8,("cmd 'CHK bits AND-out $%08x reg $%08x for $%08x'\n", + and_out, reg, data2)); + if (data != data2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + *exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x76: /* new */ + *size -= 2; + if (*size < 0) + { + LOG(8,("script size error!\n\n")); + result = B_ERROR; + } + + /* execute */ + *adress += 1; + data = *((uint8*)(&(rom[*adress]))); + *adress += 1; + data *= 5; + data += tabs.IOConditionTablePtr; + reg = *((uint16*)(&(rom[data]))); + index = *((uint8*)(&(rom[(data + 2)]))); + and_out = *((uint8*)(&(rom[(data + 3)]))); + byte2 = *((uint8*)(&(rom[(data + 4)]))); + LOG(8,("cmd 'CHK bits AND-out $%02x idx ISA reg $%02x via $%04x for $%02x'\n", + and_out, index, reg, byte2)); + translate_ISA_PCI(®); + ENG_REG8(reg) = index; + byte = ENG_REG8(reg + 1); + byte &= (uint8)and_out; + if (byte != byte2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + *exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } + break; + case 0x78: /* identical to type1 */ + *size -= 6; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint16*)(&(rom[*adress]))); + *adress += 2; + index = *((uint8*)(&(rom[*adress]))); + *adress += 1; + and_out = *((uint8*)(&(rom[*adress]))); + *adress += 1; + or_in = *((uint8*)(&(rom[*adress]))); + *adress += 1; + LOG(8,("cmd 'RD idx ISA reg $%02x via $%04x, AND-out = $%02x, OR-in = $%02x, WR-bk'\n", + index, reg, and_out, or_in)); + if (*exec) + { + translate_ISA_PCI(®); + ENG_REG8(reg) = index; + byte = ENG_REG8(reg + 1); + byte &= (uint8)and_out; + byte |= (uint8)or_in; + ENG_REG8(reg + 1) = byte; + } + break; + case 0x79: + *size -= 7; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data = *((uint16*)(&(rom[*adress]))); + *adress += 2; + LOG(8,("cmd 'calculate and set PLL 32bit reg $%08x for %.3fMHz'\n", reg, (data / 100.0))); + if (*exec) + { + float calced_clk; + uint8 m, n, p; + eng_dac_sys_pll_find((data / 100.0), &calced_clk, &m, &n, &p, 0); + /* programming the PLL needs to be done in steps! (confirmed NV28) */ + data2 = ENG_RG32(reg); + ENG_RG32(reg) = ((data2 & 0xffff0000) | (n << 8) | m); + data2 = ENG_RG32(reg); + ENG_RG32(reg) = ((p << 16) | (n << 8) | m); +//fixme? + /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ +// if ((si->ps.card_type == NV31) || (si->ps.card_type == NV36)) +// DACW(PIXPLLC2, 0x80000401); + } + log_pll(reg, (data / 100)); + break; + case 0x7a: /* identical to type1 */ + *size -= 9; + if (*size < 0) + { + LOG(8,("script size error, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + + /* execute */ + *adress += 1; + reg = *((uint32*)(&(rom[*adress]))); + *adress += 4; + data = *((uint32*)(&(rom[*adress]))); + *adress += 4; + LOG(8,("cmd 'WR 32bit reg' $%08x = $%08x\n", reg, data)); + if (*exec) ENG_RG32(reg) = data; + break; + default: + LOG(8,("unknown cmd, aborting!\n\n")); + end = true; + result = B_ERROR; + break; + } + } + + return result; +} + +static void exec_cmd_39_type2(uint8* rom, uint32 data, PinsTables tabs, bool* exec) +{ + uint8 index, byte, byte2, safe, shift; + uint32 reg, and_out, and_out2, offset32; + + data *= 9; + data += tabs.IOFlagConditionTablePtr; + reg = *((uint16*)(&(rom[data]))); + index = *((uint8*)(&(rom[(data + 2)]))); + and_out = *((uint8*)(&(rom[(data + 3)]))); + shift = *((uint8*)(&(rom[(data + 4)]))); + offset32 = *((uint16*)(&(rom[data + 5]))); + and_out2 = *((uint8*)(&(rom[(data + 7)]))); + byte2 = *((uint8*)(&(rom[(data + 8)]))); + LOG(8,("cmd 'AND-out bits $%02x idx ISA reg $%02x via $%04x, shift-right = $%02x,\n", + and_out, index, reg, shift)); + translate_ISA_PCI(®); + ENG_REG8(reg) = index; + byte = ENG_REG8(reg + 1); + byte &= (uint8)and_out; + offset32 += (byte >> shift); + safe = byte = *((uint8*)(&(rom[offset32]))); + byte &= (uint8)and_out2; + LOG(8,("INFO: (cont.) use result as index in table to get data $%02x,\n", + safe)); + LOG(8,("INFO: (cont.) then chk bits AND-out $%02x of data for $%02x'\n", + and_out2, byte2)); + if (byte != byte2) + { + LOG(8,("INFO: ---No match: not executing following command(s):\n")); + *exec = false; + } + else + { + LOG(8,("INFO: ---Match, so this cmd has no effect.\n")); + } +} + +static void setup_ram_config_nv10_up(uint8* rom) +{ + uint32 data, dummy; + uint8 cnt = 0; + status_t stat = B_ERROR; + + /* set 'refctrl is valid' */ + ENG_RG32(RG32_PFB_REFCTRL) = 0x80000000; + + /* check RAM for 256bits buswidth(?) */ + while ((cnt < 4) && (stat != B_OK)) + { + /* reset RAM bits at offset 224-255 bits four times */ + ((uint32 *)si->framebuffer)[0x07] = 0x00000000; + ((uint32 *)si->framebuffer)[0x07] = 0x00000000; + ((uint32 *)si->framebuffer)[0x07] = 0x00000000; + ((uint32 *)si->framebuffer)[0x07] = 0x00000000; + /* write testpattern */ + ((uint32 *)si->framebuffer)[0x07] = 0x4e563131; + /* reset RAM bits at offset 480-511 bits */ + ((uint32 *)si->framebuffer)[0x0f] = 0x00000000; + /* check testpattern to have survived */ + if (((uint32 *)si->framebuffer)[0x07] == 0x4e563131) stat = B_OK; + cnt++; + } + + /* if pattern did not hold modify RAM-type setup */ + if (stat != B_OK) + { + LOG(8,("INFO: ---RAM test #1 done: access errors, modified setup.\n")); + data = ENG_RG32(RG32_PFB_CONFIG_0); + if (data & 0x00000010) + { + data &= 0xffffffcf; + } + else + { + data &= 0xffffffcf; + data |= 0x00000020; + } + ENG_RG32(RG32_PFB_CONFIG_0) = data; + } + else + { + LOG(8,("INFO: ---RAM test #1 done: access is OK.\n")); + } + + /* check RAM bankswitching stuff(?) */ + cnt = 0; + stat = B_ERROR; + while ((cnt < 4) && (stat != B_OK)) + { + /* read RAM size */ + data = ENG_RG32(RG32_NV10STRAPINFO); + /* subtract 1MB */ + data -= 0x00100000; + /* write testpattern at generated RAM adress */ + ((uint32 *)si->framebuffer)[(data >> 2)] = 0x4e564441; + /* reset first RAM adress */ + ((uint32 *)si->framebuffer)[0x00] = 0x00000000; + /* dummyread first RAM adress four times */ + dummy = ((uint32 *)si->framebuffer)[0x00]; + dummy = ((uint32 *)si->framebuffer)[0x00]; + dummy = ((uint32 *)si->framebuffer)[0x00]; + dummy = ((uint32 *)si->framebuffer)[0x00]; + /* check testpattern to have survived */ + if (((uint32 *)si->framebuffer)[(data >> 2)] == 0x4e564441) stat = B_OK; + cnt++; + } + + /* if pattern did not hold modify RAM-type setup */ + if (stat != B_OK) + { + LOG(8,("INFO: ---RAM test #2 done: access errors, modified setup.\n")); + ENG_RG32(RG32_PFB_CONFIG_0) &= 0xffffefff; + } + else + { + LOG(8,("INFO: ---RAM test #2 done: access is OK.\n")); + } +} + +/* Note: this routine assumes at least 128Mb was mapped to memory (kerneldriver). + * It doesn't matter if the card actually _has_ this amount of RAM or not(!) */ +static void setup_ram_config_nv28(uint8* rom) +{ + uint32 dummy; + uint8 cnt = 0; + status_t stat = B_ERROR; + + /* set 'refctrl is valid' */ + ENG_RG32(RG32_PFB_REFCTRL) = 0x80000000; + + /* check RAM */ + while ((cnt < 4) && (stat != B_OK)) + { + /* set bit 11: 'pulse' something into a new setting? */ + ENG_RG32(RG32_PFB_CONFIG_0) |= 0x00000800; + /* write testpattern to RAM adress 127Mb */ + ((uint32 *)si->framebuffer)[0x01fc0000] = 0x4e564441; + /* reset first RAM adress */ + ((uint32 *)si->framebuffer)[0x00000000] = 0x00000000; + /* dummyread first RAM adress four times */ + dummy = ((uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("INFO: (#%d) dummy1 = $%08x, ", cnt, dummy)); + dummy = ((uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("dummy2 = $%08x, ", dummy)); + dummy = ((uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("dummy3 = $%08x, ", dummy)); + dummy = ((uint32 *)si->framebuffer)[0x00000000]; + LOG(8,("dummy4 = $%08x\n", dummy)); + /* check testpattern to have survived */ + if (((uint32 *)si->framebuffer)[0x01fc0000] == 0x4e564441) stat = B_OK; + cnt++; + } + + /* clear bit 11: set normal mode */ + ENG_RG32(RG32_PFB_CONFIG_0) &= ~0x00000800; + + if (stat == B_OK) + LOG(8,("INFO: ---RAM test done: access was OK within %d iteration(s).\n", cnt)); + else + LOG(8,("INFO: ---RAM test done: access was still not OK after 4 iterations.\n")); +} + +static status_t translate_ISA_PCI(uint32* reg) +{ + switch (*reg) + { + case 0x03c0: + *reg = RG8_ATTRDATW; + break; + case 0x03c1: + *reg = RG8_ATTRDATR; + break; + case 0x03c2: + *reg = RG8_MISCW; + break; + case 0x03c4: + *reg = RG8_SEQIND; + break; + case 0x03c5: + *reg = RG8_SEQDAT; + break; + case 0x03c6: + *reg = RG8_PALMASK; + break; + case 0x03c7: + *reg = RG8_PALINDR; + break; + case 0x03c8: + *reg = RG8_PALINDW; + break; + case 0x03c9: + *reg = RG8_PALDATA; + break; + case 0x03cc: + *reg = RG8_MISCR; + break; + case 0x03ce: + *reg = RG8_GRPHIND; + break; + case 0x03cf: + *reg = RG8_GRPHDAT; + break; + case 0x03d4: + *reg = RG8_CRTCIND; + break; + case 0x03d5: + *reg = RG8_CRTCDAT; + break; + case 0x03da: + *reg = RG8_INSTAT1; + break; + default: + LOG(8,("\n\nINFO: WARNING: ISA->PCI register adress translation failed!\n\n")); + return B_ERROR; + break; + } + + return B_OK; +} + +//fixme: move to crtc sourcefile, also setup for crtc2(?) +static status_t eng_crtc_setup_fifo() +{ + /* enable access to primary head */ + set_crtc_owner(0); + + //fixme: setup according to colordepth and RAM bus width... + /* set CRTC FIFO burst size to 256 */ + CRTCW(FIFO, 0x03); + + /* set CRTC FIFO low watermark to 32 */ + CRTCW(FIFO_LWM, 0x20); + + return B_OK; +} + +/* (pre)set 'fixed' card specifications */ +void set_specs(void) +{ + LOG(8,("INFO: setting up card specifications\n")); + + /* set failsave speeds */ + switch (si->ps.card_type) + { + case NV04: + pinsnv4_fake(); + break; + case NV05: + case NV05M64: + pinsnv5_nv5m64_fake(); + break; + case NV06: + pinsnv6_fake(); + break; + default: + switch (si->ps.card_arch) + { + case NV10A: + pinsnv10_arch_fake(); + break; + case NV20A: + pinsnv20_arch_fake(); + break; + case NV30A: + case NV40A: + pinsnv30_arch_fake(); + break; + default: + /* 'failsafe' values... */ + pinsnv10_arch_fake(); + break; + } + break; + } + + /* detect reference crystal frequency and dualhead */ + switch (si->ps.card_arch) + { + case NV04A: + getstrap_arch_nv4(); + break; + default: + getstrap_arch_nv10_20_30_40(); + break; + } +} + +/* this routine presumes the card was coldstarted by the card's BIOS for panel stuff */ +void fake_panel_start(void) +{ + LOG(8,("INFO: detecting RAM size\n")); + + /* detect RAM amount */ + switch (si->ps.card_arch) + { + case NV04A: + getRAMsize_arch_nv4(); + break; + default: + getRAMsize_arch_nv10_20_30_40(); + break; + } + + /* override memory detection if requested by user */ + if (si->settings.memory != 0) + { + LOG(2,("INFO: forcing memory size (specified in settings file)\n")); + si->ps.memory_size = si->settings.memory * 1024 * 1024; + } + + /* find out if the card has a tvout chip */ + si->ps.tvout = false; + si->ps.tvout_chip_type = NONE; +//fixme ;-) +/* if (i2c_maven_probe() == B_OK) + { + si->ps.tvout = true; + si->ps.tvout_chip_bus = ???; + si->ps.tvout_chip_type = ???; + } +*/ + + LOG(8,("INFO: faking panel startup\n")); + + /* find out the BIOS preprogrammed panel use status... */ +// detect_panels(); + + /* determine and setup output devices and heads */ + setup_output_matrix(); + + /* select other CRTC for primary head use if specified by user in settings file */ + if (si->ps.secondary_head && si->settings.switchhead) + { + LOG(2,("INFO: inverting head use (specified in settings file)\n")); + si->ps.crtc2_prim = !si->ps.crtc2_prim; + } +} + +static void detect_panels() +{ + /* detect if the BIOS enabled LCD's (internal panels or DVI) or TVout */ + + /* both external TMDS transmitters (used for LCD/DVI) and external TVencoders + * (can) use the CRTC's in slaved mode. */ + /* Note: + * DFP's are programmed with standard VESA modelines by the card's BIOS! */ + bool slaved_for_dev1 = false, slaved_for_dev2 = false; + bool tvout1 = false, tvout2 = false; + + /* check primary head: */ + /* enable access to primary head */ + set_crtc_owner(0); + + /* unlock head's registers for R/W access */ + CRTCW(LOCK, 0x57); + CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f)); + + LOG(2,("INFO: Dumping flatpanel related CRTC registers:\n")); + /* related info PIXEL register: + * b7: 1 = slaved mode (all cards). */ + LOG(2,("CRTC1: PIXEL register: $%02x\n", CRTCR(PIXEL))); + /* info LCD register: + * b7: 1 = stereo view (shutter glasses use) (all cards), + * b5: 1 = power ext. TMDS (or something)/0 = TVout use (?) (confirmed NV17, NV28), + * b4: 1 = power ext. TMDS (or something)/0 = TVout use (?) (confirmed NV34), + * b3: 1 = ??? (not panel related probably!) (confirmed NV34), + * b1: 1 = power ext. TMDS (or something) (?) (confirmed NV05?, NV17), + * b0: 1 = select panel encoder / 0 = select TVout encoder (all cards). */ + LOG(2,("CRTC1: LCD register: $%02x\n", CRTCR(LCD))); + /* info 0x59 register: + * b0: 1 = enable ext. TMDS clock (DPMS) (confirmed NV28, NV34). */ + LOG(2,("CRTC1: register $59: $%02x\n", CRTCR(0x59))); + /* info 0x9f register: + * b4: 0 = TVout use (?). */ + LOG(2,("CRTC1: register $9f: $%02x\n", CRTCR(0x9f))); + + /* detect active slave device (if any) */ + slaved_for_dev1 = (CRTCR(PIXEL) & 0x80); + if (slaved_for_dev1) + { + /* if the panel isn't selected, tvout is.. */ + tvout1 = !(CRTCR(LCD) & 0x01); + } + + if (si->ps.secondary_head) + { + /* check secondary head: */ + /* enable access to secondary head */ + set_crtc_owner(1); + /* unlock head's registers for R/W access */ + CRTC2W(LOCK, 0x57); + CRTC2W(VSYNCE ,(CRTC2R(VSYNCE) & 0x7f)); + + LOG(2,("CRTC2: PIXEL register: $%02x\n", CRTC2R(PIXEL))); + LOG(2,("CRTC2: LCD register: $%02x\n", CRTC2R(LCD))); + LOG(2,("CRTC2: register $59: $%02x\n", CRTC2R(0x59))); + LOG(2,("CRTC2: register $9f: $%02x\n", CRTC2R(0x9f))); + + /* detect active slave device (if any) */ + slaved_for_dev2 = (CRTC2R(PIXEL) & 0x80); + if (slaved_for_dev2) + { + /* if the panel isn't selected, tvout is.. */ + tvout2 = !(CRTC2R(LCD) & 0x01); + } + } + + LOG(2,("INFO: End flatpanel related CRTC registers dump.\n")); + + /* do some presets */ + si->ps.p1_timing.h_display = 0; + si->ps.p1_timing.v_display = 0; + si->ps.panel1_aspect = 0; + si->ps.p2_timing.h_display = 0; + si->ps.p2_timing.v_display = 0; + si->ps.panel2_aspect = 0; + si->ps.slaved_tmds1 = false; + si->ps.slaved_tmds2 = false; + si->ps.master_tmds1 = false; + si->ps.master_tmds2 = false; + si->ps.tmds1_active = false; + si->ps.tmds2_active = false; + /* determine the situation we are in... (regarding flatpanels) */ + /* fixme: add VESA DDC EDID stuff one day... */ + /* fixme: find out how to program those transmitters one day instead of + * relying on the cards BIOS to do it. This adds TVout options where panels + * are used! + * Currently we'd loose the panel setup while not being able to restore it. */ + + /* note: (facts) + * -> NV11 and NV17 laptops have LVDS panels, programmed in both sets registers; + * -> NV34 laptops have TMDS panels, programmed in only one set of registers; + * -> NV11, NV25 and NV34 DVI cards, so external panels (TMDS) are programmed + * in only one set of registers; + * -> a register-set's FP_TG_CTRL register, bit 31 tells you if a LVDS panel is + * connected to the primary head (0), or to the secondary head (1) except + * on some NV11's if this bit is '0' there; + * -> for LVDS panels both registersets are programmed identically by the card's + * BIOSes; + * -> the programmed set of registers tells you where a TMDS (DVI) panel is + * connected; + * -> On all cards a CRTC is used in slaved mode when a panel is connected, + * except on NV11: here master mode is (might be?) detected. */ + /* note also: + * external TMDS encoders are only used for logic-level translation: it's + * modeline registers are not used. Instead the GPU's internal modeline registers + * are used. The external encoder is not connected to a I2C bus (confirmed NV34). */ + if (slaved_for_dev1 && !tvout1) + { + uint16 width = ((DACR(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DACR(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.slaved_tmds1 = true; + si->ps.tmds1_active = true; + si->ps.p1_timing.h_display = width; + si->ps.p1_timing.v_display = height; + } + } + + if (si->ps.secondary_head && slaved_for_dev2 && !tvout2) + { + uint16 width = ((DAC2R(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DAC2R(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.slaved_tmds2 = true; + si->ps.tmds2_active = true; + si->ps.p2_timing.h_display = width; + si->ps.p2_timing.v_display = height; + } + } + + if ((si->ps.card_type == NV11) && + !si->ps.slaved_tmds1 && !tvout1) + { + uint16 width = ((DACR(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DACR(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.master_tmds1 = true; + si->ps.tmds1_active = true; + si->ps.p1_timing.h_display = width; + si->ps.p1_timing.v_display = height; + } + } + + if ((si->ps.card_type == NV11) && + si->ps.secondary_head && !si->ps.slaved_tmds2 && !tvout2) + { + uint16 width = ((DAC2R(FP_HDISPEND) & 0x0000ffff) + 1); + uint16 height = ((DAC2R(FP_VDISPEND) & 0x0000ffff) + 1); + if ((width >= 640) && (height >= 480)) + { + si->ps.master_tmds2 = true; + si->ps.tmds2_active = true; + si->ps.p2_timing.h_display = width; + si->ps.p2_timing.v_display = height; + } + } + + //fixme...: + //we are assuming that no DVI is used as external monitor on laptops; + //otherwise we probably get into trouble here if the checked specs match. + if (si->ps.laptop && si->ps.tmds1_active && si->ps.tmds2_active && + ((DACR(FP_TG_CTRL) & 0x80000000) == (DAC2R(FP_TG_CTRL) & 0x80000000)) && + (si->ps.p1_timing.h_display == si->ps.p2_timing.h_display) && + (si->ps.p1_timing.v_display == si->ps.p2_timing.v_display)) + { + LOG(2,("INFO: correcting double detection of single panel!\n")); + + if (si->ps.card_type == NV11) + { + /* LVDS panel is _always_ on CRTC2, so clear false primary detection */ + si->ps.slaved_tmds1 = false; + si->ps.master_tmds1 = false; + si->ps.tmds1_active = false; + si->ps.p1_timing.h_display = 0; + si->ps.p1_timing.v_display = 0; + } + else + { + if (DACR(FP_TG_CTRL) & 0x80000000) + { + /* LVDS panel is on CRTC2, so clear false primary detection */ + si->ps.slaved_tmds1 = false; + si->ps.master_tmds1 = false; + si->ps.tmds1_active = false; + si->ps.p1_timing.h_display = 0; + si->ps.p1_timing.v_display = 0; + } + else + { + /* LVDS panel is on CRTC1, so clear false secondary detection */ + si->ps.slaved_tmds2 = false; + si->ps.master_tmds2 = false; + si->ps.tmds2_active = false; + si->ps.p2_timing.h_display = 0; + si->ps.p2_timing.v_display = 0; + } + } + } + + /* fetch panel(s) modeline(s) */ + if (si->ps.tmds1_active) + { + /* determine panel aspect ratio */ + si->ps.panel1_aspect = + (si->ps.p1_timing.h_display / ((float)si->ps.p1_timing.v_display)); + /* horizontal timing */ + si->ps.p1_timing.h_sync_start = (DACR(FP_HSYNC_S) & 0x0000ffff) + 1; + si->ps.p1_timing.h_sync_end = (DACR(FP_HSYNC_E) & 0x0000ffff) + 1; + si->ps.p1_timing.h_total = (DACR(FP_HTOTAL) & 0x0000ffff) + 1; + /* vertical timing */ + si->ps.p1_timing.v_sync_start = (DACR(FP_VSYNC_S) & 0x0000ffff) + 1; + si->ps.p1_timing.v_sync_end = (DACR(FP_VSYNC_E) & 0x0000ffff) + 1; + si->ps.p1_timing.v_total = (DACR(FP_VTOTAL) & 0x0000ffff) + 1; + /* sync polarity */ + si->ps.p1_timing.flags = 0; + if (DACR(FP_TG_CTRL) & 0x00000001) si->ps.p1_timing.flags |= B_POSITIVE_VSYNC; + if (DACR(FP_TG_CTRL) & 0x00000010) si->ps.p1_timing.flags |= B_POSITIVE_HSYNC; + /* refreshrate: + * fix a DVI or laptop flatpanel to 60Hz refresh! */ + si->ps.p1_timing.pixel_clock = + (si->ps.p1_timing.h_total * si->ps.p1_timing.v_total * 60) / 1000; + } + if (si->ps.tmds2_active) + { + /* determine panel aspect ratio */ + si->ps.panel2_aspect = + (si->ps.p2_timing.h_display / ((float)si->ps.p2_timing.v_display)); + /* horizontal timing */ + si->ps.p2_timing.h_sync_start = (DAC2R(FP_HSYNC_S) & 0x0000ffff) + 1; + si->ps.p2_timing.h_sync_end = (DAC2R(FP_HSYNC_E) & 0x0000ffff) + 1; + si->ps.p2_timing.h_total = (DAC2R(FP_HTOTAL) & 0x0000ffff) + 1; + /* vertical timing */ + si->ps.p2_timing.v_sync_start = (DAC2R(FP_VSYNC_S) & 0x0000ffff) + 1; + si->ps.p2_timing.v_sync_end = (DAC2R(FP_VSYNC_E) & 0x0000ffff) + 1; + si->ps.p2_timing.v_total = (DAC2R(FP_VTOTAL) & 0x0000ffff) + 1; + /* sync polarity */ + si->ps.p2_timing.flags = 0; + if (DAC2R(FP_TG_CTRL) & 0x00000001) si->ps.p2_timing.flags |= B_POSITIVE_VSYNC; + if (DAC2R(FP_TG_CTRL) & 0x00000010) si->ps.p2_timing.flags |= B_POSITIVE_HSYNC; + /* refreshrate: + * fix a DVI or laptop flatpanel to 60Hz refresh! */ + si->ps.p2_timing.pixel_clock = + (si->ps.p2_timing.h_total * si->ps.p2_timing.v_total * 60) / 1000; + } + + /* dump some panel configuration registers... */ + LOG(2,("INFO: Dumping flatpanel registers:\n")); + LOG(2,("DUALHEAD_CTRL: $%08x\n", ENG_RG32(RG32_DUALHEAD_CTRL))); + LOG(2,("DAC1: FP_HDISPEND: %d\n", DACR(FP_HDISPEND))); + LOG(2,("DAC1: FP_HTOTAL: %d\n", DACR(FP_HTOTAL))); + LOG(2,("DAC1: FP_HCRTC: %d\n", DACR(FP_HCRTC))); + LOG(2,("DAC1: FP_HSYNC_S: %d\n", DACR(FP_HSYNC_S))); + LOG(2,("DAC1: FP_HSYNC_E: %d\n", DACR(FP_HSYNC_E))); + LOG(2,("DAC1: FP_HVALID_S: %d\n", DACR(FP_HVALID_S))); + LOG(2,("DAC1: FP_HVALID_E: %d\n", DACR(FP_HVALID_E))); + + LOG(2,("DAC1: FP_VDISPEND: %d\n", DACR(FP_VDISPEND))); + LOG(2,("DAC1: FP_VTOTAL: %d\n", DACR(FP_VTOTAL))); + LOG(2,("DAC1: FP_VCRTC: %d\n", DACR(FP_VCRTC))); + LOG(2,("DAC1: FP_VSYNC_S: %d\n", DACR(FP_VSYNC_S))); + LOG(2,("DAC1: FP_VSYNC_E: %d\n", DACR(FP_VSYNC_E))); + LOG(2,("DAC1: FP_VVALID_S: %d\n", DACR(FP_VVALID_S))); + LOG(2,("DAC1: FP_VVALID_E: %d\n", DACR(FP_VVALID_E))); + + LOG(2,("DAC1: FP_CHKSUM: $%08x = (dec) %d\n", DACR(FP_CHKSUM),DACR(FP_CHKSUM))); + LOG(2,("DAC1: FP_TST_CTRL: $%08x\n", DACR(FP_TST_CTRL))); + LOG(2,("DAC1: FP_TG_CTRL: $%08x\n", DACR(FP_TG_CTRL))); + LOG(2,("DAC1: FP_DEBUG0: $%08x\n", DACR(FP_DEBUG0))); + LOG(2,("DAC1: FP_DEBUG1: $%08x\n", DACR(FP_DEBUG1))); + LOG(2,("DAC1: FP_DEBUG2: $%08x\n", DACR(FP_DEBUG2))); + LOG(2,("DAC1: FP_DEBUG3: $%08x\n", DACR(FP_DEBUG3))); + + LOG(2,("DAC1: FUNCSEL: $%08x\n", ENG_RG32(RG32_FUNCSEL))); + LOG(2,("DAC1: PANEL_PWR: $%08x\n", ENG_RG32(RG32_PANEL_PWR))); + + if(si->ps.secondary_head) + { + LOG(2,("DAC2: FP_HDISPEND: %d\n", DAC2R(FP_HDISPEND))); + LOG(2,("DAC2: FP_HTOTAL: %d\n", DAC2R(FP_HTOTAL))); + LOG(2,("DAC2: FP_HCRTC: %d\n", DAC2R(FP_HCRTC))); + LOG(2,("DAC2: FP_HSYNC_S: %d\n", DAC2R(FP_HSYNC_S))); + LOG(2,("DAC2: FP_HSYNC_E: %d\n", DAC2R(FP_HSYNC_E))); + LOG(2,("DAC2: FP_HVALID_S:%d\n", DAC2R(FP_HVALID_S))); + LOG(2,("DAC2: FP_HVALID_E: %d\n", DAC2R(FP_HVALID_E))); + + LOG(2,("DAC2: FP_VDISPEND: %d\n", DAC2R(FP_VDISPEND))); + LOG(2,("DAC2: FP_VTOTAL: %d\n", DAC2R(FP_VTOTAL))); + LOG(2,("DAC2: FP_VCRTC: %d\n", DAC2R(FP_VCRTC))); + LOG(2,("DAC2: FP_VSYNC_S: %d\n", DAC2R(FP_VSYNC_S))); + LOG(2,("DAC2: FP_VSYNC_E: %d\n", DAC2R(FP_VSYNC_E))); + LOG(2,("DAC2: FP_VVALID_S: %d\n", DAC2R(FP_VVALID_S))); + LOG(2,("DAC2: FP_VVALID_E: %d\n", DAC2R(FP_VVALID_E))); + + LOG(2,("DAC2: FP_CHKSUM: $%08x = (dec) %d\n", DAC2R(FP_CHKSUM),DAC2R(FP_CHKSUM))); + LOG(2,("DAC2: FP_TST_CTRL: $%08x\n", DAC2R(FP_TST_CTRL))); + LOG(2,("DAC2: FP_TG_CTRL: $%08x\n", DAC2R(FP_TG_CTRL))); + LOG(2,("DAC2: FP_DEBUG0: $%08x\n", DAC2R(FP_DEBUG0))); + LOG(2,("DAC2: FP_DEBUG1: $%08x\n", DAC2R(FP_DEBUG1))); + LOG(2,("DAC2: FP_DEBUG2: $%08x\n", DAC2R(FP_DEBUG2))); + LOG(2,("DAC2: FP_DEBUG3: $%08x\n", DAC2R(FP_DEBUG3))); + + LOG(2,("DAC2: FUNCSEL: $%08x\n", ENG_RG32(RG32_2FUNCSEL))); + LOG(2,("DAC2: PANEL_PWR: $%08x\n", ENG_RG32(RG32_2PANEL_PWR))); + } + LOG(2,("INFO: End flatpanel registers dump.\n")); +} + +static void setup_output_matrix() +{ + /* setup defaults: */ + /* no monitors (output devices) detected */ + si->ps.monitors = 0x00; + /* head 1 will be the primary head */ + si->ps.crtc2_prim = false; + + /* setup output devices and heads */ + if (si->ps.secondary_head) + { + if (si->ps.card_type != NV11) + { + /* setup defaults: */ + /* connect analog outputs straight through */ + eng_general_output_select(false); + + /* presetup by the card's BIOS, we can't change this (lack of info) */ + if (si->ps.tmds1_active) si->ps.monitors |= 0x01; + if (si->ps.tmds2_active) si->ps.monitors |= 0x10; + /* detect analog monitors (confirmed working OK on NV18, NV28 and NV34): */ + /* sense analog monitor on primary connector */ + if (eng_dac_crt_connected()) si->ps.monitors |= 0x02; + /* sense analog monitor on secondary connector */ + if (eng_dac2_crt_connected()) si->ps.monitors |= 0x20; + + /* setup correct output and head use */ + //fixme? add TVout (only, so no CRT(s) connected) support... + switch (si->ps.monitors) + { + case 0x00: /* no monitor found at all */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x01: /* digital panel on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x02: /* analog panel or CRT on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x03: /* both types on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel AND an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: correcting...\n")); + /* cross connect analog outputs so analog panel or CRT gets head 2 */ + eng_general_output_select(true); + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x10: /* nothing on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x20: /* nothing on head 1, analog panel or CRT on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x30: /* nothing on head 1, both types on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has a digital panel AND an analog panel or CRT:\n")); + LOG(2,("INFO: correcting...\n")); + /* cross connect analog outputs so analog panel or CRT gets head 1 */ + eng_general_output_select(true); + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x11: /* digital panels on both heads */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x12: /* analog panel or CRT on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x21: /* digital panel on head 1, analog panel or CRT on head 2 */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x22: /* analog panel(s) or CRT(s) on both heads */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + default: /* more than two monitors connected to just two outputs: illegal! */ + LOG(2,("INFO: illegal monitor setup ($%02x):\n", si->ps.monitors)); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + } + } + else /* dualhead NV11 cards */ + { + /* confirmed no analog output switch-options for NV11 */ + LOG(2,("INFO: NV11 outputs are hardwired to be straight-through\n")); + + /* presetup by the card's BIOS, we can't change this (lack of info) */ + if (si->ps.tmds1_active) si->ps.monitors |= 0x01; + if (si->ps.tmds2_active) si->ps.monitors |= 0x10; + /* detect analog monitor (confirmed working OK on NV11): */ + /* sense analog monitor on primary connector */ + if (eng_dac_crt_connected()) si->ps.monitors |= 0x02; + /* (sense analog monitor on secondary connector is impossible on NV11) */ + + /* setup correct output and head use */ + //fixme? add TVout (only, so no CRT(s) connected) support... + switch (si->ps.monitors) + { + case 0x00: /* no monitor found at all */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x01: /* digital panel on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x02: /* analog panel or CRT on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x03: /* both types on head 1, nothing on head 2 */ + LOG(2,("INFO: head 1 has a digital panel AND an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has nothing connected:\n")); + LOG(2,("INFO: correction not possible...\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x10: /* nothing on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has nothing connected;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + case 0x11: /* digital panels on both heads */ + LOG(2,("INFO: head 1 has a digital panel;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + case 0x12: /* analog panel or CRT on head 1, digital panel on head 2 */ + LOG(2,("INFO: head 1 has an analog panel or CRT;\n")); + LOG(2,("INFO: head 2 has a digital panel:\n")); + LOG(2,("INFO: defaulting to head 2 for primary use.\n")); + si->ps.crtc2_prim = true; + break; + default: /* more than two monitors connected to just two outputs: illegal! */ + LOG(2,("INFO: illegal monitor setup ($%02x):\n", si->ps.monitors)); + LOG(2,("INFO: defaulting to head 1 for primary use.\n")); + break; + } + } + } + else /* singlehead cards */ + { + /* presetup by the card's BIOS, we can't change this (lack of info) */ + if (si->ps.tmds1_active) si->ps.monitors |= 0x01; + /* detect analog monitor (confirmed working OK on all cards): */ + /* sense analog monitor on primary connector */ + if (eng_dac_crt_connected()) si->ps.monitors |= 0x02; + + //fixme? add TVout (only, so no CRT connected) support... + } +} + +void get_panel_modes(display_mode *p1, display_mode *p2, bool *pan1, bool *pan2) +{ + if (si->ps.tmds1_active) + { + /* timing ('modeline') */ + p1->timing = si->ps.p1_timing; + /* setup the rest */ + p1->space = B_CMAP8; + p1->virtual_width = p1->timing.h_display; + p1->virtual_height = p1->timing.v_display; + p1->h_display_start = 0; + p1->v_display_start = 0; + p1->flags = 0; + *pan1 = true; + } + else + *pan1 = false; + + if (si->ps.tmds2_active) + { + /* timing ('modeline') */ + p2->timing = si->ps.p2_timing; + /* setup the rest */ + p2->space = B_CMAP8; + p2->virtual_width = p2->timing.h_display; + p2->virtual_height = p2->timing.v_display; + p2->h_display_start = 0; + p2->v_display_start = 0; + p2->flags = 0; + *pan2 = true; + } + else + *pan2 = false; +} + +static void pinsnv4_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 256; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 256; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 0; + si->ps.min_video_vco = 0; + si->ps.max_dac1_clock = 250; + si->ps.max_dac1_clock_8 = 250; + si->ps.max_dac1_clock_16 = 250; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 220; + si->ps.max_dac1_clock_32 = 180; + si->ps.max_dac1_clock_32dh = 180; + /* secondary head */ + si->ps.max_dac2_clock = 0; + si->ps.max_dac2_clock_8 = 0; + si->ps.max_dac2_clock_16 = 0; + si->ps.max_dac2_clock_24 = 0; + si->ps.max_dac2_clock_32 = 0; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 0; + //fixme: primary & secondary_dvi should be overrule-able via skel.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 90; + si->ps.std_memory_clock = 110; +} + +static void pinsnv5_nv5m64_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 300; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 300; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 0; + si->ps.min_video_vco = 0; + si->ps.max_dac1_clock = 300; + si->ps.max_dac1_clock_8 = 300; + si->ps.max_dac1_clock_16 = 300; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 270; + si->ps.max_dac1_clock_32 = 230; + si->ps.max_dac1_clock_32dh = 230; + /* secondary head */ + si->ps.max_dac2_clock = 0; + si->ps.max_dac2_clock_8 = 0; + si->ps.max_dac2_clock_16 = 0; + si->ps.max_dac2_clock_24 = 0; + si->ps.max_dac2_clock_32 = 0; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 0; + //fixme: primary & secondary_dvi should be overrule-able via skel.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 125; + si->ps.std_memory_clock = 150; +} + +static void pinsnv6_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 300; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 300; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 0; + si->ps.min_video_vco = 0; + si->ps.max_dac1_clock = 300; + si->ps.max_dac1_clock_8 = 300; + si->ps.max_dac1_clock_16 = 300; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 270; + si->ps.max_dac1_clock_32 = 230; + si->ps.max_dac1_clock_32dh = 230; + /* secondary head */ + si->ps.max_dac2_clock = 0; + si->ps.max_dac2_clock_8 = 0; + si->ps.max_dac2_clock_16 = 0; + si->ps.max_dac2_clock_24 = 0; + si->ps.max_dac2_clock_32 = 0; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 0; + //fixme: primary & secondary_dvi should be overrule-able via skel.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 100; + si->ps.std_memory_clock = 125; +} + +static void pinsnv10_arch_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 350; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 350; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 350; + si->ps.min_video_vco = 128; + si->ps.max_dac1_clock = 350; + si->ps.max_dac1_clock_8 = 350; + si->ps.max_dac1_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 320; + si->ps.max_dac1_clock_32 = 280; + si->ps.max_dac1_clock_32dh = 250; + /* secondary head */ + if (si->ps.card_type < NV17) + { + /* if a GeForce2 has analog VGA dualhead capability, + * it uses an external secondary DAC probably with limited capability. */ + /* (called twinview technology) */ + si->ps.max_dac2_clock = 200; + si->ps.max_dac2_clock_8 = 200; + si->ps.max_dac2_clock_16 = 200; + si->ps.max_dac2_clock_24 = 200; + si->ps.max_dac2_clock_32 = 200; + /* 'failsave' values */ + si->ps.max_dac2_clock_32dh = 180; + } + else + { + /* GeForce4 cards have dual integrated DACs with identical capaability */ + /* (called nview technology) */ + si->ps.max_dac2_clock = 350; + si->ps.max_dac2_clock_8 = 350; + si->ps.max_dac2_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac2_clock_24 = 320; + si->ps.max_dac2_clock_32 = 280; + si->ps.max_dac2_clock_32dh = 250; + } + //fixme: primary & secondary_dvi should be overrule-able via skel.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 120; + si->ps.std_memory_clock = 150; +} + +static void pinsnv20_arch_fake(void) +{ + /* we have a standard PLL */ + si->ps.ext_pll = false; + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 350; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 350; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 350; + si->ps.min_video_vco = 128; + si->ps.max_dac1_clock = 350; + si->ps.max_dac1_clock_8 = 350; + si->ps.max_dac1_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 320; + si->ps.max_dac1_clock_32 = 280; + si->ps.max_dac1_clock_32dh = 250; + /* secondary head */ + /* GeForce4 cards have dual integrated DACs with identical capaability */ + /* (called nview technology) */ + si->ps.max_dac2_clock = 350; + si->ps.max_dac2_clock_8 = 350; + si->ps.max_dac2_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac2_clock_24 = 320; + si->ps.max_dac2_clock_32 = 280; + si->ps.max_dac2_clock_32dh = 250; + //fixme: primary & secondary_dvi should be overrule-able via skel.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 175; + si->ps.std_memory_clock = 200; +} + +static void pinsnv30_arch_fake(void) +{ + /* determine PLL type */ + LOG(8,("INFO: NV30 architecture chip, PIXPLLC2 DAC1 = $%08x, DAC2 = $%08x\n", + DACR(PIXPLLC2), DAC2R(PIXPLLC2))); + switch (si->ps.card_type) + { + case NV31: + case NV36: + case NV40: + /* we have a extended PLL */ + si->ps.ext_pll = true; + break; + default: + /* we have a standard PLL */ + si->ps.ext_pll = false; + break; + } + /* carefull not to take to high limits, and high should be >= 2x low. */ + si->ps.max_system_vco = 350; + si->ps.min_system_vco = 128; + si->ps.max_pixel_vco = 350; + si->ps.min_pixel_vco = 128; + si->ps.max_video_vco = 350; + si->ps.min_video_vco = 128; + si->ps.max_dac1_clock = 350; + si->ps.max_dac1_clock_8 = 350; + si->ps.max_dac1_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac1_clock_24 = 320; + si->ps.max_dac1_clock_32 = 280; + si->ps.max_dac1_clock_32dh = 250; + /* secondary head */ + /* GeForceFX cards have dual integrated DACs with identical capaability */ + /* (called nview technology) */ + si->ps.max_dac2_clock = 350; + si->ps.max_dac2_clock_8 = 350; + si->ps.max_dac2_clock_16 = 350; + /* 'failsave' values */ + si->ps.max_dac2_clock_24 = 320; + si->ps.max_dac2_clock_32 = 280; + si->ps.max_dac2_clock_32dh = 250; + //fixme: primary & secondary_dvi should be overrule-able via skel.settings + si->ps.primary_dvi = false; + si->ps.secondary_dvi = false; + /* not used (yet) because no coldstart will be attempted (yet) */ + si->ps.std_engine_clock = 190; + si->ps.std_memory_clock = 190; +} + +static void getRAMsize_arch_nv4(void) +{ + uint32 strapinfo = ENG_RG32(RG32_NV4STRAPINFO); + + if (strapinfo & 0x00000100) + { + /* Unified memory architecture used */ + si->ps.memory_size = 1024 * 1024 * + ((((strapinfo & 0x0000f000) >> 12) * 2) + 2); + + LOG(8,("INFO: NV4 architecture chip with UMA detected\n")); + } + else + { + /* private memory architecture used */ + switch (strapinfo & 0x00000003) + { + case 0: + si->ps.memory_size = 32 * 1024 * 1024; + break; + case 1: + si->ps.memory_size = 4 * 1024 * 1024; + break; + case 2: + si->ps.memory_size = 8 * 1024 * 1024; + break; + case 3: + si->ps.memory_size = 16 * 1024 * 1024; + break; + } + } +} + +static void getstrap_arch_nv4(void) +{ + uint32 strapinfo = ENG_RG32(RG32_NVSTRAPINFO2); + + /* determine PLL reference crystal frequency */ + if (strapinfo & 0x00000040) + si->ps.f_ref = 14.31818; + else + si->ps.f_ref = 13.50000; + + /* these cards are always singlehead */ + si->ps.secondary_head = false; +} + +static void getRAMsize_arch_nv10_20_30_40(void) +{ + uint32 dev_manID = CFGR(DEVID); + uint32 strapinfo = ENG_RG32(RG32_NV10STRAPINFO); + + switch (dev_manID) + { + case 0x01a010de: /* Nvidia GeForce2 Integrated GPU */ + case 0x01f010de: /* Nvidia GeForce4 MX Integrated GPU */ + /* the kerneldriver already determined the amount of RAM these cards have at + * their disposal (UMA, values read from PCI config space in other device) */ + LOG(8,("INFO: nVidia GPU with UMA detected\n")); + break; + default: + LOG(8,("INFO: (Memory detection) Strapinfo value is: $%08x\n", strapinfo)); + + switch ((strapinfo & 0x1ff00000) >> 20) + { + case 2: + si->ps.memory_size = 2 * 1024 * 1024; + break; + case 4: + si->ps.memory_size = 4 * 1024 * 1024; + break; + case 8: + si->ps.memory_size = 8 * 1024 * 1024; + break; + case 16: + si->ps.memory_size = 16 * 1024 * 1024; + break; + case 32: + si->ps.memory_size = 32 * 1024 * 1024; + break; + case 64: + si->ps.memory_size = 64 * 1024 * 1024; + break; + case 128: + si->ps.memory_size = 128 * 1024 * 1024; + break; + case 256: + si->ps.memory_size = 256 * 1024 * 1024; + break; + default: + si->ps.memory_size = 16 * 1024 * 1024; + + LOG(8,("INFO: NV10/20/30 architecture chip with unknown RAM amount detected;\n")); + LOG(8,("INFO: Setting 16Mb\n")); + break; + } + } +} + +static void getstrap_arch_nv10_20_30_40(void) +{ + uint32 dev_manID = CFGR(DEVID); + uint32 strapinfo = ENG_RG32(RG32_NVSTRAPINFO2); + + /* determine PLL reference crystal frequency: three types are used... */ + if (strapinfo & 0x00000040) + si->ps.f_ref = 14.31818; + else + si->ps.f_ref = 13.50000; + + switch (dev_manID & 0xfff0ffff) + { + /* Nvidia cards: */ + case 0x004010de: + case 0x00c010de: + case 0x00f010de: + case 0x014010de: + case 0x017010de: + case 0x018010de: + case 0x01f010de: + case 0x025010de: + case 0x028010de: + case 0x030010de: + case 0x031010de: + case 0x032010de: + case 0x033010de: + case 0x034010de: + /* Varisys cards: */ + case 0x35001888: + if (strapinfo & 0x00400000) si->ps.f_ref = 27.00000; + break; + default: + break; + } + + /* determine if we have a dualhead card */ + switch (dev_manID & 0xfff0ffff) + { + /* Nvidia cards: */ + case 0x004010de: + case 0x00c010de: + case 0x00f010de: + case 0x011010de: + case 0x014010de: + case 0x017010de: + case 0x018010de: + case 0x01f010de: + case 0x025010de: + case 0x028010de: + case 0x030010de: + case 0x031010de: + case 0x032010de: + case 0x033010de: + case 0x034010de: + /* Varisys cards: */ + case 0x35001888: + si->ps.secondary_head = true; + break; + default: + si->ps.secondary_head = false; + break; + } +} + +void dump_pins(void) +{ + char *msg = ""; + + LOG(2,("INFO: pinsdump follows:\n")); + LOG(2,("PLL type: ")); + if (si->ps.ext_pll) LOG(2,("extended\n")); else LOG(2,("standard\n")); + LOG(2,("f_ref: %fMhz\n", si->ps.f_ref)); + LOG(2,("max_system_vco: %dMhz\n", si->ps.max_system_vco)); + LOG(2,("min_system_vco: %dMhz\n", si->ps.min_system_vco)); + LOG(2,("max_pixel_vco: %dMhz\n", si->ps.max_pixel_vco)); + LOG(2,("min_pixel_vco: %dMhz\n", si->ps.min_pixel_vco)); + LOG(2,("max_video_vco: %dMhz\n", si->ps.max_video_vco)); + LOG(2,("min_video_vco: %dMhz\n", si->ps.min_video_vco)); + LOG(2,("std_engine_clock: %dMhz\n", si->ps.std_engine_clock)); + LOG(2,("std_memory_clock: %dMhz\n", si->ps.std_memory_clock)); + LOG(2,("max_dac1_clock: %dMhz\n", si->ps.max_dac1_clock)); + LOG(2,("max_dac1_clock_8: %dMhz\n", si->ps.max_dac1_clock_8)); + LOG(2,("max_dac1_clock_16: %dMhz\n", si->ps.max_dac1_clock_16)); + LOG(2,("max_dac1_clock_24: %dMhz\n", si->ps.max_dac1_clock_24)); + LOG(2,("max_dac1_clock_32: %dMhz\n", si->ps.max_dac1_clock_32)); + LOG(2,("max_dac1_clock_32dh: %dMhz\n", si->ps.max_dac1_clock_32dh)); + LOG(2,("max_dac2_clock: %dMhz\n", si->ps.max_dac2_clock)); + LOG(2,("max_dac2_clock_8: %dMhz\n", si->ps.max_dac2_clock_8)); + LOG(2,("max_dac2_clock_16: %dMhz\n", si->ps.max_dac2_clock_16)); + LOG(2,("max_dac2_clock_24: %dMhz\n", si->ps.max_dac2_clock_24)); + LOG(2,("max_dac2_clock_32: %dMhz\n", si->ps.max_dac2_clock_32)); + LOG(2,("max_dac2_clock_32dh: %dMhz\n", si->ps.max_dac2_clock_32dh)); + LOG(2,("secondary_head: ")); + if (si->ps.secondary_head) LOG(2,("present\n")); else LOG(2,("absent\n")); + LOG(2,("tvout: ")); + if (si->ps.tvout) LOG(2,("present\n")); else LOG(2,("absent\n")); + /* setup TVout logmessage text */ + switch (si->ps.tvout_chip_type) + { + case NONE: + msg = "No"; + break; + case CH7003: + msg = "Chrontel CH7003"; + break; + case CH7004: + msg = "Chrontel CH7004"; + break; + case CH7005: + msg = "Chrontel CH7005"; + break; + case CH7006: + msg = "Chrontel CH7006"; + break; + case CH7007: + msg = "Chrontel CH7007"; + break; + case CH7008: + msg = "Chrontel CH7008"; + break; + case SAA7102: + msg = "Philips SAA7102"; + break; + case SAA7103: + msg = "Philips SAA7103"; + break; + case SAA7104: + msg = "Philips SAA7104"; + break; + case SAA7105: + msg = "Philips SAA7105"; + break; + case BT868: + msg = "Brooktree/Conexant BT868"; + break; + case BT869: + msg = "Brooktree/Conexant BT869"; + break; + case CX25870: + msg = "Conexant CX25870"; + break; + case CX25871: + msg = "Conexant CX25871"; + break; + case NVIDIA: + msg = "Nvidia internal"; + break; + default: + msg = "Unknown"; + break; + } + LOG(2, ("%s TVout chip detected\n", msg)); +// LOG(2,("primary_dvi: ")); +// if (si->ps.primary_dvi) LOG(2,("present\n")); else LOG(2,("absent\n")); +// LOG(2,("secondary_dvi: ")); +// if (si->ps.secondary_dvi) LOG(2,("present\n")); else LOG(2,("absent\n")); + LOG(2,("card memory_size: %3.3fMb\n", (si->ps.memory_size / (1024.0 * 1024.0)))); + LOG(2,("laptop: ")); + if (si->ps.laptop) LOG(2,("yes\n")); else LOG(2,("no\n")); + if (si->ps.tmds1_active) + { + LOG(2,("found DFP (digital flatpanel) on CRTC1; CRTC1 is ")); + if (si->ps.slaved_tmds1) LOG(2,("slaved\n")); else LOG(2,("master\n")); + LOG(2,("panel width: %d, height: %d, aspect ratio: %1.2f\n", + si->ps.p1_timing.h_display, si->ps.p1_timing.v_display, si->ps.panel1_aspect)); + } + if (si->ps.tmds2_active) + { + LOG(2,("found DFP (digital flatpanel) on CRTC2; CRTC2 is ")); + if (si->ps.slaved_tmds2) LOG(2,("slaved\n")); else LOG(2,("master\n")); + LOG(2,("panel width: %d, height: %d, aspect ratio: %1.2f\n", + si->ps.p2_timing.h_display, si->ps.p2_timing.v_display, si->ps.panel2_aspect)); + } + LOG(2,("monitor (output devices) setup matrix: $%02x\n", si->ps.monitors)); + LOG(2,("INFO: end pinsdump.\n")); +}