| /*************************************************************************/ /*! |
| @File |
| @Title RGX ray tracing routines |
| @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved |
| @Description RGX ray tracing routines |
| @License Dual MIT/GPLv2 |
| |
| The contents of this file are subject to the MIT license as set out below. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal |
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the Software is |
| furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in |
| all copies or substantial portions of the Software. |
| |
| Alternatively, the contents of this file may be used under the terms of |
| the GNU General Public License Version 2 ("GPL") in which case the provisions |
| of GPL are applicable instead of those above. |
| |
| If you wish to allow use of your version of this file only under the terms of |
| GPL, and not to allow others to use your version of this file under the terms |
| of the MIT license, indicate your decision by deleting the provisions above |
| and replace them with the notice and other provisions required by GPL as set |
| out in the file called "GPL-COPYING" included in this distribution. If you do |
| not delete the provisions above, a recipient may use your version of this file |
| under the terms of either the MIT license or GPL. |
| |
| This License is also included in this distribution in the file called |
| "MIT-COPYING". |
| |
| EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS |
| PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING |
| BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
| PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR |
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ /**************************************************************************/ |
| /* for the offsetof macro */ |
| #include <stddef.h> |
| #if defined(INTEGRITY_OS) |
| #include <string.h> |
| #endif |
| |
| #include "pdump_km.h" |
| #include "pvr_debug.h" |
| #include "rgxutils.h" |
| #include "rgxfwutils.h" |
| #include "rgxray.h" |
| #include "rgxmem.h" |
| #include "allocmem.h" |
| #include "devicemem.h" |
| #include "devicemem_pdump.h" |
| #include "devicemem_server.h" |
| #include "osfunc.h" |
| #include "pvrsrv.h" |
| #include "rgxccb.h" |
| #include "rgxhwperf.h" |
| #include "rgxtimerquery.h" |
| #include "htbuffer.h" |
| |
| #include "rgxdefs_km.h" |
| #include "rgx_fwif_km.h" |
| #include "physmem.h" |
| #include "sync_server.h" |
| #include "sync_internal.h" |
| #include "sync.h" |
| #include "process_stats.h" |
| |
| |
| /* |
| * FIXME: Defs copied from "rgxrpmdefs.h" |
| */ |
| |
| typedef struct _RGX_RPM_DATA_RTU_FREE_PAGE_LIST { |
| IMG_UINT32 u32_0; |
| } RGX_RPM_DATA_RTU_FREE_PAGE_LIST; |
| |
| /* |
| Page table index. |
| The field is a pointer to a free page |
| */ |
| #define RGX_RPM_DATA_RTU_FREE_PAGE_LIST_PTI_WOFF (0U) |
| #define RGX_RPM_DATA_RTU_FREE_PAGE_LIST_PTI_SHIFT (0U) |
| #define RGX_RPM_DATA_RTU_FREE_PAGE_LIST_PTI_CLRMSK (0XFFC00000U) |
| #define RGX_RPM_DATA_RTU_FREE_PAGE_LIST_SET_PTI(_ft_,_x_) ((_ft_).u32_0 = (((_ft_).u32_0 & RGX_RPM_DATA_RTU_FREE_PAGE_LIST_PTI_CLRMSK ) | (((_x_) & (0x003fffff)) << 0))) |
| #define RGX_RPM_DATA_RTU_FREE_PAGE_LIST_GET_PTI(_ft_) (((_ft_).u32_0 >> (0)) & 0x003fffff) |
| |
| typedef struct _RGX_RPM_DATA_RTU_PAGE_TABLE { |
| IMG_UINT32 u32_0; |
| } RGX_RPM_DATA_RTU_PAGE_TABLE; |
| |
| /* |
| Page Table State |
| <br> 00: Empty Block |
| <br> 01: Full Block |
| <br> 10: Fragmented Block: Partially full page |
| */ |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_PTS_WOFF (0U) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_PTS_SHIFT (30U) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_PTS_CLRMSK (0X3FFFFFFFU) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_SET_PTS(_ft_,_x_) ((_ft_).u32_0 = (((_ft_).u32_0 & RGX_RPM_DATA_RTU_PAGE_TABLE_PTS_CLRMSK ) | (((_x_) & (0x00000003)) << 30))) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_GET_PTS(_ft_) (((_ft_).u32_0 >> (30)) & 0x00000003) |
| /* |
| Primitives in Page. |
| Number of unique primitives stored in this page. |
| The memory manager will re-use this page when the RCNT drops to zero. |
| */ |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_RCNT_WOFF (0U) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_RCNT_SHIFT (22U) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_RCNT_CLRMSK (0XC03FFFFFU) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_SET_RCNT(_ft_,_x_) ((_ft_).u32_0 = (((_ft_).u32_0 & RGX_RPM_DATA_RTU_PAGE_TABLE_RCNT_CLRMSK ) | (((_x_) & (0x000000ff)) << 22))) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_GET_RCNT(_ft_) (((_ft_).u32_0 >> (22)) & 0x000000ff) |
| /* |
| Next page table index. |
| The field is a pointer to the next page for this primitive. |
| */ |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_NPTI_WOFF (0U) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_NPTI_SHIFT (0U) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_NPTI_CLRMSK (0XFFC00000U) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_SET_NPTI(_ft_,_x_) ((_ft_).u32_0 = (((_ft_).u32_0 & RGX_RPM_DATA_RTU_PAGE_TABLE_NPTI_CLRMSK ) | (((_x_) & (0x003fffff)) << 0))) |
| #define RGX_RPM_DATA_RTU_PAGE_TABLE_GET_NPTI(_ft_) (((_ft_).u32_0 >> (0)) & 0x003fffff) |
| |
| |
| #define RGX_CR_RPM_PAGE_TABLE_BASE_VALUE_ALIGNSHIFT (2U) |
| #define RGX_CR_RPM_SHF_FPL_BASE_ALIGNSHIFT (2U) |
| |
| |
| typedef struct { |
| DEVMEM_MEMDESC *psContextStateMemDesc; |
| RGX_SERVER_COMMON_CONTEXT *psServerCommonContext; |
| IMG_UINT32 ui32Priority; |
| #if 0 |
| /* FIXME - multiple frame contexts? */ |
| RGX_RPM_FREELIST *psSHFFreeList; |
| RGX_RPM_FREELIST *psSHGFreeList; |
| #endif |
| } RGX_SERVER_RAY_SH_DATA; |
| |
| |
| typedef enum { |
| NODE_EMPTY = 0, |
| NODE_SCENE_HIERARCHY, |
| NODE_RPM_PAGE_TABLE, |
| NODE_RPM_FREE_PAGE_LIST |
| } RGX_DEVMEM_NODE_TYPE; |
| |
| typedef struct _RGX_DEVMEM_NODE_ { |
| RGX_DEVMEM_NODE_TYPE eNodeType; /*!< Alloc type */ |
| PMR *psPMR; /*!< Scene hierarchy/page table/free page list phys pages */ |
| DEVMEMINT_HEAP *psDevMemHeap; /*!< Heap where the virtual mapping is made */ |
| IMG_DEV_VIRTADDR sAddr; /*!< GPU virtual address where the phys pages are mapped into */ |
| IMG_UINT32 ui32NumPhysPages; /*!< Number of physical pages mapped in for this node */ |
| IMG_UINT32 ui32StartOfMappingIndex; /*!< Start of mapping index (i.e. OS page offset from virtual base) */ |
| IMG_BOOL bInternal; |
| } RGX_DEVMEM_NODE; |
| |
| typedef struct _RGX_RPM_DEVMEM_DESC_ { |
| DLLIST_NODE sMemoryDescBlock; /*!< the hierarchy scene memory block */ |
| RGX_RPM_FREELIST *psFreeList; /*!< Free list this allocation is associated with */ |
| IMG_UINT32 ui32NumPages; /*!< Number of RPM pages added */ |
| RGX_DEVMEM_NODE sSceneHierarchyNode; /*!< scene hierarchy block descriptor */ |
| RGX_DEVMEM_NODE sRPMPageListNode; /*!< RPM page list block descriptor */ |
| RGX_DEVMEM_NODE sRPMFreeListNode; /*!< RPM free list block descriptor */ |
| } RGX_RPM_DEVMEM_DESC; |
| |
| typedef struct _DEVMEM_RPM_FREELIST_LOOKUP_ |
| { |
| IMG_UINT32 ui32FreeListID; |
| RGX_RPM_FREELIST *psFreeList; |
| } DEVMEM_RPM_FREELIST_LOOKUP; |
| |
| typedef struct { |
| RGX_SERVER_COMMON_CONTEXT *psServerCommonContext; |
| IMG_UINT32 ui32Priority; |
| RGX_CLIENT_CCB *psFCClientCCB[DPX_MAX_RAY_CONTEXTS]; |
| DEVMEM_MEMDESC *psFCClientCCBMemDesc[DPX_MAX_RAY_CONTEXTS]; |
| DEVMEM_MEMDESC *psFCClientCCBCtrlMemDesc[DPX_MAX_RAY_CONTEXTS]; |
| } RGX_SERVER_RAY_RS_DATA; |
| |
| |
| struct _RGX_SERVER_RAY_CONTEXT_ { |
| PVRSRV_DEVICE_NODE *psDeviceNode; |
| DEVMEM_MEMDESC *psFWRayContextMemDesc; |
| DEVMEM_MEMDESC *psFWFrameworkMemDesc; |
| RGX_SERVER_RAY_SH_DATA sSHData; |
| RGX_SERVER_RAY_RS_DATA sRSData; |
| IMG_UINT32 ui32CleanupStatus; |
| #define RAY_CLEANUP_SH_COMPLETE (1 << 0) |
| #define RAY_CLEANUP_RS_COMPLETE (1 << 1) |
| PVRSRV_CLIENT_SYNC_PRIM *psCleanupSync; |
| DLLIST_NODE sListNode; |
| SYNC_ADDR_LIST sSyncAddrListFence; |
| SYNC_ADDR_LIST sSyncAddrListUpdate; |
| ATOMIC_T hJobId; |
| }; |
| |
| |
| #if 0 |
| static |
| #ifdef __GNUC__ |
| __attribute__((noreturn)) |
| #endif |
| void sleep_for_ever(void) |
| { |
| #if defined(__KLOCWORK__) // klocworks would report an infinite loop because of while(1). |
| PVR_ASSERT(0); |
| #else |
| while(1) |
| { |
| OSSleepms(~0); // sleep the maximum amount of time possible |
| } |
| #endif |
| } |
| #endif |
| |
| static |
| PVRSRV_ERROR _RGXCreateRPMSparsePMR(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| RGX_DEVMEM_NODE_TYPE eBlockType, |
| IMG_UINT32 ui32NumPages, |
| IMG_UINT32 uiLog2DopplerPageSize, |
| PMR **ppsPMR); |
| |
| static PVRSRV_ERROR _RGXMapRPMPBBlock(RGX_DEVMEM_NODE *psDevMemNode, |
| RGX_RPM_FREELIST *psFreeList, |
| RGX_DEVMEM_NODE_TYPE eBlockType, |
| DEVMEMINT_HEAP *psDevmemHeap, |
| IMG_UINT32 ui32NumPages, |
| IMG_DEV_VIRTADDR sDevVAddrBase); |
| |
| static |
| PVRSRV_ERROR _RGXUnmapRPMPBBlock(RGX_DEVMEM_NODE *psDevMemNode, |
| RGX_RPM_FREELIST *psFreeList, |
| IMG_DEV_VIRTADDR sDevVAddrBase); |
| |
| static |
| PVRSRV_ERROR _CreateSHContext(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| DEVMEM_MEMDESC *psAllocatedMemDesc, |
| IMG_UINT32 ui32AllocatedOffset, |
| DEVMEM_MEMDESC *psFWMemContextMemDesc, |
| IMG_DEV_VIRTADDR sVRMCallStackAddr, |
| IMG_UINT32 ui32Priority, |
| RGX_COMMON_CONTEXT_INFO *psInfo, |
| RGX_SERVER_RAY_SH_DATA *psSHData) |
| { |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_VRDMCTX_STATE *psContextState; |
| PVRSRV_ERROR eError; |
| /* |
| Allocate device memory for the firmware GPU context suspend state. |
| Note: the FW reads/writes the state to memory by accessing the GPU register interface. |
| */ |
| PDUMPCOMMENT("Allocate RGX firmware SHG context suspend state"); |
| |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(RGXFWIF_VRDMCTX_STATE), |
| RGX_FWCOMCTX_ALLOCFLAGS, |
| "FwRaySHGContextSuspendState", |
| &psSHData->psContextStateMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to allocate firmware GPU context suspend state (%u)", |
| eError)); |
| goto fail_shcontextsuspendalloc; |
| } |
| |
| eError = DevmemAcquireCpuVirtAddr(psSHData->psContextStateMemDesc, |
| (void **)&psContextState); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to map firmware render context state (%u)", |
| eError)); |
| goto fail_suspendcpuvirtacquire; |
| } |
| psContextState->uVRDMReg_VRM_CALL_STACK_POINTER = sVRMCallStackAddr.uiAddr; |
| DevmemReleaseCpuVirtAddr(psSHData->psContextStateMemDesc); |
| |
| eError = FWCommonContextAllocate(psConnection, |
| psDeviceNode, |
| REQ_TYPE_SH, |
| RGXFWIF_DM_SHG, |
| psAllocatedMemDesc, |
| ui32AllocatedOffset, |
| psFWMemContextMemDesc, |
| psSHData->psContextStateMemDesc, |
| RGX_RTU_CCB_SIZE_LOG2, |
| ui32Priority, |
| psInfo, |
| &psSHData->psServerCommonContext); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to init TA fw common context (%u)", |
| eError)); |
| goto fail_shcommoncontext; |
| } |
| |
| /* |
| * Dump the FW SH context suspend state buffer |
| */ |
| PDUMPCOMMENT("Dump the SH context suspend state buffer"); |
| DevmemPDumpLoadMem(psSHData->psContextStateMemDesc, |
| 0, |
| sizeof(RGXFWIF_VRDMCTX_STATE), |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| psSHData->ui32Priority = ui32Priority; |
| return PVRSRV_OK; |
| |
| fail_shcommoncontext: |
| fail_suspendcpuvirtacquire: |
| DevmemFwFree(psDevInfo, psSHData->psContextStateMemDesc); |
| fail_shcontextsuspendalloc: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| |
| return eError; |
| } |
| |
| static |
| PVRSRV_ERROR _CreateRSContext(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| DEVMEM_MEMDESC *psAllocatedMemDesc, |
| IMG_UINT32 ui32AllocatedOffset, |
| DEVMEM_MEMDESC *psFWMemContextMemDesc, |
| IMG_UINT32 ui32Priority, |
| RGX_COMMON_CONTEXT_INFO *psInfo, |
| RGX_SERVER_RAY_RS_DATA *psRSData) |
| { |
| PVRSRV_ERROR eError; |
| |
| eError = FWCommonContextAllocate(psConnection, |
| psDeviceNode, |
| REQ_TYPE_RS, |
| RGXFWIF_DM_RTU, |
| psAllocatedMemDesc, |
| ui32AllocatedOffset, |
| psFWMemContextMemDesc, |
| NULL, |
| RGX_RTU_CCB_SIZE_LOG2, |
| ui32Priority, |
| psInfo, |
| &psRSData->psServerCommonContext); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to init 3D fw common context (%u)", |
| eError)); |
| goto fail_rscommoncontext; |
| } |
| |
| psRSData->ui32Priority = ui32Priority; |
| return PVRSRV_OK; |
| |
| fail_rscommoncontext: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| |
| return eError; |
| } |
| |
| |
| /* |
| Static functions used by ray context code |
| */ |
| |
| static |
| PVRSRV_ERROR _DestroySHContext(RGX_SERVER_RAY_SH_DATA *psSHData, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| PVRSRV_CLIENT_SYNC_PRIM *psCleanupSync) |
| { |
| PVRSRV_ERROR eError; |
| |
| /* Check if the FW has finished with this resource ... */ |
| eError = RGXFWRequestCommonContextCleanUp(psDeviceNode, |
| psSHData->psServerCommonContext, |
| psCleanupSync, |
| RGXFWIF_DM_SHG, |
| PDUMP_FLAGS_NONE); |
| if (eError == PVRSRV_ERROR_RETRY) |
| { |
| return eError; |
| } |
| else if (eError != PVRSRV_OK) |
| { |
| PVR_LOG(("%s: Unexpected error from RGXFWRequestCommonContextCleanUp (%s)", |
| __FUNCTION__, |
| PVRSRVGetErrorStringKM(eError))); |
| return eError; |
| } |
| |
| /* ... it has so we can free its resources */ |
| FWCommonContextFree(psSHData->psServerCommonContext); |
| DevmemFwFree(psDeviceNode->pvDevice, psSHData->psContextStateMemDesc); |
| psSHData->psContextStateMemDesc = NULL; |
| psSHData->psServerCommonContext = NULL; |
| return PVRSRV_OK; |
| } |
| |
| static |
| PVRSRV_ERROR _DestroyRSContext(RGX_SERVER_RAY_RS_DATA *psRSData, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| PVRSRV_CLIENT_SYNC_PRIM *psCleanupSync) |
| { |
| PVRSRV_ERROR eError; |
| |
| /* Check if the FW has finished with this resource ... */ |
| eError = RGXFWRequestCommonContextCleanUp(psDeviceNode, |
| psRSData->psServerCommonContext, |
| psCleanupSync, |
| RGXFWIF_DM_RTU, |
| PDUMP_FLAGS_NONE); |
| if (eError == PVRSRV_ERROR_RETRY) |
| { |
| return eError; |
| } |
| else if (eError != PVRSRV_OK) |
| { |
| PVR_LOG(("%s: Unexpected error from RGXFWRequestCommonContextCleanUp (%s)", |
| __FUNCTION__, |
| PVRSRVGetErrorStringKM(eError))); |
| return eError; |
| } |
| |
| /* ... it has so we can free its resources */ |
| |
| |
| FWCommonContextFree(psRSData->psServerCommonContext); |
| psRSData->psServerCommonContext = NULL; |
| return PVRSRV_OK; |
| } |
| |
| |
| /* |
| * RPM driver management rev 2 |
| * |
| * The RPM freelists are opaque to the client driver. Scene Hierarchy pages |
| * are managed in Blocks (analogous to PB blocks) which are alloc'd in KM |
| * and mapped into the client MMU context. |
| * |
| * Page tables are set up for each existing Scene Memory Block. |
| * |
| * Freelist entries are also updated according to the list of Scene Memory Blocks. |
| * |
| * NOTES: |
| * |
| * (1) Scene Hierarchy shrink is not expected to be used. |
| * (2) The RPM FreeLists are Circular buffers and must be contiguous in virtual space |
| * (3) Each PMR is created with no phys backing pages. Pages are mapped in on-demand |
| * via RGXGrowRPMFreeList. |
| * |
| */ |
| #if defined(DEBUG) |
| static PVRSRV_ERROR _ReadRPMFreePageList(PMR *psPMR, |
| IMG_DEVMEM_OFFSET_T uiLogicalOffset, |
| IMG_UINT32 ui32PageCount) |
| { |
| PVRSRV_ERROR eError; |
| IMG_UINT32 uiIdx, j; |
| size_t uNumBytesCopied; |
| RGX_RPM_DATA_RTU_FREE_PAGE_LIST *psFreeListBuffer; |
| IMG_UINT32 ui32PTI[4]; |
| |
| /* Allocate scratch area for setting up Page table indices */ |
| psFreeListBuffer = OSAllocMem(ui32PageCount * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST)); |
| if (psFreeListBuffer == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_WriteRPMPageList: failed to allocate scratch page table")); |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| |
| /* Read scratch buffer from PMR (FPL entries must be contiguous) */ |
| eError = PMR_ReadBytes(psPMR, |
| uiLogicalOffset, |
| (IMG_UINT8 *) psFreeListBuffer, |
| ui32PageCount * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST), |
| &uNumBytesCopied); |
| |
| if (eError == PVRSRV_OK) |
| { |
| for (uiIdx = 0; uiIdx < ui32PageCount; uiIdx +=4) |
| { |
| for (j=0; j<4; j++) |
| { |
| ui32PTI[j] = RGX_RPM_DATA_RTU_FREE_PAGE_LIST_GET_PTI(psFreeListBuffer[uiIdx + j]); |
| } |
| PVR_DPF((PVR_DBG_MESSAGE, "%4d: %7d %7d %7d %7d", uiIdx, |
| ui32PTI[0], ui32PTI[1], ui32PTI[2], ui32PTI[3])); |
| } |
| } |
| |
| /* Free scratch buffer */ |
| OSFreeMem(psFreeListBuffer); |
| |
| return eError; |
| } |
| |
| static IMG_BOOL RGXDumpRPMFreeListPageList(RGX_RPM_FREELIST *psFreeList) |
| { |
| PVR_LOG(("RPM Freelist FWAddr 0x%08x, ID = %d, CheckSum 0x%016llx", |
| psFreeList->sFreeListFWDevVAddr.ui32Addr, |
| psFreeList->ui32FreelistID, |
| psFreeList->ui64FreelistChecksum)); |
| |
| /* Dump FreeList page list */ |
| _ReadRPMFreePageList(psFreeList->psFreeListPMR, 0, psFreeList->ui32CurrentFLPages); |
| |
| return IMG_TRUE; |
| } |
| #endif |
| |
| static PVRSRV_ERROR _UpdateFwRPMFreelistSize(RGX_RPM_FREELIST *psFreeList, |
| IMG_BOOL bGrow, |
| IMG_BOOL bRestartRPM, |
| IMG_UINT32 ui32DeltaSize) |
| { |
| PVRSRV_ERROR eError; |
| RGXFWIF_KCCB_CMD sGPCCBCmd; |
| |
| if(!bGrow) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_UpdateFwRPMFreelistSize: RPM freelist shrink not supported.")); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* send feedback */ |
| sGPCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_DOPPLER_MEMORY_GROW; |
| sGPCCBCmd.uCmdData.sFreeListGSData.sFreeListFWDevVAddr.ui32Addr = psFreeList->sFreeListFWDevVAddr.ui32Addr; |
| sGPCCBCmd.uCmdData.sFreeListGSData.ui32DeltaSize = ui32DeltaSize; |
| sGPCCBCmd.uCmdData.sFreeListGSData.ui32NewSize = |
| ((bRestartRPM) ? RGX_FREELIST_GSDATA_RPM_RESTART_EN : 0) | |
| psFreeList->ui32CurrentFLPages; |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "Send FW update: RPM freelist [FWAddr=0x%08x] has 0x%08x pages", |
| psFreeList->sFreeListFWDevVAddr.ui32Addr, |
| psFreeList->ui32CurrentFLPages)); |
| |
| /* Submit command to the firmware. */ |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError = RGXScheduleCommand(psFreeList->psDevInfo, |
| RGXFWIF_DM_GP, |
| &sGPCCBCmd, |
| sizeof(sGPCCBCmd), |
| 0, |
| PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_ERROR_RETRY) |
| { |
| break; |
| } |
| OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT); |
| } END_LOOP_UNTIL_TIMEOUT(); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_UpdateFwRPMFreelistSize: failed to update FW freelist size. (error = %u)", eError)); |
| return eError; |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| #if 0 |
| static void _CheckRPMFreelist(RGX_RPM_FREELIST *psFreeList, |
| IMG_UINT32 ui32NumOfPagesToCheck, |
| IMG_UINT64 ui64ExpectedCheckSum, |
| IMG_UINT64 *pui64CalculatedCheckSum) |
| { |
| #if defined(NO_HARDWARE) |
| /* No checksum needed as we have all information in the pdumps */ |
| PVR_UNREFERENCED_PARAMETER(psFreeList); |
| PVR_UNREFERENCED_PARAMETER(ui32NumOfPagesToCheck); |
| PVR_UNREFERENCED_PARAMETER(ui64ExpectedCheckSum); |
| *pui64CalculatedCheckSum = 0; |
| #else |
| PVRSRV_ERROR eError; |
| size_t uiNumBytes; |
| IMG_UINT8* pui8Buffer; |
| IMG_UINT32* pui32Buffer; |
| IMG_UINT32 ui32CheckSumAdd = 0; |
| IMG_UINT32 ui32CheckSumXor = 0; |
| IMG_UINT32 ui32Entry; |
| IMG_UINT32 ui32Entry2; |
| IMG_BOOL bFreelistBad = IMG_FALSE; |
| |
| *pui64CalculatedCheckSum = 0; |
| |
| /* Allocate Buffer of the size of the freelist */ |
| pui8Buffer = OSAllocMem(psFreeList->ui32CurrentFLPages * sizeof(IMG_UINT32)); |
| if (pui8Buffer == NULL) |
| { |
| PVR_LOG(("_CheckRPMFreelist: Failed to allocate buffer to check freelist %p!", psFreeList)); |
| sleep_for_ever(); |
| //PVR_ASSERT(0); |
| return; |
| } |
| |
| /* Copy freelist content into Buffer */ |
| eError = PMR_ReadBytes(psFreeList->psFreeListPMR, |
| psFreeList->uiFreeListPMROffset + (psFreeList->ui32MaxFLPages - psFreeList->ui32CurrentFLPages) * sizeof(IMG_UINT32), |
| pui8Buffer, |
| psFreeList->ui32CurrentFLPages * sizeof(IMG_UINT32), |
| &uiNumBytes); |
| if (eError != PVRSRV_OK) |
| { |
| OSFreeMem(pui8Buffer); |
| PVR_LOG(("_CheckRPMFreelist: Failed to get freelist data for RPM freelist %p!", psFreeList)); |
| sleep_for_ever(); |
| //PVR_ASSERT(0); |
| return; |
| } |
| |
| PVR_ASSERT(uiNumBytes == psFreeList->ui32CurrentFLPages * sizeof(IMG_UINT32)); |
| PVR_ASSERT(ui32NumOfPagesToCheck <= psFreeList->ui32CurrentFLPages); |
| |
| /* Generate checksum */ |
| pui32Buffer = (IMG_UINT32 *)pui8Buffer; |
| for(ui32Entry = 0; ui32Entry < ui32NumOfPagesToCheck; ui32Entry++) |
| { |
| ui32CheckSumAdd += pui32Buffer[ui32Entry]; |
| ui32CheckSumXor ^= pui32Buffer[ui32Entry]; |
| |
| /* Check for double entries */ |
| for (ui32Entry2 = 0; ui32Entry2 < ui32NumOfPagesToCheck; ui32Entry2++) |
| { |
| if ((ui32Entry != ui32Entry2) && |
| (pui32Buffer[ui32Entry] == pui32Buffer[ui32Entry2])) |
| { |
| PVR_LOG(("_CheckRPMFreelist: RPM Freelist consistency failure: FW addr: 0x%08X, Double entry found 0x%08x on idx: %d and %d of %d", |
| psFreeList->sFreeListFWDevVAddr.ui32Addr, |
| pui32Buffer[ui32Entry2], |
| ui32Entry, |
| ui32Entry2, |
| psFreeList->ui32CurrentFLPages)); |
| bFreelistBad = IMG_TRUE; |
| } |
| } |
| } |
| |
| OSFreeMem(pui8Buffer); |
| |
| /* Check the calculated checksum against the expected checksum... */ |
| *pui64CalculatedCheckSum = ((IMG_UINT64)ui32CheckSumXor << 32) | ui32CheckSumAdd; |
| |
| if (ui64ExpectedCheckSum != 0 && ui64ExpectedCheckSum != *pui64CalculatedCheckSum) |
| { |
| PVR_LOG(("_CheckRPMFreelist: Checksum mismatch for RPM freelist %p! Expected 0x%016llx calculated 0x%016llx", |
| psFreeList, ui64ExpectedCheckSum, *pui64CalculatedCheckSum)); |
| bFreelistBad = IMG_TRUE; |
| } |
| |
| if (bFreelistBad) |
| { |
| PVR_LOG(("_CheckRPMFreelist: Sleeping for ever!")); |
| sleep_for_ever(); |
| // PVR_ASSERT(!bFreelistBad); |
| } |
| #endif |
| } |
| #endif |
| |
| static PVRSRV_ERROR _WriteRPMFreePageList(PMR *psPMR, |
| IMG_DEVMEM_OFFSET_T uiLogicalOffset, |
| IMG_UINT32 ui32NextPageIndex, |
| IMG_UINT32 ui32PageCount) |
| { |
| PVRSRV_ERROR eError; |
| IMG_UINT32 uiIdx; |
| size_t uNumBytesCopied; |
| RGX_RPM_DATA_RTU_FREE_PAGE_LIST *psFreeListBuffer; |
| |
| /* Allocate scratch area for setting up Page table indices */ |
| psFreeListBuffer = OSAllocMem(ui32PageCount * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST)); |
| if (psFreeListBuffer == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_WriteRPMPageList: failed to allocate scratch page table")); |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| |
| for (uiIdx = 0; uiIdx < ui32PageCount; uiIdx ++, ui32NextPageIndex ++) |
| { |
| psFreeListBuffer[uiIdx].u32_0 = 0; |
| RGX_RPM_DATA_RTU_FREE_PAGE_LIST_SET_PTI(psFreeListBuffer[uiIdx], ui32NextPageIndex); |
| } |
| |
| /* Copy scratch buffer to PMR */ |
| eError = PMR_WriteBytes(psPMR, |
| uiLogicalOffset, |
| (IMG_UINT8 *) psFreeListBuffer, |
| ui32PageCount * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST), |
| &uNumBytesCopied); |
| |
| /* Free scratch buffer */ |
| OSFreeMem(psFreeListBuffer); |
| |
| #if defined(PDUMP) |
| /* Pdump the Page tables */ |
| PDUMPCOMMENT("Dump %u RPM free page list entries.", ui32PageCount); |
| PMRPDumpLoadMem(psPMR, |
| uiLogicalOffset, |
| ui32PageCount * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST), |
| PDUMP_FLAGS_CONTINUOUS, |
| IMG_FALSE); |
| #endif |
| return eError; |
| } |
| |
| |
| static RGX_RPM_FREELIST* FindRPMFreeList(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32FreelistID) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| RGX_RPM_FREELIST *psFreeList = NULL; |
| |
| OSLockAcquire(psDevInfo->hLockRPMFreeList); |
| dllist_foreach_node(&psDevInfo->sRPMFreeListHead, psNode, psNext) |
| { |
| RGX_RPM_FREELIST *psThisFreeList = IMG_CONTAINER_OF(psNode, RGX_RPM_FREELIST, sNode); |
| |
| if (psThisFreeList->ui32FreelistID == ui32FreelistID) |
| { |
| psFreeList = psThisFreeList; |
| break; |
| } |
| } |
| OSLockRelease(psDevInfo->hLockRPMFreeList); |
| |
| return psFreeList; |
| } |
| |
| void RGXProcessRequestRPMGrow(PVRSRV_RGXDEV_INFO *psDevInfo, |
| IMG_UINT32 ui32FreelistID) |
| { |
| RGX_RPM_FREELIST *psFreeList = NULL; |
| RGXFWIF_KCCB_CMD sVRDMCCBCmd; |
| IMG_UINT32 ui32GrowValue; |
| PVRSRV_ERROR eError; |
| IMG_BOOL bRestartRPM = IMG_TRUE; /* FIXME */ |
| |
| PVR_ASSERT(psDevInfo); |
| |
| /* find the freelist with the corresponding ID */ |
| psFreeList = FindRPMFreeList(psDevInfo, ui32FreelistID); |
| |
| if (psFreeList) |
| { |
| /* Try to grow the freelist */ |
| eError = RGXGrowRPMFreeList(psFreeList, |
| psFreeList->ui32GrowFLPages, |
| &psFreeList->sMemoryBlockHead); |
| if (eError == PVRSRV_OK) |
| { |
| /* Grow successful, return size of grow size */ |
| ui32GrowValue = psFreeList->ui32GrowFLPages; |
| |
| psFreeList->ui32NumGrowReqByFW++; |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| /* Update Stats */ |
| PVRSRVStatsUpdateFreelistStats(0, |
| 1, /* Add 1 to the appropriate counter (Requests by FW) */ |
| psFreeList->ui32InitFLPages, |
| psFreeList->ui32NumHighPages, |
| psFreeList->ownerPid); |
| |
| #endif |
| |
| } |
| else |
| { |
| /* Grow failed */ |
| ui32GrowValue = 0; |
| PVR_DPF((PVR_DBG_ERROR,"Grow for FreeList %p [ID %d] failed (error %u)", |
| psFreeList, |
| psFreeList->ui32FreelistID, |
| eError)); |
| } |
| |
| /* send feedback */ |
| sVRDMCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_DOPPLER_MEMORY_GROW; |
| sVRDMCCBCmd.uCmdData.sFreeListGSData.sFreeListFWDevVAddr.ui32Addr = psFreeList->sFreeListFWDevVAddr.ui32Addr; |
| sVRDMCCBCmd.uCmdData.sFreeListGSData.ui32DeltaSize = ui32GrowValue; |
| sVRDMCCBCmd.uCmdData.sFreeListGSData.ui32NewSize = |
| ((bRestartRPM) ? RGX_FREELIST_GSDATA_RPM_RESTART_EN : 0) | |
| (psFreeList->ui32CurrentFLPages); |
| |
| PVR_DPF((PVR_DBG_ERROR,"Send feedback to RPM after grow on freelist [ID %d]", ui32FreelistID)); |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError = RGXScheduleCommand(psDevInfo, |
| RGXFWIF_DM_SHG, |
| &sVRDMCCBCmd, |
| sizeof(sVRDMCCBCmd), |
| 0, |
| PDUMP_FLAGS_NONE); |
| if (eError != PVRSRV_ERROR_RETRY) |
| { |
| break; |
| } |
| OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT); |
| } END_LOOP_UNTIL_TIMEOUT(); |
| /* Kernel CCB should never fill up, as the FW is processing them right away */ |
| |
| PVR_ASSERT(eError == PVRSRV_OK); |
| } |
| else |
| { |
| /* Should never happen */ |
| PVR_DPF((PVR_DBG_ERROR,"FreeList Lookup for FreeList ID 0x%08x failed (Populate)", ui32FreelistID)); |
| PVR_ASSERT(IMG_FALSE); |
| } |
| } |
| |
| |
| /*! |
| * RGXGrowRPMFreeList |
| * |
| * Allocate and map physical backing pages for RPM buffers |
| * |
| * @param ppsRPMDevMemDesc - RPM buffer descriptor representing new Scene memory block |
| * and its associated RPM page table and free page list entries |
| * @param psRPMContext - RPM context |
| * @param psFreeList - RPM freelist descriptor |
| * @param ui32RequestNumPages - number of RPM pages to add to Doppler scene hierarchy |
| * @param pListHeader - linked list of RGX_RPM_DEVMEM_DESC blocks |
| * |
| */ |
| PVRSRV_ERROR RGXGrowRPMFreeList(RGX_RPM_FREELIST *psFreeList, |
| IMG_UINT32 ui32RequestNumPages, |
| PDLLIST_NODE pListHeader) |
| { |
| PVRSRV_ERROR eError; |
| RGX_SERVER_RPM_CONTEXT *psRPMContext = psFreeList->psParentCtx; |
| RGX_RPM_DEVMEM_DESC *psRPMDevMemDesc; |
| IMG_DEVMEM_OFFSET_T uiPMROffset; |
| IMG_UINT32 ui32NextPageIndex; |
| |
| /* Are we allowed to grow ? */ |
| if (ui32RequestNumPages > psFreeList->psParentCtx->ui32UnallocatedPages) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXGrowRPMFreeList: Scene Hierarchy buffer exceeded (0x%x pages required, 0x%x pages available).", |
| ui32RequestNumPages, psFreeList->psParentCtx->ui32UnallocatedPages)); |
| return PVRSRV_ERROR_RPM_PBSIZE_ALREADY_MAX; |
| } |
| |
| /* Allocate descriptor */ |
| psRPMDevMemDesc = OSAllocZMem(sizeof(*psRPMDevMemDesc)); |
| if (psRPMDevMemDesc == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXGrowRPMFreeList: failed to allocate host data structure")); |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| |
| /* |
| * Lock protects simultaneous manipulation of: |
| * - the memory block list |
| * - the freelist's ui32CurrentFLPages |
| * - the context's ui32UnallocatedPages |
| */ |
| OSLockAcquire(psFreeList->psDevInfo->hLockRPMFreeList); |
| OSLockAcquire(psFreeList->psDevInfo->hLockRPMContext); |
| |
| /* Update the sparse PMRs */ |
| psRPMDevMemDesc->psFreeList = psFreeList; |
| psRPMDevMemDesc->ui32NumPages = ui32RequestNumPages; |
| psRPMDevMemDesc->sSceneHierarchyNode.psPMR = psRPMContext->psSceneHierarchyPMR; |
| psRPMDevMemDesc->sRPMPageListNode.psPMR = psRPMContext->psRPMPageTablePMR; |
| psRPMDevMemDesc->sRPMFreeListNode.psPMR = psFreeList->psFreeListPMR; |
| |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "RGXGrowRPMFreeList: mapping %d pages for Doppler scene memory to VA 0x%llx with heap ID %p", |
| ui32RequestNumPages, psRPMContext->sSceneMemoryBaseAddr.uiAddr, psRPMContext->psSceneHeap)); |
| |
| /* |
| * 1. Doppler scene hierarchy |
| */ |
| PDUMPCOMMENT("Allocate %d pages with mapping index %d for Doppler scene memory.", |
| ui32RequestNumPages, |
| psRPMContext->ui32SceneMemorySparseMappingIndex); |
| eError = _RGXMapRPMPBBlock(&psRPMDevMemDesc->sSceneHierarchyNode, |
| psFreeList, |
| NODE_SCENE_HIERARCHY, |
| psRPMContext->psSceneHeap, |
| ui32RequestNumPages, |
| psRPMContext->sSceneMemoryBaseAddr); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXGrowRPMFreeList: Unable to map RPM scene hierarchy block (status %d)", eError)); |
| goto ErrorSceneBlock; |
| } |
| |
| /* |
| * 2. RPM page list |
| */ |
| if (ui32RequestNumPages > psRPMContext->ui32RPMEntriesInPage) |
| { |
| /* we need to map in phys pages for RPM page table */ |
| PDUMPCOMMENT("Allocate %d (%d requested) page table entries with mapping index %d for RPM page table.", |
| ui32RequestNumPages - psRPMContext->ui32RPMEntriesInPage, |
| ui32RequestNumPages, |
| psRPMContext->ui32RPMPageTableSparseMappingIndex); |
| eError = _RGXMapRPMPBBlock(&psRPMDevMemDesc->sRPMPageListNode, |
| psFreeList, |
| NODE_RPM_PAGE_TABLE, |
| psRPMContext->psRPMPageTableHeap, |
| ui32RequestNumPages - psRPMContext->ui32RPMEntriesInPage, |
| psRPMContext->sRPMPageTableBaseAddr); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXGrowRPMFreeList: Unable to map RPM page table block (status %d)", eError)); |
| goto ErrorPageTableBlock; |
| } |
| } |
| |
| /* |
| * 3. Free page list (FPL) |
| */ |
| if (ui32RequestNumPages > psFreeList->ui32EntriesInPage) |
| { |
| /* we need to map in phys pages for RPM free page list */ |
| PDUMPCOMMENT("Allocate %d (%d requested) FPL entries with mapping index %d for RPM free page list.", |
| ui32RequestNumPages - psFreeList->ui32EntriesInPage, |
| ui32RequestNumPages, |
| psFreeList->ui32RPMFreeListSparseMappingIndex); |
| eError = _RGXMapRPMPBBlock(&psRPMDevMemDesc->sRPMFreeListNode, |
| psFreeList, |
| NODE_RPM_FREE_PAGE_LIST, |
| psRPMContext->psRPMPageTableHeap, |
| ui32RequestNumPages - psFreeList->ui32EntriesInPage, |
| psFreeList->sBaseDevVAddr); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXGrowRPMFreeList: Unable to map RPM free page list (status %d)", eError)); |
| goto ErrorFreeListBlock; |
| } |
| } |
| |
| /* |
| * Update FPL entries |
| */ |
| |
| /* Calculate doppler page index from base of Doppler heap */ |
| ui32NextPageIndex = (psRPMDevMemDesc->sSceneHierarchyNode.sAddr.uiAddr - |
| psRPMContext->sDopplerHeapBaseAddr.uiAddr) >> psFreeList->uiLog2DopplerPageSize; |
| |
| /* Calculate write offset into FPL PMR assuming pages are mapped in order with no gaps */ |
| uiPMROffset = (size_t)psFreeList->ui32CurrentFLPages * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST); |
| |
| eError = _WriteRPMFreePageList(psFreeList->psFreeListPMR, uiPMROffset, ui32NextPageIndex, ui32RequestNumPages); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXGrowRPMFreeList: error writing RPM free list entries (%d)", eError)); |
| goto ErrorFreeListWriteEntries; |
| } |
| |
| { |
| /* |
| * Update the entries remaining in the last mapped RPM and FPL pages. |
| * |
| * psRPMDevMemDesc->sRPMPageListNode.ui32NumPhysPages * 1024 entries are added (can be zero) |
| * ui32RequestNumPages entries are committed |
| * |
| * The number of entries remaining should always be less than a full page. |
| */ |
| IMG_UINT32 ui32PTEntriesPerChunk = OSGetPageSize() / sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST); |
| IMG_UINT32 ui32PTEntriesPerChunkClearMask = ~(ui32PTEntriesPerChunk - 1); |
| |
| psRPMContext->ui32RPMEntriesInPage = psRPMContext->ui32RPMEntriesInPage + |
| (psRPMDevMemDesc->sRPMPageListNode.ui32NumPhysPages * ui32PTEntriesPerChunk) - ui32RequestNumPages; |
| PVR_ASSERT((psRPMContext->ui32RPMEntriesInPage & ui32PTEntriesPerChunkClearMask) == 0); |
| |
| psFreeList->ui32EntriesInPage = psFreeList->ui32EntriesInPage + |
| (psRPMDevMemDesc->sRPMFreeListNode.ui32NumPhysPages * ui32PTEntriesPerChunk) - ui32RequestNumPages; |
| PVR_ASSERT((psFreeList->ui32EntriesInPage & ui32PTEntriesPerChunkClearMask) == 0); |
| } |
| |
| /* Add node to link list */ |
| dllist_add_to_head(pListHeader, &psRPMDevMemDesc->sMemoryDescBlock); |
| |
| /* Update number of available pages */ |
| psFreeList->ui32CurrentFLPages += ui32RequestNumPages; |
| psRPMContext->ui32UnallocatedPages -= ui32RequestNumPages; |
| |
| #if defined(DEBUG) |
| RGXDumpRPMFreeListPageList(psFreeList); |
| #endif |
| |
| OSLockRelease(psFreeList->psDevInfo->hLockRPMContext); |
| OSLockRelease(psFreeList->psDevInfo->hLockRPMFreeList); |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"RPM Freelist [%p, ID %d]: grow by %u pages (current pages %u/%u, unallocated pages %u)", |
| psFreeList, |
| psFreeList->ui32FreelistID, |
| ui32RequestNumPages, |
| psFreeList->ui32CurrentFLPages, |
| psRPMContext->ui32TotalRPMPages, |
| psRPMContext->ui32UnallocatedPages)); |
| |
| return PVRSRV_OK; |
| |
| /* Error handling */ |
| ErrorFreeListWriteEntries: |
| /* TODO: unmap sparse block for RPM FPL */ |
| ErrorFreeListBlock: |
| /* TODO: unmap sparse block for RPM page table */ |
| ErrorPageTableBlock: |
| /* TODO: unmap sparse block for scene hierarchy */ |
| |
| ErrorSceneBlock: |
| OSLockRelease(psFreeList->psDevInfo->hLockRPMContext); |
| OSLockRelease(psFreeList->psDevInfo->hLockRPMFreeList); |
| OSFreeMem(psRPMDevMemDesc); |
| |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| static PVRSRV_ERROR RGXShrinkRPMFreeList(PDLLIST_NODE pListHeader, |
| RGX_RPM_FREELIST *psFreeList) |
| { |
| DLLIST_NODE *psNode; |
| RGX_RPM_DEVMEM_DESC *psRPMDevMemNode; |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| IMG_UINT32 ui32OldValue; |
| |
| /* |
| * Lock protects simultaneous manipulation of: |
| * - the memory block list |
| * - the freelist's ui32CurrentFLPages value |
| */ |
| PVR_ASSERT(pListHeader); |
| PVR_ASSERT(psFreeList); |
| PVR_ASSERT(psFreeList->psDevInfo); |
| PVR_ASSERT(psFreeList->psDevInfo->hLockRPMFreeList); |
| |
| OSLockAcquire(psFreeList->psDevInfo->hLockRPMFreeList); |
| |
| /******************************************************************** |
| * All scene memory blocks must be freed together as non-contiguous |
| * virtual mappings are not yet supported. |
| ********************************************************************/ |
| |
| /* Get node from head of list and remove it */ |
| psNode = dllist_get_next_node(pListHeader); |
| PVR_DPF((PVR_DBG_MESSAGE, "Found node %p", psNode)); |
| if (psNode) |
| { |
| dllist_remove_node(psNode); |
| |
| psRPMDevMemNode = IMG_CONTAINER_OF(psNode, RGX_RPM_DEVMEM_DESC, sMemoryDescBlock); |
| PVR_ASSERT(psRPMDevMemNode); |
| PVR_ASSERT(psRPMDevMemNode->psFreeList); |
| PVR_ASSERT(psRPMDevMemNode->sSceneHierarchyNode.psPMR); |
| |
| /* remove scene hierarchy block */ |
| PVR_DPF((PVR_DBG_MESSAGE, "Removing scene hierarchy node")); |
| eError = _RGXUnmapRPMPBBlock(&psRPMDevMemNode->sSceneHierarchyNode, |
| psRPMDevMemNode->psFreeList, |
| psFreeList->psParentCtx->sSceneMemoryBaseAddr); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXShrinkRPMFreeList: Failed to unmap %d pages with mapping index %d (status %d)", |
| psRPMDevMemNode->sSceneHierarchyNode.ui32NumPhysPages, |
| psRPMDevMemNode->sSceneHierarchyNode.ui32StartOfMappingIndex, |
| eError)); |
| goto UnMapError; |
| } |
| |
| /* |
| * If the grow size is sub OS page size then the page lists may not need updating |
| */ |
| if (psRPMDevMemNode->sRPMPageListNode.eNodeType != NODE_EMPTY) |
| { |
| /* unmap the RPM page table backing pages */ |
| PVR_DPF((PVR_DBG_MESSAGE, "Removing RPM page list node")); |
| PVR_ASSERT(psRPMDevMemNode->sRPMPageListNode.psPMR); |
| eError = _RGXUnmapRPMPBBlock(&psRPMDevMemNode->sRPMPageListNode, |
| psRPMDevMemNode->psFreeList, |
| psFreeList->psParentCtx->sRPMPageTableBaseAddr); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXShrinkRPMFreeList: Failed to unmap %d pages with mapping index %d (status %d)", |
| psRPMDevMemNode->sRPMPageListNode.ui32NumPhysPages, |
| psRPMDevMemNode->sRPMPageListNode.ui32StartOfMappingIndex, |
| eError)); |
| goto UnMapError; |
| } |
| } |
| |
| if (psRPMDevMemNode->sRPMFreeListNode.eNodeType != NODE_EMPTY) |
| { |
| /* unmap the RPM free page list backing pages */ |
| PVR_DPF((PVR_DBG_MESSAGE, "Removing RPM free list node")); |
| PVR_ASSERT(psRPMDevMemNode->sRPMFreeListNode.psPMR); |
| eError = _RGXUnmapRPMPBBlock(&psRPMDevMemNode->sRPMFreeListNode, |
| psRPMDevMemNode->psFreeList, |
| psFreeList->sBaseDevVAddr); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXShrinkRPMFreeList: Failed to unmap %d pages with mapping index %d (status %d)", |
| psRPMDevMemNode->sRPMFreeListNode.ui32NumPhysPages, |
| psRPMDevMemNode->sRPMFreeListNode.ui32StartOfMappingIndex, |
| eError)); |
| goto UnMapError; |
| } |
| } |
| |
| /* update available RPM pages in freelist (NOTE: may be different from phys page count) */ |
| ui32OldValue = psFreeList->ui32CurrentFLPages; |
| psFreeList->ui32CurrentFLPages -= psRPMDevMemNode->ui32NumPages; |
| |
| /* check underflow */ |
| PVR_ASSERT(ui32OldValue > psFreeList->ui32CurrentFLPages); |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "Freelist [%p, ID %d]: shrink by %u pages (current pages %u/%u)", |
| psFreeList, |
| psFreeList->ui32FreelistID, |
| psRPMDevMemNode->ui32NumPages, |
| psFreeList->ui32CurrentFLPages, |
| psFreeList->psParentCtx->ui32UnallocatedPages)); |
| |
| OSFreeMem(psRPMDevMemNode); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_WARNING,"Freelist [0x%p]: shrink denied. PB already at zero PB size (%u pages)", |
| psFreeList, |
| psFreeList->ui32CurrentFLPages)); |
| eError = PVRSRV_ERROR_PBSIZE_ALREADY_MIN; |
| } |
| |
| OSLockRelease(psFreeList->psDevInfo->hLockRPMFreeList); |
| return PVRSRV_OK; |
| |
| UnMapError: |
| OSFreeMem(psRPMDevMemNode); |
| OSLockRelease(psFreeList->psDevInfo->hLockRPMFreeList); |
| |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| |
| /*! |
| * _RGXCreateRPMSparsePMR |
| * |
| * Creates a PMR container with no phys pages initially. Phys pages will be allocated |
| * and mapped later when requested by client or by HW RPM Out of Memory event. |
| * The PMR is created with zero phys backing pages. |
| * The sparse PMR is associated to either the RPM context or to the RPM freelist(s): |
| * |
| * RGX_SERVER_RPM_CONTEXT - Scene hierarchy, page table |
| * RGX_RPM_FREELIST - free page list PMR |
| * |
| * @param eBlockType - whether block is for scene hierarchy pages or page |
| * tables. This parameter is used to calculate size. |
| * @param ui32NumPages - total number of pages |
| * @param uiLog2DopplerPageSize - log2 Doppler/RPM page size |
| * @param ppsPMR - (Output) new PMR container. |
| * |
| * See the documentation for more details. |
| */ |
| static |
| PVRSRV_ERROR _RGXCreateRPMSparsePMR(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| RGX_DEVMEM_NODE_TYPE eBlockType, |
| IMG_UINT32 ui32NumPages, |
| IMG_UINT32 uiLog2DopplerPageSize, |
| PMR **ppsPMR) |
| { |
| PVRSRV_ERROR eError; |
| IMG_DEVMEM_SIZE_T uiMaxSize = 0; |
| IMG_UINT32 ui32NumVirtPages = 0; /*!< number of virtual pages to cover virtual range */ |
| IMG_UINT32 ui32Log2OSPageSize = OSGetPageShift(); |
| IMG_UINT32 ui32ChunkSize = OSGetPageSize(); |
| PVRSRV_MEMALLOCFLAGS_T uiCustomFlags = 0; |
| |
| /* Work out the allocation logical size = virtual size */ |
| switch(eBlockType) |
| { |
| case NODE_EMPTY: |
| PVR_ASSERT(IMG_FALSE); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| case NODE_SCENE_HIERARCHY: |
| PDUMPCOMMENT("Allocate Scene Hierarchy PMR (Pages %08X)", ui32NumPages); |
| uiMaxSize = (IMG_DEVMEM_SIZE_T)ui32NumPages * (1 << uiLog2DopplerPageSize); |
| break; |
| case NODE_RPM_PAGE_TABLE: |
| PDUMPCOMMENT("Allocate RPM Page Table PMR (Page entries %08X)", ui32NumPages); |
| uiMaxSize = (IMG_DEVMEM_SIZE_T)ui32NumPages * sizeof(RGX_RPM_DATA_RTU_PAGE_TABLE); |
| break; |
| case NODE_RPM_FREE_PAGE_LIST: |
| /* |
| * Each RPM free page list (FPL) supports the maximum range. |
| * In practise the maximum range is divided between allocations in each FPL |
| */ |
| PDUMPCOMMENT("Allocate RPM Free Page List PMR (Page entries %08X)", ui32NumPages); |
| uiMaxSize = (IMG_DEVMEM_SIZE_T)ui32NumPages * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST); |
| uiCustomFlags |= PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE; /*(PVRSRV_MEMALLOCFLAG_CPU_READABLE | PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE | PVRSRV_MEMALLOCFLAG_CPU_UNCACHED); */ |
| break; |
| /* no default case because the build should error out if a case is unhandled */ |
| } |
| |
| uiMaxSize = (uiMaxSize + ui32ChunkSize - 1) & ~(ui32ChunkSize - 1); |
| ui32NumVirtPages = uiMaxSize >> ui32Log2OSPageSize; |
| |
| eError = PhysmemNewRamBackedPMR(psConnection, |
| psDeviceNode, |
| uiMaxSize, /* the maximum size which should match num virtual pages * page size */ |
| ui32ChunkSize, |
| 0, |
| ui32NumVirtPages, |
| NULL, |
| ui32Log2OSPageSize, |
| (PVRSRV_MEMALLOCFLAG_GPU_READABLE | PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | PVRSRV_MEMALLOCFLAG_SPARSE_NO_DUMMY_BACKING | uiCustomFlags), |
| strlen("RPM Buffer") + 1, |
| "RPM Buffer", |
| ppsPMR); |
| if(eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "_RGXCreateRPMSparsePMR: Failed to allocate sparse PMR of size: 0x%016llX", |
| (IMG_UINT64)uiMaxSize)); |
| } |
| |
| return eError; |
| } |
| |
| /*! |
| * _RGXMapRPMPBBlock |
| * |
| * Maps in a block of phys pages for one of the following: |
| * |
| * NODE_SCENE_HIERARCHY - scene hierarchy |
| * NODE_RPM_PAGE_TABLE - RPM page table entries |
| * NODE_RPM_FREE_PAGE_LIST - RPM free page list entries |
| * |
| * @param psDevMemNode - device mem block descriptor (allocated by caller) |
| * @param psFreeList - free list descriptor |
| * @param eBlockType - block type: scene memory, RPM page table or RPM page free list |
| * @param psDevmemHeap - heap for GPU virtual mapping |
| * @param ui32NumPages - number of pages for scene memory, OR |
| * number of PT entries for RPM page table or page free list |
| * @param sDevVAddrBase - GPU virtual base address i.e. base address at start of sparse allocation |
| * |
| * @return PVRSRV_OK if no error occurred |
| */ |
| static |
| PVRSRV_ERROR _RGXMapRPMPBBlock(RGX_DEVMEM_NODE *psDevMemNode, |
| RGX_RPM_FREELIST *psFreeList, |
| RGX_DEVMEM_NODE_TYPE eBlockType, |
| DEVMEMINT_HEAP *psDevmemHeap, |
| IMG_UINT32 ui32NumPages, |
| IMG_DEV_VIRTADDR sDevVAddrBase) |
| { |
| PVRSRV_ERROR eError; |
| IMG_UINT64 sCpuVAddrNULL = 0; /* no CPU mapping needed */ |
| IMG_UINT32 *paui32AllocPageIndices; /* table of virtual indices for sparse mapping */ |
| IMG_PUINT32 pui32MappingIndex = NULL; /* virtual index where next physical chunk is mapped */ |
| IMG_UINT32 i; |
| size_t uiSize = 0; |
| IMG_UINT32 ui32Log2OSPageSize = OSGetPageShift(); |
| IMG_UINT32 ui32ChunkSize = OSGetPageSize(); |
| IMG_UINT32 ui32NumPhysPages = 0; /*!< number of physical pages for data pages or RPM PTs */ |
| PVRSRV_MEMALLOCFLAGS_T uiCustomFlags = 0; |
| |
| |
| /* Allocate Memory Block for scene hierarchy */ |
| switch(eBlockType) |
| { |
| case NODE_EMPTY: |
| PVR_ASSERT(IMG_FALSE); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| case NODE_SCENE_HIERARCHY: |
| PDUMPCOMMENT("Allocate Scene Hierarchy Block (Pages %08X)", ui32NumPages); |
| uiSize = (size_t)ui32NumPages * (1 << psFreeList->psParentCtx->uiLog2DopplerPageSize); |
| pui32MappingIndex = &psFreeList->psParentCtx->ui32SceneMemorySparseMappingIndex; |
| break; |
| case NODE_RPM_PAGE_TABLE: |
| PDUMPCOMMENT("Allocate RPM Page Table Block (Page entries %08X)", ui32NumPages); |
| uiSize = (size_t)ui32NumPages * sizeof(RGX_RPM_DATA_RTU_PAGE_TABLE); |
| pui32MappingIndex = &psFreeList->psParentCtx->ui32RPMPageTableSparseMappingIndex; |
| break; |
| case NODE_RPM_FREE_PAGE_LIST: |
| PDUMPCOMMENT("Allocate RPM Free Page List Block (Page entries %08X)", ui32NumPages); |
| uiSize = (size_t)ui32NumPages * sizeof(RGX_RPM_DATA_RTU_FREE_PAGE_LIST); |
| pui32MappingIndex = &psFreeList->ui32RPMFreeListSparseMappingIndex; |
| uiCustomFlags |= PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE; /*(PVRSRV_MEMALLOCFLAG_CPU_READABLE | PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE);*/ |
| break; |
| /* no default case because the build should error out if a case is unhandled */ |
| } |
| |
| /* |
| * Round size up to multiple of the sparse chunk size = OS page size. |
| */ |
| uiSize = (uiSize + ui32ChunkSize - 1) & ~(ui32ChunkSize - 1); |
| ui32NumPhysPages = uiSize >> ui32Log2OSPageSize; |
| |
| paui32AllocPageIndices = OSAllocMem(ui32NumPhysPages * sizeof(IMG_UINT32)); |
| if (paui32AllocPageIndices == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_RGXCreateRPMPBBlockSparse: failed to allocate sparse mapping index list")); |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto ErrorAllocHost; |
| } |
| for(i=0; i<ui32NumPhysPages; i++) |
| { |
| paui32AllocPageIndices[i] = *pui32MappingIndex + i; |
| } |
| |
| /* Set up some state */ |
| psDevMemNode->eNodeType = eBlockType; |
| psDevMemNode->psDevMemHeap = psDevmemHeap; |
| if (eBlockType == NODE_SCENE_HIERARCHY) |
| { |
| /* the mapped-in scene hierarchy device address will be used to set up the FPL entries */ |
| psDevMemNode->sAddr.uiAddr = sDevVAddrBase.uiAddr + (*pui32MappingIndex * ui32ChunkSize); |
| } |
| psDevMemNode->ui32NumPhysPages = ui32NumPhysPages; |
| psDevMemNode->ui32StartOfMappingIndex = *pui32MappingIndex; |
| |
| { |
| if ((eBlockType == NODE_SCENE_HIERARCHY) && |
| (ui32NumPhysPages > psFreeList->psParentCtx->ui32UnallocatedPages)) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_RGXCreateRPMPBBlockSparse: virtual address space exceeded (0x%x pages required, 0x%x pages available).", |
| ui32NumPhysPages, psFreeList->psParentCtx->ui32UnallocatedPages)); |
| OSFreeMem(paui32AllocPageIndices); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| eError = PMRLockSysPhysAddresses(psDevMemNode->psPMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_RGXCreateRPMPBBlockSparse: unable to lock PMR physical pages (status %d)", eError)); |
| goto ErrorLockPhys; |
| } |
| |
| eError = DevmemIntChangeSparse(psDevmemHeap, |
| psDevMemNode->psPMR, |
| ui32NumPhysPages, |
| paui32AllocPageIndices, |
| 0, |
| NULL, |
| SPARSE_RESIZE_ALLOC, |
| (PVRSRV_MEMALLOCFLAG_GPU_READABLE | PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | uiCustomFlags), |
| sDevVAddrBase, |
| sCpuVAddrNULL); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_RGXCreateRPMPBBlockSparse: change sparse mapping failed with %d pages starting at %d (status %d)", |
| ui32NumPhysPages, *pui32MappingIndex, eError)); |
| goto ErrorSparseMapping; |
| } |
| |
| /* FIXME: leave locked until destroy */ |
| PMRUnlockSysPhysAddresses(psDevMemNode->psPMR); |
| } |
| |
| /* |
| * Update the mapping index for the next allocation. |
| * The virtual pages should be contiguous. |
| */ |
| *pui32MappingIndex += ui32NumPhysPages; |
| |
| OSFreeMem(paui32AllocPageIndices); |
| |
| return PVRSRV_OK; |
| |
| ErrorSparseMapping: |
| PMRUnlockSysPhysAddresses(psDevMemNode->psPMR); |
| |
| ErrorLockPhys: |
| OSFreeMem(paui32AllocPageIndices); |
| |
| ErrorAllocHost: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| /*! |
| * _RGXUnmapRPMPBBlock |
| * |
| * NOTE: because the SHF and SHG requests for memory are interleaved, the |
| * page mapping offset cannot be updated (non-contiguous virtual mapping |
| * is not supported). |
| * |
| * So either |
| * (i) the allocated virtual address range is unusable after unmap |
| * (ii) all of the scene memory must be freed |
| * |
| * @param psDevMemNode - block to free |
| * @param psFreeList - RPM free list |
| * @param sDevVAddrBase - the virtual base address (i.e. where page 1 of the PMR is mapped) |
| */ |
| static |
| PVRSRV_ERROR _RGXUnmapRPMPBBlock(RGX_DEVMEM_NODE *psDevMemNode, |
| RGX_RPM_FREELIST *psFreeList, |
| IMG_DEV_VIRTADDR sDevVAddrBase) |
| { |
| PVRSRV_ERROR eError; |
| IMG_UINT64 sCpuVAddrNULL = 0; /* no CPU mapping needed */ |
| IMG_UINT32 *paui32FreePageIndices; /* table of virtual indices for sparse unmapping */ |
| IMG_UINT32 i; |
| IMG_UINT32 ui32NumPhysPages = psDevMemNode->ui32NumPhysPages; /*!< number of physical pages for data pages or RPM PTs */ |
| |
| #if defined(PDUMP) |
| /* Free Memory Block for scene hierarchy */ |
| switch(psDevMemNode->eNodeType) |
| { |
| case NODE_EMPTY: |
| PVR_ASSERT(IMG_FALSE); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| case NODE_SCENE_HIERARCHY: |
| PDUMPCOMMENT("Free Scene Hierarchy Block (Pages %08X)", ui32NumPhysPages); |
| break; |
| case NODE_RPM_PAGE_TABLE: |
| PDUMPCOMMENT("Free RPM Page Table Block (Page entries %08X)", ui32NumPhysPages); |
| break; |
| case NODE_RPM_FREE_PAGE_LIST: |
| PDUMPCOMMENT("Free RPM Free Page List Block (Page entries %08X)", ui32NumPhysPages); |
| break; |
| /* no default case because the build should error out if a case is unhandled */ |
| } |
| #endif |
| |
| paui32FreePageIndices = OSAllocMem(ui32NumPhysPages * sizeof(IMG_UINT32)); |
| if (paui32FreePageIndices == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_RGXUnmapRPMPBBlock: failed to allocate sparse mapping index list")); |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto ErrorAllocHost; |
| } |
| for(i=0; i<ui32NumPhysPages; i++) |
| { |
| paui32FreePageIndices[i] = psDevMemNode->ui32StartOfMappingIndex + i; |
| } |
| |
| { |
| eError = PMRLockSysPhysAddresses(psDevMemNode->psPMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_RGXUnmapRPMPBBlock: unable to lock PMR physical pages (status %d)", eError)); |
| goto ErrorLockPhys; |
| } |
| |
| eError = DevmemIntChangeSparse(psDevMemNode->psDevMemHeap, |
| psDevMemNode->psPMR, |
| 0, /* no pages are mapped here */ |
| NULL, |
| ui32NumPhysPages, |
| paui32FreePageIndices, |
| SPARSE_RESIZE_FREE, |
| (PVRSRV_MEMALLOCFLAG_GPU_READABLE | PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE), |
| sDevVAddrBase, |
| sCpuVAddrNULL); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "_RGXUnmapRPMPBBlock: free sparse mapping failed with %d pages starting at %d (status %d)", |
| ui32NumPhysPages, psDevMemNode->ui32StartOfMappingIndex, eError)); |
| goto ErrorSparseMapping; |
| } |
| |
| PMRUnlockSysPhysAddresses(psDevMemNode->psPMR); |
| } |
| |
| OSFreeMem(paui32FreePageIndices); |
| |
| return PVRSRV_OK; |
| |
| ErrorSparseMapping: |
| PMRUnlockSysPhysAddresses(psDevMemNode->psPMR); |
| |
| ErrorLockPhys: |
| OSFreeMem(paui32FreePageIndices); |
| |
| ErrorAllocHost: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| |
| /*! |
| * RGXCreateRPMFreeList |
| * |
| * @param ui32InitFLPages - initial allocation of mapped-in physical pages |
| * @param ui32GrowFLPages - physical pages to add to scene hierarchy if RPM OOM occurs |
| * @param sFreeListDevVAddr - virtual base address of free list |
| * @param sRPMPageListDevVAddr (DEPRECATED -- cached in RPM Context) |
| * @param ui32FLSyncAddr (DEPRECATED) |
| * @param ppsFreeList - returns a RPM freelist handle to client |
| * @param puiHWFreeList - 'handle' to FW freelist, passed in VRDM kick (FIXME) |
| * @param bIsExternal - flag which marks if the freelist is an external one |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXCreateRPMFreeList(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| RGX_SERVER_RPM_CONTEXT *psRPMContext, |
| IMG_UINT32 ui32InitFLPages, |
| IMG_UINT32 ui32GrowFLPages, |
| IMG_DEV_VIRTADDR sFreeListDevVAddr, |
| RGX_RPM_FREELIST **ppsFreeList, |
| IMG_UINT32 *puiHWFreeList, |
| IMG_BOOL bIsExternal) |
| { |
| PVRSRV_ERROR eError; |
| RGXFWIF_RPM_FREELIST *psFWRPMFreeList; |
| DEVMEM_MEMDESC *psFWRPMFreelistMemDesc; |
| RGX_RPM_FREELIST *psFreeList; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| |
| /* Allocate kernel freelist struct */ |
| psFreeList = OSAllocZMem(sizeof(*psFreeList)); |
| if (psFreeList == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMFreeList: failed to allocate host data structure")); |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto ErrorAllocHost; |
| } |
| |
| /* Allocate cleanup sync */ |
| eError = SyncPrimAlloc(psDeviceNode->hSyncPrimContext, |
| &psFreeList->psCleanupSync, |
| "RPM free list cleanup"); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateRPMFreeList: Failed to allocate cleanup sync (0x%x)", |
| eError)); |
| goto ErrorSyncAlloc; |
| } |
| |
| /* |
| * This FW FreeList context is only mapped into kernel for initialisation. |
| * Otherwise this allocation is only used by the FW. |
| * Therefore the GPU cache doesn't need coherency, |
| * and write-combine is suffice on the CPU side (WC buffer will be flushed at the first TA-kick) |
| * |
| * TODO - RPM freelist will be modified after creation, but only from host-side. |
| */ |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(*psFWRPMFreeList), |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE | |
| PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC | |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_CACHE_INCOHERENT | |
| PVRSRV_MEMALLOCFLAG_CPU_WRITE_COMBINE | |
| PVRSRV_MEMALLOCFLAG_CPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE, |
| "FwRPMFreeList", |
| &psFWRPMFreelistMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMFreeList: DevmemAllocate for RGXFWIF_FREELIST failed")); |
| goto ErrorFWFreeListAlloc; |
| } |
| |
| /* Initialise host data structures */ |
| psFreeList->psConnection = psConnection; |
| psFreeList->psDevInfo = psDevInfo; |
| psFreeList->psParentCtx = psRPMContext; |
| psFreeList->psFWFreelistMemDesc = psFWRPMFreelistMemDesc; |
| psFreeList->sBaseDevVAddr = sFreeListDevVAddr; |
| RGXSetFirmwareAddress(&psFreeList->sFreeListFWDevVAddr, psFWRPMFreelistMemDesc, 0, RFW_FWADDR_FLAG_NONE); |
| psFreeList->ui32FreelistID = psDevInfo->ui32RPMFreelistCurrID++; |
| //psFreeList->ui32MaxFLPages = ui32MaxFLPages; |
| /* TODO: is it really needed? */ |
| if(bIsExternal == IMG_FALSE) |
| { |
| psFreeList->ui32InitFLPages = ui32InitFLPages; |
| psFreeList->ui32GrowFLPages = ui32GrowFLPages; |
| } |
| //psFreeList->ui32CurrentFLPages = ui32InitFLPages; |
| psFreeList->ui32RefCount = 0; |
| dllist_init(&psFreeList->sMemoryBlockHead); |
| |
| /* Wizard2 -- support per-freelist Doppler virtual page size */ |
| psFreeList->uiLog2DopplerPageSize = psRPMContext->uiLog2DopplerPageSize; |
| |
| /* Initialise FW data structure */ |
| eError = DevmemAcquireCpuVirtAddr(psFreeList->psFWFreelistMemDesc, (void **)&psFWRPMFreeList); |
| PVR_LOGG_IF_ERROR(eError, "Devmem AcquireCpuVirtAddr", ErrorFWFreeListCpuMap); |
| |
| /* |
| * FIXME - the max pages are shared with the other freelists so this |
| * over-estimates the number of free pages. The full check is |
| * implemented in RGXGrowRPMFreeList. |
| */ |
| if(bIsExternal == IMG_TRUE) |
| { |
| /* An external RPM FreeList will never grow */ |
| psFWRPMFreeList->ui32MaxPages = ui32InitFLPages; |
| } |
| else |
| { |
| psFWRPMFreeList->ui32MaxPages = psFreeList->psParentCtx->ui32TotalRPMPages; |
| } |
| psFWRPMFreeList->ui32CurrentPages = ui32InitFLPages; |
| psFWRPMFreeList->ui32GrowPages = ui32GrowFLPages; |
| psFWRPMFreeList->ui32ReadOffset = 0; |
| psFWRPMFreeList->ui32WriteOffset = RGX_CR_RPM_SHG_FPL_WRITE_TOGGLE_EN; /* FL is full */ |
| psFWRPMFreeList->bReadToggle = IMG_FALSE; |
| psFWRPMFreeList->bWriteToggle = IMG_TRUE; |
| psFWRPMFreeList->sFreeListDevVAddr.uiAddr = sFreeListDevVAddr.uiAddr; |
| psFWRPMFreeList->ui32FreeListID = psFreeList->ui32FreelistID; |
| psFWRPMFreeList->bGrowPending = IMG_FALSE; |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "RPM Freelist %p created: FW freelist: %p, Init pages 0x%08x, Max FL base address " IMG_DEVMEM_SIZE_FMTSPEC ", Init FL base address " IMG_DEVMEM_SIZE_FMTSPEC, |
| psFreeList, |
| psFWRPMFreeList, |
| ui32InitFLPages, |
| sFreeListDevVAddr.uiAddr, |
| psFWRPMFreeList->sFreeListDevVAddr.uiAddr)); |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"RPM FW Freelist %p created: sync FW addr 0x%08x", psFWRPMFreeList, psFWRPMFreeList->sSyncAddr)); |
| |
| PDUMPCOMMENT("Dump FW RPM FreeList"); |
| DevmemPDumpLoadMem(psFreeList->psFWFreelistMemDesc, 0, sizeof(*psFWRPMFreeList), PDUMP_FLAGS_CONTINUOUS); |
| |
| /* |
| * Separate dump of the Freelist's number of Pages and stack pointer. |
| * This allows to easily modify the PB size in the out2.txt files. |
| */ |
| PDUMPCOMMENT("RPM FreeList TotalPages"); |
| DevmemPDumpLoadMemValue32(psFreeList->psFWFreelistMemDesc, |
| offsetof(RGXFWIF_RPM_FREELIST, ui32CurrentPages), |
| psFWRPMFreeList->ui32CurrentPages, |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| PDUMPCOMMENT("RPM FreeList device virtual base address"); |
| DevmemPDumpLoadMemValue64(psFreeList->psFWFreelistMemDesc, |
| offsetof(RGXFWIF_RPM_FREELIST, sFreeListDevVAddr), |
| psFWRPMFreeList->sFreeListDevVAddr.uiAddr, |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| DevmemReleaseCpuVirtAddr(psFreeList->psFWFreelistMemDesc); |
| |
| if (bIsExternal == IMG_TRUE) |
| { |
| /* Mark the freelist as an external */ |
| psFreeList->bIsExternal = IMG_TRUE; |
| |
| /* In case of an external RPM FreeList it is not needed to: |
| * - create sparse PMR |
| * - allocate physical memory for the freelist |
| * - add it to the list of freelist |
| */ |
| |
| /* return values */ |
| *puiHWFreeList = psFreeList->sFreeListFWDevVAddr.ui32Addr; |
| *ppsFreeList = psFreeList; |
| |
| return PVRSRV_OK; |
| } |
| |
| psFreeList->bIsExternal = IMG_FALSE; |
| |
| /* |
| * Create the sparse PMR for the RPM free page list |
| */ |
| eError = _RGXCreateRPMSparsePMR(psConnection, psDeviceNode, |
| NODE_RPM_FREE_PAGE_LIST, |
| psRPMContext->ui32TotalRPMPages, |
| psRPMContext->uiLog2DopplerPageSize, |
| &psFreeList->psFreeListPMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMContext: failed to allocate PMR for RPM Free page list (%d)", eError)); |
| goto ErrorSparsePMR; |
| } |
| |
| /* |
| * Lock protects simultaneous manipulation of: |
| * - the memory block list |
| * - the freelist's ui32CurrentFLPages |
| */ |
| /* Add to list of freelists */ |
| OSLockAcquire(psDevInfo->hLockRPMFreeList); |
| psFreeList->psParentCtx->uiFLRefCount++; |
| dllist_add_to_tail(&psDevInfo->sRPMFreeListHead, &psFreeList->sNode); |
| OSLockRelease(psDevInfo->hLockRPMFreeList); |
| |
| /* |
| * Add initial scene hierarchy block |
| * Allocate phys memory for scene hierarchy, free page list and RPM page-in-use list |
| */ |
| eError = RGXGrowRPMFreeList(psFreeList, ui32InitFLPages, &psFreeList->sMemoryBlockHead); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMFreeList: error during phys memory allocation and mapping (%d)", eError)); |
| goto ErrorGrowFreeList; |
| } |
| |
| /* return values */ |
| *puiHWFreeList = psFreeList->sFreeListFWDevVAddr.ui32Addr; |
| *ppsFreeList = psFreeList; |
| |
| return PVRSRV_OK; |
| |
| /* Error handling */ |
| ErrorGrowFreeList: |
| /* Remove freelists from list */ |
| OSLockAcquire(psDevInfo->hLockRPMFreeList); |
| dllist_remove_node(&psFreeList->sNode); |
| psFreeList->psParentCtx->uiFLRefCount--; |
| OSLockRelease(psDevInfo->hLockRPMFreeList); |
| |
| ErrorSparsePMR: |
| SyncPrimFree(psFreeList->psCleanupSync); |
| |
| ErrorFWFreeListCpuMap: |
| RGXUnsetFirmwareAddress(psFWRPMFreelistMemDesc); |
| DevmemFwFree(psDevInfo, psFWRPMFreelistMemDesc); |
| |
| ErrorFWFreeListAlloc: |
| PMRUnrefPMR(psFreeList->psFreeListPMR); |
| |
| ErrorSyncAlloc: |
| OSFreeMem(psFreeList); |
| |
| ErrorAllocHost: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| /* |
| * RGXDestroyRPMFreeList |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXDestroyRPMFreeList(RGX_RPM_FREELIST *psFreeList) |
| { |
| PVRSRV_ERROR eError; |
| //IMG_UINT64 ui64CheckSum; |
| |
| PVR_ASSERT(psFreeList); |
| |
| if(psFreeList->ui32RefCount != 0 && psFreeList->bIsExternal == IMG_FALSE) |
| { |
| /* Freelist still busy */ |
| PVR_DPF((PVR_DBG_WARNING, "Freelist %p is busy", psFreeList)); |
| return PVRSRV_ERROR_RETRY; |
| } |
| |
| /* Freelist is not in use => start firmware cleanup */ |
| eError = RGXFWRequestRPMFreeListCleanUp(psFreeList->psDevInfo, |
| psFreeList->sFreeListFWDevVAddr, |
| psFreeList->psCleanupSync); |
| if(eError != PVRSRV_OK) |
| { |
| /* Can happen if the firmware took too long to handle the cleanup request, |
| * or if SLC-flushes didn't went through (due to some GPU lockup) */ |
| return eError; |
| } |
| |
| /* update the statistics */ |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| PVRSRVStatsUpdateFreelistStats(psFreeList->ui32NumGrowReqByApp, |
| psFreeList->ui32NumGrowReqByFW, |
| psFreeList->ui32InitFLPages, |
| psFreeList->ui32NumHighPages, |
| 0); /* FIXME - owner PID */ |
| #endif |
| |
| /* Destroy FW structures */ |
| RGXUnsetFirmwareAddress(psFreeList->psFWFreelistMemDesc); |
| DevmemFwFree(psFreeList->psDevInfo, psFreeList->psFWFreelistMemDesc); |
| |
| if(psFreeList->bIsExternal == IMG_FALSE) |
| { |
| /* Free the phys mem block descriptors. */ |
| PVR_DPF((PVR_DBG_WARNING, "Cleaning RPM freelist index %d", psFreeList->ui32FreelistID)); |
| while (!dllist_is_empty(&psFreeList->sMemoryBlockHead)) |
| { |
| eError = RGXShrinkRPMFreeList(&psFreeList->sMemoryBlockHead, psFreeList); |
| PVR_ASSERT(eError == PVRSRV_OK); |
| } |
| psFreeList->psParentCtx->uiFLRefCount--; |
| |
| /* consistency checks */ |
| PVR_ASSERT(dllist_is_empty(&psFreeList->sMemoryBlockHead)); |
| PVR_ASSERT(psFreeList->ui32CurrentFLPages == 0); |
| |
| /* Free RPM Free page list PMR */ |
| eError = PMRUnrefPMR(psFreeList->psFreeListPMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXDestroyRPMFreeList: Failed to free RPM free page list PMR %p (error %u)", |
| psFreeList->psFreeListPMR, |
| eError)); |
| PVR_ASSERT(IMG_FALSE); |
| } |
| |
| /* Remove RPM FreeList from list */ |
| OSLockAcquire(psFreeList->psDevInfo->hLockRPMFreeList); |
| dllist_remove_node(&psFreeList->sNode); |
| OSLockRelease(psFreeList->psDevInfo->hLockRPMFreeList); |
| } |
| |
| SyncPrimFree(psFreeList->psCleanupSync); |
| |
| /* free Freelist */ |
| OSFreeMem(psFreeList); |
| |
| return eError; |
| } |
| |
| |
| /*! |
| * RGXAddBlockToRPMFreeListKM |
| * |
| * NOTE: This API isn't used but it's provided for symmetry with the parameter |
| * management API. |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXAddBlockToRPMFreeListKM(RGX_RPM_FREELIST *psFreeList, |
| IMG_UINT32 ui32NumPages) |
| { |
| PVRSRV_ERROR eError; |
| |
| /* Check if we have reference to freelist's PMR */ |
| if (psFreeList->psFreeListPMR == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RPM Freelist is not configured for grow")); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* grow freelist */ |
| eError = RGXGrowRPMFreeList(psFreeList, |
| ui32NumPages, |
| &psFreeList->sMemoryBlockHead); |
| if(eError == PVRSRV_OK) |
| { |
| /* update freelist data in firmware */ |
| _UpdateFwRPMFreelistSize(psFreeList, IMG_TRUE, IMG_TRUE, ui32NumPages); |
| |
| psFreeList->ui32NumGrowReqByApp++; |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| /* Update Stats */ |
| PVRSRVStatsUpdateFreelistStats(1, /* Add 1 to the appropriate counter (Requests by App)*/ |
| 0, |
| psFreeList->ui32InitFLPages, |
| psFreeList->ui32NumHighPages, |
| psFreeList->ownerPid); |
| |
| #endif |
| } |
| |
| return eError; |
| } |
| |
| |
| /* |
| * RGXCreateRPMContext |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXCreateRPMContext(CONNECTION_DATA * psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| RGX_SERVER_RPM_CONTEXT **ppsRPMContext, |
| IMG_UINT32 ui32TotalRPMPages, |
| IMG_UINT32 uiLog2DopplerPageSize, |
| IMG_DEV_VIRTADDR sSceneMemoryBaseAddr, |
| IMG_DEV_VIRTADDR sDopplerHeapBaseAddr, |
| DEVMEMINT_HEAP *psSceneHeap, |
| IMG_DEV_VIRTADDR sRPMPageTableBaseAddr, |
| DEVMEMINT_HEAP *psRPMPageTableHeap, |
| DEVMEM_MEMDESC **ppsMemDesc, |
| IMG_UINT32 *puiHWFrameData) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| //DEVMEM_MEMDESC *psFWRPMContextMemDesc; |
| RGX_SERVER_RPM_CONTEXT *psRPMContext; |
| RGXFWIF_RAY_FRAME_DATA *psFrameData; |
| RGXFWIF_DEV_VIRTADDR sFirmwareAddr; |
| |
| /* Allocate kernel RPM context */ |
| psRPMContext = OSAllocZMem(sizeof(*psRPMContext)); |
| if (psRPMContext == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMContext: failed to allocate host data structure")); |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto ErrorAllocHost; |
| } |
| |
| *ppsRPMContext = psRPMContext; |
| |
| /* Allocate cleanup sync */ |
| eError = SyncPrimAlloc(psDeviceNode->hSyncPrimContext, |
| &psRPMContext->psCleanupSync, |
| "RPM context cleanup"); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateRPMContext: Failed to allocate cleanup sync (0x%x)", |
| eError)); |
| goto ErrorSyncAlloc; |
| } |
| |
| /* |
| * 1. Create the sparse PMR for scene hierarchy |
| */ |
| eError = _RGXCreateRPMSparsePMR(psConnection, psDeviceNode, |
| NODE_SCENE_HIERARCHY, |
| ui32TotalRPMPages, |
| uiLog2DopplerPageSize, |
| &psRPMContext->psSceneHierarchyPMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMContext: failed to allocate PMR for Scene hierarchy (%d)", eError)); |
| goto ErrorSparsePMR1; |
| } |
| |
| /* |
| * 2. Create the sparse PMR for the RPM page list |
| */ |
| eError = _RGXCreateRPMSparsePMR(psConnection, psDeviceNode, |
| NODE_RPM_PAGE_TABLE, |
| ui32TotalRPMPages, |
| uiLog2DopplerPageSize, |
| &psRPMContext->psRPMPageTablePMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMContext: failed to allocate PMR for RPM Page list (%d)", eError)); |
| goto ErrorSparsePMR2; |
| } |
| |
| /* Allocate FW structure and return FW address to client */ |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(*psFrameData), |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC | |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_CACHE_INCOHERENT | |
| PVRSRV_MEMALLOCFLAG_CPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_CPU_WRITE_COMBINE | |
| PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE, |
| "FwRPMContext", |
| ppsMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRPMContext: DevmemAllocate for RGXFWIF_FREELIST failed")); |
| goto ErrorFWRPMContextAlloc; |
| } |
| |
| /* Update the unallocated pages, which are shared between the RPM freelists */ |
| psRPMContext->ui32UnallocatedPages = psRPMContext->ui32TotalRPMPages = ui32TotalRPMPages; |
| psRPMContext->psDeviceNode = psDeviceNode; |
| psRPMContext->psFWRPMContextMemDesc = *ppsMemDesc; |
| psRPMContext->uiLog2DopplerPageSize = uiLog2DopplerPageSize; |
| |
| /* Cache the virtual alloc state for future phys page mapping */ |
| psRPMContext->sDopplerHeapBaseAddr = sDopplerHeapBaseAddr; |
| psRPMContext->sSceneMemoryBaseAddr = sSceneMemoryBaseAddr; |
| psRPMContext->psSceneHeap = psSceneHeap; |
| psRPMContext->sRPMPageTableBaseAddr = sRPMPageTableBaseAddr; |
| psRPMContext->psRPMPageTableHeap = psRPMPageTableHeap; |
| |
| /* |
| * TODO - implement RPM abort control using HW frame data to track |
| * abort status in RTU. |
| */ |
| RGXSetFirmwareAddress(&sFirmwareAddr, *ppsMemDesc, 0, RFW_FWADDR_FLAG_NONE); |
| *puiHWFrameData = sFirmwareAddr.ui32Addr; |
| |
| //eError = DevmemAcquireCpuVirtAddr(*ppsMemDesc, (void **)&psFrameData); |
| //PVR_LOGG_IF_ERROR(eError, "Devmem AcquireCpuVirtAddr", ErrorFrameDataCpuMap); |
| |
| /* |
| * TODO: pdumping |
| */ |
| |
| |
| return PVRSRV_OK; |
| |
| /* Error handling */ |
| |
| DevmemReleaseCpuVirtAddr(*ppsMemDesc); |
| |
| ErrorFWRPMContextAlloc: |
| PMRUnrefPMR(psRPMContext->psRPMPageTablePMR); |
| |
| ErrorSparsePMR2: |
| PMRUnrefPMR(psRPMContext->psSceneHierarchyPMR); |
| |
| ErrorSparsePMR1: |
| SyncPrimFree(psRPMContext->psCleanupSync); |
| |
| ErrorSyncAlloc: |
| OSFreeMem(psRPMContext); |
| |
| ErrorAllocHost: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| |
| /* |
| * RGXDestroyRPMContext |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXDestroyRPMContext(RGX_SERVER_RPM_CONTEXT *psCleanupData) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo; |
| PRGXFWIF_RAY_FRAME_DATA psFrameData; |
| |
| /* Wait for FW to process all commands */ |
| |
| PVR_ASSERT(psCleanupData); |
| |
| RGXSetFirmwareAddress(&psFrameData, psCleanupData->psFWRPMContextMemDesc, 0, RFW_FWADDR_NOREF_FLAG); |
| |
| /* Cleanup frame data in SHG */ |
| eError = RGXFWRequestRayFrameDataCleanUp(psCleanupData->psDeviceNode, |
| psFrameData, |
| psCleanupData->psCleanupSync, |
| RGXFWIF_DM_SHG); |
| if (eError == PVRSRV_ERROR_RETRY) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "FrameData busy in SHG")); |
| return eError; |
| } |
| |
| psDevInfo = psCleanupData->psDeviceNode->pvDevice; |
| |
| /* Cleanup frame data in RTU */ |
| eError = RGXFWRequestRayFrameDataCleanUp(psCleanupData->psDeviceNode, |
| psFrameData, |
| psCleanupData->psCleanupSync, |
| RGXFWIF_DM_RTU); |
| if (eError == PVRSRV_ERROR_RETRY) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "FrameData busy in RTU")); |
| return eError; |
| } |
| |
| /* Free Scene hierarchy PMR (We should be the only one that holds a ref on the PMR) */ |
| eError = PMRUnrefPMR(psCleanupData->psSceneHierarchyPMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXDestroyRPMContext: Failed to free scene hierarchy PMR %p (error %u)", |
| psCleanupData->psSceneHierarchyPMR, |
| eError)); |
| PVR_ASSERT(IMG_FALSE); |
| } |
| |
| /* Free RPM Page list PMR */ |
| eError = PMRUnrefPMR(psCleanupData->psRPMPageTablePMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXDestroyRPMContext: Failed to free RPM page list PMR %p (error %u)", |
| psCleanupData->psRPMPageTablePMR, |
| eError)); |
| PVR_ASSERT(IMG_FALSE); |
| } |
| |
| if (psCleanupData->uiFLRefCount > 0) |
| { |
| /* Kernel RPM freelists hold reference to RPM context */ |
| PVR_DPF((PVR_DBG_WARNING, "RGXDestroyRPMContext: Free list ref count non-zero.")); |
| return PVRSRV_ERROR_NONZERO_REFCOUNT; |
| } |
| |
| /* If we got here then SHG and RTU operations on this FrameData have finished */ |
| SyncPrimFree(psCleanupData->psCleanupSync); |
| |
| /* Free the FW RPM descriptor */ |
| RGXUnsetFirmwareAddress(psCleanupData->psFWRPMContextMemDesc); |
| DevmemFwFree(psDevInfo, psCleanupData->psFWRPMContextMemDesc); |
| |
| OSFreeMem(psCleanupData); |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| /* |
| * PVRSRVRGXCreateRayContextKM |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR PVRSRVRGXCreateRayContextKM(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| IMG_UINT32 ui32Priority, |
| IMG_DEV_VIRTADDR sMCUFenceAddr, |
| IMG_DEV_VIRTADDR sVRMCallStackAddr, |
| IMG_UINT32 ui32FrameworkRegisterSize, |
| IMG_PBYTE pabyFrameworkRegisters, |
| IMG_HANDLE hMemCtxPrivData, |
| RGX_SERVER_RAY_CONTEXT **ppsRayContext) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGX_SERVER_RAY_CONTEXT *psRayContext; |
| DEVMEM_MEMDESC *psFWMemContextMemDesc = RGXGetFWMemDescFromMemoryContextHandle(hMemCtxPrivData); |
| RGX_COMMON_CONTEXT_INFO sInfo; |
| RGXFWIF_FWRAYCONTEXT *pFWRayContext; |
| IMG_UINT32 i; |
| |
| /* Prepare cleanup structure */ |
| *ppsRayContext= NULL; |
| psRayContext = OSAllocZMem(sizeof(*psRayContext)); |
| if (psRayContext == NULL) |
| { |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| |
| psRayContext->psDeviceNode = psDeviceNode; |
| |
| /* |
| Allocate device memory for the firmware ray context. |
| */ |
| PDUMPCOMMENT("Allocate RGX firmware ray context"); |
| |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(RGXFWIF_FWRAYCONTEXT), |
| RGX_FWCOMCTX_ALLOCFLAGS, |
| "FwRayContext", |
| &psRayContext->psFWRayContextMemDesc); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to allocate firmware ray context (%u)", |
| eError)); |
| goto fail_fwraycontext; |
| } |
| |
| /* Allocate cleanup sync */ |
| eError = SyncPrimAlloc(psDeviceNode->hSyncPrimContext, |
| &psRayContext->psCleanupSync, |
| "Ray context cleanup"); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to allocate cleanup sync (0x%x)", |
| eError)); |
| goto fail_syncalloc; |
| } |
| |
| /* |
| * Create the FW framework buffer |
| */ |
| eError = PVRSRVRGXFrameworkCreateKM(psDeviceNode, &psRayContext->psFWFrameworkMemDesc, ui32FrameworkRegisterSize); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to allocate firmware GPU framework state (%u)", |
| eError)); |
| goto fail_frameworkcreate; |
| } |
| |
| /* Copy the Framework client data into the framework buffer */ |
| eError = PVRSRVRGXFrameworkCopyCommand(psRayContext->psFWFrameworkMemDesc, pabyFrameworkRegisters, ui32FrameworkRegisterSize); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRayContextKM: Failed to populate the framework buffer (%u)", |
| eError)); |
| goto fail_frameworkcopy; |
| } |
| |
| sInfo.psFWFrameworkMemDesc = psRayContext->psFWFrameworkMemDesc; |
| sInfo.psMCUFenceAddr = &sMCUFenceAddr; |
| |
| eError = _CreateSHContext(psConnection, |
| psDeviceNode, |
| psRayContext->psFWRayContextMemDesc, |
| offsetof(RGXFWIF_FWRAYCONTEXT, sSHGContext), |
| psFWMemContextMemDesc, |
| sVRMCallStackAddr, |
| ui32Priority, |
| &sInfo, |
| &psRayContext->sSHData); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_shcontext; |
| } |
| |
| eError = _CreateRSContext(psConnection, |
| psDeviceNode, |
| psRayContext->psFWRayContextMemDesc, |
| offsetof(RGXFWIF_FWRAYCONTEXT, sRTUContext), |
| psFWMemContextMemDesc, |
| ui32Priority, |
| &sInfo, |
| &psRayContext->sRSData); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_rscontext; |
| } |
| |
| /* |
| Temporarily map the firmware context to the kernel and init it |
| */ |
| eError = DevmemAcquireCpuVirtAddr(psRayContext->psFWRayContextMemDesc, |
| (void **)&pFWRayContext); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Failed to map firmware %s ray context to CPU", |
| __FUNCTION__, |
| PVRSRVGetErrorStringKM(eError))); |
| goto fail_rscontext; |
| } |
| |
| |
| for (i = 0; i < DPX_MAX_RAY_CONTEXTS; i++) |
| { |
| /* Allocate the frame context client CCB */ |
| eError = RGXCreateCCB(psDevInfo, |
| RGX_RTU_CCB_SIZE_LOG2, |
| psConnection, |
| REQ_TYPE_FC0 + i, |
| psRayContext->sRSData.psServerCommonContext, |
| &psRayContext->sRSData.psFCClientCCB[i], |
| &psRayContext->sRSData.psFCClientCCBMemDesc[i], |
| &psRayContext->sRSData.psFCClientCCBCtrlMemDesc[i]); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: failed to create CCB for frame context %u (%s)", |
| __FUNCTION__, |
| i, |
| PVRSRVGetErrorStringKM(eError))); |
| goto fail_rscontext; |
| } |
| |
| /* Set the firmware CCB device addresses in the firmware common context */ |
| RGXSetFirmwareAddress(&pFWRayContext->psCCB[i], |
| psRayContext->sRSData.psFCClientCCBMemDesc[i], |
| 0, RFW_FWADDR_FLAG_NONE); |
| RGXSetFirmwareAddress(&pFWRayContext->psCCBCtl[i], |
| psRayContext->sRSData.psFCClientCCBCtrlMemDesc[i], |
| 0, RFW_FWADDR_FLAG_NONE); |
| } |
| |
| pFWRayContext->ui32ActiveFCMask = 0; |
| pFWRayContext->ui32NextFC = RGXFWIF_INVALID_FRAME_CONTEXT; |
| |
| /* We've finished the setup so release the CPU mapping */ |
| DevmemReleaseCpuVirtAddr(psRayContext->psFWRayContextMemDesc); |
| |
| /* |
| As the common context alloc will dump the SH and RS common contexts |
| after the've been setup we skip of the 2 common contexts and dump the |
| rest of the structure |
| */ |
| PDUMPCOMMENT("Dump shared part of ray context context"); |
| DevmemPDumpLoadMem(psRayContext->psFWRayContextMemDesc, |
| (sizeof(RGXFWIF_FWCOMMONCONTEXT) * 2), |
| sizeof(RGXFWIF_FWRAYCONTEXT) - (sizeof(RGXFWIF_FWCOMMONCONTEXT) * 2), |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| { |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| |
| OSWRLockAcquireWrite(psDevInfo->hRaytraceCtxListLock); |
| dllist_add_to_tail(&(psDevInfo->sRaytraceCtxtListHead), &(psRayContext->sListNode)); |
| OSWRLockReleaseWrite(psDevInfo->hRaytraceCtxListLock); |
| } |
| |
| *ppsRayContext= psRayContext; |
| return PVRSRV_OK; |
| |
| fail_rscontext: |
| _DestroySHContext(&psRayContext->sSHData, |
| psDeviceNode, |
| psRayContext->psCleanupSync); |
| fail_shcontext: |
| fail_frameworkcopy: |
| DevmemFwFree(psDevInfo, psRayContext->psFWFrameworkMemDesc); |
| fail_frameworkcreate: |
| SyncPrimFree(psRayContext->psCleanupSync); |
| fail_syncalloc: |
| DevmemFwFree(psDevInfo, psRayContext->psFWRayContextMemDesc); |
| fail_fwraycontext: |
| OSFreeMem(psRayContext); |
| PVR_ASSERT(eError != PVRSRV_OK); |
| |
| return eError; |
| } |
| |
| |
| /* |
| * PVRSRVRGXDestroyRayContextKM |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR PVRSRVRGXDestroyRayContextKM(RGX_SERVER_RAY_CONTEXT *psRayContext) |
| { |
| PVRSRV_ERROR eError; |
| IMG_UINT32 i; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psRayContext->psDeviceNode->pvDevice; |
| |
| /* remove node from list before calling destroy - as destroy, if successful |
| * will invalidate the node |
| * must be re-added if destroy fails |
| */ |
| OSWRLockAcquireWrite(psDevInfo->hRaytraceCtxListLock); |
| dllist_remove_node(&(psRayContext->sListNode)); |
| OSWRLockReleaseWrite(psDevInfo->hRaytraceCtxListLock); |
| |
| /* Cleanup the TA if we haven't already */ |
| if ((psRayContext->ui32CleanupStatus & RAY_CLEANUP_SH_COMPLETE) == 0) |
| { |
| eError = _DestroySHContext(&psRayContext->sSHData, |
| psRayContext->psDeviceNode, |
| psRayContext->psCleanupSync); |
| if (eError != PVRSRV_ERROR_RETRY) |
| { |
| psRayContext->ui32CleanupStatus |= RAY_CLEANUP_SH_COMPLETE; |
| } |
| else |
| { |
| goto e0; |
| } |
| } |
| |
| /* Cleanup the RS if we haven't already */ |
| if ((psRayContext->ui32CleanupStatus & RAY_CLEANUP_RS_COMPLETE) == 0) |
| { |
| eError = _DestroyRSContext(&psRayContext->sRSData, |
| psRayContext->psDeviceNode, |
| psRayContext->psCleanupSync); |
| if (eError != PVRSRV_ERROR_RETRY) |
| { |
| psRayContext->ui32CleanupStatus |= RAY_CLEANUP_RS_COMPLETE; |
| } |
| else |
| { |
| goto e0; |
| } |
| } |
| |
| #if 0 |
| /* |
| * FIXME - De-allocate RPM freelists (should be called from UM) |
| */ |
| RGXDestroyRPMFreeList(psRayContext->sSHData.psSHFFreeList); |
| RGXDestroyRPMFreeList(psRayContext->sSHData.psSHGFreeList); |
| #endif |
| |
| for (i = 0; i < DPX_MAX_RAY_CONTEXTS; i++) |
| { |
| RGXUnsetFirmwareAddress(psRayContext->sRSData.psFCClientCCBMemDesc[i]); |
| RGXUnsetFirmwareAddress(psRayContext->sRSData.psFCClientCCBCtrlMemDesc[i]); |
| RGXDestroyCCB(psDevInfo, psRayContext->sRSData.psFCClientCCB[i]); |
| } |
| |
| /* |
| Only if both TA and 3D contexts have been cleaned up can we |
| free the shared resources |
| */ |
| if (psRayContext->ui32CleanupStatus == (RAY_CLEANUP_RS_COMPLETE | RAY_CLEANUP_SH_COMPLETE)) |
| { |
| /* Free the framework buffer */ |
| DevmemFwFree(psDevInfo, psRayContext->psFWFrameworkMemDesc); |
| |
| /* Free the firmware ray context */ |
| DevmemFwFree(psDevInfo, psRayContext->psFWRayContextMemDesc); |
| |
| /* Free the cleanup sync */ |
| SyncPrimFree(psRayContext->psCleanupSync); |
| |
| OSFreeMem(psRayContext); |
| } |
| |
| return PVRSRV_OK; |
| |
| e0: |
| OSWRLockAcquireWrite(psDevInfo->hRaytraceCtxListLock); |
| dllist_add_to_tail(&(psDevInfo->sRaytraceCtxtListHead), &(psRayContext->sListNode)); |
| OSWRLockReleaseWrite(psDevInfo->hRaytraceCtxListLock); |
| return eError; |
| } |
| |
| /* |
| * PVRSRVRGXKickRSKM |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR PVRSRVRGXKickRSKM(RGX_SERVER_RAY_CONTEXT *psRayContext, |
| IMG_UINT32 ui32ClientCacheOpSeqNum, |
| IMG_UINT32 ui32ClientFenceCount, |
| SYNC_PRIMITIVE_BLOCK **pauiClientFenceUFOSyncPrimBlock, |
| IMG_UINT32 *paui32ClientFenceSyncOffset, |
| IMG_UINT32 *paui32ClientFenceValue, |
| IMG_UINT32 ui32ClientUpdateCount, |
| SYNC_PRIMITIVE_BLOCK **pauiClientUpdateUFOSyncPrimBlock, |
| IMG_UINT32 *paui32ClientUpdateSyncOffset, |
| IMG_UINT32 *paui32ClientUpdateValue, |
| IMG_UINT32 ui32ServerSyncPrims, |
| IMG_UINT32 *paui32ServerSyncFlags, |
| SERVER_SYNC_PRIMITIVE **pasServerSyncs, |
| IMG_UINT32 ui32CmdSize, |
| IMG_PBYTE pui8DMCmd, |
| IMG_UINT32 ui32FCCmdSize, |
| IMG_PBYTE pui8FCDMCmd, |
| IMG_UINT32 ui32FrameContextID, |
| IMG_UINT32 ui32PDumpFlags, |
| IMG_UINT32 ui32ExtJobRef) |
| { |
| RGXFWIF_KCCB_CMD sRSKCCBCmd; |
| RGX_CCB_CMD_HELPER_DATA asRSCmdHelperData[1] = {{0}}; |
| RGX_CCB_CMD_HELPER_DATA asFCCmdHelperData[1] = {{0}}; |
| PVRSRV_ERROR eError; |
| PVRSRV_ERROR eError1; |
| PVRSRV_ERROR eError2; |
| RGX_SERVER_RAY_RS_DATA *psRSData = &psRayContext->sRSData; |
| IMG_UINT32 i; |
| IMG_UINT32 ui32FCWoff; |
| IMG_UINT32 ui32RTUCmdOffset = 0; |
| IMG_UINT32 ui32JobId; |
| IMG_UINT32 ui32FWCtx; |
| |
| PRGXFWIF_TIMESTAMP_ADDR pPreAddr; |
| PRGXFWIF_TIMESTAMP_ADDR pPostAddr; |
| PRGXFWIF_UFO_ADDR pRMWUFOAddr; |
| |
| ui32JobId = OSAtomicIncrement(&psRayContext->hJobId); |
| |
| eError = SyncAddrListPopulate(&psRayContext->sSyncAddrListFence, |
| ui32ClientFenceCount, |
| pauiClientFenceUFOSyncPrimBlock, |
| paui32ClientFenceSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| eError = SyncAddrListPopulate(&psRayContext->sSyncAddrListUpdate, |
| ui32ClientUpdateCount, |
| pauiClientUpdateUFOSyncPrimBlock, |
| paui32ClientUpdateSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| /* Sanity check the server fences */ |
| for (i=0;i<ui32ServerSyncPrims;i++) |
| { |
| if (!(paui32ServerSyncFlags[i] & PVRSRV_CLIENT_SYNC_PRIM_OP_CHECK)) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Server fence (on RS) must fence", __FUNCTION__)); |
| return PVRSRV_ERROR_INVALID_SYNC_PRIM_OP; |
| } |
| } |
| |
| RGX_GetTimestampCmdHelper((PVRSRV_RGXDEV_INFO*) psRayContext->psDeviceNode->pvDevice, |
| & pPreAddr, |
| & pPostAddr, |
| & pRMWUFOAddr); |
| |
| |
| if(pui8DMCmd != NULL) |
| { |
| eError = RGXCmdHelperInitCmdCCB(psRSData->psFCClientCCB[ui32FrameContextID], |
| 0, |
| NULL, |
| NULL, |
| ui32ClientUpdateCount, |
| psRayContext->sSyncAddrListUpdate.pasFWAddrs, |
| paui32ClientUpdateValue, |
| ui32ServerSyncPrims, |
| paui32ServerSyncFlags, |
| SYNC_FLAG_MASK_ALL, |
| pasServerSyncs, |
| ui32CmdSize, |
| pui8DMCmd, |
| & pPreAddr, |
| & pPostAddr, |
| & pRMWUFOAddr, |
| RGXFWIF_CCB_CMD_TYPE_RTU, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| NULL, |
| "FC", |
| asFCCmdHelperData); |
| } |
| else |
| { |
| eError = RGXCmdHelperInitCmdCCB(psRSData->psFCClientCCB[ui32FrameContextID], |
| 0, |
| NULL, |
| NULL, |
| ui32ClientUpdateCount, |
| psRayContext->sSyncAddrListUpdate.pasFWAddrs, |
| paui32ClientUpdateValue, |
| ui32ServerSyncPrims, |
| paui32ServerSyncFlags, |
| SYNC_FLAG_MASK_ALL, |
| pasServerSyncs, |
| ui32CmdSize, |
| pui8DMCmd, |
| & pPreAddr, |
| & pPostAddr, |
| & pRMWUFOAddr, |
| RGXFWIF_CCB_CMD_TYPE_NULL, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| NULL, |
| "FC", |
| asFCCmdHelperData); |
| |
| } |
| |
| if (eError != PVRSRV_OK) |
| { |
| goto PVRSRVRGXKickRSKM_Exit; |
| } |
| |
| eError = RGXCmdHelperAcquireCmdCCB(IMG_ARR_NUM_ELEMS(asFCCmdHelperData), |
| asFCCmdHelperData); |
| if (eError != PVRSRV_OK) |
| { |
| goto PVRSRVRGXKickRSKM_Exit; |
| } |
| |
| ui32FCWoff = RGXCmdHelperGetCommandSize(IMG_ARR_NUM_ELEMS(asFCCmdHelperData), |
| asFCCmdHelperData); |
| |
| *(IMG_UINT32*)pui8FCDMCmd = RGXGetHostWriteOffsetCCB(psRSData->psFCClientCCB[ui32FrameContextID]) + ui32FCWoff; |
| |
| /* |
| We should reserved space in the kernel CCB here and fill in the command |
| directly. |
| This is so if there isn't space in the kernel CCB we can return with |
| retry back to services client before we take any operations |
| */ |
| |
| /* |
| We might only be kicking for flush out a padding packet so only submit |
| the command if the create was successful |
| */ |
| eError1 = RGXCmdHelperInitCmdCCB(FWCommonContextGetClientCCB(psRSData->psServerCommonContext), |
| ui32ClientFenceCount, |
| psRayContext->sSyncAddrListFence.pasFWAddrs, |
| paui32ClientFenceValue, |
| 0, |
| NULL, |
| NULL, |
| ui32ServerSyncPrims, |
| paui32ServerSyncFlags, |
| SYNC_FLAG_MASK_ALL, |
| pasServerSyncs, |
| ui32FCCmdSize, |
| pui8FCDMCmd, |
| NULL, |
| & pPostAddr, |
| & pRMWUFOAddr, |
| RGXFWIF_CCB_CMD_TYPE_RTU_FC, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| NULL, |
| "RS", |
| asRSCmdHelperData); |
| if (eError1 != PVRSRV_OK) |
| { |
| goto PVRSRVRGXKickRSKM_Exit; |
| } |
| |
| eError1 = RGXCmdHelperAcquireCmdCCB(IMG_ARR_NUM_ELEMS(asRSCmdHelperData), |
| asRSCmdHelperData); |
| if (eError1 != PVRSRV_OK) |
| { |
| goto PVRSRVRGXKickRSKM_Exit; |
| } |
| |
| |
| /* |
| We should reserved space in the kernel CCB here and fill in the command |
| directly. |
| This is so if there isn't space in the kernel CCB we can return with |
| retry back to services client before we take any operations |
| */ |
| |
| /* |
| We might only be kicking for flush out a padding packet so only submit |
| the command if the create was successful |
| */ |
| if (eError == PVRSRV_OK) |
| { |
| /* |
| All the required resources are ready at this point, we can't fail so |
| take the required server sync operations and commit all the resources |
| */ |
| RGXCmdHelperReleaseCmdCCB(IMG_ARR_NUM_ELEMS(asFCCmdHelperData), |
| asFCCmdHelperData, "FC", 0); |
| } |
| |
| if (eError1 == PVRSRV_OK) |
| { |
| /* |
| All the required resources are ready at this point, we can't fail so |
| take the required server sync operations and commit all the resources |
| */ |
| ui32RTUCmdOffset = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRSData->psServerCommonContext)); |
| RGXCmdHelperReleaseCmdCCB(IMG_ARR_NUM_ELEMS(asRSCmdHelperData), |
| asRSCmdHelperData, "RS", |
| FWCommonContextGetFWAddress(psRSData->psServerCommonContext).ui32Addr); |
| } |
| |
| /* |
| * Construct the kernel RTU CCB command. |
| * (Safe to release reference to ray context virtual address because |
| * ray context destruction must flush the firmware). |
| */ |
| sRSKCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_KICK; |
| sRSKCCBCmd.uCmdData.sCmdKickData.psContext = FWCommonContextGetFWAddress(psRSData->psServerCommonContext); |
| sRSKCCBCmd.uCmdData.sCmdKickData.ui32CWoffUpdate = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRSData->psServerCommonContext)); |
| sRSKCCBCmd.uCmdData.sCmdKickData.ui32NumCleanupCtl = 0; |
| |
| ui32FWCtx = FWCommonContextGetFWAddress(psRSData->psServerCommonContext).ui32Addr; |
| |
| HTBLOGK(HTB_SF_MAIN_KICK_RTU, |
| sRSKCCBCmd.uCmdData.sCmdKickData.psContext, |
| ui32RTUCmdOffset |
| ); |
| RGX_HWPERF_HOST_ENQ(psRayContext, OSGetCurrentClientProcessIDKM(), |
| ui32FWCtx, ui32ExtJobRef, ui32JobId, |
| RGX_HWPERF_KICK_TYPE_RS); |
| |
| /* |
| * Submit the RTU command to the firmware. |
| */ |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError2 = RGXScheduleCommand(psRayContext->psDeviceNode->pvDevice, |
| RGXFWIF_DM_RTU, |
| &sRSKCCBCmd, |
| sizeof(sRSKCCBCmd), |
| ui32ClientCacheOpSeqNum, |
| ui32PDumpFlags); |
| if (eError2 != PVRSRV_ERROR_RETRY) |
| { |
| break; |
| } |
| OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT); |
| } END_LOOP_UNTIL_TIMEOUT(); |
| |
| if (eError2 != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "PVRSRVRGXKickRSKM failed to schedule kernel RTU command. Error:%u", eError)); |
| if (eError == PVRSRV_OK) |
| { |
| eError = eError2; |
| } |
| goto PVRSRVRGXKickRSKM_Exit; |
| } |
| else |
| { |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| RGXHWPerfFTraceGPUEnqueueEvent(psRayContext->psDeviceNode->pvDevice, |
| ui32FWCtx, ui32JobId, RGX_HWPERF_KICK_TYPE_RS); |
| #endif |
| } |
| |
| |
| PVRSRVRGXKickRSKM_Exit: |
| err_populate_sync_addr_list: |
| return eError; |
| } |
| |
| /* |
| * PVRSRVRGXKickVRDMKM |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR PVRSRVRGXKickVRDMKM(RGX_SERVER_RAY_CONTEXT *psRayContext, |
| IMG_UINT32 ui32ClientCacheOpSeqNum, |
| IMG_UINT32 ui32ClientFenceCount, |
| SYNC_PRIMITIVE_BLOCK **pauiClientFenceUFOSyncPrimBlock, |
| IMG_UINT32 *paui32ClientFenceSyncOffset, |
| IMG_UINT32 *paui32ClientFenceValue, |
| IMG_UINT32 ui32ClientUpdateCount, |
| SYNC_PRIMITIVE_BLOCK **pauiClientUpdateUFOSyncPrimBlock, |
| IMG_UINT32 *paui32ClientUpdateSyncOffset, |
| IMG_UINT32 *paui32ClientUpdateValue, |
| IMG_UINT32 ui32ServerSyncPrims, |
| IMG_UINT32 *paui32ServerSyncFlags, |
| SERVER_SYNC_PRIMITIVE **pasServerSyncs, |
| IMG_UINT32 ui32CmdSize, |
| IMG_PBYTE pui8DMCmd, |
| IMG_UINT32 ui32PDumpFlags, |
| IMG_UINT32 ui32ExtJobRef) |
| { |
| RGXFWIF_KCCB_CMD sSHKCCBCmd; |
| RGX_CCB_CMD_HELPER_DATA sCmdHelperData; |
| PVRSRV_ERROR eError; |
| PVRSRV_ERROR eError2; |
| RGX_SERVER_RAY_SH_DATA *psSHData = &psRayContext->sSHData; |
| IMG_UINT32 i; |
| IMG_UINT32 ui32SHGCmdOffset = 0; |
| IMG_UINT32 ui32JobId; |
| IMG_UINT32 ui32FWCtx; |
| |
| PRGXFWIF_TIMESTAMP_ADDR pPreAddr; |
| PRGXFWIF_TIMESTAMP_ADDR pPostAddr; |
| PRGXFWIF_UFO_ADDR pRMWUFOAddr; |
| |
| ui32JobId = OSAtomicIncrement(&psRayContext->hJobId); |
| |
| eError = SyncAddrListPopulate(&psRayContext->sSyncAddrListFence, |
| ui32ClientFenceCount, |
| pauiClientFenceUFOSyncPrimBlock, |
| paui32ClientFenceSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| eError = SyncAddrListPopulate(&psRayContext->sSyncAddrListUpdate, |
| ui32ClientUpdateCount, |
| pauiClientUpdateUFOSyncPrimBlock, |
| paui32ClientUpdateSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| /* Sanity check the server fences */ |
| for (i=0;i<ui32ServerSyncPrims;i++) |
| { |
| if (!(paui32ServerSyncFlags[i] & PVRSRV_CLIENT_SYNC_PRIM_OP_CHECK)) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Server fence (on SH) must fence", __FUNCTION__)); |
| return PVRSRV_ERROR_INVALID_SYNC_PRIM_OP; |
| } |
| } |
| |
| RGX_GetTimestampCmdHelper((PVRSRV_RGXDEV_INFO*) psRayContext->psDeviceNode->pvDevice, |
| & pPreAddr, |
| & pPostAddr, |
| & pRMWUFOAddr); |
| |
| eError = RGXCmdHelperInitCmdCCB(FWCommonContextGetClientCCB(psSHData->psServerCommonContext), |
| ui32ClientFenceCount, |
| psRayContext->sSyncAddrListFence.pasFWAddrs, |
| paui32ClientFenceValue, |
| ui32ClientUpdateCount, |
| psRayContext->sSyncAddrListUpdate.pasFWAddrs, |
| paui32ClientUpdateValue, |
| ui32ServerSyncPrims, |
| paui32ServerSyncFlags, |
| SYNC_FLAG_MASK_ALL, |
| pasServerSyncs, |
| ui32CmdSize, |
| pui8DMCmd, |
| & pPreAddr, |
| & pPostAddr, |
| & pRMWUFOAddr, |
| RGXFWIF_CCB_CMD_TYPE_SHG, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| NULL, |
| "SH", |
| &sCmdHelperData); |
| |
| if (eError != PVRSRV_OK) |
| { |
| goto PVRSRVRGXKickSHKM_Exit; |
| } |
| |
| eError = RGXCmdHelperAcquireCmdCCB(1, &sCmdHelperData); |
| if (eError != PVRSRV_OK) |
| { |
| goto PVRSRVRGXKickSHKM_Exit; |
| } |
| |
| |
| /* |
| We should reserve space in the kernel CCB here and fill in the command |
| directly. |
| This is so if there isn't space in the kernel CCB we can return with |
| retry back to services client before we take any operations |
| */ |
| |
| /* |
| We might only be kicking for flush out a padding packet so only submit |
| the command if the create was successful |
| */ |
| if (eError == PVRSRV_OK) |
| { |
| /* |
| All the required resources are ready at this point, we can't fail so |
| take the required server sync operations and commit all the resources |
| */ |
| ui32SHGCmdOffset = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psSHData->psServerCommonContext)); |
| RGXCmdHelperReleaseCmdCCB(1, &sCmdHelperData, "SH", FWCommonContextGetFWAddress(psSHData->psServerCommonContext).ui32Addr); |
| } |
| |
| /* |
| * Construct the kernel SHG CCB command. |
| * (Safe to release reference to ray context virtual address because |
| * ray context destruction must flush the firmware). |
| */ |
| sSHKCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_KICK; |
| sSHKCCBCmd.uCmdData.sCmdKickData.psContext = FWCommonContextGetFWAddress(psSHData->psServerCommonContext); |
| sSHKCCBCmd.uCmdData.sCmdKickData.ui32CWoffUpdate = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psSHData->psServerCommonContext)); |
| sSHKCCBCmd.uCmdData.sCmdKickData.ui32NumCleanupCtl = 0; |
| |
| ui32FWCtx = FWCommonContextGetFWAddress(psSHData->psServerCommonContext).ui32Addr; |
| |
| HTBLOGK(HTB_SF_MAIN_KICK_SHG, |
| sSHKCCBCmd.uCmdData.sCmdKickData.psContext, |
| ui32SHGCmdOffset |
| ); |
| RGX_HWPERF_HOST_ENQ(psRayContext, OSGetCurrentClientProcessIDKM(), |
| ui32FWCtx, ui32ExtJobRef, ui32JobId, |
| RGX_HWPERF_KICK_TYPE_VRDM); |
| |
| /* |
| * Submit the RTU command to the firmware. |
| */ |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError2 = RGXScheduleCommand(psRayContext->psDeviceNode->pvDevice, |
| RGXFWIF_DM_SHG, |
| &sSHKCCBCmd, |
| sizeof(sSHKCCBCmd), |
| ui32ClientCacheOpSeqNum, |
| ui32PDumpFlags); |
| if (eError2 != PVRSRV_ERROR_RETRY) |
| { |
| break; |
| } |
| OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT); |
| } END_LOOP_UNTIL_TIMEOUT(); |
| |
| if (eError2 != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "PVRSRVRGXKickSHKM failed to schedule kernel RTU command. Error:%u", eError)); |
| if (eError == PVRSRV_OK) |
| { |
| eError = eError2; |
| } |
| goto PVRSRVRGXKickSHKM_Exit; |
| } |
| else |
| { |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| RGXHWPerfFTraceGPUEnqueueEvent(psRayContext->psDeviceNode->pvDevice, |
| ui32FWCtx, ui32JobId, RGX_HWPERF_KICK_TYPE_VRDM); |
| #endif |
| } |
| |
| |
| PVRSRVRGXKickSHKM_Exit: |
| err_populate_sync_addr_list: |
| return eError; |
| } |
| |
| PVRSRV_ERROR PVRSRVRGXSetRayContextPriorityKM(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE * psDeviceNode, |
| RGX_SERVER_RAY_CONTEXT *psRayContext, |
| IMG_UINT32 ui32Priority) |
| { |
| PVRSRV_ERROR eError; |
| |
| PVR_UNREFERENCED_PARAMETER(psDeviceNode); |
| |
| if (psRayContext->sSHData.ui32Priority != ui32Priority) |
| { |
| eError = ContextSetPriority(psRayContext->sSHData.psServerCommonContext, |
| psConnection, |
| psRayContext->psDeviceNode->pvDevice, |
| ui32Priority, |
| RGXFWIF_DM_SHG); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to set the priority of the SH part of the rendercontext (%s)", __FUNCTION__, PVRSRVGetErrorStringKM(eError))); |
| goto fail_shcontext; |
| } |
| |
| psRayContext->sSHData.ui32Priority = ui32Priority; |
| } |
| |
| if (psRayContext->sRSData.ui32Priority != ui32Priority) |
| { |
| eError = ContextSetPriority(psRayContext->sRSData.psServerCommonContext, |
| psConnection, |
| psRayContext->psDeviceNode->pvDevice, |
| ui32Priority, |
| RGXFWIF_DM_RTU); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to set the priority of the RS part of the rendercontext (%s)", __FUNCTION__, PVRSRVGetErrorStringKM(eError))); |
| goto fail_rscontext; |
| } |
| |
| psRayContext->sRSData.ui32Priority = ui32Priority; |
| } |
| return PVRSRV_OK; |
| |
| fail_rscontext: |
| fail_shcontext: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| void CheckForStalledRayCtxt(PVRSRV_RGXDEV_INFO *psDevInfo, |
| DUMPDEBUG_PRINTF_FUNC *pfnDumpDebugPrintf, |
| void *pvDumpDebugFile) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| OSWRLockAcquireRead(psDevInfo->hRaytraceCtxListLock); |
| dllist_foreach_node(&psDevInfo->sRaytraceCtxtListHead, psNode, psNext) |
| { |
| RGX_SERVER_RAY_CONTEXT *psCurrentServerRayCtx = |
| IMG_CONTAINER_OF(psNode, RGX_SERVER_RAY_CONTEXT, sListNode); |
| |
| DumpStalledFWCommonContext(psCurrentServerRayCtx->sSHData.psServerCommonContext, |
| pfnDumpDebugPrintf, pvDumpDebugFile); |
| DumpStalledFWCommonContext(psCurrentServerRayCtx->sRSData.psServerCommonContext, |
| pfnDumpDebugPrintf, pvDumpDebugFile); |
| } |
| OSWRLockReleaseRead(psDevInfo->hRaytraceCtxListLock); |
| } |
| |
| IMG_UINT32 CheckForStalledClientRayCtxt(PVRSRV_RGXDEV_INFO *psDevInfo) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| IMG_UINT32 ui32ContextBitMask = 0; |
| |
| OSWRLockAcquireRead(psDevInfo->hRaytraceCtxListLock); |
| |
| dllist_foreach_node(&psDevInfo->sRaytraceCtxtListHead, psNode, psNext) |
| { |
| RGX_SERVER_RAY_CONTEXT *psCurrentServerRayCtx = |
| IMG_CONTAINER_OF(psNode, RGX_SERVER_RAY_CONTEXT, sListNode); |
| if(NULL != psCurrentServerRayCtx->sSHData.psServerCommonContext) |
| { |
| if (CheckStalledClientCommonContext(psCurrentServerRayCtx->sSHData.psServerCommonContext, RGX_KICK_TYPE_DM_RTU) == PVRSRV_ERROR_CCCB_STALLED) |
| { |
| ui32ContextBitMask |= RGX_KICK_TYPE_DM_RTU; |
| } |
| } |
| |
| if(NULL != psCurrentServerRayCtx->sRSData.psServerCommonContext) |
| { |
| if (CheckStalledClientCommonContext(psCurrentServerRayCtx->sRSData.psServerCommonContext, RGX_KICK_TYPE_DM_SHG) == PVRSRV_ERROR_CCCB_STALLED) |
| { |
| ui32ContextBitMask |= RGX_KICK_TYPE_DM_SHG; |
| } |
| } |
| } |
| |
| OSWRLockReleaseRead(psDevInfo->hRaytraceCtxListLock); |
| return ui32ContextBitMask; |
| } |
| |
| /****************************************************************************** |
| End of file (rgxSHGRTU.c) |
| ******************************************************************************/ |