summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2014-08-01 11:36:35 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2014-08-01 11:37:49 +0200
commit95e524cd35a28f14cdb2215e033ba1b96321103b (patch)
treef471c60d691fd457d44c2144bea358adf57c2b3a
parent33640fd3142e141586c749bbe75ef35542908fd8 (diff)
downloadetna_viv-95e524cd35a28f14cdb2215e033ba1b96321103b.tar.gz
etna_viv-95e524cd35a28f14cdb2215e033ba1b96321103b.tar.xz
Add cleaned v4 driver from GCW tree
Much easier to read than the others.
-rw-r--r--kernel_drivers/README.md3
-rw-r--r--kernel_drivers/v4_cleaned/Makefile54
-rwxr-xr-xkernel_drivers/v4_cleaned/deobfuscate.sh6
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_base_internal.h649
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_internal.h1780
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel.c2614
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel.h718
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_command.c2572
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_context.c1675
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_context.h136
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_db.c1429
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_debug.c2484
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_debug.h81
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_device.c1214
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_device.h128
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_driver.c1119
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_event.c2197
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.c5087
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.h96
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_linux.c409
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_linux.h71
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c1236
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_os.c7495
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_os.h85
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_video_memory.c1788
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_options_internal.h465
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_types_internal.h190
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_version.h36
-rw-r--r--kernel_drivers/v4_cleaned/notes.txt46
29 files changed, 35863 insertions, 0 deletions
diff --git a/kernel_drivers/README.md b/kernel_drivers/README.md
index 666b92a..414f94d 100644
--- a/kernel_drivers/README.md
+++ b/kernel_drivers/README.md
@@ -10,3 +10,6 @@ These may be older (or newer) than the ones provided here but at least you're su
Has some VG (GC350) information.
+- v4_cleaned: Cleaned V4 driver from GCW tree
+ https://github.com/gcwnow/linux
+
diff --git a/kernel_drivers/v4_cleaned/Makefile b/kernel_drivers/v4_cleaned/Makefile
new file mode 100644
index 0000000..08a77dd
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/Makefile
@@ -0,0 +1,54 @@
+#
+# Open Vivante driver Makefile.
+#
+ccflags-$(CONFIG_GPU_VIVANTE_NEWSIGNAL) += -DUSE_NEW_LINUX_SIGNAL=1
+ccflags-$(CONFIG_GPU_VIVANTE_PROFILER) += -DVIVANTE_PROFILER=1
+ccflags-$(CONFIG_GPU_VIVANTE_DEBUG) += -DgcdDEBUG=gcdDEBUG_ALL
+ccflags-$(CONFIG_GPU_VIVANTE_NO_DMA_COHERENT) += -DNO_DMA_COHERENT
+
+#ccflags-y += -DUSE_PLATFORM_DRIVER=1
+ccflags-y += -DENUM_WORKAROUND=0
+ccflags-y += -DNO_USER_DIRECT_ACCESS_FROM_KERNEL=1
+ccflags-y += -DgcdPAGED_MEMORY_CACHEABLE=0
+ccflags-y += -DgcdNONPAGED_MEMORY_CACHEABLE=0
+ccflags-y += -DgcdNONPAGED_MEMORY_BUFFERABLE=1
+ccflags-y += -DgcdCACHE_FUNCTION_UNIMPLEMENTED=0
+ccflags-y += -DgcdSUPPORT_SWAP_RECTANGLE=0
+ccflags-y += -DgcdENABLE_OUTER_CACHE_PATCH=0
+ccflags-y += -DgcdUSE_OPENCL=0
+
+#
+# Vivante GC860 for Ingenic JZ4770 SOC.
+#
+ifneq ($(CONFIG_GPU_VIVANTE_GC860), )
+ccflags-y += \
+ -DgcdMMU_SIZE=262144 \
+ -DgcdPOWER_CONTROL_DELAY=1 \
+ -DgcdPOWER_MANAGEMENT=0 \
+ -DUSE_PLATFORM_DRIVER=0 \
+ -DNO_DMA_COHERENT
+endif
+
+#
+# Include paths.
+#
+ccflags-y += \
+ -I$(obj) \
+ -Iinclude/uapi/vivante
+
+obj-$(CONFIG_GPU_VIVANTE_V4) += galcore_v4.o
+
+galcore_v4-objs += \
+ gc_hal_kernel.o \
+ gc_hal_kernel_command.o \
+ gc_hal_kernel_context.o \
+ gc_hal_kernel_db.o \
+ gc_hal_kernel_debug.o \
+ gc_hal_kernel_device.o \
+ gc_hal_kernel_driver.o \
+ gc_hal_kernel_event.o \
+ gc_hal_kernel_hardware.o \
+ gc_hal_kernel_linux.o \
+ gc_hal_kernel_mmu.o \
+ gc_hal_kernel_os.o \
+ gc_hal_kernel_video_memory.o
diff --git a/kernel_drivers/v4_cleaned/deobfuscate.sh b/kernel_drivers/v4_cleaned/deobfuscate.sh
new file mode 100755
index 0000000..b34f0bc
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/deobfuscate.sh
@@ -0,0 +1,6 @@
+for X in *.[c]; do
+ mv $X $X.bak
+ #perl /home/orion/projects/etna_viv/tools/deobfuscate-complex-vivante.pl $X.bak > $X
+ perl /home/orion/projects/etna_viv/tools/deobfuscate-simple2-vivante.pl $X.bak > $X
+done
+
diff --git a/kernel_drivers/v4_cleaned/gc_hal_base_internal.h b/kernel_drivers/v4_cleaned/gc_hal_base_internal.h
new file mode 100644
index 0000000..3d5485f
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_base_internal.h
@@ -0,0 +1,649 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_base_internal_h_
+#define __gc_hal_base_internal_h_
+
+#include "gc_hal.h"
+#include "gc_hal_types_internal.h"
+
+#include "gc_hal_version.h"
+
+#include "gc_hal_options_internal.h"
+
+/******************************************************************************\
+****************************** Object Declarations *****************************
+\******************************************************************************/
+
+typedef struct _gckOS * gckOS;
+typedef struct _gcoHAL * gcoHAL;
+typedef struct _gcoOS * gcoOS;
+
+typedef struct _gcsPOINT * gcsPOINT_PTR;
+typedef struct _gcsSIZE * gcsSIZE_PTR;
+typedef struct _gcsRECT * gcsRECT_PTR;
+
+#define gcmkOS_SAFE_FREE(os, mem) \
+ gckOS_Free(os, mem); \
+ mem = NULL
+
+/*----------------------------------------------------------------------------*/
+/*----- Profile --------------------------------------------------------------*/
+
+gceSTATUS
+gckOS_GetProfileTick(
+ OUT u64 *Tick
+ );
+
+u32
+gckOS_ProfileToMS(
+ IN u64 Ticks
+ );
+
+#define _gcmPROFILE_INIT(prefix, freq, start) \
+ do { \
+ prefix ## OS_QueryProfileTickRate(&(freq)); \
+ prefix ## OS_GetProfileTick(&(start)); \
+ } while (gcvFALSE)
+
+#define _gcmPROFILE_QUERY(prefix, start, ticks) \
+ do { \
+ prefix ## OS_GetProfileTick(&(ticks)); \
+ (ticks) = ((ticks) > (start)) ? ((ticks) - (start)) \
+ : (~0ull - (start) + (ticks) + 1); \
+ } while (gcvFALSE)
+
+#if gcdENABLE_PROFILING
+# define gcmkPROFILE_INIT(freq, start) _gcmPROFILE_INIT(gck, freq, start)
+# define gcmkPROFILE_QUERY(start, ticks) _gcmPROFILE_QUERY(gck, start, ticks)
+# define gcmPROFILE_INIT(freq, start) _gcmPROFILE_INIT(gco, freq, start)
+# define gcmPROFILE_QUERY(start, ticks) _gcmPROFILE_QUERY(gco, start, ticks)
+# define gcmPROFILE_ONLY(x) x
+# define gcmPROFILE_ELSE(x) do { } while (gcvFALSE)
+# define gcmPROFILE_DECLARE_ONLY(x) x
+# define gcmPROFILE_DECLARE_ELSE(x) typedef x
+#else
+# define gcmkPROFILE_INIT(start, freq) do { } while (gcvFALSE)
+# define gcmkPROFILE_QUERY(start, ticks) do { } while (gcvFALSE)
+# define gcmPROFILE_INIT(start, freq) do { } while (gcvFALSE)
+# define gcmPROFILE_QUERY(start, ticks) do { } while (gcvFALSE)
+# define gcmPROFILE_ONLY(x) do { } while (gcvFALSE)
+# define gcmPROFILE_ELSE(x) x
+# define gcmPROFILE_DECLARE_ONLY(x) typedef x
+# define gcmPROFILE_DECLARE_ELSE(x) x
+#endif
+
+static inline int
+gckMATH_ModuloInt(
+ IN int X,
+ IN int Y
+ )
+{
+ if(Y ==0) {return 0;}
+ else {return X % Y;}
+}
+
+/******************************************************************************\
+**************************** Coordinate Structures *****************************
+\******************************************************************************/
+
+typedef struct _gcsPOINT
+{
+ s32 x;
+ s32 y;
+}
+gcsPOINT;
+
+typedef struct _gcsSIZE
+{
+ s32 width;
+ s32 height;
+}
+gcsSIZE;
+
+typedef struct _gcsRECT
+{
+ s32 left;
+ s32 top;
+ s32 right;
+ s32 bottom;
+}
+gcsRECT;
+
+typedef struct _gcsVIDMEM_NODE_SHARED_INFO
+{
+ int tileStatusDisabled;
+ gcsPOINT SrcOrigin;
+ gcsPOINT DestOrigin;
+ gcsSIZE RectSize;
+ u32 clearValue;
+}
+gcsVIDMEM_NODE_SHARED_INFO;
+
+/*******************************************************************************
+**
+** gcmFATAL
+**
+** Print a message to the debugger and execute a break point.
+**
+** ARGUMENTS:
+**
+** message Message.
+** ... Optional arguments.
+*/
+
+void
+gckOS_DebugFatal(
+ IN const char *Message,
+ ...
+ );
+
+#if gcmIS_DEBUG(gcdDEBUG_FATAL)
+# define gcmFATAL gcoOS_DebugFatal
+# define gcmkFATAL gckOS_DebugFatal
+#else
+# define gcmFATAL(...)
+# define gcmkFATAL(...)
+#endif
+
+/*******************************************************************************
+**
+** gcmTRACE
+**
+** Print a message to the debugfer if the correct level has been set. In
+** retail mode this macro does nothing.
+**
+** ARGUMENTS:
+**
+** level Level of message.
+** message Message.
+** ... Optional arguments.
+*/
+#define gcvLEVEL_NONE -1
+#define gcvLEVEL_ERROR 0
+#define gcvLEVEL_WARNING 1
+#define gcvLEVEL_INFO 2
+#define gcvLEVEL_VERBOSE 3
+
+void
+gckOS_DebugTrace(
+ IN u32 Level,
+ IN const char *Message,
+ ...
+ );
+
+void
+gckOS_DebugTraceN(
+ IN u32 Level,
+ IN unsigned int ArgumentSize,
+ IN const char *Message,
+ ...
+ );
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+# define gcmkTRACE gckOS_DebugTrace
+# define gcmkTRACE_N gckOS_DebugTraceN
+#else
+# define gcmkTRACE(...)
+# define gcmkTRACE_N(...)
+#endif
+
+/* Zones common for kernel and user. */
+#define gcvZONE_OS (1 << 0)
+#define gcvZONE_HARDWARE (1 << 1)
+#define gcvZONE_HEAP (1 << 2)
+#define gcvZONE_SIGNAL (1 << 27)
+
+/* Kernel zones. */
+#define gcvZONE_KERNEL (1 << 3)
+#define gcvZONE_VIDMEM (1 << 4)
+#define gcvZONE_COMMAND (1 << 5)
+#define gcvZONE_DRIVER (1 << 6)
+#define gcvZONE_CMODEL (1 << 7)
+#define gcvZONE_MMU (1 << 8)
+#define gcvZONE_EVENT (1 << 9)
+#define gcvZONE_DEVICE (1 << 10)
+#define gcvZONE_DATABASE (1 << 11)
+#define gcvZONE_INTERRUPT (1 << 12)
+
+
+/* Handy zones. */
+#define gcvZONE_NONE 0
+#define gcvZONE_ALL 0x0FFFFFFF
+
+/*******************************************************************************
+**
+** gcmTRACE_ZONE
+**
+** Print a message to the debugger if the correct level and zone has been
+** set. In retail mode this macro does nothing.
+**
+** ARGUMENTS:
+**
+** Level Level of message.
+** Zone Zone of message.
+** Message Message.
+** ... Optional arguments.
+*/
+
+void
+gckOS_DebugTraceZone(
+ IN u32 Level,
+ IN u32 Zone,
+ IN const char *Message,
+ ...
+ );
+
+void
+gckOS_DebugTraceZoneN(
+ IN u32 Level,
+ IN u32 Zone,
+ IN unsigned int ArgumentSize,
+ IN const char *Message,
+ ...
+ );
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+# define gcmkTRACE_ZONE gckOS_DebugTraceZone
+# define gcmkTRACE_ZONE_N gckOS_DebugTraceZoneN
+#else
+# define gcmkTRACE_ZONE(...)
+# define gcmkTRACE_ZONE_N(...)
+#endif
+
+/*******************************************************************************
+**
+** gcmDEBUG_ONLY
+**
+** Execute a statement or function only in DEBUG mode.
+**
+** ARGUMENTS:
+**
+** f Statement or function to execute.
+*/
+#if gcmIS_DEBUG(gcdDEBUG_CODE)
+# define gcmDEBUG_ONLY(f) f
+#else
+# define gcmDEBUG_ONLY(f)
+#endif
+
+/******************************************************************************\
+******************************** Logging Macros ********************************
+\******************************************************************************/
+
+#define gcdHEADER_LEVEL gcvLEVEL_VERBOSE
+
+#define gcmkHEADER() \
+ s8 __kernel__ = 1; \
+ s8 *__kernel_ptr__ = &__kernel__; \
+ gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \
+ "++%s(%d)", __FUNCTION__, __LINE__)
+
+#define gcmkHEADER_ARG(Text, ...) \
+ s8 __kernel__ = 1; \
+ s8 *__kernel_ptr__ = &__kernel__; \
+ gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \
+ "++%s(%d): " Text, __FUNCTION__, __LINE__, __VA_ARGS__)
+
+#define gcmkFOOTER() \
+ gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \
+ "--%s(%d): status=%d(%s)", \
+ __FUNCTION__, __LINE__, status, gckOS_DebugStatus2Name(status)); \
+ *__kernel_ptr__ -= 1
+
+#define gcmkFOOTER_NO() \
+ gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \
+ "--%s(%d)", __FUNCTION__, __LINE__); \
+ *__kernel_ptr__ -= 1
+
+#define gcmkFOOTER_ARG(Text, ...) \
+ gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \
+ "--%s(%d): " Text, \
+ __FUNCTION__, __LINE__, __VA_ARGS__); \
+ *__kernel_ptr__ -= 1
+
+#define gcmOPT_VALUE(ptr) (((ptr) == NULL) ? 0 : *(ptr))
+#define gcmOPT_POINTER(ptr) (((ptr) == NULL) ? NULL : *(ptr))
+
+void
+gckOS_Print(
+ IN const char *Message,
+ ...
+ );
+
+void
+gckOS_PrintN(
+ IN unsigned int ArgumentSize,
+ IN const char *Message,
+ ...
+ );
+
+void
+gckOS_CopyPrint(
+ IN const char *Message,
+ ...
+ );
+
+#define gcmkPRINT gckOS_Print
+#define gcmkPRINT_N gckOS_PrintN
+
+#if gcdPRINT_VERSION
+# define gcmPRINT_VERSION() do { \
+ _gcmPRINT_VERSION(gcm); \
+ gcmSTACK_DUMP(); \
+ } while (0)
+# define gcmkPRINT_VERSION() _gcmPRINT_VERSION(gcmk)
+# define _gcmPRINT_VERSION(prefix) \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ "Vivante HAL version %d.%d.%d build %d %s %s", \
+ gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, \
+ gcvVERSION_BUILD, gcvVERSION_DATE, gcvVERSION_TIME )
+#else
+# define gcmPRINT_VERSION() do { gcmSTACK_DUMP(); } while (gcvFALSE)
+# define gcmkPRINT_VERSION() do { } while (gcvFALSE)
+#endif
+
+typedef enum _gceDUMP_BUFFER
+{
+ gceDUMP_BUFFER_CONTEXT,
+ gceDUMP_BUFFER_USER,
+ gceDUMP_BUFFER_KERNEL,
+ gceDUMP_BUFFER_LINK,
+ gceDUMP_BUFFER_WAITLINK,
+ gceDUMP_BUFFER_FROM_USER,
+}
+gceDUMP_BUFFER;
+
+void
+gckOS_DumpBuffer(
+ IN gckOS Os,
+ IN void *Buffer,
+ IN unsigned int Size,
+ IN gceDUMP_BUFFER Type,
+ IN int CopyMessage
+ );
+
+#define gcmkDUMPBUFFER gckOS_DumpBuffer
+
+#if gcdDUMP_COMMAND
+# define gcmkDUMPCOMMAND(Os, Buffer, Size, Type, CopyMessage) \
+ gcmkDUMPBUFFER(Os, Buffer, Size, Type, CopyMessage)
+#else
+# define gcmkDUMPCOMMAND(Os, Buffer, Size, Type, CopyMessage)
+#endif
+
+#if gcmIS_DEBUG(gcdDEBUG_CODE)
+
+void
+gckOS_DebugFlush(
+ const char *CallerName,
+ unsigned int LineNumber,
+ u32 DmaAddress
+ );
+
+# define gcmkDEBUGFLUSH(DmaAddress) \
+ gckOS_DebugFlush(__FUNCTION__, __LINE__, DmaAddress)
+#else
+# define gcmkDEBUGFLUSH(DmaAddress)
+#endif
+
+/*******************************************************************************
+**
+** gcmBREAK
+**
+** Break into the debugger. In retail mode this macro does nothing.
+**
+** ARGUMENTS:
+**
+** None.
+*/
+
+void
+gckOS_DebugBreak(
+ void
+ );
+
+#if gcmIS_DEBUG(gcdDEBUG_BREAK)
+# define gcmkBREAK gckOS_DebugBreak
+#else
+# define gcmkBREAK()
+#endif
+
+/*******************************************************************************
+**
+** gcmASSERT
+**
+** Evaluate an expression and break into the debugger if the expression
+** evaluates to false. In retail mode this macro does nothing.
+**
+** ARGUMENTS:
+**
+** exp Expression to evaluate.
+*/
+#if gcmIS_DEBUG(gcdDEBUG_ASSERT)
+# define _gcmASSERT(prefix, exp) \
+ do \
+ { \
+ if (!(exp)) \
+ { \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ #prefix "ASSERT at %s(%d)", \
+ __FUNCTION__, __LINE__); \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ "(%s)", #exp); \
+ prefix##BREAK(); \
+ } \
+ } \
+ while (gcvFALSE)
+# define gcmASSERT(exp) _gcmASSERT(gcm, exp)
+# define gcmkASSERT(exp) _gcmASSERT(gcmk, exp)
+#else
+# define gcmASSERT(exp)
+# define gcmkASSERT(exp)
+#endif
+
+/*******************************************************************************
+**
+** gcmVERIFY
+**
+** Verify if an expression returns true. If the expression does not
+** evaluates to true, an assertion will happen in debug mode.
+**
+** ARGUMENTS:
+**
+** exp Expression to evaluate.
+*/
+#if gcmIS_DEBUG(gcdDEBUG_ASSERT)
+# define gcmVERIFY(exp) gcmASSERT(exp)
+# define gcmkVERIFY(exp) gcmkASSERT(exp)
+#else
+# define gcmVERIFY(exp) exp
+# define gcmkVERIFY(exp) exp
+#endif
+
+/*******************************************************************************
+**
+** gcmVERIFY_OK
+**
+** Verify a fucntion returns gcvSTATUS_OK. If the function does not return
+** gcvSTATUS_OK, an assertion will happen in debug mode.
+**
+** ARGUMENTS:
+**
+** func Function to evaluate.
+*/
+void
+gckOS_Verify(
+ IN gceSTATUS Status
+ );
+
+#if gcmIS_DEBUG(gcdDEBUG_ASSERT)
+# define gcmkVERIFY_OK(func) \
+ do \
+ { \
+ gceSTATUS verifyStatus = func; \
+ if (verifyStatus != gcvSTATUS_OK) \
+ { \
+ gcmkTRACE( \
+ gcvLEVEL_ERROR, \
+ "gcmkVERIFY_OK(%d): function returned %d", \
+ __LINE__, verifyStatus \
+ ); \
+ } \
+ gckOS_Verify(verifyStatus); \
+ gcmkASSERT(verifyStatus == gcvSTATUS_OK); \
+ } \
+ while (gcvFALSE)
+#else
+# define gcmVERIFY_OK(func) func
+# define gcmkVERIFY_OK(func) func
+#endif
+
+const char *
+gckOS_DebugStatus2Name(
+ gceSTATUS status
+ );
+
+/*******************************************************************************
+**
+** gcmERR_BREAK
+**
+** Executes a break statement on error.
+**
+** ASSUMPTIONS:
+**
+** 'status' variable of gceSTATUS type must be defined.
+**
+** ARGUMENTS:
+**
+** func Function to evaluate.
+*/
+#define _gcmkERR_BREAK(prefix, func) \
+ status = func; \
+ if (gcmIS_ERROR(status)) \
+ { \
+ prefix##PRINT_VERSION(); \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ #prefix "ERR_BREAK: status=%d(%s) @ %s(%d)", \
+ status, gckOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \
+ break; \
+ } \
+ do { } while (gcvFALSE)
+#define gcmkERR_BREAK(func) _gcmkERR_BREAK(gcmk, func)
+
+/*******************************************************************************
+**
+** gcmONERROR
+**
+** Jump to the error handler in case there is an error.
+**
+** ASSUMPTIONS:
+**
+** 'status' variable of gceSTATUS type must be defined.
+**
+** ARGUMENTS:
+**
+** func Function to evaluate.
+*/
+#define _gcmkONERROR(prefix, func) \
+ do \
+ { \
+ status = func; \
+ if (gcmIS_ERROR(status)) \
+ { \
+ prefix##PRINT_VERSION(); \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ #prefix "ONERROR: status=%d(%s) @ %s(%d)", \
+ status, gckOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \
+ goto OnError; \
+ } \
+ } \
+ while (gcvFALSE)
+#define gcmkONERROR(func) _gcmkONERROR(gcmk, func)
+
+/*******************************************************************************
+**
+** gcmVERIFY_ARGUMENT
+**
+** Assert if an argument does not apply to the specified expression. If
+** the argument evaluates to false, gcvSTATUS_INVALID_ARGUMENT will be
+** returned from the current function. In retail mode this macro does
+** nothing.
+**
+** ARGUMENTS:
+**
+** arg Argument to evaluate.
+*/
+# define _gcmVERIFY_ARGUMENT(prefix, arg) \
+ do \
+ { \
+ if (!(arg)) \
+ { \
+ prefix##TRACE(gcvLEVEL_ERROR, #prefix "VERIFY_ARGUMENT failed:"); \
+ prefix##ASSERT(arg); \
+ prefix##FOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); \
+ return gcvSTATUS_INVALID_ARGUMENT; \
+ } \
+ } \
+ while (gcvFALSE)
+# define gcmkVERIFY_ARGUMENT(arg) _gcmVERIFY_ARGUMENT(gcmk, arg)
+
+/*******************************************************************************
+**
+** gcmDEBUG_VERIFY_ARGUMENT
+**
+** Works just like gcmVERIFY_ARGUMENT, but is only valid in debug mode.
+** Use this to verify arguments inside non-public API functions.
+*/
+#if gcdDEBUG
+# define gcmkDEBUG_VERIFY_ARGUMENT(arg) _gcmkVERIFY_ARGUMENT(gcm, arg)
+#else
+# define gcmkDEBUG_VERIFY_ARGUMENT(arg)
+#endif
+/*******************************************************************************
+**
+** gcmVERIFY_ARGUMENT_RETURN
+**
+** Assert if an argument does not apply to the specified expression. If
+** the argument evaluates to false, gcvSTATUS_INVALID_ARGUMENT will be
+** returned from the current function. In retail mode this macro does
+** nothing.
+**
+** ARGUMENTS:
+**
+** arg Argument to evaluate.
+*/
+# define _gcmVERIFY_ARGUMENT_RETURN(prefix, arg, value) \
+ do \
+ { \
+ if (!(arg)) \
+ { \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ #prefix "gcmVERIFY_ARGUMENT_RETURN failed:"); \
+ prefix##ASSERT(arg); \
+ prefix##FOOTER_ARG("value=%d", value); \
+ return value; \
+ } \
+ } \
+ while (gcvFALSE)
+# define gcmkVERIFY_ARGUMENT_RETURN(arg, value) \
+ _gcmVERIFY_ARGUMENT_RETURN(gcmk, arg, value)
+
+#endif /* __gc_hal_base_internal_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_internal.h b/kernel_drivers/v4_cleaned/gc_hal_internal.h
new file mode 100644
index 0000000..36aa8e3
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_internal.h
@@ -0,0 +1,1780 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_internal_h_
+#define __gc_hal_internal_h_
+
+#include "gc_hal.h"
+#include "gc_hal_base_internal.h"
+
+/******************************************************************************\
+******************************* Alignment Macros *******************************
+\******************************************************************************/
+
+#define gcmALIGN(n, align) \
+( \
+ ((n) + ((align) - 1)) & ~((align) - 1) \
+)
+
+#define gcmALIGN_BASE(n, align) \
+( \
+ (n) & ~((align) - 1) \
+)
+
+/******************************************************************************\
+***************************** Element Count Macro *****************************
+\******************************************************************************/
+
+typedef struct _gckHARDWARE * gckHARDWARE;
+
+/* CORE flags. */
+typedef enum _gceCORE
+{
+ gcvCORE_MAJOR = 0x0,
+ gcvCORE_2D = 0x1,
+}
+gceCORE;
+
+#define gcdCORE_COUNT 2
+
+/*******************************************************************************
+**
+** gcmVERIFY_OBJECT
+**
+** Assert if an object is invalid or is not of the specified type. If the
+** object is invalid or not of the specified type, gcvSTATUS_INVALID_OBJECT
+** will be returned from the current function. In retail mode this macro
+** does nothing.
+**
+** ARGUMENTS:
+**
+** obj Object to test.
+** t Expected type of the object.
+*/
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+#define _gcmVERIFY_OBJECT(prefix, obj, t) \
+ if ((obj) == NULL) \
+ { \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ #prefix "VERIFY_OBJECT failed: NULL"); \
+ prefix##TRACE(gcvLEVEL_ERROR, " expected: %c%c%c%c", \
+ gcmCC_PRINT(t)); \
+ prefix##ASSERT((obj) != NULL); \
+ prefix##FOOTER_ARG("status=%d", gcvSTATUS_INVALID_OBJECT); \
+ return gcvSTATUS_INVALID_OBJECT; \
+ } \
+ else if (((gcsOBJECT*) (obj))->type != t) \
+ { \
+ prefix##TRACE(gcvLEVEL_ERROR, \
+ #prefix "VERIFY_OBJECT failed: %c%c%c%c", \
+ gcmCC_PRINT(((gcsOBJECT*) (obj))->type)); \
+ prefix##TRACE(gcvLEVEL_ERROR, " expected: %c%c%c%c", \
+ gcmCC_PRINT(t)); \
+ prefix##ASSERT(((gcsOBJECT*)(obj))->type == t); \
+ prefix##FOOTER_ARG("status=%d", gcvSTATUS_INVALID_OBJECT); \
+ return gcvSTATUS_INVALID_OBJECT; \
+ }
+
+# define gcmVERIFY_OBJECT(obj, t) _gcmVERIFY_OBJECT(gcm, obj, t)
+# define gcmkVERIFY_OBJECT(obj, t) _gcmVERIFY_OBJECT(gcmk, obj, t)
+#else
+# define gcmVERIFY_OBJECT(obj, t) do {} while (gcvFALSE)
+# define gcmkVERIFY_OBJECT(obj, t) do {} while (gcvFALSE)
+#endif
+
+/******************************************************************************\
+********************************** gckOS Object *********************************
+\******************************************************************************/
+
+/* Construct a new gckOS object. */
+gceSTATUS
+gckOS_Construct(
+ IN void *Context,
+ OUT gckOS * Os
+ );
+
+/* Destroy an gckOS object. */
+gceSTATUS
+gckOS_Destroy(
+ IN gckOS Os
+ );
+
+/* Allocate memory from the heap. */
+gceSTATUS
+gckOS_Allocate(
+ IN gckOS Os,
+ IN size_t Bytes,
+ OUT void **Memory
+ );
+
+/* Free allocated memory. */
+gceSTATUS
+gckOS_Free(
+ IN gckOS Os,
+ IN void *Memory
+ );
+
+/* Wrapper for allocation memory.. */
+gceSTATUS
+gckOS_AllocateMemory(
+ IN gckOS Os,
+ IN size_t Bytes,
+ OUT void **Memory
+ );
+
+/* Wrapper for freeing memory. */
+gceSTATUS
+gckOS_FreeMemory(
+ IN gckOS Os,
+ IN void *Memory
+ );
+
+/* Allocate paged memory. */
+gceSTATUS
+gckOS_AllocatePagedMemoryEx(
+ IN gckOS Os,
+ IN int Contiguous,
+ IN size_t Bytes,
+ OUT gctPHYS_ADDR * Physical
+ );
+
+/* Lock pages. */
+gceSTATUS
+gckOS_LockPages(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN int Cacheable,
+ OUT void **Logical,
+ OUT size_t * PageCount
+ );
+
+/* Map pages. */
+gceSTATUS
+gckOS_MapPagesEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctPHYS_ADDR Physical,
+ IN size_t PageCount,
+ IN void *PageTable
+ );
+
+/* Unlock pages. */
+gceSTATUS
+gckOS_UnlockPages(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN void *Logical
+ );
+
+/* Free paged memory. */
+gceSTATUS
+gckOS_FreePagedMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes
+ );
+
+/* Allocate non-paged memory. */
+gceSTATUS
+gckOS_AllocateNonPagedMemory(
+ IN gckOS Os,
+ IN int InUserSpace,
+ IN OUT size_t * Bytes,
+ OUT gctPHYS_ADDR * Physical,
+ OUT void **Logical
+ );
+
+/* Free non-paged memory. */
+gceSTATUS
+gckOS_FreeNonPagedMemory(
+ IN gckOS Os,
+ IN size_t Bytes,
+ IN gctPHYS_ADDR Physical,
+ IN void *Logical
+ );
+
+/* Allocate contiguous memory. */
+gceSTATUS
+gckOS_AllocateContiguous(
+ IN gckOS Os,
+ IN int InUserSpace,
+ IN OUT size_t * Bytes,
+ OUT gctPHYS_ADDR * Physical,
+ OUT void **Logical
+ );
+
+/* Free contiguous memory. */
+gceSTATUS
+gckOS_FreeContiguous(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN void *Logical,
+ IN size_t Bytes
+ );
+
+/* Get the physical address of a corresponding logical address. */
+gceSTATUS
+gckOS_GetPhysicalAddress(
+ IN gckOS Os,
+ IN void *Logical,
+ OUT u32 * Address
+ );
+
+/* Map physical memory. */
+gceSTATUS
+gckOS_MapPhysical(
+ IN gckOS Os,
+ IN u32 Physical,
+ IN size_t Bytes,
+ OUT void **Logical
+ );
+
+/* Unmap previously mapped physical memory. */
+gceSTATUS
+gckOS_UnmapPhysical(
+ IN gckOS Os,
+ IN void *Logical,
+ IN size_t Bytes
+ );
+
+/* Read data from a hardware register. */
+gceSTATUS
+gckOS_ReadRegisterEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN u32 Address,
+ OUT u32 * Data
+ );
+
+/* Write data to a hardware register. */
+gceSTATUS
+gckOS_WriteRegisterEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN u32 Address,
+ IN u32 Data
+ );
+
+/* Write data to a 32-bit memory location. */
+gceSTATUS
+gckOS_WriteMemory(
+ IN gckOS Os,
+ IN void *Address,
+ IN u32 Data
+ );
+
+/* Map physical memory into the process space. */
+gceSTATUS
+gckOS_MapMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ OUT void **Logical
+ );
+
+/* Unmap physical memory from the specified process space. */
+gceSTATUS
+gckOS_UnmapMemoryEx(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN void *Logical,
+ IN u32 PID
+ );
+
+/* Unmap physical memory from the process space. */
+gceSTATUS
+gckOS_UnmapMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN void *Logical
+ );
+
+/* Create a new mutex. */
+gceSTATUS
+gckOS_CreateMutex(
+ IN gckOS Os,
+ OUT void **Mutex
+ );
+
+/* Delete a mutex. */
+gceSTATUS
+gckOS_DeleteMutex(
+ IN gckOS Os,
+ IN void *Mutex
+ );
+
+/* Acquire a mutex. */
+gceSTATUS
+gckOS_AcquireMutex(
+ IN gckOS Os,
+ IN void *Mutex,
+ IN u32 Timeout
+ );
+
+/* Release a mutex. */
+gceSTATUS
+gckOS_ReleaseMutex(
+ IN gckOS Os,
+ IN void *Mutex
+ );
+
+/* Atomically exchange a pair of 32-bit values. */
+gceSTATUS
+gckOS_AtomicExchange(
+ IN gckOS Os,
+ IN OUT u32 *Target,
+ IN u32 NewValue,
+ OUT u32 *OldValue
+ );
+
+#ifdef CONFIG_SMP
+gceSTATUS
+gckOS_AtomSetMask(
+ IN void *Atom,
+ IN u32 Mask
+ );
+
+gceSTATUS
+gckOS_AtomClearMask(
+ IN void *Atom,
+ IN u32 Mask
+ );
+#endif
+
+gceSTATUS
+gckOS_DumpGPUState(
+ IN gckOS Os,
+ IN gceCORE Core
+ );
+
+/*******************************************************************************
+**
+** gckOS_AtomConstruct
+**
+** Create an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** OUTPUT:
+**
+** void ** Atom
+** Pointer to a variable receiving the constructed atom.
+*/
+gceSTATUS
+gckOS_AtomConstruct(
+ IN gckOS Os,
+ OUT void **Atom
+ );
+
+/*******************************************************************************
+**
+** gckOS_AtomDestroy
+**
+** Destroy an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** void *Atom
+** Pointer to the atom to destroy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomDestroy(
+ IN gckOS Os,
+ IN void *Atom
+ );
+
+/*******************************************************************************
+**
+** gckOS_AtomGet
+**
+** Get the 32-bit value protected by an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** s32 *Value
+** Pointer to a variable the receives the value of the atom.
+*/
+gceSTATUS
+gckOS_AtomGet(
+ IN gckOS Os,
+ IN void *Atom,
+ OUT s32 *Value
+ );
+
+/*******************************************************************************
+**
+** gckOS_AtomSet
+**
+** Set the 32-bit value protected by an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** s32 Value
+** The value of the atom.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomSet(
+ IN gckOS Os,
+ IN void *Atom,
+ IN s32 Value
+ );
+
+/*******************************************************************************
+**
+** gckOS_AtomIncrement
+**
+** Atomically increment the 32-bit integer value inside an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** s32 *Value
+** Pointer to a variable the receives the original value of the atom.
+*/
+gceSTATUS
+gckOS_AtomIncrement(
+ IN gckOS Os,
+ IN void *Atom,
+ OUT s32 *Value
+ );
+
+/*******************************************************************************
+**
+** gckOS_AtomDecrement
+**
+** Atomically decrement the 32-bit integer value inside an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** s32 *Value
+** Pointer to a variable the receives the original value of the atom.
+*/
+gceSTATUS
+gckOS_AtomDecrement(
+ IN gckOS Os,
+ IN void *Atom,
+ OUT s32 *Value
+ );
+
+/* Delay a number of microseconds. */
+gceSTATUS
+gckOS_Delay(
+ IN gckOS Os,
+ IN u32 Delay
+ );
+
+/* Get time in milliseconds. */
+gceSTATUS
+gckOS_GetTicks(
+ OUT u32 *Time
+ );
+
+/* Compare time value. */
+gceSTATUS
+gckOS_TicksAfter(
+ IN u32 Time1,
+ IN u32 Time2,
+ OUT int *IsAfter
+ );
+
+/* Get time in microseconds. */
+gceSTATUS
+gckOS_GetTime(
+ OUT u64 *Time
+ );
+
+/* Memory barrier. */
+gceSTATUS
+gckOS_MemoryBarrier(
+ IN gckOS Os,
+ IN void *Address
+ );
+
+/* Map user pointer. */
+gceSTATUS
+gckOS_MapUserPointer(
+ IN gckOS Os,
+ IN void *Pointer,
+ IN size_t Size,
+ OUT void **KernelPointer
+ );
+
+/* Unmap user pointer. */
+gceSTATUS
+gckOS_UnmapUserPointer(
+ IN gckOS Os,
+ IN void *Pointer,
+ IN size_t Size,
+ IN void *KernelPointer
+ );
+
+gceSTATUS
+gckOS_SuspendInterruptEx(
+ IN gckOS Os,
+ IN gceCORE Core
+ );
+
+gceSTATUS
+gckOS_ResumeInterruptEx(
+ IN gckOS Os,
+ IN gceCORE Core
+ );
+
+/* Get the base address for the physical memory. */
+gceSTATUS
+gckOS_GetBaseAddress(
+ IN gckOS Os,
+ OUT u32 *BaseAddress
+ );
+
+/* Perform a memory copy. */
+gceSTATUS
+gckOS_MemCopy(
+ IN void *Destination,
+ IN const void *Source,
+ IN size_t Bytes
+ );
+
+/* Zero memory. */
+gceSTATUS
+gckOS_ZeroMemory(
+ IN void *Memory,
+ IN size_t Bytes
+ );
+
+/******************************************************************************\
+********************************** Signal Object *********************************
+\******************************************************************************/
+
+/* Create a signal. */
+gceSTATUS
+gckOS_CreateSignal(
+ IN gckOS Os,
+ IN int ManualReset,
+ OUT gctSIGNAL * Signal
+ );
+
+/* Destroy a signal. */
+gceSTATUS
+gckOS_DestroySignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal
+ );
+
+/* Signal a signal. */
+gceSTATUS
+gckOS_Signal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN int State
+ );
+
+/* Wait for a signal. */
+gceSTATUS
+gckOS_WaitSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN u32 Wait
+ );
+
+/* Map a user signal to the kernel space. */
+gceSTATUS
+gckOS_MapSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN gctHANDLE Process,
+ OUT gctSIGNAL * MappedSignal
+ );
+
+/* Map user memory. */
+gceSTATUS
+gckOS_MapUserMemoryEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN void *Memory,
+ IN size_t Size,
+ OUT void **Info,
+ OUT u32 *Address
+ );
+
+/* Unmap user memory. */
+gceSTATUS
+gckOS_UnmapUserMemoryEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN void *Memory,
+ IN size_t Size,
+ IN void *Info,
+ IN u32 Address
+ );
+
+#if !USE_NEW_LINUX_SIGNAL
+/* Create signal to be used in the user space. */
+gceSTATUS
+gckOS_CreateUserSignal(
+ IN gckOS Os,
+ IN int ManualReset,
+ OUT int * SignalID
+ );
+
+/* Destroy signal used in the user space. */
+gceSTATUS
+gckOS_DestroyUserSignal(
+ IN gckOS Os,
+ IN int SignalID
+ );
+
+/* Wait for signal used in the user space. */
+gceSTATUS
+gckOS_WaitUserSignal(
+ IN gckOS Os,
+ IN int SignalID,
+ IN u32 Wait
+ );
+
+/* Signal a signal used in the user space. */
+gceSTATUS
+gckOS_SignalUserSignal(
+ IN gckOS Os,
+ IN int SignalID,
+ IN int State
+ );
+#endif /* USE_NEW_LINUX_SIGNAL */
+
+/* Set a signal owned by a process. */
+gceSTATUS
+gckOS_UserSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal,
+ IN gctHANDLE Process
+ );
+
+/******************************************************************************\
+** Cache Support
+*/
+
+gceSTATUS
+gckOS_CacheClean(
+ gckOS Os,
+ u32 ProcessID,
+ gctPHYS_ADDR Handle,
+ void *Physical,
+ void *Logical,
+ size_t Bytes
+ );
+
+gceSTATUS
+gckOS_CacheFlush(
+ gckOS Os,
+ u32 ProcessID,
+ gctPHYS_ADDR Handle,
+ void *Physical,
+ void *Logical,
+ size_t Bytes
+ );
+
+gceSTATUS
+gckOS_CacheInvalidate(
+ gckOS Os,
+ u32 ProcessID,
+ gctPHYS_ADDR Handle,
+ void *Physical,
+ void *Logical,
+ size_t Bytes
+ );
+
+/******************************************************************************\
+** Debug Support
+*/
+
+void
+gckOS_SetDebugLevel(
+ IN u32 Level
+ );
+
+void
+gckOS_SetDebugZones(
+ IN u32 Zones,
+ IN int Enable
+ );
+
+/*******************************************************************************
+** Broadcast interface.
+*/
+
+typedef enum _gceBROADCAST
+{
+ /* GPU might be idle. */
+ gcvBROADCAST_GPU_IDLE,
+
+ /* A commit is going to happen. */
+ gcvBROADCAST_GPU_COMMIT,
+
+ /* GPU seems to be stuck. */
+ gcvBROADCAST_GPU_STUCK,
+
+ /* First process gets attached. */
+ gcvBROADCAST_FIRST_PROCESS,
+
+ /* Last process gets detached. */
+ gcvBROADCAST_LAST_PROCESS,
+
+ /* AXI bus error. */
+ gcvBROADCAST_AXI_BUS_ERROR,
+}
+gceBROADCAST;
+
+gceSTATUS
+gckOS_Broadcast(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN gceBROADCAST Reason
+ );
+
+gceSTATUS
+gckOS_BroadcastHurry(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN unsigned int Urgency
+ );
+
+gceSTATUS
+gckOS_BroadcastCalibrateSpeed(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN unsigned int Idle,
+ IN unsigned int Time
+ );
+
+/*******************************************************************************
+**
+** gckOS_SetGPUPower
+**
+** Set the power of the GPU on or off.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.ß
+**
+** int Clock
+** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock.
+**
+** int Power
+** gcvTRUE to turn on the power, or gcvFALSE to turn off the power.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_SetGPUPower(
+ IN gckOS Os,
+ IN int Clock,
+ IN int Power
+ );
+
+/*******************************************************************************
+** Semaphores.
+*/
+
+/* Create a new semaphore. */
+gceSTATUS
+gckOS_CreateSemaphore(
+ IN gckOS Os,
+ OUT void **Semaphore
+ );
+
+/* Delete a semahore. */
+gceSTATUS
+gckOS_DestroySemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ );
+
+/* Acquire a semahore. */
+gceSTATUS
+gckOS_AcquireSemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ );
+
+/* Try to acquire a semahore. */
+gceSTATUS
+gckOS_TryAcquireSemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ );
+
+/* Release a semahore. */
+gceSTATUS
+gckOS_ReleaseSemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ );
+
+/*******************************************************************************
+** Timer API.
+*/
+
+typedef void (*gctTIMERFUNCTION)(void *);
+
+/* Create a timer. */
+gceSTATUS
+gckOS_CreateTimer(
+ IN gckOS Os,
+ IN gctTIMERFUNCTION Function,
+ IN void *Data,
+ OUT void **Timer
+ );
+
+/* Destory a timer. */
+gceSTATUS
+gckOS_DestoryTimer(
+ IN gckOS Os,
+ IN void *Timer
+ );
+
+/* Start a timer. */
+gceSTATUS
+gckOS_StartTimer(
+ IN gckOS Os,
+ IN void *Timer,
+ IN u32 Delay
+ );
+
+/* Stop a timer. */
+gceSTATUS
+gckOS_StopTimer(
+ IN gckOS Os,
+ IN void *Timer
+ );
+
+/******************************************************************************\
+******************************** gckVIDMEM Object ******************************
+\******************************************************************************/
+
+typedef struct _gckVIDMEM * gckVIDMEM;
+typedef struct _gckKERNEL * gckKERNEL;
+typedef struct _gckDB * gckDB;
+
+/* Construct a new gckVIDMEM object. */
+gceSTATUS
+gckVIDMEM_Construct(
+ IN gckOS Os,
+ IN u32 BaseAddress,
+ IN size_t Bytes,
+ IN size_t Threshold,
+ IN size_t Banking,
+ OUT gckVIDMEM * Memory
+ );
+
+/* Destroy an gckVDIMEM object. */
+gceSTATUS
+gckVIDMEM_Destroy(
+ IN gckVIDMEM Memory
+ );
+
+/* Allocate linear memory. */
+gceSTATUS
+gckVIDMEM_AllocateLinear(
+ IN gckVIDMEM Memory,
+ IN size_t Bytes,
+ IN u32 Alignment,
+ IN gceSURF_TYPE Type,
+ OUT gcuVIDMEM_NODE_PTR * Node
+ );
+
+/* Free memory. */
+gceSTATUS
+gckVIDMEM_Free(
+ IN gcuVIDMEM_NODE_PTR Node
+ );
+
+/* Lock memory. */
+gceSTATUS
+gckVIDMEM_Lock(
+ IN gckKERNEL Kernel,
+ IN gcuVIDMEM_NODE_PTR Node,
+ IN int Cacheable,
+ OUT u32 * Address
+ );
+
+/* Unlock memory. */
+gceSTATUS
+gckVIDMEM_Unlock(
+ IN gckKERNEL Kernel,
+ IN gcuVIDMEM_NODE_PTR Node,
+ IN gceSURF_TYPE Type,
+ IN OUT int * Asynchroneous
+ );
+
+/* Construct a gcuVIDMEM_NODE union for virtual memory. */
+gceSTATUS
+gckVIDMEM_ConstructVirtual(
+ IN gckKERNEL Kernel,
+ IN int Contiguous,
+ IN size_t Bytes,
+ OUT gcuVIDMEM_NODE_PTR * Node
+ );
+
+/******************************************************************************\
+******************************** gckKERNEL Object ******************************
+\******************************************************************************/
+
+struct _gcsHAL_INTERFACE;
+
+/* Notifications. */
+typedef enum _gceNOTIFY
+{
+ gcvNOTIFY_INTERRUPT,
+ gcvNOTIFY_COMMAND_QUEUE,
+}
+gceNOTIFY;
+
+/* Flush flags. */
+typedef enum _gceKERNEL_FLUSH
+{
+ gcvFLUSH_COLOR = 0x01,
+ gcvFLUSH_DEPTH = 0x02,
+ gcvFLUSH_TEXTURE = 0x04,
+ gcvFLUSH_2D = 0x08,
+ gcvFLUSH_ALL = gcvFLUSH_COLOR
+ | gcvFLUSH_DEPTH
+ | gcvFLUSH_TEXTURE
+ | gcvFLUSH_2D,
+}
+gceKERNEL_FLUSH;
+
+/* Construct a new gckKERNEL object. */
+gceSTATUS
+gckKERNEL_Construct(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN void *Context,
+ IN gckDB SharedDB,
+ OUT gckKERNEL * Kernel
+ );
+
+/* Destroy an gckKERNEL object. */
+gceSTATUS
+gckKERNEL_Destroy(
+ IN gckKERNEL Kernel
+ );
+
+/* Dispatch a user-level command. */
+gceSTATUS
+gckKERNEL_Dispatch(
+ IN gckKERNEL Kernel,
+ IN int FromUser,
+ IN OUT struct _gcsHAL_INTERFACE * Interface
+ );
+
+/* Query the video memory. */
+gceSTATUS
+gckKERNEL_QueryVideoMemory(
+ IN gckKERNEL Kernel,
+ OUT struct _gcsHAL_INTERFACE * Interface
+ );
+
+/* Lookup the gckVIDMEM object for a pool. */
+gceSTATUS
+gckKERNEL_GetVideoMemoryPool(
+ IN gckKERNEL Kernel,
+ IN gcePOOL Pool,
+ OUT gckVIDMEM * VideoMemory
+ );
+
+#if gcdUSE_VIDMEM_PER_PID
+gceSTATUS
+gckKERNEL_GetVideoMemoryPoolPid(
+ IN gckKERNEL Kernel,
+ IN gcePOOL Pool,
+ IN u32 Pid,
+ OUT gckVIDMEM * VideoMemory
+ );
+
+gceSTATUS
+gckKERNEL_CreateVideoMemoryPoolPid(
+ IN gckKERNEL Kernel,
+ IN gcePOOL Pool,
+ IN u32 Pid,
+ OUT gckVIDMEM * VideoMemory
+ );
+#endif
+
+/* Map video memory. */
+gceSTATUS
+gckKERNEL_MapVideoMemoryEx(
+ IN gckKERNEL Kernel,
+ IN gceCORE Core,
+ IN int InUserSpace,
+ IN u32 Address,
+ OUT void **Logical
+ );
+
+/* Map memory. */
+gceSTATUS
+gckKERNEL_MapMemory(
+ IN gckKERNEL Kernel,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ OUT void **Logical
+ );
+
+/* Unmap memory. */
+gceSTATUS
+gckKERNEL_UnmapMemory(
+ IN gckKERNEL Kernel,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN void *Logical
+ );
+
+/* Notification of events. */
+gceSTATUS
+gckKERNEL_Notify(
+ IN gckKERNEL Kernel,
+ IN gceNOTIFY Notifcation,
+ IN int Data
+ );
+
+gceSTATUS
+gckKERNEL_QuerySettings(
+ IN gckKERNEL Kernel,
+ OUT gcsKERNEL_SETTINGS * Settings
+ );
+
+/*******************************************************************************
+**
+** gckKERNEL_Recovery
+**
+** Try to recover the GPU from a fatal error.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_Recovery(
+ IN gckKERNEL Kernel
+ );
+
+/* Get access to the user data. */
+gceSTATUS
+gckKERNEL_OpenUserData(
+ IN gckKERNEL Kernel,
+ IN int NeedCopy,
+ IN void *StaticStorage,
+ IN void *UserPointer,
+ IN size_t Size,
+ OUT void **KernelPointer
+ );
+
+/* Release resources associated with the user data connection. */
+gceSTATUS
+gckKERNEL_CloseUserData(
+ IN gckKERNEL Kernel,
+ IN int NeedCopy,
+ IN int FlushData,
+ IN void *UserPointer,
+ IN size_t Size,
+ OUT void **KernelPointer
+ );
+
+/******************************************************************************\
+******************************* gckHARDWARE Object *****************************
+\******************************************************************************/
+
+/* Construct a new gckHARDWARE object. */
+gceSTATUS
+gckHARDWARE_Construct(
+ IN gckOS Os,
+ IN gceCORE Core,
+ OUT gckHARDWARE * Hardware
+ );
+
+/* Destroy an gckHARDWARE object. */
+gceSTATUS
+gckHARDWARE_Destroy(
+ IN gckHARDWARE Hardware
+ );
+
+/* Get hardware type. */
+gceSTATUS
+gckHARDWARE_GetType(
+ IN gckHARDWARE Hardware,
+ OUT gceHARDWARE_TYPE * Type
+ );
+
+/* Query system memory requirements. */
+gceSTATUS
+gckHARDWARE_QuerySystemMemory(
+ IN gckHARDWARE Hardware,
+ OUT size_t * SystemSize,
+ OUT u32 * SystemBaseAddress
+ );
+
+/* Build virtual address. */
+gceSTATUS
+gckHARDWARE_BuildVirtualAddress(
+ IN gckHARDWARE Hardware,
+ IN u32 Index,
+ IN u32 Offset,
+ OUT u32 * Address
+ );
+
+/* Query command buffer requirements. */
+gceSTATUS
+gckHARDWARE_QueryCommandBuffer(
+ IN gckHARDWARE Hardware,
+ OUT size_t * Alignment,
+ OUT size_t * ReservedHead,
+ OUT size_t * ReservedTail
+ );
+
+/* Add a WAIT/LINK pair in the command queue. */
+gceSTATUS
+gckHARDWARE_WaitLink(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN u32 Offset,
+ IN OUT size_t * Bytes,
+ OUT u32 * WaitOffset,
+ OUT size_t * WaitBytes
+ );
+
+/* Kickstart the command processor. */
+gceSTATUS
+gckHARDWARE_Execute(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN size_t Bytes
+ );
+
+/* Add an END command in the command queue. */
+gceSTATUS
+gckHARDWARE_End(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN OUT size_t * Bytes
+ );
+
+/* Add a NOP command in the command queue. */
+gceSTATUS
+gckHARDWARE_Nop(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN OUT size_t * Bytes
+ );
+
+/* Add a PIPESELECT command in the command queue. */
+gceSTATUS
+gckHARDWARE_PipeSelect(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN gcePIPE_SELECT Pipe,
+ IN OUT size_t * Bytes
+ );
+
+/* Add a LINK command in the command queue. */
+gceSTATUS
+gckHARDWARE_Link(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN void *FetchAddress,
+ IN size_t FetchSize,
+ IN OUT size_t * Bytes
+ );
+
+/* Add an EVENT command in the command queue. */
+gceSTATUS
+gckHARDWARE_Event(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN u8 Event,
+ IN gceKERNEL_WHERE FromWhere,
+ IN OUT size_t * Bytes
+ );
+
+/* Query the available memory. */
+gceSTATUS
+gckHARDWARE_QueryMemory(
+ IN gckHARDWARE Hardware,
+ OUT size_t * InternalSize,
+ OUT u32 * InternalBaseAddress,
+ OUT u32 * InternalAlignment,
+ OUT size_t * ExternalSize,
+ OUT u32 * ExternalBaseAddress,
+ OUT u32 * ExternalAlignment,
+ OUT u32 * HorizontalTileSize,
+ OUT u32 * VerticalTileSize
+ );
+
+/* Query the identity of the hardware. */
+gceSTATUS
+gckHARDWARE_QueryChipIdentity(
+ IN gckHARDWARE Hardware,
+ OUT struct _gcsHAL_QUERY_CHIP_IDENTITY *Identity
+ );
+
+/* Query the shader support. */
+gceSTATUS
+gckHARDWARE_QueryShaderCaps(
+ IN gckHARDWARE Hardware,
+ OUT unsigned int * VertexUniforms,
+ OUT unsigned int * FragmentUniforms,
+ OUT unsigned int * Varyings
+ );
+
+/* Split a harwdare specific address into API stuff. */
+gceSTATUS
+gckHARDWARE_SplitMemory(
+ IN gckHARDWARE Hardware,
+ IN u32 Address,
+ OUT gcePOOL * Pool,
+ OUT u32 * Offset
+ );
+
+/* Update command queue tail pointer. */
+gceSTATUS
+gckHARDWARE_UpdateQueueTail(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN u32 Offset
+ );
+
+/* Convert logical address to hardware specific address. */
+gceSTATUS
+gckHARDWARE_ConvertLogical(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ OUT u32 * Address
+ );
+
+/* Interrupt manager. */
+gceSTATUS
+gckHARDWARE_Interrupt(
+ IN gckHARDWARE Hardware,
+ IN int InterruptValid
+ );
+
+/* Program MMU. */
+gceSTATUS
+gckHARDWARE_SetMMU(
+ IN gckHARDWARE Hardware,
+ IN void *Logical
+ );
+
+/* Flush the MMU. */
+gceSTATUS
+gckHARDWARE_FlushMMU(
+ IN gckHARDWARE Hardware
+ );
+
+typedef enum _gceMMU_MODE
+{
+ gcvMMU_MODE_1K,
+ gcvMMU_MODE_4K,
+} gceMMU_MODE;
+
+/* Set the page table base address. */
+gceSTATUS
+gckHARDWARE_SetMMUv2(
+ IN gckHARDWARE Hardware,
+ IN int Enable,
+ IN void *MtlbAddress,
+ IN gceMMU_MODE Mode,
+ IN void *SafeAddress,
+ IN int FromPower
+ );
+
+/* Get idle register. */
+gceSTATUS
+gckHARDWARE_GetIdle(
+ IN gckHARDWARE Hardware,
+ IN int Wait,
+ OUT u32 * Data
+ );
+
+/* Flush the caches. */
+gceSTATUS
+gckHARDWARE_Flush(
+ IN gckHARDWARE Hardware,
+ IN gceKERNEL_FLUSH Flush,
+ IN void *Logical,
+ IN OUT size_t * Bytes
+ );
+
+/* Enable/disable fast clear. */
+gceSTATUS
+gckHARDWARE_SetFastClear(
+ IN gckHARDWARE Hardware,
+ IN int Enable,
+ IN int Compression
+ );
+
+/* Power management. */
+gceSTATUS
+gckHARDWARE_SetPowerManagementState(
+ IN gckHARDWARE Hardware,
+ IN gceCHIPPOWERSTATE State
+ );
+
+gceSTATUS
+gckHARDWARE_QueryPowerManagementState(
+ IN gckHARDWARE Hardware,
+ OUT gceCHIPPOWERSTATE* State
+ );
+
+/* Profile 2D Engine. */
+gceSTATUS
+gckHARDWARE_ProfileEngine2D(
+ IN gckHARDWARE Hardware,
+ OUT struct _gcs2D_PROFILE *Profile
+ );
+
+gceSTATUS
+gckHARDWARE_InitializeHardware(
+ IN gckHARDWARE Hardware
+ );
+
+gceSTATUS
+gckHARDWARE_Reset(
+ IN gckHARDWARE Hardware
+ );
+
+typedef gceSTATUS (*gctISRMANAGERFUNC)(void *Context);
+
+gceSTATUS
+gckHARDWARE_SetIsrManager(
+ IN gckHARDWARE Hardware,
+ IN gctISRMANAGERFUNC StartIsr,
+ IN gctISRMANAGERFUNC StopIsr,
+ IN void *Context
+ );
+
+/* Start a composition. */
+gceSTATUS
+gckHARDWARE_Compose(
+ IN gckHARDWARE Hardware,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Physical,
+ IN void *Logical,
+ IN size_t Offset,
+ IN size_t Size,
+ IN u8 EventID
+ );
+
+/* Chip features. */
+typedef enum _gceFEATURE
+{
+ gcvFEATURE_PIPE_2D = 0,
+ gcvFEATURE_PIPE_3D,
+ gcvFEATURE_PIPE_VG,
+ gcvFEATURE_DC,
+ gcvFEATURE_HIGH_DYNAMIC_RANGE,
+ gcvFEATURE_MODULE_CG,
+ gcvFEATURE_MIN_AREA,
+ gcvFEATURE_BUFFER_INTERLEAVING,
+ gcvFEATURE_BYTE_WRITE_2D,
+ gcvFEATURE_ENDIANNESS_CONFIG,
+ gcvFEATURE_DUAL_RETURN_BUS,
+ gcvFEATURE_DEBUG_MODE,
+ gcvFEATURE_YUY2_RENDER_TARGET,
+ gcvFEATURE_FRAGMENT_PROCESSOR,
+ gcvFEATURE_2DPE20,
+ gcvFEATURE_FAST_CLEAR,
+ gcvFEATURE_YUV420_TILER,
+ gcvFEATURE_YUY2_AVERAGING,
+ gcvFEATURE_FLIP_Y,
+ gcvFEATURE_EARLY_Z,
+ gcvFEATURE_Z_COMPRESSION,
+ gcvFEATURE_MSAA,
+ gcvFEATURE_SPECIAL_ANTI_ALIASING,
+ gcvFEATURE_SPECIAL_MSAA_LOD,
+ gcvFEATURE_422_TEXTURE_COMPRESSION,
+ gcvFEATURE_DXT_TEXTURE_COMPRESSION,
+ gcvFEATURE_ETC1_TEXTURE_COMPRESSION,
+ gcvFEATURE_CORRECT_TEXTURE_CONVERTER,
+ gcvFEATURE_TEXTURE_8K,
+ gcvFEATURE_SCALER,
+ gcvFEATURE_YUV420_SCALER,
+ gcvFEATURE_SHADER_HAS_W,
+ gcvFEATURE_SHADER_HAS_SIGN,
+ gcvFEATURE_SHADER_HAS_FLOOR,
+ gcvFEATURE_SHADER_HAS_CEIL,
+ gcvFEATURE_SHADER_HAS_SQRT,
+ gcvFEATURE_SHADER_HAS_TRIG,
+ gcvFEATURE_VAA,
+ gcvFEATURE_HZ,
+ gcvFEATURE_CORRECT_STENCIL,
+ gcvFEATURE_VG20,
+ gcvFEATURE_VG_FILTER,
+ gcvFEATURE_VG21,
+ gcvFEATURE_VG_DOUBLE_BUFFER,
+ gcvFEATURE_MC20,
+ gcvFEATURE_SUPER_TILED,
+ gcvFEATURE_2D_FILTERBLIT_PLUS_ALPHABLEND,
+ gcvFEATURE_2D_DITHER,
+ gcvFEATURE_2D_A8_TARGET,
+ gcvFEATURE_2D_FILTERBLIT_FULLROTATION,
+ gcvFEATURE_2D_BITBLIT_FULLROTATION,
+ gcvFEATURE_WIDE_LINE,
+ gcvFEATURE_FC_FLUSH_STALL,
+ gcvFEATURE_FULL_DIRECTFB,
+ gcvFEATURE_HALF_FLOAT_PIPE,
+ gcvFEATURE_LINE_LOOP,
+ gcvFEATURE_2D_YUV_BLIT,
+ gcvFEATURE_2D_TILING,
+ gcvFEATURE_NON_POWER_OF_TWO,
+ gcvFEATURE_3D_TEXTURE,
+ gcvFEATURE_TEXTURE_ARRAY,
+ gcvFEATURE_TILE_FILLER,
+ gcvFEATURE_LOGIC_OP,
+ gcvFEATURE_COMPOSITION,
+ gcvFEATURE_MIXED_STREAMS,
+ gcvFEATURE_2D_MULTI_SOURCE_BLT,
+ gcvFEATURE_END_EVENT,
+ gcvFEATURE_VERTEX_10_10_10_2,
+ gcvFEATURE_TEXTURE_10_10_10_2,
+ gcvFEATURE_TEXTURE_ANISOTROPIC_FILTERING,
+ gcvFEATURE_TEXTURE_FLOAT_HALF_FLOAT,
+ gcvFEATURE_2D_ROTATION_STALL_FIX,
+ gcvFEATURE_2D_MULTI_SOURCE_BLT_EX,
+ gcvFEATURE_BUG_FIXES10,
+ gcvFEATURE_2D_MINOR_TILING,
+ /* Supertiled compressed textures are supported. */
+ gcvFEATURE_TEX_COMPRRESSION_SUPERTILED,
+ gcvFEATURE_FAST_MSAA,
+ gcvFEATURE_BUG_FIXED_INDEXED_TRIANGLE_STRIP,
+ gcvFEATURE_TEXTURE_TILED_READ,
+ gcvFEATURE_DEPTH_BIAS_FIX,
+ gcvFEATURE_RECT_PRIMITIVE,
+ gcvFEATURE_BUG_FIXES11,
+ gcvFEATURE_SUPERTILED_TEXTURE,
+ gcvFEATURE_2D_NO_COLORBRUSH_INDEX8
+}
+gceFEATURE;
+
+/* Check for Hardware features. */
+gceSTATUS
+gckHARDWARE_IsFeatureAvailable(
+ IN gckHARDWARE Hardware,
+ IN gceFEATURE Feature
+ );
+
+/******************************************************************************\
+******************************** gckEVENT Object *******************************
+\******************************************************************************/
+
+typedef struct _gckEVENT * gckEVENT;
+
+/* Construct a new gckEVENT object. */
+gceSTATUS
+gckEVENT_Construct(
+ IN gckKERNEL Kernel,
+ OUT gckEVENT * Event
+ );
+
+/* Destroy an gckEVENT object. */
+gceSTATUS
+gckEVENT_Destroy(
+ IN gckEVENT Event
+ );
+
+/* Add a new event to the list of events. */
+gceSTATUS
+gckEVENT_AddList(
+ IN gckEVENT Event,
+ IN struct _gcsHAL_INTERFACE *Interface,
+ IN gceKERNEL_WHERE FromWhere,
+ IN int AllocateAllowed
+ );
+
+/* Schedule a signal event. */
+gceSTATUS
+gckEVENT_Signal(
+ IN gckEVENT Event,
+ IN gctSIGNAL Signal,
+ IN gceKERNEL_WHERE FromWhere
+ );
+
+gceSTATUS
+gckEVENT_CommitDone(
+ IN gckEVENT Event,
+ IN gceKERNEL_WHERE FromWhere
+ );
+
+gceSTATUS
+gckEVENT_Submit(
+ IN gckEVENT Event,
+ IN int Wait,
+ IN int FromPower
+ );
+
+/* Commit an event queue. */
+gceSTATUS
+gckEVENT_Commit(
+ IN gckEVENT Event,
+ IN struct _gcsQUEUE *Queue
+ );
+
+/* Schedule a composition event. */
+gceSTATUS
+gckEVENT_Compose(
+ IN gckEVENT Event,
+ IN struct _gcsHAL_COMPOSE *Info
+ );
+
+/* Event callback routine. */
+gceSTATUS
+gckEVENT_Notify(
+ IN gckEVENT Event,
+ IN u32 IDs
+ );
+
+/* Event callback routine. */
+gceSTATUS
+gckEVENT_Interrupt(
+ IN gckEVENT Event,
+ IN u32 IDs
+ );
+
+gceSTATUS
+gckEVENT_Dump(
+ IN gckEVENT Event
+ );
+/******************************************************************************\
+******************************* gckCOMMAND Object ******************************
+\******************************************************************************/
+
+typedef struct _gckCOMMAND * gckCOMMAND;
+
+/* Construct a new gckCOMMAND object. */
+gceSTATUS
+gckCOMMAND_Construct(
+ IN gckKERNEL Kernel,
+ OUT gckCOMMAND * Command
+ );
+
+/* Destroy an gckCOMMAND object. */
+gceSTATUS
+gckCOMMAND_Destroy(
+ IN gckCOMMAND Command
+ );
+
+/* Acquire command queue synchronization objects. */
+gceSTATUS
+gckCOMMAND_EnterCommit(
+ IN gckCOMMAND Command,
+ IN int FromPower
+ );
+
+/* Release command queue synchronization objects. */
+gceSTATUS
+gckCOMMAND_ExitCommit(
+ IN gckCOMMAND Command,
+ IN int FromPower
+ );
+
+/* Start the command queue. */
+gceSTATUS
+gckCOMMAND_Start(
+ IN gckCOMMAND Command
+ );
+
+/* Stop the command queue. */
+gceSTATUS
+gckCOMMAND_Stop(
+ IN gckCOMMAND Command,
+ IN int FromRecovery
+ );
+
+/* Commit a buffer to the command queue. */
+gceSTATUS
+gckCOMMAND_Commit(
+ IN gckCOMMAND Command,
+ IN gckCONTEXT Context,
+ IN gcoCMDBUF CommandBuffer,
+ IN struct _gcsSTATE_DELTA *StateDelta,
+ IN struct _gcsQUEUE *EventQueue,
+ IN u32 ProcessID
+ );
+
+/* Reserve space in the command buffer. */
+gceSTATUS
+gckCOMMAND_Reserve(
+ IN gckCOMMAND Command,
+ IN size_t RequestedBytes,
+ OUT void **Buffer,
+ OUT size_t * BufferSize
+ );
+
+/* Execute reserved space in the command buffer. */
+gceSTATUS
+gckCOMMAND_Execute(
+ IN gckCOMMAND Command,
+ IN size_t RequstedBytes
+ );
+
+/* Stall the command queue. */
+gceSTATUS
+gckCOMMAND_Stall(
+ IN gckCOMMAND Command,
+ IN int FromPower
+ );
+
+/* Attach user process. */
+gceSTATUS
+gckCOMMAND_Attach(
+ IN gckCOMMAND Command,
+ OUT gckCONTEXT * Context,
+ OUT size_t * StateCount,
+ IN u32 ProcessID
+ );
+
+/* Detach user process. */
+gceSTATUS
+gckCOMMAND_Detach(
+ IN gckCOMMAND Command,
+ IN gckCONTEXT Context
+ );
+
+/******************************************************************************\
+********************************* gckMMU Object ********************************
+\******************************************************************************/
+
+typedef struct _gckMMU * gckMMU;
+
+/* Construct a new gckMMU object. */
+gceSTATUS
+gckMMU_Construct(
+ IN gckKERNEL Kernel,
+ IN size_t MmuSize,
+ OUT gckMMU * Mmu
+ );
+
+/* Destroy an gckMMU object. */
+gceSTATUS
+gckMMU_Destroy(
+ IN gckMMU Mmu
+ );
+
+/* Enable the MMU. */
+gceSTATUS
+gckMMU_Enable(
+ IN gckMMU Mmu,
+ IN u32 PhysBaseAddr,
+ IN u32 PhysSize
+ );
+
+/* Allocate pages inside the MMU. */
+gceSTATUS
+gckMMU_AllocatePages(
+ IN gckMMU Mmu,
+ IN size_t PageCount,
+ OUT void **PageTable,
+ OUT u32 * Address
+ );
+
+/* Remove a page table from the MMU. */
+gceSTATUS
+gckMMU_FreePages(
+ IN gckMMU Mmu,
+ IN void *PageTable,
+ IN size_t PageCount
+ );
+
+/* Set the MMU page with info. */
+gceSTATUS
+gckMMU_SetPage(
+ IN gckMMU Mmu,
+ IN u32 PageAddress,
+ IN u32 *PageEntry
+ );
+
+gceSTATUS
+gckMMU_Flush(
+ IN gckMMU Mmu
+ );
+
+
+#if VIVANTE_PROFILER
+gceSTATUS
+gckHARDWARE_QueryProfileRegisters(
+ IN gckHARDWARE Hardware,
+ OUT gcsPROFILER_COUNTERS * Counters
+ );
+#endif
+
+#endif /* __gc_hal_internal_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel.c b/kernel_drivers/v4_cleaned/gc_hal_kernel.c
new file mode 100644
index 0000000..39699bc
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel.c
@@ -0,0 +1,2614 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define _GC_OBJ_ZONE gcvZONE_KERNEL
+
+/*******************************************************************************
+***** Version Signature *******************************************************/
+
+#define _gcmTXT2STR(t) #t
+#define gcmTXT2STR(t) _gcmTXT2STR(t)
+const char * _VERSION = "\n\0$VERSION$"
+ gcmTXT2STR(gcvVERSION_MAJOR) "."
+ gcmTXT2STR(gcvVERSION_MINOR) "."
+ gcmTXT2STR(gcvVERSION_PATCH) ":"
+ gcmTXT2STR(gcvVERSION_BUILD) "$\n";
+
+/******************************************************************************\
+******************************* gckKERNEL API Code ******************************
+\******************************************************************************/
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+#define gcmDEFINE2TEXT(d) #d
+const char *_DispatchText[] =
+{
+ gcmDEFINE2TEXT(gcvHAL_QUERY_VIDEO_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_QUERY_CHIP_IDENTITY),
+ gcmDEFINE2TEXT(gcvHAL_ALLOCATE_NON_PAGED_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_FREE_NON_PAGED_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_FREE_CONTIGUOUS_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_ALLOCATE_VIDEO_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_FREE_VIDEO_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_MAP_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_UNMAP_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_MAP_USER_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_UNMAP_USER_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_LOCK_VIDEO_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_UNLOCK_VIDEO_MEMORY),
+ gcmDEFINE2TEXT(gcvHAL_EVENT_COMMIT),
+ gcmDEFINE2TEXT(gcvHAL_USER_SIGNAL),
+ gcmDEFINE2TEXT(gcvHAL_SIGNAL),
+ gcmDEFINE2TEXT(gcvHAL_WRITE_DATA),
+ gcmDEFINE2TEXT(gcvHAL_COMMIT),
+ gcmDEFINE2TEXT(gcvHAL_STALL),
+ gcmDEFINE2TEXT(gcvHAL_READ_REGISTER),
+ gcmDEFINE2TEXT(gcvHAL_WRITE_REGISTER),
+ gcmDEFINE2TEXT(gcvHAL_GET_PROFILE_SETTING),
+ gcmDEFINE2TEXT(gcvHAL_SET_PROFILE_SETTING),
+ gcmDEFINE2TEXT(gcvHAL_READ_ALL_PROFILE_REGISTERS),
+ gcmDEFINE2TEXT(gcvHAL_PROFILE_REGISTERS_2D),
+ gcmDEFINE2TEXT(gcvHAL_SET_POWER_MANAGEMENT_STATE),
+ gcmDEFINE2TEXT(gcvHAL_QUERY_POWER_MANAGEMENT_STATE),
+ gcmDEFINE2TEXT(gcvHAL_GET_BASE_ADDRESS),
+ gcmDEFINE2TEXT(gcvHAL_SET_IDLE),
+ gcmDEFINE2TEXT(gcvHAL_QUERY_KERNEL_SETTINGS),
+ gcmDEFINE2TEXT(gcvHAL_RESET),
+ gcmDEFINE2TEXT(gcvHAL_MAP_PHYSICAL),
+ gcmDEFINE2TEXT(gcvHAL_DEBUG),
+ gcmDEFINE2TEXT(gcvHAL_CACHE),
+ gcmDEFINE2TEXT(gcvHAL_TIMESTAMP),
+ gcmDEFINE2TEXT(gcvHAL_DATABASE),
+ gcmDEFINE2TEXT(gcvHAL_VERSION),
+ gcmDEFINE2TEXT(gcvHAL_CHIP_INFO),
+ gcmDEFINE2TEXT(gcvHAL_ATTACH),
+ gcmDEFINE2TEXT(gcvHAL_DETACH)
+};
+#endif
+
+static void
+gckKERNEL_SetTimeOut(
+ IN gckKERNEL Kernel,
+ IN u32 timeOut
+ )
+{
+ gcmkHEADER_ARG("Kernel=0x%x timeOut=%d", Kernel, timeOut);
+#if gcdGPU_TIMEOUT
+ Kernel->timeOut = timeOut;
+#endif
+ gcmkFOOTER_NO();
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_Construct
+**
+** Construct a new gckKERNEL object.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gceCORE Core
+** Specified core.
+**
+** IN void *Context
+** Pointer to a driver defined context.
+**
+** IN gckDB SharedDB,
+** Pointer to a shared DB.
+**
+** OUTPUT:
+**
+** gckKERNEL * Kernel
+** Pointer to a variable that will hold the pointer to the gckKERNEL
+** object.
+*/
+
+gceSTATUS
+gckKERNEL_Construct(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN void *Context,
+ IN gckDB SharedDB,
+ OUT gckKERNEL * Kernel
+ )
+{
+ gckKERNEL kernel = NULL;
+ gceSTATUS status;
+ size_t i;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Os=0x%x Context=0x%x", Os, Context);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Kernel != NULL);
+
+ /* Allocate the gckKERNEL object. */
+ gcmkONERROR(gckOS_Allocate(Os,
+ sizeof(struct _gckKERNEL),
+ &pointer));
+
+ kernel = pointer;
+
+ /* Zero the object pointers. */
+ kernel->hardware = NULL;
+ kernel->command = NULL;
+ kernel->eventObj = NULL;
+ kernel->mmu = NULL;
+
+ if (SharedDB == NULL)
+ {
+ gcmkONERROR(gckOS_Allocate(Os,
+ sizeof(struct _gckDB),
+ &pointer));
+
+ kernel->db = pointer;
+ kernel->dbCreated = gcvTRUE;
+ kernel->db->freeDatabase = NULL;
+ kernel->db->freeRecord = NULL;
+ kernel->db->dbMutex = NULL;
+ kernel->db->lastDatabase = NULL;
+ kernel->db->idleTime = 0;
+ kernel->db->lastIdle = 0;
+ kernel->db->lastSlowdown = 0;
+
+ for (i = 0; i < ARRAY_SIZE(kernel->db->db); ++i)
+ {
+ kernel->db->db[i] = NULL;
+ }
+
+ /* Construct a database mutex. */
+ gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->dbMutex));
+ }
+ else
+ {
+ kernel->db = SharedDB;
+ kernel->dbCreated = gcvFALSE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(kernel->timers); ++i)
+ {
+ kernel->timers[i].startTime = 0;
+ kernel->timers[i].stopTime = 0;
+ }
+
+ kernel->timeOut = gcdGPU_TIMEOUT;
+
+ /* Initialize the gckKERNEL object. */
+ kernel->object.type = gcvOBJ_KERNEL;
+ kernel->os = Os;
+ kernel->core = Core;
+
+ /* Save context. */
+ kernel->context = Context;
+
+ /* Construct atom holding number of clients. */
+ kernel->atomClients = NULL;
+ gcmkONERROR(gckOS_AtomConstruct(Os, &kernel->atomClients));
+
+ /* Construct the gckHARDWARE object. */
+ gcmkONERROR(
+ gckHARDWARE_Construct(Os, kernel->core, &kernel->hardware));
+
+ /* Set pointer to gckKERNEL object in gckHARDWARE object. */
+ kernel->hardware->kernel = kernel;
+
+ /* Initialize the hardware. */
+ gcmkONERROR(
+ gckHARDWARE_InitializeHardware(kernel->hardware));
+
+ /* Construct the gckCOMMAND object. */
+ gcmkONERROR(
+ gckCOMMAND_Construct(kernel, &kernel->command));
+
+ /* Construct the gckEVENT object. */
+ gcmkONERROR(
+ gckEVENT_Construct(kernel, &kernel->eventObj));
+
+ /* Construct the gckMMU object. */
+ gcmkONERROR(
+ gckMMU_Construct(kernel, gcdMMU_SIZE, &kernel->mmu));
+
+ /* Return pointer to the gckKERNEL object. */
+ *Kernel = kernel;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Kernel=0x%x", *Kernel);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (kernel != NULL)
+ {
+ if (kernel->eventObj != NULL)
+ {
+ gcmkVERIFY_OK(gckEVENT_Destroy(kernel->eventObj));
+ }
+
+ if (kernel->command != NULL)
+ {
+ gcmkVERIFY_OK(gckCOMMAND_Destroy(kernel->command));
+ }
+
+ if (kernel->hardware != NULL)
+ {
+ gcmkVERIFY_OK(gckHARDWARE_Destroy(kernel->hardware));
+ }
+
+ if (kernel->atomClients != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Os, kernel->atomClients));
+ }
+
+ if (kernel->dbCreated && kernel->db != NULL)
+ {
+ if (kernel->db->dbMutex != NULL)
+ {
+ /* Destroy the database mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, kernel->db->dbMutex));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, kernel->db));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, kernel));
+ }
+
+ /* Return the error. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_Destroy
+**
+** Destroy an gckKERNEL object.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object to destroy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_Destroy(
+ IN gckKERNEL Kernel
+ )
+{
+ size_t i;
+ gcsDATABASE_PTR database, databaseNext;
+ gcsDATABASE_RECORD_PTR record, recordNext;
+
+ gcmkHEADER_ARG("Kernel=0x%x", Kernel);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ /* Destroy the database. */
+ if (Kernel->dbCreated)
+ {
+ for (i = 0; i < ARRAY_SIZE(Kernel->db->db); ++i)
+ {
+ if (Kernel->db->db[i] != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckKERNEL_DestroyProcessDB(Kernel, Kernel->db->db[i]->processID));
+ }
+ }
+
+ /* Free all databases. */
+ for (database = Kernel->db->freeDatabase;
+ database != NULL;
+ database = databaseNext)
+ {
+ databaseNext = database->next;
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, database));
+ }
+
+ if (Kernel->db->lastDatabase != NULL)
+ {
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel->db->lastDatabase));
+ }
+
+ /* Free all database records. */
+ for (record = Kernel->db->freeRecord; record != NULL; record = recordNext)
+ {
+ recordNext = record->next;
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, record));
+ }
+
+ /* Destroy the database mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->dbMutex));
+ }
+
+ /* Destroy the gckMMU object. */
+ gcmkVERIFY_OK(gckMMU_Destroy(Kernel->mmu));
+
+ /* Destroy the gckCOMMNAND object. */
+ gcmkVERIFY_OK(gckCOMMAND_Destroy(Kernel->command));
+
+ /* Destroy the gckEVENT object. */
+ gcmkVERIFY_OK(gckEVENT_Destroy(Kernel->eventObj));
+
+ /* Destroy the gckHARDWARE object. */
+ gcmkVERIFY_OK(gckHARDWARE_Destroy(Kernel->hardware));
+
+ /* Detsroy the client atom. */
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, Kernel->atomClients));
+
+ /* Mark the gckKERNEL object as unknown. */
+ Kernel->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckKERNEL object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+
+/*******************************************************************************
+**
+** _AllocateMemory
+**
+** Private function to walk all required memory pools to allocate the requested
+** amount of video memory.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gcsHAL_INTERFACE * Interface
+** Pointer to a gcsHAL_INTERFACE structure that defines the command to
+** be dispatched.
+**
+** OUTPUT:
+**
+** gcsHAL_INTERFACE * Interface
+** Pointer to a gcsHAL_INTERFACE structure that receives any data to be
+** returned.
+*/
+static gceSTATUS
+_AllocateMemory(
+ IN gckKERNEL Kernel,
+ IN OUT gcePOOL * Pool,
+ IN size_t Bytes,
+ IN size_t Alignment,
+ IN gceSURF_TYPE Type,
+ OUT gcuVIDMEM_NODE_PTR * Node
+ )
+{
+ gcePOOL pool;
+ gceSTATUS status;
+ gckVIDMEM videoMemory;
+ int loopCount;
+ gcuVIDMEM_NODE_PTR node = NULL;
+ int tileStatusInVirtual;
+
+ gcmkHEADER_ARG("Kernel=0x%x *Pool=%d Bytes=%lu Alignment=%lu Type=%d",
+ Kernel, *Pool, Bytes, Alignment, Type);
+
+ gcmkVERIFY_ARGUMENT(Pool != NULL);
+ gcmkVERIFY_ARGUMENT(Bytes != 0);
+
+ /* Get initial pool. */
+ switch (pool = *Pool)
+ {
+ case gcvPOOL_DEFAULT:
+ case gcvPOOL_LOCAL:
+ pool = gcvPOOL_LOCAL_INTERNAL;
+ loopCount = (int) gcvPOOL_NUMBER_OF_POOLS;
+ break;
+
+ case gcvPOOL_UNIFIED:
+ pool = gcvPOOL_SYSTEM;
+ loopCount = (int) gcvPOOL_NUMBER_OF_POOLS;
+ break;
+
+ case gcvPOOL_CONTIGUOUS:
+ loopCount = (int) gcvPOOL_NUMBER_OF_POOLS;
+ break;
+
+ default:
+ loopCount = 1;
+ break;
+ }
+
+ while (loopCount-- > 0)
+ {
+ if (pool == gcvPOOL_VIRTUAL)
+ {
+ /* Create a gcuVIDMEM_NODE for virtual memory. */
+ gcmkONERROR(
+ gckVIDMEM_ConstructVirtual(Kernel, gcvFALSE, Bytes, &node));
+
+ /* Success. */
+ break;
+ }
+
+ else
+ if (pool == gcvPOOL_CONTIGUOUS)
+ {
+ /* Create a gcuVIDMEM_NODE for contiguous memory. */
+ status = gckVIDMEM_ConstructVirtual(Kernel, gcvTRUE, Bytes, &node);
+ if (gcmIS_SUCCESS(status))
+ {
+ /* Memory allocated. */
+ break;
+ }
+ }
+
+ else
+ {
+ /* Get pointer to gckVIDMEM object for pool. */
+#if gcdUSE_VIDMEM_PER_PID
+ u32 pid;
+ pid = task_tgid_vnr(current);
+
+ status = gckKERNEL_GetVideoMemoryPoolPid(Kernel, pool, pid, &videoMemory);
+ if (status == gcvSTATUS_NOT_FOUND)
+ {
+ /* Create VidMem pool for this process. */
+ status = gckKERNEL_CreateVideoMemoryPoolPid(Kernel, pool, pid, &videoMemory);
+ }
+#else
+ status = gckKERNEL_GetVideoMemoryPool(Kernel, pool, &videoMemory);
+#endif
+
+ if (gcmIS_SUCCESS(status))
+ {
+ /* Allocate memory. */
+ status = gckVIDMEM_AllocateLinear(videoMemory,
+ Bytes,
+ Alignment,
+ Type,
+ &node);
+
+ if (gcmIS_SUCCESS(status))
+ {
+ /* Memory allocated. */
+ node->VidMem.pool = pool;
+ break;
+ }
+ }
+ }
+
+ if (pool == gcvPOOL_LOCAL_INTERNAL)
+ {
+ /* Advance to external memory. */
+ pool = gcvPOOL_LOCAL_EXTERNAL;
+ }
+
+ else
+ if (pool == gcvPOOL_LOCAL_EXTERNAL)
+ {
+ /* Advance to contiguous system memory. */
+ pool = gcvPOOL_SYSTEM;
+ }
+
+ else
+ if (pool == gcvPOOL_SYSTEM)
+ {
+ /* Advance to contiguous memory. */
+#ifdef CONFIG_MACH_JZ4770
+ pool = gcvPOOL_VIRTUAL;
+ // Wolfgang@ingenic.cn, modify, 2011-0
+ // do not use __get_free_page when system running,
+ // it may cause kernel hanging.
+#else
+ pool = gcvPOOL_CONTIGUOUS;
+#endif
+ }
+
+ else
+ if (pool == gcvPOOL_CONTIGUOUS)
+ {
+ tileStatusInVirtual =
+ gckHARDWARE_IsFeatureAvailable(Kernel->hardware,
+ gcvFEATURE_MC20);
+
+ if (Type == gcvSURF_TILE_STATUS && tileStatusInVirtual != gcvTRUE)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ /* Advance to virtual memory. */
+ pool = gcvPOOL_VIRTUAL;
+ }
+
+ else
+ {
+ /* Out of pools. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+ }
+
+ if (node == NULL)
+ {
+ /* Nothing allocated. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+
+ /* Return node and pool used for allocation. */
+ *Node = node;
+ *Pool = pool;
+
+ /* Return status. */
+ gcmkFOOTER_ARG("*Pool=%d *Node=0x%x", *Pool, *Node);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_Dispatch
+**
+** Dispatch a command received from the user HAL layer.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** int FromUser
+** whether the call is from the user space.
+**
+** gcsHAL_INTERFACE * Interface
+** Pointer to a gcsHAL_INTERFACE structure that defines the command to
+** be dispatched.
+**
+** OUTPUT:
+**
+** gcsHAL_INTERFACE * Interface
+** Pointer to a gcsHAL_INTERFACE structure that receives any data to be
+** returned.
+*/
+
+gceSTATUS
+gckKERNEL_Dispatch(
+ IN gckKERNEL Kernel,
+ IN int FromUser,
+ IN OUT gcsHAL_INTERFACE * Interface
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ size_t bytes;
+ gcuVIDMEM_NODE_PTR node;
+ int locked = gcvFALSE;
+ gctPHYS_ADDR physical = NULL;
+ u32 address;
+ u32 processID;
+#if gcdSECURE_USER
+ gcskSECURE_CACHE_PTR cache;
+ void *logical;
+#endif
+ int asynchronous;
+ void *paddr = NULL;
+#if !USE_NEW_LINUX_SIGNAL
+ gctSIGNAL signal;
+#endif
+
+ gcsDATABASE_RECORD record;
+ void * data;
+
+ gcmkHEADER_ARG("Kernel=0x%x FromUser=%d Interface=0x%x",
+ Kernel, FromUser, Interface);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Interface != NULL);
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL,
+ "Dispatching command %d (%s)",
+ Interface->command, _DispatchText[Interface->command]);
+#endif
+
+ /* Get the current process ID. */
+ processID = task_tgid_vnr(current);
+
+#if gcdSECURE_USER
+ gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache));
+#endif
+
+ /* Dispatch on command. */
+ switch (Interface->command)
+ {
+ case gcvHAL_GET_BASE_ADDRESS:
+ /* Get base address. */
+ gcmkONERROR(
+ gckOS_GetBaseAddress(Kernel->os,
+ &Interface->u.GetBaseAddress.baseAddress));
+ break;
+
+ case gcvHAL_QUERY_VIDEO_MEMORY:
+ /* Query video memory size. */
+ gcmkONERROR(gckKERNEL_QueryVideoMemory(Kernel, Interface));
+ break;
+
+ case gcvHAL_QUERY_CHIP_IDENTITY:
+ /* Query chip identity. */
+ gcmkONERROR(
+ gckHARDWARE_QueryChipIdentity(
+ Kernel->hardware,
+ &Interface->u.QueryChipIdentity));
+ break;
+
+ case gcvHAL_MAP_MEMORY:
+ physical = Interface->u.MapMemory.physical;
+
+ /* Map memory. */
+ gcmkONERROR(
+ gckKERNEL_MapMemory(Kernel,
+ physical,
+ Interface->u.MapMemory.bytes,
+ &Interface->u.MapMemory.logical));
+ gcmkVERIFY_OK(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_MAP_MEMORY,
+ Interface->u.MapMemory.logical,
+ physical,
+ Interface->u.MapMemory.bytes));
+ break;
+
+ case gcvHAL_UNMAP_MEMORY:
+ physical = Interface->u.UnmapMemory.physical;
+
+ /* Unmap memory. */
+ gcmkONERROR(
+ gckKERNEL_UnmapMemory(Kernel,
+ physical,
+ Interface->u.UnmapMemory.bytes,
+ Interface->u.UnmapMemory.logical));
+ gcmkVERIFY_OK(
+ gckKERNEL_RemoveProcessDB(Kernel,
+ processID, gcvDB_MAP_MEMORY,
+ Interface->u.UnmapMemory.logical));
+ break;
+
+ case gcvHAL_ALLOCATE_NON_PAGED_MEMORY:
+ /* Allocate non-paged memory. */
+ gcmkONERROR(
+ gckOS_AllocateNonPagedMemory(
+ Kernel->os,
+ FromUser,
+ &Interface->u.AllocateNonPagedMemory.bytes,
+ &Interface->u.AllocateNonPagedMemory.physical,
+ &Interface->u.AllocateNonPagedMemory.logical));
+
+ gcmkVERIFY_OK(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_NON_PAGED,
+ Interface->u.AllocateNonPagedMemory.logical,
+ Interface->u.AllocateNonPagedMemory.physical,
+ Interface->u.AllocateNonPagedMemory.bytes));
+ break;
+
+ case gcvHAL_FREE_NON_PAGED_MEMORY:
+ physical = Interface->u.FreeNonPagedMemory.physical;
+
+ /* Free non-paged memory. */
+ gcmkONERROR(
+ gckOS_FreeNonPagedMemory(Kernel->os,
+ Interface->u.FreeNonPagedMemory.bytes,
+ physical,
+ Interface->u.FreeNonPagedMemory.logical));
+
+ gcmkVERIFY_OK(
+ gckKERNEL_RemoveProcessDB(Kernel,
+ processID, gcvDB_NON_PAGED,
+ Interface->u.FreeNonPagedMemory.logical));
+
+#if gcdSECURE_USER
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Kernel,
+ cache,
+ Interface->u.FreeNonPagedMemory.logical,
+ Interface->u.FreeNonPagedMemory.bytes));
+#endif
+ break;
+
+ case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY:
+ /* Allocate contiguous memory. */
+ gcmkONERROR(gckOS_AllocateContiguous(
+ Kernel->os,
+ FromUser,
+ &Interface->u.AllocateContiguousMemory.bytes,
+ &Interface->u.AllocateContiguousMemory.physical,
+ &Interface->u.AllocateContiguousMemory.logical));
+
+ gcmkONERROR(gckHARDWARE_ConvertLogical(
+ Kernel->hardware,
+ Interface->u.AllocateContiguousMemory.logical,
+ &Interface->u.AllocateContiguousMemory.address));
+
+ gcmkVERIFY_OK(gckKERNEL_AddProcessDB(
+ Kernel,
+ processID, gcvDB_CONTIGUOUS,
+ Interface->u.AllocateContiguousMemory.logical,
+ Interface->u.AllocateContiguousMemory.physical,
+ Interface->u.AllocateContiguousMemory.bytes));
+ break;
+
+ case gcvHAL_FREE_CONTIGUOUS_MEMORY:
+ physical = Interface->u.FreeContiguousMemory.physical;
+
+ /* Free contiguous memory. */
+ gcmkONERROR(
+ gckOS_FreeContiguous(Kernel->os,
+ physical,
+ Interface->u.FreeContiguousMemory.logical,
+ Interface->u.FreeContiguousMemory.bytes));
+
+ gcmkVERIFY_OK(
+ gckKERNEL_RemoveProcessDB(Kernel,
+ processID, gcvDB_CONTIGUOUS,
+ Interface->u.FreeNonPagedMemory.logical));
+
+#if gcdSECURE_USER
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Kernel,
+ cache,
+ Interface->u.FreeContiguousMemory.logical,
+ Interface->u.FreeContiguousMemory.bytes));
+#endif
+ break;
+
+ case gcvHAL_ALLOCATE_VIDEO_MEMORY:
+
+ gcmkONERROR(gcvSTATUS_NOT_SUPPORTED);
+
+ break;
+
+ case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY:
+ /* Allocate memory. */
+ gcmkONERROR(
+ _AllocateMemory(Kernel,
+ &Interface->u.AllocateLinearVideoMemory.pool,
+ Interface->u.AllocateLinearVideoMemory.bytes,
+ Interface->u.AllocateLinearVideoMemory.alignment,
+ Interface->u.AllocateLinearVideoMemory.type,
+ &Interface->u.AllocateLinearVideoMemory.node));
+
+ /* Get actual size of node. */
+ node = Interface->u.AllocateLinearVideoMemory.node;
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ bytes = node->VidMem.bytes;
+ }
+ else
+ {
+ bytes = node->Virtual.bytes;
+ }
+
+ gcmkONERROR(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_VIDEO_MEMORY,
+ Interface->u.AllocateLinearVideoMemory.node,
+ NULL,
+ bytes));
+ break;
+
+ case gcvHAL_FREE_VIDEO_MEMORY:
+ /* Free video memory. */
+ gcmkONERROR(
+ gckVIDMEM_Free(Interface->u.FreeVideoMemory.node));
+
+ gcmkONERROR(
+ gckKERNEL_RemoveProcessDB(Kernel,
+ processID, gcvDB_VIDEO_MEMORY,
+ Interface->u.FreeVideoMemory.node));
+ break;
+
+ case gcvHAL_LOCK_VIDEO_MEMORY:
+ /* Lock video memory. */
+ gcmkONERROR(
+ gckVIDMEM_Lock(Kernel,
+ Interface->u.LockVideoMemory.node,
+ Interface->u.LockVideoMemory.cacheable,
+ &Interface->u.LockVideoMemory.address));
+
+ locked = gcvTRUE;
+
+ node = Interface->u.LockVideoMemory.node;
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ /* Map video memory address into user space. */
+ gcmkONERROR(
+ gckKERNEL_MapVideoMemoryEx(Kernel,
+ gcvCORE_MAJOR,
+ FromUser,
+ Interface->u.LockVideoMemory.address,
+ &Interface->u.LockVideoMemory.memory));
+ }
+ else
+ {
+ Interface->u.LockVideoMemory.memory = node->Virtual.logical;
+
+ /* Success. */
+ status = gcvSTATUS_OK;
+ }
+
+#if gcdSECURE_USER
+ /* Return logical address as physical address. */
+ Interface->u.LockVideoMemory.address =
+ gcmPTR2INT(Interface->u.LockVideoMemory.memory);
+#endif
+ gcmkONERROR(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_VIDEO_MEMORY_LOCKED,
+ Interface->u.LockVideoMemory.node,
+ NULL,
+ 0));
+
+ break;
+
+ case gcvHAL_UNLOCK_VIDEO_MEMORY:
+ /* Unlock video memory. */
+ node = Interface->u.UnlockVideoMemory.node;
+
+#if gcdSECURE_USER
+ /* Save node information before it disappears. */
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ logical = NULL;
+ bytes = 0;
+ }
+ else
+ {
+ logical = node->Virtual.logical;
+ bytes = node->Virtual.bytes;
+ }
+#endif
+
+ /* Unlock video memory. */
+ gcmkONERROR(
+ gckVIDMEM_Unlock(Kernel,
+ node,
+ Interface->u.UnlockVideoMemory.type,
+ &Interface->u.UnlockVideoMemory.asynchroneous));
+
+#if gcdSECURE_USER
+ /* Flush the translation cache for virtual surfaces. */
+ if (logical != NULL)
+ {
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(Kernel,
+ cache,
+ logical,
+ bytes));
+ }
+#endif
+ if (Interface->u.UnlockVideoMemory.asynchroneous == gcvFALSE)
+ {
+ /* There isn't a event to unlock this node, remove record now */
+ gcmkONERROR(
+ gckKERNEL_RemoveProcessDB(Kernel,
+ processID, gcvDB_VIDEO_MEMORY_LOCKED,
+ Interface->u.UnlockVideoMemory.node));
+ }
+
+ break;
+
+ case gcvHAL_EVENT_COMMIT:
+ /* Commit an event queue. */
+ gcmkONERROR(
+ gckEVENT_Commit(Kernel->eventObj,
+ Interface->u.Event.queue));
+ break;
+
+ case gcvHAL_COMMIT:
+ /* Commit a command and context buffer. */
+ gcmkONERROR(
+ gckCOMMAND_Commit(Kernel->command,
+ Interface->u.Commit.context,
+ Interface->u.Commit.commandBuffer,
+ Interface->u.Commit.delta,
+ Interface->u.Commit.queue,
+ processID));
+ break;
+
+ case gcvHAL_STALL:
+ /* Stall the command queue. */
+ gcmkONERROR(gckCOMMAND_Stall(Kernel->command, gcvFALSE));
+ break;
+
+ case gcvHAL_MAP_USER_MEMORY:
+ /* Map user memory to DMA. */
+ gcmkONERROR(
+ gckOS_MapUserMemoryEx(Kernel->os,
+ Kernel->core,
+ Interface->u.MapUserMemory.memory,
+ Interface->u.MapUserMemory.size,
+ &Interface->u.MapUserMemory.info,
+ &Interface->u.MapUserMemory.address));
+ gcmkVERIFY_OK(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_MAP_USER_MEMORY,
+ Interface->u.MapUserMemory.memory,
+ Interface->u.MapUserMemory.info,
+ Interface->u.MapUserMemory.size));
+ break;
+
+ case gcvHAL_UNMAP_USER_MEMORY:
+ address = Interface->u.MapUserMemory.address;
+
+ /* Unmap user memory. */
+ gcmkONERROR(
+ gckOS_UnmapUserMemoryEx(Kernel->os,
+ Kernel->core,
+ Interface->u.UnmapUserMemory.memory,
+ Interface->u.UnmapUserMemory.size,
+ Interface->u.UnmapUserMemory.info,
+ address));
+
+#if gcdSECURE_USER
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Kernel,
+ cache,
+ Interface->u.UnmapUserMemory.memory,
+ Interface->u.UnmapUserMemory.size));
+#endif
+ gcmkVERIFY_OK(
+ gckKERNEL_RemoveProcessDB(Kernel,
+ processID, gcvDB_MAP_USER_MEMORY,
+ Interface->u.UnmapUserMemory.memory));
+ break;
+
+#if !USE_NEW_LINUX_SIGNAL
+ case gcvHAL_USER_SIGNAL:
+ /* Dispatch depends on the user signal subcommands. */
+ switch(Interface->u.UserSignal.command)
+ {
+ case gcvUSER_SIGNAL_CREATE:
+ /* Create a signal used in the user space. */
+ gcmkONERROR(
+ gckOS_CreateUserSignal(Kernel->os,
+ Interface->u.UserSignal.manualReset,
+ &Interface->u.UserSignal.id));
+
+ gcmkVERIFY_OK(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_SIGNAL,
+ gcmINT2PTR(Interface->u.UserSignal.id),
+ NULL,
+ 0));
+ break;
+
+ case gcvUSER_SIGNAL_DESTROY:
+ /* Destroy the signal. */
+ gcmkONERROR(
+ gckOS_DestroyUserSignal(Kernel->os,
+ Interface->u.UserSignal.id));
+
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Kernel,
+ processID, gcvDB_SIGNAL,
+ gcmINT2PTR(Interface->u.UserSignal.id)));
+ break;
+
+ case gcvUSER_SIGNAL_SIGNAL:
+ /* Signal the signal. */
+ gcmkONERROR(
+ gckOS_SignalUserSignal(Kernel->os,
+ Interface->u.UserSignal.id,
+ Interface->u.UserSignal.state));
+ break;
+
+ case gcvUSER_SIGNAL_WAIT:
+ /* Wait on the signal. */
+ status = gckOS_WaitUserSignal(Kernel->os,
+ Interface->u.UserSignal.id,
+ Interface->u.UserSignal.wait);
+ break;
+
+ case gcvUSER_SIGNAL_MAP:
+ gcmkONERROR(
+ gckOS_MapSignal(Kernel->os,
+ (gctSIGNAL)Interface->u.UserSignal.id,
+ (gctHANDLE)processID,
+ &signal));
+
+ gcmkVERIFY_OK(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_SIGNAL,
+ gcmINT2PTR(Interface->u.UserSignal.id),
+ NULL,
+ 0));
+ break;
+
+ case gcvUSER_SIGNAL_UNMAP:
+ /* Destroy the signal. */
+ gcmkONERROR(
+ gckOS_DestroyUserSignal(Kernel->os,
+ Interface->u.UserSignal.id));
+
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Kernel,
+ processID, gcvDB_SIGNAL,
+ gcmINT2PTR(Interface->u.UserSignal.id)));
+ break;
+
+ default:
+ /* Invalid user signal command. */
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+ break;
+#endif
+
+ case gcvHAL_SET_POWER_MANAGEMENT_STATE:
+ /* Set the power management state. */
+ gcmkONERROR(
+ gckHARDWARE_SetPowerManagementState(
+ Kernel->hardware,
+ Interface->u.SetPowerManagement.state));
+ break;
+
+ case gcvHAL_QUERY_POWER_MANAGEMENT_STATE:
+ /* Chip is not idle. */
+ Interface->u.QueryPowerManagement.isIdle = gcvFALSE;
+
+ /* Query the power management state. */
+ gcmkONERROR(gckHARDWARE_QueryPowerManagementState(
+ Kernel->hardware,
+ &Interface->u.QueryPowerManagement.state));
+
+ /* Query the idle state. */
+ gcmkONERROR(
+ gckHARDWARE_QueryIdle(Kernel->hardware,
+ &Interface->u.QueryPowerManagement.isIdle));
+ break;
+
+ case gcvHAL_READ_REGISTER:
+#if gcdREGISTER_ACCESS_FROM_USER
+ {
+ gceCHIPPOWERSTATE power;
+ gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware,
+ &power));
+
+ if (power == gcvPOWER_ON)
+ {
+ /* Read a register. */
+ gcmkONERROR(gckOS_ReadRegisterEx(
+ Kernel->os,
+ Kernel->core,
+ Interface->u.ReadRegisterData.address,
+ &Interface->u.ReadRegisterData.data));
+ }
+ else
+ {
+ /* Chip is in power-state. */
+ Interface->u.ReadRegisterData.data = 0;
+ status = gcvSTATUS_CHIP_NOT_READY;
+ }
+ }
+#else
+ /* No access from user land to read registers. */
+ Interface->u.ReadRegisterData.data = 0;
+ status = gcvSTATUS_NOT_SUPPORTED;
+#endif
+ break;
+
+ case gcvHAL_WRITE_REGISTER:
+#if gcdREGISTER_ACCESS_FROM_USER
+ /* Write a register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Kernel->os,
+ Kernel->core,
+ Interface->u.WriteRegisterData.address,
+ Interface->u.WriteRegisterData.data));
+#else
+ /* No access from user land to write registers. */
+ status = gcvSTATUS_NOT_SUPPORTED;
+#endif
+ break;
+
+ case gcvHAL_READ_ALL_PROFILE_REGISTERS:
+#if VIVANTE_PROFILER
+ /* Read all 3D profile registers. */
+ gcmkONERROR(
+ gckHARDWARE_QueryProfileRegisters(
+ Kernel->hardware,
+ &Interface->u.RegisterProfileData.counters));
+#else
+ status = gcvSTATUS_OK;
+#endif
+ break;
+
+ case gcvHAL_PROFILE_REGISTERS_2D:
+#if VIVANTE_PROFILER
+ /* Read all 2D profile registers. */
+ gcmkONERROR(
+ gckHARDWARE_ProfileEngine2D(
+ Kernel->hardware,
+ Interface->u.RegisterProfileData2D.hwProfile2D));
+#else
+ status = gcvSTATUS_OK;
+#endif
+ break;
+
+ case gcvHAL_GET_PROFILE_SETTING:
+ status = gcvSTATUS_OK;
+ break;
+
+ case gcvHAL_SET_PROFILE_SETTING:
+ status = gcvSTATUS_OK;
+ break;
+
+ case gcvHAL_QUERY_KERNEL_SETTINGS:
+ /* Get kernel settings. */
+ gcmkONERROR(
+ gckKERNEL_QuerySettings(Kernel,
+ &Interface->u.QueryKernelSettings.settings));
+ break;
+
+ case gcvHAL_RESET:
+ /* Reset the hardware. */
+ gckKERNEL_Recovery(Kernel);
+ break;
+
+ case gcvHAL_DEBUG:
+ /* Set debug level and zones. */
+ if (Interface->u.Debug.set)
+ {
+ gckOS_SetDebugLevel(Interface->u.Debug.level);
+ gckOS_SetDebugZones(Interface->u.Debug.zones,
+ Interface->u.Debug.enable);
+ }
+
+ if (Interface->u.Debug.message[0] != '\0')
+ {
+ /* Print a message to the debugger. */
+ if (Interface->u.Debug.type == gcvMESSAGE_TEXT)
+ {
+ gckOS_CopyPrint(Interface->u.Debug.message);
+ }
+ else
+ {
+ gckOS_DumpBuffer(Kernel->os,
+ Interface->u.Debug.message,
+ Interface->u.Debug.messageSize,
+ gceDUMP_BUFFER_FROM_USER,
+ gcvTRUE);
+ }
+ }
+ status = gcvSTATUS_OK;
+ break;
+
+ case gcvHAL_DUMP_GPU_STATE:
+ /* Dump GPU state */
+ {
+ gceCHIPPOWERSTATE power;
+ gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware,
+ &power));
+ if (power == gcvPOWER_ON)
+ {
+ Interface->u.ReadRegisterData.data = 1;
+ gcmkVERIFY_OK(
+ gckOS_DumpGPUState(Kernel->os, Kernel->core));
+ }
+ else
+ {
+ Interface->u.ReadRegisterData.data = 0;
+ status = gcvSTATUS_CHIP_NOT_READY;
+ }
+ }
+ break;
+
+ case gcvHAL_DUMP_EVENT:
+ /* Dump GPU event */
+ gcmkVERIFY_OK(
+ gckEVENT_Dump(Kernel->eventObj));
+ break;
+
+ case gcvHAL_CACHE:
+ if (Interface->u.Cache.node == NULL)
+ {
+ /* FIXME Surface wrap some memory which is not allocated by us,
+ ** So we don't have physical address to handle outer cache, ignore it*/
+ status = gcvSTATUS_OK;
+ break;
+ }
+ else if (Interface->u.Cache.node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ /* Video memory has no physical handles. */
+ physical = NULL;
+ }
+ else
+ {
+ /* Grab physical handle. */
+ physical = Interface->u.Cache.node->Virtual.physical;
+ }
+
+ switch(Interface->u.Cache.operation)
+ {
+ case gcvCACHE_FLUSH:
+ /* Clean and invalidate the cache. */
+ status = gckOS_CacheFlush(Kernel->os,
+ processID,
+ physical,
+ paddr,
+ Interface->u.Cache.logical,
+ Interface->u.Cache.bytes);
+ break;
+ case gcvCACHE_CLEAN:
+ /* Clean the cache. */
+ status = gckOS_CacheClean(Kernel->os,
+ processID,
+ physical,
+ paddr,
+ Interface->u.Cache.logical,
+ Interface->u.Cache.bytes);
+ break;
+ case gcvCACHE_INVALIDATE:
+ /* Invalidate the cache. */
+ status = gckOS_CacheInvalidate(Kernel->os,
+ processID,
+ physical,
+ paddr,
+ Interface->u.Cache.logical,
+ Interface->u.Cache.bytes);
+ break;
+
+ case gcvCACHE_MEMORY_BARRIER:
+ status = gckOS_MemoryBarrier(Kernel->os,
+ Interface->u.Cache.logical);
+ break;
+ default:
+ status = gcvSTATUS_INVALID_ARGUMENT;
+ break;
+ }
+ break;
+
+ case gcvHAL_TIMESTAMP:
+ /* Check for invalid timer. */
+ if ((Interface->u.TimeStamp.timer >= ARRAY_SIZE(Kernel->timers))
+ || (Interface->u.TimeStamp.request != 2))
+ {
+ Interface->u.TimeStamp.timeDelta = 0;
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Return timer results and reset timer. */
+ {
+ gcsTIMER_PTR timer = &(Kernel->timers[Interface->u.TimeStamp.timer]);
+ u64 timeDelta = 0;
+
+ if (timer->stopTime < timer->startTime )
+ {
+ Interface->u.TimeStamp.timeDelta = 0;
+ gcmkONERROR(gcvSTATUS_TIMER_OVERFLOW);
+ }
+
+ timeDelta = timer->stopTime - timer->startTime;
+
+ /* Check truncation overflow. */
+ Interface->u.TimeStamp.timeDelta = (s32) timeDelta;
+ /*bit0~bit30 is available*/
+ if (timeDelta>>31)
+ {
+ Interface->u.TimeStamp.timeDelta = 0;
+ gcmkONERROR(gcvSTATUS_TIMER_OVERFLOW);
+ }
+
+ status = gcvSTATUS_OK;
+ }
+ break;
+
+ case gcvHAL_DATABASE:
+ /* Query video memory. */
+ gcmkONERROR(
+ gckKERNEL_QueryProcessDB(Kernel,
+ Interface->u.Database.processID,
+ !Interface->u.Database.validProcessID,
+ gcvDB_VIDEO_MEMORY,
+ &Interface->u.Database.vidMem));
+
+ /* Query non-paged memory. */
+ gcmkONERROR(
+ gckKERNEL_QueryProcessDB(Kernel,
+ Interface->u.Database.processID,
+ !Interface->u.Database.validProcessID,
+ gcvDB_NON_PAGED,
+ &Interface->u.Database.nonPaged));
+
+ /* Query contiguous memory. */
+ gcmkONERROR(
+ gckKERNEL_QueryProcessDB(Kernel,
+ Interface->u.Database.processID,
+ !Interface->u.Database.validProcessID,
+ gcvDB_CONTIGUOUS,
+ &Interface->u.Database.contiguous));
+
+ /* Query GPU idle time. */
+ gcmkONERROR(
+ gckKERNEL_QueryProcessDB(Kernel,
+ Interface->u.Database.processID,
+ !Interface->u.Database.validProcessID,
+ gcvDB_IDLE,
+ &Interface->u.Database.gpuIdle));
+ break;
+
+ case gcvHAL_VERSION:
+ Interface->u.Version.major = gcvVERSION_MAJOR;
+ Interface->u.Version.minor = gcvVERSION_MINOR;
+ Interface->u.Version.patch = gcvVERSION_PATCH;
+ Interface->u.Version.build = gcvVERSION_BUILD;
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL,
+ "KERNEL version %d.%d.%d build %u %s %s",
+ gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH,
+ gcvVERSION_BUILD, gcvVERSION_DATE, gcvVERSION_TIME);
+#endif
+ break;
+
+ case gcvHAL_CHIP_INFO:
+ /* Only if not support multi-core */
+ Interface->u.ChipInfo.count = 1;
+ Interface->u.ChipInfo.types[0] = Kernel->hardware->type;
+ break;
+
+ case gcvHAL_ATTACH:
+ /* Attach user process. */
+ gcmkONERROR(
+ gckCOMMAND_Attach(Kernel->command,
+ &Interface->u.Attach.context,
+ &Interface->u.Attach.stateCount,
+ processID));
+
+ gcmkVERIFY_OK(
+ gckKERNEL_AddProcessDB(Kernel,
+ processID, gcvDB_CONTEXT,
+ Interface->u.Attach.context,
+ NULL,
+ 0));
+ break;
+
+ case gcvHAL_DETACH:
+ /* Detach user process. */
+ gcmkONERROR(
+ gckCOMMAND_Detach(Kernel->command,
+ Interface->u.Detach.context));
+
+ gcmkVERIFY_OK(
+ gckKERNEL_RemoveProcessDB(Kernel,
+ processID, gcvDB_CONTEXT,
+ Interface->u.Detach.context));
+ break;
+
+ case gcvHAL_COMPOSE:
+ /* Start composition. */
+ gcmkONERROR(
+ gckEVENT_Compose(Kernel->eventObj,
+ &Interface->u.Compose));
+ break;
+
+ case gcvHAL_SET_TIMEOUT:
+ /* set timeOut value from user */
+ gckKERNEL_SetTimeOut(Kernel, Interface->u.SetTimeOut.timeOut);
+ break;
+
+#if gcdFRAME_DB
+ case gcvHAL_GET_FRAME_INFO:
+ gcmkONERROR(gckHARDWARE_GetFrameInfo(
+ Kernel->hardware,
+ Interface->u.GetFrameInfo.frameInfo));
+ break;
+#endif
+
+ case gcvHAL_GET_SHARED_INFO:
+ if (Interface->u.GetSharedInfo.dataId != 0)
+ {
+ gcmkONERROR(gckKERNEL_FindProcessDB(Kernel,
+ Interface->u.GetSharedInfo.pid,
+ 0,
+ gcvDB_SHARED_INFO,
+ gcmINT2PTR(Interface->u.GetSharedInfo.dataId),
+ &record));
+
+ /* find a record in db, check size */
+ if (record.bytes != Interface->u.GetSharedInfo.size)
+ {
+ /* Size change is not allowed */
+ gcmkONERROR(gcvSTATUS_INVALID_DATA);
+ }
+
+ /* fetch data */
+ if (copy_to_user(Interface->u.GetSharedInfo.data, record.physical, Interface->u.GetSharedInfo.size) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+
+ if ((node = Interface->u.GetSharedInfo.node) != NULL)
+ {
+ switch (Interface->u.GetSharedInfo.infoType)
+ {
+ case gcvVIDMEM_INFO_GENERIC:
+ { /* Generic data stored */
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ data = &node->VidMem.sharedInfo;
+
+ }
+ else
+ {
+ data = &node->Virtual.sharedInfo;
+ }
+
+ if (copy_to_user(Interface->u.GetSharedInfo.nodeData, data, sizeof(gcsVIDMEM_NODE_SHARED_INFO)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ break;
+
+ case gcvVIDMEM_INFO_DIRTY_RECTANGLE:
+ { /* Dirty rectangle stored */
+ gcsVIDMEM_NODE_SHARED_INFO *storedSharedInfo;
+ gcsVIDMEM_NODE_SHARED_INFO alignedSharedInfo;
+
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ storedSharedInfo = &node->VidMem.sharedInfo;
+ }
+ else
+ {
+ storedSharedInfo = &node->Virtual.sharedInfo;
+ }
+
+ /* Stored shared info holds the unaligned dirty rectangle.
+ Align it first. */
+
+ /* Hardware requires 64-byte aligned address, and 16x4 pixel aligned rectsize.
+ We simply align to 32 pixels which covers both 16- and 32-bpp formats. */
+
+ /* Make sure we have a legit rectangle. */
+ gcmkASSERT((storedSharedInfo->RectSize.width != 0) && (storedSharedInfo->RectSize.height != 0));
+
+ alignedSharedInfo.SrcOrigin.x = gcmALIGN_BASE(storedSharedInfo->SrcOrigin.x, 32);
+ alignedSharedInfo.RectSize.width = gcmALIGN((storedSharedInfo->RectSize.width + (storedSharedInfo->SrcOrigin.x - alignedSharedInfo.SrcOrigin.x)), 16);
+
+ alignedSharedInfo.SrcOrigin.y = gcmALIGN_BASE(storedSharedInfo->SrcOrigin.y, 4);
+ alignedSharedInfo.RectSize.height = gcmALIGN((storedSharedInfo->RectSize.height + (storedSharedInfo->SrcOrigin.y - alignedSharedInfo.SrcOrigin.y)), 4);
+
+ if (copy_to_user(Interface->u.GetSharedInfo.nodeData, &alignedSharedInfo, sizeof(gcsVIDMEM_NODE_SHARED_INFO)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL,
+ "Node = %p, unaligned rectangle (l=%d, t=%d, w=%d, h=%d) aligned to (l=%d, t=%d, w=%d, h=%d)", node,
+ storedSharedInfo->SrcOrigin.x, storedSharedInfo->SrcOrigin.y,
+ storedSharedInfo->RectSize.width, storedSharedInfo->RectSize.height,
+ alignedSharedInfo.SrcOrigin.x, alignedSharedInfo.SrcOrigin.y,
+ alignedSharedInfo.RectSize.width, alignedSharedInfo.RectSize.height);
+
+ /* Rectangle */
+ storedSharedInfo->SrcOrigin.x =
+ storedSharedInfo->SrcOrigin.y =
+ storedSharedInfo->RectSize.width =
+ storedSharedInfo->RectSize.height = 0;
+ }
+ break;
+ }
+ }
+ break;
+
+ case gcvHAL_SET_SHARED_INFO:
+ if (Interface->u.SetSharedInfo.dataId != 0)
+ {
+ status = gckKERNEL_FindProcessDB(Kernel, processID, 0,
+ gcvDB_SHARED_INFO,
+ gcmINT2PTR(Interface->u.SetSharedInfo.dataId),
+ &record);
+
+ if (status == gcvSTATUS_INVALID_DATA)
+ {
+ /* private data has not been created yet */
+ /* Note: we count on DestoryProcessDB to free it */
+ gcmkONERROR(gckOS_AllocateMemory(
+ Kernel->os,
+ Interface->u.SetSharedInfo.size,
+ &data
+ ));
+
+ gcmkONERROR(
+ gckKERNEL_AddProcessDB(Kernel, processID,
+ gcvDB_SHARED_INFO,
+ gcmINT2PTR(Interface->u.SetSharedInfo.dataId),
+ data,
+ Interface->u.SetSharedInfo.size
+ ));
+ }
+ else
+ {
+ /* bail on other errors */
+ gcmkONERROR(status);
+
+ /* find a record in db, check size */
+ if (record.bytes != Interface->u.SetSharedInfo.size)
+ {
+ /* Size change is not allowed */
+ gcmkONERROR(gcvSTATUS_INVALID_DATA);
+ }
+
+ /* get storage address */
+ data = record.physical;
+ }
+
+ if (copy_from_user(data, Interface->u.SetSharedInfo.data, Interface->u.SetSharedInfo.size) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+
+ if ((node = Interface->u.SetSharedInfo.node) != NULL)
+ {
+ switch (Interface->u.SetSharedInfo.infoType)
+ {
+ case gcvVIDMEM_INFO_GENERIC:
+ { /* Generic data stored */
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ data = &node->VidMem.sharedInfo;
+ }
+ else
+ {
+ data = &node->Virtual.sharedInfo;
+ }
+
+ if (copy_from_user(data, Interface->u.SetSharedInfo.nodeData, sizeof(gcsVIDMEM_NODE_SHARED_INFO)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ break;
+
+ case gcvVIDMEM_INFO_DIRTY_RECTANGLE:
+ { /* Dirty rectangle stored */
+ gcsVIDMEM_NODE_SHARED_INFO newSharedInfo;
+ gcsVIDMEM_NODE_SHARED_INFO *currentSharedInfo;
+ int dirtyX, dirtyY, right, bottom;
+
+ /* Expand the dirty rectangle stored in the node to include the rectangle passed in. */
+ if (copy_from_user(&newSharedInfo, Interface->u.SetSharedInfo.nodeData, sizeof(gcsVIDMEM_NODE_SHARED_INFO)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ currentSharedInfo = &node->VidMem.sharedInfo;
+ }
+ else
+ {
+ currentSharedInfo = &node->Virtual.sharedInfo;
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL, "Node = %p Stored rectangle (l=%d, t=%d, w=%d, h=%d)", node,
+ currentSharedInfo->SrcOrigin.x, currentSharedInfo->SrcOrigin.y,
+ currentSharedInfo->RectSize.width, currentSharedInfo->RectSize.height);
+
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL, "To combine with (l=%d, t=%d, w=%d, h=%d)",
+ newSharedInfo.SrcOrigin.x, newSharedInfo.SrcOrigin.y,
+ newSharedInfo.RectSize.width, newSharedInfo.RectSize.height);
+
+ if ((currentSharedInfo->RectSize.width == 0) || (currentSharedInfo->RectSize.height == 0))
+ { /* Setting it for the first time */
+ currentSharedInfo->SrcOrigin.x = newSharedInfo.SrcOrigin.x;
+ currentSharedInfo->SrcOrigin.y = newSharedInfo.SrcOrigin.y;
+ currentSharedInfo->RectSize.width = newSharedInfo.RectSize.width;
+ currentSharedInfo->RectSize.height = newSharedInfo.RectSize.height;
+ }
+ else
+ {
+ /* Expand the stored rectangle to include newly locked rectangle */
+ dirtyX = (newSharedInfo.SrcOrigin.x < currentSharedInfo->SrcOrigin.x) ? newSharedInfo.SrcOrigin.x : currentSharedInfo->SrcOrigin.x;
+ right = max(currentSharedInfo->SrcOrigin.x + currentSharedInfo->RectSize.width, newSharedInfo.SrcOrigin.x + newSharedInfo.RectSize.width);
+ currentSharedInfo->RectSize.width = right - dirtyX;
+ currentSharedInfo->SrcOrigin.x = dirtyX;
+
+ dirtyY = (newSharedInfo.SrcOrigin.y < currentSharedInfo->SrcOrigin.y) ? newSharedInfo.SrcOrigin.y : currentSharedInfo->SrcOrigin.y;
+ bottom = max(currentSharedInfo->SrcOrigin.y + currentSharedInfo->RectSize.height, newSharedInfo.SrcOrigin.y + newSharedInfo.RectSize.height);
+ currentSharedInfo->RectSize.height = bottom - dirtyY;
+ currentSharedInfo->SrcOrigin.y = dirtyY;
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_KERNEL, "Combined rectangle (l=%d, t=%d, w=%d, h=%d)",
+ currentSharedInfo->SrcOrigin.x, currentSharedInfo->SrcOrigin.y,
+ currentSharedInfo->RectSize.width, currentSharedInfo->RectSize.height);
+ }
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ /* Invalid command. */
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+OnError:
+ /* Save status. */
+ Interface->status = status;
+
+ if (gcmIS_ERROR(status))
+ {
+ if (locked)
+ {
+ /* Roll back the lock. */
+ gcmkVERIFY_OK(
+ gckVIDMEM_Unlock(Kernel,
+ Interface->u.LockVideoMemory.node,
+ gcvSURF_TYPE_UNKNOWN,
+ &asynchronous));
+
+ if (gcvTRUE == asynchronous)
+ {
+ /* Bottom Half */
+ gcmkVERIFY_OK(
+ gckVIDMEM_Unlock(Kernel,
+ Interface->u.LockVideoMemory.node,
+ gcvSURF_TYPE_UNKNOWN,
+ NULL));
+ }
+ }
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_AttachProcess
+**
+** Attach or detach a process.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** int Attach
+** gcvTRUE if a new process gets attached or gcFALSE when a process
+** gets detatched.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_AttachProcess(
+ IN gckKERNEL Kernel,
+ IN int Attach
+ )
+{
+ gceSTATUS status;
+ u32 processID;
+
+ gcmkHEADER_ARG("Kernel=0x%x Attach=%d", Kernel, Attach);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ /* Get current process ID. */
+ processID = task_tgid_vnr(current);
+
+ gcmkONERROR(gckKERNEL_AttachProcessEx(Kernel, Attach, processID));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_AttachProcessEx
+**
+** Attach or detach a process with the given PID. Can be paired with gckKERNEL_AttachProcess
+** provided the programmer is aware of the consequences.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** int Attach
+** gcvTRUE if a new process gets attached or gcFALSE when a process
+** gets detatched.
+**
+** u32 PID
+** PID of the process to attach or detach.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_AttachProcessEx(
+ IN gckKERNEL Kernel,
+ IN int Attach,
+ IN u32 PID
+ )
+{
+ gceSTATUS status;
+ s32 old;
+
+ gcmkHEADER_ARG("Kernel=0x%x Attach=%d PID=%d", Kernel, Attach, PID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ if (Attach)
+ {
+ /* Increment the number of clients attached. */
+ gcmkONERROR(
+ gckOS_AtomIncrement(Kernel->os, Kernel->atomClients, &old));
+
+ if (old == 0)
+ {
+ gcmkONERROR(gckOS_Broadcast(Kernel->os,
+ Kernel->hardware,
+ gcvBROADCAST_FIRST_PROCESS));
+ }
+
+ if (Kernel->dbCreated)
+ {
+ /* Create the process database. */
+ gcmkONERROR(gckKERNEL_CreateProcessDB(Kernel, PID));
+ }
+ }
+ else
+ {
+ if (Kernel->dbCreated)
+ {
+ /* Clean up the process database. */
+ gcmkONERROR(gckKERNEL_DestroyProcessDB(Kernel, PID));
+
+ /* Save the last know process ID. */
+ Kernel->db->lastProcessID = PID;
+ }
+
+ /* Decrement the number of clients attached. */
+ gcmkONERROR(
+ gckOS_AtomDecrement(Kernel->os, Kernel->atomClients, &old));
+
+ if (old == 1)
+ {
+ /* Last client detached, switch to SUSPEND power state. */
+ gcmkONERROR(gckOS_Broadcast(Kernel->os,
+ Kernel->hardware,
+ gcvBROADCAST_LAST_PROCESS));
+
+ /* Flush the debug cache. */
+ gcmkDEBUGFLUSH(~0U);
+ }
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#if gcdSECURE_USER
+gceSTATUS
+gckKERNEL_MapLogicalToPhysical(
+ IN gckKERNEL Kernel,
+ IN gcskSECURE_CACHE_PTR Cache,
+ IN OUT void **Data
+ )
+{
+ gceSTATUS status;
+ static int baseAddressValid = gcvFALSE;
+ static u32 baseAddress;
+ int needBase;
+ gcskLOGICAL_CACHE_PTR slot;
+
+ gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x *Data=0x%x",
+ Kernel, Cache, gcmOPT_POINTER(Data));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ if (!baseAddressValid)
+ {
+ /* Get base address. */
+ gcmkONERROR(gckHARDWARE_GetBaseAddress(Kernel->hardware, &baseAddress));
+
+ baseAddressValid = gcvTRUE;
+ }
+
+ /* Does this state load need a base address? */
+ gcmkONERROR(gckHARDWARE_NeedBaseAddress(Kernel->hardware,
+ ((u32 *) Data)[-1],
+ &needBase));
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU
+ {
+ gcskLOGICAL_CACHE_PTR next;
+ int i;
+
+ /* Walk all used cache slots. */
+ for (i = 1, slot = Cache->cache[0].next, next = NULL;
+ (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != NULL);
+ ++i, slot = slot->next
+ )
+ {
+ if (slot->logical == *Data)
+ {
+ /* Bail out. */
+ next = slot;
+ break;
+ }
+ }
+
+ /* See if we had a miss. */
+ if (next == NULL)
+ {
+ /* Use the tail of the cache. */
+ slot = Cache->cache[0].prev;
+
+ /* Initialize the cache line. */
+ slot->logical = *Data;
+
+ /* Map the logical address to a DMA address. */
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma));
+ }
+
+ /* Move slot to head of list. */
+ if (slot != Cache->cache[0].next)
+ {
+ /* Unlink. */
+ slot->prev->next = slot->next;
+ slot->next->prev = slot->prev;
+
+ /* Move to head of chain. */
+ slot->prev = &Cache->cache[0];
+ slot->next = Cache->cache[0].next;
+ slot->prev->next = slot;
+ slot->next->prev = slot;
+ }
+ }
+#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR
+ {
+ int i;
+ gcskLOGICAL_CACHE_PTR next = NULL;
+ gcskLOGICAL_CACHE_PTR oldestSlot = NULL;
+ slot = NULL;
+
+ if (Cache->cacheIndex != NULL)
+ {
+ /* Walk the cache forwards. */
+ for (i = 1, slot = Cache->cacheIndex;
+ (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != NULL);
+ ++i, slot = slot->next)
+ {
+ if (slot->logical == *Data)
+ {
+ /* Bail out. */
+ next = slot;
+ break;
+ }
+
+ /* Determine age of this slot. */
+ if ((oldestSlot == NULL)
+ || (oldestSlot->stamp > slot->stamp)
+ )
+ {
+ oldestSlot = slot;
+ }
+ }
+
+ if (next == NULL)
+ {
+ /* Walk the cache backwards. */
+ for (slot = Cache->cacheIndex->prev;
+ (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != NULL);
+ ++i, slot = slot->prev)
+ {
+ if (slot->logical == *Data)
+ {
+ /* Bail out. */
+ next = slot;
+ break;
+ }
+
+ /* Determine age of this slot. */
+ if ((oldestSlot == NULL)
+ || (oldestSlot->stamp > slot->stamp)
+ )
+ {
+ oldestSlot = slot;
+ }
+ }
+ }
+ }
+
+ /* See if we had a miss. */
+ if (next == NULL)
+ {
+ if (Cache->cacheFree != 0)
+ {
+ slot = &Cache->cache[Cache->cacheFree];
+ gcmkASSERT(slot->logical == NULL);
+
+ ++ Cache->cacheFree;
+ if (Cache->cacheFree >= ARRAY_SIZE(Cache->cache))
+ {
+ Cache->cacheFree = 0;
+ }
+ }
+ else
+ {
+ /* Use the oldest cache slot. */
+ gcmkASSERT(oldestSlot != NULL);
+ slot = oldestSlot;
+
+ /* Unlink from the chain. */
+ slot->prev->next = slot->next;
+ slot->next->prev = slot->prev;
+
+ /* Append to the end. */
+ slot->prev = Cache->cache[0].prev;
+ slot->next = &Cache->cache[0];
+ slot->prev->next = slot;
+ slot->next->prev = slot;
+ }
+
+ /* Initialize the cache line. */
+ slot->logical = *Data;
+
+ /* Map the logical address to a DMA address. */
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma));
+ }
+
+ /* Save time stamp. */
+ slot->stamp = ++ Cache->cacheStamp;
+
+ /* Save current slot for next lookup. */
+ Cache->cacheIndex = slot;
+ }
+#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ {
+ int i;
+ u32 data = gcmPTR2INT(*Data);
+ u32 key, index;
+ gcskLOGICAL_CACHE_PTR hash;
+
+ /* Generate a hash key. */
+ key = (data >> 24) + (data >> 16) + (data >> 8) + data;
+ index = key % ARRAY_SIZE(Cache->hash);
+
+ /* Get the hash entry. */
+ hash = &Cache->hash[index];
+
+ for (slot = hash->nextHash, i = 0;
+ (slot != NULL) && (i < gcdSECURE_CACHE_SLOTS);
+ slot = slot->nextHash, ++i
+ )
+ {
+ if (slot->logical == (*Data))
+ {
+ break;
+ }
+ }
+
+ if (slot == NULL)
+ {
+ /* Grab from the tail of the cache. */
+ slot = Cache->cache[0].prev;
+
+ /* Unlink slot from any hash table it is part of. */
+ if (slot->prevHash != NULL)
+ {
+ slot->prevHash->nextHash = slot->nextHash;
+ }
+ if (slot->nextHash != NULL)
+ {
+ slot->nextHash->prevHash = slot->prevHash;
+ }
+
+ /* Initialize the cache line. */
+ slot->logical = *Data;
+
+ /* Map the logical address to a DMA address. */
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma));
+
+ if (hash->nextHash != NULL)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL,
+ "Hash Collision: logical=0x%x key=0x%08x",
+ *Data, key);
+ }
+
+ /* Insert the slot at the head of the hash list. */
+ slot->nextHash = hash->nextHash;
+ if (slot->nextHash != NULL)
+ {
+ slot->nextHash->prevHash = slot;
+ }
+ slot->prevHash = hash;
+ hash->nextHash = slot;
+ }
+
+ /* Move slot to head of list. */
+ if (slot != Cache->cache[0].next)
+ {
+ /* Unlink. */
+ slot->prev->next = slot->next;
+ slot->next->prev = slot->prev;
+
+ /* Move to head of chain. */
+ slot->prev = &Cache->cache[0];
+ slot->next = Cache->cache[0].next;
+ slot->prev->next = slot;
+ slot->next->prev = slot;
+ }
+ }
+#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE
+ {
+ u32 index = (gcmPTR2INT(*Data) % gcdSECURE_CACHE_SLOTS) + 1;
+
+ /* Get cache slot. */
+ slot = &Cache->cache[index];
+
+ /* Check for cache miss. */
+ if (slot->logical != *Data)
+ {
+ /* Initialize the cache line. */
+ slot->logical = *Data;
+
+ /* Map the logical address to a DMA address. */
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma));
+ }
+ }
+#endif
+
+ /* Return DMA address. */
+ *Data = gcmINT2PTR(slot->dma + (needBase ? baseAddress : 0));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Data=0x%08x", *Data);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckKERNEL_FlushTranslationCache(
+ IN gckKERNEL Kernel,
+ IN gcskSECURE_CACHE_PTR Cache,
+ IN void *Logical,
+ IN size_t Bytes
+ )
+{
+ int i;
+ gcskLOGICAL_CACHE_PTR slot;
+ u8 *ptr;
+
+ gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x Logical=0x%x Bytes=%lu",
+ Kernel, Cache, Logical, Bytes);
+
+ /* Do we need to flush the entire cache? */
+ if (Logical == NULL)
+ {
+ /* Clear all cache slots. */
+ for (i = 1; i <= gcdSECURE_CACHE_SLOTS; ++i)
+ {
+ Cache->cache[i].logical = NULL;
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ Cache->cache[i].nextHash = NULL;
+ Cache->cache[i].prevHash = NULL;
+#endif
+}
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ /* Zero the hash table. */
+ for (i = 0; i < ARRAY_SIZE(Cache->hash); ++i)
+ {
+ Cache->hash[i].nextHash = NULL;
+ }
+#endif
+
+ /* Reset the cache functionality. */
+ Cache->cacheIndex = NULL;
+ Cache->cacheFree = 1;
+ Cache->cacheStamp = 0;
+ }
+
+ else
+ {
+ u8 *low = (u8 *) Logical;
+ u8 *high = low + Bytes;
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU
+ gcskLOGICAL_CACHE_PTR next;
+
+ /* Walk all used cache slots. */
+ for (i = 1, slot = Cache->cache[0].next;
+ (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != NULL);
+ ++i, slot = next
+ )
+ {
+ /* Save pointer to next slot. */
+ next = slot->next;
+
+ /* Test if this slot falls within the range to flush. */
+ ptr = (u8 *) slot->logical;
+ if ((ptr >= low) && (ptr < high))
+ {
+ /* Unlink slot. */
+ slot->prev->next = slot->next;
+ slot->next->prev = slot->prev;
+
+ /* Append slot to tail of cache. */
+ slot->prev = Cache->cache[0].prev;
+ slot->next = &Cache->cache[0];
+ slot->prev->next = slot;
+ slot->next->prev = slot;
+
+ /* Mark slot as empty. */
+ slot->logical = NULL;
+ }
+ }
+
+#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR
+ gcskLOGICAL_CACHE_PTR next;
+
+ for (i = 1, slot = Cache->cache[0].next;
+ (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != NULL);
+ ++i, slot = next)
+ {
+ /* Save pointer to next slot. */
+ next = slot->next;
+
+ /* Test if this slot falls within the range to flush. */
+ ptr = (u8 *) slot->logical;
+ if ((ptr >= low) && (ptr < high))
+ {
+ /* Test if this slot is the current slot. */
+ if (slot == Cache->cacheIndex)
+ {
+ /* Move to next or previous slot. */
+ Cache->cacheIndex = (slot->next->logical != NULL)
+ ? slot->next
+ : (slot->prev->logical != NULL)
+ ? slot->prev
+ : NULL;
+ }
+
+ /* Unlink slot from cache. */
+ slot->prev->next = slot->next;
+ slot->next->prev = slot->prev;
+
+ /* Insert slot to head of cache. */
+ slot->prev = &Cache->cache[0];
+ slot->next = Cache->cache[0].next;
+ slot->prev->next = slot;
+ slot->next->prev = slot;
+
+ /* Mark slot as empty. */
+ slot->logical = NULL;
+ slot->stamp = 0;
+ }
+ }
+
+#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ int j;
+ gcskLOGICAL_CACHE_PTR hash, next;
+
+ /* Walk all hash tables. */
+ for (i = 0, hash = Cache->hash;
+ i < ARRAY_SIZE(Cache->hash);
+ ++i, ++hash)
+ {
+ /* Walk all slots in the hash. */
+ for (j = 0, slot = hash->nextHash;
+ (j < gcdSECURE_CACHE_SLOTS) && (slot != NULL);
+ ++j, slot = next)
+ {
+ /* Save pointer to next slot. */
+ next = slot->next;
+
+ /* Test if this slot falls within the range to flush. */
+ ptr = (u8 *) slot->logical;
+ if ((ptr >= low) && (ptr < high))
+ {
+ /* Unlink slot from hash table. */
+ if (slot->prevHash == hash)
+ {
+ hash->nextHash = slot->nextHash;
+ }
+ else
+ {
+ slot->prevHash->nextHash = slot->nextHash;
+ }
+
+ if (slot->nextHash != NULL)
+ {
+ slot->nextHash->prevHash = slot->prevHash;
+ }
+
+ /* Unlink slot from cache. */
+ slot->prev->next = slot->next;
+ slot->next->prev = slot->prev;
+
+ /* Append slot to tail of cache. */
+ slot->prev = Cache->cache[0].prev;
+ slot->next = &Cache->cache[0];
+ slot->prev->next = slot;
+ slot->next->prev = slot;
+
+ /* Mark slot as empty. */
+ slot->logical = NULL;
+ slot->prevHash = NULL;
+ slot->nextHash = NULL;
+ }
+ }
+ }
+
+#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE
+ u32 index;
+
+ /* Loop while inside the range. */
+ for (i = 1; (low < high) && (i <= gcdSECURE_CACHE_SLOTS); ++i)
+ {
+ /* Get index into cache for this range. */
+ index = (gcmPTR2INT(low) % gcdSECURE_CACHE_SLOTS) + 1;
+ slot = &Cache->cache[index];
+
+ /* Test if this slot falls within the range to flush. */
+ ptr = (u8 *) slot->logical;
+ if ((ptr >= low) && (ptr < high))
+ {
+ /* Remove entry from cache. */
+ slot->logical = NULL;
+ }
+
+ /* Next block. */
+ low += gcdSECURE_CACHE_SLOTS;
+ }
+#endif
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+#endif
+
+/*******************************************************************************
+**
+** gckKERNEL_Recovery
+**
+** Try to recover the GPU from a fatal error.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_Recovery(
+ IN gckKERNEL Kernel
+ )
+{
+#if gcdENABLE_RECOVERY
+ gceSTATUS status;
+ gckEVENT eventObj;
+ gckHARDWARE hardware;
+#if gcdSECURE_USER
+ u32 processID;
+ gcskSECURE_CACHE_PTR cache;
+#endif
+
+ gcmkHEADER_ARG("Kernel=0x%x", Kernel);
+
+ /* Validate the arguemnts. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ /* Grab gckEVENT object. */
+ eventObj = Kernel->eventObj;
+ gcmkVERIFY_OBJECT(eventObj, gcvOBJ_EVENT);
+
+ /* Grab gckHARDWARE object. */
+ hardware = Kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ /* Handle all outstanding events now. */
+#ifdef CONFIG_SMP
+ gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending, ~0U));
+#else
+ eventObj->pending = ~0U;
+#endif
+ gcmkONERROR(gckEVENT_Notify(eventObj, 1));
+
+ /* Again in case more events got submitted. */
+#ifdef CONFIG_SMP
+ gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending, ~0U));
+#else
+ eventObj->pending = ~0U;
+#endif
+ gcmkONERROR(gckEVENT_Notify(eventObj, 2));
+
+#if gcdSECURE_USER
+ /* Flush the secure mapping cache. */
+ processID = task_tgid_vnr(current);
+ gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache));
+ gcmkONERROR(gckKERNEL_FlushTranslationCache(Kernel, cache, NULL, 0));
+#endif
+
+ /* Try issuing a soft reset for the GPU. */
+ status = gckHARDWARE_Reset(hardware);
+ if (status == gcvSTATUS_NOT_SUPPORTED)
+ {
+ /* Switch to OFF power. The next submit should return the GPU to ON
+ ** state. */
+ gcmkONERROR(
+ gckHARDWARE_SetPowerManagementState(hardware,
+ gcvPOWER_OFF_RECOVERY));
+ }
+ else
+ {
+ /* Bail out on reset error. */
+ gcmkONERROR(status);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+#else
+ return gcvSTATUS_OK;
+#endif
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_OpenUserData
+**
+** Get access to the user data.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** int NeedCopy
+** The flag indicating whether or not the data should be copied.
+**
+** void *StaticStorage
+** Pointer to the kernel storage where the data is to be copied if
+** NeedCopy is gcvTRUE.
+**
+** void *UserPointer
+** User pointer to the data.
+**
+** size_t Size
+** Size of the data.
+**
+** OUTPUT:
+**
+** void ** KernelPointer
+** Pointer to the kernel pointer that will be pointing to the data.
+*/
+gceSTATUS
+gckKERNEL_OpenUserData(
+ IN gckKERNEL Kernel,
+ IN int NeedCopy,
+ IN void *StaticStorage,
+ IN void *UserPointer,
+ IN size_t Size,
+ OUT void **KernelPointer
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+
+ gcmkHEADER_ARG(
+ "Kernel=0x%08X NeedCopy=%d StaticStorage=0x%08X "
+ "UserPointer=0x%08X Size=%lu KernelPointer=0x%08X",
+ Kernel, NeedCopy, StaticStorage, UserPointer, Size, KernelPointer
+ );
+
+ /* Validate the arguemnts. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(!NeedCopy || (StaticStorage != NULL));
+ gcmkVERIFY_ARGUMENT(UserPointer != NULL);
+ gcmkVERIFY_ARGUMENT(KernelPointer != NULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+
+ if (NeedCopy)
+ {
+ /* Copy the user data to the static storage. */
+ if (copy_from_user(StaticStorage, UserPointer, Size) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Set the kernel pointer. */
+ * KernelPointer = StaticStorage;
+ }
+ else
+ {
+ void *pointer = NULL;
+
+ /* Map the user pointer. */
+ gcmkONERROR(gckOS_MapUserPointer(
+ Kernel->os, UserPointer, Size, &pointer
+ ));
+
+ /* Set the kernel pointer. */
+ * KernelPointer = pointer;
+ }
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_CloseUserData
+**
+** Release resources associated with the user data connection opened by
+** gckKERNEL_OpenUserData.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** int NeedCopy
+** The flag indicating whether or not the data should be copied.
+**
+** int FlushData
+** If gcvTRUE, the data is written back to the user.
+**
+** void *UserPointer
+** User pointer to the data.
+**
+** size_t Size
+** Size of the data.
+**
+** OUTPUT:
+**
+** void ** KernelPointer
+** Kernel pointer to the data.
+*/
+gceSTATUS
+gckKERNEL_CloseUserData(
+ IN gckKERNEL Kernel,
+ IN int NeedCopy,
+ IN int FlushData,
+ IN void *UserPointer,
+ IN size_t Size,
+ OUT void **KernelPointer
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ void *pointer;
+
+ gcmkHEADER_ARG(
+ "Kernel=0x%08X NeedCopy=%d FlushData=%d "
+ "UserPointer=0x%08X Size=%lu KernelPointer=0x%08X",
+ Kernel, NeedCopy, FlushData, UserPointer, Size, KernelPointer
+ );
+
+ /* Validate the arguemnts. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(UserPointer != NULL);
+ gcmkVERIFY_ARGUMENT(KernelPointer != NULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+
+ /* Get a shortcut to the kernel pointer. */
+ pointer = * KernelPointer;
+
+ if (pointer != NULL)
+ {
+ if (NeedCopy)
+ {
+ if (FlushData)
+ {
+ if (copy_to_user(UserPointer, * KernelPointer, Size) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ }
+ else
+ {
+ /* Unmap record from kernel memory. */
+ gcmkONERROR(gckOS_UnmapUserPointer(
+ Kernel->os,
+ UserPointer,
+ Size,
+ * KernelPointer
+ ));
+ }
+
+ /* Reset the kernel pointer. */
+ * KernelPointer = NULL;
+ }
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+
+
+/*******************************************************************************
+***** Test Code ****************************************************************
+*******************************************************************************/
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel.h b/kernel_drivers/v4_cleaned/gc_hal_kernel.h
new file mode 100644
index 0000000..298a666
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel.h
@@ -0,0 +1,718 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_h_
+#define __gc_hal_kernel_h_
+
+#include "gc_hal.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel_hardware.h"
+#include "gc_hal_options_internal.h"
+
+
+/*******************************************************************************
+***** New MMU Defination *******************************************************/
+#define gcdMMU_MTLB_SHIFT 22
+#define gcdMMU_STLB_4K_SHIFT 12
+#define gcdMMU_STLB_64K_SHIFT 16
+
+#define gcdMMU_MTLB_BITS (32 - gcdMMU_MTLB_SHIFT)
+#define gcdMMU_PAGE_4K_BITS gcdMMU_STLB_4K_SHIFT
+#define gcdMMU_STLB_4K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_PAGE_4K_BITS)
+#define gcdMMU_PAGE_64K_BITS gcdMMU_STLB_64K_SHIFT
+#define gcdMMU_STLB_64K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_PAGE_64K_BITS)
+
+#define gcdMMU_MTLB_ENTRY_NUM (1 << gcdMMU_MTLB_BITS)
+#define gcdMMU_MTLB_SIZE (gcdMMU_MTLB_ENTRY_NUM << 2)
+#define gcdMMU_STLB_4K_ENTRY_NUM (1 << gcdMMU_STLB_4K_BITS)
+#define gcdMMU_STLB_4K_SIZE (gcdMMU_STLB_4K_ENTRY_NUM << 2)
+#define gcdMMU_PAGE_4K_SIZE (1 << gcdMMU_STLB_4K_SHIFT)
+#define gcdMMU_STLB_64K_ENTRY_NUM (1 << gcdMMU_STLB_64K_BITS)
+#define gcdMMU_STLB_64K_SIZE (gcdMMU_STLB_64K_ENTRY_NUM << 2)
+#define gcdMMU_PAGE_64K_SIZE (1 << gcdMMU_STLB_64K_SHIFT)
+
+#define gcdMMU_MTLB_MASK (~((1U << gcdMMU_MTLB_SHIFT)-1))
+#define gcdMMU_STLB_4K_MASK ((~0U << gcdMMU_STLB_4K_SHIFT) ^ gcdMMU_MTLB_MASK)
+#define gcdMMU_PAGE_4K_MASK (gcdMMU_PAGE_4K_SIZE - 1)
+#define gcdMMU_STLB_64K_MASK ((~((1U << gcdMMU_STLB_64K_SHIFT)-1)) ^ gcdMMU_MTLB_MASK)
+#define gcdMMU_PAGE_64K_MASK (gcdMMU_PAGE_64K_SIZE - 1)
+
+/*******************************************************************************
+***** Process Secure Cache ****************************************************/
+
+#define gcdSECURE_CACHE_LRU 1
+#define gcdSECURE_CACHE_LINEAR 2
+#define gcdSECURE_CACHE_HASH 3
+#define gcdSECURE_CACHE_TABLE 4
+
+typedef struct _gcskLOGICAL_CACHE * gcskLOGICAL_CACHE_PTR;
+typedef struct _gcskLOGICAL_CACHE gcskLOGICAL_CACHE;
+struct _gcskLOGICAL_CACHE
+{
+ /* Logical address. */
+ void * logical;
+
+ /* DMAable address. */
+ u32 dma;
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ /* Pointer to the previous and next hash tables. */
+ gcskLOGICAL_CACHE_PTR nextHash;
+ gcskLOGICAL_CACHE_PTR prevHash;
+#endif
+
+#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE
+ /* Pointer to the previous and next slot. */
+ gcskLOGICAL_CACHE_PTR next;
+ gcskLOGICAL_CACHE_PTR prev;
+#endif
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR
+ /* Time stamp. */
+ u64 stamp;
+#endif
+};
+
+typedef struct _gcskSECURE_CACHE * gcskSECURE_CACHE_PTR;
+typedef struct _gcskSECURE_CACHE
+{
+ /* Cache memory. */
+ gcskLOGICAL_CACHE cache[1 + gcdSECURE_CACHE_SLOTS];
+
+ /* Last known index for LINEAR mode. */
+ gcskLOGICAL_CACHE_PTR cacheIndex;
+
+ /* Current free slot for LINEAR mode. */
+ u32 cacheFree;
+
+ /* Time stamp for LINEAR mode. */
+ u64 cacheStamp;
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ /* Hash table for HASH mode. */
+ gcskLOGICAL_CACHE hash[256];
+#endif
+}
+gcskSECURE_CACHE;
+
+/*******************************************************************************
+***** Process Database Management *********************************************/
+
+typedef enum _gceDATABASE_TYPE
+{
+ gcvDB_VIDEO_MEMORY = 1, /* Video memory created. */
+ gcvDB_NON_PAGED, /* Non paged memory. */
+ gcvDB_CONTIGUOUS, /* Contiguous memory. */
+ gcvDB_SIGNAL, /* Signal. */
+ gcvDB_VIDEO_MEMORY_LOCKED, /* Video memory locked. */
+ gcvDB_CONTEXT, /* Context */
+ gcvDB_IDLE, /* GPU idle. */
+ gcvDB_MAP_MEMORY, /* Map memory */
+ gcvDB_SHARED_INFO, /* Private data */
+ gcvDB_MAP_USER_MEMORY /* Map user memory */
+}
+gceDATABASE_TYPE;
+
+typedef struct _gcsDATABASE_RECORD * gcsDATABASE_RECORD_PTR;
+typedef struct _gcsDATABASE_RECORD
+{
+ /* Pointer to kernel. */
+ gckKERNEL kernel;
+
+ /* Pointer to next database record. */
+ gcsDATABASE_RECORD_PTR next;
+
+ /* Type of record. */
+ gceDATABASE_TYPE type;
+
+ /* Data for record. */
+ void * data;
+ gctPHYS_ADDR physical;
+ size_t bytes;
+}
+gcsDATABASE_RECORD;
+
+typedef struct _gcsDATABASE * gcsDATABASE_PTR;
+typedef struct _gcsDATABASE
+{
+ /* Pointer to next entry is hash list. */
+ gcsDATABASE_PTR next;
+ size_t slot;
+
+ /* Process ID. */
+ u32 processID;
+
+ /* Sizes to query. */
+ gcsDATABASE_COUNTERS vidMem;
+ gcsDATABASE_COUNTERS nonPaged;
+ gcsDATABASE_COUNTERS contiguous;
+ gcsDATABASE_COUNTERS mapUserMemory;
+ gcsDATABASE_COUNTERS mapMemory;
+
+ /* Idle time management. */
+ u64 lastIdle;
+ u64 idle;
+
+ /* Pointer to database. */
+ gcsDATABASE_RECORD_PTR list;
+
+#if gcdSECURE_USER
+ /* Secure cache. */
+ gcskSECURE_CACHE cache;
+#endif
+}
+gcsDATABASE;
+
+/* Create a process database that will contain all its allocations. */
+gceSTATUS
+gckKERNEL_CreateProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID
+ );
+
+/* Add a record to the process database. */
+gceSTATUS
+gckKERNEL_AddProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN gceDATABASE_TYPE Type,
+ IN void *Pointer,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Size
+ );
+
+/* Remove a record to the process database. */
+gceSTATUS
+gckKERNEL_RemoveProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN gceDATABASE_TYPE Type,
+ IN void *Pointer
+ );
+
+/* Destroy the process database. */
+gceSTATUS
+gckKERNEL_DestroyProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID
+ );
+
+/* Find a record to the process database. */
+gceSTATUS
+gckKERNEL_FindProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN u32 ThreadID,
+ IN gceDATABASE_TYPE Type,
+ IN void *Pointer,
+ OUT gcsDATABASE_RECORD_PTR Record
+ );
+
+/* Query the process database. */
+gceSTATUS
+gckKERNEL_QueryProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN int LastProcessID,
+ IN gceDATABASE_TYPE Type,
+ OUT gcuDATABASE_INFO * Info
+ );
+
+#if gcdSECURE_USER
+/* Get secure cache from the process database. */
+gceSTATUS
+gckKERNEL_GetProcessDBCache(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ OUT gcskSECURE_CACHE_PTR * Cache
+ );
+#endif
+
+/*******************************************************************************
+********* Timer Management ****************************************************/
+typedef struct _gcsTIMER * gcsTIMER_PTR;
+typedef struct _gcsTIMER
+{
+ /* Start and Stop time holders. */
+ u64 startTime;
+ u64 stopTime;
+}
+gcsTIMER;
+
+/******************************************************************************\
+********************************** Structures **********************************
+\******************************************************************************/
+
+/* gckDB object. */
+struct _gckDB
+{
+ /* Database management. */
+ gcsDATABASE_PTR db[16];
+ void * dbMutex;
+ gcsDATABASE_PTR freeDatabase;
+ gcsDATABASE_RECORD_PTR freeRecord;
+ gcsDATABASE_PTR lastDatabase;
+ u32 lastProcessID;
+ u64 lastIdle;
+ u64 idleTime;
+ u64 lastSlowdown;
+ u64 lastSlowdownIdle;
+};
+
+/* gckKERNEL object. */
+struct _gckKERNEL
+{
+ /* Object. */
+ gcsOBJECT object;
+
+ /* Pointer to gckOS object. */
+ gckOS os;
+
+ /* Core */
+ gceCORE core;
+
+ /* Pointer to gckHARDWARE object. */
+ gckHARDWARE hardware;
+
+ /* Pointer to gckCOMMAND object. */
+ gckCOMMAND command;
+
+ /* Pointer to gckEVENT object. */
+ gckEVENT eventObj;
+
+ /* Pointer to context. */
+ void * context;
+
+ /* Pointer to gckMMU object. */
+ gckMMU mmu;
+
+ /* Arom holding number of clients. */
+ void * atomClients;
+
+ /* Database management. */
+ gckDB db;
+ int dbCreated;
+
+ /* Pointer to gckEVENT object. */
+ gcsTIMER timers[8];
+ u32 timeOut;
+};
+
+/* gckCOMMAND object. */
+struct _gckCOMMAND
+{
+ /* Object. */
+ gcsOBJECT object;
+
+ /* Pointer to required object. */
+ gckKERNEL kernel;
+ gckOS os;
+
+ /* Current pipe select. */
+ gcePIPE_SELECT pipeSelect;
+
+ /* Command queue running flag. */
+ int running;
+
+ /* Idle flag and commit stamp. */
+ int idle;
+ u64 commitStamp;
+
+ /* Command queue mutex. */
+ void * mutexQueue;
+
+ /* Context switching mutex. */
+ void * mutexContext;
+
+ /* Command queue power semaphore. */
+ void * powerSemaphore;
+
+ /* Current command queue. */
+ struct _gcskCOMMAND_QUEUE
+ {
+ gctSIGNAL signal;
+ gctPHYS_ADDR physical;
+ void * logical;
+ }
+ queues[gcdCOMMAND_QUEUES];
+
+ gctPHYS_ADDR physical;
+ void * logical;
+ u32 offset;
+ int index;
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ unsigned int wrapCount;
+#endif
+
+ /* The command queue is new. */
+ int newQueue;
+
+ /* Context management. */
+ gckCONTEXT currContext;
+
+ /* Pointer to last WAIT command. */
+ gctPHYS_ADDR waitPhysical;
+ void * waitLogical;
+ size_t waitSize;
+
+ /* Command buffer alignment. */
+ size_t alignment;
+ size_t reservedHead;
+ size_t reservedTail;
+
+ /* Commit counter. */
+ void * atomCommit;
+
+ /* Kernel process ID. */
+ u32 kernelProcessID;
+
+ /* End Event signal. */
+ gctSIGNAL endEventSignal;
+
+#if gcdSECURE_USER
+ /* Hint array copy buffer. */
+ int hintArrayAllocated;
+ unsigned int hintArraySize;
+ u32 * hintArray;
+#endif
+};
+
+typedef struct _gcsEVENT * gcsEVENT_PTR;
+
+/* Structure holding one event to be processed. */
+typedef struct _gcsEVENT
+{
+ /* Pointer to next event in queue. */
+ gcsEVENT_PTR next;
+
+ /* Event information. */
+ gcsHAL_INTERFACE info;
+
+ /* Process ID owning the event. */
+ u32 processID;
+}
+gcsEVENT;
+
+/* Structure holding a list of events to be processed by an interrupt. */
+typedef struct _gcsEVENT_QUEUE * gcsEVENT_QUEUE_PTR;
+typedef struct _gcsEVENT_QUEUE
+{
+ /* Time stamp. */
+ u64 stamp;
+
+ /* Source of the event. */
+ gceKERNEL_WHERE source;
+
+ /* Pointer to head of event queue. */
+ gcsEVENT_PTR head;
+
+ /* Pointer to tail of event queue. */
+ gcsEVENT_PTR tail;
+
+ /* Next list of events. */
+ gcsEVENT_QUEUE_PTR next;
+}
+gcsEVENT_QUEUE;
+
+/*
+ gcdREPO_LIST_COUNT defines the maximum number of event queues with different
+ hardware module sources that may coexist at the same time. Only two sources
+ are supported - gcvKERNEL_COMMAND and gcvKERNEL_PIXEL. gcvKERNEL_COMMAND
+ source is used only for managing the kernel command queue and is only issued
+ when the current command queue gets full. Since we commit event queues every
+ time we commit command buffers, in the worst case we can have up to three
+ pending event queues:
+ - gcvKERNEL_PIXEL
+ - gcvKERNEL_COMMAND (queue overflow)
+ - gcvKERNEL_PIXEL
+*/
+#define gcdREPO_LIST_COUNT 3
+
+/* gckEVENT object. */
+struct _gckEVENT
+{
+ /* The object. */
+ gcsOBJECT object;
+
+ /* Pointer to required objects. */
+ gckOS os;
+ gckKERNEL kernel;
+
+ /* Time stamp. */
+ u64 stamp;
+ u64 lastCommitStamp;
+
+ /* Queue mutex. */
+ void * eventQueueMutex;
+
+ /* Array of event queues. */
+ gcsEVENT_QUEUE queues[30];
+ u8 lastID;
+ void * freeAtom;
+
+ /* Pending events. */
+#ifdef CONFIG_SMP
+ void * pending;
+#else
+ volatile unsigned int pending;
+#endif
+
+ /* List of free event structures and its mutex. */
+ gcsEVENT_PTR freeEventList;
+ size_t freeEventCount;
+ void * freeEventMutex;
+
+ /* Event queues. */
+ gcsEVENT_QUEUE_PTR queueHead;
+ gcsEVENT_QUEUE_PTR queueTail;
+ gcsEVENT_QUEUE_PTR freeList;
+ gcsEVENT_QUEUE repoList[gcdREPO_LIST_COUNT];
+ void * eventListMutex;
+};
+
+gceSTATUS
+gckEVENT_Stop(
+ IN gckEVENT Event,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN void *Logical,
+ IN gctSIGNAL Signal,
+ IN OUT size_t * waitSize
+ );
+
+/* gcuVIDMEM_NODE structure. */
+typedef union _gcuVIDMEM_NODE
+{
+ /* Allocated from gckVIDMEM. */
+ struct _gcsVIDMEM_NODE_VIDMEM
+ {
+ /* Owner of this node. */
+ gckVIDMEM memory;
+
+ /* Dual-linked list of nodes. */
+ gcuVIDMEM_NODE_PTR next;
+ gcuVIDMEM_NODE_PTR prev;
+
+ /* Dual linked list of free nodes. */
+ gcuVIDMEM_NODE_PTR nextFree;
+ gcuVIDMEM_NODE_PTR prevFree;
+
+ /* Information for this node. */
+ u32 offset;
+ size_t bytes;
+ u32 alignment;
+
+ /* Locked counter. */
+ s32 locked;
+
+ /* Memory pool. */
+ gcePOOL pool;
+ u32 physical;
+
+ /* Process ID owning this memory. */
+ u32 processID;
+
+ /* Prevent compositor from freeing until client unlocks. */
+ int freePending;
+
+ /* */
+ gcsVIDMEM_NODE_SHARED_INFO sharedInfo;
+ }
+ VidMem;
+
+ /* Allocated from gckOS. */
+ struct _gcsVIDMEM_NODE_VIRTUAL
+ {
+ /* Pointer to gckKERNEL object. */
+ gckKERNEL kernel;
+
+ /* Information for this node. */
+ /* Contiguously allocated? */
+ int contiguous;
+ /* mdl record pointer... a kmalloc address. Process agnostic. */
+ gctPHYS_ADDR physical;
+ size_t bytes;
+ /* do_mmap_pgoff address... mapped per-process. */
+ void * logical;
+
+ /* Page table information. */
+ /* Used only when node is not contiguous */
+ size_t pageCount;
+
+ /* Used only when node is not contiguous */
+ void * pageTables[gcdCORE_COUNT];
+ /* Pointer to gckKERNEL object who lock this. */
+ gckKERNEL lockKernels[gcdCORE_COUNT];
+ /* Actual physical address */
+ u32 addresses[gcdCORE_COUNT];
+
+ /* Mutex. */
+ void * mutex;
+
+ /* Locked counter. */
+ s32 lockeds[gcdCORE_COUNT];
+
+ /* Process ID owning this memory. */
+ u32 processID;
+
+ /* Owner process sets freed to true
+ * when it trys to free a locked
+ * node */
+ int freed;
+
+ /* */
+ gcsVIDMEM_NODE_SHARED_INFO sharedInfo;
+ }
+ Virtual;
+}
+gcuVIDMEM_NODE;
+
+/* gckVIDMEM object. */
+struct _gckVIDMEM
+{
+ /* Object. */
+ gcsOBJECT object;
+
+ /* Pointer to gckOS object. */
+ gckOS os;
+
+ /* Information for this video memory heap. */
+ u32 baseAddress;
+ size_t bytes;
+ size_t freeBytes;
+
+ /* Mapping for each type of surface. */
+ int mapping[gcvSURF_NUM_TYPES];
+
+ /* Sentinel nodes for up to 8 banks. */
+ gcuVIDMEM_NODE sentinel[8];
+
+ /* Allocation threshold. */
+ size_t threshold;
+
+ /* The heap mutex. */
+ void * mutex;
+
+#if gcdUSE_VIDMEM_PER_PID
+ /* The Pid this VidMem belongs to. */
+ u32 pid;
+
+ struct _gckVIDMEM* next;
+#endif
+};
+
+/* gckMMU object. */
+struct _gckMMU
+{
+ /* The object. */
+ gcsOBJECT object;
+
+ /* Pointer to gckOS object. */
+ gckOS os;
+
+ /* Pointer to gckHARDWARE object. */
+ gckHARDWARE hardware;
+
+ /* The page table mutex. */
+ void * pageTableMutex;
+
+ /* Page table information. */
+ size_t pageTableSize;
+ gctPHYS_ADDR pageTablePhysical;
+ u32 * pageTableLogical;
+ u32 pageTableEntries;
+
+ /* Master TLB information. */
+ size_t mtlbSize;
+ gctPHYS_ADDR mtlbPhysical;
+ u32 * mtlbLogical;
+ u32 mtlbEntries;
+
+ /* Free entries. */
+ u32 heapList;
+ int freeNodes;
+
+ void * staticSTLB;
+ int enabled;
+
+ u32 dynamicMappingStart;
+};
+
+gceSTATUS
+gckKERNEL_AttachProcess(
+ IN gckKERNEL Kernel,
+ IN int Attach
+ );
+
+gceSTATUS
+gckKERNEL_AttachProcessEx(
+ IN gckKERNEL Kernel,
+ IN int Attach,
+ IN u32 PID
+ );
+
+#if gcdSECURE_USER
+gceSTATUS
+gckKERNEL_MapLogicalToPhysical(
+ IN gckKERNEL Kernel,
+ IN gcskSECURE_CACHE_PTR Cache,
+ IN OUT void **Data
+ );
+
+gceSTATUS
+gckKERNEL_FlushTranslationCache(
+ IN gckKERNEL Kernel,
+ IN gcskSECURE_CACHE_PTR Cache,
+ IN void *Logical,
+ IN size_t Bytes
+ );
+#endif
+
+gceSTATUS
+gckHARDWARE_QueryIdle(
+ IN gckHARDWARE Hardware,
+ OUT int *IsIdle
+ );
+
+/******************************************************************************\
+******************************* gckCONTEXT Object *******************************
+\******************************************************************************/
+
+gceSTATUS
+gckCONTEXT_Construct(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN u32 ProcessID,
+ OUT gckCONTEXT * Context
+ );
+
+gceSTATUS
+gckCONTEXT_Destroy(
+ IN gckCONTEXT Context
+ );
+
+gceSTATUS
+gckCONTEXT_Update(
+ IN gckCONTEXT Context,
+ IN u32 ProcessID,
+ IN struct _gcsSTATE_DELTA *StateDelta
+ );
+
+#endif /* __gc_hal_kernel_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_command.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_command.c
new file mode 100644
index 0000000..20ae41b
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_command.c
@@ -0,0 +1,2572 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+#include "gc_hal_kernel_context.h"
+
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define _GC_OBJ_ZONE gcvZONE_COMMAND
+
+/* When enabled, extra messages needed by the dump parser are left out. */
+#define gcdSIMPLE_COMMAND_DUMP 1
+
+/******************************************************************************\
+********************************* Support Code *********************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** _NewQueue
+**
+** Allocate a new command queue.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object.
+**
+** OUTPUT:
+**
+** gckCOMMAND Command
+** gckCOMMAND object has been updated with a new command queue.
+*/
+static gceSTATUS
+_NewQueue(
+ IN OUT gckCOMMAND Command
+ )
+{
+ gceSTATUS status;
+ int currentIndex, newIndex;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Switch to the next command buffer. */
+ currentIndex = Command->index;
+ newIndex = (currentIndex + 1) % gcdCOMMAND_QUEUES;
+
+ /* Wait for availability. */
+#if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
+ gcmkPRINT("@[kernel.waitsignal]");
+#endif
+
+ gcmkONERROR(gckOS_WaitSignal(
+ Command->os,
+ Command->queues[newIndex].signal,
+ gcvINFINITE
+ ));
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ if (newIndex < currentIndex)
+ {
+ Command->wrapCount += 1;
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_COMMAND,
+ 2 * 4,
+ "%s(%d): queue array wrapped around.\n",
+ __FUNCTION__, __LINE__
+ );
+ }
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_COMMAND,
+ 3 * 4,
+ "%s(%d): total queue wrap arounds %d.\n",
+ __FUNCTION__, __LINE__, Command->wrapCount
+ );
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_COMMAND,
+ 3 * 4,
+ "%s(%d): switched to queue %d.\n",
+ __FUNCTION__, __LINE__, newIndex
+ );
+#endif
+
+ /* Update gckCOMMAND object with new command queue. */
+ Command->index = newIndex;
+ Command->newQueue = gcvTRUE;
+ Command->logical = Command->queues[newIndex].logical;
+ Command->offset = 0;
+
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(
+ Command->os,
+ Command->logical,
+ (u32 *) &Command->physical
+ ));
+
+ if (currentIndex != -1)
+ {
+ /* Mark the command queue as available. */
+ gcmkONERROR(gckEVENT_Signal(
+ Command->kernel->eventObj,
+ Command->queues[currentIndex].signal,
+ gcvKERNEL_COMMAND
+ ));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("Command->index=%d", Command->index);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+_IncrementCommitAtom(
+ IN gckCOMMAND Command,
+ IN int Increment
+ )
+{
+ gceSTATUS status;
+ gckHARDWARE hardware;
+ s32 atomValue;
+ int powerAcquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Extract the gckHARDWARE and gckEVENT objects. */
+ hardware = Command->kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ /* Grab the power mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(
+ Command->os, hardware->powerMutex, gcvINFINITE
+ ));
+ powerAcquired = gcvTRUE;
+
+ /* Increment the commit atom. */
+ if (Increment)
+ {
+ gcmkONERROR(gckOS_AtomIncrement(
+ Command->os, Command->atomCommit, &atomValue
+ ));
+ }
+ else
+ {
+ gcmkONERROR(gckOS_AtomDecrement(
+ Command->os, Command->atomCommit, &atomValue
+ ));
+ }
+
+ /* Release the power mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(
+ Command->os, hardware->powerMutex
+ ));
+ powerAcquired = gcvFALSE;
+
+ /* Success. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (powerAcquired)
+ {
+ /* Release the power mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(
+ Command->os, hardware->powerMutex
+ ));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#if gcdSECURE_USER
+static gceSTATUS
+_ProcessHints(
+ IN gckCOMMAND Command,
+ IN u32 ProcessID,
+ IN gcoCMDBUF CommandBuffer
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ gckKERNEL kernel;
+ gcskSECURE_CACHE_PTR cache;
+ u8 *commandBufferLogical;
+ u8 *hintedData;
+ u32 *hintArray;
+ unsigned int i, hintCount;
+
+ gcmkHEADER_ARG(
+ "Command=0x%08X ProcessID=%d CommandBuffer=0x%08X",
+ Command, ProcessID, CommandBuffer
+ );
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ /* Reset state array pointer. */
+ hintArray = NULL;
+
+ /* Get the kernel object. */
+ kernel = Command->kernel;
+
+ /* Get the cache form the database. */
+ gcmkONERROR(gckKERNEL_GetProcessDBCache(kernel, ProcessID, &cache));
+
+ /* Determine the start of the command buffer. */
+ commandBufferLogical
+ = (u8 *) CommandBuffer->logical
+ + CommandBuffer->startOffset;
+
+ /* Determine the number of records in the state array. */
+ hintCount = CommandBuffer->hintArrayTail - CommandBuffer->hintArray;
+
+ /* Get access to the state array. */
+ if (NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ unsigned int copySize;
+
+ if (Command->hintArrayAllocated &&
+ (Command->hintArraySize < CommandBuffer->hintArraySize))
+ {
+ gcmkONERROR(gcmkOS_SAFE_FREE(Command->os, Command->hintArray));
+ Command->hintArraySize = gcvFALSE;
+ }
+
+ if (!Command->hintArrayAllocated)
+ {
+ void *pointer = NULL;
+
+ gcmkONERROR(gckOS_Allocate(
+ Command->os,
+ CommandBuffer->hintArraySize,
+ &pointer
+ ));
+
+ Command->hintArray = pointer;
+ Command->hintArrayAllocated = gcvTRUE;
+ Command->hintArraySize = CommandBuffer->hintArraySize;
+ }
+
+ hintArray = Command->hintArray;
+ copySize = hintCount * sizeof(u32);
+
+ if (copy_from_user(hintArray, CommandBuffer->hintArray, copySize) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ else
+ {
+ void *pointer = NULL;
+
+ gcmkONERROR(gckOS_MapUserPointer(
+ Command->os,
+ CommandBuffer->hintArray,
+ CommandBuffer->hintArraySize,
+ &pointer
+ ));
+
+ hintArray = pointer;
+ }
+
+ /* Scan through the buffer. */
+ for (i = 0; i < hintCount; i += 1)
+ {
+ /* Determine the location of the hinted data. */
+ hintedData = commandBufferLogical + hintArray[i];
+
+ /* Map handle into physical address. */
+ gcmkONERROR(gckKERNEL_MapLogicalToPhysical(
+ kernel, cache, (void *) hintedData
+ ));
+ }
+
+OnError:
+ /* Get access to the state array. */
+ if (!NO_USER_DIRECT_ACCESS_FROM_KERNEL && (hintArray != NULL))
+ {
+ gcmkVERIFY_OK(gckOS_UnmapUserPointer(
+ Command->os,
+ CommandBuffer->hintArray,
+ CommandBuffer->hintArraySize,
+ hintArray
+ ));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
+
+static gceSTATUS
+_FlushMMU(
+ IN gckCOMMAND Command
+ )
+{
+ gceSTATUS status;
+ u32 oldValue;
+ gckHARDWARE hardware = Command->kernel->hardware;
+
+ gcmkONERROR(gckOS_AtomicExchange(Command->os,
+ hardware->pageTableDirty,
+ 0,
+ &oldValue));
+
+ if (oldValue)
+ {
+ /* Page Table is upated, flush mmu before commit. */
+ gcmkONERROR(gckHARDWARE_FlushMMU(hardware));
+ }
+
+ return gcvSTATUS_OK;
+OnError:
+ return status;
+}
+
+/******************************************************************************\
+****************************** gckCOMMAND API Code ******************************
+\******************************************************************************/
+
+/* Size of kernel GPU command queue */
+#define COMMAND_QUEUE_SIZE (PAGE_SIZE)
+
+/*******************************************************************************
+**
+** gckCOMMAND_Construct
+**
+** Construct a new gckCOMMAND object.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** OUTPUT:
+**
+** gckCOMMAND * Command
+** Pointer to a variable that will hold the pointer to the gckCOMMAND
+** object.
+*/
+gceSTATUS
+gckCOMMAND_Construct(
+ IN gckKERNEL Kernel,
+ OUT gckCOMMAND * Command
+ )
+{
+ gckOS os;
+ gckCOMMAND command = NULL;
+ gceSTATUS status;
+ int i;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Kernel=0x%x", Kernel);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Command != NULL);
+
+ /* Extract the gckOS object. */
+ os = Kernel->os;
+
+ /* Allocate the gckCOMMAND structure. */
+ gcmkONERROR(gckOS_Allocate(os, sizeof(struct _gckCOMMAND), &pointer));
+ command = pointer;
+
+ /* Reset the entire object. */
+ gcmkONERROR(gckOS_ZeroMemory(command, sizeof(struct _gckCOMMAND)));
+
+ /* Initialize the gckCOMMAND object.*/
+ command->object.type = gcvOBJ_COMMAND;
+ command->kernel = Kernel;
+ command->os = os;
+
+ /* Get the command buffer requirements. */
+ gcmkONERROR(gckHARDWARE_QueryCommandBuffer(
+ Kernel->hardware,
+ &command->alignment,
+ &command->reservedHead,
+ &command->reservedTail
+ ));
+
+ /* Create the command queue mutex. */
+ gcmkONERROR(gckOS_CreateMutex(os, &command->mutexQueue));
+
+ /* Create the context switching mutex. */
+ gcmkONERROR(gckOS_CreateMutex(os, &command->mutexContext));
+
+ /* Create the power management semaphore. */
+ gcmkONERROR(gckOS_CreateSemaphore(os, &command->powerSemaphore));
+
+ /* Create the commit atom. */
+ gcmkONERROR(gckOS_AtomConstruct(os, &command->atomCommit));
+
+ command->kernelProcessID = task_tgid_vnr(current);
+
+ /* Set hardware to pipe 0. */
+ command->pipeSelect = gcvPIPE_INVALID;
+
+ /* Pre-allocate the command queues. */
+ for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
+ {
+ size_t bytes = COMMAND_QUEUE_SIZE;
+ gcmkONERROR(gckOS_AllocateNonPagedMemory(
+ os,
+ gcvFALSE,
+ &bytes,
+ &command->queues[i].physical,
+ &command->queues[i].logical
+ ));
+
+ gcmkONERROR(gckOS_CreateSignal(
+ os, gcvFALSE, &command->queues[i].signal
+ ));
+
+ gcmkONERROR(gckOS_Signal(
+ os, command->queues[i].signal, gcvTRUE
+ ));
+ }
+
+ /* No command queue in use yet. */
+ command->index = -1;
+ command->logical = NULL;
+ command->newQueue = gcvFALSE;
+
+ /* Command is not yet running. */
+ command->running = gcvFALSE;
+
+ /* Command queue is idle. */
+ command->idle = gcvTRUE;
+
+ /* Commit stamp is zero. */
+ command->commitStamp = 0;
+
+ /* END event signal not created. */
+ command->endEventSignal = NULL;
+
+ /* Return pointer to the gckCOMMAND object. */
+ *Command = command;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Command=0x%x", *Command);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (command != NULL)
+ {
+ if (command->atomCommit != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_AtomDestroy(os, command->atomCommit));
+ }
+
+ if (command->powerSemaphore != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DestroySemaphore(os, command->powerSemaphore));
+ }
+
+ if (command->mutexContext != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexContext));
+ }
+
+ if (command->mutexQueue != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexQueue));
+ }
+
+ for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
+ {
+ if (command->queues[i].signal != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DestroySignal(
+ os, command->queues[i].signal
+ ));
+ }
+
+ if (command->queues[i].logical != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_FreeNonPagedMemory(
+ os,
+ COMMAND_QUEUE_SIZE,
+ command->queues[i].physical,
+ command->queues[i].logical
+ ));
+ }
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, command));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Destroy
+**
+** Destroy an gckCOMMAND object.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object to destroy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_Destroy(
+ IN gckCOMMAND Command
+ )
+{
+ int i;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ /* Stop the command queue. */
+ gcmkVERIFY_OK(gckCOMMAND_Stop(Command, gcvFALSE));
+
+ for (i = 0; i < gcdCOMMAND_QUEUES; ++i)
+ {
+ gcmkASSERT(Command->queues[i].signal != NULL);
+ gcmkVERIFY_OK(gckOS_DestroySignal(
+ Command->os, Command->queues[i].signal
+ ));
+
+ gcmkASSERT(Command->queues[i].logical != NULL);
+ gcmkVERIFY_OK(gckOS_FreeNonPagedMemory(
+ Command->os,
+ COMMAND_QUEUE_SIZE,
+ Command->queues[i].physical,
+ Command->queues[i].logical
+ ));
+ }
+
+ /* END event signal. */
+ if (Command->endEventSignal != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DestroySignal(
+ Command->os, Command->endEventSignal
+ ));
+ }
+
+ /* Delete the context switching mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutexContext));
+
+ /* Delete the command queue mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutexQueue));
+
+ /* Destroy the power management semaphore. */
+ gcmkVERIFY_OK(gckOS_DestroySemaphore(Command->os, Command->powerSemaphore));
+
+ /* Destroy the commit atom. */
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Command->os, Command->atomCommit));
+
+#if gcdSECURE_USER
+ /* Free state array. */
+ if (Command->hintArrayAllocated)
+ {
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Command->os, Command->hintArray));
+ Command->hintArrayAllocated = gcvFALSE;
+ }
+#endif
+
+ /* Mark object as unknown. */
+ Command->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckCOMMAND object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Command->os, Command));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_EnterCommit
+**
+** Acquire command queue synchronization objects.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object to destroy.
+**
+** int FromPower
+** Determines whether the call originates from inside the power
+** management or not.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_EnterCommit(
+ IN gckCOMMAND Command,
+ IN int FromPower
+ )
+{
+ gceSTATUS status;
+ gckHARDWARE hardware;
+ int atomIncremented = gcvFALSE;
+ int semaAcquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Extract the gckHARDWARE and gckEVENT objects. */
+ hardware = Command->kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ if (!FromPower)
+ {
+ /* Increment COMMIT atom to let power management know that a commit is
+ ** in progress. */
+ gcmkONERROR(_IncrementCommitAtom(Command, gcvTRUE));
+ atomIncremented = gcvTRUE;
+
+ /* Notify the system the GPU has a commit. */
+ gcmkONERROR(gckOS_Broadcast(Command->os,
+ hardware,
+ gcvBROADCAST_GPU_COMMIT));
+
+ /* Acquire the power management semaphore. */
+ gcmkONERROR(gckOS_AcquireSemaphore(Command->os,
+ Command->powerSemaphore));
+ semaAcquired = gcvTRUE;
+ }
+
+ /* Grab the conmmand queue mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Command->os,
+ Command->mutexQueue,
+ gcvINFINITE));
+
+ /* Success. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (semaAcquired)
+ {
+ /* Release the power management semaphore. */
+ gcmkVERIFY_OK(gckOS_ReleaseSemaphore(
+ Command->os, Command->powerSemaphore
+ ));
+ }
+
+ if (atomIncremented)
+ {
+ /* Decrement the commit atom. */
+ gcmkVERIFY_OK(_IncrementCommitAtom(
+ Command, gcvFALSE
+ ));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_ExitCommit
+**
+** Release command queue synchronization objects.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object to destroy.
+**
+** int FromPower
+** Determines whether the call originates from inside the power
+** management or not.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_ExitCommit(
+ IN gckCOMMAND Command,
+ IN int FromPower
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Release the power mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexQueue));
+
+ if (!FromPower)
+ {
+ /* Release the power management semaphore. */
+ gcmkONERROR(gckOS_ReleaseSemaphore(Command->os,
+ Command->powerSemaphore));
+
+ /* Decrement the commit atom. */
+ gcmkONERROR(_IncrementCommitAtom(Command, gcvFALSE));
+ }
+
+ /* Success. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Start
+**
+** Start up the command queue.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object to start.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_Start(
+ IN gckCOMMAND Command
+ )
+{
+ gceSTATUS status;
+ gckHARDWARE hardware;
+ u32 waitOffset;
+ size_t waitLinkBytes;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ if (Command->running)
+ {
+ /* Command queue already running. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ /* Extract the gckHARDWARE object. */
+ hardware = Command->kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ if (Command->logical == NULL)
+ {
+ /* Start at beginning of a new queue. */
+ gcmkONERROR(_NewQueue(Command));
+ }
+
+ /* Start at beginning of page. */
+ Command->offset = 0;
+
+ /* Set abvailable number of bytes for WAIT/LINK command sequence. */
+ waitLinkBytes = COMMAND_QUEUE_SIZE;
+
+ /* Append WAIT/LINK. */
+ gcmkONERROR(gckHARDWARE_WaitLink(
+ hardware,
+ Command->logical,
+ 0,
+ &waitLinkBytes,
+ &waitOffset,
+ &Command->waitSize
+ ));
+
+ Command->waitLogical = (u8 *) Command->logical + waitOffset;
+ Command->waitPhysical = (u8 *) Command->physical + waitOffset;
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache for the wait/link. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ Command->kernelProcessID,
+ NULL,
+ Command->physical,
+ Command->logical,
+ waitLinkBytes
+ ));
+#endif
+
+ /* Adjust offset. */
+ Command->offset = waitLinkBytes;
+ Command->newQueue = gcvFALSE;
+
+ /* Enable command processor. */
+ gcmkONERROR(gckHARDWARE_Execute(
+ hardware,
+ Command->logical,
+ waitLinkBytes
+ ));
+
+ /* Command queue is running. */
+ Command->running = gcvTRUE;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Stop
+**
+** Stop the command queue.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object to stop.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_Stop(
+ IN gckCOMMAND Command,
+ IN int FromRecovery
+ )
+{
+ gckHARDWARE hardware;
+ gceSTATUS status;
+ u32 idle;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ if (!Command->running)
+ {
+ /* Command queue is not running. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ /* Extract the gckHARDWARE object. */
+ hardware = Command->kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ if (gckHARDWARE_IsFeatureAvailable(hardware,
+ gcvFEATURE_END_EVENT) == gcvSTATUS_TRUE)
+ {
+ /* Allocate the signal. */
+ if (Command->endEventSignal == NULL)
+ {
+ gcmkONERROR(gckOS_CreateSignal(Command->os,
+ gcvTRUE,
+ &Command->endEventSignal));
+ }
+
+ /* Append the END EVENT command to trigger the signal. */
+ gcmkONERROR(gckEVENT_Stop(Command->kernel->eventObj,
+ Command->kernelProcessID,
+ Command->waitPhysical,
+ Command->waitLogical,
+ Command->endEventSignal,
+ &Command->waitSize));
+ }
+ else
+ {
+ /* Replace last WAIT with END. */
+ gcmkONERROR(gckHARDWARE_End(
+ hardware, Command->waitLogical, &Command->waitSize
+ ));
+
+ /* Update queue tail pointer. */
+ gcmkONERROR(gckHARDWARE_UpdateQueueTail(Command->kernel->hardware,
+ Command->logical,
+ Command->offset));
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache for the END. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ Command->kernelProcessID,
+ NULL,
+ Command->waitPhysical,
+ Command->waitLogical,
+ Command->waitSize
+ ));
+#endif
+
+ /* Wait for idle. */
+ gcmkONERROR(gckHARDWARE_GetIdle(hardware, !FromRecovery, &idle));
+ }
+
+ /* Command queue is no longer running. */
+ Command->running = gcvFALSE;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Commit
+**
+** Commit a command buffer to the command queue.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to a gckCOMMAND object.
+**
+** gckCONTEXT Context
+** Pointer to a gckCONTEXT object.
+**
+** gcoCMDBUF CommandBuffer
+** Pointer to a gcoCMDBUF object.
+**
+** struct _gcsSTATE_DELTA *StateDelta
+** Pointer to the state delta.
+**
+** u32 ProcessID
+** Current process ID.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_Commit(
+ IN gckCOMMAND Command,
+ IN gckCONTEXT Context,
+ IN gcoCMDBUF CommandBuffer,
+ IN struct _gcsSTATE_DELTA *StateDelta,
+ IN struct _gcsQUEUE *EventQueue,
+ IN u32 ProcessID
+ )
+{
+ gceSTATUS status;
+ int commitEntered = gcvFALSE;
+ int contextAcquired = gcvFALSE;
+ gckHARDWARE hardware;
+ struct _gcsQUEUE *eventRecord = NULL;
+ gcsQUEUE _eventRecord;
+ struct _gcsQUEUE *nextEventRecord;
+ int commandBufferMapped = gcvFALSE;
+ gcoCMDBUF commandBufferObject = NULL;
+
+#if !gcdNULL_DRIVER
+ gcsCONTEXT_PTR contextBuffer;
+ struct _gcoCMDBUF _commandBufferObject;
+ gctPHYS_ADDR commandBufferPhysical;
+ u8 *commandBufferLogical;
+ u8 *commandBufferLink;
+ unsigned int commandBufferSize;
+ size_t nopBytes;
+ size_t pipeBytes;
+ size_t linkBytes;
+ size_t bytes;
+ u32 offset;
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ gctPHYS_ADDR entryPhysical;
+#endif
+ void *entryLogical;
+ size_t entryBytes;
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ gctPHYS_ADDR exitPhysical;
+#endif
+ void *exitLogical;
+ size_t exitBytes;
+ gctPHYS_ADDR waitLinkPhysical;
+ void *waitLinkLogical;
+ size_t waitLinkBytes;
+ gctPHYS_ADDR waitPhysical;
+ void *waitLogical;
+ u32 waitOffset;
+ size_t waitSize;
+
+#if gcdDUMP_COMMAND
+ void *contextDumpLogical = NULL;
+ size_t contextDumpBytes = 0;
+ void *bufferDumpLogical = NULL;
+ size_t bufferDumpBytes = 0;
+# endif
+#endif
+
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG(
+ "Command=0x%x CommandBuffer=0x%x ProcessID=%d",
+ Command, CommandBuffer, ProcessID
+ );
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ if (Command->kernel->core == gcvCORE_2D)
+ {
+ /* There is no context for 2D. */
+ Context = NULL;
+ }
+
+ gcmkONERROR(_FlushMMU(Command));
+
+ /* Acquire the command queue. */
+ gcmkONERROR(gckCOMMAND_EnterCommit(Command, gcvFALSE));
+ commitEntered = gcvTRUE;
+
+ /* Acquire the context switching mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(
+ Command->os, Command->mutexContext, gcvINFINITE
+ ));
+ contextAcquired = gcvTRUE;
+
+ /* Extract the gckHARDWARE and gckEVENT objects. */
+ hardware = Command->kernel->hardware;
+
+#if gcdNULL_DRIVER
+ /* Context switch required? */
+ if ((Context != NULL) && (Command->currContext != Context))
+ {
+ /* Yes, merge in the deltas. */
+ gckCONTEXT_Update(Context, ProcessID, StateDelta);
+
+ /* Update the current context. */
+ Command->currContext = Context;
+ }
+#else
+ if (NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ commandBufferObject = &_commandBufferObject;
+
+ if (copy_from_user(commandBufferObject, CommandBuffer, sizeof(struct _gcoCMDBUF)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ gcmkVERIFY_OBJECT(commandBufferObject, gcvOBJ_COMMANDBUFFER);
+ }
+ else
+ {
+ gcmkONERROR(gckOS_MapUserPointer(
+ Command->os,
+ CommandBuffer,
+ sizeof(struct _gcoCMDBUF),
+ &pointer
+ ));
+
+ commandBufferObject = pointer;
+
+ gcmkVERIFY_OBJECT(commandBufferObject, gcvOBJ_COMMANDBUFFER);
+ commandBufferMapped = gcvTRUE;
+ }
+
+ /* Query the size of NOP command. */
+ gcmkONERROR(gckHARDWARE_Nop(
+ hardware, NULL, &nopBytes
+ ));
+
+ /* Query the size of pipe select command sequence. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ hardware, NULL, gcvPIPE_3D, &pipeBytes
+ ));
+
+ /* Query the size of LINK command. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware, NULL, NULL, 0, &linkBytes
+ ));
+
+ /* Compute the command buffer entry and the size. */
+ commandBufferLogical
+ = (u8 *) commandBufferObject->logical
+ + commandBufferObject->startOffset;
+
+ gcmkONERROR(gckOS_GetPhysicalAddress(
+ Command->os,
+ commandBufferLogical,
+ (u32 *)&commandBufferPhysical
+ ));
+
+ commandBufferSize
+ = commandBufferObject->offset
+ + Command->reservedTail
+ - commandBufferObject->startOffset;
+
+ /* Context switch required? */
+ if (Context == NULL)
+ {
+ /* See if we have to switch pipes for the command buffer. */
+ if (commandBufferObject->entryPipe == Command->pipeSelect)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the entry command buffer pipes
+ ** are different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* Compute the entry. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) commandBufferPhysical + offset;
+#endif
+ entryLogical = commandBufferLogical + offset;
+ entryBytes = commandBufferSize - offset;
+ }
+ else if (Command->currContext != Context)
+ {
+ /* Temporary disable context length oprimization. */
+ Context->dirty = gcvTRUE;
+
+ /* Get the current context buffer. */
+ contextBuffer = Context->buffer;
+
+ /* Yes, merge in the deltas. */
+ gcmkONERROR(gckCONTEXT_Update(Context, ProcessID, StateDelta));
+
+ /* Determine context entry and exit points. */
+ if (0)
+ {
+ /* Reset 2D dirty flag. */
+ Context->dirty2D = gcvFALSE;
+
+ if (Context->dirty || commandBufferObject->using3D)
+ {
+ /***************************************************************
+ ** SWITCHING CONTEXT: 2D and 3D are used.
+ */
+
+ /* Reset 3D dirty flag. */
+ Context->dirty3D = gcvFALSE;
+
+ /* Compute the entry. */
+ if (Command->pipeSelect == gcvPIPE_2D)
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical + pipeBytes;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical + pipeBytes;
+ entryBytes = Context->bufferSize - pipeBytes;
+ }
+ else
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical;
+ entryBytes = Context->bufferSize;
+ }
+
+ /* See if we have to switch pipes between the context
+ and command buffers. */
+ if (commandBufferObject->entryPipe == gcvPIPE_3D)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the initial context pipes are
+ different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* Ensure the NOP between 2D and 3D is in place so that the
+ execution falls through from 2D to 3D. */
+ gcmkONERROR(gckHARDWARE_Nop(
+ hardware,
+ contextBuffer->link2D,
+ &nopBytes
+ ));
+
+ /* Generate a LINK from the context buffer to
+ the command buffer. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ contextBuffer->link3D,
+ commandBufferLogical + offset,
+ commandBufferSize - offset,
+ &linkBytes
+ ));
+
+ /* Mark context as not dirty. */
+ Context->dirty = gcvFALSE;
+ }
+ else
+ {
+ /***************************************************************
+ ** SWITCHING CONTEXT: 2D only command buffer.
+ */
+
+ /* Mark 3D as dirty. */
+ Context->dirty3D = gcvTRUE;
+
+ /* Compute the entry. */
+ if (Command->pipeSelect == gcvPIPE_2D)
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical + pipeBytes;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical + pipeBytes;
+ entryBytes = Context->entryOffset3D - pipeBytes;
+ }
+ else
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical;
+ entryBytes = Context->entryOffset3D;
+ }
+
+ /* Store the current context buffer. */
+ Context->dirtyBuffer = contextBuffer;
+
+ /* See if we have to switch pipes between the context
+ and command buffers. */
+ if (commandBufferObject->entryPipe == gcvPIPE_2D)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the initial context pipes are
+ different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* 3D is not used, generate a LINK from the end of 2D part of
+ the context buffer to the command buffer. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ contextBuffer->link2D,
+ commandBufferLogical + offset,
+ commandBufferSize - offset,
+ &linkBytes
+ ));
+ }
+ }
+
+ /* Not using 2D. */
+ else
+ {
+ /* Mark 2D as dirty. */
+ Context->dirty2D = gcvTRUE;
+
+ /* Store the current context buffer. */
+ Context->dirtyBuffer = contextBuffer;
+
+ if (Context->dirty || commandBufferObject->using3D)
+ {
+ /***************************************************************
+ ** SWITCHING CONTEXT: 3D only command buffer.
+ */
+
+ /* Reset 3D dirty flag. */
+ Context->dirty3D = gcvFALSE;
+
+ /* Determine context buffer entry offset. */
+ offset = (Command->pipeSelect == gcvPIPE_3D)
+
+ /* Skip pipe switching sequence. */
+ ? Context->entryOffset3D + pipeBytes
+
+ /* Do not skip pipe switching sequence. */
+ : Context->entryOffset3D;
+
+ /* Compute the entry. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical + offset;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical + offset;
+ entryBytes = Context->bufferSize - offset;
+
+ /* See if we have to switch pipes between the context
+ and command buffers. */
+ if (commandBufferObject->entryPipe == gcvPIPE_3D)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the initial context pipes are
+ different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* Generate a LINK from the context buffer to
+ the command buffer. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ contextBuffer->link3D,
+ commandBufferLogical + offset,
+ commandBufferSize - offset,
+ &linkBytes
+ ));
+ }
+ else
+ {
+ /***************************************************************
+ ** SWITCHING CONTEXT: "XD" command buffer - neither 2D nor 3D.
+ */
+
+ /* Mark 3D as dirty. */
+ Context->dirty3D = gcvTRUE;
+
+ /* Compute the entry. */
+ if (Command->pipeSelect == gcvPIPE_3D)
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical
+ = (u8 *) contextBuffer->physical
+ + Context->entryOffsetXDFrom3D;
+#endif
+ entryLogical
+ = (u8 *) contextBuffer->logical
+ + Context->entryOffsetXDFrom3D;
+
+ entryBytes
+ = Context->bufferSize
+ - Context->entryOffsetXDFrom3D;
+ }
+ else
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical
+ = (u8 *) contextBuffer->physical
+ + Context->entryOffsetXDFrom2D;
+#endif
+ entryLogical
+ = (u8 *) contextBuffer->logical
+ + Context->entryOffsetXDFrom2D;
+
+ entryBytes
+ = Context->totalSize
+ - Context->entryOffsetXDFrom2D;
+ }
+
+ /* See if we have to switch pipes between the context
+ and command buffers. */
+ if (commandBufferObject->entryPipe == gcvPIPE_3D)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the initial context pipes are
+ different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* Generate a LINK from the context buffer to
+ the command buffer. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ contextBuffer->link3D,
+ commandBufferLogical + offset,
+ commandBufferSize - offset,
+ &linkBytes
+ ));
+ }
+ }
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the context buffer cache. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ Command->kernelProcessID,
+ NULL,
+ entryPhysical,
+ entryLogical,
+ entryBytes
+ ));
+#endif
+
+ /* Update the current context. */
+ Command->currContext = Context;
+
+#if gcdDUMP_COMMAND
+ contextDumpLogical = entryLogical;
+ contextDumpBytes = entryBytes;
+#endif
+ }
+
+ /* Same context. */
+ else
+ {
+ /* Determine context entry and exit points. */
+ if (commandBufferObject->using2D && Context->dirty2D)
+ {
+ /* Reset 2D dirty flag. */
+ Context->dirty2D = gcvFALSE;
+
+ /* Get the "dirty" context buffer. */
+ contextBuffer = Context->dirtyBuffer;
+
+ if (commandBufferObject->using3D && Context->dirty3D)
+ {
+ /* Reset 3D dirty flag. */
+ Context->dirty3D = gcvFALSE;
+
+ /* Compute the entry. */
+ if (Command->pipeSelect == gcvPIPE_2D)
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical + pipeBytes;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical + pipeBytes;
+ entryBytes = Context->bufferSize - pipeBytes;
+ }
+ else
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical;
+ entryBytes = Context->bufferSize;
+ }
+
+ /* See if we have to switch pipes between the context
+ and command buffers. */
+ if (commandBufferObject->entryPipe == gcvPIPE_3D)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the initial context pipes are
+ different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* Ensure the NOP between 2D and 3D is in place so that the
+ execution falls through from 2D to 3D. */
+ gcmkONERROR(gckHARDWARE_Nop(
+ hardware,
+ contextBuffer->link2D,
+ &nopBytes
+ ));
+
+ /* Generate a LINK from the context buffer to
+ the command buffer. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ contextBuffer->link3D,
+ commandBufferLogical + offset,
+ commandBufferSize - offset,
+ &linkBytes
+ ));
+ }
+ else
+ {
+ /* Compute the entry. */
+ if (Command->pipeSelect == gcvPIPE_2D)
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical + pipeBytes;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical + pipeBytes;
+ entryBytes = Context->entryOffset3D - pipeBytes;
+ }
+ else
+ {
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical;
+ entryBytes = Context->entryOffset3D;
+ }
+
+ /* See if we have to switch pipes between the context
+ and command buffers. */
+ if (commandBufferObject->entryPipe == gcvPIPE_2D)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the initial context pipes are
+ different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* 3D is not used, generate a LINK from the end of 2D part of
+ the context buffer to the command buffer. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ contextBuffer->link2D,
+ commandBufferLogical + offset,
+ commandBufferSize - offset,
+ &linkBytes
+ ));
+ }
+ }
+ else
+ {
+ if (commandBufferObject->using3D && Context->dirty3D)
+ {
+ /* Reset 3D dirty flag. */
+ Context->dirty3D = gcvFALSE;
+
+ /* Get the "dirty" context buffer. */
+ contextBuffer = Context->dirtyBuffer;
+
+ /* Determine context buffer entry offset. */
+ offset = (Command->pipeSelect == gcvPIPE_3D)
+
+ /* Skip pipe switching sequence. */
+ ? Context->entryOffset3D + pipeBytes
+
+ /* Do not skip pipe switching sequence. */
+ : Context->entryOffset3D;
+
+ /* Compute the entry. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) contextBuffer->physical + offset;
+#endif
+ entryLogical = (u8 *) contextBuffer->logical + offset;
+ entryBytes = Context->bufferSize - offset;
+
+ /* See if we have to switch pipes between the context
+ and command buffers. */
+ if (commandBufferObject->entryPipe == gcvPIPE_3D)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the initial context pipes are
+ different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* Generate a LINK from the context buffer to
+ the command buffer. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ contextBuffer->link3D,
+ commandBufferLogical + offset,
+ commandBufferSize - offset,
+ &linkBytes
+ ));
+ }
+ else
+ {
+ /* See if we have to switch pipes for the command buffer. */
+ if (commandBufferObject->entryPipe == Command->pipeSelect)
+ {
+ /* Skip pipe switching sequence. */
+ offset = pipeBytes;
+ }
+ else
+ {
+ /* The current hardware and the entry command buffer pipes
+ ** are different, switch to the correct pipe. */
+ gcmkONERROR(gckHARDWARE_PipeSelect(
+ Command->kernel->hardware,
+ commandBufferLogical,
+ commandBufferObject->entryPipe,
+ &pipeBytes
+ ));
+
+ /* Do not skip pipe switching sequence. */
+ offset = 0;
+ }
+
+ /* Compute the entry. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ entryPhysical = (u8 *) commandBufferPhysical + offset;
+#endif
+ entryLogical = commandBufferLogical + offset;
+ entryBytes = commandBufferSize - offset;
+ }
+ }
+ }
+
+#if gcdDUMP_COMMAND
+ bufferDumpLogical = commandBufferLogical + offset;
+ bufferDumpBytes = commandBufferSize - offset;
+#endif
+
+#if gcdSECURE_USER
+ /* Process user hints. */
+ gcmkONERROR(_ProcessHints(Command, ProcessID, commandBufferObject));
+#endif
+
+ /* Get the current offset. */
+ offset = Command->offset;
+
+ /* Compute number of bytes left in current kernel command queue. */
+ bytes = COMMAND_QUEUE_SIZE - offset;
+
+ /* Query the size of WAIT/LINK command sequence. */
+ gcmkONERROR(gckHARDWARE_WaitLink(
+ hardware,
+ NULL,
+ offset,
+ &waitLinkBytes,
+ NULL,
+ NULL
+ ));
+
+ /* Is there enough space in the current command queue? */
+ if (bytes < waitLinkBytes)
+ {
+ /* No, create a new one. */
+ gcmkONERROR(_NewQueue(Command));
+
+ /* Get the new current offset. */
+ offset = Command->offset;
+
+ /* Recompute the number of bytes in the new kernel command queue. */
+ bytes = COMMAND_QUEUE_SIZE - offset;
+ gcmkASSERT(bytes >= waitLinkBytes);
+ }
+
+ /* Compute the location if WAIT/LINK command sequence. */
+ waitLinkPhysical = (u8 *) Command->physical + offset;
+ waitLinkLogical = (u8 *) Command->logical + offset;
+
+ /* Determine the location to jump to for the command buffer being
+ ** scheduled. */
+ if (Command->newQueue)
+ {
+ /* New command queue, jump to the beginning of it. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ exitPhysical = Command->physical;
+#endif
+ exitLogical = Command->logical;
+ exitBytes = Command->offset + waitLinkBytes;
+ }
+ else
+ {
+ /* Still within the preexisting command queue, jump to the new
+ WAIT/LINK command sequence. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ exitPhysical = waitLinkPhysical;
+#endif
+ exitLogical = waitLinkLogical;
+ exitBytes = waitLinkBytes;
+ }
+
+ /* Add a new WAIT/LINK command sequence. When the command buffer which is
+ currently being scheduled is fully executed by the GPU, the FE will
+ jump to this WAIT/LINK sequence. */
+ gcmkONERROR(gckHARDWARE_WaitLink(
+ hardware,
+ waitLinkLogical,
+ offset,
+ &waitLinkBytes,
+ &waitOffset,
+ &waitSize
+ ));
+
+ /* Compute the location if WAIT command. */
+ waitPhysical = (u8 *) waitLinkPhysical + waitOffset;
+ waitLogical = (u8 *) waitLinkLogical + waitOffset;
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the command queue cache. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ Command->kernelProcessID,
+ NULL,
+ exitPhysical,
+ exitLogical,
+ exitBytes
+ ));
+#endif
+
+ /* Determine the location of the LINK command in the command buffer. */
+ commandBufferLink
+ = (u8 *) commandBufferObject->logical
+ + commandBufferObject->offset;
+
+ /* Generate a LINK from the end of the command buffer being scheduled
+ back to the kernel command queue. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ commandBufferLink,
+ exitLogical,
+ exitBytes,
+ &linkBytes
+ ));
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the command buffer cache. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ ProcessID,
+ NULL,
+ commandBufferPhysical,
+ commandBufferLogical,
+ commandBufferSize
+ ));
+#endif
+
+ /* Generate a LINK from the previous WAIT/LINK command sequence to the
+ entry determined above (either the context or the command buffer).
+ This LINK replaces the WAIT instruction from the previous WAIT/LINK
+ pair, therefore we use WAIT metrics for generation of this LINK.
+ This action will execute the entire sequence. */
+ gcmkONERROR(gckHARDWARE_Link(
+ hardware,
+ Command->waitLogical,
+ entryLogical,
+ entryBytes,
+ &Command->waitSize
+ ));
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache for the link. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ Command->kernelProcessID,
+ NULL,
+ Command->waitPhysical,
+ Command->waitLogical,
+ Command->waitSize
+ ));
+#endif
+
+ gcmkDUMPCOMMAND(
+ Command->os,
+ Command->waitLogical,
+ Command->waitSize,
+ gceDUMP_BUFFER_LINK,
+ gcvFALSE
+ );
+
+ gcmkDUMPCOMMAND(
+ Command->os,
+ contextDumpLogical,
+ contextDumpBytes,
+ gceDUMP_BUFFER_CONTEXT,
+ gcvFALSE
+ );
+
+ gcmkDUMPCOMMAND(
+ Command->os,
+ bufferDumpLogical,
+ bufferDumpBytes,
+ gceDUMP_BUFFER_USER,
+ gcvFALSE
+ );
+
+ gcmkDUMPCOMMAND(
+ Command->os,
+ waitLinkLogical,
+ waitLinkBytes,
+ gceDUMP_BUFFER_WAITLINK,
+ gcvFALSE
+ );
+
+ /* Update the current pipe. */
+ Command->pipeSelect = commandBufferObject->exitPipe;
+
+ /* Update command queue offset. */
+ Command->offset += waitLinkBytes;
+ Command->newQueue = gcvFALSE;
+
+ /* Update address of last WAIT. */
+ Command->waitPhysical = waitPhysical;
+ Command->waitLogical = waitLogical;
+ Command->waitSize = waitSize;
+
+ /* Update queue tail pointer. */
+ gcmkONERROR(gckHARDWARE_UpdateQueueTail(
+ hardware, Command->logical, Command->offset
+ ));
+
+#if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
+ gcmkPRINT("@[kernel.commit]");
+#endif
+#endif /* gcdNULL_DRIVER */
+
+ /* Release the context switching mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
+ contextAcquired = gcvFALSE;
+
+ /* Release the command queue. */
+ gcmkONERROR(gckCOMMAND_ExitCommit(Command, gcvFALSE));
+ commitEntered = gcvFALSE;
+
+ /* Loop while there are records in the queue. */
+ while (EventQueue != NULL)
+ {
+ if (NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ /* Point to stack record. */
+ eventRecord = &_eventRecord;
+
+ /* Copy the data from the client. */
+ if (copy_from_user(eventRecord, EventQueue, sizeof(gcsQUEUE)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ else
+ {
+ /* Map record into kernel memory. */
+ gcmkONERROR(gckOS_MapUserPointer(Command->os,
+ EventQueue,
+ sizeof(gcsQUEUE),
+ &pointer));
+
+ eventRecord = pointer;
+ }
+
+ /* Append event record to event queue. */
+ gcmkONERROR(gckEVENT_AddList(
+ Command->kernel->eventObj, &eventRecord->iface, gcvKERNEL_PIXEL, gcvTRUE
+ ));
+
+ /* Next record in the queue. */
+ nextEventRecord = eventRecord->next;
+
+ if (!NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ /* Unmap record from kernel memory. */
+ gcmkONERROR(gckOS_UnmapUserPointer(
+ Command->os, EventQueue, sizeof(gcsQUEUE), (void **) eventRecord
+ ));
+
+ eventRecord = NULL;
+ }
+
+ EventQueue = nextEventRecord;
+ }
+
+ if (Command->kernel->eventObj->queueHead == NULL)
+ {
+ /* Commit done event by which work thread knows all jobs done. */
+ gcmkVERIFY_OK(
+ gckEVENT_CommitDone(Command->kernel->eventObj, gcvKERNEL_PIXEL));
+ }
+
+ /* Submit events. */
+ gcmkONERROR(gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE));
+
+ /* Unmap the command buffer pointer. */
+ if (commandBufferMapped)
+ {
+ gcmkONERROR(gckOS_UnmapUserPointer(
+ Command->os,
+ CommandBuffer,
+ sizeof(struct _gcoCMDBUF),
+ commandBufferObject
+ ));
+
+ commandBufferMapped = gcvFALSE;
+ }
+
+ /* Return status. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ if ((eventRecord != NULL) && !NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ /* Roll back. */
+ gcmkVERIFY_OK(gckOS_UnmapUserPointer(
+ Command->os,
+ EventQueue,
+ sizeof(gcsQUEUE),
+ (void **) eventRecord
+ ));
+ }
+
+ if (contextAcquired)
+ {
+ /* Release the context switching mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
+ }
+
+ if (commitEntered)
+ {
+ /* Release the command queue mutex. */
+ gcmkVERIFY_OK(gckCOMMAND_ExitCommit(Command, gcvFALSE));
+ }
+
+ /* Unmap the command buffer pointer. */
+ if (commandBufferMapped)
+ {
+ gcmkVERIFY_OK(gckOS_UnmapUserPointer(
+ Command->os,
+ CommandBuffer,
+ sizeof(struct _gcoCMDBUF),
+ commandBufferObject
+ ));
+ }
+
+ /* Return status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Reserve
+**
+** Reserve space in the command queue. Also acquire the command queue mutex.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object.
+**
+** size_t RequestedBytes
+** Number of bytes previously reserved.
+**
+** OUTPUT:
+**
+** void ** Buffer
+** Pointer to a variable that will receive the address of the reserved
+** space.
+**
+** size_t * BufferSize
+** Pointer to a variable that will receive the number of bytes
+** available in the command queue.
+*/
+gceSTATUS
+gckCOMMAND_Reserve(
+ IN gckCOMMAND Command,
+ IN size_t RequestedBytes,
+ OUT void **Buffer,
+ OUT size_t * BufferSize
+ )
+{
+ gceSTATUS status;
+ size_t bytes;
+ size_t requiredBytes;
+ u32 requestedAligned;
+
+ gcmkHEADER_ARG("Command=0x%x RequestedBytes=%lu", Command, RequestedBytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ /* Compute aligned number of reuested bytes. */
+ requestedAligned = gcmALIGN(RequestedBytes, Command->alignment);
+
+ /* Another WAIT/LINK command sequence will have to be appended after
+ the requested area being reserved. Compute the number of bytes
+ required for WAIT/LINK at the location after the reserved area. */
+ gcmkONERROR(gckHARDWARE_WaitLink(
+ Command->kernel->hardware,
+ NULL,
+ Command->offset + requestedAligned,
+ &requiredBytes,
+ NULL,
+ NULL
+ ));
+
+ /* Compute total number of bytes required. */
+ requiredBytes += requestedAligned;
+
+ /* Compute number of bytes available in command queue. */
+ bytes = COMMAND_QUEUE_SIZE - Command->offset;
+
+ /* Is there enough space in the current command queue? */
+ if (bytes < requiredBytes)
+ {
+ /* Create a new command queue. */
+ gcmkONERROR(_NewQueue(Command));
+
+ /* Recompute the number of bytes in the new kernel command queue. */
+ bytes = COMMAND_QUEUE_SIZE - Command->offset;
+
+ /* Still not enough space? */
+ if (bytes < requiredBytes)
+ {
+ /* Rare case, not enough room in command queue. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+ }
+
+ /* Return pointer to empty slot command queue. */
+ *Buffer = (u8 *) Command->logical + Command->offset;
+
+ /* Return number of bytes left in command queue. */
+ *BufferSize = bytes;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Buffer=0x%x *BufferSize=%lu", *Buffer, *BufferSize);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Execute
+**
+** Execute a previously reserved command queue by appending a WAIT/LINK command
+** sequence after it and modifying the last WAIT into a LINK command. The
+** command FIFO mutex will be released whether this function succeeds or not.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object.
+**
+** size_t RequestedBytes
+** Number of bytes previously reserved.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_Execute(
+ IN gckCOMMAND Command,
+ IN size_t RequestedBytes
+ )
+{
+ gceSTATUS status;
+
+ gctPHYS_ADDR waitLinkPhysical;
+ u8 *waitLinkLogical;
+ u32 waitLinkOffset;
+ size_t waitLinkBytes;
+
+ gctPHYS_ADDR waitPhysical;
+ void *waitLogical;
+ u32 waitOffset;
+ size_t waitBytes;
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ gctPHYS_ADDR execPhysical;
+#endif
+ void *execLogical;
+ size_t execBytes;
+
+ gcmkHEADER_ARG("Command=0x%x RequestedBytes=%lu", Command, RequestedBytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ /* Compute offset for WAIT/LINK. */
+ waitLinkOffset = Command->offset + RequestedBytes;
+
+ /* Compute number of bytes left in command queue. */
+ waitLinkBytes = COMMAND_QUEUE_SIZE - waitLinkOffset;
+
+ /* Compute the location if WAIT/LINK command sequence. */
+ waitLinkPhysical = (u8 *) Command->physical + waitLinkOffset;
+ waitLinkLogical = (u8 *) Command->logical + waitLinkOffset;
+
+ /* Append WAIT/LINK in command queue. */
+ gcmkONERROR(gckHARDWARE_WaitLink(
+ Command->kernel->hardware,
+ waitLinkLogical,
+ waitLinkOffset,
+ &waitLinkBytes,
+ &waitOffset,
+ &waitBytes
+ ));
+
+ /* Compute the location if WAIT command. */
+ waitPhysical = (u8 *) waitLinkPhysical + waitOffset;
+ waitLogical = waitLinkLogical + waitOffset;
+
+ /* Determine the location to jump to for the command buffer being
+ ** scheduled. */
+ if (Command->newQueue)
+ {
+ /* New command queue, jump to the beginning of it. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ execPhysical = Command->physical;
+#endif
+ execLogical = Command->logical;
+ execBytes = waitLinkOffset + waitLinkBytes;
+ }
+ else
+ {
+ /* Still within the preexisting command queue, jump directly to the
+ reserved area. */
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ execPhysical = (u8 *) Command->physical + Command->offset;
+#endif
+ execLogical = (u8 *) Command->logical + Command->offset;
+ execBytes = RequestedBytes + waitLinkBytes;
+ }
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ Command->kernelProcessID,
+ NULL,
+ execPhysical,
+ execLogical,
+ execBytes
+ ));
+#endif
+
+ /* Convert the last WAIT into a LINK. */
+ gcmkONERROR(gckHARDWARE_Link(
+ Command->kernel->hardware,
+ Command->waitLogical,
+ execLogical,
+ execBytes,
+ &Command->waitSize
+ ));
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache. */
+ gcmkONERROR(gckOS_CacheClean(
+ Command->os,
+ Command->kernelProcessID,
+ NULL,
+ Command->waitPhysical,
+ Command->waitLogical,
+ Command->waitSize
+ ));
+#endif
+
+ gcmkDUMPCOMMAND(
+ Command->os,
+ Command->waitLogical,
+ Command->waitSize,
+ gceDUMP_BUFFER_LINK,
+ gcvFALSE
+ );
+
+ gcmkDUMPCOMMAND(
+ Command->os,
+ execLogical,
+ execBytes,
+ gceDUMP_BUFFER_KERNEL,
+ gcvFALSE
+ );
+
+ /* Update the pointer to the last WAIT. */
+ Command->waitPhysical = waitPhysical;
+ Command->waitLogical = waitLogical;
+ Command->waitSize = waitBytes;
+
+ /* Update the command queue. */
+ Command->offset += RequestedBytes + waitLinkBytes;
+ Command->newQueue = gcvFALSE;
+
+ /* Update queue tail pointer. */
+ gcmkONERROR(gckHARDWARE_UpdateQueueTail(
+ Command->kernel->hardware, Command->logical, Command->offset
+ ));
+
+#if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
+ gcmkPRINT("@[kernel.execute]");
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Stall
+**
+** The calling thread will be suspended until the command queue has been
+** completed.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to an gckCOMMAND object.
+**
+** int FromPower
+** Determines whether the call originates from inside the power
+** management or not.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_Stall(
+ IN gckCOMMAND Command,
+ IN int FromPower
+ )
+{
+#if gcdNULL_DRIVER
+ /* Do nothing with infinite hardware. */
+ return gcvSTATUS_OK;
+#else
+ gckOS os;
+ gckHARDWARE hardware;
+ gckEVENT eventObject;
+ gceSTATUS status;
+ gctSIGNAL signal = NULL;
+ unsigned int timer = 0;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ /* Extract the gckOS object pointer. */
+ os = Command->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Extract the gckHARDWARE object pointer. */
+ hardware = Command->kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ /* Extract the gckEVENT object pointer. */
+ eventObject = Command->kernel->eventObj;
+ gcmkVERIFY_OBJECT(eventObject, gcvOBJ_EVENT);
+
+ /* Allocate the signal. */
+ gcmkONERROR(gckOS_CreateSignal(os, gcvTRUE, &signal));
+
+ /* Append the EVENT command to trigger the signal. */
+ gcmkONERROR(gckEVENT_Signal(eventObject, signal, gcvKERNEL_PIXEL));
+
+ /* Submit the event queue. */
+ gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower));
+
+#if gcdDUMP_COMMAND && !gcdSIMPLE_COMMAND_DUMP
+ gcmkPRINT("@[kernel.stall]");
+#endif
+
+ if (status == gcvSTATUS_CHIP_NOT_READY)
+ {
+ /* Error. */
+ goto OnError;
+ }
+
+ do
+ {
+ /* Wait for the signal. */
+ status = gckOS_WaitSignal(os, signal, gcdGPU_ADVANCETIMER);
+
+ if (status == gcvSTATUS_TIMEOUT)
+ {
+#if gcmIS_DEBUG(gcdDEBUG_CODE)
+ u32 idle;
+
+ /* Read idle register. */
+ gcmkVERIFY_OK(gckHARDWARE_GetIdle(
+ hardware, gcvFALSE, &idle
+ ));
+
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): idle=%08x",
+ __FUNCTION__, __LINE__, idle
+ );
+
+ gcmkONERROR(gckOS_MemoryBarrier(os, NULL));
+#endif
+ /* Advance timer. */
+ timer += gcdGPU_ADVANCETIMER;
+ }
+ else if (status == gcvSTATUS_INTERRUPTED)
+ {
+ gcmkONERROR(gcvSTATUS_INTERRUPTED);
+ }
+
+ }
+ while (gcmIS_ERROR(status)
+#if gcdGPU_TIMEOUT
+ && (timer < Command->kernel->timeOut)
+#endif
+ );
+
+ /* Bail out on timeout. */
+ if (gcmIS_ERROR(status))
+ {
+ /* Broadcast the stuck GPU. */
+ gcmkONERROR(gckOS_Broadcast(
+ os, hardware, gcvBROADCAST_GPU_STUCK
+ ));
+
+ gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING);
+ }
+
+ /* Delete the signal. */
+ gcmkVERIFY_OK(gckOS_DestroySignal(os, signal));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (signal != NULL)
+ {
+ /* Free the signal. */
+ gcmkVERIFY_OK(gckOS_DestroySignal(os, signal));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+#endif
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Attach
+**
+** Attach user process.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to a gckCOMMAND object.
+**
+** u32 ProcessID
+** Current process ID.
+**
+** OUTPUT:
+**
+** gckCONTEXT * Context
+** Pointer to a variable that will receive a pointer to a new
+** gckCONTEXT object.
+**
+** size_t * StateCount
+** Pointer to a variable that will receive the number of states
+** in the context buffer.
+*/
+gceSTATUS
+gckCOMMAND_Attach(
+ IN gckCOMMAND Command,
+ OUT gckCONTEXT * Context,
+ OUT size_t * StateCount,
+ IN u32 ProcessID
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Command=0x%x", Command);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ /* Acquire the context switching mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(
+ Command->os, Command->mutexContext, gcvINFINITE
+ ));
+ acquired = gcvTRUE;
+
+ /* Construct a gckCONTEXT object. */
+ gcmkONERROR(gckCONTEXT_Construct(
+ Command->os,
+ Command->kernel->hardware,
+ ProcessID,
+ Context
+ ));
+
+ /* Return the number of states in the context. */
+ * StateCount = (* Context)->stateCount;
+
+ /* Release the context switching mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
+ acquired = gcvFALSE;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Context=0x%x", *Context);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Release mutex. */
+ if (acquired)
+ {
+ /* Release the context switching mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
+ acquired = gcvFALSE;
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckCOMMAND_Detach
+**
+** Detach user process.
+**
+** INPUT:
+**
+** gckCOMMAND Command
+** Pointer to a gckCOMMAND object.
+**
+** gckCONTEXT Context
+** Pointer to a gckCONTEXT object to be destroyed.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCOMMAND_Detach(
+ IN gckCOMMAND Command,
+ IN gckCONTEXT Context
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Command=0x%x Context=0x%x", Command, Context);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
+
+ /* Acquire the context switching mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(
+ Command->os, Command->mutexContext, gcvINFINITE
+ ));
+ acquired = gcvTRUE;
+
+ /* Construct a gckCONTEXT object. */
+ gcmkONERROR(gckCONTEXT_Destroy(Context));
+
+ /* Release the context switching mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
+ acquired = gcvFALSE;
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Release mutex. */
+ if (acquired)
+ {
+ /* Release the context switching mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext));
+ acquired = gcvFALSE;
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_context.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_context.c
new file mode 100644
index 0000000..f108b22
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_context.c
@@ -0,0 +1,1675 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+#include "gc_hal_kernel_context.h"
+
+/******************************************************************************\
+******************************** Debugging Macro *******************************
+\******************************************************************************/
+
+/* Zone used for header/footer. */
+#define _GC_OBJ_ZONE gcvZONE_HARDWARE
+
+
+/******************************************************************************\
+************************** Context State Buffer Helpers ************************
+\******************************************************************************/
+
+#define _STATE(reg) \
+ _State(\
+ Context, index, \
+ reg ## _Address >> 2, \
+ reg ## _ResetValue, \
+ reg ## _Count, \
+ gcvFALSE, gcvFALSE \
+ )
+
+#define _STATE_COUNT(reg, count) \
+ _State(\
+ Context, index, \
+ reg ## _Address >> 2, \
+ reg ## _ResetValue, \
+ count, \
+ gcvFALSE, gcvFALSE \
+ )
+
+#define _STATE_COUNT_OFFSET(reg, offset, count) \
+ _State(\
+ Context, index, \
+ (reg ## _Address >> 2) + offset, \
+ reg ## _ResetValue, \
+ count, \
+ gcvFALSE, gcvFALSE \
+ )
+
+#define _STATE_MIRROR_COUNT(reg, mirror, count) \
+ _StateMirror(\
+ Context, \
+ reg ## _Address >> 2, \
+ count, \
+ mirror ## _Address >> 2 \
+ )
+
+#define _STATE_HINT(reg) \
+ _State(\
+ Context, index, \
+ reg ## _Address >> 2, \
+ reg ## _ResetValue, \
+ reg ## _Count, \
+ gcvFALSE, gcvTRUE \
+ )
+
+#define _STATE_HINT_BLOCK(reg, block, count) \
+ _State(\
+ Context, index, \
+ (reg ## _Address >> 2) + (block << reg ## _BLK), \
+ reg ## _ResetValue, \
+ count, \
+ gcvFALSE, gcvTRUE \
+ )
+
+#define _STATE_X(reg) \
+ _State(\
+ Context, index, \
+ reg ## _Address >> 2, \
+ reg ## _ResetValue, \
+ reg ## _Count, \
+ gcvTRUE, gcvFALSE \
+ )
+
+#define _CLOSE_RANGE() \
+ _TerminateStateBlock(Context, index)
+
+
+/******************************************************************************\
+*********************** Support Functions and Definitions **********************
+\******************************************************************************/
+
+#define gcdSTATE_MASK \
+ (gcmSETFIELD(0, 31:27, 0x03 | 0xC0FFEE ))
+
+#if !VIVANTE_NO_3D
+static size_t
+_TerminateStateBlock(
+ IN gckCONTEXT Context,
+ IN size_t Index
+ )
+{
+ u32 *buffer;
+ size_t align;
+
+ /* Determine if we need alignment. */
+ align = (Index & 1) ? 1 : 0;
+
+ /* Address correct index. */
+ buffer = (Context->buffer == NULL)
+ ? NULL
+ : Context->buffer->logical;
+
+ /* Flush the current state block; make sure no pairing with the states
+ to follow happens. */
+ if (align && (buffer != NULL))
+ {
+ buffer[Index] = 0xDEADDEAD;
+ }
+
+ /* Reset last address. */
+ Context->lastAddress = ~0U;
+
+ /* Return alignment requirement. */
+ return align;
+}
+#endif
+
+
+static size_t
+_FlushPipe(
+ IN gckCONTEXT Context,
+ IN size_t Index,
+ IN gcePIPE_SELECT Pipe
+ )
+{
+ if (Context->buffer != NULL)
+ {
+ u32 *buffer;
+
+ /* Address correct index. */
+ buffer = Context->buffer->logical + Index;
+
+ /* Flush the current pipe. */
+ *buffer++
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E03);
+
+ *buffer++
+ = (Pipe == gcvPIPE_2D)
+ ? gcmSETFIELD(0, 3:3, 0x1 )
+ : gcmSETFIELD(0, 0:0, 0x1 )
+ | gcmSETFIELD(0, 1:1, 0x1 )
+ | gcmSETFIELD(0, 2:2, 0x1 );
+
+ /* Semaphore from FE to PE. */
+ *buffer++
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ *buffer++
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* Stall from FE to PE. */
+ *buffer++
+ = gcmSETFIELD(0, 31:27, 0x09 );
+
+ *buffer
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+ }
+
+ /* Flushing 3D pipe takes 6 slots. */
+ return 6;
+}
+
+static size_t
+_SemaphoreStall(
+ IN gckCONTEXT Context,
+ IN size_t Index
+ )
+{
+ if (Context->buffer != NULL)
+ {
+ u32 *buffer;
+
+ /* Address correct index. */
+ buffer = Context->buffer->logical + Index;
+
+ /* Semaphore from FE to PE. */
+ *buffer++
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ *buffer++
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* Stall from FE to PE. */
+ *buffer++
+ = gcmSETFIELD(0, 31:27, 0x09 );
+
+ *buffer
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+ }
+
+ /* Semaphore/stall takes 4 slots. */
+ return 4;
+}
+
+static size_t
+_SwitchPipe(
+ IN gckCONTEXT Context,
+ IN size_t Index,
+ IN gcePIPE_SELECT Pipe
+ )
+{
+ if (Context->buffer != NULL)
+ {
+ u32 *buffer;
+
+ /* Address correct index. */
+ buffer = Context->buffer->logical + Index;
+
+ /* LoadState(AQPipeSelect, 1), pipe. */
+ *buffer++
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0E00)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ *buffer
+ = (Pipe == gcvPIPE_2D)
+ ? 0x1
+ : 0x0;
+ }
+
+ return 2;
+}
+
+#if !VIVANTE_NO_3D
+static size_t
+_State(
+ IN gckCONTEXT Context,
+ IN size_t Index,
+ IN u32 Address,
+ IN u32 Value,
+ IN size_t Size,
+ IN int FixedPoint,
+ IN int Hinted
+ )
+{
+ u32 *buffer;
+ size_t align, i;
+
+ /* Determine if we need alignment. */
+ align = (Index & 1) ? 1 : 0;
+
+ /* Address correct index. */
+ buffer = (Context->buffer == NULL)
+ ? NULL
+ : Context->buffer->logical;
+
+ if ((buffer == NULL) && (Address + Size > Context->stateCount))
+ {
+ /* Determine maximum state. */
+ Context->stateCount = Address + Size;
+ }
+
+ /* Do we need a new entry? */
+ if ((Address != Context->lastAddress) || (FixedPoint != Context->lastFixed))
+ {
+ if (buffer != NULL)
+ {
+ if (align)
+ {
+ /* Add filler. */
+ buffer[Index++] = 0xDEADDEAD;
+ }
+
+ /* LoadState(Address, Count). */
+ gcmkASSERT((Index & 1) == 0);
+
+ if (FixedPoint)
+ {
+ buffer[Index]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 26:26, 0x1 )
+ | gcmSETFIELD(0, 25:16, Size)
+ | gcmSETFIELD(0, 15:0, Address);
+ }
+ else
+ {
+ buffer[Index]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 26:26, 0x0 )
+ | gcmSETFIELD(0, 25:16, Size)
+ | gcmSETFIELD(0, 15:0, Address);
+ }
+
+ /* Walk all the states. */
+ for (i = 0; i < Size; i += 1)
+ {
+ /* Set state to uninitialized value. */
+ buffer[Index + 1 + i] = Value;
+
+ /* Set index in state mapping table. */
+ Context->map[Address + i].index = Index + 1 + i;
+
+#if gcdSECURE_USER
+ /* Save hint. */
+ if (Context->hint != NULL)
+ {
+ Context->hint[Address + i] = Hinted;
+ }
+#endif
+ }
+ }
+
+ /* Save information for this LoadState. */
+ Context->lastIndex = Index;
+ Context->lastAddress = Address + Size;
+ Context->lastSize = Size;
+ Context->lastFixed = FixedPoint;
+
+ /* Return size for load state. */
+ return align + 1 + Size;
+ }
+
+ /* Append this state to the previous one. */
+ if (buffer != NULL)
+ {
+ /* Update last load state. */
+ buffer[Context->lastIndex] =
+ gcmSETFIELD(buffer[Context->lastIndex], 25:16, Context->lastSize + Size);
+
+ /* Walk all the states. */
+ for (i = 0; i < Size; i += 1)
+ {
+ /* Set state to uninitialized value. */
+ buffer[Index + i] = Value;
+
+ /* Set index in state mapping table. */
+ Context->map[Address + i].index = Index + i;
+
+#if gcdSECURE_USER
+ /* Save hint. */
+ if (Context->hint != NULL)
+ {
+ Context->hint[Address + i] = Hinted;
+ }
+#endif
+ }
+ }
+
+ /* Update last address and size. */
+ Context->lastAddress += Size;
+ Context->lastSize += Size;
+
+ /* Return number of slots required. */
+ return Size;
+}
+
+static size_t
+_StateMirror(
+ IN gckCONTEXT Context,
+ IN u32 Address,
+ IN size_t Size,
+ IN u32 AddressMirror
+ )
+{
+ size_t i;
+
+ /* Process when buffer is set. */
+ if (Context->buffer != NULL)
+ {
+ /* Walk all states. */
+ for (i = 0; i < Size; i++)
+ {
+ /* Copy the mapping address. */
+ Context->map[Address + i].index =
+ Context->map[AddressMirror + i].index;
+ }
+ }
+
+ /* Return the number of required maps. */
+ return Size;
+}
+#endif
+
+static gceSTATUS
+_InitializeContextBuffer(
+ IN gckCONTEXT Context
+ )
+{
+ u32 *buffer;
+ size_t index;
+
+#if !VIVANTE_NO_3D
+ unsigned int i;
+ unsigned int vertexUniforms, fragmentUniforms;
+ unsigned int fe2vsCount;
+#endif
+
+ /* Reset the buffer index. */
+ index = 0;
+
+ /* Reset the last state address. */
+ Context->lastAddress = ~0U;
+
+ /* Get the buffer pointer. */
+ buffer = (Context->buffer == NULL)
+ ? NULL
+ : Context->buffer->logical;
+
+
+ /**************************************************************************/
+ /* Build 2D states. *******************************************************/
+
+
+#if !VIVANTE_NO_3D
+ /**************************************************************************/
+ /* Build 3D states. *******************************************************/
+
+ /* Query shader support. */
+ gcmkVERIFY_OK(gckHARDWARE_QueryShaderCaps(
+ Context->hardware, &vertexUniforms, &fragmentUniforms, NULL));
+
+ /* Store the 3D entry index. */
+ Context->entryOffset3D = index * sizeof(u32);
+
+ /* Flush 2D pipe. */
+ index += _FlushPipe(Context, index, gcvPIPE_2D);
+
+ /* Switch to 3D pipe. */
+ index += _SwitchPipe(Context, index, gcvPIPE_3D);
+
+ /* Current context pointer. */
+#if gcdDEBUG && 1
+ index += _State(Context, index, 0x03850 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+#endif
+
+ /* Global states. */
+ index += _State(Context, index, 0x03814 >> 2, 0x00000001, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x03818 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0381C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x03820 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x03828 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0382C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x03834 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x03838 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0384C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+
+ /* Front End states. */
+ fe2vsCount = 12;
+ if (gcmGETFIELD(Context->hardware->identity.chipMinorFeatures1, 23:23))
+ {
+ fe2vsCount = 16;
+ }
+ index += _State(Context, index, 0x00600 >> 2, 0x00000000, fe2vsCount, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+
+ index += _State(Context, index, 0x00644 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x00648 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0064C >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x00650 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00680 >> 2, 0x00000000, 8, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x006A0 >> 2, 0x00000000, 8, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00670 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00678 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0067C >> 2, 0xFFFFFFFF, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x006C0 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00700 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00740 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00780 >> 2, 0x3F800000, 16, gcvFALSE, gcvFALSE);
+
+ /* Vertex Shader states. */
+ index += _State(Context, index, 0x00800 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00804 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00808 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0080C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00810 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00820 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00830 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00838 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ if (Context->hardware->identity.instructionCount <= 256)
+ {
+ index += _State(Context, index, 0x04000 >> 2, 0x00000000, 1024, gcvFALSE, gcvFALSE);
+ }
+
+ index += _CLOSE_RANGE();
+ index += _State(Context, index, 0x05000 >> 2, 0x00000000, vertexUniforms * 4, gcvFALSE, gcvFALSE);
+
+ /* Primitive Assembly states. */
+ index += _State(Context, index, 0x00A00 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00A04 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00A08 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A0C >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00A10 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00A14 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A18 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A1C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A28 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A2C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A30 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A40 >> 2, 0x00000000, 10, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A34 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A38 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A3C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A80 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00A84 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+
+ /* Setup states. */
+ index += _State(Context, index, 0x00C00 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00C04 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00C08 >> 2, 0x45000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00C0C >> 2, 0x45000000, 1, gcvTRUE, gcvFALSE);
+ index += _State(Context, index, 0x00C10 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00C14 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00C18 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00C1C >> 2, 0x42000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00C20 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00C24 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+
+ /* Raster states. */
+ index += _State(Context, index, 0x00E00 >> 2, 0x00000001, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00E10 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00E04 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00E40 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00E08 >> 2, 0x00000031, 1, gcvFALSE, gcvFALSE);
+
+ /* Pixel Shader states. */
+ index += _State(Context, index, 0x01000 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01004 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01008 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0100C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01010 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01018 >> 2, 0x01000000, 1, gcvFALSE, gcvFALSE);
+ if (Context->hardware->identity.instructionCount <= 256)
+ {
+ index += _State(Context, index, 0x06000 >> 2, 0x00000000, 1024, gcvFALSE, gcvFALSE);
+ }
+
+ index += _CLOSE_RANGE();
+ index += _State(Context, index, 0x07000 >> 2, 0x00000000, fragmentUniforms * 4, gcvFALSE, gcvFALSE);
+
+ /* Texture states. */
+ index += _State(Context, index, 0x02000 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x02040 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x02080 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x020C0 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x02100 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x02140 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x02180 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x021C0 >> 2, 0x00321000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x02200 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x02240 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, (0x02400 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02440 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02480 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x024C0 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02500 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02540 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02580 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x025C0 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02600 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02640 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02680 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x026C0 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02700 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, (0x02740 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE);
+ index += _CLOSE_RANGE();
+
+ if (gcmGETFIELD(Context->hardware->identity.chipMinorFeatures2, 11:11))
+ {
+ unsigned int texBlockCount;
+
+ /* New texture block. */
+ index += _State(Context, index, 0x10000 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10080 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10100 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10180 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10200 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10280 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10300 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10380 >> 2, 0x00321000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10400 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x10480 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE);
+
+ if (gcmGETFIELD(Context->hardware->identity.chipMinorFeatures2, 15:15))
+ {
+ index += _State(Context, index, 0x12000 >> 2, 0x00000000, 256, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x12400 >> 2, 0x00000000, 256, gcvFALSE, gcvFALSE);
+ }
+
+ if ((Context->hardware->identity.chipModel == gcv2000)
+ && (Context->hardware->identity.chipRevision == 0x5108))
+ {
+ texBlockCount = 12;
+ }
+ else
+ {
+ texBlockCount = ((512) >> (4));
+ }
+ for (i = 0; i < texBlockCount; i += 1)
+ {
+ index += _State(Context, index, (0x10800 >> 2) + (i << 4), 0x00000000, 14, gcvFALSE, gcvTRUE);
+ }
+ }
+
+ /* YUV. */
+ index += _State(Context, index, 0x01678 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0167C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01680 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x01684 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01688 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x0168C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01690 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x01694 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01698 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x0169C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+
+ /* Thread walker states. */
+ index += _State(Context, index, 0x00900 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00904 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00908 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0090C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00910 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00914 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00918 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0091C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00924 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+
+ if (Context->hardware->identity.instructionCount > 1024)
+ {
+ /* New Shader instruction memory. */
+ index += _State(Context, index, 0x0085C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0101C >> 2, 0x00000100, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x00860 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+
+ for (i = 0;
+ i < Context->hardware->identity.instructionCount << 2;
+ i += 256 << 2
+ )
+ {
+ index += _State(Context, index, (0x20000 >> 2) + i, 0x00000000, 256 << 2, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+ }
+ }
+ else if (Context->hardware->identity.instructionCount > 256)
+ {
+ /* VX instruction memory. */
+ for (i = 0;
+ i < Context->hardware->identity.instructionCount << 2;
+ i += 256 << 2
+ )
+ {
+ index += _State(Context, index, (0x0C000 >> 2) + i, 0x00000000, 256 << 2, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+ }
+
+ _StateMirror(Context, 0x08000 >> 2, Context->hardware->identity.instructionCount << 2 , 0x0C000 >> 2);
+ }
+
+ /* Store the index of the "XD" entry. */
+ Context->entryOffsetXDFrom3D = index * sizeof(u32);
+
+ index += _FlushPipe(Context, index, gcvPIPE_3D);
+
+ /* Pixel Engine states. */
+ index += _State(Context, index, 0x01400 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01404 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01408 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0140C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01414 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01418 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0141C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01420 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01424 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01428 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0142C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01434 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01454 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01458 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x0145C >> 2, 0x00000010, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x014A0 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x014A8 >> 2, 0xFFFFFFFF, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x014AC >> 2, 0xFFFFFFFF, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x014B0 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x014B4 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x014A4 >> 2, 0x000E400C, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01580 >> 2, 0x00000000, 3, gcvFALSE, gcvFALSE);
+
+ /* Composition states. */
+ index += _State(Context, index, 0x03008 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+
+ if (Context->hardware->identity.pixelPipes == 1)
+ {
+ index += _State(Context, index, 0x01430 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x01410 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ }
+ else
+ {
+ index += _State(Context, index, (0x01460 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE);
+
+ index += _State(Context, index, (0x01480 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE);
+
+ for (i = 0; i < 2; i++)
+ {
+ index += _State(Context, index, (0x01500 >> 2) + (i << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE);
+ }
+ }
+
+ /* Resolve states. */
+ index += _State(Context, index, 0x01604 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01608 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x0160C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01610 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x01614 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01620 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01630 >> 2, 0x00000000, 2, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01640 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x0163C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x016A0 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x016B4 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+
+ if (Context->hardware->identity.pixelPipes > 1)
+ {
+ index += _State(Context, index, (0x016C0 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE);
+
+ index += _State(Context, index, (0x016E0 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE);
+
+ index += _State(Context, index, 0x01700 >> 2, 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvFALSE);
+ }
+
+ /* Tile status. */
+ index += _State(Context, index, 0x01654 >> 2, 0x00200000, 1, gcvFALSE, gcvFALSE);
+
+ index += _CLOSE_RANGE();
+ index += _State(Context, index, 0x01658 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x0165C >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x01660 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01664 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x01668 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x0166C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01670 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01674 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x016A4 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x016A8 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01720 >> 2, 0x00000000, 8, gcvFALSE, gcvFALSE);
+ index += _State(Context, index, 0x01740 >> 2, 0x00000000, 8, gcvFALSE, gcvTRUE);
+ index += _State(Context, index, 0x01760 >> 2, 0x00000000, 8, gcvFALSE, gcvFALSE);
+ index += _CLOSE_RANGE();
+
+ /* Semaphore/stall. */
+ index += _SemaphoreStall(Context, index);
+#endif
+
+ /**************************************************************************/
+ /* Link to another address. ***********************************************/
+
+ Context->linkIndex3D = index;
+
+ if (buffer != NULL)
+ {
+ buffer[index + 0]
+ = gcmSETFIELD(0, 31:27, 0x08 )
+ | gcmSETFIELD(0, 15:0, 0);
+
+ buffer[index + 1]
+ = 0;
+ }
+
+ index += 2;
+
+ /* Store the end of the context buffer. */
+ Context->bufferSize = index * sizeof(u32);
+
+
+ /**************************************************************************/
+ /* Pipe switch for the case where neither 2D nor 3D are used. *************/
+
+ /* Store the 3D entry index. */
+ Context->entryOffsetXDFrom2D = index * sizeof(u32);
+
+ /* Flush 2D pipe. */
+ index += _FlushPipe(Context, index, gcvPIPE_2D);
+
+ /* Switch to 3D pipe. */
+ index += _SwitchPipe(Context, index, gcvPIPE_3D);
+
+ /* Store the location of the link. */
+ Context->linkIndexXD = index;
+
+ if (buffer != NULL)
+ {
+ buffer[index + 0]
+ = gcmSETFIELD(0, 31:27, 0x08 )
+ | gcmSETFIELD(0, 15:0, 0);
+
+ buffer[index + 1]
+ = 0;
+ }
+
+ index += 2;
+
+
+ /**************************************************************************/
+ /* Save size for buffer. **************************************************/
+
+ Context->totalSize = index * sizeof(u32);
+
+
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+static gceSTATUS
+_DestroyContext(
+ IN gckCONTEXT Context
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+
+ if (Context != NULL)
+ {
+ gcsCONTEXT_PTR bufferHead;
+
+ /* Free context buffers. */
+ for (bufferHead = Context->buffer; Context->buffer != NULL;)
+ {
+ /* Get a shortcut to the current buffer. */
+ gcsCONTEXT_PTR buffer = Context->buffer;
+
+ /* Get the next buffer. */
+ gcsCONTEXT_PTR next = buffer->next;
+
+ /* Last item? */
+ if (next == bufferHead)
+ {
+ next = NULL;
+ }
+
+ /* Destroy the signal. */
+ if (buffer->signal != NULL)
+ {
+ gcmkONERROR(gckOS_DestroySignal(
+ Context->os, buffer->signal
+ ));
+
+ buffer->signal = NULL;
+ }
+
+ /* Free state delta map. */
+ if (buffer->logical != NULL)
+ {
+ gcmkONERROR(gckOS_FreeContiguous(
+ Context->os,
+ buffer->physical,
+ buffer->logical,
+ Context->totalSize
+ ));
+
+ buffer->logical = NULL;
+ }
+
+ /* Free context buffer. */
+ gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, buffer));
+
+ /* Remove from the list. */
+ Context->buffer = next;
+ }
+
+#if gcdSECURE_USER
+ /* Free the hint array. */
+ if (Context->hint != NULL)
+ {
+ gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->hint));
+ }
+#endif
+ /* Free record array copy. */
+ if (Context->recordArray != NULL)
+ {
+ gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->recordArray));
+ }
+
+ /* Free the state mapping. */
+ if (Context->map != NULL)
+ {
+ gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->map));
+ }
+
+ /* Mark the gckCONTEXT object as unknown. */
+ Context->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckCONTEXT object. */
+ gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context));
+ }
+
+OnError:
+ return status;
+}
+
+
+/******************************************************************************\
+**************************** Context Management API ****************************
+\******************************************************************************/
+
+/******************************************************************************\
+**
+** gckCONTEXT_Construct
+**
+** Construct a new gckCONTEXT object.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** u32 ProcessID
+** Current process ID.
+**
+** gckHARDWARE Hardware
+** Pointer to gckHARDWARE object.
+**
+** OUTPUT:
+**
+** gckCONTEXT * Context
+** Pointer to a variable thet will receive the gckCONTEXT object
+** pointer.
+*/
+gceSTATUS
+gckCONTEXT_Construct(
+ IN gckOS Os,
+ IN gckHARDWARE Hardware,
+ IN u32 ProcessID,
+ OUT gckCONTEXT * Context
+ )
+{
+ gceSTATUS status;
+ gckCONTEXT context = NULL;
+ size_t allocationSize;
+ unsigned int i;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Os=0x%08X Hardware=0x%08X", Os, Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Context != NULL);
+
+
+ /**************************************************************************/
+ /* Allocate and initialize basic fields of gckCONTEXT. ********************/
+
+ /* The context object size. */
+ allocationSize = sizeof(struct _gckCONTEXT);
+
+ /* Allocate the object. */
+ gcmkONERROR(gckOS_Allocate(
+ Os, allocationSize, &pointer
+ ));
+
+ context = pointer;
+
+ /* Reset the entire object. */
+ gcmkONERROR(gckOS_ZeroMemory(context, allocationSize));
+
+ /* Initialize the gckCONTEXT object. */
+ context->object.type = gcvOBJ_CONTEXT;
+ context->os = Os;
+ context->hardware = Hardware;
+
+
+#if VIVANTE_NO_3D
+ context->entryPipe = gcvPIPE_2D;
+ context->exitPipe = gcvPIPE_2D;
+#elif gcdCMD_NO_2D_CONTEXT
+ context->entryPipe = gcvPIPE_3D;
+ context->exitPipe = gcvPIPE_3D;
+#else
+ context->entryPipe
+ = gcmGETFIELD(context->hardware->identity.chipFeatures, 9:9)
+ ? gcvPIPE_2D
+ : gcvPIPE_3D;
+ context->exitPipe = gcvPIPE_3D;
+#endif
+
+ /* Get the command buffer requirements. */
+ gcmkONERROR(gckHARDWARE_QueryCommandBuffer(
+ Hardware,
+ &context->alignment,
+ &context->reservedHead,
+ &context->reservedTail
+ ));
+
+ /* Mark the context as dirty to force loading of the entire state table
+ the first time. */
+ context->dirty = gcvTRUE;
+
+
+ /**************************************************************************/
+ /* Get the size of the context buffer. ************************************/
+
+ gcmkONERROR(_InitializeContextBuffer(context));
+
+
+ /**************************************************************************/
+ /* Compute the size of the record array. **********************************/
+
+ context->recordArraySize
+ = sizeof(gcsSTATE_DELTA_RECORD) * context->stateCount;
+
+
+ if (context->stateCount > 0)
+ {
+ /**************************************************************************/
+ /* Allocate and reset the state mapping table. ****************************/
+
+ /* Allocate the state mapping table. */
+ gcmkONERROR(gckOS_Allocate(
+ Os,
+ sizeof(gcsSTATE_MAP) * context->stateCount,
+ &pointer
+ ));
+
+ context->map = pointer;
+
+ /* Zero the state mapping table. */
+ gcmkONERROR(gckOS_ZeroMemory(
+ context->map, sizeof(gcsSTATE_MAP) * context->stateCount
+ ));
+
+
+ /**************************************************************************/
+ /* Allocate the hint array. ***********************************************/
+
+#if gcdSECURE_USER
+ /* Allocate hints. */
+ gcmkONERROR(gckOS_Allocate(
+ Os,
+ sizeof(int) * context->stateCount,
+ &pointer
+ ));
+
+ context->hint = pointer;
+#endif
+ }
+
+ /**************************************************************************/
+ /* Allocate the context and state delta buffers. **************************/
+
+ for (i = 0; i < gcdCONTEXT_BUFFER_COUNT; i += 1)
+ {
+ /* Allocate a context buffer. */
+ gcsCONTEXT_PTR buffer;
+
+ /* Allocate the context buffer structure. */
+ gcmkONERROR(gckOS_Allocate(
+ Os,
+ sizeof(gcsCONTEXT),
+ &pointer
+ ));
+
+ buffer = pointer;
+
+ /* Reset the context buffer structure. */
+ gcmkVERIFY_OK(gckOS_ZeroMemory(
+ buffer, sizeof(gcsCONTEXT)
+ ));
+
+ /* Append to the list. */
+ if (context->buffer == NULL)
+ {
+ buffer->next = buffer;
+ context->buffer = buffer;
+ }
+ else
+ {
+ buffer->next = context->buffer->next;
+ context->buffer->next = buffer;
+ }
+
+ /* Set the number of delta in the order of creation. */
+#if gcmIS_DEBUG(gcdDEBUG_CODE)
+ buffer->num = i;
+#endif
+
+ /* Create the busy signal. */
+ gcmkONERROR(gckOS_CreateSignal(
+ Os, gcvFALSE, &buffer->signal
+ ));
+
+ /* Set the signal, buffer is currently not busy. */
+ gcmkONERROR(gckOS_Signal(
+ Os, buffer->signal, gcvTRUE
+ ));
+
+ /* Create a new physical context buffer. */
+ gcmkONERROR(gckOS_AllocateContiguous(
+ Os,
+ gcvFALSE,
+ &context->totalSize,
+ &buffer->physical,
+ &pointer
+ ));
+
+ buffer->logical = pointer;
+
+ /* Set gckEVENT object pointer. */
+ buffer->eventObj = Hardware->kernel->eventObj;
+
+ /* Set the pointers to the LINK commands. */
+ if (context->linkIndex2D != 0)
+ {
+ buffer->link2D = &buffer->logical[context->linkIndex2D];
+ }
+
+ if (context->linkIndex3D != 0)
+ {
+ buffer->link3D = &buffer->logical[context->linkIndex3D];
+ }
+
+ if (context->linkIndexXD != 0)
+ {
+ void *xdLink;
+ u8 *xdEntryLogical;
+ size_t xdEntrySize;
+ size_t linkBytes;
+
+ /* Determine LINK parameters. */
+ xdLink
+ = &buffer->logical[context->linkIndexXD];
+
+ xdEntryLogical
+ = (u8 *) buffer->logical
+ + context->entryOffsetXDFrom3D;
+
+ xdEntrySize
+ = context->bufferSize
+ - context->entryOffsetXDFrom3D;
+
+ /* Query LINK size. */
+ gcmkONERROR(gckHARDWARE_Link(
+ Hardware, NULL, NULL, 0, &linkBytes
+ ));
+
+ /* Generate a LINK. */
+ gcmkONERROR(gckHARDWARE_Link(
+ Hardware,
+ xdLink,
+ xdEntryLogical,
+ xdEntrySize,
+ &linkBytes
+ ));
+ }
+ }
+
+
+ /**************************************************************************/
+ /* Initialize the context buffers. ****************************************/
+
+ /* Initialize the current context buffer. */
+ gcmkONERROR(_InitializeContextBuffer(context));
+
+ /* Make all created contexts equal. */
+ {
+ gcsCONTEXT_PTR currContext, tempContext;
+
+ /* Set the current context buffer. */
+ currContext = context->buffer;
+
+ /* Get the next context buffer. */
+ tempContext = currContext->next;
+
+ /* Loop through all buffers. */
+ while (tempContext != currContext)
+ {
+ if (tempContext == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_FOUND);
+ }
+
+ /* Copy the current context. */
+ gcmkONERROR(gckOS_MemCopy(
+ tempContext->logical,
+ currContext->logical,
+ context->totalSize
+ ));
+
+ /* Get the next context buffer. */
+ tempContext = tempContext->next;
+ }
+ }
+
+ /* Return pointer to the gckCONTEXT object. */
+ *Context = context;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Context=0x%08X", *Context);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back on error. */
+ gcmkVERIFY_OK(_DestroyContext(context));
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/******************************************************************************\
+**
+** gckCONTEXT_Destroy
+**
+** Destroy a gckCONTEXT object.
+**
+** INPUT:
+**
+** gckCONTEXT Context
+** Pointer to an gckCONTEXT object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCONTEXT_Destroy(
+ IN gckCONTEXT Context
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Context=0x%08X", Context);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Context, gcvOBJ_CONTEXT);
+
+ /* Destroy the context and all related objects. */
+ status = _DestroyContext(Context);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return status;
+}
+
+/******************************************************************************\
+**
+** gckCONTEXT_Update
+**
+** Merge all pending state delta buffers into the current context buffer.
+**
+** INPUT:
+**
+** gckCONTEXT Context
+** Pointer to an gckCONTEXT object.
+**
+** u32 ProcessID
+** Current process ID.
+**
+** struct _gcsSTATE_DELTA *StateDelta
+** Pointer to the state delta.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckCONTEXT_Update(
+ IN gckCONTEXT Context,
+ IN u32 ProcessID,
+ IN struct _gcsSTATE_DELTA *StateDelta
+ )
+{
+#if !VIVANTE_NO_3D
+ gceSTATUS status = gcvSTATUS_OK;
+ gcsSTATE_DELTA _stateDelta;
+ gckKERNEL kernel;
+ gcsCONTEXT_PTR buffer;
+ gcsSTATE_MAP_PTR map;
+ struct _gcsSTATE_DELTA *nDelta;
+ struct _gcsSTATE_DELTA *uDelta = NULL;
+ struct _gcsSTATE_DELTA *kDelta = NULL;
+ struct _gcsSTATE_DELTA_RECORD *record;
+ struct _gcsSTATE_DELTA_RECORD *recordArray = NULL;
+ unsigned int elementCount;
+ unsigned int address;
+ u32 mask;
+ u32 data;
+ unsigned int index;
+ unsigned int i, j;
+
+#if gcdSECURE_USER
+ gcskSECURE_CACHE_PTR cache;
+#endif
+
+ gcmkHEADER_ARG(
+ "Context=0x%08X ProcessID=%d StateDelta=0x%08X",
+ Context, ProcessID, StateDelta
+ );
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Context, gcvOBJ_CONTEXT);
+
+ /* Get a shortcut to the kernel object. */
+ kernel = Context->hardware->kernel;
+
+ /* Allocate the copy buffer for the user record array. */
+ if (NO_USER_DIRECT_ACCESS_FROM_KERNEL && (Context->recordArray == NULL))
+ {
+ /* Allocate the buffer. */
+ gcmkONERROR(gckOS_Allocate(
+ Context->os,
+ Context->recordArraySize,
+ (void **) &Context->recordArray
+ ));
+ }
+
+ /* Get the current context buffer. */
+ buffer = Context->buffer;
+
+ /* Wait until the context buffer becomes available; this will
+ also reset the signal and mark the buffer as busy. */
+ gcmkONERROR(gckOS_WaitSignal(
+ Context->os, buffer->signal, gcvINFINITE
+ ));
+
+#if gcdSECURE_USER
+ /* Get the cache form the database. */
+ gcmkONERROR(gckKERNEL_GetProcessDBCache(kernel, ProcessID, &cache));
+#endif
+
+#if gcmIS_DEBUG(gcdDEBUG_CODE) && !VIVANTE_NO_3D
+ /* Update current context token. */
+ buffer->logical[Context->map[0x0E14].index]
+ = gcmPTR2INT(Context);
+#endif
+
+ /* Are there any pending deltas? */
+ if (buffer->deltaCount != 0)
+ {
+ /* Get the state map. */
+ map = Context->map;
+
+ /* Get the first delta item. */
+ uDelta = buffer->delta;
+
+ /* Reset the vertex stream count. */
+ elementCount = 0;
+
+ /* Merge all pending deltas. */
+ for (i = 0; i < buffer->deltaCount; i += 1)
+ {
+ /* Get access to the state delta. */
+ gcmkONERROR(gckKERNEL_OpenUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ &_stateDelta,
+ uDelta, sizeof(gcsSTATE_DELTA),
+ (void **) &kDelta
+ ));
+
+ /* Get access to the state records. */
+ gcmkONERROR(gckKERNEL_OpenUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ Context->recordArray,
+ kDelta->recordArray, Context->recordArraySize,
+ (void **) &recordArray
+ ));
+
+ /* Merge all pending states. */
+ for (j = 0; j < kDelta->recordCount; j += 1)
+ {
+ if (j >= Context->stateCount)
+ {
+ break;
+ }
+
+ /* Get the current state record. */
+ record = &recordArray[j];
+
+ /* Get the state address. */
+ address = record->address;
+
+ /* Make sure the state is a part of the mapping table. */
+ if (address >= Context->stateCount)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): State 0x%04X is not mapped.\n",
+ __FUNCTION__, __LINE__,
+ address
+ );
+
+ continue;
+ }
+
+ /* Get the state index. */
+ index = map[address].index;
+
+ /* Skip the state if not mapped. */
+ if (index == 0)
+ {
+#if gcdDEBUG
+ if ((address != 0x0594)
+ && (address != 0x0E00)
+ && (address != 0x0E03)
+ )
+ {
+#endif
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): State 0x%04X is not mapped.\n",
+ __FUNCTION__, __LINE__,
+ address
+ );
+#if gcdDEBUG
+ }
+#endif
+ continue;
+ }
+
+ /* Get the data mask. */
+ mask = record->mask;
+
+ /* Masked states that are being completly reset or regular states. */
+ if ((mask == 0) || (mask == ~0U))
+ {
+ /* Get the new data value. */
+ data = record->data;
+
+ /* Process special states. */
+ if (address == 0x0595)
+ {
+ /* Force auto-disable to be disabled. */
+ data = gcmSETFIELD(data, 5:5, 0x0 );
+ data = gcmSETFIELD(data, 4:4, 0x0 );
+ data = gcmSETFIELD(data, 13:13, 0x0 );
+ }
+
+#if gcdSECURE_USER
+ /* Do we need to convert the logical address? */
+ if (Context->hint[address])
+ {
+ /* Map handle into physical address. */
+ gcmkONERROR(gckKERNEL_MapLogicalToPhysical(
+ kernel, cache, (void *) &data
+ ));
+ }
+#endif
+
+ /* Set new data. */
+ buffer->logical[index] = data;
+ }
+
+ /* Masked states that are being set partially. */
+ else
+ {
+ buffer->logical[index]
+ = (~mask & buffer->logical[index])
+ | (mask & record->data);
+ }
+ }
+
+ /* Get the element count. */
+ if (kDelta->elementCount != 0)
+ {
+ elementCount = kDelta->elementCount;
+ }
+
+ /* Dereference delta. */
+ kDelta->refCount -= 1;
+ gcmkASSERT(kDelta->refCount >= 0);
+
+ /* Get the next state delta. */
+ nDelta = kDelta->next;
+
+ /* Get access to the state records. */
+ gcmkONERROR(gckKERNEL_CloseUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ gcvFALSE,
+ kDelta->recordArray, Context->recordArraySize,
+ (void **) &recordArray
+ ));
+
+ /* Close access to the current state delta. */
+ gcmkONERROR(gckKERNEL_CloseUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ gcvTRUE,
+ uDelta, sizeof(gcsSTATE_DELTA),
+ (void **) &kDelta
+ ));
+
+ /* Update the user delta pointer. */
+ uDelta = nDelta;
+ }
+
+ /* Hardware disables all input streams when the stream 0 is programmed,
+ it then reenables those streams that were explicitely programmed by
+ the software. Because of this we cannot program the entire array of
+ values, otherwise we'll get all streams reenabled, but rather program
+ only those that are actully needed by the software. */
+ if (elementCount != 0)
+ {
+ unsigned int base;
+ unsigned int nopCount;
+ u32 *nop;
+ unsigned int fe2vsCount = 12;
+
+ if (gcmGETFIELD(Context->hardware->identity.chipMinorFeatures1, 23:23))
+ {
+ fe2vsCount = 16;
+ }
+
+ /* Determine the base index of the vertex stream array. */
+ base = map[0x0180].index;
+
+ /* Set the proper state count. */
+ buffer->logical[base - 1]
+ = gcmSETFIELD(buffer->logical[base - 1], 25:16, elementCount );
+
+ /* Determine the number of NOP commands. */
+ nopCount
+ = (fe2vsCount / 2)
+ - (elementCount / 2);
+
+ /* Determine the location of the first NOP. */
+ nop = &buffer->logical[base + (elementCount | 1)];
+
+ /* Fill the unused space with NOPs. */
+ for (i = 0; i < nopCount; i += 1)
+ {
+ if (nop >= buffer->logical + Context->totalSize)
+ {
+ break;
+ }
+
+ /* Generate a NOP command. */
+ *nop = gcmSETFIELD(0, 31:27, 0x03 );
+
+ /* Advance. */
+ nop += 2;
+ }
+ }
+
+ /* Reset pending deltas. */
+ buffer->deltaCount = 0;
+ buffer->delta = NULL;
+ }
+
+ /* Set state delta user pointer. */
+ uDelta = StateDelta;
+
+ /* Get access to the state delta. */
+ gcmkONERROR(gckKERNEL_OpenUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ &_stateDelta,
+ uDelta, sizeof(gcsSTATE_DELTA),
+ (void **) &kDelta
+ ));
+
+ /* State delta cannot be attached to anything yet. */
+ if (kDelta->refCount != 0)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): kDelta->refCount = %d (has to be 0).\n",
+ __FUNCTION__, __LINE__,
+ kDelta->refCount
+ );
+ }
+
+ /* Attach to all contexts. */
+ buffer = Context->buffer;
+
+ do
+ {
+ /* Attach to the context if nothing is attached yet. If a delta
+ is allready attached, all we need to do is to increment
+ the number of deltas in the context. */
+ if (buffer->delta == NULL)
+ {
+ buffer->delta = uDelta;
+ }
+
+ /* Update reference count. */
+ kDelta->refCount += 1;
+
+ /* Update counters. */
+ buffer->deltaCount += 1;
+
+ /* Get the next context buffer. */
+ buffer = buffer->next;
+
+ if (buffer == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_FOUND);
+ }
+ }
+ while (Context->buffer != buffer);
+
+ /* Close access to the current state delta. */
+ gcmkONERROR(gckKERNEL_CloseUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ gcvTRUE,
+ uDelta, sizeof(gcsSTATE_DELTA),
+ (void **) &kDelta
+ ));
+
+ /* Schedule an event to mark the context buffer as available. */
+ gcmkONERROR(gckEVENT_Signal(
+ buffer->eventObj, buffer->signal, gcvKERNEL_PIXEL
+ ));
+
+ /* Advance to the next context buffer. */
+ Context->buffer = buffer->next;
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Get access to the state records. */
+ if (kDelta != NULL)
+ {
+ gcmkVERIFY_OK(gckKERNEL_CloseUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ gcvFALSE,
+ kDelta->recordArray, Context->recordArraySize,
+ (void **) &recordArray
+ ));
+ }
+
+ /* Close access to the current state delta. */
+ gcmkVERIFY_OK(gckKERNEL_CloseUserData(
+ kernel, NO_USER_DIRECT_ACCESS_FROM_KERNEL,
+ gcvTRUE,
+ uDelta, sizeof(gcsSTATE_DELTA),
+ (void **) &kDelta
+ ));
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+#else
+ return gcvSTATUS_OK;
+#endif
+}
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_context.h b/kernel_drivers/v4_cleaned/gc_hal_kernel_context.h
new file mode 100644
index 0000000..8020e2c
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_context.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_context_h_
+#define __gc_hal_kernel_context_h_
+
+#include "gc_hal.h"
+
+typedef struct _gckEVENT * gckEVENT;
+
+/* Maps state locations within the context buffer. */
+typedef struct _gcsSTATE_MAP * gcsSTATE_MAP_PTR;
+typedef struct _gcsSTATE_MAP
+{
+ /* Index of the state in the context buffer. */
+ unsigned int index;
+
+ /* State mask. */
+ u32 mask;
+}
+gcsSTATE_MAP;
+
+/* Context buffer. */
+typedef struct _gcsCONTEXT * gcsCONTEXT_PTR;
+typedef struct _gcsCONTEXT
+{
+ /* For debugging: the number of context buffer in the order of creation. */
+#if gcmIS_DEBUG(gcdDEBUG_CODE)
+ unsigned int num;
+#endif
+
+ /* Pointer to gckEVENT object. */
+ gckEVENT eventObj;
+
+ /* Context busy signal. */
+ gctSIGNAL signal;
+
+ /* Physical address of the context buffer. */
+ gctPHYS_ADDR physical;
+
+ /* Logical address of the context buffer. */
+ u32 * logical;
+
+ /* Pointer to the LINK commands. */
+ void * link2D;
+ void * link3D;
+
+ /* The number of pending state deltas. */
+ unsigned int deltaCount;
+
+ /* Pointer to the first delta to be applied. */
+ struct _gcsSTATE_DELTA * delta;
+
+ /* Next context buffer. */
+ gcsCONTEXT_PTR next;
+}
+gcsCONTEXT;
+
+/* gckCONTEXT structure that hold the current context. */
+struct _gckCONTEXT
+{
+ /* Object. */
+ gcsOBJECT object;
+
+ /* Pointer to gckOS object. */
+ gckOS os;
+
+ /* Pointer to gckHARDWARE object. */
+ gckHARDWARE hardware;
+
+ /* Command buffer alignment. */
+ size_t alignment;
+ size_t reservedHead;
+ size_t reservedTail;
+
+ /* Context buffer metrics. */
+ size_t stateCount;
+ size_t totalSize;
+ size_t bufferSize;
+ u32 linkIndex2D;
+ u32 linkIndex3D;
+ u32 linkIndexXD;
+ u32 entryOffset3D;
+ u32 entryOffsetXDFrom2D;
+ u32 entryOffsetXDFrom3D;
+
+ /* Dirty flags. */
+ int dirty;
+ int dirty2D;
+ int dirty3D;
+ gcsCONTEXT_PTR dirtyBuffer;
+
+ /* State mapping. */
+ gcsSTATE_MAP_PTR map;
+
+ /* List of context buffers. */
+ gcsCONTEXT_PTR buffer;
+
+ /* A copy of the user record array. */
+ unsigned int recordArraySize;
+ struct _gcsSTATE_DELTA_RECORD *recordArray;
+
+ /* Requested pipe select for context. */
+ gcePIPE_SELECT entryPipe;
+ gcePIPE_SELECT exitPipe;
+
+ /* Variables used for building state buffer. */
+ u32 lastAddress;
+ size_t lastSize;
+ u32 lastIndex;
+ int lastFixed;
+
+ /* Hint array. */
+#if gcdSECURE_USER
+ int * hint;
+#endif
+};
+
+#endif /* __gc_hal_kernel_context_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_db.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_db.c
new file mode 100644
index 0000000..fa859f0
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_db.c
@@ -0,0 +1,1429 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+
+#define _GC_OBJ_ZONE gcvZONE_DATABASE
+
+/*******************************************************************************
+***** Private fuctions ********************************************************/
+
+/*******************************************************************************
+** gckKERNEL_NewDatabase
+**
+** Create a new database structure and insert it to the head of the hash list.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** ProcessID that identifies the database.
+**
+** OUTPUT:
+**
+** gcsDATABASE_PTR * Database
+** Pointer to a variable receiving the database structure pointer on
+** success.
+*/
+static gceSTATUS
+gckKERNEL_NewDatabase(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ OUT gcsDATABASE_PTR * Database
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+ int acquired = gcvFALSE;
+ size_t slot;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID);
+
+ /* Acquire the database mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ if (Kernel->db->freeDatabase != NULL)
+ {
+ /* Allocate a database from the free list. */
+ database = Kernel->db->freeDatabase;
+ Kernel->db->freeDatabase = database->next;
+ }
+ else
+ {
+ void *pointer = NULL;
+
+ /* Allocate a new database from the heap. */
+ gcmkONERROR(gckOS_Allocate(Kernel->os,
+ sizeof(gcsDATABASE),
+ &pointer));
+
+ database = pointer;
+ }
+
+ /* Compute the hash for the database. */
+ slot = ProcessID % ARRAY_SIZE(Kernel->db->db);
+
+ /* Insert the database into the hash. */
+ database->next = Kernel->db->db[slot];
+ Kernel->db->db[slot] = database;
+
+ /* Save the hash slot. */
+ database->slot = slot;
+
+ /* Release the database mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+
+ /* Return the database. */
+ *Database = database;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Database=0x%x", *Database);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the database mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_FindDatabase
+**
+** Find a database identified by a process ID and move it to the head of the
+** hash list.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** ProcessID that identifies the database.
+**
+** int LastProcessID
+** gcvTRUE if searching for the last known process ID. gcvFALSE if
+** we need to search for the process ID specified by the ProcessID
+** argument.
+**
+** OUTPUT:
+**
+** gcsDATABASE_PTR * Database
+** Pointer to a variable receiving the database structure pointer on
+** success.
+*/
+static gceSTATUS
+gckKERNEL_FindDatabase(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN int LastProcessID,
+ OUT gcsDATABASE_PTR * Database
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database, previous;
+ size_t slot;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d LastProcessID=%d",
+ Kernel, ProcessID, LastProcessID);
+
+ /* Compute the hash for the database. */
+ slot = ProcessID % ARRAY_SIZE(Kernel->db->db);
+
+ /* Acquire the database mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Check whether we are getting the last known database. */
+ if (LastProcessID)
+ {
+ /* Use last database. */
+ database = Kernel->db->lastDatabase;
+
+ if (database == NULL)
+ {
+ /* Database not found. */
+ gcmkONERROR(gcvSTATUS_INVALID_DATA);
+ }
+ }
+ else
+ {
+ /* Walk the hash list. */
+ for (previous = NULL, database = Kernel->db->db[slot];
+ database != NULL;
+ database = database->next)
+ {
+ if (database->processID == ProcessID)
+ {
+ /* Found it! */
+ break;
+ }
+
+ previous = database;
+ }
+
+ if (database == NULL)
+ {
+ /* Database not found. */
+ gcmkONERROR(gcvSTATUS_INVALID_DATA);
+ }
+
+ if (previous != NULL)
+ {
+ /* Move database to the head of the hash list. */
+ previous->next = database->next;
+ database->next = Kernel->db->db[slot];
+ Kernel->db->db[slot] = database;
+ }
+ }
+
+ /* Release the database mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+
+ /* Return the database. */
+ *Database = database;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Database=0x%x", *Database);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the database mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_DeleteDatabase
+**
+** Remove a database from the hash list and delete its structure.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** gcsDATABASE_PTR Database
+** Pointer to the database structure to remove.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+static gceSTATUS
+gckKERNEL_DeleteDatabase(
+ IN gckKERNEL Kernel,
+ IN gcsDATABASE_PTR Database
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ gcsDATABASE_PTR database;
+
+ gcmkHEADER_ARG("Kernel=0x%x Database=0x%x", Kernel, Database);
+
+ /* Acquire the database mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Check slot value. */
+ gcmkVERIFY_ARGUMENT(Database->slot < ARRAY_SIZE(Kernel->db->db));
+
+ if (Database->slot < ARRAY_SIZE(Kernel->db->db))
+ {
+ /* Check if database if the head of the hash list. */
+ if (Kernel->db->db[Database->slot] == Database)
+ {
+ /* Remove the database from the hash list. */
+ Kernel->db->db[Database->slot] = Database->next;
+ }
+ else
+ {
+ /* Walk the has list to find the database. */
+ for (database = Kernel->db->db[Database->slot];
+ database != NULL;
+ database = database->next
+ )
+ {
+ /* Check if the next list entry is this database. */
+ if (database->next == Database)
+ {
+ /* Remove the database from the hash list. */
+ database->next = Database->next;
+ break;
+ }
+ }
+
+ if (database == NULL)
+ {
+ /* Ouch! Something got corrupted. */
+ gcmkONERROR(gcvSTATUS_INVALID_DATA);
+ }
+ }
+ }
+
+ if (Kernel->db->lastDatabase != NULL)
+ {
+ /* Insert database to the free list. */
+ Kernel->db->lastDatabase->next = Kernel->db->freeDatabase;
+ Kernel->db->freeDatabase = Kernel->db->lastDatabase;
+ }
+
+ /* Keep database as the last database. */
+ Kernel->db->lastDatabase = Database;
+
+ /* Release the database mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the database mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_NewRecord
+**
+** Create a new database record structure and insert it to the head of the
+** database.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** gcsDATABASE_PTR Database
+** Pointer to a database structure.
+**
+** OUTPUT:
+**
+** gcsDATABASE_RECORD_PTR * Record
+** Pointer to a variable receiving the database record structure
+** pointer on success.
+*/
+static gceSTATUS
+gckKERNEL_NewRecord(
+ IN gckKERNEL Kernel,
+ IN gcsDATABASE_PTR Database,
+ OUT gcsDATABASE_RECORD_PTR * Record
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ gcsDATABASE_RECORD_PTR record = NULL;
+
+ gcmkHEADER_ARG("Kernel=0x%x Database=0x%x", Kernel, Database);
+
+ /* Acquire the database mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ if (Kernel->db->freeRecord != NULL)
+ {
+ /* Allocate the record from the free list. */
+ record = Kernel->db->freeRecord;
+ Kernel->db->freeRecord = record->next;
+ }
+ else
+ {
+ void *pointer = NULL;
+
+ /* Allocate the record from the heap. */
+ gcmkONERROR(gckOS_Allocate(Kernel->os,
+ sizeof(gcsDATABASE_RECORD),
+ &pointer));
+
+ record = pointer;
+ }
+
+ /* Insert the record in the database. */
+ record->next = Database->list;
+ Database->list = record;
+
+ /* Release the database mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+
+ /* Return the record. */
+ *Record = record;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Record=0x%x", *Record);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the database mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+ }
+ if (record != NULL)
+ {
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, record));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_DeleteRecord
+**
+** Remove a database record from the database and delete its structure.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** gcsDATABASE_PTR Database
+** Pointer to a database structure.
+**
+** gceDATABASE_TYPE Type
+** Type of the record to remove.
+**
+** void *Data
+** Data of the record to remove.
+**
+** OUTPUT:
+**
+** size_t *Bytes
+** Pointer to a variable that receives the size of the record deleted.
+** Can be NULL if the size is not required.
+*/
+static gceSTATUS
+gckKERNEL_DeleteRecord(
+ IN gckKERNEL Kernel,
+ IN gcsDATABASE_PTR Database,
+ IN gceDATABASE_TYPE Type,
+ IN void *Data,
+ OUT size_t *Bytes OPTIONAL
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ gcsDATABASE_RECORD_PTR record, previous;
+
+ gcmkHEADER_ARG("Kernel=0x%x Database=0x%x Type=%d Data=0x%x",
+ Kernel, Database, Type, Data);
+
+ /* Acquire the database mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Scan the database for this record. */
+ for (record = Database->list, previous = NULL;
+ record != NULL;
+ record = record->next
+ )
+ {
+ if ((record->type == Type)
+ && (record->data == Data)
+ )
+ {
+ /* Found it! */
+ break;
+ }
+
+ previous = record;
+ }
+
+ if (record == NULL)
+ {
+ /* Ouch! This record is not found? */
+ gcmkONERROR(gcvSTATUS_INVALID_DATA);
+ }
+
+ if (Bytes != NULL)
+ {
+ /* Return size of record. */
+ *Bytes = record->bytes;
+ }
+
+ /* Remove record from database. */
+ if (previous == NULL)
+ {
+ Database->list = record->next;
+ }
+ else
+ {
+ previous->next = record->next;
+ }
+
+ /* Insert record in free list. */
+ record->next = Kernel->db->freeRecord;
+ Kernel->db->freeRecord = record;
+
+ /* Release the database mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes));
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the database mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_FindRecord
+**
+** Find a database record from the database.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** gcsDATABASE_PTR Database
+** Pointer to a database structure.
+**
+** gceDATABASE_TYPE Type
+** Type of the record to remove.
+**
+** void *Data
+** Data of the record to remove.
+**
+** OUTPUT:
+**
+** gcsDATABASE_RECORD_PTR Record
+** Pointer to a variable that receives a copy of the record deleted.
+** Can be NULL if a copy is not required.
+*/
+static gceSTATUS
+gckKERNEL_FindRecord(
+ IN gckKERNEL Kernel,
+ IN gcsDATABASE_PTR Database,
+ IN gceDATABASE_TYPE Type,
+ IN void *Data,
+ OUT gcsDATABASE_RECORD_PTR Record
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ gcsDATABASE_RECORD_PTR record;
+
+ gcmkHEADER_ARG("Kernel=0x%x Database=0x%x Type=%d Data=0x%x",
+ Kernel, Database, Type, Data);
+
+ /* Acquire the database mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Scan the database for this record. */
+ for (record = Database->list;
+ record != NULL;
+ record = record->next
+ )
+ {
+ if ((record->type == Type)
+ && (record->data == Data)
+ )
+ {
+ /* Found it! */
+ break;
+ }
+ }
+
+ if (record == NULL)
+ {
+ /* Ouch! This record is not found? */
+ gcmkONERROR(gcvSTATUS_INVALID_DATA);
+ }
+
+ if (Record != NULL)
+ {
+ /* Return information of record. */
+ gcmkONERROR(
+ gckOS_MemCopy(Record, record, sizeof(gcsDATABASE_RECORD)));
+ }
+
+ /* Release the database mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+
+ /* Success. */
+ gcmkFOOTER_ARG("Record=0x%x", Record);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the database mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+
+/*******************************************************************************
+***** Public API **************************************************************/
+
+/*******************************************************************************
+** gckKERNEL_CreateProcessDB
+**
+** Create a new process database.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** Process ID used to identify the database.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_CreateProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database = NULL;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ /* Create a new database. */
+ gcmkONERROR(gckKERNEL_NewDatabase(Kernel, ProcessID, &database));
+
+ /* Initialize the database. */
+ database->processID = ProcessID;
+ database->vidMem.bytes = 0;
+ database->vidMem.maxBytes = 0;
+ database->vidMem.totalBytes = 0;
+ database->nonPaged.bytes = 0;
+ database->nonPaged.maxBytes = 0;
+ database->nonPaged.totalBytes = 0;
+ database->contiguous.bytes = 0;
+ database->contiguous.maxBytes = 0;
+ database->contiguous.totalBytes = 0;
+ database->mapMemory.bytes = 0;
+ database->mapMemory.maxBytes = 0;
+ database->mapMemory.totalBytes = 0;
+ database->mapUserMemory.bytes = 0;
+ database->mapUserMemory.maxBytes = 0;
+ database->mapUserMemory.totalBytes = 0;
+ database->list = NULL;
+
+#if gcdSECURE_USER
+ {
+ int slot;
+ gcskSECURE_CACHE * cache = &database->cache;
+
+ /* Setup the linked list of cache nodes. */
+ for (slot = 1; slot <= gcdSECURE_CACHE_SLOTS; ++slot)
+ {
+ cache->cache[slot].logical = NULL;
+
+#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE
+ cache->cache[slot].prev = &cache->cache[slot - 1];
+ cache->cache[slot].next = &cache->cache[slot + 1];
+# endif
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ cache->cache[slot].nextHash = NULL;
+ cache->cache[slot].prevHash = NULL;
+# endif
+ }
+
+#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE
+ /* Setup the head and tail of the cache. */
+ cache->cache[0].next = &cache->cache[1];
+ cache->cache[0].prev = &cache->cache[gcdSECURE_CACHE_SLOTS];
+ cache->cache[0].logical = NULL;
+
+ /* Fix up the head and tail pointers. */
+ cache->cache[0].next->prev = &cache->cache[0];
+ cache->cache[0].prev->next = &cache->cache[0];
+# endif
+
+#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH
+ /* Zero out the hash table. */
+ for (slot = 0; slot < ARRAY_SIZE(cache->hash); ++slot)
+ {
+ cache->hash[slot].logical = NULL;
+ cache->hash[slot].nextHash = NULL;
+ }
+# endif
+
+ /* Initialize cache index. */
+ cache->cacheIndex = NULL;
+ cache->cacheFree = 1;
+ cache->cacheStamp = 0;
+ }
+#endif
+
+ /* Reset idle timer. */
+ Kernel->db->lastIdle = 0;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_AddProcessDB
+**
+** Add a record to a process database.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** Process ID used to identify the database.
+**
+** gceDATABASE_TYPE TYPE
+** Type of the record to add.
+**
+** void *Pointer
+** Data of the record to add.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the record to add.
+**
+** size_t Size
+** Size of the record to add.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_AddProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN gceDATABASE_TYPE Type,
+ IN void *Pointer,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Size
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+ gcsDATABASE_RECORD_PTR record = NULL;
+ gcsDATABASE_COUNTERS * count;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x "
+ "Physical=0x%x Size=%lu",
+ Kernel, ProcessID, Type, Pointer, Physical, Size);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ /* Special case the idle record. */
+ if (Type == gcvDB_IDLE)
+ {
+ u64 time;
+
+ /* Get the current profile time. */
+ gcmkONERROR(gckOS_GetProfileTick(&time));
+
+ if ((ProcessID == 0) && (Kernel->db->lastIdle != 0))
+ {
+ /* Out of idle, adjust time it was idle. */
+ Kernel->db->idleTime += time - Kernel->db->lastIdle;
+ Kernel->db->lastIdle = 0;
+ }
+ else if (ProcessID == 1)
+ {
+ /* Save current idle time. */
+ Kernel->db->lastIdle = time;
+ }
+
+#if gcdDYNAMIC_SPEED
+ {
+ /* Test for first call. */
+ if (Kernel->db->lastSlowdown == 0)
+ {
+ /* Save milliseconds. */
+ Kernel->db->lastSlowdown = time;
+ Kernel->db->lastSlowdownIdle = Kernel->db->idleTime;
+ }
+ else
+ {
+ /* Compute ellapsed time in milliseconds. */
+ unsigned int delta = gckOS_ProfileToMS(time - Kernel->db->lastSlowdown);
+
+ /* Test for end of period. */
+ if (delta >= gcdDYNAMIC_SPEED)
+ {
+ /* Compute number of idle milliseconds. */
+ unsigned int idle = gckOS_ProfileToMS(
+ Kernel->db->idleTime - Kernel->db->lastSlowdownIdle);
+
+ /* Broadcast to slow down the GPU. */
+ gcmkONERROR(gckOS_BroadcastCalibrateSpeed(Kernel->os,
+ Kernel->hardware,
+ idle,
+ delta));
+
+ /* Save current time. */
+ Kernel->db->lastSlowdown = time;
+ Kernel->db->lastSlowdownIdle = Kernel->db->idleTime;
+ }
+ }
+ }
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Pointer != NULL);
+
+ /* Find the database. */
+ gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database));
+
+ /* Create a new record in the database. */
+ gcmkONERROR(gckKERNEL_NewRecord(Kernel, database, &record));
+
+ /* Initialize the record. */
+ record->kernel = Kernel;
+ record->type = Type;
+ record->data = Pointer;
+ record->physical = Physical;
+ record->bytes = Size;
+
+ /* Get pointer to counters. */
+ switch (Type)
+ {
+ case gcvDB_VIDEO_MEMORY:
+ count = &database->vidMem;
+ break;
+
+ case gcvDB_NON_PAGED:
+ count = &database->nonPaged;
+ break;
+
+ case gcvDB_CONTIGUOUS:
+ count = &database->contiguous;
+ break;
+
+ case gcvDB_MAP_MEMORY:
+ count = &database->mapMemory;
+ break;
+
+ case gcvDB_MAP_USER_MEMORY:
+ count = &database->mapUserMemory;
+ break;
+
+ default:
+ count = NULL;
+ break;
+ }
+
+ if (count != NULL)
+ {
+ /* Adjust counters. */
+ count->totalBytes += Size;
+ count->bytes += Size;
+
+ if (count->bytes > count->maxBytes)
+ {
+ count->maxBytes = count->bytes;
+ }
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_RemoveProcessDB
+**
+** Remove a record from a process database.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** Process ID used to identify the database.
+**
+** gceDATABASE_TYPE TYPE
+** Type of the record to remove.
+**
+** void *Pointer
+** Data of the record to remove.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_RemoveProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN gceDATABASE_TYPE Type,
+ IN void *Pointer
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+ size_t bytes = 0;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x",
+ Kernel, ProcessID, Type, Pointer);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Pointer != NULL);
+
+ /* Find the database. */
+ gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database));
+
+ /* Delete the record. */
+ gcmkONERROR(
+ gckKERNEL_DeleteRecord(Kernel, database, Type, Pointer, &bytes));
+
+ /* Update counters. */
+ switch (Type)
+ {
+ case gcvDB_VIDEO_MEMORY:
+ database->vidMem.bytes -= bytes;
+ break;
+
+ case gcvDB_NON_PAGED:
+ database->nonPaged.bytes -= bytes;
+ break;
+
+ case gcvDB_CONTIGUOUS:
+ database->contiguous.bytes -= bytes;
+ break;
+
+ case gcvDB_MAP_MEMORY:
+ database->mapMemory.bytes -= bytes;
+ break;
+
+ case gcvDB_MAP_USER_MEMORY:
+ database->mapUserMemory.bytes -= bytes;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_FindProcessDB
+**
+** Find a record from a process database.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** Process ID used to identify the database.
+**
+** gceDATABASE_TYPE TYPE
+** Type of the record to remove.
+**
+** void *Pointer
+** Data of the record to remove.
+**
+** OUTPUT:
+**
+** gcsDATABASE_RECORD_PTR Record
+** Copy of record.
+*/
+gceSTATUS
+gckKERNEL_FindProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN u32 ThreadID,
+ IN gceDATABASE_TYPE Type,
+ IN void *Pointer,
+ OUT gcsDATABASE_RECORD_PTR Record
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x",
+ Kernel, ProcessID, ThreadID, Type, Pointer);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Pointer != NULL);
+
+ /* Find the database. */
+ gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database));
+
+ /* Find the record. */
+ gcmkONERROR(
+ gckKERNEL_FindRecord(Kernel, database, Type, Pointer, Record));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_DestroyProcessDB
+**
+** Destroy a process database. If the database contains any records, the data
+** inside those records will be deleted as well. This aids in the cleanup if
+** a process has died unexpectedly or has memory leaks.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** Process ID used to identify the database.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_DestroyProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+ gcsDATABASE_RECORD_PTR record, next;
+ int asynchronous;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+
+ /* Find the database. */
+ gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database));
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE,
+ "DB(%d): VidMem: total=%lu max=%lu",
+ ProcessID, database->vidMem.totalBytes,
+ database->vidMem.maxBytes);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE,
+ "DB(%d): NonPaged: total=%lu max=%lu",
+ ProcessID, database->nonPaged.totalBytes,
+ database->nonPaged.maxBytes);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE,
+ "DB(%d): Contiguous: total=%lu max=%lu",
+ ProcessID, database->contiguous.totalBytes,
+ database->contiguous.maxBytes);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE,
+ "DB(%d): Idle time=%llu",
+ ProcessID, Kernel->db->idleTime);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE,
+ "DB(%d): Map: total=%lu max=%lu",
+ ProcessID, database->mapMemory.totalBytes,
+ database->mapMemory.maxBytes);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE,
+ "DB(%d): Map: total=%lu max=%lu",
+ ProcessID, database->mapUserMemory.totalBytes,
+ database->mapUserMemory.maxBytes);
+
+ if (database->list != NULL)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "Process %d has entries in its database:",
+ ProcessID);
+ }
+
+ /* Walk all records. */
+ for (record = database->list; record != NULL; record = next)
+ {
+ /* Next next record. */
+ next = record->next;
+
+ /* Dispatch on record type. */
+ switch (record->type)
+ {
+ case gcvDB_VIDEO_MEMORY:
+ /* Free the video memory. */
+ status = gckVIDMEM_Free(record->data);
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: VIDEO_MEMORY 0x%x (status=%d)",
+ record->data, status);
+ break;
+
+ case gcvDB_NON_PAGED:
+ /* Free the non paged memory. */
+ status = gckOS_FreeNonPagedMemory(Kernel->os,
+ record->bytes,
+ record->physical,
+ record->data);
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: NON_PAGED 0x%x, bytes=%lu (status=%d)",
+ record->data, record->bytes, status);
+ break;
+
+ case gcvDB_CONTIGUOUS:
+ /* Free the contiguous memory. */
+ status = gckOS_FreeContiguous(Kernel->os,
+ record->physical,
+ record->data,
+ record->bytes);
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: CONTIGUOUS 0x%x bytes=%lu (status=%d)",
+ record->data, record->bytes, status);
+ break;
+
+ case gcvDB_SIGNAL:
+#if USE_NEW_LINUX_SIGNAL
+ status = gcvSTATUS_NOT_SUPPORTED;
+#else
+ /* Free the user signal. */
+ status = gckOS_DestroyUserSignal(Kernel->os,
+ gcmPTR2INT(record->data));
+#endif /* USE_NEW_LINUX_SIGNAL */
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: SIGNAL %d (status=%d)",
+ (int) record->data, status);
+ break;
+
+ case gcvDB_VIDEO_MEMORY_LOCKED:
+ /* Unlock what we still locked */
+ status = gckVIDMEM_Unlock(record->kernel,
+ record->data,
+ gcvSURF_TYPE_UNKNOWN,
+ &asynchronous);
+
+ if (gcmIS_SUCCESS(status) && (gcvTRUE == asynchronous))
+ {
+ /* TODO: we maybe need to schedule a event here */
+ status = gckVIDMEM_Unlock(record->kernel,
+ record->data,
+ gcvSURF_TYPE_UNKNOWN,
+ NULL);
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: VIDEO_MEMORY_LOCKED 0x%x (status=%d)",
+ record->data, status);
+ break;
+
+ case gcvDB_CONTEXT:
+ /* TODO: Free the context */
+ status = gckCOMMAND_Detach(Kernel->command, record->data);
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: CONTEXT 0x%x (status=%d)",
+ record->data, status);
+ break;
+
+ case gcvDB_MAP_MEMORY:
+ /* Unmap memory. */
+ status = gckKERNEL_UnmapMemory(Kernel,
+ record->physical,
+ record->bytes,
+ record->data);
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: MAP MEMORY %d (status=%d)",
+ gcmPTR2INT(record->data), status);
+ break;
+
+ case gcvDB_MAP_USER_MEMORY:
+ /* TODO: Unmap user memory. */
+ status = gckOS_UnmapUserMemoryEx(Kernel->os,
+ Kernel->core,
+ record->data,
+ record->bytes,
+ record->physical,
+ 0);
+
+ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE,
+ "DB: MAP USER MEMORY %d (status=%d)",
+ gcmPTR2INT(record->data), status);
+ break;
+
+ case gcvDB_SHARED_INFO:
+ status = gckOS_FreeMemory(Kernel->os, record->physical);
+ break;
+
+ default:
+ gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DATABASE,
+ "DB: Correcupted record=0x%08x type=%d",
+ record, record->type);
+ break;
+ }
+
+ /* Delete the record. */
+ gcmkONERROR(gckKERNEL_DeleteRecord(Kernel,
+ database,
+ record->type,
+ record->data,
+ NULL));
+ }
+
+ /* Delete the database. */
+ gcmkONERROR(gckKERNEL_DeleteDatabase(Kernel, database));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckKERNEL_QueryProcessDB
+**
+** Query a process database for the current usage of a particular record type.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** Process ID used to identify the database.
+**
+** int LastProcessID
+** gcvTRUE if searching for the last known process ID. gcvFALSE if
+** we need to search for the process ID specified by the ProcessID
+** argument.
+**
+** gceDATABASE_TYPE Type
+** Type of the record to query.
+**
+** OUTPUT:
+**
+** gcuDATABASE_INFO * Info
+** Pointer to a variable that receives the requested information.
+*/
+gceSTATUS
+gckKERNEL_QueryProcessDB(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ IN int LastProcessID,
+ IN gceDATABASE_TYPE Type,
+ OUT gcuDATABASE_INFO * Info
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Info=0x%x",
+ Kernel, ProcessID, Type, Info);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Info != NULL);
+
+ /* Find the database. */
+ gcmkONERROR(
+ gckKERNEL_FindDatabase(Kernel, ProcessID, LastProcessID, &database));
+
+ /* Get pointer to counters. */
+ switch (Type)
+ {
+ case gcvDB_VIDEO_MEMORY:
+ gcmkONERROR(gckOS_MemCopy(&Info->counters,
+ &database->vidMem,
+ sizeof(database->vidMem)));
+ break;
+
+ case gcvDB_NON_PAGED:
+ gcmkONERROR(gckOS_MemCopy(&Info->counters,
+ &database->nonPaged,
+ sizeof(database->vidMem)));
+ break;
+
+ case gcvDB_CONTIGUOUS:
+ gcmkONERROR(gckOS_MemCopy(&Info->counters,
+ &database->contiguous,
+ sizeof(database->vidMem)));
+ break;
+
+ case gcvDB_IDLE:
+ Info->time = Kernel->db->idleTime;
+ Kernel->db->idleTime = 0;
+ break;
+
+ case gcvDB_MAP_MEMORY:
+ gcmkONERROR(gckOS_MemCopy(&Info->counters,
+ &database->mapMemory,
+ sizeof(database->mapMemory)));
+ break;
+
+ case gcvDB_MAP_USER_MEMORY:
+ gcmkONERROR(gckOS_MemCopy(&Info->counters,
+ &database->mapUserMemory,
+ sizeof(database->mapUserMemory)));
+ break;
+
+ default:
+ break;
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#if gcdSECURE_USER
+/*******************************************************************************
+** gckKERNEL_GetProcessDBCache
+**
+** Get teh secure cache from a process database.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to a gckKERNEL object.
+**
+** u32 ProcessID
+** Process ID used to identify the database.
+**
+** OUTPUT:
+**
+** gcskSECURE_CACHE_PTR * Cache
+** Pointer to a variable that receives the secure cache pointer.
+*/
+gceSTATUS
+gckKERNEL_GetProcessDBCache(
+ IN gckKERNEL Kernel,
+ IN u32 ProcessID,
+ OUT gcskSECURE_CACHE_PTR * Cache
+ )
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+
+ gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Cache != NULL);
+
+ /* Find the database. */
+ gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database));
+
+ /* Return the pointer to the cache. */
+ *Cache = &database->cache;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Cache=0x%x", *Cache);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_debug.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_debug.c
new file mode 100644
index 0000000..2caaa02
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_debug.c
@@ -0,0 +1,2484 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+#include "gc_hal_kernel_debug.h"
+
+/******************************************************************************\
+******************************** Debug Variables *******************************
+\******************************************************************************/
+
+static gceSTATUS _lastError = gcvSTATUS_OK;
+static u32 _debugLevel = gcvLEVEL_ERROR;
+/*
+_debugZones config value
+Please Reference define in gc_hal_base.h
+*/
+static u32 _debugZones = gcvZONE_NONE;
+
+/******************************************************************************\
+********************************* Debug Switches *******************************
+\******************************************************************************/
+
+/*
+ gcdBUFFERED_OUTPUT
+
+ When set to non-zero, all output is collected into a buffer with the
+ specified size. Once the buffer gets full, the debug buffer will be
+ printed to the console. gcdBUFFERED_SIZE determines the size of the buffer.
+*/
+#define gcdBUFFERED_OUTPUT 0
+
+/*
+ gcdBUFFERED_SIZE
+
+ When set to non-zero, all output is collected into a buffer with the
+ specified size. Once the buffer gets full, the debug buffer will be
+ printed to the console.
+*/
+#define gcdBUFFERED_SIZE (1024 * 1024 * 2)
+
+/*
+ gcdDMA_BUFFER_COUNT
+
+ If greater then zero, the debugger will attempt to find the command buffer
+ where DMA is currently executing and then print this buffer and
+ (gcdDMA_BUFFER_COUNT - 1) buffers before the current one. If set to zero
+ or the current buffer is not found, all buffers are printed.
+*/
+#define gcdDMA_BUFFER_COUNT 0
+
+/*
+ gcdTHREAD_BUFFERS
+
+ When greater then one, will accumulate messages from the specified number
+ of threads in separate output buffers.
+*/
+#define gcdTHREAD_BUFFERS 1
+
+/*
+ gcdENABLE_OVERFLOW
+
+ When set to non-zero, and the output buffer gets full, instead of being
+ printed, it will be allowed to overflow removing the oldest messages.
+*/
+#define gcdENABLE_OVERFLOW 1
+
+/*
+ gcdSHOW_LINE_NUMBER
+
+ When enabledm each print statement will be preceeded with the current
+ line number.
+*/
+#define gcdSHOW_LINE_NUMBER 0
+
+/*
+ gcdSHOW_PROCESS_ID
+
+ When enabledm each print statement will be preceeded with the current
+ process ID.
+*/
+#define gcdSHOW_PROCESS_ID 0
+
+/*
+ gcdSHOW_THREAD_ID
+
+ When enabledm each print statement will be preceeded with the current
+ thread ID.
+*/
+#define gcdSHOW_THREAD_ID 0
+
+/*
+ gcdSHOW_TIME
+
+ When enabled each print statement will be preceeded with the current
+ high-resolution time.
+*/
+#define gcdSHOW_TIME 0
+
+
+/******************************************************************************\
+****************************** Miscellaneous Macros ****************************
+\******************************************************************************/
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+# define gcmDBGASSERT(Expression, Format, Value) \
+ if (!(Expression)) \
+ { \
+ _DirectPrint( \
+ "*** gcmDBGASSERT ***************************\n" \
+ " function : %s\n" \
+ " line : %d\n" \
+ " expression : " #Expression "\n" \
+ " actual value : " Format "\n", \
+ __FUNCTION__, __LINE__, Value \
+ ); \
+ }
+#else
+# define gcmDBGASSERT(Expression, Format, Value)
+#endif
+
+#define gcmPTRALIGNMENT(Pointer, Alignemnt) \
+( \
+ gcmALIGN(gcmPTR2INT(Pointer), Alignemnt) - gcmPTR2INT(Pointer) \
+)
+
+#if gcdALIGNBYSIZE
+# define gcmISALIGNED(Offset, Alignment) \
+ (((Offset) & ((Alignment) - 1)) == 0)
+
+# define gcmkALIGNPTR(Type, Pointer, Alignment) \
+ Pointer = (Type) gcmINT2PTR(gcmALIGN(gcmPTR2INT(Pointer), Alignment))
+#else
+# define gcmISALIGNED(Offset, Alignment) \
+ gcvTRUE
+
+# define gcmkALIGNPTR(Type, Pointer, Alignment)
+#endif
+
+#define gcmALIGNSIZE(Offset, Size) \
+ ((Size - Offset) + Size)
+
+#define gcdHAVEPREFIX \
+( \
+ gcdSHOW_TIME \
+ || gcdSHOW_LINE_NUMBER \
+ || gcdSHOW_PROCESS_ID \
+ || gcdSHOW_THREAD_ID \
+)
+
+#if gcdHAVEPREFIX
+
+# define gcdOFFSET 0
+
+#if gcdSHOW_TIME
+#if gcmISALIGNED(gcdOFFSET, 8)
+# define gcdTIMESIZE sizeof(u64)
+# elif gcdOFFSET == 4
+# define gcdTIMESIZE gcmALIGNSIZE(4, sizeof(u64))
+# else
+# error "Unexpected offset value."
+# endif
+# undef gcdOFFSET
+# define gcdOFFSET 8
+#if !defined(gcdPREFIX_LEADER)
+# define gcdPREFIX_LEADER sizeof(u64)
+# define gcdTIMEFORMAT "0x%016llX"
+# else
+# define gcdTIMEFORMAT ", 0x%016llX"
+# endif
+# else
+# define gcdTIMESIZE 0
+# define gcdTIMEFORMAT
+# endif
+
+#if gcdSHOW_LINE_NUMBER
+#if gcmISALIGNED(gcdOFFSET, 8)
+# define gcdNUMSIZE sizeof(u64)
+# elif gcdOFFSET == 4
+# define gcdNUMSIZE gcmALIGNSIZE(4, sizeof(u64))
+# else
+# error "Unexpected offset value."
+# endif
+# undef gcdOFFSET
+# define gcdOFFSET 8
+#if !defined(gcdPREFIX_LEADER)
+# define gcdPREFIX_LEADER sizeof(u64)
+# define gcdNUMFORMAT "%8llu"
+# else
+# define gcdNUMFORMAT ", %8llu"
+# endif
+# else
+# define gcdNUMSIZE 0
+# define gcdNUMFORMAT
+# endif
+
+#if gcdSHOW_PROCESS_ID
+#if gcmISALIGNED(gcdOFFSET, 4)
+# define gcdPIDSIZE sizeof(u32)
+# else
+# error "Unexpected offset value."
+# endif
+# undef gcdOFFSET
+# define gcdOFFSET 4
+#if !defined(gcdPREFIX_LEADER)
+# define gcdPREFIX_LEADER sizeof(u32)
+# define gcdPIDFORMAT "pid=%5d"
+# else
+# define gcdPIDFORMAT ", pid=%5d"
+# endif
+# else
+# define gcdPIDSIZE 0
+# define gcdPIDFORMAT
+# endif
+
+#if gcdSHOW_THREAD_ID
+#if gcmISALIGNED(gcdOFFSET, 4)
+# define gcdTIDSIZE sizeof(u32)
+# else
+# error "Unexpected offset value."
+# endif
+# undef gcdOFFSET
+# define gcdOFFSET 4
+#if !defined(gcdPREFIX_LEADER)
+# define gcdPREFIX_LEADER sizeof(u32)
+# define gcdTIDFORMAT "tid=%5d"
+# else
+# define gcdTIDFORMAT ", tid=%5d"
+# endif
+# else
+# define gcdTIDSIZE 0
+# define gcdTIDFORMAT
+# endif
+
+# define gcdPREFIX_SIZE \
+ ( \
+ gcdTIMESIZE \
+ + gcdNUMSIZE \
+ + gcdPIDSIZE \
+ + gcdTIDSIZE \
+ )
+
+ static const char * _prefixFormat =
+ "["
+ gcdTIMEFORMAT
+ gcdNUMFORMAT
+ gcdPIDFORMAT
+ gcdTIDFORMAT
+ "] ";
+
+#else
+
+# define gcdPREFIX_LEADER sizeof(u32)
+# define gcdPREFIX_SIZE 0
+
+#endif
+
+/* Assumed largest variable argument leader size. */
+#define gcdVARARG_LEADER sizeof(u64)
+
+/* Alignnments. */
+#if gcdALIGNBYSIZE
+# define gcdPREFIX_ALIGNMENT gcdPREFIX_LEADER
+# define gcdVARARG_ALIGNMENT gcdVARARG_LEADER
+#else
+# define gcdPREFIX_ALIGNMENT 0
+# define gcdVARARG_ALIGNMENT 0
+#endif
+
+#if gcdBUFFERED_OUTPUT
+# define gcdOUTPUTPREFIX _AppendPrefix
+# define gcdOUTPUTSTRING _AppendString
+# define gcdOUTPUTCOPY _AppendCopy
+# define gcdOUTPUTBUFFER _AppendBuffer
+#else
+# define gcdOUTPUTPREFIX _PrintPrefix
+# define gcdOUTPUTSTRING _PrintString
+# define gcdOUTPUTCOPY _PrintString
+# define gcdOUTPUTBUFFER _PrintBuffer
+#endif
+
+/******************************************************************************\
+****************************** Private Structures ******************************
+\******************************************************************************/
+
+typedef enum _gceBUFITEM
+{
+ gceBUFITEM_NONE,
+ gcvBUFITEM_PREFIX,
+ gcvBUFITEM_STRING,
+ gcvBUFITEM_COPY,
+ gcvBUFITEM_BUFFER
+}
+gceBUFITEM;
+
+/* Common item head/buffer terminator. */
+typedef struct _gcsBUFITEM_HEAD * gcsBUFITEM_HEAD_PTR;
+typedef struct _gcsBUFITEM_HEAD
+{
+ gceBUFITEM type;
+}
+gcsBUFITEM_HEAD;
+
+/* String prefix (for ex. [ 1,tid=0x019A]) */
+typedef struct _gcsBUFITEM_PREFIX * gcsBUFITEM_PREFIX_PTR;
+typedef struct _gcsBUFITEM_PREFIX
+{
+ gceBUFITEM type;
+#if gcdHAVEPREFIX
+ void * prefixData;
+#endif
+}
+gcsBUFITEM_PREFIX;
+
+/* Buffered string. */
+typedef struct _gcsBUFITEM_STRING * gcsBUFITEM_STRING_PTR;
+typedef struct _gcsBUFITEM_STRING
+{
+ gceBUFITEM type;
+ int indent;
+ const char * message;
+ void * messageData;
+ unsigned int messageDataSize;
+}
+gcsBUFITEM_STRING;
+
+/* Buffered string (copy of the string is included with the record). */
+typedef struct _gcsBUFITEM_COPY * gcsBUFITEM_COPY_PTR;
+typedef struct _gcsBUFITEM_COPY
+{
+ gceBUFITEM type;
+ int indent;
+ void * messageData;
+ unsigned int messageDataSize;
+}
+gcsBUFITEM_COPY;
+
+/* Memory buffer. */
+typedef struct _gcsBUFITEM_BUFFER * gcsBUFITEM_BUFFER_PTR;
+typedef struct _gcsBUFITEM_BUFFER
+{
+ gceBUFITEM type;
+ int indent;
+ gceDUMP_BUFFER bufferType;
+
+#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1)
+ u32 dmaAddress;
+#endif
+
+ unsigned int dataSize;
+ u32 address;
+#if gcdHAVEPREFIX
+ void * prefixData;
+#endif
+}
+gcsBUFITEM_BUFFER;
+
+typedef struct _gcsBUFFERED_OUTPUT * gcsBUFFERED_OUTPUT_PTR;
+typedef struct _gcsBUFFERED_OUTPUT
+{
+#if gcdTHREAD_BUFFERS > 1
+ u32 threadID;
+#endif
+
+#if gcdSHOW_LINE_NUMBER
+ u64 lineNumber;
+#endif
+
+ int indent;
+
+#if gcdBUFFERED_OUTPUT
+ int start;
+ int index;
+ int count;
+ u8 buffer[gcdBUFFERED_SIZE];
+#endif
+
+ gcsBUFFERED_OUTPUT_PTR prev;
+ gcsBUFFERED_OUTPUT_PTR next;
+}
+gcsBUFFERED_OUTPUT;
+
+typedef unsigned int (* gcfPRINTSTRING) (
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN gcsBUFITEM_HEAD_PTR Item
+ );
+
+typedef int (* gcfGETITEMSIZE) (
+ IN gcsBUFITEM_HEAD_PTR Item
+ );
+
+/******************************************************************************\
+******************************* Private Variables ******************************
+\******************************************************************************/
+
+static gcsBUFFERED_OUTPUT _outputBuffer[gcdTHREAD_BUFFERS];
+static gcsBUFFERED_OUTPUT_PTR _outputBufferHead = NULL;
+static gcsBUFFERED_OUTPUT_PTR _outputBufferTail = NULL;
+
+/******************************************************************************\
+****************************** Item Size Functions *****************************
+\******************************************************************************/
+
+#if gcdBUFFERED_OUTPUT
+static int
+_GetTerminatorItemSize(
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+ return sizeof(gcsBUFITEM_HEAD);
+}
+
+static int
+_GetPrefixItemSize(
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+#if gcdHAVEPREFIX
+ gcsBUFITEM_PREFIX_PTR item = (gcsBUFITEM_PREFIX_PTR) Item;
+ unsigned int vlen = ((u8 *) item->prefixData) - ((u8 *) item);
+ return vlen + gcdPREFIX_SIZE;
+#else
+ return sizeof(gcsBUFITEM_PREFIX);
+#endif
+}
+
+static int
+_GetStringItemSize(
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+ gcsBUFITEM_STRING_PTR item = (gcsBUFITEM_STRING_PTR) Item;
+ unsigned int vlen = ((u8 *) item->messageData) - ((u8 *) item);
+ return vlen + item->messageDataSize;
+}
+
+static int
+_GetCopyItemSize(
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+ gcsBUFITEM_COPY_PTR item = (gcsBUFITEM_COPY_PTR) Item;
+ unsigned int vlen = ((u8 *) item->messageData) - ((u8 *) item);
+ return vlen + item->messageDataSize;
+}
+
+static int
+_GetBufferItemSize(
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+#if gcdHAVEPREFIX
+ gcsBUFITEM_BUFFER_PTR item = (gcsBUFITEM_BUFFER_PTR) Item;
+ unsigned int vlen = ((u8 *) item->prefixData) - ((u8 *) item);
+ return vlen + gcdPREFIX_SIZE + item->dataSize;
+#else
+ gcsBUFITEM_BUFFER_PTR item = (gcsBUFITEM_BUFFER_PTR) Item;
+ return sizeof(gcsBUFITEM_BUFFER) + item->dataSize;
+#endif
+}
+
+static gcfGETITEMSIZE _itemSize[] =
+{
+ _GetTerminatorItemSize,
+ _GetPrefixItemSize,
+ _GetStringItemSize,
+ _GetCopyItemSize,
+ _GetBufferItemSize
+};
+#endif
+
+/******************************************************************************\
+******************************* Printing Functions *****************************
+\******************************************************************************/
+
+#if gcdDEBUG || gcdBUFFERED_OUTPUT
+static void
+_DirectPrint(
+ const char *Message,
+ ...
+ )
+{
+ int len;
+ char buffer[768];
+ gctARGUMENTS arguments;
+
+ gcmkARGUMENTS_START(arguments, Message);
+ len = gcmkVSPRINTF(buffer, sizeof(buffer), Message, arguments);
+ gcmkARGUMENTS_END(arguments);
+
+ buffer[len] = '\0';
+ gcmkOUTPUT_STRING(buffer);
+}
+#endif
+
+static int
+_AppendIndent(
+ IN int Indent,
+ IN char * Buffer,
+ IN int BufferSize
+ )
+{
+ int i;
+
+ int len = 0;
+ int indent = Indent % 40;
+
+ for (i = 0; i < indent; i += 1)
+ {
+ Buffer[len++] = ' ';
+ }
+
+ if (indent != Indent)
+ {
+ len += gcmkSPRINTF(
+ Buffer + len, BufferSize - len, " <%d> ", Indent
+ );
+
+ Buffer[len] = '\0';
+ }
+
+ return len;
+}
+
+#if gcdHAVEPREFIX
+static void
+_PrintPrefix(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN void *Data
+ )
+{
+ char buffer[768];
+ int len;
+
+ /* Format the string. */
+ len = gcmkVSPRINTF(buffer, sizeof(buffer), _prefixFormat, Data);
+ buffer[len] = '\0';
+
+ /* Print the string. */
+ gcmkOUTPUT_STRING(buffer);
+}
+#endif
+
+static void
+_PrintString(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN int Indent,
+ IN const char *Message,
+ IN unsigned int ArgumentSize,
+ IN void *Data
+ )
+{
+ char buffer[768];
+ int len;
+
+ /* Append the indent string. */
+ len = _AppendIndent(Indent, buffer, sizeof(buffer));
+
+ /* Format the string. */
+ len += gcmkVSPRINTF(buffer + len, sizeof(buffer) - len, Message, Data);
+ buffer[len] = '\0';
+
+ /* Add end-of-line if missing. */
+ if (buffer[len - 1] != '\n')
+ {
+ buffer[len++] = '\n';
+ buffer[len] = '\0';
+ }
+
+ /* Print the string. */
+ gcmkOUTPUT_STRING(buffer);
+}
+
+static void
+_PrintBuffer(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN int Indent,
+ IN void *PrefixData,
+ IN void *Data,
+ IN unsigned int Address,
+ IN unsigned int DataSize,
+ IN gceDUMP_BUFFER Type,
+ IN u32 DmaAddress
+ )
+{
+ static const char *_titleString[] =
+ {
+ "CONTEXT BUFFER",
+ "USER COMMAND BUFFER",
+ "KERNEL COMMAND BUFFER",
+ "LINK BUFFER",
+ "WAIT LINK BUFFER",
+ ""
+ };
+
+ static const int COLUMN_COUNT = 8;
+
+ unsigned int i, count, column, address;
+ u32 *data;
+ char buffer[768];
+ unsigned int indent, len;
+ int command;
+
+ /* Append space for the prefix. */
+#if gcdHAVEPREFIX
+ indent = gcmkVSPRINTF(buffer, sizeof(buffer), _prefixFormat, PrefixData);
+ buffer[indent] = '\0';
+#else
+ indent = 0;
+#endif
+
+ /* Append the indent string. */
+ indent += _AppendIndent(
+ Indent, buffer + indent, sizeof(buffer) - indent
+ );
+
+ switch (Type)
+ {
+ case gceDUMP_BUFFER_CONTEXT:
+ case gceDUMP_BUFFER_USER:
+ case gceDUMP_BUFFER_KERNEL:
+ case gceDUMP_BUFFER_LINK:
+ case gceDUMP_BUFFER_WAITLINK:
+ /* Form and print the title string. */
+ gcmkSPRINTF2(
+ buffer + indent, sizeof(buffer) - indent,
+ "%s%s\n", _titleString[Type],
+ ((DmaAddress >= Address) && (DmaAddress < Address + DataSize))
+ ? " (CURRENT)" : ""
+ );
+
+ gcmkOUTPUT_STRING(buffer);
+
+ /* Terminate the string. */
+ buffer[indent] = '\0';
+
+ /* This is a command buffer. */
+ command = gcvTRUE;
+ break;
+
+ case gceDUMP_BUFFER_FROM_USER:
+ /* This is not a command buffer. */
+ command = gcvFALSE;
+
+ /* No title. */
+ break;
+
+ default:
+ gcmDBGASSERT(gcvFALSE, "%s", "invalid buffer type");
+
+ /* This is not a command buffer. */
+ command = gcvFALSE;
+ }
+
+ /* Overwrite the prefix with spaces. */
+ for (i = 0; i < indent; i += 1)
+ {
+ buffer[i] = ' ';
+ }
+
+ /* Form and print the opening string. */
+ if (command)
+ {
+ gcmkSPRINTF2(
+ buffer + indent, sizeof(buffer) - indent,
+ "@[kernel.command %08X %08X\n", Address, DataSize
+ );
+
+ gcmkOUTPUT_STRING(buffer);
+
+ /* Terminate the string. */
+ buffer[indent] = '\0';
+ }
+
+ /* Get initial address. */
+ address = Address;
+
+ /* Cast the data pointer. */
+ data = (u32 *) Data;
+
+ /* Compute the number of double words. */
+ count = DataSize / sizeof(u32);
+
+ /* Print the buffer. */
+ for (i = 0, len = indent, column = 0; i < count; i += 1)
+ {
+ /* Append the address. */
+ if (column == 0)
+ {
+ len += gcmkSPRINTF(
+ buffer + len, sizeof(buffer) - len, "0x%08X:", address
+ );
+ }
+
+ /* Append the data value. */
+ len += gcmkSPRINTF2(
+ buffer + len, sizeof(buffer) - len, "%c%08X",
+ (address == DmaAddress)? '>' : ' ', data[i]
+ );
+
+ buffer[len] = '\0';
+
+ /* Update the address. */
+ address += sizeof(u32);
+
+ /* Advance column count. */
+ column += 1;
+
+ /* End of line? */
+ if ((column % COLUMN_COUNT) == 0)
+ {
+ /* Append EOL. */
+ gcmkSTRCAT(buffer + len, sizeof(buffer) - len, "\n");
+
+ /* Print the string. */
+ gcmkOUTPUT_STRING(buffer);
+
+ /* Reset. */
+ len = indent;
+ column = 0;
+ }
+ }
+
+ /* Print the last partial string. */
+ if (column != 0)
+ {
+ /* Append EOL. */
+ gcmkSTRCAT(buffer + len, sizeof(buffer) - len, "\n");
+
+ /* Print the string. */
+ gcmkOUTPUT_STRING(buffer);
+ }
+
+ /* Form and print the opening string. */
+ if (command)
+ {
+ buffer[indent] = '\0';
+ gcmkSTRCAT(buffer, sizeof(buffer), "] -- command\n");
+ gcmkOUTPUT_STRING(buffer);
+ }
+}
+
+#if gcdBUFFERED_OUTPUT
+static unsigned int
+_PrintNone(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+ /* Return the size of the node. */
+ return sizeof(gcsBUFITEM_HEAD);
+}
+
+static unsigned int
+_PrintPrefixWrapper(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+#if gcdHAVEPREFIX
+ gcsBUFITEM_PREFIX_PTR item;
+ unsigned int vlen;
+
+ /* Get access to the data. */
+ item = (gcsBUFITEM_PREFIX_PTR) Item;
+
+ /* Print the message. */
+ _PrintPrefix(OutputBuffer, item->prefixData);
+
+ /* Compute the size of the variable portion of the structure. */
+ vlen = ((u8 *) item->prefixData) - ((u8 *) item);
+
+ /* Return the size of the node. */
+ return vlen + gcdPREFIX_SIZE;
+#else
+ return sizeof(gcsBUFITEM_PREFIX);
+#endif
+}
+
+static unsigned int
+_PrintStringWrapper(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+ gcsBUFITEM_STRING_PTR item;
+ unsigned int vlen;
+
+ /* Get access to the data. */
+ item = (gcsBUFITEM_STRING_PTR) Item;
+
+ /* Print the message. */
+ _PrintString(
+ OutputBuffer,
+ item->indent, item->message, item->messageDataSize, item->messageData
+ );
+
+ /* Compute the size of the variable portion of the structure. */
+ vlen = ((u8 *) item->messageData) - ((u8 *) item);
+
+ /* Return the size of the node. */
+ return vlen + item->messageDataSize;
+}
+
+static unsigned int
+_PrintCopyWrapper(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+ gcsBUFITEM_COPY_PTR item;
+ const char *message;
+ unsigned int vlen;
+
+ /* Get access to the data. */
+ item = (gcsBUFITEM_COPY_PTR) Item;
+
+ /* Determine the string pointer. */
+ message = (const char *) (item + 1);
+
+ /* Print the message. */
+ _PrintString(
+ OutputBuffer,
+ item->indent, message, item->messageDataSize, item->messageData
+ );
+
+ /* Compute the size of the variable portion of the structure. */
+ vlen = ((u8 *) item->messageData) - ((u8 *) item);
+
+ /* Return the size of the node. */
+ return vlen + item->messageDataSize;
+}
+
+static unsigned int
+_PrintBufferWrapper(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN gcsBUFITEM_HEAD_PTR Item
+ )
+{
+#if gcdHAVEPREFIX
+ u32 dmaAddress;
+ gcsBUFITEM_BUFFER_PTR item;
+ void *data;
+ unsigned int vlen;
+
+ /* Get access to the data. */
+ item = (gcsBUFITEM_BUFFER_PTR) Item;
+
+#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1)
+ dmaAddress = item->dmaAddress;
+#else
+ dmaAddress = 0xFFFFFFFF;
+#endif
+
+ if (dmaAddress != 0)
+ {
+ /* Compute the data address. */
+ data = ((u8 *) item->prefixData) + gcdPREFIX_SIZE;
+
+ /* Print buffer. */
+ _PrintBuffer(
+ OutputBuffer,
+ item->indent, item->prefixData,
+ data, item->address, item->dataSize,
+ item->bufferType, dmaAddress
+ );
+ }
+
+ /* Compute the size of the variable portion of the structure. */
+ vlen = ((u8 *) item->prefixData) - ((u8 *) item);
+
+ /* Return the size of the node. */
+ return vlen + gcdPREFIX_SIZE + item->dataSize;
+#else
+ u32 dmaAddress;
+ gcsBUFITEM_BUFFER_PTR item;
+
+ /* Get access to the data. */
+ item = (gcsBUFITEM_BUFFER_PTR) Item;
+
+#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1)
+ dmaAddress = item->dmaAddress;
+#else
+ dmaAddress = 0xFFFFFFFF;
+#endif
+
+ if (dmaAddress != 0)
+ {
+ /* Print buffer. */
+ _PrintBuffer(
+ OutputBuffer,
+ item->indent, NULL,
+ item + 1, item->address, item->dataSize,
+ item->bufferType, dmaAddress
+ );
+ }
+
+ /* Return the size of the node. */
+ return sizeof(gcsBUFITEM_BUFFER) + item->dataSize;
+#endif
+}
+
+static gcfPRINTSTRING _printArray[] =
+{
+ _PrintNone,
+ _PrintPrefixWrapper,
+ _PrintStringWrapper,
+ _PrintCopyWrapper,
+ _PrintBufferWrapper
+};
+#endif
+
+/******************************************************************************\
+******************************* Private Functions ******************************
+\******************************************************************************/
+
+#if gcdBUFFERED_OUTPUT
+
+#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1)
+static gcsBUFITEM_BUFFER_PTR
+_FindCurrentDMABuffer(
+ u32 DmaAddress
+ )
+{
+ int i, skip;
+ gcsBUFITEM_HEAD_PTR item;
+ gcsBUFITEM_BUFFER_PTR dmaCurrent;
+
+ /* Reset the current buffer. */
+ dmaCurrent = NULL;
+
+ /* Get the first stored item. */
+ item = (gcsBUFITEM_HEAD_PTR) &_outputBufferHead->buffer[_outputBufferHead->start];
+
+ /* Run through all items. */
+ for (i = 0; i < _outputBufferHead->count; i += 1)
+ {
+ /* Buffer item? */
+ if (item->type == gcvBUFITEM_BUFFER)
+ {
+ gcsBUFITEM_BUFFER_PTR buffer = (gcsBUFITEM_BUFFER_PTR) item;
+
+ if ((DmaAddress >= buffer->address) &&
+ (DmaAddress < buffer->address + buffer->dataSize))
+ {
+ dmaCurrent = buffer;
+ }
+ }
+
+ /* Get the item size and skip it. */
+ skip = (* _itemSize[item->type]) (item);
+ item = (gcsBUFITEM_HEAD_PTR) ((u8 *) item + skip);
+
+ /* End of the buffer? Wrap around. */
+ if (item->type == gceBUFITEM_NONE)
+ {
+ item = (gcsBUFITEM_HEAD_PTR) _outputBufferHead->buffer;
+ }
+ }
+
+ /* Return result. */
+ return dmaCurrent;
+}
+
+static void
+_EnableAllDMABuffers(
+ void
+ )
+{
+ int i, skip;
+ gcsBUFITEM_HEAD_PTR item;
+
+ /* Get the first stored item. */
+ item = (gcsBUFITEM_HEAD_PTR) &_outputBufferHead->buffer[_outputBufferHead->start];
+
+ /* Run through all items. */
+ for (i = 0; i < _outputBufferHead->count; i += 1)
+ {
+ /* Buffer item? */
+ if (item->type == gcvBUFITEM_BUFFER)
+ {
+ gcsBUFITEM_BUFFER_PTR buffer = (gcsBUFITEM_BUFFER_PTR) item;
+
+ /* Enable the buffer. */
+ buffer->dmaAddress = ~0U;
+ }
+
+ /* Get the item size and skip it. */
+ skip = (* _itemSize[item->type]) (item);
+ item = (gcsBUFITEM_HEAD_PTR) ((u8 *) item + skip);
+
+ /* End of the buffer? Wrap around. */
+ if (item->type == gceBUFITEM_NONE)
+ {
+ item = (gcsBUFITEM_HEAD_PTR) _outputBufferHead->buffer;
+ }
+ }
+}
+
+static void
+_EnableDMABuffers(
+ u32 DmaAddress,
+ gcsBUFITEM_BUFFER_PTR CurrentDMABuffer
+ )
+{
+ int i, skip, index;
+ gcsBUFITEM_HEAD_PTR item;
+ gcsBUFITEM_BUFFER_PTR buffers[gcdDMA_BUFFER_COUNT];
+
+ /* Reset buffer pointers. */
+ gckOS_ZeroMemory(buffers, sizeof(buffers));
+
+ /* Set the current buffer index. */
+ index = -1;
+
+ /* Get the first stored item. */
+ item = (gcsBUFITEM_HEAD_PTR) &_outputBufferHead->buffer[_outputBufferHead->start];
+
+ /* Run through all items until the current DMA buffer is found. */
+ for (i = 0; i < _outputBufferHead->count; i += 1)
+ {
+ /* Buffer item? */
+ if (item->type == gcvBUFITEM_BUFFER)
+ {
+ /* Advance the index. */
+ index = (index + 1) % gcdDMA_BUFFER_COUNT;
+
+ /* Add to the buffer array. */
+ buffers[index] = (gcsBUFITEM_BUFFER_PTR) item;
+
+ /* Stop if this is the current DMA buffer. */
+ if ((gcsBUFITEM_BUFFER_PTR) item == CurrentDMABuffer)
+ {
+ break;
+ }
+ }
+
+ /* Get the item size and skip it. */
+ skip = (* _itemSize[item->type]) (item);
+ item = (gcsBUFITEM_HEAD_PTR) ((u8 *) item + skip);
+
+ /* End of the buffer? Wrap around. */
+ if (item->type == gceBUFITEM_NONE)
+ {
+ item = (gcsBUFITEM_HEAD_PTR) _outputBufferHead->buffer;
+ }
+ }
+
+ /* Enable the found buffers. */
+ gcmDBGASSERT(index != -1, "%d", index);
+
+ for (i = 0; i < gcdDMA_BUFFER_COUNT; i += 1)
+ {
+ if (buffers[index] == NULL)
+ {
+ break;
+ }
+
+ buffers[index]->dmaAddress = DmaAddress;
+
+ index -= 1;
+
+ if (index == -1)
+ {
+ index = gcdDMA_BUFFER_COUNT - 1;
+ }
+ }
+}
+#endif
+
+static void
+_Flush(
+ u32 DmaAddress
+ )
+{
+ int i, skip;
+ gcsBUFITEM_HEAD_PTR item;
+
+ gcsBUFFERED_OUTPUT_PTR outputBuffer = _outputBufferHead;
+
+#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1)
+ if ((outputBuffer != NULL) && (outputBuffer->count != 0))
+ {
+ /* Find the current DMA buffer. */
+ gcsBUFITEM_BUFFER_PTR dmaCurrent = _FindCurrentDMABuffer(DmaAddress);
+
+ /* Was the current buffer found? */
+ if (dmaCurrent == NULL)
+ {
+ /* No, print all buffers. */
+ _EnableAllDMABuffers();
+ }
+ else
+ {
+ /* Yes, enable only specified number of buffers. */
+ _EnableDMABuffers(DmaAddress, dmaCurrent);
+ }
+ }
+#endif
+
+ while (outputBuffer != NULL)
+ {
+ if (outputBuffer->count != 0)
+ {
+ _DirectPrint("********************************************************************************\n");
+ _DirectPrint("FLUSHING DEBUG OUTPUT BUFFER (%d elements).\n", outputBuffer->count);
+ _DirectPrint("********************************************************************************\n");
+
+ item = (gcsBUFITEM_HEAD_PTR) &outputBuffer->buffer[outputBuffer->start];
+
+ for (i = 0; i < outputBuffer->count; i += 1)
+ {
+ skip = (* _printArray[item->type]) (outputBuffer, item);
+
+ item = (gcsBUFITEM_HEAD_PTR) ((u8 *) item + skip);
+
+ if (item->type == gceBUFITEM_NONE)
+ {
+ item = (gcsBUFITEM_HEAD_PTR) outputBuffer->buffer;
+ }
+ }
+
+ outputBuffer->start = 0;
+ outputBuffer->index = 0;
+ outputBuffer->count = 0;
+ }
+
+ outputBuffer = outputBuffer->next;
+ }
+}
+
+static gcsBUFITEM_HEAD_PTR
+_AllocateItem(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN int Size
+ )
+{
+ int skip;
+ gcsBUFITEM_HEAD_PTR item, next;
+
+#if gcdENABLE_OVERFLOW
+ if (
+ (OutputBuffer->index + Size >= gcdBUFFERED_SIZE - sizeof(gcsBUFITEM_HEAD))
+ ||
+ (
+ (OutputBuffer->index < OutputBuffer->start) &&
+ (OutputBuffer->index + Size >= OutputBuffer->start)
+ )
+ )
+ {
+ if (OutputBuffer->index + Size >= gcdBUFFERED_SIZE - sizeof(gcsBUFITEM_HEAD))
+ {
+ if (OutputBuffer->index < OutputBuffer->start)
+ {
+ item = (gcsBUFITEM_HEAD_PTR) &OutputBuffer->buffer[OutputBuffer->start];
+
+ while (item->type != gceBUFITEM_NONE)
+ {
+ skip = (* _itemSize[item->type]) (item);
+
+ OutputBuffer->start += skip;
+ OutputBuffer->count -= 1;
+
+ item->type = gceBUFITEM_NONE;
+ item = (gcsBUFITEM_HEAD_PTR) ((u8 *) item + skip);
+ }
+
+ OutputBuffer->start = 0;
+ }
+
+ OutputBuffer->index = 0;
+ }
+
+ item = (gcsBUFITEM_HEAD_PTR) &OutputBuffer->buffer[OutputBuffer->start];
+
+ while (OutputBuffer->start - OutputBuffer->index <= Size)
+ {
+ skip = (* _itemSize[item->type]) (item);
+
+ OutputBuffer->start += skip;
+ OutputBuffer->count -= 1;
+
+ item->type = gceBUFITEM_NONE;
+ item = (gcsBUFITEM_HEAD_PTR) ((u8 *) item + skip);
+
+ if (item->type == gceBUFITEM_NONE)
+ {
+ OutputBuffer->start = 0;
+ break;
+ }
+ }
+ }
+#else
+ if (OutputBuffer->index + Size > gcdBUFFERED_SIZE - sizeof(gcsBUFITEM_HEAD))
+ {
+ _DirectPrint("\nMessage buffer full; forcing message flush.\n\n");
+ _Flush(~0U);
+ }
+#endif
+
+ item = (gcsBUFITEM_HEAD_PTR) &OutputBuffer->buffer[OutputBuffer->index];
+
+ OutputBuffer->index += Size;
+ OutputBuffer->count += 1;
+
+ next = (gcsBUFITEM_HEAD_PTR) ((u8 *) item + Size);
+ next->type = gceBUFITEM_NONE;
+
+ return item;
+}
+
+#if gcdALIGNBYSIZE
+static void
+_FreeExtraSpace(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN void *Item,
+ IN int ItemSize,
+ IN int FreeSize
+ )
+{
+ gcsBUFITEM_HEAD_PTR next;
+
+ OutputBuffer->index -= FreeSize;
+
+ next = (gcsBUFITEM_HEAD_PTR) ((u8 *) Item + ItemSize);
+ next->type = gceBUFITEM_NONE;
+}
+#endif
+
+#if gcdHAVEPREFIX
+static void
+_AppendPrefix(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN void *Data
+ )
+{
+ u8 *prefixData;
+ gcsBUFITEM_PREFIX_PTR item;
+ int allocSize;
+
+#if gcdALIGNBYSIZE
+ unsigned int alignment;
+ int size, freeSize;
+#endif
+
+ gcmDBGASSERT(Data != NULL, "%p", Data);
+
+ /* Determine the maximum item size. */
+ allocSize
+ = sizeof(gcsBUFITEM_PREFIX)
+ + gcdPREFIX_SIZE
+ + gcdPREFIX_ALIGNMENT;
+
+ /* Allocate prefix item. */
+ item = (gcsBUFITEM_PREFIX_PTR) _AllocateItem(OutputBuffer, allocSize);
+
+ /* Compute the initial prefix data pointer. */
+ prefixData = (u8 *) (item + 1);
+
+ /* Align the data pointer as necessary. */
+#if gcdALIGNBYSIZE
+ alignment = gcmPTRALIGNMENT(prefixData, gcdPREFIX_ALIGNMENT);
+ prefixData += alignment;
+#endif
+
+ /* Set item data. */
+ item->type = gcvBUFITEM_PREFIX;
+ item->prefixData = prefixData;
+
+ /* Copy argument value. */
+ memcpy(prefixData, Data, gcdPREFIX_SIZE);
+
+#if gcdALIGNBYSIZE
+ /* Compute the actual node size. */
+ size = sizeof(gcsBUFITEM_PREFIX) + gcdPREFIX_SIZE + alignment;
+
+ /* Free extra memory if any. */
+ freeSize = allocSize - size;
+ if (freeSize != 0)
+ {
+ _FreeExtraSpace(OutputBuffer, item, size, freeSize);
+ }
+#endif
+}
+#endif
+
+static void
+_AppendString(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN int Indent,
+ IN const char *Message,
+ IN unsigned int ArgumentSize,
+ IN void *Data
+ )
+{
+ u8 *messageData;
+ gcsBUFITEM_STRING_PTR item;
+ int allocSize;
+
+#if gcdALIGNBYSIZE
+ unsigned int alignment;
+ int size, freeSize;
+#endif
+
+ /* Determine the maximum item size. */
+ allocSize
+ = sizeof(gcsBUFITEM_STRING)
+ + ArgumentSize
+ + gcdVARARG_ALIGNMENT;
+
+ /* Allocate prefix item. */
+ item = (gcsBUFITEM_STRING_PTR) _AllocateItem(OutputBuffer, allocSize);
+
+ /* Compute the initial message data pointer. */
+ messageData = (u8 *) (item + 1);
+
+ /* Align the data pointer as necessary. */
+#if gcdALIGNBYSIZE
+ alignment = gcmPTRALIGNMENT(messageData, gcdVARARG_ALIGNMENT);
+ messageData += alignment;
+#endif
+
+ /* Set item data. */
+ item->type = gcvBUFITEM_STRING;
+ item->indent = Indent;
+ item->message = Message;
+ item->messageData = messageData;
+ item->messageDataSize = ArgumentSize;
+
+ /* Copy argument value. */
+ if (ArgumentSize != 0)
+ {
+ memcpy(messageData, Data, ArgumentSize);
+ }
+
+#if gcdALIGNBYSIZE
+ /* Compute the actual node size. */
+ size = sizeof(gcsBUFITEM_STRING) + ArgumentSize + alignment;
+
+ /* Free extra memory if any. */
+ freeSize = allocSize - size;
+ if (freeSize != 0)
+ {
+ _FreeExtraSpace(OutputBuffer, item, size, freeSize);
+ }
+#endif
+}
+
+static void
+_AppendCopy(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN int Indent,
+ IN const char *Message,
+ IN unsigned int ArgumentSize,
+ IN void *Data
+ )
+{
+ u8 *messageData;
+ gcsBUFITEM_COPY_PTR item;
+ int allocSize;
+ int messageLength;
+ const char *message;
+
+#if gcdALIGNBYSIZE
+ unsigned int alignment;
+ int size, freeSize;
+#endif
+
+ /* Get the length of the string. */
+ messageLength = strlen(Message) + 1;
+
+ /* Determine the maximum item size. */
+ allocSize
+ = sizeof(gcsBUFITEM_COPY)
+ + messageLength
+ + ArgumentSize
+ + gcdVARARG_ALIGNMENT;
+
+ /* Allocate prefix item. */
+ item = (gcsBUFITEM_COPY_PTR) _AllocateItem(OutputBuffer, allocSize);
+
+ /* Determine the message placement. */
+ message = (const char *) (item + 1);
+
+ /* Compute the initial message data pointer. */
+ messageData = (u8 *) message + messageLength;
+
+ /* Align the data pointer as necessary. */
+#if gcdALIGNBYSIZE
+ if (ArgumentSize == 0)
+ {
+ alignment = 0;
+ }
+ else
+ {
+ alignment = gcmPTRALIGNMENT(messageData, gcdVARARG_ALIGNMENT);
+ messageData += alignment;
+ }
+#endif
+
+ /* Set item data. */
+ item->type = gcvBUFITEM_COPY;
+ item->indent = Indent;
+ item->messageData = messageData;
+ item->messageDataSize = ArgumentSize;
+
+ /* Copy the message. */
+ memcpy((void *) message, Message, messageLength);
+
+ /* Copy argument value. */
+ if (ArgumentSize != 0)
+ {
+ memcpy(messageData, Data, ArgumentSize);
+ }
+
+#if gcdALIGNBYSIZE
+ /* Compute the actual node size. */
+ size
+ = sizeof(gcsBUFITEM_COPY)
+ + messageLength
+ + ArgumentSize
+ + alignment;
+
+ /* Free extra memory if any. */
+ freeSize = allocSize - size;
+ if (freeSize != 0)
+ {
+ _FreeExtraSpace(OutputBuffer, item, size, freeSize);
+ }
+#endif
+}
+
+static void
+_AppendBuffer(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN int Indent,
+ IN void *PrefixData,
+ IN void *Data,
+ IN unsigned int Address,
+ IN unsigned int DataSize,
+ IN gceDUMP_BUFFER Type,
+ IN u32 DmaAddress
+ )
+{
+#if gcdHAVEPREFIX
+ u8 *prefixData;
+ gcsBUFITEM_BUFFER_PTR item;
+ int allocSize;
+ void *data;
+
+#if gcdALIGNBYSIZE
+ unsigned int alignment;
+ int size, freeSize;
+#endif
+
+ gcmDBGASSERT(DataSize != 0, "%d", DataSize);
+ gcmDBGASSERT(Data != NULL, "%p", Data);
+
+ /* Determine the maximum item size. */
+ allocSize
+ = sizeof(gcsBUFITEM_BUFFER)
+ + gcdPREFIX_SIZE
+ + gcdPREFIX_ALIGNMENT
+ + DataSize;
+
+ /* Allocate prefix item. */
+ item = (gcsBUFITEM_BUFFER_PTR) _AllocateItem(OutputBuffer, allocSize);
+
+ /* Compute the initial prefix data pointer. */
+ prefixData = (u8 *) (item + 1);
+
+#if gcdALIGNBYSIZE
+ /* Align the data pointer as necessary. */
+ alignment = gcmPTRALIGNMENT(prefixData, gcdPREFIX_ALIGNMENT);
+ prefixData += alignment;
+#endif
+
+ /* Set item data. */
+ item->type = gcvBUFITEM_BUFFER;
+ item->indent = Indent;
+ item->bufferType = Type;
+ item->dataSize = DataSize;
+ item->address = Address;
+ item->prefixData = prefixData;
+
+#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1)
+ item->dmaAddress = DmaAddress;
+#endif
+
+ /* Copy prefix data. */
+ memcpy(prefixData, PrefixData, gcdPREFIX_SIZE);
+
+ /* Compute the data pointer. */
+ data = prefixData + gcdPREFIX_SIZE;
+
+ /* Copy argument value. */
+ memcpy(data, Data, DataSize);
+
+#if gcdALIGNBYSIZE
+ /* Compute the actual node size. */
+ size
+ = sizeof(gcsBUFITEM_BUFFER)
+ + gcdPREFIX_SIZE
+ + alignment
+ + DataSize;
+
+ /* Free extra memory if any. */
+ freeSize = allocSize - size;
+ if (freeSize != 0)
+ {
+ _FreeExtraSpace(OutputBuffer, item, size, freeSize);
+ }
+#endif
+#else
+ gcsBUFITEM_BUFFER_PTR item;
+ int size;
+
+ gcmDBGASSERT(DataSize != 0, "%d", DataSize);
+ gcmDBGASSERT(Data != NULL, "%p", Data);
+
+ /* Determine the maximum item size. */
+ size = sizeof(gcsBUFITEM_BUFFER) + DataSize;
+
+ /* Allocate prefix item. */
+ item = (gcsBUFITEM_BUFFER_PTR) _AllocateItem(OutputBuffer, size);
+
+ /* Set item data. */
+ item->type = gcvBUFITEM_BUFFER;
+ item->indent = Indent;
+ item->dataSize = DataSize;
+ item->address = Address;
+
+ /* Copy argument value. */
+ memcpy(item + 1, Data, DataSize);
+#endif
+}
+#endif
+
+static void
+_InitBuffers(
+ void
+ )
+{
+ int i;
+
+ if (_outputBufferHead == NULL)
+ {
+ for (i = 0; i < gcdTHREAD_BUFFERS; i += 1)
+ {
+ if (_outputBufferTail == NULL)
+ {
+ _outputBufferHead = &_outputBuffer[i];
+ }
+ else
+ {
+ _outputBufferTail->next = &_outputBuffer[i];
+ }
+
+#if gcdTHREAD_BUFFERS > 1
+ _outputBuffer[i].threadID = ~0U;
+#endif
+
+ _outputBuffer[i].prev = _outputBufferTail;
+ _outputBuffer[i].next = NULL;
+
+ _outputBufferTail = &_outputBuffer[i];
+ }
+ }
+}
+
+static gcsBUFFERED_OUTPUT_PTR
+_GetOutputBuffer(
+ void
+ )
+{
+ gcsBUFFERED_OUTPUT_PTR outputBuffer;
+
+#if gcdTHREAD_BUFFERS > 1
+ /* Get the current thread ID. */
+ u32 threadID = gcmkGETTHREADID();
+
+ /* Locate the output buffer for the thread. */
+ outputBuffer = _outputBufferHead;
+
+ while (outputBuffer != NULL)
+ {
+ if (outputBuffer->threadID == ThreadID)
+ {
+ break;
+ }
+
+ outputBuffer = outputBuffer->next;
+ }
+
+ /* No matching buffer found? */
+ if (outputBuffer == NULL)
+ {
+ /* Get the tail for the buffer. */
+ outputBuffer = _outputBufferTail;
+
+ /* Move it to the head. */
+ _outputBufferTail = _outputBufferTail->prev;
+ _outputBufferTail->next = NULL;
+
+ outputBuffer->prev = NULL;
+ outputBuffer->next = _outputBufferHead;
+
+ _outputBufferHead->prev = outputBuffer;
+ _outputBufferHead = outputBuffer;
+
+ /* Reset the buffer. */
+ outputBuffer->threadID = ThreadID;
+ outputBuffer->start = 0;
+ outputBuffer->index = 0;
+ outputBuffer->count = 0;
+ outputBuffer->lineNumber = 0;
+ }
+#else
+ outputBuffer = _outputBufferHead;
+#endif
+
+ return outputBuffer;
+}
+
+static int _GetArgumentSize(
+ IN const char *Message
+ )
+{
+ int i, count;
+
+ gcmDBGASSERT(Message != NULL, "%p", Message);
+
+ for (i = 0, count = 0; Message[i]; i += 1)
+ {
+ if (Message[i] == '%')
+ {
+ count += 1;
+ }
+ }
+
+ return count * sizeof(u32);
+}
+
+#if gcdHAVEPREFIX
+static void
+_InitPrefixData(
+ IN gcsBUFFERED_OUTPUT_PTR OutputBuffer,
+ IN void *Data
+ )
+{
+ u8 *data = (u8 *) Data;
+
+#if gcdSHOW_TIME
+ {
+ u64 time;
+ gckOS_GetProfileTick(&time);
+ gcmkALIGNPTR(u8 *, data, sizeof(u64));
+ * ((u64 *) data) = time;
+ data += sizeof(u64);
+ }
+#endif
+
+#if gcdSHOW_LINE_NUMBER
+ {
+ gcmkALIGNPTR(u8 *, data, sizeof(u64));
+ * ((u64 *) data) = OutputBuffer->lineNumber;
+ data += sizeof(u64);
+ }
+#endif
+
+#if gcdSHOW_PROCESS_ID
+ {
+ gcmkALIGNPTR(u8 *, data, sizeof(u32));
+ * ((u32 *) data) = gcmkGETPROCESSID();
+ data += sizeof(u32);
+ }
+#endif
+
+#if gcdSHOW_THREAD_ID
+ {
+ gcmkALIGNPTR(u8 *, data, sizeof(u32));
+ * ((u32 *) data) = gcmkGETTHREADID();
+ }
+#endif
+}
+#endif
+
+static void
+_Print(
+ IN unsigned int ArgumentSize,
+ IN int CopyMessage,
+ IN const char *Message,
+ IN gctARGUMENTS Arguments
+ )
+{
+ gcsBUFFERED_OUTPUT_PTR outputBuffer;
+ gcmkDECLARE_LOCK(lockHandle);
+
+ gcmkLOCKSECTION(lockHandle);
+
+ /* Initialize output buffer list. */
+ _InitBuffers();
+
+ /* Locate the proper output buffer. */
+ outputBuffer = _GetOutputBuffer();
+
+ /* Update the line number. */
+#if gcdSHOW_LINE_NUMBER
+ outputBuffer->lineNumber += 1;
+#endif
+
+ /* Print prefix. */
+#if gcdHAVEPREFIX
+ {
+ u8 *alignedPrefixData;
+ u8 prefixData[gcdPREFIX_SIZE + gcdPREFIX_ALIGNMENT];
+
+ /* Compute aligned pointer. */
+ alignedPrefixData = prefixData;
+ gcmkALIGNPTR(u8 *, alignedPrefixData, gcdPREFIX_ALIGNMENT);
+
+ /* Initialize the prefix data. */
+ _InitPrefixData(outputBuffer, alignedPrefixData);
+
+ /* Print the prefix. */
+ gcdOUTPUTPREFIX(outputBuffer, alignedPrefixData);
+ }
+#endif
+
+ /* Form the indent string. */
+ if (strncmp(Message, "--", 2) == 0)
+ {
+ outputBuffer->indent -= 2;
+ }
+
+ /* Print the message. */
+ if (CopyMessage)
+ {
+ gcdOUTPUTCOPY(
+ outputBuffer, outputBuffer->indent,
+ Message, ArgumentSize, * (void **) &Arguments
+ );
+ }
+ else
+ {
+ gcdOUTPUTSTRING(
+ outputBuffer, outputBuffer->indent,
+ Message, ArgumentSize, * (void **) &Arguments
+ );
+ }
+
+ /* Check increasing indent. */
+ if (strncmp(Message, "++", 2) == 0)
+ {
+ outputBuffer->indent += 2;
+ }
+
+ gcmkUNLOCKSECTION(lockHandle);
+}
+
+
+/******************************************************************************\
+********************************* Debug Macros *********************************
+\******************************************************************************/
+
+#define gcmDEBUGPRINT(ArgumentSize, CopyMessage, Message) \
+{ \
+ gctARGUMENTS __arguments__; \
+ gcmkARGUMENTS_START(__arguments__, Message); \
+ _Print(ArgumentSize, CopyMessage, Message, __arguments__); \
+ gcmkARGUMENTS_END(__arguments__); \
+}
+
+/******************************************************************************\
+********************************** Debug Code **********************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckOS_Print
+**
+** Send a message to the debugger.
+**
+** INPUT:
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_Print(
+ IN const char *Message,
+ ...
+ )
+{
+ gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message);
+}
+
+/*******************************************************************************
+**
+** gckOS_PrintN
+**
+** Send a message to the debugger.
+**
+** INPUT:
+**
+** unsigned int ArgumentSize
+** The size of the optional arguments in bytes.
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_PrintN(
+ IN unsigned int ArgumentSize,
+ IN const char *Message,
+ ...
+ )
+{
+ gcmDEBUGPRINT(ArgumentSize, gcvFALSE, Message);
+}
+
+/*******************************************************************************
+**
+** gckOS_CopyPrint
+**
+** Send a message to the debugger. If in buffered output mode, the entire
+** message will be copied into the buffer instead of using the pointer to
+** the string.
+**
+** INPUT:
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_CopyPrint(
+ IN const char *Message,
+ ...
+ )
+{
+ gcmDEBUGPRINT(_GetArgumentSize(Message), gcvTRUE, Message);
+}
+
+/*******************************************************************************
+**
+** gckOS_DumpBuffer
+**
+** Print the contents of the specified buffer.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** void *Buffer
+** Pointer to the buffer to print.
+**
+** unsigned int Size
+** Size of the buffer.
+**
+** gceDUMP_BUFFER Type
+** Buffer type.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_DumpBuffer(
+ IN gckOS Os,
+ IN void *Buffer,
+ IN unsigned int Size,
+ IN gceDUMP_BUFFER Type,
+ IN int CopyMessage
+ )
+{
+ u32 address;
+ gcsBUFFERED_OUTPUT_PTR outputBuffer;
+ static int userLocked;
+ char *buffer = (char*)Buffer;
+
+ gcmkDECLARE_LOCK(lockHandle);
+
+ /* Request lock when not coming from user,
+ or coming from user and not yet locked
+ and message is starting with @[. */
+ if (Type == gceDUMP_BUFFER_FROM_USER)
+ {
+ if ((Size > 2)
+ && (buffer[0] == '@')
+ && (buffer[1] == '['))
+ {
+ /* Beginning of a user dump. */
+ gcmkLOCKSECTION(lockHandle);
+ userLocked = gcvTRUE;
+ }
+ /* Else, let it pass through. */
+ }
+ else
+ {
+ gcmkLOCKSECTION(lockHandle);
+ userLocked = gcvFALSE;
+ }
+
+ if (Buffer != NULL)
+ {
+ /* Initialize output buffer list. */
+ _InitBuffers();
+
+ /* Locate the proper output buffer. */
+ outputBuffer = _GetOutputBuffer();
+
+ /* Update the line number. */
+#if gcdSHOW_LINE_NUMBER
+ outputBuffer->lineNumber += 1;
+#endif
+
+ /* Get the physical address of the buffer. */
+ if (Type != gceDUMP_BUFFER_FROM_USER)
+ {
+ gcmkVERIFY_OK(gckOS_GetPhysicalAddress(Os, Buffer, &address));
+ }
+ else
+ {
+ address = 0;
+ }
+
+#if gcdHAVEPREFIX
+ {
+ u8 *alignedPrefixData;
+ u8 prefixData[gcdPREFIX_SIZE + gcdPREFIX_ALIGNMENT];
+
+ /* Compute aligned pointer. */
+ alignedPrefixData = prefixData;
+ gcmkALIGNPTR(u8 *, alignedPrefixData, gcdPREFIX_ALIGNMENT);
+
+ /* Initialize the prefix data. */
+ _InitPrefixData(outputBuffer, alignedPrefixData);
+
+ /* Print/schedule the buffer. */
+ gcdOUTPUTBUFFER(
+ outputBuffer, outputBuffer->indent,
+ alignedPrefixData, Buffer, address, Size, Type, 0
+ );
+ }
+#else
+ /* Print/schedule the buffer. */
+ if (Type == gceDUMP_BUFFER_FROM_USER)
+ {
+ gcdOUTPUTSTRING(
+ outputBuffer, outputBuffer->indent,
+ Buffer, 0, NULL
+ );
+ }
+ else
+ {
+ gcdOUTPUTBUFFER(
+ outputBuffer, outputBuffer->indent,
+ NULL, Buffer, address, Size, Type, 0
+ );
+ }
+#endif
+ }
+
+ /* Unlock when not coming from user,
+ or coming from user and not yet locked. */
+ if (userLocked)
+ {
+ if ((Size > 4)
+ && (buffer[0] == ']')
+ && (buffer[1] == ' ')
+ && (buffer[2] == '-')
+ && (buffer[3] == '-'))
+ {
+ /* End of a user dump. */
+ gcmkUNLOCKSECTION(lockHandle);
+ userLocked = gcvFALSE;
+ }
+ /* Else, let it pass through, don't unlock. */
+ }
+ else
+ {
+ gcmkUNLOCKSECTION(lockHandle);
+ }
+}
+
+/*******************************************************************************
+**
+** gckOS_DebugTrace
+**
+** Send a leveled message to the debugger.
+**
+** INPUT:
+**
+** u32 Level
+** Debug level of message.
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_DebugTrace(
+ IN u32 Level,
+ IN const char *Message,
+ ...
+ )
+{
+ if (Level > _debugLevel)
+ {
+ return;
+ }
+
+ gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message);
+}
+
+/*******************************************************************************
+**
+** gckOS_DebugTraceN
+**
+** Send a leveled message to the debugger.
+**
+** INPUT:
+**
+** u32 Level
+** Debug level of message.
+**
+** unsigned int ArgumentSize
+** The size of the optional arguments in bytes.
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_DebugTraceN(
+ IN u32 Level,
+ IN unsigned int ArgumentSize,
+ IN const char *Message,
+ ...
+ )
+{
+ if (Level > _debugLevel)
+ {
+ return;
+ }
+
+ gcmDEBUGPRINT(ArgumentSize, gcvFALSE, Message);
+}
+
+/*******************************************************************************
+**
+** gckOS_DebugTraceZone
+**
+** Send a leveled and zoned message to the debugger.
+**
+** INPUT:
+**
+** u32 Level
+** Debug level for message.
+**
+** u32 Zone
+** Debug zone for message.
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_DebugTraceZone(
+ IN u32 Level,
+ IN u32 Zone,
+ IN const char *Message,
+ ...
+ )
+{
+ if ((Level > _debugLevel) || !(Zone & _debugZones))
+ {
+ return;
+ }
+
+ gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message);
+}
+
+/*******************************************************************************
+**
+** gckOS_DebugTraceZoneN
+**
+** Send a leveled and zoned message to the debugger.
+**
+** INPUT:
+**
+** u32 Level
+** Debug level for message.
+**
+** u32 Zone
+** Debug zone for message.
+**
+** unsigned int ArgumentSize
+** The size of the optional arguments in bytes.
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_DebugTraceZoneN(
+ IN u32 Level,
+ IN u32 Zone,
+ IN unsigned int ArgumentSize,
+ IN const char *Message,
+ ...
+ )
+{
+ if ((Level > _debugLevel) || !(Zone & _debugZones))
+ {
+ return;
+ }
+
+ gcmDEBUGPRINT(ArgumentSize, gcvFALSE, Message);
+}
+
+/*******************************************************************************
+**
+** gckOS_DebugBreak
+**
+** Break into the debugger.
+**
+** INPUT:
+**
+** Nothing.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+void
+gckOS_DebugBreak(
+ void
+ )
+{
+ gckOS_DebugTrace(gcvLEVEL_ERROR, "%s(%d)", __FUNCTION__, __LINE__);
+}
+
+/*******************************************************************************
+**
+** gckOS_DebugFatal
+**
+** Send a message to the debugger and break into the debugger.
+**
+** INPUT:
+**
+** const char *Message
+** Pointer to message.
+**
+** ...
+** Optional arguments.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+void
+gckOS_DebugFatal(
+ IN const char *Message,
+ ...
+ )
+{
+ gcmkPRINT_VERSION();
+ gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message);
+
+ /* Break into the debugger. */
+ gckOS_DebugBreak();
+}
+
+/*******************************************************************************
+**
+** gckOS_SetDebugLevel
+**
+** Set the debug level.
+**
+** INPUT:
+**
+** u32 Level
+** New debug level.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_SetDebugLevel(
+ IN u32 Level
+ )
+{
+ _debugLevel = Level;
+}
+
+/*******************************************************************************
+**
+** gckOS_SetDebugZones
+**
+** Enable or disable debug zones.
+**
+** INPUT:
+**
+** u32 Zones
+** Debug zones to enable or disable.
+**
+** int Enable
+** Set to gcvTRUE to enable the zones (or the Zones with the current
+** zones) or gcvFALSE to disable the specified Zones.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_SetDebugZones(
+ IN u32 Zones,
+ IN int Enable
+ )
+{
+ if (Enable)
+ {
+ /* Enable the zones. */
+ _debugZones |= Zones;
+ }
+ else
+ {
+ /* Disable the zones. */
+ _debugZones &= ~Zones;
+ }
+}
+
+/*******************************************************************************
+**
+** gckOS_Verify
+**
+** Called to verify the result of a function call.
+**
+** INPUT:
+**
+** gceSTATUS Status
+** Function call result.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_Verify(
+ IN gceSTATUS Status
+ )
+{
+ _lastError = Status;
+}
+
+/*******************************************************************************
+**
+** gckOS_DebugFlush
+**
+** Force messages to be flushed out.
+**
+** INPUT:
+**
+** const char *CallerName
+** Name of the caller function.
+**
+** unsigned int LineNumber
+** Line number of the caller.
+**
+** u32 DmaAddress
+** The current DMA address or ~0U to ignore.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+
+void
+gckOS_DebugFlush(
+ const char *CallerName,
+ unsigned int LineNumber,
+ u32 DmaAddress
+ )
+{
+#if gcdBUFFERED_OUTPUT
+ _DirectPrint("\nFlush requested by %s(%d).\n\n", CallerName, LineNumber);
+ _Flush(DmaAddress);
+#endif
+}
+const char *
+gckOS_DebugStatus2Name(
+ gceSTATUS status
+ )
+{
+ switch (status)
+ {
+ case gcvSTATUS_OK:
+ return "gcvSTATUS_OK";
+ case gcvSTATUS_TRUE:
+ return "gcvSTATUS_TRUE";
+ case gcvSTATUS_NO_MORE_DATA:
+ return "gcvSTATUS_NO_MORE_DATA";
+ case gcvSTATUS_CACHED:
+ return "gcvSTATUS_CACHED";
+ case gcvSTATUS_MIPMAP_TOO_LARGE:
+ return "gcvSTATUS_MIPMAP_TOO_LARGE";
+ case gcvSTATUS_NAME_NOT_FOUND:
+ return "gcvSTATUS_NAME_NOT_FOUND";
+ case gcvSTATUS_NOT_OUR_INTERRUPT:
+ return "gcvSTATUS_NOT_OUR_INTERRUPT";
+ case gcvSTATUS_MISMATCH:
+ return "gcvSTATUS_MISMATCH";
+ case gcvSTATUS_MIPMAP_TOO_SMALL:
+ return "gcvSTATUS_MIPMAP_TOO_SMALL";
+ case gcvSTATUS_LARGER:
+ return "gcvSTATUS_LARGER";
+ case gcvSTATUS_SMALLER:
+ return "gcvSTATUS_SMALLER";
+ case gcvSTATUS_CHIP_NOT_READY:
+ return "gcvSTATUS_CHIP_NOT_READY";
+ case gcvSTATUS_NEED_CONVERSION:
+ return "gcvSTATUS_NEED_CONVERSION";
+ case gcvSTATUS_SKIP:
+ return "gcvSTATUS_SKIP";
+ case gcvSTATUS_DATA_TOO_LARGE:
+ return "gcvSTATUS_DATA_TOO_LARGE";
+ case gcvSTATUS_INVALID_CONFIG:
+ return "gcvSTATUS_INVALID_CONFIG";
+ case gcvSTATUS_CHANGED:
+ return "gcvSTATUS_CHANGED";
+ case gcvSTATUS_NOT_SUPPORT_DITHER:
+ return "gcvSTATUS_NOT_SUPPORT_DITHER";
+
+ case gcvSTATUS_INVALID_ARGUMENT:
+ return "gcvSTATUS_INVALID_ARGUMENT";
+ case gcvSTATUS_INVALID_OBJECT:
+ return "gcvSTATUS_INVALID_OBJECT";
+ case gcvSTATUS_OUT_OF_MEMORY:
+ return "gcvSTATUS_OUT_OF_MEMORY";
+ case gcvSTATUS_MEMORY_LOCKED:
+ return "gcvSTATUS_MEMORY_LOCKED";
+ case gcvSTATUS_MEMORY_UNLOCKED:
+ return "gcvSTATUS_MEMORY_UNLOCKED";
+ case gcvSTATUS_HEAP_CORRUPTED:
+ return "gcvSTATUS_HEAP_CORRUPTED";
+ case gcvSTATUS_GENERIC_IO:
+ return "gcvSTATUS_GENERIC_IO";
+ case gcvSTATUS_INVALID_ADDRESS:
+ return "gcvSTATUS_INVALID_ADDRESS";
+ case gcvSTATUS_CONTEXT_LOSSED:
+ return "gcvSTATUS_CONTEXT_LOSSED";
+ case gcvSTATUS_TOO_COMPLEX:
+ return "gcvSTATUS_TOO_COMPLEX";
+ case gcvSTATUS_BUFFER_TOO_SMALL:
+ return "gcvSTATUS_BUFFER_TOO_SMALL";
+ case gcvSTATUS_INTERFACE_ERROR:
+ return "gcvSTATUS_INTERFACE_ERROR";
+ case gcvSTATUS_NOT_SUPPORTED:
+ return "gcvSTATUS_NOT_SUPPORTED";
+ case gcvSTATUS_MORE_DATA:
+ return "gcvSTATUS_MORE_DATA";
+ case gcvSTATUS_TIMEOUT:
+ return "gcvSTATUS_TIMEOUT";
+ case gcvSTATUS_OUT_OF_RESOURCES:
+ return "gcvSTATUS_OUT_OF_RESOURCES";
+ case gcvSTATUS_INVALID_DATA:
+ return "gcvSTATUS_INVALID_DATA";
+ case gcvSTATUS_INVALID_MIPMAP:
+ return "gcvSTATUS_INVALID_MIPMAP";
+ case gcvSTATUS_NOT_FOUND:
+ return "gcvSTATUS_NOT_FOUND";
+ case gcvSTATUS_NOT_ALIGNED:
+ return "gcvSTATUS_NOT_ALIGNED";
+ case gcvSTATUS_INVALID_REQUEST:
+ return "gcvSTATUS_INVALID_REQUEST";
+ case gcvSTATUS_GPU_NOT_RESPONDING:
+ return "gcvSTATUS_GPU_NOT_RESPONDING";
+ case gcvSTATUS_TIMER_OVERFLOW:
+ return "gcvSTATUS_TIMER_OVERFLOW";
+ case gcvSTATUS_VERSION_MISMATCH:
+ return "gcvSTATUS_VERSION_MISMATCH";
+ case gcvSTATUS_LOCKED:
+ return "gcvSTATUS_LOCKED";
+
+ /* Linker errors. */
+ case gcvSTATUS_GLOBAL_TYPE_MISMATCH:
+ return "gcvSTATUS_GLOBAL_TYPE_MISMATCH";
+ case gcvSTATUS_TOO_MANY_ATTRIBUTES:
+ return "gcvSTATUS_TOO_MANY_ATTRIBUTES";
+ case gcvSTATUS_TOO_MANY_UNIFORMS:
+ return "gcvSTATUS_TOO_MANY_UNIFORMS";
+ case gcvSTATUS_TOO_MANY_VARYINGS:
+ return "gcvSTATUS_TOO_MANY_VARYINGS";
+ case gcvSTATUS_UNDECLARED_VARYING:
+ return "gcvSTATUS_UNDECLARED_VARYING";
+ case gcvSTATUS_VARYING_TYPE_MISMATCH:
+ return "gcvSTATUS_VARYING_TYPE_MISMATCH";
+ case gcvSTATUS_MISSING_MAIN:
+ return "gcvSTATUS_MISSING_MAIN";
+ case gcvSTATUS_NAME_MISMATCH:
+ return "gcvSTATUS_NAME_MISMATCH";
+ case gcvSTATUS_INVALID_INDEX:
+ return "gcvSTATUS_INVALID_INDEX";
+ default:
+ return "nil";
+ }
+}
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_debug.h b/kernel_drivers/v4_cleaned/gc_hal_kernel_debug.h
new file mode 100644
index 0000000..19e7251
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_debug.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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>
+
+
+/******************************************************************************\
+****************************** 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 DEFINE_SPINLOCK(__spinLock__);
+
+#define gcmkLOCKSECTION(__spinLock__) \
+ spin_lock(&__spinLock__)
+
+#define gcmkUNLOCKSECTION(__spinLock__) \
+ spin_unlock(&__spinLock__)
+
+#define gcmkGETPROCESSID() \
+ task_tgid_vnr(current)
+
+#define gcmkGETTHREADID() \
+ task_pid_vnr(current)
+
+#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
+
+#endif /* __gc_hal_kernel_debug_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_device.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_device.c
new file mode 100644
index 0000000..43f4863
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_device.c
@@ -0,0 +1,1214 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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
+
+/******************************************************************************\
+*************************** Memory Allocation Wrappers *************************
+\******************************************************************************/
+
+static gceSTATUS
+_AllocateMemory(
+ IN gckGALDEVICE Device,
+ IN size_t Bytes,
+ OUT void **Logical,
+ OUT gctPHYS_ADDR *Physical,
+ OUT u32 *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 void *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]);
+ if (down); /*To make gcc4.6 happy*/
+ 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]);
+ if (down); /*To make gcc4.6 happy*/
+ 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);
+ }
+}
+
+/******************************************************************************\
+******************************* gckGALDEVICE Code ******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** 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.
+*/
+static gceSTATUS
+gckGALDEVICE_Setup_ISR(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ int 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. */
+ ret = request_irq(
+ Device->irqLines[gcvCORE_MAJOR], isrRoutine, IRQF_DISABLED,
+ "galcore interrupt service", Device
+ );
+
+ 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;
+}
+
+static gceSTATUS
+gckGALDEVICE_Setup_ISR_2D(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ int 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. */
+ ret = request_irq(
+ Device->irqLines[gcvCORE_2D], isrRoutine2D, IRQF_DISABLED,
+ "galcore interrupt service for 2D", Device
+ );
+
+ 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;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Release_ISR
+**
+** Release the irq line.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+static 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])
+ {
+ free_irq(Device->irqLines[gcvCORE_MAJOR], Device);
+
+ Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+static 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])
+ {
+ free_irq(Device->irqLines[gcvCORE_2D], Device);
+
+ Device->isrInitializeds[gcvCORE_2D] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Construct
+**
+** Constructor.
+**
+** INPUT:
+**
+** OUTPUT:
+**
+** gckGALDEVICE * Device
+** Pointer to a variable receiving the gckGALDEVICE object pointer on
+** success.
+*/
+gceSTATUS
+gckGALDEVICE_Construct(
+ IN int IrqLine,
+ IN u32 RegisterMemBase,
+ IN size_t RegisterMemSize,
+ IN int IrqLine2D,
+ IN u32 RegisterMemBase2D,
+ IN size_t RegisterMemSize2D,
+ IN u32 ContiguousBase,
+ IN size_t ContiguousSize,
+ IN size_t BankSize,
+ IN int FastClear,
+ IN int Compression,
+ IN u32 PhysBaseAddr,
+ IN u32 PhysSize,
+ IN int Signal,
+ OUT gckGALDEVICE *Device
+ )
+{
+ u32 internalBaseAddress = 0, internalAlignment = 0;
+ u32 externalBaseAddress = 0, externalAlignment = 0;
+ u32 horizontalTileSize, verticalTileSize;
+ struct resource* mem_region;
+ u32 physAddr;
+ u32 physical;
+ gckGALDEVICE device;
+ gceSTATUS status;
+ s32 i;
+ gceHARDWARE_TYPE type;
+ gckDB sharedDB = NULL;
+
+ gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%u "
+ "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%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,
+ ContiguousBase, ContiguousSize, BankSize, FastClear, Compression,
+ PhysBaseAddr, PhysSize, Signal);
+
+ /* Allocate device structure. */
+ device = kmalloc(sizeof(struct _gckGALDEVICE), GFP_KERNEL | __GFP_NOWARN);
+
+ 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;
+ }
+
+ 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 0
+ if (mem_region == NULL)
+ {
+ 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);
+ }
+#endif
+
+ device->registerBases[i] = (void *) ioremap_nocache(
+ physical, device->requestedRegisterMemSizes[i]);
+
+ if (device->registerBases[i] == NULL)
+ {
+ 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] = NULL;
+ }
+ }
+
+ /* 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,
+ NULL, &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
+ ));
+
+ /* Start the command queue. */
+ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command));
+ }
+ else
+ {
+ device->kernels[gcvCORE_MAJOR] = NULL;
+ }
+
+ if (IrqLine2D != -1)
+ {
+ gcmkONERROR(gckKERNEL_Construct(
+ device->os, gcvCORE_2D, device,
+ sharedDB, &device->kernels[gcvCORE_2D]));
+
+ if (sharedDB == NULL) 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] == NULL)
+ {
+ 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
+ ));
+
+ /* Start the command queue. */
+ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_2D]->command));
+ }
+ else
+ {
+ device->kernels[gcvCORE_2D] = NULL;
+ }
+
+ /* Initialize the ISR. */
+ device->irqLines[gcvCORE_MAJOR] = IrqLine;
+ device->irqLines[gcvCORE_2D] = IrqLine2D;
+
+ /* 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] != NULL) break;
+ }
+
+ if (i == gcdCORE_COUNT) gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+
+ /* 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
+ = (void *) ioremap_nocache(physical, device->internalSize);
+
+ if (device->internalLogical == NULL)
+ {
+ 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
+ = (void *) ioremap_nocache(physical, device->externalSize);
+
+ if (device->externalLogical == NULL)
+ {
+ 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 = NULL;
+ device->contiguousPhysical = NULL;
+ }
+
+ 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 = NULL;
+ device->contiguousSize = 0;
+ }
+ else
+ {
+ mem_region = request_mem_region(
+ ContiguousBase, ContiguousSize, "galcore managed memory"
+ );
+
+#if 0
+ if (mem_region == NULL)
+ {
+ 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);
+ }
+#endif
+
+ device->requestedContiguousBase = ContiguousBase;
+ device->requestedContiguousSize = ContiguousSize;
+
+ 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)
+{
+ int i;
+ gceSTATUS status = gcvSTATUS_OK;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ if (Device != NULL)
+ {
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (Device->kernels[i] != NULL)
+ {
+ /* Destroy the gckKERNEL object. */
+ gcmkVERIFY_OK(gckKERNEL_Destroy(Device->kernels[i]));
+ Device->kernels[i] = NULL;
+ }
+ }
+
+ {
+ if (Device->internalLogical != NULL)
+ {
+ /* Unmap the internal memory. */
+ iounmap(Device->internalLogical);
+ Device->internalLogical = NULL;
+ }
+
+ if (Device->internalVidMem != NULL)
+ {
+ /* Destroy the internal heap. */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->internalVidMem));
+ Device->internalVidMem = NULL;
+ }
+ }
+
+ {
+ if (Device->externalLogical != NULL)
+ {
+ /* Unmap the external memory. */
+ iounmap(Device->externalLogical);
+ Device->externalLogical = NULL;
+ }
+
+ if (Device->externalVidMem != NULL)
+ {
+ /* destroy the external heap */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem));
+ Device->externalVidMem = NULL;
+ }
+ }
+
+ {
+ if (Device->contiguousBase != NULL)
+ {
+ if (!Device->contiguousMapped)
+ {
+ gcmkONERROR(_FreeMemory(
+ Device,
+ Device->contiguousBase,
+ Device->contiguousPhysical
+ ));
+ }
+
+ Device->contiguousBase = NULL;
+ Device->contiguousPhysical = NULL;
+ }
+
+ if (Device->requestedContiguousBase != 0)
+ {
+ release_mem_region(Device->requestedContiguousBase, Device->requestedContiguousSize);
+ Device->requestedContiguousBase = 0;
+ Device->requestedContiguousSize = 0;
+ }
+
+ if (Device->contiguousVidMem != NULL)
+ {
+ /* Destroy the contiguous heap. */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem));
+ Device->contiguousVidMem = NULL;
+ }
+ }
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (Device->registerBases[i] != NULL)
+ {
+ /* Unmap register memory. */
+ iounmap(Device->registerBases[i]);
+ if (Device->requestedRegisterMemBases[i] != 0)
+ {
+ release_mem_region(Device->requestedRegisterMemBases[i], Device->requestedRegisterMemSizes[i]);
+ }
+
+ Device->registerBases[i] = NULL;
+ Device->requestedRegisterMemBases[i] = 0;
+ Device->requestedRegisterMemSizes[i] = 0;
+ }
+ }
+
+ /* Destroy the gckOS object. */
+ if (Device->os != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_Destroy(Device->os));
+ Device->os = NULL;
+ }
+
+ /* Free the device. */
+ kfree(Device);
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** 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.
+*/
+static 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] != NULL)
+ {
+ /* 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 (Device->kernels[gcvCORE_2D] != NULL)
+ {
+ /* 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;
+ }
+
+ 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.
+*/
+static gceSTATUS
+gckGALDEVICE_Stop_Threads(
+ gckGALDEVICE Device
+ )
+{
+ int 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] = NULL;
+ Device->threadInitializeds[i] = gcvFALSE;
+ }
+ }
+
+ 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] != NULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR(Device));
+
+ /* Switch to SUSPEND power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF_BROADCAST
+ ));
+ }
+
+ if (Device->kernels[gcvCORE_2D] != NULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR_2D(Device));
+
+ /* Switch to SUSPEND power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF_BROADCAST
+ ));
+ }
+
+ 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] != NULL)
+ {
+ /* 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] != NULL)
+ {
+ /* 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
+ ));
+ }
+
+ /* Stop the kernel thread. */
+ gcmkONERROR(gckGALDEVICE_Stop_Threads(Device));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_device.h b/kernel_drivers/v4_cleaned/gc_hal_kernel_device.h
new file mode 100644
index 0000000..d441196
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_device.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_
+
+/******************************************************************************\
+******************************* gckGALDEVICE Structure *******************************
+\******************************************************************************/
+
+typedef struct _gckGALDEVICE
+{
+ /* Objects. */
+ gckOS os;
+ gckKERNEL kernels[gcdCORE_COUNT];
+
+ /* Attributes. */
+ size_t internalSize;
+ gctPHYS_ADDR internalPhysical;
+ void * internalLogical;
+ gckVIDMEM internalVidMem;
+ size_t externalSize;
+ gctPHYS_ADDR externalPhysical;
+ void * externalLogical;
+ gckVIDMEM externalVidMem;
+ gckVIDMEM contiguousVidMem;
+ void * contiguousBase;
+ gctPHYS_ADDR contiguousPhysical;
+ size_t contiguousSize;
+ int contiguousMapped;
+ void * contiguousMappedUser;
+ size_t systemMemorySize;
+ u32 systemMemoryBaseAddress;
+ void * registerBases[gcdCORE_COUNT];
+ size_t registerSizes[gcdCORE_COUNT];
+ u32 baseAddress;
+ u32 requestedRegisterMemBases[gcdCORE_COUNT];
+ size_t requestedRegisterMemSizes[gcdCORE_COUNT];
+ u32 requestedContiguousBase;
+ size_t requestedContiguousSize;
+
+ /* IRQ management. */
+ int irqLines[gcdCORE_COUNT];
+ int isrInitializeds[gcdCORE_COUNT];
+ int dataReadys[gcdCORE_COUNT];
+
+ /* Thread management. */
+ struct task_struct *threadCtxts[gcdCORE_COUNT];
+ struct semaphore semas[gcdCORE_COUNT];
+ int threadInitializeds[gcdCORE_COUNT];
+ int killThread;
+
+ /* Signal management. */
+ int signal;
+
+ /* Core mapping */
+ gceCORE coreMapping[8];
+
+ /* States before suspend. */
+ gceCHIPPOWERSTATE statesStored[gcdCORE_COUNT];
+
+ /* Clock management. */
+ struct clk *clk;
+ int clk_enabled;
+
+ /* Device pointer for dma_alloc_coherent */
+ struct device *dev;
+}
+* gckGALDEVICE;
+
+typedef struct _gcsHAL_PRIVATE_DATA
+{
+ gckGALDEVICE device;
+ void * mappedMemory;
+ void * contiguousLogical;
+ /* The process opening the device may not be the same as the one that closes it. */
+ u32 pidOpen;
+}
+gcsHAL_PRIVATE_DATA, * gcsHAL_PRIVATE_DATA_PTR;
+
+gceSTATUS gckGALDEVICE_Start(
+ IN gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Stop(
+ gckGALDEVICE Device
+ );
+
+gceSTATUS gckGALDEVICE_Construct(
+ IN int IrqLine,
+ IN u32 RegisterMemBase,
+ IN size_t RegisterMemSize,
+ IN int IrqLine2D,
+ IN u32 RegisterMemBase2D,
+ IN size_t RegisterMemSize2D,
+ IN u32 ContiguousBase,
+ IN size_t ContiguousSize,
+ IN size_t BankSize,
+ IN int FastClear,
+ IN int Compression,
+ IN u32 PhysBaseAddr,
+ IN u32 PhysSize,
+ IN int Signal,
+ OUT gckGALDEVICE *Device
+ );
+
+gceSTATUS gckGALDEVICE_Destroy(
+ IN gckGALDEVICE Device
+ );
+
+#endif /* __gc_hal_kernel_device_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_driver.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_driver.c
new file mode 100644
index 0000000..491ed63
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_driver.c
@@ -0,0 +1,1119 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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 <linux/device.h>
+#include <linux/slab.h>
+#if defined(JZSOC) && defined(CONFIG_PREEMPT)
+#include <linux/kernel_lock.h>
+#endif
+
+#include "gc_hal_kernel_linux.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);
+
+#ifdef CONFIG_MACH_JZ4770
+#include <asm/mach-jz4770/jz4770cpm.h>
+
+#ifndef IRQ_GPU
+#define IRQ_GPU 6
+#endif
+#ifndef GPU_BASE
+#define GPU_BASE 0x13040000
+#endif
+#ifndef JZ_GPU_MEM_BASE
+#define JZ_GPU_MEM_BASE 0 /* if GPU_MEM_BASE = 0, alloc gpu memory dynamicly on bootup */
+#endif
+#ifndef JZ_GPU_MEM_SIZE
+#define JZ_GPU_MEM_SIZE 0x400000 /* set default reserved memory 4M Bytes. */
+#endif
+
+static int irqLine = IRQ_GPU;
+module_param(irqLine, int, 0644);
+
+static long registerMemBase = GPU_BASE;
+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 long contiguousSize = JZ_GPU_MEM_SIZE;
+module_param(contiguousSize, long, 0644);
+
+static ulong contiguousBase = JZ_GPU_MEM_BASE;
+module_param(contiguousBase, ulong, 0644);
+
+#else /* CONFIG_MACH_JZ4770 */
+
+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 long contiguousSize = 4 << 20;
+module_param(contiguousSize, long, 0644);
+
+static ulong contiguousBase = 0;
+module_param(contiguousBase, ulong, 0644);
+#endif /* CONFIG_MACH_JZ4770 */
+
+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 = 1;
+module_param(showArgs, int, 0644);
+
+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;
+ int attached = gcvFALSE;
+ gcsHAL_PRIVATE_DATA_PTR data = NULL;
+ int i;
+
+ gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);
+
+ if (filp == NULL)
+ {
+ 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 | __GFP_NOWARN);
+
+ if (data == NULL)
+ {
+ 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 = NULL;
+ data->contiguousLogical = NULL;
+ data->pidOpen = task_tgid_vnr(current);
+
+ /* Attached the process. */
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != NULL)
+ {
+ gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE));
+ }
+ }
+ attached = gcvTRUE;
+
+ if (!galDevice->contiguousMapped)
+ {
+ gcmkONERROR(gckOS_MapMemory(
+ galDevice->os,
+ galDevice->contiguousPhysical,
+ galDevice->contiguousSize,
+ &data->contiguousLogical
+ ));
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != NULL)
+ {
+ gcmkVERIFY_OK(gckKERNEL_AddProcessDB(
+ galDevice->kernels[i],
+ data->pidOpen,
+ gcvDB_MAP_MEMORY,
+ data->contiguousLogical,
+ galDevice->contiguousPhysical,
+ galDevice->contiguousSize));
+ }
+ }
+ }
+
+ filp->private_data = data;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return 0;
+
+OnError:
+ if (data != NULL)
+ {
+ if (data->contiguousLogical != NULL)
+ {
+ 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] != NULL)
+ {
+ 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;
+ int i;
+ u32 processID;
+
+
+ gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);
+
+ if (filp == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): filp is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ data = filp->private_data;
+
+ if (data == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): private_data is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ device = data->device;
+
+ if (device == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): device is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ if (!device->contiguousMapped)
+ {
+ if (data->contiguousLogical != NULL)
+ {
+ processID = task_tgid_vnr(current);
+ gcmkONERROR(gckOS_UnmapMemoryEx(
+ galDevice->os,
+ galDevice->contiguousPhysical,
+ galDevice->contiguousSize,
+ data->contiguousLogical,
+ data->pidOpen
+ ));
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckKERNEL_RemoveProcessDB(galDevice->kernels[i],
+ processID, gcvDB_MAP_MEMORY,
+ data->contiguousLogical));
+ }
+ }
+
+ data->contiguousLogical = NULL;
+ }
+ }
+
+ /* Clean user signals if exit unnormally. */
+ processID = task_tgid_vnr(current);
+ gcmkVERIFY_OK(gckOS_CleanProcessSignal(galDevice->os, (gctHANDLE)processID));
+
+ /* A process gets detached. */
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (galDevice->kernels[i] != NULL)
+ {
+ 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;
+ u32 copyLen;
+ DRIVER_ARGS drvArgs;
+ gckGALDEVICE device;
+ gcsHAL_PRIVATE_DATA_PTR data;
+ s32 i, count;
+
+#if defined(JZSOC) && defined(CONFIG_PREEMPT)
+ /* 1: lock_kernel, fix bug WOWFish. */
+ lock_kernel();
+#endif
+ gcmkHEADER_ARG(
+ "filp=0x%08X ioctlCode=0x%08X arg=0x%08X",
+ filp, ioctlCode, arg
+ );
+
+ if (filp == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): filp is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ data = filp->private_data;
+
+ if (data == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): private_data is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ device = data->device;
+
+ if (device == NULL)
+ {
+ 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): error copying of the input arguments.\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] != NULL)
+ {
+ 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);
+ }
+
+ status = gckKERNEL_Dispatch(device->kernels[device->coreMapping[iface.hardwareType]],
+ (ioctlCode == IOCTL_GCHAL_INTERFACE),
+ &iface);
+ }
+
+ /* Redo system call after pending signal is handled. */
+ if (status == gcvSTATUS_INTERRUPTED)
+ {
+ gcmkFOOTER();
+ return -ERESTARTSYS;
+ }
+
+ if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY))
+ {
+ /* Special case for mapped memory. */
+ if ((data->mappedMemory != NULL)
+ && (iface.u.LockVideoMemory.node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ )
+ {
+ /* Compute offset into mapped memory. */
+ u32 offset
+ = (u8 *) iface.u.LockVideoMemory.memory
+ - (u8 *) device->contiguousBase;
+
+ /* Compute offset into user-mapped region. */
+ iface.u.LockVideoMemory.memory =
+ (u8 *) 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();
+#if defined(JZSOC) && defined(CONFIG_PREEMPT)
+ /* 1: lock_kernel, fix bug WOWFish. */
+ unlock_kernel();
+#endif
+ return 0;
+
+OnError:
+ gcmkFOOTER();
+#if defined(JZSOC) && defined(CONFIG_PREEMPT)
+ /* 1: lock_kernel, fix bug WOWFish. */
+ unlock_kernel();
+#endif
+ 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 == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): filp is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ data = filp->private_data;
+
+ if (data == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): private_data is NULL\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ device = data->device;
+
+ if (device == NULL)
+ {
+ 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_writecombine(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,
+ (u32) 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 = NULL;
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ data->mappedMemory = (void *) vma->vm_start;
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return 0;
+
+OnError:
+ gcmkFOOTER();
+ return -ENOTTY;
+}
+
+#ifdef CONFIG_JZSOC
+static void enable_jzsoc_gpu_clock(void)
+{
+#ifdef CONFIG_MACH_JZ4770
+ {
+ /* JZ4770 GPU CLK2x 100MHz -- 500MHz */
+#define GPU_CLK_MAX 500000000
+ unsigned int GPUCDR_VAL=0;
+ int div;
+ int gpu_use_pll1 = 1;
+ unsigned int pll_clk;
+ unsigned int gpu_clk = 0;
+
+ /* Right now: hardcode PLL0.
+ * Later: use generic clock interface.
+ pll_clk = cpm_get_pllout1();
+ if ( pll_clk == 0 )*/ {
+ gpu_use_pll1 = 0; /* use pll0 */
+ pll_clk = cpm_get_pllout();
+ if ((INREG32(CPM_CPCCR) & CPCCR_PCS) != 0 )
+ pll_clk /= 2;
+ }
+
+ for ( div=1; div <= ((GPUCDR_GPUDIV_MASK>>GPUCDR_GPUDIV_LSB)+1); div++ ) {
+ gpu_clk = pll_clk/div;
+ if ( gpu_clk < GPU_CLK_MAX )
+ break;
+ }
+
+ cpm_stop_clock(CGM_GPU);
+ GPUCDR_VAL = (div-1);
+ if (gpu_use_pll1)
+ GPUCDR_VAL |= 1<<31;
+ REG_CPM_GPUCDR = GPUCDR_VAL;
+ cpm_start_clock(CGM_GPU);
+
+ printk("REG_CPM_GPUCDR= 0x%08x\n", GPUCDR_VAL);
+ printk("GPU CLOCK USE PLL%d\n", gpu_use_pll1);
+ printk("GPU GPU_CLK2x= %d MHz\n", gpu_clk/1000000);
+ }
+#endif
+}
+#endif
+
+#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 = NULL;
+ struct class* device_class = NULL;
+
+ gcmkHEADER();
+
+#ifdef CONFIG_JZSOC
+ enable_jzsoc_gpu_clock();
+#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);
+ }
+
+ 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);
+ }
+
+ /* Create the GAL device. */
+ gcmkONERROR(gckGALDEVICE_Construct(
+ irqLine,
+ registerMemBase, registerMemSize,
+ irqLine2D,
+ registerMemBase2D, registerMemSize2D,
+ contiguousBase, contiguousSize,
+ bankSize, fastClear, compression, baseAddress, physSize, signal,
+ &device
+ ));
+
+ /* Start the GAL device. */
+ gcmkONERROR(gckGALDEVICE_Start(device));
+
+ if ((physSize != 0)
+ && (device->kernels[gcvCORE_MAJOR] != NULL)
+ && (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] != NULL)
+ && (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);
+ }
+
+ device_create(device_class, NULL, MKDEV(major, 0), NULL, "galcore");
+
+ 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 != NULL)
+ {
+ device_destroy(device_class, MKDEV(major, 0));
+ class_destroy(device_class);
+ }
+
+ if (device != NULL)
+ {
+ 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
+{
+#ifndef CONFIG_JZSOC
+ struct clk *clk = galDevice->clk;
+#endif
+
+ gcmkHEADER();
+
+ gcmkASSERT(gpuClass != NULL);
+ device_destroy(gpuClass, MKDEV(major, 0));
+ class_destroy(gpuClass);
+
+ unregister_chrdev(major, DRV_NAME);
+
+ gcmkVERIFY_OK(gckGALDEVICE_Stop(galDevice));
+ gcmkVERIFY_OK(gckGALDEVICE_Destroy(galDevice));
+
+#ifndef CONFIG_JZSOC
+ clk_disable(clk);
+ clk_put(clk);
+#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 gpu_probe(struct platform_device *pdev)
+{
+ int ret = -ENODEV;
+ struct resource* res;
+ struct clk *clk;
+
+ gcmkHEADER();
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "gpu_irq");
+
+ if (!res)
+ {
+ printk(KERN_ERR "%s: No irq line supplied.\n",__FUNCTION__);
+ goto gpu_probe_fail;
+ }
+
+ irqLine = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpu_base");
+
+ if (!res)
+ {
+ printk(KERN_ERR "%s: No register base supplied.\n",__FUNCTION__);
+ goto gpu_probe_fail;
+ }
+
+ registerMemBase = res->start;
+ registerMemSize = res->end - res->start + 1;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "gpu_mem");
+
+ if (!res)
+ {
+ printk(KERN_ERR "%s: No memory base supplied.\n",__FUNCTION__);
+ goto gpu_probe_fail;
+ }
+
+ contiguousBase = res->start;
+ contiguousSize = res->end - res->start + 1;
+
+ dev_info(&pdev->dev, "driver v4.6.6, initializing\n");
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(clk);
+ goto gpu_probe_fail;
+ }
+ clk_enable(clk);
+
+ ret = drv_init();
+ galDevice->dev = &pdev->dev;
+
+ if (!ret)
+ {
+ platform_set_drvdata(pdev, galDevice);
+ galDevice->clk = clk;
+ galDevice->clk_enabled = 0;
+
+ dev_info(&pdev->dev, "GPU initialized, clocked at %luMHz\n",
+ clk_get_rate(clk) / 1000000);
+
+ clk_disable(clk);
+
+ gcmkFOOTER_NO();
+ return ret;
+ }
+
+ clk_disable(clk);
+ clk_put(clk);
+
+gpu_probe_fail:
+ gcmkFOOTER_ARG(KERN_INFO "Failed to register gpu driver: %d\n", ret);
+ return ret;
+}
+
+static int gpu_remove(struct platform_device *pdev)
+{
+ gcmkHEADER();
+ drv_exit();
+ gcmkFOOTER_NO();
+ return 0;
+}
+
+static int gpu_suspend(struct platform_device *dev, pm_message_t state)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+ int i;
+
+#ifdef CONFIG_JZSOC
+ cpm_stop_clock(CGM_GPU);
+#endif
+ device = platform_get_drvdata(dev);
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->kernels[i] != NULL)
+ {
+ /* Store states. */
+ status = gckHARDWARE_QueryPowerManagementState(device->kernels[i]->hardware, &device->statesStored[i]);
+ if (gcmIS_ERROR(status))
+ {
+ return -1;
+ }
+
+ status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_OFF);
+ if (gcmIS_ERROR(status))
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int gpu_resume(struct platform_device *dev)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+ int i;
+ gceCHIPPOWERSTATE statesStored;
+
+#ifdef CONFIG_JZSOC
+ cpm_start_clock(CGM_GPU);
+#endif
+ device = platform_get_drvdata(dev);
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->kernels[i] != NULL)
+ {
+ status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_ON);
+ if (gcmIS_ERROR(status))
+ {
+ return -1;
+ }
+
+ /* Convert global state to crossponding internal state. */
+ switch(device->statesStored[i])
+ {
+ case gcvPOWER_OFF:
+ statesStored = gcvPOWER_OFF_BROADCAST;
+ break;
+ case gcvPOWER_IDLE:
+ statesStored = gcvPOWER_IDLE_BROADCAST;
+ break;
+ case gcvPOWER_SUSPEND:
+ statesStored = gcvPOWER_SUSPEND_BROADCAST;
+ break;
+ case gcvPOWER_ON:
+ statesStored = gcvPOWER_ON_AUTO;
+ break;
+ default:
+ statesStored = device->statesStored[i];
+ break;
+ }
+
+ /* Restore states. */
+ status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, statesStored);
+ 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,
+ }
+};
+
+static int __init gpu_init(void)
+{
+ int ret = 0;
+
+ ret = platform_driver_register(&gpu_driver);
+ return ret;
+}
+
+static void __exit gpu_exit(void)
+{
+ platform_driver_unregister(&gpu_driver);
+}
+
+module_init(gpu_init);
+module_exit(gpu_exit);
+
+#endif
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_event.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_event.c
new file mode 100644
index 0000000..10d3921
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_event.c
@@ -0,0 +1,2197 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define _GC_OBJ_ZONE gcvZONE_EVENT
+
+#define gcdEVENT_ALLOCATION_COUNT (4096 / sizeof(gcsHAL_INTERFACE))
+#define gcdEVENT_MIN_THRESHOLD 4
+
+/******************************************************************************\
+********************************* Support Code *********************************
+\******************************************************************************/
+
+static gceSTATUS
+gckEVENT_AllocateQueue(
+ IN gckEVENT Event,
+ OUT gcsEVENT_QUEUE_PTR * Queue
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Queue != NULL);
+
+ /* Do we have free queues? */
+ if (Event->freeList == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Move one free queue from the free list. */
+ * Queue = Event->freeList;
+ Event->freeList = Event->freeList->next;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Queue=0x%x", gcmOPT_POINTER(Queue));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+gckEVENT_FreeQueue(
+ IN gckEVENT Event,
+ OUT gcsEVENT_QUEUE_PTR Queue
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Queue != NULL);
+
+ /* Move one free queue from the free list. */
+ Queue->next = Event->freeList;
+ Event->freeList = Queue;
+
+ /* Success. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+gckEVENT_FreeRecord(
+ IN gckEVENT Event,
+ IN gcsEVENT_PTR Record
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Record != NULL);
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->freeEventMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Push the record on the free list. */
+ Record->next = Event->freeEventList;
+ Event->freeEventList = Record;
+ Event->freeEventCount += 1;
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+}
+
+static gceSTATUS
+gckEVENT_IsEmpty(
+ IN gckEVENT Event,
+ OUT int *IsEmpty
+ )
+{
+ gceSTATUS status;
+ size_t i;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(IsEmpty != NULL);
+
+ /* Assume the event queue is empty. */
+ *IsEmpty = gcvTRUE;
+
+ /* Walk the event queue. */
+ for (i = 0; i < ARRAY_SIZE(Event->queues); ++i)
+ {
+ /* Check whether this event is in use. */
+ if (Event->queues[i].head != NULL)
+ {
+ /* The event is in use, hence the queue is not empty. */
+ *IsEmpty = gcvFALSE;
+ break;
+ }
+ }
+
+ /* Try acquiring the mutex. */
+ status = gckOS_AcquireMutex(Event->os, Event->eventQueueMutex, 0);
+ if (status == gcvSTATUS_TIMEOUT)
+ {
+ /* Timeout - queue is no longer empty. */
+ *IsEmpty = gcvFALSE;
+ }
+ else
+ {
+ /* Bail out on error. */
+ gcmkONERROR(status);
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*IsEmpty=%d", gcmOPT_VALUE(IsEmpty));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+_TryToIdleGPU(
+ IN gckEVENT Event
+)
+{
+ gceSTATUS status;
+ int empty = gcvFALSE, idle = gcvFALSE;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Check whether the event queue is empty. */
+ gcmkONERROR(gckEVENT_IsEmpty(Event, &empty));
+
+ if (empty)
+ {
+ /* Query whether the hardware is idle. */
+ gcmkONERROR(gckHARDWARE_QueryIdle(Event->kernel->hardware, &idle));
+
+ if (idle)
+ {
+ /* Inform the system of idle GPU. */
+ gcmkONERROR(gckOS_Broadcast(Event->os,
+ Event->kernel->hardware,
+ gcvBROADCAST_GPU_IDLE));
+ }
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+__RemoveRecordFromProcessDB(
+ IN gckEVENT Event,
+ IN gcsEVENT_PTR Record
+ )
+{
+ gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record);
+ gcmkVERIFY_ARGUMENT(Record != NULL);
+
+ while (Record != NULL)
+ {
+ switch (Record->info.command)
+ {
+ case gcvHAL_FREE_NON_PAGED_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_NON_PAGED,
+ Record->info.u.FreeNonPagedMemory.logical));
+ break;
+
+ case gcvHAL_FREE_CONTIGUOUS_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_CONTIGUOUS,
+ Record->info.u.FreeContiguousMemory.logical));
+ break;
+
+ case gcvHAL_FREE_VIDEO_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_VIDEO_MEMORY,
+ Record->info.u.FreeVideoMemory.node));
+ break;
+
+ case gcvHAL_UNLOCK_VIDEO_MEMORY:
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ Record->processID,
+ gcvDB_VIDEO_MEMORY_LOCKED,
+ Record->info.u.UnlockVideoMemory.node));
+ break;
+
+ default:
+ break;
+ }
+
+ Record = Record->next;
+ }
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/******************************************************************************\
+******************************* gckEVENT API Code *******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckEVENT_Construct
+**
+** Construct a new gckEVENT object.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** OUTPUT:
+**
+** gckEVENT * Event
+** Pointer to a variable that receives the gckEVENT object pointer.
+*/
+gceSTATUS
+gckEVENT_Construct(
+ IN gckKERNEL Kernel,
+ OUT gckEVENT * Event
+ )
+{
+ gckOS os;
+ gceSTATUS status;
+ gckEVENT eventObj = NULL;
+ int i;
+ gcsEVENT_PTR record;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Kernel=0x%x", Kernel);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Event != NULL);
+
+ /* Extract the pointer to the gckOS object. */
+ os = Kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Allocate the gckEVENT object. */
+ gcmkONERROR(gckOS_Allocate(os, sizeof(struct _gckEVENT), &pointer));
+
+ eventObj = pointer;
+
+ /* Reset the object. */
+ gcmkVERIFY_OK(gckOS_ZeroMemory(eventObj, sizeof(struct _gckEVENT)));
+
+ /* Initialize the gckEVENT object. */
+ eventObj->object.type = gcvOBJ_EVENT;
+ eventObj->kernel = Kernel;
+ eventObj->os = os;
+
+ /* Create the mutexes. */
+ gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventQueueMutex));
+ gcmkONERROR(gckOS_CreateMutex(os, &eventObj->freeEventMutex));
+ gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventListMutex));
+
+ /* Create a bunch of event reccords. */
+ for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1)
+ {
+ /* Allocate an event record. */
+ gcmkONERROR(gckOS_Allocate(os, sizeof(gcsEVENT), &pointer));
+
+ record = pointer;
+
+ /* Push it on the free list. */
+ record->next = eventObj->freeEventList;
+ eventObj->freeEventList = record;
+ eventObj->freeEventCount += 1;
+ }
+
+ /* Initialize the free list of event queues. */
+ for (i = 0; i < gcdREPO_LIST_COUNT; i += 1)
+ {
+ eventObj->repoList[i].next = eventObj->freeList;
+ eventObj->freeList = &eventObj->repoList[i];
+ }
+
+ /* Construct the atom. */
+ gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->freeAtom));
+ gcmkONERROR(gckOS_AtomSet(os,
+ eventObj->freeAtom,
+ ARRAY_SIZE(eventObj->queues)));
+
+#ifdef CONFIG_SMP
+ gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending));
+#endif
+
+ /* Return pointer to the gckEVENT object. */
+ *Event = eventObj;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Event=0x%x", *Event);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (eventObj != NULL)
+ {
+ if (eventObj->eventQueueMutex != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventQueueMutex));
+ }
+
+ if (eventObj->freeEventMutex != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->freeEventMutex));
+ }
+
+ if (eventObj->eventListMutex != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventListMutex));
+ }
+
+ while (eventObj->freeEventList != NULL)
+ {
+ record = eventObj->freeEventList;
+ eventObj->freeEventList = record->next;
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, record));
+ }
+
+ if (eventObj->freeAtom != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->freeAtom));
+ }
+
+#ifdef CONFIG_SMP
+ if (eventObj->pending != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending));
+ }
+#endif
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, eventObj));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Destroy
+**
+** Destroy an gckEVENT object.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Destroy(
+ IN gckEVENT Event
+ )
+{
+ gcsEVENT_PTR record;
+ gcsEVENT_QUEUE_PTR queue;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Delete the queue mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventQueueMutex));
+
+ /* Free all free events. */
+ while (Event->freeEventList != NULL)
+ {
+ record = Event->freeEventList;
+ Event->freeEventList = record->next;
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record));
+ }
+
+ /* Delete the free mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->freeEventMutex));
+
+ /* Free all pending queues. */
+ while (Event->queueHead != NULL)
+ {
+ /* Get the current queue. */
+ queue = Event->queueHead;
+
+ /* Free all pending events. */
+ while (queue->head != NULL)
+ {
+ record = queue->head;
+ queue->head = record->next;
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_WARNING, gcvZONE_EVENT,
+ sizeof(record) + sizeof(queue->source),
+ "Event record 0x%x is still pending for %d.",
+ record, queue->source
+ );
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record));
+ }
+
+ /* Remove the top queue from the list. */
+ if (Event->queueHead == Event->queueTail)
+ {
+ Event->queueHead =
+ Event->queueTail = NULL;
+ }
+ else
+ {
+ Event->queueHead = Event->queueHead->next;
+ }
+
+ /* Free the queue. */
+ gcmkVERIFY_OK(gckEVENT_FreeQueue(Event, queue));
+ }
+
+ /* Delete the list mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventListMutex));
+
+ /* Delete the atom. */
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->freeAtom));
+
+#ifdef CONFIG_SMP
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending));
+#endif
+ /* Mark the gckEVENT object as unknown. */
+ Event->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckEVENT object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, Event));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_GetEvent
+**
+** Reserve the next available hardware event.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** int Wait
+** Set to gcvTRUE to force the function to wait if no events are
+** immediately available.
+**
+** gceKERNEL_WHERE Source
+** Source of the event.
+**
+** OUTPUT:
+**
+** u8 * EventID
+** Reserved event ID.
+*/
+static gceSTATUS
+gckEVENT_GetEvent(
+ IN gckEVENT Event,
+ IN int Wait,
+ OUT u8 * EventID,
+ IN gceKERNEL_WHERE Source
+ )
+{
+ int i, id;
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ s32 free;
+
+#if gcdGPU_TIMEOUT
+ u32 timer = 0;
+#endif
+
+ gcmkHEADER_ARG("Event=0x%x Source=%d", Event, Source);
+
+ while (gcvTRUE)
+ {
+ /* Grab the queue mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->eventQueueMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Walk through all events. */
+ id = Event->lastID;
+ for (i = 0; i < ARRAY_SIZE(Event->queues); ++i)
+ {
+ int nextID = gckMATH_ModuloInt((id + 1),
+ ARRAY_SIZE(Event->queues));
+
+ if (Event->queues[id].head == NULL)
+ {
+ *EventID = (u8) id;
+
+ Event->lastID = (u8) nextID;
+
+ /* Save time stamp of event. */
+ Event->queues[id].stamp = ++(Event->stamp);
+ Event->queues[id].source = Source;
+
+ gcmkONERROR(gckOS_AtomDecrement(Event->os,
+ Event->freeAtom,
+ &free));
+#if gcdDYNAMIC_SPEED
+ if (free <= gcdDYNAMIC_EVENT_THRESHOLD)
+ {
+ gcmkONERROR(gckOS_BroadcastHurry(
+ Event->os,
+ Event->kernel->hardware,
+ gcdDYNAMIC_EVENT_THRESHOLD - free));
+ }
+#endif
+
+ /* Release the queue mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os,
+ Event->eventQueueMutex));
+
+ /* Success. */
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ sizeof(id),
+ "Using id=%d",
+ id
+ );
+
+ gcmkFOOTER_ARG("*EventID=%u", *EventID);
+ return gcvSTATUS_OK;
+ }
+
+ id = nextID;
+ }
+
+#if gcdDYNAMIC_SPEED
+ /* No free events, speed up the GPU right now! */
+ gcmkONERROR(gckOS_BroadcastHurry(Event->os,
+ Event->kernel->hardware,
+ gcdDYNAMIC_EVENT_THRESHOLD));
+#endif
+
+ /* Release the queue mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ acquired = gcvFALSE;
+
+ /* Fail if wait is not requested. */
+ if (!Wait)
+ {
+ /* Out of resources. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Delay a while. */
+ gcmkONERROR(gckOS_Delay(Event->os, 1));
+
+#if gcdGPU_TIMEOUT
+ /* Increment the wait timer. */
+ timer += 1;
+
+ if (timer == gcdGPU_TIMEOUT)
+ {
+ /* Try to call any outstanding events. */
+ gcmkONERROR(gckHARDWARE_Interrupt(Event->kernel->hardware,
+ gcvTRUE));
+ }
+ else if (timer > gcdGPU_TIMEOUT)
+ {
+ gcmkTRACE_N(
+ gcvLEVEL_ERROR,
+ sizeof(const char *) + sizeof(int),
+ "%s(%d): no available events\n",
+ __FUNCTION__, __LINE__
+ );
+
+ /* Broadcast GPU stuck. */
+ gcmkONERROR(gckOS_Broadcast(Event->os,
+ Event->kernel->hardware,
+ gcvBROADCAST_GPU_STUCK));
+
+ /* Bail out. */
+ gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING);
+ }
+#endif
+ }
+
+OnError:
+ if (acquired)
+ {
+ /* Release the queue mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_AllocateRecord
+**
+** Allocate a record for the new event.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** int AllocateAllowed
+** State for allocation if out of free events.
+**
+** OUTPUT:
+**
+** gcsEVENT_PTR * Record
+** Allocated event record.
+*/
+static gceSTATUS
+gckEVENT_AllocateRecord(
+ IN gckEVENT Event,
+ IN int AllocateAllowed,
+ OUT gcsEVENT_PTR * Record
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ int i;
+ gcsEVENT_PTR record;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Event=0x%x AllocateAllowed=%d", Event, AllocateAllowed);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Record != NULL);
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->freeEventMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Test if we are below the allocation threshold. */
+ if ( (AllocateAllowed && (Event->freeEventCount < gcdEVENT_MIN_THRESHOLD)) ||
+ (Event->freeEventCount == 0) )
+ {
+ /* Allocate a bunch of records. */
+ for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1)
+ {
+ /* Allocate an event record. */
+ gcmkONERROR(gckOS_Allocate(Event->os,
+ sizeof(gcsEVENT),
+ &pointer));
+
+ record = pointer;
+
+ /* Push it on the free list. */
+ record->next = Event->freeEventList;
+ Event->freeEventList = record;
+ Event->freeEventCount += 1;
+ }
+ }
+
+ *Record = Event->freeEventList;
+ Event->freeEventList = Event->freeEventList->next;
+ Event->freeEventCount -= 1;
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+ acquired = gcvFALSE;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Record=0x%x", gcmOPT_POINTER(Record));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_AddList
+**
+** Add a new event to the list of events.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** struct _gcsHAL_INTERFACE *Interface
+** Pointer to the interface for the event to be added.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+**
+** int AllocateAllowed
+** State for allocation if out of free events.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_AddList(
+ IN gckEVENT Event,
+ IN struct _gcsHAL_INTERFACE *Interface,
+ IN gceKERNEL_WHERE FromWhere,
+ IN int AllocateAllowed
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ gcsEVENT_PTR record = NULL;
+ gcsEVENT_QUEUE_PTR queue;
+
+ gcmkHEADER_ARG("Event=0x%x Interface=0x%x",
+ Event, Interface);
+
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, _GC_OBJ_ZONE,
+ "FromWhere=%d AllocateAllowed=%d",
+ FromWhere, AllocateAllowed);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Interface != NULL);
+
+ /* Verify the event command. */
+ gcmkASSERT
+ ( (Interface->command == gcvHAL_FREE_NON_PAGED_MEMORY)
+ || (Interface->command == gcvHAL_FREE_CONTIGUOUS_MEMORY)
+ || (Interface->command == gcvHAL_FREE_VIDEO_MEMORY)
+ || (Interface->command == gcvHAL_WRITE_DATA)
+ || (Interface->command == gcvHAL_UNLOCK_VIDEO_MEMORY)
+ || (Interface->command == gcvHAL_SIGNAL)
+ || (Interface->command == gcvHAL_UNMAP_USER_MEMORY)
+ || (Interface->command == gcvHAL_TIMESTAMP)
+ || (Interface->command == gcvHAL_COMMIT_DONE)
+ );
+
+ /* Validate the source. */
+ if ((FromWhere != gcvKERNEL_COMMAND) && (FromWhere != gcvKERNEL_PIXEL))
+ {
+ /* Invalid argument. */
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Allocate a free record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, AllocateAllowed, &record));
+
+ /* Termninate the record. */
+ record->next = NULL;
+
+ /* Copy the event interface into the record. */
+ gcmkONERROR(gckOS_MemCopy(&record->info, Interface, sizeof(record->info)));
+
+ /* Get process ID. */
+ record->processID = task_tgid_vnr(current);
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->eventListMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Do we need to allocate a new queue? */
+ if ((Event->queueTail == NULL) || (Event->queueTail->source != FromWhere))
+ {
+ /* Allocate a new queue. */
+ gcmkONERROR(gckEVENT_AllocateQueue(Event, &queue));
+
+ /* Initialize the queue. */
+ queue->source = FromWhere;
+ queue->head = NULL;
+ queue->next = NULL;
+
+ /* Attach it to the list of allocated queues. */
+ if (Event->queueTail == NULL)
+ {
+ Event->queueHead =
+ Event->queueTail = queue;
+ }
+ else
+ {
+ Event->queueTail->next = queue;
+ Event->queueTail = queue;
+ }
+ }
+ else
+ {
+ queue = Event->queueTail;
+ }
+
+ /* Attach the record to the queue. */
+ if (queue->head == NULL)
+ {
+ queue->head = record;
+ queue->tail = record;
+ }
+ else
+ {
+ queue->tail->next = record;
+ queue->tail = record;
+ }
+
+ /* Release the mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+ }
+
+ if (record != NULL)
+ {
+ gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Signal
+**
+** Schedule an event to trigger a signal.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gctSIGNAL Signal
+** Pointer to the signal to trigger.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Signal(
+ IN gckEVENT Event,
+ IN gctSIGNAL Signal,
+ IN gceKERNEL_WHERE FromWhere
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+
+ gcmkHEADER_ARG("Event=0x%x Signal=0x%x FromWhere=%d",
+ Event, Signal, FromWhere);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Signal != NULL);
+
+ /* Mark the event as a signal. */
+ iface.command = gcvHAL_SIGNAL;
+ iface.u.Signal.signal = Signal;
+ iface.u.Signal.auxSignal = NULL;
+ iface.u.Signal.process = NULL;
+
+ /* Append it to the queue. */
+ gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_CommitDone
+**
+** Schedule an event to wake up work thread when commit is done by GPU.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** gceKERNEL_WHERE FromWhere
+** Place in the pipe where the event needs to be generated.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_CommitDone(
+ IN gckEVENT Event,
+ IN gceKERNEL_WHERE FromWhere
+ )
+{
+ gceSTATUS status;
+ gcsHAL_INTERFACE iface;
+
+ gcmkHEADER_ARG("Event=0x%x FromWhere=%d", Event, FromWhere);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ iface.command = gcvHAL_COMMIT_DONE;
+
+ /* Append it to the queue. */
+ gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+/*******************************************************************************
+**
+** gckEVENT_Submit
+**
+** Submit the current event queue to the GPU.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** int Wait
+** Submit requires one vacant event; if Wait is set to not zero,
+** and there are no vacant events at this time, the function will
+** wait until an event becomes vacant so that submission of the
+** queue is successful.
+**
+** int FromPower
+** Determines whether the call originates from inside the power
+** management or not.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Submit(
+ IN gckEVENT Event,
+ IN int Wait,
+ IN int FromPower
+ )
+{
+ gceSTATUS status;
+ u8 id = 0xFF;
+ gcsEVENT_QUEUE_PTR queue;
+ int acquired = gcvFALSE;
+ gckCOMMAND command = NULL;
+ int commitEntered = gcvFALSE;
+#if !gcdNULL_DRIVER
+ size_t bytes;
+ void *buffer;
+#endif
+
+ gcmkHEADER_ARG("Event=0x%x Wait=%d", Event, Wait);
+
+ /* Get gckCOMMAND object. */
+ command = Event->kernel->command;
+
+ /* Are there event queues? */
+ if (Event->queueHead != NULL)
+ {
+ /* Acquire the command queue. */
+ gcmkONERROR(gckCOMMAND_EnterCommit(command, FromPower));
+ commitEntered = gcvTRUE;
+
+ /* Process all queues. */
+ while (Event->queueHead != NULL)
+ {
+ /* Acquire the list mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->eventListMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Get the current queue. */
+ queue = Event->queueHead;
+
+ /* Allocate an event ID. */
+ gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source));
+
+ /* Copy event list to event ID queue. */
+ Event->queues[id].source = queue->source;
+ Event->queues[id].head = queue->head;
+
+ /* Remove the top queue from the list. */
+ if (Event->queueHead == Event->queueTail)
+ {
+ Event->queueHead = NULL;
+ Event->queueTail = NULL;
+ }
+ else
+ {
+ Event->queueHead = Event->queueHead->next;
+ }
+
+ /* Free the queue. */
+ gcmkONERROR(gckEVENT_FreeQueue(Event, queue));
+
+ /* Release the list mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+ acquired = gcvFALSE;
+
+ gcmkONERROR(__RemoveRecordFromProcessDB(Event,
+ Event->queues[id].head));
+
+#if gcdNULL_DRIVER
+ /* Notify immediately on infinite hardware. */
+ gcmkONERROR(gckEVENT_Interrupt(Event, 1 << id));
+
+ gcmkONERROR(gckEVENT_Notify(Event, 0));
+#else
+ /* Get the size of the hardware event. */
+ gcmkONERROR(gckHARDWARE_Event(Event->kernel->hardware,
+ NULL,
+ id,
+ gcvKERNEL_PIXEL,
+ &bytes));
+
+ /* Reserve space in the command queue. */
+ gcmkONERROR(gckCOMMAND_Reserve(command,
+ bytes,
+ &buffer,
+ &bytes));
+
+ /* Set the hardware event in the command queue. */
+ gcmkONERROR(gckHARDWARE_Event(Event->kernel->hardware,
+ buffer,
+ id,
+ Event->queues[id].source,
+ &bytes));
+
+ /* Execute the hardware event. */
+ gcmkONERROR(gckCOMMAND_Execute(command, bytes));
+#endif
+ }
+
+ /* Release the command queue. */
+ gcmkONERROR(gckCOMMAND_ExitCommit(command, FromPower));
+ commitEntered = gcvFALSE;
+
+#if !gcdNULL_DRIVER
+ gcmkVERIFY_OK(_TryToIdleGPU(Event));
+#endif
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (commitEntered)
+ {
+ /* Release the command queue mutex. */
+ gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, FromPower));
+ }
+
+ if (acquired)
+ {
+ /* Need to unroll the mutex acquire. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
+ }
+
+ if (id != 0xFF)
+ {
+ /* Need to unroll the event allocation. */
+ Event->queues[id].head = NULL;
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Commit
+**
+** Commit an event queue from the user.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** struct _gcsQUEUE *Queue
+** User event queue.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Commit(
+ IN gckEVENT Event,
+ IN struct _gcsQUEUE *Queue
+ )
+{
+ gceSTATUS status;
+ struct _gcsQUEUE *record = NULL, *next;
+ u32 processID;
+
+ gcmkHEADER_ARG("Event=0x%x Queue=0x%x", Event, Queue);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Get the current process ID. */
+ processID = task_tgid_vnr(current);
+
+ /* Loop while there are records in the queue. */
+ while (Queue != NULL)
+ {
+ gcsQUEUE queue;
+
+ if (NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ /* Point to stack record. */
+ record = &queue;
+
+ /* Copy the data from the client. */
+ if (copy_from_user(record, Queue, sizeof(gcsQUEUE)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ else
+ {
+ void *pointer = NULL;
+
+ /* Map record into kernel memory. */
+ gcmkONERROR(gckOS_MapUserPointer(Event->os,
+ Queue,
+ sizeof(gcsQUEUE),
+ &pointer));
+
+ record = pointer;
+ }
+
+ /* Append event record to event queue. */
+ gcmkONERROR(
+ gckEVENT_AddList(Event, &record->iface, gcvKERNEL_PIXEL, gcvTRUE));
+
+ /* Next record in the queue. */
+ next = record->next;
+
+ if (!NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ /* Unmap record from kernel memory. */
+ gcmkONERROR(
+ gckOS_UnmapUserPointer(Event->os,
+ Queue,
+ sizeof(gcsQUEUE),
+ (void **) record));
+ record = NULL;
+ }
+
+ Queue = next;
+ }
+
+ /* Submit the event list. */
+ gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE));
+
+ /* Success */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if ((record != NULL) && !NO_USER_DIRECT_ACCESS_FROM_KERNEL)
+ {
+ /* Roll back. */
+ gcmkVERIFY_OK(gckOS_UnmapUserPointer(Event->os,
+ Queue,
+ sizeof(gcsQUEUE),
+ (void **) record));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Compose
+**
+** Schedule a composition event and start a composition.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** struct _gcsHAL_COMPOSE *Info
+** Pointer to the composition structure.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Compose(
+ IN gckEVENT Event,
+ IN struct _gcsHAL_COMPOSE *Info
+ )
+{
+ gceSTATUS status;
+ gcsEVENT_PTR headRecord;
+ gcsEVENT_PTR tailRecord;
+ gcsEVENT_PTR tempRecord;
+ u8 id = 0xFF;
+ u32 processID;
+
+ gcmkHEADER_ARG("Event=0x%x Info=0x%x", Event, Info);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+ gcmkVERIFY_ARGUMENT(Info != NULL);
+
+ /* Allocate an event ID. */
+ gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL));
+
+ /* Get process ID. */
+ processID = task_tgid_vnr(current);
+
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord));
+ headRecord = tailRecord = tempRecord;
+
+ /* Initialize the record. */
+ tempRecord->info.command = gcvHAL_SIGNAL;
+ tempRecord->info.u.Signal.process = Info->process;
+ tempRecord->info.u.Signal.signal = Info->signal;
+ tempRecord->info.u.Signal.auxSignal = NULL;
+ tempRecord->next = NULL;
+ tempRecord->processID = processID;
+
+ /* Allocate another record for user signal #1. */
+ if (Info->userSignal1 != NULL)
+ {
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord));
+ tailRecord->next = tempRecord;
+ tailRecord = tempRecord;
+
+ /* Initialize the record. */
+ tempRecord->info.command = gcvHAL_SIGNAL;
+ tempRecord->info.u.Signal.process = Info->userProcess;
+ tempRecord->info.u.Signal.signal = Info->userSignal1;
+ tempRecord->info.u.Signal.auxSignal = NULL;
+ tempRecord->next = NULL;
+ tempRecord->processID = processID;
+ }
+
+ /* Allocate another record for user signal #2. */
+ if (Info->userSignal2 != NULL)
+ {
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord));
+ tailRecord->next = tempRecord;
+ tailRecord = tempRecord;
+
+ /* Initialize the record. */
+ tempRecord->info.command = gcvHAL_SIGNAL;
+ tempRecord->info.u.Signal.process = Info->userProcess;
+ tempRecord->info.u.Signal.signal = Info->userSignal2;
+ tempRecord->info.u.Signal.auxSignal = NULL;
+ tempRecord->next = NULL;
+ tempRecord->processID = processID;
+ }
+
+ /* Set the event list. */
+ Event->queues[id].head = headRecord;
+
+ /* Start composition. */
+ gcmkONERROR(gckHARDWARE_Compose(
+ Event->kernel->hardware, processID,
+ Info->physical, Info->logical, Info->offset, Info->size, id
+ ));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Interrupt
+**
+** Called by the interrupt service routine to store the triggered interrupt
+** mask to be later processed by gckEVENT_Notify.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** u32 Data
+** Mask for the 32 interrupts.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Interrupt(
+ IN gckEVENT Event,
+ IN u32 Data
+ )
+{
+ gcmkHEADER_ARG("Event=0x%x Data=0x%x", Event, Data);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Combine current interrupt status with pending flags. */
+#ifdef CONFIG_SMP
+ gckOS_AtomSetMask(Event->pending, Data);
+#else
+ Event->pending |= Data;
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckEVENT_Notify
+**
+** Process all triggered interrupts.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Notify(
+ IN gckEVENT Event,
+ IN u32 IDs
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ int i;
+ gcsEVENT_QUEUE * queue;
+ unsigned int mask = 0;
+ int acquired = gcvFALSE;
+ unsigned int pending;
+ int suspended = gcvFALSE;
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ int eventNumber = 0;
+#endif
+ s32 free;
+#if gcdSECURE_USER
+ gcskSECURE_CACHE_PTR cache;
+#endif
+
+ gcmkHEADER_ARG("Event=0x%x IDs=0x%x", Event, IDs);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ gcmDEBUG_ONLY(
+ if (IDs != 0)
+ {
+ for (i = 0; i < ARRAY_SIZE(Event->queues); ++i)
+ {
+ if (Event->queues[i].head != NULL)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "Queue(%d): stamp=%llu source=%d",
+ i,
+ Event->queues[i].stamp,
+ Event->queues[i].source);
+ }
+ }
+ }
+ );
+
+ for (;;)
+ {
+ /* Suspend interrupts. */
+ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvTRUE;
+
+ /* Get current interrupts. */
+#ifdef CONFIG_SMP
+ gckOS_AtomGet(Event->os, Event->pending, (s32 *)&pending);
+#else
+ pending = Event->pending;
+#endif
+
+ /* Resume interrupts. */
+ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvFALSE;
+
+ if (pending == 0)
+ {
+ /* No more pending interrupts - done. */
+ break;
+ }
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ sizeof(pending),
+ "Pending interrupts 0x%x",
+ pending
+ );
+
+ queue = NULL;
+
+ gcmDEBUG_ONLY(
+ if (IDs == 0)
+ {
+ for (i = 0; i < ARRAY_SIZE(Event->queues); ++i)
+ {
+ if (Event->queues[i].head != NULL)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "Queue(%d): stamp=%llu source=%d",
+ i,
+ Event->queues[i].stamp,
+ Event->queues[i].source);
+ }
+ }
+ }
+ );
+
+ /* Find the oldest pending interrupt. */
+ for (i = 0; i < ARRAY_SIZE(Event->queues); ++i)
+ {
+ if ((Event->queues[i].head != NULL)
+ && (pending & (1 << i))
+ )
+ {
+ if ((queue == NULL)
+ || (Event->queues[i].stamp < queue->stamp)
+ )
+ {
+ queue = &Event->queues[i];
+ mask = 1 << i;
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ eventNumber = i;
+#endif
+ }
+ }
+ }
+
+ if (queue == NULL)
+ {
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_ERROR, gcvZONE_EVENT,
+ sizeof(pending),
+ "Interrupts 0x%x are not pending.",
+ pending
+ );
+
+ /* Suspend interrupts. */
+ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvTRUE;
+
+ /* Mark pending interrupts as handled. */
+#ifdef CONFIG_SMP
+ gckOS_AtomClearMask(Event->pending, pending);
+#else
+ Event->pending &= ~pending;
+#endif
+
+ /* Resume interrupts. */
+ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvFALSE;
+
+ break;
+ }
+
+ /* Check whether there is a missed interrupt. */
+ for (i = 0; i < ARRAY_SIZE(Event->queues); ++i)
+ {
+ if ((Event->queues[i].head != NULL)
+ && (Event->queues[i].stamp < queue->stamp)
+ && (Event->queues[i].source == queue->source)
+ )
+ {
+ gcmkTRACE_N(
+ gcvLEVEL_ERROR,
+ sizeof(i) + sizeof(Event->queues[i].stamp),
+ "Event %d lost (stamp %llu)",
+ i, Event->queues[i].stamp
+ );
+
+ /* Use this event instead. */
+ queue = &Event->queues[i];
+ mask = 0;
+ }
+ }
+
+ if (mask != 0)
+ {
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ sizeof(eventNumber),
+ "Processing interrupt %d",
+ eventNumber
+ );
+#endif
+ }
+
+ /* Walk all events for this interrupt. */
+ for (;;)
+ {
+ gcsEVENT_PTR record;
+ gcsEVENT_PTR recordNext = NULL;
+ void *logical;
+#if gcdSECURE_USER
+ size_t bytes;
+#endif
+
+ /* Grab the mutex queue. */
+ gcmkONERROR(gckOS_AcquireMutex(Event->os,
+ Event->eventQueueMutex,
+ gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Grab the event head. */
+ record = queue->head;
+
+ if (record != NULL)
+ {
+ queue->head = record->next;
+ recordNext = record->next;
+ }
+
+ /* Release the mutex queue. */
+ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ acquired = gcvFALSE;
+
+ /* Dispatch on event type. */
+ if (record != NULL)
+ {
+#if gcdSECURE_USER
+ /* Get the cache that belongs to this process. */
+ gcmkONERROR(gckKERNEL_GetProcessDBCache(Event->kernel,
+ record->processID,
+ &cache));
+#endif
+
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_INFO, gcvZONE_EVENT,
+ sizeof(record->info.command),
+ "Processing event type: %d",
+ record->info.command
+ );
+
+ switch (record->info.command)
+ {
+ case gcvHAL_FREE_NON_PAGED_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_FREE_NON_PAGED_MEMORY: 0x%x",
+ record->info.u.FreeNonPagedMemory.physical);
+
+ /* Free non-paged memory. */
+ status = gckOS_FreeNonPagedMemory(
+ Event->os,
+ record->info.u.FreeNonPagedMemory.bytes,
+ record->info.u.FreeNonPagedMemory.physical,
+ record->info.u.FreeNonPagedMemory.logical);
+
+ if (gcmIS_SUCCESS(status))
+ {
+#if gcdSECURE_USER
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ record->event.u.FreeNonPagedMemory.logical,
+ record->event.u.FreeNonPagedMemory.bytes));
+#endif
+ }
+ break;
+
+ case gcvHAL_FREE_CONTIGUOUS_MEMORY:
+ gcmkTRACE_ZONE(
+ gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_FREE_CONTIGUOUS_MEMORY: 0x%x",
+ record->info.u.FreeContiguousMemory.physical);
+
+ /* Unmap the user memory. */
+ status = gckOS_FreeContiguous(
+ Event->os,
+ record->info.u.FreeContiguousMemory.physical,
+ record->info.u.FreeContiguousMemory.logical,
+ record->info.u.FreeContiguousMemory.bytes);
+
+ if (gcmIS_SUCCESS(status))
+ {
+#if gcdSECURE_USER
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ event->event.u.FreeContiguousMemory.logical,
+ event->event.u.FreeContiguousMemory.bytes));
+#endif
+ }
+ break;
+
+ case gcvHAL_FREE_VIDEO_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_FREE_VIDEO_MEMORY: 0x%x",
+ record->info.u.FreeVideoMemory.node);
+
+ /* Free video memory. */
+ status =
+ gckVIDMEM_Free(record->info.u.FreeVideoMemory.node);
+
+ break;
+
+ case gcvHAL_WRITE_DATA:
+ /* Convert physical into logical address. */
+ gcmkERR_BREAK(
+ gckOS_MapPhysical(Event->os,
+ record->info.u.WriteData.address,
+ sizeof(u32),
+ &logical));
+
+ /* Write data. */
+ gcmkERR_BREAK(
+ gckOS_WriteMemory(Event->os,
+ logical,
+ record->info.u.WriteData.data));
+
+ /* Unmap the physical memory. */
+ gcmkERR_BREAK(
+ gckOS_UnmapPhysical(Event->os,
+ logical,
+ sizeof(u32)));
+ break;
+
+ case gcvHAL_UNLOCK_VIDEO_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_UNLOCK_VIDEO_MEMORY: 0x%x",
+ record->info.u.UnlockVideoMemory.node);
+
+ /* Save node information before it disappears. */
+#if gcdSECURE_USER
+ node = event->event.u.UnlockVideoMemory.node;
+ if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ logical = NULL;
+ bytes = 0;
+ }
+ else
+ {
+ logical = node->Virtual.logical;
+ bytes = node->Virtual.bytes;
+ }
+#endif
+
+ /* Unlock. */
+ status = gckVIDMEM_Unlock(
+ Event->kernel,
+ record->info.u.UnlockVideoMemory.node,
+ record->info.u.UnlockVideoMemory.type,
+ NULL);
+
+#if gcdSECURE_USER
+ if (gcmIS_SUCCESS(status) && (logical != NULL))
+ {
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ logical,
+ bytes));
+ }
+#endif
+ break;
+
+ case gcvHAL_SIGNAL:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_SIGNAL: 0x%x",
+ record->info.u.Signal.signal);
+
+ /* Set signal. */
+ if (record->info.u.Signal.process == NULL)
+ {
+ /* Kernel signal. */
+ gcmkERR_BREAK(
+ gckOS_Signal(Event->os,
+ record->info.u.Signal.signal,
+ gcvTRUE));
+ }
+ else
+ {
+ /* User signal. */
+ gcmkERR_BREAK(
+ gckOS_UserSignal(Event->os,
+ record->info.u.Signal.signal,
+ record->info.u.Signal.process));
+ }
+
+ gcmkASSERT(record->info.u.Signal.auxSignal == NULL);
+ break;
+
+ case gcvHAL_UNMAP_USER_MEMORY:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_UNMAP_USER_MEMORY: 0x%x",
+ record->info.u.UnmapUserMemory.info);
+
+ /* Unmap the user memory. */
+ status = gckOS_UnmapUserMemoryEx(
+ Event->os,
+ Event->kernel->core,
+ record->info.u.UnmapUserMemory.memory,
+ record->info.u.UnmapUserMemory.size,
+ record->info.u.UnmapUserMemory.info,
+ record->info.u.UnmapUserMemory.address);
+
+#if gcdSECURE_USER
+ if (gcmIS_SUCCESS(status))
+ {
+ gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(
+ Event->kernel,
+ cache,
+ event->event.u.UnmapUserMemory.memory,
+ event->event.u.UnmapUserMemory.size));
+ }
+#endif
+ gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
+ Event->kernel,
+ record->processID, gcvDB_MAP_USER_MEMORY,
+ record->info.u.UnmapUserMemory.memory));
+ break;
+
+ case gcvHAL_TIMESTAMP:
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "gcvHAL_TIMESTAMP: %d %d",
+ record->info.u.TimeStamp.timer,
+ record->info.u.TimeStamp.request);
+
+ /* Process the timestamp. */
+ switch (record->info.u.TimeStamp.request)
+ {
+ case 0:
+ status = gckOS_GetTime(&Event->kernel->timers[
+ record->info.u.TimeStamp.timer].
+ stopTime);
+ break;
+
+ case 1:
+ status = gckOS_GetTime(&Event->kernel->timers[
+ record->info.u.TimeStamp.timer].
+ startTime);
+ break;
+
+ default:
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_ERROR, gcvZONE_EVENT,
+ sizeof(record->info.u.TimeStamp.request),
+ "Invalid timestamp request: %d",
+ record->info.u.TimeStamp.request
+ );
+
+ status = gcvSTATUS_INVALID_ARGUMENT;
+ break;
+ }
+ break;
+
+ case gcvHAL_COMMIT_DONE:
+ break;
+
+ default:
+ /* Invalid argument. */
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_ERROR, gcvZONE_EVENT,
+ sizeof(record->info.command),
+ "Unknown event type: %d",
+ record->info.command
+ );
+
+ status = gcvSTATUS_INVALID_ARGUMENT;
+ break;
+ }
+
+ /* Make sure there are no errors generated. */
+ if (gcmIS_ERROR(status))
+ {
+ gcmkTRACE_ZONE_N(
+ gcvLEVEL_WARNING, gcvZONE_EVENT,
+ sizeof(status),
+ "Event produced status: %d(%s)",
+ status, gckOS_DebugStatus2Name(status));
+ }
+
+ /* Free the event. */
+ gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record));
+ }
+
+ if (recordNext == NULL)
+ {
+ break;
+ }
+ }
+
+ /* Increase the number of free events. */
+ gcmkONERROR(gckOS_AtomIncrement(Event->os, Event->freeAtom, &free));
+
+ gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
+ "Handled interrupt 0x%x", mask);
+
+ /* Suspend interrupts. */
+ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvTRUE;
+
+ /* Mark pending interrupt as handled. */
+#ifdef CONFIG_SMP
+ gckOS_AtomClearMask(Event->pending, mask);
+#else
+ Event->pending &= ~mask;
+#endif
+
+ /* Resume interrupts. */
+ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ suspended = gcvFALSE;
+ }
+
+ if (IDs == 0)
+ {
+ gcmkONERROR(_TryToIdleGPU(Event));
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
+ }
+
+ if (suspended)
+ {
+ /* Resume interrupts. */
+ gcmkVERIFY_OK(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** gckEVENT_Stop
+**
+** Stop the hardware using the End event mechanism.
+**
+** INPUT:
+**
+** gckEVENT Event
+** Pointer to an gckEVENT object.
+**
+** u32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If NULL it is video memory.
+**
+** void *Logical
+** Logical address to flush.
+**
+** gctSIGNAL Signal
+** Pointer to the signal to trigger.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckEVENT_Stop(
+ IN gckEVENT Event,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN void *Logical,
+ IN gctSIGNAL Signal,
+ IN OUT size_t * waitSize
+ )
+{
+ gceSTATUS status;
+ /* size_t waitSize;*/
+ gcsEVENT_PTR record;
+ u8 id = 0xFF;
+
+ gcmkHEADER_ARG("Event=0x%x ProcessID=%u Handle=0x%x Logical=0x%x "
+ "Signal=0x%x",
+ Event, ProcessID, Handle, Logical, Signal);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
+
+ /* Submit the current event queue. */
+ gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE));
+
+ gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL));
+
+ /* Allocate a record. */
+ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &record));
+
+ /* Initialize the record. */
+ record->next = NULL;
+ record->processID = ProcessID;
+ record->info.command = gcvHAL_SIGNAL;
+ record->info.u.Signal.signal = Signal;
+ record->info.u.Signal.auxSignal = NULL;
+ record->info.u.Signal.process = NULL;
+
+ /* Append the record. */
+ Event->queues[id].head = record;
+
+ /* Replace last WAIT with END. */
+ gcmkONERROR(gckHARDWARE_End(
+ Event->kernel->hardware, Logical, waitSize
+ ));
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache for the END. */
+ gcmkONERROR(gckOS_CacheClean(
+ Event->os,
+ ProcessID,
+ NULL,
+ Handle,
+ Logical,
+ *waitSize
+ ));
+#endif
+
+ /* Wait for the signal. */
+ gcmkONERROR(gckOS_WaitSignal(Event->os, Signal, gcvINFINITE));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+static void
+_PrintRecord(
+ gcsEVENT_PTR record
+ )
+{
+ switch (record->info.command)
+ {
+ case gcvHAL_FREE_NON_PAGED_MEMORY:
+ gcmkPRINT(" gcvHAL_FREE_NON_PAGED_MEMORY");
+ break;
+
+ case gcvHAL_FREE_CONTIGUOUS_MEMORY:
+ gcmkPRINT(" gcvHAL_FREE_CONTIGUOUS_MEMORY");
+ break;
+
+ case gcvHAL_FREE_VIDEO_MEMORY:
+ gcmkPRINT(" gcvHAL_FREE_VIDEO_MEMORY");
+ break;
+
+ case gcvHAL_WRITE_DATA:
+ gcmkPRINT(" gcvHAL_WRITE_DATA");
+ break;
+
+ case gcvHAL_UNLOCK_VIDEO_MEMORY:
+ gcmkPRINT(" gcvHAL_UNLOCK_VIDEO_MEMORY");
+ break;
+
+ case gcvHAL_SIGNAL:
+ gcmkPRINT(" gcvHAL_SIGNAL process=%d signal=0x%x",
+ record->info.u.Signal.process,
+ record->info.u.Signal.signal);
+ break;
+
+ case gcvHAL_UNMAP_USER_MEMORY:
+ gcmkPRINT(" gcvHAL_UNMAP_USER_MEMORY");
+ break;
+
+ case gcvHAL_TIMESTAMP:
+ gcmkPRINT(" gcvHAL_TIMESTAMP");
+ break;
+
+ case gcvHAL_COMMIT_DONE:
+ gcmkPRINT(" gcvHAL_COMMIT_DONE");
+ break;
+
+ default:
+ gcmkPRINT(" Illegal Event %d", record->info.command);
+ break;
+ }
+}
+
+/*******************************************************************************
+** gckEVENT_Dump
+**
+** Dump record in event queue when stuck happens.
+** No protection for the event queue.
+**/
+gceSTATUS
+gckEVENT_Dump(
+ IN gckEVENT Event
+ )
+{
+ gcsEVENT_QUEUE_PTR queueHead = Event->queueHead;
+ gcsEVENT_QUEUE_PTR queue;
+ gcsEVENT_PTR record = NULL;
+ int i;
+
+ gcmkHEADER_ARG("Event=0x%x", Event);
+
+ gcmkPRINT("**************************\n");
+ gcmkPRINT("*** EVENT STATE DUMP ***\n");
+ gcmkPRINT("**************************\n");
+
+
+ gcmkPRINT(" Unsumbitted Event:");
+ while(queueHead)
+ {
+ queue = queueHead;
+ record = queueHead->head;
+
+ gcmkPRINT(" [%x]:", queue);
+ while(record)
+ {
+ _PrintRecord(record);
+ record = record->next;
+ }
+
+ if (queueHead == Event->queueTail)
+ {
+ queueHead = NULL;
+ }
+ else
+ {
+ queueHead = queueHead->next;
+ }
+ }
+
+ gcmkPRINT(" Untriggered Event:");
+ for (i = 0; i < 30; i++)
+ {
+ queue = &Event->queues[i];
+ record = queue->head;
+
+ gcmkPRINT(" [%d]:", i);
+ while(record)
+ {
+ _PrintRecord(record);
+ record = record->next;
+ }
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.c
new file mode 100644
index 0000000..fcc5c3f
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.c
@@ -0,0 +1,5087 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.
+*
+*****************************************************************************/
+
+
+
+#ifdef CONFIG_MACH_JZ4770
+#include <linux/sched.h>
+#endif
+
+#include "gc_hal.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+
+#include <linux/kernel.h>
+
+#define _GC_OBJ_ZONE gcvZONE_HARDWARE
+
+/******************************************************************************\
+********************************* Support Code *********************************
+\******************************************************************************/
+static gceSTATUS
+_ResetGPU(
+ IN gckHARDWARE Hardware,
+ IN gckOS Os,
+ IN gceCORE Core
+ );
+
+static gceSTATUS
+_IdentifyHardware(
+ IN gckOS Os,
+ IN gceCORE Core,
+ OUT struct _gcsHAL_QUERY_CHIP_IDENTITY *Identity
+ )
+{
+ gceSTATUS status;
+
+ u32 chipIdentity;
+
+ u32 streamCount = 0;
+ u32 registerMax = 0;
+ u32 threadCount = 0;
+ u32 shaderCoreCount = 0;
+ u32 vertexCacheSize = 0;
+ u32 vertexOutputBufferSize = 0;
+ u32 pixelPipes = 0;
+ u32 instructionCount = 0;
+ u32 numConstants = 0;
+ u32 bufferSize = 0;
+
+ gcmkHEADER_ARG("Os=0x%x", Os);
+
+ /***************************************************************************
+ ** Get chip ID and revision.
+ */
+
+ /* Read chip identity register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00018,
+ &chipIdentity));
+
+ /* Special case for older graphic cores. */
+ if (gcmVERIFYFIELDVALUE(chipIdentity, 31:24, 0x01 ))
+ {
+ Identity->chipModel = gcv500;
+ Identity->chipRevision = gcmGETFIELD(chipIdentity, 15:12);
+ }
+
+ else
+ {
+ /* Read chip identity register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00020,
+ (u32 *) &Identity->chipModel));
+
+ /* !!!! HACK ALERT !!!! */
+ /* Because people change device IDs without letting software know
+ ** about it - here is the hack to make it all look the same. Only
+ ** for GC400 family. Next time - TELL ME!!! */
+ if (((Identity->chipModel & 0xFF00) == 0x0400)
+ && (Identity->chipModel != 0x0420))
+ {
+ Identity->chipModel = (gceCHIPMODEL) (Identity->chipModel & 0x0400);
+ }
+
+ /* Read CHIP_REV register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00024,
+ &Identity->chipRevision));
+
+ if ((Identity->chipModel == gcv300)
+ && (Identity->chipRevision == 0x2201)
+ )
+ {
+ u32 chipDate;
+ u32 chipTime;
+
+ /* Read date and time registers. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00028,
+ &chipDate));
+
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x0002C,
+ &chipTime));
+
+ if ((chipDate == 0x20080814) && (chipTime == 0x12051100))
+ {
+ /* This IP has an ECO; put the correct revision in it. */
+ Identity->chipRevision = 0x1051;
+ }
+ }
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Identity: chipModel=%X",
+ Identity->chipModel);
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Identity: chipRevision=%X",
+ Identity->chipRevision);
+
+
+ /***************************************************************************
+ ** Get chip features.
+ */
+
+ /* Read chip feature register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x0001C,
+ &Identity->chipFeatures));
+
+#if !VIVANTE_NO_3D
+ /* Disable fast clear on GC700. */
+ if (Identity->chipModel == gcv700)
+ {
+ Identity->chipFeatures
+ = gcmSETFIELD(Identity->chipFeatures, 0:0, 0x0 );
+ }
+#endif
+
+ if (((Identity->chipModel == gcv500) && (Identity->chipRevision < 2))
+ || ((Identity->chipModel == gcv300) && (Identity->chipRevision < 0x2000))
+ )
+ {
+ /* GC500 rev 1.x and GC300 rev < 2.0 doesn't have these registers. */
+ Identity->chipMinorFeatures = 0;
+ Identity->chipMinorFeatures1 = 0;
+ Identity->chipMinorFeatures2 = 0;
+ Identity->chipMinorFeatures3 = 0;
+ }
+ else
+ {
+ /* Read chip minor feature register #0. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00034,
+ &Identity->chipMinorFeatures));
+
+ if (gcmVERIFYFIELDVALUE(Identity->chipMinorFeatures, 21:21, 0x1 )
+ )
+ {
+ /* Read chip minor featuress register #1. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00074,
+ &Identity->chipMinorFeatures1));
+
+ /* Read chip minor featuress register #2. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00084,
+ &Identity->chipMinorFeatures2));
+
+ /*Identity->chipMinorFeatures2 &= ~(0x1 << 3);*/
+
+ /* Read chip minor featuress register #1. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00088,
+ &Identity->chipMinorFeatures3));
+ }
+ else
+ {
+ /* Chip doesn't has minor features register #1 or 2 or 3. */
+ Identity->chipMinorFeatures1 = 0;
+ Identity->chipMinorFeatures2 = 0;
+ Identity->chipMinorFeatures3 = 0;
+ }
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Identity: chipFeatures=0x%08X",
+ Identity->chipFeatures);
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Identity: chipMinorFeatures=0x%08X",
+ Identity->chipMinorFeatures);
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Identity: chipMinorFeatures1=0x%08X",
+ Identity->chipMinorFeatures1);
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Identity: chipMinorFeatures2=0x%08X",
+ Identity->chipMinorFeatures2);
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Identity: chipMinorFeatures3=0x%08X",
+ Identity->chipMinorFeatures3);
+
+ /***************************************************************************
+ ** Get chip specs.
+ */
+
+ if (gcmVERIFYFIELDVALUE(Identity->chipMinorFeatures, 21:21, 0x1 ))
+ {
+ u32 specs, specs2;
+
+ /* Read gcChipSpecs register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00048,
+ &specs));
+
+ /* Extract the fields. */
+ streamCount = gcmGETFIELD(specs, 3:0);
+ registerMax = gcmGETFIELD(specs, 7:4);
+ threadCount = gcmGETFIELD(specs, 11:8);
+ shaderCoreCount = gcmGETFIELD(specs, 24:20);
+ vertexCacheSize = gcmGETFIELD(specs, 16:12);
+ vertexOutputBufferSize = gcmGETFIELD(specs, 31:28);
+ pixelPipes = gcmGETFIELD(specs, 27:25);
+
+ /* Read gcChipSpecs2 register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Os, Core,
+ 0x00080,
+ &specs2));
+
+ instructionCount = gcmGETFIELD(specs2, 15:8);
+ numConstants = gcmGETFIELD(specs2, 31:16);
+ bufferSize = gcmGETFIELD(specs2, 7:0);
+ }
+
+ /* Get the number of pixel pipes. */
+ Identity->pixelPipes = max(pixelPipes, 1u);
+
+ /* Get the stream count. */
+ Identity->streamCount = (streamCount != 0)
+ ? streamCount
+ : (Identity->chipModel >= gcv1000) ? 4 : 1;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: streamCount=%u%s",
+ Identity->streamCount,
+ (streamCount == 0) ? " (default)" : "");
+
+ /* Get the vertex output buffer size. */
+ Identity->vertexOutputBufferSize = (vertexOutputBufferSize != 0)
+ ? 1 << vertexOutputBufferSize
+ : (Identity->chipModel == gcv400)
+ ? (Identity->chipRevision < 0x4000) ? 512
+ : (Identity->chipRevision < 0x4200) ? 256
+ : 128
+ : (Identity->chipModel == gcv530)
+ ? (Identity->chipRevision < 0x4200) ? 512
+ : 128
+ : 512;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: vertexOutputBufferSize=%u%s",
+ Identity->vertexOutputBufferSize,
+ (vertexOutputBufferSize == 0) ? " (default)" : "");
+
+ /* Get the maximum number of threads. */
+ Identity->threadCount = (threadCount != 0)
+ ? 1 << threadCount
+ : (Identity->chipModel == gcv400) ? 64
+ : (Identity->chipModel == gcv500) ? 128
+ : (Identity->chipModel == gcv530) ? 128
+ : 256;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: threadCount=%u%s",
+ Identity->threadCount,
+ (threadCount == 0) ? " (default)" : "");
+
+ /* Get the number of shader cores. */
+ Identity->shaderCoreCount = (shaderCoreCount != 0)
+ ? shaderCoreCount
+ : (Identity->chipModel >= gcv1000) ? 2
+ : 1;
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: shaderCoreCount=%u%s",
+ Identity->shaderCoreCount,
+ (shaderCoreCount == 0) ? " (default)" : "");
+
+ /* Get the vertex cache size. */
+ Identity->vertexCacheSize = (vertexCacheSize != 0)
+ ? vertexCacheSize
+ : 8;
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: vertexCacheSize=%u%s",
+ Identity->vertexCacheSize,
+ (vertexCacheSize == 0) ? " (default)" : "");
+
+ /* Get the maximum number of temporary registers. */
+ Identity->registerMax = (registerMax != 0)
+ /* Maximum of registerMax/4 registers are accessible to 1 shader */
+ ? 1 << registerMax
+ : (Identity->chipModel == gcv400) ? 32
+ : 64;
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: registerMax=%u%s",
+ Identity->registerMax,
+ (registerMax == 0) ? " (default)" : "");
+
+ /* Get the instruction count. */
+ Identity->instructionCount = (instructionCount == 0) ? 256
+ : (instructionCount == 1) ? 1024
+ : (instructionCount == 2) ? 2048
+ : 256;
+
+ if (Identity->chipModel == gcv2000 && Identity->chipRevision == 0x5108)
+ {
+ Identity->instructionCount = 512;
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: instructionCount=%u%s",
+ Identity->instructionCount,
+ (instructionCount == 0) ? " (default)" : "");
+
+ /* Get the number of constants. */
+ Identity->numConstants = numConstants;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: numConstants=%u%s",
+ Identity->numConstants,
+ (numConstants == 0) ? " (default)" : "");
+
+ /* Get the buffer size. */
+ Identity->bufferSize = bufferSize;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Specs: bufferSize=%u%s",
+ Identity->bufferSize,
+ (bufferSize == 0) ? " (default)" : "");
+
+ /* Success. */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#if gcdPOWEROFF_TIMEOUT
+static void
+_PowerTimerFunction(
+ void *Data
+ )
+{
+ gckHARDWARE hardware = (gckHARDWARE)Data;
+ gcmkVERIFY_OK(
+ gckHARDWARE_SetPowerManagementState(hardware, gcvPOWER_OFF_TIMEOUT));
+}
+#endif
+
+/******************************************************************************\
+****************************** gckHARDWARE API code *****************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckHARDWARE_Construct
+**
+** Construct a new gckHARDWARE object.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an initialized gckOS object.
+**
+** gceCORE Core
+** Specified core.
+**
+** OUTPUT:
+**
+** gckHARDWARE * Hardware
+** Pointer to a variable that will hold the pointer to the gckHARDWARE
+** object.
+*/
+gceSTATUS
+gckHARDWARE_Construct(
+ IN gckOS Os,
+ IN gceCORE Core,
+ OUT gckHARDWARE * Hardware
+ )
+{
+ gceSTATUS status;
+ gckHARDWARE hardware = NULL;
+ u16 data = 0xff00;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Os=0x%x", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Hardware != NULL);
+
+ /* Enable the GPU. */
+ gcmkONERROR(gckOS_SetGPUPower(Os, gcvTRUE, gcvTRUE));
+ gcmkONERROR(gckOS_WriteRegisterEx(Os, Core, 0x00000, 0));
+
+ /* Allocate the gckHARDWARE object. */
+ gcmkONERROR(gckOS_Allocate(Os,
+ sizeof(struct _gckHARDWARE),
+ &pointer));
+
+ hardware = (gckHARDWARE) pointer;
+
+ /* Initialize the gckHARDWARE object. */
+ hardware->object.type = gcvOBJ_HARDWARE;
+ hardware->os = Os;
+ hardware->core = Core;
+
+ /* Identify the hardware. */
+ gcmkONERROR(_IdentifyHardware(Os, Core, &hardware->identity));
+
+ /* Determine the hardware type */
+ switch (hardware->identity.chipModel)
+ {
+ case gcv350:
+ case gcv355:
+ hardware->type = gcvHARDWARE_VG;
+ break;
+
+ case gcv300:
+ case gcv320:
+ hardware->type = gcvHARDWARE_2D;
+ break;
+
+ default:
+ hardware->type = gcvHARDWARE_3D;
+
+ if (gcmGETFIELD(hardware->identity.chipFeatures, 9:9))
+ {
+ hardware->type = (gceHARDWARE_TYPE) (hardware->type | gcvHARDWARE_2D);
+ }
+ }
+
+ hardware->powerBaseAddress
+ = ((hardware->identity.chipModel == gcv300)
+ && (hardware->identity.chipRevision < 0x2000))
+ ? 0x0100
+ : 0x0000;
+
+ /* _ResetGPU need powerBaseAddress. */
+ status = _ResetGPU(hardware, Os, Core);
+
+ if (status != gcvSTATUS_OK)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "_ResetGPU failed: status=%d\n", status);
+ }
+
+ hardware->powerMutex = NULL;
+
+ hardware->mmuVersion
+ = gcmGETFIELD(hardware->identity.chipMinorFeatures1, 28:28);
+
+ /* Determine whether bug fixes #1 are present. */
+ hardware->extraEventStates = gcmVERIFYFIELDVALUE(hardware->identity.chipMinorFeatures1, 3:3, 0x0 );
+
+ /* Check if big endian */
+ hardware->bigEndian = (*(u8 *)&data == 0xff);
+
+ /* Initialize the fast clear. */
+ gcmkONERROR(gckHARDWARE_SetFastClear(hardware, -1, -1));
+
+#if !gcdENABLE_128B_MERGE && 1 && 1
+
+ if (gcmVERIFYFIELDVALUE(hardware->identity.chipMinorFeatures2, 21:21, 0x1 ))
+ {
+ /* 128B merge is turned on by default. Disable it. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Os, Core, 0x00558, 0));
+ }
+
+#endif
+
+ /* Set power state to ON. */
+ hardware->chipPowerState = gcvPOWER_ON;
+ hardware->clockState = gcvTRUE;
+ hardware->powerState = gcvTRUE;
+ hardware->lastWaitLink = ~0U;
+ hardware->globalSemaphore = NULL;
+
+ gcmkONERROR(gckOS_CreateMutex(Os, &hardware->powerMutex));
+ gcmkONERROR(gckOS_CreateSemaphore(Os, &hardware->globalSemaphore));
+
+#if gcdPOWEROFF_TIMEOUT
+ hardware->powerOffTimeout = gcdPOWEROFF_TIMEOUT;
+
+ gcmkVERIFY_OK(gckOS_CreateTimer(Os,
+ (void *)_PowerTimerFunction,
+ (void *)hardware,
+ &hardware->powerOffTimer));
+#endif
+
+ gcmkONERROR(gckOS_AtomConstruct(Os, &hardware->pageTableDirty));
+
+ /* Return pointer to the gckHARDWARE object. */
+ *Hardware = hardware;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Hardware=0x%x", *Hardware);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (hardware != NULL)
+ {
+ /* Turn off the power. */
+ gcmkVERIFY_OK(gckOS_SetGPUPower(Os, gcvFALSE, gcvFALSE));
+
+ if (hardware->globalSemaphore != NULL)
+ {
+ /* Destroy the global semaphore. */
+ gcmkVERIFY_OK(gckOS_DestroySemaphore(Os,
+ hardware->globalSemaphore));
+ }
+
+ if (hardware->powerMutex != NULL)
+ {
+ /* Destroy the power mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, hardware->powerMutex));
+ }
+
+#if gcdPOWEROFF_TIMEOUT
+ if (hardware->powerOffTimer != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_StopTimer(Os, hardware->powerOffTimer));
+ gcmkVERIFY_OK(gckOS_DestoryTimer(Os, hardware->powerOffTimer));
+ }
+#endif
+
+ if (hardware->pageTableDirty != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Os, hardware->pageTableDirty));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, hardware));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_Destroy
+**
+** Destroy an gckHARDWARE object.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object that needs to be destroyed.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_Destroy(
+ IN gckHARDWARE Hardware
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Turn off the power. */
+ gcmkVERIFY_OK(gckOS_SetGPUPower(Hardware->os, gcvFALSE, gcvFALSE));
+
+ /* Destroy the power semaphore. */
+ gcmkVERIFY_OK(gckOS_DestroySemaphore(Hardware->os,
+ Hardware->globalSemaphore));
+
+ /* Destroy the power mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Hardware->os, Hardware->powerMutex));
+
+#if gcdPOWEROFF_TIMEOUT
+ gcmkVERIFY_OK(gckOS_StopTimer(Hardware->os, Hardware->powerOffTimer));
+ gcmkVERIFY_OK(gckOS_DestoryTimer(Hardware->os, Hardware->powerOffTimer));
+#endif
+
+ gcmkVERIFY_OK(gckOS_AtomDestroy(Hardware->os, Hardware->pageTableDirty));
+
+ /* Mark the object as unknown. */
+ Hardware->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the object. */
+ gcmkONERROR(gcmkOS_SAFE_FREE(Hardware->os, Hardware));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_GetType
+**
+** Get the hardware type.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** OUTPUT:
+**
+** gceHARDWARE_TYPE * Type
+** Pointer to a variable that receives the type of hardware object.
+*/
+gceSTATUS
+gckHARDWARE_GetType(
+ IN gckHARDWARE Hardware,
+ OUT gceHARDWARE_TYPE * Type
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+ gcmkVERIFY_ARGUMENT(Type != NULL);
+
+ *Type = Hardware->type;
+
+ gcmkFOOTER_ARG("*Type=%d", *Type);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_InitializeHardware
+**
+** Initialize the hardware.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_InitializeHardware(
+ IN gckHARDWARE Hardware
+ )
+{
+ gceSTATUS status;
+ u32 baseAddress;
+ u32 chipRev;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Read the chip revision register. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00024,
+ &chipRev));
+
+ if (chipRev != Hardware->identity.chipRevision)
+ {
+ /* Chip is not there! */
+ gcmkONERROR(gcvSTATUS_CONTEXT_LOSSED);
+ }
+
+ /* Disable isolate GPU bit. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00000,
+ gcmSETFIELD(0x00000100, 19:19, 0)));
+
+ /* Reset memory counters. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0003C,
+ ~0U));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0003C,
+ 0));
+
+ /* Get the system's physical base address. */
+ gcmkONERROR(gckOS_GetBaseAddress(Hardware->os, &baseAddress));
+
+ /* Program the base addesses. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0041C,
+ baseAddress));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00418,
+ baseAddress));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00428,
+ baseAddress));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00420,
+ baseAddress));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00424,
+ baseAddress));
+
+#if !VIVANTE_PROFILER && 1
+ {
+ u32 data;
+
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ Hardware->powerBaseAddress +
+ 0x00100,
+ &data));
+
+ /* Enable clock gating. */
+ data = gcmSETFIELD(data, 0:0, 1);
+
+ if ((Hardware->identity.chipRevision == 0x4301)
+ || (Hardware->identity.chipRevision == 0x4302)
+ )
+ {
+ /* Disable stall module level clock gating for 4.3.0.1 and 4.3.0.2
+ ** revisions. */
+ data = gcmSETFIELD(data, 1:1, 1);
+ }
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ Hardware->powerBaseAddress
+ + 0x00100,
+ data));
+
+#if !VIVANTE_NO_3D
+ /* Disable PE clock gating on revs < 5.0 when HZ is present without a
+ ** bug fix. */
+ if ((Hardware->identity.chipRevision < 0x5000)
+ && gcmVERIFYFIELDVALUE(Hardware->identity.chipMinorFeatures1, 9:9, 0x0 )
+ && gcmVERIFYFIELDVALUE(Hardware->identity.chipMinorFeatures, 27:27, 0x1 )
+ )
+ {
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ Hardware->powerBaseAddress
+ + 0x00104,
+ &data));
+
+ /* Disable PE clock gating. */
+ data = gcmSETFIELD(data, 2:2, 1);
+
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ Hardware->powerBaseAddress
+ + 0x00104,
+ data));
+ }
+
+#endif
+ }
+#endif
+
+ /* Special workaround for this core
+ ** Make sure pulse eater kicks in only when SH is idle */
+ if (Hardware->identity.chipModel == gcv4000 &&
+ Hardware->identity.chipRevision == 0x5208)
+ {
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0010C,
+ gcmSETFIELD(0x01590880, 23:23, 1)));
+ }
+
+ /* Special workaround for this core
+ ** Make sure FE and TX are on different buses */
+ if ((Hardware->identity.chipModel == gcv2000)
+ && (Hardware->identity.chipRevision == 0x5108))
+ {
+ u32 data;
+
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00480,
+ &data));
+
+ /* Set FE bus to one, TX bus to zero */
+ data = gcmSETFIELD(data, 3:3, 1);
+ data = gcmSETFIELD(data, 7:7, 0);
+
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00480,
+ data));
+ }
+
+ /* Test if MMU is initialized. */
+ if ((Hardware->kernel != NULL)
+ && (Hardware->kernel->mmu != NULL)
+ )
+ {
+ /* Reset MMU. */
+ if (Hardware->mmuVersion == 0)
+ {
+ gcmkONERROR(
+ gckHARDWARE_SetMMU(Hardware,
+ Hardware->kernel->mmu->pageTableLogical));
+ }
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the error. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_QueryMemory
+**
+** Query the amount of memory available on the hardware.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** OUTPUT:
+**
+** size_t * InternalSize
+** Pointer to a variable that will hold the size of the internal video
+** memory in bytes. If 'InternalSize' is NULL, no information of the
+** internal memory will be returned.
+**
+** u32 * InternalBaseAddress
+** Pointer to a variable that will hold the hardware's base address for
+** the internal video memory. This pointer cannot be NULL if
+** 'InternalSize' is also non-NULL.
+**
+** u32 * InternalAlignment
+** Pointer to a variable that will hold the hardware's base address for
+** the internal video memory. This pointer cannot be NULL if
+** 'InternalSize' is also non-NULL.
+**
+** size_t * ExternalSize
+** Pointer to a variable that will hold the size of the external video
+** memory in bytes. If 'ExternalSize' is NULL, no information of the
+** external memory will be returned.
+**
+** u32 * ExternalBaseAddress
+** Pointer to a variable that will hold the hardware's base address for
+** the external video memory. This pointer cannot be NULL if
+** 'ExternalSize' is also non-NULL.
+**
+** u32 * ExternalAlignment
+** Pointer to a variable that will hold the hardware's base address for
+** the external video memory. This pointer cannot be NULL if
+** 'ExternalSize' is also non-NULL.
+**
+** u32 * HorizontalTileSize
+** Number of horizontal pixels per tile. If 'HorizontalTileSize' is
+** NULL, no horizontal pixel per tile will be returned.
+**
+** u32 * VerticalTileSize
+** Number of vertical pixels per tile. If 'VerticalTileSize' is
+** NULL, no vertical pixel per tile will be returned.
+*/
+gceSTATUS
+gckHARDWARE_QueryMemory(
+ IN gckHARDWARE Hardware,
+ OUT size_t * InternalSize,
+ OUT u32 * InternalBaseAddress,
+ OUT u32 * InternalAlignment,
+ OUT size_t * ExternalSize,
+ OUT u32 * ExternalBaseAddress,
+ OUT u32 * ExternalAlignment,
+ OUT u32 * HorizontalTileSize,
+ OUT u32 * VerticalTileSize
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ if (InternalSize != NULL)
+ {
+ /* No internal memory. */
+ *InternalSize = 0;
+ }
+
+ if (ExternalSize != NULL)
+ {
+ /* No external memory. */
+ *ExternalSize = 0;
+ }
+
+ if (HorizontalTileSize != NULL)
+ {
+ /* 4x4 tiles. */
+ *HorizontalTileSize = 4;
+ }
+
+ if (VerticalTileSize != NULL)
+ {
+ /* 4x4 tiles. */
+ *VerticalTileSize = 4;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*InternalSize=%lu *InternalBaseAddress=0x%08x "
+ "*InternalAlignment=0x%08x *ExternalSize=%lu "
+ "*ExternalBaseAddress=0x%08x *ExtenalAlignment=0x%08x "
+ "*HorizontalTileSize=%u *VerticalTileSize=%u",
+ gcmOPT_VALUE(InternalSize),
+ gcmOPT_VALUE(InternalBaseAddress),
+ gcmOPT_VALUE(InternalAlignment),
+ gcmOPT_VALUE(ExternalSize),
+ gcmOPT_VALUE(ExternalBaseAddress),
+ gcmOPT_VALUE(ExternalAlignment),
+ gcmOPT_VALUE(HorizontalTileSize),
+ gcmOPT_VALUE(VerticalTileSize));
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_QueryChipIdentity
+**
+** Query the identity of the hardware.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** OUTPUT:
+**
+** struct _gcsHAL_QUERY_CHIP_IDENTITY *Identity
+** Pointer to the identity structure.
+**
+*/
+gceSTATUS
+gckHARDWARE_QueryChipIdentity(
+ IN gckHARDWARE Hardware,
+ OUT struct _gcsHAL_QUERY_CHIP_IDENTITY *Identity
+ )
+{
+ u32 features;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(Identity != NULL);
+
+ /* Return chip model and revision. */
+ Identity->chipModel = Hardware->identity.chipModel;
+ Identity->chipRevision = Hardware->identity.chipRevision;
+
+ /* Return feature set. */
+ features = Hardware->identity.chipFeatures;
+
+ if (gcmGETFIELD(features, 0:0))
+ {
+ /* Override fast clear by command line. */
+ features = gcmSETFIELD(features, 0:0, Hardware->allowFastClear);
+ }
+
+ if (gcmGETFIELD(features, 5:5))
+ {
+ /* Override compression by command line. */
+ features = gcmSETFIELD(features, 5:5, Hardware->allowCompression);
+ }
+
+ /* Mark 2D pipe as available for GC500.0 through GC500.2 and GC300,
+ ** since they did not have this bit. */
+ if (((Hardware->identity.chipModel == gcv500) && (Hardware->identity.chipRevision <= 2))
+ || (Hardware->identity.chipModel == gcv300)
+ )
+ {
+ features = gcmSETFIELD(features, 9:9, 0x1 );
+ }
+
+ Identity->chipFeatures = features;
+
+ /* Return minor features. */
+ Identity->chipMinorFeatures = Hardware->identity.chipMinorFeatures;
+ Identity->chipMinorFeatures1 = Hardware->identity.chipMinorFeatures1;
+ Identity->chipMinorFeatures2 = Hardware->identity.chipMinorFeatures2;
+ Identity->chipMinorFeatures3 = Hardware->identity.chipMinorFeatures3;
+
+ /* Return chip specs. */
+ Identity->streamCount = Hardware->identity.streamCount;
+ Identity->registerMax = Hardware->identity.registerMax;
+ Identity->threadCount = Hardware->identity.threadCount;
+ Identity->shaderCoreCount = Hardware->identity.shaderCoreCount;
+ Identity->vertexCacheSize = Hardware->identity.vertexCacheSize;
+ Identity->vertexOutputBufferSize = Hardware->identity.vertexOutputBufferSize;
+ Identity->pixelPipes = Hardware->identity.pixelPipes;
+ Identity->instructionCount = Hardware->identity.instructionCount;
+ Identity->numConstants = Hardware->identity.numConstants;
+ Identity->bufferSize = Hardware->identity.bufferSize;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_SplitMemory
+**
+** Split a hardware specific memory address into a pool and offset.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** u32 Address
+** Address in hardware specific format.
+**
+** OUTPUT:
+**
+** gcePOOL * Pool
+** Pointer to a variable that will hold the pool type for the address.
+**
+** u32 * Offset
+** Pointer to a variable that will hold the offset for the address.
+*/
+gceSTATUS
+gckHARDWARE_SplitMemory(
+ IN gckHARDWARE Hardware,
+ IN u32 Address,
+ OUT gcePOOL * Pool,
+ OUT u32 * Offset
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x Addres=0x%08x", Hardware, Address);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(Pool != NULL);
+ gcmkVERIFY_ARGUMENT(Offset != NULL);
+
+ /* Dispatch on memory type. */
+ switch (gcmGETFIELD(Address, 31:31))
+ {
+ case 0x0:
+ /* System memory. */
+ *Pool = gcvPOOL_SYSTEM;
+ break;
+
+ case 0x1:
+ /* Virtual memory. */
+ *Pool = gcvPOOL_VIRTUAL;
+ break;
+
+ default:
+ /* Invalid memory type. */
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT);
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ /* Return offset of address. */
+ *Offset = gcmGETFIELD(Address, 30:0);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Pool=%d *Offset=0x%08x", *Pool, *Offset);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_Execute
+**
+** Kickstart the hardware's command processor with an initialized command
+** buffer.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** void *Logical
+** Logical address of command buffer.
+**
+** size_t Bytes
+** Number of bytes for the prefetch unit (until after the first LINK).
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_Execute(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN size_t Bytes
+ )
+{
+ gceSTATUS status;
+ u32 address = 0, control;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Bytes=%lu",
+ Hardware, Logical, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+
+ /* Convert logical into hardware specific address. */
+ gcmkONERROR(
+ gckHARDWARE_ConvertLogical(Hardware, Logical, &address));
+
+ /* Enable all events. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00014, ~0U));
+
+ /* Write address register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00654, address));
+
+ /* Build control register. */
+ control = gcmSETFIELD(0, 16:16, 0x1 )
+ | gcmSETFIELD(0, 15:0, (Bytes + 7) >> 3);
+
+ /* Set big endian */
+ if (Hardware->bigEndian)
+ {
+ control |= gcmSETFIELD(0, 21:20, 0x2 );
+ }
+
+ /* Write control register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00658, control));
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Started command buffer @ 0x%08x",
+ address);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_WaitLink
+**
+** Append a WAIT/LINK command sequence at the specified location in the command
+** queue.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Pointer to the current location inside the command queue to append
+** WAIT/LINK command sequence at or NULL just to query the size of the
+** WAIT/LINK command sequence.
+**
+** u32 Offset
+** Offset into command buffer required for alignment.
+**
+** size_t * Bytes
+** Pointer to the number of bytes available for the WAIT/LINK command
+** sequence. If 'Logical' is NULL, this argument will be ignored.
+**
+** OUTPUT:
+**
+** size_t * Bytes
+** Pointer to a variable that will receive the number of bytes required
+** by the WAIT/LINK command sequence. If 'Bytes' is NULL, nothing will
+** be returned.
+**
+** u32 * WaitOffset
+** Pointer to a variable that will receive the offset of the WAIT command
+** from the specified logcial pointer.
+** If 'WaitOffset' is NULL nothing will be returned.
+**
+** size_t * WaitSize
+** Pointer to a variable that will receive the number of bytes used by
+** the WAIT command. If 'LinkSize' is NULL nothing will be returned.
+*/
+gceSTATUS
+gckHARDWARE_WaitLink(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN u32 Offset,
+ IN OUT size_t * Bytes,
+ OUT u32 * WaitOffset,
+ OUT size_t * WaitSize
+ )
+{
+ static const unsigned int waitCount = 200;
+
+ gceSTATUS status;
+ u32 address;
+ u32 *logical;
+ size_t bytes;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Offset=0x%08x *Bytes=%lu",
+ Hardware, Logical, Offset, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT((Logical != NULL) || (Bytes != NULL));
+
+ /* Compute number of bytes required. */
+#if gcd6000_SUPPORT
+ bytes = gcmALIGN(Offset + 96, 8) - Offset;
+#else
+ bytes = gcmALIGN(Offset + 16, 8) - Offset;
+#endif
+
+ /* Cast the input pointer. */
+ logical = (u32 *) Logical;
+
+ if (logical != NULL)
+ {
+ /* Not enough space? */
+ if (*Bytes < bytes)
+ {
+ /* Command queue too small. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+
+ /* Convert logical into hardware specific address. */
+ gcmkONERROR(gckHARDWARE_ConvertLogical(Hardware, logical, &address));
+
+ /* Store the WAIT/LINK address. */
+ Hardware->lastWaitLink = address;
+
+ /* Append WAIT(count). */
+ logical[0]
+ = gcmSETFIELD(0, 31:27, 0x07 )
+ | gcmSETFIELD(0, 15:0, waitCount);
+
+#if gcd6000_SUPPORT
+ /* Send FE-PE sempahore token. */
+ logical[2]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ logical[3]
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* Send FE-PE stall token. */
+ logical[4]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0F00);
+
+ logical[5]
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /*************************************************************/
+ /* Enable chip ID 0. */
+ logical[6] =
+ gcmSETFIELD(0, 31:27, 0x0D )
+ | (1 << 0);
+
+ /* Send semaphore from FE to ChipID 1. */
+ logical[8] =
+ gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ logical[9] =
+ gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x0F )
+ | gcmSETFIELD(0, 27:24, 1);
+
+ /* Send semaphore from FE to ChipID 1. */
+ logical[10] =
+ gcmSETFIELD(0, 31:27, 0x09 );
+
+ logical[11] =
+ gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x0F )
+ | gcmSETFIELD(0, 27:24, 0);
+
+ /*************************************************************/
+ /* Enable chip ID 1. */
+ logical[12] =
+ gcmSETFIELD(0, 31:27, 0x0D )
+ | (1 << 1);
+
+ /* Send semaphore from FE to ChipID 1. */
+ logical[14] =
+ gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ logical[15] =
+ gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x0F )
+ | gcmSETFIELD(0, 27:24, 0);
+
+ /* Wait for semaphore from ChipID 0. */
+ logical[16] =
+ gcmSETFIELD(0, 31:27, 0x09 );
+
+ logical[17] =
+ gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x0F )
+ | gcmSETFIELD(0, 27:24, 1);
+
+ /*************************************************************/
+ /* Enable all chips. */
+ logical[18] =
+ gcmSETFIELD(0, 31:27, 0x0D )
+ | (0xFFFF);
+
+ /* LoadState(AQFlush, 1), flush. */
+ logical[20]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0E03)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ logical[21]
+ = gcmSETFIELD(0, 6:6, 0x1 );
+
+ /* Append LINK(2, address). */
+ logical[22]
+ = gcmSETFIELD(0, 31:27, 0x08 )
+ | gcmSETFIELD(0, 15:0, bytes >> 3);
+
+ logical[23] = address;
+#else
+ /* Append LINK(2, address). */
+ logical[2]
+ = gcmSETFIELD(0, 31:27, 0x08 )
+ | gcmSETFIELD(0, 15:0, bytes >> 3);
+
+ logical[3] = address;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%08x: WAIT %u", address, waitCount
+ );
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%08x: LINK 0x%08x, #%lu",
+ address + 8, address, bytes
+ );
+#endif
+
+ if (WaitOffset != NULL)
+ {
+ /* Return the offset pointer to WAIT command. */
+ *WaitOffset = 0;
+ }
+
+ if (WaitSize != NULL)
+ {
+ /* Return number of bytes used by the WAIT command. */
+ *WaitSize = 8;
+ }
+ }
+
+ if (Bytes != NULL)
+ {
+ /* Return number of bytes required by the WAIT/LINK command
+ ** sequence. */
+ *Bytes = bytes;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu *WaitOffset=0x%x *WaitSize=%lu",
+ gcmOPT_VALUE(Bytes), gcmOPT_VALUE(WaitOffset),
+ gcmOPT_VALUE(WaitSize));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_End
+**
+** Append an END command at the specified location in the command queue.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Pointer to the current location inside the command queue to append
+** END command at or NULL just to query the size of the END command.
+**
+** size_t * Bytes
+** Pointer to the number of bytes available for the END command. If
+** 'Logical' is NULL, this argument will be ignored.
+**
+** OUTPUT:
+**
+** size_t * Bytes
+** Pointer to a variable that will receive the number of bytes required
+** for the END command. If 'Bytes' is NULL, nothing will be returned.
+*/
+gceSTATUS
+gckHARDWARE_End(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN OUT size_t * Bytes
+ )
+{
+ u32 *logical = (u32 *) Logical;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x *Bytes=%lu",
+ Hardware, Logical, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT((Logical == NULL) || (Bytes != NULL));
+
+ if (Logical != NULL)
+ {
+ if (*Bytes < 8)
+ {
+ /* Command queue too small. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+
+ /* Append END. */
+ logical[0] =
+ gcmSETFIELD(0, 31:27, 0x02 );
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "0x%x: END", Logical);
+
+ /* Make sure the CPU writes out the data to memory. */
+ gcmkONERROR(
+ gckOS_MemoryBarrier(Hardware->os, Logical));
+ }
+
+ if (Bytes != NULL)
+ {
+ /* Return number of bytes required by the END command. */
+ *Bytes = 8;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_Nop
+**
+** Append a NOP command at the specified location in the command queue.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Pointer to the current location inside the command queue to append
+** NOP command at or NULL just to query the size of the NOP command.
+**
+** size_t * Bytes
+** Pointer to the number of bytes available for the NOP command. If
+** 'Logical' is NULL, this argument will be ignored.
+**
+** OUTPUT:
+**
+** size_t * Bytes
+** Pointer to a variable that will receive the number of bytes required
+** for the NOP command. If 'Bytes' is NULL, nothing will be returned.
+*/
+gceSTATUS
+gckHARDWARE_Nop(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN OUT size_t * Bytes
+ )
+{
+ u32 *logical = (u32 *) Logical;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x *Bytes=%lu",
+ Hardware, Logical, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT((Logical == NULL) || (Bytes != NULL));
+
+ if (Logical != NULL)
+ {
+ if (*Bytes < 8)
+ {
+ /* Command queue too small. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+
+ /* Append NOP. */
+ logical[0] = gcmSETFIELD(0, 31:27, 0x03 );
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "0x%x: NOP", Logical);
+ }
+
+ if (Bytes != NULL)
+ {
+ /* Return number of bytes required by the NOP command. */
+ *Bytes = 8;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_Event
+**
+** Append an EVENT command at the specified location in the command queue.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Pointer to the current location inside the command queue to append
+** the EVENT command at or NULL just to query the size of the EVENT
+** command.
+**
+** u8 Event
+** Event ID to program.
+**
+** gceKERNEL_WHERE FromWhere
+** Location of the pipe to send the event.
+**
+** size_t * Bytes
+** Pointer to the number of bytes available for the EVENT command. If
+** 'Logical' is NULL, this argument will be ignored.
+**
+** OUTPUT:
+**
+** size_t * Bytes
+** Pointer to a variable that will receive the number of bytes required
+** for the EVENT command. If 'Bytes' is NULL, nothing will be
+** returned.
+*/
+gceSTATUS
+gckHARDWARE_Event(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN u8 Event,
+ IN gceKERNEL_WHERE FromWhere,
+ IN OUT size_t * Bytes
+ )
+{
+ unsigned int size;
+ u32 destination = 0;
+ u32 *logical = (u32 *) Logical;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Event=%u FromWhere=%d *Bytes=%lu",
+ Hardware, Logical, Event, FromWhere, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT((Logical == NULL) || (Bytes != NULL));
+ gcmkVERIFY_ARGUMENT(Event < 32);
+
+ /* Determine the size of the command. */
+
+#if gcdUSE_OPENCL
+ /* Temporary workaround for lost events */
+ size = gcmALIGN(8 + (1 + 5) * 4 * 20, 8); /* EVENT + 100 STATES */
+#else
+ size = (Hardware->extraEventStates && (FromWhere == gcvKERNEL_PIXEL))
+ ? gcmALIGN(8 + (1 + 5) * 4, 8) /* EVENT + 5 STATES */
+ : 8;
+#endif
+
+ if (Logical != NULL)
+ {
+ if (*Bytes < size)
+ {
+ /* Command queue too small. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+
+ switch (FromWhere)
+ {
+ case gcvKERNEL_COMMAND:
+ /* From command processor. */
+#if gcdUSE_OPENCL
+ /* Send all events via PE */
+ destination = gcmSETFIELD(0, 6:6, 0x1 );
+#else
+ destination = gcmSETFIELD(0, 5:5, 0x1 );
+#endif
+ break;
+
+ case gcvKERNEL_PIXEL:
+ /* From pixel engine. */
+ destination = gcmSETFIELD(0, 6:6, 0x1 );
+ break;
+
+ default:
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Append EVENT(Event, destiantion). */
+ logical[0] = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0E01)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ logical[1] = gcmSETFIELD(destination, 4:0, Event);
+
+ /* Make sure the event ID gets written out before GPU can access it. */
+ gcmkONERROR(
+ gckOS_MemoryBarrier(Hardware->os, logical + 1));
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ {
+ u32 phys;
+ gckOS_GetPhysicalAddress(Hardware->os, Logical, &phys);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%08x: EVENT %d", phys, Event);
+ }
+#endif
+
+ /* Append the extra states. These are needed for the chips that do not
+ ** support back-to-back events due to the async interface. The extra
+ ** states add the necessary delay to ensure that event IDs do not
+ ** collide. */
+ if (size > 8)
+ {
+#if gcdUSE_OPENCL
+ unsigned int i;
+
+ for (i = 0; i < 20; i++)
+ {
+ logical[i*6+2] = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0100)
+ | gcmSETFIELD(0, 25:16, 5);
+ logical[i*6+3] = 0;
+ logical[i*6+4] = 0;
+ logical[i*6+5] = 0;
+ logical[i*6+6] = 0;
+ logical[i*6+7] = 0;
+ }
+#else
+ logical[2] = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0100)
+ | gcmSETFIELD(0, 25:16, 5);
+ logical[3] = 0;
+ logical[4] = 0;
+ logical[5] = 0;
+ logical[6] = 0;
+ logical[7] = 0;
+#endif
+ }
+ }
+
+ if (Bytes != NULL)
+ {
+ /* Return number of bytes required by the EVENT command. */
+ *Bytes = size;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_PipeSelect
+**
+** Append a PIPESELECT command at the specified location in the command queue.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Pointer to the current location inside the command queue to append
+** the PIPESELECT command at or NULL just to query the size of the
+** PIPESELECT command.
+**
+** gcePIPE_SELECT Pipe
+** Pipe value to select.
+**
+** size_t * Bytes
+** Pointer to the number of bytes available for the PIPESELECT command.
+** If 'Logical' is NULL, this argument will be ignored.
+**
+** OUTPUT:
+**
+** size_t * Bytes
+** Pointer to a variable that will receive the number of bytes required
+** for the PIPESELECT command. If 'Bytes' is NULL, nothing will be
+** returned.
+*/
+gceSTATUS
+gckHARDWARE_PipeSelect(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN gcePIPE_SELECT Pipe,
+ IN OUT size_t * Bytes
+ )
+{
+ u32 *logical = (u32 *) Logical;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Pipe=%d *Bytes=%lu",
+ Hardware, Logical, Pipe, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT((Logical == NULL) || (Bytes != NULL));
+
+ /* Append a PipeSelect. */
+ if (Logical != NULL)
+ {
+ u32 flush, stall;
+
+ if (*Bytes < 32)
+ {
+ /* Command queue too small. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+
+ flush = (Pipe == gcvPIPE_2D)
+ ? gcmSETFIELD(0, 1:1, 0x1 )
+ | gcmSETFIELD(0, 0:0, 0x1 )
+ : gcmSETFIELD(0, 3:3, 0x1 );
+
+ stall = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* LoadState(AQFlush, 1), flush. */
+ logical[0]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0E03)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ logical[1]
+ = flush;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%x: FLUSH 0x%x", logical, flush);
+
+ /* LoadState(AQSempahore, 1), stall. */
+ logical[2]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ logical[3]
+ = stall;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%x: SEMAPHORE 0x%x", logical + 2, stall);
+
+ /* Stall, stall. */
+ logical[4] = gcmSETFIELD(0, 31:27, 0x09 );
+ logical[5] = stall;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%x: STALL 0x%x", logical + 4, stall);
+
+ /* LoadState(AQPipeSelect, 1), pipe. */
+ logical[6]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0E00)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ logical[7] = (Pipe == gcvPIPE_2D)
+ ? 0x1
+ : 0x0;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%x: PIPE %d", logical + 6, Pipe);
+ }
+
+ if (Bytes != NULL)
+ {
+ /* Return number of bytes required by the PIPESELECT command. */
+ *Bytes = 32;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_Link
+**
+** Append a LINK command at the specified location in the command queue.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Pointer to the current location inside the command queue to append
+** the LINK command at or NULL just to query the size of the LINK
+** command.
+**
+** void *FetchAddress
+** Logical address of destination of LINK.
+**
+** size_t FetchSize
+** Number of bytes in destination of LINK.
+**
+** size_t * Bytes
+** Pointer to the number of bytes available for the LINK command. If
+** 'Logical' is NULL, this argument will be ignored.
+**
+** OUTPUT:
+**
+** size_t * Bytes
+** Pointer to a variable that will receive the number of bytes required
+** for the LINK command. If 'Bytes' is NULL, nothing will be returned.
+*/
+gceSTATUS
+gckHARDWARE_Link(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN void *FetchAddress,
+ IN size_t FetchSize,
+ IN OUT size_t * Bytes
+ )
+{
+ gceSTATUS status;
+ size_t bytes;
+ u32 address;
+ u32 link;
+ u32 *logical = (u32 *) Logical;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x FetchAddress=0x%x FetchSize=%lu "
+ "*Bytes=%lu",
+ Hardware, Logical, FetchAddress, FetchSize,
+ gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT((Logical == NULL) || (Bytes != NULL));
+
+ if (Logical != NULL)
+ {
+ if (*Bytes < 8)
+ {
+ /* Command queue too small. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+
+ /* Convert logical address to hardware address. */
+ gcmkONERROR(
+ gckHARDWARE_ConvertLogical(Hardware, FetchAddress, &address));
+
+ gcmkONERROR(
+ gckOS_WriteMemory(Hardware->os, logical + 1, address));
+
+ /* Make sure the address got written before the LINK command. */
+ gcmkONERROR(
+ gckOS_MemoryBarrier(Hardware->os, logical + 1));
+
+ /* Compute number of 64-byte aligned bytes to fetch. */
+ bytes = gcmALIGN(address + FetchSize, 64) - address;
+
+ /* Append LINK(bytes / 8), FetchAddress. */
+ link = gcmSETFIELD(0, 31:27, 0x08 )
+ | gcmSETFIELD(0, 15:0, bytes >> 3);
+
+ gcmkONERROR(
+ gckOS_WriteMemory(Hardware->os, logical, link));
+
+ /* Memory barrier. */
+ gcmkONERROR(
+ gckOS_MemoryBarrier(Hardware->os, logical));
+ }
+
+ if (Bytes != NULL)
+ {
+ /* Return number of bytes required by the LINK command. */
+ *Bytes = 8;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_UpdateQueueTail
+**
+** Update the tail of the command queue.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Logical address of the start of the command queue.
+**
+** u32 Offset
+** Offset into the command queue of the tail (last command).
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_UpdateQueueTail(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ IN u32 Offset
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Offset=0x%08x",
+ Hardware, Logical, Offset);
+
+ /* Verify the hardware. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Force a barrier. */
+ gcmkONERROR(
+ gckOS_MemoryBarrier(Hardware->os, Logical));
+
+ /* Notify gckKERNEL object of change. */
+ gcmkONERROR(
+ gckKERNEL_Notify(Hardware->kernel,
+ gcvNOTIFY_COMMAND_QUEUE,
+ gcvFALSE));
+
+ if (status == gcvSTATUS_CHIP_NOT_READY)
+ {
+ gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_ConvertLogical
+**
+** Convert a logical system address into a hardware specific address.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Logical address to convert.
+**
+** u32* Address
+** Return hardware specific address.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_ConvertLogical(
+ IN gckHARDWARE Hardware,
+ IN void *Logical,
+ OUT u32 * Address
+ )
+{
+ u32 address;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x", Hardware, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+ gcmkVERIFY_ARGUMENT(Address != NULL);
+
+ /* Convert logical address into a physical address. */
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(Hardware->os, Logical, &address));
+
+ /* Return hardware specific address. */
+ *Address = (Hardware->mmuVersion == 0)
+ ? gcmSETFIELD(0, 31:31, 0x0 )
+ | gcmSETFIELD(0, 30:0, address)
+ : address;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Address=0x%08x", *Address);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_Interrupt
+**
+** Process an interrupt.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** int InterruptValid
+** If gcvTRUE, this function will read the interrupt acknowledge
+** register, stores the data, and return whether or not the interrupt
+** is ours or not. If gcvFALSE, this functions will read the interrupt
+** acknowledge register and combine it with any stored value to handle
+** the event notifications.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_Interrupt(
+ IN gckHARDWARE Hardware,
+ IN int InterruptValid
+ )
+{
+ gckEVENT eventObj;
+ u32 data;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x InterruptValid=%d", Hardware, InterruptValid);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Extract gckEVENT object. */
+ eventObj = Hardware->kernel->eventObj;
+ gcmkVERIFY_OBJECT(eventObj, gcvOBJ_EVENT);
+
+ if (InterruptValid)
+ {
+ /* Read AQIntrAcknowledge register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00010,
+ &data));
+
+ if (data & 0x80000000)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_HARDWARE, "AXI BUS ERROR");
+ }
+
+ if (data == 0)
+ {
+ /* Not our interrupt. */
+ status = gcvSTATUS_NOT_OUR_INTERRUPT;
+ }
+ else
+ {
+ /* Inform gckEVENT of the interrupt. */
+ status = gckEVENT_Interrupt(eventObj, data & 0x7FFFFFFF);
+ }
+ }
+ else
+ {
+ /* Handle events. */
+ status = gckEVENT_Notify(eventObj, 0);
+ }
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_QueryCommandBuffer
+**
+** Query the command buffer alignment and number of reserved bytes.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** OUTPUT:
+**
+** size_t * Alignment
+** Pointer to a variable receiving the alignment for each command.
+**
+** size_t * ReservedHead
+** Pointer to a variable receiving the number of reserved bytes at the
+** head of each command buffer.
+**
+** size_t * ReservedTail
+** Pointer to a variable receiving the number of bytes reserved at the
+** tail of each command buffer.
+*/
+gceSTATUS
+gckHARDWARE_QueryCommandBuffer(
+ IN gckHARDWARE Hardware,
+ OUT size_t * Alignment,
+ OUT size_t * ReservedHead,
+ OUT size_t * ReservedTail
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ if (Alignment != NULL)
+ {
+ /* Align every 8 bytes. */
+ *Alignment = 8;
+ }
+
+ if (ReservedHead != NULL)
+ {
+ /* Reserve space for SelectPipe(). */
+ *ReservedHead = 32;
+ }
+
+ if (ReservedTail != NULL)
+ {
+ /* Reserve space for Link(). */
+ *ReservedTail = 8;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Alignment=%lu *ReservedHead=%lu *ReservedTail=%lu",
+ gcmOPT_VALUE(Alignment), gcmOPT_VALUE(ReservedHead),
+ gcmOPT_VALUE(ReservedTail));
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_QuerySystemMemory
+**
+** Query the command buffer alignment and number of reserved bytes.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** OUTPUT:
+**
+** size_t * SystemSize
+** Pointer to a variable that receives the maximum size of the system
+** memory.
+**
+** u32 * SystemBaseAddress
+** Poinetr to a variable that receives the base address for system
+** memory.
+*/
+gceSTATUS
+gckHARDWARE_QuerySystemMemory(
+ IN gckHARDWARE Hardware,
+ OUT size_t * SystemSize,
+ OUT u32 * SystemBaseAddress
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ if (SystemSize != NULL)
+ {
+ /* Maximum system memory can be 2GB. */
+ *SystemSize = 1U << 31;
+ }
+
+ if (SystemBaseAddress != NULL)
+ {
+ /* Set system memory base address. */
+ *SystemBaseAddress = gcmSETFIELD(0, 31:31, 0x0 );
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*SystemSize=%lu *SystemBaseAddress=%lu",
+ gcmOPT_VALUE(SystemSize), gcmOPT_VALUE(SystemBaseAddress));
+ return gcvSTATUS_OK;
+}
+
+#if !VIVANTE_NO_3D
+/*******************************************************************************
+**
+** gckHARDWARE_QueryShaderCaps
+**
+** Query the shader capabilities.
+**
+** INPUT:
+**
+** Nothing.
+**
+** OUTPUT:
+**
+** unsigned int * VertexUniforms
+** Pointer to a variable receiving the number of uniforms in the vertex
+** shader.
+**
+** unsigned int * FragmentUniforms
+** Pointer to a variable receiving the number of uniforms in the
+** fragment shader.
+**
+** unsigned int * Varyings
+** Pointer to a variable receiving the maimum number of varyings.
+*/
+gceSTATUS
+gckHARDWARE_QueryShaderCaps(
+ IN gckHARDWARE Hardware,
+ OUT unsigned int * VertexUniforms,
+ OUT unsigned int * FragmentUniforms,
+ OUT unsigned int * Varyings
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x VertexUniforms=0x%x "
+ "FragmentUniforms=0x%x Varyings=0x%x",
+ Hardware, VertexUniforms,
+ FragmentUniforms, Varyings);
+
+ if (VertexUniforms != NULL)
+ {
+ /* Return the vs shader const count. */
+ if (Hardware->identity.chipModel < gcv4000)
+ {
+ *VertexUniforms = 168;
+ }
+ else
+ {
+ *VertexUniforms = 256;
+ }
+ }
+
+ if (FragmentUniforms != NULL)
+ {
+ /* Return the ps shader const count. */
+ if (Hardware->identity.chipModel < gcv4000)
+ {
+ *FragmentUniforms = 64;
+ }
+ else
+ {
+ *FragmentUniforms = 256;
+ }
+ }
+
+ if (Varyings != NULL)
+ {
+ /* Return the shader varyings count. */
+ if (gcmVERIFYFIELDVALUE(Hardware->identity.chipMinorFeatures1, 23:23, 0x1 ))
+ {
+ *Varyings = 12;
+ }
+ else
+ {
+ *Varyings = 8;
+ }
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+#endif
+
+/*******************************************************************************
+**
+** gckHARDWARE_SetMMU
+**
+** Set the page table base address.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** void *Logical
+** Logical address of the page table.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_SetMMU(
+ IN gckHARDWARE Hardware,
+ IN void *Logical
+ )
+{
+ gceSTATUS status;
+ u32 address = 0;
+ u32 baseAddress;
+
+ gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x", Hardware, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+
+ /* Convert the logical address into an hardware address. */
+ gcmkONERROR(
+ gckHARDWARE_ConvertLogical(Hardware, Logical, &address));
+
+ /* Also get the base address - we need a real physical address. */
+ gcmkONERROR(
+ gckOS_GetBaseAddress(Hardware->os, &baseAddress));
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Setting page table to 0x%08X",
+ address + baseAddress);
+
+ /* Write the AQMemoryFePageTable register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00400,
+ address + baseAddress));
+
+ /* Write the AQMemoryRaPageTable register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00410,
+ address + baseAddress));
+
+ /* Write the AQMemoryTxPageTable register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00404,
+ address + baseAddress));
+
+
+ /* Write the AQMemoryPePageTable register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00408,
+ address + baseAddress));
+
+ /* Write the AQMemoryPezPageTable register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0040C,
+ address + baseAddress));
+
+ /* Return the status. */
+ gcmkFOOTER_NO();
+ return status;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_FlushMMU
+**
+** Flush the page table.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_FlushMMU(
+ IN gckHARDWARE Hardware
+ )
+{
+ gceSTATUS status;
+ gckCOMMAND command;
+ u32 *buffer;
+ size_t bufferSize;
+ int commitEntered = gcvFALSE;
+ void *pointer = NULL;
+ u32 flushSize;
+ u32 count;
+ u32 physical;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Verify the gckCOMMAND object pointer. */
+ command = Hardware->kernel->command;
+
+ /* Acquire the command queue. */
+ gcmkONERROR(gckCOMMAND_EnterCommit(command, gcvFALSE));
+ commitEntered = gcvTRUE;
+
+ /* Flush the memory controller. */
+ if (Hardware->mmuVersion == 0)
+ {
+ gcmkONERROR(gckCOMMAND_Reserve(
+ command, 8, &pointer, &bufferSize
+ ));
+
+ buffer = (u32 *) pointer;
+
+ buffer[0]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0E04)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ buffer[1]
+ = gcmSETFIELD(0, 0:0, 0x1 )
+ | gcmSETFIELD(0, 1:1, 0x1 )
+ | gcmSETFIELD(0, 2:2, 0x1 )
+ | gcmSETFIELD(0, 3:3, 0x1 )
+ | gcmSETFIELD(0, 4:4, 0x1 );
+
+ gcmkONERROR(gckCOMMAND_Execute(command, 8));
+ }
+ else
+ {
+ flushSize = 16 * 4;
+
+ gcmkONERROR(gckCOMMAND_Reserve(
+ command, flushSize, &pointer, &bufferSize
+ ));
+
+ buffer = (u32 *) pointer;
+
+ count = (bufferSize - flushSize + 7) >> 3;
+
+ gcmkONERROR(gckOS_GetPhysicalAddress(command->os, buffer, &physical));
+
+ /* Flush cache. */
+ buffer[0]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E03);
+
+ buffer[1]
+ = gcmSETFIELD(0, 3:3, 0x1 )
+ | gcmSETFIELD(0, 1:1, 0x1 )
+ | gcmSETFIELD(0, 2:2, 0x1 )
+ | gcmSETFIELD(0, 4:4, 0x1 )
+ | gcmSETFIELD(0, 5:5, 0x1 )
+ | gcmSETFIELD(0, 6:6, 0x1 );
+
+ /* Arm the PE-FE Semaphore. */
+ buffer[2]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ buffer[3]
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* STALL FE until PE is done flushing. */
+ buffer[4]
+ = gcmSETFIELD(0, 31:27, 0x09 );
+
+ buffer[5]
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* LINK to next slot to flush FE FIFO. */
+ buffer[6]
+ = gcmSETFIELD(0, 31:27, 0x08 )
+ | gcmSETFIELD(0, 15:0, 4);
+
+ buffer[7]
+ = physical + 8 * sizeof(u32);
+
+ /* Flush MMU cache. */
+ buffer[8]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0061)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ buffer[9]
+ = (gcmSETFIELD(~0, 4:4, 0x1 ) & gcmSETFIELD(~0, 7:7, 0x0 ) );
+
+ /* Arm the PE-FE Semaphore. */
+ buffer[10]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 25:16, 1)
+ | gcmSETFIELD(0, 15:0, 0x0E02);
+
+ buffer[11]
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* STALL FE until PE is done flushing. */
+ buffer[12]
+ = gcmSETFIELD(0, 31:27, 0x09 );
+
+ buffer[13]
+ = gcmSETFIELD(0, 4:0, 0x01 )
+ | gcmSETFIELD(0, 12:8, 0x07 );
+
+ /* LINK to next slot to flush FE FIFO. */
+ buffer[14]
+ = gcmSETFIELD(0, 31:27, 0x08 )
+ | gcmSETFIELD(0, 15:0, count);
+
+ buffer[15]
+ = physical + flushSize;
+
+ gcmkONERROR(gckCOMMAND_Execute(command, flushSize));
+ }
+
+ /* Release the command queue. */
+ gcmkONERROR(gckCOMMAND_ExitCommit(command, gcvFALSE));
+ commitEntered = gcvFALSE;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (commitEntered)
+ {
+ /* Release the command queue mutex. */
+ gcmkVERIFY_OK(gckCOMMAND_ExitCommit(Hardware->kernel->command,
+ gcvFALSE));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_SetMMUv2
+**
+** Set the page table base address.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_SetMMUv2(
+ IN gckHARDWARE Hardware,
+ IN int Enable,
+ IN void *MtlbAddress,
+ IN gceMMU_MODE Mode,
+ IN void *SafeAddress,
+ IN int FromPower
+ )
+{
+ gceSTATUS status;
+ u32 config, address;
+ gckCOMMAND command;
+ u32 *buffer;
+ size_t bufferSize;
+ int commitEntered = gcvFALSE;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Hardware=0x%x Enable=%d", Hardware, Enable);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Convert logical address into physical address. */
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(Hardware->os, MtlbAddress, &config));
+
+ gcmkONERROR(
+ gckOS_GetPhysicalAddress(Hardware->os, SafeAddress, &address));
+
+ if (address & 0x3F)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
+ }
+
+ switch (Mode)
+ {
+ case gcvMMU_MODE_1K:
+ if (config & 0x3FF)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
+ }
+
+ config |= gcmSETFIELD(0, 0:0, 0x1 );
+
+ break;
+
+ case gcvMMU_MODE_4K:
+ if (config & 0xFFF)
+ {
+ gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
+ }
+
+ config |= gcmSETFIELD(0, 0:0, 0x0 );
+
+ break;
+
+ default:
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Verify the gckCOMMAND object pointer. */
+ command = Hardware->kernel->command;
+
+ /* Acquire the command queue. */
+ gcmkONERROR(gckCOMMAND_EnterCommit(command, FromPower));
+ commitEntered = gcvTRUE;
+
+ gcmkONERROR(gckCOMMAND_Reserve(
+ command, 16, &pointer, &bufferSize
+ ));
+
+ buffer = pointer;
+
+ buffer[0]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0061)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ buffer[1] = config;
+
+ buffer[2]
+ = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0060)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ buffer[3] = address;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Setup MMU: config=%08x, Safe Address=%08x\n.", config, address);
+
+ gcmkONERROR(gckCOMMAND_Execute(command, 16));
+
+ /* Release the command queue. */
+ gcmkONERROR(gckCOMMAND_ExitCommit(command, FromPower));
+ commitEntered = gcvFALSE;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "call gckCOMMAND_Stall to make sure the config is done.\n ");
+
+ gcmkONERROR(gckCOMMAND_Stall(command, FromPower));
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Enable MMU through GCREG_MMU_CONTROL.");
+
+ /* Enable MMU. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0018C,
+ gcmSETFIELD(0, 0:0, Enable)));
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "call gckCOMMAND_Stall to check MMU available.\n");
+
+ gcmkONERROR(gckCOMMAND_Stall(command, FromPower));
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "The MMU is available.\n");
+
+ /* Return the status. */
+ gcmkFOOTER_NO();
+ return status;
+
+OnError:
+ if (commitEntered)
+ {
+ /* Release the command queue mutex. */
+ gcmkVERIFY_OK(gckCOMMAND_ExitCommit(Hardware->kernel->command,
+ gcvFALSE));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_BuildVirtualAddress
+**
+** Build a virtual address.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** u32 Index
+** Index into page table.
+**
+** u32 Offset
+** Offset into page.
+**
+** OUTPUT:
+**
+** u32 * Address
+** Pointer to a variable receiving te hardware address.
+*/
+gceSTATUS
+gckHARDWARE_BuildVirtualAddress(
+ IN gckHARDWARE Hardware,
+ IN u32 Index,
+ IN u32 Offset,
+ OUT u32 * Address
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x Index=%u Offset=%u", Hardware, Index, Offset);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(Address != NULL);
+
+ /* Build virtual address. */
+ *Address = gcmSETFIELD(0, 31:31, 0x1 )
+ | gcmSETFIELD(0, 30:0, Offset | (Index << 12));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Address=0x%08x", *Address);
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckHARDWARE_GetIdle(
+ IN gckHARDWARE Hardware,
+ IN int Wait,
+ OUT u32 * Data
+ )
+{
+ gceSTATUS status;
+ u32 idle = 0;
+ int retry, poll, pollCount;
+
+ gcmkHEADER_ARG("Hardware=0x%x Wait=%d", Hardware, Wait);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(Data != NULL);
+
+
+ /* If we have to wait, try 100 polls per millisecond. */
+ pollCount = Wait ? 100 : 1;
+
+ /* At most, try for 1 second. */
+ for (retry = 0; retry < 1000; ++retry)
+ {
+ /* If we have to wait, try 100 polls per millisecond. */
+ for (poll = pollCount; poll > 0; --poll)
+ {
+ /* Read register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00004, &idle));
+
+ /* See if we have to wait for FE idle. */
+ if (gcmGETFIELD(idle, 0:0))
+ {
+ /* FE is idle. */
+ break;
+ }
+ }
+
+ /* Check if we need to wait for FE and FE is busy. */
+ if (Wait && !gcmGETFIELD(idle, 0:0))
+ {
+ /* Wait a little. */
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "%s: Waiting for idle: 0x%08X",
+ __FUNCTION__, idle);
+#ifdef CONFIG_MACH_JZ4770
+ if (retry & 0x3)
+ schedule();
+#endif
+
+ gcmkVERIFY_OK(gckOS_Delay(Hardware->os, 1));
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* Return idle to caller. */
+ *Data = idle;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Data=0x%08x", *Data);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/* Flush the caches. */
+gceSTATUS
+gckHARDWARE_Flush(
+ IN gckHARDWARE Hardware,
+ IN gceKERNEL_FLUSH Flush,
+ IN void *Logical,
+ IN OUT size_t * Bytes
+ )
+{
+ u32 pipe;
+ u32 flush = 0;
+ u32 *logical = (u32 *) Logical;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Flush=0x%x Logical=0x%x *Bytes=%lu",
+ Hardware, Flush, Logical, gcmOPT_VALUE(Bytes));
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Get current pipe. */
+ pipe = Hardware->kernel->command->pipeSelect;
+
+ /* Flush 3D color cache. */
+ if ((Flush & gcvFLUSH_COLOR) && (pipe == 0x0))
+ {
+ flush |= gcmSETFIELD(0, 1:1, 0x1 );
+ }
+
+ /* Flush 3D depth cache. */
+ if ((Flush & gcvFLUSH_DEPTH) && (pipe == 0x0))
+ {
+ flush |= gcmSETFIELD(0, 0:0, 0x1 );
+ }
+
+ /* Flush 3D texture cache. */
+ if ((Flush & gcvFLUSH_TEXTURE) && (pipe == 0x0))
+ {
+ flush |= gcmSETFIELD(0, 2:2, 0x1 );
+ }
+
+ /* Flush 2D cache. */
+ if ((Flush & gcvFLUSH_2D) && (pipe == 0x1))
+ {
+ flush |= gcmSETFIELD(0, 3:3, 0x1 );
+ }
+
+ /* See if there is a valid flush. */
+ if (flush == 0)
+ {
+ if (Bytes != NULL)
+ {
+ /* No bytes required. */
+ *Bytes = 0;
+ }
+ }
+
+ else
+ {
+ /* Copy to command queue. */
+ if (Logical != NULL)
+ {
+ if (*Bytes < 8)
+ {
+ /* Command queue too small. */
+ gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL);
+ }
+
+ /* Append LOAD_STATE to AQFlush. */
+ logical[0] = gcmSETFIELD(0, 31:27, 0x01 )
+ | gcmSETFIELD(0, 15:0, 0x0E03)
+ | gcmSETFIELD(0, 25:16, 1);
+
+ logical[1] = flush;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "0x%x: FLUSH 0x%x", logical, flush);
+ }
+
+ if (Bytes != NULL)
+ {
+ /* 8 bytes required. */
+ *Bytes = 8;
+ }
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes));
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckHARDWARE_SetFastClear(
+ IN gckHARDWARE Hardware,
+ IN int Enable,
+ IN int Compression
+ )
+{
+#if !VIVANTE_NO_3D
+ u32 debug;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x Enable=%d Compression=%d",
+ Hardware, Enable, Compression);
+
+ /* Only process if fast clear is available. */
+ if (gcmGETFIELD(Hardware->identity.chipFeatures, 0:0))
+ {
+ if (Enable == -1)
+ {
+ /* Determine automatic value for fast clear. */
+ Enable = ((Hardware->identity.chipModel != gcv500)
+ || (Hardware->identity.chipRevision >= 3)
+ ) ? 1 : 0;
+ }
+
+ if (Compression == -1)
+ {
+ /* Determine automatic value for compression. */
+ Compression = Enable
+ && gcmGETFIELD(Hardware->identity.chipFeatures, 5:5)
+ && !(Hardware->identity.chipModel == gcv860 && Hardware->identity.chipRevision == 0x4621);
+ }
+
+ /* Read AQMemoryDebug register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00414, &debug));
+
+ /* Set fast clear bypass. */
+ debug = gcmSETFIELD(debug, 20:20, Enable == 0);
+
+ /* Set compression bypass. */
+ debug = gcmSETFIELD(debug, 21:21, Compression == 0);
+
+ /* Write back AQMemoryDebug register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00414,
+ debug));
+
+ /* Store fast clear and comprersison flags. */
+ Hardware->allowFastClear = Enable;
+ Hardware->allowCompression = Compression;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "FastClear=%d Compression=%d", Enable, Compression);
+ }
+
+ /* Special patch for 0x320 0x5220. */
+ if (Hardware->identity.chipRevision == 0x5220 && Hardware->identity.chipModel == gcv320)
+ {
+ u32 debug;
+
+ /* Read AQMemoryDebug register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00414, &debug));
+
+ debug |= 8;
+
+ /* Write back AQMemoryDebug register. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00414,
+ debug));
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+#else
+ return gcvSTATUS_OK;
+#endif
+}
+
+typedef enum
+{
+ gcvPOWER_FLAG_INITIALIZE = 1 << 0,
+ gcvPOWER_FLAG_STALL = 1 << 1,
+ gcvPOWER_FLAG_STOP = 1 << 2,
+ gcvPOWER_FLAG_START = 1 << 3,
+ gcvPOWER_FLAG_RELEASE = 1 << 4,
+ gcvPOWER_FLAG_DELAY = 1 << 5,
+ gcvPOWER_FLAG_SAVE = 1 << 6,
+ gcvPOWER_FLAG_ACQUIRE = 1 << 7,
+ gcvPOWER_FLAG_POWER_OFF = 1 << 8,
+ gcvPOWER_FLAG_CLOCK_OFF = 1 << 9,
+ gcvPOWER_FLAG_CLOCK_ON = 1 << 10,
+}
+gcePOWER_FLAGS;
+
+#if gcmIS_DEBUG(gcdDEBUG_TRACE) && gcdPOWER_MANAGEMENT
+static const char *
+_PowerEnum(gceCHIPPOWERSTATE State)
+{
+ const const char *states[] =
+ {
+ gcmSTRING(gcvPOWER_ON),
+ gcmSTRING(gcvPOWER_OFF),
+ gcmSTRING(gcvPOWER_IDLE),
+ gcmSTRING(gcvPOWER_SUSPEND),
+ gcmSTRING(gcvPOWER_SUSPEND_ATPOWERON),
+ gcmSTRING(gcvPOWER_OFF_ATPOWERON),
+ gcmSTRING(gcvPOWER_IDLE_BROADCAST),
+ gcmSTRING(gcvPOWER_SUSPEND_BROADCAST),
+ gcmSTRING(gcvPOWER_OFF_BROADCAST),
+ gcmSTRING(gcvPOWER_OFF_RECOVERY),
+ gcmSTRING(gcvPOWER_ON_AUTO)
+ };
+
+ if ((State >= gcvPOWER_ON) && (State <= gcvPOWER_ON_AUTO))
+ {
+ return states[State - gcvPOWER_ON];
+ }
+
+ return "unknown";
+}
+#endif
+
+/*******************************************************************************
+**
+** gckHARDWARE_SetPowerManagementState
+**
+** Set GPU to a specified power state.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** gceCHIPPOWERSTATE State
+** Power State.
+**
+*/
+gceSTATUS
+gckHARDWARE_SetPowerManagementState(
+ IN gckHARDWARE Hardware,
+ IN gceCHIPPOWERSTATE State
+ )
+{
+#if gcdPOWER_MANAGEMENT
+ gceSTATUS status;
+ gckCOMMAND command = NULL;
+ gckOS os;
+ unsigned int flag, clock;
+ void *buffer;
+ size_t bytes, requested;
+ int acquired = gcvFALSE;
+ int mutexAcquired = gcvFALSE;
+ int stall = gcvTRUE;
+ int broadcast = gcvFALSE;
+#if gcdPOWEROFF_TIMEOUT
+ int timeout = gcvFALSE;
+ int isAfter = gcvFALSE;
+ u32 currentTime;
+#endif
+ u32 process, thread;
+ int commitEntered = gcvFALSE;
+#if gcdENABLE_PROFILING
+ u64 time, freq, mutexTime, onTime, stallTime, stopTime, delayTime,
+ initTime, offTime, startTime, totalTime;
+#endif
+ int global = gcvFALSE;
+ int globalAcquired = gcvFALSE;
+ int configMmu = gcvFALSE;
+
+ /* State transition flags. */
+ static const unsigned int flags[4][4] =
+ {
+ /* gcvPOWER_ON */
+ { /* ON */ 0,
+ /* OFF */ gcvPOWER_FLAG_ACQUIRE |
+ gcvPOWER_FLAG_STALL |
+ gcvPOWER_FLAG_STOP |
+ gcvPOWER_FLAG_POWER_OFF |
+ gcvPOWER_FLAG_CLOCK_OFF,
+ /* IDLE */ gcvPOWER_FLAG_ACQUIRE |
+ gcvPOWER_FLAG_STALL,
+ /* SUSPEND */ gcvPOWER_FLAG_ACQUIRE |
+ gcvPOWER_FLAG_STALL |
+ gcvPOWER_FLAG_STOP |
+ gcvPOWER_FLAG_CLOCK_OFF,
+ },
+
+ /* gcvPOWER_OFF */
+ { /* ON */ gcvPOWER_FLAG_INITIALIZE |
+ gcvPOWER_FLAG_START |
+ gcvPOWER_FLAG_RELEASE |
+ gcvPOWER_FLAG_DELAY,
+ /* OFF */ 0,
+ /* IDLE */ gcvPOWER_FLAG_INITIALIZE |
+ gcvPOWER_FLAG_START |
+ gcvPOWER_FLAG_DELAY,
+ /* SUSPEND */ gcvPOWER_FLAG_INITIALIZE |
+ gcvPOWER_FLAG_CLOCK_OFF,
+ },
+
+ /* gcvPOWER_IDLE */
+ { /* ON */ gcvPOWER_FLAG_RELEASE,
+ /* OFF */ gcvPOWER_FLAG_STOP |
+ gcvPOWER_FLAG_POWER_OFF |
+ gcvPOWER_FLAG_CLOCK_OFF,
+ /* IDLE */ 0,
+ /* SUSPEND */ gcvPOWER_FLAG_STOP |
+ gcvPOWER_FLAG_CLOCK_OFF,
+ },
+
+ /* gcvPOWER_SUSPEND */
+ { /* ON */ gcvPOWER_FLAG_START |
+ gcvPOWER_FLAG_RELEASE |
+ gcvPOWER_FLAG_DELAY |
+ gcvPOWER_FLAG_CLOCK_ON,
+ /* OFF */ gcvPOWER_FLAG_SAVE |
+ gcvPOWER_FLAG_POWER_OFF |
+ gcvPOWER_FLAG_CLOCK_OFF,
+ /* IDLE */ gcvPOWER_FLAG_START |
+ gcvPOWER_FLAG_DELAY |
+ gcvPOWER_FLAG_CLOCK_ON,
+ /* SUSPEND */ 0,
+ },
+ };
+
+ /* Clocks. */
+ static const unsigned int clocks[4] =
+ {
+ /* gcvPOWER_ON */
+ gcmSETFIELD(0, 0:0, 0) |
+ gcmSETFIELD(0, 1:1, 0) |
+ gcmSETFIELD(0, 8:2, 64) |
+ gcmSETFIELD(0, 9:9, 1),
+
+ /* gcvPOWER_OFF */
+ gcmSETFIELD(0, 0:0, 1) |
+ gcmSETFIELD(0, 1:1, 1) |
+ gcmSETFIELD(0, 8:2, 1) |
+ gcmSETFIELD(0, 9:9, 1),
+
+ /* gcvPOWER_IDLE */
+ gcmSETFIELD(0, 0:0, 0) |
+ gcmSETFIELD(0, 1:1, 0) |
+ gcmSETFIELD(0, 8:2, 1) |
+ gcmSETFIELD(0, 9:9, 1),
+
+ /* gcvPOWER_SUSPEND */
+ gcmSETFIELD(0, 0:0, 1) |
+ gcmSETFIELD(0, 1:1, 1) |
+ gcmSETFIELD(0, 8:2, 1) |
+ gcmSETFIELD(0, 9:9, 1),
+ };
+
+ gcmkHEADER_ARG("Hardware=0x%x State=%d", Hardware, State);
+#if gcmIS_DEBUG(gcdDEBUG_TRACE)
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Switching to power state %d(%s)",
+ State, _PowerEnum(State));
+#endif
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Get the gckOS object pointer. */
+ os = Hardware->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Get the gckCOMMAND object pointer. */
+ gcmkVERIFY_OBJECT(Hardware->kernel, gcvOBJ_KERNEL);
+ command = Hardware->kernel->command;
+ gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND);
+
+ /* Start profiler. */
+ gcmkPROFILE_INIT(freq, time);
+
+ /* Convert the broadcast power state. */
+ switch (State)
+ {
+ case gcvPOWER_SUSPEND_ATPOWERON:
+ /* Convert to SUSPEND and don't wait for STALL. */
+ State = gcvPOWER_SUSPEND;
+ stall = gcvFALSE;
+ break;
+
+ case gcvPOWER_OFF_ATPOWERON:
+ /* Convert to OFF and don't wait for STALL. */
+ State = gcvPOWER_OFF;
+ stall = gcvFALSE;
+ break;
+
+ case gcvPOWER_IDLE_BROADCAST:
+ /* Convert to IDLE and note we are inside broadcast. */
+ State = gcvPOWER_IDLE;
+ broadcast = gcvTRUE;
+ break;
+
+ case gcvPOWER_SUSPEND_BROADCAST:
+ /* Convert to SUSPEND and note we are inside broadcast. */
+ State = gcvPOWER_SUSPEND;
+ broadcast = gcvTRUE;
+ break;
+
+ case gcvPOWER_OFF_BROADCAST:
+ /* Convert to OFF and note we are inside broadcast. */
+ State = gcvPOWER_OFF;
+ broadcast = gcvTRUE;
+ break;
+
+ case gcvPOWER_OFF_RECOVERY:
+ /* Convert to OFF and note we are inside recovery. */
+ State = gcvPOWER_OFF;
+ stall = gcvFALSE;
+ broadcast = gcvTRUE;
+ break;
+
+ case gcvPOWER_ON_AUTO:
+ /* Convert to ON and note we are inside recovery. */
+ State = gcvPOWER_ON;
+ break;
+
+ case gcvPOWER_ON:
+ case gcvPOWER_IDLE:
+ case gcvPOWER_SUSPEND:
+ case gcvPOWER_OFF:
+ /* Mark as global power management. */
+ global = gcvTRUE;
+ break;
+
+#if gcdPOWEROFF_TIMEOUT
+ case gcvPOWER_OFF_TIMEOUT:
+ /* Convert to OFF and note we are inside broadcast. */
+ State = gcvPOWER_OFF;
+ broadcast = gcvTRUE;
+ /* Check time out */
+ timeout = gcvTRUE;
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ /* Get current process and thread IDs. */
+ process = task_tgid_vnr(current);
+ thread = task_pid_vnr(current);
+
+ if (broadcast)
+ {
+ /* Try to acquire the power mutex. */
+ status = gckOS_AcquireMutex(os, Hardware->powerMutex, 0);
+
+ if (status == gcvSTATUS_TIMEOUT)
+ {
+ /* Check if we already own this mutex. */
+ if ((Hardware->powerProcess == process)
+ && (Hardware->powerThread == thread)
+ )
+ {
+ /* Bail out on recursive power management. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+ else if (State == gcvPOWER_IDLE || State == gcvPOWER_SUSPEND)
+ {
+ /* Called from IST,
+ ** so waiting here will cause deadlock,
+ ** if lock holder call gckCOMMAND_Stall() */
+ gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
+ }
+ else
+ {
+ /* Acquire the power mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(os,
+ Hardware->powerMutex,
+ gcvINFINITE));
+ }
+ }
+ }
+ else
+ {
+ /* Acquire the power mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(os, Hardware->powerMutex, gcvINFINITE));
+ }
+
+ /* Get time until mtuex acquired. */
+ gcmkPROFILE_QUERY(time, mutexTime);
+
+ Hardware->powerProcess = process;
+ Hardware->powerThread = thread;
+ mutexAcquired = gcvTRUE;
+
+ /* Grab control flags and clock. */
+ flag = flags[Hardware->chipPowerState][State];
+ clock = clocks[State];
+
+#if gcdPOWEROFF_TIMEOUT
+ if (timeout)
+ {
+ gcmkONERROR(gckOS_GetTicks(&currentTime));
+
+ gcmkONERROR(
+ gckOS_TicksAfter(Hardware->powerOffTime, currentTime, &isAfter));
+
+ /* powerOffTime is pushed forward, give up.*/
+ if (isAfter
+ /* Expect a transition start from IDLE or SUSPEND. */
+ || (Hardware->chipPowerState == gcvPOWER_ON)
+ || (Hardware->chipPowerState == gcvPOWER_OFF)
+ )
+ {
+ /* Release the power mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex));
+
+ /* No need to do anything. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Power Off GPU[%d] at %u [supposed to be at %u]",
+ Hardware->core, currentTime, Hardware->powerOffTime);
+ }
+#endif
+
+ if (flag == 0)
+ {
+ /* Release the power mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex));
+
+ /* No need to do anything. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ /* If this is an internal power management, we have to check if we can grab
+ ** the global power semaphore. If we cannot, we have to wait until the
+ ** external world changes power management. */
+ if (!global)
+ {
+ /* Try to acquire the global semaphore. */
+ status = gckOS_TryAcquireSemaphore(os, Hardware->globalSemaphore);
+ if (status == gcvSTATUS_TIMEOUT)
+ {
+ if (State == gcvPOWER_IDLE || State == gcvPOWER_SUSPEND)
+ {
+ /* Called from thread routine which should NEVER sleep.*/
+ gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
+ }
+
+ /* Release the power mutex. */
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Releasing the power mutex.");
+ gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex));
+ mutexAcquired = gcvFALSE;
+
+ /* Wait for the semaphore. */
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Waiting for global semaphore.");
+ gcmkONERROR(gckOS_AcquireSemaphore(os, Hardware->globalSemaphore));
+ globalAcquired = gcvTRUE;
+
+ /* Acquire the power mutex. */
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "Reacquiring the power mutex.");
+ gcmkONERROR(gckOS_AcquireMutex(os,
+ Hardware->powerMutex,
+ gcvINFINITE));
+ mutexAcquired = gcvTRUE;
+
+ /* chipPowerState may be changed by external world during the time
+ ** we give up powerMutex, so updating flag now is necessary. */
+ flag = flags[Hardware->chipPowerState][State];
+
+ if (flag == 0)
+ {
+ gcmkONERROR(gckOS_ReleaseSemaphore(os, Hardware->globalSemaphore));
+ globalAcquired = gcvFALSE;
+
+ gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex));
+ mutexAcquired = gcvFALSE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+ }
+ else
+ {
+ /* Error. */
+ gcmkONERROR(status);
+ }
+
+ /* Release the global semaphore again. */
+ gcmkONERROR(gckOS_ReleaseSemaphore(os, Hardware->globalSemaphore));
+ globalAcquired = gcvFALSE;
+ }
+ else
+ {
+ if (State == gcvPOWER_OFF || State == gcvPOWER_SUSPEND || State == gcvPOWER_IDLE)
+ {
+ /* Acquire the global semaphore if it has not been acquired. */
+ status = gckOS_TryAcquireSemaphore(os, Hardware->globalSemaphore);
+ if (status == gcvSTATUS_OK)
+ {
+ globalAcquired = gcvTRUE;
+ }
+ else if (status != gcvSTATUS_TIMEOUT)
+ {
+ /* Other errors. */
+ gcmkONERROR(status);
+ }
+ /* Ignore gcvSTATUS_TIMEOUT and leave globalAcquired as gcvFALSE.
+ ** gcvSTATUS_TIMEOUT means global semaphore has already
+ ** been acquired before this operation, so even if we fail,
+ ** we should not release it in our error handling. It should be
+ ** released by the next successful global gcvPOWER_ON. */
+ }
+
+ /* Global power management can't be aborted, so sync with
+ ** proceeding last commit. */
+ if (flag & gcvPOWER_FLAG_ACQUIRE)
+ {
+ /* Acquire the power management semaphore. */
+ gcmkONERROR(gckOS_AcquireSemaphore(os, command->powerSemaphore));
+ acquired = gcvTRUE;
+
+ /* avoid acquiring again. */
+ flag &= ~gcvPOWER_FLAG_ACQUIRE;
+ }
+ }
+
+ if (flag & (gcvPOWER_FLAG_INITIALIZE | gcvPOWER_FLAG_CLOCK_ON))
+ {
+ /* Turn on the power. */
+ gcmkONERROR(gckOS_SetGPUPower(os, gcvTRUE, gcvTRUE));
+
+ /* Mark clock and power as enabled. */
+ Hardware->clockState = gcvTRUE;
+ Hardware->powerState = gcvTRUE;
+ }
+
+ /* Get time until powered on. */
+ gcmkPROFILE_QUERY(time, onTime);
+
+ if ((flag & gcvPOWER_FLAG_STALL) && stall)
+ {
+ int idle;
+ s32 atomValue;
+
+ /* For global operation, all pending commits have already been
+ ** blocked by globalSemaphore or powerSemaphore.*/
+ if (!global)
+ {
+ /* Check commit atom. */
+ gcmkONERROR(gckOS_AtomGet(os, command->atomCommit, &atomValue));
+
+ if (atomValue > 0)
+ {
+ /* Commits are pending - abort power management. */
+ status = broadcast ? gcvSTATUS_CHIP_NOT_READY
+ : gcvSTATUS_MORE_DATA;
+ goto OnError;
+ }
+ }
+
+ if (broadcast)
+ {
+ /* Check for idle. */
+ gcmkONERROR(gckHARDWARE_QueryIdle(Hardware, &idle));
+
+ if (!idle)
+ {
+ status = gcvSTATUS_CHIP_NOT_READY;
+ goto OnError;
+ }
+ }
+
+ else
+ {
+ /* Acquire the command queue. */
+ gcmkONERROR(gckCOMMAND_EnterCommit(command, gcvTRUE));
+ commitEntered = gcvTRUE;
+
+ /* Get the size of the flush command. */
+ gcmkONERROR(gckHARDWARE_Flush(Hardware,
+ gcvFLUSH_ALL,
+ NULL,
+ &requested));
+
+ /* Reserve space in the command queue. */
+ gcmkONERROR(gckCOMMAND_Reserve(command,
+ requested,
+ &buffer,
+ &bytes));
+
+ /* Append a flush. */
+ gcmkONERROR(gckHARDWARE_Flush(
+ Hardware, gcvFLUSH_ALL, buffer, &bytes
+ ));
+
+ /* Execute the command queue. */
+ gcmkONERROR(gckCOMMAND_Execute(command, requested));
+
+ /* Release the command queue. */
+ gcmkONERROR(gckCOMMAND_ExitCommit(command, gcvTRUE));
+ commitEntered = gcvFALSE;
+
+ /* Wait to finish all commands. */
+ gcmkONERROR(gckCOMMAND_Stall(command, gcvTRUE));
+ }
+ }
+
+ /* Get time until stalled. */
+ gcmkPROFILE_QUERY(time, stallTime);
+
+ if (flag & gcvPOWER_FLAG_ACQUIRE)
+ {
+ /* Acquire the power management semaphore. */
+ gcmkONERROR(gckOS_AcquireSemaphore(os, command->powerSemaphore));
+ acquired = gcvTRUE;
+ }
+
+ if (flag & gcvPOWER_FLAG_STOP)
+ {
+ /* Stop the command parser. */
+ gcmkONERROR(gckCOMMAND_Stop(command, gcvFALSE));
+
+ /* Stop the Isr. */
+ gcmkONERROR(Hardware->stopIsr(Hardware->isrContext));
+ }
+
+ /* Get time until stopped. */
+ gcmkPROFILE_QUERY(time, stopTime);
+
+ /* Only process this when hardware is enabled. */
+ if (Hardware->clockState && Hardware->powerState)
+ {
+ if (flag & (gcvPOWER_FLAG_POWER_OFF | gcvPOWER_FLAG_CLOCK_OFF))
+ {
+ if (Hardware->identity.chipModel == gcv4000
+ && Hardware->identity.chipRevision == 0x5208)
+ {
+ clock &= ~2U;
+ }
+ }
+
+ /* Write the clock control register. */
+ gcmkONERROR(gckOS_WriteRegisterEx(os,
+ Hardware->core,
+ 0x00000,
+ clock));
+
+ /* Done loading the frequency scaler. */
+ gcmkONERROR(gckOS_WriteRegisterEx(os,
+ Hardware->core,
+ 0x00000,
+ gcmSETFIELD(clock, 9:9, 0)));
+ }
+
+ if (flag & gcvPOWER_FLAG_DELAY)
+ {
+ /* Wait for the specified amount of time to settle coming back from
+ ** power-off or suspend state. */
+ gcmkONERROR(gckOS_Delay(os, gcdPOWER_CONTROL_DELAY));
+ }
+
+ /* Get time until delayed. */
+ gcmkPROFILE_QUERY(time, delayTime);
+
+ if (flag & gcvPOWER_FLAG_INITIALIZE)
+ {
+ /* Initialize hardware. */
+ gcmkONERROR(gckHARDWARE_InitializeHardware(Hardware));
+
+ gcmkONERROR(gckHARDWARE_SetFastClear(Hardware,
+ Hardware->allowFastClear,
+ Hardware->allowCompression));
+
+ /* Force the command queue to reload the next context. */
+ command->currContext = NULL;
+
+ /* Need to config mmu after command start. */
+ configMmu = gcvTRUE;
+ }
+
+ /* Get time until initialized. */
+ gcmkPROFILE_QUERY(time, initTime);
+
+ if (flag & (gcvPOWER_FLAG_POWER_OFF | gcvPOWER_FLAG_CLOCK_OFF))
+ {
+ /* Turn off the GPU power. */
+ gcmkONERROR(
+ gckOS_SetGPUPower(os,
+ (flag & gcvPOWER_FLAG_CLOCK_OFF) ? gcvFALSE
+ : gcvTRUE,
+ (flag & gcvPOWER_FLAG_POWER_OFF) ? gcvFALSE
+ : gcvTRUE));
+
+ /* Save current hardware power and clock states. */
+ Hardware->clockState = (flag & gcvPOWER_FLAG_CLOCK_OFF) ? gcvFALSE
+ : gcvTRUE;
+ Hardware->powerState = (flag & gcvPOWER_FLAG_POWER_OFF) ? gcvFALSE
+ : gcvTRUE;
+ }
+
+ /* Get time until off. */
+ gcmkPROFILE_QUERY(time, offTime);
+
+ if (flag & gcvPOWER_FLAG_START)
+ {
+ /* Start the command processor. */
+ gcmkONERROR(gckCOMMAND_Start(command));
+
+ /* Start the Isr. */
+ gcmkONERROR(Hardware->startIsr(Hardware->isrContext));
+
+ /* Set NEW MMU. */
+ if (Hardware->mmuVersion != 0 && configMmu)
+ {
+ gcmkONERROR(
+ gckHARDWARE_SetMMUv2(
+ Hardware,
+ gcvTRUE,
+ Hardware->kernel->mmu->mtlbLogical,
+ gcvMMU_MODE_4K,
+ (u8 *)Hardware->kernel->mmu->mtlbLogical + gcdMMU_MTLB_SIZE,
+ gcvTRUE
+ ));
+ }
+ }
+
+ /* Get time until started. */
+ gcmkPROFILE_QUERY(time, startTime);
+
+ if (flag & gcvPOWER_FLAG_RELEASE)
+ {
+ /* Release the power management semaphore. */
+ gcmkONERROR(gckOS_ReleaseSemaphore(os, command->powerSemaphore));
+ acquired = gcvFALSE;
+
+ if (global)
+ {
+ /* Verify global semaphore has been acquired already before
+ ** we release it.
+ ** If it was acquired, gckOS_TryAcquireSemaphore will return
+ ** gcvSTATUS_TIMEOUT and we release it. Otherwise, global
+ ** semaphore will be acquried now, but it still is released
+ ** immediately. */
+ status = gckOS_TryAcquireSemaphore(os, Hardware->globalSemaphore);
+ if (status != gcvSTATUS_TIMEOUT)
+ {
+ gcmkONERROR(status);
+ }
+
+ /* Release the global semaphore. */
+ gcmkONERROR(gckOS_ReleaseSemaphore(os, Hardware->globalSemaphore));
+ globalAcquired = gcvFALSE;
+ }
+ }
+
+ /* Save the new power state. */
+ Hardware->chipPowerState = State;
+
+#if gcdPOWEROFF_TIMEOUT
+ /* Reset power off time */
+ gcmkONERROR(gckOS_GetTicks(&currentTime));
+
+ Hardware->powerOffTime = currentTime + Hardware->powerOffTimeout;
+
+ if (State == gcvPOWER_IDLE || State == gcvPOWER_SUSPEND)
+ {
+ /* Start a timer to power off GPU when GPU enters IDLE or SUSPEND. */
+ gcmkVERIFY_OK(gckOS_StartTimer(os,
+ Hardware->powerOffTimer,
+ Hardware->powerOffTimeout));
+ }
+ else
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "Cancel powerOfftimer");
+
+ /* Cancel running timer when GPU enters ON or OFF. */
+ gcmkVERIFY_OK(gckOS_StopTimer(os, Hardware->powerOffTimer));
+ }
+#endif
+
+ /* Release the power mutex. */
+ gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex));
+
+ /* Get total time. */
+ gcmkPROFILE_QUERY(time, totalTime);
+#if gcdENABLE_PROFILING
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ "PROF(%llu): mutex:%llu on:%llu stall:%llu stop:%llu",
+ freq, mutexTime, onTime, stallTime, stopTime);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE,
+ " delay:%llu init:%llu off:%llu start:%llu total:%llu",
+ delayTime, initTime, offTime, startTime, totalTime);
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (commitEntered)
+ {
+ /* Release the command queue mutex. */
+ gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, gcvTRUE));
+ }
+
+ if (acquired)
+ {
+ /* Release semaphore. */
+ gcmkVERIFY_OK(gckOS_ReleaseSemaphore(Hardware->os,
+ command->powerSemaphore));
+ }
+
+ if (globalAcquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseSemaphore(Hardware->os,
+ Hardware->globalSemaphore));
+ }
+
+ if (mutexAcquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+#else /* gcdPOWER_MANAGEMENT */
+ /* Do nothing */
+ return gcvSTATUS_OK;
+#endif
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_QueryPowerManagementState
+**
+** Get GPU power state.
+**
+** INPUT:
+**
+** gckHARDWARE Harwdare
+** Pointer to an gckHARDWARE object.
+**
+** gceCHIPPOWERSTATE* State
+** Power State.
+**
+*/
+gceSTATUS
+gckHARDWARE_QueryPowerManagementState(
+ IN gckHARDWARE Hardware,
+ OUT gceCHIPPOWERSTATE* State
+ )
+{
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(State != NULL);
+
+ /* Return the statue. */
+ *State = Hardware->chipPowerState;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*State=%d", *State);
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckHARDWARE_QueryIdle(
+ IN gckHARDWARE Hardware,
+ OUT int *IsIdle
+ )
+{
+ gceSTATUS status;
+ u32 idle, address;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(IsIdle != NULL);
+
+ /* We are idle when the power is not ON. */
+ if (Hardware->chipPowerState != gcvPOWER_ON)
+ {
+ *IsIdle = gcvTRUE;
+ }
+
+ else
+ {
+ /* Read idle register. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00004, &idle));
+
+ /* Pipe must be idle. */
+ if ((gcmGETFIELD(idle, 1:1) != 1)
+ || (gcmGETFIELD(idle, 3:3) != 1)
+ || (gcmGETFIELD(idle, 4:4) != 1)
+ || (gcmGETFIELD(idle, 5:5) != 1)
+ || (gcmGETFIELD(idle, 6:6) != 1)
+ || (gcmGETFIELD(idle, 7:7) != 1)
+ || (gcmGETFIELD(idle, 2:2) != 1)
+ )
+ {
+ /* Something is busy. */
+ *IsIdle = gcvFALSE;
+ }
+
+ else
+ {
+ /* Read the current FE address. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00664,
+ &address));
+
+ /* Test if address is inside the last WAIT/LINK sequence. */
+ if ((address >= Hardware->lastWaitLink)
+ && (address <= Hardware->lastWaitLink + 16)
+ )
+ {
+ /* FE is in last WAIT/LINK and the pipe is idle. */
+ *IsIdle = gcvTRUE;
+ }
+ else
+ {
+ /* FE is not in WAIT/LINK yet. */
+ *IsIdle = gcvFALSE;
+ }
+ }
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+** Handy macros that will help in reading those debug registers.
+*/
+
+#define gcmkREAD_DEBUG_REGISTER(control, block, index, data) \
+ gcmkONERROR(\
+ gckOS_WriteRegisterEx(Hardware->os, \
+ Hardware->core, \
+ GC_DEBUG_CONTROL##control##_Address, \
+ gcmSETFIELD(0, \
+ GC_DEBUG_CONTROL##control, \
+ block, \
+ index))); \
+ gcmkONERROR(\
+ gckOS_ReadRegisterEx(Hardware->os, \
+ Hardware->core, \
+ GC_DEBUG_SIGNALS_##block##_Address, \
+ &profiler->data))
+
+#define gcmkRESET_DEBUG_REGISTER(control, block) \
+ gcmkONERROR(\
+ gckOS_WriteRegisterEx(Hardware->os, \
+ Hardware->core, \
+ GC_DEBUG_CONTROL##control##_Address, \
+ gcmSETFIELD(0, \
+ GC_DEBUG_CONTROL##control, \
+ block, \
+ 15))); \
+ gcmkONERROR(\
+ gckOS_WriteRegisterEx(Hardware->os, \
+ Hardware->core, \
+ GC_DEBUG_CONTROL##control##_Address, \
+ gcmSETFIELD(0, \
+ GC_DEBUG_CONTROL##control, \
+ block, \
+ 0)))
+
+/*******************************************************************************
+**
+** gckHARDWARE_ProfileEngine2D
+**
+** Read the profile registers available in the 2D engine and sets them in the
+** profile. The function will also reset the pixelsRendered counter every time.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** OPTIONAL struct _gcs2D_PROFILE *Profile
+** Pointer to a gcs2D_Profile structure.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_ProfileEngine2D(
+ IN gckHARDWARE Hardware,
+ OPTIONAL struct _gcs2D_PROFILE *Profile
+ )
+{
+ gceSTATUS status;
+ struct _gcs2D_PROFILE *profiler = Profile;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ if (Profile != NULL)
+ {
+ /* Read the cycle count. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00438,
+ &Profile->cycleCount));
+
+ /* Read pixels rendered by 2D engine. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 11) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &profiler->pixelsRendered));
+
+ /* Reset counter. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 0)
+));
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+#if VIVANTE_PROFILER
+gceSTATUS
+gckHARDWARE_QueryProfileRegisters(
+ IN gckHARDWARE Hardware,
+ OUT gcsPROFILER_COUNTERS * Counters
+ )
+{
+ gceSTATUS status;
+ gcsPROFILER_COUNTERS * profiler = Counters;
+
+ gcmkHEADER_ARG("Hardware=0x%x Counters=0x%x", Hardware, Counters);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Read the counters. */
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00040,
+ &profiler->gpuTotalRead64BytesPerFrame));
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00044,
+ &profiler->gpuTotalWrite64BytesPerFrame));
+ gcmkONERROR(
+ gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00438,
+ &profiler->gpuCyclesCounter));
+
+ /* Reset counters. */
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x0003C, 1));
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x0003C, 0));
+ gcmkONERROR(
+ gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00438, 0));
+
+ /* PE */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 0) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &profiler->pe_pixel_count_killed_by_color_pipe));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 1) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &profiler->pe_pixel_count_killed_by_depth_pipe));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 2) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &profiler->pe_pixel_count_drawn_by_color_pipe));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 3) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &profiler->pe_pixel_count_drawn_by_depth_pipe));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 19:16, 0)
+));
+
+ /* SH */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 7) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->ps_inst_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 8) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->rendered_pixel_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 9) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vs_inst_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 10) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->rendered_vertice_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 11) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vtx_branch_inst_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 12) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vtx_texld_inst_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 13) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->pxl_branch_inst_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 14) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->pxl_texld_inst_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, gcmSETFIELD(0, 27:24, 0)
+));
+
+ /* PA */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 3) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_input_vtx_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 4) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_input_prim_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 5) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_output_prim_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 6) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_depth_clipped_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 7) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_trivial_rejected_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 8) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_culled_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 3:0, 0)
+));
+
+ /* SE */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 11:8, 0) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00464, &profiler->se_culled_triangle_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 11:8, 1) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00464, &profiler->se_culled_lines_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 11:8, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 11:8, 0)
+));
+
+ /* RA */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 0) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_valid_pixel_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 1) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_total_quad_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 2) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_valid_quad_count_after_early_z));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 3) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_total_primitive_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 9) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_pipe_cache_miss_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 10) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_prefetch_cache_miss_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 11) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_eez_culled_counter));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 19:16, 0)
+));
+
+ /* TX */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 0) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_bilinear_requests));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 1) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_trilinear_requests));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 2) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_discarded_texture_requests));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 3) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_texture_requests));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 5) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_mem_read_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 6) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_mem_read_in_8B_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 7) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_miss_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 8) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_hit_texel_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 9) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_miss_texel_count));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, gcmSETFIELD(0, 27:24, 0)
+));
+
+ /* MC */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 3:0, 1) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_read_req_8B_from_pipeline));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 3:0, 2) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_read_req_8B_from_IP));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 3:0, 3) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_write_req_8B_from_pipeline));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 3:0, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 3:0, 0)
+));
+
+ /* HI */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 11:8, 0) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_read_request_stalled));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 11:8, 1) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_write_request_stalled));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 11:8, 2) ));
+gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_write_data_stalled));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 11:8, 15) ));
+gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, gcmSETFIELD(0, 11:8, 0)
+));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
+
+static gceSTATUS
+_ResetGPU(
+ IN gckHARDWARE Hardware,
+ IN gckOS Os,
+ IN gceCORE Core
+ )
+{
+ u32 control, idle;
+ gceSTATUS status;
+
+ for (;;)
+ {
+ /* Disable clock gating. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ Hardware->powerBaseAddress +
+ 0x00104,
+ 0x00000000));
+
+ control = gcmSETFIELD(0x01590880, 17:17, 1);
+
+ /* Disable pulse-eater. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x0010C,
+ control));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x0010C,
+ gcmSETFIELD(control, 0:0, 1)));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x0010C,
+ control));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x00000,
+ gcmSETFIELD(0x00000100, 9:9, 1)));
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x00000,
+ 0x00000100));
+
+ /* Wait for clock being stable. */
+ gcmkONERROR(gckOS_Delay(Os, 1));
+
+ /* Isolate the GPU. */
+ control = gcmSETFIELD(0x00000100, 19:19, 1);
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x00000,
+ control));
+
+ /* Set soft reset. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x00000,
+ gcmSETFIELD(control, 12:12, 1)));
+
+ /* Wait for reset. */
+ gcmkONERROR(gckOS_Delay(Os, 1));
+
+ /* Reset soft reset bit. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x00000,
+ gcmSETFIELD(control, 12:12, 0)));
+
+ /* Reset GPU isolation. */
+ control = gcmSETFIELD(control, 19:19, 0);
+
+ gcmkONERROR(gckOS_WriteRegisterEx(Os,
+ Core,
+ 0x00000,
+ control));
+
+ /* Read idle register. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Os,
+ Core,
+ 0x00004,
+ &idle));
+
+ if (gcmGETFIELD(idle, 0:0) == 0)
+ {
+ continue;
+ }
+
+ /* Read reset register. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Os,
+ Core,
+ 0x00000,
+ &control));
+
+ if ((gcmGETFIELD(control, 16:16) == 0)
+ || (gcmGETFIELD(control, 17:17) == 0)
+ )
+ {
+ continue;
+ }
+
+ /* GPU is idle. */
+ break;
+ }
+
+ /* Success. */
+ return gcvSTATUS_OK;
+
+OnError:
+
+ /* Return the error. */
+ return status;
+}
+
+gceSTATUS
+gckHARDWARE_Reset(
+ IN gckHARDWARE Hardware
+ )
+{
+ gceSTATUS status;
+ gckCOMMAND command;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_OBJECT(Hardware->kernel, gcvOBJ_KERNEL);
+ command = Hardware->kernel->command;
+ gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND);
+
+ if (Hardware->identity.chipRevision < 0x4600)
+ {
+ /* Not supported - we need the isolation bit. */
+ gcmkONERROR(gcvSTATUS_NOT_SUPPORTED);
+ }
+
+ if (Hardware->chipPowerState == gcvPOWER_ON)
+ {
+ /* Acquire the power management semaphore. */
+ gcmkONERROR(
+ gckOS_AcquireSemaphore(Hardware->os, command->powerSemaphore));
+ acquired = gcvTRUE;
+ }
+
+ if ((Hardware->chipPowerState == gcvPOWER_ON)
+ || (Hardware->chipPowerState == gcvPOWER_IDLE)
+ )
+ {
+ /* Stop the command processor. */
+ gcmkONERROR(gckCOMMAND_Stop(command, gcvTRUE));
+ }
+
+ /* Stop isr, we will start it again when power on GPU. */
+ gcmkONERROR(Hardware->stopIsr(Hardware->isrContext));
+
+ gcmkONERROR(_ResetGPU(Hardware, Hardware->os, Hardware->core));
+
+ /* Force an OFF to ON power switch. */
+ Hardware->chipPowerState = gcvPOWER_OFF;
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(Hardware, gcvPOWER_ON));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the power management semaphore. */
+ gcmkVERIFY_OK(
+ gckOS_ReleaseSemaphore(Hardware->os, command->powerSemaphore));
+ }
+
+ /* Return the error. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckHARDWARE_GetBaseAddress(
+ IN gckHARDWARE Hardware,
+ OUT u32 *BaseAddress
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(BaseAddress != NULL);
+
+ /* Test if we have a new Memory Controller. */
+ if (gcmVERIFYFIELDVALUE(Hardware->identity.chipMinorFeatures, 22:22, 0x1 ))
+ {
+ /* No base address required. */
+ *BaseAddress = 0;
+ }
+ else
+ {
+ /* Get the base address from the OS. */
+ gcmkONERROR(gckOS_GetBaseAddress(Hardware->os, BaseAddress));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*BaseAddress=0x%08x", *BaseAddress);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckHARDWARE_NeedBaseAddress(
+ IN gckHARDWARE Hardware,
+ IN u32 State,
+ OUT int *NeedBase
+ )
+{
+ int need = gcvFALSE;
+
+ gcmkHEADER_ARG("Hardware=0x%x State=0x%08x", Hardware, State);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(NeedBase != NULL);
+
+ /* Make sure this is a load state. */
+ if (gcmVERIFYFIELDVALUE(State, 31:27, 0x01 ))
+ {
+#if !VIVANTE_NO_3D
+ /* Get the state address. */
+ switch (gcmGETFIELD(State, 15:0))
+ {
+ case 0x0596:
+ case 0x0597:
+ case 0x0599:
+ case 0x059A:
+ case 0x05A9:
+ /* These states need a TRUE physical address. */
+ need = gcvTRUE;
+ break;
+ }
+#else
+ /* 2D addresses don't need a base address. */
+#endif
+ }
+
+ /* Return the flag. */
+ *NeedBase = need;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*NeedBase=%d", *NeedBase);
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckHARDWARE_SetIsrManager(
+ IN gckHARDWARE Hardware,
+ IN gctISRMANAGERFUNC StartIsr,
+ IN gctISRMANAGERFUNC StopIsr,
+ IN void *Context
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+
+ gcmkHEADER_ARG("Hardware=0x%x, StartIsr=0x%x, StopIsr=0x%x, Context=0x%x",
+ Hardware, StartIsr, StopIsr, Context);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ if (StartIsr == NULL ||
+ StopIsr == NULL ||
+ Context == NULL)
+ {
+ status = gcvSTATUS_INVALID_ARGUMENT;
+
+ gcmkFOOTER();
+ return status;
+ }
+
+ Hardware->startIsr = StartIsr;
+ Hardware->stopIsr = StopIsr;
+ Hardware->isrContext = Context;
+
+ /* Success. */
+ gcmkFOOTER();
+
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_Compose
+**
+** Start a composition.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to the gckHARDWARE object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckHARDWARE_Compose(
+ IN gckHARDWARE Hardware,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Physical,
+ IN void *Logical,
+ IN size_t Offset,
+ IN size_t Size,
+ IN u8 EventID
+ )
+{
+#if !VIVANTE_NO_3D
+ gceSTATUS status;
+ u32 *triggerState;
+
+ gcmkHEADER_ARG("Hardware=0x%x Physical=0x%x Logical=0x%x"
+ " Offset=%d Size=%d EventID=%d",
+ Hardware, Physical, Logical, Offset, Size, EventID);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+ gcmkVERIFY_ARGUMENT(((Size + 8) & 63) == 0);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+
+ /* Program the trigger state. */
+ triggerState = (u32 *) ((u8 *) Logical + Offset + Size);
+ triggerState[0] = 0x0C03;
+ triggerState[1]
+ = gcmSETFIELD(0, 1:0, 0x1 )
+ | gcmSETFIELD(0, 5:4, 0x3 )
+ | gcmSETFIELD(0, 8:8, 1)
+ | gcmSETFIELD(0, 24:24, 1)
+ | gcmSETFIELD(0, 12:12, 1)
+ | gcmSETFIELD(0, 20:16, EventID)
+ ;
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Flush the cache for the wait/link. */
+ gcmkONERROR(gckOS_CacheClean(
+ Hardware->os, ProcessID, NULL,
+ Physical, Logical, Offset + Size
+ ));
+#endif
+
+ /* Start composition. */
+ gcmkONERROR(gckOS_WriteRegisterEx(
+ Hardware->os, Hardware->core, 0x00554,
+ gcmSETFIELD(0, 1:0, 0x3 )
+ ));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+#else
+ /* Return the status. */
+ return gcvSTATUS_NOT_SUPPORTED;
+#endif
+}
+
+/*******************************************************************************
+**
+** gckHARDWARE_IsFeatureAvailable
+**
+** Verifies whether the specified feature is available in hardware.
+**
+** INPUT:
+**
+** gckHARDWARE Hardware
+** Pointer to an gckHARDWARE object.
+**
+** gceFEATURE Feature
+** Feature to be verified.
+*/
+gceSTATUS
+gckHARDWARE_IsFeatureAvailable(
+ IN gckHARDWARE Hardware,
+ IN gceFEATURE Feature
+ )
+{
+ int available;
+
+ gcmkHEADER_ARG("Hardware=0x%x Feature=%d", Hardware, Feature);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
+
+ /* Only features needed by common kernel logic added here. */
+ switch (Feature)
+ {
+ case gcvFEATURE_END_EVENT:
+ /*available = gcmVERIFYFIELDVALUE(Hardware->identity.chipMinorFeatures2,
+ GC_MINOR_FEATURES2, END_EVENT, AVAILABLE
+ );*/
+ available = gcvFALSE;
+ break;
+ case gcvFEATURE_MC20:
+ available = gcmVERIFYFIELDVALUE(Hardware->identity.chipMinorFeatures, 22:22, 0x1 );
+ break;
+
+ default:
+ gcmkFATAL("Invalid feature has been requested.");
+ available = gcvFALSE;
+ }
+
+ /* Return result. */
+ gcmkFOOTER_ARG("%d", available ? gcvSTATUS_TRUE : gcvSTATUS_OK);
+ return available ? gcvSTATUS_TRUE : gcvSTATUS_OK;
+}
+
+#if gcdFRAME_DB
+static gceSTATUS
+gckHARDWARE_ReadPerformanceRegister(
+ IN gckHARDWARE Hardware,
+ IN unsigned int PerformanceAddress,
+ IN unsigned int IndexAddress,
+ IN unsigned int IndexShift,
+ IN unsigned int Index,
+ OUT u32 *Value
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Hardware=0x%x PerformanceAddress=0x%x IndexAddress=0x%x "
+ "IndexShift=%u Index=%u",
+ Hardware, PerformanceAddress, IndexAddress, IndexShift,
+ Index);
+
+ /* Write the index. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ IndexAddress,
+ Index << IndexShift));
+
+ /* Read the register. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ PerformanceAddress,
+ Value));
+
+ /* Test for reset. */
+ if (Index == 15)
+ {
+ /* Index another register to get out of reset. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, IndexAddress, 0));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Value=0x%x", *Value);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckHARDWARE_GetFrameInfo(
+ IN gckHARDWARE Hardware,
+ OUT gcsHAL_FRAME_INFO * FrameInfo
+ )
+{
+ gceSTATUS status;
+ unsigned int i, clock;
+ gcsHAL_FRAME_INFO info;
+#if gcdFRAME_DB_RESET
+ unsigned int reset;
+#endif
+
+ gcmkHEADER_ARG("Hardware=0x%x", Hardware);
+
+ /* Get profile tick. */
+ gcmkONERROR(gckOS_GetProfileTick(&info.ticks));
+
+ /* Read SH counters and reset them. */
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0045C,
+ 0x00470,
+ 24,
+ 4,
+ &info.shaderCycles));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0045C,
+ 0x00470,
+ 24,
+ 9,
+ &info.vsInstructionCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0045C,
+ 0x00470,
+ 24,
+ 12,
+ &info.vsTextureCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0045C,
+ 0x00470,
+ 24,
+ 7,
+ &info.psInstructionCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0045C,
+ 0x00470,
+ 24,
+ 14,
+ &info.psTextureCount));
+#if gcdFRAME_DB_RESET
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0045C,
+ 0x00470,
+ 24,
+ 15,
+ &reset));
+#endif
+
+ /* Read PA counters and reset them. */
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00460,
+ 0x00474,
+ 0,
+ 3,
+ &info.vertexCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00460,
+ 0x00474,
+ 0,
+ 4,
+ &info.primitiveCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00460,
+ 0x00474,
+ 0,
+ 7,
+ &info.rejectedPrimitives));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00460,
+ 0x00474,
+ 0,
+ 8,
+ &info.culledPrimitives));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00460,
+ 0x00474,
+ 0,
+ 6,
+ &info.clippedPrimitives));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00460,
+ 0x00474,
+ 0,
+ 5,
+ &info.outPrimitives));
+#if gcdFRAME_DB_RESET
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00460,
+ 0x00474,
+ 0,
+ 15,
+ &reset));
+#endif
+
+ /* Read RA counters and reset them. */
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00448,
+ 0x00474,
+ 16,
+ 3,
+ &info.inPrimitives));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00448,
+ 0x00474,
+ 16,
+ 11,
+ &info.culledQuadCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00448,
+ 0x00474,
+ 16,
+ 1,
+ &info.totalQuadCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00448,
+ 0x00474,
+ 16,
+ 2,
+ &info.quadCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00448,
+ 0x00474,
+ 16,
+ 0,
+ &info.totalPixelCount));
+#if gcdFRAME_DB_RESET
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00448,
+ 0x00474,
+ 16,
+ 15,
+ &reset));
+#endif
+
+ /* Read TX counters and reset them. */
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0044C,
+ 0x00474,
+ 24,
+ 0,
+ &info.bilinearRequests));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0044C,
+ 0x00474,
+ 24,
+ 1,
+ &info.trilinearRequests));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0044C,
+ 0x00474,
+ 24,
+ 8,
+ &info.txHitCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0044C,
+ 0x00474,
+ 24,
+ 9,
+ &info.txMissCount));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0044C,
+ 0x00474,
+ 24,
+ 6,
+ &info.txBytes8));
+#if gcdFRAME_DB_RESET
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x0044C,
+ 0x00474,
+ 24,
+ 15,
+ &reset));
+#endif
+
+ /* Read clock control register. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00000,
+ &clock));
+
+ /* Walk through all avaiable pixel pipes. */
+ for (i = 0; i < Hardware->identity.pixelPipes; ++i)
+ {
+ /* Select proper pipe. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00000,
+ gcmSETFIELD(clock, 23:20, i)));
+
+ /* Read cycle registers. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00078,
+ &info.cycles[i]));
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0007C,
+ &info.idleCycles[i]));
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00438,
+ &info.mcCycles[i]));
+
+ /* Read bandwidth registers. */
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0005C,
+ &info.readRequests[i]));
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00040,
+ &info.readBytes8[i]));
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00050,
+ &info.writeRequests[i]));
+ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00044,
+ &info.writeBytes8[i]));
+
+ /* Read PE counters. */
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00454,
+ 0x00470,
+ 16,
+ 0,
+ &info.colorKilled[i]));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00454,
+ 0x00470,
+ 16,
+ 2,
+ &info.colorDrawn[i]));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00454,
+ 0x00470,
+ 16,
+ 1,
+ &info.depthKilled[i]));
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00454,
+ 0x00470,
+ 16,
+ 3,
+ &info.depthDrawn[i]));
+ }
+
+ /* Zero out remaning reserved counters. */
+ for (; i < 8; ++i)
+ {
+ info.readBytes8[i] = 0;
+ info.writeBytes8[i] = 0;
+ info.cycles[i] = 0;
+ info.idleCycles[i] = 0;
+ info.mcCycles[i] = 0;
+ info.readRequests[i] = 0;
+ info.writeRequests[i] = 0;
+ info.colorKilled[i] = 0;
+ info.colorDrawn[i] = 0;
+ info.depthKilled[i] = 0;
+ info.depthDrawn[i] = 0;
+ }
+
+ /* Reset clock control register. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00000,
+ clock));
+
+ /* Reset cycle and bandwidth counters. */
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0003C,
+ 1));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x0003C,
+ 0));
+ gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os,
+ Hardware->core,
+ 0x00078,
+ 0));
+
+#if gcdFRAME_DB_RESET
+ /* Reset PE counters. */
+ gcmkONERROR(gckHARDWARE_ReadPerformanceRegister(
+ Hardware,
+ 0x00454,
+ 0x00470,
+ 16,
+ 15,
+ &reset));
+#endif
+
+ /* Copy to user. */
+ if (copy_to_user(FrameInfo, &info, sizeof(info)) != 0)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.h b/kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.h
new file mode 100644
index 0000000..4185e2b
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_hardware.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_hardware_h_
+#define __gc_hal_kernel_hardware_h_
+
+/* gckHARDWARE object. */
+struct _gckHARDWARE
+{
+ /* Object. */
+ gcsOBJECT object;
+
+ /* Pointer to gctKERNEL object. */
+ gckKERNEL kernel;
+
+ /* Pointer to gctOS object. */
+ gckOS os;
+
+ /* Core */
+ gceCORE core;
+
+ /* Chip characteristics. */
+ gcsHAL_QUERY_CHIP_IDENTITY identity;
+ int allowFastClear;
+ int allowCompression;
+ u32 powerBaseAddress;
+ int extraEventStates;
+
+ /* Big endian */
+ int bigEndian;
+
+ /* Chip status */
+ void * powerMutex;
+ u32 powerProcess;
+ u32 powerThread;
+ gceCHIPPOWERSTATE chipPowerState;
+ u32 lastWaitLink;
+ int clockState;
+ int powerState;
+ void * globalSemaphore;
+
+ gctISRMANAGERFUNC startIsr;
+ gctISRMANAGERFUNC stopIsr;
+ void * isrContext;
+
+ u32 mmuVersion;
+
+ /* Type */
+ gceHARDWARE_TYPE type;
+
+#if gcdPOWEROFF_TIMEOUT
+ u32 powerOffTime;
+ u32 powerOffTimeout;
+ void * powerOffTimer;
+#endif
+
+ void * pageTableDirty;
+};
+
+gceSTATUS
+gckHARDWARE_GetBaseAddress(
+ IN gckHARDWARE Hardware,
+ OUT u32 *BaseAddress
+ );
+
+gceSTATUS
+gckHARDWARE_NeedBaseAddress(
+ IN gckHARDWARE Hardware,
+ IN u32 State,
+ OUT int *NeedBase
+ );
+
+gceSTATUS
+gckHARDWARE_GetFrameInfo(
+ IN gckHARDWARE Hardware,
+ OUT gcsHAL_FRAME_INFO * FrameInfo
+ );
+
+#endif /* __gc_hal_kernel_hardware_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_linux.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_linux.c
new file mode 100644
index 0000000..b2ffe04
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_linux.c
@@ -0,0 +1,409 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.
+**
+** size_t Bytes
+** Number of bytes to map.
+**
+** OUTPUT:
+**
+** void ** 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 size_t Bytes,
+ OUT void **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.
+**
+** size_t Bytes
+** Number of bytes to map.
+**
+** void *Logical
+** Base address of the mapped memory region.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckKERNEL_UnmapMemory(
+ IN gckKERNEL Kernel,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN void *Logical
+ )
+{
+ return gckOS_UnmapMemory(Kernel->os, Physical, Bytes, Logical);
+}
+
+/*******************************************************************************
+**
+** gckKERNEL_MapVideoMemoryEx
+**
+** Get the logical address for a hardware specific memory address for the
+** current process.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** int InUserSpace
+** gcvTRUE to map the memory into the user space.
+**
+** u32 Address
+** Hardware specific memory address.
+**
+** OUTPUT:
+**
+** void ** 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 int InUserSpace,
+ IN u32 Address,
+ OUT void **Logical
+ )
+{
+ gckGALDEVICE device;
+ PLINUX_MDL mdl;
+ PLINUX_MDL_MAP mdlMap;
+ gcePOOL pool;
+ u32 offset, base;
+ gceSTATUS status;
+ void *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;
+
+ /* 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
+ {
+ int processID = task_tgid_vnr(current);
+
+ mdl = (PLINUX_MDL) device->contiguousPhysical;
+
+ mdlMap = FindMdlMap(mdl, processID);
+ gcmkASSERT(mdlMap);
+
+ logical = (void *) mdlMap->vmaAddr;
+ }
+
+ 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 = (void *) ((u8 *) logical + offset);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Logical=%p", *Logical);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Retunn the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** 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 int 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. */
+ status = gckHARDWARE_Interrupt(Kernel->hardware, Data);
+ 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 != NULL);
+
+ /* 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/kernel_drivers/v4_cleaned/gc_hal_kernel_linux.h b/kernel_drivers/v4_cleaned/gc_hal_kernel_linux.h
new file mode 100644
index 0000000..db93ac7
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_linux.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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 "gc_hal_options_internal.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>
+
+#include <linux/clk.h>
+
+#include "gc_hal.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+#include "gc_hal_kernel_device.h"
+#include "gc_hal_kernel_os.h"
+
+#define DRV_NAME "galcore"
+
+#define GetPageCount(size, offset) ((((size) + ((offset) & ~PAGE_CACHE_MASK)) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
+
+static inline int
+GetOrder(
+ IN int numPages
+ )
+{
+ int order = 0;
+
+ while ((1 << order) < numPages) order++;
+
+ return order;
+}
+
+#endif /* __gc_hal_kernel_linux_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c
new file mode 100644
index 0000000..e9e1d5d
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_mmu.c
@@ -0,0 +1,1236 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+
+#define _GC_OBJ_ZONE gcvZONE_MMU
+
+typedef enum _gceMMU_TYPE
+{
+ gcvMMU_USED = (0 << 4),
+ gcvMMU_SINGLE = (1 << 4),
+ gcvMMU_FREE = (2 << 4),
+}
+gceMMU_TYPE;
+
+#define gcmENTRY_TYPE(x) (x & 0xF0)
+
+#define gcdMMU_TABLE_DUMP 0
+
+/*
+ gcdMMU_CLEAR_VALUE
+
+ The clear value for the entry of the old MMU.
+*/
+#ifndef gcdMMU_CLEAR_VALUE
+# define gcdMMU_CLEAR_VALUE 0x00000ABC
+#endif
+
+typedef struct _gcsMMU_STLB *gcsMMU_STLB_PTR;
+
+typedef struct _gcsMMU_STLB
+{
+ gctPHYS_ADDR physical;
+ u32 * logical;
+ size_t size;
+ u32 physBase;
+ size_t pageCount;
+ u32 mtlbIndex;
+ u32 mtlbEntryNum;
+ gcsMMU_STLB_PTR next;
+} gcsMMU_STLB;
+
+#if gcdSHARED_PAGETABLE
+typedef struct _gcsSharedPageTable * gcsSharedPageTable_PTR;
+typedef struct _gcsSharedPageTable
+{
+ /* Shared gckMMU object. */
+ gckMMU mmu;
+
+ /* Hardwares which use this shared pagetable. */
+ gckHARDWARE hardwares[gcdCORE_COUNT];
+
+ /* Number of cores use this shared pagetable. */
+ u32 reference;
+}
+gcsSharedPageTable;
+
+static gcsSharedPageTable_PTR sharedPageTable = NULL;
+#endif
+
+static gceSTATUS
+_Link(
+ IN gckMMU Mmu,
+ IN u32 Index,
+ IN u32 Next
+ )
+{
+ if (Index >= Mmu->pageTableEntries)
+ {
+ /* Just move heap pointer. */
+ Mmu->heapList = Next;
+ }
+ else
+ {
+ /* Address page table. */
+ u32 *pageTable = Mmu->pageTableLogical;
+
+ /* Dispatch on node type. */
+ switch (gcmENTRY_TYPE(pageTable[Index]))
+ {
+ case gcvMMU_SINGLE:
+ /* Set single index. */
+ pageTable[Index] = (Next << 8) | gcvMMU_SINGLE;
+ break;
+
+ case gcvMMU_FREE:
+ /* Set index. */
+ pageTable[Index + 1] = Next;
+ break;
+
+ default:
+ gcmkFATAL("MMU table correcupted at index %u!", Index);
+ return gcvSTATUS_HEAP_CORRUPTED;
+ }
+ }
+
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+static gceSTATUS
+_AddFree(
+ IN gckMMU Mmu,
+ IN u32 Index,
+ IN u32 Node,
+ IN u32 Count
+ )
+{
+ u32 *pageTable = Mmu->pageTableLogical;
+
+ if (Count == 1)
+ {
+ /* Initialize a single page node. */
+ pageTable[Node] = (~((1U<<8)-1)) | gcvMMU_SINGLE;
+ }
+ else
+ {
+ /* Initialize the node. */
+ pageTable[Node + 0] = (Count << 8) | gcvMMU_FREE;
+ pageTable[Node + 1] = ~0U;
+ }
+
+ /* Append the node. */
+ return _Link(Mmu, Index, Node);
+}
+
+static gceSTATUS
+_Collect(
+ IN gckMMU Mmu
+ )
+{
+ u32 *pageTable = Mmu->pageTableLogical;
+ gceSTATUS status;
+ u32 i, previous, start = 0, count = 0;
+
+ previous = Mmu->heapList = ~0U;
+ Mmu->freeNodes = gcvFALSE;
+
+ /* Walk the entire page table. */
+ for (i = 0; i < Mmu->pageTableEntries; ++i)
+ {
+ /* Dispatch based on type of page. */
+ switch (gcmENTRY_TYPE(pageTable[i]))
+ {
+ case gcvMMU_USED:
+ /* Used page, so close any open node. */
+ if (count > 0)
+ {
+ /* Add the node. */
+ gcmkONERROR(_AddFree(Mmu, previous, start, count));
+
+ /* Reset the node. */
+ previous = start;
+ count = 0;
+ }
+ break;
+
+ case gcvMMU_SINGLE:
+ /* Single free node. */
+ if (count++ == 0)
+ {
+ /* Start a new node. */
+ start = i;
+ }
+ break;
+
+ case gcvMMU_FREE:
+ /* A free node. */
+ if (count == 0)
+ {
+ /* Start a new node. */
+ start = i;
+ }
+
+ /* Advance the count. */
+ count += pageTable[i] >> 8;
+
+ /* Advance the index into the page table. */
+ i += (pageTable[i] >> 8) - 1;
+ break;
+
+ default:
+ gcmkFATAL("MMU page table correcupted at index %u!", i);
+ return gcvSTATUS_HEAP_CORRUPTED;
+ }
+ }
+
+ /* See if we have an open node left. */
+ if (count > 0)
+ {
+ /* Add the node to the list. */
+ gcmkONERROR(_AddFree(Mmu, previous, start, count));
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_MMU,
+ "Performed a garbage collection of the MMU heap.");
+
+ /* Success. */
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the staus. */
+ return status;
+}
+
+static u32
+_SetPage(u32 PageAddress)
+{
+ return PageAddress
+ /* writable */
+ | (1 << 2)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+}
+
+static gceSTATUS
+_FillFlatMapping(
+ IN gckMMU Mmu,
+ IN u32 PhysBase,
+ OUT size_t Size
+ )
+{
+ gceSTATUS status;
+ int mutex = gcvFALSE;
+ gcsMMU_STLB_PTR head = NULL, pre = NULL;
+ u32 start = PhysBase & (~gcdMMU_PAGE_64K_MASK);
+ u32 end = (PhysBase + Size - 1) & (~gcdMMU_PAGE_64K_MASK);
+ u32 mStart = start >> gcdMMU_MTLB_SHIFT;
+ u32 mEnd = end >> gcdMMU_MTLB_SHIFT;
+ u32 sStart = (start & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
+ u32 sEnd = (end & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT;
+
+ /* Grab the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
+ mutex = gcvTRUE;
+
+ while (mStart <= mEnd)
+ {
+ gcmkASSERT(mStart < gcdMMU_MTLB_ENTRY_NUM);
+ if (*(Mmu->mtlbLogical + mStart) == 0)
+ {
+ gcsMMU_STLB_PTR stlb;
+ void *pointer = NULL;
+ u32 last = (mStart == mEnd) ? sEnd : (gcdMMU_STLB_64K_ENTRY_NUM - 1);
+
+ gcmkONERROR(gckOS_Allocate(Mmu->os, sizeof(struct _gcsMMU_STLB), &pointer));
+ stlb = pointer;
+
+ stlb->mtlbEntryNum = 0;
+ stlb->next = NULL;
+ stlb->physical = NULL;
+ stlb->logical = NULL;
+ stlb->size = gcdMMU_STLB_64K_SIZE;
+ stlb->pageCount = 0;
+
+ if (pre == NULL)
+ {
+ pre = head = stlb;
+ }
+ else
+ {
+ gcmkASSERT(pre->next == NULL);
+ pre->next = stlb;
+ pre = stlb;
+ }
+
+ gcmkONERROR(
+ gckOS_AllocateContiguous(Mmu->os,
+ gcvFALSE,
+ &stlb->size,
+ &stlb->physical,
+ (void *)&stlb->logical));
+
+ gcmkONERROR(gckOS_ZeroMemory(stlb->logical, stlb->size));
+
+ gcmkONERROR(gckOS_GetPhysicalAddress(
+ Mmu->os,
+ stlb->logical,
+ &stlb->physBase));
+
+ if (stlb->physBase & (gcdMMU_STLB_64K_SIZE - 1))
+ {
+ gcmkONERROR(gcvSTATUS_NOT_ALIGNED);
+ }
+
+ *(Mmu->mtlbLogical + mStart)
+ = stlb->physBase
+ /* 64KB page size */
+ | (1 << 2)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
+ __FUNCTION__, __LINE__,
+ mStart,
+ *(Mmu->mtlbLogical + mStart));
+#endif
+
+ stlb->mtlbIndex = mStart;
+ stlb->mtlbEntryNum = 1;
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): STLB: logical:%08x -> physical:%08x\n",
+ __FUNCTION__, __LINE__,
+ stlb->logical,
+ stlb->physBase);
+#endif
+
+ while (sStart <= last)
+ {
+ gcmkASSERT(!(start & gcdMMU_PAGE_64K_MASK));
+ *(stlb->logical + sStart) = _SetPage(start);
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): insert STLB[%d]: %08x\n",
+ __FUNCTION__, __LINE__,
+ sStart,
+ *(stlb->logical + sStart));
+#endif
+ /* next page. */
+ start += gcdMMU_PAGE_64K_SIZE;
+ sStart++;
+ stlb->pageCount++;
+ }
+
+ sStart = 0;
+ ++mStart;
+ }
+ else
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
+ }
+ }
+
+ /* Insert the stlb into staticSTLB. */
+ if (Mmu->staticSTLB == NULL)
+ {
+ Mmu->staticSTLB = head;
+ }
+ else
+ {
+ gcmkASSERT(pre == NULL);
+ gcmkASSERT(pre->next == NULL);
+ pre->next = Mmu->staticSTLB;
+ Mmu->staticSTLB = head;
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+
+ return gcvSTATUS_OK;
+
+OnError:
+
+ /* Roll back. */
+ while (head != NULL)
+ {
+ pre = head;
+ head = head->next;
+
+ if (pre->physical != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ pre->physical,
+ pre->logical,
+ pre->size));
+ }
+
+ if (pre->mtlbEntryNum != 0)
+ {
+ gcmkASSERT(pre->mtlbEntryNum == 1);
+ *(Mmu->mtlbLogical + pre->mtlbIndex) = 0;
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
+ }
+
+ if (mutex)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+ }
+
+ return status;
+}
+
+static gceSTATUS
+_SetupDynamicSpace(
+ IN gckMMU Mmu
+ )
+{
+ gceSTATUS status;
+ int i;
+ u32 physical;
+ int numEntries;
+ u32 *pageTable;
+ int acquired = gcvFALSE;
+
+ /* find the start of dynamic address space. */
+ for (i = 0; i < gcdMMU_MTLB_ENTRY_NUM; i++)
+ {
+ if (!Mmu->mtlbLogical[i])
+ {
+ break;
+ }
+ }
+
+ Mmu->dynamicMappingStart = i;
+
+ /* Number of entries in Master TLB for dynamic mapping. */
+ numEntries = gcdMMU_MTLB_ENTRY_NUM - i;
+
+ Mmu->pageTableSize = numEntries * 4096;
+
+ Mmu->pageTableEntries = Mmu->pageTableSize / sizeof(u32);
+
+ /* Construct Slave TLB. */
+ gcmkONERROR(gckOS_AllocateContiguous(Mmu->os,
+ gcvFALSE,
+ &Mmu->pageTableSize,
+ &Mmu->pageTablePhysical,
+ (void *)&Mmu->pageTableLogical));
+
+ /* Invalidate all entries. */
+ gcmkONERROR(gckOS_ZeroMemory(Mmu->pageTableLogical,
+ Mmu->pageTableSize));
+
+ /* Initilization. */
+ pageTable = Mmu->pageTableLogical;
+ pageTable[0] = (Mmu->pageTableEntries << 8) | gcvMMU_FREE;
+ pageTable[1] = ~0U;
+ Mmu->heapList = 0;
+ Mmu->freeNodes = gcvFALSE;
+
+ gcmkONERROR(gckOS_GetPhysicalAddress(Mmu->os,
+ Mmu->pageTableLogical,
+ &physical));
+
+ /* Grab the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ /* Map to Master TLB. */
+ for (; i < gcdMMU_MTLB_ENTRY_NUM; i++)
+ {
+ Mmu->mtlbLogical[i] = physical
+ /* 4KB page size */
+ | (0 << 2)
+ /* Ignore exception */
+ | (0 << 1)
+ /* Present */
+ | (1 << 0);
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n",
+ __FUNCTION__, __LINE__,
+ i,
+ *(Mmu->mtlbLogical + i));
+#endif
+ physical += gcdMMU_STLB_4K_SIZE;
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+
+ return gcvSTATUS_OK;
+
+OnError:
+ if (Mmu->pageTableLogical)
+ {
+ /* Free the page table. */
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ Mmu->pageTablePhysical,
+ (void *) Mmu->pageTableLogical,
+ Mmu->pageTableSize));
+ }
+
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+ }
+
+ return status;
+}
+
+/*******************************************************************************
+**
+** _Construct
+**
+** Construct a new gckMMU object.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** size_t MmuSize
+** Number of bytes for the page table.
+**
+** OUTPUT:
+**
+** gckMMU * Mmu
+** Pointer to a variable that receives the gckMMU object pointer.
+*/
+static gceSTATUS
+_Construct(
+ IN gckKERNEL Kernel,
+ IN size_t MmuSize,
+ OUT gckMMU * Mmu
+ )
+{
+ gckOS os;
+ gckHARDWARE hardware;
+ gceSTATUS status;
+ gckMMU mmu = NULL;
+ u32 *pageTable;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Kernel=0x%x MmuSize=%lu", Kernel, MmuSize);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(MmuSize > 0);
+ gcmkVERIFY_ARGUMENT(Mmu != NULL);
+
+ /* Extract the gckOS object pointer. */
+ os = Kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Extract the gckHARDWARE object pointer. */
+ hardware = Kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ /* Allocate memory for the gckMMU object. */
+ gcmkONERROR(gckOS_Allocate(os, sizeof(struct _gckMMU), &pointer));
+
+ mmu = pointer;
+
+ /* Initialize the gckMMU object. */
+ mmu->object.type = gcvOBJ_MMU;
+ mmu->os = os;
+ mmu->hardware = hardware;
+ mmu->pageTableMutex = NULL;
+ mmu->pageTableLogical = NULL;
+ mmu->mtlbLogical = NULL;
+ mmu->staticSTLB = NULL;
+ mmu->enabled = gcvFALSE;
+
+ /* Create the page table mutex. */
+ gcmkONERROR(gckOS_CreateMutex(os, &mmu->pageTableMutex));
+
+ if (hardware->mmuVersion == 0)
+ {
+ mmu->pageTableSize = MmuSize;
+
+ gcmkONERROR(
+ gckOS_AllocateContiguous(os,
+ gcvFALSE,
+ &mmu->pageTableSize,
+ &mmu->pageTablePhysical,
+ &pointer));
+
+ mmu->pageTableLogical = pointer;
+
+ /* Compute number of entries in page table. */
+ mmu->pageTableEntries = mmu->pageTableSize / sizeof(u32);
+
+ /* Mark all pages as free. */
+ pageTable = mmu->pageTableLogical;
+
+#if gcdMMU_CLEAR_VALUE
+ {
+ u32 i;
+
+ for (i = 0; i < mmu->pageTableEntries; ++i)
+ {
+ pageTable[i] = gcdMMU_CLEAR_VALUE;
+ }
+ }
+#endif
+
+ pageTable[0] = (mmu->pageTableEntries << 8) | gcvMMU_FREE;
+ pageTable[1] = ~0U;
+ mmu->heapList = 0;
+ mmu->freeNodes = gcvFALSE;
+
+ /* Set page table address. */
+ gcmkONERROR(
+ gckHARDWARE_SetMMU(hardware, (void *) mmu->pageTableLogical));
+ }
+ else
+ {
+ /* Allocate the 4K mode MTLB table. */
+ mmu->mtlbSize = gcdMMU_MTLB_SIZE + 64;
+
+ gcmkONERROR(
+ gckOS_AllocateContiguous(os,
+ gcvFALSE,
+ &mmu->mtlbSize,
+ &mmu->mtlbPhysical,
+ &pointer));
+
+ mmu->mtlbLogical = pointer;
+
+ /* Invalid all the entries. */
+ gcmkONERROR(
+ gckOS_ZeroMemory(pointer, mmu->mtlbSize));
+ }
+
+ /* Return the gckMMU object pointer. */
+ *Mmu = mmu;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Mmu=0x%x", *Mmu);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (mmu != NULL)
+ {
+ if (mmu->pageTableLogical != NULL)
+ {
+ /* Free the page table. */
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(os,
+ mmu->pageTablePhysical,
+ (void *) mmu->pageTableLogical,
+ mmu->pageTableSize));
+
+ }
+
+ if (mmu->mtlbLogical != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(os,
+ mmu->mtlbPhysical,
+ (void *) mmu->mtlbLogical,
+ mmu->mtlbSize));
+ }
+
+ if (mmu->pageTableMutex != NULL)
+ {
+ /* Delete the mutex. */
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, mmu->pageTableMutex));
+ }
+
+ /* Mark the gckMMU object as unknown. */
+ mmu->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the allocates memory. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, mmu));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** _Destroy
+**
+** Destroy a gckMMU object.
+**
+** INPUT:
+**
+** gckMMU Mmu
+** Pointer to an gckMMU object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+static gceSTATUS
+_Destroy(
+ IN gckMMU Mmu
+ )
+{
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+
+ while (Mmu->staticSTLB != NULL)
+ {
+ gcsMMU_STLB_PTR pre = Mmu->staticSTLB;
+ Mmu->staticSTLB = pre->next;
+
+ if (pre->physical != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ pre->physical,
+ pre->logical,
+ pre->size));
+ }
+
+ if (pre->mtlbEntryNum != 0)
+ {
+ gcmkASSERT(pre->mtlbEntryNum == 1);
+ *(Mmu->mtlbLogical + pre->mtlbIndex) = 0;
+#if gcdMMU_TABLE_DUMP
+ gckOS_Print("%s(%d): clean MTLB[%d]\n",
+ __FUNCTION__, __LINE__,
+ pre->mtlbIndex);
+#endif
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre));
+ }
+
+ if (Mmu->hardware->mmuVersion != 0)
+ {
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ Mmu->mtlbPhysical,
+ (void *) Mmu->mtlbLogical,
+ Mmu->mtlbSize));
+ }
+
+ /* Free the page table. */
+ gcmkVERIFY_OK(
+ gckOS_FreeContiguous(Mmu->os,
+ Mmu->pageTablePhysical,
+ (void *) Mmu->pageTableLogical,
+ Mmu->pageTableSize));
+
+ /* Delete the page table mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->pageTableMutex));
+
+ /* Mark the gckMMU object as unknown. */
+ Mmu->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckMMU object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, Mmu));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckMMU_Construct(
+ IN gckKERNEL Kernel,
+ IN size_t MmuSize,
+ OUT gckMMU * Mmu
+ )
+{
+#if gcdSHARED_PAGETABLE
+ gceSTATUS status;
+ void *pointer;
+
+ gcmkHEADER_ARG("Kernel=0x%08x", Kernel);
+
+ if (sharedPageTable == NULL)
+ {
+ gcmkONERROR(
+ gckOS_Allocate(Kernel->os,
+ sizeof(struct _gcsSharedPageTable),
+ &pointer));
+ sharedPageTable = pointer;
+
+ gcmkONERROR(
+ gckOS_ZeroMemory(sharedPageTable,
+ sizeof(struct _gcsSharedPageTable)));
+
+ gcmkONERROR(_Construct(Kernel, MmuSize, &sharedPageTable->mmu));
+ }
+
+ *Mmu = sharedPageTable->mmu;
+
+ sharedPageTable->hardwares[sharedPageTable->reference] = Kernel->hardware;
+
+ sharedPageTable->reference++;
+
+ gcmkFOOTER_ARG("sharedPageTable->reference=%lu", sharedPageTable->reference);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (sharedPageTable)
+ {
+ if (sharedPageTable->mmu)
+ {
+ gcmkVERIFY_OK(gckMMU_Destroy(sharedPageTable->mmu));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, sharedPageTable));
+ }
+
+ gcmkFOOTER();
+ return status;
+#else
+ return _Construct(Kernel, MmuSize, Mmu);
+#endif
+}
+
+gceSTATUS
+gckMMU_Destroy(
+ IN gckMMU Mmu
+ )
+{
+#if gcdSHARED_PAGETABLE
+ sharedPageTable->reference--;
+
+ if (sharedPageTable->reference == 0)
+ {
+ if (sharedPageTable->mmu)
+ {
+ gcmkVERIFY_OK(_Destroy(Mmu));
+ }
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, sharedPageTable));
+ }
+
+ return gcvSTATUS_OK;
+#else
+ return _Destroy(Mmu);
+#endif
+}
+
+/*******************************************************************************
+**
+** gckMMU_AllocatePages
+**
+** Allocate pages inside the page table.
+**
+** INPUT:
+**
+** gckMMU Mmu
+** Pointer to an gckMMU object.
+**
+** size_t PageCount
+** Number of pages to allocate.
+**
+** OUTPUT:
+**
+** void ** PageTable
+** Pointer to a variable that receives the base address of the page
+** table.
+**
+** u32 * Address
+** Pointer to a variable that receives the hardware specific address.
+*/
+gceSTATUS
+gckMMU_AllocatePages(
+ IN gckMMU Mmu,
+ IN size_t PageCount,
+ OUT void **PageTable,
+ OUT u32 * Address
+ )
+{
+ gceSTATUS status;
+ int mutex = gcvFALSE;
+ u32 index = 0, previous = ~0U, left;
+ u32 *pageTable;
+ int gotIt;
+ u32 address;
+
+ gcmkHEADER_ARG("Mmu=0x%x PageCount=%lu", Mmu, PageCount);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+ gcmkVERIFY_ARGUMENT(PageTable != NULL);
+
+ if (PageCount > Mmu->pageTableEntries)
+ {
+ /* Not enough pages avaiable. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ /* Grab the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE));
+ mutex = gcvTRUE;
+
+ /* Cast pointer to page table. */
+ for (pageTable = Mmu->pageTableLogical, gotIt = gcvFALSE; !gotIt;)
+ {
+ /* Walk the heap list. */
+ for (index = Mmu->heapList; !gotIt && (index < Mmu->pageTableEntries);)
+ {
+ /* Check the node type. */
+ switch (gcmENTRY_TYPE(pageTable[index]))
+ {
+ case gcvMMU_SINGLE:
+ /* Single odes are valid if we only need 1 page. */
+ if (PageCount == 1)
+ {
+ gotIt = gcvTRUE;
+ }
+ else
+ {
+ /* Move to next node. */
+ previous = index;
+ index = pageTable[index] >> 8;
+ }
+ break;
+
+ case gcvMMU_FREE:
+ /* Test if the node has enough space. */
+ if (PageCount <= (pageTable[index] >> 8))
+ {
+ gotIt = gcvTRUE;
+ }
+ else
+ {
+ /* Move to next node. */
+ previous = index;
+ index = pageTable[index + 1];
+ }
+ break;
+
+ default:
+ gcmkFATAL("MMU table correcupted at index %u!", index);
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+
+ /* Test if we are out of memory. */
+ if (index >= Mmu->pageTableEntries)
+ {
+ if (Mmu->freeNodes)
+ {
+ /* Time to move out the trash! */
+ gcmkONERROR(_Collect(Mmu));
+ }
+ else
+ {
+ /* Out of resources. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ }
+
+ switch (gcmENTRY_TYPE(pageTable[index]))
+ {
+ case gcvMMU_SINGLE:
+ /* Unlink single node from free list. */
+ gcmkONERROR(
+ _Link(Mmu, previous, pageTable[index] >> 8));
+ break;
+
+ case gcvMMU_FREE:
+ /* Check how many pages will be left. */
+ left = (pageTable[index] >> 8) - PageCount;
+ switch (left)
+ {
+ case 0:
+ /* The entire node is consumed, just unlink it. */
+ gcmkONERROR(
+ _Link(Mmu, previous, pageTable[index + 1]));
+ break;
+
+ case 1:
+ /* One page will remain. Convert the node to a single node and
+ ** advance the index. */
+ pageTable[index] = (pageTable[index + 1] << 8) | gcvMMU_SINGLE;
+ index ++;
+ break;
+
+ default:
+ /* Enough pages remain for a new node. However, we will just adjust
+ ** the size of the current node and advance the index. */
+ pageTable[index] = (left << 8) | gcvMMU_FREE;
+ index += left;
+ break;
+ }
+ break;
+ }
+
+ /* Mark node as used. */
+ pageTable[index] = gcvMMU_USED;
+
+ /* Return pointer to page table. */
+ *PageTable = &pageTable[index];
+
+ /* Build virtual address. */
+ if (Mmu->hardware->mmuVersion == 0)
+ {
+ gcmkONERROR(
+ gckHARDWARE_BuildVirtualAddress(Mmu->hardware, index, 0, &address));
+ }
+ else
+ {
+ u32 masterOffset = index / gcdMMU_STLB_4K_ENTRY_NUM
+ + Mmu->dynamicMappingStart;
+ u32 slaveOffset = index % gcdMMU_STLB_4K_ENTRY_NUM;
+
+ address = (masterOffset << gcdMMU_MTLB_SHIFT)
+ | (slaveOffset << gcdMMU_STLB_4K_SHIFT);
+ }
+
+ if (Address != NULL)
+ {
+ *Address = address;
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+
+ /* Success. */
+ gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x",
+ *PageTable, gcmOPT_VALUE(Address));
+ return gcvSTATUS_OK;
+
+OnError:
+
+ if (mutex)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckMMU_FreePages
+**
+** Free pages inside the page table.
+**
+** INPUT:
+**
+** gckMMU Mmu
+** Pointer to an gckMMU object.
+**
+** void *PageTable
+** Base address of the page table to free.
+**
+** size_t PageCount
+** Number of pages to free.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckMMU_FreePages(
+ IN gckMMU Mmu,
+ IN void *PageTable,
+ IN size_t PageCount
+ )
+{
+ u32 *pageTable;
+
+ gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=%lu",
+ Mmu, PageTable, PageCount);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageTable != NULL);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+
+ /* Convert the pointer. */
+ pageTable = (u32 *) PageTable;
+
+#if gcdMMU_CLEAR_VALUE
+ {
+ u32 i;
+
+ for (i = 0; i < PageCount; ++i)
+ {
+ pageTable[i] = gcdMMU_CLEAR_VALUE;
+ }
+ }
+#endif
+
+ if (PageCount == 1)
+ {
+ /* Single page node. */
+ pageTable[0] = (~((1U<<8)-1)) | gcvMMU_SINGLE;
+ }
+ else
+ {
+ /* Mark the node as free. */
+ pageTable[0] = (PageCount << 8) | gcvMMU_FREE;
+ pageTable[1] = ~0U;
+ }
+
+ /* We have free nodes. */
+ Mmu->freeNodes = gcvTRUE;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckMMU_Enable(
+ IN gckMMU Mmu,
+ IN u32 PhysBaseAddr,
+ IN u32 PhysSize
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+
+#if gcdSHARED_PAGETABLE
+ if (Mmu->enabled)
+ {
+ gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
+ return gcvSTATUS_SKIP;
+ }
+#endif
+
+ if (Mmu->hardware->mmuVersion == 0)
+ {
+ /* Success. */
+ gcmkFOOTER_ARG("Status=%d", gcvSTATUS_SKIP);
+ return gcvSTATUS_SKIP;
+ }
+ else
+ {
+ if (PhysSize != 0)
+ {
+ gcmkONERROR(_FillFlatMapping(
+ Mmu,
+ PhysBaseAddr,
+ PhysSize
+ ));
+ }
+
+ gcmkONERROR(_SetupDynamicSpace(Mmu));
+
+ gcmkONERROR(
+ gckHARDWARE_SetMMUv2(
+ Mmu->hardware,
+ gcvTRUE,
+ Mmu->mtlbLogical,
+ gcvMMU_MODE_4K,
+ (u8 *)Mmu->mtlbLogical + gcdMMU_MTLB_SIZE,
+ gcvFALSE
+ ));
+
+ Mmu->enabled = gcvTRUE;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckMMU_SetPage(
+ IN gckMMU Mmu,
+ IN u32 PageAddress,
+ IN u32 *PageEntry
+ )
+{
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageEntry != NULL);
+ gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF));
+
+ if (Mmu->hardware->mmuVersion == 0)
+ {
+ *PageEntry = PageAddress;
+ }
+ else
+ {
+ *PageEntry = _SetPage(PageAddress);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckMMU_Flush(
+ IN gckMMU Mmu
+ )
+{
+ gckHARDWARE hardware;
+#if gcdSHARED_PAGETABLE
+ int i;
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ hardware = sharedPageTable->hardwares[i];
+ if (hardware)
+ {
+ /* Notify cores who use this page table. */
+ gcmkVERIFY_OK(
+ gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
+ }
+ }
+#else
+ hardware = Mmu->hardware;
+ gcmkVERIFY_OK(
+ gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1));
+#endif
+
+ return gcvSTATUS_OK;
+}
+
+/******************************************************************************
+****************************** T E S T C O D E ******************************
+******************************************************************************/
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_os.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_os.c
new file mode 100644
index 0000000..e21b97f
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_os.c
@@ -0,0 +1,7495 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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>
+#ifdef NO_DMA_COHERENT
+#include <linux/dma-mapping.h>
+#endif /* NO_DMA_COHERENT */
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/math64.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+#define _GC_OBJ_ZONE gcvZONE_OS
+
+/*******************************************************************************
+***** Version Signature *******************************************************/
+
+const char * _PLATFORM = "\n\0$PLATFORM$Linux$\n";
+
+#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))
+
+/* Protection bit when mapping memroy to user sapce */
+#define gcmkPAGED_MEMROY_PROT(x) pgprot_writecombine(x)
+
+#if gcdNONPAGED_MEMORY_BUFFERABLE
+#define gcmkNONPAGED_MEMROY_PROT(x) pgprot_writecombine(x)
+#elif !gcdNONPAGED_MEMORY_CACHEABLE
+#define gcmkNONPAGED_MEMROY_PROT(x) pgprot_noncached(x)
+#endif
+
+#define gcdINFINITE_TIMEOUT (60 * 1000)
+#define gcdDETECT_TIMEOUT 0
+#define gcdDETECT_DMA_ADDRESS 1
+#define gcdDETECT_DMA_STATE 1
+
+#define gcdUSE_NON_PAGED_MEMORY_CACHE 10
+
+/******************************************************************************\
+********************************** Structures **********************************
+\******************************************************************************/
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+typedef struct _gcsNonPagedMemoryCache
+{
+#ifndef NO_DMA_COHERENT
+ int size;
+ char * addr;
+ dma_addr_t dmaHandle;
+#else
+ long order;
+ struct page * page;
+#endif
+
+ struct _gcsNonPagedMemoryCache * prev;
+ struct _gcsNonPagedMemoryCache * next;
+}
+gcsNonPagedMemoryCache;
+#endif /* gcdUSE_NON_PAGED_MEMORY_CACHE */
+
+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. */
+ u32 physical;
+
+ /* Logical address of this mapping. */
+ void * logical;
+
+ /* Number of bytes of this mapping. */
+ size_t bytes;
+
+ /* Starting address of this mapping. */
+ s8 * start;
+
+ /* Ending address of this mapping. */
+ s8 * end;
+}
+gcsUSER_MAPPING;
+
+struct _gckOS
+{
+ /* Object. */
+ gcsOBJECT object;
+
+ /* Pointer to device */
+ gckGALDEVICE device;
+
+ /* Memory management */
+ void * memoryLock;
+ void * memoryMapLock;
+
+ struct _LINUX_MDL *mdlHead;
+ struct _LINUX_MDL *mdlTail;
+
+ /* Kernel process ID. */
+ u32 kernelProcessID;
+
+ /* Signal management. */
+ struct _signal
+ {
+ /* Unused signal ID number. */
+ int unused;
+
+ /* The pointer to the table. */
+ void ** table;
+
+ /* Signal table length. */
+ int tableLen;
+
+ /* The current unused signal ID. */
+ int currentID;
+
+ /* Lock. */
+ void * lock;
+ }
+ signal;
+
+ gcsUSER_MAPPING_PTR userMap;
+ void * debugLock;
+
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+ unsigned int cacheSize;
+ gcsNonPagedMemoryCache * cacheHead;
+ gcsNonPagedMemoryCache * cacheTail;
+#endif
+
+ /* workqueue for os timer. */
+ struct workqueue_struct * workqueue;
+};
+
+typedef struct _gcsSIGNAL * gcsSIGNAL_PTR;
+typedef struct _gcsSIGNAL
+{
+ /* Kernel sync primitive. */
+ struct completion obj;
+
+ /* Manual reset flag. */
+ int 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;
+ u32 *pageTable;
+}
+gcsPageInfo;
+
+typedef struct _gcsiDEBUG_REGISTERS * gcsiDEBUG_REGISTERS_PTR;
+typedef struct _gcsiDEBUG_REGISTERS
+{
+ char * module;
+ unsigned int index;
+ unsigned int shift;
+ unsigned int data;
+ unsigned int count;
+ u32 signature;
+}
+gcsiDEBUG_REGISTERS;
+
+typedef struct _gcsOSTIMER * gcsOSTIMER_PTR;
+typedef struct _gcsOSTIMER
+{
+ struct delayed_work work;
+ gctTIMERFUNCTION function;
+ void * data;
+} gcsOSTIMER;
+
+/******************************************************************************\
+******************************* Private Functions ******************************
+\******************************************************************************/
+
+static gceSTATUS
+_VerifyDMA(
+ IN gckOS Os,
+ IN gceCORE Core,
+ u32 *Address1,
+ u32 *Address2,
+ u32 *State1,
+ u32 *State2
+ )
+{
+ gceSTATUS status;
+ u32 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;
+ u32 select;
+ u32 data;
+ unsigned int 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_WriteRegisterEx(Os, gcvCORE_MAJOR, Descriptor->index, select));
+#if !gcdENABLE_RECOVERY
+ gcmkONERROR(gckOS_Delay(Os, 1000));
+#endif
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, gcvCORE_MAJOR, 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_WriteRegisterEx(Os, gcvCORE_MAJOR, Descriptor->index, select));
+#if !gcdENABLE_RECOVERY
+ gcmkONERROR(gckOS_Delay(Os, 1000));
+#endif
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, gcvCORE_MAJOR, 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,
+ IN gceCORE Core
+ )
+{
+ static const char *_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 const char *_cmdDmaState[] =
+ {
+ "CMD_IDLE_ST", "CMD_START_ST", "CMD_REQ_ST", "CMD_END_ST"
+ };
+
+ static const char *_cmdFetState[] =
+ {
+ "FET_IDLE_ST", "FET_RAMVALID_ST", "FET_VALID_ST"
+ };
+
+ static const char *_reqDmaState[] =
+ {
+ "REQ_IDLE_ST", "REQ_WAITIDX_ST", "REQ_CAL_ST"
+ };
+
+ static const char *_calState[] =
+ {
+ "CAL_IDLE_ST", "CAL_LDADR_ST", "CAL_IDXCALC_ST"
+ };
+
+ static const char *_veReqState[] =
+ {
+ "VER_IDLE_ST", "VER_CKCACHE_ST", "VER_MISS_ST"
+ };
+
+ static gcsiDEBUG_REGISTERS _dbgRegs[] =
+ {
+ { "RA", 0x474, 16, 0x448, 16, 0x12344321 },
+ { "TX", 0x474, 24, 0x44C, 16, 0x12211221 },
+ { "FE", 0x470, 0, 0x450, 16, 0xBABEF00D },
+ { "PE", 0x470, 16, 0x454, 16, 0xBABEF00D },
+ { "DE", 0x470, 8, 0x458, 16, 0xBABEF00D },
+ { "SH", 0x470, 24, 0x45C, 16, 0xDEADBEEF },
+ { "PA", 0x474, 0, 0x460, 16, 0x0000AAAA },
+ { "SE", 0x474, 8, 0x464, 16, 0x5E5E5E5E },
+ { "MC", 0x478, 0, 0x468, 16, 0x12345678 },
+ { "HI", 0x478, 8, 0x46C, 16, 0xAAAAAAAA }
+ };
+
+ static u32 _otherRegs[] =
+ {
+ 0x040, 0x044, 0x04C, 0x050, 0x054, 0x058, 0x05C, 0x060,
+ 0x43c, 0x440, 0x444, 0x414,
+ };
+
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ gckGALDEVICE device;
+ gckKERNEL kernel;
+ u32 idle, axi;
+ u32 dmaAddress1, dmaAddress2;
+ u32 dmaState1, dmaState2;
+ u32 dmaLow, dmaHigh;
+ u32 cmdState, cmdDmaState, cmdFetState;
+ u32 dmaReqState, calState, veReqState;
+ unsigned int i;
+
+ gcmkHEADER_ARG("Os=0x%X, Core=%d", Os, Core);
+
+ 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[Core];
+ gcmkPRINT_N(4, "Core = 0x%d\n",Core);
+
+ if (kernel == NULL)
+ {
+ gcmkFOOTER();
+ 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 < ARRAY_SIZE(_dbgRegs); i += 1)
+ {
+ gcmkONERROR(_DumpDebugRegisters(Os, &_dbgRegs[i]));
+ }
+
+ if (kernel->hardware->identity.chipFeatures & (1 << 4))
+ {
+ u32 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);
+ }
+
+ gcmkPRINT_N(0, " Other Registers:\n");
+ for (i = 0; i < ARRAY_SIZE(_otherRegs); i += 1)
+ {
+ u32 read;
+ gcmkONERROR(gckOS_ReadRegisterEx(Os, kernel->core, _otherRegs[i], &read));
+ gcmkPRINT_N(12, " [0x%04X] 0x%08X\n", _otherRegs[i], read);
+ }
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->debugLock));
+ }
+
+ /* Return the error. */
+ gcmkFOOTER();
+ return status;
+}
+
+static PLINUX_MDL
+_CreateMdl(
+ IN int ProcessID
+ )
+{
+ PLINUX_MDL mdl;
+
+ gcmkHEADER_ARG("ProcessID=%d", ProcessID);
+
+ mdl = (PLINUX_MDL)kmalloc(sizeof(struct _LINUX_MDL), GFP_KERNEL | __GFP_NOWARN);
+ if (mdl == NULL)
+ {
+ gcmkFOOTER_NO();
+ return NULL;
+ }
+
+ mdl->pid = ProcessID;
+ mdl->maps = NULL;
+ mdl->prev = NULL;
+ mdl->next = NULL;
+
+ 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 != NULL);
+
+ mdlMap = Mdl->maps;
+
+ while (mdlMap != NULL)
+ {
+ 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 int 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 | __GFP_NOWARN);
+ if (mdlMap == NULL)
+ {
+ gcmkFOOTER_NO();
+ return NULL;
+ }
+
+ mdlMap->pid = ProcessID;
+ mdlMap->vmaAddr = NULL;
+ mdlMap->vma = NULL;
+
+ 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 != NULL);
+ gcmkASSERT(Mdl->maps != NULL);
+
+ if (Mdl->maps == MdlMap)
+ {
+ Mdl->maps = MdlMap->next;
+ }
+ else
+ {
+ prevMdlMap = Mdl->maps;
+
+ while (prevMdlMap->next != MdlMap)
+ {
+ prevMdlMap = prevMdlMap->next;
+
+ gcmkASSERT(prevMdlMap != NULL);
+ }
+
+ prevMdlMap->next = MdlMap->next;
+ }
+
+ kfree(MdlMap);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+extern PLINUX_MDL_MAP
+FindMdlMap(
+ IN PLINUX_MDL Mdl,
+ IN int ProcessID
+ )
+{
+ PLINUX_MDL_MAP mdlMap;
+
+ gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID);
+ if(Mdl == NULL)
+ {
+ gcmkFOOTER_NO();
+ return NULL;
+ }
+ mdlMap = Mdl->maps;
+
+ while (mdlMap != NULL)
+ {
+ if (mdlMap->pid == ProcessID)
+ {
+ gcmkFOOTER_ARG("0x%X", mdlMap);
+ return mdlMap;
+ }
+
+ mdlMap = mdlMap->next;
+ }
+
+ gcmkFOOTER_NO();
+ return NULL;
+}
+
+static void
+_NonContiguousFree(
+ IN struct page ** Pages,
+ IN u32 NumPages
+ )
+{
+ int i;
+
+ gcmkHEADER_ARG("Pages=0x%X, NumPages=%d", Pages, NumPages);
+
+ gcmkASSERT(Pages != NULL);
+
+ for (i = 0; i < NumPages; i++)
+ {
+ __free_page(Pages[i]);
+ }
+
+ if (is_vmalloc_addr(Pages))
+ {
+ vfree(Pages);
+ }
+ else
+ {
+ kfree(Pages);
+ }
+
+ gcmkFOOTER_NO();
+}
+
+static struct page **
+_NonContiguousAlloc(
+ IN u32 NumPages
+ )
+{
+ struct page ** pages;
+ struct page *p;
+ int i, size;
+
+ gcmkHEADER_ARG("NumPages=%lu", NumPages);
+
+ if (NumPages > totalram_pages)
+ {
+ gcmkFOOTER_NO();
+ return NULL;
+ }
+
+ size = NumPages * sizeof(struct page *);
+
+ pages = kmalloc(size, GFP_KERNEL | __GFP_NOWARN);
+
+ if (!pages)
+ {
+ pages = vmalloc(size);
+
+ if (!pages)
+ {
+ gcmkFOOTER_NO();
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < NumPages; i++)
+ {
+ p = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_NOWARN);
+
+ if (!p)
+ {
+ _NonContiguousFree(pages, i);
+ gcmkFOOTER_NO();
+ return NULL;
+ }
+
+ pages[i] = p;
+ }
+
+ gcmkFOOTER_ARG("pages=0x%X", pages);
+ return pages;
+}
+
+static inline struct page *
+_NonContiguousToPage(
+ IN struct page ** Pages,
+ IN u32 Index
+ )
+{
+ gcmkASSERT(Pages != NULL);
+ return Pages[Index];
+}
+
+static inline unsigned long
+_NonContiguousToPfn(
+ IN struct page ** Pages,
+ IN u32 Index
+ )
+{
+ gcmkASSERT(Pages != NULL);
+ return page_to_pfn(_NonContiguousToPage(Pages, Index));
+}
+
+static inline unsigned long
+_NonContiguousToPhys(
+ IN struct page ** Pages,
+ IN u32 Index
+ )
+{
+ gcmkASSERT(Pages != NULL);
+ return page_to_phys(_NonContiguousToPage(Pages, Index));
+}
+
+
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+
+static int
+_AddNonPagedMemoryCache(
+ gckOS Os,
+#ifndef NO_DMA_COHERENT
+ int Size,
+ char *Addr,
+ dma_addr_t DmaHandle
+#else
+ long Order,
+ struct page * Page
+#endif
+ )
+{
+ gcsNonPagedMemoryCache *cache;
+
+ if (Os->cacheSize >= gcdUSE_NON_PAGED_MEMORY_CACHE)
+ {
+ return gcvFALSE;
+ }
+
+ /* Allocate the cache record */
+ cache = (gcsNonPagedMemoryCache *)kmalloc(sizeof(gcsNonPagedMemoryCache), GFP_ATOMIC);
+
+ if (cache == NULL) return gcvFALSE;
+
+#ifndef NO_DMA_COHERENT
+ cache->size = Size;
+ cache->addr = Addr;
+ cache->dmaHandle = DmaHandle;
+#else
+ cache->order = Order;
+ cache->page = Page;
+#endif
+
+ /* Add to list */
+ if (Os->cacheHead == NULL)
+ {
+ cache->prev = NULL;
+ cache->next = NULL;
+ Os->cacheHead =
+ Os->cacheTail = cache;
+ }
+ else
+ {
+ /* Add to the tail. */
+ cache->prev = Os->cacheTail;
+ cache->next = NULL;
+ Os->cacheTail->next = cache;
+ Os->cacheTail = cache;
+ }
+
+ Os->cacheSize++;
+
+ return gcvTRUE;
+}
+
+#ifndef NO_DMA_COHERENT
+static char *
+_GetNonPagedMemoryCache(
+ gckOS Os,
+ int Size,
+ dma_addr_t * DmaHandle
+ )
+#else
+static struct page *
+_GetNonPagedMemoryCache(
+ gckOS Os,
+ long Order
+ )
+#endif
+{
+ gcsNonPagedMemoryCache *cache;
+#ifndef NO_DMA_COHERENT
+ char *addr;
+#else
+ struct page * page;
+#endif
+
+ if (Os->cacheHead == NULL) return NULL;
+
+ /* Find the right cache */
+ cache = Os->cacheHead;
+
+ while (cache != NULL)
+ {
+#ifndef NO_DMA_COHERENT
+ if (cache->size == Size) break;
+#else
+ if (cache->order == Order) break;
+#endif
+
+ cache = cache->next;
+ }
+
+ if (cache == NULL) return NULL;
+
+ /* Remove the cache from list */
+ if (cache == Os->cacheHead)
+ {
+ Os->cacheHead = cache->next;
+
+ if (Os->cacheHead == NULL)
+ {
+ Os->cacheTail = NULL;
+ }
+ }
+ else
+ {
+ cache->prev->next = cache->next;
+
+ if (cache == Os->cacheTail)
+ {
+ Os->cacheTail = cache->prev;
+ }
+ else
+ {
+ cache->next->prev = cache->prev;
+ }
+ }
+
+ /* Destroy cache */
+#ifndef NO_DMA_COHERENT
+ addr = cache->addr;
+ *DmaHandle = cache->dmaHandle;
+#else
+ page = cache->page;
+#endif
+
+ kfree(cache);
+
+ Os->cacheSize--;
+
+#ifndef NO_DMA_COHERENT
+ return addr;
+#else
+ return page;
+#endif
+}
+
+static void
+_FreeAllNonPagedMemoryCache(
+ gckOS Os
+ )
+{
+ gcsNonPagedMemoryCache *cache, *nextCache;
+
+ MEMORY_LOCK(Os);
+
+ cache = Os->cacheHead;
+
+ while (cache != NULL)
+ {
+ if (cache != Os->cacheTail)
+ {
+ nextCache = cache->next;
+ }
+ else
+ {
+ nextCache = NULL;
+ }
+
+ /* Remove the cache from list */
+ if (cache == Os->cacheHead)
+ {
+ Os->cacheHead = cache->next;
+
+ if (Os->cacheHead == NULL)
+ {
+ Os->cacheTail = NULL;
+ }
+ }
+ else
+ {
+ cache->prev->next = cache->next;
+
+ if (cache == Os->cacheTail)
+ {
+ Os->cacheTail = cache->prev;
+ }
+ else
+ {
+ cache->next->prev = cache->prev;
+ }
+ }
+
+#ifndef NO_DMA_COHERENT
+ dma_free_coherent(NULL,
+ cache->size,
+ cache->addr,
+ cache->dmaHandle);
+#else
+ free_pages((unsigned long)page_address(cache->page), cache->order);
+#endif
+
+ kfree(cache);
+
+ cache = nextCache;
+ }
+
+ MEMORY_UNLOCK(Os);
+}
+
+#endif /* gcdUSE_NON_PAGED_MEMORY_CACHE */
+
+/*******************************************************************************
+**
+** gckOS_Construct
+**
+** Construct a new gckOS object.
+**
+** INPUT:
+**
+** void *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 void *Context,
+ OUT gckOS * Os
+ )
+{
+ gckOS os;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Context=0x%X", Context);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Os != NULL);
+
+ /* Allocate the gckOS object. */
+ os = (gckOS) kmalloc(sizeof(struct _gckOS), GFP_KERNEL | __GFP_NOWARN);
+
+ if (os == NULL)
+ {
+ /* Out of memory. */
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+
+ /* Zero the memory. */
+ gckOS_ZeroMemory(os, sizeof(struct _gckOS));
+
+ /* Initialize the gckOS object. */
+ os->object.type = gcvOBJ_OS;
+
+ /* Set device device. */
+ os->device = Context;
+
+ /* 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));
+
+
+ os->mdlHead = os->mdlTail = NULL;
+
+ /* Get the kernel process ID. */
+ os->kernelProcessID = task_tgid_vnr(current);
+
+ /*
+ * 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(sizeof(void *) * USER_SIGNAL_TABLE_LEN_INIT, GFP_KERNEL | __GFP_NOWARN);
+
+ if (os->signal.table == NULL)
+ {
+ /* Out of memory. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ gckOS_ZeroMemory(os->signal.table,
+ sizeof(void *) * 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;
+
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+ os->cacheSize = 0;
+ os->cacheHead = NULL;
+ os->cacheTail = NULL;
+#endif
+
+ /* Create a workqueue for os timer. */
+ os->workqueue = create_singlethread_workqueue("galcore workqueue");
+
+ if (os->workqueue == NULL)
+ {
+ /* Out of memory. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ /* 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 != NULL)
+ {
+ kfree(os->signal.table);
+ }
+
+ if (os->signal.lock != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->signal.lock));
+ }
+
+ if (os->memoryMapLock != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->memoryMapLock));
+ }
+
+ if (os->memoryLock != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->memoryLock));
+ }
+
+ if (os->debugLock != NULL)
+ {
+ gcmkVERIFY_OK(
+ gckOS_DeleteMutex(os, os->debugLock));
+ }
+
+ if (os->workqueue != NULL)
+ {
+ destroy_workqueue(os->workqueue);
+ }
+
+ 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
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+ _FreeAllNonPagedMemoryCache(Os);
+#endif
+
+ /*
+ * Destroy the signal manager.
+ */
+
+ /* Destroy the mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->signal.lock));
+
+ /* Free the signal table. */
+ kfree(Os->signal.table);
+
+ /* 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));
+
+ /* Wait for all works done. */
+ flush_workqueue(Os->workqueue);
+
+ /* Destory work queue. */
+ destroy_workqueue(Os->workqueue);
+
+ /* 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;
+}
+
+#ifdef NO_DMA_COHERENT
+static char *
+_CreateKernelVirtualMapping(
+ IN struct page * Page,
+ IN int NumPages
+ )
+{
+ char *addr = 0;
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ addr = page_address(Page);
+#else
+ struct page ** pages;
+ int i;
+
+ pages = kmalloc(sizeof(struct page *) * NumPages, GFP_KERNEL | __GFP_NOWARN);
+
+ if (!pages)
+ {
+ return NULL;
+ }
+
+ for (i = 0; i < NumPages; i++)
+ {
+ pages[i] = nth_page(Page, i);
+ }
+
+ /* ioremap() can't work on system memory since 2.6.38. */
+ addr = vmap(pages, NumPages, 0, gcmkNONPAGED_MEMROY_PROT(PAGE_KERNEL));
+
+ kfree(pages);
+#endif
+
+ return addr;
+}
+
+static void
+_DestoryKernelVirtualMapping(
+ IN char *Addr
+ )
+{
+#if !gcdNONPAGED_MEMORY_CACHEABLE
+ vunmap(Addr);
+#endif
+}
+#endif
+
+/*******************************************************************************
+**
+** gckOS_Allocate
+**
+** Allocate memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** size_t Bytes
+** Number of bytes to allocate.
+**
+** OUTPUT:
+**
+** void ** Memory
+** Pointer to a variable that will hold the allocated memory location.
+*/
+gceSTATUS
+gckOS_Allocate(
+ IN gckOS Os,
+ IN size_t Bytes,
+ OUT void **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 != NULL);
+
+ 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.
+**
+** void *Memory
+** Pointer to memory allocation to free.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_Free(
+ IN gckOS Os,
+ IN void *Memory
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Memory=0x%X", Os, Memory);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Memory != NULL);
+
+ gcmkONERROR(gckOS_FreeMemory(Os, Memory));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_AllocateMemory
+**
+** Allocate memory wrapper.
+**
+** INPUT:
+**
+** size_t Bytes
+** Number of bytes to allocate.
+**
+** OUTPUT:
+**
+** void ** Memory
+** Pointer to a variable that will hold the allocated memory location.
+*/
+gceSTATUS
+gckOS_AllocateMemory(
+ IN gckOS Os,
+ IN size_t Bytes,
+ OUT void **Memory
+ )
+{
+ void *memory;
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Memory != NULL);
+
+ if (Bytes > PAGE_SIZE)
+ {
+ memory = (void *) vmalloc(Bytes);
+ }
+ else
+ {
+ memory = (void *) kmalloc(Bytes, GFP_KERNEL | __GFP_NOWARN);
+ }
+
+ if (memory == NULL)
+ {
+ /* 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:
+**
+** void *Memory
+** Pointer to memory allocation to free.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_FreeMemory(
+ IN gckOS Os,
+ IN void *Memory
+ )
+{
+ gcmkHEADER_ARG("Memory=0x%X", Memory);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Memory != NULL);
+
+ /* Free the memory from the OS pool. */
+ if (is_vmalloc_addr(Memory))
+ {
+ vfree(Memory);
+ }
+ else
+ {
+ 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.
+**
+** size_t Bytes
+** Number of bytes to map.
+**
+** OUTPUT:
+**
+** void ** 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 size_t Bytes,
+ OUT void **Logical
+ )
+{
+ PLINUX_MDL_MAP mdlMap;
+ PLINUX_MDL mdl = (PLINUX_MDL)Physical;
+ long populate;
+
+ 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 != NULL);
+
+ MEMORY_LOCK(Os);
+
+ mdlMap = FindMdlMap(mdl, task_tgid_vnr(current));
+
+ if (mdlMap == NULL)
+ {
+ mdlMap = _CreateMdlMap(mdl, task_tgid_vnr(current));
+
+ if (mdlMap == NULL)
+ {
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+ }
+
+ if (mdlMap->vmaAddr == NULL)
+ {
+ down_write(&current->mm->mmap_sem);
+
+ mdlMap->vmaAddr = (char *)do_mmap_pgoff(NULL,
+ 0L,
+ mdl->numPages * PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 0, &populate);
+
+ 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 = NULL;
+
+ 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 = NULL;
+
+ 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(Os->device->dev,
+ 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 = NULL;
+
+ 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 = gcmkPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot);
+ mdlMap->vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP;
+# 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 = NULL;
+
+ 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.
+**
+** size_t Bytes
+** Number of bytes to unmap.
+**
+** void *Memory
+** Pointer to a previously mapped memory region.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN void *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 != NULL);
+
+ gckOS_UnmapMemoryEx(Os, Physical, Bytes, Logical, task_tgid_vnr(current));
+
+ /* 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.
+**
+** size_t Bytes
+** Number of bytes to unmap.
+**
+** void *Memory
+** Pointer to a previously mapped memory region.
+**
+** u32 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 size_t Bytes,
+ IN void *Logical,
+ IN u32 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 != NULL);
+ gcmkVERIFY_ARGUMENT(PID != 0);
+
+ MEMORY_LOCK(Os);
+
+ if (Logical)
+ {
+ mdlMap = FindMdlMap(mdl, PID);
+
+ if (mdlMap == NULL || mdlMap->vmaAddr == NULL)
+ {
+ 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 = pid_task(find_vpid(mdlMap->pid), PIDTYPE_PID);
+
+ if (task != NULL && task->mm != NULL)
+ {
+ 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.
+**
+** int InUserSpace
+** gcvTRUE if the pages need to be mapped into user space.
+**
+** size_t * Bytes
+** Pointer to a variable that holds the number of bytes to allocate.
+**
+** OUTPUT:
+**
+** size_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.
+**
+** void ** Logical
+** Pointer to a variable that will hold the logical address of the
+** allocation.
+*/
+gceSTATUS
+gckOS_AllocateNonPagedMemory(
+ IN gckOS Os,
+ IN int InUserSpace,
+ IN OUT size_t * Bytes,
+ OUT gctPHYS_ADDR * Physical,
+ OUT void **Logical
+ )
+{
+ size_t bytes;
+ int numPages;
+ PLINUX_MDL mdl = NULL;
+ PLINUX_MDL_MAP mdlMap = NULL;
+ char *addr;
+#ifdef NO_DMA_COHERENT
+ struct page * page;
+ long size, order;
+ void *vaddr;
+#endif
+ int locked = gcvFALSE;
+ gceSTATUS status;
+ long populate;
+
+ 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 != NULL);
+ gcmkVERIFY_ARGUMENT(*Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Physical != NULL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+
+ /* 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(task_tgid_vnr(current));
+ if (mdl == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ mdl->pagedMem = 0;
+ mdl->numPages = numPages;
+
+ MEMORY_LOCK(Os);
+ locked = gcvTRUE;
+
+#ifndef NO_DMA_COHERENT
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+ addr = _GetNonPagedMemoryCache(Os,
+ mdl->numPages * PAGE_SIZE,
+ &mdl->dmaHandle);
+
+ if (addr == NULL)
+#endif
+ {
+ addr = dma_alloc_coherent(Os->device->dev,
+ mdl->numPages * PAGE_SIZE,
+ &mdl->dmaHandle,
+ GFP_KERNEL | __GFP_NOWARN);
+ }
+#else
+ size = mdl->numPages * PAGE_SIZE;
+ order = get_order(size);
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+ page = _GetNonPagedMemoryCache(Os, order);
+
+ if (page == NULL)
+#endif
+ {
+ page = alloc_pages(GFP_KERNEL | __GFP_NOWARN, order);
+ }
+
+ if (page == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ vaddr = (void *)page_address(page);
+ addr = _CreateKernelVirtualMapping(page, mdl->numPages);
+ mdl->dmaHandle = virt_to_phys(vaddr);
+ mdl->kaddr = vaddr;
+ mdl->u.contiguousPages = page;
+
+ /* Cache invalidate. */
+ dma_sync_single_for_device(
+ NULL,
+ 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 == NULL)
+ {
+ 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;
+
+ /* Return allocated memory. */
+ *Bytes = bytes;
+ *Physical = (gctPHYS_ADDR) mdl;
+
+ if (InUserSpace)
+ {
+ mdlMap = _CreateMdlMap(mdl, task_tgid_vnr(current));
+
+ if (mdlMap == NULL)
+ {
+ 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 = (char *) do_mmap_pgoff(NULL,
+ 0L,
+ mdl->numPages * PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 0, &populate);
+
+ if (IS_ERR(mdlMap->vmaAddr))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_WARNING, gcvZONE_OS,
+ "%s(%d): do_mmap_pgoff error",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = NULL;
+
+ up_write(&current->mm->mmap_sem);
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr);
+
+ if (mdlMap->vma == NULL)
+ {
+ 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(Os->device->dev,
+ 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 = gcmkNONPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot);
+ mdlMap->vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP;
+ 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 = (void *)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 != NULL)
+ {
+ /* Free LINUX_MDL_MAP. */
+ gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap));
+ }
+
+ if (mdl != NULL)
+ {
+ /* 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.
+**
+** size_t Bytes
+** Number of bytes allocated.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocated memory.
+**
+** void *Logical
+** Logical address of the allocated memory.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS gckOS_FreeNonPagedMemory(
+ IN gckOS Os,
+ IN size_t Bytes,
+ IN gctPHYS_ADDR Physical,
+ IN void *Logical
+ )
+{
+ PLINUX_MDL mdl;
+ PLINUX_MDL_MAP mdlMap;
+ struct task_struct * task;
+#ifdef NO_DMA_COHERENT
+ unsigned size;
+ void *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 != NULL);
+
+ /* Convert physical address into a pointer to a MDL. */
+ mdl = (PLINUX_MDL) Physical;
+
+ MEMORY_LOCK(Os);
+
+#ifndef NO_DMA_COHERENT
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+ if (!_AddNonPagedMemoryCache(Os,
+ mdl->numPages * PAGE_SIZE,
+ mdl->addr,
+ mdl->dmaHandle))
+#endif
+ {
+ dma_free_coherent(Os->device->dev,
+ 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;
+ }
+
+#if gcdUSE_NON_PAGED_MEMORY_CACHE
+ if (!_AddNonPagedMemoryCache(Os,
+ get_order(mdl->numPages * PAGE_SIZE),
+ virt_to_page(mdl->kaddr)))
+#endif
+ {
+ free_pages((unsigned long)mdl->kaddr, get_order(mdl->numPages * PAGE_SIZE));
+ }
+
+ _DestoryKernelVirtualMapping(mdl->addr);
+#endif /* NO_DMA_COHERENT */
+
+ mdlMap = mdl->maps;
+
+ while (mdlMap != NULL)
+ {
+ if (mdlMap->vmaAddr != NULL)
+ {
+ /* Get the current pointer for the task with stored pid. */
+ task = pid_task(find_vpid(mdlMap->pid), PIDTYPE_PID);
+
+ if (task != NULL && task->mm != NULL)
+ {
+ 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 = NULL;
+ }
+
+ mdlMap = mdlMap->next;
+ }
+
+ /* Remove the node from global list.. */
+ if (mdl == Os->mdlHead)
+ {
+ if ((Os->mdlHead = mdl->next) == NULL)
+ {
+ Os->mdlTail = NULL;
+ }
+ }
+ 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_ReadRegisterEx
+**
+** Read data from a register.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** u32 Address
+** Address of register.
+**
+** OUTPUT:
+**
+** u32 * Data
+** Pointer to a variable that receives the data read from the register.
+*/
+gceSTATUS
+gckOS_ReadRegisterEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN u32 Address,
+ OUT u32 * 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 != NULL);
+
+ *Data = readl((u8 *)Os->device->registerBases[Core] + Address);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Data=0x%08x", *Data);
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_WriteRegisterEx
+**
+** Write data to a register.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** u32 Address
+** Address of register.
+**
+** u32 Data
+** Data for register.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_WriteRegisterEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN u32 Address,
+ IN u32 Data
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X Data=0x%08x", Os, Core, Address, Data);
+
+ writel(Data, (u8 *)Os->device->registerBases[Core] + Address);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+#if gcdSECURE_USER
+static gceSTATUS
+gckOS_AddMapping(
+ IN gckOS Os,
+ IN u32 Physical,
+ IN void *Logical,
+ IN size_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,
+ sizeof(gcsUSER_MAPPING),
+ (void **) &map));
+
+ map->next = Os->userMap;
+ map->physical = Physical - Os->device->baseAddress;
+ map->logical = Logical;
+ map->bytes = Bytes;
+ map->start = (s8 *) 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 void *Logical,
+ IN size_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 = NULL; map != NULL; map = map->next)
+ {
+ if ((map->logical == Logical)
+ && (map->bytes == Bytes)
+ )
+ {
+ break;
+ }
+
+ prev = map;
+ }
+
+ if (map == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_ADDRESS);
+ }
+
+ if (prev == NULL)
+ {
+ 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 void *Logical,
+ IN u32 ProcessID,
+ IN PLINUX_MDL Mdl,
+ OUT u32 *Physical
+ )
+{
+ s8 *base, *vBase;
+ u32 offset;
+ PLINUX_MDL_MAP map;
+ gcsUSER_MAPPING_PTR userMap;
+
+ base = (Mdl == NULL) ? NULL : (s8 *) Mdl->addr;
+
+ /* Check for the logical address match. */
+ if ((base != NULL)
+ && ((s8 *) Logical >= base)
+ && ((s8 *) Logical < base + Mdl->numPages * PAGE_SIZE)
+ )
+ {
+ offset = (s8 *) Logical - base;
+
+ if (Mdl->dmaHandle != 0)
+ {
+ /* The memory was from coherent area. */
+ *Physical = (u32) Mdl->dmaHandle + offset;
+ }
+ else if (Mdl->pagedMem && !Mdl->contiguous)
+ {
+ /* paged memory is not mapped to kernel space. */
+ return gcvSTATUS_INVALID_ADDRESS;
+ }
+ else
+ {
+ *Physical = gcmPTR2INT(virt_to_phys(base)) + offset;
+ }
+
+ return gcvSTATUS_OK;
+ }
+
+ /* Walk user maps. */
+ for (userMap = Os->userMap; userMap != NULL; userMap = userMap->next)
+ {
+ if (((s8 *) Logical >= userMap->start)
+ && ((s8 *) Logical < userMap->end)
+ )
+ {
+ *Physical = userMap->physical
+ + (u32) ((s8 *) Logical - userMap->start);
+
+ return gcvSTATUS_OK;
+ }
+ }
+
+ if (ProcessID != Os->kernelProcessID)
+ {
+ map = FindMdlMap(Mdl, (int) ProcessID);
+ vBase = (map == NULL) ? NULL : (s8 *) map->vmaAddr;
+
+ /* Is the given address within that range. */
+ if ((vBase != NULL)
+ && ((s8 *) Logical >= vBase)
+ && ((s8 *) Logical < vBase + Mdl->numPages * PAGE_SIZE)
+ )
+ {
+ offset = (s8 *) Logical - vBase;
+
+ if (Mdl->dmaHandle != 0)
+ {
+ /* The memory was from coherent area. */
+ *Physical = (u32) Mdl->dmaHandle + offset;
+ }
+ else if (Mdl->pagedMem && !Mdl->contiguous)
+ {
+ *Physical = _NonContiguousToPhys(Mdl->u.nonContiguousPages, offset/PAGE_SIZE);
+ }
+ else
+ {
+ *Physical = page_to_phys(Mdl->u.contiguousPages) + 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.
+**
+** void *Logical
+** Logical address.
+**
+** u32 ProcessID
+** Process ID.
+**
+** OUTPUT:
+**
+** u32 * Address
+** Poinetr to a variable that receives the 32-bit physical adress.
+*/
+static gceSTATUS
+gckOS_GetPhysicalAddressProcess(
+ IN gckOS Os,
+ IN void *Logical,
+ IN u32 ProcessID,
+ OUT u32 * Address
+ )
+{
+ PLINUX_MDL mdl;
+ s8 *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 != NULL);
+
+ MEMORY_LOCK(Os);
+
+ /* First try the contiguous memory pool. */
+ if (Os->device->contiguousMapped)
+ {
+ base = (s8 *) Os->device->contiguousBase;
+
+ if (((s8 *) Logical >= base)
+ && ((s8 *) Logical < base + Os->device->contiguousSize)
+ )
+ {
+ /* Convert logical address into physical. */
+ *Address = Os->device->contiguousVidMem->baseAddress
+ + (s8 *) 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 != NULL; 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_GetPhysicalAddress
+**
+** Get the physical system address of a corresponding virtual address.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** void *Logical
+** Logical address.
+**
+** OUTPUT:
+**
+** u32 * Address
+** Poinetr to a variable that receives the 32-bit physical adress.
+*/
+gceSTATUS
+gckOS_GetPhysicalAddress(
+ IN gckOS Os,
+ IN void *Logical,
+ OUT u32 * Address
+ )
+{
+ gceSTATUS status;
+ u32 processID;
+
+ gcmkHEADER_ARG("Os=0x%X Logical=0x%X", Os, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Address != NULL);
+
+ /* Get current process ID. */
+ processID = task_tgid_vnr(current);
+
+ /* 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;
+}
+
+/*******************************************************************************
+**
+** gckOS_MapPhysical
+**
+** Map a physical address into kernel space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** u32 Physical
+** Physical address of the memory to map.
+**
+** size_t Bytes
+** Number of bytes to map.
+**
+** OUTPUT:
+**
+** void ** Logical
+** Pointer to a variable that receives the base address of the mapped
+** memory.
+*/
+gceSTATUS
+gckOS_MapPhysical(
+ IN gckOS Os,
+ IN u32 Physical,
+ IN size_t Bytes,
+ OUT void **Logical
+ )
+{
+ void *logical;
+ PLINUX_MDL mdl;
+ u32 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 != NULL);
+
+ 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 != NULL)
+ {
+ 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 == NULL)
+ {
+ /* Map memory as cached memory. */
+ request_mem_region(physical, Bytes, "MapRegion");
+ logical = (void *) ioremap_nocache(physical, Bytes);
+
+ if (logical == NULL)
+ {
+ 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.
+**
+** void *Logical
+** Pointer to the base address of the memory to unmap.
+**
+** size_t Bytes
+** Number of bytes to unmap.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapPhysical(
+ IN gckOS Os,
+ IN void *Logical,
+ IN size_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 != NULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ MEMORY_LOCK(Os);
+
+ mdl = Os->mdlHead;
+
+ while (mdl != NULL)
+ {
+ if (mdl->addr != NULL)
+ {
+ if (Logical >= (void *)mdl->addr
+ && Logical < (void *)((char *)mdl->addr + mdl->numPages * PAGE_SIZE))
+ {
+ break;
+ }
+ }
+
+ mdl = mdl->next;
+ }
+
+ if (mdl == NULL)
+ {
+ /* 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:
+**
+** void ** Mutex
+** Pointer to a variable that will hold a pointer to the mutex.
+*/
+gceSTATUS
+gckOS_CreateMutex(
+ IN gckOS Os,
+ OUT void **Mutex
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Validate the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Mutex != NULL);
+
+ /* Allocate a FAST_MUTEX structure. */
+ *Mutex = (void *)kmalloc(sizeof(struct semaphore), GFP_KERNEL | __GFP_NOWARN);
+
+ if (*Mutex == NULL)
+ {
+ 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.
+**
+** void *Mutex
+** Pointer to the mute to be deleted.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DeleteMutex(
+ IN gckOS Os,
+ IN void *Mutex
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Mutex=0x%X", Os, Mutex);
+
+ /* Validate the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Mutex != NULL);
+
+ /* Delete the fast mutex. */
+ kfree(Mutex);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AcquireMutex
+**
+** Acquire a mutex.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** void *Mutex
+** Pointer to the mutex to be acquired.
+**
+** u32 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 void *Mutex,
+ IN u32 Timeout
+ )
+{
+#if gcdDETECT_TIMEOUT
+ u32 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 != NULL);
+
+#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)
+ {
+ u32 dmaAddress1, dmaAddress2;
+ u32 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, gcvCORE_MAJOR));
+
+ 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.
+**
+** void *Mutex
+** Pointer to the mutex to be released.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_ReleaseMutex(
+ IN gckOS Os,
+ IN void *Mutex
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Mutex=0x%0x", Os, Mutex);
+
+ /* Validate the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Mutex != NULL);
+
+ /* 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 s32 *Target
+** Pointer to the 32-bit value to exchange.
+**
+** IN s32 NewValue
+** Specifies a new value for the 32-bit value pointed to by Target.
+**
+** OUT s32 *OldValue
+** The old value of the 32-bit value pointed to by Target.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomicExchange(
+ IN gckOS Os,
+ IN OUT u32 *Target,
+ IN u32 NewValue,
+ OUT u32 *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 = (u32) atomic_xchg((atomic_t *) Target, (int) NewValue);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*OldValue=%u", *OldValue);
+ return gcvSTATUS_OK;
+}
+
+#ifdef CONFIG_SMP
+/*******************************************************************************
+**
+** gckOS_AtomicSetMask
+**
+** Atomically set mask to Atom
+**
+** INPUT:
+** IN OUT void *Atom
+** Pointer to the atom to set.
+**
+** IN u32 Mask
+** Mask to set.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomSetMask(
+ IN OUT void *Atom,
+ IN u32 Mask
+ )
+{
+ u32 oval, nval;
+
+ gcmkHEADER_ARG("Atom=0x%0x", Atom);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ do
+ {
+ oval = atomic_read((atomic_t *) Atom);
+ nval = oval | Mask;
+ } while (atomic_cmpxchg((atomic_t *) Atom, oval, nval) != oval);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AtomClearMask
+**
+** Atomically clear mask from Atom
+**
+** INPUT:
+** IN OUT void *Atom
+** Pointer to the atom to clear.
+**
+** IN u32 Mask
+** Mask to clear.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomClearMask(
+ IN OUT void *Atom,
+ IN u32 Mask
+ )
+{
+ u32 oval, nval;
+
+ gcmkHEADER_ARG("Atom=0x%0x", Atom);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ do
+ {
+ oval = atomic_read((atomic_t *) Atom);
+ nval = oval & ~Mask;
+ } while (atomic_cmpxchg((atomic_t *) Atom, oval, nval) != oval);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+#endif
+
+/*******************************************************************************
+**
+** gckOS_AtomConstruct
+**
+** Create an atom.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** OUTPUT:
+**
+** void ** Atom
+** Pointer to a variable receiving the constructed atom.
+*/
+gceSTATUS
+gckOS_AtomConstruct(
+ IN gckOS Os,
+ OUT void **Atom
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ /* Allocate the atom. */
+ gcmkONERROR(gckOS_Allocate(Os, sizeof(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.
+**
+** void *Atom
+** Pointer to the atom to destroy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomDestroy(
+ IN gckOS Os,
+ IN void *Atom
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%x Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ /* 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.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** s32 *Value
+** Pointer to a variable the receives the value of the atom.
+*/
+gceSTATUS
+gckOS_AtomGet(
+ IN gckOS Os,
+ IN void *Atom,
+ OUT s32 *Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ /* 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.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** s32 Value
+** The value of the atom.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AtomSet(
+ IN gckOS Os,
+ IN void *Atom,
+ IN s32 Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x Value=%d", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ /* 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.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** s32 *Value
+** Pointer to a variable that receives the original value of the atom.
+*/
+gceSTATUS
+gckOS_AtomIncrement(
+ IN gckOS Os,
+ IN void *Atom,
+ OUT s32 *Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ /* 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.
+**
+** void *Atom
+** Pointer to the atom.
+**
+** OUTPUT:
+**
+** s32 *Value
+** Pointer to a variable that receives the original value of the atom.
+*/
+gceSTATUS
+gckOS_AtomDecrement(
+ IN gckOS Os,
+ IN void *Atom,
+ OUT s32 *Value
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Atom != NULL);
+
+ /* 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.
+**
+** u32 Delay
+** Delay to sleep, specified in milliseconds.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_Delay(
+ IN gckOS Os,
+ IN u32 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:
+**
+** u32 *Time
+** Pointer to a variable to get time.
+**
+*/
+gceSTATUS
+gckOS_GetTicks(
+ OUT u32 *Time
+ )
+{
+ gcmkHEADER();
+
+ *Time = jiffies * 1000 / HZ;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_TicksAfter
+**
+** Compare time values got from gckOS_GetTicks.
+**
+** INPUT:
+** u32 Time1
+** First time value to be compared.
+**
+** u32 Time2
+** Second time value to be compared.
+**
+** OUTPUT:
+**
+** int *IsAfter
+** Pointer to a variable to result.
+**
+*/
+gceSTATUS
+gckOS_TicksAfter(
+ IN u32 Time1,
+ IN u32 Time2,
+ OUT int *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:
+**
+** u64 *Time
+** Pointer to a variable to get time.
+**
+*/
+gceSTATUS
+gckOS_GetTime(
+ OUT u64 *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.
+**
+** void *Address
+** Address of memory that needs to be barriered.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_MemoryBarrier(
+ IN gckOS Os,
+ IN void *Address
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Address=0x%X", Os, Address);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+#if defined(CONFIG_MIPS)
+ iob();
+#else
+ mb();
+#endif
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_AllocatePagedMemoryEx
+**
+** Allocate memory from the paged pool.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** int Contiguous
+** Need contiguous memory or not.
+**
+** size_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 int Contiguous,
+ IN size_t Bytes,
+ OUT gctPHYS_ADDR * Physical
+ )
+{
+ int numPages;
+ int i;
+ PLINUX_MDL mdl = NULL;
+ size_t bytes;
+ int 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 != NULL);
+
+ bytes = gcmALIGN(Bytes, PAGE_SIZE);
+
+ numPages = GetPageCount(bytes, 0);
+
+ MEMORY_LOCK(Os);
+ locked = gcvTRUE;
+
+ mdl = _CreateMdl(task_tgid_vnr(current));
+ if (mdl == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ if (Contiguous)
+ {
+ /* Get contiguous pages, and suppress warning (stack dump) from kernel when
+ we run out of memory. */
+ mdl->u.contiguousPages =
+ alloc_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_NO_KSWAPD, GetOrder(numPages));
+
+ if (mdl->u.contiguousPages == NULL)
+ {
+ mdl->u.contiguousPages =
+ alloc_pages(GFP_KERNEL | __GFP_HIGHMEM | __GFP_NOWARN | __GFP_NO_KSWAPD, GetOrder(numPages));
+ }
+ }
+ else
+ {
+ mdl->u.nonContiguousPages = _NonContiguousAlloc(numPages);
+ }
+
+ if (mdl->u.contiguousPages == NULL && mdl->u.nonContiguousPages == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ mdl->dmaHandle = 0;
+ mdl->addr = 0;
+ mdl->numPages = numPages;
+ mdl->pagedMem = 1;
+ mdl->contiguous = Contiguous;
+
+ for (i = 0; i < mdl->numPages; i++)
+ {
+ struct page *page;
+
+ if (mdl->contiguous)
+ {
+ page = nth_page(mdl->u.contiguousPages, i);
+ }
+ else
+ {
+ page = _NonContiguousToPage(mdl->u.nonContiguousPages, i);
+ }
+
+ SetPageReserved(page);
+
+ if (!PageHighMem(page) && page_to_phys(page))
+ {
+ gcmkVERIFY_OK(
+ gckOS_CacheFlush(Os, task_tgid_vnr(current), NULL,
+ (void *)page_to_phys(page),
+ page_address(page),
+ PAGE_SIZE));
+ }
+ }
+
+ /* 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 != NULL)
+ {
+ /* 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.
+**
+** size_t Bytes
+** Number of bytes of the allocation.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_FreePagedMemory(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes
+ )
+{
+ PLINUX_MDL mdl = (PLINUX_MDL) Physical;
+ int 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 != NULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ /*addr = mdl->addr;*/
+
+ MEMORY_LOCK(Os);
+
+ for (i = 0; i < mdl->numPages; i++)
+ {
+ if (mdl->contiguous)
+ {
+ ClearPageReserved(nth_page(mdl->u.contiguousPages, i));
+ }
+ else
+ {
+ ClearPageReserved(_NonContiguousToPage(mdl->u.nonContiguousPages, i));
+ }
+ }
+
+ if (mdl->contiguous)
+ {
+ __free_pages(mdl->u.contiguousPages, GetOrder(mdl->numPages));
+ }
+ else
+ {
+ _NonContiguousFree(mdl->u.nonContiguousPages, mdl->numPages);
+ }
+
+ /* Remove the node from global list. */
+ if (mdl == Os->mdlHead)
+ {
+ if ((Os->mdlHead = mdl->next) == NULL)
+ {
+ Os->mdlTail = NULL;
+ }
+ }
+ 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.
+**
+** size_t Bytes
+** Number of bytes of the allocation.
+**
+** int Cacheable
+** Cache mode of mapping.
+**
+** OUTPUT:
+**
+** void ** Logical
+** Pointer to a variable that receives the address of the mapped
+** memory.
+**
+** size_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 size_t Bytes,
+ IN int Cacheable,
+ OUT void **Logical,
+ OUT size_t * PageCount
+ )
+{
+ PLINUX_MDL mdl;
+ PLINUX_MDL_MAP mdlMap;
+ char * addr;
+ unsigned long start;
+ unsigned long pfn;
+ int i;
+ long populate;
+
+ gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Logical);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Physical != NULL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+ gcmkVERIFY_ARGUMENT(PageCount != NULL);
+
+ mdl = (PLINUX_MDL) Physical;
+
+ MEMORY_LOCK(Os);
+
+ mdlMap = FindMdlMap(mdl, task_tgid_vnr(current));
+
+ if (mdlMap == NULL)
+ {
+ mdlMap = _CreateMdlMap(mdl, task_tgid_vnr(current));
+
+ if (mdlMap == NULL)
+ {
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY);
+ return gcvSTATUS_OUT_OF_MEMORY;
+ }
+ }
+
+ if (mdlMap->vmaAddr == NULL)
+ {
+ down_write(&current->mm->mmap_sem);
+
+ mdlMap->vmaAddr = (char *)do_mmap_pgoff(NULL,
+ 0L,
+ mdl->numPages * PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 0, &populate);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): vmaAddr->0x%X for phys_addr->0x%X",
+ __FUNCTION__, __LINE__,
+ (u32) mdlMap->vmaAddr,
+ (u32) 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 = NULL;
+
+ 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 == NULL)
+ {
+ up_write(&current->mm->mmap_sem);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): find_vma error",
+ __FUNCTION__, __LINE__
+ );
+
+ mdlMap->vmaAddr = NULL;
+
+ MEMORY_UNLOCK(Os);
+
+ gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_RESOURCES);
+ return gcvSTATUS_OUT_OF_RESOURCES;
+ }
+
+ mdlMap->vma->vm_flags |= VM_DONTDUMP;
+#if !gcdPAGED_MEMORY_CACHEABLE
+ if (Cacheable == gcvFALSE)
+ {
+ /* Make this mapping non-cached. */
+ mdlMap->vma->vm_page_prot = gcmkPAGED_MEMROY_PROT(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,
+ page_to_pfn(mdl->u.contiguousPages),
+ 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 = NULL;
+
+ 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 = _NonContiguousToPfn(mdl->u.nonContiguousPages, i);
+
+ 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__,
+ (u32) Physical,
+ (u32) *Logical,
+ (u32) addr,
+ (u32) start
+ );
+
+ mdlMap->vmaAddr = NULL;
+
+ 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 != NULL 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_MapPagesEx
+**
+** Map paged memory into a page table.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctPHYS_ADDR Physical
+** Physical address of the allocation.
+**
+** size_t PageCount
+** Number of pages required for the physical address.
+**
+** void *PageTable
+** Pointer to the page table to fill in.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_MapPagesEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN gctPHYS_ADDR Physical,
+ IN size_t PageCount,
+ IN void *PageTable
+ )
+{
+ gceSTATUS status = gcvSTATUS_OK;
+ PLINUX_MDL mdl;
+ u32* table;
+ u32 offset;
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ gckMMU mmu;
+ PLINUX_MDL mmuMdl;
+ u32 bytes;
+ gctPHYS_ADDR pageTablePhysical;
+#endif
+
+ 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 != NULL);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+ gcmkVERIFY_ARGUMENT(PageTable != NULL);
+
+ /* 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__,
+ (u32) Physical,
+ (u32) PageCount,
+ mdl->pagedMem
+ );
+
+ MEMORY_LOCK(Os);
+
+ table = (u32 *)PageTable;
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ mmu = Os->device->kernels[Core]->mmu;
+ bytes = PageCount * sizeof(*table);
+ mmuMdl = (PLINUX_MDL)mmu->pageTablePhysical;
+#endif
+
+ /* Get all the physical addresses and store them in the page table. */
+
+ offset = 0;
+
+ if (mdl->pagedMem)
+ {
+ /* Try to get the user pages so DMA can happen. */
+ while (PageCount-- > 0)
+ {
+ if (mdl->contiguous)
+ {
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ page_to_phys(nth_page(mdl->u.contiguousPages, offset)),
+ table));
+ }
+ else
+ {
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ _NonContiguousToPhys(mdl->u.nonContiguousPages, offset),
+ table));
+ }
+
+ table++;
+ offset += 1;
+ }
+ }
+ else
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_OS,
+ "%s(%d): we should not get this call for Non Paged Memory!",
+ __FUNCTION__, __LINE__
+ );
+
+ while (PageCount-- > 0)
+ {
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ page_to_phys(nth_page(mdl->u.contiguousPages, offset)),
+ table));
+
+ table++;
+ offset += 1;
+ }
+ }
+
+#if gcdNONPAGED_MEMORY_CACHEABLE
+ /* Get physical address of pageTable */
+ pageTablePhysical = (gctPHYS_ADDR)(mmuMdl->dmaHandle +
+ ((u32 *)PageTable - mmu->pageTableLogical));
+
+ /* Flush the mmu page table cache. */
+ gcmkONERROR(gckOS_CacheClean(
+ Os,
+ task_tgid_vnr(current),
+ NULL,
+ 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.
+**
+** size_t Bytes
+** Number of bytes of the allocation.
+**
+** void *Logical
+** Address of the mapped memory.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnlockPages(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN size_t Bytes,
+ IN void *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 != NULL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+
+ /* Make sure there is already a mapping...*/
+ gcmkVERIFY_ARGUMENT(mdl->u.nonContiguousPages != NULL
+ || mdl->u.contiguousPages != NULL);
+
+ MEMORY_LOCK(Os);
+
+ mdlMap = mdl->maps;
+
+ while (mdlMap != NULL)
+ {
+ if ((mdlMap->vmaAddr != NULL) && (task_tgid_vnr(current) == mdlMap->pid))
+ {
+ /* Get the current pointer for the task with stored pid. */
+ task = pid_task(find_vpid(mdlMap->pid), PIDTYPE_PID);
+
+ if (task != NULL && task->mm != NULL)
+ {
+ 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 = NULL;
+ }
+
+ 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.
+**
+** int InUserSpace
+** gcvTRUE if the pages need to be mapped into user space.
+**
+** size_t * Bytes
+** Pointer to the number of bytes to allocate.
+**
+** OUTPUT:
+**
+** size_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.
+**
+** void ** Logical
+** Pointer to a variable that receives the logical address of the
+** memory allocation.
+*/
+gceSTATUS
+gckOS_AllocateContiguous(
+ IN gckOS Os,
+ IN int InUserSpace,
+ IN OUT size_t * Bytes,
+ OUT gctPHYS_ADDR * Physical,
+ OUT void **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 != NULL);
+ gcmkVERIFY_ARGUMENT(*Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Physical != NULL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+
+ /* 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.
+**
+** void *Logical
+** Logicval address of the allocation.
+**
+** size_t Bytes
+** Number of bytes of the allocation.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_FreeContiguous(
+ IN gckOS Os,
+ IN gctPHYS_ADDR Physical,
+ IN void *Logical,
+ IN size_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 != NULL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+ 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;
+}
+
+/*******************************************************************************
+**
+** gckOS_MapUserPointer
+**
+** Map a pointer from the user process into the kernel address space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** void *Pointer
+** Pointer in user process space that needs to be mapped.
+**
+** size_t Size
+** Number of bytes that need to be mapped.
+**
+** OUTPUT:
+**
+** void ** KernelPointer
+** Pointer to a variable receiving the mapped pointer in kernel address
+** space.
+*/
+gceSTATUS
+gckOS_MapUserPointer(
+ IN gckOS Os,
+ IN void *Pointer,
+ IN size_t Size,
+ OUT void **KernelPointer
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu", Os, Pointer, Size);
+
+#if NO_USER_DIRECT_ACCESS_FROM_KERNEL
+{
+ void *buf = NULL;
+ u32 len;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Pointer != NULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(KernelPointer != NULL);
+
+ buf = kmalloc(Size, GFP_KERNEL | __GFP_NOWARN);
+ if (buf == NULL)
+ {
+ 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 != NULL)
+ {
+ 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.
+**
+** void *Pointer
+** Pointer in user process space that needs to be unmapped.
+**
+** size_t Size
+** Number of bytes that need to be unmapped.
+**
+** void *KernelPointer
+** Pointer in kernel address space that needs to be unmapped.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapUserPointer(
+ IN gckOS Os,
+ IN void *Pointer,
+ IN size_t Size,
+ IN void *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
+{
+ u32 len;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Pointer != NULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(KernelPointer != NULL);
+
+ 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_WriteMemory
+**
+** Write data to a memory.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** void *Address
+** Address of the memory to write to.
+**
+** u32 Data
+** Data for register.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_WriteMemory(
+ IN gckOS Os,
+ IN void *Address,
+ IN u32 Data
+ )
+{
+#if NO_USER_DIRECT_ACCESS_FROM_KERNEL
+ gceSTATUS status;
+#endif
+ gcmkHEADER_ARG("Os=0x%X Address=0x%X Data=%u", Os, Address, Data);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Address != NULL);
+
+ /* Write memory. */
+#if NO_USER_DIRECT_ACCESS_FROM_KERNEL
+ if (access_ok(VERIFY_WRITE, Address, 4))
+ {
+ /* User address. */
+ if(put_user(Data, (u32*)Address))
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_ADDRESS);
+ }
+ }
+ else
+#endif
+ {
+ /* Kernel address. */
+ *(u32 *)Address = Data;
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+#if NO_USER_DIRECT_ACCESS_FROM_KERNEL
+OnError:
+ gcmkFOOTER();
+ return status;
+#endif
+}
+
+/*******************************************************************************
+**
+** gckOS_MapUserMemoryEx
+**
+** Lock down a user buffer and return an DMA'able address to be used by the
+** hardware to access it.
+**
+** INPUT:
+**
+** void *Memory
+** Pointer to memory to lock down.
+**
+** size_t Size
+** Size in bytes of the memory to lock down.
+**
+** OUTPUT:
+**
+** void ** Info
+** Pointer to variable receiving the information record required by
+** gckOS_UnmapUserMemoryEx.
+**
+** u32 *Address
+** Pointer to a variable that will receive the address DMA'able by the
+** hardware.
+*/
+gceSTATUS
+gckOS_MapUserMemoryEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN void *Memory,
+ IN size_t Size,
+ OUT void **Info,
+ OUT u32 *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
+{
+ size_t pageCount, i, j;
+ u32 *pageTable;
+ u32 address = 0, physical = ~0U;
+ u32 start, end, memory;
+ int result = 0;
+
+ gcsPageInfo_PTR info = NULL;
+ struct page **pages = NULL;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Memory != NULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(Info != NULL);
+ gcmkVERIFY_ARGUMENT(Address != NULL);
+
+ do
+ {
+ memory = (u32) 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 | __GFP_NOWARN);
+
+ if (info == NULL)
+ {
+ status = gcvSTATUS_OUT_OF_MEMORY;
+ break;
+ }
+
+ /* Allocate the array of page addresses. */
+ pages = (struct page **)kmalloc(pageCount * sizeof(struct page *), GFP_KERNEL | __GFP_NOWARN);
+
+ if (pages == NULL)
+ {
+ 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,
+ NULL
+ );
+ up_read(&current->mm->mmap_sem);
+
+ if (result <=0 || result < pageCount)
+ {
+ struct vm_area_struct *vma;
+
+ /* Free the page table. */
+ if (pages != NULL)
+ {
+ /* Release the pages if any. */
+ if (result > 0)
+ {
+ for (i = 0; i < result; i++)
+ {
+ if (pages[i] == NULL)
+ {
+ break;
+ }
+
+ page_cache_release(pages[i]);
+ }
+ }
+
+ kfree(pages);
+ pages = NULL;
+ }
+
+ vma = find_vma(current->mm, memory);
+
+ if (vma && (vma->vm_flags & VM_PFNMAP) )
+ {
+ 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)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+ else
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ pfn = pte_pfn(*pte);
+
+ physical = (pfn << PAGE_SHIFT) | (memory & ~PAGE_MASK);
+
+ pte_unmap_unlock(pte, ptl);
+
+ if ((Os->device->kernels[Core]->hardware->mmuVersion == 0)
+ && !((physical - Os->device->baseAddress) & 0x80000000))
+ {
+ /* Release page info struct. */
+ if (info != NULL)
+ {
+ /* Free the page info struct. */
+ kfree(info);
+ }
+
+ MEMORY_MAP_UNLOCK(Os);
+
+ *Address = physical - Os->device->baseAddress;
+ *Info = NULL;
+
+ gcmkFOOTER_ARG("*Info=0x%X *Address=0x%08x",
+ *Info, *Address);
+
+ return gcvSTATUS_OK;
+ }
+ }
+ else
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ }
+
+ if (pages)
+ {
+ for (i = 0; i < pageCount; i++)
+ {
+ /* Flush(clean) the data cache. */
+ gcmkONERROR(gckOS_CacheFlush(Os, task_tgid_vnr(current), NULL,
+ (void *)page_to_phys(pages[i]),
+ (void *)(memory & PAGE_MASK) + i*PAGE_SIZE,
+ PAGE_SIZE));
+ }
+ }
+ else
+ {
+ /* Flush(clean) the data cache. */
+ gcmkONERROR(gckOS_CacheFlush(Os, task_tgid_vnr(current), NULL,
+ (void *)(physical & PAGE_MASK),
+ (void *)(memory & PAGE_MASK),
+ PAGE_SIZE * pageCount));
+
+ }
+
+ /* Allocate pages inside the page table. */
+ gcmkERR_BREAK(gckMMU_AllocatePages(Os->device->kernels[Core]->mmu,
+ pageCount * (PAGE_SIZE/4096),
+ (void **) &pageTable,
+ &address));
+
+ /* Fill the page table. */
+ for (i = 0; i < pageCount; i++)
+ {
+ u32 phys;
+ u32 *tab = pageTable + i * (PAGE_SIZE/4096);
+
+ if (pages)
+ {
+ phys = page_to_phys(pages[i]);
+ }
+ else
+ {
+ phys = (physical & PAGE_MASK) + i * PAGE_SIZE;
+ }
+
+ /* Get the physical address from page struct. */
+ gcmkONERROR(
+ gckMMU_SetPage(Os->device->kernels[Core]->mmu,
+ phys,
+ tab));
+
+ 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): pageTable[%d]: 0x%X 0x%X.",
+ __FUNCTION__, __LINE__,
+ i, phys, pageTable[i]);
+ }
+
+ gcmkONERROR(gckMMU_Flush(Os->device->kernels[Core]->mmu));
+
+ /* Save pointer to page table. */
+ info->pageTable = pageTable;
+ info->pages = pages;
+
+ *Info = (void *) 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 != NULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): error: page table is freed.",
+ __FUNCTION__, __LINE__
+ );
+
+ for (i = 0; i < result; i++)
+ {
+ if (pages[i] == NULL)
+ {
+ break;
+ }
+ page_cache_release(pages[i]);
+ }
+ }
+
+ if (info!= NULL && pages != NULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): error: pages is freed.",
+ __FUNCTION__, __LINE__
+ );
+
+ /* Free the page table. */
+ kfree(pages);
+ info->pages = NULL;
+ }
+
+ /* Release page info struct. */
+ if (info != NULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): error: info is freed.",
+ __FUNCTION__, __LINE__
+ );
+
+ /* Free the page info struct. */
+ kfree(info);
+ *Info = NULL;
+ }
+ }
+
+ 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_UnmapUserMemoryEx
+**
+** Unlock a user buffer and that was previously locked down by
+** gckOS_MapUserMemoryEx.
+**
+** INPUT:
+**
+** void *Memory
+** Pointer to memory to unlock.
+**
+** size_t Size
+** Size in bytes of the memory to unlock.
+**
+** void *Info
+** Information record returned by gckOS_MapUserMemoryEx.
+**
+** u32 *Address
+** The address returned by gckOS_MapUserMemoryEx.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_UnmapUserMemoryEx(
+ IN gckOS Os,
+ IN gceCORE Core,
+ IN void *Memory,
+ IN size_t Size,
+ IN void *Info,
+ IN u32 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
+{
+ u32 memory, start, end;
+ gcsPageInfo_PTR info;
+ size_t pageCount, i;
+ struct page **pages;
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Memory != NULL);
+ gcmkVERIFY_ARGUMENT(Size > 0);
+ gcmkVERIFY_ARGUMENT(Info != NULL);
+
+ do
+ {
+ /*u32 physical = ~0U;*/
+
+ 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 == NULL)
+ {
+ if (info->pageTable == NULL)
+ {
+ kfree(info);
+
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT);
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+ else
+ {
+ /*physical = (*info->pageTable) & PAGE_MASK;*/
+ }
+ }
+
+ memory = (u32) Memory;
+ end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ start = memory >> PAGE_SHIFT;
+ pageCount = end - start;
+
+ /* Overflow. */
+ if ((memory + Size) < memory)
+ {
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT);
+ return gcvSTATUS_INVALID_ARGUMENT;
+ }
+
+ /* Invalid argument. */
+ if (pageCount == 0)
+ {
+ gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT);
+ 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);
+
+ /* 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. */
+ if (pages)
+ {
+ 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]);
+ }
+
+ page_cache_release(pages[i]);
+ }
+ }
+
+ /* Success. */
+ status = gcvSTATUS_OK;
+ }
+ while (gcvFALSE);
+
+ if (info != NULL)
+ {
+ /* Free the page array. */
+ if (info->pages != NULL)
+ {
+ 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:
+**
+** u32 *BaseAddress
+** Pointer to a variable that will receive the base address.
+*/
+gceSTATUS
+gckOS_GetBaseAddress(
+ IN gckOS Os,
+ OUT u32 *BaseAddress
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(BaseAddress != NULL);
+
+ /* Return base address. */
+ *BaseAddress = Os->device->baseAddress;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*BaseAddress=0x%08x", *BaseAddress);
+ return gcvSTATUS_OK;
+}
+
+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_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 void *Destination,
+ IN const void *Source,
+ IN size_t Bytes
+ )
+{
+ gcmkHEADER_ARG("Destination=0x%X Source=0x%X Bytes=%lu",
+ Destination, Source, Bytes);
+
+ gcmkVERIFY_ARGUMENT(Destination != NULL);
+ gcmkVERIFY_ARGUMENT(Source != NULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ memcpy(Destination, Source, Bytes);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckOS_ZeroMemory(
+ IN void *Memory,
+ IN size_t Bytes
+ )
+{
+ gcmkHEADER_ARG("Memory=0x%X Bytes=%lu", Memory, Bytes);
+
+ gcmkVERIFY_ARGUMENT(Memory != NULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+ memset(Memory, 0, Bytes);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+********************************* Cache Control ********************************
+*******************************************************************************/
+
+#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;
+}
+
+#if gcdENABLE_OUTER_CACHE_PATCH
+/*******************************************************************************
+** _HandleOuterCache
+**
+** Handle the outer cache for the specified addresses.
+**
+** ARGUMENTS:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** u32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If NULL it is video memory.
+**
+** void *Physical
+** Physical address to flush.
+**
+** void *Logical
+** Logical address to flush.
+**
+** size_t Bytes
+** Size of the address range in bytes to flush.
+**
+** gceOUTERCACHE_OPERATION Type
+** Operation need to be execute.
+*/
+static gceSTATUS
+_HandleOuterCache(
+ IN gckOS Os,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN void *Physical,
+ IN void *Logical,
+ IN size_t Bytes,
+ IN gceCACHEOPERATION Type
+ )
+{
+ gceSTATUS status;
+ u32 i, pageNum;
+ unsigned long paddr;
+ void *vaddr;
+
+ gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu",
+ Os, ProcessID, Handle, Logical, Bytes);
+
+ if (Physical != NULL)
+ {
+ /* Non paged memory or gcvPOOL_USER surface */
+ paddr = (unsigned long) Physical;
+ gcmkONERROR(outer_func(Type, paddr, paddr + Bytes));
+ }
+ else if ((Handle == NULL)
+ || (Handle != NULL && ((PLINUX_MDL)Handle)->contiguous)
+ )
+ {
+ /* Video Memory or contiguous virtual memory */
+ gcmkONERROR(gckOS_GetPhysicalAddress(Os, Logical, (u32*)&paddr));
+ gcmkONERROR(outer_func(Type, paddr, paddr + Bytes));
+ }
+ else
+ {
+ /* Non contiguous virtual memory */
+ vaddr = (void *)gcmALIGN_BASE((u32)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,
+ (u32*)&paddr
+ ));
+
+ gcmkONERROR(outer_func(Type, paddr, paddr + PAGE_SIZE));
+ }
+ }
+
+ mb();
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+#endif
+#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.
+**
+** u32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If NULL it is video memory.
+**
+** void *Physical
+** Physical address to flush.
+**
+** void *Logical
+** Logical address to flush.
+**
+** size_t Bytes
+** Size of the address range in bytes to flush.
+*/
+gceSTATUS
+gckOS_CacheClean(
+ IN gckOS Os,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN void *Physical,
+ IN void *Logical,
+ IN size_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 != NULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+#if !gcdCACHE_FUNCTION_UNIMPLEMENTED
+#ifdef CONFIG_ARM
+
+ /* Inner cache. */
+ dmac_map_area(Logical, Bytes, DMA_TO_DEVICE);
+
+#if defined(CONFIG_OUTER_CACHE)
+ /* Outer cache. */
+#if gcdENABLE_OUTER_CACHE_PATCH
+ _HandleOuterCache(Os, ProcessID, Handle, Physical, Logical, Bytes, gcvCACHE_CLEAN);
+#else
+ outer_clean_range((unsigned long) Handle, (unsigned long) Handle + Bytes);
+#endif
+#endif
+
+#elif defined(CONFIG_MIPS)
+
+ dma_cache_wback((unsigned long) Logical, Bytes);
+
+#else
+ dma_sync_single_for_device(
+ NULL,
+ 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.
+**
+** u32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If NULL it is video memory.
+**
+** void *Logical
+** Logical address to flush.
+**
+** size_t Bytes
+** Size of the address range in bytes to flush.
+*/
+gceSTATUS
+gckOS_CacheInvalidate(
+ IN gckOS Os,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN void *Physical,
+ IN void *Logical,
+ IN size_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 != NULL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+
+#if !gcdCACHE_FUNCTION_UNIMPLEMENTED
+#ifdef CONFIG_ARM
+
+ /* Inner cache. */
+ dmac_map_area(Logical, Bytes, DMA_FROM_DEVICE);
+
+#if defined(CONFIG_OUTER_CACHE)
+ /* Outer cache. */
+#if gcdENABLE_OUTER_CACHE_PATCH
+ _HandleOuterCache(Os, ProcessID, Handle, Physical, Logical, Bytes, gcvCACHE_INVALIDATE);
+#else
+ outer_inv_range((unsigned long) Handle, (unsigned long) Handle + Bytes);
+#endif
+#endif
+
+#elif defined(CONFIG_MIPS)
+ dma_cache_inv((unsigned long) Logical, Bytes);
+#else
+ dma_sync_single_for_device(
+ NULL,
+ 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.
+**
+** u32 ProcessID
+** Process ID Logical belongs.
+**
+** gctPHYS_ADDR Handle
+** Physical address handle. If NULL it is video memory.
+**
+** void *Logical
+** Logical address to flush.
+**
+** size_t Bytes
+** Size of the address range in bytes to flush.
+*/
+gceSTATUS
+gckOS_CacheFlush(
+ IN gckOS Os,
+ IN u32 ProcessID,
+ IN gctPHYS_ADDR Handle,
+ IN void *Physical,
+ IN void *Logical,
+ IN size_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 != NULL);
+ 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. */
+#if gcdENABLE_OUTER_CACHE_PATCH
+ _HandleOuterCache(Os, ProcessID, Handle, Physical, Logical, Bytes, gcvCACHE_FLUSH);
+#else
+ outer_flush_range((unsigned long) Handle, (unsigned long) Handle + Bytes);
+#endif
+#endif
+
+#elif defined(CONFIG_MIPS)
+ dma_cache_wback_inv((unsigned long) Logical, Bytes);
+#else
+ dma_sync_single_for_device(
+ NULL,
+ 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,
+ NULL, NULL, 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,
+ NULL, NULL, 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, gcvCORE_MAJOR));
+ gcmkONERROR(gckKERNEL_Recovery(Hardware->kernel));
+ break;
+
+ case gcvBROADCAST_AXI_BUS_ERROR:
+ gcmkTRACE_N(gcvLEVEL_ERROR, 0, "gcvBROADCAST_AXI_BUS_ERROR\n");
+ gcmkONERROR(_DumpGPUState(Os, gcvCORE_MAJOR));
+ 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.
+**
+** unsigned int 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 unsigned int 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.
+**
+** unsigned int 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 unsigned int Idle,
+ IN unsigned int 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:
+**
+** void ** Semaphore
+** Pointer to the variable that will receive the created semaphore.
+*/
+gceSTATUS
+gckOS_CreateSemaphore(
+ IN gckOS Os,
+ OUT void **Semaphore
+ )
+{
+ gceSTATUS status;
+ struct semaphore *sem = NULL;
+
+ gcmkHEADER_ARG("Os=0x%X", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != NULL);
+
+ /* Allocate the semaphore structure. */
+ sem = (struct semaphore *)kmalloc(sizeof(struct semaphore), GFP_KERNEL | __GFP_NOWARN);
+ if (sem == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ /* Initialize the semaphore. */
+ sema_init(sem, 1);
+
+ /* Return to caller. */
+ *Semaphore = (void *) 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.
+**
+** void *Semaphore
+** Pointer to the semaphore thet needs to be acquired.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_AcquireSemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%08X Semaphore=0x%08X", Os, Semaphore);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != NULL);
+
+ /* 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.
+**
+** void *Semaphore
+** Pointer to the semaphore thet needs to be acquired.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_TryAcquireSemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Os=0x%x", Os);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != NULL);
+
+ /* 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.
+**
+** void *Semaphore
+** Pointer to the semaphore thet needs to be released.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_ReleaseSemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != NULL);
+
+ /* 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.
+**
+** void *Semaphore
+** Pointer to the semaphore thet needs to be destroyed.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DestroySemaphore(
+ IN gckOS Os,
+ IN void *Semaphore
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Semaphore != NULL);
+
+ /* Free the sempahore structure. */
+ kfree(Semaphore);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+static void galdevice_clk_enable(gckGALDEVICE device)
+{
+ struct clk *clk = device->clk;
+
+ if (!clk)
+ return;
+
+ if (device->clk_enabled)
+ return;
+
+ clk_enable(clk);
+ device->clk_enabled = 1;
+}
+
+static void galdevice_clk_disable(gckGALDEVICE device)
+{
+ struct clk *clk = device->clk;
+
+ if (!clk)
+ return;
+
+ if (!device->clk_enabled)
+ return;
+
+ clk_disable(clk);
+ device->clk_enabled = 0;
+}
+
+/*******************************************************************************
+**
+** gckOS_SetGPUPower
+**
+** Set the power of the GPU on or off.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to a gckOS object.
+**
+** int Clock
+** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock.
+**
+** int Power
+** gcvTRUE to turn on the power, or gcvFALSE to turn off the power.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_SetGPUPower(
+ IN gckOS Os,
+ IN int Clock,
+ IN int Power
+ )
+{
+ gckGALDEVICE device = (gckGALDEVICE) Os->device;
+
+ gcmkHEADER_ARG("Os=0x%X Clock=%d Power=%d", Os, Clock, Power);
+
+ /* TODO: Put your code here. */
+ if (Clock == gcvFALSE)
+ galdevice_clk_disable(device);
+ else
+ galdevice_clk_enable(device);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*----- Profile --------------------------------------------------------------*/
+
+gceSTATUS
+gckOS_GetProfileTick(
+ OUT u64 *Tick
+ )
+{
+ struct timespec time;
+
+ ktime_get_ts(&time);
+
+ *Tick = time.tv_nsec + time.tv_sec * 1000000000ULL;
+
+ return gcvSTATUS_OK;
+}
+
+u32
+gckOS_ProfileToMS(
+ IN u64 Ticks
+ )
+{
+ return div_u64(Ticks, 1000000);
+}
+
+/******************************************************************************\
+******************************* 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.
+**
+** int 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 int 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 != NULL);
+
+ /* Create an event structure. */
+ signal = (gcsSIGNAL_PTR) kmalloc(sizeof(gcsSIGNAL), GFP_KERNEL | __GFP_NOWARN);
+
+ if (signal == NULL)
+ {
+ 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 != NULL);
+
+ signal = (gcsSIGNAL_PTR) Signal;
+
+ if (atomic_dec_and_test(&signal->ref))
+ {
+ /* Free the sgianl. */
+ kfree(Signal);
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_UnmapSignal
+**
+** Unmap a signal .
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to that gctSIGNAL mapped.
+*/
+static gceSTATUS
+gckOS_UnmapSignal(
+ IN gckOS Os,
+ IN gctSIGNAL Signal
+ )
+{
+ int signalID;
+ gcsSIGNAL_PTR signal;
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X ", Os, Signal);
+
+ gcmkVERIFY_ARGUMENT(Signal != NULL);
+
+ signalID = (int) 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 == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ if (atomic_read(&signal->ref) == 1)
+ {
+ /* Update the table. */
+ Os->signal.table[signalID] = NULL;
+
+ 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_Signal
+**
+** Set a state of the specified signal.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gctSIGNAL Signal
+** Pointer to the gctSIGNAL.
+**
+** int 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 int 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 != NULL);
+
+ 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;
+}
+
+/*******************************************************************************
+**
+** 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, (s32) 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.
+**
+** u32 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 u32 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 != NULL);
+
+ 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
+ int timeout = (Wait == gcvINFINITE)
+ ? gcdINFINITE_TIMEOUT * HZ / 1000
+ : Wait * HZ / 1000;
+
+ unsigned int complained = 0;
+#else
+ int 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;
+#ifdef CONFIG_JZSOC
+ /* Fix WOW_Fish suspend resume render bugs. Code from
+ * Vivante Yun.Li.
+ */
+// INIT_COMPLETION(signal->obj);
+#endif
+ break;
+ }
+
+#if gcdDETECT_TIMEOUT
+ if ((Wait == gcvINFINITE) && (timeout == 0))
+ {
+ u32 dmaAddress1, dmaAddress2;
+ u32 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, gcvCORE_MAJOR));
+
+ 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
+ )
+{
+ int signalID;
+ gcsSIGNAL_PTR signal;
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=0x%X", Os, Signal, Process);
+
+ gcmkVERIFY_ARGUMENT(Signal != NULL);
+ gcmkVERIFY_ARGUMENT(MappedSignal != NULL);
+
+ signalID = (int) 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 == NULL)
+ {
+ 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_CreateUserSignal
+**
+** Create a new signal to be used in the user space.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** int 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:
+**
+** int * SignalID
+** Pointer to a variable receiving the created signal's ID.
+*/
+gceSTATUS
+gckOS_CreateUserSignal(
+ IN gckOS Os,
+ IN int ManualReset,
+ OUT int * SignalID
+ )
+{
+ gcsSIGNAL_PTR signal = NULL;
+ int unused, currentID, tableLen;
+ void ** table;
+ int i;
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Os=0x%0x ManualReset=%d", Os, ManualReset);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(SignalID != NULL);
+
+ /* Lock the table. */
+ gcmkONERROR(gckOS_AcquireMutex(Os, Os->signal.lock, gcvINFINITE));
+
+ acquired = gcvTRUE;
+
+ if (Os->signal.unused < 1)
+ {
+ /* Enlarge the table. */
+ table = (void **) kmalloc(
+ sizeof(void *) * (Os->signal.tableLen + USER_SIGNAL_TABLE_LEN_INIT),
+ GFP_KERNEL | __GFP_NOWARN);
+
+ if (table == NULL)
+ {
+ /* Out of memory. */
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ memset(table + Os->signal.tableLen, 0, sizeof(void *) * USER_SIGNAL_TABLE_LEN_INIT);
+ memcpy(table, Os->signal.table, sizeof(void *) * 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) task_tgid_vnr(current);
+
+ table[currentID] = signal;
+
+ /* Plus 1 to avoid NULL 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] == NULL)
+ {
+ 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.
+**
+** int SignalID
+** The signal's ID.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DestroyUserSignal(
+ IN gckOS Os,
+ IN int SignalID
+ )
+{
+ gceSTATUS status;
+ gcsSIGNAL_PTR signal;
+ int 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__,
+ (int) SignalID
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ SignalID -= 1;
+
+ signal = Os->signal.table[SignalID];
+
+ if (signal == NULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): signal is NULL.",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+
+ if (atomic_read(&signal->ref) == 1)
+ {
+ /* Update the table. */
+ Os->signal.table[SignalID] = NULL;
+
+ 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.
+**
+** int SignalID
+** Signal ID.
+**
+** u32 Wait
+** Number of milliseconds to wait.
+** Pass the value of gcvINFINITE for an infinite wait.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_WaitUserSignal(
+ IN gckOS Os,
+ IN int SignalID,
+ IN u32 Wait
+ )
+{
+ gceSTATUS status;
+ gcsSIGNAL_PTR signal;
+ int 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 == NULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): signal is NULL.",
+ __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.
+**
+** int SignalID
+** SignalID.
+**
+** int 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 int SignalID,
+ IN int State
+ )
+{
+ gceSTATUS status;
+ gcsSIGNAL_PTR signal;
+ int 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 == NULL)
+ {
+ gcmkTRACE(
+ gcvLEVEL_ERROR,
+ "%s(%d): signal is NULL.",
+ __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
+ )
+{
+ int 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] != NULL &&
+ ((gcsSIGNAL_PTR)Os->signal.table[signal])->process == Process)
+ {
+ gckOS_DestroySignal(Os, Os->signal.table[signal]);
+
+ /* Update the signal table. */
+ Os->signal.table[signal] = NULL;
+ if (Os->signal.unused++ == 0)
+ {
+ Os->signal.currentID = signal;
+ }
+ }
+ }
+
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Os,
+ Os->signal.lock
+ ));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_DumpGPUState
+**
+** Dump GPU state.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gceCORE Core
+** The core type of kernel.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DumpGPUState(
+ IN gckOS Os,
+ IN gceCORE Core
+ )
+{
+ gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core);
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+
+ _DumpGPUState(Os, Core);
+
+ gcmkFOOTER_NO();
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+/******************************************************************************\
+******************************** Software Timer ********************************
+\******************************************************************************/
+
+static void
+_TimerFunction(
+ struct work_struct * work
+ )
+{
+ gcsOSTIMER_PTR timer = (gcsOSTIMER_PTR)work;
+
+ gctTIMERFUNCTION function = timer->function;
+
+ function(timer->data);
+}
+
+/*******************************************************************************
+**
+** gckOS_CreateTimer
+**
+** Create a software timer.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** gctTIMERFUNCTION Function.
+** Pointer to a call back function which will be called when timer is
+** expired.
+**
+** void *Data.
+** Private data which will be passed to call back function.
+**
+** OUTPUT:
+**
+** void ** Timer
+** Pointer to a variable receiving the created timer.
+*/
+gceSTATUS
+gckOS_CreateTimer(
+ IN gckOS Os,
+ IN gctTIMERFUNCTION Function,
+ IN void *Data,
+ OUT void **Timer
+ )
+{
+ gceSTATUS status;
+ gcsOSTIMER_PTR pointer;
+ gcmkHEADER_ARG("Os=0x%X Function=0x%X Data=0x%X", Os, Function, Data);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Timer != NULL);
+
+ gcmkONERROR(gckOS_Allocate(Os, sizeof(gcsOSTIMER), (void *)&pointer));
+
+ pointer->function = Function;
+ pointer->data = Data;
+
+ INIT_DELAYED_WORK(&pointer->work, _TimerFunction);
+
+ *Timer = pointer;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckOS_DestoryTimer
+**
+** Destory a software timer.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** void *Timer
+** Pointer to the timer to be destoryed.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_DestoryTimer(
+ IN gckOS Os,
+ IN void *Timer
+ )
+{
+ gcsOSTIMER_PTR timer;
+ gcmkHEADER_ARG("Os=0x%X Timer=0x%X", Os, Timer);
+
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Timer != NULL);
+
+ timer = (gcsOSTIMER_PTR)Timer;
+
+ cancel_delayed_work_sync(&timer->work);
+
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, Timer));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_StartTimer
+**
+** Schedule a software timer.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** void *Timer
+** Pointer to the timer to be scheduled.
+**
+** u32 Delay
+** Delay in milliseconds.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_StartTimer(
+ IN gckOS Os,
+ IN void *Timer,
+ IN u32 Delay
+ )
+{
+ gcsOSTIMER_PTR timer;
+
+ gcmkHEADER_ARG("Os=0x%X Timer=0x%X Delay=%u", Os, Timer, Delay);
+
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Timer != NULL);
+ gcmkVERIFY_ARGUMENT(Delay != 0);
+
+ timer = (gcsOSTIMER_PTR)Timer;
+
+ if (unlikely(delayed_work_pending(&timer->work)))
+ {
+ cancel_delayed_work(&timer->work);
+ }
+
+ queue_delayed_work(Os->workqueue, &timer->work, msecs_to_jiffies(Delay));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckOS_StopTimer
+**
+** Cancel a unscheduled timer.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to the gckOS object.
+**
+** void *Timer
+** Pointer to the timer to be cancel.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckOS_StopTimer(
+ IN gckOS Os,
+ IN void *Timer
+ )
+{
+ gcsOSTIMER_PTR timer;
+ gcmkHEADER_ARG("Os=0x%X Timer=0x%X", Os, Timer);
+
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Timer != NULL);
+
+ timer = (gcsOSTIMER_PTR)Timer;
+
+ cancel_delayed_work(&timer->work);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_os.h b/kernel_drivers/v4_cleaned/gc_hal_kernel_os.h
new file mode 100644
index 0000000..84f5e7e
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_os.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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
+{
+ int pid;
+ void * 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
+{
+ int pid;
+ char * addr;
+
+ union _pages
+ {
+ /* Pointer to a array of pages. */
+ struct page * contiguousPages;
+ /* Pointer to a array of pointers to page. */
+ struct page ** nonContiguousPages;
+ }
+ u;
+
+#ifdef NO_DMA_COHERENT
+ void * kaddr;
+#endif /* NO_DMA_COHERENT */
+
+ int numPages;
+ int pagedMem;
+ int 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 int PID
+ );
+
+typedef struct _DRIVER_ARGS
+{
+ void * InputBuffer;
+ u32 InputBufferSize;
+ void * OutputBuffer;
+ u32 OutputBufferSize;
+}
+DRIVER_ARGS;
+
+/* Cleanup the signal table. */
+gceSTATUS
+gckOS_CleanProcessSignal(
+ gckOS Os,
+ gctHANDLE Process
+ );
+
+#endif /* __gc_hal_kernel_os_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_video_memory.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_video_memory.c
new file mode 100644
index 0000000..0052772
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_video_memory.c
@@ -0,0 +1,1788 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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.h"
+#include "gc_hal_internal.h"
+#include "gc_hal_kernel.h"
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+
+#define _GC_OBJ_ZONE gcvZONE_VIDMEM
+
+/******************************************************************************\
+******************************* Private Functions ******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** _Split
+**
+** Split a node on the required byte boundary.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to the node to split.
+**
+** size_t Bytes
+** Number of bytes to keep in the node.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** int
+** gcvTRUE if the node was split successfully, or gcvFALSE if there is an
+** error.
+**
+*/
+static int
+_Split(
+ IN gckOS Os,
+ IN gcuVIDMEM_NODE_PTR Node,
+ IN size_t Bytes
+ )
+{
+ gcuVIDMEM_NODE_PTR node;
+ void *pointer = NULL;
+
+ /* Make sure the byte boundary makes sense. */
+ if ((Bytes <= 0) || (Bytes > Node->VidMem.bytes))
+ {
+ return gcvFALSE;
+ }
+
+ /* Allocate a new gcuVIDMEM_NODE object. */
+ if (gcmIS_ERROR(gckOS_Allocate(Os,
+ sizeof(gcuVIDMEM_NODE),
+ &pointer)))
+ {
+ /* Error. */
+ return gcvFALSE;
+ }
+
+ node = pointer;
+
+ /* Initialize gcuVIDMEM_NODE structure. */
+ node->VidMem.offset = Node->VidMem.offset + Bytes;
+ node->VidMem.bytes = Node->VidMem.bytes - Bytes;
+ node->VidMem.alignment = 0;
+ node->VidMem.locked = 0;
+ node->VidMem.memory = Node->VidMem.memory;
+ node->VidMem.pool = Node->VidMem.pool;
+ node->VidMem.physical = Node->VidMem.physical;
+
+ /* Insert node behind specified node. */
+ node->VidMem.next = Node->VidMem.next;
+ node->VidMem.prev = Node;
+ Node->VidMem.next = node->VidMem.next->VidMem.prev = node;
+
+ /* Insert free node behind specified node. */
+ node->VidMem.nextFree = Node->VidMem.nextFree;
+ node->VidMem.prevFree = Node;
+ Node->VidMem.nextFree = node->VidMem.nextFree->VidMem.prevFree = node;
+
+ /* Adjust size of specified node. */
+ Node->VidMem.bytes = Bytes;
+
+ /* Success. */
+ return gcvTRUE;
+}
+
+/*******************************************************************************
+**
+** _Merge
+**
+** Merge two adjacent nodes together.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to the first of the two nodes to merge.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+*/
+static gceSTATUS
+_Merge(
+ IN gckOS Os,
+ IN gcuVIDMEM_NODE_PTR Node
+ )
+{
+ gcuVIDMEM_NODE_PTR node;
+ gceSTATUS status;
+
+ /* Save pointer to next node. */
+ node = Node->VidMem.next;
+#if gcdUSE_VIDMEM_PER_PID
+ /* Check if the nodes are adjacent physically. */
+ if ( ((Node->VidMem.physical + Node->VidMem.bytes) != node->VidMem.physical) ||
+ ((Node->VidMem.logical + Node->VidMem.bytes) != node->VidMem.logical) )
+ {
+ /* Can't merge. */
+ return gcvSTATUS_OK;
+ }
+#else
+
+ /* This is a good time to make sure the heap is not corrupted. */
+ if (Node->VidMem.offset + Node->VidMem.bytes != node->VidMem.offset)
+ {
+ /* Corrupted heap. */
+ gcmkASSERT(
+ Node->VidMem.offset + Node->VidMem.bytes == node->VidMem.offset);
+ return gcvSTATUS_HEAP_CORRUPTED;
+ }
+#endif
+
+ /* Adjust byte count. */
+ Node->VidMem.bytes += node->VidMem.bytes;
+
+ /* Unlink next node from linked list. */
+ Node->VidMem.next = node->VidMem.next;
+ Node->VidMem.nextFree = node->VidMem.nextFree;
+
+ Node->VidMem.next->VidMem.prev =
+ Node->VidMem.nextFree->VidMem.prevFree = Node;
+
+ /* Free next node. */
+ status = gcmkOS_SAFE_FREE(Os, node);
+ return status;
+}
+
+/******************************************************************************\
+******************************* gckVIDMEM API Code ******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckVIDMEM_ConstructVirtual
+**
+** Construct a new gcuVIDMEM_NODE union for virtual memory.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** size_t Bytes
+** Number of byte to allocate.
+**
+** OUTPUT:
+**
+** gcuVIDMEM_NODE_PTR * Node
+** Pointer to a variable that receives the gcuVIDMEM_NODE union pointer.
+*/
+gceSTATUS
+gckVIDMEM_ConstructVirtual(
+ IN gckKERNEL Kernel,
+ IN int Contiguous,
+ IN size_t Bytes,
+ OUT gcuVIDMEM_NODE_PTR * Node
+ )
+{
+ gckOS os;
+ gceSTATUS status;
+ gcuVIDMEM_NODE_PTR node = NULL;
+ void *pointer = NULL;
+ int i;
+
+ gcmkHEADER_ARG("Kernel=0x%x Contiguous=%d Bytes=%lu", Kernel, Contiguous, Bytes);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Node != NULL);
+
+ /* Extract the gckOS object pointer. */
+ os = Kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Allocate an gcuVIDMEM_NODE union. */
+ gcmkONERROR(gckOS_Allocate(os, sizeof(gcuVIDMEM_NODE), &pointer));
+
+ node = pointer;
+
+ /* Initialize gcuVIDMEM_NODE union for virtual memory. */
+ node->Virtual.kernel = Kernel;
+ node->Virtual.contiguous = Contiguous;
+ node->Virtual.logical = NULL;
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ node->Virtual.lockeds[i] = 0;
+ node->Virtual.pageTables[i] = NULL;
+ node->Virtual.lockKernels[i] = NULL;
+ }
+
+ node->Virtual.mutex = NULL;
+
+ node->Virtual.processID = task_tgid_vnr(current);
+
+ node->Virtual.freed = gcvFALSE;
+
+ gcmkONERROR(gckOS_ZeroMemory(&node->Virtual.sharedInfo, sizeof(gcsVIDMEM_NODE_SHARED_INFO)));
+
+ /* Create the mutex. */
+ gcmkONERROR(
+ gckOS_CreateMutex(os, &node->Virtual.mutex));
+
+ /* Allocate the virtual memory. */
+ gcmkONERROR(
+ gckOS_AllocatePagedMemoryEx(os,
+ node->Virtual.contiguous,
+ node->Virtual.bytes = Bytes,
+ &node->Virtual.physical));
+
+ /* Return pointer to the gcuVIDMEM_NODE union. */
+ *Node = node;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Created virtual node 0x%x for %u bytes @ 0x%x",
+ node, Bytes, node->Virtual.physical);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Node=0x%x", *Node);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (node != NULL)
+ {
+ if (node->Virtual.mutex != NULL)
+ {
+ /* Destroy the mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, node->Virtual.mutex));
+ }
+
+ /* Free the structure. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckVIDMEM_DestroyVirtual
+**
+** Destroy an gcuVIDMEM_NODE union for virtual memory.
+**
+** INPUT:
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to a gcuVIDMEM_NODE union.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+static gceSTATUS
+gckVIDMEM_DestroyVirtual(
+ IN gcuVIDMEM_NODE_PTR Node
+ )
+{
+ gckOS os;
+ int i;
+
+ gcmkHEADER_ARG("Node=0x%x", Node);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Node->Virtual.kernel, gcvOBJ_KERNEL);
+
+ /* Extact the gckOS object pointer. */
+ os = Node->Virtual.kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Delete the mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, Node->Virtual.mutex));
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (Node->Virtual.pageTables[i] != NULL)
+ {
+ /* Free the pages. */
+ gcmkVERIFY_OK(gckMMU_FreePages(Node->Virtual.lockKernels[i]->mmu,
+ Node->Virtual.pageTables[i],
+ Node->Virtual.pageCount));
+ }
+ }
+
+ /* Delete the gcuVIDMEM_NODE union. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, Node));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckVIDMEM_Construct
+**
+** Construct a new gckVIDMEM object.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to an gckOS object.
+**
+** u32 BaseAddress
+** Base address for the video memory heap.
+**
+** size_t Bytes
+** Number of bytes in the video memory heap.
+**
+** size_t Threshold
+** Minimum number of bytes beyond am allocation before the node is
+** split. Can be used as a minimum alignment requirement.
+**
+** size_t BankSize
+** Number of bytes per physical memory bank. Used by bank
+** optimization.
+**
+** OUTPUT:
+**
+** gckVIDMEM * Memory
+** Pointer to a variable that will hold the pointer to the gckVIDMEM
+** object.
+*/
+gceSTATUS
+gckVIDMEM_Construct(
+ IN gckOS Os,
+ IN u32 BaseAddress,
+ IN size_t Bytes,
+ IN size_t Threshold,
+ IN size_t BankSize,
+ OUT gckVIDMEM * Memory
+ )
+{
+ gckVIDMEM memory = NULL;
+ gceSTATUS status;
+ gcuVIDMEM_NODE_PTR node;
+ int i, banks = 0;
+ void *pointer = NULL;
+
+ gcmkHEADER_ARG("Os=0x%x BaseAddress=%08x Bytes=%lu Threshold=%lu "
+ "BankSize=%lu",
+ Os, BaseAddress, Bytes, Threshold, BankSize);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Memory != NULL);
+
+ /* Allocate the gckVIDMEM object. */
+ gcmkONERROR(gckOS_Allocate(Os, sizeof(struct _gckVIDMEM), &pointer));
+
+ memory = pointer;
+
+ /* Initialize the gckVIDMEM object. */
+ memory->object.type = gcvOBJ_VIDMEM;
+ memory->os = Os;
+
+ /* Set video memory heap information. */
+ memory->baseAddress = BaseAddress;
+ memory->bytes = Bytes;
+ memory->freeBytes = Bytes;
+ memory->threshold = Threshold;
+ memory->mutex = NULL;
+#if gcdUSE_VIDMEM_PER_PID
+ memory->pid = task_tgid_vnr(current);
+#endif
+
+ BaseAddress = 0;
+
+ /* Walk all possible banks. */
+ for (i = 0; i < ARRAY_SIZE(memory->sentinel); ++i)
+ {
+ size_t bytes;
+
+ if (BankSize == 0)
+ {
+ /* Use all bytes for the first bank. */
+ bytes = Bytes;
+ }
+ else
+ {
+ /* Compute number of bytes for this bank. */
+ bytes = gcmALIGN(BaseAddress + 1, BankSize) - BaseAddress;
+
+ if (bytes > Bytes)
+ {
+ /* Make sure we don't exceed the total number of bytes. */
+ bytes = Bytes;
+ }
+ }
+
+ if (bytes == 0)
+ {
+ /* Mark heap is not used. */
+ memory->sentinel[i].VidMem.next =
+ memory->sentinel[i].VidMem.prev =
+ memory->sentinel[i].VidMem.nextFree =
+ memory->sentinel[i].VidMem.prevFree = NULL;
+ continue;
+ }
+
+ /* Allocate one gcuVIDMEM_NODE union. */
+ gcmkONERROR(gckOS_Allocate(Os, sizeof(gcuVIDMEM_NODE), &pointer));
+
+ node = pointer;
+
+ /* Initialize gcuVIDMEM_NODE union. */
+ node->VidMem.memory = memory;
+
+ node->VidMem.next =
+ node->VidMem.prev =
+ node->VidMem.nextFree =
+ node->VidMem.prevFree = &memory->sentinel[i];
+
+ node->VidMem.offset = BaseAddress;
+ node->VidMem.bytes = bytes;
+ node->VidMem.alignment = 0;
+ node->VidMem.physical = 0;
+ node->VidMem.pool = gcvPOOL_UNKNOWN;
+
+ node->VidMem.locked = 0;
+
+ gcmkONERROR(gckOS_ZeroMemory(&node->VidMem.sharedInfo, sizeof(gcsVIDMEM_NODE_SHARED_INFO)));
+
+ /* Initialize the linked list of nodes. */
+ memory->sentinel[i].VidMem.next =
+ memory->sentinel[i].VidMem.prev =
+ memory->sentinel[i].VidMem.nextFree =
+ memory->sentinel[i].VidMem.prevFree = node;
+
+ /* Mark sentinel. */
+ memory->sentinel[i].VidMem.bytes = 0;
+
+ /* Adjust address for next bank. */
+ BaseAddress += bytes;
+ Bytes -= bytes;
+ banks ++;
+ }
+
+ /* Assign all the bank mappings. */
+ memory->mapping[gcvSURF_RENDER_TARGET] = banks - 1;
+ memory->mapping[gcvSURF_BITMAP] = banks - 1;
+ if (banks > 1) --banks;
+ memory->mapping[gcvSURF_DEPTH] = banks - 1;
+ memory->mapping[gcvSURF_HIERARCHICAL_DEPTH] = banks - 1;
+ if (banks > 1) --banks;
+ memory->mapping[gcvSURF_TEXTURE] = banks - 1;
+ if (banks > 1) --banks;
+ memory->mapping[gcvSURF_VERTEX] = banks - 1;
+ if (banks > 1) --banks;
+ memory->mapping[gcvSURF_INDEX] = banks - 1;
+ if (banks > 1) --banks;
+ memory->mapping[gcvSURF_TILE_STATUS] = banks - 1;
+ if (banks > 1) --banks;
+ memory->mapping[gcvSURF_TYPE_UNKNOWN] = 0;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "[GALCORE] INDEX: bank %d",
+ memory->mapping[gcvSURF_INDEX]);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "[GALCORE] VERTEX: bank %d",
+ memory->mapping[gcvSURF_VERTEX]);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "[GALCORE] TEXTURE: bank %d",
+ memory->mapping[gcvSURF_TEXTURE]);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "[GALCORE] RENDER_TARGET: bank %d",
+ memory->mapping[gcvSURF_RENDER_TARGET]);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "[GALCORE] DEPTH: bank %d",
+ memory->mapping[gcvSURF_DEPTH]);
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "[GALCORE] TILE_STATUS: bank %d",
+ memory->mapping[gcvSURF_TILE_STATUS]);
+
+ /* Allocate the mutex. */
+ gcmkONERROR(gckOS_CreateMutex(Os, &memory->mutex));
+
+ /* Return pointer to the gckVIDMEM object. */
+ *Memory = memory;
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Memory=0x%x", *Memory);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ if (memory != NULL)
+ {
+ if (memory->mutex != NULL)
+ {
+ /* Delete the mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Os, memory->mutex));
+ }
+
+ for (i = 0; i < banks; ++i)
+ {
+ /* Free the heap. */
+ gcmkASSERT(memory->sentinel[i].VidMem.next != NULL);
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory->sentinel[i].VidMem.next));
+ }
+
+ /* Free the object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckVIDMEM_Destroy
+**
+** Destroy an gckVIDMEM object.
+**
+** INPUT:
+**
+** gckVIDMEM Memory
+** Pointer to an gckVIDMEM object to destroy.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckVIDMEM_Destroy(
+ IN gckVIDMEM Memory
+ )
+{
+ gcuVIDMEM_NODE_PTR node, next;
+ int i;
+
+ gcmkHEADER_ARG("Memory=0x%x", Memory);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
+
+ /* Walk all sentinels. */
+ for (i = 0; i < ARRAY_SIZE(Memory->sentinel); ++i)
+ {
+ /* Bail out of the heap is not used. */
+ if (Memory->sentinel[i].VidMem.next == NULL)
+ {
+ break;
+ }
+
+ /* Walk all the nodes until we reach the sentinel. */
+ for (node = Memory->sentinel[i].VidMem.next;
+ node->VidMem.bytes != 0;
+ node = next)
+ {
+ /* Save pointer to the next node. */
+ next = node->VidMem.next;
+
+ /* Free the node. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, node));
+ }
+ }
+
+ /* Free the mutex. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Memory->os, Memory->mutex));
+
+ /* Mark the object as unknown. */
+ Memory->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckVIDMEM object. */
+ gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, Memory));
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+#if gcdENABLE_BANK_ALIGNMENT
+
+#if !gcdBANK_BIT_START
+#error gcdBANK_BIT_START not defined.
+#endif
+
+#if !gcdBANK_BIT_END
+#error gcdBANK_BIT_END not defined.
+#endif
+/*******************************************************************************
+** _GetSurfaceBankAlignment
+**
+** Return the required offset alignment required to the make BaseAddress
+** aligned properly.
+**
+** INPUT:
+**
+** gckOS Os
+** Pointer to gckOS object.
+**
+** gceSURF_TYPE Type
+** Type of allocation.
+**
+** u32 BaseAddress
+** Base address of current video memory node.
+**
+** OUTPUT:
+**
+** u32 *AlignmentOffset
+** Pointer to a variable that will hold the number of bytes to skip in
+** the current video memory node in order to make the alignment bank
+** aligned.
+*/
+static gceSTATUS
+_GetSurfaceBankAlignment(
+ IN gceSURF_TYPE Type,
+ IN u32 BaseAddress,
+ OUT u32 *AlignmentOffset
+ )
+{
+ u32 bank;
+ /* To retrieve the bank. */
+ static const u32 bankMask = (0xFFFFFFFF << gcdBANK_BIT_START)
+ ^ (0xFFFFFFFF << (gcdBANK_BIT_END + 1));
+
+ /* To retrieve the bank and all the lower bytes. */
+ static const u32 byteMask = ~(0xFFFFFFFF << (gcdBANK_BIT_END + 1));
+
+ gcmkHEADER_ARG("Type=%d BaseAddress=0x%x ", Type, BaseAddress);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(AlignmentOffset != NULL);
+
+ switch (Type)
+ {
+ case gcvSURF_RENDER_TARGET:
+ bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START);
+
+ /* Align to the first bank. */
+ *AlignmentOffset = (bank == 0) ?
+ 0 :
+ ((1 << (gcdBANK_BIT_END + 1)) + 0) - (BaseAddress & byteMask);
+ break;
+
+ case gcvSURF_DEPTH:
+ bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START);
+
+ /* Align to the third bank. */
+ *AlignmentOffset = (bank == 2) ?
+ 0 :
+ ((1 << (gcdBANK_BIT_END + 1)) + (2 << gcdBANK_BIT_START)) - (BaseAddress & byteMask);
+
+ /* Add a channel offset at the channel bit. */
+ *AlignmentOffset += (1 << gcdBANK_CHANNEL_BIT);
+ break;
+
+ default:
+ /* no alignment needed. */
+ *AlignmentOffset = 0;
+ }
+
+ /* Return the status. */
+ gcmkFOOTER_ARG("*AlignmentOffset=%u", *AlignmentOffset);
+ return gcvSTATUS_OK;
+}
+#endif
+
+static gcuVIDMEM_NODE_PTR
+_FindNode(
+ IN gckVIDMEM Memory,
+ IN int Bank,
+ IN size_t Bytes,
+ IN gceSURF_TYPE Type,
+ IN OUT u32 *Alignment
+ )
+{
+ gcuVIDMEM_NODE_PTR node;
+ u32 alignment;
+
+#if gcdENABLE_BANK_ALIGNMENT
+ u32 bankAlignment;
+ gceSTATUS status;
+#endif
+
+ if (Memory->sentinel[Bank].VidMem.nextFree == NULL)
+ {
+ /* No free nodes left. */
+ return NULL;
+ }
+
+#if gcdENABLE_BANK_ALIGNMENT
+ /* Walk all free nodes until we have one that is big enough or we have
+ ** reached the sentinel. */
+ for (node = Memory->sentinel[Bank].VidMem.nextFree;
+ node->VidMem.bytes != 0;
+ node = node->VidMem.nextFree)
+ {
+ gcmkONERROR(_GetSurfaceBankAlignment(
+ Type,
+ node->VidMem.memory->baseAddress + node->VidMem.offset,
+ &bankAlignment));
+
+ bankAlignment = gcmALIGN(bankAlignment, *Alignment);
+
+ /* Compute number of bytes to skip for alignment. */
+ alignment = (*Alignment == 0)
+ ? 0
+ : (*Alignment - (node->VidMem.offset % *Alignment));
+
+ if (alignment == *Alignment)
+ {
+ /* Node is already aligned. */
+ alignment = 0;
+ }
+
+ if (node->VidMem.bytes >= Bytes + alignment + bankAlignment)
+ {
+ /* This node is big enough. */
+ *Alignment = alignment + bankAlignment;
+ return node;
+ }
+ }
+#endif
+
+ /* Walk all free nodes until we have one that is big enough or we have
+ reached the sentinel. */
+ for (node = Memory->sentinel[Bank].VidMem.nextFree;
+ node->VidMem.bytes != 0;
+ node = node->VidMem.nextFree)
+ {
+
+ int modulo = gckMATH_ModuloInt(node->VidMem.offset, *Alignment);
+
+ /* Compute number of bytes to skip for alignment. */
+ alignment = (*Alignment == 0) ? 0 : (*Alignment - modulo);
+
+ if (alignment == *Alignment)
+ {
+ /* Node is already aligned. */
+ alignment = 0;
+ }
+
+ if (node->VidMem.bytes >= Bytes + alignment)
+ {
+ /* This node is big enough. */
+ *Alignment = alignment;
+ return node;
+ }
+ }
+
+#if gcdENABLE_BANK_ALIGNMENT
+OnError:
+#endif
+ /* Not enough memory. */
+ return NULL;
+}
+
+/*******************************************************************************
+**
+** gckVIDMEM_AllocateLinear
+**
+** Allocate linear memory from the gckVIDMEM object.
+**
+** INPUT:
+**
+** gckVIDMEM Memory
+** Pointer to an gckVIDMEM object.
+**
+** size_t Bytes
+** Number of bytes to allocate.
+**
+** u32 Alignment
+** Byte alignment for allocation.
+**
+** gceSURF_TYPE Type
+** Type of surface to allocate (use by bank optimization).
+**
+** OUTPUT:
+**
+** gcuVIDMEM_NODE_PTR * Node
+** Pointer to a variable that will hold the allocated memory node.
+*/
+gceSTATUS
+gckVIDMEM_AllocateLinear(
+ IN gckVIDMEM Memory,
+ IN size_t Bytes,
+ IN u32 Alignment,
+ IN gceSURF_TYPE Type,
+ OUT gcuVIDMEM_NODE_PTR * Node
+ )
+{
+ gceSTATUS status;
+ gcuVIDMEM_NODE_PTR node;
+ u32 alignment;
+ int bank, i;
+ int acquired = gcvFALSE;
+
+ gcmkHEADER_ARG("Memory=0x%x Bytes=%lu Alignment=%u Type=%d",
+ Memory, Bytes, Alignment, Type);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
+ gcmkVERIFY_ARGUMENT(Bytes > 0);
+ gcmkVERIFY_ARGUMENT(Node != NULL);
+ gcmkVERIFY_ARGUMENT(Type < gcvSURF_NUM_TYPES);
+
+ /* Acquire the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(Memory->os, Memory->mutex, gcvINFINITE));
+
+ acquired = gcvTRUE;
+#if !gcdUSE_VIDMEM_PER_PID
+
+ if (Bytes > Memory->freeBytes)
+ {
+ /* Not enough memory. */
+ status = gcvSTATUS_OUT_OF_MEMORY;
+ goto OnError;
+ }
+#endif
+
+#if gcdSMALL_BLOCK_SIZE
+ if ((Memory->freeBytes < (Memory->bytes/gcdRATIO_FOR_SMALL_MEMORY))
+ && (Bytes >= gcdSMALL_BLOCK_SIZE)
+ )
+ {
+ /* The left memory is for small memory.*/
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+#endif
+
+ /* Find the default bank for this surface type. */
+ gcmkASSERT((int) Type < ARRAY_SIZE(Memory->mapping));
+ bank = Memory->mapping[Type];
+ alignment = Alignment;
+
+#if gcdUSE_VIDMEM_PER_PID
+ if (Bytes <= Memory->freeBytes)
+ {
+#endif
+ /* Find a free node in the default bank. */
+ node = _FindNode(Memory, bank, Bytes, Type, &alignment);
+
+ /* Out of memory? */
+ if (node == NULL)
+ {
+ /* Walk all lower banks. */
+ for (i = bank - 1; i >= 0; --i)
+ {
+ /* Find a free node inside the current bank. */
+ node = _FindNode(Memory, i, Bytes, Type, &alignment);
+ if (node != NULL)
+ {
+ break;
+ }
+ }
+ }
+
+ if (node == NULL)
+ {
+ /* Walk all upper banks. */
+ for (i = bank + 1; i < ARRAY_SIZE(Memory->sentinel); ++i)
+ {
+ if (Memory->sentinel[i].VidMem.nextFree == NULL)
+ {
+ /* Abort when we reach unused banks. */
+ break;
+ }
+
+ /* Find a free node inside the current bank. */
+ node = _FindNode(Memory, i, Bytes, Type, &alignment);
+ if (node != NULL)
+ {
+ break;
+ }
+ }
+ }
+#if gcdUSE_VIDMEM_PER_PID
+ }
+#endif
+
+ if (node == NULL)
+ {
+ /* Out of memory. */
+#if gcdUSE_VIDMEM_PER_PID
+ /* Allocate more memory from shared pool. */
+ size_t bytes;
+ gctPHYS_ADDR physical_temp;
+ u32 physical;
+ void *logical;
+
+ bytes = gcmALIGN(Bytes, gcdUSE_VIDMEM_PER_PID_SIZE);
+
+ gcmkONERROR(gckOS_AllocateContiguous(Memory->os,
+ gcvTRUE,
+ &bytes,
+ &physical_temp,
+ &logical));
+
+ /* physical address is returned as 0 for user space. workaround. */
+ if (physical_temp == NULL)
+ {
+ gcmkONERROR(gckOS_GetPhysicalAddress(Memory->os, logical, &physical));
+ }
+
+ /* Allocate one gcuVIDMEM_NODE union. */
+ gcmkONERROR(
+ gckOS_Allocate(Memory->os,
+ sizeof(gcuVIDMEM_NODE),
+ (void **) &node));
+
+ /* Initialize gcuVIDMEM_NODE union. */
+ node->VidMem.memory = Memory;
+
+ node->VidMem.offset = 0;
+ node->VidMem.bytes = bytes;
+ node->VidMem.alignment = 0;
+ node->VidMem.physical = physical;
+ node->VidMem.pool = gcvPOOL_UNKNOWN;
+
+ node->VidMem.locked = 0;
+
+ /* Insert node behind sentinel node. */
+ node->VidMem.next = Memory->sentinel[bank].VidMem.next;
+ node->VidMem.prev = &Memory->sentinel[bank];
+ Memory->sentinel[bank].VidMem.next = node->VidMem.next->VidMem.prev = node;
+
+ /* Insert free node behind sentinel node. */
+ node->VidMem.nextFree = Memory->sentinel[bank].VidMem.nextFree;
+ node->VidMem.prevFree = &Memory->sentinel[bank];
+ Memory->sentinel[bank].VidMem.nextFree = node->VidMem.nextFree->VidMem.prevFree = node;
+
+ Memory->freeBytes += bytes;
+#else
+ status = gcvSTATUS_OUT_OF_MEMORY;
+ goto OnError;
+#endif
+ }
+
+ /* Do we have an alignment? */
+ if (alignment > 0)
+ {
+ /* Split the node so it is aligned. */
+ if (_Split(Memory->os, node, alignment))
+ {
+ /* Successful split, move to aligned node. */
+ node = node->VidMem.next;
+
+ /* Remove alignment. */
+ alignment = 0;
+ }
+ }
+
+ /* Do we have enough memory after the allocation to split it? */
+ if (node->VidMem.bytes - Bytes > Memory->threshold)
+ {
+ /* Adjust the node size. */
+ _Split(Memory->os, node, Bytes);
+ }
+
+ /* Remove the node from the free list. */
+ node->VidMem.prevFree->VidMem.nextFree = node->VidMem.nextFree;
+ node->VidMem.nextFree->VidMem.prevFree = node->VidMem.prevFree;
+ node->VidMem.nextFree =
+ node->VidMem.prevFree = NULL;
+
+ /* Fill in the information. */
+ node->VidMem.alignment = alignment;
+ node->VidMem.memory = Memory;
+
+ /* Adjust the number of free bytes. */
+ Memory->freeBytes -= node->VidMem.bytes;
+
+ node->VidMem.freePending = gcvFALSE;
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
+
+ /* Return the pointer to the node. */
+ *Node = node;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Allocated %u bytes @ 0x%x [0x%08X]",
+ node->VidMem.bytes, node, node->VidMem.offset);
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Node=0x%x", *Node);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckVIDMEM_Free
+**
+** Free an allocated video memory node.
+**
+** INPUT:
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to a gcuVIDMEM_NODE object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS
+gckVIDMEM_Free(
+ IN gcuVIDMEM_NODE_PTR Node
+ )
+{
+ gceSTATUS status;
+ gckKERNEL kernel = NULL;
+ gckVIDMEM memory = NULL;
+ gcuVIDMEM_NODE_PTR node;
+ int mutexAcquired = gcvFALSE;
+ gckOS os = gcvFALSE;
+ int acquired = gcvFALSE;
+ s32 i, totalLocked;
+
+ gcmkHEADER_ARG("Node=0x%x", Node);
+
+ /* Verify the arguments. */
+ if ((Node == NULL)
+ || (Node->VidMem.memory == NULL)
+ )
+ {
+ /* Invalid object. */
+ gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
+ }
+
+ /**************************** Video Memory ********************************/
+
+ if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ if (Node->VidMem.locked > 0)
+ {
+ /* Client still has a lock, defer free op 'till when lock reaches 0. */
+ Node->VidMem.freePending = gcvTRUE;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Node 0x%x is locked (%d)... deferring free.",
+ Node, Node->VidMem.locked);
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ /* Extract pointer to gckVIDMEM object owning the node. */
+ memory = Node->VidMem.memory;
+
+ /* Acquire the mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE));
+
+ mutexAcquired = gcvTRUE;
+
+ /* Update the number of free bytes. */
+ memory->freeBytes += Node->VidMem.bytes;
+
+ /* Find the next free node. */
+ for (node = Node->VidMem.next;
+ node != NULL && node->VidMem.nextFree == NULL;
+ node = node->VidMem.next) ;
+
+ /* Insert this node in the free list. */
+ Node->VidMem.nextFree = node;
+ Node->VidMem.prevFree = node->VidMem.prevFree;
+
+ Node->VidMem.prevFree->VidMem.nextFree =
+ node->VidMem.prevFree = Node;
+
+ /* Is the next node a free node and not the sentinel? */
+ if ((Node->VidMem.next == Node->VidMem.nextFree)
+ && (Node->VidMem.next->VidMem.bytes != 0)
+ )
+ {
+ /* Merge this node with the next node. */
+ gcmkONERROR(_Merge(memory->os, node = Node));
+ gcmkASSERT(node->VidMem.nextFree != node);
+ gcmkASSERT(node->VidMem.prevFree != node);
+ }
+
+ /* Is the previous node a free node and not the sentinel? */
+ if ((Node->VidMem.prev == Node->VidMem.prevFree)
+ && (Node->VidMem.prev->VidMem.bytes != 0)
+ )
+ {
+ /* Merge this node with the previous node. */
+ gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev));
+ gcmkASSERT(node->VidMem.nextFree != node);
+ gcmkASSERT(node->VidMem.prevFree != node);
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex));
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Node 0x%x is freed.",
+ Node);
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+ }
+
+ /*************************** Virtual Memory *******************************/
+
+ /* Get gckKERNEL object. */
+ kernel = Node->Virtual.kernel;
+
+ /* Verify the gckKERNEL object pointer. */
+ gcmkVERIFY_OBJECT(kernel, gcvOBJ_KERNEL);
+
+ /* Get the gckOS object pointer. */
+ os = kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Grab the mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(os, Node->Virtual.mutex, gcvINFINITE));
+
+ acquired = gcvTRUE;
+
+ for (i = 0, totalLocked = 0; i < gcdCORE_COUNT; i++)
+ {
+ totalLocked += Node->Virtual.lockeds[i];
+ }
+
+ if (totalLocked > 0)
+ {
+ gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_VIDMEM,
+ "gckVIDMEM_Free: Virtual node 0x%x is locked (%d)",
+ Node, totalLocked);
+
+ /* Set Flag */
+ Node->Virtual.freed = gcvTRUE;
+
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+ }
+ else
+ {
+ /* Free the virtual memory. */
+ gcmkVERIFY_OK(gckOS_FreePagedMemory(kernel->os,
+ Node->Virtual.physical,
+ Node->Virtual.bytes));
+
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+
+ /* Destroy the gcuVIDMEM_NODE union. */
+ gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node));
+ }
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ if (mutexAcquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(
+ memory->os, memory->mutex
+ ));
+ }
+
+ if (acquired)
+ {
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** _NeedVirtualMapping
+**
+** Whether setup GPU page table for video node.
+**
+** INPUT:
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to a gcuVIDMEM_NODE union.
+**
+** gceCORE Core
+** Id of current GPU.
+**
+** OUTPUT:
+** int * NeedMapping
+** A pointer hold the result whether Node should be mapping.
+*/
+static gceSTATUS
+_NeedVirtualMapping(
+ IN gckKERNEL Kernel,
+ IN gceCORE Core,
+ IN gcuVIDMEM_NODE_PTR Node,
+ OUT int * NeedMapping
+)
+{
+ gceSTATUS status;
+ u32 phys;
+ u32 end;
+ gcePOOL pool;
+ u32 offset;
+
+ gcmkHEADER_ARG("Node=0x%X", Node);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Kernel != NULL);
+ gcmkVERIFY_ARGUMENT(Node != NULL);
+ gcmkVERIFY_ARGUMENT(NeedMapping != NULL);
+ gcmkVERIFY_ARGUMENT(Core < gcdCORE_COUNT);
+
+ if (Node->Virtual.contiguous)
+ {
+ /* For cores which can't access all physical address. */
+ gcmkONERROR(gckOS_GetPhysicalAddress(Kernel->os,
+ Node->Virtual.logical,
+ &phys));
+
+ /* If part of region is belong to gcvPOOL_VIRTUAL,
+ ** whole region has to be mapped. */
+ end = phys + Node->Virtual.bytes - 1;
+
+ gcmkONERROR(gckHARDWARE_SplitMemory(
+ Kernel->hardware, end, &pool, &offset
+ ));
+
+ *NeedMapping = (pool == gcvPOOL_VIRTUAL);
+ }
+ else
+ {
+ *NeedMapping = gcvTRUE;
+ }
+
+ gcmkFOOTER_ARG("*NeedMapping=%d", *NeedMapping);
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckVIDMEM_Lock
+**
+** Lock a video memory node and return its hardware specific address.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to a gcuVIDMEM_NODE union.
+**
+** OUTPUT:
+**
+** u32 * Address
+** Pointer to a variable that will hold the hardware specific address.
+*/
+gceSTATUS
+gckVIDMEM_Lock(
+ IN gckKERNEL Kernel,
+ IN gcuVIDMEM_NODE_PTR Node,
+ IN int Cacheable,
+ OUT u32 * Address
+ )
+{
+ gceSTATUS status;
+ int acquired = gcvFALSE;
+ int locked = gcvFALSE;
+ gckOS os = NULL;
+ int needMapping;
+
+ gcmkHEADER_ARG("Node=0x%x", Node);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_ARGUMENT(Address != NULL);
+
+ if ((Node == NULL)
+ || (Node->VidMem.memory == NULL)
+ )
+ {
+ /* Invalid object. */
+ gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
+ }
+
+ /**************************** Video Memory ********************************/
+
+ if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ if (Cacheable == gcvTRUE)
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
+ }
+
+ /* Increment the lock count. */
+ Node->VidMem.locked ++;
+
+ /* Return the address of the node. */
+#if !gcdUSE_VIDMEM_PER_PID
+ *Address = Node->VidMem.memory->baseAddress
+ + Node->VidMem.offset
+ + Node->VidMem.alignment;
+#else
+ *Address = Node->VidMem.physical;
+#endif
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Locked node 0x%x (%d) @ 0x%08X",
+ Node,
+ Node->VidMem.locked,
+ *Address);
+ }
+
+ /*************************** Virtual Memory *******************************/
+
+ else
+ {
+ /* Verify the gckKERNEL object pointer. */
+ gcmkVERIFY_OBJECT(Node->Virtual.kernel, gcvOBJ_KERNEL);
+
+ /* Extract the gckOS object pointer. */
+ os = Node->Virtual.kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Grab the mutex. */
+ gcmkONERROR(gckOS_AcquireMutex(os, Node->Virtual.mutex, gcvINFINITE));
+ acquired = gcvTRUE;
+
+ gcmkONERROR(
+ gckOS_LockPages(os,
+ Node->Virtual.physical,
+ Node->Virtual.bytes,
+ Cacheable,
+ &Node->Virtual.logical,
+ &Node->Virtual.pageCount));
+
+ /* Increment the lock count. */
+ if (Node->Virtual.lockeds[Kernel->core] ++ == 0)
+ {
+ /* Is this node pending for a final unlock? */
+ locked = gcvTRUE;
+
+ gcmkONERROR(_NeedVirtualMapping(Kernel, Kernel->core, Node, &needMapping));
+
+ if (needMapping == gcvFALSE)
+ {
+ /* Get physical address directly */
+ gcmkONERROR(gckOS_GetPhysicalAddress(os,
+ Node->Virtual.logical,
+ &Node->Virtual.addresses[Kernel->core]));
+ }
+ else
+ {
+ /* Allocate pages inside the MMU. */
+ gcmkONERROR(
+ gckMMU_AllocatePages(Kernel->mmu,
+ Node->Virtual.pageCount,
+ &Node->Virtual.pageTables[Kernel->core],
+ &Node->Virtual.addresses[Kernel->core]));
+
+ Node->Virtual.lockKernels[Kernel->core] = Kernel;
+
+ /* Map the pages. */
+ gcmkONERROR(
+ gckOS_MapPagesEx(os,
+ Kernel->core,
+ Node->Virtual.physical,
+ Node->Virtual.pageCount,
+ Node->Virtual.pageTables[Kernel->core]));
+
+ gcmkONERROR(gckMMU_Flush(Kernel->mmu));
+ }
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Mapped virtual node 0x%x to 0x%08X",
+ Node,
+ Node->Virtual.addresses[Kernel->core]);
+ }
+
+ /* Return hardware address. */
+ *Address = Node->Virtual.addresses[Kernel->core];
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Address=%08x", *Address);
+ return gcvSTATUS_OK;
+
+OnError:
+ if (locked)
+ {
+ if (Node->Virtual.pageTables[Kernel->core] != NULL)
+ {
+ /* Free the pages from the MMU. */
+ gcmkVERIFY_OK(
+ gckMMU_FreePages(Kernel->mmu,
+ Node->Virtual.pageTables[Kernel->core],
+ Node->Virtual.pageCount));
+
+ Node->Virtual.pageTables[Kernel->core] = NULL;
+ Node->Virtual.lockKernels[Kernel->core] = NULL;
+ }
+
+ /* Unlock the pages. */
+ gcmkVERIFY_OK(
+ gckOS_UnlockPages(os,
+ Node->Virtual.physical,
+ Node->Virtual.bytes,
+ Node->Virtual.logical
+ ));
+
+ Node->Virtual.lockeds[Kernel->core]--;
+ }
+
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckVIDMEM_Unlock
+**
+** Unlock a video memory node.
+**
+** INPUT:
+**
+** gckKERNEL Kernel
+** Pointer to an gckKERNEL object.
+**
+** gcuVIDMEM_NODE_PTR Node
+** Pointer to a locked gcuVIDMEM_NODE union.
+**
+** gceSURF_TYPE Type
+** Type of surface to unlock.
+**
+** int * Asynchroneous
+** Pointer to a variable specifying whether the surface should be
+** unlocked asynchroneously or not.
+**
+** OUTPUT:
+**
+** int * Asynchroneous
+** Pointer to a variable receiving the number of bytes used in the
+** command buffer specified by 'Commands'. If NULL, there is no
+** command buffer.
+*/
+gceSTATUS
+gckVIDMEM_Unlock(
+ IN gckKERNEL Kernel,
+ IN gcuVIDMEM_NODE_PTR Node,
+ IN gceSURF_TYPE Type,
+ IN OUT int * Asynchroneous
+ )
+{
+ gceSTATUS status;
+ gckHARDWARE hardware;
+ void *buffer;
+ size_t requested, bufferSize;
+ gckCOMMAND command = NULL;
+ gceKERNEL_FLUSH flush;
+ gckOS os = NULL;
+ int acquired = gcvFALSE;
+ int commitEntered = gcvFALSE;
+ s32 i, totalLocked;
+
+ gcmkHEADER_ARG("Node=0x%x Type=%d *Asynchroneous=%d",
+ Node, Type, gcmOPT_VALUE(Asynchroneous));
+
+ /* Verify the arguments. */
+ if ((Node == NULL)
+ || (Node->VidMem.memory == NULL)
+ )
+ {
+ /* Invalid object. */
+ gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
+ }
+
+ /**************************** Video Memory ********************************/
+
+ if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
+ {
+ if (Node->VidMem.locked <= 0)
+ {
+ /* The surface was not locked. */
+ status = gcvSTATUS_MEMORY_UNLOCKED;
+ goto OnError;
+ }
+
+ /* Decrement the lock count. */
+ Node->VidMem.locked --;
+
+ if (Asynchroneous != NULL)
+ {
+ /* No need for any events. */
+ *Asynchroneous = gcvFALSE;
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Unlocked node 0x%x (%d)",
+ Node,
+ Node->VidMem.locked);
+
+ if (Node->VidMem.freePending && (Node->VidMem.locked == 0))
+ {
+ /* Client has unlocked node previously attempted to be freed by compositor. Free now. */
+ Node->VidMem.freePending = gcvFALSE;
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Deferred-freeing Node 0x%x.",
+ Node);
+ gcmkONERROR(gckVIDMEM_Free(Node));
+ }
+ }
+
+ /*************************** Virtual Memory *******************************/
+
+ else
+ {
+ /* Verify the gckHARDWARE object pointer. */
+ hardware = Kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ /* Verify the gckCOMMAND object pointer. */
+ command = Kernel->command;
+ gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND);
+
+ /* Get the gckOS object pointer. */
+ os = Kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Grab the mutex. */
+ gcmkONERROR(
+ gckOS_AcquireMutex(os, Node->Virtual.mutex, gcvINFINITE));
+
+ acquired = gcvTRUE;
+
+ if (Asynchroneous == NULL)
+ {
+ if (Node->Virtual.lockeds[Kernel->core] == 0)
+ {
+ status = gcvSTATUS_MEMORY_UNLOCKED;
+ goto OnError;
+ }
+
+ /* Decrement lock count. */
+ -- Node->Virtual.lockeds[Kernel->core];
+
+ /* See if we can unlock the resources. */
+ if (Node->Virtual.lockeds[Kernel->core] == 0)
+ {
+ /* Free the page table. */
+ if (Node->Virtual.pageTables[Kernel->core] != NULL)
+ {
+ gcmkONERROR(
+ gckMMU_FreePages(Kernel->mmu,
+ Node->Virtual.pageTables[Kernel->core],
+ Node->Virtual.pageCount));
+
+ /* Mark page table as freed. */
+ Node->Virtual.pageTables[Kernel->core] = NULL;
+ Node->Virtual.lockKernels[Kernel->core] = NULL;
+ }
+ }
+
+ for (i = 0, totalLocked = 0; i < gcdCORE_COUNT; i++)
+ {
+ totalLocked += Node->Virtual.lockeds[i];
+ }
+
+ if (totalLocked == 0)
+ {
+ /* Owner have already freed this node
+ ** and we are the last one to unlock, do
+ ** real free */
+ if (Node->Virtual.freed)
+ {
+ /* Free the virtual memory. */
+ gcmkVERIFY_OK(gckOS_FreePagedMemory(Kernel->os,
+ Node->Virtual.physical,
+ Node->Virtual.bytes));
+
+ /* Release mutex before node is destroyed */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+
+ acquired = gcvFALSE;
+
+ /* Destroy the gcuVIDMEM_NODE union. */
+ gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node));
+
+ /* Node has been destroyed, so we should not touch it any more */
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+ }
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Unmapped virtual node 0x%x from 0x%08X",
+ Node, Node->Virtual.addresses[Kernel->core]);
+
+ }
+
+ else
+ {
+ /* If we need to unlock a node from virtual memory we have to be
+ ** very carefull. If the node is still inside the caches we
+ ** might get a bus error later if the cache line needs to be
+ ** replaced. So - we have to flush the caches before we do
+ ** anything. */
+
+ /* gckCommand_EnterCommit() can't be called in interrupt handler because
+ ** of a dead lock situation:
+ ** process call Command_Commit(), and acquire Command->mutexQueue in
+ ** gckCOMMAND_EnterCommit(). Then it will wait for a signal which depends
+ ** on interrupt handler to generate, if interrupt handler enter
+ ** gckCommand_EnterCommit(), process will never get the signal. */
+
+ /* So, flush cache when we still in process context, and then ask caller to
+ ** schedule a event. */
+
+ gcmkONERROR(
+ gckOS_UnlockPages(os,
+ Node->Virtual.physical,
+ Node->Virtual.bytes,
+ Node->Virtual.logical));
+
+ if (!Node->Virtual.contiguous
+ && (Node->Virtual.lockeds[Kernel->core] == 1)
+ )
+ {
+ if (Type == gcvSURF_BITMAP)
+ {
+ /* Flush 2D cache. */
+ flush = gcvFLUSH_2D;
+ }
+ else if (Type == gcvSURF_RENDER_TARGET)
+ {
+ /* Flush color cache. */
+ flush = gcvFLUSH_COLOR;
+ }
+ else if (Type == gcvSURF_DEPTH)
+ {
+ /* Flush depth cache. */
+ flush = gcvFLUSH_DEPTH;
+ }
+ else
+ {
+ /* No flush required. */
+ flush = (gceKERNEL_FLUSH) 0;
+ }
+
+ gcmkONERROR(
+ gckHARDWARE_Flush(hardware, flush, NULL, &requested));
+
+ if (requested != 0)
+ {
+ /* Acquire the command queue. */
+ gcmkONERROR(gckCOMMAND_EnterCommit(command, gcvFALSE));
+ commitEntered = gcvTRUE;
+
+ gcmkONERROR(gckCOMMAND_Reserve(
+ command, requested, &buffer, &bufferSize
+ ));
+
+ gcmkONERROR(gckHARDWARE_Flush(
+ hardware, flush, buffer, &bufferSize
+ ));
+
+ /* Mark node as pending. */
+ gcmkONERROR(gckCOMMAND_Execute(command, requested));
+
+ /* Release the command queue. */
+ gcmkONERROR(gckCOMMAND_ExitCommit(command, gcvFALSE));
+ commitEntered = gcvFALSE;
+ }
+ }
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
+ "Scheduled unlock for virtual node 0x%x",
+ Node);
+
+ /* Schedule the surface to be unlocked. */
+ *Asynchroneous = gcvTRUE;
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+
+ acquired = gcvFALSE;
+ }
+
+ /* Success. */
+ gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous));
+ return gcvSTATUS_OK;
+
+OnError:
+ if (commitEntered)
+ {
+ /* Release the command queue mutex. */
+ gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, gcvFALSE));
+ }
+
+ if (acquired)
+ {
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
+ }
+
+ /* Return the status. */
+ gcmkFOOTER();
+ return status;
+}
diff --git a/kernel_drivers/v4_cleaned/gc_hal_options_internal.h b/kernel_drivers/v4_cleaned/gc_hal_options_internal.h
new file mode 100644
index 0000000..e6f9d5d
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_options_internal.h
@@ -0,0 +1,465 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_options_internal_h_
+#define __gc_hal_options_internal_h_
+
+#include "gc_hal.h"
+
+/*
+ VIVANTE_NO_3D
+
+ This define disables support for 3D rendering.
+*/
+#ifndef VIVANTE_NO_3D
+# define VIVANTE_NO_3D 0
+#endif
+
+/*
+ gcdPRINT_VERSION
+
+ Print HAL version.
+*/
+#ifndef gcdPRINT_VERSION
+# define gcdPRINT_VERSION 0
+#endif
+
+/*
+ USE_NEW_LINUX_SIGNAL
+
+ This define enables the Linux kernel signaling between kernel and user.
+*/
+#ifndef USE_NEW_LINUX_SIGNAL
+# define USE_NEW_LINUX_SIGNAL 0
+#endif
+
+/*
+ NO_USER_DIRECT_ACCESS_FROM_KERNEL
+
+ This define enables the Linux kernel behavior accessing user memory.
+*/
+#ifndef NO_USER_DIRECT_ACCESS_FROM_KERNEL
+# define NO_USER_DIRECT_ACCESS_FROM_KERNEL 0
+#endif
+
+/*
+ PROFILE_HAL_COUNTERS
+
+ This define enables HAL counter profiling support. HW and SHADER
+ counter profiling depends on this.
+*/
+#ifndef PROFILE_HAL_COUNTERS
+# define PROFILE_HAL_COUNTERS 1
+#endif
+
+/*
+ gcdDUMP
+
+ When set to 1, a dump of all states and memory uploads, as well as other
+ hardware related execution will be printed to the debug console. This
+ data can be used for playing back applications.
+*/
+#ifndef gcdDUMP
+# define gcdDUMP 0
+#endif
+
+/*
+ gcdDUMP_API
+
+ When set to 1, a high level dump of the EGL and GL/VG APs's are
+ captured.
+*/
+#ifndef gcdDUMP_API
+# define gcdDUMP_API 0
+#endif
+
+/*
+ gcdDUMP_FRAMERATE
+ When set to a value other than zero, averaqe frame rate will be dumped.
+ The value set is the starting frame that the average will be calculated.
+ This is needed because sometimes first few frames are too slow to be included
+ in the average. Frame count starts from 1.
+*/
+#ifndef gcdDUMP_FRAMERATE
+# define gcdDUMP_FRAMERATE 0
+#endif
+
+/*
+ gcdDUMP_COMMAND
+
+ When set to non-zero, the command queue will dump all incoming command
+ and context buffers as well as all other modifications to the command
+ queue.
+*/
+#ifndef gcdDUMP_COMMAND
+# define gcdDUMP_COMMAND 0
+#endif
+
+/*
+ gcdNULL_DRIVER
+
+ Set to 1 for infinite speed hardware.
+ Set to 2 for bypassing the HAL.
+ Set to 3 for bypassing the drivers.
+*/
+#ifndef gcdNULL_DRIVER
+# define gcdNULL_DRIVER 0
+#endif
+
+/*
+ gcdCOMMAND_QUEUES
+
+ Number of command queues in the kernel.
+*/
+#ifndef gcdCOMMAND_QUEUES
+# define gcdCOMMAND_QUEUES 2
+#endif
+
+/*
+ gcdPOWER_CONTROL_DELAY
+
+ The delay in milliseconds required to wait until the GPU has woke up
+ from a suspend or power-down state. This is system dependent because
+ the bus clock also needs to stabalize.
+*/
+#ifndef gcdPOWER_CONTROL_DELAY
+# define gcdPOWER_CONTROL_DELAY 0
+#endif
+
+/*
+ gcdMMU_SIZE
+
+ Size of the MMU page table in bytes. Each 4 bytes can hold 4kB worth of
+ virtual data.
+*/
+#ifndef gcdMMU_SIZE
+# define gcdMMU_SIZE (128 << 10)
+#endif
+
+/*
+ gcdSECURE_CACHE_SLOTS
+
+ Number of slots in the logical to DMA address cache table. Each time a
+ logical address needs to be translated into a DMA address for the GPU,
+ this cache will be walked. The replacement scheme is LRU.
+*/
+#ifndef gcdSECURE_CACHE_SLOTS
+# define gcdSECURE_CACHE_SLOTS 1024
+#endif
+
+/*
+ gcdSECURE_CACHE_METHOD
+
+ Replacement scheme used for Secure Cache. The following options are
+ available:
+
+ gcdSECURE_CACHE_LRU
+ A standard LRU cache.
+
+ gcdSECURE_CACHE_LINEAR
+ A linear walker with the idea that an application will always
+ render the scene in a similar way, so the next entry in the
+ cache should be a hit most of the time.
+
+ gcdSECURE_CACHE_HASH
+ A 256-entry hash table.
+
+ gcdSECURE_CACHE_TABLE
+ A simple cache but with potential of a lot of cache replacement.
+*/
+#ifndef gcdSECURE_CACHE_METHOD
+# define gcdSECURE_CACHE_METHOD gcdSECURE_CACHE_HASH
+#endif
+
+/*
+ gcdREGISTER_ACCESS_FROM_USER
+
+ Set to 1 to allow IOCTL calls to get through from user land. This
+ should only be in debug or development drops.
+*/
+#ifndef gcdREGISTER_ACCESS_FROM_USER
+# define gcdREGISTER_ACCESS_FROM_USER 1
+#endif
+
+/*
+ gcdPOWER_MANAGEMENT
+
+ This define enables the power management code.
+*/
+#ifndef gcdPOWER_MANAGEMENT
+# define gcdPOWER_MANAGEMENT 1
+#endif
+
+/*
+ gcdGPU_TIMEOUT
+
+ This define specified the number of milliseconds the system will wait
+ before it broadcasts the GPU is stuck. In other words, it will define
+ the timeout of any operation that needs to wait for the GPU.
+
+ If the value is 0, no timeout will be checked for.
+*/
+#ifndef gcdGPU_TIMEOUT
+# define gcdGPU_TIMEOUT (2000 * 5)
+#endif
+
+/*
+ gcdGPU_ADVANCETIMER
+
+ it is advance timer.
+*/
+#ifndef gcdGPU_ADVANCETIMER
+# define gcdGPU_ADVANCETIMER 250
+#endif
+
+/*
+ gcdCMD_NO_2D_CONTEXT
+
+ This define enables no-context 2D command buffer.
+*/
+#ifndef gcdCMD_NO_2D_CONTEXT
+# define gcdCMD_NO_2D_CONTEXT 1
+#endif
+
+/*
+ gcdENABLE_BANK_ALIGNMENT
+
+ When enabled, video memory is allocated bank aligned. The vendor can modify
+ _GetSurfaceBankAlignment() and gcoSURF_GetBankOffsetBytes() to define how
+ different types of allocations are bank and channel aligned.
+ When disabled (default), no bank alignment is done.
+*/
+#ifndef gcdENABLE_BANK_ALIGNMENT
+# define gcdENABLE_BANK_ALIGNMENT 0
+#endif
+
+/*
+ gcdBANK_BIT_START
+
+ Specifies the start bit of the bank (inclusive).
+*/
+#ifndef gcdBANK_BIT_START
+# define gcdBANK_BIT_START 12
+#endif
+
+/*
+ gcdBANK_BIT_END
+
+ Specifies the end bit of the bank (inclusive).
+*/
+#ifndef gcdBANK_BIT_END
+# define gcdBANK_BIT_END 14
+#endif
+
+/*
+ gcdBANK_CHANNEL_BIT
+
+ When set, video memory when allocated bank aligned is allocated such that
+ render and depth buffer addresses alternate on the channel bit specified.
+ This option has an effect only when gcdENABLE_BANK_ALIGNMENT is enabled.
+ When disabled (default), no alteration is done.
+*/
+#ifndef gcdBANK_CHANNEL_BIT
+# define gcdBANK_CHANNEL_BIT 7
+#endif
+
+/*
+ gcdDYNAMIC_SPEED
+
+ When non-zero, it informs the kernel driver to use the speed throttling
+ broadcasting functions to inform the system the GPU should be spet up or
+ slowed down. It will send a broadcast for slowdown each "interval"
+ specified by this define in milliseconds
+ (gckOS_BroadcastCalibrateSpeed).
+*/
+#ifndef gcdDYNAMIC_SPEED
+# define gcdDYNAMIC_SPEED 2000
+#endif
+
+/*
+ gcdDYNAMIC_EVENT_THRESHOLD
+
+ When non-zero, it specifies the maximum number of available events at
+ which the kernel driver will issue a broadcast to speed up the GPU
+ (gckOS_BroadcastHurry).
+*/
+#ifndef gcdDYNAMIC_EVENT_THRESHOLD
+# define gcdDYNAMIC_EVENT_THRESHOLD 5
+#endif
+
+/*
+ gcdENABLE_PROFILING
+
+ Enable profiling macros.
+*/
+#ifndef gcdENABLE_PROFILING
+# define gcdENABLE_PROFILING 0
+#endif
+
+/*
+ gcdENABLE_128B_MERGE
+
+ Enable 128B merge for the BUS control.
+*/
+#ifndef gcdENABLE_128B_MERGE
+# define gcdENABLE_128B_MERGE 0
+#endif
+
+/*
+ gcdFRAME_DB
+
+ When non-zero, it specified the number of frames inside the frame
+ database. The frame DB will collect per-frame timestamps and hardware
+ counters.
+*/
+#ifndef gcdFRAME_DB
+# define gcdFRAME_DB 0
+# define gcdFRAME_DB_RESET 0
+#endif
+
+/*
+ gcdPAGED_MEMORY_CACHEABLE
+
+ When non-zero, paged memory will be cacheable.
+
+ Normally, driver will detemines whether a video memory
+ is cacheable or not. When cacheable is not neccessary,
+ it will be writecombine.
+
+ This option is only for those SOC which can't enable
+ writecombine without enabling cacheable.
+*/
+
+#ifndef gcdPAGED_MEMORY_CACHEABLE
+# define gcdPAGED_MEMORY_CACHEABLE 0
+#endif
+
+/*
+ gcdNONPAGED_MEMORY_CACHEABLE
+
+ When non-zero, non paged memory will be cacheable.
+*/
+
+#ifndef gcdNONPAGED_MEMORY_CACHEABLE
+# define gcdNONPAGED_MEMORY_CACHEABLE 0
+#endif
+
+/*
+ gcdNONPAGED_MEMORY_BUFFERABLE
+
+ When non-zero, non paged memory will be bufferable.
+ gcdNONPAGED_MEMORY_BUFFERABLE and gcdNONPAGED_MEMORY_CACHEABLE
+ can't be set 1 at same time
+*/
+
+#ifndef gcdNONPAGED_MEMORY_BUFFERABLE
+# define gcdNONPAGED_MEMORY_BUFFERABLE 1
+#endif
+
+/*
+ gcd6000_SUPPORT
+
+ Temporary define to enable/disable 6000 support.
+ */
+#ifndef gcd6000_SUPPORT
+# define gcd6000_SUPPORT 0
+#endif
+
+/*
+ gcdPOWEROFF_TIMEOUT
+
+ When non-zero, GPU will power off automatically from
+ idle state, and gcdPOWEROFF_TIMEOUT is also the default
+ timeout in milliseconds.
+ */
+
+#ifndef gcdPOWEROFF_TIMEOUT
+# define gcdPOWEROFF_TIMEOUT 300
+#endif
+
+/*
+ gcdUSE_VIDMEM_PER_PID
+*/
+#ifndef gcdUSE_VIDMEM_PER_PID
+# define gcdUSE_VIDMEM_PER_PID 0
+#endif
+
+/*
+ gcdENABLE_RECOVERY
+
+ This define enables the recovery code.
+*/
+#ifndef gcdENABLE_RECOVERY
+# define gcdENABLE_RECOVERY 1
+#endif
+
+/*
+ gcdENABLE_OUTER_CACHE_PATCH
+
+ Enable the outer cache patch.
+*/
+#ifndef gcdENABLE_OUTER_CACHE_PATCH
+# define gcdENABLE_OUTER_CACHE_PATCH 0
+#endif
+
+#ifndef gcdSHARED_PAGETABLE
+# define gcdSHARED_PAGETABLE 1
+#endif
+
+#ifndef gcdUSE_OPENCL
+# define gcdUSE_OPENCL 0
+#endif
+
+/*
+ gcdSMALL_BLOCK_SIZE
+
+ When non-zero, a part of VIDMEM will be reserved for requests
+ whose requesting size is less than gcdSMALL_BLOCK_SIZE.
+
+ For Linux, it's the size of a page. If this requeset fallbacks
+ to gcvPOOL_CONTIGUOUS or gcvPOOL_VIRTUAL, memory will be wasted
+ because they allocate a page at least.
+ */
+#ifndef gcdSMALL_BLOCK_SIZE
+# define gcdSMALL_BLOCK_SIZE 4096
+# define gcdRATIO_FOR_SMALL_MEMORY 32
+#endif
+
+/******************************************************************************\
+************************************* Debug ************************************
+\******************************************************************************/
+
+/* Possible debug flags. */
+#define gcdDEBUG_NONE 0
+#define gcdDEBUG_ALL (1 << 0)
+#define gcdDEBUG_FATAL (1 << 1)
+#define gcdDEBUG_TRACE (1 << 2)
+#define gcdDEBUG_BREAK (1 << 3)
+#define gcdDEBUG_ASSERT (1 << 4)
+#define gcdDEBUG_CODE (1 << 5)
+#define gcdDEBUG_STACK (1 << 6)
+
+#define gcmIS_DEBUG(flag) ( gcdDEBUG & (flag | gcdDEBUG_ALL) )
+
+#ifndef gcdDEBUG
+# define gcdDEBUG gcdDEBUG_NONE
+#endif
+
+#endif /* __gc_hal_options_internal_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_types_internal.h b/kernel_drivers/v4_cleaned/gc_hal_types_internal.h
new file mode 100644
index 0000000..45a8626
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_types_internal.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_types_internal_h_
+#define __gc_hal_types_internal_h_
+
+#include <linux/stddef.h>
+
+/******************************************************************************\
+********************************** Common Types ********************************
+\******************************************************************************/
+
+#define gcvFALSE 0
+#define gcvTRUE 1
+
+#define gcvINFINITE ((u32) ~0U)
+
+/* Stringizing macro. */
+#define gcmSTRING(Value) #Value
+
+#define gcmPRINTABLE(c) ((((c) >= ' ') && ((c) <= '}')) ? ((c) != '%' ? (c) : ' ') : ' ')
+
+#define gcmCC_PRINT(cc) \
+ gcmPRINTABLE((char) ( (cc) & 0xFF)), \
+ gcmPRINTABLE((char) (((cc) >> 8) & 0xFF)), \
+ gcmPRINTABLE((char) (((cc) >> 16) & 0xFF)), \
+ gcmPRINTABLE((char) (((cc) >> 24) & 0xFF))
+
+/******************************************************************************\
+********************************* Status Macros ********************************
+\******************************************************************************/
+
+#define gcmIS_ERROR(status) (status < 0)
+#define gcmIS_SUCCESS(status) (status == gcvSTATUS_OK)
+
+/******************************************************************************\
+********************************* Field Macros *********************************
+\******************************************************************************/
+
+#define __gcmSTART(reg_field) \
+ (0 ? reg_field)
+
+#define __gcmEND(reg_field) \
+ (1 ? reg_field)
+
+#define __gcmGETSIZE(reg_field) \
+ (__gcmEND(reg_field) - __gcmSTART(reg_field) + 1)
+
+#define __gcmALIGN(data, reg_field) \
+ (((u32) (data)) << __gcmSTART(reg_field))
+
+#define __gcmMASK(reg_field) \
+ ((u32) ((__gcmGETSIZE(reg_field) == 32) \
+ ? ~0 \
+ : (~(~0 << __gcmGETSIZE(reg_field)))))
+
+/*******************************************************************************
+**
+** gcmFIELDMASK
+**
+** Get aligned field mask.
+**
+** ARGUMENTS:
+**
+** reg_field Bit field.
+*/
+#define gcmFIELDMASK(reg_field) \
+( \
+ __gcmALIGN(__gcmMASK(reg_field), reg_field) \
+)
+
+/*******************************************************************************
+**
+** gcmSETFIELD
+**
+** Set the value of a field within specified data.
+**
+** ARGUMENTS:
+**
+** data Data value.
+** reg_field Bit field.
+** value Value for field.
+*/
+#define gcmSETFIELD(data, reg_field, value) \
+( \
+ (((u32) (data)) \
+ & ~__gcmALIGN(__gcmMASK(reg_field), reg_field)) \
+ | __gcmALIGN((u32) (value) \
+ & __gcmMASK(reg_field), reg_field) \
+)
+
+/*******************************************************************************
+**
+** gcmGETFIELD
+**
+** Get the value of a field within specified data.
+**
+** ARGUMENTS:
+**
+** data Data value.
+** reg_field Bit field.
+*/
+#define gcmGETFIELD(data, reg_field) \
+( \
+ ((((u32) (data)) >> __gcmSTART(reg_field)) & __gcmMASK(reg_field)) \
+)
+
+/*******************************************************************************
+**
+** gcmVERIFYFIELDVALUE
+**
+** Verify if the value of a field within specified data equals a
+** predefined value.
+**
+** ARGUMENTS:
+**
+** data Data value.
+** reg_field Bit field.
+** value Name of the value within the field.
+*/
+#define gcmVERIFYFIELDVALUE(data, reg_field, value) \
+( \
+ (((u32) (data)) >> __gcmSTART(reg_field) & \
+ __gcmMASK(reg_field)) \
+ == \
+ (value & __gcmMASK(reg_field)) \
+)
+
+/*******************************************************************************
+**
+** gcmPTR2INT
+**
+** Convert a pointer to an integer value.
+**
+** ARGUMENTS:
+**
+** p Pointer value.
+*/
+#if defined(__LP64__) && __LP64__
+# define gcmPTR2INT(p) \
+ ( \
+ (u32) (u64) (p) \
+ )
+#else
+# define gcmPTR2INT(p) \
+ ( \
+ (u32) (p) \
+ )
+#endif
+
+/*******************************************************************************
+**
+** gcmINT2PTR
+**
+** Convert an integer value into a pointer.
+**
+** ARGUMENTS:
+**
+** v Integer value.
+*/
+#ifdef __LP64__
+# define gcmINT2PTR(i) \
+ ( \
+ (void *) (s64) (i) \
+ )
+#else
+# define gcmINT2PTR(i) \
+ ( \
+ (void *) (i) \
+ )
+#endif
+
+#endif /* __gc_hal_types_internal_h_ */
diff --git a/kernel_drivers/v4_cleaned/gc_hal_version.h b/kernel_drivers/v4_cleaned/gc_hal_version.h
new file mode 100644
index 0000000..252923c
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_version.h
@@ -0,0 +1,36 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_version_h_
+#define __gc_hal_version_h_
+
+#define gcvVERSION_MAJOR 4
+
+#define gcvVERSION_MINOR 6
+
+#define gcvVERSION_PATCH 6
+
+#define gcvVERSION_BUILD 1381
+
+#define gcvVERSION_DATE __DATE__
+
+#define gcvVERSION_TIME __TIME__
+
+#endif /* __gc_hal_version_h_ */
diff --git a/kernel_drivers/v4_cleaned/notes.txt b/kernel_drivers/v4_cleaned/notes.txt
new file mode 100644
index 0000000..b90c672
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/notes.txt
@@ -0,0 +1,46 @@
+gckMATH_ModuloInt: can be inlined, is used only in two places
+gcoDUMP: can be removed
+
+
+All gco functions can go, these are userspace.
+Same for gcm (not gcmk)
+
+gc_hal_profiler_internal.h, except for one #define
+
+TODO
+
+- Don't need the profileFileName (VIVANTE_PROFILER) and profileEnabled on
+ _gckKERNEL.
+
+ Used only by commands gcvHAL_GET_PROFILE_SETTING and gcvHAL_SET_PROFILE_SETTING,
+ for the user space to store the profiling name and status inside the kernel.
+
+- Semaphores/queues: could simply use Linux built-in objects instead of an
+ abstraction.
+
+- gckOS_QueryNeedCopy: can be replaced by TRUE or FALSE based on NO_USER_DIRECT_ACCESS_FROM_KERNEL
+ Inlined?
+ Doesn't need any of the argument validation
+
+- gcdNULL_DRIVER: not needed
+
+- Commented out code (#if 0) in gckCOMMAND_Commit
+
+ /* Determine context entry and exit points. */
+
+Bugs?
+---------
+
+- Unnecessary check in context update
+Line 1419 in gckCONTEXT_Update: if (j >= Context->stateCount)
+Why? Seems wrong.
+ ("more updates than states" ok, can't be logical)
+
+
+Optimization
+----------------
+Cache settings
+
+- gcdNONPAGED_MEMORY_CACHEABLE
+
+