/*******************************************************************************
**
**  gckKERNEL_Destroy
**
**  Destroy an gckKERNEL object.
**
**  INPUT:
**
**      gckKERNEL Kernel
**          Pointer to an gckKERNEL object to destroy.
**
**  OUTPUT:
**
**      Nothing.
*/
gceSTATUS gckVGKERNEL_Destroy(
    IN gckVGKERNEL Kernel
    )
{
    gceSTATUS status;

    gcmkHEADER_ARG("Kernel=0x%x", Kernel);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);

    do
    {
        /* Destroy the gckVGMMU object. */
        if (Kernel->mmu != gcvNULL)
        {
            gcmkERR_BREAK(gckVGMMU_Destroy(Kernel->mmu));
            Kernel->mmu = gcvNULL;
        }

        /* Destroy the gckVGCOMMAND object. */
        if (Kernel->command != gcvNULL)
        {
            gcmkERR_BREAK(gckVGCOMMAND_Destroy(Kernel->command));
            Kernel->command = gcvNULL;
        }

        /* Destroy the gckVGINTERRUPT object. */
        if (Kernel->interrupt != gcvNULL)
        {
            gcmkERR_BREAK(gckVGINTERRUPT_Destroy(Kernel->interrupt));
            Kernel->interrupt = gcvNULL;
        }

        /* Destroy the gckVGHARDWARE object. */
        if (Kernel->hardware != gcvNULL)
        {
            gcmkERR_BREAK(gckVGHARDWARE_Destroy(Kernel->hardware));
            Kernel->hardware = gcvNULL;
        }

        /* Mark the gckKERNEL object as unknown. */
        Kernel->object.type = gcvOBJ_UNKNOWN;

        /* Free the gckKERNEL object. */
        gcmkERR_BREAK(gckOS_Free(Kernel->os, Kernel));
    }
    while (gcvFALSE);

    gcmkFOOTER();

    /* Return status. */
    return status;
}
/*******************************************************************************
**
**  gckKERNEL_Construct
**
**  Construct a new gckKERNEL object.
**
**  INPUT:
**
**      gckOS Os
**          Pointer to an gckOS object.
**
**      IN gctPOINTER Context
**          Pointer to a driver defined context.
**
**  OUTPUT:
**
**      gckKERNEL * Kernel
**          Pointer to a variable that will hold the pointer to the gckKERNEL
**          object.
*/
gceSTATUS gckVGKERNEL_Construct(
    IN gckOS Os,
    IN gctPOINTER Context,
    IN gckKERNEL  inKernel,
    OUT gckVGKERNEL * Kernel
    )
{
    gceSTATUS status;
    gckVGKERNEL kernel = gcvNULL;

    gcmkHEADER_ARG("Os=0x%x Context=0x%x", Os, Context);
    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
    gcmkVERIFY_ARGUMENT(Kernel != gcvNULL);

    do
    {
        /* Allocate the gckKERNEL object. */
        gcmkERR_BREAK(gckOS_Allocate(
            Os,
            sizeof(struct _gckVGKERNEL),
            (gctPOINTER *) &kernel
            ));

        /* Initialize the gckKERNEL object. */
        kernel->object.type = gcvOBJ_KERNEL;
        kernel->os          = Os;
        kernel->context     = Context;
        kernel->hardware    = gcvNULL;
        kernel->interrupt   = gcvNULL;
        kernel->command     = gcvNULL;
        kernel->mmu         = gcvNULL;
        kernel->kernel      = inKernel;

        /* Construct the gckVGHARDWARE object. */
        gcmkERR_BREAK(gckVGHARDWARE_Construct(
            Os, &kernel->hardware
            ));

        /* Set pointer to gckKERNEL object in gckVGHARDWARE object. */
        kernel->hardware->kernel = kernel;

        /* Construct the gckVGINTERRUPT object. */
        gcmkERR_BREAK(gckVGINTERRUPT_Construct(
            kernel, &kernel->interrupt
            ));

        /* Construct the gckVGCOMMAND object. */
        gcmkERR_BREAK(gckVGCOMMAND_Construct(
            kernel, gcmKB2BYTES(8), gcmKB2BYTES(2), &kernel->command
            ));

        /* Construct the gckVGMMU object. */
        gcmkERR_BREAK(gckVGMMU_Construct(
            kernel, gcmKB2BYTES(32), &kernel->mmu
            ));

        /* Return pointer to the gckKERNEL object. */
        *Kernel = kernel;

        gcmkFOOTER_ARG("*Kernel=0x%x", *Kernel);
        /* Success. */
        return gcvSTATUS_OK;
    }
    while (gcvFALSE);

    /* Roll back. */
    if (kernel != gcvNULL)
    {
        if (kernel->mmu != gcvNULL)
        {
            gcmkVERIFY_OK(gckVGMMU_Destroy(kernel->mmu));
        }

        if (kernel->command != gcvNULL)
        {
            gcmkVERIFY_OK(gckVGCOMMAND_Destroy(kernel->command));
        }

        if (kernel->interrupt != gcvNULL)
        {
            gcmkVERIFY_OK(gckVGINTERRUPT_Destroy(kernel->interrupt));
        }

        if (kernel->hardware != gcvNULL)
        {
            gcmkVERIFY_OK(gckVGHARDWARE_Destroy(kernel->hardware));
        }

        gcmkVERIFY_OK(gckOS_Free(Os, kernel));
    }

    gcmkFOOTER();
    /* Return status. */
    return status;
}
/*******************************************************************************
**
**  gckKERNEL_Dispatch
**
**  Dispatch a command received from the user HAL layer.
**
**  INPUT:
**
**      gckKERNEL Kernel
**          Pointer to an gckKERNEL object.
**
**      gcsHAL_INTERFACE * Interface
**          Pointer to a gcsHAL_INTERFACE structure that defines the command to
**          be dispatched.
**
**  OUTPUT:
**
**      gcsHAL_INTERFACE * Interface
**          Pointer to a gcsHAL_INTERFACE structure that receives any data to be
**          returned.
*/
gceSTATUS gckVGKERNEL_Dispatch(
    IN gckKERNEL Kernel,
    IN gctBOOL FromUser,
    IN OUT gcsHAL_INTERFACE * Interface
    )
{
    gceSTATUS status;
    gcsHAL_INTERFACE * kernelInterface = Interface;
    gcuVIDMEM_NODE_PTR node;
    gctUINT32 processID;

    gcmkHEADER_ARG("Kernel=0x%x Interface=0x%x ", Kernel, Interface);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
    gcmkVERIFY_ARGUMENT(Interface != gcvNULL);

    gcmkONERROR(gckOS_GetProcessID(&processID));

    /* Dispatch on command. */
    switch (Interface->command)
    {
    case gcvHAL_QUERY_VIDEO_MEMORY:
        /* Query video memory size. */
        gcmkERR_BREAK(gckKERNEL_QueryVideoMemory(
            Kernel, kernelInterface
            ));
        break;

    case gcvHAL_QUERY_CHIP_IDENTITY:
        /* Query chip identity. */
        gcmkERR_BREAK(gckVGHARDWARE_QueryChipIdentity(
            Kernel->vg->hardware,
            &kernelInterface->u.QueryChipIdentity.chipModel,
            &kernelInterface->u.QueryChipIdentity.chipRevision,
            &kernelInterface->u.QueryChipIdentity.chipFeatures,
            &kernelInterface->u.QueryChipIdentity.chipMinorFeatures,
            &kernelInterface->u.QueryChipIdentity.chipMinorFeatures2
            ));
        break;

    case gcvHAL_QUERY_COMMAND_BUFFER:
        /* Query command buffer information. */
        gcmkERR_BREAK(gckKERNEL_QueryCommandBuffer(
            Kernel,
            &kernelInterface->u.QueryCommandBuffer.information
            ));
        break;
    case gcvHAL_ALLOCATE_NON_PAGED_MEMORY:
        /* Allocate non-paged memory. */
        gcmkERR_BREAK(gckOS_AllocateContiguous(
            Kernel->os,
            gcvTRUE,
            &kernelInterface->u.AllocateNonPagedMemory.bytes,
            &kernelInterface->u.AllocateNonPagedMemory.physical,
            &kernelInterface->u.AllocateNonPagedMemory.logical
            ));
        break;

    case gcvHAL_FREE_NON_PAGED_MEMORY:
        /* Unmap user logical out of physical memory first. */
        gcmkERR_BREAK(gckOS_UnmapUserLogical(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.bytes,
            kernelInterface->u.AllocateNonPagedMemory.logical
            ));

        /* Free non-paged memory. */
        gcmkERR_BREAK(gckOS_FreeNonPagedMemory(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.bytes,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.logical
            ));
        break;

    case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY:
        /* Allocate contiguous memory. */
        gcmkERR_BREAK(gckOS_AllocateContiguous(
            Kernel->os,
            gcvTRUE,
            &kernelInterface->u.AllocateNonPagedMemory.bytes,
            &kernelInterface->u.AllocateNonPagedMemory.physical,
            &kernelInterface->u.AllocateNonPagedMemory.logical
            ));
        break;

    case gcvHAL_FREE_CONTIGUOUS_MEMORY:
        /* Unmap user logical out of physical memory first. */
        gcmkERR_BREAK(gckOS_UnmapUserLogical(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.bytes,
            kernelInterface->u.AllocateNonPagedMemory.logical
            ));

        /* Free contiguous memory. */
        gcmkERR_BREAK(gckOS_FreeContiguous(
            Kernel->os,
            kernelInterface->u.AllocateNonPagedMemory.physical,
            kernelInterface->u.AllocateNonPagedMemory.logical,
            kernelInterface->u.AllocateNonPagedMemory.bytes
            ));
        break;

    case gcvHAL_ALLOCATE_VIDEO_MEMORY:
        {
            gctSIZE_T bytes;
            gctUINT32 bitsPerPixel;
            gctUINT32 bits;

            /* Align width and height to tiles. */
            gcmkERR_BREAK(gckVGHARDWARE_AlignToTile(
                Kernel->vg->hardware,
                kernelInterface->u.AllocateVideoMemory.type,
                &kernelInterface->u.AllocateVideoMemory.width,
                &kernelInterface->u.AllocateVideoMemory.height
                ));

            /* Convert format into bytes per pixel and bytes per tile. */
            gcmkERR_BREAK(gckVGHARDWARE_ConvertFormat(
                Kernel->vg->hardware,
                kernelInterface->u.AllocateVideoMemory.format,
                &bitsPerPixel,
                gcvNULL
                ));

            /* Compute number of bits for the allocation. */
            bits
                = kernelInterface->u.AllocateVideoMemory.width
                * kernelInterface->u.AllocateVideoMemory.height
                * kernelInterface->u.AllocateVideoMemory.depth
                * bitsPerPixel;

            /* Compute number of bytes for the allocation. */
            bytes = gcmALIGN(bits, 8) / 8;

            /* Allocate memory. */
            gcmkERR_BREAK(gckKERNEL_AllocateLinearMemory(
                Kernel,
                &kernelInterface->u.AllocateVideoMemory.pool,
                bytes,
                64,
                kernelInterface->u.AllocateVideoMemory.type,
                &kernelInterface->u.AllocateVideoMemory.node
                ));
        }
        break;

    case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY:
        /* Allocate memory. */
        gcmkERR_BREAK(gckKERNEL_AllocateLinearMemory(
            Kernel,
            &kernelInterface->u.AllocateLinearVideoMemory.pool,
            kernelInterface->u.AllocateLinearVideoMemory.bytes,
            kernelInterface->u.AllocateLinearVideoMemory.alignment,
            kernelInterface->u.AllocateLinearVideoMemory.type,
            &kernelInterface->u.AllocateLinearVideoMemory.node
            ));

        gcmkERR_BREAK(gckKERNEL_AddProcessDB(Kernel,
           processID, gcvDB_VIDEO_MEMORY,
           Interface->u.AllocateLinearVideoMemory.node,
           gcvNULL,
           kernelInterface->u.AllocateLinearVideoMemory.bytes
           ));

        break;

    case gcvHAL_FREE_VIDEO_MEMORY:
#ifdef __QNXNTO__
        /* Unmap the video memory */
        node = Interface->u.FreeVideoMemory.node;

        if ((node->VidMem.memory->object.type == gcvOBJ_VIDMEM) &&
            (node->VidMem.logical != gcvNULL))
        {
            gckKERNEL_UnmapVideoMemory(Kernel,
                                       node->VidMem.logical,
                                       processID,
                                       node->VidMem.bytes);
            node->VidMem.logical = gcvNULL;
        }
#endif /* __QNXNTO__ */

        /* Free video memory. */
        gcmkERR_BREAK(gckVIDMEM_Free(
            Interface->u.FreeVideoMemory.node
            ));

        gcmkERR_BREAK(gckKERNEL_RemoveProcessDB(
            Kernel,
            processID, gcvDB_VIDEO_MEMORY,
            Interface->u.FreeVideoMemory.node
            ));

        break;

    case gcvHAL_MAP_MEMORY:
        /* Map memory. */
        gcmkERR_BREAK(gckKERNEL_MapMemory(
            Kernel,
            kernelInterface->u.MapMemory.physical,
            kernelInterface->u.MapMemory.bytes,
            &kernelInterface->u.MapMemory.logical
            ));
        break;

    case gcvHAL_UNMAP_MEMORY:
        /* Unmap memory. */
        gcmkERR_BREAK(gckKERNEL_UnmapMemory(
            Kernel,
            kernelInterface->u.MapMemory.physical,
            kernelInterface->u.MapMemory.bytes,
            kernelInterface->u.MapMemory.logical
            ));
        break;

    case gcvHAL_MAP_USER_MEMORY:
        /* Map user memory to DMA. */
        gcmkERR_BREAK(gckOS_MapUserMemory(
            Kernel->os,
            gcvCORE_VG,
            kernelInterface->u.MapUserMemory.memory,
            kernelInterface->u.MapUserMemory.physical,
            kernelInterface->u.MapUserMemory.size,
            &kernelInterface->u.MapUserMemory.info,
            &kernelInterface->u.MapUserMemory.address
            ));
        break;

    case gcvHAL_UNMAP_USER_MEMORY:
        /* Unmap user memory. */
        gcmkERR_BREAK(gckOS_UnmapUserMemory(
            Kernel->os,
            gcvCORE_VG,
            kernelInterface->u.UnmapUserMemory.memory,
            kernelInterface->u.UnmapUserMemory.size,
            kernelInterface->u.UnmapUserMemory.info,
            kernelInterface->u.UnmapUserMemory.address
            ));
        break;
    case gcvHAL_LOCK_VIDEO_MEMORY:
        /* Lock video memory. */
        gcmkERR_BREAK(
            gckVIDMEM_Lock(Kernel,
                           Interface->u.LockVideoMemory.node,
                           gcvFALSE,
                           &Interface->u.LockVideoMemory.address));

        node = Interface->u.LockVideoMemory.node;
        if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
        {
            /* Map video memory address into user space. */
#ifdef __QNXNTO__
        if (node->VidMem.logical == gcvNULL)
        {
            gcmkONERROR(
                gckKERNEL_MapVideoMemory(Kernel,
                                         FromUser,
                                         Interface->u.LockVideoMemory.address,
                                         processID,
                                         node->VidMem.bytes,
                                         &node->VidMem.logical));
        }

        Interface->u.LockVideoMemory.memory = node->VidMem.logical;
#else
            gcmkERR_BREAK(
                gckKERNEL_MapVideoMemoryEx(Kernel,
                                         gcvCORE_VG,
                                         FromUser,
                                         Interface->u.LockVideoMemory.address,
                                         &Interface->u.LockVideoMemory.memory));
#endif
        }
        else
        {
            Interface->u.LockVideoMemory.memory = node->Virtual.logical;

            /* Success. */
            status = gcvSTATUS_OK;
        }

#if gcdSECURE_USER
        /* Return logical address as physical address. */
        Interface->u.LockVideoMemory.address =
            gcmPTR2INT(Interface->u.LockVideoMemory.memory);
#endif
        gcmkERR_BREAK(
            gckKERNEL_AddProcessDB(Kernel,
                                   processID, gcvDB_VIDEO_MEMORY_LOCKED,
                                   Interface->u.LockVideoMemory.node,
                                   gcvNULL,
                                   0));
        break;

    case gcvHAL_UNLOCK_VIDEO_MEMORY:
        /* Unlock video memory. */
        node = Interface->u.UnlockVideoMemory.node;

#if gcdSECURE_USER
        /* Save node information before it disappears. */
        if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
        {
            logical = gcvNULL;
            bytes   = 0;
        }
        else
        {
            logical = node->Virtual.logical;
            bytes   = node->Virtual.bytes;
        }
#endif

        /* Unlock video memory. */
        gcmkERR_BREAK(
            gckVIDMEM_Unlock(Kernel,
                             node,
                             Interface->u.UnlockVideoMemory.type,
                             &Interface->u.UnlockVideoMemory.asynchroneous,
                             gcvFALSE));

#if gcdSECURE_USER
        /* Flush the translation cache for virtual surfaces. */
        if (logical != gcvNULL)
        {
            gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(Kernel,
                                                          cache,
                                                          logical,
                                                          bytes));
        }
#endif

        if (Interface->u.UnlockVideoMemory.asynchroneous == gcvFALSE)
        {
            /* There isn't a event to unlock this node, remove record now */
            gcmkERR_BREAK(
                    gckKERNEL_RemoveProcessDB(Kernel,
                        processID, gcvDB_VIDEO_MEMORY_LOCKED,
                        Interface->u.UnlockVideoMemory.node));
        }

        break;
    case gcvHAL_USER_SIGNAL:
#if !USE_NEW_LINUX_SIGNAL
        /* Dispatch depends on the user signal subcommands. */
        switch(Interface->u.UserSignal.command)
        {
        case gcvUSER_SIGNAL_CREATE:
            /* Create a signal used in the user space. */
            gcmkERR_BREAK(
                gckOS_CreateUserSignal(Kernel->os,
                                       Interface->u.UserSignal.manualReset,
                                       &Interface->u.UserSignal.id));

            gcmkVERIFY_OK(
                gckKERNEL_AddProcessDB(Kernel,
                                       processID, gcvDB_SIGNAL,
                                       gcmINT2PTR(Interface->u.UserSignal.id),
                                       gcvNULL,
                                       0));
            break;

        case gcvUSER_SIGNAL_DESTROY:
            /* Destroy the signal. */
            gcmkERR_BREAK(
                gckOS_DestroyUserSignal(Kernel->os,
                                        Interface->u.UserSignal.id));

            gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
                Kernel,
                processID, gcvDB_SIGNAL,
                gcmINT2PTR(Interface->u.UserSignal.id)));
            break;

        case gcvUSER_SIGNAL_SIGNAL:
            /* Signal the signal. */
            gcmkERR_BREAK(
                gckOS_SignalUserSignal(Kernel->os,
                                       Interface->u.UserSignal.id,
                                       Interface->u.UserSignal.state));
            break;

        case gcvUSER_SIGNAL_WAIT:
            /* Wait on the signal. */
            status = gckOS_WaitUserSignal(Kernel->os,
                                          Interface->u.UserSignal.id,
                                          Interface->u.UserSignal.wait);
            break;

        default:
            /* Invalid user signal command. */
            gcmkERR_BREAK(gcvSTATUS_INVALID_ARGUMENT);
        }
