| /*************************************************************************/ /*! |
| @File |
| @Title RGX TA/3D routines |
| @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved |
| @Description RGX TA/3D 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> |
| |
| #include "pdump_km.h" |
| #include "pvr_debug.h" |
| #include "rgxutils.h" |
| #include "rgxfwutils.h" |
| #include "rgxta3d.h" |
| #include "rgxmem.h" |
| #include "allocmem.h" |
| #include "devicemem.h" |
| #include "devicemem_pdump.h" |
| #include "ri_server.h" |
| #include "osfunc.h" |
| #include "pvrsrv.h" |
| #include "rgx_memallocflags.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" |
| |
| #if defined(SUPPORT_BUFFER_SYNC) |
| #include "pvr_buffer_sync.h" |
| #endif |
| |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| #include "pvr_sync.h" |
| #endif |
| |
| #if defined(SUPPORT_PDVFS) |
| #include "rgxpdvfs.h" |
| #endif |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| #include "hash.h" |
| #include "rgxworkest.h" |
| |
| #define HASH_CLEAN_LIMIT 6 |
| #endif |
| |
| typedef struct _DEVMEM_REF_LOOKUP_ |
| { |
| IMG_UINT32 ui32ZSBufferID; |
| RGX_ZSBUFFER_DATA *psZSBuffer; |
| } DEVMEM_REF_LOOKUP; |
| |
| typedef struct _DEVMEM_FREELIST_LOOKUP_ |
| { |
| IMG_UINT32 ui32FreeListID; |
| RGX_FREELIST *psFreeList; |
| } DEVMEM_FREELIST_LOOKUP; |
| |
| typedef struct { |
| DEVMEM_MEMDESC *psContextStateMemDesc; |
| RGX_SERVER_COMMON_CONTEXT *psServerCommonContext; |
| IMG_UINT32 ui32Priority; |
| } RGX_SERVER_RC_TA_DATA; |
| |
| typedef struct { |
| DEVMEM_MEMDESC *psContextStateMemDesc; |
| RGX_SERVER_COMMON_CONTEXT *psServerCommonContext; |
| IMG_UINT32 ui32Priority; |
| } RGX_SERVER_RC_3D_DATA; |
| |
| struct _RGX_SERVER_RENDER_CONTEXT_ { |
| PVRSRV_DEVICE_NODE *psDeviceNode; |
| DEVMEM_MEMDESC *psFWRenderContextMemDesc; |
| DEVMEM_MEMDESC *psFWFrameworkMemDesc; |
| RGX_SERVER_RC_TA_DATA sTAData; |
| RGX_SERVER_RC_3D_DATA s3DData; |
| IMG_UINT32 ui32CleanupStatus; |
| #define RC_CLEANUP_TA_COMPLETE (1 << 0) |
| #define RC_CLEANUP_3D_COMPLETE (1 << 1) |
| PVRSRV_CLIENT_SYNC_PRIM *psCleanupSync; |
| DLLIST_NODE sListNode; |
| SYNC_ADDR_LIST sSyncAddrListTAFence; |
| SYNC_ADDR_LIST sSyncAddrListTAUpdate; |
| SYNC_ADDR_LIST sSyncAddrList3DFence; |
| SYNC_ADDR_LIST sSyncAddrList3DUpdate; |
| ATOMIC_T hJobId; |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| WORKEST_HOST_DATA sWorkEstData; |
| #endif |
| }; |
| |
| |
| #if ! defined(NO_HARDWARE) |
| 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 functions used by render context code |
| */ |
| |
| static |
| PVRSRV_ERROR _DestroyTAContext(RGX_SERVER_RC_TA_DATA *psTAData, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| PVRSRV_CLIENT_SYNC_PRIM *psCleanupSync) |
| { |
| PVRSRV_ERROR eError; |
| |
| /* Check if the FW has finished with this resource ... */ |
| eError = RGXFWRequestCommonContextCleanUp(psDeviceNode, |
| psTAData->psServerCommonContext, |
| psCleanupSync, |
| RGXFWIF_DM_TA, |
| 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 it's resources */ |
| #if defined(DEBUG) |
| /* Log the number of TA context stores which occurred */ |
| { |
| RGXFWIF_TACTX_STATE *psFWTAState; |
| |
| eError = DevmemAcquireCpuVirtAddr(psTAData->psContextStateMemDesc, |
| (void**)&psFWTAState); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Failed to map firmware render context state (%u)", |
| __FUNCTION__, eError)); |
| } |
| else |
| { |
| /* Release the CPU virt addr */ |
| DevmemReleaseCpuVirtAddr(psTAData->psContextStateMemDesc); |
| } |
| } |
| #endif |
| FWCommonContextFree(psTAData->psServerCommonContext); |
| DevmemFwFree(psDeviceNode->pvDevice, psTAData->psContextStateMemDesc); |
| psTAData->psServerCommonContext = NULL; |
| return PVRSRV_OK; |
| } |
| |
| static |
| PVRSRV_ERROR _Destroy3DContext(RGX_SERVER_RC_3D_DATA *ps3DData, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| PVRSRV_CLIENT_SYNC_PRIM *psCleanupSync) |
| { |
| PVRSRV_ERROR eError; |
| |
| /* Check if the FW has finished with this resource ... */ |
| eError = RGXFWRequestCommonContextCleanUp(psDeviceNode, |
| ps3DData->psServerCommonContext, |
| psCleanupSync, |
| RGXFWIF_DM_3D, |
| 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 it's resources */ |
| #if defined(DEBUG) |
| /* Log the number of 3D context stores which occurred */ |
| { |
| RGXFWIF_3DCTX_STATE *psFW3DState; |
| |
| eError = DevmemAcquireCpuVirtAddr(ps3DData->psContextStateMemDesc, |
| (void**)&psFW3DState); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Failed to map firmware render context state (%u)", |
| __FUNCTION__, eError)); |
| } |
| else |
| { |
| /* Release the CPU virt addr */ |
| DevmemReleaseCpuVirtAddr(ps3DData->psContextStateMemDesc); |
| } |
| } |
| #endif |
| |
| FWCommonContextFree(ps3DData->psServerCommonContext); |
| DevmemFwFree(psDeviceNode->pvDevice, ps3DData->psContextStateMemDesc); |
| ps3DData->psServerCommonContext = NULL; |
| return PVRSRV_OK; |
| } |
| |
| static void _RGXDumpPMRPageList(DLLIST_NODE *psNode) |
| { |
| RGX_PMR_NODE *psPMRNode = IMG_CONTAINER_OF(psNode, RGX_PMR_NODE, sMemoryBlock); |
| PVRSRV_ERROR eError; |
| |
| eError = PMRDumpPageList(psPMRNode->psPMR, |
| RGX_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"Error (%u) printing pmr %p", eError, psPMRNode->psPMR)); |
| } |
| } |
| |
| IMG_BOOL RGXDumpFreeListPageList(RGX_FREELIST *psFreeList) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| |
| PVR_LOG(("Freelist FWAddr 0x%08x, ID = %d, CheckSum 0x%016llx", |
| psFreeList->sFreeListFWDevVAddr.ui32Addr, |
| psFreeList->ui32FreelistID, |
| psFreeList->ui64FreelistChecksum)); |
| |
| /* Dump Init FreeList page list */ |
| PVR_LOG((" Initial Memory block")); |
| dllist_foreach_node(&psFreeList->sMemoryBlockInitHead, psNode, psNext) |
| { |
| _RGXDumpPMRPageList(psNode); |
| } |
| |
| /* Dump Grow FreeList page list */ |
| PVR_LOG((" Grow Memory blocks")); |
| dllist_foreach_node(&psFreeList->sMemoryBlockHead, psNode, psNext) |
| { |
| _RGXDumpPMRPageList(psNode); |
| } |
| |
| return IMG_TRUE; |
| } |
| |
| static PVRSRV_ERROR _UpdateFwFreelistSize(RGX_FREELIST *psFreeList, |
| IMG_BOOL bGrow, |
| IMG_UINT32 ui32DeltaSize) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGXFWIF_KCCB_CMD sGPCCBCmd; |
| |
| sGPCCBCmd.eCmdType = (bGrow) ? RGXFWIF_KCCB_CMD_FREELIST_GROW_UPDATE : RGXFWIF_KCCB_CMD_FREELIST_SHRINK_UPDATE; |
| sGPCCBCmd.uCmdData.sFreeListGSData.sFreeListFWDevVAddr.ui32Addr = psFreeList->sFreeListFWDevVAddr.ui32Addr; |
| sGPCCBCmd.uCmdData.sFreeListGSData.ui32DeltaSize = ui32DeltaSize; |
| sGPCCBCmd.uCmdData.sFreeListGSData.ui32NewSize = psFreeList->ui32CurrentFLPages; |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "Send FW update: 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, "_UpdateFwFreelistSize: failed to update FW freelist size. (error = %u)", eError)); |
| return eError; |
| } |
| |
| return eError; |
| } |
| |
| static void _CheckFreelist(RGX_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(("_CheckFreelist: 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(("_CheckFreelist: Failed to get freelist data for 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(("_CheckFreelist: 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(("_CheckFreelist: Checksum mismatch for freelist %p! Expected 0x%016llx calculated 0x%016llx", |
| psFreeList, ui64ExpectedCheckSum, *pui64CalculatedCheckSum)); |
| bFreelistBad = IMG_TRUE; |
| } |
| |
| if (bFreelistBad) |
| { |
| PVR_LOG(("_CheckFreelist: Sleeping for ever!")); |
| sleep_for_ever(); |
| // PVR_ASSERT(!bFreelistBad); |
| } |
| #endif |
| } |
| |
| PVRSRV_ERROR RGXGrowFreeList(RGX_FREELIST *psFreeList, |
| IMG_UINT32 ui32NumPages, |
| PDLLIST_NODE pListHeader) |
| { |
| RGX_PMR_NODE *psPMRNode; |
| IMG_DEVMEM_SIZE_T uiSize; |
| IMG_UINT32 ui32MappingTable = 0; |
| IMG_DEVMEM_OFFSET_T uiOffset; |
| IMG_DEVMEM_SIZE_T uiLength; |
| IMG_DEVMEM_SIZE_T uistartPage; |
| PVRSRV_ERROR eError; |
| const IMG_CHAR * pszAllocName = "Free List"; |
| |
| /* Are we allowed to grow ? */ |
| if ((psFreeList->ui32MaxFLPages - psFreeList->ui32CurrentFLPages) < ui32NumPages) |
| { |
| PVR_DPF((PVR_DBG_WARNING,"Freelist [0x%p]: grow by %u pages denied. Max PB size reached (current pages %u/%u)", |
| psFreeList, |
| ui32NumPages, |
| psFreeList->ui32CurrentFLPages, |
| psFreeList->ui32MaxFLPages)); |
| return PVRSRV_ERROR_PBSIZE_ALREADY_MAX; |
| } |
| |
| /* Allocate kernel memory block structure */ |
| psPMRNode = OSAllocMem(sizeof(*psPMRNode)); |
| if (psPMRNode == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXGrowFreeList: failed to allocate host data structure")); |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto ErrorAllocHost; |
| } |
| |
| /* |
| * Lock protects simultaneous manipulation of: |
| * - the memory block list |
| * - the freelist's ui32CurrentFLPages |
| */ |
| OSLockAcquire(psFreeList->psDevInfo->hLockFreeList); |
| |
| |
| psPMRNode->ui32NumPages = ui32NumPages; |
| psPMRNode->psFreeList = psFreeList; |
| |
| /* Allocate Memory Block */ |
| PDUMPCOMMENT("Allocate PB Block (Pages %08X)", ui32NumPages); |
| uiSize = (IMG_DEVMEM_SIZE_T)ui32NumPages * RGX_BIF_PM_PHYSICAL_PAGE_SIZE; |
| eError = PhysmemNewRamBackedPMR(NULL, |
| psFreeList->psDevInfo->psDeviceNode, |
| uiSize, |
| uiSize, |
| 1, |
| 1, |
| &ui32MappingTable, |
| RGX_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT, |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE, |
| OSStringLength(pszAllocName) + 1, |
| pszAllocName, |
| &psPMRNode->psPMR); |
| if(eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXGrowFreeList: Failed to allocate PB block of size: 0x%016llX", |
| (IMG_UINT64)uiSize)); |
| goto ErrorBlockAlloc; |
| } |
| |
| /* Zeroing physical pages pointed by the PMR */ |
| if (psFreeList->psDevInfo->ui32DeviceFlags & RGXKM_DEVICE_STATE_ZERO_FREELIST) |
| { |
| eError = PMRZeroingPMR(psPMRNode->psPMR, RGX_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXGrowFreeList: Failed to zero PMR %p of freelist %p with Error %d", |
| psPMRNode->psPMR, |
| psFreeList, |
| eError)); |
| PVR_ASSERT(0); |
| } |
| } |
| |
| uiLength = psPMRNode->ui32NumPages * sizeof(IMG_UINT32); |
| uistartPage = (psFreeList->ui32MaxFLPages - psFreeList->ui32CurrentFLPages - psPMRNode->ui32NumPages); |
| uiOffset = psFreeList->uiFreeListPMROffset + (uistartPage * sizeof(IMG_UINT32)); |
| |
| #if defined(PVR_RI_DEBUG) |
| |
| eError = RIWritePMREntryKM(psPMRNode->psPMR, |
| OSStringNLength(pszAllocName, RI_MAX_TEXT_LEN), |
| pszAllocName, |
| uiSize); |
| if( eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "%s: call to RIWritePMREntryKM failed (eError=%d)", |
| __func__, |
| eError)); |
| } |
| |
| /* Attach RI information */ |
| eError = RIWriteMEMDESCEntryKM(psPMRNode->psPMR, |
| OSStringNLength(pszAllocName, RI_MAX_TEXT_LEN), |
| pszAllocName, |
| 0, |
| uiSize, |
| uiSize, |
| IMG_FALSE, |
| IMG_FALSE, |
| &psPMRNode->hRIHandle); |
| if( eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "%s: call to RIWriteMEMDESCEntryKM failed (eError=%d)", |
| __func__, |
| eError)); |
| } |
| |
| #endif /* if defined(PVR_RI_DEBUG) */ |
| |
| /* write Freelist with Memory Block physical addresses */ |
| eError = PMRWritePMPageList( |
| /* Target PMR, offset, and length */ |
| psFreeList->psFreeListPMR, |
| uiOffset, |
| uiLength, |
| /* Referenced PMR, and "page" granularity */ |
| psPMRNode->psPMR, |
| RGX_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT, |
| &psPMRNode->psPageList); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXGrowFreeList: Failed to write pages of Node %p", |
| psPMRNode)); |
| goto ErrorPopulateFreelist; |
| } |
| |
| /* We add It must be added to the tail, otherwise the freelist population won't work */ |
| dllist_add_to_head(pListHeader, &psPMRNode->sMemoryBlock); |
| |
| /* Update number of available pages */ |
| psFreeList->ui32CurrentFLPages += ui32NumPages; |
| |
| /* Update statistics */ |
| if (psFreeList->ui32NumHighPages < psFreeList->ui32CurrentFLPages) |
| { |
| psFreeList->ui32NumHighPages = psFreeList->ui32CurrentFLPages; |
| } |
| |
| if (psFreeList->bCheckFreelist) |
| { |
| /* We can only do a freelist check if the list is full (e.g. at initial creation time) */ |
| if (psFreeList->ui32CurrentFLPages == ui32NumPages) |
| { |
| IMG_UINT64 ui64Dummy; |
| _CheckFreelist(psFreeList, ui32NumPages, psFreeList->ui64FreelistChecksum, &ui64Dummy); |
| } |
| } |
| |
| OSLockRelease(psFreeList->psDevInfo->hLockFreeList); |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"Freelist [%p]: grow by %u pages (current pages %u/%u)", |
| psFreeList, |
| ui32NumPages, |
| psFreeList->ui32CurrentFLPages, |
| psFreeList->ui32MaxFLPages)); |
| |
| return PVRSRV_OK; |
| |
| /* Error handling */ |
| ErrorPopulateFreelist: |
| PMRUnrefPMR(psPMRNode->psPMR); |
| |
| ErrorBlockAlloc: |
| OSFreeMem(psPMRNode); |
| OSLockRelease(psFreeList->psDevInfo->hLockFreeList); |
| |
| ErrorAllocHost: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| |
| } |
| |
| static PVRSRV_ERROR RGXShrinkFreeList(PDLLIST_NODE pListHeader, |
| RGX_FREELIST *psFreeList) |
| { |
| DLLIST_NODE *psNode; |
| RGX_PMR_NODE *psPMRNode; |
| 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->hLockFreeList); |
| |
| OSLockAcquire(psFreeList->psDevInfo->hLockFreeList); |
| |
| /* Get node from head of list and remove it */ |
| psNode = dllist_get_next_node(pListHeader); |
| if (psNode) |
| { |
| dllist_remove_node(psNode); |
| |
| psPMRNode = IMG_CONTAINER_OF(psNode, RGX_PMR_NODE, sMemoryBlock); |
| PVR_ASSERT(psPMRNode); |
| PVR_ASSERT(psPMRNode->psPMR); |
| PVR_ASSERT(psPMRNode->psFreeList); |
| |
| /* remove block from freelist list */ |
| |
| /* Unwrite Freelist with Memory Block physical addresses */ |
| eError = PMRUnwritePMPageList(psPMRNode->psPageList); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXRemoveBlockFromFreeListKM: Failed to unwrite pages of Node %p", |
| psPMRNode)); |
| PVR_ASSERT(IMG_FALSE); |
| } |
| |
| #if defined(PVR_RI_DEBUG) |
| |
| if (psPMRNode->hRIHandle) |
| { |
| PVRSRV_ERROR eError; |
| |
| eError = RIDeleteMEMDESCEntryKM(psPMRNode->hRIHandle); |
| if( eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: call to RIDeleteMEMDESCEntryKM failed (eError=%d)", __func__, eError)); |
| } |
| } |
| |
| #endif /* if defined(PVR_RI_DEBUG) */ |
| |
| /* Free PMR (We should be the only one that holds a ref on the PMR) */ |
| eError = PMRUnrefPMR(psPMRNode->psPMR); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXRemoveBlockFromFreeListKM: Failed to free PB block %p (error %u)", |
| psPMRNode->psPMR, |
| eError)); |
| PVR_ASSERT(IMG_FALSE); |
| } |
| |
| /* update available pages in freelist */ |
| ui32OldValue = psFreeList->ui32CurrentFLPages; |
| psFreeList->ui32CurrentFLPages -= psPMRNode->ui32NumPages; |
| |
| /* check underflow */ |
| PVR_ASSERT(ui32OldValue > psFreeList->ui32CurrentFLPages); |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "Freelist [%p]: shrink by %u pages (current pages %u/%u)", |
| psFreeList, |
| psPMRNode->ui32NumPages, |
| psFreeList->ui32CurrentFLPages, |
| psFreeList->ui32MaxFLPages)); |
| |
| OSFreeMem(psPMRNode); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_WARNING,"Freelist [0x%p]: shrink denied. PB already at initial PB size (%u pages)", |
| psFreeList, |
| psFreeList->ui32InitFLPages)); |
| eError = PVRSRV_ERROR_PBSIZE_ALREADY_MIN; |
| } |
| |
| OSLockRelease(psFreeList->psDevInfo->hLockFreeList); |
| |
| return eError; |
| } |
| |
| static RGX_FREELIST *FindFreeList(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32FreelistID) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| RGX_FREELIST *psFreeList = NULL; |
| |
| OSLockAcquire(psDevInfo->hLockFreeList); |
| |
| dllist_foreach_node(&psDevInfo->sFreeListHead, psNode, psNext) |
| { |
| RGX_FREELIST *psThisFreeList = IMG_CONTAINER_OF(psNode, RGX_FREELIST, sNode); |
| |
| if (psThisFreeList->ui32FreelistID == ui32FreelistID) |
| { |
| psFreeList = psThisFreeList; |
| break; |
| } |
| } |
| |
| OSLockRelease(psDevInfo->hLockFreeList); |
| return psFreeList; |
| } |
| |
| void RGXProcessRequestGrow(PVRSRV_RGXDEV_INFO *psDevInfo, |
| IMG_UINT32 ui32FreelistID) |
| { |
| RGX_FREELIST *psFreeList = NULL; |
| RGXFWIF_KCCB_CMD s3DCCBCmd; |
| IMG_UINT32 ui32GrowValue; |
| PVRSRV_ERROR eError; |
| |
| PVR_ASSERT(psDevInfo); |
| |
| psFreeList = FindFreeList(psDevInfo, ui32FreelistID); |
| |
| if (psFreeList) |
| { |
| /* Try to grow the freelist */ |
| eError = RGXGrowFreeList(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 failed (error %u)", |
| psFreeList, |
| eError)); |
| } |
| |
| /* send feedback */ |
| s3DCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_FREELIST_GROW_UPDATE; |
| s3DCCBCmd.uCmdData.sFreeListGSData.sFreeListFWDevVAddr.ui32Addr = psFreeList->sFreeListFWDevVAddr.ui32Addr; |
| s3DCCBCmd.uCmdData.sFreeListGSData.ui32DeltaSize = ui32GrowValue; |
| s3DCCBCmd.uCmdData.sFreeListGSData.ui32NewSize = psFreeList->ui32CurrentFLPages; |
| |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError = RGXScheduleCommand(psDevInfo, |
| RGXFWIF_DM_3D, |
| &s3DCCBCmd, |
| sizeof(s3DCCBCmd), |
| 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); |
| } |
| } |
| |
| static void _RGXCheckFreeListReconstruction(PDLLIST_NODE psNode) |
| { |
| |
| PVRSRV_RGXDEV_INFO *psDevInfo; |
| RGX_FREELIST *psFreeList; |
| RGX_PMR_NODE *psPMRNode; |
| PVRSRV_ERROR eError; |
| IMG_DEVMEM_OFFSET_T uiOffset; |
| IMG_DEVMEM_SIZE_T uiLength; |
| IMG_UINT32 ui32StartPage; |
| |
| psPMRNode = IMG_CONTAINER_OF(psNode, RGX_PMR_NODE, sMemoryBlock); |
| psFreeList = psPMRNode->psFreeList; |
| PVR_ASSERT(psFreeList); |
| psDevInfo = psFreeList->psDevInfo; |
| PVR_ASSERT(psDevInfo); |
| |
| uiLength = psPMRNode->ui32NumPages * sizeof(IMG_UINT32); |
| ui32StartPage = (psFreeList->ui32MaxFLPages - psFreeList->ui32CurrentFLPages - psPMRNode->ui32NumPages); |
| uiOffset = psFreeList->uiFreeListPMROffset + (ui32StartPage * sizeof(IMG_UINT32)); |
| |
| PMRUnwritePMPageList(psPMRNode->psPageList); |
| psPMRNode->psPageList = NULL; |
| eError = PMRWritePMPageList( |
| /* Target PMR, offset, and length */ |
| psFreeList->psFreeListPMR, |
| uiOffset, |
| uiLength, |
| /* Referenced PMR, and "page" granularity */ |
| psPMRNode->psPMR, |
| RGX_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT, |
| &psPMRNode->psPageList); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"Error (%u) writing FL 0x%08x", eError, (IMG_UINT32)psFreeList->ui32FreelistID)); |
| } |
| |
| /* Zeroing physical pages pointed by the reconstructed freelist */ |
| if (psDevInfo->ui32DeviceFlags & RGXKM_DEVICE_STATE_ZERO_FREELIST) |
| { |
| eError = PMRZeroingPMR(psPMRNode->psPMR, RGX_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"_RGXCheckFreeListReconstruction: Failed to zero PMR %p of freelist %p with Error %d", |
| psPMRNode->psPMR, |
| psFreeList, |
| eError)); |
| PVR_ASSERT(0); |
| } |
| } |
| |
| psFreeList->ui32CurrentFLPages += psPMRNode->ui32NumPages; |
| } |
| |
| |
| static PVRSRV_ERROR RGXReconstructFreeList(RGX_FREELIST *psFreeList) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| RGXFWIF_FREELIST *psFWFreeList; |
| PVRSRV_ERROR eError; |
| |
| //PVR_DPF((PVR_DBG_ERROR, "FreeList RECONSTRUCTION: Reconstructing freelist %p (ID=%u)", psFreeList, psFreeList->ui32FreelistID)); |
| |
| /* Do the FreeList Reconstruction */ |
| psFreeList->ui32CurrentFLPages = 0; |
| |
| /* Reconstructing Init FreeList pages */ |
| dllist_foreach_node(&psFreeList->sMemoryBlockInitHead, psNode, psNext) |
| { |
| _RGXCheckFreeListReconstruction(psNode); |
| } |
| |
| /* Reconstructing Grow FreeList pages */ |
| dllist_foreach_node(&psFreeList->sMemoryBlockHead, psNode, psNext) |
| { |
| _RGXCheckFreeListReconstruction(psNode); |
| } |
| |
| /* Reset the firmware freelist structure */ |
| eError = DevmemAcquireCpuVirtAddr(psFreeList->psFWFreelistMemDesc, (void **)&psFWFreeList); |
| if (eError != PVRSRV_OK) |
| { |
| return eError; |
| } |
| |
| psFWFreeList->ui32CurrentStackTop = psFWFreeList->ui32CurrentPages - 1; |
| psFWFreeList->ui32AllocatedPageCount = 0; |
| psFWFreeList->ui32AllocatedMMUPageCount = 0; |
| psFWFreeList->ui32HWRCounter++; |
| |
| DevmemReleaseCpuVirtAddr(psFreeList->psFWFreelistMemDesc); |
| |
| /* Check the Freelist checksum if required (as the list is fully populated) */ |
| if (psFreeList->bCheckFreelist) |
| { |
| IMG_UINT64 ui64CheckSum; |
| |
| _CheckFreelist(psFreeList, psFreeList->ui32CurrentFLPages, psFreeList->ui64FreelistChecksum, &ui64CheckSum); |
| } |
| |
| return eError; |
| } |
| |
| |
| void RGXProcessRequestFreelistsReconstruction(PVRSRV_RGXDEV_INFO *psDevInfo, |
| IMG_UINT32 ui32FreelistsCount, |
| IMG_UINT32 *paui32Freelists) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| DLLIST_NODE *psNode, *psNext; |
| IMG_UINT32 ui32Loop; |
| RGXFWIF_KCCB_CMD sTACCBCmd; |
| |
| PVR_ASSERT(psDevInfo != NULL); |
| PVR_ASSERT(ui32FreelistsCount <= (MAX_HW_TA3DCONTEXTS * RGXFW_MAX_FREELISTS)); |
| |
| //PVR_DPF((PVR_DBG_ERROR, "FreeList RECONSTRUCTION: %u freelist(s) requested for reconstruction", ui32FreelistsCount)); |
| |
| /* |
| * Initialise the response command (in case we don't find a freelist ID)... |
| */ |
| sTACCBCmd.eCmdType = RGXFWIF_KCCB_CMD_FREELISTS_RECONSTRUCTION_UPDATE; |
| sTACCBCmd.uCmdData.sFreeListsReconstructionData.ui32FreelistsCount = ui32FreelistsCount; |
| |
| for (ui32Loop = 0; ui32Loop < ui32FreelistsCount; ui32Loop++) |
| { |
| sTACCBCmd.uCmdData.sFreeListsReconstructionData.aui32FreelistIDs[ui32Loop] = paui32Freelists[ui32Loop] | |
| RGXFWIF_FREELISTS_RECONSTRUCTION_FAILED_FLAG; |
| } |
| |
| /* |
| * The list of freelists we have been given for reconstruction will |
| * consist of local and global freelists (maybe MMU as well). Any |
| * local freelists will have their global list specified as well. |
| * However there may be other local freelists not listed, which are |
| * going to have their global freelist reconstructed. Therefore we |
| * have to find those freelists as well meaning we will have to |
| * iterate the entire list of freelists to find which must be reconstructed. |
| */ |
| OSLockAcquire(psDevInfo->hLockFreeList); |
| dllist_foreach_node(&psDevInfo->sFreeListHead, psNode, psNext) |
| { |
| RGX_FREELIST *psFreeList = IMG_CONTAINER_OF(psNode, RGX_FREELIST, sNode); |
| IMG_BOOL bReconstruct = IMG_FALSE; |
| |
| /* |
| * Check if this freelist needs to be reconstructed (was it requested |
| * or was its global freelist requested)... |
| */ |
| for (ui32Loop = 0; ui32Loop < ui32FreelistsCount; ui32Loop++) |
| { |
| if (paui32Freelists[ui32Loop] == psFreeList->ui32FreelistID || |
| paui32Freelists[ui32Loop] == psFreeList->ui32FreelistGlobalID) |
| { |
| bReconstruct = IMG_TRUE; |
| break; |
| } |
| } |
| |
| if (bReconstruct) |
| { |
| eError = RGXReconstructFreeList(psFreeList); |
| if (eError == PVRSRV_OK) |
| { |
| for (ui32Loop = 0; ui32Loop < ui32FreelistsCount; ui32Loop++) |
| { |
| if (paui32Freelists[ui32Loop] == psFreeList->ui32FreelistID) |
| { |
| /* Reconstruction of this requested freelist was successful... */ |
| sTACCBCmd.uCmdData.sFreeListsReconstructionData.aui32FreelistIDs[ui32Loop] &= ~RGXFWIF_FREELISTS_RECONSTRUCTION_FAILED_FLAG; |
| break; |
| } |
| } |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_ERROR,"Reconstructing of FreeList %p failed (error %u)", |
| psFreeList, |
| eError)); |
| } |
| } |
| } |
| OSLockRelease(psDevInfo->hLockFreeList); |
| |
| /* Check that all freelists were found and reconstructed... */ |
| for (ui32Loop = 0; ui32Loop < ui32FreelistsCount; ui32Loop++) |
| { |
| PVR_ASSERT((sTACCBCmd.uCmdData.sFreeListsReconstructionData.aui32FreelistIDs[ui32Loop] & |
| RGXFWIF_FREELISTS_RECONSTRUCTION_FAILED_FLAG) == 0); |
| } |
| |
| /* send feedback */ |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError = RGXScheduleCommand(psDevInfo, |
| RGXFWIF_DM_TA, |
| &sTACCBCmd, |
| sizeof(sTACCBCmd), |
| 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); |
| } |
| |
| /* Create HWRTDataSet */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXCreateHWRTData(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| IMG_UINT32 psRenderTarget, /* FIXME this should not be IMG_UINT32 */ |
| IMG_DEV_VIRTADDR psPMMListDevVAddr, |
| IMG_DEV_VIRTADDR psVFPPageTableAddr, |
| RGX_FREELIST *apsFreeLists[RGXFW_MAX_FREELISTS], |
| RGX_RTDATA_CLEANUP_DATA **ppsCleanupData, |
| DEVMEM_MEMDESC **ppsRTACtlMemDesc, |
| IMG_UINT32 ui32PPPScreen, |
| IMG_UINT32 ui32PPPGridOffset, |
| IMG_UINT64 ui64PPPMultiSampleCtl, |
| IMG_UINT32 ui32TPCStride, |
| IMG_DEV_VIRTADDR sTailPtrsDevVAddr, |
| IMG_UINT32 ui32TPCSize, |
| IMG_UINT32 ui32TEScreen, |
| IMG_UINT32 ui32TEAA, |
| IMG_UINT32 ui32TEMTILE1, |
| IMG_UINT32 ui32TEMTILE2, |
| IMG_UINT32 ui32MTileStride, |
| IMG_UINT32 ui32ISPMergeLowerX, |
| IMG_UINT32 ui32ISPMergeLowerY, |
| IMG_UINT32 ui32ISPMergeUpperX, |
| IMG_UINT32 ui32ISPMergeUpperY, |
| IMG_UINT32 ui32ISPMergeScaleX, |
| IMG_UINT32 ui32ISPMergeScaleY, |
| IMG_UINT16 ui16MaxRTs, |
| DEVMEM_MEMDESC **ppsMemDesc, |
| IMG_UINT32 *puiHWRTData) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo; |
| RGXFWIF_DEV_VIRTADDR pFirmwareAddr; |
| RGXFWIF_HWRTDATA *psHWRTData; |
| RGXFWIF_RTA_CTL *psRTACtl; |
| IMG_UINT32 ui32Loop; |
| RGX_RTDATA_CLEANUP_DATA *psTmpCleanup; |
| |
| PVR_UNREFERENCED_PARAMETER(psConnection); |
| |
| /* Prepare cleanup struct */ |
| psTmpCleanup = OSAllocZMem(sizeof(*psTmpCleanup)); |
| if (psTmpCleanup == NULL) |
| { |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto AllocError; |
| } |
| |
| *ppsCleanupData = psTmpCleanup; |
| |
| /* Allocate cleanup sync */ |
| eError = SyncPrimAlloc(psDeviceNode->hSyncPrimContext, |
| &psTmpCleanup->psCleanupSync, |
| "HWRTData cleanup"); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateHWRTData: Failed to allocate cleanup sync (0x%x)", |
| eError)); |
| goto SyncAlloc; |
| } |
| |
| psDevInfo = psDeviceNode->pvDevice; |
| |
| /* |
| * This FW RT-Data 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) |
| */ |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(RGXFWIF_HWRTDATA), |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) | |
| 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, |
| "FwHWRTData", |
| ppsMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateHWRTData: DevmemAllocate for RGX_FWIF_HWRTDATA failed")); |
| goto FWRTDataAllocateError; |
| } |
| |
| psTmpCleanup->psDeviceNode = psDeviceNode; |
| psTmpCleanup->psFWHWRTDataMemDesc = *ppsMemDesc; |
| |
| RGXSetFirmwareAddress(&pFirmwareAddr, *ppsMemDesc, 0, RFW_FWADDR_FLAG_NONE); |
| |
| *puiHWRTData = pFirmwareAddr.ui32Addr; |
| |
| eError = DevmemAcquireCpuVirtAddr(*ppsMemDesc, (void **)&psHWRTData); |
| PVR_LOGG_IF_ERROR(eError, "Devmem AcquireCpuVirtAddr", FWRTDataCpuMapError); |
| |
| /* FIXME: MList is something that that PM writes physical addresses to, |
| * so ideally its best allocated in kernel */ |
| psHWRTData->psPMMListDevVAddr = psPMMListDevVAddr; |
| psHWRTData->psParentRenderTarget.ui32Addr = psRenderTarget; |
| #if defined(SUPPORT_VFP) |
| psHWRTData->sVFPPageTableAddr = psVFPPageTableAddr; |
| #endif |
| |
| psHWRTData->ui32PPPScreen = ui32PPPScreen; |
| psHWRTData->ui32PPPGridOffset = ui32PPPGridOffset; |
| psHWRTData->ui64PPPMultiSampleCtl = ui64PPPMultiSampleCtl; |
| psHWRTData->ui32TPCStride = ui32TPCStride; |
| psHWRTData->sTailPtrsDevVAddr = sTailPtrsDevVAddr; |
| psHWRTData->ui32TPCSize = ui32TPCSize; |
| psHWRTData->ui32TEScreen = ui32TEScreen; |
| psHWRTData->ui32TEAA = ui32TEAA; |
| psHWRTData->ui32TEMTILE1 = ui32TEMTILE1; |
| psHWRTData->ui32TEMTILE2 = ui32TEMTILE2; |
| psHWRTData->ui32MTileStride = ui32MTileStride; |
| psHWRTData->ui32ISPMergeLowerX = ui32ISPMergeLowerX; |
| psHWRTData->ui32ISPMergeLowerY = ui32ISPMergeLowerY; |
| psHWRTData->ui32ISPMergeUpperX = ui32ISPMergeUpperX; |
| psHWRTData->ui32ISPMergeUpperY = ui32ISPMergeUpperY; |
| psHWRTData->ui32ISPMergeScaleX = ui32ISPMergeScaleX; |
| psHWRTData->ui32ISPMergeScaleY = ui32ISPMergeScaleY; |
| |
| OSLockAcquire(psDevInfo->hLockFreeList); |
| for (ui32Loop = 0; ui32Loop < RGXFW_MAX_FREELISTS; ui32Loop++) |
| { |
| psTmpCleanup->apsFreeLists[ui32Loop] = apsFreeLists[ui32Loop]; |
| psTmpCleanup->apsFreeLists[ui32Loop]->ui32RefCount++; |
| psHWRTData->apsFreeLists[ui32Loop].ui32Addr = psTmpCleanup->apsFreeLists[ui32Loop]->sFreeListFWDevVAddr.ui32Addr; |
| /* invalid initial snapshot value, the snapshot is always taken during first kick |
| * and hence the value get replaced during the first kick anyway. So it's safe to set it 0. |
| */ |
| psHWRTData->aui32FreeListHWRSnapshot[ui32Loop] = 0; |
| } |
| OSLockRelease(psDevInfo->hLockFreeList); |
| |
| PDUMPCOMMENT("Allocate RGXFW RTA control"); |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(RGXFWIF_RTA_CTL), |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) | |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_UNCACHED | |
| PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC, |
| "FwRTAControl", |
| ppsRTACtlMemDesc); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateHWRTData: Failed to allocate RGX RTA control (%u)", |
| eError)); |
| goto FWRTAAllocateError; |
| } |
| psTmpCleanup->psRTACtlMemDesc = *ppsRTACtlMemDesc; |
| RGXSetFirmwareAddress(&psHWRTData->psRTACtl, |
| *ppsRTACtlMemDesc, |
| 0, RFW_FWADDR_FLAG_NONE); |
| |
| eError = DevmemAcquireCpuVirtAddr(*ppsRTACtlMemDesc, (void **)&psRTACtl); |
| PVR_LOGG_IF_ERROR(eError, "Devmem AcquireCpuVirtAddr", FWRTACpuMapError); |
| psRTACtl->ui32RenderTargetIndex = 0; |
| psRTACtl->ui32ActiveRenderTargets = 0; |
| |
| if (ui16MaxRTs > 1) |
| { |
| /* Allocate memory for the checks */ |
| PDUMPCOMMENT("Allocate memory for shadow render target cache"); |
| eError = DevmemFwAllocate(psDevInfo, |
| ui16MaxRTs * sizeof(IMG_UINT32), |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) | |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_UNCACHED| |
| PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC, |
| "FwShadowRTCache", |
| &psTmpCleanup->psRTArrayMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateHWRTData: Failed to allocate %d bytes for render target array (%u)", |
| ui16MaxRTs, eError)); |
| goto FWAllocateRTArryError; |
| } |
| |
| RGXSetFirmwareAddress(&psRTACtl->sValidRenderTargets, |
| psTmpCleanup->psRTArrayMemDesc, |
| 0, RFW_FWADDR_FLAG_NONE); |
| |
| /* Allocate memory for the checks */ |
| PDUMPCOMMENT("Allocate memory for tracking renders accumulation"); |
| eError = DevmemFwAllocate(psDevInfo, |
| ui16MaxRTs * sizeof(IMG_UINT32), |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) | |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_UNCACHED| |
| PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC, |
| "FwRendersAccumulation", |
| &psTmpCleanup->psRendersAccArrayMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateHWRTData: Failed to allocate %d bytes for render target array (%u) (renders accumulation)", |
| ui16MaxRTs, eError)); |
| goto FWAllocateRTAccArryError; |
| } |
| |
| RGXSetFirmwareAddress(&psRTACtl->sNumRenders, |
| psTmpCleanup->psRendersAccArrayMemDesc, |
| 0, RFW_FWADDR_FLAG_NONE); |
| psRTACtl->ui16MaxRTs = ui16MaxRTs; |
| } |
| else |
| { |
| psRTACtl->sValidRenderTargets.ui32Addr = 0; |
| psRTACtl->sNumRenders.ui32Addr = 0; |
| psRTACtl->ui16MaxRTs = 1; |
| } |
| |
| PDUMPCOMMENT("Dump HWRTData 0x%08X", *puiHWRTData); |
| DevmemPDumpLoadMem(*ppsMemDesc, 0, sizeof(*psHWRTData), PDUMP_FLAGS_CONTINUOUS); |
| PDUMPCOMMENT("Dump RTACtl"); |
| DevmemPDumpLoadMem(*ppsRTACtlMemDesc, 0, sizeof(*psRTACtl), PDUMP_FLAGS_CONTINUOUS); |
| |
| DevmemReleaseCpuVirtAddr(*ppsMemDesc); |
| DevmemReleaseCpuVirtAddr(*ppsRTACtlMemDesc); |
| return PVRSRV_OK; |
| |
| FWAllocateRTAccArryError: |
| DevmemFwFree(psDevInfo, psTmpCleanup->psRTArrayMemDesc); |
| FWAllocateRTArryError: |
| DevmemReleaseCpuVirtAddr(*ppsRTACtlMemDesc); |
| FWRTACpuMapError: |
| RGXUnsetFirmwareAddress(*ppsRTACtlMemDesc); |
| DevmemFwFree(psDevInfo, *ppsRTACtlMemDesc); |
| FWRTAAllocateError: |
| OSLockAcquire(psDevInfo->hLockFreeList); |
| for (ui32Loop = 0; ui32Loop < RGXFW_MAX_FREELISTS; ui32Loop++) |
| { |
| PVR_ASSERT(psTmpCleanup->apsFreeLists[ui32Loop]->ui32RefCount > 0); |
| psTmpCleanup->apsFreeLists[ui32Loop]->ui32RefCount--; |
| } |
| OSLockRelease(psDevInfo->hLockFreeList); |
| DevmemReleaseCpuVirtAddr(*ppsMemDesc); |
| FWRTDataCpuMapError: |
| RGXUnsetFirmwareAddress(*ppsMemDesc); |
| DevmemFwFree(psDevInfo, *ppsMemDesc); |
| FWRTDataAllocateError: |
| SyncPrimFree(psTmpCleanup->psCleanupSync); |
| SyncAlloc: |
| *ppsCleanupData = NULL; |
| OSFreeMem(psTmpCleanup); |
| |
| AllocError: |
| return eError; |
| } |
| |
| /* Destroy HWRTDataSet */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXDestroyHWRTData(RGX_RTDATA_CLEANUP_DATA *psCleanupData) |
| { |
| PVRSRV_RGXDEV_INFO *psDevInfo; |
| PVRSRV_ERROR eError; |
| PRGXFWIF_HWRTDATA psHWRTData; |
| IMG_UINT32 ui32Loop; |
| |
| PVR_ASSERT(psCleanupData); |
| |
| RGXSetFirmwareAddress(&psHWRTData, psCleanupData->psFWHWRTDataMemDesc, 0, RFW_FWADDR_NOREF_FLAG); |
| |
| /* Cleanup HWRTData in TA */ |
| eError = RGXFWRequestHWRTDataCleanUp(psCleanupData->psDeviceNode, |
| psHWRTData, |
| psCleanupData->psCleanupSync, |
| RGXFWIF_DM_TA); |
| if (eError == PVRSRV_ERROR_RETRY) |
| { |
| return eError; |
| } |
| |
| psDevInfo = psCleanupData->psDeviceNode->pvDevice; |
| |
| /* Cleanup HWRTData in 3D */ |
| eError = RGXFWRequestHWRTDataCleanUp(psCleanupData->psDeviceNode, |
| psHWRTData, |
| psCleanupData->psCleanupSync, |
| RGXFWIF_DM_3D); |
| if (eError == PVRSRV_ERROR_RETRY) |
| { |
| return eError; |
| } |
| |
| /* If we got here then TA and 3D operations on this RTData have finished */ |
| if(psCleanupData->psRTACtlMemDesc) |
| { |
| RGXUnsetFirmwareAddress(psCleanupData->psRTACtlMemDesc); |
| DevmemFwFree(psDevInfo, psCleanupData->psRTACtlMemDesc); |
| } |
| |
| RGXUnsetFirmwareAddress(psCleanupData->psFWHWRTDataMemDesc); |
| DevmemFwFree(psDevInfo, psCleanupData->psFWHWRTDataMemDesc); |
| |
| if(psCleanupData->psRTArrayMemDesc) |
| { |
| RGXUnsetFirmwareAddress(psCleanupData->psRTArrayMemDesc); |
| DevmemFwFree(psDevInfo, psCleanupData->psRTArrayMemDesc); |
| } |
| |
| if(psCleanupData->psRendersAccArrayMemDesc) |
| { |
| RGXUnsetFirmwareAddress(psCleanupData->psRendersAccArrayMemDesc); |
| DevmemFwFree(psDevInfo, psCleanupData->psRendersAccArrayMemDesc); |
| } |
| |
| SyncPrimFree(psCleanupData->psCleanupSync); |
| |
| /* decrease freelist refcount */ |
| OSLockAcquire(psDevInfo->hLockFreeList); |
| for (ui32Loop = 0; ui32Loop < RGXFW_MAX_FREELISTS; ui32Loop++) |
| { |
| PVR_ASSERT(psCleanupData->apsFreeLists[ui32Loop]->ui32RefCount > 0); |
| psCleanupData->apsFreeLists[ui32Loop]->ui32RefCount--; |
| } |
| OSLockRelease(psDevInfo->hLockFreeList); |
| |
| OSFreeMem(psCleanupData); |
| |
| return PVRSRV_OK; |
| } |
| |
| IMG_EXPORT |
| PVRSRV_ERROR RGXCreateFreeList(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| IMG_UINT32 ui32MaxFLPages, |
| IMG_UINT32 ui32InitFLPages, |
| IMG_UINT32 ui32GrowFLPages, |
| RGX_FREELIST *psGlobalFreeList, |
| IMG_BOOL bCheckFreelist, |
| IMG_DEV_VIRTADDR sFreeListDevVAddr, |
| PMR *psFreeListPMR, |
| IMG_DEVMEM_OFFSET_T uiFreeListPMROffset, |
| RGX_FREELIST **ppsFreeList) |
| { |
| PVRSRV_ERROR eError; |
| RGXFWIF_FREELIST *psFWFreeList; |
| DEVMEM_MEMDESC *psFWFreelistMemDesc; |
| RGX_FREELIST *psFreeList; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| |
| /* Allocate kernel freelist struct */ |
| psFreeList = OSAllocZMem(sizeof(*psFreeList)); |
| if (psFreeList == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateFreeList: failed to allocate host data structure")); |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto ErrorAllocHost; |
| } |
| |
| /* Allocate cleanup sync */ |
| eError = SyncPrimAlloc(psDeviceNode->hSyncPrimContext, |
| &psFreeList->psCleanupSync, |
| "ta3d free list cleanup"); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateFreeList: Failed to allocate cleanup sync (0x%x)", |
| eError)); |
| goto SyncAlloc; |
| } |
| |
| /* |
| * This FW FreeList context is only mapped into kernel for initialisation |
| * and reconstruction (at other times it is not mapped and 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) |
| */ |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(*psFWFreeList), |
| 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, |
| "FwFreeList", |
| &psFWFreelistMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateFreeList: DevmemAllocate for RGXFWIF_FREELIST failed")); |
| goto FWFreeListAlloc; |
| } |
| |
| /* Initialise host data structures */ |
| psFreeList->psDevInfo = psDevInfo; |
| psFreeList->psFreeListPMR = psFreeListPMR; |
| psFreeList->uiFreeListPMROffset = uiFreeListPMROffset; |
| psFreeList->psFWFreelistMemDesc = psFWFreelistMemDesc; |
| RGXSetFirmwareAddress(&psFreeList->sFreeListFWDevVAddr, psFWFreelistMemDesc, 0, RFW_FWADDR_FLAG_NONE); |
| psFreeList->ui32FreelistID = psDevInfo->ui32FreelistCurrID++; |
| psFreeList->ui32FreelistGlobalID = (psGlobalFreeList ? psGlobalFreeList->ui32FreelistID : 0); |
| psFreeList->ui32MaxFLPages = ui32MaxFLPages; |
| psFreeList->ui32InitFLPages = ui32InitFLPages; |
| psFreeList->ui32GrowFLPages = ui32GrowFLPages; |
| psFreeList->ui32CurrentFLPages = 0; |
| psFreeList->ui64FreelistChecksum = 0; |
| psFreeList->ui32RefCount = 0; |
| psFreeList->bCheckFreelist = bCheckFreelist; |
| dllist_init(&psFreeList->sMemoryBlockHead); |
| dllist_init(&psFreeList->sMemoryBlockInitHead); |
| |
| |
| /* Add to list of freelists */ |
| OSLockAcquire(psDevInfo->hLockFreeList); |
| dllist_add_to_tail(&psDevInfo->sFreeListHead, &psFreeList->sNode); |
| OSLockRelease(psDevInfo->hLockFreeList); |
| |
| |
| /* Initialise FW data structure */ |
| eError = DevmemAcquireCpuVirtAddr(psFreeList->psFWFreelistMemDesc, (void **)&psFWFreeList); |
| PVR_LOGG_IF_ERROR(eError, "Devmem AcquireCpuVirtAddr", FWFreeListCpuMap); |
| |
| psFWFreeList->ui32MaxPages = ui32MaxFLPages; |
| psFWFreeList->ui32CurrentPages = ui32InitFLPages; |
| psFWFreeList->ui32GrowPages = ui32GrowFLPages; |
| psFWFreeList->ui32CurrentStackTop = ui32InitFLPages - 1; |
| psFWFreeList->psFreeListDevVAddr = sFreeListDevVAddr; |
| psFWFreeList->ui64CurrentDevVAddr = sFreeListDevVAddr.uiAddr + ((ui32MaxFLPages - ui32InitFLPages) * sizeof(IMG_UINT32)); |
| psFWFreeList->ui32FreeListID = psFreeList->ui32FreelistID; |
| psFWFreeList->bGrowPending = IMG_FALSE; |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"Freelist %p created: Max pages 0x%08x, Init pages 0x%08x, Max FL base address 0x%016llx, Init FL base address 0x%016llx", |
| psFreeList, |
| ui32MaxFLPages, |
| ui32InitFLPages, |
| sFreeListDevVAddr.uiAddr, |
| psFWFreeList->psFreeListDevVAddr.uiAddr)); |
| |
| PDUMPCOMMENT("Dump FW FreeList"); |
| DevmemPDumpLoadMem(psFreeList->psFWFreelistMemDesc, 0, sizeof(*psFWFreeList), 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("FreeList TotalPages"); |
| DevmemPDumpLoadMemValue32(psFreeList->psFWFreelistMemDesc, |
| offsetof(RGXFWIF_FREELIST, ui32CurrentPages), |
| psFWFreeList->ui32CurrentPages, |
| PDUMP_FLAGS_CONTINUOUS); |
| PDUMPCOMMENT("FreeList StackPointer"); |
| DevmemPDumpLoadMemValue32(psFreeList->psFWFreelistMemDesc, |
| offsetof(RGXFWIF_FREELIST, ui32CurrentStackTop), |
| psFWFreeList->ui32CurrentStackTop, |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| DevmemReleaseCpuVirtAddr(psFreeList->psFWFreelistMemDesc); |
| |
| |
| /* Add initial PB block */ |
| eError = RGXGrowFreeList(psFreeList, |
| ui32InitFLPages, |
| &psFreeList->sMemoryBlockInitHead); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXCreateFreeList: failed to allocate initial memory block for free list 0x%016llx (error = %u)", |
| sFreeListDevVAddr.uiAddr, |
| eError)); |
| goto FWFreeListCpuMap; |
| } |
| #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 |
| |
| psFreeList->ownerPid = OSGetCurrentClientProcessIDKM(); |
| /* return values */ |
| *ppsFreeList = psFreeList; |
| |
| return PVRSRV_OK; |
| |
| /* Error handling */ |
| |
| FWFreeListCpuMap: |
| /* Remove freelists from list */ |
| OSLockAcquire(psDevInfo->hLockFreeList); |
| dllist_remove_node(&psFreeList->sNode); |
| OSLockRelease(psDevInfo->hLockFreeList); |
| |
| RGXUnsetFirmwareAddress(psFWFreelistMemDesc); |
| DevmemFwFree(psDevInfo, psFWFreelistMemDesc); |
| |
| FWFreeListAlloc: |
| SyncPrimFree(psFreeList->psCleanupSync); |
| |
| SyncAlloc: |
| OSFreeMem(psFreeList); |
| |
| ErrorAllocHost: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| |
| /* |
| RGXDestroyFreeList |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXDestroyFreeList(RGX_FREELIST *psFreeList) |
| { |
| PVRSRV_ERROR eError; |
| |
| PVR_ASSERT(psFreeList); |
| |
| if (psFreeList->ui32RefCount != 0) |
| { |
| /* Freelist still busy */ |
| return PVRSRV_ERROR_RETRY; |
| } |
| |
| /* Freelist is not in use => start firmware cleanup */ |
| eError = RGXFWRequestFreeListCleanUp(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; |
| } |
| |
| /* Remove FreeList from linked list before we destroy it... */ |
| OSLockAcquire(psFreeList->psDevInfo->hLockFreeList); |
| dllist_remove_node(&psFreeList->sNode); |
| OSLockRelease(psFreeList->psDevInfo->hLockFreeList); |
| |
| if (psFreeList->bCheckFreelist) |
| { |
| RGXFWIF_FREELIST *psFWFreeList; |
| IMG_UINT64 ui32CurrentStackTop; |
| IMG_UINT64 ui64CheckSum; |
| |
| /* Get the current stack pointer for this free list */ |
| DevmemAcquireCpuVirtAddr(psFreeList->psFWFreelistMemDesc, (void **)&psFWFreeList); |
| ui32CurrentStackTop = psFWFreeList->ui32CurrentStackTop; |
| DevmemReleaseCpuVirtAddr(psFreeList->psFWFreelistMemDesc); |
| |
| if (ui32CurrentStackTop == psFreeList->ui32CurrentFLPages-1) |
| { |
| /* Do consistency tests (as the list is fully populated) */ |
| _CheckFreelist(psFreeList, psFreeList->ui32CurrentFLPages, psFreeList->ui64FreelistChecksum, &ui64CheckSum); |
| } |
| else |
| { |
| /* Check for duplicate pages, but don't check the checksum as the list is not fully populated */ |
| _CheckFreelist(psFreeList, ui32CurrentStackTop+1, 0, &ui64CheckSum); |
| } |
| } |
| |
| /* Destroy FW structures */ |
| RGXUnsetFirmwareAddress(psFreeList->psFWFreelistMemDesc); |
| DevmemFwFree(psFreeList->psDevInfo, psFreeList->psFWFreelistMemDesc); |
| |
| /* Remove grow shrink blocks */ |
| while (!dllist_is_empty(&psFreeList->sMemoryBlockHead)) |
| { |
| eError = RGXShrinkFreeList(&psFreeList->sMemoryBlockHead, psFreeList); |
| PVR_ASSERT(eError == PVRSRV_OK); |
| } |
| |
| /* Remove initial PB block */ |
| eError = RGXShrinkFreeList(&psFreeList->sMemoryBlockInitHead, psFreeList); |
| PVR_ASSERT(eError == PVRSRV_OK); |
| |
| /* consistency checks */ |
| PVR_ASSERT(dllist_is_empty(&psFreeList->sMemoryBlockInitHead)); |
| PVR_ASSERT(psFreeList->ui32CurrentFLPages == 0); |
| |
| SyncPrimFree(psFreeList->psCleanupSync); |
| |
| /* free Freelist */ |
| OSFreeMem(psFreeList); |
| |
| return eError; |
| } |
| |
| |
| |
| /* |
| RGXAddBlockToFreeListKM |
| */ |
| |
| IMG_EXPORT |
| PVRSRV_ERROR RGXAddBlockToFreeListKM(RGX_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, "Freelist is not configured for grow")); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* grow freelist */ |
| eError = RGXGrowFreeList(psFreeList, |
| ui32NumPages, |
| &psFreeList->sMemoryBlockHead); |
| if(eError == PVRSRV_OK) |
| { |
| /* update freelist data in firmware */ |
| _UpdateFwFreelistSize(psFreeList, 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; |
| } |
| |
| /* |
| RGXRemoveBlockFromFreeListKM |
| */ |
| |
| IMG_EXPORT |
| PVRSRV_ERROR RGXRemoveBlockFromFreeListKM(RGX_FREELIST *psFreeList) |
| { |
| PVRSRV_ERROR eError; |
| |
| /* |
| * Make sure the pages part of the memory block are not in use anymore. |
| * Instruct the firmware to update the freelist pointers accordingly. |
| */ |
| |
| eError = RGXShrinkFreeList(&psFreeList->sMemoryBlockHead, |
| psFreeList); |
| |
| return eError; |
| } |
| |
| |
| /* |
| RGXCreateRenderTarget |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXCreateRenderTarget(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| IMG_DEV_VIRTADDR psVHeapTableDevVAddr, |
| RGX_RT_CLEANUP_DATA **ppsCleanupData, |
| IMG_UINT32 *sRenderTargetFWDevVAddr) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGXFWIF_RENDER_TARGET *psRenderTarget; |
| RGXFWIF_DEV_VIRTADDR pFirmwareAddr; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGX_RT_CLEANUP_DATA *psCleanupData; |
| |
| PVR_UNREFERENCED_PARAMETER(psConnection); |
| |
| psCleanupData = OSAllocZMem(sizeof(*psCleanupData)); |
| if (psCleanupData == NULL) |
| { |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto err_out; |
| } |
| |
| psCleanupData->psDeviceNode = psDeviceNode; |
| /* |
| * This FW render target 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) |
| */ |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(*psRenderTarget), |
| 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, |
| "FwRenderTarget", |
| &psCleanupData->psRenderTargetMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXCreateRenderTarget: DevmemAllocate for Render Target failed")); |
| goto err_free; |
| } |
| RGXSetFirmwareAddress(&pFirmwareAddr, psCleanupData->psRenderTargetMemDesc, 0, RFW_FWADDR_FLAG_NONE); |
| *sRenderTargetFWDevVAddr = pFirmwareAddr.ui32Addr; |
| |
| eError = DevmemAcquireCpuVirtAddr(psCleanupData->psRenderTargetMemDesc, (void **)&psRenderTarget); |
| PVR_LOGG_IF_ERROR(eError, "Devmem AcquireCpuVirtAddr", err_fwalloc); |
| |
| psRenderTarget->psVHeapTableDevVAddr = psVHeapTableDevVAddr; |
| psRenderTarget->bTACachesNeedZeroing = IMG_FALSE; |
| PDUMPCOMMENT("Dump RenderTarget"); |
| DevmemPDumpLoadMem(psCleanupData->psRenderTargetMemDesc, 0, sizeof(*psRenderTarget), PDUMP_FLAGS_CONTINUOUS); |
| DevmemReleaseCpuVirtAddr(psCleanupData->psRenderTargetMemDesc); |
| |
| *ppsCleanupData = psCleanupData; |
| |
| err_out: |
| return eError; |
| |
| err_free: |
| OSFreeMem(psCleanupData); |
| goto err_out; |
| |
| err_fwalloc: |
| DevmemFwFree(psDevInfo, psCleanupData->psRenderTargetMemDesc); |
| goto err_free; |
| |
| } |
| |
| |
| /* |
| RGXDestroyRenderTarget |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXDestroyRenderTarget(RGX_RT_CLEANUP_DATA *psCleanupData) |
| { |
| PVRSRV_DEVICE_NODE *psDeviceNode = psCleanupData->psDeviceNode; |
| |
| RGXUnsetFirmwareAddress(psCleanupData->psRenderTargetMemDesc); |
| |
| /* |
| Note: |
| When we get RT cleanup in the FW call that instead |
| */ |
| /* Flush the the SLC before freeing */ |
| { |
| RGXFWIF_KCCB_CMD sFlushInvalCmd; |
| PVRSRV_ERROR eError; |
| |
| /* Schedule the SLC flush command ... */ |
| #if defined(PDUMP) |
| PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "Submit SLC flush and invalidate"); |
| #endif |
| sFlushInvalCmd.eCmdType = RGXFWIF_KCCB_CMD_SLCFLUSHINVAL; |
| sFlushInvalCmd.uCmdData.sSLCFlushInvalData.bInval = IMG_TRUE; |
| sFlushInvalCmd.uCmdData.sSLCFlushInvalData.bDMContext = IMG_FALSE; |
| sFlushInvalCmd.uCmdData.sSLCFlushInvalData.eDM = 0; |
| sFlushInvalCmd.uCmdData.sSLCFlushInvalData.psContext.ui32Addr = 0; |
| |
| eError = RGXSendCommandWithPowLock(psDeviceNode->pvDevice, |
| RGXFWIF_DM_GP, |
| &sFlushInvalCmd, |
| sizeof(sFlushInvalCmd), |
| PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXDestroyRenderTarget: Failed to schedule SLC flush command with error (%u)", eError)); |
| } |
| else |
| { |
| /* Wait for the SLC flush to complete */ |
| eError = RGXWaitForFWOp(psDeviceNode->pvDevice, RGXFWIF_DM_GP, psDeviceNode->psSyncPrim, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXDestroyRenderTarget: SLC flush and invalidate aborted with error (%u)", eError)); |
| } |
| } |
| } |
| |
| DevmemFwFree(psDeviceNode->pvDevice, psCleanupData->psRenderTargetMemDesc); |
| OSFreeMem(psCleanupData); |
| return PVRSRV_OK; |
| } |
| |
| /* |
| RGXCreateZSBuffer |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXCreateZSBufferKM(CONNECTION_DATA * psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| DEVMEMINT_RESERVATION *psReservation, |
| PMR *psPMR, |
| PVRSRV_MEMALLOCFLAGS_T uiMapFlags, |
| RGX_ZSBUFFER_DATA **ppsZSBuffer, |
| IMG_UINT32 *pui32ZSBufferFWDevVAddr) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_FWZSBUFFER *psFWZSBuffer; |
| RGX_ZSBUFFER_DATA *psZSBuffer; |
| DEVMEM_MEMDESC *psFWZSBufferMemDesc; |
| IMG_BOOL bOnDemand = PVRSRV_CHECK_ON_DEMAND(uiMapFlags) ? IMG_TRUE : IMG_FALSE; |
| |
| /* Allocate host data structure */ |
| psZSBuffer = OSAllocZMem(sizeof(*psZSBuffer)); |
| if (psZSBuffer == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateZSBufferKM: Failed to allocate cleanup data structure for ZS-Buffer")); |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto ErrorAllocCleanup; |
| } |
| |
| eError = SyncPrimAlloc(psDeviceNode->hSyncPrimContext, |
| &psZSBuffer->psCleanupSync, |
| "ta3d zs buffer cleanup"); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateZSBufferKM: Failed to allocate cleanup sync (0x%x)", |
| eError)); |
| goto ErrorSyncAlloc; |
| } |
| |
| /* Populate Host data */ |
| psZSBuffer->psDevInfo = psDevInfo; |
| psZSBuffer->psReservation = psReservation; |
| psZSBuffer->psPMR = psPMR; |
| psZSBuffer->uiMapFlags = uiMapFlags; |
| psZSBuffer->ui32RefCount = 0; |
| psZSBuffer->bOnDemand = bOnDemand; |
| if (bOnDemand) |
| { |
| psZSBuffer->ui32ZSBufferID = psDevInfo->ui32ZSBufferCurrID++; |
| psZSBuffer->psMapping = NULL; |
| |
| OSLockAcquire(psDevInfo->hLockZSBuffer); |
| dllist_add_to_tail(&psDevInfo->sZSBufferHead, &psZSBuffer->sNode); |
| OSLockRelease(psDevInfo->hLockZSBuffer); |
| } |
| |
| /* Allocate firmware memory for ZS-Buffer. */ |
| PDUMPCOMMENT("Allocate firmware ZS-Buffer data structure"); |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(*psFWZSBuffer), |
| 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, |
| "FwZSBuffer", |
| &psFWZSBufferMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateZSBufferKM: Failed to allocate firmware ZS-Buffer (%u)", eError)); |
| goto ErrorAllocFWZSBuffer; |
| } |
| psZSBuffer->psZSBufferMemDesc = psFWZSBufferMemDesc; |
| |
| /* Temporarily map the firmware render context to the kernel. */ |
| eError = DevmemAcquireCpuVirtAddr(psFWZSBufferMemDesc, |
| (void **)&psFWZSBuffer); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCreateZSBufferKM: Failed to map firmware ZS-Buffer (%u)", eError)); |
| goto ErrorAcquireFWZSBuffer; |
| } |
| |
| /* Populate FW ZS-Buffer data structure */ |
| psFWZSBuffer->bOnDemand = bOnDemand; |
| psFWZSBuffer->eState = (bOnDemand) ? RGXFWIF_ZSBUFFER_UNBACKED : RGXFWIF_ZSBUFFER_BACKED; |
| psFWZSBuffer->ui32ZSBufferID = psZSBuffer->ui32ZSBufferID; |
| |
| /* Get firmware address of ZS-Buffer. */ |
| RGXSetFirmwareAddress(&psZSBuffer->sZSBufferFWDevVAddr, psFWZSBufferMemDesc, 0, RFW_FWADDR_FLAG_NONE); |
| |
| /* Dump the ZS-Buffer and the memory content */ |
| PDUMPCOMMENT("Dump firmware ZS-Buffer"); |
| DevmemPDumpLoadMem(psFWZSBufferMemDesc, 0, sizeof(*psFWZSBuffer), PDUMP_FLAGS_CONTINUOUS); |
| |
| /* Release address acquired above. */ |
| DevmemReleaseCpuVirtAddr(psFWZSBufferMemDesc); |
| |
| |
| /* define return value */ |
| *ppsZSBuffer = psZSBuffer; |
| *pui32ZSBufferFWDevVAddr = psZSBuffer->sZSBufferFWDevVAddr.ui32Addr; |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "ZS-Buffer [%p] created (%s)", |
| psZSBuffer, |
| (bOnDemand) ? "On-Demand": "Up-front")); |
| |
| psZSBuffer->owner=OSGetCurrentClientProcessIDKM(); |
| |
| return PVRSRV_OK; |
| |
| /* error handling */ |
| |
| ErrorAcquireFWZSBuffer: |
| DevmemFwFree(psDevInfo, psFWZSBufferMemDesc); |
| |
| ErrorAllocFWZSBuffer: |
| SyncPrimFree(psZSBuffer->psCleanupSync); |
| |
| ErrorSyncAlloc: |
| OSFreeMem(psZSBuffer); |
| |
| ErrorAllocCleanup: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| |
| /* |
| RGXDestroyZSBuffer |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR RGXDestroyZSBufferKM(RGX_ZSBUFFER_DATA *psZSBuffer) |
| { |
| POS_LOCK hLockZSBuffer; |
| PVRSRV_ERROR eError; |
| |
| PVR_ASSERT(psZSBuffer); |
| hLockZSBuffer = psZSBuffer->psDevInfo->hLockZSBuffer; |
| |
| /* Request ZS Buffer cleanup */ |
| eError = RGXFWRequestZSBufferCleanUp(psZSBuffer->psDevInfo, |
| psZSBuffer->sZSBufferFWDevVAddr, |
| psZSBuffer->psCleanupSync); |
| if (eError != PVRSRV_ERROR_RETRY) |
| { |
| /* Free the firmware render context. */ |
| RGXUnsetFirmwareAddress(psZSBuffer->psZSBufferMemDesc); |
| DevmemFwFree(psZSBuffer->psDevInfo, psZSBuffer->psZSBufferMemDesc); |
| |
| /* Remove Deferred Allocation from list */ |
| if (psZSBuffer->bOnDemand) |
| { |
| OSLockAcquire(hLockZSBuffer); |
| PVR_ASSERT(dllist_node_is_in_list(&psZSBuffer->sNode)); |
| dllist_remove_node(&psZSBuffer->sNode); |
| OSLockRelease(hLockZSBuffer); |
| } |
| |
| SyncPrimFree(psZSBuffer->psCleanupSync); |
| |
| PVR_ASSERT(psZSBuffer->ui32RefCount == 0); |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"ZS-Buffer [%p] destroyed",psZSBuffer)); |
| |
| /* Free ZS-Buffer host data structure */ |
| OSFreeMem(psZSBuffer); |
| |
| } |
| |
| return eError; |
| } |
| |
| PVRSRV_ERROR |
| RGXBackingZSBuffer(RGX_ZSBUFFER_DATA *psZSBuffer) |
| { |
| POS_LOCK hLockZSBuffer; |
| PVRSRV_ERROR eError; |
| |
| if (!psZSBuffer) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (!psZSBuffer->bOnDemand) |
| { |
| /* Only deferred allocations can be populated */ |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"ZS Buffer [%p, ID=0x%08x]: Physical backing requested", |
| psZSBuffer, |
| psZSBuffer->ui32ZSBufferID)); |
| hLockZSBuffer = psZSBuffer->psDevInfo->hLockZSBuffer; |
| |
| OSLockAcquire(hLockZSBuffer); |
| |
| if (psZSBuffer->ui32RefCount == 0) |
| { |
| if (psZSBuffer->bOnDemand) |
| { |
| IMG_HANDLE hDevmemHeap; |
| |
| PVR_ASSERT(psZSBuffer->psMapping == NULL); |
| |
| /* Get Heap */ |
| eError = DevmemServerGetHeapHandle(psZSBuffer->psReservation, &hDevmemHeap); |
| PVR_ASSERT(psZSBuffer->psMapping == NULL); |
| |
| eError = DevmemIntMapPMR(hDevmemHeap, |
| psZSBuffer->psReservation, |
| psZSBuffer->psPMR, |
| psZSBuffer->uiMapFlags, |
| &psZSBuffer->psMapping); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"Unable populate ZS Buffer [%p, ID=0x%08x] with error %u", |
| psZSBuffer, |
| psZSBuffer->ui32ZSBufferID, |
| eError)); |
| OSLockRelease(hLockZSBuffer); |
| return eError; |
| |
| } |
| PVR_DPF((PVR_DBG_MESSAGE, "ZS Buffer [%p, ID=0x%08x]: Physical backing acquired", |
| psZSBuffer, |
| psZSBuffer->ui32ZSBufferID)); |
| } |
| } |
| |
| /* Increase refcount*/ |
| psZSBuffer->ui32RefCount++; |
| |
| OSLockRelease(hLockZSBuffer); |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| PVRSRV_ERROR |
| RGXPopulateZSBufferKM(RGX_ZSBUFFER_DATA *psZSBuffer, |
| RGX_POPULATION **ppsPopulation) |
| { |
| RGX_POPULATION *psPopulation; |
| PVRSRV_ERROR eError; |
| |
| psZSBuffer->ui32NumReqByApp++; |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| PVRSRVStatsUpdateZSBufferStats(1,0,psZSBuffer->owner); |
| #endif |
| |
| /* Do the backing */ |
| eError = RGXBackingZSBuffer(psZSBuffer); |
| if (eError != PVRSRV_OK) |
| { |
| goto OnErrorBacking; |
| } |
| |
| /* Create the handle to the backing */ |
| psPopulation = OSAllocMem(sizeof(*psPopulation)); |
| if (psPopulation == NULL) |
| { |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto OnErrorAlloc; |
| } |
| |
| psPopulation->psZSBuffer = psZSBuffer; |
| |
| /* return value */ |
| *ppsPopulation = psPopulation; |
| |
| return PVRSRV_OK; |
| |
| OnErrorAlloc: |
| RGXUnbackingZSBuffer(psZSBuffer); |
| |
| OnErrorBacking: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| PVRSRV_ERROR |
| RGXUnbackingZSBuffer(RGX_ZSBUFFER_DATA *psZSBuffer) |
| { |
| POS_LOCK hLockZSBuffer; |
| PVRSRV_ERROR eError; |
| |
| if (!psZSBuffer) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| PVR_ASSERT(psZSBuffer->ui32RefCount); |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"ZS Buffer [%p, ID=0x%08x]: Physical backing removal requested", |
| psZSBuffer, |
| psZSBuffer->ui32ZSBufferID)); |
| |
| hLockZSBuffer = psZSBuffer->psDevInfo->hLockZSBuffer; |
| |
| OSLockAcquire(hLockZSBuffer); |
| |
| if (psZSBuffer->bOnDemand) |
| { |
| if (psZSBuffer->ui32RefCount == 1) |
| { |
| PVR_ASSERT(psZSBuffer->psMapping); |
| |
| eError = DevmemIntUnmapPMR(psZSBuffer->psMapping); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"Unable to unpopulate ZS Buffer [%p, ID=0x%08x] with error %u", |
| psZSBuffer, |
| psZSBuffer->ui32ZSBufferID, |
| eError)); |
| OSLockRelease(hLockZSBuffer); |
| return eError; |
| } |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "ZS Buffer [%p, ID=0x%08x]: Physical backing removed", |
| psZSBuffer, |
| psZSBuffer->ui32ZSBufferID)); |
| } |
| } |
| |
| /* Decrease refcount*/ |
| psZSBuffer->ui32RefCount--; |
| |
| OSLockRelease(hLockZSBuffer); |
| |
| return PVRSRV_OK; |
| } |
| |
| PVRSRV_ERROR |
| RGXUnpopulateZSBufferKM(RGX_POPULATION *psPopulation) |
| { |
| PVRSRV_ERROR eError; |
| |
| if (!psPopulation) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| eError = RGXUnbackingZSBuffer(psPopulation->psZSBuffer); |
| if (eError != PVRSRV_OK) |
| { |
| return eError; |
| } |
| |
| OSFreeMem(psPopulation); |
| |
| return PVRSRV_OK; |
| } |
| |
| static RGX_ZSBUFFER_DATA *FindZSBuffer(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32ZSBufferID) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| RGX_ZSBUFFER_DATA *psZSBuffer = NULL; |
| |
| OSLockAcquire(psDevInfo->hLockZSBuffer); |
| |
| dllist_foreach_node(&psDevInfo->sZSBufferHead, psNode, psNext) |
| { |
| RGX_ZSBUFFER_DATA *psThisZSBuffer = IMG_CONTAINER_OF(psNode, RGX_ZSBUFFER_DATA, sNode); |
| |
| if (psThisZSBuffer->ui32ZSBufferID == ui32ZSBufferID) |
| { |
| psZSBuffer = psThisZSBuffer; |
| break; |
| } |
| } |
| |
| OSLockRelease(psDevInfo->hLockZSBuffer); |
| return psZSBuffer; |
| } |
| |
| void RGXProcessRequestZSBufferBacking(PVRSRV_RGXDEV_INFO *psDevInfo, |
| IMG_UINT32 ui32ZSBufferID) |
| { |
| RGX_ZSBUFFER_DATA *psZSBuffer; |
| RGXFWIF_KCCB_CMD sTACCBCmd; |
| PVRSRV_ERROR eError; |
| |
| PVR_ASSERT(psDevInfo); |
| |
| /* scan all deferred allocations */ |
| psZSBuffer = FindZSBuffer(psDevInfo, ui32ZSBufferID); |
| |
| if (psZSBuffer) |
| { |
| IMG_BOOL bBackingDone = IMG_TRUE; |
| |
| /* Populate ZLS */ |
| eError = RGXBackingZSBuffer(psZSBuffer); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"Populating ZS-Buffer failed with error %u (ID = 0x%08x)", eError, ui32ZSBufferID)); |
| bBackingDone = IMG_FALSE; |
| } |
| |
| /* send confirmation */ |
| sTACCBCmd.eCmdType = RGXFWIF_KCCB_CMD_ZSBUFFER_BACKING_UPDATE; |
| sTACCBCmd.uCmdData.sZSBufferBackingData.sZSBufferFWDevVAddr.ui32Addr = psZSBuffer->sZSBufferFWDevVAddr.ui32Addr; |
| sTACCBCmd.uCmdData.sZSBufferBackingData.bDone = bBackingDone; |
| |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError = RGXScheduleCommand(psDevInfo, |
| RGXFWIF_DM_TA, |
| &sTACCBCmd, |
| sizeof(sTACCBCmd), |
| 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); |
| |
| psZSBuffer->ui32NumReqByFW++; |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| PVRSRVStatsUpdateZSBufferStats(0,1,psZSBuffer->owner); |
| #endif |
| |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_ERROR,"ZS Buffer Lookup for ZS Buffer ID 0x%08x failed (Populate)", ui32ZSBufferID)); |
| } |
| } |
| |
| void RGXProcessRequestZSBufferUnbacking(PVRSRV_RGXDEV_INFO *psDevInfo, |
| IMG_UINT32 ui32ZSBufferID) |
| { |
| RGX_ZSBUFFER_DATA *psZSBuffer; |
| RGXFWIF_KCCB_CMD sTACCBCmd; |
| PVRSRV_ERROR eError; |
| |
| PVR_ASSERT(psDevInfo); |
| |
| /* scan all deferred allocations */ |
| psZSBuffer = FindZSBuffer(psDevInfo, ui32ZSBufferID); |
| |
| if (psZSBuffer) |
| { |
| /* Unpopulate ZLS */ |
| eError = RGXUnbackingZSBuffer(psZSBuffer); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"UnPopulating ZS-Buffer failed failed with error %u (ID = 0x%08x)", eError, ui32ZSBufferID)); |
| PVR_ASSERT(IMG_FALSE); |
| } |
| |
| /* send confirmation */ |
| sTACCBCmd.eCmdType = RGXFWIF_KCCB_CMD_ZSBUFFER_UNBACKING_UPDATE; |
| sTACCBCmd.uCmdData.sZSBufferBackingData.sZSBufferFWDevVAddr.ui32Addr = psZSBuffer->sZSBufferFWDevVAddr.ui32Addr; |
| sTACCBCmd.uCmdData.sZSBufferBackingData.bDone = IMG_TRUE; |
| |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError = RGXScheduleCommand(psDevInfo, |
| RGXFWIF_DM_TA, |
| &sTACCBCmd, |
| sizeof(sTACCBCmd), |
| 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 |
| { |
| PVR_DPF((PVR_DBG_ERROR,"ZS Buffer Lookup for ZS Buffer ID 0x%08x failed (UnPopulate)", ui32ZSBufferID)); |
| } |
| } |
| |
| static |
| PVRSRV_ERROR _CreateTAContext(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| DEVMEM_MEMDESC *psAllocatedMemDesc, |
| IMG_UINT32 ui32AllocatedOffset, |
| DEVMEM_MEMDESC *psFWMemContextMemDesc, |
| IMG_DEV_VIRTADDR sVDMCallStackAddr, |
| IMG_UINT32 ui32Priority, |
| RGX_COMMON_CONTEXT_INFO *psInfo, |
| RGX_SERVER_RC_TA_DATA *psTAData) |
| { |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_TACTX_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 TA context suspend state"); |
| |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(RGXFWIF_TACTX_STATE), |
| RGX_FWCOMCTX_ALLOCFLAGS, |
| "FwTAContextState", |
| &psTAData->psContextStateMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to allocate firmware GPU context suspend state (%u)", |
| eError)); |
| goto fail_tacontextsuspendalloc; |
| } |
| |
| eError = DevmemAcquireCpuVirtAddr(psTAData->psContextStateMemDesc, |
| (void **)&psContextState); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to map firmware render context state (%u)", |
| eError)); |
| goto fail_suspendcpuvirtacquire; |
| } |
| psContextState->uTAReg_VDM_CALL_STACK_POINTER_Init = sVDMCallStackAddr.uiAddr; |
| DevmemReleaseCpuVirtAddr(psTAData->psContextStateMemDesc); |
| |
| eError = FWCommonContextAllocate(psConnection, |
| psDeviceNode, |
| REQ_TYPE_TA, |
| RGXFWIF_DM_TA, |
| psAllocatedMemDesc, |
| ui32AllocatedOffset, |
| psFWMemContextMemDesc, |
| psTAData->psContextStateMemDesc, |
| RGX_TA_CCB_SIZE_LOG2, |
| ui32Priority, |
| psInfo, |
| &psTAData->psServerCommonContext); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to init TA fw common context (%u)", |
| eError)); |
| goto fail_tacommoncontext; |
| } |
| |
| /* |
| * Dump the FW 3D context suspend state buffer |
| */ |
| PDUMPCOMMENT("Dump the TA context suspend state buffer"); |
| DevmemPDumpLoadMem(psTAData->psContextStateMemDesc, |
| 0, |
| sizeof(RGXFWIF_TACTX_STATE), |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| psTAData->ui32Priority = ui32Priority; |
| return PVRSRV_OK; |
| |
| fail_tacommoncontext: |
| fail_suspendcpuvirtacquire: |
| DevmemFwFree(psDevInfo, psTAData->psContextStateMemDesc); |
| fail_tacontextsuspendalloc: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| |
| return eError; |
| } |
| |
| static |
| PVRSRV_ERROR _Create3DContext(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_RC_3D_DATA *ps3DData) |
| { |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| 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 3D context suspend state"); |
| |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(RGXFWIF_3DCTX_STATE), |
| RGX_FWCOMCTX_ALLOCFLAGS, |
| "Fw3DContextState", |
| &ps3DData->psContextStateMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to allocate firmware GPU context suspend state (%u)", |
| eError)); |
| goto fail_3dcontextsuspendalloc; |
| } |
| |
| eError = FWCommonContextAllocate(psConnection, |
| psDeviceNode, |
| REQ_TYPE_3D, |
| RGXFWIF_DM_3D, |
| psAllocatedMemDesc, |
| ui32AllocatedOffset, |
| psFWMemContextMemDesc, |
| ps3DData->psContextStateMemDesc, |
| RGX_3D_CCB_SIZE_LOG2, |
| ui32Priority, |
| psInfo, |
| &ps3DData->psServerCommonContext); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to init 3D fw common context (%u)", |
| eError)); |
| goto fail_3dcommoncontext; |
| } |
| |
| /* |
| * Dump the FW 3D context suspend state buffer |
| */ |
| PDUMPCOMMENT("Dump the 3D context suspend state buffer"); |
| DevmemPDumpLoadMem(ps3DData->psContextStateMemDesc, |
| 0, |
| sizeof(RGXFWIF_3DCTX_STATE), |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| ps3DData->ui32Priority = ui32Priority; |
| return PVRSRV_OK; |
| |
| fail_3dcommoncontext: |
| DevmemFwFree(psDevInfo, ps3DData->psContextStateMemDesc); |
| fail_3dcontextsuspendalloc: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| |
| return eError; |
| } |
| |
| |
| /* |
| * PVRSRVRGXCreateRenderContextKM |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR PVRSRVRGXCreateRenderContextKM(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| IMG_UINT32 ui32Priority, |
| IMG_DEV_VIRTADDR sMCUFenceAddr, |
| IMG_DEV_VIRTADDR sVDMCallStackAddr, |
| IMG_UINT32 ui32FrameworkRegisterSize, |
| IMG_PBYTE pabyFrameworkRegisters, |
| IMG_HANDLE hMemCtxPrivData, |
| RGX_SERVER_RENDER_CONTEXT **ppsRenderContext) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGX_SERVER_RENDER_CONTEXT *psRenderContext; |
| DEVMEM_MEMDESC *psFWMemContextMemDesc = RGXGetFWMemDescFromMemoryContextHandle(hMemCtxPrivData); |
| RGX_COMMON_CONTEXT_INFO sInfo; |
| |
| /* Prepare cleanup structure */ |
| *ppsRenderContext = NULL; |
| psRenderContext = OSAllocZMem(sizeof(*psRenderContext)); |
| if (psRenderContext == NULL) |
| { |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| |
| psRenderContext->psDeviceNode = psDeviceNode; |
| |
| /* |
| Create the FW render context, this has the TA and 3D FW common |
| contexts embedded within it |
| */ |
| eError = DevmemFwAllocate(psDevInfo, |
| sizeof(RGXFWIF_FWRENDERCONTEXT), |
| RGX_FWCOMCTX_ALLOCFLAGS, |
| "FwRenderContext", |
| &psRenderContext->psFWRenderContextMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_fwrendercontext; |
| } |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| WorkEstRCInit(&(psRenderContext->sWorkEstData)); |
| #endif |
| |
| /* Allocate cleanup sync */ |
| eError = SyncPrimAlloc(psDeviceNode->hSyncPrimContext, |
| &psRenderContext->psCleanupSync, |
| "ta3d render context cleanup"); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to allocate cleanup sync (0x%x)", |
| eError)); |
| goto fail_syncalloc; |
| } |
| |
| /* |
| * Create the FW framework buffer |
| */ |
| eError = PVRSRVRGXFrameworkCreateKM(psDeviceNode, |
| &psRenderContext->psFWFrameworkMemDesc, |
| ui32FrameworkRegisterSize); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to allocate firmware GPU framework state (%u)", |
| eError)); |
| goto fail_frameworkcreate; |
| } |
| |
| /* Copy the Framework client data into the framework buffer */ |
| eError = PVRSRVRGXFrameworkCopyCommand(psRenderContext->psFWFrameworkMemDesc, |
| pabyFrameworkRegisters, |
| ui32FrameworkRegisterSize); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXCreateRenderContextKM: Failed to populate the framework buffer (%u)", |
| eError)); |
| goto fail_frameworkcopy; |
| } |
| |
| sInfo.psFWFrameworkMemDesc = psRenderContext->psFWFrameworkMemDesc; |
| sInfo.psMCUFenceAddr = &sMCUFenceAddr; |
| |
| eError = _CreateTAContext(psConnection, |
| psDeviceNode, |
| psRenderContext->psFWRenderContextMemDesc, |
| offsetof(RGXFWIF_FWRENDERCONTEXT, sTAContext), |
| psFWMemContextMemDesc, |
| sVDMCallStackAddr, |
| ui32Priority, |
| &sInfo, |
| &psRenderContext->sTAData); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_tacontext; |
| } |
| |
| eError = _Create3DContext(psConnection, |
| psDeviceNode, |
| psRenderContext->psFWRenderContextMemDesc, |
| offsetof(RGXFWIF_FWRENDERCONTEXT, s3DContext), |
| psFWMemContextMemDesc, |
| ui32Priority, |
| &sInfo, |
| &psRenderContext->s3DData); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_3dcontext; |
| } |
| |
| SyncAddrListInit(&psRenderContext->sSyncAddrListTAFence); |
| SyncAddrListInit(&psRenderContext->sSyncAddrListTAUpdate); |
| SyncAddrListInit(&psRenderContext->sSyncAddrList3DFence); |
| SyncAddrListInit(&psRenderContext->sSyncAddrList3DUpdate); |
| |
| { |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| |
| OSWRLockAcquireWrite(psDevInfo->hRenderCtxListLock); |
| dllist_add_to_tail(&(psDevInfo->sRenderCtxtListHead), &(psRenderContext->sListNode)); |
| OSWRLockReleaseWrite(psDevInfo->hRenderCtxListLock); |
| } |
| |
| *ppsRenderContext= psRenderContext; |
| return PVRSRV_OK; |
| |
| fail_3dcontext: |
| _DestroyTAContext(&psRenderContext->sTAData, |
| psDeviceNode, |
| psRenderContext->psCleanupSync); |
| fail_tacontext: |
| fail_frameworkcopy: |
| DevmemFwFree(psDevInfo, psRenderContext->psFWFrameworkMemDesc); |
| fail_frameworkcreate: |
| SyncPrimFree(psRenderContext->psCleanupSync); |
| fail_syncalloc: |
| DevmemFwFree(psDevInfo, psRenderContext->psFWRenderContextMemDesc); |
| fail_fwrendercontext: |
| OSFreeMem(psRenderContext); |
| PVR_ASSERT(eError != PVRSRV_OK); |
| |
| return eError; |
| } |
| |
| /* |
| * PVRSRVRGXDestroyRenderContextKM |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR PVRSRVRGXDestroyRenderContextKM(RGX_SERVER_RENDER_CONTEXT *psRenderContext) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psRenderContext->psDeviceNode->pvDevice; |
| RGXFWIF_FWRENDERCONTEXT *psFWRenderContext; |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| IMG_UINT32 ui32WorkEstCCBSubmitted; |
| #endif |
| |
| /* remove node from list before calling destroy - as destroy, if successful |
| * will invalidate the node |
| * must be re-added if destroy fails |
| */ |
| OSWRLockAcquireWrite(psDevInfo->hRenderCtxListLock); |
| dllist_remove_node(&(psRenderContext->sListNode)); |
| OSWRLockReleaseWrite(psDevInfo->hRenderCtxListLock); |
| |
| /* Cleanup the TA if we haven't already */ |
| if ((psRenderContext->ui32CleanupStatus & RC_CLEANUP_TA_COMPLETE) == 0) |
| { |
| eError = _DestroyTAContext(&psRenderContext->sTAData, |
| psRenderContext->psDeviceNode, |
| psRenderContext->psCleanupSync); |
| if (eError == PVRSRV_OK) |
| { |
| psRenderContext->ui32CleanupStatus |= RC_CLEANUP_TA_COMPLETE; |
| } |
| else |
| { |
| goto e0; |
| } |
| } |
| |
| /* Cleanup the 3D if we haven't already */ |
| if ((psRenderContext->ui32CleanupStatus & RC_CLEANUP_3D_COMPLETE) == 0) |
| { |
| eError = _Destroy3DContext(&psRenderContext->s3DData, |
| psRenderContext->psDeviceNode, |
| psRenderContext->psCleanupSync); |
| if (eError == PVRSRV_OK) |
| { |
| psRenderContext->ui32CleanupStatus |= RC_CLEANUP_3D_COMPLETE; |
| } |
| else |
| { |
| goto e0; |
| } |
| } |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| eError = DevmemAcquireCpuVirtAddr(psRenderContext->psFWRenderContextMemDesc, |
| (void **)&psFWRenderContext); |
| if(eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXDestroyRenderContextKM: Failed to map firmware render context (%u)", |
| eError)); |
| goto e0; |
| } |
| |
| ui32WorkEstCCBSubmitted = psFWRenderContext->ui32WorkEstCCBSubmitted; |
| |
| DevmemReleaseCpuVirtAddr(psRenderContext->psFWRenderContextMemDesc); |
| |
| /* Check if all of the workload estimation CCB commands for this workload |
| * are read |
| */ |
| if(ui32WorkEstCCBSubmitted != psRenderContext->sWorkEstData.ui32WorkEstCCBReceived) |
| { |
| eError = PVRSRV_ERROR_RETRY; |
| goto e0; |
| } |
| #endif |
| |
| /* |
| Only if both TA and 3D contexts have been cleaned up can we |
| free the shared resources |
| */ |
| if (psRenderContext->ui32CleanupStatus == (RC_CLEANUP_3D_COMPLETE | RC_CLEANUP_TA_COMPLETE)) |
| { |
| |
| /* Update SPM statistics */ |
| eError = DevmemAcquireCpuVirtAddr(psRenderContext->psFWRenderContextMemDesc, |
| (void **)&psFWRenderContext); |
| if (eError == PVRSRV_OK) |
| { |
| DevmemReleaseCpuVirtAddr(psRenderContext->psFWRenderContextMemDesc); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_ERROR,"PVRSRVRGXDestroyRenderContextKM: Failed to map firmware render context (%u)", |
| eError)); |
| } |
| |
| /* Free the framework buffer */ |
| DevmemFwFree(psDevInfo, psRenderContext->psFWFrameworkMemDesc); |
| |
| /* Free the firmware render context */ |
| DevmemFwFree(psDevInfo, psRenderContext->psFWRenderContextMemDesc); |
| |
| /* Free the cleanup sync */ |
| SyncPrimFree(psRenderContext->psCleanupSync); |
| |
| SyncAddrListDeinit(&psRenderContext->sSyncAddrListTAFence); |
| SyncAddrListDeinit(&psRenderContext->sSyncAddrListTAUpdate); |
| SyncAddrListDeinit(&psRenderContext->sSyncAddrList3DFence); |
| SyncAddrListDeinit(&psRenderContext->sSyncAddrList3DUpdate); |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| WorkEstRCDeInit(&(psRenderContext->sWorkEstData), |
| psDevInfo); |
| #endif |
| |
| OSFreeMem(psRenderContext); |
| } |
| |
| return PVRSRV_OK; |
| |
| e0: |
| OSWRLockAcquireWrite(psDevInfo->hRenderCtxListLock); |
| dllist_add_to_tail(&(psDevInfo->sRenderCtxtListHead), &(psRenderContext->sListNode)); |
| OSWRLockReleaseWrite(psDevInfo->hRenderCtxListLock); |
| return eError; |
| } |
| |
| |
| /* TODO !!! this was local on the stack, and we managed to blow the stack for the kernel. |
| * THIS - 46 argument function needs to be sorted out. |
| */ |
| /* 1 command for the TA */ |
| static RGX_CCB_CMD_HELPER_DATA asTACmdHelperData[1]; |
| /* Up to 3 commands for the 3D (partial render fence, partial reader, and render) */ |
| static RGX_CCB_CMD_HELPER_DATA as3DCmdHelperData[3]; |
| |
| /* |
| * PVRSRVRGXKickTA3DKM |
| */ |
| IMG_EXPORT |
| PVRSRV_ERROR PVRSRVRGXKickTA3DKM(RGX_SERVER_RENDER_CONTEXT *psRenderContext, |
| IMG_UINT32 ui32ClientCacheOpSeqNum, |
| IMG_UINT32 ui32ClientTAFenceCount, |
| SYNC_PRIMITIVE_BLOCK **apsClientTAFenceSyncPrimBlock, |
| IMG_UINT32 *paui32ClientTAFenceSyncOffset, |
| IMG_UINT32 *paui32ClientTAFenceValue, |
| IMG_UINT32 ui32ClientTAUpdateCount, |
| SYNC_PRIMITIVE_BLOCK **apsClientTAUpdateSyncPrimBlock, |
| IMG_UINT32 *paui32ClientTAUpdateSyncOffset, |
| IMG_UINT32 *paui32ClientTAUpdateValue, |
| IMG_UINT32 ui32ServerTASyncPrims, |
| IMG_UINT32 *paui32ServerTASyncFlags, |
| SERVER_SYNC_PRIMITIVE **pasServerTASyncs, |
| IMG_UINT32 ui32Client3DFenceCount, |
| SYNC_PRIMITIVE_BLOCK **apsClient3DFenceSyncPrimBlock, |
| IMG_UINT32 *paui32Client3DFenceSyncOffset, |
| IMG_UINT32 *paui32Client3DFenceValue, |
| IMG_UINT32 ui32Client3DUpdateCount, |
| SYNC_PRIMITIVE_BLOCK **apsClient3DUpdateSyncPrimBlock, |
| IMG_UINT32 *paui32Client3DUpdateSyncOffset, |
| IMG_UINT32 *paui32Client3DUpdateValue, |
| IMG_UINT32 ui32Server3DSyncPrims, |
| IMG_UINT32 *paui32Server3DSyncFlags, |
| SERVER_SYNC_PRIMITIVE **pasServer3DSyncs, |
| SYNC_PRIMITIVE_BLOCK *psPRFenceSyncPrimBlock, |
| IMG_UINT32 ui32PRFenceSyncOffset, |
| IMG_UINT32 ui32PRFenceValue, |
| IMG_INT32 i32CheckFenceFD, |
| IMG_INT32 i32UpdateTimelineFD, |
| IMG_INT32 *pi32UpdateFenceFD, |
| IMG_CHAR szFenceName[32], |
| IMG_UINT32 ui32TACmdSize, |
| IMG_PBYTE pui8TADMCmd, |
| IMG_UINT32 ui323DPRCmdSize, |
| IMG_PBYTE pui83DPRDMCmd, |
| IMG_UINT32 ui323DCmdSize, |
| IMG_PBYTE pui83DDMCmd, |
| IMG_UINT32 ui32ExtJobRef, |
| IMG_BOOL bLastTAInScene, |
| IMG_BOOL bKickTA, |
| IMG_BOOL bKickPR, |
| IMG_BOOL bKick3D, |
| IMG_BOOL bAbort, |
| IMG_UINT32 ui32PDumpFlags, |
| RGX_RTDATA_CLEANUP_DATA *psRTDataCleanup, |
| RGX_ZSBUFFER_DATA *psZBuffer, |
| RGX_ZSBUFFER_DATA *psSBuffer, |
| IMG_BOOL bCommitRefCountsTA, |
| IMG_BOOL bCommitRefCounts3D, |
| IMG_BOOL *pbCommittedRefCountsTA, |
| IMG_BOOL *pbCommittedRefCounts3D, |
| IMG_UINT32 ui32SyncPMRCount, |
| IMG_UINT32 *paui32SyncPMRFlags, |
| PMR **ppsSyncPMRs, |
| IMG_UINT32 ui32RenderTargetSize, |
| IMG_UINT32 ui32NumberOfDrawCalls, |
| IMG_UINT32 ui32NumberOfIndices, |
| IMG_UINT32 ui32NumberOfMRTs, |
| IMG_UINT64 ui64DeadlineInus) |
| { |
| |
| IMG_UINT32 ui32TACmdCount=0; |
| IMG_UINT32 ui323DCmdCount=0; |
| IMG_UINT32 ui32TACmdOffset=0; |
| IMG_UINT32 ui323DCmdOffset=0; |
| RGXFWIF_UFO sPRUFO; |
| IMG_UINT32 i; |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_ERROR eError2; |
| IMG_INT32 i32UpdateFenceFD = -1; |
| IMG_UINT32 ui32JobId; |
| |
| IMG_UINT32 ui32ClientPRUpdateCount = 0; |
| PRGXFWIF_UFO_ADDR *pauiClientPRUpdateUFOAddress = NULL; |
| IMG_UINT32 *paui32ClientPRUpdateValue = NULL; |
| |
| PRGXFWIF_TIMESTAMP_ADDR pPreAddr; |
| PRGXFWIF_TIMESTAMP_ADDR pPostAddr; |
| PRGXFWIF_UFO_ADDR pRMWUFOAddr; |
| |
| PRGXFWIF_UFO_ADDR *pauiClientTAFenceUFOAddress; |
| PRGXFWIF_UFO_ADDR *pauiClientTAUpdateUFOAddress; |
| PRGXFWIF_UFO_ADDR *pauiClient3DFenceUFOAddress; |
| PRGXFWIF_UFO_ADDR *pauiClient3DUpdateUFOAddress; |
| PRGXFWIF_UFO_ADDR uiPRFenceUFOAddress; |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| RGXFWIF_WORKEST_KICK_DATA sWorkloadKickDataTA; |
| RGXFWIF_WORKEST_KICK_DATA sWorkloadKickData3D; |
| IMG_UINT32 ui32TACommandOffset = 0; |
| IMG_UINT32 ui323DCommandOffset = 0; |
| IMG_UINT32 ui32TACmdHeaderOffset = 0; |
| IMG_UINT32 ui323DCmdHeaderOffset = 0; |
| IMG_UINT32 ui323DFullRenderCommandOffset = 0; |
| IMG_UINT32 ui32TACmdOffsetWrapCheck = 0; |
| IMG_UINT32 ui323DCmdOffsetWrapCheck = 0; |
| #endif |
| |
| #if defined(SUPPORT_BUFFER_SYNC) |
| struct pvr_buffer_sync_append_data *psAppendData = NULL; |
| #endif |
| |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| /* Android fd sync update info */ |
| struct pvr_sync_append_data *psFDData = NULL; |
| if (i32UpdateTimelineFD >= 0 && !pi32UpdateFenceFD) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| #else |
| if (i32UpdateTimelineFD >= 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "%s: Providing native sync timeline (%d) in non native sync enabled driver", |
| __func__, i32UpdateTimelineFD)); |
| } |
| if (i32CheckFenceFD >= 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "%s: Providing native check sync (%d) in non native sync enabled driver", |
| __func__, i32CheckFenceFD)); |
| } |
| #endif |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| sWorkloadKickDataTA.ui64ReturnDataIndex = 0; |
| sWorkloadKickData3D.ui64ReturnDataIndex = 0; |
| #endif |
| |
| ui32JobId = OSAtomicIncrement(&psRenderContext->hJobId); |
| |
| /* Ensure the string is null-terminated (Required for safety) */ |
| szFenceName[31] = '\0'; |
| *pbCommittedRefCountsTA = IMG_FALSE; |
| *pbCommittedRefCounts3D = IMG_FALSE; |
| |
| eError = SyncAddrListPopulate(&psRenderContext->sSyncAddrListTAFence, |
| ui32ClientTAFenceCount, |
| apsClientTAFenceSyncPrimBlock, |
| paui32ClientTAFenceSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| pauiClientTAFenceUFOAddress = psRenderContext->sSyncAddrListTAFence.pasFWAddrs; |
| |
| eError = SyncAddrListPopulate(&psRenderContext->sSyncAddrListTAUpdate, |
| ui32ClientTAUpdateCount, |
| apsClientTAUpdateSyncPrimBlock, |
| paui32ClientTAUpdateSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| pauiClientTAUpdateUFOAddress = psRenderContext->sSyncAddrListTAUpdate.pasFWAddrs; |
| |
| eError = SyncAddrListPopulate(&psRenderContext->sSyncAddrList3DFence, |
| ui32Client3DFenceCount, |
| apsClient3DFenceSyncPrimBlock, |
| paui32Client3DFenceSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| pauiClient3DFenceUFOAddress = psRenderContext->sSyncAddrList3DFence.pasFWAddrs; |
| |
| eError = SyncAddrListPopulate(&psRenderContext->sSyncAddrList3DUpdate, |
| ui32Client3DUpdateCount, |
| apsClient3DUpdateSyncPrimBlock, |
| paui32Client3DUpdateSyncOffset); |
| if(eError != PVRSRV_OK) |
| { |
| goto err_populate_sync_addr_list; |
| } |
| |
| pauiClient3DUpdateUFOAddress = psRenderContext->sSyncAddrList3DUpdate.pasFWAddrs; |
| |
| eError = SyncPrimitiveBlockToFWAddr(psPRFenceSyncPrimBlock, |
| ui32PRFenceSyncOffset, |
| &uiPRFenceUFOAddress); |
| |
| if(eError != PVRSRV_OK) |
| { |
| goto err_pr_fence_address; |
| } |
| |
| |
| |
| /* Sanity check the server fences */ |
| for (i=0;i<ui32ServerTASyncPrims;i++) |
| { |
| if (!(paui32ServerTASyncFlags[i] & PVRSRV_CLIENT_SYNC_PRIM_OP_CHECK)) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Server fence (on TA) must fence", __FUNCTION__)); |
| return PVRSRV_ERROR_INVALID_SYNC_PRIM_OP; |
| } |
| } |
| |
| for (i=0;i<ui32Server3DSyncPrims;i++) |
| { |
| if (!(paui32Server3DSyncFlags[i] & PVRSRV_CLIENT_SYNC_PRIM_OP_CHECK)) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Server fence (on 3D) must fence", __FUNCTION__)); |
| return PVRSRV_ERROR_INVALID_SYNC_PRIM_OP; |
| } |
| } |
| |
| RGX_GetTimestampCmdHelper((PVRSRV_RGXDEV_INFO*) psRenderContext->psDeviceNode->pvDevice, |
| & pPreAddr, |
| & pPostAddr, |
| & pRMWUFOAddr); |
| |
| /* |
| Sanity check we have a PR kick if there are client or server fences |
| */ |
| if (!bKickPR && ((ui32Client3DFenceCount != 0) || (ui32Server3DSyncPrims != 0))) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: 3D fence (client or server) passed without a PR kick", __FUNCTION__)); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (ui32SyncPMRCount) |
| { |
| #if defined(SUPPORT_BUFFER_SYNC) |
| PVRSRV_DEVICE_NODE *psDeviceNode = psRenderContext->psDeviceNode; |
| IMG_UINT32 ui32ClientIntUpdateCount = 0; |
| PRGXFWIF_UFO_ADDR *pauiClientIntUpdateUFOAddress = NULL; |
| IMG_UINT32 *paui32ClientIntUpdateValue = NULL; |
| int err; |
| |
| if (!bKickTA) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Buffer sync only supported for kicks including a TA", |
| __FUNCTION__)); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (!bKickPR) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Buffer sync only supported for kicks including a PR", |
| __FUNCTION__)); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (bKick3D) |
| { |
| ui32ClientIntUpdateCount = ui32Client3DUpdateCount; |
| pauiClientIntUpdateUFOAddress = pauiClient3DUpdateUFOAddress; |
| paui32ClientIntUpdateValue = paui32Client3DUpdateValue; |
| } |
| else |
| { |
| ui32ClientIntUpdateCount = ui32ClientPRUpdateCount; |
| pauiClientIntUpdateUFOAddress = pauiClientPRUpdateUFOAddress; |
| paui32ClientIntUpdateValue = paui32ClientPRUpdateValue; |
| } |
| |
| err = pvr_buffer_sync_append_start(psDeviceNode->psBufferSyncContext, |
| ui32SyncPMRCount, |
| ppsSyncPMRs, |
| paui32SyncPMRFlags, |
| ui32ClientTAFenceCount, |
| pauiClientTAFenceUFOAddress, |
| paui32ClientTAFenceValue, |
| ui32ClientIntUpdateCount, |
| pauiClientIntUpdateUFOAddress, |
| paui32ClientIntUpdateValue, |
| &psAppendData); |
| if (err) |
| { |
| eError = (err == -ENOMEM) ? PVRSRV_ERROR_OUT_OF_MEMORY : PVRSRV_ERROR_INVALID_PARAMS; |
| goto fail_sync_append; |
| } |
| |
| pvr_buffer_sync_append_checks_get(psAppendData, |
| &ui32ClientTAFenceCount, |
| &pauiClientTAFenceUFOAddress, |
| &paui32ClientTAFenceValue); |
| |
| if (bKick3D) |
| { |
| pvr_buffer_sync_append_updates_get(psAppendData, |
| &ui32Client3DUpdateCount, |
| &pauiClient3DUpdateUFOAddress, |
| &paui32Client3DUpdateValue); |
| } |
| else |
| { |
| pvr_buffer_sync_append_updates_get(psAppendData, |
| &ui32ClientPRUpdateCount, |
| &pauiClientPRUpdateUFOAddress, |
| &paui32ClientPRUpdateValue); |
| } |
| #else |
| PVR_DPF((PVR_DBG_ERROR, "%s: Buffer sync not supported but got %u buffers", __FUNCTION__, ui32SyncPMRCount)); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| #endif /* defined(SUPPORT_BUFFER_SYNC) */ |
| } |
| |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| /* |
| * The hardware requires a PR to be submitted if there is a TA (otherwise |
| * it can wedge if we run out of PB space with no PR to run) |
| * |
| * If we only have a TA, attach native checks to the TA and updates to the PR |
| * If we have a TA and 3D, attach checks to TA, updates to 3D |
| * If we only have a 3D, attach checks and updates to the 3D |
| * |
| * Note that 'updates' includes the cleanup syncs for 'check' fence FDs, in |
| * addition to the update fence FD (if supplied) |
| * |
| * Currently, the client driver never kicks only the 3D, so we only support |
| * that for the time being. |
| */ |
| if (i32CheckFenceFD >= 0 || i32UpdateTimelineFD >= 0) |
| { |
| IMG_UINT32 ui32ClientIntUpdateCount = 0; |
| PRGXFWIF_UFO_ADDR *pauiClientIntUpdateUFOAddress = NULL; |
| IMG_UINT32 *paui32ClientIntUpdateValue = NULL; |
| |
| if (!bKickTA) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Native syncs only supported for kicks including a TA", |
| __FUNCTION__)); |
| eError = PVRSRV_ERROR_INVALID_PARAMS; |
| goto fail_fdsync; |
| } |
| if (!bKickPR) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Native syncs require a PR for all kicks", |
| __FUNCTION__)); |
| eError = PVRSRV_ERROR_INVALID_PARAMS; |
| goto fail_fdsync; |
| } |
| /* If we have a 3D, attach updates to that. Otherwise, we attach it to a PR */ |
| if (bKick3D) |
| { |
| ui32ClientIntUpdateCount = ui32Client3DUpdateCount; |
| pauiClientIntUpdateUFOAddress = pauiClient3DUpdateUFOAddress; |
| paui32ClientIntUpdateValue = paui32Client3DUpdateValue; |
| } |
| else |
| { |
| ui32ClientIntUpdateCount = ui32ClientPRUpdateCount; |
| pauiClientIntUpdateUFOAddress = pauiClientPRUpdateUFOAddress; |
| paui32ClientIntUpdateValue = paui32ClientPRUpdateValue; |
| } |
| |
| eError = |
| pvr_sync_append_fences(szFenceName, |
| i32CheckFenceFD, |
| i32UpdateTimelineFD, |
| ui32ClientIntUpdateCount, |
| pauiClientIntUpdateUFOAddress, |
| paui32ClientIntUpdateValue, |
| ui32ClientTAFenceCount, |
| pauiClientTAFenceUFOAddress, |
| paui32ClientTAFenceValue, |
| &psFDData); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_fdsync; |
| } |
| /* If we have a 3D, attach updates to that. Otherwise, we attach it to a PR */ |
| if (bKick3D) |
| { |
| pvr_sync_get_updates(psFDData, &ui32Client3DUpdateCount, |
| &pauiClient3DUpdateUFOAddress, &paui32Client3DUpdateValue); |
| } |
| else |
| { |
| pvr_sync_get_updates(psFDData, &ui32ClientPRUpdateCount, |
| &pauiClientPRUpdateUFOAddress, &paui32ClientPRUpdateValue); |
| } |
| pvr_sync_get_checks(psFDData, &ui32ClientTAFenceCount, |
| &pauiClientTAFenceUFOAddress, &paui32ClientTAFenceValue); |
| if (ui32ClientPRUpdateCount) |
| { |
| PVR_ASSERT(pauiClientPRUpdateUFOAddress); |
| PVR_ASSERT(paui32ClientPRUpdateValue); |
| } |
| if (ui32Client3DUpdateCount) |
| { |
| PVR_ASSERT(pauiClient3DUpdateUFOAddress); |
| PVR_ASSERT(paui32Client3DUpdateValue); |
| } |
| } |
| #endif /* SUPPORT_NATIVE_FENCE_SYNC */ |
| |
| /* Init and acquire to TA command if required */ |
| if(bKickTA) |
| { |
| RGX_SERVER_RC_TA_DATA *psTAData = &psRenderContext->sTAData; |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| /* Prepare workload estimation */ |
| WorkEstPrepare(psRenderContext->psDeviceNode->pvDevice, |
| &(psRenderContext->sWorkEstData), |
| &(psRenderContext->sWorkEstData.sWorkloadMatchingDataTA), |
| ui32RenderTargetSize, |
| ui32NumberOfDrawCalls, |
| ui32NumberOfIndices, |
| ui32NumberOfMRTs, |
| ui64DeadlineInus, |
| &sWorkloadKickDataTA); |
| #endif |
| |
| /* Init the TA command helper */ |
| eError = RGXCmdHelperInitCmdCCB(FWCommonContextGetClientCCB(psTAData->psServerCommonContext), |
| ui32ClientTAFenceCount, |
| pauiClientTAFenceUFOAddress, |
| paui32ClientTAFenceValue, |
| ui32ClientTAUpdateCount, |
| pauiClientTAUpdateUFOAddress, |
| paui32ClientTAUpdateValue, |
| ui32ServerTASyncPrims, |
| paui32ServerTASyncFlags, |
| SYNC_FLAG_MASK_ALL, |
| pasServerTASyncs, |
| ui32TACmdSize, |
| pui8TADMCmd, |
| & pPreAddr, |
| (bKick3D ? NULL : & pPostAddr), |
| (bKick3D ? NULL : & pRMWUFOAddr), |
| RGXFWIF_CCB_CMD_TYPE_TA, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| &sWorkloadKickDataTA, |
| #else |
| NULL, |
| #endif |
| "TA", |
| asTACmdHelperData); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_tacmdinit; |
| } |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| /* The following is used to determine the offset of the command header |
| * containing the workload estimation data so that can be accessed when |
| * the KCCB is read. |
| */ |
| ui32TACmdHeaderOffset = RGXCmdHelperGetDMCommandHeaderOffset(asTACmdHelperData); |
| #endif |
| |
| eError = RGXCmdHelperAcquireCmdCCB(IMG_ARR_NUM_ELEMS(asTACmdHelperData), |
| asTACmdHelperData); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_taacquirecmd; |
| } |
| else |
| { |
| ui32TACmdCount++; |
| } |
| } |
| |
| /* Only kick the 3D if required */ |
| if (bKickPR) |
| { |
| RGX_SERVER_RC_3D_DATA *ps3DData = &psRenderContext->s3DData; |
| |
| /* |
| The command helper doesn't know about the PR fence so create |
| the command with all the fences against it and later create |
| the PR command itself which _must_ come after the PR fence. |
| */ |
| sPRUFO.puiAddrUFO = uiPRFenceUFOAddress; |
| sPRUFO.ui32Value = ui32PRFenceValue; |
| |
| /* Init the PR fence command helper */ |
| eError = RGXCmdHelperInitCmdCCB(FWCommonContextGetClientCCB(ps3DData->psServerCommonContext), |
| ui32Client3DFenceCount, |
| pauiClient3DFenceUFOAddress, |
| paui32Client3DFenceValue, |
| 0, |
| NULL, |
| NULL, |
| (bKick3D ? ui32Server3DSyncPrims : 0), |
| paui32Server3DSyncFlags, |
| PVRSRV_CLIENT_SYNC_PRIM_OP_CHECK, |
| pasServer3DSyncs, |
| sizeof(sPRUFO), |
| (IMG_UINT8*) &sPRUFO, |
| NULL, |
| NULL, |
| NULL, |
| RGXFWIF_CCB_CMD_TYPE_FENCE_PR, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| NULL, |
| "3D-PR-Fence", |
| &as3DCmdHelperData[ui323DCmdCount++]); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_prfencecmdinit; |
| } |
| |
| /* Init the 3D PR command helper */ |
| /* |
| See note above PVRFDSyncQueryFencesKM as to why updates for android |
| syncs are passed in with the PR |
| */ |
| eError = RGXCmdHelperInitCmdCCB(FWCommonContextGetClientCCB(ps3DData->psServerCommonContext), |
| 0, |
| NULL, |
| NULL, |
| ui32ClientPRUpdateCount, |
| pauiClientPRUpdateUFOAddress, |
| paui32ClientPRUpdateValue, |
| 0, |
| NULL, |
| SYNC_FLAG_MASK_ALL, |
| NULL, |
| ui323DPRCmdSize, |
| pui83DPRDMCmd, |
| NULL, |
| NULL, |
| NULL, |
| RGXFWIF_CCB_CMD_TYPE_3D_PR, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| NULL, |
| "3D-PR", |
| &as3DCmdHelperData[ui323DCmdCount++]); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_prcmdinit; |
| } |
| } |
| |
| if (bKick3D || bAbort) |
| { |
| RGX_SERVER_RC_3D_DATA *ps3DData = &psRenderContext->s3DData; |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| /* Prepare workload estimation */ |
| WorkEstPrepare(psRenderContext->psDeviceNode->pvDevice, |
| &(psRenderContext->sWorkEstData), |
| &(psRenderContext->sWorkEstData.sWorkloadMatchingData3D), |
| ui32RenderTargetSize, |
| ui32NumberOfDrawCalls, |
| ui32NumberOfIndices, |
| ui32NumberOfMRTs, |
| ui64DeadlineInus, |
| &sWorkloadKickData3D); |
| #endif |
| /* Init the 3D command helper */ |
| eError = RGXCmdHelperInitCmdCCB(FWCommonContextGetClientCCB(ps3DData->psServerCommonContext), |
| 0, |
| NULL, |
| NULL, |
| ui32Client3DUpdateCount, |
| pauiClient3DUpdateUFOAddress, |
| paui32Client3DUpdateValue, |
| ui32Server3DSyncPrims, |
| paui32Server3DSyncFlags, |
| PVRSRV_CLIENT_SYNC_PRIM_OP_UPDATE, |
| pasServer3DSyncs, |
| ui323DCmdSize, |
| pui83DDMCmd, |
| (bKickTA ? NULL : & pPreAddr), |
| & pPostAddr, |
| & pRMWUFOAddr, |
| RGXFWIF_CCB_CMD_TYPE_3D, |
| ui32ExtJobRef, |
| ui32JobId, |
| ui32PDumpFlags, |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| &sWorkloadKickData3D, |
| #else |
| NULL, |
| #endif |
| "3D", |
| &as3DCmdHelperData[ui323DCmdCount++]); |
| if (eError != PVRSRV_OK) |
| { |
| goto fail_3dcmdinit; |
| } |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| /* The following are used to determine the offset of the command header |
| * containing the workload estimation data so that can be accessed when |
| * the KCCB is read. |
| */ |
| ui323DCmdHeaderOffset = |
| RGXCmdHelperGetDMCommandHeaderOffset(&as3DCmdHelperData[ui323DCmdCount - 1]); |
| ui323DFullRenderCommandOffset = |
| RGXCmdHelperGetCommandOffset(as3DCmdHelperData, |
| ui323DCmdCount - 1); |
| #endif |
| } |
| |
| /* Protect against array overflow in RGXCmdHelperAcquireCmdCCB() */ |
| if (ui323DCmdCount > IMG_ARR_NUM_ELEMS(as3DCmdHelperData)) |
| { |
| goto fail_3dcmdinit; |
| } |
| |
| if (ui323DCmdCount) |
| { |
| PVR_ASSERT(bKickPR || bKick3D); |
| |
| /* Acquire space for all the 3D command(s) */ |
| eError = RGXCmdHelperAcquireCmdCCB(ui323DCmdCount, |
| as3DCmdHelperData); |
| if (eError != PVRSRV_OK) |
| { |
| /* If RGXCmdHelperAcquireCmdCCB fails we skip the scheduling |
| * of a new TA command with the same Write offset in Kernel CCB. |
| */ |
| goto fail_3dacquirecmd; |
| } |
| } |
| |
| /* |
| We should acquire the space in the kernel CCB here as after this point |
| we release the commands which will take operations on server syncs |
| which can't be undone |
| */ |
| |
| /* |
| Everything is ready to go now, release the commands |
| */ |
| if (ui32TACmdCount) |
| { |
| ui32TACmdOffset = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRenderContext->sTAData.psServerCommonContext)); |
| RGXCmdHelperReleaseCmdCCB(ui32TACmdCount, |
| asTACmdHelperData, |
| "TA", |
| FWCommonContextGetFWAddress(psRenderContext->sTAData.psServerCommonContext).ui32Addr); |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| ui32TACmdOffsetWrapCheck = |
| RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRenderContext->sTAData.psServerCommonContext)); |
| |
| /* This checks if the command would wrap around at the end of the CCB |
| * and therefore would start at an offset of 0 rather than the current |
| * command offset. |
| */ |
| if(ui32TACmdOffset < ui32TACmdOffsetWrapCheck) |
| { |
| ui32TACommandOffset = ui32TACmdOffset; |
| } |
| else |
| { |
| ui32TACommandOffset = 0; |
| } |
| #endif |
| } |
| |
| if (ui323DCmdCount) |
| { |
| ui323DCmdOffset = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRenderContext->s3DData.psServerCommonContext)); |
| RGXCmdHelperReleaseCmdCCB(ui323DCmdCount, |
| as3DCmdHelperData, |
| "3D", |
| FWCommonContextGetFWAddress(psRenderContext->s3DData.psServerCommonContext).ui32Addr); |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| ui323DCmdOffsetWrapCheck = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRenderContext->s3DData.psServerCommonContext)); |
| |
| if(ui323DCmdOffset < ui323DCmdOffsetWrapCheck) |
| { |
| ui323DCommandOffset = ui323DCmdOffset; |
| } |
| else |
| { |
| ui323DCommandOffset = 0; |
| } |
| #endif |
| } |
| |
| if (ui32TACmdCount) |
| { |
| RGXFWIF_KCCB_CMD sTAKCCBCmd; |
| IMG_UINT32 ui32FWCtx = FWCommonContextGetFWAddress(psRenderContext->sTAData.psServerCommonContext).ui32Addr; |
| |
| /* Construct the kernel TA CCB command. */ |
| sTAKCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_KICK; |
| sTAKCCBCmd.uCmdData.sCmdKickData.psContext = FWCommonContextGetFWAddress(psRenderContext->sTAData.psServerCommonContext); |
| sTAKCCBCmd.uCmdData.sCmdKickData.ui32CWoffUpdate = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRenderContext->sTAData.psServerCommonContext)); |
| |
| /* Add the Workload data into the KCCB kick */ |
| sTAKCCBCmd.uCmdData.sCmdKickData.sWorkloadDataFWAddress.ui32Addr = 0; |
| sTAKCCBCmd.uCmdData.sCmdKickData.ui32WorkEstCmdHeaderOffset = 0; |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| /* Store the offset to the CCCB command header so that it can be |
| * referenced when the KCCB command reaches the FW |
| */ |
| sTAKCCBCmd.uCmdData.sCmdKickData.ui32WorkEstCmdHeaderOffset = |
| ui32TACommandOffset + ui32TACmdHeaderOffset; |
| #endif |
| |
| if(bCommitRefCountsTA) |
| { |
| AttachKickResourcesCleanupCtls((PRGXFWIF_CLEANUP_CTL *) &sTAKCCBCmd.uCmdData.sCmdKickData.apsCleanupCtl, |
| &sTAKCCBCmd.uCmdData.sCmdKickData.ui32NumCleanupCtl, |
| RGXFWIF_DM_TA, |
| bKickTA, |
| psRTDataCleanup, |
| psZBuffer, |
| psSBuffer); |
| *pbCommittedRefCountsTA = IMG_TRUE; |
| } |
| else |
| { |
| sTAKCCBCmd.uCmdData.sCmdKickData.ui32NumCleanupCtl = 0; |
| } |
| |
| HTBLOGK(HTB_SF_MAIN_KICK_TA, |
| sTAKCCBCmd.uCmdData.sCmdKickData.psContext, |
| ui32TACmdOffset |
| ); |
| RGX_HWPERF_HOST_ENQ(psRenderContext, OSGetCurrentClientProcessIDKM(), |
| ui32FWCtx, ui32ExtJobRef, ui32JobId, |
| RGX_HWPERF_KICK_TYPE_TA3D); |
| |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError2 = RGXScheduleCommand(psRenderContext->psDeviceNode->pvDevice, |
| RGXFWIF_DM_TA, |
| &sTAKCCBCmd, |
| sizeof(sTAKCCBCmd), |
| ui32ClientCacheOpSeqNum, |
| ui32PDumpFlags); |
| if (eError2 != PVRSRV_ERROR_RETRY) |
| { |
| break; |
| } |
| OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT); |
| } END_LOOP_UNTIL_TIMEOUT(); |
| |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| RGXHWPerfFTraceGPUEnqueueEvent(psRenderContext->psDeviceNode->pvDevice, |
| ui32FWCtx, ui32JobId, RGX_HWPERF_KICK_TYPE_TA3D); |
| #endif |
| } |
| |
| if (ui323DCmdCount) |
| { |
| RGXFWIF_KCCB_CMD s3DKCCBCmd; |
| |
| /* Construct the kernel 3D CCB command. */ |
| s3DKCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_KICK; |
| s3DKCCBCmd.uCmdData.sCmdKickData.psContext = FWCommonContextGetFWAddress(psRenderContext->s3DData.psServerCommonContext); |
| s3DKCCBCmd.uCmdData.sCmdKickData.ui32CWoffUpdate = RGXGetHostWriteOffsetCCB(FWCommonContextGetClientCCB(psRenderContext->s3DData.psServerCommonContext)); |
| |
| /* Add the Workload data into the KCCB kick */ |
| s3DKCCBCmd.uCmdData.sCmdKickData.sWorkloadDataFWAddress.ui32Addr = 0; |
| s3DKCCBCmd.uCmdData.sCmdKickData.ui32WorkEstCmdHeaderOffset = 0; |
| |
| #if defined(SUPPORT_WORKLOAD_ESTIMATION) |
| /* Store the offset to the CCCB command header so that it can be |
| * referenced when the KCCB command reaches the FW |
| */ |
| s3DKCCBCmd.uCmdData.sCmdKickData.ui32WorkEstCmdHeaderOffset = ui323DCommandOffset + ui323DCmdHeaderOffset + ui323DFullRenderCommandOffset; |
| #endif |
| |
| if(bCommitRefCounts3D) |
| { |
| AttachKickResourcesCleanupCtls((PRGXFWIF_CLEANUP_CTL *) &s3DKCCBCmd.uCmdData.sCmdKickData.apsCleanupCtl, |
| &s3DKCCBCmd.uCmdData.sCmdKickData.ui32NumCleanupCtl, |
| RGXFWIF_DM_3D, |
| bKick3D, |
| psRTDataCleanup, |
| psZBuffer, |
| psSBuffer); |
| *pbCommittedRefCounts3D = IMG_TRUE; |
| } |
| else |
| { |
| s3DKCCBCmd.uCmdData.sCmdKickData.ui32NumCleanupCtl = 0; |
| } |
| |
| |
| HTBLOGK(HTB_SF_MAIN_KICK_3D, |
| s3DKCCBCmd.uCmdData.sCmdKickData.psContext, |
| ui323DCmdOffset); |
| |
| LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) |
| { |
| eError2 = RGXScheduleCommand(psRenderContext->psDeviceNode->pvDevice, |
| RGXFWIF_DM_3D, |
| &s3DKCCBCmd, |
| sizeof(s3DKCCBCmd), |
| ui32ClientCacheOpSeqNum, |
| ui32PDumpFlags); |
| if (eError2 != PVRSRV_ERROR_RETRY) |
| { |
| break; |
| } |
| OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT); |
| } END_LOOP_UNTIL_TIMEOUT(); |
| } |
| |
| /* |
| * Now check eError (which may have returned an error from our earlier calls |
| * to RGXCmdHelperAcquireCmdCCB) - we needed to process any flush command first |
| * so we check it now... |
| */ |
| if (eError != PVRSRV_OK ) |
| { |
| goto fail_3dacquirecmd; |
| } |
| |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| if (i32UpdateTimelineFD >= 0) |
| { |
| /* If we get here, this should never fail. Hitting that likely implies |
| * a code error above */ |
| i32UpdateFenceFD = pvr_sync_get_update_fd(psFDData); |
| if (i32UpdateFenceFD < 0) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to get install update sync fd", |
| __FUNCTION__)); |
| /* If we fail here, we cannot rollback the syncs as the hw already |
| * has references to resources they may be protecting in the kick |
| * so fallthrough */ |
| |
| eError = PVRSRV_ERROR_INVALID_PARAMS; |
| goto fail_3dacquirecmd; |
| } |
| } |
| #if defined(NO_HARDWARE) |
| pvr_sync_nohw_complete_fences(psFDData); |
| #endif |
| pvr_sync_free_append_fences_data(psFDData); |
| |
| #endif |
| |
| #if defined(SUPPORT_BUFFER_SYNC) |
| if (psAppendData) |
| { |
| pvr_buffer_sync_append_finish(psAppendData); |
| } |
| #endif |
| |
| *pi32UpdateFenceFD = i32UpdateFenceFD; |
| |
| return PVRSRV_OK; |
| |
| fail_3dacquirecmd: |
| fail_3dcmdinit: |
| fail_prcmdinit: |
| fail_prfencecmdinit: |
| fail_taacquirecmd: |
| fail_tacmdinit: |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| pvr_sync_rollback_append_fences(psFDData); |
| pvr_sync_free_append_fences_data(psFDData); |
| fail_fdsync: |
| #endif |
| #if defined(SUPPORT_BUFFER_SYNC) |
| pvr_buffer_sync_append_abort(psAppendData); |
| fail_sync_append: |
| #endif |
| err_pr_fence_address: |
| err_populate_sync_addr_list: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| PVRSRV_ERROR PVRSRVRGXSetRenderContextPriorityKM(CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE * psDeviceNode, |
| RGX_SERVER_RENDER_CONTEXT *psRenderContext, |
| IMG_UINT32 ui32Priority) |
| { |
| PVRSRV_ERROR eError; |
| |
| PVR_UNREFERENCED_PARAMETER(psDeviceNode); |
| |
| if (psRenderContext->sTAData.ui32Priority != ui32Priority) |
| { |
| eError = ContextSetPriority(psRenderContext->sTAData.psServerCommonContext, |
| psConnection, |
| psRenderContext->psDeviceNode->pvDevice, |
| ui32Priority, |
| RGXFWIF_DM_TA); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to set the priority of the TA part of the rendercontext (%s)", __FUNCTION__, PVRSRVGetErrorStringKM(eError))); |
| goto fail_tacontext; |
| } |
| psRenderContext->sTAData.ui32Priority = ui32Priority; |
| } |
| |
| if (psRenderContext->s3DData.ui32Priority != ui32Priority) |
| { |
| eError = ContextSetPriority(psRenderContext->s3DData.psServerCommonContext, |
| psConnection, |
| psRenderContext->psDeviceNode->pvDevice, |
| ui32Priority, |
| RGXFWIF_DM_3D); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to set the priority of the 3D part of the rendercontext (%s)", __FUNCTION__, PVRSRVGetErrorStringKM(eError))); |
| goto fail_3dcontext; |
| } |
| psRenderContext->s3DData.ui32Priority = ui32Priority; |
| } |
| return PVRSRV_OK; |
| |
| fail_3dcontext: |
| fail_tacontext: |
| PVR_ASSERT(eError != PVRSRV_OK); |
| return eError; |
| } |
| |
| |
| /* |
| * PVRSRVRGXGetLastRenderContextResetReasonKM |
| */ |
| PVRSRV_ERROR PVRSRVRGXGetLastRenderContextResetReasonKM(RGX_SERVER_RENDER_CONTEXT *psRenderContext, |
| IMG_UINT32 *peLastResetReason, |
| IMG_UINT32 *pui32LastResetJobRef) |
| { |
| RGX_SERVER_RC_TA_DATA *psRenderCtxTAData; |
| RGX_SERVER_RC_3D_DATA *psRenderCtx3DData; |
| RGX_SERVER_COMMON_CONTEXT *psCurrentServerTACommonCtx, *psCurrentServer3DCommonCtx; |
| RGXFWIF_CONTEXT_RESET_REASON eLastTAResetReason, eLast3DResetReason; |
| IMG_UINT32 ui32LastTAResetJobRef, ui32Last3DResetJobRef; |
| |
| PVR_ASSERT(psRenderContext != NULL); |
| PVR_ASSERT(peLastResetReason != NULL); |
| PVR_ASSERT(pui32LastResetJobRef != NULL); |
| |
| psRenderCtxTAData = &(psRenderContext->sTAData); |
| psCurrentServerTACommonCtx = psRenderCtxTAData->psServerCommonContext; |
| psRenderCtx3DData = &(psRenderContext->s3DData); |
| psCurrentServer3DCommonCtx = psRenderCtx3DData->psServerCommonContext; |
| |
| /* Get the last reset reasons from both the TA and 3D so they are reset... */ |
| eLastTAResetReason = FWCommonContextGetLastResetReason(psCurrentServerTACommonCtx, &ui32LastTAResetJobRef); |
| eLast3DResetReason = FWCommonContextGetLastResetReason(psCurrentServer3DCommonCtx, &ui32Last3DResetJobRef); |
| |
| /* Combine the reset reason from TA and 3D into one... */ |
| *peLastResetReason = (IMG_UINT32) eLast3DResetReason; |
| *pui32LastResetJobRef = ui32Last3DResetJobRef; |
| if (eLast3DResetReason == RGXFWIF_CONTEXT_RESET_REASON_NONE || |
| ((eLast3DResetReason == RGXFWIF_CONTEXT_RESET_REASON_INNOCENT_LOCKUP || |
| eLast3DResetReason == RGXFWIF_CONTEXT_RESET_REASON_INNOCENT_OVERRUNING) && |
| (eLastTAResetReason == RGXFWIF_CONTEXT_RESET_REASON_GUILTY_LOCKUP || |
| eLastTAResetReason == RGXFWIF_CONTEXT_RESET_REASON_GUILTY_OVERRUNING))) |
| { |
| *peLastResetReason = eLastTAResetReason; |
| *pui32LastResetJobRef = ui32LastTAResetJobRef; |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| /* |
| * PVRSRVRGXGetPartialRenderCountKM |
| */ |
| PVRSRV_ERROR PVRSRVRGXGetPartialRenderCountKM(DEVMEM_MEMDESC *psHWRTDataMemDesc, |
| IMG_UINT32 *pui32NumPartialRenders) |
| { |
| RGXFWIF_HWRTDATA *psHWRTData; |
| PVRSRV_ERROR eError; |
| |
| eError = DevmemAcquireCpuVirtAddr(psHWRTDataMemDesc, (void **)&psHWRTData); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "PVRSRVRGXGetPartialRenderCountKM: Failed to map Firmware Render Target Data (%u)", eError)); |
| return eError; |
| } |
| |
| *pui32NumPartialRenders = psHWRTData->ui32NumPartialRenders; |
| |
| DevmemReleaseCpuVirtAddr(psHWRTDataMemDesc); |
| |
| return PVRSRV_OK; |
| } |
| |
| void CheckForStalledRenderCtxt(PVRSRV_RGXDEV_INFO *psDevInfo, |
| DUMPDEBUG_PRINTF_FUNC *pfnDumpDebugPrintf, |
| void *pvDumpDebugFile) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| OSWRLockAcquireRead(psDevInfo->hRenderCtxListLock); |
| dllist_foreach_node(&psDevInfo->sRenderCtxtListHead, psNode, psNext) |
| { |
| RGX_SERVER_RENDER_CONTEXT *psCurrentServerRenderCtx = |
| IMG_CONTAINER_OF(psNode, RGX_SERVER_RENDER_CONTEXT, sListNode); |
| |
| DumpStalledFWCommonContext(psCurrentServerRenderCtx->sTAData.psServerCommonContext, |
| pfnDumpDebugPrintf, pvDumpDebugFile); |
| DumpStalledFWCommonContext(psCurrentServerRenderCtx->s3DData.psServerCommonContext, |
| pfnDumpDebugPrintf, pvDumpDebugFile); |
| } |
| OSWRLockReleaseRead(psDevInfo->hRenderCtxListLock); |
| } |
| |
| IMG_UINT32 CheckForStalledClientRenderCtxt(PVRSRV_RGXDEV_INFO *psDevInfo) |
| { |
| DLLIST_NODE *psNode, *psNext; |
| IMG_UINT32 ui32ContextBitMask = 0; |
| |
| OSWRLockAcquireRead(psDevInfo->hRenderCtxListLock); |
| |
| dllist_foreach_node(&psDevInfo->sRenderCtxtListHead, psNode, psNext) |
| { |
| RGX_SERVER_RENDER_CONTEXT *psCurrentServerRenderCtx = |
| IMG_CONTAINER_OF(psNode, RGX_SERVER_RENDER_CONTEXT, sListNode); |
| if(NULL != psCurrentServerRenderCtx->sTAData.psServerCommonContext) |
| { |
| if (CheckStalledClientCommonContext(psCurrentServerRenderCtx->sTAData.psServerCommonContext, RGX_KICK_TYPE_DM_TA) == PVRSRV_ERROR_CCCB_STALLED) |
| { |
| ui32ContextBitMask |= RGX_KICK_TYPE_DM_TA; |
| } |
| } |
| |
| if(NULL != psCurrentServerRenderCtx->s3DData.psServerCommonContext) |
| { |
| if (CheckStalledClientCommonContext(psCurrentServerRenderCtx->s3DData.psServerCommonContext, RGX_KICK_TYPE_DM_3D) == PVRSRV_ERROR_CCCB_STALLED) |
| { |
| ui32ContextBitMask |= RGX_KICK_TYPE_DM_3D; |
| } |
| } |
| } |
| |
| OSWRLockReleaseRead(psDevInfo->hRenderCtxListLock); |
| return ui32ContextBitMask; |
| } |
| |
| /****************************************************************************** |
| End of file (rgxta3d.c) |
| ******************************************************************************/ |