root/src/add-ons/accelerants/s3/virge_init.cpp
/*
        Haiku S3 Virge driver adapted from the X.org Virge driver.

        Copyright (C) 1994-1999 The XFree86 Project, Inc.       All Rights Reserved.

        Copyright 2007-2008 Haiku, Inc.  All rights reserved.
        Distributed under the terms of the MIT license.

        Authors:
        Gerald Zajac 2007-2008
*/


#include "accel.h"
#include "virge.h"



static void
VirgeWaitFifoGX2(uint32 slots )
{
        while (((ReadReg32(SUBSYS_STAT_REG) >> 9) & 0x60) < slots) {}
}



static void
VirgeWaitFifoMain(uint32 slots )
{
        while (((ReadReg32(SUBSYS_STAT_REG) >> 8) & 0x1f) < slots) {}
}


static void
VirgeWaitIdleEmpty()
{
        // Wait until GP is idle and queue is empty.

        if(gInfo.sharedInfo->chipType == S3_TRIO_3D)
                while ((IN_SUBSYS_STAT() & 0x3f802000 & 0x20002000) != 0x20002000);
        else
                while ((IN_SUBSYS_STAT() & 0x3f00) != 0x3000);
}


static bool
Virge_GetColorSpaceParams(int colorSpace, uint32& bitsPerPixel, uint32& maxPixelClock)
{
        // Get parameters for a color space which is supported by the Virge chips.
        // Argument maxPixelClock is in KHz.
        // Return true if the color space is supported;  else return false.

        // Note that the X.org code set the max clock to 440000 for a Virge VX chip
        // and 270000 for all other chips.  440000 seems rather high for a chip that
        // old;  thus, 270000 is used for all chips.

        switch (colorSpace) {
                case B_RGB16:
                        bitsPerPixel = 16;
                        maxPixelClock = 270000;
                        break;
                case B_CMAP8:
                        bitsPerPixel = 8;
                        maxPixelClock = 270000;
                        break;
                default:
                        TRACE("Unsupported color space: 0x%X\n", colorSpace);
                        return false;
        }

        return true;
}



