/*****************************************************************************
 * AGPLIB.C - AGP services code to detect and enable the AGP aperture        *
 *                                                                           *
 * Copyright (c) 1999, ATI Technologies Inc.  All Rights Reserved.           *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include "main.h"
#include "regdef.h"
#include "defines.h"
#include "agp.h"

static WORD AGP_VendorID [1] = {
    VENDOR_ID_VIA,
};

static WORD AGP_ChipsetID [2] =   {
    DEVICE_ID_597,      // VIA 597
    DEVICE_ID_598,      // VIA 598
};

static _AGP_INFO AGP_INFO;
static BYTE R128_AGP_cap_ptr;

extern BOOL SetupGARTTable (DWORD, DWORD);

BOOL R128_DetectAGP (void)
{
    WORD wval1, wval2, j;
    WORD bus, busmax, device;
    BYTE cap_ptr;

    if (R128_AdapterInfo.FLAGS & R128_FORCE_PCI)
    {
        // Pretend we are PCI, hence fail AGP detect.
        return (FALSE);
    } // if

    busmax = PCIInstallCheck ();
    bus = 0;

    if (busmax > 255)
    {
        // no PCI BIOS installed, return a failure.
        return (FALSE);
    } // if

    // First, see if the Rage 128 board has AGP capabilities.

    R128_AGP_cap_ptr = GetAGPCaps (R128_AdapterInfo.bus_num, R128_AdapterInfo.dev_num);
    if (R128_AGP_cap_ptr == 0)
    {
        // AGP capabilities not present.  Must be a PCI board.
        return (FALSE);
    } // if

    // Let's go through the PCI Config space until we find an AGP chipset.

    for (bus = 0; bus <= busmax; bus += 1)
    {
        for (device = 0; device < 32; device += 1)
        {
            wval1 = GetPCIWord (bus, device, 0x0000);
            for (j = 0; j < MAX_AGP_VENDOR_IDS; j++)
            {
                if (wval1 == AGP_VendorID[j]) break;
            } // for

            if (j == MAX_AGP_VENDOR_IDS) continue;

            // if we've gotten here, we have an AGP chipset vendor.
            // However, let's find out if it's an AGP chipset installed

            wval2 = GetPCIWord (bus, device, 0x0002); // get DeviceID
            for (j = 0; j < MAX_AGP_CHIPSET_IDS; j++)
            {
                if (wval2 == AGP_ChipsetID[j])
                {
                    // We've got an AGP chipset.  As a final sanity check,
                    // make sure that AGP capabilities are present.

                    cap_ptr = GetAGPCaps (bus, device);
                    if (cap_ptr == 0) continue;

                    // AGP capabilities are listed.  Fill in the struct.

                    AGP_INFO.Vendor_ID = wval1;
                    AGP_INFO.Device_ID = wval2;
                    AGP_INFO.bn = bus;
                    AGP_INFO.dn = device;
                    AGP_INFO.cap_ptr = cap_ptr;

                    // call this function to get the aperture address

                    AGP_INFO.ApertureBase = GetAGPBase (AGP_TARGET_DEVICE);
                    return (TRUE);
                } // if
            } // for
        } // for
    } // for

    // If we made it this far, we never found an AGP chipset.

    return (FALSE);
} // R128_DetectAGP

BYTE GetAGPCaps (WORD bus, WORD device)
{
    WORD cap_ptr;

    // Check that AGP capabilities are present by checking for their existence
    // in config space.  Bit 4 of the PCI Status register indicates if a list
    // of capabilities is present.

    if ((GetPCIWord (bus, device, 0x0006) & 0x0010) == 0)
    {
        // List is unavailable.  Assume AGP is not present.

        return (0);
    } // if

    // Capability list is present.  Offset 0x34 points to the
    // first entry in the list.

    cap_ptr = (WORD) GetPCIByte (bus, device, 0x0034);

    while (cap_ptr != 0)
    {
        // Check the CAP_ID byte for this list entry.  If the ID is 2 (the
        // PCI SIG assigned ID for AGP) then AGP capabilities have been found.

        if (GetPCIByte (bus, device, cap_ptr) == 2) break;

        // Try the next list entry (pointer in next byte).

        cap_ptr = GetPCIByte (bus, device, cap_ptr + 1);
    } // for

    // At this point cap_ptr will either contain a pointer to AGP capabilities
    // or zero if none were found.

    return ((BYTE) cap_ptr);
} // GetAGPCaps

DWORD GetAGPBase (WORD device)
{
    DWORD agpbase;

    if (device == AGP_MASTER_DEVICE)
    {
        agpbase = regr (AGP_BASE);
        return (agpbase);
    } // if

    switch (AGP_INFO.Device_ID)
    {
        case DEVICE_ID_597:
        case DEVICE_ID_598:
            agpbase = GetPCIDword (AGP_INFO.bn, AGP_INFO.dn, 0x10) & 0xFFFFFFF0;
            break;

        default:
            agpbase = 0;
    } // switch

    return (agpbase);

} // GetAGPBase


WORD GetAGPSize (WORD device)
{
    BYTE bn;
    BYTE dn;
    BYTE byte;
    WORD agpsize;
    DWORD temp;

    // set initial default size, see if the chipset must change it.
    agpsize = AGP_SIZE_256MB;

    if (device == AGP_MASTER_DEVICE)
    {
        temp = regr (AGP_CNTL) & 0x0000003F;
        agpsize = (WORD) temp;
    }
    else
    {
        // Get ids

        bn = AGP_INFO.bn;
        dn = AGP_INFO.dn;

        // Check for specific vendors

        switch (AGP_INFO.Device_ID)
        {
            case DEVICE_ID_597:
            case DEVICE_ID_598:
                byte = GetPCIByte (bn, dn, 0x84);
                agpsize = (WORD) ((byte >> 2) & 0x3F);
                break;
		} // switch

    } // if

    return (agpsize);

} // GetAGPSize


void SetAGPSize (WORD device, WORD agpsize)
{
    BYTE  bn;
    BYTE  dn;
    BYTE  byte;
    BYTE  temp;
    DWORD temp1;

    if (device == AGP_MASTER_DEVICE)
    {
        temp = regr (AGP_CNTL);
        temp &= 0xFFFFFFC0;
        temp |= (DWORD) agpsize;
        regw (AGP_CNTL, temp);
        return;
    }

    // Get ids

    bn = AGP_INFO.bn;
    dn = AGP_INFO.dn;

    // Check for specific vendors

    switch (AGP_INFO.Device_ID)
    {
        case DEVICE_ID_597:
        case DEVICE_ID_598:
            byte = (BYTE) ((agpsize & 0x3F) << 2);
            SetPCIByte (bn, dn, 0x84, byte);
            break;

    } // switch

    temp1 = GetAGPByteSize (agpsize);
    AGP_INFO.ApertureSize = temp1;

    return;
} // SetAGPSize

DWORD GetAGPByteSize (WORD agpsize)
{
    DWORD bytesize;

    // return byte size of given AGP size code
    switch (agpsize)
    {
        default:
        case AGP_SIZE_4MB:
            bytesize = 0x00400000;
            break;

        case AGP_SIZE_8MB:
            bytesize = 0x00800000;
            break;

        case AGP_SIZE_16MB:
            bytesize = 0x01000000;
            break;

        case AGP_SIZE_32MB:
            bytesize = 0x02000000;
            break;

        case AGP_SIZE_64MB:
            bytesize = 0x04000000;
            break;

        case AGP_SIZE_128MB:
            bytesize = 0x08000000;
            break;

        case AGP_SIZE_256MB:
            bytesize = 0x10000000;
            break;

        case AGP_SIZE_512MB:
            bytesize = 0x20000000;
            break;

        case AGP_SIZE_1GB:
            bytesize = 0x40000000;
            break;

        case AGP_SIZE_2GB:
            bytesize = 0x80000000;
            break;
    } // switch

    return (bytesize);
} // GetAGPByteSize


void GetAGPStatus (WORD device, AGPSTATUS *STATUS)
{
    BYTE cap_ptr, bn, dn;
    DWORD temp;

    if (device == AGP_MASTER_DEVICE)
    {
        bn = R128_AdapterInfo.bus_num;
        dn = R128_AdapterInfo.dev_num;
        cap_ptr = R128_AGP_cap_ptr;
    }
    else
    {
        bn = AGP_INFO.bn;
        dn = AGP_INFO.dn;
        cap_ptr = AGP_INFO.cap_ptr;
    } // if

    temp = GetPCIDword (bn, dn, (cap_ptr + 4));

    STATUS->Rate = (BYTE) (temp & 0x00000007);
    STATUS->SBA = (BYTE) ((temp & 0x00000200) >> 9);
    STATUS->RQ = (BYTE) ((temp & 0xFF000000) >> 24);

    return;
} // GetAGPStatus


BOOL AGP_Enable (WORD device)
{
    BYTE dn, bn, cap_ptr;
    DWORD temp;

    if (device == AGP_MASTER_DEVICE)
    {
        bn = R128_AdapterInfo.bus_num;
        dn = R128_AdapterInfo.dev_num;
        cap_ptr = R128_AGP_cap_ptr;
    }
    else
    {
        bn = AGP_INFO.bn;
        dn = AGP_INFO.dn;
        cap_ptr = AGP_INFO.cap_ptr;
    } // if

    temp = GetPCIDword (bn, dn, (cap_ptr + 8));

    temp |= 0x00000100; // enable the AGP_ENABLE bit
    SetPCIDword (bn, dn, (cap_ptr + 8), temp);

    return (TRUE);
} // AGP_Enable


void SetSBAState (WORD device, BYTE sba)
{
    BYTE dn, bn, cap_ptr;
    DWORD temp;

    if (device == AGP_MASTER_DEVICE)
    {
        bn = R128_AdapterInfo.bus_num;
        dn = R128_AdapterInfo.dev_num;
        cap_ptr = R128_AGP_cap_ptr;
    }
    else
    {
        bn = AGP_INFO.bn;
        dn = AGP_INFO.dn;
        cap_ptr = AGP_INFO.cap_ptr;
    } // if

    temp = GetPCIDword (bn, dn, (cap_ptr + 8));
    temp &= 0xFFFFFDFF; // isolate SBA_ENABLE
    temp |= (sba << 9); // enable the SBA_ENABLE bit state
    SetPCIDword (bn, dn, (cap_ptr + 8), temp);

    return;
} // SetSBAState


void SetAGPRate (WORD device, BYTE rate)
{
    BYTE dn, bn, cap_ptr;
    DWORD temp;

    if (device == AGP_MASTER_DEVICE)
    {
        bn = R128_AdapterInfo.bus_num;
        dn = R128_AdapterInfo.dev_num;
        cap_ptr = R128_AGP_cap_ptr;
    }
    else
    {
        bn = AGP_INFO.bn;
        dn = AGP_INFO.dn;
        cap_ptr = AGP_INFO.cap_ptr;
    } // if

    temp = GetPCIDword (bn, dn, (cap_ptr + 8));

    temp &= 0xFFFFFFF8; // isolate the DATA_RATE field
    temp |= rate;

    SetPCIDword (bn, dn, (cap_ptr + 8), temp);

    return;
} // SetAGPRate


void SetRQ (WORD device, BYTE rq)
{
    BYTE dn, bn, cap_ptr;
    DWORD temp;

    if (device == AGP_MASTER_DEVICE)
    {
        bn = R128_AdapterInfo.bus_num;
        dn = R128_AdapterInfo.dev_num;
        cap_ptr = R128_AGP_cap_ptr;
    }
    else
    {
        bn = AGP_INFO.bn;
        dn = AGP_INFO.dn;
        cap_ptr = AGP_INFO.cap_ptr;
    } // if

    temp = GetPCIDword (bn, dn, (cap_ptr + 8));

    temp &= 0x00FFFFFF; // isolate the RQ_DEPTH field
    temp |= (rq << 24);

    SetPCIDword (bn, dn, (cap_ptr + 8), temp);

    return;
} // SetRQ


void GetAGPINFO (_AGP_INFO **info)
{
    *info = &AGP_INFO;
} // GetAGPINFO


BOOL AGP_OpenAperture (DWORD max_aperture_size)
{
    WORD  sizecode;
    DWORD segment, selector;
    DWORD appbase, appsize, laddr, nbytes, address, handle;
    BYTE  bn, dn;
    BYTE  byte;

    // Get AGP aperture base and size from target

    appbase = GetAGPBase (AGP_TARGET_DEVICE);

    if (appbase == 0)
    {
        return (FALSE);
    } // if

    sizecode = GetAGPSize (AGP_TARGET_DEVICE);

    switch (sizecode)
    {
        case AGP_SIZE_4MB:
            appsize = 1L << 22;
            break;

        case AGP_SIZE_8MB:
            appsize = 1L << 23;
            break;

        case AGP_SIZE_16MB:
            appsize = 1L << 24;
            break;

        case AGP_SIZE_32MB:
            appsize = 1L << 25;
            break;

        case AGP_SIZE_64MB:
            appsize = 1L << 26;
            break;

        case AGP_SIZE_128MB:
            appsize = 1L << 27;
            break;

        case AGP_SIZE_256MB:
            appsize = 1L << 28;
            break;

        default:
            return (FALSE);
    } // switch

    if (appsize > max_aperture_size)
    {
        appsize = max_aperture_size;
    } // if

    // get logical address mapping for AGP aperture

    laddr = DPMI_allocatelogicalregion (appbase, appsize);
    if (laddr == 0)
    {
        return (FALSE);
    } // if

    AGP_INFO.LogicalAddress  = laddr;
    AGP_INFO.BytePointer = (BYTE *) laddr;

    // Allocate memory for GART from DOS pool - it must be 4k aligned
    // and contigious. The maximum GART size required is 64K for a
    // 256M AGP window size.

    nbytes = appsize/1024;
    if (DPMI_allocdosmem ((DWORD) ((nbytes + 4096)/16), &segment, &selector) != 1)
    {
        DPMI_freelogicalregion (laddr);
        AGP_INFO.LogicalAddress = 0;
        return (FALSE);
    } // if

    address = (DWORD) segment << 4;
    address = align (address, 4096);
    handle  = (DWORD) selector;

    AGP_INFO.GART.handle   = handle;
    AGP_INFO.GART.laddress = address;
    AGP_INFO.GART.paddress = address;
    AGP_INFO.GART.pointer  = (DWORD *) (address);
    AGP_INFO.GART.entries  = nbytes / 4;

    // Clear gart

    memset (AGP_INFO.GART.pointer, 0, nbytes);

    // Flush P6 cache

    AGP_flush ();

    // Setup any specific bits required in the chip set

    bn = AGP_INFO.bn;
    dn = AGP_INFO.dn;

    // Check for specifc vendors

    switch (AGP_INFO.Device_ID)
    {
        case DEVICE_ID_597:
        case DEVICE_ID_598:

            // Setup gart table physical address and enable aperture

            SetPCIDword (bn, dn, 0x88, AGP_INFO.GART.paddress | 0x02);

            // Enable GTLB and translation

            byte = GetPCIByte (bn, dn, 0x80);
            SetPCIByte (bn, dn, 0x80, byte | 0x0F);
            break;

    } // switch

    return (TRUE);
} // AGP_OpenAperture


void AGP_CloseAperture (void)
{
    // TODO:  reset the target (chipset) AGP status

    DPMI_freedosmem (AGP_INFO.GART.handle);
    DPMI_freelogicalregion (AGP_INFO.LogicalAddress);

    return;
} // AGP_CloseAperture


void SetAGPBase (WORD device, DWORD base)
{
    if (device == AGP_MASTER_DEVICE)
    {
        R128_WaitForFifo (1);
        regw (AGP_BASE, base);
        return;
    }
    else
    {
        switch (AGP_INFO.Device_ID)
        {
            case DEVICE_ID_597:
            case DEVICE_ID_598:
                SetPCIDword (AGP_INFO.bn, AGP_INFO.dn, 0x10, base);
                break;

        } // switch

        AGP_INFO.ApertureBase = base;
    } // if

    return;
} // SetAGPBase


BOOL R128_InitAGP (DWORD aperture_size)
{
    AGPSTATUS ATI_AGP;
    BYTE Rate;
    int i;

    // First, ensure aperture_size is power-of-2 between 2MB and 2GB.

    for (i = 31; i > 20; i -= 1)
    {
        if (aperture_size == (1 << i)) break;
    } // for

    if (i == 20)
    {
        return (FALSE);
    } // if

    // Detect if AGP is present, and what type of chipset is involved.

    if (!R128_DetectAGP ())
    {
        return (FALSE);
    } // if

    // Get the status of both the target and master

    GetAGPStatus (AGP_TARGET_DEVICE, &(AGP_INFO.Status));
    GetAGPStatus (AGP_MASTER_DEVICE, &ATI_AGP);

    // Let's determine the common values for Rate, SBA and RQ
    // so that both can be set to the same value.

    if (((AGP_INFO.Status.Rate & AGP_RATE_2X) == AGP_RATE_2X) &&
        ((ATI_AGP.Rate & AGP_RATE_2X) == AGP_RATE_2X))
    {
        Rate = AGP_RATE_2X;
    }
    else
    {
        Rate = AGP_RATE_1X;
    } // if

    // Set up the GART table

    if (!AGP_OpenAperture (aperture_size))
    {
        // couldn't open and set up aperture, so return failure
        return (FALSE);
    } // if

    // Allocate the common AGP memory

    if (DPMI_allocatememory (aperture_size, &AGP_INFO.SystemAddress, &AGP_INFO.Handle) != 1)
    {
        return (FALSE);
    } // if

    SetupGARTTable ((AGP_INFO.SystemAddress), aperture_size);

    // Set lowest common SBA support

    if (ATI_AGP.SBA > AGP_INFO.Status.SBA)
    {
        SetSBAState (AGP_MASTER_DEVICE, AGP_INFO.Status.SBA);
        SetSBAState (AGP_TARGET_DEVICE, AGP_INFO.Status.SBA);
    }
    else
    {
        SetSBAState (AGP_MASTER_DEVICE, ATI_AGP.SBA);
        SetSBAState (AGP_TARGET_DEVICE, ATI_AGP.SBA);
    } // if

    // Set lowest common RQ support

    if (ATI_AGP.RQ > AGP_INFO.Status.RQ)
    {
        SetRQ (AGP_MASTER_DEVICE, AGP_INFO.Status.RQ);
    }
    else
    {
        SetRQ (AGP_MASTER_DEVICE, ATI_AGP.RQ);
    } // if

    // Set transfer rate

    SetAGPRate (AGP_MASTER_DEVICE, Rate);
    SetAGPRate (AGP_TARGET_DEVICE, Rate);

    // Get the target (mb chipset) aperture address

    AGP_INFO.ApertureBase = GetAGPBase (AGP_TARGET_DEVICE);

    // Set the master (ATI) AGP base address equal to the target

    SetAGPBase (AGP_MASTER_DEVICE, AGP_INFO.ApertureBase);

    // Let's set the target aperture size to 4MB.

    SetAGPSize (AGP_TARGET_DEVICE, 63);

    // Set the master (ATI) aperture size to 4MB

    SetAGPSize (AGP_MASTER_DEVICE, 63);

    // Enable AGP

    AGP_Enable (AGP_TARGET_DEVICE);
    if (!AGP_Enable (AGP_MASTER_DEVICE))
    {
        // Couldn't start AGP.  Get the hell outta Dodge.
        R128_EndAGP ();
        return (FALSE);
    } // if

    return (TRUE);

} // R128_InitAGP


void R128_EndAGP (void)
{
    if (DPMI_freememory (AGP_INFO.Handle) != 1)
    {
        printf ("Error: unable to free memory\n");
    } // if
    AGP_CloseAperture ();
} // R128_EndAGP
