/* Haiku S3 Savage driver adapted from the X.org Savage driver. Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved. Copyright (c) 2003-2006, X.Org Foundation 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" static bool Savage_GetColorSpaceParams(int colorSpace, uint32& bitsPerPixel, uint32& maxPixelClock) { // Get parameters for a color space which is supported by the Savage chips. // Argument maxPixelClock is in KHz. // Return true if the color space is supported; else return false. switch (colorSpace) { case B_RGB32: bitsPerPixel = 32; maxPixelClock = 220000; break; case B_RGB16: bitsPerPixel = 16; maxPixelClock = 250000; break; case B_CMAP8: bitsPerPixel = 8; maxPixelClock = 250000; break; default: TRACE("Unsupported color space: 0x%X\n", colorSpace); return false; } return true; } // Wait until "v" queue entries are free. static void WaitQueue3D(uint32 v) { uint32 slots = MAXFIFO - v; while ((STATUS_WORD0 & 0x0000ffff) > slots); } static void WaitQueue4(uint32 v) { uint32 slots = MAXFIFO - v; while ((ALT_STATUS_WORD0 & 0x001fffff) > slots); } static void WaitQueue2K(uint32 v) { uint32 slots = MAXFIFO - v; while ((ALT_STATUS_WORD0 & 0x000fffff) > slots); } // Wait until GP is idle and queue is empty. static void WaitIdleEmpty3D() { while ((STATUS_WORD0 & 0x0008ffff) != 0x80000); } static void WaitIdleEmpty4() { while ((ALT_STATUS_WORD0 & 0x00e1ffff) != 0x00e00000) ; } static void WaitIdleEmpty2K() { while ((ALT_STATUS_WORD0 & 0x009fffff) != 0); } static bool IsLCDWidthValid(int width) { // Search the array of valid LCD widths to find a width that matches the // width by the caller, and return true if a match is found. const int lcdWidths[] = { 640, 800, 1024, 1152, 1280, 1400, 1440, 1600, 1680 }; for (int i = 0; i < NUM_ELEMENTS(lcdWidths); i++) { if (lcdWidths[i] == width) return true; } return false; // match not found } static void Savage_GetPanelInfo() { SharedInfo& si = *gInfo.sharedInfo; enum ACTIVE_DISPLAYS { // these are the bits in CR6B ActiveCRT = 0x01, ActiveLCD = 0x02, ActiveTV = 0x04, ActiveCRT2 = 0x20, ActiveDUO = 0x80 }; // Check LCD panel information. uint8 cr6b = ReadCrtcReg(0x6b); int panelX = (ReadSeqReg(0x61) + ((ReadSeqReg(0x66) & 0x02) << 7) + 1) * 8; int panelY = ReadSeqReg(0x69) + ((ReadSeqReg(0x6e) & 0x70) << 4) + 1; if ( ! IsLCDWidthValid(panelX)) { // Some chips such as the Savage IX/MV in a Thinkpad T-22 will return // a width that is 8 pixels too wide probably because reg SR61 is set // to a value +1 higher than it should be. Subtract 8 from the width, // and check if that is a valid width. panelX -= 8; if ( ! IsLCDWidthValid(panelX)) { TRACE("%dx%d LCD panel width invalid.\n", panelX + 8, panelY); si.displayType = MT_CRT; return; } } char* sTechnology; if ((ReadSeqReg(0x39) & 0x03) == 0) sTechnology = "TFT"; else if ((ReadSeqReg(0x30) & 0x01) == 0) sTechnology = "DSTN"; else sTechnology = "STN"; TRACE("%dx%d %s LCD panel detected %s\n", panelX, panelY, sTechnology, cr6b & ActiveLCD ? "and active" : "but not active"); if (cr6b & ActiveLCD) { TRACE("Limiting max video mode to %dx%d\n", panelX, panelY); si.panelX = panelX; si.panelY = panelY; } else { si.displayType = MT_CRT; } } status_t Savage_Init(void) { TRACE("Savage_Init()\n"); SharedInfo& si = *gInfo.sharedInfo; // MMIO should be automatically enabled for Savage chips; thus, use MMIO // to enable VGA and turn color on. WriteReg8(VGA_ENABLE + 0x8000, ReadReg8(VGA_ENABLE + 0x8000) | 0x01); WriteMiscOutReg(ReadMiscOutReg() | 0x01); // turn color on if (si.chipType >= S3_SAVAGE4) WriteCrtcReg(0x40, 0x01, 0x01); WriteCrtcReg(0x11, 0x00, 0x80); // unlock CRTC reg's 0-7 by clearing bit 7 of cr11 WriteCrtcReg(0x38, 0x48); // unlock sys regs CR20~CR3F WriteCrtcReg(0x39, 0xa0); // unlock sys regs CR40~CRFF WriteSeqReg(0x08, 0x06); // unlock sequencer regs SR09~SRFF WriteCrtcReg(0x40, 0x00, 0x01); WriteCrtcReg(0x38, 0x48); // unlock sys regs CR20~CR3F // Compute the amount of video memory and offscreen memory. static const uint8 RamSavage3D[] = { 8, 4, 4, 2 }; static uint8 RamSavage4[] = { 2, 4, 8, 12, 16, 32, 64, 32 }; static const uint8 RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 }; static const uint8 RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 16, 2 }; int ramSizeMB = 0; // memory size in megabytes uint8 cr36 = ReadCrtcReg(0x36); // get amount of video ram switch (si.chipType) { case S3_SAVAGE_3D: ramSizeMB = RamSavage3D[ (cr36 & 0xC0) >> 6 ]; break; case S3_SAVAGE4: // The Savage4 has one ugly special case to consider. On // systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB // when it really means 8MB. Why do it the same when you // can do it different... if ((ReadCrtcReg(0x68) & 0xC0) == (0x01 << 6)) RamSavage4[1] = 8; // FALL THROUGH case S3_SAVAGE2000: ramSizeMB = RamSavage4[ (cr36 & 0xE0) >> 5 ]; break; case S3_SAVAGE_MX: case S3_SUPERSAVAGE: ramSizeMB = RamSavageMX[ (cr36 & 0x0E) >> 1 ]; break; case S3_PROSAVAGE: case S3_PROSAVAGE_DDR: case S3_TWISTER: ramSizeMB = RamSavageNB[ (cr36 & 0xE0) >> 5 ]; break; default: // How did we get here? ramSizeMB = 0; break; } TRACE("Savage_Init() memory size: %d MB\n", ramSizeMB); if (ramSizeMB <= 0) return B_ERROR; si.videoMemSize = ramSizeMB * 1024 * 1024; // Certain Savage4 and ProSavage chips can have coherency problems // with respect to the Command Overflow Buffer (COB); thus, to avoid // problems with these chips, set bDisableCOB to true. si.bDisableCOB = false; // Compute the Command Overflow Buffer (COB) location. if ((S3_SAVAGE4_SERIES(si.chipType) || S3_SUPERSAVAGE == si.chipType) && si.bDisableCOB) { // The Savage4 and ProSavage have COB coherency bugs which render // the buffer useless. si.cobIndex = 0; si.cobSize = 0; si.bciThresholdHi = 32; si.bciThresholdLo = 0; } else { // We use 128kB for the COB on all other chips. si.cobSize = 0x20000; if (S3_SAVAGE_3D_SERIES(si.chipType) || si.chipType == S3_SAVAGE2000) si.cobIndex = 7; // rev.A savage4 apparently also uses 7 else si.cobIndex = 2; // Max command size: 2560 entries. si.bciThresholdHi = si.cobSize / 4 + 32 - 2560; si.bciThresholdLo = si.bciThresholdHi - 2560; } // Note that the X.org developers stated that the command overflow buffer // (COB) must END at a 4MB boundary which for all practical purposes means // the very end of the video memory. The cursor must be at the beginning // of the video memory. It had been tried immediately preceding the COB, // but the Savage MX chip screws up the cursor in that case. si.cobOffset = (si.videoMemSize - si.cobSize) & ~0x1ffff; // align cob to 128k si.cursorOffset = 0; si.frameBufferOffset = si.cursorOffset + CURSOR_BYTES; si.maxFrameBufferSize = si.cobOffset - si.frameBufferOffset; TRACE("cobIndex: %d cobSize: %d cobOffset: 0x%x\n", si.cobIndex, si.cobSize, si.cobOffset); TRACE("cursorOffset: 0x%x frameBufferOffset: 0x%x\n", si.cursorOffset, si.frameBufferOffset); // Reset graphics engine to avoid memory corruption. WriteCrtcReg(0x66, 0x02, 0x02); // set reset flag snooze(10000); WriteCrtcReg(0x66, 0x00, 0x02); // clear reset flag snooze(10000); // Check for DVI/flat panel. bool bDvi = false; if (si.chipType == S3_SAVAGE4) { WriteSeqReg(0x30, 0x00, 0x02); // clear bit 1 if (ReadSeqReg(0x30) & 0x02 /* 0x04 */) { bDvi = true; TRACE("Digital Flat Panel Detected\n"); } } if (S3_SAVAGE_MOBILE_SERIES(si.chipType) || S3_MOBILE_TWISTER_SERIES(si.chipType)) { si.displayType = MT_LCD; Savage_GetPanelInfo(); } else if (bDvi) si.displayType = MT_DFP; else si.displayType = MT_CRT; TRACE("Display Type: %d\n", si.displayType); // Detect current mclk. WriteSeqReg(0x08, 0x06); // unlock extended sequencer regs uint8 m = ReadSeqReg(0x11) & 0x7f; uint8 n = ReadSeqReg(0x10); uint8 n1 = n & 0x1f; uint8 n2 = (n >> 5) & 0x03; si.mclk = ((1431818 * (m + 2)) / (n1 + 2) / (1 << n2) + 50) / 100; TRACE("Detected current MCLK value of %1.3f MHz\n", si.mclk / 1000.0); // Set up the array of color spaces supported by the Savage chips. si.colorSpaces[0] = B_CMAP8; si.colorSpaces[1] = B_RGB16; si.colorSpaces[2] = B_RGB32; si.colorSpaceCount = 3; // Get info about the display capabilities (EDID). Savage_GetEdidInfo(); // Setup the mode list. return CreateModeList(IsModeUsable); } void Savage_SetFunctionPointers(void) { // Setting the function pointers must be done prior to first ModeInit call // or any accel activity. switch (gInfo.sharedInfo->chipType) { case S3_SAVAGE_3D: case S3_SAVAGE_MX: gInfo.WaitQueue = WaitQueue3D; gInfo.WaitIdleEmpty = WaitIdleEmpty3D; break; case S3_SAVAGE4: case S3_PROSAVAGE: case S3_SUPERSAVAGE: case S3_PROSAVAGE_DDR: case S3_TWISTER: gInfo.WaitQueue = WaitQueue4; gInfo.WaitIdleEmpty = WaitIdleEmpty4; break; case S3_SAVAGE2000: gInfo.WaitQueue = WaitQueue2K; gInfo.WaitIdleEmpty = WaitIdleEmpty2K; break; } gInfo.DPMSCapabilities = Savage_DPMSCapabilities; gInfo.DPMSMode = Savage_DPMSMode; gInfo.SetDPMSMode = Savage_SetDPMSMode; gInfo.LoadCursorImage = Savage_LoadCursorImage; gInfo.SetCursorPosition = Savage_SetCursorPosition; gInfo.ShowCursor = Savage_ShowCursor; gInfo.FillRectangle = Savage_FillRectangle; gInfo.FillSpan = Savage_FillSpan; gInfo.InvertRectangle = Savage_InvertRectangle; gInfo.ScreenToScreenBlit = Savage_ScreenToScreenBlit; gInfo.AdjustFrame = Savage_AdjustFrame; gInfo.ChipInit = Savage_Init; gInfo.GetColorSpaceParams = Savage_GetColorSpaceParams; gInfo.SetDisplayMode = Savage_SetDisplayMode; gInfo.SetIndexedColors = Savage_SetIndexedColors; }