aboutsummaryrefslogtreecommitdiff
path: root/drivers/mxc/gpu-viv/hal/os/linux
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/os/linux')
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_debug.h100
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c1577
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h167
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c1167
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.c470
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h88
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_math.c34
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c7714
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.h79
9 files changed, 11396 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_debug.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_debug.h
new file mode 100644
index 00000000000..a9b633e5d85
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_debug.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#ifndef __gc_hal_kernel_debug_h_
+#define __gc_hal_kernel_debug_h_
+
+#include <gc_hal_kernel_linux.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+****************************** OS-dependent Macros *****************************
+\******************************************************************************/
+
+typedef va_list gctARGUMENTS;
+
+#define gcmkARGUMENTS_START(Arguments, Pointer) \
+ va_start(Arguments, Pointer)
+
+#define gcmkARGUMENTS_END(Arguments) \
+ va_end(Arguments)
+
+#define gcmkDECLARE_LOCK(__spinLock__) \
+ static spinlock_t __spinLock__ = SPIN_LOCK_UNLOCKED;
+
+#define gcmkLOCKSECTION(__spinLock__) \
+ spin_lock(&__spinLock__)
+
+#define gcmkUNLOCKSECTION(__spinLock__) \
+ spin_unlock(&__spinLock__)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+# define gcmkGETPROCESSID() \
+ task_tgid_vnr(current)
+#else
+# define gcmkGETPROCESSID() \
+ current->tgid
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+# define gcmkGETTHREADID() \
+ task_pid_vnr(current)
+#else
+# define gcmkGETTHREADID() \
+ current->pid
+#endif
+
+#define gcmkOUTPUT_STRING(String) \
+ printk(String); \
+ touch_softlockup_watchdog()
+
+#define gcmkSPRINTF(Destination, Size, Message, Value) \
+ snprintf(Destination, Size, Message, Value)
+
+#define gcmkSPRINTF2(Destination, Size, Message, Value1, Value2) \
+ snprintf(Destination, Size, Message, Value1, Value2)
+
+#define gcmkSPRINTF3(Destination, Size, Message, Value1, Value2, Value3) \
+ snprintf(Destination, Size, Message, Value1, Value2, Value3)
+
+#define gcmkVSPRINTF(Destination, Size, Message, Arguments) \
+ vsnprintf(Destination, Size, Message, *(va_list *) &Arguments)
+
+#define gcmkSTRCAT(Destination, Size, String) \
+ strncat(Destination, String, Size)
+
+/* If not zero, forces data alignment in the variable argument list
+ by its individual size. */
+#define gcdALIGNBYSIZE 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __gc_hal_kernel_debug_h_ */
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
new file mode 100644
index 00000000000..fb4ccc56860
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
@@ -0,0 +1,1577 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#include "gc_hal_kernel_linux.h"
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/slab.h>
+
+#define _GC_OBJ_ZONE gcvZONE_DEVICE
+
+#ifdef FLAREON
+ static struct dove_gpio_irq_handler gc500_handle;
+#endif
+
+/******************************************************************************\
+*************************** Memory Allocation Wrappers *************************
+\******************************************************************************/
+
+static gceSTATUS
+_AllocateMemory(
+ IN gckGALDEVICE Device,
+ IN gctSIZE_T Bytes,
+ OUT gctPOINTER *Logical,
+ OUT gctPHYS_ADDR *Physical,
+ OUT gctUINT32 *PhysAddr
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x Bytes=%lu", Device, Bytes);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+ gcmkVERIFY_ARGUMENT(Physical != NULL);
+ gcmkVERIFY_ARGUMENT(PhysAddr != NULL);
+
+ gcmkONERROR(gckOS_AllocateContiguous(
+ Device->os, gcvFALSE, &Bytes, Physical, Logical
+ ));
+
+ *PhysAddr = ((PLINUX_MDL)*Physical)->dmaHandle - Device->baseAddress;
+
+ /* Success. */
+ gcmkFOOTER_ARG(
+ "*Logical=0x%x *Physical=0x%x *PhysAddr=0x%08x",
+ *Logical, *Physical, *PhysAddr
+ );
+
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+_FreeMemory(
+ IN gckGALDEVICE Device,
+ IN gctPOINTER Logical,
+ IN gctPHYS_ADDR Physical)
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x Logical=0x%x Physical=0x%x",
+ Device, Logical, Physical);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ status = gckOS_FreeContiguous(
+ Device->os, Physical, Logical,
+ ((PLINUX_MDL) Physical)->numPages * PAGE_SIZE
+ );
+
+ gcmkFOOTER();
+ return status;
+}
+
+
+
+/******************************************************************************\
+******************************* Interrupt Handler ******************************
+\******************************************************************************/
+static irqreturn_t isrRoutine(int irq, void *ctxt)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+
+ device = (gckGALDEVICE) ctxt;
+
+ /* Call kernel interrupt notification. */
+ status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvTRUE);
+
+ if (gcmIS_SUCCESS(status))
+ {
+ device->dataReadys[gcvCORE_MAJOR] = gcvTRUE;
+
+ up(&device->semas[gcvCORE_MAJOR]);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int threadRoutine(void *ctxt)
+{
+ gckGALDEVICE device = (gckGALDEVICE) ctxt;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Starting isr Thread with extension=%p",
+ device);
+
+ for (;;)
+ {
+ static int down;
+
+ down = down_interruptible(&device->semas[gcvCORE_MAJOR]);
+ device->dataReadys[gcvCORE_MAJOR] = gcvFALSE;
+
+ if (device->killThread == gcvTRUE)
+ {
+ /* The daemon exits. */
+ while (!kthread_should_stop())
+ {
+ gckOS_Delay(device->os, 1);
+ }
+
+ return 0;
+ }
+
+ gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvFALSE);
+ }
+}
+
+static irqreturn_t isrRoutine2D(int irq, void *ctxt)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+
+ device = (gckGALDEVICE) ctxt;
+
+ /* Call kernel interrupt notification. */
+ status = gckKERNEL_Notify(device->kernels[gcvCORE_2D], gcvNOTIFY_INTERRUPT, gcvTRUE);
+
+ if (gcmIS_SUCCESS(status))
+ {
+ device->dataReadys[gcvCORE_2D] = gcvTRUE;
+
+ up(&device->semas[gcvCORE_2D]);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int threadRoutine2D(void *ctxt)
+{
+ gckGALDEVICE device = (gckGALDEVICE) ctxt;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Starting isr Thread with extension=%p",
+ device);
+
+ for (;;)
+ {
+ static int down;
+
+ down = down_interruptible(&device->semas[gcvCORE_2D]);
+ device->dataReadys[gcvCORE_2D] = gcvFALSE;
+
+ if (device->killThread == gcvTRUE)
+ {
+ /* The daemon exits. */
+ while (!kthread_should_stop())
+ {
+ gckOS_Delay(device->os, 1);
+ }
+
+ return 0;
+ }
+
+ gckKERNEL_Notify(device->kernels[gcvCORE_2D], gcvNOTIFY_INTERRUPT, gcvFALSE);
+ }
+}
+
+static irqreturn_t isrRoutineVG(int irq, void *ctxt)
+{
+#if gcdENABLE_VG
+ gceSTATUS status;
+ gckGALDEVICE device;
+
+ device = (gckGALDEVICE) ctxt;
+
+ /* Serve the interrupt. */
+ status = gckVGINTERRUPT_Enque(device->kernels[gcvCORE_VG]->vg->interrupt);
+
+ /* Determine the return value. */
+ return (status == gcvSTATUS_NOT_OUR_INTERRUPT)
+ ? IRQ_RETVAL(0)
+ : IRQ_RETVAL(1);
+#else
+ return IRQ_NONE;
+#endif
+}
+
+static int threadRoutineVG(void *ctxt)
+{
+ gckGALDEVICE device = (gckGALDEVICE) ctxt;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Starting isr Thread with extension=%p",
+ device);
+
+ for (;;)
+ {
+ static int down;
+
+ down = down_interruptible(&device->semas[gcvCORE_VG]);
+ device->dataReadys[gcvCORE_VG] = gcvFALSE;
+
+ if (device->killThread == gcvTRUE)
+ {
+ /* The daemon exits. */
+ while (!kthread_should_stop())
+ {
+ gckOS_Delay(device->os, 1);
+ }
+
+ return 0;
+ }
+
+ gckKERNEL_Notify(device->kernels[gcvCORE_VG], gcvNOTIFY_INTERRUPT, gcvFALSE);
+ }
+}
+
+#if gcdPOWEROFF_TIMEOUT
+/*
+** PM Thread Routine
+**/
+static int threadRoutinePM(void *ctxt)
+{
+ gckGALDEVICE device = (gckGALDEVICE) ctxt;
+ gckHARDWARE hardware = device->kernels[gcvCORE_MAJOR]->hardware;
+ gceCHIPPOWERSTATE state;
+
+ for(;;)
+ {
+ /* wait for idle */
+ gcmkVERIFY_OK(
+ gckOS_AcquireMutex(device->os, hardware->powerOffSema, gcvINFINITE));
+
+ /* We try to power off every 200 ms, until GPU is not idle */
+ do
+ {
+ if (device->killThread == gcvTRUE)
+ {
+ /* The daemon exits. */
+ while (!kthread_should_stop())
+ {
+ gckOS_Delay(device->os, 1);
+ }
+ return 0;
+ }
+
+ gcmkVERIFY_OK(
+ gckHARDWARE_SetPowerManagementState(
+ hardware,
+ gcvPOWER_OFF_TIMEOUT));
+
+ /* relax cpu 200 ms before retry */
+ gckOS_Delay(device->os, 200);
+
+ gcmkVERIFY_OK(
+ gckHARDWARE_QueryPowerManagementState(hardware, &state));
+ }
+ while (state == gcvPOWER_IDLE);
+ }
+}
+#endif
+
+/******************************************************************************\
+******************************* gckGALDEVICE Code ******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Construct
+**
+** Constructor.
+**
+** INPUT:
+**
+** OUTPUT:
+**
+** gckGALDEVICE * Device
+** Pointer to a variable receiving the gckGALDEVICE object pointer on
+** success.
+*/
+gceSTATUS
+gckGALDEVICE_Construct(
+ IN gctINT IrqLine,
+ IN gctUINT32 RegisterMemBase,
+ IN gctSIZE_T RegisterMemSize,
+ IN gctINT IrqLine2D,
+ IN gctUINT32 RegisterMemBase2D,
+ IN gctSIZE_T RegisterMemSize2D,
+ IN gctINT IrqLineVG,
+ IN gctUINT32 RegisterMemBaseVG,
+ IN gctSIZE_T RegisterMemSizeVG,
+ IN gctUINT32 ContiguousBase,
+ IN gctSIZE_T ContiguousSize,
+ IN gctSIZE_T BankSize,
+ IN gctINT FastClear,
+ IN gctINT Compression,
+ IN gctUINT32 PhysBaseAddr,
+ IN gctUINT32 PhysSize,
+ IN gctINT Signal,
+ OUT gckGALDEVICE *Device
+ )
+{
+ gctUINT32 internalBaseAddress = 0, internalAlignment = 0;
+ gctUINT32 externalBaseAddress = 0, externalAlignment = 0;
+ gctUINT32 horizontalTileSize, verticalTileSize;
+ struct resource* mem_region;
+ gctUINT32 physAddr;
+ gctUINT32 physical;
+ gckGALDEVICE device;
+ gceSTATUS status;
+ gctINT32 i;
+ gceHARDWARE_TYPE type;
+ gckDB sharedDB = gcvNULL;
+
+ gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%u "
+ "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u "
+ "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u "
+ "ContiguousBase=0x%08x ContiguousSize=%lu BankSize=%lu "
+ "FastClear=%d Compression=%d PhysBaseAddr=0x%x PhysSize=%d Signal=%d",
+ IrqLine, RegisterMemBase, RegisterMemSize,
+ IrqLine2D, RegisterMemBase2D, RegisterMemSize2D,
+ IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG,
+ ContiguousBase, ContiguousSize, BankSize, FastClear, Compression,
+ PhysBaseAddr, PhysSize, Signal);
+
+ /* Allocate device structure. */
+ device = kmalloc(sizeof(struct _gckGALDEVICE), GFP_KERNEL);
+
+ if (!device)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ memset(device, 0, sizeof(struct _gckGALDEVICE));
+
+ if (IrqLine != -1)
+ {
+ device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase;
+ device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize;
+ }
+
+ if (IrqLine2D != -1)
+ {
+ device->requestedRegisterMemBases[gcvCORE_2D] = RegisterMemBase2D;
+ device->requestedRegisterMemSizes[gcvCORE_2D] = RegisterMemSize2D;
+ }
+
+ if (IrqLineVG != -1)
+ {
+ device->requestedRegisterMemBases[gcvCORE_VG] = RegisterMemBaseVG;
+ device->requestedRegisterMemSizes[gcvCORE_VG] = RegisterMemSizeVG;
+ }
+
+ device->requestedContiguousBase = 0;
+ device->requestedContiguousSize = 0;
+
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ physical = device->requestedRegisterMemBases[i];
+
+ /* Set up register memory region. */
+ if (physical != 0)
+ {
+ mem_region = request_mem_region(
+ physical, device->requestedRegisterMemSizes[i], "galcore register region"
+ );
+
+ if (mem_region == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Failed to claim %lu bytes @ 0x%08X\n",
+ __FUNCTION__, __LINE__,
+ physical, device->requestedRegisterMemSizes[i]
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->registerBases[i] = (gctPOINTER) ioremap_nocache(
+ physical, device->requestedRegisterMemSizes[i]);
+
+ if (device->registerBases[i] == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Unable to map %ld bytes @ 0x%08X\n",
+ __FUNCTION__, __LINE__,
+ physical, device->requestedRegisterMemSizes[i]
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ physical += device->requestedRegisterMemSizes[i];
+ }
+ else
+ {
+ device->registerBases[i] = gcvNULL;
+ }
+ }
+
+ /* Set the base address */
+ device->baseAddress = PhysBaseAddr;
+
+ /* Construct the gckOS object. */
+ gcmkONERROR(gckOS_Construct(device, &device->os));
+
+ if (IrqLine != -1)
+ {
+ /* Construct the gckKERNEL object. */
+ gcmkONERROR(gckKERNEL_Construct(
+ device->os, gcvCORE_MAJOR, device,
+ gcvNULL, &device->kernels[gcvCORE_MAJOR]));
+
+ sharedDB = device->kernels[gcvCORE_MAJOR]->db;
+
+ /* Initialize core mapping */
+ for (i = 0; i < 8; i++)
+ {
+ device->coreMapping[i] = gcvCORE_MAJOR;
+ }
+
+ /* Setup the ISR manager. */
+ gcmkONERROR(gckHARDWARE_SetIsrManager(
+ device->kernels[gcvCORE_MAJOR]->hardware,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR,
+ device
+ ));
+
+ gcmkONERROR(gckHARDWARE_SetFastClear(
+ device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression
+ ));
+
+
+#if COMMAND_PROCESSOR_VERSION == 1
+ /* Start the command queue. */
+ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command));
+#endif
+ }
+ else
+ {
+ device->kernels[gcvCORE_MAJOR] = gcvNULL;
+ }
+
+ if (IrqLine2D != -1)
+ {
+ gcmkONERROR(gckKERNEL_Construct(
+ device->os, gcvCORE_2D, device,
+ sharedDB, &device->kernels[gcvCORE_2D]));
+
+ if (sharedDB == gcvNULL) sharedDB = device->kernels[gcvCORE_2D]->db;
+
+ /* Verify the hardware type */
+ gcmkONERROR(gckHARDWARE_GetType(device->kernels[gcvCORE_2D]->hardware, &type));
+
+ if (type != gcvHARDWARE_2D)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Unexpected hardware type: %d\n",
+ __FUNCTION__, __LINE__,
+ type
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Initialize core mapping */
+ if (device->kernels[gcvCORE_MAJOR] == gcvNULL)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ device->coreMapping[i] = gcvCORE_2D;
+ }
+ }
+ else
+ {
+ device->coreMapping[gcvHARDWARE_2D] = gcvCORE_2D;
+ }
+
+ /* Setup the ISR manager. */
+ gcmkONERROR(gckHARDWARE_SetIsrManager(
+ device->kernels[gcvCORE_2D]->hardware,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR_2D,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR_2D,
+ device
+ ));
+
+#if COMMAND_PROCESSOR_VERSION == 1
+ /* Start the command queue. */
+ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_2D]->command));
+#endif
+ }
+ else
+ {
+ device->kernels[gcvCORE_2D] = gcvNULL;
+ }
+
+ if (IrqLineVG != -1)
+ {
+#if gcdENABLE_VG
+ gcmkONERROR(gckKERNEL_Construct(
+ device->os, gcvCORE_VG, device,
+ sharedDB, &device->kernels[gcvCORE_VG]));
+ /* Initialize core mapping */
+ if (device->kernels[gcvCORE_MAJOR] == gcvNULL
+ && device->kernels[gcvCORE_2D] == gcvNULL
+ )
+ {
+ for (i = 0; i < 8; i++)
+ {
+ device->coreMapping[i] = gcvCORE_VG;
+ }
+ }
+ else
+ {
+ device->coreMapping[gcvHARDWARE_VG] = gcvCORE_VG;
+ }
+
+#endif
+ }
+ else
+ {
+ device->kernels[gcvCORE_VG] = gcvNULL;
+ }
+
+ /* Initialize the ISR. */
+ device->irqLines[gcvCORE_MAJOR] = IrqLine;
+ device->irqLines[gcvCORE_2D] = IrqLine2D;
+ device->irqLines[gcvCORE_VG] = IrqLineVG;
+
+ /* Initialize the kernel thread semaphores. */
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0);
+ }
+
+ device->signal = Signal;
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->kernels[i] != gcvNULL) break;
+ }
+
+ if (i == gcdCORE_COUNT) gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+
+#if gcdENABLE_VG
+ if (i == gcvCORE_VG)
+ {
+ /* Query the ceiling of the system memory. */
+ gcmkONERROR(gckVGHARDWARE_QuerySystemMemory(
+ device->kernels[i]->vg->hardware,
+ &device->systemMemorySize,
+ &device->systemMemoryBaseAddress
+ ));
+ /* query the amount of video memory */
+ gcmkONERROR(gckVGHARDWARE_QueryMemory(
+ device->kernels[i]->vg->hardware,
+ &device->internalSize, &internalBaseAddress, &internalAlignment,
+ &device->externalSize, &externalBaseAddress, &externalAlignment,
+ &horizontalTileSize, &verticalTileSize
+ ));
+ }
+ else
+#endif
+ {
+ /* Query the ceiling of the system memory. */
+ gcmkONERROR(gckHARDWARE_QuerySystemMemory(
+ device->kernels[i]->hardware,
+ &device->systemMemorySize,
+ &device->systemMemoryBaseAddress
+ ));
+
+ /* query the amount of video memory */
+ gcmkONERROR(gckHARDWARE_QueryMemory(
+ device->kernels[i]->hardware,
+ &device->internalSize, &internalBaseAddress, &internalAlignment,
+ &device->externalSize, &externalBaseAddress, &externalAlignment,
+ &horizontalTileSize, &verticalTileSize
+ ));
+ }
+
+
+ /* Set up the internal memory region. */
+ if (device->internalSize > 0)
+ {
+ status = gckVIDMEM_Construct(
+ device->os,
+ internalBaseAddress, device->internalSize, internalAlignment,
+ 0, &device->internalVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable internal heap. */
+ device->internalSize = 0;
+ }
+ else
+ {
+ /* Map internal memory. */
+ device->internalLogical
+ = (gctPOINTER) ioremap_nocache(physical, device->internalSize);
+
+ if (device->internalLogical == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->internalPhysical = (gctPHYS_ADDR) physical;
+ physical += device->internalSize;
+ }
+ }
+
+ if (device->externalSize > 0)
+ {
+ /* create the external memory heap */
+ status = gckVIDMEM_Construct(
+ device->os,
+ externalBaseAddress, device->externalSize, externalAlignment,
+ 0, &device->externalVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable internal heap. */
+ device->externalSize = 0;
+ }
+ else
+ {
+ /* Map external memory. */
+ device->externalLogical
+ = (gctPOINTER) ioremap_nocache(physical, device->externalSize);
+
+ if (device->externalLogical == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->externalPhysical = (gctPHYS_ADDR) physical;
+ physical += device->externalSize;
+ }
+ }
+
+ /* set up the contiguous memory */
+ device->contiguousSize = ContiguousSize;
+
+ if (ContiguousSize > 0)
+ {
+ if (ContiguousBase == 0)
+ {
+ while (device->contiguousSize > 0)
+ {
+ /* Allocate contiguous memory. */
+ status = _AllocateMemory(
+ device,
+ device->contiguousSize,
+ &device->contiguousBase,
+ &device->contiguousPhysical,
+ &physAddr
+ );
+
+ if (gcmIS_SUCCESS(status))
+ {
+ status = gckVIDMEM_Construct(
+ device->os,
+ physAddr | device->systemMemoryBaseAddress,
+ device->contiguousSize,
+ 64,
+ BankSize,
+ &device->contiguousVidMem
+ );
+
+ if (gcmIS_SUCCESS(status))
+ {
+ break;
+ }
+
+ gcmkONERROR(_FreeMemory(
+ device,
+ device->contiguousBase,
+ device->contiguousPhysical
+ ));
+
+ device->contiguousBase = gcvNULL;
+ device->contiguousPhysical = gcvNULL;
+ }
+
+ if (device->contiguousSize <= (4 << 20))
+ {
+ device->contiguousSize = 0;
+ }
+ else
+ {
+ device->contiguousSize -= (4 << 20);
+ }
+ }
+ }
+ else
+ {
+ /* Create the contiguous memory heap. */
+ status = gckVIDMEM_Construct(
+ device->os,
+ (ContiguousBase - device->baseAddress) | device->systemMemoryBaseAddress,
+ ContiguousSize,
+ 64, BankSize,
+ &device->contiguousVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable contiguous memory pool. */
+ device->contiguousVidMem = gcvNULL;
+ device->contiguousSize = 0;
+ }
+ else
+ {
+ mem_region = request_mem_region(
+ ContiguousBase, ContiguousSize, "galcore managed memory"
+ );
+
+ if (mem_region == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Failed to claim %ld bytes @ 0x%08X\n",
+ __FUNCTION__, __LINE__,
+ ContiguousSize, ContiguousBase
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->requestedContiguousBase = ContiguousBase;
+ device->requestedContiguousSize = ContiguousSize;
+
+ device->contiguousBase
+#if gcdPAGED_MEMORY_CACHEABLE
+ = (gctPOINTER) ioremap_cached(ContiguousBase, ContiguousSize);
+#else
+ = (gctPOINTER) ioremap_nocache(ContiguousBase, ContiguousSize);
+#endif
+ if (device->contiguousBase == gcvNULL)
+ {
+ device->contiguousVidMem = gcvNULL;
+ device->contiguousSize = 0;
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->contiguousPhysical = (gctPHYS_ADDR) ContiguousBase;
+ device->contiguousSize = ContiguousSize;
+ device->contiguousMapped = gcvTRUE;
+ }
+ }
+ }
+
+ /* Return pointer to the device. */
+ * Device = device;
+
+ gcmkFOOTER_ARG("*Device=0x%x", * Device);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ gcmkVERIFY_OK(gckGALDEVICE_Destroy(device));
+
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Destroy
+**
+** Class destructor.
+**
+** INPUT:
+**
+** Nothing.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+gceSTATUS
+gckGALDEVICE_Destroy(
+ gckGALDEVICE Device)
+{
+ gctINT i;
+ gceSTATUS status = gcvSTATUS_OK;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ if (Device != gcvNULL)
+ {
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (Device->kernels[i] != gcvNULL)
+ {
+ /* Destroy the gckKERNEL object. */
+ gcmkVERIFY_OK(gckKERNEL_Destroy(Device->kernels[i]));
+ Device->kernels[i] = gcvNULL;
+ }
+ }
+
+ {
+ if (Device->internalLogical != gcvNULL)
+ {
+ /* Unmap the internal memory. */
+ iounmap(Device->internalLogical);
+ Device->internalLogical = gcvNULL;
+ }
+
+ if (Device->internalVidMem != gcvNULL)
+ {
+ /* Destroy the internal heap. */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->internalVidMem));
+ Device->internalVidMem = gcvNULL;
+ }
+ }
+
+ {
+ if (Device->externalLogical != gcvNULL)
+ {
+ /* Unmap the external memory. */
+ iounmap(Device->externalLogical);
+ Device->externalLogical = gcvNULL;
+ }
+
+ if (Device->externalVidMem != gcvNULL)
+ {
+ /* destroy the external heap */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem));
+ Device->externalVidMem = gcvNULL;
+ }
+ }
+
+ {
+ if (Device->contiguousBase != gcvNULL)
+ {
+ if (Device->contiguousMapped)
+ {
+ /* Unmap the contiguous memory. */
+ iounmap(Device->contiguousBase);
+ }
+ else
+ {
+ gcmkONERROR(_FreeMemory(
+ Device,
+ Device->contiguousBase,
+ Device->contiguousPhysical
+ ));
+ }
+
+ if (Device->requestedContiguousBase != 0)
+ {
+ release_mem_region(Device->requestedContiguousBase, Device->requestedContiguousSize);
+ }
+
+ Device->contiguousBase = gcvNULL;
+ Device->contiguousPhysical = gcvNULL;
+ Device->requestedContiguousBase = 0;
+ Device->requestedContiguousSize = 0;
+ }
+
+ if (Device->contiguousVidMem != gcvNULL)
+ {
+ /* Destroy the contiguous heap. */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem));
+ Device->contiguousVidMem = gcvNULL;
+ }
+ }
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (Device->registerBases[i] != gcvNULL)
+ {
+ /* Unmap register memory. */
+ iounmap(Device->registerBases[i]);
+ if (Device->requestedRegisterMemBases[i] != 0)
+ {
+ release_mem_region(Device->requestedRegisterMemBases[i], Device->requestedRegisterMemSizes[i]);
+ }
+
+ Device->registerBases[i] = gcvNULL;
+ Device->requestedRegisterMemBases[i] = 0;
+ Device->requestedRegisterMemSizes[i] = 0;
+ }
+ }
+
+ /* Destroy the gckOS object. */
+ if (Device->os != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckOS_Destroy(Device->os));
+ Device->os = gcvNULL;
+ }
+
+ /* Free the device. */
+ kfree(Device);
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Setup_ISR
+**
+** Start the ISR routine.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** gcvSTATUS_OK
+** Setup successfully.
+** gcvSTATUS_GENERIC_IO
+** Setup failed.
+*/
+gceSTATUS
+gckGALDEVICE_Setup_ISR(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ gctINT ret;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->irqLines[gcvCORE_MAJOR] < 0)
+ {
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Hook up the isr based on the irq line. */
+#ifdef FLAREON
+ gc500_handle.dev_name = "galcore interrupt service";
+ gc500_handle.dev_id = Device;
+ gc500_handle.handler = isrRoutine;
+ gc500_handle.intr_gen = GPIO_INTR_LEVEL_TRIGGER;
+ gc500_handle.intr_trig = GPIO_TRIG_HIGH_LEVEL;
+
+ ret = dove_gpio_request(
+ DOVE_GPIO0_7, &gc500_handle
+ );
+#else
+ ret = request_irq(
+ Device->irqLines[gcvCORE_MAJOR], isrRoutine, IRQF_DISABLED,
+ "galcore interrupt service", Device
+ );
+#endif
+
+ if (ret != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not register irq line %d (error=%d)\n",
+ __FUNCTION__, __LINE__,
+ Device->irqLines[gcvCORE_MAJOR], ret
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Mark ISR as initialized. */
+ Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckGALDEVICE_Setup_ISR_2D(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ gctINT ret;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->irqLines[gcvCORE_2D] < 0)
+ {
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Hook up the isr based on the irq line. */
+#ifdef FLAREON
+ gc500_handle.dev_name = "galcore interrupt service";
+ gc500_handle.dev_id = Device;
+ gc500_handle.handler = isrRoutine2D;
+ gc500_handle.intr_gen = GPIO_INTR_LEVEL_TRIGGER;
+ gc500_handle.intr_trig = GPIO_TRIG_HIGH_LEVEL;
+
+ ret = dove_gpio_request(
+ DOVE_GPIO0_7, &gc500_handle
+ );
+#else
+ ret = request_irq(
+ Device->irqLines[gcvCORE_2D], isrRoutine2D, IRQF_DISABLED,
+ "galcore interrupt service for 2D", Device
+ );
+#endif
+
+ if (ret != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not register irq line %d (error=%d)\n",
+ __FUNCTION__, __LINE__,
+ Device->irqLines[gcvCORE_2D], ret
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Mark ISR as initialized. */
+ Device->isrInitializeds[gcvCORE_2D] = gcvTRUE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckGALDEVICE_Setup_ISR_VG(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ gctINT ret;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->irqLines[gcvCORE_VG] < 0)
+ {
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Hook up the isr based on the irq line. */
+#ifdef FLAREON
+ gc500_handle.dev_name = "galcore interrupt service";
+ gc500_handle.dev_id = Device;
+ gc500_handle.handler = isrRoutineVG;
+ gc500_handle.intr_gen = GPIO_INTR_LEVEL_TRIGGER;
+ gc500_handle.intr_trig = GPIO_TRIG_HIGH_LEVEL;
+
+ ret = dove_gpio_request(
+ DOVE_GPIO0_7, &gc500_handle
+ );
+#else
+ ret = request_irq(
+ Device->irqLines[gcvCORE_VG], isrRoutineVG, IRQF_DISABLED,
+ "galcore interrupt service for 2D", Device
+ );
+#endif
+
+ if (ret != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not register irq line %d (error=%d)\n",
+ __FUNCTION__, __LINE__,
+ Device->irqLines[gcvCORE_VG], ret
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Mark ISR as initialized. */
+ Device->isrInitializeds[gcvCORE_VG] = gcvTRUE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Release_ISR
+**
+** Release the irq line.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+gceSTATUS
+gckGALDEVICE_Release_ISR(
+ IN gckGALDEVICE Device
+ )
+{
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ /* release the irq */
+ if (Device->isrInitializeds[gcvCORE_MAJOR])
+ {
+#ifdef FLAREON
+ dove_gpio_free(DOVE_GPIO0_7, "galcore interrupt service");
+#else
+ free_irq(Device->irqLines[gcvCORE_MAJOR], Device);
+#endif
+
+ Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckGALDEVICE_Release_ISR_2D(
+ IN gckGALDEVICE Device
+ )
+{
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ /* release the irq */
+ if (Device->isrInitializeds[gcvCORE_2D])
+ {
+#ifdef FLAREON
+ dove_gpio_free(DOVE_GPIO0_7, "galcore interrupt service");
+#else
+ free_irq(Device->irqLines[gcvCORE_2D], Device);
+#endif
+
+ Device->isrInitializeds[gcvCORE_2D] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckGALDEVICE_Release_ISR_VG(
+ IN gckGALDEVICE Device
+ )
+{
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ /* release the irq */
+ if (Device->isrInitializeds[gcvCORE_VG])
+ {
+#ifdef FLAREON
+ dove_gpio_free(DOVE_GPIO0_7, "galcore interrupt service");
+#else
+ free_irq(Device->irqLines[gcvCORE_VG], Device);
+#endif
+
+ Device->isrInitializeds[gcvCORE_VG] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Start_Threads
+**
+** Start the daemon threads.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** gcvSTATUS_OK
+** Start successfully.
+** gcvSTATUS_GENERIC_IO
+** Start failed.
+*/
+gceSTATUS
+gckGALDEVICE_Start_Threads(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ struct task_struct * task;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->kernels[gcvCORE_MAJOR] != gcvNULL)
+ {
+ /* Start the kernel thread. */
+ task = kthread_run(threadRoutine, Device, "galcore daemon thread");
+
+ if (IS_ERR(task))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not start the kernel thread.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ Device->threadCtxts[gcvCORE_MAJOR] = task;
+ Device->threadInitializeds[gcvCORE_MAJOR] = gcvTRUE;
+
+#if gcdPOWEROFF_TIMEOUT
+ /* Start the kernel thread. */
+ task = kthread_run(threadRoutinePM, Device, "galcore pm thread");
+
+ if (IS_ERR(task))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not start the kernel thread.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ Device->pmThreadCtxts = task;
+ Device->pmThreadInitializeds = gcvTRUE;
+#endif
+ }
+
+ if (Device->kernels[gcvCORE_2D] != gcvNULL)
+ {
+ /* Start the kernel thread. */
+ task = kthread_run(threadRoutine2D, Device, "galcore daemon thread for 2D");
+
+ if (IS_ERR(task))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not start the kernel thread.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ Device->threadCtxts[gcvCORE_2D] = task;
+ Device->threadInitializeds[gcvCORE_2D] = gcvTRUE;
+ }
+ else
+ {
+ Device->threadInitializeds[gcvCORE_2D] = gcvFALSE;
+ }
+
+ if (Device->kernels[gcvCORE_VG] != gcvNULL)
+ {
+ /* Start the kernel thread. */
+ task = kthread_run(threadRoutineVG, Device, "galcore daemon thread for VG");
+
+ if (IS_ERR(task))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not start the kernel thread.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ Device->threadCtxts[gcvCORE_VG] = task;
+ Device->threadInitializeds[gcvCORE_VG] = gcvTRUE;
+ }
+ else
+ {
+ Device->threadInitializeds[gcvCORE_VG] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Stop_Threads
+**
+** Stop the gal device, including the following actions: stop the daemon
+** thread, release the irq.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+gceSTATUS
+gckGALDEVICE_Stop_Threads(
+ gckGALDEVICE Device
+ )
+{
+ gctINT i;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ /* Stop the kernel threads. */
+ if (Device->threadInitializeds[i])
+ {
+ Device->killThread = gcvTRUE;
+ up(&Device->semas[i]);
+
+ kthread_stop(Device->threadCtxts[i]);
+ Device->threadCtxts[i] = gcvNULL;
+ Device->threadInitializeds[i] = gcvFALSE;
+ }
+ }
+
+#if gcdPOWEROFF_TIMEOUT
+ /* Stop the kernel threads. */
+ if (Device->pmThreadInitializeds)
+ {
+ gckHARDWARE hardware = Device->kernels[gcvCORE_MAJOR]->hardware;
+ Device->killThread = gcvTRUE;
+ gckOS_ReleaseSemaphore(Device->os, hardware->powerOffSema);
+
+ kthread_stop(Device->pmThreadCtxts);
+ Device->pmThreadCtxts = gcvNULL;
+ Device->pmThreadInitializeds = gcvFALSE;
+ }
+#endif
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Start
+**
+** Start the gal device, including the following actions: setup the isr routine
+** and start the daemoni thread.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** gcvSTATUS_OK
+** Start successfully.
+*/
+gceSTATUS
+gckGALDEVICE_Start(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ /* Start the kernel thread. */
+ gcmkONERROR(gckGALDEVICE_Start_Threads(Device));
+
+ if (Device->kernels[gcvCORE_MAJOR] != gcvNULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR(Device));
+
+ /* Switch to SUSPEND power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_SUSPEND_ATPOWERON
+ ));
+ }
+
+ if (Device->kernels[gcvCORE_2D] != gcvNULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR_2D(Device));
+
+ /* Switch to SUSPEND power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_SUSPEND_ATPOWERON
+ ));
+ }
+
+ if (Device->kernels[gcvCORE_VG] != gcvNULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR_VG(Device));
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Stop
+**
+** Stop the gal device, including the following actions: stop the daemon
+** thread, release the irq.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+gceSTATUS
+gckGALDEVICE_Stop(
+ gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->kernels[gcvCORE_MAJOR] != gcvNULL)
+ {
+ /* Switch to OFF power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF
+ ));
+
+ /* Remove the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Release_ISR(Device));
+ }
+
+ if (Device->kernels[gcvCORE_2D] != gcvNULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Release_ISR_2D(Device));
+
+ /* Switch to OFF power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF
+ ));
+ }
+
+ if (Device->kernels[gcvCORE_VG] != gcvNULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Release_ISR_VG(Device));
+ }
+
+ /* Stop the kernel thread. */
+ gcmkONERROR(gckGALDEVICE_Stop_Threads(Device));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h
new file mode 100644
index 00000000000..08c5e82696b
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#ifndef __gc_hal_kernel_device_h_
+#define __gc_hal_kernel_device_h_
+
+#ifdef ANDROID
+#define gcdkREPORT_VIDMEM_LEAK 0
+#else
+#define gcdkREPORT_VIDMEM_LEAK 1
+#endif
+
+/******************************************************************************\
+******************************* gckGALDEVICE Structure *******************************
+\******************************************************************************/
+
+typedef struct _gckGALDEVICE
+{
+ /* Objects. */
+ gckOS os;
+ gckKERNEL kernels[gcdCORE_COUNT];
+
+ /* Attributes. */
+ gctSIZE_T internalSize;
+ gctPHYS_ADDR internalPhysical;
+ gctPOINTER internalLogical;
+ gckVIDMEM internalVidMem;
+ gctSIZE_T externalSize;
+ gctPHYS_ADDR externalPhysical;
+ gctPOINTER externalLogical;
+ gckVIDMEM externalVidMem;
+ gckVIDMEM contiguousVidMem;
+ gctPOINTER contiguousBase;
+ gctPHYS_ADDR contiguousPhysical;
+ gctSIZE_T contiguousSize;
+ gctBOOL contiguousMapped;
+ gctPOINTER contiguousMappedUser;
+ gctSIZE_T systemMemorySize;
+ gctUINT32 systemMemoryBaseAddress;
+ gctPOINTER registerBases[gcdCORE_COUNT];
+ gctSIZE_T registerSizes[gcdCORE_COUNT];
+ gctUINT32 baseAddress;
+ gctUINT32 requestedRegisterMemBases[gcdCORE_COUNT];
+ gctSIZE_T requestedRegisterMemSizes[gcdCORE_COUNT];
+ gctUINT32 requestedContiguousBase;
+ gctSIZE_T requestedContiguousSize;
+
+ /* IRQ management. */
+ gctINT irqLines[gcdCORE_COUNT];
+ gctBOOL isrInitializeds[gcdCORE_COUNT];
+ gctBOOL dataReadys[gcdCORE_COUNT];
+
+ /* Thread management. */
+ struct task_struct *threadCtxts[gcdCORE_COUNT];
+ struct semaphore semas[gcdCORE_COUNT];
+ gctBOOL threadInitializeds[gcdCORE_COUNT];
+ gctBOOL killThread;
+
+ /* Signal management. */
+ gctINT signal;
+
+ /* Core mapping */
+ gceCORE coreMapping[8];
+
+#if gcdPOWEROFF_TIMEOUT
+ struct task_struct *pmThreadCtxts;
+ gctBOOL pmThreadInitializeds;
+#endif
+}
+* gckGALDEVICE;
+
+typedef struct _gcsHAL_PRIVATE_DATA
+{
+ gckGALDEVICE device;
+ gctPOINTER mappedMemory;
+ gctPOINTER contiguousLogical;
+ /* The process opening the device may not be the same as the one that closes it. */
+ gctUINT32 pidOpen;
+}
+gcsHAL_PRIVATE_DATA, * gcsHAL_PRIVATE_DATA_PTR;
+
+gceSTATUS gckGALDEVICE_Setup_ISR(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Setup_ISR_2D(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Setup_ISR_VG(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Release_ISR(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Release_ISR_2D(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Release_ISR_VG(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Start_Threads(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Stop_Threads(
+ gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Start(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Stop(
+ gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Construct(
+ IN gctINT IrqLine,
+ IN gctUINT32 RegisterMemBase,
+ IN gctSIZE_T RegisterMemSize,
+ IN gctINT IrqLine2D,
+ IN gctUINT32 RegisterMemBase2D,
+ IN gctSIZE_T RegisterMemSize2D,
+ IN gctINT IrqLineVG,
+ IN gctUINT32 RegisterMemBaseVG,
+ IN gctSIZE_T RegisterMemSizeVG,
+ IN gctUINT32 ContiguousBase,
+ IN gctSIZE_T ContiguousSize,
+ IN gctSIZE_T BankSize,
+ IN gctINT FastClear,
+ IN gctINT Compression,
+ IN gctUINT32 PhysBaseAddr,
+ IN gctUINT32 PhysSize,
+ IN gctINT Signal,
+ OUT gckGALDEVICE *Device
+ );
+
+gceSTATUS gckGALDEVICE_Destroy(
+ IN gckGALDEVICE Device
+ );
+
+#endif /* __gc_hal_kernel_device_h_ */
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c
new file mode 100644
index 00000000000..a826b2438a7
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c
@@ -0,0 +1,1167 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+* Copyright (C) 2011 Freescale Semiconductor, Inc.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <mach/viv_gpu.h>
+
+#include "gc_hal_kernel_linux.h"
+#include "gc_hal_driver.h"
+
+#if USE_PLATFORM_DRIVER
+# include <linux/platform_device.h>
+#endif
+
+#ifdef CONFIG_PXA_DVFM
+# include <mach/dvfm.h>
+# include <mach/pxa3xx_dvfm.h>
+#endif
+
+
+/* Zone used for header/footer. */
+#define _GC_OBJ_ZONE gcvZONE_DRIVER
+
+MODULE_DESCRIPTION("Vivante Graphics Driver");
+MODULE_LICENSE("GPL");
+
+static struct class* gpuClass;
+
+static gckGALDEVICE galDevice;
+
+static int major = 199;
+module_param(major, int, 0644);
+
+static int irqLine = -1;
+module_param(irqLine, int, 0644);
+
+static long registerMemBase = 0x80000000;
+module_param(registerMemBase, long, 0644);
+
+static ulong registerMemSize = 256 << 10;
+module_param(registerMemSize, ulong, 0644);
+
+static int irqLine2D = -1;
+module_param(irqLine2D, int, 0644);
+
+static long registerMemBase2D = 0x00000000;
+module_param(registerMemBase2D, long, 0644);
+
+static ulong registerMemSize2D = 256 << 10;
+module_param(registerMemSize2D, ulong, 0644);
+
+static int irqLineVG = -1;
+module_param(irqLineVG, int, 0644);
+
+static long registerMemBaseVG = 0x00000000;
+module_param(registerMemBaseVG, long, 0644);
+
+static ulong registerMemSizeVG = 256 << 10;
+module_param(registerMemSizeVG, ulong, 0644);
+
+static long contiguousSize = 4 << 20;
+module_param(contiguousSize, long, 0644);
+
+static ulong contiguousBase = 0;
+module_param(contiguousBase, ulong, 0644);
+
+static long bankSize = 32 << 20;
+module_param(bankSize, long, 0644);
+
+static int fastClear = -1;
+module_param(fastClear, int, 0644);
+
+static int compression = -1;
+module_param(compression, int, 0644);
+
+static int signal = 48;
+module_param(signal, int, 0644);
+
+static ulong baseAddress = 0;
+module_param(baseAddress, ulong, 0644);
+
+static ulong physSize = 0;
+module_param(physSize, ulong, 0644);
+
+static int showArgs = 0;
+module_param(showArgs, int, 0644);
+
+#if ENABLE_GPU_CLOCK_BY_DRIVER
+ unsigned long coreClock = 156000000;
+ module_param(coreClock, ulong, 0644);
+#endif
+
+static struct clk * clk_3d_core;
+static struct clk * clk_3d_shader;
+static struct clk * clk_2d_core;
+
+static int drv_open(
+ struct inode* inode,
+ struct file* filp
+ );
+
+static int drv_release(
+ struct inode* inode,
+ struct file* filp
+ );
+
+static long drv_ioctl(
+ struct file* filp,
+ unsigned int ioctlCode,
+ unsigned long arg
+ );
+
+static int drv_mmap(
+ struct file* filp,
+ struct vm_area_struct* vma
+ );
+
+static struct file_operations driver_fops =
+{
+ .open = drv_open,
+ .release = drv_release,
+ .unlocked_ioctl = drv_ioctl,
+ .mmap = drv_mmap,
+};
+
+int drv_open(
+ struct inode* inode,
+ struct file* filp
+ )
+{
+ gceSTATUS status;
+ gctBOOL attached = gcvFALSE;
+ gcsHAL_PRIVATE_DATA_PTR data = gcvNULL;
+ gctINT i;
+
+ gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);
+
+ if (filp == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): filp is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ data = kmalloc(sizeof(gcsHAL_PRIVATE_DATA), GFP_KERNEL);
+
+ if (data == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): private_data is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ data->device = galDevice;
+ data->mappedMemory = gcvNULL;
+ data->contiguousLogical = gcvNULL;
+ gcmkONERROR(gckOS_GetProcessID(&data->pidOpen));
+
+ /* Attached the process. */
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != gcvNULL)
+ {
+ gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE));
+ }
+ }
+ attached = gcvTRUE;
+
+ if (!galDevice->contiguousMapped)
+ {
+ gcmkONERROR(gckOS_MapMemory(
+ galDevice->os,
+ galDevice->contiguousPhysical,
+ galDevice->contiguousSize,
+ &data->contiguousLogical
+ ));
+ }
+
+ filp->private_data = data;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return 0;
+
+OnError:
+ if (data != gcvNULL)
+ {
+ if (data->contiguousLogical != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckOS_UnmapMemory(
+ galDevice->os,
+ galDevice->contiguousPhysical,
+ galDevice->contiguousSize,
+ data->contiguousLogical
+ ));
+ }
+
+ kfree(data);
+ }
+
+ if (attached)
+ {
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvFALSE));
+ }
+ }
+ }
+
+ gcmkFOOTER();
+ return -ENOTTY;
+}
+
+int drv_release(
+ struct inode* inode,
+ struct file* filp
+ )
+{
+ gceSTATUS status;
+ gcsHAL_PRIVATE_DATA_PTR data;
+ gckGALDEVICE device;
+ gctINT i;
+
+ gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);
+
+ if (filp == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): filp is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ data = filp->private_data;
+
+ if (data == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): private_data is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ device = data->device;
+
+ if (device == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): device is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ if (!device->contiguousMapped)
+ {
+ if (data->contiguousLogical != gcvNULL)
+ {
+ gctUINT32 processID;
+ gcmkVERIFY_OK(gckOS_GetProcessID(&processID));
+ gcmkONERROR(gckOS_UnmapMemoryEx(
+ galDevice->os,
+ galDevice->contiguousPhysical,
+ galDevice->contiguousSize,
+ data->contiguousLogical,
+ data->pidOpen
+ ));
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != gcvNULL)
+ {
+ gcmkVERIFY_OK(
+ gckKERNEL_RemoveProcessDB(galDevice->kernels[i],
+ processID, gcvDB_MAP_MEMORY,
+ data->contiguousLogical));
+ }
+ }
+
+ data->contiguousLogical = gcvNULL;
+ }
+ }
+
+ /* A process gets detached. */
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != gcvNULL)
+ {
+ gcmkONERROR(gckKERNEL_AttachProcessEx(galDevice->kernels[i], gcvFALSE, data->pidOpen));
+ }
+ }
+
+ kfree(data);
+ filp->private_data = NULL;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return 0;
+
+OnError:
+ gcmkFOOTER();
+ return -ENOTTY;
+}
+
+long drv_ioctl(
+ struct file* filp,
+ unsigned int ioctlCode,
+ unsigned long arg
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+ gctUINT32 copyLen;
+ DRIVER_ARGS drvArgs;
+ gckGALDEVICE device;
+ gcsHAL_PRIVATE_DATA_PTR data;
+ gctINT32 i, count;
+
+ gcmkHEADER_ARG(
+ "filp=0x%08X ioctlCode=0x%08X arg=0x%08X",
+ filp, ioctlCode, arg
+ );
+
+ if (filp == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): filp is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ data = filp->private_data;
+
+ if (data == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): private_data is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ device = data->device;
+
+ if (device == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): device is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ if ((ioctlCode != IOCTL_GCHAL_INTERFACE)
+ && (ioctlCode != IOCTL_GCHAL_KERNEL_INTERFACE)
+ )
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): unknown command %d\n",
+ __FUNCTION__, __LINE__,
+ ioctlCode
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Get the drvArgs. */
+ copyLen = copy_from_user(
+ &drvArgs, (void *) arg, sizeof(DRIVER_ARGS)
+ );
+
+ if (copyLen != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): error copying of the input arguments.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Now bring in the gcsHAL_INTERFACE structure. */
+ if ((drvArgs.InputBufferSize != sizeof(gcsHAL_INTERFACE))
+ || (drvArgs.OutputBufferSize != sizeof(gcsHAL_INTERFACE))
+ )
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): input or/and output structures are invalid.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ copyLen = copy_from_user(
+ &iface, drvArgs.InputBuffer, sizeof(gcsHAL_INTERFACE)
+ );
+
+ if (copyLen != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): error copying of input HAL interface.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ if (iface.command == gcvHAL_CHIP_INFO)
+ {
+ count = 0;
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->kernels[i] != gcvNULL)
+ {
+#if gcdENABLE_VG
+ if (i == gcvCORE_VG)
+ {
+ iface.u.ChipInfo.types[count] = gcvHARDWARE_VG;
+ }
+ else
+#endif
+ {
+ gcmkVERIFY_OK(gckHARDWARE_GetType(device->kernels[i]->hardware,
+ &iface.u.ChipInfo.types[count]));
+ }
+ count++;
+ }
+ }
+
+ iface.u.ChipInfo.count = count;
+ status = gcvSTATUS_OK;
+ }
+ else
+ {
+ if (iface.hardwareType < 0 || iface.hardwareType > 7)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): unknown hardwareType %d\n",
+ __FUNCTION__, __LINE__,
+ iface.hardwareType
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+#if gcdENABLE_VG
+ if (device->coreMapping[iface.hardwareType] == gcvCORE_VG)
+ {
+ status = gckVGKERNEL_Dispatch(device->kernels[gcvCORE_VG],
+ (ioctlCode == IOCTL_GCHAL_INTERFACE),
+ &iface);
+ }
+ else
+#endif
+ {
+ status = gckKERNEL_Dispatch(device->kernels[device->coreMapping[iface.hardwareType]],
+ (ioctlCode == IOCTL_GCHAL_INTERFACE),
+ &iface);
+ }
+ }
+
+ if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY))
+ {
+ /* Special case for mapped memory. */
+ if ((data->mappedMemory != gcvNULL)
+ && (iface.u.LockVideoMemory.node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ )
+ {
+ /* Compute offset into mapped memory. */
+ gctUINT32 offset
+ = (gctUINT8 *) iface.u.LockVideoMemory.memory
+ - (gctUINT8 *) device->contiguousBase;
+
+ /* Compute offset into user-mapped region. */
+ iface.u.LockVideoMemory.memory =
+ (gctUINT8 *) data->mappedMemory + offset;
+ }
+ }
+
+ /* Copy data back to the user. */
+ copyLen = copy_to_user(
+ drvArgs.OutputBuffer, &iface, sizeof(gcsHAL_INTERFACE)
+ );
+
+ if (copyLen != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): error copying of output HAL interface.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return 0;
+
+OnError:
+ gcmkFOOTER();
+ return -ENOTTY;
+}
+
+static int drv_mmap(
+ struct file* filp,
+ struct vm_area_struct* vma
+ )
+{
+ gceSTATUS status;
+ gcsHAL_PRIVATE_DATA_PTR data;
+ gckGALDEVICE device;
+
+ gcmkHEADER_ARG("filp=0x%08X vma=0x%08X", filp, vma);
+
+ if (filp == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): filp is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ data = filp->private_data;
+
+ if (data == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): private_data is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ device = data->device;
+
+ if (device == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): device is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+#if !gcdPAGED_MEMORY_CACHEABLE
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND;
+#endif
+ vma->vm_pgoff = 0;
+
+ if (device->contiguousMapped)
+ {
+ unsigned long size = vma->vm_end - vma->vm_start;
+
+ int ret = io_remap_pfn_range(
+ vma,
+ vma->vm_start,
+ (gctUINT32) device->contiguousPhysical >> PAGE_SHIFT,
+ size,
+ vma->vm_page_prot
+ );
+
+ if (ret != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): io_remap_pfn_range failed %d\n",
+ __FUNCTION__, __LINE__,
+ ret
+ );
+
+ data->mappedMemory = gcvNULL;
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ data->mappedMemory = (gctPOINTER) vma->vm_start;
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return 0;
+
+OnError:
+ gcmkFOOTER();
+ return -ENOTTY;
+}
+
+
+#if !USE_PLATFORM_DRIVER
+static int __init drv_init(void)
+#else
+static int drv_init(void)
+#endif
+{
+ int ret;
+ int result = -EINVAL;
+ gceSTATUS status;
+ gckGALDEVICE device = gcvNULL;
+ struct class* device_class = gcvNULL;
+
+ gcmkHEADER();
+
+#if ENABLE_GPU_CLOCK_BY_DRIVER && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
+ {
+# if 0
+ struct clk * clk;
+
+ clk = clk_get(NULL, "GCCLK");
+
+ if (IS_ERR(clk))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): clk get error: %d\n",
+ __FUNCTION__, __LINE__,
+ PTR_ERR(clk)
+ );
+
+ result = -ENODEV;
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /*
+ * APMU_GC_156M, APMU_GC_312M, APMU_GC_PLL2, APMU_GC_PLL2_DIV2 currently.
+ * Use the 2X clock.
+ */
+ if (clk_set_rate(clk, coreClock * 2))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Failed to set core clock.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ result = -EAGAIN;
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ clk_enable(clk);
+
+#if defined(CONFIG_PXA_DVFM) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
+ gc_pwr(1);
+# endif
+# else
+ if (irqLine != -1) {
+ clk_3d_core = clk_get(NULL, "gpu3d_clk");
+ if (!IS_ERR(clk_3d_core)) {
+ clk_3d_shader = clk_get(NULL, "gpu3d_shader_clk");
+ if (!IS_ERR(clk_3d_shader)) {
+ clk_enable(clk_3d_core);
+ clk_enable(clk_3d_shader);
+ } else {
+ irqLine = -1;
+ clk_put(clk_3d_core);
+ clk_3d_core = NULL;
+ clk_3d_shader = NULL;
+ printk(KERN_ERR "galcore: clk_get gpu3d_shader_clk failed, disable 3d!\n");
+ }
+ } else {
+ irqLine = -1;
+ clk_3d_core = NULL;
+ printk(KERN_ERR "galcore: clk_get gpu3d_clk failed, disable 3d!\n");
+ }
+ }
+ if ((irqLine2D != -1) || (irqLineVG != -1)) {
+ clk_2d_core = clk_get(NULL, "gpu2d_clk");
+ if (IS_ERR(clk_2d_core)) {
+ irqLine2D = -1;
+ irqLineVG = -1;
+ clk_2d_core = NULL;
+ printk(KERN_ERR "galcore: clk_get 2d clock failed, disable 2d/vg!\n");
+ } else {
+ clk_enable(clk_2d_core);
+ }
+ }
+# endif
+ }
+#endif
+
+ if (showArgs)
+ {
+ printk("galcore options:\n");
+ printk(" irqLine = %d\n", irqLine);
+ printk(" registerMemBase = 0x%08lX\n", registerMemBase);
+ printk(" registerMemSize = 0x%08lX\n", registerMemSize);
+
+ if (irqLine2D != -1)
+ {
+ printk(" irqLine2D = %d\n", irqLine2D);
+ printk(" registerMemBase2D = 0x%08lX\n", registerMemBase2D);
+ printk(" registerMemSize2D = 0x%08lX\n", registerMemSize2D);
+ }
+
+ if (irqLineVG != -1)
+ {
+ printk(" irqLineVG = %d\n", irqLineVG);
+ printk(" registerMemBaseVG = 0x%08lX\n", registerMemBaseVG);
+ printk(" registerMemSizeVG = 0x%08lX\n", registerMemSizeVG);
+ }
+
+ printk(" contiguousSize = %ld\n", contiguousSize);
+ printk(" contiguousBase = 0x%08lX\n", contiguousBase);
+ printk(" bankSize = 0x%08lX\n", bankSize);
+ printk(" fastClear = %d\n", fastClear);
+ printk(" compression = %d\n", compression);
+ printk(" signal = %d\n", signal);
+ printk(" baseAddress = 0x%08lX\n", baseAddress);
+ printk(" physSize = 0x%08lX\n", physSize);
+#if ENABLE_GPU_CLOCK_BY_DRIVER
+ printk(" coreClock = %lu\n", coreClock);
+#endif
+ }
+
+ /* Create the GAL device. */
+ gcmkONERROR(gckGALDEVICE_Construct(
+ irqLine,
+ registerMemBase, registerMemSize,
+ irqLine2D,
+ registerMemBase2D, registerMemSize2D,
+ irqLineVG,
+ registerMemBaseVG, registerMemSizeVG,
+ contiguousBase, contiguousSize,
+ bankSize, fastClear, compression, baseAddress, physSize, signal,
+ &device
+ ));
+
+ /* Start the GAL device. */
+ gcmkONERROR(gckGALDEVICE_Start(device));
+
+ if ((physSize != 0)
+ && (device->kernels[gcvCORE_MAJOR] != gcvNULL)
+ && (device->kernels[gcvCORE_MAJOR]->hardware->mmuVersion != 0))
+ {
+ status = gckMMU_Enable(device->kernels[gcvCORE_MAJOR]->mmu, baseAddress, physSize);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Enable new MMU: status=%d\n", status);
+
+ if ((device->kernels[gcvCORE_2D] != gcvNULL)
+ && (device->kernels[gcvCORE_2D]->hardware->mmuVersion != 0))
+ {
+ status = gckMMU_Enable(device->kernels[gcvCORE_2D]->mmu, baseAddress, physSize);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Enable new MMU for 2D: status=%d\n", status);
+ }
+
+ /* Reset the base address */
+ device->baseAddress = 0;
+ }
+
+ /* Register the character device. */
+ ret = register_chrdev(major, DRV_NAME, &driver_fops);
+
+ if (ret < 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not allocate major number for mmap.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ if (major == 0)
+ {
+ major = ret;
+ }
+
+ /* Create the device class. */
+ device_class = class_create(THIS_MODULE, "graphics_class");
+
+ if (IS_ERR(device_class))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Failed to create the class.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+ device_create(device_class, NULL, MKDEV(major, 0), NULL, "galcore");
+#else
+ device_create(device_class, NULL, MKDEV(major, 0), "galcore");
+#endif
+
+ galDevice = device;
+ gpuClass = device_class;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "%s(%d): irqLine=%d, contiguousSize=%lu, memBase=0x%lX\n",
+ __FUNCTION__, __LINE__,
+ irqLine, contiguousSize, registerMemBase
+ );
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return 0;
+
+OnError:
+ /* Roll back. */
+ if (device_class != gcvNULL)
+ {
+ device_destroy(device_class, MKDEV(major, 0));
+ class_destroy(device_class);
+ }
+
+ if (device != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckGALDEVICE_Stop(device));
+ gcmkVERIFY_OK(gckGALDEVICE_Destroy(device));
+ }
+
+ gcmkFOOTER();
+ return result;
+}
+
+#if !USE_PLATFORM_DRIVER
+static void __exit drv_exit(void)
+#else
+static void drv_exit(void)
+#endif
+{
+ gcmkHEADER();
+
+ gcmkASSERT(gpuClass != gcvNULL);
+ device_destroy(gpuClass, MKDEV(major, 0));
+ class_destroy(gpuClass);
+
+ unregister_chrdev(major, DRV_NAME);
+
+ gcmkVERIFY_OK(gckGALDEVICE_Stop(galDevice));
+ gcmkVERIFY_OK(gckGALDEVICE_Destroy(galDevice));
+
+#if ENABLE_GPU_CLOCK_BY_DRIVER && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+ {
+# if 0
+ struct clk * clk = NULL;
+
+#if defined(CONFIG_PXA_DVFM) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
+ gc_pwr(0);
+#endif
+ clk = clk_get(NULL, "GCCLK");
+ clk_disable(clk);
+# else
+ if (clk_3d_core) {
+ clk_disable(clk_3d_core);
+ clk_put(clk_3d_core);
+ clk_3d_core = NULL;
+ }
+ if (clk_3d_shader) {
+ clk_disable(clk_3d_shader);
+ clk_put(clk_3d_shader);
+ clk_3d_shader = NULL;
+ }
+ if (clk_2d_core) {
+ clk_disable(clk_2d_core);
+ clk_put(clk_2d_core);
+ clk_2d_core = NULL;
+ }
+# endif
+ }
+#endif
+
+ gcmkFOOTER_NO();
+}
+
+#if !USE_PLATFORM_DRIVER
+ module_init(drv_init);
+ module_exit(drv_exit);
+#else
+
+#ifdef CONFIG_DOVE_GPU
+# define DEVICE_NAME "dove_gpu"
+#else
+# define DEVICE_NAME "galcore"
+#endif
+
+static int __devinit gpu_probe(struct platform_device *pdev)
+{
+ int ret = -ENODEV;
+ struct resource* res;
+ struct viv_gpu_platform_data *pdata;
+
+ gcmkHEADER();
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq_3d");
+ if (res)
+ irqLine = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iobase_3d");
+ if (res)
+ {
+ registerMemBase = res->start;
+ registerMemSize = res->end - res->start + 1;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq_2d");
+ if (res)
+ irqLine2D = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iobase_2d");
+ if (res)
+ {
+ registerMemBase2D = res->start;
+ registerMemSize2D = res->end - res->start + 1;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq_vg");
+ if (res)
+ irqLineVG = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iobase_vg");
+ if (res)
+ {
+ registerMemBaseVG = res->start;
+ registerMemSizeVG = res->end - res->start + 1;
+ }
+
+ pdata = pdev->dev.platform_data;
+ if (pdata) {
+ contiguousBase = pdata->reserved_mem_base;
+ contiguousSize = pdata->reserved_mem_size;
+ }
+
+ ret = drv_init();
+
+ if (!ret)
+ {
+ platform_set_drvdata(pdev, galDevice);
+
+ gcmkFOOTER_NO();
+ return ret;
+ }
+
+ gcmkFOOTER_ARG(KERN_INFO "Failed to register gpu driver: %d\n", ret);
+ return ret;
+}
+
+static int __devinit gpu_remove(struct platform_device *pdev)
+{
+ gcmkHEADER();
+ drv_exit();
+ gcmkFOOTER_NO();
+ return 0;
+}
+
+static int __devinit gpu_suspend(struct platform_device *dev, pm_message_t state)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+ gctINT i;
+
+ device = platform_get_drvdata(dev);
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->kernels[i] != gcvNULL)
+ {
+#if gcdENABLE_VG
+ if (i != gcvCORE_VG)
+#endif
+ {
+ status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_OFF);
+
+ if (gcmIS_ERROR(status))
+ {
+ return -1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int __devinit gpu_resume(struct platform_device *dev)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+ gctINT i;
+
+ device = platform_get_drvdata(dev);
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->kernels[i] != gcvNULL)
+ {
+#if gcdENABLE_VG
+ if (i != gcvCORE_VG)
+#endif
+ {
+ status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_ON);
+
+ if (gcmIS_ERROR(status))
+ {
+ return -1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver gpu_driver = {
+ .probe = gpu_probe,
+ .remove = gpu_remove,
+
+ .suspend = gpu_suspend,
+ .resume = gpu_resume,
+
+ .driver = {
+ .name = DEVICE_NAME,
+ }
+};
+
+#if 0 /*CONFIG_DOVE_GPU*/
+static struct resource gpu_resources[] = {
+ {
+ .name = "gpu_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "gpu_base",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "gpu_mem",
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device * gpu_device;
+#endif
+
+static int __init gpu_init(void)
+{
+ int ret = 0;
+
+#if 0 /*ndef CONFIG_DOVE_GPU*/
+ gpu_resources[0].start = gpu_resources[0].end = irqLine;
+
+ gpu_resources[1].start = registerMemBase;
+ gpu_resources[1].end = registerMemBase + registerMemSize - 1;
+
+ gpu_resources[2].start = contiguousBase;
+ gpu_resources[2].end = contiguousBase + contiguousSize - 1;
+
+ /* Allocate device */
+ gpu_device = platform_device_alloc(DEVICE_NAME, -1);
+ if (!gpu_device)
+ {
+ printk(KERN_ERR "galcore: platform_device_alloc failed.\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Insert resource */
+ ret = platform_device_add_resources(gpu_device, gpu_resources, 3);
+ if (ret)
+ {
+ printk(KERN_ERR "galcore: platform_device_add_resources failed.\n");
+ goto put_dev;
+ }
+
+ /* Add device */
+ ret = platform_device_add(gpu_device);
+ if (ret)
+ {
+ printk(KERN_ERR "galcore: platform_device_add failed.\n");
+ goto put_dev;
+ }
+#endif
+
+ ret = platform_driver_register(&gpu_driver);
+ if (!ret)
+ {
+ goto out;
+ }
+
+#if 0 /*ndef CONFIG_DOVE_GPU*/
+ platform_device_del(gpu_device);
+put_dev:
+ platform_device_put(gpu_device);
+#endif
+
+out:
+ return ret;
+}
+
+static void __exit gpu_exit(void)
+{
+ platform_driver_unregister(&gpu_driver);
+#if 0 /*ndef CONFIG_DOVE_GPU*/
+ platform_device_unregister(gpu_device);
+#endif
+}
+
+module_init(gpu_init);
+module_exit(gpu_exit);
+
+#endif
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.c
new file mode 100644
index 00000000000..497cf5571d8
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.c
@@ -0,0 +1,470 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#include "gc_hal_kernel_linux.h"
+
+#define _GC_OBJ_ZONE gcvZONE_KERNEL
+
+/******************************************************************************\
+******************************* gckKERNEL API Code ******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckKERNEL_QueryVideoMemory
+**
+** Query the amount of video memory.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** OUTPUT:
+**
+** gcsHAL_INTERFACE * Interface
+** Pointer to an gcsHAL_INTERFACE structure that will be filled in with
+** the memory information.
+*/
+gceSTATUS
+gckKERNEL_QueryVideoMemory(
+ IN gckKERNEL Kernel,
+ OUT gcsHAL_INTERFACE * Interface
+ )
+{
+ gckGALDEVICE device;
+
+ gcmkHEADER_ARG("Kernel=%p", Kernel);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Interface != NULL);
+
+ /* Extract the pointer to the gckGALDEVICE class. */
+ device = (gckGALDEVICE) Kernel->context;
+
+ /* Get internal memory size and physical address. */
+ Interface->u.QueryVideoMemory.internalSize = device->internalSize;
+ Interface->u.QueryVideoMemory.internalPhysical = device->internalPhysical;
+
+ /* Get external memory size and physical address. */
+ Interface->u.QueryVideoMemory.externalSize = device->externalSize;
+ Interface->u.QueryVideoMemory.externalPhysical = device->externalPhysical;
+
+ /* Get contiguous memory size and physical address. */
+ Interface->u.QueryVideoMemory.contiguousSize = device->contiguousSize;
+ Interface->u.QueryVideoMemory.contiguousPhysical = device->contiguousPhysical;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_GetVideoMemoryPool
+**
+** Get the gckVIDMEM object belonging to the specified pool.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gcePOOL Pool
+** Pool to query gckVIDMEM object for.
+**
+** OUTPUT:
+**
+** gckVIDMEM * VideoMemory
+** Pointer to a variable that will hold the pointer to the gckVIDMEM
+** object belonging to the requested pool.
+*/
+gceSTATUS
+gckKERNEL_GetVideoMemoryPool(
+ IN gckKERNEL Kernel,
+ IN gcePOOL Pool,
+ OUT gckVIDMEM * VideoMemory
+ )
+{
+ gckGALDEVICE device;
+ gckVIDMEM videoMemory;
+
+ gcmkHEADER_ARG("Kernel=%p Pool=%d", Kernel, Pool);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(VideoMemory != NULL);
+
+ /* Extract the pointer to the gckGALDEVICE class. */
+ device = (gckGALDEVICE) Kernel->context;
+
+ /* Dispatch on pool. */
+ switch (Pool)
+ {
+ case gcvPOOL_LOCAL_INTERNAL:
+ /* Internal memory. */
+ videoMemory = device->internalVidMem;
+ break;
+
+ case gcvPOOL_LOCAL_EXTERNAL:
+ /* External memory. */
+ videoMemory = device->externalVidMem;
+ break;
+
+ case gcvPOOL_SYSTEM:
+ /* System memory. */
+ videoMemory = device->contiguousVidMem;
+ break;
+
+ default:
+ /* Unknown pool. */
+ videoMemory = NULL;
+ }
+
+ /* Return pointer to the gckVIDMEM object. */
+ *VideoMemory = videoMemory;
+
+ /* Return status. */
+ gcmkFOOTER_ARG("*VideoMemory=%p", *VideoMemory);
+ return (videoMemory == NULL) ? gcvSTATUS_OUT_OF_MEMORY : gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_MapMemory
+**
+** Map video memory into the current process space.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of video memory to map.
+**
+** gctSIZE_T Bytes
+** Number of bytes to map.
+**
+** OUTPUT:
+**
+** gctPOINTER * Logical
+** Pointer to a variable that will hold the base address of the mapped
+** memory region.
+*/
+gceSTATUS
+gckKERNEL_MapMemory(
+ IN gckKERNEL Kernel,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes,
+ OUT gctPOINTER * Logical
+ )
+{
+ return gckOS_MapMemory(Kernel->os, Physical, Bytes, Logical);
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_UnmapMemory
+**
+** Unmap video memory from the current process space.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of video memory to map.
+**
+** gctSIZE_T Bytes
+** Number of bytes to map.
+**
+** gctPOINTER Logical
+** Base address of the mapped memory region.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_UnmapMemory(
+ IN gckKERNEL Kernel,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes,
+ IN gctPOINTER Logical
+ )
+{
+ return gckOS_UnmapMemory(Kernel->os, Physical, Bytes, Logical);
+}
+
+/*******************************************************************************
+**
+** 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
+ )
+{
+ gckGALDEVICE device;
+ PLINUX_MDL mdl;
+ PLINUX_MDL_MAP mdlMap;
+ gcePOOL pool;
+ gctUINT32 offset, base;
+ gceSTATUS status;
+ gctPOINTER logical;
+
+ gcmkHEADER_ARG("Kernel=%p InUserSpace=%d Address=%08x",
+ Kernel, InUserSpace, Address);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+
+ /* Extract the pointer to the gckGALDEVICE class. */
+ device = (gckGALDEVICE) Kernel->context;
+
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ /* Split the memory address into a pool type and offset. */
+ gcmkONERROR(
+ gckVGHARDWARE_SplitMemory(Kernel->vg->hardware, Address, &pool, &offset));
+ }
+ else
+#endif
+ {
+ /* Split the memory address into a pool type and offset. */
+ gcmkONERROR(
+ gckHARDWARE_SplitMemory(Kernel->hardware, Address, &pool, &offset));
+ }
+
+ /* Dispatch on pool. */
+ switch (pool)
+ {
+ case gcvPOOL_LOCAL_INTERNAL:
+ /* Internal memory. */
+ logical = device->internalLogical;
+ break;
+
+ case gcvPOOL_LOCAL_EXTERNAL:
+ /* External memory. */
+ logical = device->externalLogical;
+ break;
+
+ case gcvPOOL_SYSTEM:
+ /* System memory. */
+ if (device->contiguousMapped)
+ {
+ logical = device->contiguousBase;
+ }
+ else
+ {
+ gctINT processID;
+ gckOS_GetProcessID(&processID);
+
+ mdl = (PLINUX_MDL) device->contiguousPhysical;
+
+ mdlMap = FindMdlMap(mdl, processID);
+ gcmkASSERT(mdlMap);
+
+ logical = (gctPOINTER) mdlMap->vmaAddr;
+ }
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ gcmkVERIFY_OK(
+ gckVGHARDWARE_SplitMemory(Kernel->vg->hardware,
+ device->contiguousVidMem->baseAddress,
+ &pool,
+ &base));
+ }
+ else
+#endif
+ {
+ gcmkVERIFY_OK(
+ gckHARDWARE_SplitMemory(Kernel->hardware,
+ device->contiguousVidMem->baseAddress,
+ &pool,
+ &base));
+ }
+ offset -= base;
+ break;
+
+ default:
+ /* Invalid memory pool. */
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Build logical address of specified address. */
+ *Logical = (gctPOINTER) ((gctUINT8_PTR) logical + offset);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Logical=%p", *Logical);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Retunn the status. */
+ gcmkFOOTER();
+ 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_MapVideoMemory(
+ IN gckKERNEL Kernel,
+ IN gctBOOL InUserSpace,
+ IN gctUINT32 Address,
+ OUT gctPOINTER * Logical
+ )
+{
+ return gckKERNEL_MapVideoMemoryEx(Kernel, gcvCORE_MAJOR, InUserSpace, Address, Logical);
+}
+/*******************************************************************************
+**
+** gckKERNEL_Notify
+**
+** This function iscalled by clients to notify the gckKERNRL object of an event.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gceNOTIFY Notification
+** Notification event.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_Notify(
+ IN gckKERNEL Kernel,
+ IN gceNOTIFY Notification,
+ IN gctBOOL Data
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Kernel=%p Notification=%d Data=%d",
+ Kernel, Notification, Data);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ /* Dispatch on notifcation. */
+ switch (Notification)
+ {
+ case gcvNOTIFY_INTERRUPT:
+ /* Process the interrupt. */
+#if COMMAND_PROCESSOR_VERSION > 1
+ status = gckINTERRUPT_Notify(Kernel->interrupt, Data);
+#else
+ status = gckHARDWARE_Interrupt(Kernel->hardware, Data);
+#endif
+ break;
+
+ default:
+ status = gcvSTATUS_OK;
+ break;
+ }
+
+ /* Success. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckKERNEL_QuerySettings(
+ IN gckKERNEL Kernel,
+ OUT gcsKERNEL_SETTINGS * Settings
+ )
+{
+ gckGALDEVICE device;
+
+ gcmkHEADER_ARG("Kernel=%p", Kernel);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Settings != gcvNULL);
+
+ /* Extract the pointer to the gckGALDEVICE class. */
+ device = (gckGALDEVICE) Kernel->context;
+
+ /* Fill in signal. */
+ Settings->signal = device->signal;
+
+ /* Success. */
+ gcmkFOOTER_ARG("Settings->signal=%d", Settings->signal);
+ return gcvSTATUS_OK;
+}
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h
new file mode 100644
index 00000000000..067c7400859
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_linux.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#ifndef __gc_hal_kernel_linux_h_
+#define __gc_hal_kernel_linux_h_
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#ifdef FLAREON
+# include <asm/arch-realview/dove_gpio_irq.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/kthread.h>
+
+#ifdef MODVERSIONS
+# include <linux/modversions.h>
+#endif
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#if ENABLE_GPU_CLOCK_BY_DRIVER && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+#include <linux/clk.h>
+#endif
+
+#define NTSTRSAFE_NO_CCH_FUNCTIONS
+#include "gc_hal.h"
+#include "gc_hal_driver.h"
+#include "gc_hal_kernel.h"
+#include "gc_hal_kernel_device.h"
+#include "gc_hal_kernel_os.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
+#define FIND_TASK_BY_PID(x) pid_task(find_vpid(x), PIDTYPE_PID)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#define FIND_TASK_BY_PID(x) find_task_by_vpid(x)
+#else
+#define FIND_TASK_BY_PID(x) find_task_by_pid(x)
+#endif
+
+#define _WIDE(string) L##string
+#define WIDE(string) _WIDE(string)
+
+#define countof(a) (sizeof(a) / sizeof(a[0]))
+
+#define DRV_NAME "galcore"
+
+#define GetPageCount(size, offset) ((((size) + ((offset) & ~PAGE_CACHE_MASK)) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
+
+static inline gctINT
+GetOrder(
+ IN gctINT numPages
+ )
+{
+ gctINT order = 0;
+
+ while ((1 << order) < numPages) order++;
+
+ return order;
+}
+
+#endif /* __gc_hal_kernel_linux_h_ */
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_math.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_math.c
new file mode 100644
index 00000000000..80e44957634
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_math.c
@@ -0,0 +1,34 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#include "gc_hal_kernel_linux.h"
+
+gctINT
+gckMATH_ModuloInt(
+ IN gctINT X,
+ IN gctINT Y
+ )
+{
+ if(Y ==0) {return 0;}
+ else {return X % Y;}
+}
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
new file mode 100644
index 00000000000..979493830c7
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
@@ -0,0 +1,7714 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#include "gc_hal_kernel_linux.h"
+
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/sched.h>
+#include <asm/atomic.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+#include <linux/math64.h>
+#endif
+
+#define _GC_OBJ_ZONE gcvZONE_OS
+
+/*******************************************************************************
+***** Version Signature *******************************************************/
+
+#ifdef ANDROID
+const char * _PLATFORM = "\n\0$PLATFORM$Android$\n";
+#else
+const char * _PLATFORM = "\n\0$PLATFORM$Linux$\n";
+#endif
+
+#define USER_SIGNAL_TABLE_LEN_INIT 64
+
+#define MEMORY_LOCK(os) \
+ gcmkVERIFY_OK(gckOS_AcquireMutex( \
+ (os), \
+ (os)->memoryLock, \
+ gcvINFINITE))
+
+#define MEMORY_UNLOCK(os) \
+ gcmkVERIFY_OK(gckOS_ReleaseMutex((os), (os)->memoryLock))
+
+#define MEMORY_MAP_LOCK(os) \
+ gcmkVERIFY_OK(gckOS_AcquireMutex( \
+ (os), \
+ (os)->memoryMapLock, \
+ gcvINFINITE))
+
+#define MEMORY_MAP_UNLOCK(os) \
+ gcmkVERIFY_OK(gckOS_ReleaseMutex((os), (os)->memoryMapLock))
+
+#define gcdINFINITE_TIMEOUT (60 * 1000)
+#define gcdDETECT_TIMEOUT 0
+#define gcdDETECT_DMA_ADDRESS 1
+#define gcdDETECT_DMA_STATE 1
+
+/******************************************************************************\
+********************************** Structures **********************************
+\******************************************************************************/
+
+typedef struct _gcsUSER_MAPPING * gcsUSER_MAPPING_PTR;
+typedef struct _gcsUSER_MAPPING
+{
+ /* Pointer to next mapping structure. */
+ gcsUSER_MAPPING_PTR next;
+
+ /* Physical address of this mapping. */
+ gctUINT32 physical;
+
+ /* Logical address of this mapping. */
+ gctPOINTER logical;
+
+ /* Number of bytes of this mapping. */
+ gctSIZE_T bytes;
+
+ /* Starting address of this mapping. */
+ gctINT8_PTR start;
+
+ /* Ending address of this mapping. */
+ gctINT8_PTR end;
+}
+gcsUSER_MAPPING;
+
+struct _gckOS
+{
+ /* Object. */
+ gcsOBJECT object;
+
+ /* Heap. */
+ gckHEAP heap;
+
+ /* Pointer to device */
+ gckGALDEVICE device;
+
+ /* Memory management */
+ gctPOINTER memoryLock;
+ gctPOINTER memoryMapLock;
+
+ struct _LINUX_MDL *mdlHead;
+ struct _LINUX_MDL *mdlTail;
+
+ /* Kernel process ID. */
+ gctUINT32 kernelProcessID;
+
+ /* Signal management. */
+ struct _signal
+ {
+ /* Unused signal ID number. */
+ gctINT unused;
+
+ /* The pointer to the table. */
+ gctPOINTER * table;
+
+ /* Signal table length. */
+ gctINT tableLen;
+
+ /* The current unused signal ID. */
+ gctINT currentID;
+
+ /* Lock. */
+ gctPOINTER lock;
+ }
+ signal;
+
+ gcsUSER_MAPPING_PTR userMap;
+ gctPOINTER debugLock;
+};
+
+typedef struct _gcsSIGNAL * gcsSIGNAL_PTR;
+typedef struct _gcsSIGNAL
+{
+ /* Kernel sync primitive. */
+ struct completion obj;
+
+ /* Manual reset flag. */
+ gctBOOL manualReset;
+
+ /* The reference counter. */
+ atomic_t ref;
+
+ /* The owner of the signal. */
+ gctHANDLE process;
+}
+gcsSIGNAL;
+
+typedef struct _gcsPageInfo * gcsPageInfo_PTR;
+typedef struct _gcsPageInfo
+{
+ struct page **pages;
+ gctUINT32_PTR pageTable;
+}
+gcsPageInfo;
+
+typedef struct _gcsiDEBUG_REGISTERS * gcsiDEBUG_REGISTERS_PTR;
+typedef struct _gcsiDEBUG_REGISTERS
+{
+ gctSTRING module;
+ gctUINT index;
+ gctUINT shift;
+ gctUINT data;
+ gctUINT count;
+ gctUINT32 signature;
+}
+gcsiDEBUG_REGISTERS;
+
+
+/******************************************************************************\
+******************************* Private Functions ******************************
+\******************************************************************************/
+
+static gceSTATUS
+_VerifyDMA(
+ IN gckOS Os,
+ IN gceCORE Core,
+ gctUINT32_PTR Address1,
+ gctUINT32_PTR Address2,
+ gctUINT32_PTR State1,
+ gctUINT32_PTR State2
+ )
+{
+ gceSTATUS status;
+ gctUINT32 i;
+
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x660, State1));
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x664, Address1));
+
+ for (i = 0; i < 500; i += 1)
+ {
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x660, State2));
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x664, Address2));
+
+ if (*Address1 != *Address2)
+ {
+ break;
+ }
+
+#if gcdDETECT_DMA_STATE
+ if (*State1 != *State2)
+ {
+ break;
+ }
+#endif
+ }
+
+OnError:
+ return status;
+}
+
+static gceSTATUS
+_DumpDebugRegisters(
+ IN gckOS Os,
+ IN gcsiDEBUG_REGISTERS_PTR Descriptor
+ )
+{
+ gceSTATUS status;
+ gctUINT32 select;
+ gctUINT32 data;
+ gctUINT i;
+
+ gcmkHEADER_ARG("Os=0x%X Descriptor=0x%X", Os, Descriptor);
+
+ gcmkPRINT_N(4, " %s debug registers:\n", Descriptor->module);
+
+ select = 0xF << Descriptor->shift;
+
+ for (i = 0; i < 500; i += 1)
+ {
+ gcmkONERROR(gckOS_WriteRegister(Os, Descriptor->index, select));
+ gcmkONERROR(gckOS_Delay(Os, 1000));
+ gcmkONERROR(gckOS_ReadRegister(Os, Descriptor->data, &data));
+
+ if (data == Descriptor->signature)
+ {
+ break;
+ }
+ }
+
+ if (i == 500)
+ {
+ gcmkPRINT_N(4, " failed to obtain the signature (read 0x%08X).\n", data);
+ }
+ else
+ {
+ gcmkPRINT_N(8, " signature = 0x%08X (%d read attempt(s))\n", data, i + 1);
+ }
+
+ for (i = 0; i < Descriptor->count; i += 1)
+ {
+ select = i << Descriptor->shift;
+
+ gcmkONERROR(gckOS_WriteRegister(Os, Descriptor->index, select));
+ gcmkONERROR(gckOS_Delay(Os, 1000));
+ gcmkONERROR(gckOS_ReadRegister(Os, Descriptor->data, &data));
+
+ gcmkPRINT_N(12, " [0x%02X] 0x%08X\n", i, data);
+ }
+
+OnError:
+ /* Return the error. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+_DumpGPUState(
+ IN gckOS Os
+ )
+{
+ static gctCONST_STRING _cmdState[] =
+ {
+ "PAR_IDLE_ST", "PAR_DEC_ST", "PAR_ADR0_ST", "PAR_LOAD0_ST",
+ "PAR_ADR1_ST", "PAR_LOAD1_ST", "PAR_3DADR_ST", "PAR_3DCMD_ST",
+ "PAR_3DCNTL_ST", "PAR_3DIDXCNTL_ST", "PAR_INITREQDMA_ST",
+ "PAR_DRAWIDX_ST", "PAR_DRAW_ST", "PAR_2DRECT0_ST", "PAR_2DRECT1_ST",
+ "PAR_2DDATA0_ST", "PAR_2DDATA1_ST", "PAR_WAITFIFO_ST", "PAR_WAIT_ST",
+ "PAR_LINK_ST", "PAR_END_ST", "PAR_STALL_ST"
+ };
+
+ static gctCONST_STRING _cmdDmaState[] =
+ {
+ "CMD_IDLE_ST", "CMD_START_ST", "CMD_REQ_ST", "CMD_END_ST"
+ };
+
+ static gctCONST_STRING _cmdFetState[] =
+ {
+ "FET_IDLE_ST", "FET_RAMVALID_ST", "FET_VALID_ST"
+ };
+
+ static gctCONST_STRING _reqDmaState[] =
+ {
+ "REQ_IDLE_ST", "REQ_WAITIDX_ST", "REQ_CAL_ST"
+ };
+
+ static gctCONST_STRING _calState[] =
+ {
+ "CAL_IDLE_ST", "CAL_LDADR_ST", "CAL_IDXCALC_ST"
+ };
+
+ static gctCONST_STRING _veReqState[] =
+ {
+ "VER_IDLE_ST", "VER_CKCACHE_ST", "VER_MISS_ST"
+ };
+
+ static gcsiDEBUG_REGISTERS _dbgRegs[] =
+ {
+ { "RA", 0x474, 16, 0x448, 4, 0x12344321 },
+ { "TX", 0x474, 24, 0x44C, 4, 0x12211221 },
+ { "FE", 0x470, 0, 0x450, 4, 0xBABEF00D },
+ { "PE", 0x470, 16, 0x454, 4, 0xBABEF00D },
+ { "DE", 0x470, 8, 0x458, 4, 0xBABEF00D },
+ { "SH", 0x470, 24, 0x45C, 15, 0xDEADBEEF },
+ { "PA", 0x474, 0, 0x460, 4, 0x0000AAAA },
+ { "SE", 0x474, 8, 0x464, 4, 0x5E5E5E5E },
+ { "MC", 0x478, 0, 0x468, 4, 0x12345678 },
+ { "HI", 0x478, 8, 0x46C, 4, 0xAAAAAAAA }
+ };
+
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+ gckGALDEVICE device;
+ gckKERNEL kernel;
+ gctUINT32 idle, axi;
+ gctUINT32 dmaAddress1, dmaAddress2;
+ gctUINT32 dmaState1, dmaState2;
+ gctUINT32 dmaLow, dmaHigh;
+ gctUINT32 cmdState, cmdDmaState, cmdFetState;
+ gctUINT32 dmaReqState, calState, veReqState;
+ gctUINT i;
+
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ gcmkONERROR(gckOS_AcquireMutex(Os, Os->debugLock, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Extract the pointer to the gckGALDEVICE class. */
+ device = (gckGALDEVICE) Os->device;
+
+ /* TODO: Kernel shortcut. */
+ kernel = device->kernels[gcvCORE_MAJOR];
+
+ if (kernel == gcvNULL) return gcvSTATUS_OK;
+
+ /* Reset register values. */
+ idle = axi =
+ dmaState1 = dmaState2 =
+ dmaAddress1 = dmaAddress2 =
+ dmaLow = dmaHigh = 0;
+
+ /* Verify whether DMA is running. */
+ gcmkONERROR(_VerifyDMA(
+ Os, kernel->core, &dmaAddress1, &dmaAddress2, &dmaState1, &dmaState2
+ ));
+
+ cmdState = dmaState2 & 0x1F;
+ cmdDmaState = (dmaState2 >> 8) & 0x03;
+ cmdFetState = (dmaState2 >> 10) & 0x03;
+ dmaReqState = (dmaState2 >> 12) & 0x03;
+ calState = (dmaState2 >> 14) & 0x03;
+ veReqState = (dmaState2 >> 16) & 0x03;
+
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, 0x004, &idle));
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, 0x00C, &axi));
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, 0x668, &dmaLow));
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, 0x66C, &dmaHigh));
+
+ gcmkPRINT_N(0, "**************************\n");
+ gcmkPRINT_N(0, "*** GPU STATE DUMP ***\n");
+ gcmkPRINT_N(0, "**************************\n");
+
+ gcmkPRINT_N(4, " axi = 0x%08X\n", axi);
+
+ gcmkPRINT_N(4, " idle = 0x%08X\n", idle);
+ if ((idle & 0x00000001) == 0) gcmkPRINT_N(0, " FE not idle\n");
+ if ((idle & 0x00000002) == 0) gcmkPRINT_N(0, " DE not idle\n");
+ if ((idle & 0x00000004) == 0) gcmkPRINT_N(0, " PE not idle\n");
+ if ((idle & 0x00000008) == 0) gcmkPRINT_N(0, " SH not idle\n");
+ if ((idle & 0x00000010) == 0) gcmkPRINT_N(0, " PA not idle\n");
+ if ((idle & 0x00000020) == 0) gcmkPRINT_N(0, " SE not idle\n");
+ if ((idle & 0x00000040) == 0) gcmkPRINT_N(0, " RA not idle\n");
+ if ((idle & 0x00000080) == 0) gcmkPRINT_N(0, " TX not idle\n");
+ if ((idle & 0x00000100) == 0) gcmkPRINT_N(0, " VG not idle\n");
+ if ((idle & 0x00000200) == 0) gcmkPRINT_N(0, " IM not idle\n");
+ if ((idle & 0x00000400) == 0) gcmkPRINT_N(0, " FP not idle\n");
+ if ((idle & 0x00000800) == 0) gcmkPRINT_N(0, " TS not idle\n");
+ if ((idle & 0x80000000) != 0) gcmkPRINT_N(0, " AXI low power mode\n");
+
+ if (
+ (dmaAddress1 == dmaAddress2)
+
+#if gcdDETECT_DMA_STATE
+ && (dmaState1 == dmaState2)
+#endif
+ )
+ {
+ gcmkPRINT_N(0, " DMA appears to be stuck at this address:\n");
+ gcmkPRINT_N(4, " 0x%08X\n", dmaAddress1);
+ }
+ else
+ {
+ if (dmaAddress1 == dmaAddress2)
+ {
+ gcmkPRINT_N(0, " DMA address is constant, but state is changing:\n");
+ gcmkPRINT_N(4, " 0x%08X\n", dmaState1);
+ gcmkPRINT_N(4, " 0x%08X\n", dmaState2);
+ }
+ else
+ {
+ gcmkPRINT_N(0, " DMA is running; known addresses are:\n");
+ gcmkPRINT_N(4, " 0x%08X\n", dmaAddress1);
+ gcmkPRINT_N(4, " 0x%08X\n", dmaAddress2);
+ }
+ }
+
+ gcmkPRINT_N(4, " dmaLow = 0x%08X\n", dmaLow);
+ gcmkPRINT_N(4, " dmaHigh = 0x%08X\n", dmaHigh);
+ gcmkPRINT_N(4, " dmaState = 0x%08X\n", dmaState2);
+ gcmkPRINT_N(8, " command state = %d (%s)\n", cmdState, _cmdState [cmdState]);
+ gcmkPRINT_N(8, " command DMA state = %d (%s)\n", cmdDmaState, _cmdDmaState[cmdDmaState]);
+ gcmkPRINT_N(8, " command fetch state = %d (%s)\n", cmdFetState, _cmdFetState[cmdFetState]);
+ gcmkPRINT_N(8, " DMA request state = %d (%s)\n", dmaReqState, _reqDmaState[dmaReqState]);
+ gcmkPRINT_N(8, " cal state = %d (%s)\n", calState, _calState [calState]);
+ gcmkPRINT_N(8, " VE request state = %d (%s)\n", veReqState, _veReqState [veReqState]);
+
+ for (i = 0; i < gcmCOUNTOF(_dbgRegs); i += 1)
+ {
+ gcmkONERROR(_DumpDebugRegisters(Os, &_dbgRegs[i]));
+ }
+
+ if (kernel->hardware->chipFeatures & (1 << 4))
+ {
+ gctUINT32 read0, read1, write;
+
+ read0 = read1 = write = 0;
+
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, 0x43C, &read0));
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, 0x440, &read1));
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, 0x444, &write));
+
+ gcmkPRINT_N(4, " read0 = 0x%08X\n", read0);
+ gcmkPRINT_N(4, " read1 = 0x%08X\n", read1);
+ gcmkPRINT_N(4, " write = 0x%08X\n", write);
+ }
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->debugLock));
+ }
+
+ /* Return the error. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gctINT
+_GetProcessID(
+ void
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ return task_tgid_vnr(current);
+#else
+ return current->tgid;
+#endif
+}
+
+static gctINT
+_GetThreadID(
+ void
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ return task_pid_vnr(current);
+#else
+ return current->pid;
+#endif
+}
+
+static PLINUX_MDL
+_CreateMdl(
+ IN gctINT ProcessID
+ )
+{
+ PLINUX_MDL mdl;
+
+ gcmkHEADER_ARG("ProcessID=%d", ProcessID);
+
+ mdl = (PLINUX_MDL)kmalloc(sizeof(struct _LINUX_MDL), GFP_KERNEL);
+ if (mdl == gcvNULL)
+ {
+ gcmkFOOTER_NO();
+ return gcvNULL;
+ }
+
+ mdl->pid = ProcessID;
+ mdl->maps = gcvNULL;
+ mdl->prev = gcvNULL;
+ mdl->next = gcvNULL;
+
+ gcmkFOOTER_ARG("0x%X", mdl);
+ return mdl;
+}
+
+static gceSTATUS
+_DestroyMdlMap(
+ IN PLINUX_MDL Mdl,
+ IN PLINUX_MDL_MAP MdlMap
+ );
+
+static gceSTATUS
+_DestroyMdl(
+ IN PLINUX_MDL Mdl
+ )
+{
+ PLINUX_MDL_MAP mdlMap, next;
+
+ gcmkHEADER_ARG("Mdl=0x%X", Mdl);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Mdl != gcvNULL);
+
+ mdlMap = Mdl->maps;
+
+ while (mdlMap != gcvNULL)
+ {
+ next = mdlMap->next;
+
+ gcmkVERIFY_OK(_DestroyMdlMap(Mdl, mdlMap));
+
+ mdlMap = next;
+ }
+
+ kfree(Mdl);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+static PLINUX_MDL_MAP
+_CreateMdlMap(
+ IN PLINUX_MDL Mdl,
+ IN gctINT ProcessID
+ )
+{
+ PLINUX_MDL_MAP mdlMap;
+
+ gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID);
+
+ mdlMap = (PLINUX_MDL_MAP)kmalloc(sizeof(struct _LINUX_MDL_MAP), GFP_KERNEL);
+ if (mdlMap == gcvNULL)
+ {
+ gcmkFOOTER_NO();
+ return gcvNULL;
+ }
+
+ mdlMap->pid = ProcessID;
+ mdlMap->vmaAddr = gcvNULL;
+ mdlMap->vma = gcvNULL;
+
+ mdlMap->next = Mdl->maps;
+ Mdl->maps = mdlMap;
+
+ gcmkFOOTER_ARG("0x%X", mdlMap);
+ return mdlMap;
+}
+
+static gceSTATUS
+_DestroyMdlMap(
+ IN PLINUX_MDL Mdl,
+ IN PLINUX_MDL_MAP MdlMap
+ )
+{
+ PLINUX_MDL_MAP prevMdlMap;
+
+ gcmkHEADER_ARG("Mdl=0x%X MdlMap=0x%X", Mdl, MdlMap);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(MdlMap != gcvNULL);
+ gcmkASSERT(Mdl->maps != gcvNULL);
+
+ if (Mdl->maps == MdlMap)
+ {
+ Mdl->maps = MdlMap->next;
+ }
+ else
+ {
+ prevMdlMap = Mdl->maps;
+
+ while (prevMdlMap->next != MdlMap)
+ {
+ prevMdlMap = prevMdlMap->next;
+
+ gcmkASSERT(prevMdlMap != gcvNULL);
+ }
+
+ prevMdlMap->next = MdlMap->next;
+ }
+
+ kfree(MdlMap);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+extern PLINUX_MDL_MAP
+FindMdlMap(
+ IN PLINUX_MDL Mdl,
+ IN gctINT ProcessID
+ )
+{
+ PLINUX_MDL_MAP mdlMap;
+
+ gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID);
+ if(Mdl == gcvNULL)
+ {
+ return gcvNULL;
+ }
+ mdlMap = Mdl->maps;
+
+ while (mdlMap != gcvNULL)
+ {
+ if (mdlMap->pid == ProcessID)
+ {
+ gcmkFOOTER_ARG("0x%X", mdlMap);
+ return mdlMap;
+ }
+
+ mdlMap = mdlMap->next;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvNULL;
+}
+
+void
+OnProcessExit(
+ IN gckOS Os,
+ IN gckKERNEL Kernel
+ )
+{
+}
+
+/*******************************************************************************
+**
+** gckOS_Construct
+**
+** Construct a new gckOS object.
+**
+** INPUT:
+**
+** gctPOINTER Context
+** Pointer to the gckGALDEVICE class.
+**
+** OUTPUT:
+**
+** gckOS * Os
+** Pointer to a variable that will hold the pointer to the gckOS object.
+*/
+gceSTATUS
+gckOS_Construct(
+ IN gctPOINTER Context,
+ OUT gckOS * Os
+ )
+{
+ gckOS os;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Context=0x%X", Context);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Os != gcvNULL);
+
+ /* Allocate the gckOS object. */
+ os = (gckOS) kmalloc(gcmSIZEOF(struct _gckOS), GFP_KERNEL);
+
+ if (os == gcvNULL)
+ {
+ /* Out of memory. */
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ /* Zero the memory. */
+ gckOS_ZeroMemory(os, gcmSIZEOF(struct _gckOS));
+
+ /* Initialize the gckOS object. */
+ os->object.type = gcvOBJ_OS;
+
+ /* Set device device. */
+ os->device = Context;
+
+ /* IMPORTANT! No heap yet. */
+ os->heap = gcvNULL;
+
+ /* Initialize the memory lock. */
+ gcmkONERROR(gckOS_CreateMutex(os, &os->memoryLock));
+ gcmkONERROR(gckOS_CreateMutex(os, &os->memoryMapLock));
+
+ /* Create debug lock mutex. */
+ gcmkONERROR(gckOS_CreateMutex(os, &os->debugLock));
+
+ /* Create the gckHEAP object. */
+ gcmkONERROR(gckHEAP_Construct(os, gcdHEAP_SIZE, &os->heap));
+
+ os->mdlHead = os->mdlTail = gcvNULL;
+
+ /* Get the kernel process ID. */
+ gcmkONERROR(gckOS_GetProcessID(&os->kernelProcessID));
+
+ /*
+ * Initialize the signal manager.
+ * It creates the signals to be used in
+ * the user space.
+ */
+
+ /* Initialize mutex. */
+ gcmkONERROR(
+ gckOS_CreateMutex(os, &os->signal.lock));
+
+ /* Initialize the signal table. */
+ os->signal.table =
+ kmalloc(gcmSIZEOF(gctPOINTER) * USER_SIGNAL_TABLE_LEN_INIT, GFP_KERNEL);
+
+ if (os->signal.table == gcvNULL)
+ {
+ /* Out of memory. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ gckOS_ZeroMemory(os->signal.table,
+ gcmSIZEOF(gctPOINTER) * USER_SIGNAL_TABLE_LEN_INIT);
+
+ /* Set the signal table length. */
+ os->signal.tableLen = USER_SIGNAL_TABLE_LEN_INIT;
+
+ /* The table is empty. */
+ os->signal.unused = os->signal.tableLen;
+
+ /* Initial signal ID. */
+ os->signal.currentID = 0;
+
+ /* Return pointer to the gckOS object. */
+ *Os = os;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Os=0x%X", *Os);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back any allocation. */
+ if (os->signal.table != gcvNULL)
+ {
+ kfree(os->signal.table);
+ }
+
+ if (os->signal.lock != gcvNULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->signal.lock));
+ }
+
+ if (os->heap != gcvNULL)
+ {
+ gcmkVERIFY_OK(
+ gckHEAP_Destroy(os->heap));
+ }
+
+ if (os->memoryMapLock != gcvNULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->memoryMapLock));
+ }
+
+ if (os->memoryLock != gcvNULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->memoryLock));
+ }
+
+ if (os->debugLock != gcvNULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->debugLock));
+ }
+
+ kfree(os);
+
+ /* Return the error. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_Destroy
+**
+** Destroy an gckOS object.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object that needs to be destroyed.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_Destroy(
+ IN gckOS Os
+ )
+{
+ gckHEAP heap;
+
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ /*
+ * Destroy the signal manager.
+ */
+
+ /* Destroy the mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->signal.lock));
+
+ /* Free the signal table. */
+ kfree(Os->signal.table);
+
+ if (Os->heap != gcvNULL)
+ {
+ /* Mark gckHEAP as gone. */
+ heap = Os->heap;
+ Os->heap = gcvNULL;
+
+ /* Destroy the gckHEAP object. */
+ gcmkVERIFY_OK(gckHEAP_Destroy(heap));
+ }
+
+ /* Destroy the memory lock. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->memoryMapLock));
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->memoryLock));
+
+ /* Destroy debug lock mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->debugLock));
+
+ /* Flush the debug cache. */
+ gcmkDEBUGFLUSH(~0U);
+
+ /* Mark the gckOS object as unknown. */
+ Os->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckOS object. */
+ kfree(Os);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_Allocate
+**
+** Allocate memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIZE_T Bytes
+** Number of bytes to allocate.
+**
+** OUTPUT:
+**
+** gctPOINTER * Memory
+** Pointer to a variable that will hold the allocated memory location.
+*/
+gceSTATUS
+gckOS_Allocate(
+ IN gckOS Os,
+ IN gctSIZE_T Bytes,
+ OUT gctPOINTER * Memory
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
+
+ /* Do we have a heap? */
+ if (Os->heap != gcvNULL)
+ {
+ /* Allocate from the heap. */
+ gcmkONERROR(gckHEAP_Allocate(Os->heap, Bytes, Memory));
+ }
+ else
+ {
+ gcmkONERROR(gckOS_AllocateMemory(Os, Bytes, Memory));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Memory=0x%X", *Memory);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_Free
+**
+** Free allocated memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Memory
+** Pointer to memory allocation to free.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_Free(
+ IN gckOS Os,
+ IN gctPOINTER Memory
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Memory=0x%X", Os, Memory);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
+
+ /* Do we have a heap? */
+ if (Os->heap != gcvNULL)
+ {
+ /* Free from the heap. */
+ gcmkONERROR(gckHEAP_Free(Os->heap, Memory));
+ }
+ else
+ {
+ gcmkONERROR(gckOS_FreeMemory(Os, Memory));
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_AllocateMemory
+**
+** Allocate memory wrapper.
+**
+** INPUT:
+**
+** gctSIZE_T Bytes
+** Number of bytes to allocate.
+**
+** OUTPUT:
+**
+** gctPOINTER * Memory
+** Pointer to a variable that will hold the allocated memory location.
+*/
+gceSTATUS
+gckOS_AllocateMemory(
+ IN gckOS Os,
+ IN gctSIZE_T Bytes,
+ OUT gctPOINTER * Memory
+ )
+{
+ gctPOINTER memory;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
+
+ memory = (gctPOINTER) kmalloc(Bytes, GFP_KERNEL);
+
+ if (memory == gcvNULL)
+ {
+ /* Out of memory. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ /* Return pointer to the memory allocation. */
+ *Memory = memory;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Memory=0x%X", *Memory);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_FreeMemory
+**
+** Free allocated memory wrapper.
+**
+** INPUT:
+**
+** gctPOINTER Memory
+** Pointer to memory allocation to free.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_FreeMemory(
+ IN gckOS Os,
+ IN gctPOINTER Memory
+ )
+{
+ gcmkHEADER_ARG("Memory=0x%X", Memory);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
+
+ /* Free the memory from the OS pool. */
+ kfree(Memory);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_MapMemory
+**
+** Map physical memory into the current process.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Start of physical address memory.
+**
+** gctSIZE_T Bytes
+** Number of bytes to map.
+**
+** OUTPUT:
+**
+** gctPOINTER * Memory
+** Pointer to a variable that will hold the logical address of the
+** mapped memory.
+*/
+gceSTATUS
+gckOS_MapMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes,
+ OUT gctPOINTER * Logical
+ )
+{
+ PLINUX_MDL_MAP mdlMap;
+ PLINUX_MDL mdl = (PLINUX_MDL)Physical;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != 0);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+
+ MEMORY_LOCK(Os);
+
+ mdlMap = FindMdlMap(mdl, _GetProcessID());
+
+ if (mdlMap == gcvNULL)
+ {
+ mdlMap = _CreateMdlMap(mdl, _GetProcessID());
+
+ if (mdlMap == gcvNULL)
+ {
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+ }
+
+ if (mdlMap->vmaAddr == gcvNULL)
+ {
+ down_write(&current->mm->mmap_sem);
+
+ mdlMap->vmaAddr = (char *)do_mmap_pgoff(gcvNULL,
+ 0L,
+ mdl->numPages * PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 0);
+
+ if (IS_ERR(mdlMap->vmaAddr))
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): do_mmap_pgoff error",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): mdl->numPages: %d mdl->vmaAddr: 0x%X",
+ __FUNCTION__, __LINE__,
+ mdl->numPages,
+ mdlMap->vmaAddr
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ up_write(&current->mm->mmap_sem);
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr);
+
+ if (!mdlMap->vma)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): find_vma error.",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ up_write(&current->mm->mmap_sem);
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES);
+ return gcvSTATUS_OUT_OF_RESOURCES;
+ }
+
+#ifndef NO_DMA_COHERENT
+ if (dma_mmap_coherent(gcvNULL,
+ mdlMap->vma,
+ mdl->addr,
+ mdl->dmaHandle,
+ mdl->numPages * PAGE_SIZE) < 0)
+ {
+ up_write(&current->mm->mmap_sem);
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): dma_mmap_coherent error.",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES);
+ return gcvSTATUS_OUT_OF_RESOURCES;
+ }
+#else
+#if !gcdPAGED_MEMORY_CACHEABLE
+ mdlMap->vma->vm_page_prot = pgprot_noncached(mdlMap->vma->vm_page_prot);
+ mdlMap->vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED;
+# endif
+ mdlMap->vma->vm_pgoff = 0;
+
+ if (remap_pfn_range(mdlMap->vma,
+ mdlMap->vma->vm_start,
+ mdl->dmaHandle >> PAGE_SHIFT,
+ mdl->numPages*PAGE_SIZE,
+ mdlMap->vma->vm_page_prot) < 0)
+ {
+ up_write(&current->mm->mmap_sem);
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): remap_pfn_range error.",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES);
+ return gcvSTATUS_OUT_OF_RESOURCES;
+ }
+#endif
+
+ up_write(&current->mm->mmap_sem);
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ *Logical = mdlMap->vmaAddr;
+
+ gcmkFOOTER_ARG("*Logical=0x%X", *Logical);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_UnmapMemory
+**
+** Unmap physical memory out of the current process.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Start of physical address memory.
+**
+** gctSIZE_T Bytes
+** Number of bytes to unmap.
+**
+** gctPOINTER Memory
+** Pointer to a previously mapped memory region.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes,
+ IN gctPOINTER Logical
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X",
+ Os, Physical, Bytes, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != 0);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+
+ gckOS_UnmapMemoryEx(Os, Physical, Bytes, Logical, _GetProcessID());
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+
+/*******************************************************************************
+**
+** gckOS_UnmapMemoryEx
+**
+** Unmap physical memory in the specified process.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Start of physical address memory.
+**
+** gctSIZE_T Bytes
+** Number of bytes to unmap.
+**
+** gctPOINTER Memory
+** Pointer to a previously mapped memory region.
+**
+** gctUINT32 PID
+** Pid of the process that opened the device and mapped this memory.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapMemoryEx(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes,
+ IN gctPOINTER Logical,
+ IN gctUINT32 PID
+ )
+{
+ PLINUX_MDL_MAP mdlMap;
+ PLINUX_MDL mdl = (PLINUX_MDL)Physical;
+ struct task_struct * task;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X PID=%d",
+ Os, Physical, Bytes, Logical, PID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != 0);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(PID != 0);
+
+ MEMORY_LOCK(Os);
+
+ if (Logical)
+ {
+ mdlMap = FindMdlMap(mdl, PID);
+
+ if (mdlMap == gcvNULL || mdlMap->vmaAddr == gcvNULL)
+ {
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT);
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ /* Get the current pointer for the task with stored pid. */
+ task = FIND_TASK_BY_PID(mdlMap->pid);
+
+ if (task != gcvNULL && task->mm != gcvNULL)
+ {
+ down_write(&task->mm->mmap_sem);
+ do_munmap(task->mm, (unsigned long)Logical, mdl->numPages*PAGE_SIZE);
+ up_write(&task->mm->mmap_sem);
+ }
+ else
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): can't find the task with pid->%d. No unmapping",
+ __FUNCTION__, __LINE__,
+ mdlMap->pid
+ );
+ }
+
+ gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap));
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AllocateNonPagedMemory
+**
+** Allocate a number of pages from non-paged memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctBOOL InUserSpace
+** gcvTRUE if the pages need to be mapped into user space.
+**
+** gctSIZE_T * Bytes
+** Pointer to a variable that holds the number of bytes to allocate.
+**
+** OUTPUT:
+**
+** gctSIZE_T * Bytes
+** Pointer to a variable that hold the number of bytes allocated.
+**
+** gctPHYS_ADDR * Physical
+** Pointer to a variable that will hold the physical address of the
+** allocation.
+**
+** gctPOINTER * Logical
+** Pointer to a variable that will hold the logical address of the
+** allocation.
+*/
+gceSTATUS
+gckOS_AllocateNonPagedMemory(
+ IN gckOS Os,
+ IN gctBOOL InUserSpace,
+ IN OUT gctSIZE_T * Bytes,
+ OUT gctPHYS_ADDR * Physical,
+ OUT gctPOINTER * Logical
+ )
+{
+ gctSIZE_T bytes;
+ gctINT numPages;
+ PLINUX_MDL mdl = gcvNULL;
+ PLINUX_MDL_MAP mdlMap = gcvNULL;
+ gctSTRING addr;
+#ifdef NO_DMA_COHERENT
+ struct page * page;
+ long size, order;
+ gctPOINTER vaddr;
+#endif
+ gctBOOL locked = gcvFALSE;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu",
+ Os, InUserSpace, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes != gcvNULL);
+ gcmkVERIFY_ARGUMENT(*Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+
+ /* Align number of bytes to page size. */
+ bytes = gcmALIGN(*Bytes, PAGE_SIZE);
+
+ /* Get total number of pages.. */
+ numPages = GetPageCount(bytes, 0);
+
+ /* Allocate mdl+vector structure */
+ mdl = _CreateMdl(_GetProcessID());
+ if (mdl == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ mdl->pagedMem = 0;
+ mdl->numPages = numPages;
+
+ MEMORY_LOCK(Os);
+ locked = gcvTRUE;
+
+#ifndef NO_DMA_COHERENT
+ addr = dma_alloc_coherent(gcvNULL,
+ mdl->numPages * PAGE_SIZE,
+ &mdl->dmaHandle,
+ GFP_KERNEL);
+#else
+ size = mdl->numPages * PAGE_SIZE;
+ order = get_order(size);
+ page = alloc_pages(GFP_KERNEL, order);
+
+ if (page == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ vaddr = (gctPOINTER)page_address(page);
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ addr = vaddr;
+# elif gcdNONPAGED_MEMORY_BUFFERABLE
+ addr = ioremap_wc(virt_to_phys(vaddr), size);
+# else
+ addr = ioremap_nocache(virt_to_phys(vaddr), size);
+# endif
+
+ mdl->dmaHandle = virt_to_phys(vaddr);
+ mdl->kaddr = vaddr;
+
+ /* Cache invalidate. */
+ dma_sync_single_for_device(
+ gcvNULL,
+ page_to_phys(page),
+ bytes,
+ DMA_FROM_DEVICE);
+
+ while (size > 0)
+ {
+ SetPageReserved(virt_to_page(vaddr));
+
+ vaddr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+#endif
+
+ if (addr == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ if ((Os->device->baseAddress & 0x80000000) != (mdl->dmaHandle & 0x80000000))
+ {
+ mdl->dmaHandle = (mdl->dmaHandle & ~0x80000000)
+ | (Os->device->baseAddress & 0x80000000);
+ }
+
+ mdl->addr = addr;
+
+ /*
+ * We will not do any mapping from here.
+ * Mapping will happen from mmap method.
+ * mdl structure will be used.
+ */
+
+ /* Return allocated memory. */
+ *Bytes = bytes;
+ *Physical = (gctPHYS_ADDR) mdl;
+
+ if (InUserSpace)
+ {
+ mdlMap = _CreateMdlMap(mdl, _GetProcessID());
+
+ if (mdlMap == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ /* Only after mmap this will be valid. */
+
+ /* We need to map this to user space. */
+ down_write(&current->mm->mmap_sem);
+
+ mdlMap->vmaAddr = (gctSTRING) do_mmap_pgoff(gcvNULL,
+ 0L,
+ mdl->numPages * PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 0);
+
+ if (IS_ERR(mdlMap->vmaAddr))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_WARNING, gcvZONE_OS,
+ "%s(%d): do_mmap_pgoff error",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ up_write(&current->mm->mmap_sem);
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr);
+
+ if (mdlMap->vma == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_WARNING, gcvZONE_OS,
+ "%s(%d): find_vma error",
+ __FUNCTION__, __LINE__
+ );
+
+ up_write(&current->mm->mmap_sem);
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+#ifndef NO_DMA_COHERENT
+ if (dma_mmap_coherent(gcvNULL,
+ mdlMap->vma,
+ mdl->addr,
+ mdl->dmaHandle,
+ mdl->numPages * PAGE_SIZE) < 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_WARNING, gcvZONE_OS,
+ "%s(%d): dma_mmap_coherent error",
+ __FUNCTION__, __LINE__
+ );
+
+ up_write(&current->mm->mmap_sem);
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+#else
+ mdlMap->vma->vm_page_prot = pgprot_noncached(mdlMap->vma->vm_page_prot);
+ mdlMap->vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED;
+ mdlMap->vma->vm_pgoff = 0;
+
+ if (remap_pfn_range(mdlMap->vma,
+ mdlMap->vma->vm_start,
+ mdl->dmaHandle >> PAGE_SHIFT,
+ mdl->numPages * PAGE_SIZE,
+ mdlMap->vma->vm_page_prot))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_WARNING, gcvZONE_OS,
+ "%s(%d): remap_pfn_range error",
+ __FUNCTION__, __LINE__
+ );
+
+ up_write(&current->mm->mmap_sem);
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+#endif /* NO_DMA_COHERENT */
+
+ up_write(&current->mm->mmap_sem);
+
+ *Logical = mdlMap->vmaAddr;
+ }
+ else
+ {
+ *Logical = (gctPOINTER)mdl->addr;
+ }
+
+ /*
+ * Add this to a global list.
+ * Will be used by get physical address
+ * and mapuser pointer functions.
+ */
+
+ if (!Os->mdlHead)
+ {
+ /* Initialize the queue. */
+ Os->mdlHead = Os->mdlTail = mdl;
+ }
+ else
+ {
+ /* Add to the tail. */
+ mdl->prev = Os->mdlTail;
+ Os->mdlTail->next = mdl;
+ Os->mdlTail = mdl;
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu *Physical=0x%X *Logical=0x%X",
+ *Bytes, *Physical, *Logical);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (mdlMap != gcvNULL)
+ {
+ /* Free LINUX_MDL_MAP. */
+ gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap));
+ }
+
+ if (mdl != gcvNULL)
+ {
+ /* Free LINUX_MDL. */
+ gcmkVERIFY_OK(_DestroyMdl(mdl));
+ }
+
+ if (locked)
+ {
+ /* Unlock memory. */
+ MEMORY_UNLOCK(Os);
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_FreeNonPagedMemory
+**
+** Free previously allocated and mapped pages from non-paged memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIZE_T Bytes
+** Number of bytes allocated.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocated memory.
+**
+** gctPOINTER Logical
+** Logical address of the allocated memory.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS gckOS_FreeNonPagedMemory(
+ IN gckOS Os,
+ IN gctSIZE_T Bytes,
+ IN gctPHYS_ADDR Physical,
+ IN gctPOINTER Logical
+ )
+{
+ PLINUX_MDL mdl;
+ PLINUX_MDL_MAP mdlMap;
+ struct task_struct * task;
+#ifdef NO_DMA_COHERENT
+ unsigned size;
+ gctPOINTER vaddr;
+#endif /* NO_DMA_COHERENT */
+
+ gcmkHEADER_ARG("Os=0x%X Bytes=%lu Physical=0x%X Logical=0x%X",
+ Os, Bytes, Physical, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Physical != 0);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+
+ /* Convert physical address into a pointer to a MDL. */
+ mdl = (PLINUX_MDL) Physical;
+
+ MEMORY_LOCK(Os);
+
+#ifndef NO_DMA_COHERENT
+ dma_free_coherent(gcvNULL,
+ mdl->numPages * PAGE_SIZE,
+ mdl->addr,
+ mdl->dmaHandle);
+#else
+ size = mdl->numPages * PAGE_SIZE;
+ vaddr = mdl->kaddr;
+
+ while (size > 0)
+ {
+ ClearPageReserved(virt_to_page(vaddr));
+
+ vaddr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ free_pages((unsigned long)mdl->kaddr, get_order(mdl->numPages * PAGE_SIZE));
+
+#if !gcdNONPAGED_MEMORY_CACHEABLE
+ iounmap(mdl->addr);
+#endif
+
+#endif /* NO_DMA_COHERENT */
+
+ mdlMap = mdl->maps;
+
+ while (mdlMap != gcvNULL)
+ {
+ if (mdlMap->vmaAddr != gcvNULL)
+ {
+ /* Get the current pointer for the task with stored pid. */
+ task = FIND_TASK_BY_PID(mdlMap->pid);
+
+ if (task != gcvNULL && task->mm != gcvNULL)
+ {
+ down_write(&task->mm->mmap_sem);
+
+ if (do_munmap(task->mm,
+ (unsigned long)mdlMap->vmaAddr,
+ mdl->numPages * PAGE_SIZE) < 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_WARNING, gcvZONE_OS,
+ "%s(%d): do_munmap failed",
+ __FUNCTION__, __LINE__
+ );
+ }
+
+ up_write(&task->mm->mmap_sem);
+ }
+
+ mdlMap->vmaAddr = gcvNULL;
+ }
+
+ mdlMap = mdlMap->next;
+ }
+
+ /* Remove the node from global list.. */
+ if (mdl == Os->mdlHead)
+ {
+ if ((Os->mdlHead = mdl->next) == gcvNULL)
+ {
+ Os->mdlTail = gcvNULL;
+ }
+ }
+ else
+ {
+ mdl->prev->next = mdl->next;
+ if (mdl == Os->mdlTail)
+ {
+ Os->mdlTail = mdl->prev;
+ }
+ else
+ {
+ mdl->next->prev = mdl->prev;
+ }
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkVERIFY_OK(_DestroyMdl(mdl));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_ReadRegister
+**
+** Read data from a register.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctUINT32 Address
+** Address of register.
+**
+** OUTPUT:
+**
+** gctUINT32 * Data
+** Pointer to a variable that receives the data read from the register.
+*/
+gceSTATUS
+gckOS_ReadRegister(
+ IN gckOS Os,
+ IN gctUINT32 Address,
+ OUT gctUINT32 * Data
+ )
+{
+ return gckOS_ReadRegisterEx(Os, gcvCORE_MAJOR, Address, Data);
+}
+
+gceSTATUS
+gckOS_ReadRegisterEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctUINT32 Address,
+ OUT gctUINT32 * Data
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X", Os, Core, Address);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Data != gcvNULL);
+
+ *Data = readl((gctUINT8 *)Os->device->registerBases[Core] + Address);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Data=0x%08x", *Data);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_WriteRegister
+**
+** Write data to a register.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctUINT32 Address
+** Address of register.
+**
+** gctUINT32 Data
+** Data for register.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_WriteRegister(
+ IN gckOS Os,
+ IN gctUINT32 Address,
+ IN gctUINT32 Data
+ )
+{
+ return gckOS_WriteRegisterEx(Os, gcvCORE_MAJOR, Address, Data);
+}
+
+gceSTATUS
+gckOS_WriteRegisterEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctUINT32 Address,
+ IN gctUINT32 Data
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X Data=0x%08x", Os, Core, Address, Data);
+
+ writel(Data, (gctUINT8 *)Os->device->registerBases[Core] + Address);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_GetPageSize
+**
+** Get the system's page size.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** OUTPUT:
+**
+** gctSIZE_T * PageSize
+** Pointer to a variable that will receive the system's page size.
+*/
+gceSTATUS gckOS_GetPageSize(
+ IN gckOS Os,
+ OUT gctSIZE_T * PageSize
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(PageSize != gcvNULL);
+
+ /* Return the page size. */
+ *PageSize = (gctSIZE_T) PAGE_SIZE;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*PageSize", *PageSize);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_GetPhysicalAddress
+**
+** Get the physical system address of a corresponding virtual address.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Logical
+** Logical address.
+**
+** OUTPUT:
+**
+** gctUINT32 * Address
+** Poinetr to a variable that receives the 32-bit physical adress.
+*/
+gceSTATUS
+gckOS_GetPhysicalAddress(
+ IN gckOS Os,
+ IN gctPOINTER Logical,
+ OUT gctUINT32 * Address
+ )
+{
+ gceSTATUS status;
+ gctUINT32 processID;
+
+ gcmkHEADER_ARG("Os=0x%X Logical=0x%X", Os, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Address != gcvNULL);
+
+ /* Get current process ID. */
+ processID = _GetProcessID();
+
+ /* Route through other function. */
+ gcmkONERROR(
+ gckOS_GetPhysicalAddressProcess(Os, Logical, processID, Address));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Address=0x%08x", *Address);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#if gcdSECURE_USER
+static gceSTATUS
+gckOS_AddMapping(
+ IN gckOS Os,
+ IN gctUINT32 Physical,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes
+ )
+{
+ gceSTATUS status;
+ gcsUSER_MAPPING_PTR map;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Logical=0x%X Bytes=%lu",
+ Os, Physical, Logical, Bytes);
+
+ gcmkONERROR(gckOS_Allocate(Os,
+ gcmSIZEOF(gcsUSER_MAPPING),
+ (gctPOINTER *) &map));
+
+ map->next = Os->userMap;
+ map->physical = Physical - Os->device->baseAddress;
+ map->logical = Logical;
+ map->bytes = Bytes;
+ map->start = (gctINT8_PTR) Logical;
+ map->end = map->start + Bytes;
+
+ Os->userMap = map;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+gckOS_RemoveMapping(
+ IN gckOS Os,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes
+ )
+{
+ gceSTATUS status;
+ gcsUSER_MAPPING_PTR map, prev;
+
+ gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", Os, Logical, Bytes);
+
+ for (map = Os->userMap, prev = gcvNULL; map != gcvNULL; map = map->next)
+ {
+ if ((map->logical == Logical)
+ && (map->bytes == Bytes)
+ )
+ {
+ break;
+ }
+
+ prev = map;
+ }
+
+ if (map == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_ADDRESS);
+ }
+
+ if (prev == gcvNULL)
+ {
+ Os->userMap = map->next;
+ }
+ else
+ {
+ prev->next = map->next;
+ }
+
+ gcmkONERROR(gcmkOS_SAFE_FREE(Os, map));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+#endif
+
+static gceSTATUS
+_ConvertLogical2Physical(
+ IN gckOS Os,
+ IN gctPOINTER Logical,
+ IN gctUINT32 ProcessID,
+ IN PLINUX_MDL Mdl,
+ OUT gctUINT32_PTR Physical
+ )
+{
+ gctINT8_PTR base, vBase;
+ gctUINT32 offset;
+ PLINUX_MDL_MAP map;
+ gcsUSER_MAPPING_PTR userMap;
+
+ base = (Mdl == gcvNULL) ? gcvNULL : (gctINT8_PTR) Mdl->addr;
+
+ /* Check for the logical address match. */
+ if ((base != gcvNULL)
+ && ((gctINT8_PTR) Logical >= base)
+ && ((gctINT8_PTR) Logical < base + Mdl->numPages * PAGE_SIZE)
+ )
+ {
+ offset = (gctINT8_PTR) Logical - base;
+
+ if (Mdl->dmaHandle != 0)
+ {
+ /* The memory was from coherent area. */
+ *Physical = (gctUINT32) Mdl->dmaHandle + offset;
+ }
+ else if (Mdl->pagedMem && !Mdl->contiguous)
+ {
+ *Physical = page_to_phys(vmalloc_to_page(base + offset));
+ }
+ else
+ {
+ *Physical = gcmPTR2INT(virt_to_phys(base)) + offset;
+ }
+
+ return gcvSTATUS_OK;
+ }
+
+ /* Walk user maps. */
+ for (userMap = Os->userMap; userMap != gcvNULL; userMap = userMap->next)
+ {
+ if (((gctINT8_PTR) Logical >= userMap->start)
+ && ((gctINT8_PTR) Logical < userMap->end)
+ )
+ {
+ *Physical = userMap->physical
+ + (gctUINT32) ((gctINT8_PTR) Logical - userMap->start);
+
+ return gcvSTATUS_OK;
+ }
+ }
+
+ if (ProcessID != Os->kernelProcessID)
+ {
+ map = FindMdlMap(Mdl, (gctINT) ProcessID);
+ vBase = (map == gcvNULL) ? gcvNULL : (gctINT8_PTR) map->vmaAddr;
+
+ /* Is the given address within that range. */
+ if ((vBase != gcvNULL)
+ && ((gctINT8_PTR) Logical >= vBase)
+ && ((gctINT8_PTR) Logical < vBase + Mdl->numPages * PAGE_SIZE)
+ )
+ {
+ offset = (gctINT8_PTR) Logical - vBase;
+
+ if (Mdl->dmaHandle != 0)
+ {
+ /* The memory was from coherent area. */
+ *Physical = (gctUINT32) Mdl->dmaHandle + offset;
+ }
+ else if (Mdl->pagedMem && !Mdl->contiguous)
+ {
+ *Physical = page_to_phys(vmalloc_to_page(base + offset));
+ }
+ else
+ {
+ /* Return the kernel virtual pointer based on this. */
+ *Physical = gcmPTR2INT(virt_to_phys(base)) + offset;
+ }
+
+ return gcvSTATUS_OK;
+ }
+ }
+
+ /* Address not yet found. */
+ return gcvSTATUS_INVALID_ADDRESS;
+}
+
+/*******************************************************************************
+**
+** gckOS_GetPhysicalAddressProcess
+**
+** Get the physical system address of a corresponding virtual address for a
+** given process.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** gctPOINTER Logical
+** Logical address.
+**
+** gctUINT32 ProcessID
+** Process ID.
+**
+** OUTPUT:
+**
+** gctUINT32 * Address
+** Poinetr to a variable that receives the 32-bit physical adress.
+*/
+gceSTATUS
+gckOS_GetPhysicalAddressProcess(
+ IN gckOS Os,
+ IN gctPOINTER Logical,
+ IN gctUINT32 ProcessID,
+ OUT gctUINT32 * Address
+ )
+{
+ PLINUX_MDL mdl;
+ gctINT8_PTR base;
+ gceSTATUS status = gcvSTATUS_INVALID_ADDRESS;
+
+ gcmkHEADER_ARG("Os=0x%X Logical=0x%X ProcessID=%d", Os, Logical, ProcessID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Address != gcvNULL);
+
+ MEMORY_LOCK(Os);
+
+ /* First try the contiguous memory pool. */
+ if (Os->device->contiguousMapped)
+ {
+ base = (gctINT8_PTR) Os->device->contiguousBase;
+
+ if (((gctINT8_PTR) Logical >= base)
+ && ((gctINT8_PTR) Logical < base + Os->device->contiguousSize)
+ )
+ {
+ /* Convert logical address into physical. */
+ *Address = Os->device->contiguousVidMem->baseAddress
+ + (gctINT8_PTR) Logical - base;
+ status = gcvSTATUS_OK;
+ }
+ }
+ else
+ {
+ /* Try the contiguous memory pool. */
+ mdl = (PLINUX_MDL) Os->device->contiguousPhysical;
+ status = _ConvertLogical2Physical(Os,
+ Logical,
+ ProcessID,
+ mdl,
+ Address);
+ }
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Walk all MDLs. */
+ for (mdl = Os->mdlHead; mdl != gcvNULL; mdl = mdl->next)
+ {
+ /* Try this MDL. */
+ status = _ConvertLogical2Physical(Os,
+ Logical,
+ ProcessID,
+ mdl,
+ Address);
+ if (gcmIS_SUCCESS(status))
+ {
+ break;
+ }
+ }
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkONERROR(status);
+
+ if (Os->device->baseAddress != 0)
+ {
+ /* Subtract base address to get a GPU physical address. */
+ gcmkASSERT(*Address >= Os->device->baseAddress);
+ *Address -= Os->device->baseAddress;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Address=0x%08x", *Address);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_MapPhysical
+**
+** Map a physical address into kernel space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctUINT32 Physical
+** Physical address of the memory to map.
+**
+** gctSIZE_T Bytes
+** Number of bytes to map.
+**
+** OUTPUT:
+**
+** gctPOINTER * Logical
+** Pointer to a variable that receives the base address of the mapped
+** memory.
+*/
+gceSTATUS
+gckOS_MapPhysical(
+ IN gckOS Os,
+ IN gctUINT32 Physical,
+ IN gctSIZE_T Bytes,
+ OUT gctPOINTER * Logical
+ )
+{
+ gctPOINTER logical;
+ PLINUX_MDL mdl;
+ gctUINT32 physical;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+
+ MEMORY_LOCK(Os);
+
+ /* Compute true physical address (before subtraction of the baseAddress). */
+ physical = Physical + Os->device->baseAddress;
+
+ /* Go through our mapping to see if we know this physical address already. */
+ mdl = Os->mdlHead;
+
+ while (mdl != gcvNULL)
+ {
+ if (mdl->dmaHandle != 0)
+ {
+ if ((physical >= mdl->dmaHandle)
+ && (physical < mdl->dmaHandle + mdl->numPages * PAGE_SIZE)
+ )
+ {
+ *Logical = mdl->addr + (physical - mdl->dmaHandle);
+ break;
+ }
+ }
+
+ mdl = mdl->next;
+ }
+
+ if (mdl == gcvNULL)
+ {
+ /* Map memory as cached memory. */
+ request_mem_region(physical, Bytes, "MapRegion");
+ logical = (gctPOINTER) ioremap_nocache(physical, Bytes);
+
+ if (logical == gcvNULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): Failed to ioremap",
+ __FUNCTION__, __LINE__
+ );
+
+ MEMORY_UNLOCK(Os);
+
+ /* Out of resources. */
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES);
+ return gcvSTATUS_OUT_OF_RESOURCES;
+ }
+
+ /* Return pointer to mapped memory. */
+ *Logical = logical;
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Logical=0x%X", *Logical);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_UnmapPhysical
+**
+** Unmap a previously mapped memory region from kernel memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Logical
+** Pointer to the base address of the memory to unmap.
+**
+** gctSIZE_T Bytes
+** Number of bytes to unmap.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapPhysical(
+ IN gckOS Os,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes
+ )
+{
+ PLINUX_MDL mdl;
+
+ gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", Os, Logical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ MEMORY_LOCK(Os);
+
+ mdl = Os->mdlHead;
+
+ while (mdl != gcvNULL)
+ {
+ if (mdl->addr != gcvNULL)
+ {
+ if (Logical >= (gctPOINTER)mdl->addr
+ && Logical < (gctPOINTER)((gctSTRING)mdl->addr + mdl->numPages * PAGE_SIZE))
+ {
+ break;
+ }
+ }
+
+ mdl = mdl->next;
+ }
+
+ if (mdl == gcvNULL)
+ {
+ /* Unmap the memory. */
+ iounmap(Logical);
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_CreateMutex
+**
+** Create a new mutex.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** OUTPUT:
+**
+** gctPOINTER * Mutex
+** Pointer to a variable that will hold a pointer to the mutex.
+*/
+gceSTATUS
+gckOS_CreateMutex(
+ IN gckOS Os,
+ OUT gctPOINTER * Mutex
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Validate the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Mutex != gcvNULL);
+
+ /* Allocate a FAST_MUTEX structure. */
+ *Mutex = (gctPOINTER)kmalloc(sizeof(struct semaphore), GFP_KERNEL);
+
+ if (*Mutex == gcvNULL)
+ {
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ /* Initialize the semaphore.. Come up in unlocked state. */
+ sema_init(*Mutex, 1);
+
+ /* Return status. */
+ gcmkFOOTER_ARG("*Mutex=0x%X", *Mutex);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_DeleteMutex
+**
+** Delete a mutex.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Mutex
+** Pointer to the mute to be deleted.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DeleteMutex(
+ IN gckOS Os,
+ IN gctPOINTER Mutex
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Mutex=0x%X", Os, Mutex);
+
+ /* Validate the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Mutex != gcvNULL);
+
+ /* Delete the fast mutex. */
+ kfree(Mutex);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AcquireMutex
+**
+** Acquire a mutex.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Mutex
+** Pointer to the mutex to be acquired.
+**
+** gctUINT32 Timeout
+** Timeout value specified in milliseconds.
+** Specify the value of gcvINFINITE to keep the thread suspended
+** until the mutex has been acquired.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AcquireMutex(
+ IN gckOS Os,
+ IN gctPOINTER Mutex,
+ IN gctUINT32 Timeout
+ )
+{
+#if gcdDETECT_TIMEOUT
+ gctUINT32 timeout;
+#endif
+
+ gcmkHEADER_ARG("Os=0x%X Mutex=0x%0x Timeout=%u", Os, Mutex, Timeout);
+
+ /* Validate the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Mutex != gcvNULL);
+
+#if gcdDETECT_TIMEOUT
+ timeout = 0;
+
+ for (;;)
+ {
+ /* Try to acquire the mutex. */
+ if (!down_trylock((struct semaphore *) Mutex))
+ {
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ /* Advance the timeout. */
+ timeout += 1;
+
+ if (Timeout == gcvINFINITE)
+ {
+ if (timeout == gcdINFINITE_TIMEOUT)
+ {
+ gctUINT32 dmaAddress1, dmaAddress2;
+ gctUINT32 dmaState1, dmaState2;
+
+ dmaState1 = dmaState2 =
+ dmaAddress1 = dmaAddress2 = 0;
+
+ /* Verify whether DMA is running. */
+ gcmkVERIFY_OK(_VerifyDMA(
+ Os, &dmaAddress1, &dmaAddress2, &dmaState1, &dmaState2
+ ));
+
+#if gcdDETECT_DMA_ADDRESS
+ /* Dump only if DMA appears stuck. */
+ if (
+ (dmaAddress1 == dmaAddress2)
+#if gcdDETECT_DMA_STATE
+ && (dmaState1 == dmaState2)
+# endif
+ )
+# endif
+ {
+ gcmkVERIFY_OK(_DumpGPUState(Os));
+
+ gcmkPRINT(
+ "%s(%d): mutex 0x%X; forced message flush.",
+ __FUNCTION__, __LINE__, Mutex
+ );
+
+ /* Flush the debug cache. */
+ gcmkDEBUGFLUSH(dmaAddress2);
+ }
+
+ timeout = 0;
+ }
+ }
+ else
+ {
+ /* Timedout? */
+ if (timeout >= Timeout)
+ {
+ break;
+ }
+ }
+
+ /* Wait for 1 millisecond. */
+ gcmkVERIFY_OK(gckOS_Delay(Os, 1));
+ }
+#else
+ if (Timeout == gcvINFINITE)
+ {
+ down((struct semaphore *) Mutex);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ for (;;)
+ {
+ /* Try to acquire the mutex. */
+ if (!down_trylock((struct semaphore *) Mutex))
+ {
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ if (Timeout-- == 0)
+ {
+ break;
+ }
+
+ /* Wait for 1 millisecond. */
+ gcmkVERIFY_OK(gckOS_Delay(Os, 1));
+ }
+#endif
+
+ /* Timeout. */
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_TIMEOUT);
+ return gcvSTATUS_TIMEOUT;
+}
+
+/*******************************************************************************
+**
+** gckOS_ReleaseMutex
+**
+** Release an acquired mutex.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Mutex
+** Pointer to the mutex to be released.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_ReleaseMutex(
+ IN gckOS Os,
+ IN gctPOINTER Mutex
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Mutex=0x%0x", Os, Mutex);
+
+ /* Validate the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Mutex != gcvNULL);
+
+ /* Release the fast mutex. */
+ up((struct semaphore *) Mutex);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomicExchange
+**
+** Atomically exchange a pair of 32-bit values.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** IN OUT gctINT32_PTR Target
+** Pointer to the 32-bit value to exchange.
+**
+** IN gctINT32 NewValue
+** Specifies a new value for the 32-bit value pointed to by Target.
+**
+** OUT gctINT32_PTR OldValue
+** The old value of the 32-bit value pointed to by Target.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomicExchange(
+ IN gckOS Os,
+ IN OUT gctUINT32_PTR Target,
+ IN gctUINT32 NewValue,
+ OUT gctUINT32_PTR OldValue
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Target=0x%X NewValue=%u", Os, Target, NewValue);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ /* Exchange the pair of 32-bit values. */
+ *OldValue = (gctUINT32) atomic_xchg((atomic_t *) Target, (int) NewValue);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*OldValue=%u", *OldValue);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomicExchangePtr
+**
+** Atomically exchange a pair of pointers.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** IN OUT gctPOINTER * Target
+** Pointer to the 32-bit value to exchange.
+**
+** IN gctPOINTER NewValue
+** Specifies a new value for the pointer pointed to by Target.
+**
+** OUT gctPOINTER * OldValue
+** The old value of the pointer pointed to by Target.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomicExchangePtr(
+ IN gckOS Os,
+ IN OUT gctPOINTER * Target,
+ IN gctPOINTER NewValue,
+ OUT gctPOINTER * OldValue
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Target=0x%X NewValue=0x%X", Os, Target, NewValue);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ /* Exchange the pair of pointers. */
+ *OldValue = (gctPOINTER) atomic_xchg((atomic_t *) Target, (int) NewValue);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*OldValue=0x%X", *OldValue);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomConstruct
+**
+** Create an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** OUTPUT:
+**
+** gctPOINTER * Atom
+** Pointer to a variable receiving the constructed atom.
+*/
+gceSTATUS
+gckOS_AtomConstruct(
+ IN gckOS Os,
+ OUT gctPOINTER * Atom
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != gcvNULL);
+
+ /* Allocate the atom. */
+ gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(atomic_t), Atom));
+
+ /* Initialize the atom. */
+ atomic_set((atomic_t *) *Atom, 0);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Atom=0x%X", *Atom);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomDestroy
+**
+** Destroy an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** gctPOINTER Atom
+** Pointer to the atom to destroy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomDestroy(
+ IN gckOS Os,
+ OUT gctPOINTER Atom
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != gcvNULL);
+
+ /* Free the atom. */
+ gcmkONERROR(gcmkOS_SAFE_FREE(Os, Atom));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomGet
+**
+** Get the 32-bit value protected by an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** gctPOINTER Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** gctINT32_PTR Value
+** Pointer to a variable the receives the value of the atom.
+*/
+gceSTATUS
+gckOS_AtomGet(
+ IN gckOS Os,
+ IN gctPOINTER Atom,
+ OUT gctINT32_PTR Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != gcvNULL);
+
+ /* Return the current value of atom. */
+ *Value = atomic_read((atomic_t *) Atom);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Value=%d", *Value);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomSet
+**
+** Set the 32-bit value protected by an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** gctPOINTER Atom
+** Pointer to the atom.
+**
+** gctINT32 Value
+** The value of the atom.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomSet(
+ IN gckOS Os,
+ IN gctPOINTER Atom,
+ IN gctINT32 Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x Value=%d", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != gcvNULL);
+
+ /* Set the current value of atom. */
+ atomic_set((atomic_t *) Atom, Value);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomIncrement
+**
+** Atomically increment the 32-bit integer value inside an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** gctPOINTER Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** gctINT32_PTR Value
+** Pointer to a variable that receives the original value of the atom.
+*/
+gceSTATUS
+gckOS_AtomIncrement(
+ IN gckOS Os,
+ IN gctPOINTER Atom,
+ OUT gctINT32_PTR Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != gcvNULL);
+
+ /* Increment the atom. */
+ *Value = atomic_inc_return((atomic_t *) Atom) - 1;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Value=%d", *Value);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomDecrement
+**
+** Atomically decrement the 32-bit integer value inside an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** gctPOINTER Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** gctINT32_PTR Value
+** Pointer to a variable that receives the original value of the atom.
+*/
+gceSTATUS
+gckOS_AtomDecrement(
+ IN gckOS Os,
+ IN gctPOINTER Atom,
+ OUT gctINT32_PTR Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != gcvNULL);
+
+ /* Decrement the atom. */
+ *Value = atomic_dec_return((atomic_t *) Atom) + 1;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Value=%d", *Value);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_Delay
+**
+** Delay execution of the current thread for a number of milliseconds.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctUINT32 Delay
+** Delay to sleep, specified in milliseconds.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_Delay(
+ IN gckOS Os,
+ IN gctUINT32 Delay
+ )
+{
+ struct timeval now;
+ unsigned long jiffies;
+
+ gcmkHEADER_ARG("Os=0x%X Delay=%u", Os, Delay);
+
+ if (Delay > 0)
+ {
+ /* Convert milliseconds into seconds and microseconds. */
+ now.tv_sec = Delay / 1000;
+ now.tv_usec = (Delay % 1000) * 1000;
+
+ /* Convert timeval to jiffies. */
+ jiffies = timeval_to_jiffies(&now);
+
+ /* Schedule timeout. */
+ schedule_timeout_interruptible(jiffies);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_GetTicks
+**
+** Get the number of milliseconds since the system started.
+**
+** INPUT:
+**
+** OUTPUT:
+**
+** gctUINT32_PTR Time
+** Pointer to a variable to get time.
+**
+*/
+gceSTATUS
+gckOS_GetTicks(
+ OUT gctUINT32_PTR Time
+ )
+{
+ gcmkHEADER();
+
+ *Time = jiffies * 1000 / HZ;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_TicksAfter
+**
+** Compare time values got from gckOS_GetTicks.
+**
+** INPUT:
+** gctUINT32 Time1
+** First time value to be compared.
+**
+** gctUINT32 Time2
+** Second time value to be compared.
+**
+** OUTPUT:
+**
+** gctBOOL_PTR IsAfter
+** Pointer to a variable to result.
+**
+*/
+gceSTATUS
+gckOS_TicksAfter(
+ IN gctUINT32 Time1,
+ IN gctUINT32 Time2,
+ OUT gctBOOL_PTR IsAfter
+ )
+{
+ gcmkHEADER();
+
+ *IsAfter = time_after((unsigned long)Time1, (unsigned long)Time2);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_GetTime
+**
+** Get the number of microseconds since the system started.
+**
+** INPUT:
+**
+** OUTPUT:
+**
+** gctUINT64_PTR Time
+** Pointer to a variable to get time.
+**
+*/
+gceSTATUS
+gckOS_GetTime(
+ OUT gctUINT64_PTR Time
+ )
+{
+ gcmkHEADER();
+
+ *Time = 0;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_MemoryBarrier
+**
+** Make sure the CPU has executed everything up to this point and the data got
+** written to the specified pointer.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Address
+** Address of memory that needs to be barriered.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_MemoryBarrier(
+ IN gckOS Os,
+ IN gctPOINTER Address
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Address=0x%X", Os, Address);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+#if gcdNONPAGED_MEMORY_BUFFERABLE \
+ && defined (CONFIG_ARM) \
+ && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34))
+ /* drain write buffer */
+ dsb();
+
+ /* drain outer cache's write buffer? */
+#else
+ mb();
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AllocatePagedMemory
+**
+** Allocate memory from the paged pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIZE_T Bytes
+** Number of bytes to allocate.
+**
+** OUTPUT:
+**
+** gctPHYS_ADDR * Physical
+** Pointer to a variable that receives the physical address of the
+** memory allocation.
+*/
+gceSTATUS
+gckOS_AllocatePagedMemory(
+ IN gckOS Os,
+ IN gctSIZE_T Bytes,
+ OUT gctPHYS_ADDR * Physical
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+
+ /* Allocate the memory. */
+ gcmkONERROR(gckOS_AllocatePagedMemoryEx(Os, gcvFALSE, Bytes, Physical));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Physical=0x%X", *Physical);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_AllocatePagedMemoryEx
+**
+** Allocate memory from the paged pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctBOOL Contiguous
+** Need contiguous memory or not.
+**
+** gctSIZE_T Bytes
+** Number of bytes to allocate.
+**
+** OUTPUT:
+**
+** gctPHYS_ADDR * Physical
+** Pointer to a variable that receives the physical address of the
+** memory allocation.
+*/
+gceSTATUS
+gckOS_AllocatePagedMemoryEx(
+ IN gckOS Os,
+ IN gctBOOL Contiguous,
+ IN gctSIZE_T Bytes,
+ OUT gctPHYS_ADDR * Physical
+ )
+{
+ gctINT numPages;
+ gctINT i;
+ PLINUX_MDL mdl = gcvNULL;
+ gctSTRING addr;
+ gctSIZE_T bytes;
+ gctBOOL locked = gcvFALSE;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Contiguous=%d Bytes=%lu", Os, Contiguous, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+
+ bytes = gcmALIGN(Bytes, PAGE_SIZE);
+
+ numPages = GetPageCount(bytes, 0);
+
+ MEMORY_LOCK(Os);
+ locked = gcvTRUE;
+
+ mdl = _CreateMdl(_GetProcessID());
+ if (mdl == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ if (Contiguous)
+ {
+ /* Get free pages, and suppress warning (stack dump) from kernel when
+ we run out of memory. */
+ addr = (char *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN, GetOrder(numPages));
+ }
+ else
+ {
+ addr = vmalloc(bytes);
+ }
+
+ if (addr == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ mdl->dmaHandle = 0;
+ mdl->addr = addr;
+ mdl->numPages = numPages;
+ mdl->pagedMem = 1;
+ mdl->contiguous = Contiguous;
+
+ for (i = 0; i < mdl->numPages; i++)
+ {
+ struct page *page;
+
+ if (mdl->contiguous)
+ {
+ page = virt_to_page(addr + i * PAGE_SIZE);
+ }
+ else
+ {
+ page = vmalloc_to_page(addr + i * PAGE_SIZE);
+ }
+
+ SetPageReserved(page);
+ flush_dcache_page(page);
+ }
+
+ /* Return physical address. */
+ *Physical = (gctPHYS_ADDR) mdl;
+
+ /*
+ * Add this to a global list.
+ * Will be used by get physical address
+ * and mapuser pointer functions.
+ */
+ if (!Os->mdlHead)
+ {
+ /* Initialize the queue. */
+ Os->mdlHead = Os->mdlTail = mdl;
+ }
+ else
+ {
+ /* Add to tail. */
+ mdl->prev = Os->mdlTail;
+ Os->mdlTail->next = mdl;
+ Os->mdlTail = mdl;
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Physical=0x%X", *Physical);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (mdl != gcvNULL)
+ {
+ /* Free the memory. */
+ _DestroyMdl(mdl);
+ }
+
+ if (locked)
+ {
+ /* Unlock the memory. */
+ MEMORY_UNLOCK(Os);
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_FreePagedMemory
+**
+** Free memory allocated from the paged pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocation.
+**
+** gctSIZE_T Bytes
+** Number of bytes of the allocation.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_FreePagedMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes
+ )
+{
+ PLINUX_MDL mdl = (PLINUX_MDL) Physical;
+ gctSTRING addr;
+ gctINT i;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ addr = mdl->addr;
+
+ MEMORY_LOCK(Os);
+
+ for (i = 0; i < mdl->numPages; i++)
+ {
+ if (mdl->contiguous)
+ {
+ ClearPageReserved(virt_to_page((gctPOINTER)(((unsigned long)addr) + i * PAGE_SIZE)));
+ }
+ else
+ {
+ ClearPageReserved(vmalloc_to_page((gctPOINTER)(((unsigned long)addr) + i * PAGE_SIZE)));
+ }
+ }
+
+ if (mdl->contiguous)
+ {
+ free_pages((unsigned long)mdl->addr, GetOrder(mdl->numPages));
+ }
+ else
+ {
+ vfree(mdl->addr);
+ }
+
+ /* Remove the node from global list. */
+ if (mdl == Os->mdlHead)
+ {
+ if ((Os->mdlHead = mdl->next) == gcvNULL)
+ {
+ Os->mdlTail = gcvNULL;
+ }
+ }
+ else
+ {
+ mdl->prev->next = mdl->next;
+
+ if (mdl == Os->mdlTail)
+ {
+ Os->mdlTail = mdl->prev;
+ }
+ else
+ {
+ mdl->next->prev = mdl->prev;
+ }
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ /* Free the structure... */
+ gcmkVERIFY_OK(_DestroyMdl(mdl));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_LockPages
+**
+** Lock memory allocated from the paged pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocation.
+**
+** gctSIZE_T Bytes
+** Number of bytes of the allocation.
+**
+** gctBOOL Cacheable
+** Cache mode of mapping.
+**
+** OUTPUT:
+**
+** gctPOINTER * Logical
+** Pointer to a variable that receives the address of the mapped
+** memory.
+**
+** gctSIZE_T * PageCount
+** Pointer to a variable that receives the number of pages required for
+** the page table according to the GPU page size.
+*/
+gceSTATUS
+gckOS_LockPages(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes,
+ IN gctBOOL Cacheable,
+ OUT gctPOINTER * Logical,
+ OUT gctSIZE_T * PageCount
+ )
+{
+ PLINUX_MDL mdl;
+ PLINUX_MDL_MAP mdlMap;
+ gctSTRING addr;
+ unsigned long start;
+ unsigned long pfn;
+ gctINT i;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(PageCount != gcvNULL);
+
+ mdl = (PLINUX_MDL) Physical;
+
+ MEMORY_LOCK(Os);
+
+ mdlMap = FindMdlMap(mdl, _GetProcessID());
+
+ if (mdlMap == gcvNULL)
+ {
+ mdlMap = _CreateMdlMap(mdl, _GetProcessID());
+
+ if (mdlMap == gcvNULL)
+ {
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+ }
+
+ if (mdlMap->vmaAddr == gcvNULL)
+ {
+ down_write(&current->mm->mmap_sem);
+
+ mdlMap->vmaAddr = (gctSTRING)do_mmap_pgoff(gcvNULL,
+ 0L,
+ mdl->numPages * PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 0);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): vmaAddr->0x%X for phys_addr->0x%X",
+ __FUNCTION__, __LINE__,
+ (gctUINT32) mdlMap->vmaAddr,
+ (gctUINT32) mdl
+ );
+
+ if (IS_ERR(mdlMap->vmaAddr))
+ {
+ up_write(&current->mm->mmap_sem);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): do_mmap_pgoff error",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr);
+
+ if (mdlMap->vma == gcvNULL)
+ {
+ up_write(&current->mm->mmap_sem);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): find_vma error",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_RESOURCES);
+ return gcvSTATUS_OUT_OF_RESOURCES;
+ }
+
+ mdlMap->vma->vm_flags |= VM_RESERVED;
+#if !gcdPAGED_MEMORY_CACHEABLE
+ if (Cacheable == gcvFALSE)
+ {
+ /* Make this mapping non-cached. */
+ mdlMap->vma->vm_page_prot = pgprot_noncached(mdlMap->vma->vm_page_prot);
+ }
+#endif
+ addr = mdl->addr;
+
+ /* Now map all the vmalloc pages to this user address. */
+ if (mdl->contiguous)
+ {
+ /* map kernel memory to user space.. */
+ if (remap_pfn_range(mdlMap->vma,
+ mdlMap->vma->vm_start,
+ virt_to_phys((gctPOINTER)mdl->addr) >> PAGE_SHIFT,
+ mdlMap->vma->vm_end - mdlMap->vma->vm_start,
+ mdlMap->vma->vm_page_prot) < 0)
+ {
+ up_write(&current->mm->mmap_sem);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): unable to mmap ret",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+ }
+ else
+ {
+ start = mdlMap->vma->vm_start;
+
+ for (i = 0; i < mdl->numPages; i++)
+ {
+ pfn = vmalloc_to_pfn(addr);
+
+ if (remap_pfn_range(mdlMap->vma,
+ start,
+ pfn,
+ PAGE_SIZE,
+ mdlMap->vma->vm_page_prot) < 0)
+ {
+ up_write(&current->mm->mmap_sem);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): gctPHYS_ADDR->0x%X Logical->0x%X Unable to map addr->0x%X to start->0x%X",
+ __FUNCTION__, __LINE__,
+ (gctUINT32) Physical,
+ (gctUINT32) *Logical,
+ (gctUINT32) addr,
+ (gctUINT32) start
+ );
+
+ mdlMap->vmaAddr = gcvNULL;
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ start += PAGE_SIZE;
+ addr += PAGE_SIZE;
+ }
+ }
+
+ up_write(&current->mm->mmap_sem);
+ }
+ else
+ {
+ /* mdlMap->vmaAddr != gcvNULL means current process has already locked this node. */
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d, mdlMap->vmaAddr=%x", gcvSTATUS_MEMORY_LOCKED, mdlMap->vmaAddr);
+ return gcvSTATUS_MEMORY_LOCKED;
+ }
+
+ /* Convert pointer to MDL. */
+ *Logical = mdlMap->vmaAddr;
+
+ /* Return the page number according to the GPU page size. */
+ gcmkASSERT((PAGE_SIZE % 4096) == 0);
+ gcmkASSERT((PAGE_SIZE / 4096) >= 1);
+
+ *PageCount = mdl->numPages * (PAGE_SIZE / 4096);
+
+ MEMORY_UNLOCK(Os);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Logical=0x%X *PageCount=%lu", *Logical, *PageCount);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_MapPages
+**
+** Map paged memory into a page table.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocation.
+**
+** gctSIZE_T PageCount
+** Number of pages required for the physical address.
+**
+** gctPOINTER PageTable
+** Pointer to the page table to fill in.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_MapPages(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T PageCount,
+ IN gctPOINTER PageTable
+ )
+{
+ return gckOS_MapPagesEx(Os,
+ gcvCORE_MAJOR,
+ Physical,
+ PageCount,
+ PageTable);
+}
+
+gceSTATUS
+gckOS_MapPagesEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T PageCount,
+ IN gctPOINTER PageTable
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ PLINUX_MDL mdl;
+ gctUINT32* table;
+ gctSTRING addr;
+ gctUINT32 bytes;
+ gckMMU mmu;
+ PLINUX_MDL mmuMdl;
+ gctPHYS_ADDR pageTablePhysical;
+
+ gcmkHEADER_ARG("Os=0x%X Core=%d Physical=0x%X PageCount=%u PageTable=0x%X",
+ Os, Core, Physical, PageCount, PageTable);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+ gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
+
+ /* Convert pointer to MDL. */
+ mdl = (PLINUX_MDL)Physical;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): Physical->0x%X PageCount->0x%X PagedMemory->?%d",
+ __FUNCTION__, __LINE__,
+ (gctUINT32) Physical,
+ (gctUINT32) PageCount,
+ mdl->pagedMem
+ );
+
+ MEMORY_LOCK(Os);
+
+ table = (gctUINT32 *)PageTable;
+ bytes = PageCount * sizeof(*table);
+ mmu = Os->device->kernels[Core]->mmu;
+ mmuMdl = (PLINUX_MDL)mmu->pageTablePhysical;
+
+ /* Get all the physical addresses and store them in the page table. */
+
+ addr = mdl->addr;
+
+ if (mdl->pagedMem)
+ {
+ /* Try to get the user pages so DMA can happen. */
+ while (PageCount-- > 0)
+ {
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ if (mdl->contiguous)
+ {
+ gcmkONERROR(
+ gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu,
+ virt_to_phys(addr),
+ table));
+ }
+ else
+ {
+ gcmkONERROR(
+ gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu,
+ page_to_phys(vmalloc_to_page(addr)),
+ table));
+ }
+ }
+ else
+#endif
+ {
+ if (mdl->contiguous)
+ {
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ virt_to_phys(addr),
+ table));
+ }
+ else
+ {
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ page_to_phys(vmalloc_to_page(addr)),
+ table));
+ }
+ }
+
+ table++;
+ addr += 4096;
+ }
+ }
+ else
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): we should not get this call for Non Paged Memory!",
+ __FUNCTION__, __LINE__
+ );
+
+ while (PageCount-- > 0)
+ {
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ gcmkONERROR(
+ gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu,
+ (gctUINT32)virt_to_phys(addr),
+ table));
+ }
+ else
+#endif
+ {
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ (gctUINT32)virt_to_phys(addr),
+ table));
+ }
+ table++;
+ addr += 4096;
+ }
+ }
+
+ /* Get physical address of pageTable */
+ pageTablePhysical = (gctPHYS_ADDR)(mmuMdl->dmaHandle +
+ ((gctUINT32 *)PageTable - mmu->pageTableLogical));
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the mmu page table cache. */
+ gcmkONERROR(gckOS_CacheClean(
+ Os,
+ _GetProcessID(),
+ gcvNULL,
+ pageTablePhysical,
+ PageTable,
+ bytes
+ ));
+#endif
+
+OnError:
+
+ MEMORY_UNLOCK(Os);
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_UnlockPages
+**
+** Unlock memory allocated from the paged pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocation.
+**
+** gctSIZE_T Bytes
+** Number of bytes of the allocation.
+**
+** gctPOINTER Logical
+** Address of the mapped memory.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnlockPages(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctSIZE_T Bytes,
+ IN gctPOINTER Logical
+ )
+{
+ PLINUX_MDL_MAP mdlMap;
+ PLINUX_MDL mdl = (PLINUX_MDL)Physical;
+ struct task_struct * task;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%u Logical=0x%X",
+ Os, Physical, Bytes, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+
+ /* Make sure there is already a mapping...*/
+ gcmkVERIFY_ARGUMENT(mdl->addr != gcvNULL);
+
+ MEMORY_LOCK(Os);
+
+ mdlMap = mdl->maps;
+
+ while (mdlMap != gcvNULL)
+ {
+ if ((mdlMap->vmaAddr != gcvNULL) && (_GetProcessID() == mdlMap->pid))
+ {
+ /* Get the current pointer for the task with stored pid. */
+ task = FIND_TASK_BY_PID(mdlMap->pid);
+
+ if (task != gcvNULL && task->mm != gcvNULL)
+ {
+ down_write(&task->mm->mmap_sem);
+ do_munmap(task->mm, (unsigned long)mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE);
+ up_write(&task->mm->mmap_sem);
+ }
+
+ mdlMap->vmaAddr = gcvNULL;
+ }
+
+ mdlMap = mdlMap->next;
+ }
+
+ MEMORY_UNLOCK(Os);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+
+/*******************************************************************************
+**
+** gckOS_AllocateContiguous
+**
+** Allocate memory from the contiguous pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctBOOL InUserSpace
+** gcvTRUE if the pages need to be mapped into user space.
+**
+** gctSIZE_T * Bytes
+** Pointer to the number of bytes to allocate.
+**
+** OUTPUT:
+**
+** gctSIZE_T * Bytes
+** Pointer to a variable that receives the number of bytes allocated.
+**
+** gctPHYS_ADDR * Physical
+** Pointer to a variable that receives the physical address of the
+** memory allocation.
+**
+** gctPOINTER * Logical
+** Pointer to a variable that receives the logical address of the
+** memory allocation.
+*/
+gceSTATUS
+gckOS_AllocateContiguous(
+ IN gckOS Os,
+ IN gctBOOL InUserSpace,
+ IN OUT gctSIZE_T * Bytes,
+ OUT gctPHYS_ADDR * Physical,
+ OUT gctPOINTER * Logical
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu",
+ Os, InUserSpace, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes != gcvNULL);
+ gcmkVERIFY_ARGUMENT(*Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+
+ /* Same as non-paged memory for now. */
+ gcmkONERROR(gckOS_AllocateNonPagedMemory(Os,
+ InUserSpace,
+ Bytes,
+ Physical,
+ Logical));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu *Physical=0x%X *Logical=0x%X",
+ *Bytes, *Physical, *Logical);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_FreeContiguous
+**
+** Free memory allocated from the contiguous pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocation.
+**
+** gctPOINTER Logical
+** Logicval address of the allocation.
+**
+** gctSIZE_T Bytes
+** Number of bytes of the allocation.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_FreeContiguous(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Logical=0x%X Bytes=%lu",
+ Os, Physical, Logical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ /* Same of non-paged memory for now. */
+ gcmkONERROR(gckOS_FreeNonPagedMemory(Os, Bytes, Physical, Logical));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#if gcdENABLE_VG
+/******************************************************************************
+**
+** gckOS_GetKernelLogical
+**
+** Return the kernel logical pointer that corresponods to the specified
+** hardware address.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctUINT32 Address
+** Hardware physical address.
+**
+** OUTPUT:
+**
+** gctPOINTER * KernelPointer
+** Pointer to a variable receiving the pointer in kernel address space.
+*/
+gceSTATUS
+gckOS_GetKernelLogical(
+ IN gckOS Os,
+ IN gctUINT32 Address,
+ OUT gctPOINTER * KernelPointer
+ )
+{
+ return gckOS_GetKernelLogicalEx(Os, gcvCORE_MAJOR, Address, KernelPointer);
+}
+
+gceSTATUS
+gckOS_GetKernelLogicalEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctUINT32 Address,
+ OUT gctPOINTER * KernelPointer
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%08x", Os, Core, Address);
+
+ do
+ {
+ gckGALDEVICE device;
+ gckKERNEL kernel;
+ gcePOOL pool;
+ gctUINT32 offset;
+ gctPOINTER logical;
+
+ /* Extract the pointer to the gckGALDEVICE class. */
+ device = (gckGALDEVICE) Os->device;
+
+ /* Kernel shortcut. */
+ kernel = device->kernels[Core];
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ 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 = device->internalLogical;
+ break;
+
+ case gcvPOOL_LOCAL_EXTERNAL:
+ /* External memory. */
+ logical = device->externalLogical;
+ break;
+
+ case gcvPOOL_SYSTEM:
+ /* System memory. */
+ logical = device->contiguousBase;
+ break;
+
+ default:
+ /* Invalid memory pool. */
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ /* Build logical address of specified address. */
+ * KernelPointer = ((gctUINT8_PTR) logical) + offset;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*KernelPointer=0x%X", *KernelPointer);
+ return gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+ /* Return status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
+
+/*******************************************************************************
+**
+** gckOS_MapUserPointer
+**
+** Map a pointer from the user process into the kernel address space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Pointer
+** Pointer in user process space that needs to be mapped.
+**
+** gctSIZE_T Size
+** Number of bytes that need to be mapped.
+**
+** OUTPUT:
+**
+** gctPOINTER * KernelPointer
+** Pointer to a variable receiving the mapped pointer in kernel address
+** space.
+*/
+gceSTATUS
+gckOS_MapUserPointer(
+ IN gckOS Os,
+ IN gctPOINTER Pointer,
+ IN gctSIZE_T Size,
+ OUT gctPOINTER * KernelPointer
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu", Os, Pointer, Size);
+
+#if NO_USER_DIRECT_ACCESS_FROM_KERNEL
+{
+ gctPOINTER buf = gcvNULL;
+ gctUINT32 len;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Pointer != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL);
+
+ buf = kmalloc(Size, GFP_KERNEL);
+ if (buf == gcvNULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): Failed to allocate memory.",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ len = copy_from_user(buf, Pointer, Size);
+ if (len != 0)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): Failed to copy data from user.",
+ __FUNCTION__, __LINE__
+ );
+
+ if (buf != gcvNULL)
+ {
+ kfree(buf);
+ }
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_GENERIC_IO);
+ return gcvSTATUS_GENERIC_IO;
+ }
+
+ *KernelPointer = buf;
+}
+#else
+ *KernelPointer = Pointer;
+#endif /* NO_USER_DIRECT_ACCESS_FROM_KERNEL */
+
+ gcmkFOOTER_ARG("*KernelPointer=0x%X", *KernelPointer);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_UnmapUserPointer
+**
+** Unmap a user process pointer from the kernel address space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Pointer
+** Pointer in user process space that needs to be unmapped.
+**
+** gctSIZE_T Size
+** Number of bytes that need to be unmapped.
+**
+** gctPOINTER KernelPointer
+** Pointer in kernel address space that needs to be unmapped.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapUserPointer(
+ IN gckOS Os,
+ IN gctPOINTER Pointer,
+ IN gctSIZE_T Size,
+ IN gctPOINTER KernelPointer
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu KernelPointer=0x%X",
+ Os, Pointer, Size, KernelPointer);
+
+#if NO_USER_DIRECT_ACCESS_FROM_KERNEL
+{
+ gctUINT32 len;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Pointer != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL);
+
+ len = copy_to_user(Pointer, KernelPointer, Size);
+
+ kfree(KernelPointer);
+
+ if (len != 0)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): Failed to copy data to user.",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_GENERIC_IO);
+ return gcvSTATUS_GENERIC_IO;
+ }
+}
+#endif /* NO_USER_DIRECT_ACCESS_FROM_KERNEL */
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_QueryNeedCopy
+**
+** Query whether the memory can be accessed or mapped directly or it has to be
+** copied.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctUINT32 ProcessID
+** Process ID of the current process.
+**
+** OUTPUT:
+**
+** gctBOOL_PTR NeedCopy
+** Pointer to a boolean receiving gcvTRUE if the memory needs a copy or
+** gcvFALSE if the memory can be accessed or mapped dircetly.
+*/
+gceSTATUS
+gckOS_QueryNeedCopy(
+ IN gckOS Os,
+ IN gctUINT32 ProcessID,
+ OUT gctBOOL_PTR NeedCopy
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X ProcessID=%d", Os, ProcessID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(NeedCopy != gcvNULL);
+
+#if NO_USER_DIRECT_ACCESS_FROM_KERNEL
+ /* We need to copy data. */
+ *NeedCopy = gcvTRUE;
+#else
+ /* No need to copy data. */
+ *NeedCopy = gcvFALSE;
+#endif
+
+ /* Success. */
+ gcmkFOOTER_ARG("*NeedCopy=%d", *NeedCopy);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_CopyFromUserData
+**
+** Copy data from user to kernel memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER KernelPointer
+** Pointer to kernel memory.
+**
+** gctPOINTER Pointer
+** Pointer to user memory.
+**
+** gctSIZE_T Size
+** Number of bytes to copy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_CopyFromUserData(
+ IN gckOS Os,
+ IN gctPOINTER KernelPointer,
+ IN gctPOINTER Pointer,
+ IN gctSIZE_T Size
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X KernelPointer=0x%X Pointer=0x%X Size=%lu",
+ Os, KernelPointer, Pointer, Size);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Pointer != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+
+ /* Copy data from user. */
+ if (copy_from_user(KernelPointer, Pointer, Size) != 0)
+ {
+ /* Could not copy all the bytes. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_CopyToUserData
+**
+** Copy data from kernel to user memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER KernelPointer
+** Pointer to kernel memory.
+**
+** gctPOINTER Pointer
+** Pointer to user memory.
+**
+** gctSIZE_T Size
+** Number of bytes to copy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_CopyToUserData(
+ IN gckOS Os,
+ IN gctPOINTER KernelPointer,
+ IN gctPOINTER Pointer,
+ IN gctSIZE_T Size
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X KernelPointer=0x%X Pointer=0x%X Size=%lu",
+ Os, KernelPointer, Pointer, Size);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Pointer != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+
+ /* Copy data to user. */
+ if (copy_to_user(Pointer, KernelPointer, Size) != 0)
+ {
+ /* Could not copy all the bytes. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_WriteMemory
+**
+** Write data to a memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPOINTER Address
+** Address of the memory to write to.
+**
+** gctUINT32 Data
+** Data for register.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_WriteMemory(
+ IN gckOS Os,
+ IN gctPOINTER Address,
+ IN gctUINT32 Data
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Address=0x%X Data=%u", Os, Address, Data);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Address != gcvNULL);
+
+ /* Write memory. */
+ writel(Data, (gctUINT8 *)Address);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_MapUserMemory
+**
+** Lock down a user buffer and return an DMA'able address to be used by the
+** hardware to access it.
+**
+** INPUT:
+**
+** gctPOINTER Memory
+** Pointer to memory to lock down.
+**
+** gctSIZE_T Size
+** Size in bytes of the memory to lock down.
+**
+** OUTPUT:
+**
+** gctPOINTER * Info
+** Pointer to variable receiving the information record required by
+** gckOS_UnmapUserMemory.
+**
+** gctUINT32_PTR Address
+** Pointer to a variable that will receive the address DMA'able by the
+** hardware.
+*/
+gceSTATUS
+gckOS_MapUserMemory(
+ IN gckOS Os,
+ IN gctPOINTER Memory,
+ IN gctSIZE_T Size,
+ OUT gctPOINTER * Info,
+ OUT gctUINT32_PTR Address
+ )
+{
+ return gckOS_MapUserMemoryEx(Os, gcvCORE_MAJOR, Memory, Size, Info, Address);
+}
+
+gceSTATUS
+gckOS_MapUserMemoryEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctPOINTER Memory,
+ IN gctSIZE_T Size,
+ OUT gctPOINTER * Info,
+ OUT gctUINT32_PTR Address
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%x Core=%d Memory=0x%x Size=%lu", Os, Core, Memory, Size);
+
+#if gcdSECURE_USER
+ gcmkONERROR(gckOS_AddMapping(Os, *Address, Memory, Size));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+#else
+{
+ gctSIZE_T pageCount, i, j;
+ gctUINT32_PTR pageTable;
+ gctUINT32 address;
+ gctUINT32 start, end, memory;
+ gctINT result = 0;
+
+ gcsPageInfo_PTR info = gcvNULL;
+ struct page **pages = gcvNULL;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(Info != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Address != gcvNULL);
+
+ do
+ {
+ memory = (gctUINT32) Memory;
+
+ /* Get the number of required pages. */
+ end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ start = memory >> PAGE_SHIFT;
+ pageCount = end - start;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): pageCount: %d.",
+ __FUNCTION__, __LINE__,
+ pageCount
+ );
+
+ /* Invalid argument. */
+ if (pageCount == 0)
+ {
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT);
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ /* Overflow. */
+ if ((memory + Size) < memory)
+ {
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT);
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ MEMORY_MAP_LOCK(Os);
+
+ /* Allocate the Info struct. */
+ info = (gcsPageInfo_PTR)kmalloc(sizeof(gcsPageInfo), GFP_KERNEL);
+
+ if (info == gcvNULL)
+ {
+ status = gcvSTATUS_OUT_OF_MEMORY;
+ break;
+ }
+
+ /* Allocate the array of page addresses. */
+ pages = (struct page **)kmalloc(pageCount * sizeof(struct page *), GFP_KERNEL);
+
+ if (pages == gcvNULL)
+ {
+ status = gcvSTATUS_OUT_OF_MEMORY;
+ break;
+ }
+
+ /* Get the user pages. */
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current,
+ current->mm,
+ memory & PAGE_MASK,
+ pageCount,
+ 1,
+ 0,
+ pages,
+ gcvNULL
+ );
+ up_read(&current->mm->mmap_sem);
+
+ if (result <=0 || result < pageCount)
+ {
+ struct vm_area_struct *vma;
+
+ vma = find_vma(current->mm, memory);
+
+ if (vma && (vma->vm_flags & VM_PFNMAP) )
+ {
+ do
+ {
+ pte_t * pte;
+ spinlock_t * ptl;
+ unsigned long pfn;
+
+ pgd_t * pgd = pgd_offset(current->mm, memory);
+ pud_t * pud = pud_offset(pgd, memory);
+ if (pud)
+ {
+ pmd_t * pmd = pmd_offset(pud, memory);
+ pte = pte_offset_map_lock(current->mm, pmd, memory, &ptl);
+ if (!pte)
+ {
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ pfn = pte_pfn(*pte);
+ *Address = ((pfn << PAGE_SHIFT) | (((unsigned long)Memory) & ~PAGE_MASK))
+ - Os->device->baseAddress;
+ *Info = gcvNULL;
+
+ pte_unmap_unlock(pte, ptl);
+
+ /* Release page info struct. */
+ if (info != gcvNULL)
+ {
+ /* Free the page info struct. */
+ kfree(info);
+ }
+
+ /* Free the page table. */
+ if (pages != gcvNULL)
+ {
+ /* Release the pages if any. */
+ if (result > 0)
+ {
+ for (i = 0; i < result; i++)
+ {
+ if (pages[i] == gcvNULL)
+ {
+ break;
+ }
+
+ page_cache_release(pages[i]);
+ }
+ }
+
+ kfree(pages);
+ }
+
+ MEMORY_MAP_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*Info=0x%X *Address=0x%08x",
+ *Info, *Address);
+ return gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+ *Address = ~0;
+ *Info = gcvNULL;
+
+ status = gcvSTATUS_OUT_OF_RESOURCES;
+ break;
+ }
+ else
+ {
+ status = gcvSTATUS_OUT_OF_RESOURCES;
+ break;
+ }
+ }
+
+ for (i = 0; i < pageCount; i++)
+ {
+ /* Flush(clean) the data cache. */
+#if !defined(ANDROID)
+ dma_sync_single_for_device(
+ gcvNULL,
+ page_to_phys(pages[i]),
+ PAGE_SIZE,
+ DMA_TO_DEVICE);
+#else
+ flush_dcache_page(pages[i]);
+#endif
+ }
+
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ /* Allocate pages inside the page table. */
+ gcmkERR_BREAK(gckVGMMU_AllocatePages(Os->device->kernels[Core]->vg->mmu,
+ pageCount * (PAGE_SIZE/4096),
+ (gctPOINTER *) &pageTable,
+ &address));
+ }
+ else
+#endif
+ {
+ /* Allocate pages inside the page table. */
+ gcmkERR_BREAK(gckMMU_AllocatePages(Os->device->kernels[Core]->mmu,
+ pageCount * (PAGE_SIZE/4096),
+ (gctPOINTER *) &pageTable,
+ &address));
+ }
+ /* Fill the page table. */
+ for (i = 0; i < pageCount; i++)
+ {
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ /* Get the physical address from page struct. */
+ gcmkONERROR(
+ gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu,
+ page_to_phys(pages[i]),
+ pageTable + i * (PAGE_SIZE/4096)));
+ }
+ else
+#endif
+ {
+ /* Get the physical address from page struct. */
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ page_to_phys(pages[i]),
+ pageTable + i * (PAGE_SIZE/4096)));
+ }
+
+ for (j = 1; j < (PAGE_SIZE/4096); j++)
+ {
+ pageTable[i * (PAGE_SIZE/4096) + j] = pageTable[i * (PAGE_SIZE/4096)] + 4096 * j;
+ }
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): pages[%d]: 0x%X, pageTable[%d]: 0x%X.",
+ __FUNCTION__, __LINE__,
+ i, pages[i],
+ i, pageTable[i]);
+ }
+
+ /* Save pointer to page table. */
+ info->pageTable = pageTable;
+ info->pages = pages;
+
+ *Info = (gctPOINTER) info;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): info->pages: 0x%X, info->pageTable: 0x%X, info: 0x%X.",
+ __FUNCTION__, __LINE__,
+ info->pages,
+ info->pageTable,
+ info
+ );
+
+ /* Return address. */
+ *Address = address + (memory & ~PAGE_MASK);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): Address: 0x%X.",
+ __FUNCTION__, __LINE__,
+ *Address
+ );
+
+ /* Success. */
+ status = gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+OnError:
+
+ if (gcmIS_ERROR(status))
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): error occured: %d.",
+ __FUNCTION__, __LINE__,
+ status
+ );
+
+ /* Release page array. */
+ if (result > 0 && pages != gcvNULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): error: page table is freed.",
+ __FUNCTION__, __LINE__
+ );
+
+ for (i = 0; i < result; i++)
+ {
+ if (pages[i] == gcvNULL)
+ {
+ break;
+ }
+ page_cache_release(pages[i]);
+ }
+ }
+
+ if (info!= gcvNULL && pages != gcvNULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): error: pages is freed.",
+ __FUNCTION__, __LINE__
+ );
+
+ /* Free the page table. */
+ kfree(pages);
+ info->pages = gcvNULL;
+ }
+
+ /* Release page info struct. */
+ if (info != gcvNULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): error: info is freed.",
+ __FUNCTION__, __LINE__
+ );
+
+ /* Free the page info struct. */
+ kfree(info);
+ *Info = gcvNULL;
+ }
+ }
+
+ MEMORY_MAP_UNLOCK(Os);
+
+ /* Return the status. */
+ if (gcmIS_SUCCESS(status))
+ {
+ gcmkFOOTER_ARG("*Info=0x%X *Address=0x%08x", *Info, *Address);
+ }
+ else
+ {
+ gcmkFOOTER();
+ }
+ return status;
+}
+#endif
+}
+
+/*******************************************************************************
+**
+** gckOS_UnmapUserMemory
+**
+** Unlock a user buffer and that was previously locked down by
+** gckOS_MapUserMemory.
+**
+** INPUT:
+**
+** gctPOINTER Memory
+** Pointer to memory to unlock.
+**
+** gctSIZE_T Size
+** Size in bytes of the memory to unlock.
+**
+** gctPOINTER Info
+** Information record returned by gckOS_MapUserMemory.
+**
+** gctUINT32_PTR Address
+** The address returned by gckOS_MapUserMemory.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapUserMemory(
+ IN gckOS Os,
+ IN gctPOINTER Memory,
+ IN gctSIZE_T Size,
+ IN gctPOINTER Info,
+ IN gctUINT32 Address
+ )
+{
+ return gckOS_UnmapUserMemoryEx(Os, gcvCORE_MAJOR, Memory, Size, Info, Address);
+}
+
+gceSTATUS
+gckOS_UnmapUserMemoryEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctPOINTER Memory,
+ IN gctSIZE_T Size,
+ IN gctPOINTER Info,
+ IN gctUINT32 Address
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Core=%d Memory=0x%X Size=%lu Info=0x%X Address0x%08x",
+ Os, Core, Memory, Size, Info, Address);
+
+#if gcdSECURE_USER
+ gcmkONERROR(gckOS_RemoveMapping(Os, Memory, Size));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+#else
+{
+ gctUINT32 memory, start, end;
+ gcsPageInfo_PTR info;
+ gctSIZE_T pageCount, i;
+ struct page **pages;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(Info != gcvNULL);
+
+ do
+ {
+ info = (gcsPageInfo_PTR) Info;
+
+ pages = info->pages;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): info=0x%X, pages=0x%X.",
+ __FUNCTION__, __LINE__,
+ info, pages
+ );
+
+ /* Invalid page array. */
+ if (pages == gcvNULL)
+ {
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ memory = (gctUINT32) Memory;
+ end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ start = memory >> PAGE_SHIFT;
+ pageCount = end - start;
+
+ /* Overflow. */
+ if ((memory + Size) < memory)
+ {
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ /* Invalid argument. */
+ if (pageCount == 0)
+ {
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): memory: 0x%X, pageCount: %d, pageTable: 0x%X.",
+ __FUNCTION__, __LINE__,
+ memory, pageCount, info->pageTable
+ );
+
+ MEMORY_MAP_LOCK(Os);
+
+#if gcdENABLE_VG
+ if (Core == gcvCORE_VG)
+ {
+ /* Free the pages from the MMU. */
+ gcmkERR_BREAK(gckVGMMU_FreePages(Os->device->kernels[Core]->vg->mmu,
+ info->pageTable,
+ pageCount * (PAGE_SIZE/4096)
+ ));
+ }
+ else
+#endif
+ {
+ /* Free the pages from the MMU. */
+ gcmkERR_BREAK(gckMMU_FreePages(Os->device->kernels[Core]->mmu,
+ info->pageTable,
+ pageCount * (PAGE_SIZE/4096)
+ ));
+ }
+
+ /* Release the page cache. */
+ for (i = 0; i < pageCount; i++)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): pages[%d]: 0x%X.",
+ __FUNCTION__, __LINE__,
+ i, pages[i]
+ );
+
+ if (!PageReserved(pages[i]))
+ {
+ SetPageDirty(pages[i]);
+ }
+
+#if !defined(ANDROID)
+ /* Invalidate the data cache. */
+ dma_sync_single_for_device(
+ gcvNULL,
+ page_to_phys(pages[i]),
+ PAGE_SIZE,
+ DMA_FROM_DEVICE);
+#endif
+ page_cache_release(pages[i]);
+ }
+
+ /* Success. */
+ status = gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+ if (info != gcvNULL)
+ {
+ /* Free the page array. */
+ if (info->pages != gcvNULL)
+ {
+ kfree(info->pages);
+ }
+
+ kfree(info);
+ }
+
+ MEMORY_MAP_UNLOCK(Os);
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
+}
+
+/*******************************************************************************
+**
+** gckOS_GetBaseAddress
+**
+** Get the base address for the physical memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** OUTPUT:
+**
+** gctUINT32_PTR BaseAddress
+** Pointer to a variable that will receive the base address.
+*/
+gceSTATUS
+gckOS_GetBaseAddress(
+ IN gckOS Os,
+ OUT gctUINT32_PTR BaseAddress
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(BaseAddress != gcvNULL);
+
+ /* Return base address. */
+ *BaseAddress = Os->device->baseAddress;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*BaseAddress=0x%08x", *BaseAddress);
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_SuspendInterrupt(
+ IN gckOS Os
+ )
+{
+ return gckOS_SuspendInterruptEx(Os, gcvCORE_MAJOR);
+}
+
+gceSTATUS
+gckOS_SuspendInterruptEx(
+ IN gckOS Os,
+ IN gceCORE Core
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ disable_irq(Os->device->irqLines[Core]);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_ResumeInterrupt(
+ IN gckOS Os
+ )
+{
+ return gckOS_ResumeInterruptEx(Os, gcvCORE_MAJOR);
+}
+
+gceSTATUS
+gckOS_ResumeInterruptEx(
+ IN gckOS Os,
+ IN gceCORE Core
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ enable_irq(Os->device->irqLines[Core]);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_MemCopy(
+ IN gctPOINTER Destination,
+ IN gctCONST_POINTER Source,
+ IN gctSIZE_T Bytes
+ )
+{
+ gcmkHEADER_ARG("Destination=0x%X Source=0x%X Bytes=%lu",
+ Destination, Source, Bytes);
+
+ gcmkVERIFY_ARGUMENT(Destination != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Source != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ memcpy(Destination, Source, Bytes);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_ZeroMemory(
+ IN gctPOINTER Memory,
+ IN gctSIZE_T Bytes
+ )
+{
+ gcmkHEADER_ARG("Memory=0x%X Bytes=%lu", Memory, Bytes);
+
+ gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ memset(Memory, 0, Bytes);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+********************************* Cache Control ********************************
+*******************************************************************************/
+
+/*******************************************************************************
+** _HandleOuterCache
+**
+** Handle the outer cache for the specified addresses.
+**
+** ARGUMENTS:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** gctUINT32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If gcvNULL it is video memory.
+**
+** gctPOINTER Physical
+** Physical address to flush.
+**
+** gctPOINTER Logical
+** Logical address to flush.
+**
+** gctSIZE_T Bytes
+** Size of the address range in bytes to flush.
+**
+** gceOUTERCACHE_OPERATION Type
+** Operation need to be execute.
+*/
+#if !gcdCACHE_FUNCTION_UNIMPLEMENTED && defined(CONFIG_OUTER_CACHE)
+static inline gceSTATUS
+outer_func(
+ gceCACHEOPERATION Type,
+ unsigned long Start,
+ unsigned long End
+ )
+{
+ switch (Type)
+ {
+ case gcvCACHE_CLEAN:
+ outer_clean_range(Start, End);
+ break;
+ case gcvCACHE_INVALIDATE:
+ outer_inv_range(Start, End);
+ break;
+ case gcvCACHE_FLUSH:
+ outer_flush_range(Start, End);
+ break;
+ default:
+ return gcvSTATUS_INVALID_ARGUMENT;
+ break;
+ }
+ return gcvSTATUS_OK;
+}
+
+static gceSTATUS
+_HandleOuterCache(
+ IN gckOS Os,
+ IN gctUINT32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN gctPOINTER Physical,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes,
+ IN gceCACHEOPERATION Type
+ )
+{
+ gceSTATUS status;
+ gctUINT32 i, pageNum;
+ unsigned long paddr;
+ gctPOINTER vaddr;
+
+ gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu",
+ Os, ProcessID, Handle, Logical, Bytes);
+
+ if (Physical != gcvNULL)
+ {
+ /* Non paged memory or gcvPOOL_USER surface */
+ paddr = (unsigned long) Physical;
+ gcmkONERROR(outer_func(Type, paddr, paddr + Bytes));
+ }
+ else if ((Handle == gcvNULL)
+ || (Handle != gcvNULL && ((PLINUX_MDL)Handle)->contiguous)
+ )
+ {
+ /* Video Memory or contiguous virtual memory */
+ gcmkONERROR(gckOS_GetPhysicalAddress(Os, Logical, (gctUINT32*)&paddr));
+ gcmkONERROR(outer_func(Type, paddr, paddr + Bytes));
+ }
+ else
+ {
+ /* Non contiguous virtual memory */
+ vaddr = (gctPOINTER)gcmALIGN_BASE((gctUINT32)Logical, PAGE_SIZE);
+ pageNum = GetPageCount(Bytes, 0);
+
+ for (i = 0; i < pageNum; i += 1)
+ {
+ gcmkONERROR(_ConvertLogical2Physical(
+ Os,
+ vaddr + PAGE_SIZE * i,
+ ProcessID,
+ (PLINUX_MDL)Handle,
+ (gctUINT32*)&paddr
+ ));
+
+ gcmkONERROR(outer_func(Type, paddr, paddr + PAGE_SIZE));
+ }
+ }
+
+ mb();
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
+
+/*******************************************************************************
+** gckOS_CacheClean
+**
+** Clean the cache for the specified addresses. The GPU is going to need the
+** data. If the system is allocating memory as non-cachable, this function can
+** be ignored.
+**
+** ARGUMENTS:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** gctUINT32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If gcvNULL it is video memory.
+**
+** gctPOINTER Physical
+** Physical address to flush.
+**
+** gctPOINTER Logical
+** Logical address to flush.
+**
+** gctSIZE_T Bytes
+** Size of the address range in bytes to flush.
+*/
+gceSTATUS
+gckOS_CacheClean(
+ IN gckOS Os,
+ IN gctUINT32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN gctPOINTER Physical,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu",
+ Os, ProcessID, Handle, Logical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+#if !gcdCACHE_FUNCTION_UNIMPLEMENTED
+#ifdef CONFIG_ARM
+
+ /* Inner cache. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+ dmac_map_area(Logical, Bytes, DMA_TO_DEVICE);
+# else
+ dmac_clean_range(Logical, Logical + Bytes);
+# endif
+
+#if defined(CONFIG_OUTER_CACHE)
+ /* Outer cache. */
+ _HandleOuterCache(Os, ProcessID, Handle, Physical, Logical, Bytes, gcvCACHE_CLEAN);
+#endif
+
+#elif defined(CONFIG_MIPS)
+
+ dma_cache_wback((unsigned long) Logical, Bytes);
+
+#else
+ dma_sync_single_for_device(
+ gcvNULL,
+ Physical,
+ Bytes,
+ DMA_TO_DEVICE);
+#endif
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+** gckOS_CacheInvalidate
+**
+** Invalidate the cache for the specified addresses. The GPU is going to need
+** data. If the system is allocating memory as non-cachable, this function can
+** be ignored.
+**
+** ARGUMENTS:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** gctUINT32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If gcvNULL it is video memory.
+**
+** gctPOINTER Logical
+** Logical address to flush.
+**
+** gctSIZE_T Bytes
+** Size of the address range in bytes to flush.
+*/
+gceSTATUS
+gckOS_CacheInvalidate(
+ IN gckOS Os,
+ IN gctUINT32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN gctPOINTER Physical,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu",
+ Os, ProcessID, Handle, Logical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+#if !gcdCACHE_FUNCTION_UNIMPLEMENTED
+#ifdef CONFIG_ARM
+
+ /* Inner cache. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+ dmac_map_area(Logical, Bytes, DMA_FROM_DEVICE);
+# else
+ dmac_inv_range(Logical, Logical + Bytes);
+# endif
+
+#if defined(CONFIG_OUTER_CACHE)
+ /* Outer cache. */
+ _HandleOuterCache(Os, ProcessID, Handle, Physical, Logical, Bytes, gcvCACHE_INVALIDATE);
+#endif
+
+#elif defined(CONFIG_MIPS)
+ dma_cache_inv((unsigned long) Logical, Bytes);
+#else
+ dma_sync_single_for_device(
+ gcvNULL,
+ Physical,
+ Bytes,
+ DMA_FROM_DEVICE);
+#endif
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+** gckOS_CacheFlush
+**
+** Clean the cache for the specified addresses and invalidate the lines as
+** well. The GPU is going to need and modify the data. If the system is
+** allocating memory as non-cachable, this function can be ignored.
+**
+** ARGUMENTS:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** gctUINT32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If gcvNULL it is video memory.
+**
+** gctPOINTER Logical
+** Logical address to flush.
+**
+** gctSIZE_T Bytes
+** Size of the address range in bytes to flush.
+*/
+gceSTATUS
+gckOS_CacheFlush(
+ IN gckOS Os,
+ IN gctUINT32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN gctPOINTER Physical,
+ IN gctPOINTER Logical,
+ IN gctSIZE_T Bytes
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu",
+ Os, ProcessID, Handle, Logical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Logical != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+#if !gcdCACHE_FUNCTION_UNIMPLEMENTED
+#ifdef CONFIG_ARM
+ /* Inner cache. */
+ dmac_flush_range(Logical, Logical + Bytes);
+
+#if defined(CONFIG_OUTER_CACHE)
+ /* Outer cache. */
+ _HandleOuterCache(Os, ProcessID, Handle, Physical, Logical, Bytes, gcvCACHE_FLUSH);
+#endif
+
+#elif defined(CONFIG_MIPS)
+ dma_cache_wback_inv((unsigned long) Logical, Bytes);
+#else
+ dma_sync_single_for_device(
+ gcvNULL,
+ Physical,
+ Bytes,
+ DMA_BIDIRECTIONAL);
+#endif
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+********************************* Broadcasting *********************************
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** gckOS_Broadcast
+**
+** System hook for broadcast events from the kernel driver.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** gceBROADCAST Reason
+** Reason for the broadcast. Can be one of the following values:
+**
+** gcvBROADCAST_GPU_IDLE
+** Broadcasted when the kernel driver thinks the GPU might be
+** idle. This can be used to handle power management.
+**
+** gcvBROADCAST_GPU_COMMIT
+** Broadcasted when any client process commits a command
+** buffer. This can be used to handle power management.
+**
+** gcvBROADCAST_GPU_STUCK
+** Broadcasted when the kernel driver hits the timeout waiting
+** for the GPU.
+**
+** gcvBROADCAST_FIRST_PROCESS
+** First process is trying to connect to the kernel.
+**
+** gcvBROADCAST_LAST_PROCESS
+** Last process has detached from the kernel.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_Broadcast(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN gceBROADCAST Reason
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Hardware=0x%X Reason=%d", Os, Hardware, Reason);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ switch (Reason)
+ {
+ case gcvBROADCAST_FIRST_PROCESS:
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "First process has attached");
+ break;
+
+ case gcvBROADCAST_LAST_PROCESS:
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "Last process has detached");
+
+ /* Put GPU OFF. */
+ gcmkONERROR(
+ gckHARDWARE_SetPowerManagementState(Hardware,
+ gcvPOWER_OFF_BROADCAST));
+ break;
+
+ case gcvBROADCAST_GPU_IDLE:
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "GPU idle.");
+
+ /* Put GPU IDLE. */
+ gcmkONERROR(
+ gckHARDWARE_SetPowerManagementState(Hardware,
+ gcvPOWER_IDLE_BROADCAST));
+
+ /* Add idle process DB. */
+ gcmkONERROR(gckKERNEL_AddProcessDB(Hardware->kernel,
+ 1,
+ gcvDB_IDLE,
+ gcvNULL, gcvNULL, 0));
+ break;
+
+ case gcvBROADCAST_GPU_COMMIT:
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "COMMIT has arrived.");
+
+ /* Add busy process DB. */
+ gcmkONERROR(gckKERNEL_AddProcessDB(Hardware->kernel,
+ 0,
+ gcvDB_IDLE,
+ gcvNULL, gcvNULL, 0));
+
+ /* Put GPU ON. */
+ gcmkONERROR(
+ gckHARDWARE_SetPowerManagementState(Hardware, gcvPOWER_ON_AUTO));
+ break;
+
+ case gcvBROADCAST_GPU_STUCK:
+ gcmkTRACE_N(gcvLEVEL_ERROR, 0, "gcvBROADCAST_GPU_STUCK\n");
+ gcmkONERROR(_DumpGPUState(Os));
+ gcmkONERROR(gckKERNEL_Recovery(Hardware->kernel));
+ break;
+
+ case gcvBROADCAST_AXI_BUS_ERROR:
+ gcmkTRACE_N(gcvLEVEL_ERROR, 0, "gcvBROADCAST_AXI_BUS_ERROR\n");
+ gcmkONERROR(_DumpGPUState(Os));
+ /*gcmkONERROR(gckKERNEL_Recovery(Hardware->kernel));*/
+ break;
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_BroadcastHurry
+**
+** The GPU is running too slow.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** gctUINT Urgency
+** The higher the number, the higher the urgency to speed up the GPU.
+** The maximum value is defined by the gcdDYNAMIC_EVENT_THRESHOLD.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_BroadcastHurry(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN gctUINT Urgency
+ )
+{
+ gcmkHEADER_ARG("Os=0x%x Hardware=0x%x Urgency=%u", Os, Hardware, Urgency);
+
+ /* Do whatever you need to do to speed up the GPU now. */
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_BroadcastCalibrateSpeed
+**
+** Calibrate the speed of the GPU.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** gctUINT Idle, Time
+** Idle/Time will give the percentage the GPU is idle, so you can use
+** this to calibrate the working point of the GPU.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_BroadcastCalibrateSpeed(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN gctUINT Idle,
+ IN gctUINT Time
+ )
+{
+ gcmkHEADER_ARG("Os=0x%x Hardware=0x%x Idle=%u Time=%u",
+ Os, Hardware, Idle, Time);
+
+ /* Do whatever you need to do to callibrate the GPU speed. */
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+********************************** Semaphores **********************************
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** gckOS_CreateSemaphore
+**
+** Create a semaphore.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** OUTPUT:
+**
+** gctPOINTER * Semaphore
+** Pointer to the variable that will receive the created semaphore.
+*/
+gceSTATUS
+gckOS_CreateSemaphore(
+ IN gckOS Os,
+ OUT gctPOINTER * Semaphore
+ )
+{
+ gceSTATUS status;
+ struct semaphore *sem = gcvNULL;
+
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ /* Allocate the semaphore structure. */
+ sem = (struct semaphore *)kmalloc(gcmSIZEOF(struct semaphore), GFP_KERNEL);
+ if (sem == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ /* Initialize the semaphore. */
+ sema_init(sem, 1);
+
+ /* Return to caller. */
+ *Semaphore = (gctPOINTER) sem;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_AcquireSemaphore
+**
+** Acquire a semaphore.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gctPOINTER Semaphore
+** Pointer to the semaphore thet needs to be acquired.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AcquireSemaphore(
+ IN gckOS Os,
+ IN gctPOINTER Semaphore
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%08X Semaphore=0x%08X", Os, Semaphore);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ /* Acquire the semaphore. */
+ if (down_interruptible((struct semaphore *) Semaphore))
+ {
+ gcmkONERROR(gcvSTATUS_TIMEOUT);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_TryAcquireSemaphore
+**
+** Try to acquire a semaphore.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gctPOINTER Semaphore
+** Pointer to the semaphore thet needs to be acquired.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_TryAcquireSemaphore(
+ IN gckOS Os,
+ IN gctPOINTER Semaphore
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%x", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ /* Acquire the semaphore. */
+ if (down_trylock((struct semaphore *) Semaphore))
+ {
+ /* Timeout. */
+ status = gcvSTATUS_TIMEOUT;
+ gcmkFOOTER();
+ return status;
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_ReleaseSemaphore
+**
+** Release a previously acquired semaphore.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gctPOINTER Semaphore
+** Pointer to the semaphore thet needs to be released.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_ReleaseSemaphore(
+ IN gckOS Os,
+ IN gctPOINTER Semaphore
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ /* Release the semaphore. */
+ up((struct semaphore *) Semaphore);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_DestroySemaphore
+**
+** Destroy a semaphore.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gctPOINTER Semaphore
+** Pointer to the semaphore thet needs to be destroyed.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DestroySemaphore(
+ IN gckOS Os,
+ IN gctPOINTER Semaphore
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ /* Free the sempahore structure. */
+ kfree(Semaphore);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_GetProcessID
+**
+** Get current process ID.
+**
+** INPUT:
+**
+** Nothing.
+**
+** OUTPUT:
+**
+** gctUINT32_PTR ProcessID
+** Pointer to the variable that receives the process ID.
+*/
+gceSTATUS
+gckOS_GetProcessID(
+ OUT gctUINT32_PTR ProcessID
+ )
+{
+ /* Get process ID. */
+ if (ProcessID != gcvNULL)
+ {
+ *ProcessID = _GetProcessID();
+ }
+
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_GetThreadID
+**
+** Get current thread ID.
+**
+** INPUT:
+**
+** Nothing.
+**
+** OUTPUT:
+**
+** gctUINT32_PTR ThreadID
+** Pointer to the variable that receives the thread ID.
+*/
+gceSTATUS
+gckOS_GetThreadID(
+ OUT gctUINT32_PTR ThreadID
+ )
+{
+ /* Get thread ID. */
+ if (ThreadID != gcvNULL)
+ {
+ *ThreadID = _GetThreadID();
+ }
+
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_SetGPUPower
+**
+** Set the power of the GPU on or off.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** gctBOOL Clock
+** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock.
+**
+** gctBOOL Power
+** gcvTRUE to turn on the power, or gcvFALSE to turn off the power.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_SetGPUPower(
+ IN gckOS Os,
+ IN gctBOOL Clock,
+ IN gctBOOL Power
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Clock=%d Power=%d", Os, Clock, Power);
+
+ /* TODO: Put your code here. */
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*----- Profile --------------------------------------------------------------*/
+
+gceSTATUS
+gckOS_GetProfileTick(
+ OUT gctUINT64_PTR Tick
+ )
+{
+ struct timespec time;
+
+ ktime_get_ts(&time);
+
+ *Tick = time.tv_nsec + time.tv_sec * 1000000000ULL;
+
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_QueryProfileTickRate(
+ OUT gctUINT64_PTR TickRate
+ )
+{
+ struct timespec res;
+
+ hrtimer_get_res(CLOCK_MONOTONIC, &res);
+
+ *TickRate = res.tv_nsec + res.tv_sec * 1000000000ULL;
+
+ return gcvSTATUS_OK;
+}
+
+gctUINT32
+gckOS_ProfileToMS(
+ IN gctUINT64 Ticks
+ )
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23)
+ return div_u64(Ticks, 1000000);
+#else
+ gctUINT64 rem = Ticks;
+ gctUINT64 b = 1000000;
+ gctUINT64 res, d = 1;
+ gctUINT32 high = rem >> 32;
+
+ /* Reduce the thing a bit first */
+ res = 0;
+ if (high >= 1000000)
+ {
+ high /= 1000000;
+ res = (gctUINT64) high << 32;
+ rem -= (gctUINT64) (high * 1000000) << 32;
+ }
+
+ while (((gctINT64) b > 0) && (b < rem))
+ {
+ b <<= 1;
+ d <<= 1;
+ }
+
+ do
+ {
+ if (rem >= b)
+ {
+ rem -= b;
+ res += d;
+ }
+
+ b >>= 1;
+ d >>= 1;
+ }
+ while (d);
+
+ return (gctUINT32) res;
+#endif
+}
+
+#if gcdENABLE_BANK_ALIGNMENT
+/*******************************************************************************
+** gckOS_GetSurfaceBankAlignment
+**
+** Return the required offset alignment required to the make BaseAddress
+** aligned properly.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to gcoOS object.
+**
+** gceSURF_TYPE Type
+** Type of allocation.
+**
+** gctUINT32 BaseAddress
+** Base address of current video memory node.
+**
+** OUTPUT:
+**
+** gctUINT32_PTR Alignment
+** Pointer to a variable thah twil hold the number of bytes to skip in
+** the current video memory node in order to make the alignment bank
+** aligned.
+*/
+gceSTATUS
+gckOS_GetSurfaceBankAlignment(
+ IN gckOS Os,
+ IN gceSURF_TYPE Type,
+ IN gctUINT32 BaseAddress,
+ OUT gctUINT32_PTR Alignment
+ )
+{
+ gctUINT32 alignedBaseAddress;
+
+ gcmkHEADER_ARG("Os=0x%x Type=%d BaseAddress=0x%x ", Os, Type, BaseAddress);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Alignment != gcvNULL);
+
+ switch (Type)
+ {
+ case gcvSURF_RENDER_TARGET:
+ /* Align to first 4kB bank. */
+ alignedBaseAddress = (((BaseAddress >> 15) << 3) + (0x8 + 0x0)) << 12;
+ break;
+
+ case gcvSURF_DEPTH:
+ /* Align to third 4kB bank. */
+ alignedBaseAddress = (((BaseAddress >> 15) << 3) + (0x8 + 0x2)) << 12;
+
+ /* Add 64-byte offset to change channel bit 6. */
+ alignedBaseAddress += 64;
+ break;
+
+ default:
+ /* no alignment needed. */
+ alignedBaseAddress = BaseAddress;
+ }
+
+ /* Return alignment. */
+ *Alignment = alignedBaseAddress - BaseAddress;
+
+ /* Return the status. */
+ gcmkFOOTER_ARG("*Alignment=%u", *Alignment);
+ return gcvSTATUS_OK;
+}
+#endif
+
+/******************************************************************************\
+******************************* Signal Management ******************************
+\******************************************************************************/
+
+#undef _GC_OBJ_ZONE
+#define _GC_OBJ_ZONE gcvZONE_SIGNAL
+
+/*******************************************************************************
+**
+** gckOS_CreateSignal
+**
+** Create a new signal.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctBOOL ManualReset
+** If set to gcvTRUE, gckOS_Signal with gcvFALSE must be called in
+** order to set the signal to nonsignaled state.
+** If set to gcvFALSE, the signal will automatically be set to
+** nonsignaled state by gckOS_WaitSignal function.
+**
+** OUTPUT:
+**
+** gctSIGNAL * Signal
+** Pointer to a variable receiving the created gctSIGNAL.
+*/
+gceSTATUS
+gckOS_CreateSignal(
+ IN gckOS Os,
+ IN gctBOOL ManualReset,
+ OUT gctSIGNAL * Signal
+ )
+{
+ gcsSIGNAL_PTR signal;
+
+ gcmkHEADER_ARG("Os=0x%X ManualReset=%d", Os, ManualReset);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
+
+ /* Create an event structure. */
+ signal = (gcsSIGNAL_PTR) kmalloc(sizeof(gcsSIGNAL), GFP_KERNEL);
+
+ if (signal == gcvNULL)
+ {
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ signal->manualReset = ManualReset;
+ init_completion(&signal->obj);
+ atomic_set(&signal->ref, 1);
+
+ *Signal = (gctSIGNAL) signal;
+
+ gcmkFOOTER_ARG("*Signal=0x%X", *Signal);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_DestroySignal
+**
+** Destroy a signal.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to the gctSIGNAL.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DestroySignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal
+ )
+{
+ gcsSIGNAL_PTR signal;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X", Os, Signal);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
+
+ signal = (gcsSIGNAL_PTR) Signal;
+
+ if (atomic_dec_and_test(&signal->ref))
+ {
+ /* Free the sgianl. */
+ kfree(Signal);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_Signal
+**
+** Set a state of the specified signal.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to the gctSIGNAL.
+**
+** gctBOOL State
+** If gcvTRUE, the signal will be set to signaled state.
+** If gcvFALSE, the signal will be set to nonsignaled state.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_Signal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN gctBOOL State
+ )
+{
+ gcsSIGNAL_PTR signal;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X State=%d", Os, Signal, State);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
+
+ signal = (gcsSIGNAL_PTR) Signal;
+
+ if (State)
+ {
+ /* Set the event to a signaled state. */
+ complete(&signal->obj);
+ }
+ else
+ {
+ /* Set the event to an unsignaled state. */
+ INIT_COMPLETION(signal->obj);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+#if gcdENABLE_VG
+gceSTATUS
+gckOS_SetSignalVG(
+ IN gckOS Os,
+ IN gctHANDLE Process,
+ IN gctSIGNAL Signal
+ )
+{
+ gceSTATUS status;
+ gctINT result;
+ struct task_struct * userTask;
+ struct siginfo info;
+
+ userTask = FIND_TASK_BY_PID((pid_t) Process);
+
+ if (userTask != gcvNULL)
+ {
+ info.si_signo = 48;
+ info.si_code = __SI_CODE(__SI_RT, SI_KERNEL);
+ info.si_pid = 0;
+ info.si_uid = 0;
+ info.si_ptr = (gctPOINTER) Signal;
+
+ /* Signals with numbers between 32 and 63 are real-time,
+ send a real-time signal to the user process. */
+ result = send_sig_info(48, &info, userTask);
+
+ printk("gckOS_SetSignalVG:0x%x\n", result);
+ /* Error? */
+ if (result < 0)
+ {
+ status = gcvSTATUS_GENERIC_IO;
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): an error has occurred.\n",
+ __FUNCTION__, __LINE__
+ );
+ }
+ else
+ {
+ status = gcvSTATUS_OK;
+ }
+ }
+ else
+ {
+ status = gcvSTATUS_GENERIC_IO;
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): an error has occurred.\n",
+ __FUNCTION__, __LINE__
+ );
+ }
+
+ /* Return status. */
+ return status;
+}
+#endif
+
+/*******************************************************************************
+**
+** gckOS_UserSignal
+**
+** Set the specified signal which is owned by a process to signaled state.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to the gctSIGNAL.
+**
+** gctHANDLE Process
+** Handle of process owning the signal.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UserSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN gctHANDLE Process
+ )
+{
+ gceSTATUS status;
+ gctSIGNAL signal;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=%d",
+ Os, Signal, (gctINT32) Process);
+
+ /* Map the signal into kernel space. */
+ gcmkONERROR(gckOS_MapSignal(Os, Signal, Process, &signal));
+
+ /* Signal. */
+ status = gckOS_Signal(Os, signal, gcvTRUE);
+
+ /* Unmap the signal */
+ gcmkVERIFY_OK(gckOS_UnmapSignal(Os, Signal));
+
+ gcmkFOOTER();
+ return status;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_WaitSignal
+**
+** Wait for a signal to become signaled.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to the gctSIGNAL.
+**
+** gctUINT32 Wait
+** Number of milliseconds to wait.
+** Pass the value of gcvINFINITE for an infinite wait.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_WaitSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN gctUINT32 Wait
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ gcsSIGNAL_PTR signal;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X Wait=0x%08X", Os, Signal, Wait);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
+
+ signal = (gcsSIGNAL_PTR) Signal;
+
+ might_sleep();
+
+ spin_lock_irq(&signal->obj.wait.lock);
+
+ if (signal->obj.done)
+ {
+ if (!signal->manualReset)
+ {
+ signal->obj.done = 0;
+ }
+
+ status = gcvSTATUS_OK;
+ }
+ else if (Wait == 0)
+ {
+ status = gcvSTATUS_TIMEOUT;
+ }
+ else
+ {
+ /* Convert wait to milliseconds. */
+#if gcdDETECT_TIMEOUT
+ gctINT timeout = (Wait == gcvINFINITE)
+ ? gcdINFINITE_TIMEOUT * HZ / 1000
+ : Wait * HZ / 1000;
+
+ gctUINT complained = 0;
+#else
+ gctINT timeout = (Wait == gcvINFINITE)
+ ? MAX_SCHEDULE_TIMEOUT
+ : Wait * HZ / 1000;
+#endif
+
+ DECLARE_WAITQUEUE(wait, current);
+ wait.flags |= WQ_FLAG_EXCLUSIVE;
+ __add_wait_queue_tail(&signal->obj.wait, &wait);
+
+ while (gcvTRUE)
+ {
+ if (signal_pending(current))
+ {
+ /* Interrupt received. */
+ status = gcvSTATUS_INTERRUPTED;
+ break;
+ }
+
+ __set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&signal->obj.wait.lock);
+ timeout = schedule_timeout(timeout);
+ spin_lock_irq(&signal->obj.wait.lock);
+
+ if (signal->obj.done)
+ {
+ if (!signal->manualReset)
+ {
+ signal->obj.done = 0;
+ }
+
+ status = gcvSTATUS_OK;
+ break;
+ }
+
+#if gcdDETECT_TIMEOUT
+ if ((Wait == gcvINFINITE) && (timeout == 0))
+ {
+ gctUINT32 dmaAddress1, dmaAddress2;
+ gctUINT32 dmaState1, dmaState2;
+
+ dmaState1 = dmaState2 =
+ dmaAddress1 = dmaAddress2 = 0;
+
+ /* Verify whether DMA is running. */
+ gcmkVERIFY_OK(_VerifyDMA(
+ Os, &dmaAddress1, &dmaAddress2, &dmaState1, &dmaState2
+ ));
+
+#if gcdDETECT_DMA_ADDRESS
+ /* Dump only if DMA appears stuck. */
+ if (
+ (dmaAddress1 == dmaAddress2)
+#if gcdDETECT_DMA_STATE
+ && (dmaState1 == dmaState2)
+#endif
+ )
+#endif
+ {
+ /* Increment complain count. */
+ complained += 1;
+
+ gcmkVERIFY_OK(_DumpGPUState(Os));
+
+ gcmkPRINT(
+ "%s(%d): signal 0x%X; forced message flush (%d).",
+ __FUNCTION__, __LINE__, Signal, complained
+ );
+
+ /* Flush the debug cache. */
+ gcmkDEBUGFLUSH(dmaAddress2);
+ }
+
+ /* Reset timeout. */
+ timeout = gcdINFINITE_TIMEOUT * HZ / 1000;
+ }
+#endif
+
+ if (timeout == 0)
+ {
+
+ status = gcvSTATUS_TIMEOUT;
+ break;
+ }
+ }
+
+ __remove_wait_queue(&signal->obj.wait, &wait);
+
+#if gcdDETECT_TIMEOUT
+ if (complained)
+ {
+ gcmkPRINT(
+ "%s(%d): signal=0x%X; waiting done; status=%d",
+ __FUNCTION__, __LINE__, Signal, status
+ );
+ }
+#endif
+ }
+
+ spin_unlock_irq(&signal->obj.wait.lock);
+
+ /* Return status. */
+ gcmkFOOTER_ARG("Signal=0x%X status=%d", Signal, status);
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_MapSignal
+**
+** Map a signal in to the current process space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to tha gctSIGNAL to map.
+**
+** gctHANDLE Process
+** Handle of process owning the signal.
+**
+** OUTPUT:
+**
+** gctSIGNAL * MappedSignal
+** Pointer to a variable receiving the mapped gctSIGNAL.
+*/
+gceSTATUS
+gckOS_MapSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN gctHANDLE Process,
+ OUT gctSIGNAL * MappedSignal
+ )
+{
+ gctINT signalID;
+ gcsSIGNAL_PTR signal;
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=0x%X", Os, Signal, Process);
+
+ gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
+ gcmkVERIFY_ARGUMENT(MappedSignal != gcvNULL);
+
+ signalID = (gctINT) Signal - 1;
+
+ gcmkONERROR(gckOS_AcquireMutex(Os, Os->signal.lock, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ if (signalID >= 0 && signalID < Os->signal.tableLen)
+ {
+ /* It is a user space signal. */
+ signal = Os->signal.table[signalID];
+
+ if (signal == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_FOUND);
+ }
+ }
+ else
+ {
+ /* It is a kernel space signal structure. */
+ signal = (gcsSIGNAL_PTR) Signal;
+ }
+
+ if (atomic_inc_return(&signal->ref) <= 1)
+ {
+ /* The previous value is 0, it has been deleted. */
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Os, Os->signal.lock));
+
+ *MappedSignal = (gctSIGNAL) signal;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*MappedSignal=0x%X", *MappedSignal);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signal.lock));
+ }
+
+ /* Return the staus. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_UnmapSignal
+**
+** Unmap a signal .
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to that gctSIGNAL mapped.
+*/
+gceSTATUS
+gckOS_UnmapSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal
+ )
+{
+ gctINT signalID;
+ gcsSIGNAL_PTR signal;
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X ", Os, Signal);
+
+ gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
+
+ signalID = (gctINT) Signal - 1;
+
+ gcmkONERROR(gckOS_AcquireMutex(Os, Os->signal.lock, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ if (signalID >= 0 && signalID < Os->signal.tableLen)
+ {
+ /* It is a user space signal. */
+ signal = Os->signal.table[signalID];
+
+ if (signal == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ if (atomic_read(&signal->ref) == 1)
+ {
+ /* Update the table. */
+ Os->signal.table[signalID] = gcvNULL;
+
+ if (Os->signal.unused++ == 0)
+ {
+ Os->signal.currentID = signalID;
+ }
+ }
+
+ gcmkONERROR(gckOS_DestroySignal(Os, signal));
+ }
+ else
+ {
+ /* It is a kernel space signal structure. */
+ signal = (gcsSIGNAL_PTR) Signal;
+
+ gcmkONERROR(gckOS_DestroySignal(Os, signal));
+ }
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Os, Os->signal.lock));
+
+ /* Success. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signal.lock));
+ }
+
+ /* Return the staus. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_CreateUserSignal
+**
+** Create a new signal to be used in the user space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctBOOL ManualReset
+** If set to gcvTRUE, gckOS_Signal with gcvFALSE must be called in
+** order to set the signal to nonsignaled state.
+** If set to gcvFALSE, the signal will automatically be set to
+** nonsignaled state by gckOS_WaitSignal function.
+**
+** OUTPUT:
+**
+** gctINT * SignalID
+** Pointer to a variable receiving the created signal's ID.
+*/
+gceSTATUS
+gckOS_CreateUserSignal(
+ IN gckOS Os,
+ IN gctBOOL ManualReset,
+ OUT gctINT * SignalID
+ )
+{
+ gcsSIGNAL_PTR signal = gcvNULL;
+ gctINT unused, currentID, tableLen;
+ gctPOINTER * table;
+ gctINT i;
+ gceSTATUS status;
+ gctBOOL acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%0x ManualReset=%d", Os, ManualReset);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(SignalID != gcvNULL);
+
+ /* Lock the table. */
+ gcmkONERROR(gckOS_AcquireMutex(Os, Os->signal.lock, gcvINFINITE));
+
+ acquired = gcvTRUE;
+
+ if (Os->signal.unused < 1)
+ {
+ /* Enlarge the table. */
+ table = (gctPOINTER *) kmalloc(
+ sizeof(gctPOINTER) * (Os->signal.tableLen + USER_SIGNAL_TABLE_LEN_INIT),
+ GFP_KERNEL);
+
+ if (table == gcvNULL)
+ {
+ /* Out of memory. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ memset(table + Os->signal.tableLen, 0, sizeof(gctPOINTER) * USER_SIGNAL_TABLE_LEN_INIT);
+ memcpy(table, Os->signal.table, sizeof(gctPOINTER) * Os->signal.tableLen);
+
+ /* Release the old table. */
+ kfree(Os->signal.table);
+
+ /* Update the table. */
+ Os->signal.table = table;
+ Os->signal.currentID = Os->signal.tableLen;
+ Os->signal.tableLen += USER_SIGNAL_TABLE_LEN_INIT;
+ Os->signal.unused += USER_SIGNAL_TABLE_LEN_INIT;
+ }
+
+ table = Os->signal.table;
+ currentID = Os->signal.currentID;
+ tableLen = Os->signal.tableLen;
+ unused = Os->signal.unused;
+
+ /* Create a new signal. */
+ gcmkONERROR(
+ gckOS_CreateSignal(Os, ManualReset, (gctSIGNAL *) &signal));
+
+ /* Save the process ID. */
+ signal->process = (gctHANDLE) _GetProcessID();
+
+ table[currentID] = signal;
+
+ /* Plus 1 to avoid gcvNULL claims. */
+ *SignalID = currentID + 1;
+
+ /* Update the currentID. */
+ if (--unused > 0)
+ {
+ for (i = 0; i < tableLen; i++)
+ {
+ if (++currentID >= tableLen)
+ {
+ /* Wrap to the begin. */
+ currentID = 0;
+ }
+
+ if (table[currentID] == gcvNULL)
+ {
+ break;
+ }
+ }
+ }
+
+ Os->signal.table = table;
+ Os->signal.currentID = currentID;
+ Os->signal.tableLen = tableLen;
+ Os->signal.unused = unused;
+
+ gcmkONERROR(
+ gckOS_ReleaseMutex(Os, Os->signal.lock));
+
+ gcmkFOOTER_ARG("*SignalID=%d", gcmOPT_VALUE(SignalID));
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkONERROR(
+ gckOS_ReleaseMutex(Os, Os->signal.lock));
+ }
+
+ /* Return the staus. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_DestroyUserSignal
+**
+** Destroy a signal to be used in the user space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctINT SignalID
+** The signal's ID.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DestroyUserSignal(
+ IN gckOS Os,
+ IN gctINT SignalID
+ )
+{
+ gceSTATUS status;
+ gcsSIGNAL_PTR signal;
+ gctBOOL acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%X SignalID=%d", Os, SignalID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ gcmkONERROR(
+ gckOS_AcquireMutex(Os, Os->signal.lock, gcvINFINITE));
+
+ acquired = gcvTRUE;
+
+ if (SignalID < 1 || SignalID > Os->signal.tableLen)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): invalid signal->%d.",
+ __FUNCTION__, __LINE__,
+ (gctINT) SignalID
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ SignalID -= 1;
+
+ signal = Os->signal.table[SignalID];
+
+ if (signal == gcvNULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): signal is gcvNULL.",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+
+ if (atomic_read(&signal->ref) == 1)
+ {
+ /* Update the table. */
+ Os->signal.table[SignalID] = gcvNULL;
+
+ if (Os->signal.unused++ == 0)
+ {
+ Os->signal.currentID = SignalID;
+ }
+ }
+
+ gcmkONERROR(
+ gckOS_DestroySignal(Os, signal));
+
+ gcmkVERIFY_OK(
+ gckOS_ReleaseMutex(Os, Os->signal.lock));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(
+ gckOS_ReleaseMutex(Os, Os->signal.lock));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_WaitUserSignal
+**
+** Wait for a signal used in the user mode to become signaled.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctINT SignalID
+** Signal ID.
+**
+** gctUINT32 Wait
+** Number of milliseconds to wait.
+** Pass the value of gcvINFINITE for an infinite wait.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_WaitUserSignal(
+ IN gckOS Os,
+ IN gctINT SignalID,
+ IN gctUINT32 Wait
+ )
+{
+ gceSTATUS status;
+ gcsSIGNAL_PTR signal;
+ gctBOOL acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%X SignalID=%d Wait=%u", Os, SignalID, Wait);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ gcmkONERROR(gckOS_AcquireMutex(Os, Os->signal.lock, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ if (SignalID < 1 || SignalID > Os->signal.tableLen)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): invalid signal %d",
+ __FUNCTION__, __LINE__,
+ SignalID
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ SignalID -= 1;
+
+ signal = Os->signal.table[SignalID];
+
+ gcmkONERROR(gckOS_ReleaseMutex(Os, Os->signal.lock));
+ acquired = gcvFALSE;
+
+ if (signal == gcvNULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): signal is gcvNULL.",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+
+ status = gckOS_WaitSignal(Os, signal, Wait);
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signal.lock));
+ }
+
+ /* Return the staus. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_SignalUserSignal
+**
+** Set a state of the specified signal to be used in the user space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctINT SignalID
+** SignalID.
+**
+** gctBOOL State
+** If gcvTRUE, the signal will be set to signaled state.
+** If gcvFALSE, the signal will be set to nonsignaled state.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_SignalUserSignal(
+ IN gckOS Os,
+ IN gctINT SignalID,
+ IN gctBOOL State
+ )
+{
+ gceSTATUS status;
+ gcsSIGNAL_PTR signal;
+ gctBOOL acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%X SignalID=%d State=%d", Os, SignalID, State);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ gcmkONERROR(gckOS_AcquireMutex(Os, Os->signal.lock, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ if ((SignalID < 1)
+ || (SignalID > Os->signal.tableLen)
+ )
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): invalid signal->%d.",
+ __FUNCTION__, __LINE__,
+ SignalID
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ SignalID -= 1;
+
+ signal = Os->signal.table[SignalID];
+
+ gcmkONERROR(gckOS_ReleaseMutex(Os, Os->signal.lock));
+ acquired = gcvFALSE;
+
+ if (signal == gcvNULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): signal is gcvNULL.",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
+ }
+
+
+ status = gckOS_Signal(Os, signal, State);
+
+ /* Success. */
+ gcmkFOOTER();
+ return status;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(
+ gckOS_ReleaseMutex(Os, Os->signal.lock));
+ }
+
+ /* Return the staus. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckOS_CleanProcessSignal(
+ gckOS Os,
+ gctHANDLE Process
+ )
+{
+ gctINT signal;
+
+ gcmkHEADER_ARG("Os=0x%X Process=%d", Os, Process);
+
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ gcmkVERIFY_OK(gckOS_AcquireMutex(Os,
+ Os->signal.lock,
+ gcvINFINITE
+ ));
+
+ if (Os->signal.unused == Os->signal.tableLen)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os,
+ Os->signal.lock
+ ));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ for (signal = 0; signal < Os->signal.tableLen; signal++)
+ {
+ if (Os->signal.table[signal] != gcvNULL &&
+ ((gcsSIGNAL_PTR)Os->signal.table[signal])->process == Process)
+ {
+ gckOS_DestroySignal(Os, Os->signal.table[signal]);
+
+ /* Update the signal table. */
+ Os->signal.table[signal] = gcvNULL;
+ if (Os->signal.unused++ == 0)
+ {
+ Os->signal.currentID = signal;
+ }
+ }
+ }
+
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os,
+ Os->signal.lock
+ ));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+#if gcdENABLE_VG
+gceSTATUS
+gckOS_CreateSemaphoreVG(
+ IN gckOS Os,
+ OUT gctSEMAPHORE * Semaphore
+ )
+{
+ gceSTATUS status;
+ struct semaphore * newSemaphore;
+
+ gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore);
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ do
+ {
+ /* Allocate the semaphore structure. */
+ newSemaphore = (struct semaphore *)kmalloc(gcmSIZEOF(struct semaphore), GFP_KERNEL);
+ if (newSemaphore == gcvNULL)
+ {
+ gcmkERR_BREAK(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ /* Initialize the semaphore. */
+ sema_init(newSemaphore, 0);
+
+ /* Set the handle. */
+ * Semaphore = (gctSEMAPHORE) newSemaphore;
+
+ /* Success. */
+ status = gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+ gcmkFOOTER();
+ /* Return the status. */
+ return status;
+}
+
+
+gceSTATUS
+gckOS_IncrementSemaphore(
+ IN gckOS Os,
+ IN gctSEMAPHORE Semaphore
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore);
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ /* Increment the semaphore's count. */
+ up((struct semaphore *) Semaphore);
+
+ gcmkFOOTER_NO();
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_DecrementSemaphore(
+ IN gckOS Os,
+ IN gctSEMAPHORE Semaphore
+ )
+{
+ gceSTATUS status;
+ gctINT result;
+
+ gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore);
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL);
+
+ do
+ {
+ /* Decrement the semaphore's count. If the count is zero, wait
+ until it gets incremented. */
+ result = down_interruptible((struct semaphore *) Semaphore);
+
+ /* Signal received? */
+ if (result != 0)
+ {
+ status = gcvSTATUS_TERMINATE;
+ break;
+ }
+
+ /* Success. */
+ status = gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+ gcmkFOOTER();
+ /* Return the status. */
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_SetSignal
+**
+** Set the specified signal to signaled state.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gctHANDLE Process
+** Handle of process owning the signal.
+**
+** gctSIGNAL Signal
+** Pointer to the gctSIGNAL.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_SetSignal(
+ IN gckOS Os,
+ IN gctHANDLE Process,
+ IN gctSIGNAL Signal
+ )
+{
+ gceSTATUS status;
+ gctINT result;
+ struct task_struct * userTask;
+ struct siginfo info;
+
+ userTask = FIND_TASK_BY_PID((pid_t) Process);
+
+ if (userTask != gcvNULL)
+ {
+ info.si_signo = 48;
+ info.si_code = __SI_CODE(__SI_RT, SI_KERNEL);
+ info.si_pid = 0;
+ info.si_uid = 0;
+ info.si_ptr = (gctPOINTER) Signal;
+
+ /* Signals with numbers between 32 and 63 are real-time,
+ send a real-time signal to the user process. */
+ result = send_sig_info(48, &info, userTask);
+
+ /* Error? */
+ if (result < 0)
+ {
+ status = gcvSTATUS_GENERIC_IO;
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): an error has occurred.\n",
+ __FUNCTION__, __LINE__
+ );
+ }
+ else
+ {
+ status = gcvSTATUS_OK;
+ }
+ }
+ else
+ {
+ status = gcvSTATUS_GENERIC_IO;
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): an error has occurred.\n",
+ __FUNCTION__, __LINE__
+ );
+ }
+
+ /* Return status. */
+ return status;
+}
+
+/******************************************************************************\
+******************************** Thread Object *********************************
+\******************************************************************************/
+
+gceSTATUS
+gckOS_StartThread(
+ IN gckOS Os,
+ IN gctTHREADFUNC ThreadFunction,
+ IN gctPOINTER ThreadParameter,
+ OUT gctTHREAD * Thread
+ )
+{
+ gceSTATUS status;
+ struct task_struct * thread;
+
+ gcmkHEADER_ARG("Os=0x%X ", Os);
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(ThreadFunction != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Thread != gcvNULL);
+
+ do
+ {
+ /* Create the thread. */
+ thread = kthread_create(
+ ThreadFunction,
+ ThreadParameter,
+ "Vivante Kernel Thread"
+ );
+
+ /* Failed? */
+ if (IS_ERR(thread))
+ {
+ status = gcvSTATUS_GENERIC_IO;
+ break;
+ }
+
+ /* Start the thread. */
+ wake_up_process(thread);
+
+ /* Set the thread handle. */
+ * Thread = (gctTHREAD) thread;
+
+ /* Success. */
+ status = gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+ gcmkFOOTER();
+ /* Return the status. */
+ return status;
+}
+
+gceSTATUS
+gckOS_StopThread(
+ IN gckOS Os,
+ IN gctTHREAD Thread
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Thread=0x%x", Os, Thread);
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Thread != gcvNULL);
+
+ /* Thread should have already been enabled to terminate. */
+ kthread_stop((struct task_struct *) Thread);
+
+ gcmkFOOTER_NO();
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_VerifyThread(
+ IN gckOS Os,
+ IN gctTHREAD Thread
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Thread=0x%x", Os, Thread);
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Thread != gcvNULL);
+
+ gcmkFOOTER_NO();
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+#endif
+
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.h
new file mode 100644
index 00000000000..f155f10cb7d
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#ifndef __gc_hal_kernel_os_h_
+#define __gc_hal_kernel_os_h_
+
+typedef struct _LINUX_MDL_MAP
+{
+ gctINT pid;
+ gctPOINTER vmaAddr;
+ struct vm_area_struct * vma;
+ struct _LINUX_MDL_MAP * next;
+}
+LINUX_MDL_MAP;
+
+typedef struct _LINUX_MDL_MAP * PLINUX_MDL_MAP;
+
+typedef struct _LINUX_MDL
+{
+ gctINT pid;
+ char * addr;
+
+#ifdef NO_DMA_COHERENT
+ gctPOINTER kaddr;
+#endif /* NO_DMA_COHERENT */
+
+ gctINT numPages;
+ gctINT pagedMem;
+ gctBOOL contiguous;
+ dma_addr_t dmaHandle;
+ PLINUX_MDL_MAP maps;
+ struct _LINUX_MDL * prev;
+ struct _LINUX_MDL * next;
+}
+LINUX_MDL, *PLINUX_MDL;
+
+extern PLINUX_MDL_MAP
+FindMdlMap(
+ IN PLINUX_MDL Mdl,
+ IN gctINT PID
+ );
+
+typedef struct _DRIVER_ARGS
+{
+ gctPOINTER InputBuffer;
+ gctUINT32 InputBufferSize;
+ gctPOINTER OutputBuffer;
+ gctUINT32 OutputBufferSize;
+}
+DRIVER_ARGS;
+
+/* Cleanup the signal table. */
+gceSTATUS
+gckOS_CleanProcessSignal(
+ gckOS Os,
+ gctHANDLE Process
+ );
+
+#endif /* __gc_hal_kernel_os_h_ */