/*****************************************************************************
 * GART.C - Functions to write the GART table.                               *
 *                                                                           *
 * Copyright (c) 1999, ATI Technologies Inc.  All Rights Reserved.           *
 *****************************************************************************/

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

static GART_INFO PCI_GART_Info;

extern DWORD GetPhysical (DWORD);

/******************************************************************************
 *  AGP_SETUPGARTTABLE - setup GART table for 3D engine use                   *
 *                                                                            *
 *  Inputs:                                                                   *
 *                                                                            *
 *      memory_address - logical memory address (align to 4K bytes)           *
 *                                                                            *
 *      memory_size    - size of memory region                                *
 *                                                                            *
 *  Outputs:                                                                  *
 *                                                                            *
 *      PGL_TRUE  - successful                                                *
 *      PGL_FALSE - errors found                                              *
 *                                                                            *
 *  This function takes a given allocated region of memory (extended only)    *
 *  and sets up the GART table so that it is contigious for the 3D engine     *
 *  to use. Each table entry corresponds to a 4K block of system memory.      *
 *  Therefore, the memory address should be aligned to 4K bytes.              *
 *                                                                            *
 *  This function will return and do nothing if:                              *
 *                                                                            *
 *      - gart table is not allocated                                         *
 *      - memory size is larger than the allocated gart table entries         *
 *      - memory size is zero                                                 *
 *      - memory address is zero                                              *
 *                                                                            *
 *  The GART table must be correctly allocated before using this function.    *
 *  See AGP_openaperture().                                                   *
 ******************************************************************************/

BOOL SetupGARTTable (DWORD memory_address, DWORD memory_size)
{
    DWORD i;
    DWORD entries;
    DWORD temp;
    _AGP_INFO *AGP_INFO;

    GetAGPINFO (&AGP_INFO);

    // abort if table is not allocated

    if ((AGP_INFO->LogicalAddress == 0) || (memory_address == 0) || (memory_size == 0))
    {
        return (FALSE);
    } // if

    // determine # of gart entries required

    entries = memory_size/4096;
    if (entries == 0)
    {
        entries++;
    } // if

    // abort if too many entries

    if (entries > AGP_INFO->GART.entries)
    {
        return (FALSE);
    } // if

    // setup initial list of addresses

    for (i = 0; i < entries; i++)
    {
        // The GetPhysical() function is affected by the timer interrupt.
        // Disabling the interrupts is not sufficient to insure a correct
        // conversion. To overcome this, the function is called right after
        // the timer tick changes, thus avoiding a timer interrupt. On a P90
        // and faster systems, 128 conversions can be safely done within a
        // 55msec timer tick period.

        if ((i % 128) == 0)
        {
            R128_Delay (1);
        } // if
        AGP_INFO->GART.pointer[i] = GetPhysical (memory_address + (i*4096)) | 1;
    } // for

    // write zero to unused entries

    for (i = entries; i < AGP_INFO->GART.entries; i++)
    {
        AGP_INFO->GART.pointer[i] = 0;
    } // for
    temp = AGP_INFO->GART.pointer[0] & 0xFFFFFFFE;

    // flush P6 cache
    AGP_flush ();
    
    // check if first table address is zero - if so, error

    return ((AGP_INFO->GART.pointer[0] & 0xFFFFFFFE) != 0);
} // SetupGARTTable


/******************************************************************************
 * SetupPCIGARTTable                                                          *
 *  Function: This function sets up a PCI GART table for bus mastering.       *
 *    Inputs: Aperture size                                                   *
 *   Outputs: Boolean.                                                        *
 ******************************************************************************/

GART_INFO *SetupPCIGARTTable (DWORD aperture_size)
{
    DWORD i;
    DWORD entries;
    DWORD segment, selector;
    DWORD address, memory_address, memory_handle;
    DWORD *ptr;
    DWORD temp;

    // abort if table is not allocated

    if (aperture_size == 0)
    {
        return (NULL);
    } // if

    // determine # of gart entries required

    entries = aperture_size / 4096;
    if (entries == 0)
    {
        entries++;
    } // if

    // abort if too many entries

    if (entries > 8192)
    {
        return (NULL);
    } // if

    // Allocate memory for GART from DOS pool - it must be 4k aligned and
    // contiguous. Since the GART size required is 32K for a 32M window,
    // allocate 36K (32K + 4K for alignment) or 2304 paragraphs.

    if (DPMI_allocdosmem (2304, &segment, &selector) != 1)
    {
        return (NULL);
    } // if

    address = align ((segment << 4), 4096);

    // Since the DOS/4GW DPMI server maps the first megabyte of physical memory
    // to the first megabyte of linearly addressable memory (for easier access
    // to DOS memory) the following pointer cast is valid.

    ptr = (DWORD *) address;

    if (DPMI_allocatememory (aperture_size, &memory_address, &memory_handle) != 1)
    {
        DPMI_freedosmem (selector);
        return (NULL);
    } // if

    PCI_GART_Info.handle = selector;
    PCI_GART_Info.mem_handle = memory_handle;
    PCI_GART_Info.laddress = memory_address;
    PCI_GART_Info.paddress = address;
    PCI_GART_Info.pointer = (DWORD *) memory_address;
    PCI_GART_Info.entries = entries;

    // setup initial list of addresses

    for (i = 0; i < entries; i++)
    {
        // The GetPhysical() function is affected by the timer interrupt.
        // Disabling the interrupts is not sufficient to insure a correct
        // conversion. To overcome this, the function is called right after
        // the timer tick changes, thus avoiding a timer interrupt. On a P90
        // and faster systems, 128 conversions can be safely done within a
        // 55msec timer tick period.

        if ((i % 128) == 0)
        {
            R128_Delay (1);
        } // if

        temp = GetPhysical (memory_address + (i*4096));
        ptr[i] = temp;
    } // for

    // write zero to unused entries

    for (i = entries; i < 8192; i++)
    {
        ptr[i] = 0;
    } // for

    // return gart table address to caller (should be nonzero by this point)

    return (&PCI_GART_Info);
} // SetupPCIGARTTable