#endif
        break;

    case gcvHAL_COMMIT:
        /* Commit a command and context buffer. */
        gcmkERR_BREAK(gckVGCOMMAND_Commit(
            Kernel->vg->command,
            kernelInterface->u.VGCommit.context,
            kernelInterface->u.VGCommit.queue,
            kernelInterface->u.VGCommit.entryCount,
            kernelInterface->u.VGCommit.taskTable
            ));
        break;
    case gcvHAL_VERSION:
        kernelInterface->u.Version.major = gcvVERSION_MAJOR;
        kernelInterface->u.Version.minor = gcvVERSION_MINOR;
        kernelInterface->u.Version.patch = gcvVERSION_PATCH;
        kernelInterface->u.Version.build = gcvVERSION_BUILD;
        status = gcvSTATUS_OK;
        break;

    case gcvHAL_GET_BASE_ADDRESS:
        /* Get base address. */
        gcmkERR_BREAK(
            gckOS_GetBaseAddress(Kernel->os,
                                 &kernelInterface->u.GetBaseAddress.baseAddress));
        break;
    default:
        /* Invalid command. */
        status = gcvSTATUS_INVALID_ARGUMENT;
    }

OnError:
    /* Save status. */
    kernelInterface->status = status;

    gcmkFOOTER();

    /* Return the status. */
    return status;
}
/*******************************************************************************
**
**  gckKERNEL_AllocateLinearMemory
**
**  Function walks all required memory pools and allocates the requested
**  amount of video memory.
**
**  INPUT:
**
**      gckKERNEL Kernel
**          Pointer to an gckKERNEL object.
**
**      gcePOOL * Pool
**          Pointer the desired memory pool.
**
**      gctSIZE_T Bytes
**          Number of bytes to allocate.
**
**      gctSIZE_T Alignment
**          Required buffer alignment.
**
**      gceSURF_TYPE Type
**          Surface type.
**
**  OUTPUT:
**
**      gcePOOL * Pool
**          Pointer to the actual pool where the memory was allocated.
**
**      gcuVIDMEM_NODE_PTR * Node
**          Allocated node.
*/
gceSTATUS
gckKERNEL_AllocateLinearMemory(
    IN gckKERNEL Kernel,
    IN OUT gcePOOL * Pool,
    IN gctSIZE_T Bytes,
    IN gctSIZE_T Alignment,
    IN gceSURF_TYPE Type,
    OUT gcuVIDMEM_NODE_PTR * Node
    )
{
    gcePOOL pool;
    gceSTATUS status;
    gckVIDMEM videoMemory;

    /* Get initial pool. */
    switch (pool = *Pool)
    {
    case gcvPOOL_DEFAULT:
    case gcvPOOL_LOCAL:
        pool = gcvPOOL_LOCAL_INTERNAL;
        break;

    case gcvPOOL_UNIFIED:
        pool = gcvPOOL_SYSTEM;
        break;

    default:
        break;
    }

    do
    {
        /* Verify the number of bytes to allocate. */
        if (Bytes == 0)
        {
            status = gcvSTATUS_INVALID_ARGUMENT;
            break;
        }

        if (pool == gcvPOOL_VIRTUAL)
        {
            /* Create a gcuVIDMEM_NODE for virtual memory. */
            gcmkERR_BREAK(gckVIDMEM_ConstructVirtual(Kernel, gcvFALSE, Bytes, Node));

            /* Success. */
            break;
        }

        else
        {
            /* Get pointer to gckVIDMEM object for pool. */
            status = gckKERNEL_GetVideoMemoryPool(Kernel, pool, &videoMemory);

            if (status == gcvSTATUS_OK)
            {
                /* Allocate memory. */
                status = gckVIDMEM_AllocateLinear(Kernel,
                                                  videoMemory,
                                                  Bytes,
                                                  Alignment,
                                                  Type,
                                                  Node);

                if (status == gcvSTATUS_OK)
                {
                    /* Memory allocated. */
                    break;
                }
            }
        }

        if (pool == gcvPOOL_LOCAL_INTERNAL)
        {
            /* Advance to external memory. */
            pool = gcvPOOL_LOCAL_EXTERNAL;
        }
        else if (pool == gcvPOOL_LOCAL_EXTERNAL)
        {
            /* Advance to contiguous system memory. */
            pool = gcvPOOL_SYSTEM;
        }
        else if (pool == gcvPOOL_SYSTEM)
        {
            /* Advance to virtual memory. */
            pool = gcvPOOL_VIRTUAL;
        }
        else
        {
            /* Out of pools. */
            break;
        }
    }
    /* Loop only for multiple selection pools. */
    while ((*Pool == gcvPOOL_DEFAULT)
    ||     (*Pool == gcvPOOL_LOCAL)
    ||     (*Pool == gcvPOOL_UNIFIED)
    );

    if (gcmIS_SUCCESS(status))
    {
        /* Return pool used for allocation. */
        *Pool = pool;
    }

    /* Return status. */
    return status;
}
/*******************************************************************************
**
**	_AllocateMemory
**
**	Private function to walk all required memory pools to allocate the requested
**	amount of video memory.
**
**	INPUT:
**
**		gckKERNEL Kernel
**			Pointer to an gckKERNEL object.
**
**		gcsHAL_INTERFACE * Interface
**			Pointer to a gcsHAL_INTERFACE structure that defines the command to
**			be dispatched.
**
**	OUTPUT:
**
**		gcsHAL_INTERFACE * Interface
**			Pointer to a gcsHAL_INTERFACE structure that receives any data to be
**			returned.
*/
static gceSTATUS
_AllocateMemory(
	IN gckKERNEL Kernel,
	IN OUT gcePOOL * Pool,
	IN gctSIZE_T Bytes,
	IN gctSIZE_T Alignment,
	IN gceSURF_TYPE Type,
#ifdef __QNXNTO__
	IN gctHANDLE Handle,
#endif
	OUT gcuVIDMEM_NODE_PTR * Node
	)
{
	gcePOOL pool;
	gceSTATUS status;
	gckVIDMEM videoMemory;

	gcmkVERIFY_ARGUMENT(Pool != gcvNULL);

	/* Get initial pool. */
	switch (pool = *Pool)
	{
	case gcvPOOL_DEFAULT:
	case gcvPOOL_LOCAL:
		pool = gcvPOOL_LOCAL_INTERNAL;
		break;

	case gcvPOOL_UNIFIED:
		pool = gcvPOOL_SYSTEM;
		break;

	default:
		break;
	}

	do
	{
		/* Verify the number of bytes to allocate. */
		if (Bytes == 0)
		{
			gcmkERR_BREAK(gcvSTATUS_INVALID_ARGUMENT);
		}

		if (pool == gcvPOOL_VIRTUAL)
		{
			/* Create a gcuVIDMEM_NODE for virtual memory. */
#ifdef __QNXNTO__
			gcmkERR_BREAK(
				gckVIDMEM_ConstructVirtual(Kernel, gcvFALSE, Bytes, Handle, Node));
#else
			gcmkERR_BREAK(
				gckVIDMEM_ConstructVirtual(Kernel, gcvFALSE, Bytes, Node));
#endif

			/* Success. */
			break;
		}
		else if (pool == gcvPOOL_CONTIGUOUS)
		{
			/* Create a gcuVIDMEM_NODE for contiguous memory. */
#ifdef __QNXNTO__
			status = gckVIDMEM_ConstructVirtual(Kernel, gcvTRUE, Bytes, Handle, Node);
#else
			status = gckVIDMEM_ConstructVirtual(Kernel, gcvTRUE, Bytes, Node);
#endif
			if (gcmIS_SUCCESS(status))
			{
				/* Memory allocated. */
				break;
			}
		}
		else
		{
			/* Get pointer to gckVIDMEM object for pool. */
			status = gckKERNEL_GetVideoMemoryPool(Kernel, pool, &videoMemory);

			if (gcmIS_SUCCESS(status))
			{
				/* Allocate memory. */
				status = gckVIDMEM_AllocateLinear(videoMemory,
												  Bytes,
												  Alignment,
												  Type,
#ifdef __QNXNTO__
												  Handle,
#endif
												  Node);

				if (gcmIS_SUCCESS(status))
				{
					/* Memory allocated. */
					(*Node)->VidMem.pool = pool;
					break;
				}
			}
		}

		if (pool == gcvPOOL_LOCAL_INTERNAL)
		{
			/* Advance to external memory. */
			pool = gcvPOOL_LOCAL_EXTERNAL;
		}
		else
		if (pool == gcvPOOL_LOCAL_EXTERNAL)
		{
			/* Advance to contiguous system memory. */
			pool = gcvPOOL_SYSTEM;
		}
		else
		if (pool == gcvPOOL_SYSTEM)
		{
			/* Advance to contiguous memory. */
			pool = gcvPOOL_CONTIGUOUS;
		}
		else
		if ((pool == gcvPOOL_CONTIGUOUS)
		&&  (Type != gcvSURF_TILE_STATUS)
		)
		{
            static int count= 1;
			/* Advance to virtual memory. */
            if (count == 1)
            {
				gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL,
					"Try to allocate virtual memory!\n");
                count = 0;
            }
			pool = gcvPOOL_VIRTUAL;
		}
		else
		{
			/* Out of pools. */
			break;
		}
	}
	/* Loop only for multiple selection pools. */
	while ((*Pool == gcvPOOL_DEFAULT)
	||     (*Pool == gcvPOOL_LOCAL)
	||     (*Pool == gcvPOOL_UNIFIED)
	);

	if (gcmIS_SUCCESS(status))
	{
		/* Return pool used for allocation. */
		*Pool = pool;
	}

	/* Return status. */
	return status;
}
/*******************************************************************************
**
**	gckKERNEL_MapVideoMemory
**
**	Get the logical address for a hardware specific memory address for the
**	current process.
**
**	INPUT:
**
**		gckKERNEL Kernel
**			Pointer to an gckKERNEL object.
**
**      gctBOOL InUserSpace
**          gcvTRUE to map the memory into the user space.
**
**		gctUINT32 Address
**			Hardware specific memory address.
**
**	OUTPUT:
**
**		gctPOINTER * Logical
**			Pointer to a variable that will hold the logical address of the
**			specified memory address.
*/
gceSTATUS gckKERNEL_MapVideoMemoryEx(
	IN gckKERNEL Kernel,
    IN gceCORE   Core,
	IN gctBOOL InUserSpace,
	IN gctUINT32 Address,
	OUT gctPOINTER * Logical
	)
{
	GCHAL * gchal;
	gcePOOL pool;
	gctUINT32 offset, base;
	gceSTATUS status;
	gctPOINTER logical;

    gcmkHEADER_ARG("Kernel=%p InUserSpace=%d Address=%08x",
                   Kernel, InUserSpace, Address);

	gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL,
			   "[ENTER] gckKERNEL_MapVideoMemory");

	/* Verify the arguments. */
	gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
	gcmkVERIFY_ARGUMENT(Logical != gcvNULL);

	/* Extract the pointer to the GCHAL class. */
	gchal = (GCHAL *) Kernel->context;

	do
	{
#if gcdENABLE_VG
		if (Core == gcvCORE_VG)
		{
			/* Split the memory address into a pool type and offset. */
			gcmkERR_BREAK(gckVGHARDWARE_SplitMemory(Kernel->vg->hardware,
											 Address,
											 &pool,
											 &offset));
		}
		else
#endif
		{
			/* Split the memory address into a pool type and offset. */
			gcmkERR_BREAK(gckHARDWARE_SplitMemory(Kernel->hardware,
											 Address,
											 &pool,
											 &offset));
		}
		/* Dispatch on pool. */
		switch (pool)
		{
		case gcvPOOL_LOCAL_INTERNAL:
			/* Internal memory. */
			logical = gchal->GetInternalLogical();
			break;

		case gcvPOOL_LOCAL_EXTERNAL:
			/* External memory. */
			logical = gchal->GetExternalLogical();
			break;

		case gcvPOOL_SYSTEM:
			/* System memory. */
#if UNDER_CE >= 600
			if (InUserSpace)
			{
				logical = gchal->GetProcessContiguousLogical();
			}
			else
			{
				logical = gchal->GetContiguousLogical();
			}
#else
			logical = gchal->GetContiguousLogical();
#endif

#if gcdENABLE_VG
			if (Core == gcvCORE_VG)
			{
				gcmkVERIFY_OK(gckVGHARDWARE_SplitMemory(Kernel->vg->hardware,
												 gchal->GetContiguousHeap()->baseAddress,
												 &pool,
												 &base));
			}
			else
#endif
			{
				gcmkVERIFY_OK(gckHARDWARE_SplitMemory(Kernel->hardware,
												 gchal->GetContiguousHeap()->baseAddress,
												 &pool,
												 &base));
			}
			offset -= base;
			break;

		default:
			/* Invalid memory pool. */
			gcmkFATAL("Unknown memory pool: %u", pool);
			return gcvSTATUS_INVALID_ARGUMENT;
		}

		/* Build logical address of specified address. */
		*Logical = reinterpret_cast<gctPOINTER>
			(static_cast<gctUINT8 *>(logical) + offset);
	}
	while (gcvFALSE);

	if (gcmIS_SUCCESS(status))
	{
		gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL,
				   "gckKERNEL_MapVideoMemory: Address 0x%08X maps to %p",
				   Address, *Logical);
	}

	gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL,
			   "[LEAVE] gckKERNEL_MapVideoMemory(%u)",
			   status);

	/* Return the status. */
    gcmkFOOTER();
	return status;
}