diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-08-01 11:36:35 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-08-01 11:37:49 +0200 |
commit | 95e524cd35a28f14cdb2215e033ba1b96321103b (patch) | |
tree | f471c60d691fd457d44c2144bea358adf57c2b3a /kernel_drivers | |
parent | 33640fd3142e141586c749bbe75ef35542908fd8 (diff) |
Add cleaned v4 driver from GCW tree
Much easier to read than the others.
Diffstat (limited to 'kernel_drivers')
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(¤tTime)); + + 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(¤tTime)); + + 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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->mm->mmap_sem); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } +#endif /* NO_DMA_COHERENT */ + + up_write(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->mm->mmap_sem); + result = get_user_pages(current, + current->mm, + memory & PAGE_MASK, + pageCount, + 1, + 0, + pages, + NULL + ); + up_read(¤t->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 + + |