/* Read initialisation information from card */ /* some bits are hacks, where PINS is not known */ /* Author: Rudolf Cornelissen 7/2003-4/2021 */ #define MODULE_BIT 0x00002000 #include "nv_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 nv_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 */ unsigned char* signon_msg = &(rom[(rom[offset + 24] + (rom[offset + 25] * 256))]); unsigned char* vendor_name = &(rom[(rom[offset + 40] + (rom[offset + 41] * 256))]); unsigned char* product_name = &(rom[(rom[offset + 42] + (rom[offset + 43] * 256))]); unsigned 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; /* confirmed on a TNT2-M64 with pins V5.1 */ uint16 ram_tab = rom[offset + 24] + (rom[offset + 25] * 256); /* fixme: PPC BIOSes (might) return NULL pointers for messages here */ unsigned char* signon_msg = &(rom[(rom[offset + 30] + (rom[offset + 31] * 256))]); unsigned char* vendor_name = &(rom[(rom[offset + 46] + (rom[offset + 47] * 256))]); unsigned char* product_name = &(rom[(rom[offset + 48] + (rom[offset + 49] * 256))]); unsigned 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 */ NV_REG8(NV8_MISCW) = 0xcb; /* unknown.. */ NV_REG8(NV8_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) */ nv_crtc_dpms(false, false, false, true); nv_crtc_cursor_hide(); if (si->ps.secondary_head) { nv_crtc2_dpms(false, false, false, true); nv_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.. nv_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 = NV_REG32(NV32_FB_MRS2); fb_mrs1 = NV_REG32(NV32_FB_MRS1); } /* select colormode CRTC registers base adresses */ NV_REG8(NV8_MISCW) = 0xcb; /* unknown.. */ NV_REG8(NV8_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) */ nv_crtc_dpms(false, false, false, true); nv_crtc_cursor_hide(); if (si->ps.secondary_head) { nv_crtc2_dpms(false, false, false, true); nv_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? NV_REG32(NV32_FB_MRS2) = fb_mrs2; NV_REG32(NV32_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.. nv_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 two no-name TNT2-M64's. All 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; nv_dac_sys_pll_find(((float)data2), &calced_clk, &m, &n, &p, 0); NV_REG32(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) NV_REG32(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 */ NV_REG32(reg) = data; NV_REG32(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 = NV_REG8(reg); byte &= (uint8)and_out; byte |= (uint8)or_in; NV_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 = NV_REG32(NV32_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 = NV_REG32(reg); data &= and_out; data |= or_in; NV_REG32(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 = NV_REG32(NV32_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) NV_REG32(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(®); NV_REG8(reg) = index; byte = NV_REG8(reg + 1); byte &= (uint8)and_out; byte |= (uint8)or_in; NV_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; nv_dac_sys_pll_find((data / 100.0), &calced_clk, &m, &n, &p, 0); NV_REG32(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) NV_REG32(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 NV32_MEMPLL: LOG(8,("INFO: ---Memory PLL accessed.\n")); /* update the card's specs */ si->ps.std_memory_clock = freq; break; case NV32_COREPLL: LOG(8,("INFO: ---Core PLL accessed.\n")); /* update the card's specs */ si->ps.std_engine_clock = freq; break; case NVDAC_PIXPLLC: LOG(8,("INFO: ---DAC1 PLL accessed.\n")); break; case NVDAC2_PIXPLLC: LOG(8,("INFO: ---DAC2 PLL accessed.\n")); break; /* unexpected cases, here for learning goals... */ case NV32_MEMPLL2: LOG(8,("INFO: ---NV31/NV36 extension to memory PLL accessed only!\n")); break; case NV32_COREPLL2: LOG(8,("INFO: ---NV31/NV36 extension to core PLL accessed only!\n")); break; case NVDAC_PIXPLLC2: LOG(8,("INFO: ---NV31/NV36 extension to DAC1 PLL accessed only!\n")); break; case NVDAC2_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 */ NV_REG32(NV32_PFB_DEBUG_0) &= 0xffffffef; /* read RAM config hardware(?) strap */ ram_cfg = ((NV_REG32(NV32_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 = (NV_REG32(NV32_NV4STRAPINFO) & 0xffffffc0); NV_REG32(NV32_NV4STRAPINFO) = (data | (ram_cfg & 0x0000003f)); /* setup write to read delay (?) */ data = (NV_REG32(NV32_PFB_CONFIG_1) & 0xff8ffffe); data |= ((ram_cfg & 0x00000700) << 12); /* force update via b0 = 0... */ NV_REG32(NV32_PFB_CONFIG_1) = data; /* ... followed by b0 = 1(?) */ NV_REG32(NV32_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++) ((volatile 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 (((volatile uint32 *)si->framebuffer)[3] != data) { LOG(8,("INFO: ---RAM width tested: width is 64bits, correcting settings.\n")); NV_REG32(NV32_NV4STRAPINFO) &= ~0x00000004; } else { LOG(8,("INFO: ---RAM width tested: access is OK.\n")); } /* do RAM size test to confirm RAM size set to be correct */ ram_cfg = (NV_REG32(NV32_NV4STRAPINFO) & 0x00000003); data = 0x4e563542; /* first check for 32Mb... */ if (!ram_cfg) { /* write testpattern to just above the 16Mb boundary */ ((volatile uint32 *)si->framebuffer)[(16 * 1024 * 1024) >> 2] = data; /* check if pattern reads back */ if (((volatile uint32 *)si->framebuffer)[(16 * 1024 * 1024) >> 2] == data) { /* write second testpattern to base adress */ data = 0x4135564e; ((volatile uint32 *)si->framebuffer)[0] = data; if (((volatile uint32 *)si->framebuffer)[0] == data) { LOG(8,("INFO: ---RAM size tested: size was set OK (32Mb).\n")); return; } } /* one of the two tests for 32Mb failed, we must have 16Mb */ ram_cfg = 0x00000003; LOG(8,("INFO: ---RAM size tested: size is 16Mb, correcting settings.\n")); NV_REG32(NV32_NV4STRAPINFO) = (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); return; } /* ... now check for 16Mb... */ if (ram_cfg == 0x00000003) { /* increment testpattern */ data++; /* write testpattern to just above the 8Mb boundary */ ((volatile uint32 *)si->framebuffer)[(8 * 1024 * 1024) >> 2] = data; /* check if pattern reads back */ if (((volatile uint32 *)si->framebuffer)[(8 * 1024 * 1024) >> 2] == data) { LOG(8,("INFO: ---RAM size tested: size was set OK (16Mb).\n")); return; } else { /* assuming 8Mb: retesting below! */ ram_cfg = 0x00000002; LOG(8,("INFO: ---RAM size tested: size is NOT 16Mb, testing for 8Mb...\n")); NV_REG32(NV32_NV4STRAPINFO) = (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); } } /* ... and now check for 8Mb! (ram_cfg will be 'pre'set to 4Mb or 8Mb here) */ { /* increment testpattern (again) */ data++; /* write testpattern to just above the 4Mb boundary */ ((volatile uint32 *)si->framebuffer)[(4 * 1024 * 1024) >> 2] = data; /* check if pattern reads back */ if (((volatile uint32 *)si->framebuffer)[(4 * 1024 * 1024) >> 2] == data) { /* we have 8Mb, make sure this is set. */ ram_cfg = 0x00000002; LOG(8,("INFO: ---RAM size tested: size is 8Mb, setting 8Mb.\n")); /* fixme? assuming this should be done here! */ NV_REG32(NV32_NV4STRAPINFO) = (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); return; } else { /* we must have 4Mb, make sure this is set. */ ram_cfg = 0x00000001; LOG(8,("INFO: ---RAM size tested: size is 4Mb, setting 4Mb.\n")); NV_REG32(NV32_NV4STRAPINFO) = (((NV_REG32(NV32_NV4STRAPINFO)) & 0xfffffffc) | ram_cfg); return; } } } /* 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 = NV_REG32(reg); data &= and_out; data >>= shift; data2 = *((uint32*)(&(rom[(*adress + (data << 2))]))); NV_REG32(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(®); NV_REG8(reg) = index; byte = NV_REG8(reg + 1); byte &= (uint8)and_out; byte >>= byte2; offset32 = (byte << 2); data = *((uint32*)(&(rom[(*adress + offset32)]))); NV_REG32(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(®); NV_REG8(reg) = index; byte = NV_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; nv_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 = NV_REG32(reg2); NV_REG32(reg2) = ((data & 0xffff0000) | (n << 8) | m); data = NV_REG32(reg2); NV_REG32(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 = NV_REG32(reg); if (byte2 < 0x80) { data >>= byte2; } else { data <<= (0x0100 - byte2); } data &= and_out; translate_ISA_PCI(®2); NV_REG8(reg2) = index; byte = NV_REG8(reg2 + 1); byte &= (uint8)and_out2; byte |= (uint8)data; NV_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; NV_REG32(reg2) = data2; data = NV_REG32(reg); data &= and_out; data |= or_in; data |= or_in2; NV_REG32(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(®); NV_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(®); NV_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 */ NV_REG32(reg) = data; NV_REG32(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 = NV_REG8(reg); byte &= (uint8)and_out; byte |= (uint8)or_in; NV_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 = NV_REG32(reg); data &= and_out; data |= or_in; NV_REG32(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)]))); NV_REG32(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 = NV_REG32(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(®); NV_REG8(reg) = index; byte = NV_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(®); NV_REG8(reg) = index; byte = NV_REG8(reg + 1); byte &= (uint8)and_out; byte |= (uint8)or_in; NV_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; nv_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 = NV_REG32(reg); NV_REG32(reg) = ((data2 & 0xffff0000) | (n << 8) | m); data2 = NV_REG32(reg); NV_REG32(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) NV_REG32(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(®); NV_REG8(reg) = index; byte = NV_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) { /* note: * After writing data to RAM a snooze is required to make the test work. * Confirmed a NV11: without snooze it worked OK on a low-voltage AGP2.0 slot, * but on a higher-voltage AGP 1.0 slot it failed to identify errors correctly!! * Adding the snooze fixed that. */ uint32 data, dummy; uint8 cnt = 0; status_t stat = B_ERROR; /* set 'refctrl is valid' */ NV_REG32(NV32_PFB_REFCTRL) = 0x80000000; /* check RAM for 256bits buswidth(?) */ while ((cnt < 4) && (stat != B_OK)) { /* reset RAM bits at offset 224-255 bits four times */ ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; snooze(10); ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; snooze(10); ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; snooze(10); ((volatile uint32 *)si->framebuffer)[0x07] = 0x00000000; snooze(10); /* write testpattern */ ((volatile uint32 *)si->framebuffer)[0x07] = 0x4e563131; snooze(10); /* reset RAM bits at offset 480-511 bits */ ((volatile uint32 *)si->framebuffer)[0x0f] = 0x00000000; snooze(10); /* check testpattern to have survived */ if (((volatile 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 = NV_REG32(NV32_PFB_CONFIG_0); if (data & 0x00000010) { data &= 0xffffffcf; } else { data &= 0xffffffcf; data |= 0x00000020; } NV_REG32(NV32_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 = NV_REG32(NV32_NV10STRAPINFO); /* subtract 1MB */ data -= 0x00100000; /* write testpattern at generated RAM adress */ ((volatile uint32 *)si->framebuffer)[(data >> 2)] = 0x4e564441; snooze(10); /* reset first RAM adress */ ((volatile uint32 *)si->framebuffer)[0x00] = 0x00000000; snooze(10); /* dummyread first RAM adress four times */ dummy = ((volatile uint32 *)si->framebuffer)[0x00]; dummy = ((volatile uint32 *)si->framebuffer)[0x00]; dummy = ((volatile uint32 *)si->framebuffer)[0x00]; dummy = ((volatile uint32 *)si->framebuffer)[0x00]; /* check testpattern to have survived */ if (((volatile 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")); NV_REG32(NV32_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) { /* note: * After writing data to RAM a snooze is required to make the test work. * Confirmed a NV11: without snooze it worked OK on a low-voltage AGP2.0 slot, * but on a higher-voltage AGP 1.0 slot it failed to identify errors correctly!! * Adding the snooze fixed that. */ uint32 dummy; uint8 cnt = 0; status_t stat = B_ERROR; /* set 'refctrl is valid' */ NV_REG32(NV32_PFB_REFCTRL) = 0x80000000; /* check RAM */ while ((cnt < 4) && (stat != B_OK)) { /* set bit 11: 'pulse' something into a new setting? */ NV_REG32(NV32_PFB_CONFIG_0) |= 0x00000800; /* write testpattern to RAM adress 127Mb */ ((volatile uint32 *)si->framebuffer)[0x01fc0000] = 0x4e564441; snooze(10); /* reset first RAM adress */ ((volatile uint32 *)si->framebuffer)[0x00000000] = 0x00000000; snooze(10); /* dummyread first RAM adress four times */ dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; LOG(8,("INFO: (#%d) dummy1 = $%08x, ", cnt, dummy)); dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; LOG(8,("dummy2 = $%08x, ", dummy)); dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; LOG(8,("dummy3 = $%08x, ", dummy)); dummy = ((volatile uint32 *)si->framebuffer)[0x00000000]; LOG(8,("dummy4 = $%08x\n", dummy)); /* check testpattern to have survived */ if (((volatile uint32 *)si->framebuffer)[0x01fc0000] == 0x4e564441) stat = B_OK; cnt++; } /* clear bit 11: set normal mode */ NV_REG32(NV32_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 = NV8_ATTRDATW; break; case 0x03c1: *reg = NV8_ATTRDATR; break; case 0x03c2: *reg = NV8_MISCW; break; case 0x03c4: *reg = NV8_SEQIND; break; case 0x03c5: *reg = NV8_SEQDAT; break; case 0x03c6: *reg = NV8_PALMASK; break; case 0x03c7: *reg = NV8_PALINDR; break; case 0x03c8: *reg = NV8_PALINDW; break; case 0x03c9: *reg = NV8_PALDATA; break; case 0x03cc: *reg = NV8_MISCR; break; case 0x03ce: *reg = NV8_GRPHIND; break; case 0x03cf: *reg = NV8_GRPHDAT; break; case 0x03d4: *reg = NV8_CRTCIND; break; case 0x03d5: *reg = NV8_CRTCDAT; break; case 0x03da: *reg = NV8_INSTAT1; break; default: LOG(8,("\n\nINFO: WARNING: ISA->PCI register adress translation failed!\n\n")); return B_ERROR; break; } return B_OK; } void set_pll(uint32 reg, uint32 req_clk) { uint32 data; float calced_clk; uint8 m, n, p; nv_dac_sys_pll_find(req_clk, &calced_clk, &m, &n, &p, 0); /* programming the PLL needs to be done in steps! (confirmed NV28) */ data = NV_REG32(reg); NV_REG32(reg) = ((data & 0xffff0000) | (n << 8) | m); data = NV_REG32(reg); NV_REG32(reg) = ((p << 16) | (n << 8) | m); //fixme? /* program 2nd set N and M scalers if they exist (b31=1 enables them) */ if (si->ps.ext_pll) { if (reg == NV32_COREPLL) NV_REG32(NV32_COREPLL2) = 0x80000401; if (reg == NV32_MEMPLL) NV_REG32(NV32_MEMPLL2) = 0x80000401; } log_pll(reg, req_clk); } /* doing general fail-safe default setup here */ static status_t nv_crtc_setup_fifo() { /* enable access to primary head */ set_crtc_owner(0); /* 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.tv_encoder.type = NONE; si->ps.tv_encoder.version = 0; /* I2C init currently also fetches DDC EDID info from all connected screens */ i2c_init(); //fixme: add support for more encoders... BT_probe(); 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 nvidia.settings file)\n")); si->ps.crtc2_prim = !si->ps.crtc2_prim; } } /* notes: * - Since laptops don't have edid for their internal panels, and we can't switch * digitally connected external panels to DAC's, we keep needing the info fetched * from card programming done by the cardBIOS. * - Because we can only output modes on digitally connected panels upto/including * the modeline programmed into the GPU by the cardBIOS we don't use the actual * native modelines fetched via EDID for digitally connected panels. * - If the BIOS didn't program a modeline in the GPU for a digitally connected panel * we can't use that panel. * - The above restrictions exist due to missing nVidia hardware specs. */ 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.p2_timing.h_display = 0; si->ps.p2_timing.v_display = 0; si->ps.slaved_tmds1 = false; si->ps.slaved_tmds2 = false; si->ps.master_tmds1 = false; si->ps.master_tmds2 = false; si->ps.monitors = 0x00; /* 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.monitors |= CRTC1_TMDS; 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.monitors |= CRTC2_TMDS; 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.monitors |= CRTC1_TMDS; 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.monitors |= CRTC2_TMDS; 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.monitors & (CRTC1_TMDS | CRTC2_TMDS)) == (CRTC1_TMDS | CRTC2_TMDS)) && ((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.monitors &= ~CRTC1_TMDS; 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.monitors &= ~CRTC1_TMDS; 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.monitors &= ~CRTC2_TMDS; si->ps.p2_timing.h_display = 0; si->ps.p2_timing.v_display = 0; } } } /* fetch panel(s) modeline(s) */ if (si->ps.monitors & CRTC1_TMDS) { /* 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; /* display enable polarity (not an official flag) */ if (DACR(FP_TG_CTRL) & 0x10000000) si->ps.p1_timing.flags |= B_BLANK_PEDESTAL; /* 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.monitors & CRTC2_TMDS) { /* 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; /* display enable polarity (not an official flag) */ if (DAC2R(FP_TG_CTRL) & 0x10000000) si->ps.p2_timing.flags |= B_BLANK_PEDESTAL; /* 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", NV_REG32(NV32_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", NV_REG32(NV32_FUNCSEL))); LOG(2,("DAC1: PANEL_PWR: $%08x\n", NV_REG32(NV32_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", NV_REG32(NV32_2FUNCSEL))); LOG(2,("DAC2: PANEL_PWR: $%08x\n", NV_REG32(NV32_2PANEL_PWR))); } /* if something goes wrong during driver init reading those registers below might * result in a hanging system. Without reading them at least a blind (or KDL) * restart becomes possible. Leaving the code here for debugging purposes. */ if (0) { /* determine flatpanel type(s) */ /* note: * - on NV11 accessing registerset(s) hangs card. * - the same applies for NV34. * Confirmed for DAC2 access on ID 0x0322, * but only after a failed VESA modeswitch by the HAIKU kernel * at boot time caused by a BIOS fault inside the gfx card: it says * it can do VESA 1280x1024x32 on CRTC1/DAC1 but it fails: no signal. */ if ((si->ps.monitors & CRTC1_TMDS) && (si->ps.card_type != NV11)) { /* Read a indexed register to see if it indicates LVDS or TMDS panel presence. * b0-7 = adress, b16 = 1 = write_enable */ DACW(FP_TMDS_CTRL, ((1 << 16) | 0x04)); /* (b0-7 = data) */ if (DACR(FP_TMDS_DATA) & 0x01) LOG(2,("INFO: Flatpanel on head 1 is LVDS type\n")); else LOG(2,("INFO: Flatpanel on head 1 is TMDS type\n")); } if ((si->ps.monitors & CRTC2_TMDS) && (si->ps.card_type != NV11)) { /* Read a indexed register to see if it indicates LVDS or TMDS panel presence. * b0-7 = adress, b16 = 1 = write_enable */ DAC2W(FP_TMDS_CTRL, ((1 << 16) | 0x04)); /* (b0-7 = data) */ if (DAC2R(FP_TMDS_DATA) & 0x01) LOG(2,("INFO: Flatpanel on head 2 is LVDS type\n")); else LOG(2,("INFO: Flatpanel on head 2 is TMDS type\n")); } } LOG(2,("INFO: End flatpanel registers dump.\n")); } static void setup_output_matrix() { /* setup defaults: */ /* head 1 will be the primary head */ si->ps.crtc2_prim = false; /* no screens usable */ si->ps.crtc1_screen.have_native_edid = false; si->ps.crtc1_screen.have_full_edid = false; si->ps.crtc1_screen.timing.h_display = 0; si->ps.crtc1_screen.timing.v_display = 0; si->ps.crtc2_screen.have_native_edid = false; si->ps.crtc2_screen.have_full_edid = false; si->ps.crtc2_screen.timing.h_display = 0; si->ps.crtc2_screen.timing.v_display = 0; /* connect analog outputs straight through if possible */ if ((si->ps.secondary_head) && (si->ps.card_type != NV11)) nv_general_output_select(false); /* panels are pre-connected to a CRTC (1 or 2) by the card's BIOS, * we can't change this (lack of info) */ /* detect analog monitors. First try EDID, else use load sensing. */ /* (load sensing is confirmed working OK on NV04, NV05, NV11, NV18, NV28 and NV34.) */ /* primary connector: */ if (si->ps.con1_screen.have_native_edid) { if (!si->ps.con1_screen.digital) si->ps.monitors |= CRTC1_VGA; } else { if (nv_dac_crt_connected()) { si->ps.monitors |= CRTC1_VGA; /* assume 4:3 monitor on con1 */ si->ps.con1_screen.aspect = 1.33; } } /* Note: digitally connected panels take precedence over analog connected screens. */ /* fill-out crtc1_screen. This is tricky since we don't know which connector connects * to crtc1. * Also the BIOS might have programmed for a lower mode than EDID reports: * which limits our use of the panel (LVDS link setup too slow). */ if(si->ps.monitors & CRTC1_TMDS) { /* see if we have the full EDID info somewhere (lacking a hardware spec so * we need to compare: we don't know the location of a digital cross switch. * Note: don't compare pixelclock and flags as we make them up ourselves!) */ if (si->ps.con1_screen.have_full_edid && si->ps.con1_screen.digital && (si->ps.p1_timing.h_display == si->ps.con1_screen.timing.h_display) && (si->ps.p1_timing.h_sync_start == si->ps.con1_screen.timing.h_sync_start) && (si->ps.p1_timing.h_sync_end == si->ps.con1_screen.timing.h_sync_end) && (si->ps.p1_timing.h_total == si->ps.con1_screen.timing.h_total) && (si->ps.p1_timing.v_display == si->ps.con1_screen.timing.v_display) && (si->ps.p1_timing.v_sync_start == si->ps.con1_screen.timing.v_sync_start) && (si->ps.p1_timing.v_sync_end == si->ps.con1_screen.timing.v_sync_end) && (si->ps.p1_timing.v_total == si->ps.con1_screen.timing.v_total)) { /* fill-out crtc1_screen from EDID info fetched from connector 1 */ memcpy(&(si->ps.crtc1_screen), &(si->ps.con1_screen), sizeof(si->ps.crtc1_screen)); } else { if (si->ps.con2_screen.have_full_edid && si->ps.con2_screen.digital && (si->ps.p1_timing.h_display == si->ps.con2_screen.timing.h_display) && (si->ps.p1_timing.h_sync_start == si->ps.con2_screen.timing.h_sync_start) && (si->ps.p1_timing.h_sync_end == si->ps.con2_screen.timing.h_sync_end) && (si->ps.p1_timing.h_total == si->ps.con2_screen.timing.h_total) && (si->ps.p1_timing.v_display == si->ps.con2_screen.timing.v_display) && (si->ps.p1_timing.v_sync_start == si->ps.con2_screen.timing.v_sync_start) && (si->ps.p1_timing.v_sync_end == si->ps.con2_screen.timing.v_sync_end) && (si->ps.p1_timing.v_total == si->ps.con2_screen.timing.v_total)) { /* fill-out crtc1_screen from EDID info fetched from connector 2 */ memcpy(&(si->ps.crtc1_screen), &(si->ps.con2_screen), sizeof(si->ps.crtc1_screen)); } else { /* no match or no full EDID info: use the info fetched from the GPU */ si->ps.crtc1_screen.timing.pixel_clock = si->ps.p1_timing.pixel_clock; si->ps.crtc1_screen.timing.h_display = si->ps.p1_timing.h_display; si->ps.crtc1_screen.timing.h_sync_start = si->ps.p1_timing.h_sync_start; si->ps.crtc1_screen.timing.h_sync_end = si->ps.p1_timing.h_sync_end; si->ps.crtc1_screen.timing.h_total = si->ps.p1_timing.h_total; si->ps.crtc1_screen.timing.v_display = si->ps.p1_timing.v_display; si->ps.crtc1_screen.timing.v_sync_start = si->ps.p1_timing.v_sync_start; si->ps.crtc1_screen.timing.v_sync_end = si->ps.p1_timing.v_sync_end; si->ps.crtc1_screen.timing.v_total = si->ps.p1_timing.v_total; si->ps.crtc1_screen.timing.flags = si->ps.p1_timing.flags; si->ps.crtc1_screen.have_native_edid = true; si->ps.crtc1_screen.aspect = (si->ps.p1_timing.h_display / ((float)si->ps.p1_timing.v_display)); si->ps.crtc1_screen.digital = true; } } } else if(si->ps.monitors & CRTC1_VGA) { /* fill-out crtc1_screen from EDID info, or faked info if EDID failed. */ memcpy(&(si->ps.crtc1_screen), &(si->ps.con1_screen), sizeof(si->ps.crtc1_screen)); } /* force widescreen types if requested */ if (si->settings.force_ws) si->ps.crtc1_screen.aspect = 1.78; /* setup output devices and heads */ if (si->ps.secondary_head) { /* fill-out crtc2_screen. This is tricky since we don't know which connector * connects to crtc2. * Also the BIOS might have programmed for a lower mode than EDID reports: * which limits our use of the panel (LVDS link setup too slow). */ if(si->ps.monitors & CRTC2_TMDS) { /* see if we have the full EDID info somewhere (lacking a hardware spec so * we need to compare: we don't know the location of a digital cross switch. * Note: don't compare pixelclock and flags as we make them up ourselves!) */ if (si->ps.con1_screen.have_full_edid && si->ps.con1_screen.digital && (si->ps.p2_timing.h_display == si->ps.con1_screen.timing.h_display) && (si->ps.p2_timing.h_sync_start == si->ps.con1_screen.timing.h_sync_start) && (si->ps.p2_timing.h_sync_end == si->ps.con1_screen.timing.h_sync_end) && (si->ps.p2_timing.h_total == si->ps.con1_screen.timing.h_total) && (si->ps.p2_timing.v_display == si->ps.con1_screen.timing.v_display) && (si->ps.p2_timing.v_sync_start == si->ps.con1_screen.timing.v_sync_start) && (si->ps.p2_timing.v_sync_end == si->ps.con1_screen.timing.v_sync_end) && (si->ps.p2_timing.v_total == si->ps.con1_screen.timing.v_total)) { /* fill-out crtc2_screen from EDID info fetched from connector 1 */ memcpy(&(si->ps.crtc2_screen), &(si->ps.con1_screen), sizeof(si->ps.crtc2_screen)); } else { if (si->ps.con2_screen.have_full_edid && si->ps.con2_screen.digital && (si->ps.p2_timing.h_display == si->ps.con2_screen.timing.h_display) && (si->ps.p2_timing.h_sync_start == si->ps.con2_screen.timing.h_sync_start) && (si->ps.p2_timing.h_sync_end == si->ps.con2_screen.timing.h_sync_end) && (si->ps.p2_timing.h_total == si->ps.con2_screen.timing.h_total) && (si->ps.p2_timing.v_display == si->ps.con2_screen.timing.v_display) && (si->ps.p2_timing.v_sync_start == si->ps.con2_screen.timing.v_sync_start) && (si->ps.p2_timing.v_sync_end == si->ps.con2_screen.timing.v_sync_end) && (si->ps.p2_timing.v_total == si->ps.con2_screen.timing.v_total)) { /* fill-out crtc2_screen from EDID info fetched from connector 2 */ memcpy(&(si->ps.crtc2_screen), &(si->ps.con2_screen), sizeof(si->ps.crtc2_screen)); } else { /* no match or no full EDID info: use the info fetched from the GPU */ si->ps.crtc2_screen.timing.pixel_clock = si->ps.p2_timing.pixel_clock; si->ps.crtc2_screen.timing.h_display = si->ps.p2_timing.h_display; si->ps.crtc2_screen.timing.h_sync_start = si->ps.p2_timing.h_sync_start; si->ps.crtc2_screen.timing.h_sync_end = si->ps.p2_timing.h_sync_end; si->ps.crtc2_screen.timing.h_total = si->ps.p2_timing.h_total; si->ps.crtc2_screen.timing.v_display = si->ps.p2_timing.v_display; si->ps.crtc2_screen.timing.v_sync_start = si->ps.p2_timing.v_sync_start; si->ps.crtc2_screen.timing.v_sync_end = si->ps.p2_timing.v_sync_end; si->ps.crtc2_screen.timing.v_total = si->ps.p2_timing.v_total; si->ps.crtc2_screen.timing.flags = si->ps.p2_timing.flags; si->ps.crtc2_screen.have_native_edid = true; si->ps.crtc2_screen.aspect = (si->ps.p2_timing.h_display / ((float)si->ps.p2_timing.v_display)); si->ps.crtc2_screen.digital = true; } } } if (si->ps.card_type != NV11) { /* panels are pre-connected to a CRTC (1 or 2) by the card's BIOS, * we can't change this (lack of info) */ /* detect analog monitors. First try EDID, else use load sensing. */ /* (load sensing is confirmed working OK on NV18, NV28 and NV34.) */ /* secondary connector */ if (si->ps.con2_screen.have_native_edid) { if (!si->ps.con2_screen.digital) si->ps.monitors |= CRTC2_VGA; } else { if (nv_dac2_crt_connected()) { si->ps.monitors |= CRTC2_VGA; /* assume 4:3 monitor on con2 */ si->ps.con2_screen.aspect = 1.33; } } /* Note: digitally connected panels take precedence over analog connected screens. */ /* fill-out crtc2_screen if not already filled in by TMDS */ if ((si->ps.monitors & (CRTC2_TMDS | CRTC2_VGA)) == CRTC2_VGA) { /* fill-out crtc2_screen from EDID info, or faked info if EDID failed. */ memcpy(&(si->ps.crtc2_screen), &(si->ps.con2_screen), sizeof(si->ps.crtc2_screen)); } /* force widescreen types if requested */ if (si->settings.force_ws) si->ps.crtc2_screen.aspect = 1.78; /* 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 */ nv_general_output_select(true); si->ps.monitors = 0x21; /* tell head 2 it now has a screen (connected at connector 1) */ memcpy(&(si->ps.crtc2_screen), &(si->ps.con1_screen), sizeof(si->ps.crtc2_screen)); 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 */ if (si->ps.card_arch < NV40A) { 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; } else { switch (si->ps.card_type) { case NV40: /* Geforce 6800 AGP was confirmed OK 'in the old days' */ case NV41: /* Geforce 6800 type as well - needs to be confirmed (guessing) */ case NV45: /* Geforce 6800 PCIe - needs to be confirmed (guessing) */ 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 NV44: /* NV44 is a special case in this situation (confirmed Geforce 6200LE): * It's hardware behaves as a NV40/41/45 but there's some unknown extra bit * which needs to be programmed now to get CRTC2/DAC2 displaying anything * else than blackness (with monitor ON @ correct refresh and resolution). * We are therefore forced to use CRTC1/DAC1 instead (as these are presetup * fully by the card's BIOS at POST in this situation). */ LOG(2,("INFO: head 1 has nothing connected;\n")); LOG(2,("INFO: head 2 has an analog panel or CRT:\n")); LOG(2,("INFO: cross-switching outputs for NV44!\n")); nv_general_output_select(true); LOG(2,("INFO: defaulting to head 1 for primary use.\n")); break; default: /* newer NV40 architecture cards contains (an) additional switch(es) * to connect a CRTC/DAC combination to a connector. The BIOSes of * these cards connect head1 to connectors 1 and 2 simultaneously if * only one VGA screen is found being on connector 2. Which is the * case here. * Confirmed on NV43, G71 and G73. */ LOG(2,("INFO: Both card outputs are connected to head 1;\n")); LOG(2,("INFO: defaulting to head 1 for primary use.\n")); break; } } 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 */ nv_general_output_select(true); si->ps.monitors = 0x12; /* tell head 1 it now has a screen (connected at connector 2) */ memcpy(&(si->ps.crtc1_screen), &(si->ps.con2_screen), sizeof(si->ps.crtc1_screen)); 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; case 0x32: /* more than two monitors connected to just two outputs: illegal! */ LOG(2,("INFO: illegal monitor setup ($%02x):\n", si->ps.monitors)); /* head 2 takes precedence because it has a digital panel while * head 1 has not. */ 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 /* dualhead NV11 cards */ { /* confirmed no analog output switch-options for NV11 */ LOG(2,("INFO: NV11 outputs are hardwired to be straight-through\n")); /* (DDC or load sense analog monitor on secondary connector is impossible on NV11) */ /* force widescreen types if requested */ if (si->settings.force_ws) si->ps.crtc2_screen.aspect = 1.78; /* 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 */ { //fixme? add TVout (only, so no CRT connected) support... } } status_t get_crtc1_screen_native_mode(display_mode *mode) { if (!si->ps.crtc1_screen.have_native_edid) return B_ERROR; mode->space = B_RGB32_LITTLE; mode->virtual_width = si->ps.crtc1_screen.timing.h_display; mode->virtual_height = si->ps.crtc1_screen.timing.v_display; mode->h_display_start = 0; mode->v_display_start = 0; mode->flags = 0; memcpy(&mode->timing, &si->ps.crtc1_screen.timing, sizeof(mode->timing)); return B_OK; } status_t get_crtc2_screen_native_mode(display_mode *mode) { if (!si->ps.crtc2_screen.have_native_edid) return B_ERROR; mode->space = B_RGB32_LITTLE; mode->virtual_width = si->ps.crtc2_screen.timing.h_display; mode->virtual_height = si->ps.crtc2_screen.timing.v_display; mode->h_display_start = 0; mode->v_display_start = 0; mode->flags = 0; memcpy(&mode->timing, &si->ps.crtc2_screen.timing, sizeof(mode->timing)); return B_OK; } 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 nv.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 nv.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 nv.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 nv.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 nv.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; } /* notes on PLL's: on NV34, GeForce FX 5200, id 0x0322 DAC1 PLL observed behaviour: - Fcomp may be as high as 27Mhz (BIOS), and current set range seems ok as well; - Fvco may not be as low as 216Mhz (DVI pixelclock intermittant locking error, visible as horizontal shifting picture and black screen (out of range): both intermittant); - Fvco may be as high as 432Mhz (BIOS); - This is not an extended PLL: the second divisor register does not do anything. */ static void pinsnv30_arch_fake(void) { /* determine PLL type */ if ((si->ps.card_type == NV31) || (si->ps.card_type == NV36) || (si->ps.card_type >= NV40)) { /* we have a extended PLL */ si->ps.ext_pll = true; } else { /* 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; if (si->ps.ext_pll) { si->ps.max_pixel_vco = 600; si->ps.min_pixel_vco = 220; si->ps.max_video_vco = 600; si->ps.min_video_vco = 220; } else { 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 capability */ /* (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 nv.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; if ((CFGR(DEVID) & 0xfff0ffff) == 0x024010de) { /* C51 chipset */ si->ps.max_system_vco = 1000; si->ps.min_system_vco = 500; si->ps.max_pixel_vco = 1000; si->ps.min_pixel_vco = 500; si->ps.max_video_vco = 1000; si->ps.min_video_vco = 500; } } static void getRAMsize_arch_nv4(void) { uint32 strapinfo = NV_REG32(NV32_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 = NV_REG32(NV32_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 = NV_REG32(NV32_NV10STRAPINFO); uint32 mem_size; 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: kerneldriver mapped %3.3fMb framebuffer memory\n", (si->ps.memory_size / (1024.0 * 1024.0)))); LOG(8,("INFO: (Memory detection) Strapinfo value is: $%08x\n", strapinfo)); mem_size = (strapinfo & 0x3ff00000); if (!mem_size) { mem_size = 16 * 1024 * 1024; LOG(8,("INFO: NV10/20/30 architecture chip with unknown RAM amount detected;\n")); LOG(8,("INFO: Setting 16Mb\n")); } /* don't attempt to adress memory not mapped by the kerneldriver */ if (si->ps.memory_size > mem_size) si->ps.memory_size = mem_size; } } static void getstrap_arch_nv10_20_30_40(void) { uint32 dev_manID = CFGR(DEVID); uint32 strapinfo = NV_REG32(NV32_NVSTRAPINFO2); /* determine if we have a dualhead card */ si->ps.secondary_head = false; switch (si->ps.card_type) { case NV04: case NV05: case NV05M64: case NV06: case NV10: case NV15: case NV20: break; default: if ((dev_manID & 0xfff0ffff) == 0x01a010de) { /* this is a singlehead NV11! */ } else { si->ps.secondary_head = true; } } /* determine PLL reference crystal frequency: three types are used... */ if ((si->ps.secondary_head) && (si->ps.card_type != NV11)) strapinfo &= 0x00400040; else strapinfo &= 0x00000040; switch (strapinfo) { case 0x00000000: si->ps.f_ref = 13.50000; break; case 0x00000040: si->ps.f_ref = 14.31818; break; case 0x00400000: si->ps.f_ref = 27.00000; break; case 0x00400040: si->ps.f_ref = 25.00000; break; } } void dump_pins(void) { char *msg = ""; LOG(2,("INFO: pinsdump follows:\n")); LOG(2,("PLL type: %s\n", si->ps.ext_pll ? "extended" : "standard")); 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: %s\n", si->ps.secondary_head ? "present" : "absent")); LOG(2,("tvout: %s\n", si->ps.tvout ? "present" : "absent")); /* setup TVout logmessage text */ switch (si->ps.tv_encoder.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 TV encoder detected; silicon revision is $%02x\n", msg, si->ps.tv_encoder.version)); // 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.monitors & CRTC1_TMDS) { LOG(2,("found DFP (digital flatpanel) on CRTC1; CRTC1 is %s\n", si->ps.slaved_tmds1 ? "slaved" : "master")); 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.crtc1_screen.aspect)); } if (si->ps.monitors & CRTC2_TMDS) { LOG(2,("found DFP (digital flatpanel) on CRTC2; CRTC2 is %s\n", si->ps.slaved_tmds2 ? "slaved" : "master")); 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.crtc2_screen.aspect)); } LOG(2,("monitor (output devices) setup matrix: $%02x\n", si->ps.monitors)); LOG(2,("INFO: end pinsdump.\n")); }