/* Haiku S3 Savage driver adapted from the X.org Savage driver. Copyright 1995-1997 The XFree86 Project, Inc. Copyright 2007-2008 Haiku, Inc. All rights reserved. Distributed under the terms of the MIT license. Authors: Gerald Zajac 2006-2008 */ #include "accel.h" #include "savage.h" #define BASE_FREQ 14.31818 struct SavageRegRec { uint8 CRTC[25]; // Crtc Controller reg's uint8 SR12, SR13, SR15, SR18, SR1B, SR29; uint8 CR31, CR33, CR34, CR3A, CR3B, CR3C; uint8 CR40, CR42, CR43, CR45; uint8 CR50, CR51, CR53, CR58, CR5D, CR5E; uint8 CR65, CR66, CR67, CR69; uint8 CR86, CR88; uint8 CR90, CR91, CRB0; }; static void Savage_SetGBD_Twister(const DisplayModeEx& mode) { SharedInfo& si = *gInfo.sharedInfo; int bci_enable; TRACE("Savage_SetGBD_Twister()\n"); if (si.chipType == S3_SAVAGE4) bci_enable = BCI_ENABLE; else bci_enable = BCI_ENABLE_TWISTER; // MM81C0 and 81C4 are used to control primary stream. WriteReg32(PRI_STREAM_FBUF_ADDR0, 0); WriteReg32(PRI_STREAM_FBUF_ADDR1, 0); // Program Primary Stream Stride Register. // // Tell engine if tiling on or off, set primary stream stride, and // if tiling, set tiling bits/pixel and primary stream tile offset. // Note that tile offset (bits 16 - 29) must be scanline width in // bytes/128bytespertile * 256 Qwords/tile. This is equivalent to // lDelta * 2. Remember that if tiling, lDelta is screenwidth in // bytes padded up to an even number of tilewidths. WriteReg32(PRI_STREAM_STRIDE, (((mode.bytesPerRow * 2) << 16) & 0x3FFFE000) | (mode.bytesPerRow & 0x00001fff)); // CR69, bit 7 = 1 // to use MM streams processor registers to control primary stream. WriteCrtcReg(0x69, 0x80, 0x80); WriteReg32(0x8128, 0xFFFFFFFFL); WriteReg32(0x812C, 0xFFFFFFFFL); WriteReg32(S3_GLB_BD_HIGH, bci_enable | S3_LITTLE_ENDIAN | S3_BD64); WriteCrtcReg(0x50, 0xc1, 0xc1); // CR50, bit 7,6,0 = 111, Use GBD // If MS1NB style linear tiling mode. // bit MM850C[15] = 0 select NB linear tile mode. // bit MM850C[15] = 1 select MS-1 128-bit non-linear tile mode. uint32 ulTmp = ReadReg32(ADVANCED_FUNC_CTRL) | 0x8000; // use MS-s style tile mode WriteReg32(ADVANCED_FUNC_CTRL, ulTmp); // Set up Tiled Surface Registers // Bit 25:20 - Surface width in tiles. // Bit 29 - Y Range Flag. // Bit 31:30 = 00, 4 bpp. // = 01, 8 bpp. // = 10, 16 bpp. // = 11, 32 bpp. // Global Bitmap Descriptor Register MM816C - twister/prosavage // bit 24~25: tile format // 00: linear // 01: destination tiling format // 10: texture tiling format // 11: reserved // bit 28: block write disble/enable // 0: disable // 1: enable // Global Bitmap Descriptor Register MM816C - savage4 // bit 24~25: tile format // 00: linear // 01: reserved // 10: 16 bpp tiles // 11: 32 bpp tiles // bit 28: block write disable/enable // 0: enable // 1: disable // Do not enable block_write even for non-tiling modes, because // the driver cannot determine if the memory type is the certain // type of SGRAM for which block_write can be used. si.GlobalBD.bd1.HighPart.ResBWTile = TILE_FORMAT_LINEAR; // linear si.GlobalBD.bd1.HighPart.ResBWTile |= 0x10; // disable block write // HW uses width. si.GlobalBD.bd1.HighPart.Stride = (uint16)(mode.timing.h_display); // number of pixels per line si.GlobalBD.bd1.HighPart.Bpp = (uint8)(mode.bpp); si.GlobalBD.bd1.Offset = si.frameBufferOffset; // CR88, bit 4 - Block write enabled/disabled. // // Note: Block write must be disabled when writing to tiled // memory. Even when writing to non-tiled memory, block // write should only be enabled for certain types of SGRAM. WriteCrtcReg(0x88, DISABLE_BLOCK_WRITE_2D, DISABLE_BLOCK_WRITE_2D); // CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0). // bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window // at A000:0. WriteCrtcReg(MEMORY_CONFIG_REG, 0x00, 0x01); // Program the GBD and SBD's. WriteReg32(S3_GLB_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_GLB_BD_HIGH, si.GlobalBD.bd2.HiPart | bci_enable | S3_LITTLE_ENDIAN | S3_BD64); WriteReg32(S3_PRI_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_PRI_BD_HIGH, si.GlobalBD.bd2.HiPart); } static void Savage_SetGBD_3D(const DisplayModeEx& mode) { TRACE("Savage_SetGBD_3D()\n"); SharedInfo& si = *gInfo.sharedInfo; int bci_enable = BCI_ENABLE; // MM81C0 and 81C4 are used to control primary stream. WriteReg32(PRI_STREAM_FBUF_ADDR0, 0); WriteReg32(PRI_STREAM_FBUF_ADDR1, 0); // Program Primary Stream Stride Register. // // Tell engine if tiling on or off, set primary stream stride, and // if tiling, set tiling bits/pixel and primary stream tile offset. // Note that tile offset (bits 16 - 29) must be scanline width in // bytes/128bytespertile * 256 Qwords/tile. This is equivalent to // lDelta * 2. Remember that if tiling, lDelta is screenwidth in // bytes padded up to an even number of tilewidths. WriteReg32(PRI_STREAM_STRIDE, (((mode.bytesPerRow * 2) << 16) & 0x3FFFE000) | (mode.bytesPerRow & 0x00001fff)); // CR69, bit 7 = 1 to use MM streams processor registers to control primary // stream. WriteCrtcReg(0x69, 0x80, 0x80); WriteReg32(0x8128, 0xFFFFFFFFL); WriteReg32(0x812C, 0xFFFFFFFFL); WriteReg32(S3_GLB_BD_HIGH, bci_enable | S3_LITTLE_ENDIAN | S3_BD64); WriteCrtcReg(0x50, 0xc1, 0xc1); // CR50, bit 7,6,0 = 111, Use GBD // If MS1NB style linear tiling mode. // bit MM850C[15] = 0 select NB linear tile mode. // bit MM850C[15] = 1 select MS-1 128-bit non-linear tile mode. uint32 ulTmp = ReadReg32(ADVANCED_FUNC_CTRL) | 0x8000; // use MS-s style tile mode WriteReg32(ADVANCED_FUNC_CTRL, ulTmp); // Tiled Surface 0 Registers MM48C40: // bit 0~23: tile surface 0 frame buffer offset // bit 24~29:tile surface 0 width // bit 30~31:tile surface 0 bits/pixel // 00: reserved // 01, 8 bits // 10, 16 Bits. // 11, 32 Bits. // Global Bitmap Descriptor Register MM816C // bit 24~25: tile format // 00: linear // 01: reserved // 10: 16 bpp tiles // 11: 32 bpp tiles // bit 28: block write disable/enable // 0: enable // 1: disable // Do not enable block_write even for non-tiling modes, because // the driver cannot determine if the memory type is the certain // type of SGRAM for which block_write can be used. si.GlobalBD.bd1.HighPart.ResBWTile = TILE_FORMAT_LINEAR; // linear si.GlobalBD.bd1.HighPart.ResBWTile |= 0x10; // disable block write // HW uses width. si.GlobalBD.bd1.HighPart.Stride = (uint16)(mode.timing.h_display); // number of pixels per line si.GlobalBD.bd1.HighPart.Bpp = (uint8)(mode.bpp); si.GlobalBD.bd1.Offset = si.frameBufferOffset; // CR88, bit 4 - Block write enabled/disabled. // // Note: Block write must be disabled when writing to tiled // memory. Even when writing to non-tiled memory, block // write should only be enabled for certain types of SGRAM. WriteCrtcReg(0x88, DISABLE_BLOCK_WRITE_2D, DISABLE_BLOCK_WRITE_2D); // CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0). // bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window // at A000:0. WriteCrtcReg(MEMORY_CONFIG_REG, 0x00, 0x01); // Program the GBD and SBD's. WriteReg32(S3_GLB_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_GLB_BD_HIGH, si.GlobalBD.bd2.HiPart | bci_enable | S3_LITTLE_ENDIAN | S3_BD64); WriteReg32(S3_PRI_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_PRI_BD_HIGH, si.GlobalBD.bd2.HiPart); } static void Savage_SetGBD_MX(const DisplayModeEx& mode) { TRACE("Savage_SetGBD_MX()\n"); SharedInfo& si = *gInfo.sharedInfo; int bci_enable = BCI_ENABLE; // CR67_3: // = 1 stream processor MMIO address and stride register // are used to control the primary stream. // = 0 standard VGA address and stride registers // are used to control the primary streams. WriteCrtcReg(0x67, 0x08, 0x08); // IGA 2. WriteSeqReg(0x26, 0x4f); // select IGA 2 read/writes WriteCrtcReg(0x67, 0x08, 0x08); WriteSeqReg(0x26, 0x40); // select IGA 1 // Set primary stream to bank 0 via reg CRCA. WriteCrtcReg(MEMORY_CTRL0_REG, 0x00, MEM_PS1 + MEM_PS2); // MM81C0 and 81C4 are used to control primary stream. WriteReg32(PRI_STREAM_FBUF_ADDR0, si.frameBufferOffset & 0x7fffff); WriteReg32(PRI_STREAM_FBUF_ADDR1, si.frameBufferOffset & 0x7fffff); WriteReg32(PRI_STREAM2_FBUF_ADDR0, si.frameBufferOffset & 0x7fffff); WriteReg32(PRI_STREAM2_FBUF_ADDR1, si.frameBufferOffset & 0x7fffff); // Program Primary Stream Stride Register. // // Tell engine if tiling on or off, set primary stream stride, and // if tiling, set tiling bits/pixel and primary stream tile offset. // Note that tile offset (bits 16 - 29) must be scanline width in // bytes/128bytespertile * 256 Qwords/tile. This is equivalent to // lDelta * 2. Remember that if tiling, lDelta is screenwidth in // bytes padded up to an even number of tilewidths. WriteReg32(PRI_STREAM_STRIDE, (((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) | (mode.bytesPerRow & 0x00003fff)); WriteReg32(PRI_STREAM2_STRIDE, (((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) | (mode.bytesPerRow & 0x00003fff)); WriteReg32(0x8128, 0xFFFFFFFFL); WriteReg32(0x812C, 0xFFFFFFFFL); WriteReg32(S3_GLB_BD_HIGH, bci_enable | S3_LITTLE_ENDIAN | S3_BD64); WriteCrtcReg(0x50, 0xc1, 0xc1); // CR50, bit 7,6,0 = 111, Use GBD // CR78, bit 3 - Block write enabled(1)/disabled(0). // bit 2 - Block write cycle time(0:2 cycles,1: 1 cycle) // Note: Block write must be disabled when writing to tiled // memory. Even when writing to non-tiled memory, block // write should only be enabled for certain types of SGRAM. WriteCrtcReg(0x78, 0xfb, 0xfb); // Tiled Surface 0 Registers MM48C40: // bit 0~23: tile surface 0 frame buffer offset // bit 24~29:tile surface 0 width // bit 30~31:tile surface 0 bits/pixel // 00: reserved // 01, 8 bits // 10, 16 Bits. // 11, 32 Bits. // Global Bitmap Descriptor Register MM816C // bit 24~25: tile format // 00: linear // 01: reserved // 10: 16 bit // 11: 32 bit // bit 28: block write disble/enable // 0: enable // 1: disable // Do not enable block_write even for non-tiling modes, because // the driver cannot determine if the memory type is the certain // type of SGRAM for which block_write can be used. si.GlobalBD.bd1.HighPart.ResBWTile = TILE_FORMAT_LINEAR; // linear si.GlobalBD.bd1.HighPart.ResBWTile |= 0x10; // disable block write // HW uses width. si.GlobalBD.bd1.HighPart.Stride = (uint16)(mode.timing.h_display); // number of pixels per line si.GlobalBD.bd1.HighPart.Bpp = (uint8)(mode.bpp); si.GlobalBD.bd1.Offset = si.frameBufferOffset; // CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0). // bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window // at A000:0. WriteCrtcReg(MEMORY_CONFIG_REG, 0x04, 0x05); // CR31 // Program the GBD and SBD's. WriteReg32(S3_GLB_BD_LOW, si.GlobalBD.bd2.LoPart ); // 8: bci enable. WriteReg32(S3_GLB_BD_HIGH, (si.GlobalBD.bd2.HiPart | bci_enable | S3_LITTLE_ENDIAN | S3_BD64)); WriteReg32(S3_PRI_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_PRI_BD_HIGH, si.GlobalBD.bd2.HiPart); WriteReg32(S3_SEC_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_SEC_BD_HIGH, si.GlobalBD.bd2.HiPart); } static void Savage_SetGBD_Super(const DisplayModeEx& mode) { TRACE("Savage_SetGBD_Super()\n"); SharedInfo& si = *gInfo.sharedInfo; int bci_enable = BCI_ENABLE_TWISTER; // CR67_3: // = 1 stream processor MMIO address and stride register // are used to control the primary stream. // = 0 standard VGA address and stride registers // are used to control the primary streams. WriteCrtcReg(0x67, 0x08, 0x08); // IGA 2. WriteSeqReg(0x26, 0x4f); // select IGA 2 read/writes WriteCrtcReg(0x67, 0x08, 0x08); WriteSeqReg(0x26, 0x40); // select IGA 1 // Load ps1 active registers as determined by MM81C0/81C4. // Load ps2 active registers as determined by MM81B0/81B4. WriteCrtcReg(0x65, 0x03, 0x03); // Program Primary Stream Stride Register. // // Tell engine if tiling on or off, set primary stream stride, and // if tiling, set tiling bits/pixel and primary stream tile offset. // Note that tile offset (bits 16 - 29) must be scanline width in // bytes/128bytespertile * 256 Qwords/tile. This is equivalent to // lDelta * 2. Remember that if tiling, lDelta is screenwidth in // bytes padded up to an even number of tilewidths. WriteReg32(PRI_STREAM_STRIDE, (((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) | (mode.bytesPerRow & 0x00001fff)); WriteReg32(PRI_STREAM2_STRIDE, (((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) | (mode.bytesPerRow & 0x00001fff)); // MM81C0 and 81C4 are used to control primary stream. WriteReg32(PRI_STREAM_FBUF_ADDR0, si.frameBufferOffset); WriteReg32(PRI_STREAM_FBUF_ADDR1, 0x80000000); WriteReg32(PRI_STREAM2_FBUF_ADDR0, (si.frameBufferOffset & 0xfffffffc) | 0x80000000); WriteReg32(PRI_STREAM2_FBUF_ADDR1, si.frameBufferOffset & 0xfffffffc); WriteReg32(0x8128, 0xFFFFFFFFL); WriteReg32(0x812C, 0xFFFFFFFFL); // Bit 28:block write disable. WriteReg32(S3_GLB_BD_HIGH, bci_enable | S3_BD64 | 0x10000000); WriteCrtcReg(0x50, 0xc1, 0xc1); // CR50, bit 7,6,0 = 111, Use GBD // Do not enable block_write even for non-tiling modes, because // the driver cannot determine if the memory type is the certain // type of SGRAM for which block_write can be used. si.GlobalBD.bd1.HighPart.ResBWTile = TILE_FORMAT_LINEAR; // linear si.GlobalBD.bd1.HighPart.ResBWTile |= 0x10; // disable block write // HW uses width. si.GlobalBD.bd1.HighPart.Stride = (uint16)(mode.timing.h_display); // number of pixels per line si.GlobalBD.bd1.HighPart.Bpp = (uint8)(mode.bpp); si.GlobalBD.bd1.Offset = si.frameBufferOffset; // CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0). // bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window // at A000:0. WriteCrtcReg(MEMORY_CONFIG_REG, 0x00, 0x01); // Program the GBD and SBDs. WriteReg32(S3_GLB_BD_LOW, si.GlobalBD.bd2.LoPart ); WriteReg32(S3_GLB_BD_HIGH, (si.GlobalBD.bd2.HiPart | bci_enable | S3_LITTLE_ENDIAN | S3_BD64)); WriteReg32(S3_PRI_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_PRI_BD_HIGH, si.GlobalBD.bd2.HiPart); WriteReg32(S3_SEC_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_SEC_BD_HIGH, si.GlobalBD.bd2.HiPart); } static void Savage_SetGBD_2000(const DisplayModeEx& mode) { TRACE("Savage_SetGBD_2000()\n"); SharedInfo& si = *gInfo.sharedInfo; int bci_enable = BCI_ENABLE_TWISTER; // MM81C0 and 81B0 are used to control primary stream. WriteReg32(PRI_STREAM_FBUF_ADDR0, si.frameBufferOffset); WriteReg32(PRI_STREAM2_FBUF_ADDR0, si.frameBufferOffset); // Program Primary Stream Stride Register. // // Tell engine if tiling on or off, set primary stream stride, and // if tiling, set tiling bits/pixel and primary stream tile offset. // Note that tile offset (bits 16 - 29) must be scanline width in // bytes/128bytespertile * 256 Qwords/tile. This is equivalent to // lDelta * 2. Remember that if tiling, lDelta is screenwidth in // bytes padded up to an even number of tilewidths. WriteReg32(PRI_STREAM_STRIDE, ((mode.bytesPerRow << 4) & 0x7ff0)); WriteReg32(PRI_STREAM2_STRIDE, ((mode.bytesPerRow << 4) & 0x7ff0)); // CR67_3: // = 1 stream processor MMIO address and stride register // are used to control the primary stream. // = 0 standard VGA address and stride registers // are used to control the primary streams WriteCrtcReg(0x67, 0x08, 0x08); WriteReg32(0x8128, 0xFFFFFFFFL); WriteReg32(0x812C, 0xFFFFFFFFL); // Bit 28:block write disable. WriteReg32(S3_GLB_BD_HIGH, bci_enable | S3_BD64 | 0x10000000); WriteCrtcReg(0x50, 0xc1, 0xc1); // CR50, bit 7,6,0 = 111, Use GBD WriteCrtcReg(0x73, 0x00, 0x20); // CR73 bit 5 = 0 block write disable // Do not enable block_write even for non-tiling modes, because // the driver cannot determine if the memory type is the certain // type of SGRAM for which block_write can be used. si.GlobalBD.bd1.HighPart.ResBWTile = TILE_FORMAT_LINEAR; // linear si.GlobalBD.bd1.HighPart.ResBWTile |= 0x10; // disable block write // HW uses width. si.GlobalBD.bd1.HighPart.Stride = (uint16)(mode.timing.h_display); // number of pixels per line si.GlobalBD.bd1.HighPart.Bpp = (uint8)(mode.bpp); si.GlobalBD.bd1.Offset = si.frameBufferOffset; // CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0). // bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window // at A000:0. WriteCrtcReg(MEMORY_CONFIG_REG, 0x00, 0x01); // Program the GBD and SBDs. WriteReg32(S3_GLB_BD_LOW, si.GlobalBD.bd2.LoPart ); WriteReg32(S3_GLB_BD_HIGH, (si.GlobalBD.bd2.HiPart | bci_enable | S3_LITTLE_ENDIAN | S3_BD64)); WriteReg32(S3_PRI_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_PRI_BD_HIGH, si.GlobalBD.bd2.HiPart); WriteReg32(S3_SEC_BD_LOW, si.GlobalBD.bd2.LoPart); WriteReg32(S3_SEC_BD_HIGH, si.GlobalBD.bd2.HiPart); } static void Savage_SetGBD(const DisplayModeEx& mode) { TRACE("Savage_SetGBD()\n"); VerticalRetraceWait(); switch (gInfo.sharedInfo->chipType) { case S3_SAVAGE_3D: Savage_SetGBD_3D(mode); break; case S3_SAVAGE_MX: Savage_SetGBD_MX(mode); break; case S3_SAVAGE4: case S3_PROSAVAGE: case S3_TWISTER: case S3_PROSAVAGE_DDR: Savage_SetGBD_Twister(mode); break; case S3_SUPERSAVAGE: Savage_SetGBD_Super(mode); break; case S3_SAVAGE2000: Savage_SetGBD_2000(mode); break; } } static void Savage_Initialize2DEngine(const DisplayModeEx& mode) { TRACE("Savage_Initialize2DEngine()\n"); SharedInfo& si = *gInfo.sharedInfo; uint32 thresholds; WriteCrtcReg(0x40, 0x01); WriteCrtcReg(0x31, 0x0c); // Setup plane masks. WriteReg32(0x8128, ~0); // enable all write planes WriteReg32(0x812C, ~0); // enable all read planes WriteReg16(0x8134, 0x27); WriteReg16(0x8136, 0x07); switch (si.chipType) { case S3_SAVAGE_3D: case S3_SAVAGE_MX: // Disable BCI. WriteReg32(0x48C18, ReadReg32(0x48C18) & 0x3FF0); // Setup BCI command overflow buffer. WriteReg32(0x48C14, (si.cobOffset >> 11) | (si.cobIndex << 29)); // tim // Program shadow status update. thresholds = ((si.bciThresholdLo & 0xffff) << 16) | (si.bciThresholdHi & 0xffff); WriteReg32(0x48C10, thresholds); WriteReg32(0x48C0C, 0); // Enable BCI and command overflow buffer. WriteReg32(0x48C18, ReadReg32(0x48C18) | 0x0C); break; case S3_SAVAGE4: case S3_PROSAVAGE: case S3_TWISTER: case S3_PROSAVAGE_DDR: case S3_SUPERSAVAGE: // Disable BCI. WriteReg32(0x48C18, ReadReg32(0x48C18) & 0x3FF0); if ( ! si.bDisableCOB) { // Setup BCI command overflow buffer. WriteReg32(0x48C14, (si.cobOffset >> 11) | (si.cobIndex << 29)); } // Program shadow status update; AGD: what should this be? thresholds = ((si.bciThresholdLo & 0x1fffe0) << 11) | ((si.bciThresholdHi & 0x1fffe0) >> 5); WriteReg32(0x48C10, thresholds); WriteReg32(0x48C0C, 0); if (si.bDisableCOB) WriteReg32(0x48C18, ReadReg32(0x48C18) | 0x08); // enable BCI without COB else WriteReg32(0x48C18, ReadReg32(0x48C18) | 0x0C); // enable BCI with COB break; case S3_SAVAGE2000: // Disable BCI. WriteReg32(0x48C18, 0); // Setup BCI command overflow buffer. WriteReg32(0x48C18, (si.cobOffset >> 7) | (si.cobIndex)); // Disable shadow status update. WriteReg32(0x48A30, 0); // Enable BCI and command overflow buffer. WriteReg32(0x48C18, ReadReg32(0x48C18) | 0x00280000 ); break; } // Use and set global bitmap descriptor. // For reasons I do not fully understand yet, on the Savage4, the // write to the GBD register, MM816C, does not "take" at this time. // Only the low-order byte is acknowledged, resulting in an incorrect // stride. Writing the register later, after the mode switch, works // correctly. This needs to get resolved. Savage_SetGBD(mode); } static void Savage_GEReset(const DisplayModeEx& mode) { TRACE("Savage_GEReset() begin\n"); gInfo.WaitIdleEmpty(); snooze(10000); for (int r = 1; r < 10; r++) { bool bSuccess = false; WriteCrtcReg(0x66, 0x02, 0x02); snooze(10000); WriteCrtcReg(0x66, 0x00, 0x02); snooze(10000); gInfo.WaitIdleEmpty(); WriteReg32(DEST_SRC_STR, mode.bytesPerRow << 16 | mode.bytesPerRow); snooze(10000); switch (gInfo.sharedInfo->chipType) { case S3_SAVAGE_3D: case S3_SAVAGE_MX: bSuccess = (STATUS_WORD0 & 0x0008ffff) == 0x00080000; break; case S3_SAVAGE4: case S3_PROSAVAGE: case S3_PROSAVAGE_DDR: case S3_TWISTER: case S3_SUPERSAVAGE: bSuccess = (ALT_STATUS_WORD0 & 0x0081ffff) == 0x00800000; break; case S3_SAVAGE2000: bSuccess = (ALT_STATUS_WORD0 & 0x008fffff) == 0; break; } if (bSuccess) break; snooze(10000); TRACE("Restarting S3 graphics engine reset %2d ...\n", r); } // At this point, the FIFO is empty and the engine is idle. WriteReg32(SRC_BASE, 0); WriteReg32(DEST_BASE, 0); WriteReg32(CLIP_L_R, ((0) << 16) | mode.timing.h_display); WriteReg32(CLIP_T_B, ((0) << 16) | mode.timing.v_display); WriteReg32(MONO_PAT_0, ~0); WriteReg32(MONO_PAT_1, ~0); Savage_SetGBD(mode); TRACE("Savage_GEReset() end\n"); } static void Savage_CalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2, int max_n2, long freq_min, long freq_max, unsigned int *mdiv, unsigned int *ndiv, unsigned int *r) { uint8 best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2; double ffreq = freq / 1000.0 / BASE_FREQ; double ffreq_max = freq_max / 1000.0 / BASE_FREQ; double ffreq_min = freq_min / 1000.0 / BASE_FREQ; if (ffreq < ffreq_min / (1 << max_n2)) { TRACE("Savage_CalcClock() invalid frequency %1.3f Mhz\n", ffreq * BASE_FREQ); ffreq = ffreq_min / (1 << max_n2); } if (ffreq > ffreq_max / (1 << min_n2)) { TRACE("Savage_CalcClock() invalid frequency %1.3f Mhz\n", ffreq * BASE_FREQ); ffreq = ffreq_max / (1 << min_n2); } // Work out suitable timings. double best_diff = ffreq; for (uint8 n2 = min_n2; n2 <= max_n2; n2++) { for (uint8 n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) { int m = (int)(ffreq * n1 * (1 << n2) + 0.5); if (m < min_m + 2 || m > 127 + 2) continue; double div = (double)(m) / (double)(n1); if ((div >= ffreq_min) && (div <= ffreq_max)) { double diff = ffreq - div / (1 << n2); if (diff < 0.0) diff = -diff; if (diff < best_diff) { best_diff = diff; best_m = m; best_n1 = n1; best_n2 = n2; } } } } *ndiv = best_n1 - 2; *r = best_n2; *mdiv = best_m - 2; } static void Savage_WriteMode(const DisplayModeEx& mode, const SavageRegRec& regRec) { TRACE("Savage_WriteMode() enter\n"); SharedInfo& si = *gInfo.sharedInfo; gInfo.WaitIdleEmpty(); WriteMiscOutReg(0x23); WriteCrtcReg(0x38, 0x48); // unlock sys regs CR20~CR3F WriteCrtcReg(0x39, 0xa0); // unlock sys regs CR40~CRFF WriteSeqReg(0x08, 0x06); // unlock sequencer regs SR09~SRFF if ( ! S3_SAVAGE_MOBILE_SERIES(si.chipType)) Savage_Initialize2DEngine(mode); if (ReadCrtcReg(0x66) & 0x01) { Savage_GEReset(mode); // reset GE to make sure nothing is going on } WriteCrtcReg(0x67, regRec.CR67 & ~0x0e); // no STREAMS yet old and new // Set register SR19 to zero so that the ProSavage chips will start up // when booting under BeOS using the default boot screen. if (si.chipType == S3_PROSAVAGE || si.chipType == S3_TWISTER) WriteSeqReg(0x19, 0); // Clear bit 3 in SR30 so that Savage MX chip will startup. If bit 3 is // not cleared, it will startup only if booting under BeOS using the // default boot screen or the boot screen resolution matches the resolution // of the mode currently being set. if (si.chipType == S3_SAVAGE_MX) WriteSeqReg(0x30, 0x00, 0x08); // Set extended regs. WriteCrtcReg(0x66, regRec.CR66); WriteCrtcReg(0x3a, regRec.CR3A); WriteCrtcReg(0x31, regRec.CR31); WriteCrtcReg(0x58, regRec.CR58); WriteCrtcReg(0x53, regRec.CR53 & 0x7f); // Set DCLK registers. WriteSeqReg(0x29, regRec.SR29); WriteSeqReg(0x15, regRec.SR15); // Set the standard CRTC vga regs. WriteCrtcReg(0x11, 0x00, 0x80); // unlock CRTC reg's 0-7 by clearing bit 7 of cr11 for (int j = 0; j < NUM_ELEMENTS(regRec.CRTC); j++) WriteCrtcReg(j, regRec.CRTC[j]); // Setup HSYNC & VSYNC polarity. uint8 temp = ((ReadMiscOutReg() & 0x3f) | 0x0c); if (!(mode.timing.flags & B_POSITIVE_HSYNC)) temp |= 0x40; if (!(mode.timing.flags & B_POSITIVE_VSYNC)) temp |= 0x80; WriteMiscOutReg(temp); // Extended mode timing registers. WriteCrtcReg(0x53, regRec.CR53); WriteCrtcReg(0x5d, regRec.CR5D); WriteCrtcReg(0x5e, regRec.CR5E); WriteCrtcReg(0x3b, regRec.CR3B); WriteCrtcReg(0x3c, regRec.CR3C); WriteCrtcReg(0x43, regRec.CR43); WriteCrtcReg(0x65, regRec.CR65); // Restore the desired video mode with cr67. WriteCrtcReg(0x67, regRec.CR67 & ~0x0e); // no streams for new and old streams engines // Other mode timing and extended regs. WriteCrtcReg(0x34, regRec.CR34); WriteCrtcReg(0x40, regRec.CR40); WriteCrtcReg(0x42, regRec.CR42); WriteCrtcReg(0x45, regRec.CR45); WriteCrtcReg(0x50, regRec.CR50); WriteCrtcReg(0x51, regRec.CR51); // Memory timings. VerticalRetraceWait(); WriteCrtcReg(0x69, regRec.CR69); WriteCrtcReg(0x33, regRec.CR33); WriteCrtcReg(0x86, regRec.CR86); WriteCrtcReg(0x88, regRec.CR88); WriteCrtcReg(0x90, regRec.CR90); WriteCrtcReg(0x91, regRec.CR91); if (si.chipType == S3_SAVAGE4) WriteCrtcReg(0xb0, regRec.CRB0); // Set extended seq regs for dclk. WriteSeqReg(0x12, regRec.SR12); WriteSeqReg(0x13, regRec.SR13); WriteSeqReg(0x29, regRec.SR29); WriteSeqReg(0x18, regRec.SR18); WriteSeqReg(0x1b, regRec.SR1B); // Load new m, n pll values for dclk & mclk. temp = ReadSeqReg(0x15) & ~0x21; WriteSeqReg(0x15, temp | 0x03); WriteSeqReg(0x15, temp | 0x23); WriteSeqReg(0x15, temp | 0x03); WriteSeqReg(0x15, regRec.SR15); snooze( 100 ); // Now write out cr67 in full, possibly starting STREAMS. VerticalRetraceWait(); WriteCrtcReg(0x67, regRec.CR67); uint8 cr66 = ReadCrtcReg(0x66); WriteCrtcReg(0x66, cr66 | 0x80); uint8 cr3a = ReadCrtcReg(0x3a); WriteCrtcReg(0x3a, cr3a | 0x80); Savage_GEReset(mode); WriteCrtcReg(0x66, cr66); WriteCrtcReg(0x3a, cr3a); Savage_Initialize2DEngine(mode); WriteCrtcReg(0x40, 0x01); // enable graphics engine Savage_SetGBD(mode); TRACE("Savage_WriteMode() end\n"); return; } static bool Savage_ModeInit(const DisplayModeEx& mode) { TRACE("Savage_ModeInit(%dx%d, %dHz)\n", mode.timing.h_display, mode.timing.v_display, mode.timing.pixel_clock); SharedInfo& si = *gInfo.sharedInfo; SavageRegRec regRec; int horizScaleFactor = 1; if (mode.bpp == 16 && si.chipType == S3_SAVAGE_3D) horizScaleFactor = 2; InitCrtcTimingValues(mode, horizScaleFactor, regRec.CRTC, regRec.CR3B, regRec.CR3C, regRec.CR5D, regRec.CR5E); regRec.CRTC[23] = 0xEB; int dclk = mode.timing.pixel_clock; regRec.CR67 = 0x00; switch (mode.bpp) { case 8: if ((si.chipType == S3_SAVAGE2000) && (dclk >= 230000)) regRec.CR67 = 0x10; // 8bpp, 2 pixels/clock else regRec.CR67 = 0x00; // 8bpp, 1 pixel/clock break; case 15: if (S3_SAVAGE_MOBILE_SERIES(si.chipType) || ((si.chipType == S3_SAVAGE2000) && (dclk >= 230000))) regRec.CR67 = 0x30; // 15bpp, 2 pixel/clock else regRec.CR67 = 0x20; // 15bpp, 1 pixels/clock break; case 16: if (S3_SAVAGE_MOBILE_SERIES(si.chipType) || ((si.chipType == S3_SAVAGE2000) && (dclk >= 230000))) regRec.CR67 = 0x50; // 16bpp, 2 pixel/clock else regRec.CR67 = 0x40; // 16bpp, 1 pixels/clock break; case 32: regRec.CR67 = 0xd0; break; } // Use traditional register-crunching to set mode. regRec.CR3A = (ReadCrtcReg(0x3a) & 0x7f) | 0x15; regRec.CR53 = 0x00; regRec.CR31 = 0x8c; regRec.CR66 = 0x89; regRec.CR58 = (ReadCrtcReg(0x58) & 0x80) | 0x13; if (si.chipType == S3_SAVAGE2000) regRec.SR15 = 0x02; else regRec.SR15 = 0x83; regRec.SR18 = 0x00; regRec.SR1B = ReadSeqReg(0x1b) | 0x10; // enable 8-bit Color Lookup Table regRec.CR43 = regRec.CR45 = regRec.CR65 = 0x00; regRec.CR40 = ReadCrtcReg(0x40) & ~0x01; unsigned int m, n, r; Savage_CalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r); regRec.SR12 = (r << 6) | (n & 0x3f); regRec.SR13 = m & 0xff; regRec.SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2; TRACE("CalcClock, m: %d n: %d r: %d\n", m, n, r); regRec.CR42 = 0x00; regRec.CR34 = 0x10; int width = mode.bytesPerRow / 8; regRec.CR91 = 0xff & width; regRec.CR51 = (0x300 & width) >> 4; regRec.CR90 = 0x80 | (width >> 8); // Set frame buffer description. if (mode.bpp <= 8) regRec.CR50 = 0; else if (mode.bpp <= 16) regRec.CR50 = 0x10; else regRec.CR50 = 0x30; width = mode.timing.h_display; // width of display in pixels if (width == 640) regRec.CR50 |= 0x40; else if (width == 800) regRec.CR50 |= 0x80; else if (width == 1024) regRec.CR50 |= 0x00; else if (width == 1152) regRec.CR50 |= 0x01; else if (width == 1280) regRec.CR50 |= 0xc0; else if (width == 1600) regRec.CR50 |= 0x81; else regRec.CR50 |= 0xc1; // use GBD if (S3_SAVAGE_MOBILE_SERIES(si.chipType)) regRec.CR33 = 0x00; else regRec.CR33 = 0x08; regRec.CR67 |= 1; regRec.CR69 = 0; regRec.CR86 = ReadCrtcReg(0x86) | 0x08; regRec.CR88 = ReadCrtcReg(0x88) | DISABLE_BLOCK_WRITE_2D; regRec.CRB0 = ReadCrtcReg(0xb0) | 0x80; Savage_WriteMode(mode, regRec); // write registers to set mode return true; } bool Savage_SetDisplayMode(const DisplayModeEx& mode) { // The code to actually configure the display. // All the error checking must be done in ProposeDisplayMode(), // and assume that the mode values we get here are acceptable. WriteSeqReg(0x01, 0x20, 0x20); // blank the screen if ( ! Savage_ModeInit(mode)) { TRACE("Savage_ModeInit() failed\n"); return false; } Savage_AdjustFrame(mode); WriteSeqReg(0x01, 0x00, 0x20); // unblank the screen return true; } void Savage_AdjustFrame(const DisplayModeEx& mode) { // Adjust start address in frame buffer. SharedInfo& si = *gInfo.sharedInfo; int address = (mode.v_display_start * mode.virtual_width) + ((mode.h_display_start & ~0x3F) * (mode.bpp / 8)); address &= ~0x1F; address += si.frameBufferOffset; switch (si.chipType) { case S3_SAVAGE_MX: WriteReg32(PRI_STREAM_FBUF_ADDR0, address & 0xFFFFFFFC); WriteReg32(PRI_STREAM_FBUF_ADDR1, address & 0xFFFFFFFC); break; case S3_SUPERSAVAGE: WriteReg32(PRI_STREAM_FBUF_ADDR0, 0x80000000); WriteReg32(PRI_STREAM_FBUF_ADDR1, address & 0xFFFFFFF8); break; case S3_SAVAGE2000: // Certain Y values seems to cause havoc, not sure why WriteReg32(PRI_STREAM_FBUF_ADDR0, (address & 0xFFFFFFF8)); WriteReg32(PRI_STREAM2_FBUF_ADDR0, (address & 0xFFFFFFF8)); break; default: WriteReg32(PRI_STREAM_FBUF_ADDR0, address | 0xFFFFFFFC); WriteReg32(PRI_STREAM_FBUF_ADDR1, address | 0x80000000); break; } return; } void Savage_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags) { // Set the indexed color palette for 8-bit color depth mode. (void)flags; // avoid compiler warning for unused arg if (gInfo.sharedInfo->displayMode.space != B_CMAP8) return ; while (count--) { WriteIndexedColor(first++, // color index colorData[0], // red colorData[1], // green colorData[2]); // blue colorData += 3; } }