status_t 
Virge_Init(void)
{
        TRACE("Virge_Init()\n");

        SharedInfo& si = *gInfo.sharedInfo;

        // Use PIO for following operations since MMIO may not be currently enabled.

        WritePIO_8(VGA_ENABLE, ReadPIO_8(VGA_ENABLE) | 0x01);   // enable VGA
        WritePIO_8(MISC_OUT_W, ReadPIO_8(MISC_OUT_R) | 0x01);   // enable color

        // Set linear base register to the PCI register value;
        // some DX chipsets don't seem to do it automatically.

        WritePIO_8(CRTC_INDEX, 0x59);
        WritePIO_8(CRTC_DATA, (uint8)((uint32)(si.videoMemPCI) >> 24));
        WritePIO_8(CRTC_INDEX, 0x5A);
        WritePIO_8(CRTC_DATA, (uint8)((uint32)(si.videoMemPCI) >> 16));

        // Enable MMIO.

        WritePIO_8(CRTC_INDEX, 0x53);
        WritePIO_8(CRTC_DATA, ReadPIO_8(CRTC_DATA) | 0x8);

        if (si.chipType == S3_TRIO_3D)
                WriteCrtcReg(0x40, 0x01, 0x01);

        // Detect amount of installed ram.

        uint8 config1 = ReadCrtcReg(0x36);      // get amount of vram installed
        uint8 config2 = ReadCrtcReg(0x37);      // get amount of off-screen ram

        // Compute the amount of video memory and offscreen memory.

        int   ramOffScreenMB = 0;       // off screen memory size in megabytes
        int   ramSizeMB = 0;            // memory size in megabytes

        if (si.chipType == S3_VIRGE_VX) {
                switch ((config2 & 0x60) >> 5) {
                        case 1:
                                ramOffScreenMB = 4;
                                break;
                        case 2:
                                ramOffScreenMB = 2;
                                break;
                }

                switch ((config1 & 0x60) >> 5) {
                        case 0:
                                ramSizeMB = 2;
                                break;
                        case 1:
                                ramSizeMB = 4;
                                break;
                        case 2:
                                ramSizeMB = 6;
                                break;
                        case 3:
                                ramSizeMB = 8;
                                break;
                }
                ramSizeMB -= ramOffScreenMB;

        } else if (si.chipType == S3_TRIO_3D_2X) {
                switch ((config1 & 0xE0) >> 5) {
                        case 0:         // 8MB -- only 4MB usable for display/cursor
                                ramSizeMB = 4;
                                ramOffScreenMB = 4;
                                break;
                        case 1:     // 32 bit interface -- yuck
                                TRACE("Undefined video memory size on S3 Trio 3D/2X\n");
                        case 2:
                                ramSizeMB = 4;
                                break;
                        case 6:
                                ramSizeMB = 2;
                                break;
                }
        } else if (si.chipType == S3_TRIO_3D) {
                switch ((config1 & 0xE0) >> 5) {
                        case 0:
                        case 2:
                                ramSizeMB = 4;
                                break;
                        case 4:
                                ramSizeMB = 2;
                                break;
                }
        } else if (si.chipType == S3_VIRGE_GX2 || S3_VIRGE_MX_SERIES(si.chipType)) {
                switch ((config1 & 0xC0) >> 6) {
                        case 1:
                                ramSizeMB = 4;
                                break;
                        case 3:
                                ramSizeMB = 2;
                                break;
                }
        } else {
                switch ((config1 & 0xE0) >> 5) {
                        case 0:
                                ramSizeMB = 4;
                                break;
                        case 4:
                                ramSizeMB = 2;
                                break;
                        case 6:
                                ramSizeMB = 1;
                                break;
                }
        }

        TRACE("usable memory: %d MB,  off-screen memory: %d MB\n", ramSizeMB, ramOffScreenMB);

        if (ramSizeMB <= 0)
                return B_ERROR;

        si.videoMemSize = ramSizeMB * 1024 * 1024;
        si.cursorOffset = si.videoMemSize - CURSOR_BYTES;       // put cursor at end of video memory
        si.frameBufferOffset = 0;
        si.maxFrameBufferSize = si.videoMemSize - CURSOR_BYTES;

        // 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);

        if (S3_VIRGE_MX_SERIES(si.chipType)) {
                si.displayType = ((ReadSeqReg(0x31) & 0x10) ? MT_LCD : MT_CRT);
                si.panelX = (ReadSeqReg(0x61) + ((ReadSeqReg(0x66) & 0x02) << 7) + 1) * 8;
                si.panelY =  ReadSeqReg(0x69) + ((ReadSeqReg(0x6e) & 0x70) << 4) + 1;

                TRACE("%dx%d LCD panel detected %s\n", si.panelX, si.panelY,
                                 si.displayType == MT_LCD ? "and active" : "but not active");
        } else {
                si.displayType = MT_CRT;
                si.panelX = 0;
                si.panelY = 0;
        }

        // Set up the array of color spaces supported by the Virge/Trio3D chips.

        si.colorSpaces[0] = B_CMAP8;
        si.colorSpaces[1] = B_RGB16;
        si.colorSpaceCount = 2;

        si.bDisableHdwCursor = false;   // allow use of hardware cursor
        si.bDisableAccelDraw = false;   // allow use of accelerated drawing functions

        // Setup the mode list.

        return CreateModeList(IsModeUsable, Virge_GetEdidInfo);
}


void
Virge_SetFunctionPointers(void)
{
        // Setting the function pointers must be done prior to first ModeInit call
        // or any accel activity.

        if (S3_VIRGE_GX2_SERIES(gInfo.sharedInfo->chipType)) {
                gInfo.WaitQueue = VirgeWaitFifoGX2;
        } else {
                gInfo.WaitQueue = VirgeWaitFifoMain;
        }

        gInfo.WaitIdleEmpty = VirgeWaitIdleEmpty;

        gInfo.DPMSCapabilities = Virge_DPMSCapabilities;
        gInfo.GetDPMSMode = Virge_GetDPMSMode;
        gInfo.SetDPMSMode = Virge_SetDPMSMode;

        gInfo.LoadCursorImage = Virge_LoadCursorImage;
        gInfo.SetCursorPosition = Virge_SetCursorPosition;
        gInfo.ShowCursor = Virge_ShowCursor;

        gInfo.FillRectangle = Virge_FillRectangle;
        gInfo.FillSpan = Virge_FillSpan;
        gInfo.InvertRectangle = Virge_InvertRectangle;
        gInfo.ScreenToScreenBlit = Virge_ScreenToScreenBlit;

        gInfo.AdjustFrame = Virge_AdjustFrame;
        gInfo.ChipInit = Virge_Init;
        gInfo.GetColorSpaceParams = Virge_GetColorSpaceParams;
        gInfo.SetDisplayMode = Virge_SetDisplayMode;
        gInfo.SetIndexedColors = Virge_SetIndexedColors;
}