/***************************************************************************************\

Module Name:    mtx_strintf.c

Description:    This module defines interface functions to access Matrox structures.

Comments:       This is simply used to allow the binary to be compatible with kernels 
                compiled with switches that modifies the ABI.  It allows the binary to
                be kernel-independant.

    Copyright (c) 2004, Matrox Graphics Inc.
    All Rights Reserved.

\***************************************************************************************/


/* -------------------------------------------------------------------------------------- *\
                          I N C L U D E S   A N D   U S I N G S
\* -------------------------------------------------------------------------------------- */

#include "mtx_drv.h"

/* -------------------------------------------------------------------------------------- *\
                          C O N S T A N T S   A N D   T Y P E S
\* -------------------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------------------- *\
                 G L O B A L   V A R I A B L E S   D E C L A R A T I O N S
\* -------------------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------------------- *\
                   L O C A L   F U N C T I O N   D E C L A R A T I O N S
\* -------------------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------------------- *\
                                         M A C R O S
\* -------------------------------------------------------------------------------------- */

/* -------------------------------------------------------------------------------------- *\
                                           C O D E
\* -------------------------------------------------------------------------------------- */

/***************************************************************************************
 * Function:       MtxRegion_Alloc
 *
 * Description:    Its job is to allocate memory for a device region with
 *                 the specified size and type.
 *
 * Parameters:     dev              MTX device
 *                 size             amount of memory desired
 *                 type             type of memory
 *                 reg              if not NULL, init this region instead of allocating 
 *                                  a new one.
 *
 * Return Value:   a MTX memory region, NULL if failed
 *
 * Comments:       Caller is responsible of incrementing accordingly the region 
 *                 reference count. It is initialized 0, and must be 0 when
 *                 calling MtxRegion_Free.
 */
STACK_LINKAGE mtx_region_t* MtxRegion_Alloc(mtx_device_t *dev, size_t size, mtx_memory_type type, mtx_memory_scope scope, mtx_region_t* reg) 
{
    mtx_region_t *region;
    
    if (!size)
    {
        MTX_ERROR("Allocating a zero sized memory region (type: %u))\n", type);
        return NULL;
    }

    if (!reg) { 
     
        /* Allocate a new region */
        region = MTXALLOC(sizeof(mtx_region_t));
        if (!region)
            return NULL;

        region->flags = MTX_REGION_DYNAMIC;
        
    } else {
        
        /* Init a already existing region */
        region = reg;
        region->flags = 0;
    }
    
    /* init fields to defaults */
    region->scope     = scope;
    region->io_flags  = 0;
    region->type      = type;
    region->size      = size;
    region->mtrr      = -1;
    region->pid       = -1;
    atomic_set(&region->ref_count, 0);

    switch (region->type) {

        case MTX_MEMTYPE_PCI_LOCKED:
                
            /* Allocate shared region in locked memory */
            region->kaddr = mtx_mem_alloc_pci(region->size);
            if (!region->kaddr) goto FAILED;

            /* Get physical and bus address */
            region->base    = __pa(region->kaddr);
            region->busaddr = virt_to_bus(region->kaddr);
            break;

        case MTX_MEMTYPE_SYS:

            /* Allocate standard system to this region 
             * (size must be page-aligned to allow file mapping) */        
            region->kaddr = ClientMemAlloc((region->size + (PAGE_SIZE - 1)) & PAGE_MASK);
            if (!region->kaddr) goto FAILED;
            
            /* Get physical and bus address */
            region->base    = __pa(region->kaddr);
            region->busaddr = 0; /* no PCI accesses for a system region */
            break;

        case MTX_MEMTYPE_AGP:

            /* Allocate AGP memory to this region */
            region->base = mtx_mem_alloc_agp(region->size);
            if (!region->base) goto FAILED;
            
            /* invalidate virtual addresses */
            region->kaddr   = NULL;
            region->busaddr = region->base;
            break;

        default:
            goto FAILED;
    }

    region->flags |= MTX_REGION_ALLOCATED;
    region->real_size = last_alloc_size;

    ADD_REGION_TO_LIST(dev, region);
    
    MTX_DEBUG("Created device buffer at 0x%08lx (size: %lu, type: %u)\n", 
              region->base, region->size, region->type);

    return region;

FAILED:

    MTX_ERROR("Failed to create device buffer (size: %u, type: %u)\n", size, type);
    MtxRegion_Free(dev, region);

    return NULL;
}

/***************************************************************************************
 * Function:       MtxRegion_Free
 *
 * Description:    This free memory allocated for a region.
 *
 * Parameters:     dev              MTX device
 *                 region           Region to free
 *
 * Return Value:   None.
 *
 * Comments:
 */
