/* Author: Rudolf Cornelissen 4/2002-11/2005 */ #define MODULE_BIT 0x00100000 #include "nv_std.h" #define PRADR 0x88 #define SCADR 0x8a #define WR 0x00 #define RD 0x01 enum { // TVoutput mode to set NOT_SUPPORTED = 0, NTSC640_TST, NTSC640, NTSC800, PAL800_TST, PAL640, PAL800, NTSC720, PAL720, NTSC640_OS, PAL800_OS }; /* Dirk Thierbach's Macro setup for registers 0xda-0xfe. * (also see http://sourceforge.net/projects/nv-tv-out/) */ static uint8 BtNtscMacro0 [] = { 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, 0xbd,0x67,0xb5,0x90,0xb2,0x7d,0x00,0x00}; static uint8 BtNtscMacro1 [] = { 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, 0xbd,0x67,0xb5,0x90,0xb2,0x7d,0x63,0x00}; static uint8 BtNtscMacro2 [] = { 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, 0xbd,0x6c,0x31,0x92,0x32,0xdd,0xe3,0x00}; static uint8 BtNtscMacro3 [] = { 0x0f,0xfc,0x20,0xd0,0x6f,0x0f,0x00,0x00,0x0c,0xf3,0x09, 0xbd,0x66,0xb5,0x90,0xb2,0x7d,0xe3,0x00}; static uint8 BtPalMacro0 [] = { 0x05,0x57,0x20,0x40,0x6e,0x7e,0xf4,0x51,0x0f,0xf1,0x05, 0xd3,0x78,0xa2,0x25,0x54,0xa5,0x00,0x00}; static uint8 BtPalMacro1 [] = { 0x05,0x57,0x20,0x40,0x6e,0x7e,0xf4,0x51,0x0f,0xf1,0x05, 0xd3,0x78,0xa2,0x25,0x54,0xa5,0x63,0x00}; static uint8 BT_set_macro (int std, int mode) { uint8 stat; uint8 buffer[21]; LOG(4,("Brooktree: Setting Macro:\n")); if ((std < 0) | (std > 1) | (mode < 0) | (mode > 3)) { LOG(4,("Brooktree: Non existing mode or standard selected, aborting.\n")); return 0x80; } switch (std) { case 0: /* NTSC */ switch (mode) { case 0: /* disabled */ LOG(4,("Brooktree: NTSC, disabled\n")); memcpy(&buffer[2], &BtNtscMacro0, 19); break; case 1: /* enabled mode 1 */ LOG(4,("Brooktree: NTSC, mode 1\n")); memcpy(&buffer[2], &BtNtscMacro1, 19); break; case 2: /* enabled mode 2 */ LOG(4,("Brooktree: NTSC, mode 2\n")); memcpy(&buffer[2], &BtNtscMacro2, 19); break; case 3: /* enabled mode 3 */ LOG(4,("Brooktree: NTSC, mode 3\n")); memcpy(&buffer[2], &BtNtscMacro3, 19); break; } break; case 1: /* PAL */ switch (mode) { case 0: /* disabled */ LOG(4,("Brooktree: PAL, disabled\n")); memcpy(&buffer[2], &BtPalMacro0, 19); break; case 1: case 2: case 3: /* enabled */ LOG(4,("Brooktree: PAL, enabled\n")); memcpy(&buffer[2], &BtPalMacro1, 19); break; } break; } buffer[0] = si->ps.tv_encoder.adress + WR; /* select first register to write to */ buffer[1] = 0xda; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting Macro\n")); return stat; }//end BT_set_macro. /* see if a (possible) BT/CX chip resides at the given adress. Return zero if no errors occurred. */ static uint8 BT_check (uint8 bus, uint8 adress) { uint8 buffer[3]; buffer[0] = adress + WR; /* set ESTATUS at b'00'; and enable bt chip-outputs * WARNING: * If bit0 = 0 is issued below (EN_OUT = disabled), the BT will lock SDA * after writing adress $A0 (setting EN_XCLK)!!! * Until a reboot the corresponding I2C bus will be inacessable then!!! */ buffer[1] = 0xc4; /* fixme: if testimage 'was' active txbuffer[3] should become 0x05... * (currently this cannot be detected in a 'foolproof' way so don't touch...) */ /* (ESTATUS b'0x' means: RX ID and VERSION info later..) */ buffer[2] = 0x01; /* reset status */ i2c_flag_error (-1); /* do check */ i2c_bstart(bus); i2c_writebuffer(bus, buffer, sizeof(buffer)); i2c_bstop(bus); return i2c_flag_error(0); } /* identify chiptype */ static uint8 BT_read_type (void) { uint8 id, type, stat; uint8 buffer[3]; /* Make sure a CX (Conexant) chip (if this turns out to be there) is set to * BT-compatibility mode! (This command will do nothing on a BT chip...) */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select CX reg. for BT-compatible readback, video still off */ buffer[1] = 0x6c; /* set it up */ buffer[2] = 0x02; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* abort on errors */ stat = i2c_flag_error(0); if (stat) return stat; /* Do actual readtype command */ i2c_bstart(si->ps.tv_encoder.bus); /* issue I2C read command */ i2c_writebyte(si->ps.tv_encoder.bus, si->ps.tv_encoder.adress + RD); /* receive 1 byte; * ACK level to TX after last byte to RX should be 1 (= NACK) (see I2C spec). */ /* note: * While the BT's don't care, CX chips will block the SDA line if * an ACK gets sent! */ id = i2c_readbyte(si->ps.tv_encoder.bus, true); i2c_bstop(si->ps.tv_encoder.bus); /* abort on errors */ stat = i2c_flag_error(0); if (stat) return stat; /* check type to be supported one */ type = (id & 0xe0) >> 5; if (type > 3) { LOG(4,("Brooktree: Found unsupported encoder type %d, aborting.\n", type)); return 0x80; } /* inform driver about TV encoder found */ si->ps.tvout = true; si->ps.tv_encoder.type = BT868 + type; si->ps.tv_encoder.version = id & 0x1f; return stat; } bool BT_probe() { uint8 bus; bool btfound = false; bool *i2c_bus = &(si->ps.i2c_bus0); LOG(4,("Brooktree: Checking wired I2C bus(ses) for first possible TV encoder...\n")); for (bus = 0; bus < 3; bus++) { if (i2c_bus[bus] && !btfound) { /* try primary adress on bus */ if (!BT_check(bus, PRADR)) { btfound = true; si->ps.tv_encoder.adress = PRADR; si->ps.tv_encoder.bus = bus; } else { /* try secondary adress on bus */ if (!BT_check(bus, SCADR)) { btfound = true; si->ps.tv_encoder.adress = SCADR; si->ps.tv_encoder.bus = bus; } } } } /* identify exact TV encoder type */ if (btfound) { /* if errors are found, retry */ /* note: * NACK: occurs on some ASUS V7700 GeForce cards! * (apparantly the video-in chip or another chip resides at 'BT' adresses * there..) */ uint8 stat; uint8 cnt = 0; while ((stat = BT_read_type()) && (cnt < 3)) { /* don't retry on unsupported chiptype */ if (stat == 0x80) { btfound = 0; break; } cnt++; } if (stat & 0x7f) { LOG(4,("Brooktree: Too much errors occurred, aborting.\n")); btfound = 0; } } if (btfound) LOG(4,("Brooktree: Found TV encoder on bus %d, adress $%02x\n", si->ps.tv_encoder.bus, si->ps.tv_encoder.adress)); else LOG(4,("Brooktree: No TV encoder Found\n")); return btfound; } static uint8 BT_init_PAL640() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting PAL 640x480 desktop mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to buffer[2] = 0x60; buffer[3] = 0x80; buffer[4] = 0x8a; buffer[5] = 0xa6; buffer[6] = 0x68; buffer[7] = 0xc1; buffer[8] = 0x2e; buffer[9] = 0xf2; buffer[10] = 0x27; buffer[11] = 0x00; buffer[12] = 0xb0; buffer[13] = 0x0a; buffer[14] = 0x0b; buffer[15] = 0x71; buffer[16] = 0x5a; buffer[17] = 0xe0; buffer[18] = 0x36; buffer[19] = 0x00; buffer[20] = 0x50; buffer[21] = 0x72; buffer[22] = 0x1c; buffer[23] = 0x8d; //chip-pin CLKI is pixel clock (only non-default here!) buffer[24] = 0x24; buffer[25] = 0xf0; buffer[26] = 0x58; buffer[27] = 0x81; buffer[28] = 0x49; buffer[29] = 0x8c; buffer[30] = 0x0c; buffer[31] = 0x8c; buffer[32] = 0x79; buffer[33] = 0x26; buffer[34] = 0x00; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode PAL640\n")); return stat; }//end BT_init_PAL640. static uint8 BT_init_PAL800() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting PAL 800x600 desktop mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to buffer[2] = 0x00; buffer[3] = 0x20; buffer[4] = 0xaa; buffer[5] = 0xca; buffer[6] = 0x9a; buffer[7] = 0x0d; buffer[8] = 0x29; buffer[9] = 0xfc; buffer[10] = 0x39; buffer[11] = 0x00; buffer[12] = 0xc0; buffer[13] = 0x8c; buffer[14] = 0x03; buffer[15] = 0xee; buffer[16] = 0x5f; buffer[17] = 0x58; buffer[18] = 0x3a; buffer[19] = 0x66; buffer[20] = 0x96; buffer[21] = 0x00; buffer[22] = 0x00; buffer[23] = 0x90; //chip-pin CLKI is pixel clock (only non-default here!) buffer[24] = 0x24; buffer[25] = 0xf0; buffer[26] = 0x57; buffer[27] = 0x80; buffer[28] = 0x48; buffer[29] = 0x8c; buffer[30] = 0x18; buffer[31] = 0x28; buffer[32] = 0x87; buffer[33] = 0x1f; buffer[34] = 0x00; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode PAL800\n")); return stat; }//end BT_init_PAL800. static uint8 BT_init_NTSC640() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting NTSC 640x480 desktop mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to buffer[2] = 0x00; buffer[3] = 0x80; buffer[4] = 0x84; buffer[5] = 0x96; buffer[6] = 0x60; buffer[7] = 0x7d; buffer[8] = 0x22; buffer[9] = 0xd4; buffer[10] = 0x27; buffer[11] = 0x00; buffer[12] = 0x10; buffer[13] = 0x7e; buffer[14] = 0x03; buffer[15] = 0x58; buffer[16] = 0x4b; buffer[17] = 0xe0; buffer[18] = 0x36; buffer[19] = 0x92; buffer[20] = 0x54; buffer[21] = 0x0e; buffer[22] = 0x88; buffer[23] = 0x8c; //chip-pin CLKI is pixel clock (only non-default here!) buffer[24] = 0x0a; buffer[25] = 0xe5; buffer[26] = 0x76; buffer[27] = 0x79; buffer[28] = 0x44; buffer[29] = 0x85; buffer[30] = 0x00; buffer[31] = 0x00; buffer[32] = 0x80; buffer[33] = 0x20; buffer[34] = 0x00; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode NTSC640\n")); return stat; }//end BT_init_NTSC640. static uint8 BT_init_NTSC800() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting NTSC 800x600 desktop mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to buffer[2] = 0xa0; buffer[3] = 0x20; buffer[4] = 0xb6; buffer[5] = 0xce; buffer[6] = 0x84; buffer[7] = 0x55; buffer[8] = 0x20; buffer[9] = 0xd8; buffer[10] = 0x39; buffer[11] = 0x00; buffer[12] = 0x70; buffer[13] = 0x42; buffer[14] = 0x03; buffer[15] = 0xdf; buffer[16] = 0x56; buffer[17] = 0x58; buffer[18] = 0x3a; buffer[19] = 0xcd; buffer[20] = 0x9c; buffer[21] = 0x14; buffer[22] = 0x3b; buffer[23] = 0x91; //chip-pin CLKI is pixel clock (only non-default here!) buffer[24] = 0x0a; buffer[25] = 0xe5; buffer[26] = 0x74; buffer[27] = 0x77; buffer[28] = 0x43; buffer[29] = 0x85; buffer[30] = 0xba; buffer[31] = 0xe8; buffer[32] = 0xa2; buffer[33] = 0x17; buffer[34] = 0x00; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode PAL800\n")); return stat; }//end BT_init_NTSC800. static uint8 BT_init_PAL720() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting PAL 720x576 overscanning DVD mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to buffer[2] = 0xf0; buffer[3] = 0xd0; buffer[4] = 0x82; buffer[5] = 0x9c; buffer[6] = 0x5a; buffer[7] = 0x31; buffer[8] = 0x16; buffer[9] = 0x22; buffer[10] = 0xa6; buffer[11] = 0x00; buffer[12] = 0x78; buffer[13] = 0x93; buffer[14] = 0x03; buffer[15] = 0x71; buffer[16] = 0x2a; buffer[17] = 0x40; buffer[18] = 0x0a; buffer[19] = 0x00; buffer[20] = 0x50; buffer[21] = 0x55; buffer[22] = 0x55; buffer[23] = 0x8c; //chip-pin CLKI is pixel clock (only non-default here!) buffer[24] = 0x24; buffer[25] = 0xf0; buffer[26] = 0x59; buffer[27] = 0x82; buffer[28] = 0x49; buffer[29] = 0x8c; buffer[30] = 0x8e; buffer[31] = 0xb0; buffer[32] = 0xe6; buffer[33] = 0x28; buffer[34] = 0x00; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode PAL720\n")); return stat; }//end BT_init_PAL720. static uint8 BT_init_NTSC720() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting NTSC 720x480 overscanning DVD mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to. buffer[2] = 0xf0; //lsb h_clk_o: overscan comp = 0, so h_clk_o = 2 * h_clk_i (VSR=2 = scaling=1) buffer[3] = 0xd0; //lsb h_active: h_active = 720 pixels wide port buffer[4] = 0x83; //scope: OK hsync_width: (hsync_width / h_clk_o) * 63.55556uS = 4.70uS for NTSC buffer[5] = 0x98; //scope: OK hburst_begin: (hburst_begin / h_clk_o) * 63.55556uS = 5.3uS for NTSC buffer[6] = 0x5e; //scope: OK hburst_end: ((hburst_end + 128) / h_clk_o) * 63.55556uS = 7.94uS for NTSC //How to find the correct values for h_blank_o and v_blank_o: // 1. Calculate h_blank_o according to initial setting guideline mentioned below; // 2. Set v_blank_o in the neighbourhood of $18, so that TV picture does not have ghosts on right side in it while // horizontal position is about OK; // 3. Then tune h_blank_o for centered output on scope (look at front porch and back porch); // 4. Now shift the TV output using Hsync_offset for centered output while looking at TV (in method 'SetBT_Hphase' above); // 5. If no vertical shivering occurs when image is centered, you're done. Else: // 6. Modify the RIVA (BeScreen) h_sync_start setting somewhat to get stable centered picture possible on TV AND!: // 7. Make sure you update the Chrontel horizontal Phase setting also then! if (si->ps.tv_encoder.type >= CX25870)//set CX value { /* confirmed on NV11 using 4:3 TV and 16:9 TV */ buffer[7] = 0x0c; //scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV //(guideline for initial setting: (h_blank_o / h_clk_0) * 63.55556uS = 9.5uS for NTSC) } else //set BT value { /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ buffer[7] = 0x28; //scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV //(guideline for initial setting: (h_blank_o / h_clk_0) * 63.55556uS = 9.5uS for NTSC) } buffer[8] = 0x18; //try-out; scope: checked against other modes, looks OK. v_blank_o: 1e active line ('pixel') buffer[9] = 0xf2; //v_active_o: = (active output lines + 2) / field (on TV) buffer[10] = 0x26; //lsn = msn h_clk_o; //b4-5 = msbits h_active; //b7 = b8 v_avtive_o. buffer[11] = 0x00; //h_fract is always 0. buffer[12] = 0x78; //lsb h_clk_i: h_clk_i is horizontal total = 888. buffer[13] = 0x90; //try-out; lsb h_blank_i: #clks between start sync and new line 1st pixel; copy to VGA delta-sync! buffer[14] = 0x03; //b2-0 = msn h_clk_i; //try-out: b3 = msn h_blank_i; //b4 = vblankdly is always 0. buffer[15] = 0x0d; //lsb v_lines_i: v_lines_i = 525 buffer[16] = 0x1a; //try-out; v_blank_i: #input lines between start sync and new line (pixel); copy to VGA delta-sync! //Make sure though that this value for the BT is *even*, or image will shiver a *lot* horizontally on TV. buffer[17] = 0xe0; //lsb v_active_i: v_active_i = 480 buffer[18] = 0x36; //b1-0 = msn v_lines_i; //b3-2 = msn v_active_i; //b5-4 = ylpf = 3; //b7-6 = clpf = 0. buffer[19] = 0x00; //lsb v_scale: v_scale = off = $1000 buffer[20] = 0x50; //b5-0 = msn v_scale; //scope: tuned. b7-6 = msn h_blank_o. //(((PLL_INT + (PLL_FRACT/65536)) / 6) * 13500000) = PIXEL_CLK = (hor.tot. * v_lines_i * 60Hz) buffer[21] = 0x98; //lsb PLL fract: PLL fract = 0x6e98 buffer[22] = 0x6e; //msb PLL fract buffer[23] = 0x8c; //b5-0 = PLL int: PLL int = 0x0c; //b6 = by_pll: by_pll = 0; //b7 = EN_XCLK: chip-pin CLKI is pixel clock. buffer[24] = 0x0a; //b0 = ni_out is always 0; //b1 = setup = 1 for NTSC; //b2 = 625line = 0 for NTSC; //b3 = vsync_dur = 1 for NTSC; //b4 = dic_screset is always 0; //b5 = pal_md = 0 for NTSC; //b6 = eclip is always 0; //b7 = reserved (en_scart) is always 0. buffer[25] = 0xe5; //sync_amp $e5 for NTSC buffer[26] = 0x75; //bst_amp $74-$76 for NTSC buffer[27] = 0x78; //mcr: r-y $77-$79 for NTSC buffer[28] = 0x44; //mcb: b-y $43-$44 for NTSC buffer[29] = 0x85; //my: y $85 for NTSC buffer[30] = 0x3c; //lsb msc: msc b31-0: NTSC formula: ((3579545 / pixelclk) * 2^32) = MSC buffer[31] = 0x91; //msc = $20c2913c buffer[32] = 0xc2; buffer[33] = 0x20; //msb msc. buffer[34] = 0x00; //phase_off always $00 /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode NTSC720\n")); return stat; }//end BT_init_NTSC720. static uint8 BT_init_PAL800_OS() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting PAL 800x600 overscanning VCD mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to. buffer[2] = 0x60; //lsb h_clk_o: overscan comp = 0, so h_clk_o = 2 * h_clk_i (VSR=2 = scaling=1) buffer[3] = 0x20; //lsb h_active: h_active = 800 pixels wide port buffer[4] = 0x8b; //scope: OK hsync_width: (hsync_width / h_clk_o) * 64.0uS = 4.70uS for PAL buffer[5] = 0xa5; //scope: OK hburst_begin: (hburst_begin / h_clk_o) * 64.0uS = 5.6uS for PAL buffer[6] = 0x6b; //scope: OK hburst_end: ((hburst_end + 128) / h_clk_o) * 64.0uS = 7.97uS for PAL //How to find the correct values for h_blank_o and v_blank_o: // 1. Calculate h_blank_o according to initial setting guideline mentioned below; // 2. Set v_blank_o in the neighbourhood of $18, so that TV picture does not have ghosts on right side in it while // horizontal position is about OK; // 3. Then tune h_blank_o for centered output on scope (look at front porch and back porch); // 4. Now shift the TV output using Hsync_offset for centered output while looking at TV (in method 'SetBT_Hphase' above); // 5. If no vertical shivering occurs when image is centered, you're done. Else: // 6. Modify the RIVA (BeScreen) h_sync_start setting somewhat to get stable centered picture possible on TV AND!: // 7. Make sure you update the Chrontel horizontal Phase setting also then! if (si->ps.tv_encoder.type >= CX25870)//set CX value { /* confirmed on NV11 using 4:3 TV and 16:9 TV */ buffer[7] = 0xf0; buffer[8] = 0x17; } else //set BT value { /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ buffer[7] = 0xd0;//scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV //(guideline for initial setting: (h_blank_o / h_clk_0) * 64.0uS = 10.0uS for PAL) buffer[8] = 0x18;//try-out; scope: checked against other modes, looks OK. v_blank_o: 1e active line ('pixel') } buffer[9] = 0x2e; //v_active_o: = (active output lines + 2) / field (on TV) buffer[10] = 0xb7; //lsn = msn h_clk_o; //b4-5 = msbits h_active; //b7 = b8 v_avtive_o. buffer[11] = 0x00; //h_fract is always 0. buffer[12] = 0xb0; //lsb h_clk_i: h_clk_i is horizontal total = 944. if (si->ps.tv_encoder.type >= CX25870)//set CX value buffer[13] = 0x20; else //set BT value buffer[13] = 0x14;//try-out; lsb h_blank_i: #clks between start sync and new line 1st pixel; copy to VGA delta-sync! buffer[14] = 0x03; //b2-0 = msn h_clk_i; //try-out: b3 = msn h_blank_i; //b4 = vblankdly is always 0. buffer[15] = 0x71; //lsb v_lines_i: v_lines_i = 625 if (si->ps.tv_encoder.type >= CX25870)//set CX value buffer[16] = 0x08; else //set BT value buffer[16] = 0x2a;//try-out; v_blank_i: #input lines between start sync and new line (pixel); copy to VGA delta-sync! //Make sure though that this value for the BT is *even*, or image will shiver a *lot* horizontally on TV. buffer[17] = 0x58; //lsb v_active_i: v_active_i = 600 buffer[18] = 0x3a; //b1-0 = msn v_lines_i; //b3-2 = msn v_active_i; //b5-4 = ylpf = 3; //b7-6 = clpf = 0. buffer[19] = 0x00; //lsb v_scale: v_scale = off = $1000 buffer[20] = 0x10; //b5-0 = msn v_scale; //scope: tuned. b7-6 = msn h_blank_o. //(((PLL_INT + (PLL_FRACT/65536)) / 6) * 13500000) = PIXEL_CLK = (hor.tot. * v_lines_i * 50Hz) buffer[21] = 0x72; //lsb PLL fract: PLL fract = 0x1c72 buffer[22] = 0x1c; //msb PLL fract buffer[23] = 0x8d; //b5-0 = PLL int: PLL int = 0x0d; //b6 = by_pll: by_pll = 0; //b7 = EN_XCLK: chip-pin CLKI is pixel clock. buffer[24] = 0x24; //b0 = ni_out is always 0; //b1 = setup = 0 for PAL; //b2 = 625line = 1 for PAL; //b3 = vsync_dur = 0 for PAL; //b4 = dic_screset is always 0; //b5 = pal_md = 1 for PAL; //b6 = eclip is always 0; //b7 = reserved (en_scart) is always 0. buffer[25] = 0xf0; //sync_amp $f0 for PAL buffer[26] = 0x57; //bst_amp $57-$58 for PAL buffer[27] = 0x80; //mcr: r-y $80-$81 for PAL buffer[28] = 0x48; //mcb: b-y $48-$49 for PAL buffer[29] = 0x8c; //my: y $8c for PAL buffer[30] = 0x31; //lsb msc: msc b31-0: PAL formula: ((4433619 / pixelclk) * 2^32) = MSC buffer[31] = 0x8c; //msc = $26798c31 buffer[32] = 0x79; buffer[33] = 0x26; //msb msc. buffer[34] = 0x00; //phase_off always $00 /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode PAL800 OS\n")); return stat; }//end BT_init_PAL800_OS. static uint8 BT_init_NTSC640_OS() { uint8 stat; uint8 buffer[35]; LOG(4,("Brooktree: Setting NTSC 640x480 overscanning VCD mode\n")); buffer[0] = si->ps.tv_encoder.adress + WR; //issue I2C write command buffer[1] = 0x76; //select first bt register to write to. buffer[2] = 0x20; //lsb h_clk_o: overscan comp = 0, so h_clk_o = 2 * h_clk_i (VSR=2 = scaling=1) buffer[3] = 0x80; //lsb h_active: h_active = 640 pixels wide port buffer[4] = 0x74; //scope: OK hsync_width: (hsync_width / h_clk_o) * 63.55556uS = 4.70uS for NTSC buffer[5] = 0x83; //scope: OK hburst_begin: (hburst_begin / h_clk_o) * 63.55556uS = 5.3uS for NTSC buffer[6] = 0x44; //scope: OK hburst_end: ((hburst_end + 128) / h_clk_o) * 63.55556uS = 7.94uS for NTSC //How to find the correct values for h_blank_o and v_blank_o: // 1. Calculate h_blank_o according to initial setting guideline mentioned below; // 2. Set v_blank_o in the neighbourhood of $18, so that TV picture does not have ghosts on right side in it while // horizontal position is about OK; // 3. Then tune h_blank_o for centered output on scope (look at front porch and back porch); // 4. Now shift the TV output using Hsync_offset for centered output while looking at TV (in method 'SetBT_Hphase' above); // 5. If no vertical shivering occurs when image is centered, you're done. Else: // 6. Modify the RIVA (BeScreen) h_sync_start setting somewhat to get stable centered picture possible on TV AND!: // 7. Make sure you update the Chrontel horizontal Phase setting also then! buffer[7] = 0xf7; //scope: tuned. lsb h_blank_o: h_blank_o = horizontal viewport location on TV: //(guideline for initial setting: (h_blank_o / h_clk_0) * 63.55556uS = 9.5uS for NTSC) if (si->ps.tv_encoder.type >= CX25870)//set CX value buffer[8] = 0x1d; else //set BT value buffer[8] = 0x1c;//try-out; scope: checked against other modes, looks OK. v_blank_o: 1e active line ('pixel') buffer[9] = 0xf2; //v_active_o: = (active output lines + 2) / field (on TV) buffer[10] = 0x26; //lsn = msn h_clk_o; //b4-5 = msbits h_active; //b7 = b8 v_avtive_o. buffer[11] = 0x00; //h_fract is always 0. buffer[12] = 0x10; //lsb h_clk_i: h_clk_i is horizontal total = 784. buffer[13] = 0x14; //try-out; lsb h_blank_i: #clks between start sync and new line 1st pixel; copy to VGA delta-sync! buffer[14] = 0x03; //b2-0 = msn h_clk_i; //try-out: b3 = msn h_blank_i; //b4 = vblankdly is always 0. buffer[15] = 0x0d; //lsb v_lines_i: v_lines_i = 525 buffer[16] = 0x18; //try-out; v_blank_i: #input lines between start sync and new line (pixel); copy to VGA delta-sync! //Make sure though that this value for the BT is *even*, or image will shiver a *lot* horizontally on TV. buffer[17] = 0xe0; //lsb v_active_i: v_active_i = 480 buffer[18] = 0x36; //b1-0 = msn v_lines_i; //b3-2 = msn v_active_i; //b5-4 = ylpf = 3; //b7-6 = clpf = 0. buffer[19] = 0x00; //lsb v_scale: v_scale = off = $1000 buffer[20] = 0x10; //b5-0 = msn v_scale; //scope: tuned. b7-6 = msn h_blank_o. //(((PLL_INT + (PLL_FRACT/65536)) / 6) * 13500000) = PIXEL_CLK = (hor.tot. * v_lines_i * 60Hz) buffer[21] = 0xdb; //lsb PLL fract: PLL fract = 0xf9db buffer[22] = 0xf9; //msb PLL fract buffer[23] = 0x8a; //b5-0 = PLL int: PLL int = 0x0a; //b6 = by_pll: by_pll = 0; //b7 = EN_XCLK: chip-pin CLKI is pixel clock. buffer[24] = 0x0a; //b0 = ni_out is always 0; //b1 = setup = 1 for NTSC; //b2 = 625line = 0 for NTSC; //b3 = vsync_dur = 1 for NTSC; //b4 = dic_screset is always 0; //b5 = pal_md = 0 for NTSC; //b6 = eclip is always 0; //b7 = reserved (en_scart) is always 0. buffer[25] = 0xe5; //sync_amp $e5 for NTSC buffer[26] = 0x75; //bst_amp $74-$76 for NTSC buffer[27] = 0x78; //mcr: r-y $77-$79 for NTSC buffer[28] = 0x44; //mcb: b-y $43-$44 for NTSC buffer[29] = 0x85; //my: y $85 for NTSC buffer[30] = 0x37; //lsb msc: msc b31-0: NTSC formula: ((3579545 / pixelclk) * 2^32) = MSC buffer[31] = 0x12; //msc = $251b1237 buffer[32] = 0x1b; buffer[33] = 0x25; //msb msc. buffer[34] = 0x00; //phase_off always $00 /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting mode NTSC640 OS\n")); return stat; }//end BT_init_NTSC640_OS. static uint8 BT_testsignal(void) { uint8 stat; uint8 buffer[3]; LOG(4,("Brooktree: Enabling testsignal\n")); buffer[0] = si->ps.tv_encoder.adress + WR; /* select bt register for enabling colorbars and outputs */ buffer[1] = 0xc4; /* issue the actual command */ buffer[2] = 0x05; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting up flickerfilter and outputs\n")); return stat; }//end BT_testsignal. static uint8 BT_setup_output(uint8 monstat, uint8 output, uint8 ffilter) { uint8 stat; uint8 buffer[7]; buffer[0] = si->ps.tv_encoder.adress + WR; /* select first TV config register to write */ buffer[1] = 0xc6; /* input is 24bit mpx'd RGB, BLANK = out, sync = act. hi */ buffer[2] = 0x98; /* disable all filters, exept flicker filter */ buffer[3] = 0x98; if (!ffilter) { /* disable flicker filter */ buffer[3] = 0xc0; LOG(4,("Brooktree: Disabling flickerfilter\n")); } else LOG(4,("Brooktree: Enabling flickerfilter\n")); /* (disable filters) */ buffer[4] = 0xc0; /* (disable filters) */ buffer[5] = 0xc0; switch (output) /* Description of ELSA Erazor III hardware layout: * (This is the default (recommended) layout by NVIDIA) * DAC A = CVBS * DAC B = C (chrominance) * DAC C = Y (luminance) */ /* Description of Diamond VIPER550: * DAC A = Not connected * DAC B = C (chrominance) * DAC C = Y (luminance) * To be able to connect to CVBS TV's a special cable is supplied: * This cable connects the Y (DAC C) output to the TV CVBS input. */ { case 1: LOG(4,("Brooktree: Forcing both Y/C and CVBS signals where supported by hardware\n")); buffer[6] = 0x18; // Y/C and CVBS out if all ports implemented // in hardware, else only Y/C or CVBS out. break; case 2: LOG(4,("Brooktree: Forcing CVBS signals on all outputs\n")); buffer[6] = 0x00; // put CVBS on all outputs. Used for cards break; // with only Y/C out and 'translation cable'. default: LOG(4,("Brooktree: Outputting signals according to autodetect status:\n")); switch (monstat) // only 'autodetect' remains... { case 1: LOG(4,("Brooktree: Only Y connected, outputting CVBS on all outputs\n")); buffer[6] = 0x00; //only Y connected: must be CVBS! break; case 2: LOG(4,("Brooktree: Only C connected, outputting CVBS on all outputs\n")); buffer[6] = 0x00; //only C connected: must be CVBS! break; //(though cable is wired wrong...) case 5: LOG(4,("Brooktree: CVBS and only Y connected, outputting CVBS on all outputs\n")); buffer[6] = 0x00; //CVBS and only Y connected: 2x CVBS! break; //(officially not supported...) case 6: LOG(4,("Brooktree: CVBS and only C connected, outputting CVBS on all outputs\n")); buffer[6] = 0x00; //CVBS and only C connected: 2x CVBS! break; //(officially not supported...) default: LOG(4,("Brooktree: Outputting both Y/C and CVBS where supported by hardware\n")); buffer[6] = 0x18; //nothing, or //Y/C only, or //CVBS only (but on CVBS output), or //Y/C and CVBS connected: //So activate recommended signals. } } /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting up flickerfilter and outputs\n")); return stat; }//end BT_setup_output. static uint8 BT_setup_hphase(uint8 mode) { uint8 stat, hoffset; uint8 buffer[7]; LOG(4,("Brooktree: Tuning horizontal phase\n")); /* CX needs timing reset (advised on BT also), first 1mS delay needed! */ snooze(1000); /* values below are all tested on TNT1, TNT2 and GeForce2MX */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select first TV output timing register to write */ buffer[1] = 0x6c; /* turn on active video & generate timing reset on CX chips! */ buffer[2] = 0x86; /* (set fail save values...) */ buffer[3] = 0x00; //set default horizontal sync offset buffer[4] = 0x02; //set default horizontal sync width buffer[5] = 0x00; //set default vertical sync offset /* do specific timing setup for all chips and modes: */ switch (si->ps.card_type) { case NV05: case NV05M64: case NV15: /* confirmed TNT2, TNT2M64, GeForce2Ti. * (8 pixels delayed hpos, so picture more to the right) */ hoffset = 8; break; default: /* confirmed TNT1, GeForce256, GeForce2MX. * (std hpos) * NOTE: It might be that GeForce needs TNT2 offset: * for now CX chips get seperate extra offset, until sure. * (CX is only found AFAIK on GeForce cards, no BT tested * on GeForce yet. CH was tested on GeForce and seemed to * indicate TNT1 offset was needed.) */ hoffset = 0; break; } switch (mode) { case NTSC640_TST: case NTSC640: if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... /* confirmed on TNT1 with BT869 using 4:3 TV and 16:9 TV */ buffer[3] = (0x1e + hoffset); //set horizontal sync offset break; case NTSC800: if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... buffer[3] = (0xe1 + hoffset); //set horizontal sync offset buffer[4] = 0xc2; //Vsync offset reg. does not exist on CX: mode is checked and OK. buffer[5] = 0x40; //set VSync offset (on BT's only) break; case PAL640: if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... buffer[3] = (0xa8 + hoffset); break; case PAL800_TST: case PAL800: if (si->ps.tv_encoder.type >= CX25870) hoffset +=8; //if CX shift picture right some more... buffer[3] = (0x2c + hoffset); break; case NTSC720: if (si->ps.tv_encoder.type >= CX25870) buffer[3] = (0xb2 + hoffset); //set horizontal sync offset CX else buffer[3] = (0xd0 + hoffset); //set horizontal sync offset BT buffer[4] = 0xff; //hsync width = max: break; //to prevent vertical image 'shivering'. case PAL720: buffer[3] = (0xd4 + hoffset); buffer[4] = 0xff; break; case NTSC640_OS: buffer[3] = (0xc8 + hoffset); buffer[4] = 0xff; break; case PAL800_OS: if (si->ps.tv_encoder.type >= CX25870) buffer[3] = (0x78 + hoffset); //set horizontal sync offset CX else buffer[3] = (0xc4 + hoffset); //set horizontal sync offset BT buffer[4] = 0xff; break; default: //nothing to be done here... break; } buffer[6] = 0x01; //set default vertical sync width /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) LOG(4,("Brooktree: I2C errors occurred while setting up h_phase\n")); return stat; }//end BT_setup_hphase. static uint8 BT_read_monstat(uint8* monstat) { uint8 stat; uint8 buffer[3]; /* make sure we have the recommended failsafe selected */ *monstat = 0; LOG(4,("Brooktree: Autodetecting connected output devices\n")); /* set BT to return connection status in ESTATUS on next read CMD: */ buffer[0] = si->ps.tv_encoder.adress + WR; /* set ESTATUS at b'01' (return conn.stat.) */ buffer[1] = 0xc4; /* and leave chip outputs on. */ buffer[2] = 0x41; /* reset status */ i2c_flag_error (-1); i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while reading connection status (1)\n")); return stat; } /* do actual read connection status: */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select register with CHECK_STAT CMD */ buffer[1] = 0xba; /* issue actual command. */ buffer[2] = 0x40; i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while reading connection status (2)\n")); return stat; } /* CX: Wait 600uS for signals to stabilize (see datasheet) */ /* warning, note: * datasheet is in error! 60mS needed!! */ snooze(60000); /* read back updated connection status: */ buffer[0] = si->ps.tv_encoder.adress + RD; /* transmit 1 byte */ i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 1); /* receive 1 byte */ /* ACK level to TX after last byte to RX should be 1 (= NACK) (see I2C spec) * While the BT's don't care, CX chips will block the SDA line if an ACK gets sent! */ buffer[0] = 1; i2c_readbuffer(si->ps.tv_encoder.bus, buffer, 1); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while reading connection status (3)\n")); return stat; } *monstat = ((buffer[0] & 0xe0) >> 5); LOG(4,("Brooktree: TV output monitor status = %d\n", *monstat)); /* instruct BT to go back to normal operation: */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select register with CHECK_STAT CMD */ buffer[1] = 0xba; /* issue actual command. */ buffer[2] = 0x00; i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while reading connection status (4)\n")); return stat; } return stat; }//end BT_read_monstat. static uint8 BT_killclk_blackout(void) { uint8 stat; uint8 buffer[4]; LOG(4,("Brooktree: Killing clock and/or blacking out (blocking output signals)\n")); /* reset status */ i2c_flag_error (-1); if (si->ps.tv_encoder.type <= BT869) //BT... { /* Only disable external pixelclock input on BT's. * CX chips will lock the bus if you do this. * (It looks like the external pixelclock is always OK as long as a valid * mode is programmed for the TVout chip. This means that disabling the use * of this clock is not needed anyway. * If you do disable this input, this pixelclock will rise to about 60Mhz BTW..) */ /* disable use of external pixelclock source... */ /* (should prevent BT for being 'overclocked' by RIVA in VGA-only mode...) */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select BT register for setting EN_XCLK */ buffer[1] = 0xa0; /* clear it */ buffer[2] = 0x00; i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 3); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while doing killclk_blackout (1-BT)\n")); return stat; } } else //CX... { /* Disable CX video out (or wild output will be seen on TV..) */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select register in CX */ buffer[1] = 0x6c; /* disable active video out. */ buffer[2] = 0x02; i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 3); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while doing killclk_blackout (1-CX)\n")); return stat; } } /* black-out TVout while outputs are enabled... */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select first TV config register to write */ buffer[1] = 0xc4; /* disable testimage while outputs remain enabled */ buffer[2] = 0x01; /* input is 24bit mpx'd RGB, BLANK = in, sync = act. hi */ buffer[3] = 0x18; i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, sizeof(buffer)); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while doing killclk_blackout (2)\n")); return stat; } return stat; }//end BT_killclk_blackout. uint8 BT_dpms(bool display) { uint8 stat; uint8 buffer[3]; LOG(4,("Brooktree: setting DPMS: ")); /* reset status */ i2c_flag_error (-1); /* shutdown all analog electronics... */ buffer[0] = si->ps.tv_encoder.adress + WR; /* select first TV config register to write */ buffer[1] = 0xba; if (display) { /* enable all DACs */ buffer[2] = 0x00; LOG(4,("display on\n")); } else { /* shutdown all DACs */ buffer[2] = 0x10; LOG(4,("display off\n")); } i2c_bstart(si->ps.tv_encoder.bus); i2c_writebuffer(si->ps.tv_encoder.bus, buffer, 3); i2c_bstop(si->ps.tv_encoder.bus); /* log on errors */ stat = i2c_flag_error(0); if (stat) { LOG(4,("Brooktree: I2C errors occurred while setting DPMS\n")); return stat; } return stat; }//end BT_dpms. uint8 BT_check_tvmode(display_mode target) { uint8 status = NOT_SUPPORTED; uint32 mode = ((target.timing.h_display) | ((target.timing.v_display) << 16)); switch (mode) { case (640 | (480 << 16)): if (((target.flags & TV_BITS) == TV_PAL) && (!(target.flags & TV_VIDEO))) status = PAL640; if ((target.flags & TV_BITS) == TV_NTSC) { if (!(target.flags & TV_VIDEO)) status = NTSC640; else status = NTSC640_OS; } break; case (768 | (576 << 16)): if (((target.flags & TV_BITS) == TV_PAL) && (target.flags & TV_VIDEO)) status = PAL800_OS; break; case (800 | (600 << 16)): if (((target.flags & TV_BITS) == TV_PAL) && (!(target.flags & TV_VIDEO))) status = PAL800; if (((target.flags & TV_BITS) == TV_NTSC) && (!(target.flags & TV_VIDEO))) status = NTSC800; break; case (720 | (480 << 16)): if (((target.flags & TV_BITS) == TV_NTSC) && (target.flags & TV_VIDEO)) status = NTSC720; break; case (720 | (576 << 16)): if (((target.flags & TV_BITS) == TV_PAL) && (target.flags & TV_VIDEO)) status = PAL720; break; } return status; }//end BT_check_tvmode. /* //BeTVOut's SwitchRIVAtoTV(vtot) timing formula: (note: vtot = (v_total - 2)) //----------------------------------------------------------------------------------- //HORIZONTAL: //----------- h_sync_start = h_display; //fixme, note, checkout: //feels like in fact TNT2-M64 nv_crtc.c registerprogramming should be adapted... if (TNT2-M64) { h_sync_end = h_display + 8; h_total = h_display + 56; } else //TNT1, TNT2, Geforce2... (so default) { h_sync_end = h_display + 16; h_total = h_display + 48; } //fixme, note, checkout: //BeTVOut uses two 'tweaks': // - on TNT2-M64 only: // register h_blank_e is increased with 1 (so should be done in nv_crtc.c here) // - 'all cards': // register h_blank_e b6 = 0 (only influences TNT2-M64 in modes NTSC800 and PAL800). //----------------------------------------------------------------------------------- //VERTICAL: //--------- v_sync_start = v_display; v_total = vtot + 2; v_sync_end = v_total - 1; //(This takes care of the 'cursor trash' on TNT1's...) //----------------------------------------------------------------------------------- */ static status_t BT_update_mode_for_gpu(display_mode *target, uint8 tvmode) { //fixme if needed: //pixelclock is not actually pgm'd because PLL is pgm' earlier during setmode... switch (tvmode) { case NTSC640: case NTSC640_TST: target->timing.h_display = 640; target->timing.h_sync_start = 640; if (si->ps.card_type == NV05M64) { target->timing.h_sync_end = 648; target->timing.h_total = 696; } else { //fixme if possible: //see if tweaking h_sync_end can shift picture 8 pixels right to fix //ws tv's tuning fault (always going for max. compatibility :) target->timing.h_sync_end = 656; target->timing.h_total = 688; } target->timing.v_display = 480; target->timing.v_sync_start = 480; target->timing.v_sync_end = 555; //This prevents 'cursor trash' on TNT1's target->timing.v_total = 556; //Above 525 because mode scales down if (si->ps.card_type == NV05M64) target->timing.pixel_clock = ((696 * 556 * 60) / 1000); else target->timing.pixel_clock = ((688 * 556 * 60) / 1000); break; case NTSC800: target->timing.h_display = 800; target->timing.h_sync_start = 800; if (si->ps.card_type == NV05M64) { target->timing.h_sync_end = 808; target->timing.h_total = 856; } else { target->timing.h_sync_end = 816; target->timing.h_total = 848; } target->timing.v_display = 600; target->timing.v_sync_start = 600; target->timing.v_sync_end = 685; //This prevents 'cursor trash' on TNT1's target->timing.v_total = 686; //Above 525 because mode scales down if (si->ps.card_type == NV05M64) target->timing.pixel_clock = ((856 * 686 * 60) / 1000); else target->timing.pixel_clock = ((848 * 686 * 60) / 1000); break; case PAL640: target->timing.h_display = 640; target->timing.h_sync_start = 640; if (si->ps.card_type == NV05M64) { target->timing.h_sync_end = 648; target->timing.h_total = 696; } else { target->timing.h_sync_end = 656; target->timing.h_total = 688; } target->timing.v_display = 480; target->timing.v_sync_start = 480; target->timing.v_sync_end = 570; //This prevents 'cursor trash' on TNT1's target->timing.v_total = 571; //Below 625 because mode scales up if (si->ps.card_type == NV05M64) target->timing.pixel_clock = ((696 * 571 * 50) / 1000); else target->timing.pixel_clock = ((688 * 571 * 50) / 1000); break; case PAL800: case PAL800_TST: target->timing.h_display = 800; target->timing.h_sync_start = 800; if (si->ps.card_type == NV05M64) { target->timing.h_sync_end = 808; target->timing.h_total = 856; } else { target->timing.h_sync_end = 816; target->timing.h_total = 848; } target->timing.v_display = 600; target->timing.v_sync_start = 600; target->timing.v_sync_end = 695; //This prevents 'cursor trash' on TNT1's target->timing.v_total = 696; //Above 625 because mode scales down if (si->ps.card_type == NV05M64) target->timing.pixel_clock = ((856 * 696 * 50) / 1000); else target->timing.pixel_clock = ((848 * 696 * 50) / 1000); break; case NTSC640_OS: target->timing.h_display = 640; //BT H_ACTIVE target->timing.h_sync_start = 744; //set for CH/BT compatible TV output target->timing.h_sync_end = 744+20; //delta is BT H_BLANKI target->timing.h_total = 784; //BT H_CLKI target->timing.v_display = 480; //BT V_ACTIVEI target->timing.v_sync_start = 490; //set for centered sync pulse target->timing.v_sync_end = 490+25; //delta is BT V_BLANKI target->timing.v_total = 525; //BT V_LINESI (== 525: 1:1 scaled mode) target->timing.pixel_clock = ((784 * 525 * 60) / 1000); //refresh break; case PAL800_OS: target->timing.h_display = 768; //H_ACTIVE if (si->ps.tv_encoder.type <= BT869) { /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ target->timing.h_sync_start = 856; //set for centered TV output target->timing.h_sync_end = 856+20; //delta is BT H_BLANKI } else { /* confirmed on NV11 using 4:3 TV and 16:9 TV */ target->timing.h_sync_start = 848; //set for centered TV output target->timing.h_sync_end = 848+20; //delta is BT H_BLANKI } target->timing.h_total = 944; //BT H_CLKI target->timing.v_display = 576; //V_ACTIVEI target->timing.v_sync_start = 579; //set for centered sync pulse target->timing.v_sync_end = 579+42; //delta is BT V_BLANKI target->timing.v_total = 625; //BT V_LINESI (== 625: 1:1 scaled mode) target->timing.pixel_clock = ((944 * 625 * 50) / 1000); //refresh break; case NTSC720: /* (tested on TNT2 with BT869) */ target->timing.h_display = 720; //H_ACTIVE if (si->ps.tv_encoder.type <= BT869) { /* confirmed on TNT1 using 4:3 TV and 16:9 TV */ target->timing.h_sync_start = 744; //do not change! target->timing.h_sync_end = 744+144; //delta is H_sync_pulse } else { /* confirmed on NV11 using 4:3 TV and 16:9 TV */ target->timing.h_sync_start = 728; //do not change! target->timing.h_sync_end = 728+160; //delta is H_sync_pulse } target->timing.h_total = 888; //BT H_TOTAL target->timing.v_display = 480; //V_ACTIVEI target->timing.v_sync_start = 490; //set for centered sync pulse target->timing.v_sync_end = 490+26; //delta is V_sync_pulse target->timing.v_total = 525; //CH V_TOTAL (== 525: 1:1 scaled mode) target->timing.pixel_clock = ((888 * 525 * 60) / 1000); //refresh break; case PAL720: target->timing.h_display = 720; //BT H_ACTIVE target->timing.h_sync_start = 744; //set for centered sync pulse target->timing.h_sync_end = 744+140; //delta is BT H_BLANKI target->timing.h_total = 888; //BT H_CLKI target->timing.v_display = 576; //BT V_ACTIVEI target->timing.v_sync_start = 579; //set for centered sync pulse target->timing.v_sync_end = 579+42; //delta is BT V_BLANKI target->timing.v_total = 625; //BT V_LINESI (== 625: 1:1 scaled mode) target->timing.pixel_clock = ((888 * 625 * 50) / 1000); //refresh break; default: return B_ERROR; } return B_OK; }//end BT_update_mode_for_gpu. /* note: * tested on ELSA Erazor III 32Mb AGP (TNT2/BT869), * Diamond Viper V550 16Mb PCI (TNT1/BT869), * and ASUS V7100 GeForce2 MX200 AGP/32Mb (CH7007). */ static status_t BT_start_tvout(display_mode tv_target) { /* TV_PRIMARY tells us that the head to be used with TVout is the head that's * actually assigned as being the primary head at powerup: * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ if (tv_target.flags & TV_PRIMARY) { if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) head1_start_tvout(); else head2_start_tvout(); } else { if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) head2_start_tvout(); else head1_start_tvout(); } return B_OK; }//end BT_start_tvout. /* note: * tested on ELSA Erazor III 32Mb AGP (TNT2/BT869), * Diamond Viper V550 16Mb PCI (TNT1/BT869), * and ASUS V7100 GeForce2 MX200 AGP/32Mb (CH7007). */ status_t BT_stop_tvout(void) { /* prevent BT from being overclocked by VGA-only modes & black-out TV-out */ BT_killclk_blackout(); /* TV_PRIMARY tells us that the head to be used with TVout is the head that's * actually assigned as being the primary head at powerup: * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ if (si->dm.flags & TV_PRIMARY) { if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) head1_stop_tvout(); else head2_stop_tvout(); } else { if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) head2_stop_tvout(); else head1_stop_tvout(); } /* fixme if needed: * a full encoder chip reset could be done here (so after decoupling crtc)... */ /* (but: beware of the 'locked SDA' syndrome then!) */ /* fixme if needed: we _could_ setup a TVout mode and apply the testsignal here... */ if (0) { //set mode (selecting PAL/NTSC according to board wiring for example) etc, then: BT_testsignal(); } return B_OK; }//end BT_stop_tvout. status_t BT_setmode(display_mode target) { uint8 tvmode, monstat; /* enable flickerfilter in desktop modes, disable it in video modes. */ uint8 ffilter = 0; /* use a display_mode copy because we might tune it for TVout compatibility */ display_mode tv_target = target; /* preset new TVout mode */ tvmode = BT_check_tvmode(tv_target); if (!tvmode) return B_ERROR; /* read current output devices connection status */ BT_read_monstat(&monstat); /* (pre)set TV mode */ /* note: * Manual config is non-dependent of the state of the PAL hardware input pin; * Also SDA lockups occur when setting EN_XCLK after autoconfig! * Make sure PAL_MD=0 for NTSC and PAL_MD = 1 for PAL... */ switch (tvmode) { case NTSC640: case NTSC640_TST: ffilter = 1; BT_init_NTSC640(); break; case NTSC800: ffilter = 1; BT_init_NTSC800(); break; case PAL640: ffilter = 1; BT_init_PAL640(); break; case PAL800: case PAL800_TST: ffilter = 1; BT_init_PAL800(); break; case NTSC640_OS: BT_init_NTSC640_OS(); break; case PAL800_OS: BT_init_PAL800_OS(); break; case NTSC720: BT_init_NTSC720(); break; case PAL720: BT_init_PAL720(); break; } /* modify BT Hphase signal to center TV image... */ BT_setup_hphase(tvmode); /* disable Macro mode */ switch (tvmode) { case NTSC640: case NTSC640_TST: case NTSC800: case NTSC640_OS: case NTSC720: /* NTSC */ BT_set_macro (0, 0); break; default: /* PAL */ BT_set_macro (1, 0); break; } /* setup output signal routing and flickerfilter */ BT_setup_output(monstat, (uint8)(si->settings.tv_output), ffilter); /* update the GPU CRTC timing for the requested mode */ BT_update_mode_for_gpu(&tv_target, tvmode); /* setup GPU CRTC timing */ /* TV_PRIMARY tells us that the head to be used with TVout is the head that's * actually assigned as being the primary head at powerup: * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ if (tv_target.flags & TV_PRIMARY) { if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) head1_set_timing(tv_target); else head2_set_timing(tv_target); } else { if ((tv_target.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) head2_set_timing(tv_target); else head1_set_timing(tv_target); } /* now set GPU CRTC to slave mode */ BT_start_tvout(tv_target); return B_OK; }