STACK_LINKAGE void MtxRegion_Free(mtx_device_t *dev, mtx_region_t *region)
{
    /* Sanity check */
    if (atomic_read(&region->ref_count) != 0) {
        
        MTX_DEBUG("WARNING: Freeing a buffer still in use (addr:0x%08lx, size: %lu, type: %u, ref: %i))\n", 
                  region->base, region->size, region->type, atomic_read(&region->ref_count));
    }

    if (region->mtrr >= 0) {
      #if defined(__i386__)
        /* release memory range */
        mtrr_del(region->mtrr, region->base, region->size);
      #endif
    }

    if (region->flags & MTX_REGION_ALLOCATED) {
        
        MTX_DEBUG("Freeing device buffer at 0x%08lx (size: %lu, type: %u)\n", 
                  region->base, region->size, region->type);

        DEL_REGION_FROM_LIST(dev, region);
        
        switch (region->type) {

            case MTX_MEMTYPE_PCI_LOCKED:

                /* Free pci locked memory */
                mtx_mem_free_pci(region->kaddr, region->size);
                break;

            case MTX_MEMTYPE_AGP:

                /* Free agp memory */
                if (region->kaddr)
                    ClientIoUnmap(region->kaddr);

                mtx_mem_free_agp(region->base, region->size);
                break;

            case MTX_MEMTYPE_SYS:

                /* Free kernel memory */
                ClientMemFree(region->kaddr, (region->size + (PAGE_SIZE - 1)) & PAGE_MASK);
                break;

            case MTX_MEMTYPE_IO:

                /* unmap IO memory region */
                if (region->kaddr)
                    ClientIoUnmap(region->kaddr);
                break;

            default:
                break;
        }
    }
   
    if (region->flags & MTX_REGION_DYNAMIC) {

        /* Free region memory that has been allocated dynamically */
        MTXFREE(region, sizeof(mtx_region_t));

    } else {

        region->kaddr = NULL;
        region->base = 0L;
        region->size = 0L;
        region->pid = -1;
        region->flags &= ~MTX_REGION_ALLOCATED;
    }
    
    return;
}

/* 
 * (mtx_find_region_from_base)
 *
 * find a region in a list of regions by matching
 * its base address
 */
STACK_LINKAGE mtx_region_t* MtxRegion_FindFromBase(mtx_device_t* dev, PHYSADDR base)
{
    struct list_head* pos;
    mtx_region_t *region;

    list_for_each(pos, &dev->memory_regions) {
        region = list_entry(pos, mtx_region_t, list_node);
        if (region && (region->base == base))
            return region;
    }

    return NULL;
}

/* 
 * (mtx_find_region_from_virt)
 *
 * find a region in a list of regions by matching
 * its virtual address
 */
STACK_LINKAGE mtx_region_t* MtxRegion_FindFromVirtual(mtx_device_t* dev, void* kaddr)
{
    struct list_head* pos;
    mtx_region_t *region;

    list_for_each(pos, &dev->memory_regions) {
        region = list_entry(pos, mtx_region_t, list_node);
        if (region && (region->kaddr == kaddr))
            return region;
    }

    return NULL;
}


STACK_LINKAGE MEMHANDLE MtxRegion_GetLinear( HREGION poRegion )
{
    return poRegion->kaddr;
}

STACK_LINKAGE UINT32 MtxRegion_GetPhys( HREGION poRegion )
{
    return poRegion->base;
}

STACK_LINKAGE UINT32 MtxRegion_GetSize( HREGION poRegion )
{
    return poRegion->size;
}

STACK_LINKAGE void MtxRegion_IncrementRefCount( HREGION poRegion )
{
    atomic_inc( &poRegion->ref_count );
}

STACK_LINKAGE void MtxRegion_DecrementRefCount( HREGION poRegion )
{
    atomic_dec( &poRegion->ref_count );
}

STACK_LINKAGE int MtxRegion_DecrementRefCountAndTest( HREGION poRegion )
{
    return atomic_dec_and_test( &poRegion->ref_count );
}

STACK_LINKAGE PCIHANDLE MtxPciDevice_GetHostHandle( void )
{
    return (PCIHANDLE) pci_host_dev;
}

STACK_LINKAGE PCIHANDLE MtxDevice_GetPciHandle( HDEVICE poDevice)
{
    return (PCIHANDLE) poDevice->pci_dev;
}

STACK_LINKAGE HREGION MtxDevice_GetRegisters( HDEVICE poDevice )
{
    return &poDevice->registers;
}

STACK_LINKAGE HREGION MtxDevice_GetFrameBuffer( HDEVICE poDevice )
{
    return &poDevice->framebuffer;
}

STACK_LINKAGE UINT32 MtxDevice_GetBmFlags( HDEVICE poDevice )
{
    return poDevice->bm_flags;
}

STACK_LINKAGE MTXHANDLE MtxDevice_GetPrivate( HDEVICE poDevice )
{
    return poDevice->core_data;
}

STACK_LINKAGE void MtxIoctl_Register(UINT32 iIoControlNumber, CORE_IOCTL pfnCallback, LBOOL bCoreIoControl, LBOOL bRestritedAccess, LBOOL bSuperUserOnly)
{
   MTX_ADD_IOCTL(iIoControlNumber, pfnCallback, bCoreIoControl, bRestritedAccess, bSuperUserOnly);
}
