/*************************************************************************/ /*! @Title Device specific utility routines @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved @Description Device specific functions @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. */ /**************************************************************************/ #include #include "sgxdefs.h" #include "services_headers.h" #include "buffer_manager.h" #include "sgx_bridge_km.h" #include "sgxapi_km.h" #include "sgxinfo.h" #include "sgx_mkif_km.h" #include "sysconfig.h" #include "pdump_km.h" #include "mmu.h" #include "pvr_bridge_km.h" #include "osfunc.h" #include "pvr_debug.h" #include "sgxutils.h" #include "ttrace.h" #ifdef __linux__ #include // sprintf #include // strncpy, strlen #else #include #endif IMG_UINT64 ui64KickCount; #if defined(SYS_CUSTOM_POWERDOWN) PVRSRV_ERROR SysPowerDownMISR(PVRSRV_DEVICE_NODE * psDeviceNode, IMG_UINT32 ui32CallerID); #endif /*! ****************************************************************************** @Function SGXPostActivePowerEvent @Description post power event functionality (e.g. restart) @Input psDeviceNode : SGX Device Node @Input ui32CallerID - KERNEL_ID or ISR_ID @Return IMG_VOID : ******************************************************************************/ static IMG_VOID SGXPostActivePowerEvent(PVRSRV_DEVICE_NODE * psDeviceNode, IMG_UINT32 ui32CallerID) { PVRSRV_SGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; SGXMKIF_HOST_CTL *psSGXHostCtl = psDevInfo->psSGXHostCtl; /* Update the counter for stats. */ psSGXHostCtl->ui32NumActivePowerEvents++; if ((psSGXHostCtl->ui32PowerStatus & PVRSRV_USSE_EDM_POWMAN_POWEROFF_RESTART_IMMEDIATE) != 0) { PVR_DPF((PVR_DBG_MESSAGE, "SGXPostActivePowerEvent: SGX requests immediate restart")); /* Events were queued during the active power request, so SGX will need to be restarted. */ if (ui32CallerID == ISR_ID) { psDeviceNode->bReProcessDeviceCommandComplete = IMG_TRUE; } else { SGXScheduleProcessQueuesKM(psDeviceNode); } } } /*! ****************************************************************************** @Function SGXTestActivePowerEvent @Description Checks whether the microkernel has generated an active power event. If so, perform the power transition. @Input psDeviceNode : SGX Device Node @Input ui32CallerID - KERNEL_ID or ISR_ID @Return IMG_VOID : ******************************************************************************/ IMG_VOID SGXTestActivePowerEvent (PVRSRV_DEVICE_NODE *psDeviceNode, IMG_UINT32 ui32CallerID) { PVRSRV_ERROR eError = PVRSRV_OK; PVRSRV_SGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; SGXMKIF_HOST_CTL *psSGXHostCtl = psDevInfo->psSGXHostCtl; /* * Quickly check (without lock) if there is an IDLE or APM event we should handle. * This check fails most of the time so we don't want to incur lock overhead. * Check the flags in the reverse order that microkernel clears them to prevent * us from seeing an inconsistent state. */ if ((((psSGXHostCtl->ui32InterruptClearFlags & PVRSRV_USSE_EDM_INTERRUPT_IDLE) == 0) && ((psSGXHostCtl->ui32InterruptFlags & PVRSRV_USSE_EDM_INTERRUPT_IDLE) != 0)) || (((psSGXHostCtl->ui32InterruptClearFlags & PVRSRV_USSE_EDM_INTERRUPT_ACTIVE_POWER) == 0) && ((psSGXHostCtl->ui32InterruptFlags & PVRSRV_USSE_EDM_INTERRUPT_ACTIVE_POWER) != 0))) { eError = PVRSRVPowerLock(ui32CallerID, IMG_FALSE); if (eError == PVRSRV_ERROR_RETRY) { return; } else if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR,"SGXTestActivePowerEvent failed to acquire lock - " "ui32CallerID:%d eError:%u", ui32CallerID, eError)); return; } /* * Check again (with lock) if IDLE event has been cleared or handled. A race * condition may allow multiple threads to pass the quick check. */ if(((psSGXHostCtl->ui32InterruptClearFlags & PVRSRV_USSE_EDM_INTERRUPT_IDLE) == 0) && ((psSGXHostCtl->ui32InterruptFlags & PVRSRV_USSE_EDM_INTERRUPT_IDLE) != 0)) { psSGXHostCtl->ui32InterruptClearFlags |= PVRSRV_USSE_EDM_INTERRUPT_IDLE; if (psDevInfo->bSGXIdle == IMG_FALSE) { psDevInfo->bSGXIdle = IMG_TRUE; SysSGXIdleEntered(); } } /* * Check again (with lock) if APM event has been cleared or handled. A race * condition may allow multiple threads to pass the quick check. */ if (((psSGXHostCtl->ui32InterruptClearFlags & PVRSRV_USSE_EDM_INTERRUPT_ACTIVE_POWER) == 0) && ((psSGXHostCtl->ui32InterruptFlags & PVRSRV_USSE_EDM_INTERRUPT_ACTIVE_POWER) != 0)) { /* Microkernel is idle and is requesting to be powered down. */ psSGXHostCtl->ui32InterruptClearFlags |= PVRSRV_USSE_EDM_INTERRUPT_ACTIVE_POWER; /* Suspend pdumping. */ PDUMPSUSPEND(); #if defined(SYS_CUSTOM_POWERDOWN) /* Some power down code cannot be executed inside an MISR on some platforms that use mutexes inside the power code. */ eError = SysPowerDownMISR(psDeviceNode, ui32CallerID); #else eError = PVRSRVSetDevicePowerStateKM(psDeviceNode->sDevId.ui32DeviceIndex, PVRSRV_DEV_POWER_STATE_OFF); #endif if (eError == PVRSRV_OK) { SGXPostActivePowerEvent(psDeviceNode, ui32CallerID); } /* Resume pdumping */ PDUMPRESUME(); } PVRSRVPowerUnlock(ui32CallerID); } if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXTestActivePowerEvent error:%u", eError)); } } /****************************************************************************** FUNCTION : SGXAcquireKernelCCBSlot PURPOSE : Attempts to obtain a slot in the Kernel CCB PARAMETERS : psCCB - the CCB RETURNS : Address of space if available, IMG_NULL otherwise ******************************************************************************/ #ifdef INLINE_IS_PRAGMA #pragma inline(SGXAcquireKernelCCBSlot) #endif static INLINE SGXMKIF_COMMAND * SGXAcquireKernelCCBSlot(PVRSRV_SGX_CCB_INFO *psCCB) { LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) { if(((*psCCB->pui32WriteOffset + 1) & 255) != *psCCB->pui32ReadOffset) { return &psCCB->psCommands[*psCCB->pui32WriteOffset]; } OSSleepms(1); } END_LOOP_UNTIL_TIMEOUT(); /* Time out on waiting for CCB space */ return IMG_NULL; } /*! ****************************************************************************** @Function SGXScheduleCCBCommand @Description - Submits a CCB command and kicks the ukernel (without power management) @Input psDevInfo - pointer to device info @Input eCmdType - see SGXMKIF_CMD_* @Input psCommandData - kernel CCB command @Input ui32CallerID - KERNEL_ID or ISR_ID @Input ui32PDumpFlags @Return ui32Error - success or failure ******************************************************************************/ PVRSRV_ERROR SGXScheduleCCBCommand(PVRSRV_DEVICE_NODE *psDeviceNode, SGXMKIF_CMD_TYPE eCmdType, SGXMKIF_COMMAND *psCommandData, IMG_UINT32 ui32CallerID, IMG_UINT32 ui32PDumpFlags, IMG_HANDLE hDevMemContext, IMG_BOOL bLastInScene) { PVRSRV_SGX_CCB_INFO *psKernelCCB; PVRSRV_ERROR eError = PVRSRV_OK; SGXMKIF_COMMAND *psSGXCommand; PVRSRV_SGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; SGXMKIF_HOST_CTL *psSGXHostCtl = psDevInfo->psSGXHostCtl; #if defined(FIX_HW_BRN_31620) IMG_UINT32 ui32CacheMasks[4]; IMG_UINT32 i; MMU_CONTEXT *psMMUContext; #endif #if defined(PDUMP) IMG_VOID *pvDumpCommand; IMG_BOOL bPDumpIsSuspended = PDumpIsSuspended(); IMG_BOOL bPersistentProcess = IMG_FALSE; #else PVR_UNREFERENCED_PARAMETER(ui32CallerID); PVR_UNREFERENCED_PARAMETER(ui32PDumpFlags); #endif #if defined(FIX_HW_BRN_31620) for(i=0;i<4;i++) { ui32CacheMasks[i] = 0; } psMMUContext = psDevInfo->hKernelMMUContext; psDeviceNode->pfnMMUGetCacheFlushRange(psMMUContext, &ui32CacheMasks[0]); /* Put the apps memory context in the bottom half */ if (hDevMemContext) { BM_CONTEXT *psBMContext = (BM_CONTEXT *) hDevMemContext; psMMUContext = psBMContext->psMMUContext; psDeviceNode->pfnMMUGetCacheFlushRange(psMMUContext, &ui32CacheMasks[2]); } /* If we have an outstanding flush request then set the cachecontrol bit */ if (ui32CacheMasks[0] || ui32CacheMasks[1] || ui32CacheMasks[2] || ui32CacheMasks[3]) { psDevInfo->ui32CacheControl |= SGXMKIF_CC_INVAL_BIF_PD; } #endif #if defined(FIX_HW_BRN_28889) /* If the data cache and bif cache need invalidating there has been a cleanup request. Therefore, we need to send the invalidate seperately and wait for it to complete. */ if ( (eCmdType != SGXMKIF_CMD_PROCESS_QUEUES) && ((psDevInfo->ui32CacheControl & SGXMKIF_CC_INVAL_DATA) != 0) && ((psDevInfo->ui32CacheControl & (SGXMKIF_CC_INVAL_BIF_PT | SGXMKIF_CC_INVAL_BIF_PD)) != 0)) { #if defined(PDUMP) PVRSRV_KERNEL_MEM_INFO *psSGXHostCtlMemInfo = psDevInfo->psKernelSGXHostCtlMemInfo; #endif SGXMKIF_HOST_CTL *psSGXHostCtl = psDevInfo->psSGXHostCtl; SGXMKIF_COMMAND sCacheCommand = {0}; eError = SGXScheduleCCBCommand(psDeviceNode, SGXMKIF_CMD_PROCESS_QUEUES, &sCacheCommand, ui32CallerID, ui32PDumpFlags, hDevMemContext, bLastInScene); if (eError != PVRSRV_OK) { goto Exit; } /* Wait for the invalidate to happen */ #if !defined(NO_HARDWARE) if(PollForValueKM(&psSGXHostCtl->ui32InvalStatus, PVRSRV_USSE_EDM_BIF_INVAL_COMPLETE, PVRSRV_USSE_EDM_BIF_INVAL_COMPLETE, 2 * MAX_HW_TIME_US, MAX_HW_TIME_US/WAIT_TRY_COUNT, IMG_FALSE) != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR,"SGXScheduleCCBCommand: Wait for uKernel to Invalidate BIF cache failed")); PVR_DBG_BREAK; } #endif #if defined(PDUMP) /* Pdump the poll as well. */ PDUMPCOMMENTWITHFLAGS(0, "Host Control - Poll for BIF cache invalidate request to complete"); PDUMPMEMPOL(psSGXHostCtlMemInfo, offsetof(SGXMKIF_HOST_CTL, ui32InvalStatus), PVRSRV_USSE_EDM_BIF_INVAL_COMPLETE, PVRSRV_USSE_EDM_BIF_INVAL_COMPLETE, PDUMP_POLL_OPERATOR_EQUAL, 0, MAKEUNIQUETAG(psSGXHostCtlMemInfo)); #endif /* PDUMP */ psSGXHostCtl->ui32InvalStatus &= ~(PVRSRV_USSE_EDM_BIF_INVAL_COMPLETE); PDUMPMEM(IMG_NULL, psSGXHostCtlMemInfo, offsetof(SGXMKIF_HOST_CTL, ui32CleanupStatus), sizeof(IMG_UINT32), 0, MAKEUNIQUETAG(psSGXHostCtlMemInfo)); } #else PVR_UNREFERENCED_PARAMETER(hDevMemContext); #endif #if defined(FIX_HW_BRN_31620) if ((eCmdType != SGXMKIF_CMD_FLUSHPDCACHE) && (psDevInfo->ui32CacheControl & SGXMKIF_CC_INVAL_BIF_PD)) { SGXMKIF_COMMAND sPDECacheCommand = {0}; IMG_DEV_PHYADDR sDevPAddr; /* Put the kernel info in the top 1/2 of the data */ psMMUContext = psDevInfo->hKernelMMUContext; psDeviceNode->pfnMMUGetPDPhysAddr(psMMUContext, &sDevPAddr); sPDECacheCommand.ui32Data[0] = sDevPAddr.uiAddr | 1; sPDECacheCommand.ui32Data[1] = ui32CacheMasks[0]; sPDECacheCommand.ui32Data[2] = ui32CacheMasks[1]; /* Put the apps memory context in the bottom half */ if (hDevMemContext) { BM_CONTEXT *psBMContext = (BM_CONTEXT *) hDevMemContext; psMMUContext = psBMContext->psMMUContext; psDeviceNode->pfnMMUGetPDPhysAddr(psMMUContext, &sDevPAddr); /* Or in 1 to the lsb to show we have a valid context */ sPDECacheCommand.ui32Data[3] = sDevPAddr.uiAddr | 1; sPDECacheCommand.ui32Data[4] = ui32CacheMasks[2]; sPDECacheCommand.ui32Data[5] = ui32CacheMasks[3]; } /* Only do a kick if there is any update */ if (sPDECacheCommand.ui32Data[1] | sPDECacheCommand.ui32Data[2] | sPDECacheCommand.ui32Data[4] | sPDECacheCommand.ui32Data[5]) { eError = SGXScheduleCCBCommand(psDeviceNode, SGXMKIF_CMD_FLUSHPDCACHE, &sPDECacheCommand, ui32CallerID, ui32PDumpFlags, hDevMemContext, bLastInScene); if (eError != PVRSRV_OK) { goto Exit; } } } #endif #if defined(PDUMP) /* * For persistent processes, the HW kicks should not go into the * extended init phase; only keep memory transactions from the * window system which are necessary to run the client app. */ { PVRSRV_PER_PROCESS_DATA* psPerProc = PVRSRVFindPerProcessData(); if(psPerProc != IMG_NULL) { bPersistentProcess = psPerProc->bPDumpPersistent; } } #endif /* PDUMP */ psKernelCCB = psDevInfo->psKernelCCBInfo; psSGXCommand = SGXAcquireKernelCCBSlot(psKernelCCB); /* Wait for CCB space timed out */ if(!psSGXCommand) { PVR_DPF((PVR_DBG_ERROR, "SGXScheduleCCBCommand: Wait for CCB space timed out")) ; eError = PVRSRV_ERROR_TIMEOUT; goto Exit; } /* embed cache control word */ psCommandData->ui32CacheControl = psDevInfo->ui32CacheControl; #if defined(PDUMP) /* Accumulate any cache invalidates that may have happened */ psDevInfo->sPDContext.ui32CacheControl |= psDevInfo->ui32CacheControl; #endif /* and clear it */ psDevInfo->ui32CacheControl = 0; /* Copy command data over */ *psSGXCommand = *psCommandData; if (eCmdType >= SGXMKIF_CMD_MAX) { PVR_DPF((PVR_DBG_ERROR, "SGXScheduleCCBCommand: Unknown command type: %d", eCmdType)) ; eError = PVRSRV_ERROR_INVALID_CCB_COMMAND; goto Exit; } if (eCmdType == SGXMKIF_CMD_2D || eCmdType == SGXMKIF_CMD_TRANSFER || ((eCmdType == SGXMKIF_CMD_TA) && bLastInScene)) { SYS_DATA *psSysData; /* CPU cache clean control */ SysAcquireData(&psSysData); if(psSysData->ePendingCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_FLUSH) { OSFlushCPUCacheKM(); } else if(psSysData->ePendingCacheOpType == PVRSRV_MISC_INFO_CPUCACHEOP_CLEAN) { OSCleanCPUCacheKM(); } /* Clear the pending op */ psSysData->ePendingCacheOpType = PVRSRV_MISC_INFO_CPUCACHEOP_NONE; } PVR_ASSERT(eCmdType < SGXMKIF_CMD_MAX); psSGXCommand->ui32ServiceAddress = psDevInfo->aui32HostKickAddr[eCmdType]; /* PRQA S 3689 */ /* misuse of enums for bounds checking */ #if defined(PDUMP) if ((ui32CallerID != ISR_ID) && (bPDumpIsSuspended == IMG_FALSE) && (bPersistentProcess == IMG_FALSE) ) { /* Poll for space in the CCB. */ PDUMPCOMMENTWITHFLAGS(ui32PDumpFlags, "Poll for space in the Kernel CCB\r\n"); PDUMPMEMPOL(psKernelCCB->psCCBCtlMemInfo, offsetof(PVRSRV_SGX_CCB_CTL, ui32ReadOffset), (psKernelCCB->ui32CCBDumpWOff + 1) & 0xff, 0xff, PDUMP_POLL_OPERATOR_NOTEQUAL, ui32PDumpFlags, MAKEUNIQUETAG(psKernelCCB->psCCBCtlMemInfo)); PDUMPCOMMENTWITHFLAGS(ui32PDumpFlags, "Kernel CCB command (type == %d)\r\n", eCmdType); pvDumpCommand = (IMG_VOID *)((IMG_UINT8 *)psKernelCCB->psCCBMemInfo->pvLinAddrKM + (*psKernelCCB->pui32WriteOffset * sizeof(SGXMKIF_COMMAND))); PDUMPMEM(pvDumpCommand, psKernelCCB->psCCBMemInfo, psKernelCCB->ui32CCBDumpWOff * sizeof(SGXMKIF_COMMAND), sizeof(SGXMKIF_COMMAND), ui32PDumpFlags, MAKEUNIQUETAG(psKernelCCB->psCCBMemInfo)); /* Overwrite cache control with pdump shadow */ PDUMPMEM(&psDevInfo->sPDContext.ui32CacheControl, psKernelCCB->psCCBMemInfo, psKernelCCB->ui32CCBDumpWOff * sizeof(SGXMKIF_COMMAND) + offsetof(SGXMKIF_COMMAND, ui32CacheControl), sizeof(IMG_UINT32), ui32PDumpFlags, MAKEUNIQUETAG(psKernelCCB->psCCBMemInfo)); if (PDumpIsCaptureFrameKM() || ((ui32PDumpFlags & PDUMP_FLAGS_CONTINUOUS) != 0)) { /* Clear cache invalidate shadow */ psDevInfo->sPDContext.ui32CacheControl = 0; } } #endif #if defined(FIX_HW_BRN_26620) && defined(SGX_FEATURE_SYSTEM_CACHE) && !defined(SGX_BYPASS_SYSTEM_CACHE) /* Make sure the previous command has been read before send the next one */ eError = PollForValueKM (psKernelCCB->pui32ReadOffset, *psKernelCCB->pui32WriteOffset, 0xFF, MAX_HW_TIME_US, MAX_HW_TIME_US/WAIT_TRY_COUNT, IMG_FALSE); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXScheduleCCBCommand: Timeout waiting for previous command to be read")) ; eError = PVRSRV_ERROR_TIMEOUT; goto Exit; } #endif /* Increment the write offset */ *psKernelCCB->pui32WriteOffset = (*psKernelCCB->pui32WriteOffset + 1) & 255; #if defined(PDUMP) if ((ui32CallerID != ISR_ID) && (bPDumpIsSuspended == IMG_FALSE) && (bPersistentProcess == IMG_FALSE) ) { #if defined(FIX_HW_BRN_26620) && defined(SGX_FEATURE_SYSTEM_CACHE) && !defined(SGX_BYPASS_SYSTEM_CACHE) PDUMPCOMMENTWITHFLAGS(ui32PDumpFlags, "Poll for previous Kernel CCB CMD to be read\r\n"); PDUMPMEMPOL(psKernelCCB->psCCBCtlMemInfo, offsetof(PVRSRV_SGX_CCB_CTL, ui32ReadOffset), (psKernelCCB->ui32CCBDumpWOff), 0xFF, PDUMP_POLL_OPERATOR_EQUAL, ui32PDumpFlags, MAKEUNIQUETAG(psKernelCCB->psCCBCtlMemInfo)); #endif if (PDumpIsCaptureFrameKM() || ((ui32PDumpFlags & PDUMP_FLAGS_CONTINUOUS) != 0)) { psKernelCCB->ui32CCBDumpWOff = (psKernelCCB->ui32CCBDumpWOff + 1) & 0xFF; psDevInfo->ui32KernelCCBEventKickerDumpVal = (psDevInfo->ui32KernelCCBEventKickerDumpVal + 1) & 0xFF; } PDUMPCOMMENTWITHFLAGS(ui32PDumpFlags, "Kernel CCB write offset\r\n"); PDUMPMEM(&psKernelCCB->ui32CCBDumpWOff, psKernelCCB->psCCBCtlMemInfo, offsetof(PVRSRV_SGX_CCB_CTL, ui32WriteOffset), sizeof(IMG_UINT32), ui32PDumpFlags, MAKEUNIQUETAG(psKernelCCB->psCCBCtlMemInfo)); PDUMPCOMMENTWITHFLAGS(ui32PDumpFlags, "Kernel CCB event kicker\r\n"); PDUMPMEM(&psDevInfo->ui32KernelCCBEventKickerDumpVal, psDevInfo->psKernelCCBEventKickerMemInfo, 0, sizeof(IMG_UINT32), ui32PDumpFlags, MAKEUNIQUETAG(psDevInfo->psKernelCCBEventKickerMemInfo)); PDUMPCOMMENTWITHFLAGS(ui32PDumpFlags, "Kick the SGX microkernel\r\n"); #if defined(FIX_HW_BRN_26620) && defined(SGX_FEATURE_SYSTEM_CACHE) && !defined(SGX_BYPASS_SYSTEM_CACHE) PDUMPREGWITHFLAGS(SGX_PDUMPREG_NAME, SGX_MP_CORE_SELECT(EUR_CR_EVENT_KICK2, 0), EUR_CR_EVENT_KICK2_NOW_MASK, ui32PDumpFlags); #else PDUMPREGWITHFLAGS(SGX_PDUMPREG_NAME, SGX_MP_CORE_SELECT(EUR_CR_EVENT_KICK, 0), EUR_CR_EVENT_KICK_NOW_MASK, ui32PDumpFlags); #endif } #endif *psDevInfo->pui32KernelCCBEventKicker = (*psDevInfo->pui32KernelCCBEventKicker + 1) & 0xFF; /* * New command submission is considered a proper handling of any pending * IDLE or APM event, so mark them as handled to prevent other host threads * from taking action. */ psSGXHostCtl->ui32InterruptClearFlags |= PVRSRV_USSE_EDM_INTERRUPT_IDLE; psSGXHostCtl->ui32InterruptClearFlags |= PVRSRV_USSE_EDM_INTERRUPT_ACTIVE_POWER; OSWriteMemoryBarrier(); /* Order is importent for post processor! */ PVR_TTRACE_UI32(PVRSRV_TRACE_GROUP_MKSYNC, PVRSRV_TRACE_CLASS_NONE, MKSYNC_TOKEN_KERNEL_CCB_OFFSET, *psKernelCCB->pui32WriteOffset); PVR_TTRACE_UI32(PVRSRV_TRACE_GROUP_MKSYNC, PVRSRV_TRACE_CLASS_NONE, MKSYNC_TOKEN_CORE_CLK, psDevInfo->ui32CoreClockSpeed); PVR_TTRACE_UI32(PVRSRV_TRACE_GROUP_MKSYNC, PVRSRV_TRACE_CLASS_NONE, MKSYNC_TOKEN_UKERNEL_CLK, psDevInfo->ui32uKernelTimerClock); #if defined(FIX_HW_BRN_26620) && defined(SGX_FEATURE_SYSTEM_CACHE) && !defined(SGX_BYPASS_SYSTEM_CACHE) OSWriteHWReg(psDevInfo->pvRegsBaseKM, SGX_MP_CORE_SELECT(EUR_CR_EVENT_KICK2, 0), EUR_CR_EVENT_KICK2_NOW_MASK); #else OSWriteHWReg(psDevInfo->pvRegsBaseKM, SGX_MP_CORE_SELECT(EUR_CR_EVENT_KICK, 0), EUR_CR_EVENT_KICK_NOW_MASK); #endif OSMemoryBarrier(); #if defined(NO_HARDWARE) /* Increment read offset */ *psKernelCCB->pui32ReadOffset = (*psKernelCCB->pui32ReadOffset + 1) & 255; #endif ui64KickCount++; Exit: return eError; } /*! ****************************************************************************** @Function SGXScheduleCCBCommandKM @Description - Submits a CCB command and kicks the ukernel @Input psDeviceNode - pointer to SGX device node @Input eCmdType - see SGXMKIF_CMD_* @Input psCommandData - kernel CCB command @Input ui32CallerID - KERNEL_ID or ISR_ID @Input ui32PDumpFlags @Return ui32Error - success or failure ******************************************************************************/ PVRSRV_ERROR SGXScheduleCCBCommandKM(PVRSRV_DEVICE_NODE *psDeviceNode, SGXMKIF_CMD_TYPE eCmdType, SGXMKIF_COMMAND *psCommandData, IMG_UINT32 ui32CallerID, IMG_UINT32 ui32PDumpFlags, IMG_HANDLE hDevMemContext, IMG_BOOL bLastInScene) { PVRSRV_ERROR eError; PVRSRV_SGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; eError = PVRSRVPowerLock(ui32CallerID, IMG_FALSE); if (eError == PVRSRV_ERROR_RETRY) { if (ui32CallerID == ISR_ID) { SYS_DATA *psSysData; /* ISR failed to acquire lock so it must be held by a kernel thread. Bring up and kick SGX if necessary when the lock is available. */ psDeviceNode->bReProcessDeviceCommandComplete = IMG_TRUE; eError = PVRSRV_OK; SysAcquireData(&psSysData); OSScheduleMISR(psSysData); } else { /* Return to srvclient for retry. */ } return eError; } else if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR,"SGXScheduleCCBCommandKM failed to acquire lock - " "ui32CallerID:%d eError:%u", ui32CallerID, eError)); return eError; } /* Note that a power-up has been dumped in the init phase. */ PDUMPSUSPEND(); /* Ensure that SGX is powered up before kicking the ukernel. */ eError = PVRSRVSetDevicePowerStateKM(psDeviceNode->sDevId.ui32DeviceIndex, PVRSRV_DEV_POWER_STATE_ON); PDUMPRESUME(); if (eError == PVRSRV_OK) { psDeviceNode->bReProcessDeviceCommandComplete = IMG_FALSE; } else { PVR_DPF((PVR_DBG_ERROR,"SGXScheduleCCBCommandKM failed to acquire lock - " "ui32CallerID:%d eError:%u", ui32CallerID, eError)); return eError; } SysSGXCommandPending(psDevInfo->bSGXIdle); psDevInfo->bSGXIdle = IMG_FALSE; eError = SGXScheduleCCBCommand(psDeviceNode, eCmdType, psCommandData, ui32CallerID, ui32PDumpFlags, hDevMemContext, bLastInScene); PVRSRVPowerUnlock(ui32CallerID); return eError; } /*! ****************************************************************************** @Function SGXScheduleProcessQueuesKM @Description - Software command complete handler @Input psDeviceNode - SGX device node ******************************************************************************/ PVRSRV_ERROR SGXScheduleProcessQueuesKM(PVRSRV_DEVICE_NODE *psDeviceNode) { PVRSRV_ERROR eError; PVRSRV_SGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; SGXMKIF_HOST_CTL *psHostCtl = psDevInfo->psKernelSGXHostCtlMemInfo->pvLinAddrKM; IMG_UINT32 ui32PowerStatus; SGXMKIF_COMMAND sCommand = {0}; ui32PowerStatus = psHostCtl->ui32PowerStatus; if ((ui32PowerStatus & PVRSRV_USSE_EDM_POWMAN_NO_WORK) != 0) { /* The ukernel has no work to do so don't waste power. */ return PVRSRV_OK; } eError = SGXScheduleCCBCommandKM(psDeviceNode, SGXMKIF_CMD_PROCESS_QUEUES, &sCommand, ISR_ID, 0, IMG_NULL, IMG_FALSE); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR,"SGXScheduleProcessQueuesKM failed to schedule CCB command: %u", eError)); return eError; } return PVRSRV_OK; } /*! ****************************************************************************** @Function SGXIsDevicePowered @Description Whether the device is powered, for the purposes of lockup detection. @Input psDeviceNode - pointer to device node @Return IMG_BOOL : Whether device is powered ******************************************************************************/ IMG_BOOL SGXIsDevicePowered(PVRSRV_DEVICE_NODE *psDeviceNode) { return PVRSRVIsDevicePowered(psDeviceNode->sDevId.ui32DeviceIndex); } /*! ******************************************************************************* @Function SGXGetInternalDevInfoKM @Description Gets device information that is not intended to be passed on beyond the srvclient libs. @Input hDevCookie @Output psSGXInternalDevInfo @Return PVRSRV_ERROR : ******************************************************************************/ IMG_EXPORT PVRSRV_ERROR SGXGetInternalDevInfoKM(IMG_HANDLE hDevCookie, #if defined (SUPPORT_SID_INTERFACE) SGX_INTERNAL_DEVINFO_KM *psSGXInternalDevInfo) #else SGX_INTERNAL_DEVINFO *psSGXInternalDevInfo) #endif { PVRSRV_SGXDEV_INFO *psDevInfo = (PVRSRV_SGXDEV_INFO *)((PVRSRV_DEVICE_NODE *)hDevCookie)->pvDevice; psSGXInternalDevInfo->ui32Flags = psDevInfo->ui32Flags; psSGXInternalDevInfo->bForcePTOff = (IMG_BOOL)psDevInfo->bForcePTOff; /* This should be patched up by OS bridge code */ psSGXInternalDevInfo->hHostCtlKernelMemInfoHandle = (IMG_HANDLE)psDevInfo->psKernelSGXHostCtlMemInfo; return PVRSRV_OK; } /***************************************************************************** FUNCTION : SGXCleanupRequest PURPOSE : Wait for the microkernel to clean up its references to either a render context or render target. PARAMETERS : psDeviceNode - SGX device node psHWDataDevVAddr - Device Address of the resource ui32CleanupType - PVRSRV_CLEANUPCMD_* bForceCleanup - Skips sync polling RETURNS : error status *****************************************************************************/ PVRSRV_ERROR SGXCleanupRequest(PVRSRV_DEVICE_NODE *psDeviceNode, IMG_DEV_VIRTADDR *psHWDataDevVAddr, IMG_UINT32 ui32CleanupType, IMG_BOOL bForceCleanup) { PVRSRV_ERROR eError; PVRSRV_SGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; PVRSRV_KERNEL_MEM_INFO *psHostCtlMemInfo = psDevInfo->psKernelSGXHostCtlMemInfo; SGXMKIF_HOST_CTL *psHostCtl = psHostCtlMemInfo->pvLinAddrKM; SGXMKIF_COMMAND sCommand = {0}; if (bForceCleanup != FORCE_CLEANUP) { sCommand.ui32Data[0] = ui32CleanupType; sCommand.ui32Data[1] = (psHWDataDevVAddr == IMG_NULL) ? 0 : psHWDataDevVAddr->uiAddr; PDUMPCOMMENTWITHFLAGS(0, "Request ukernel resource clean-up, Type %u, Data 0x%X", sCommand.ui32Data[0], sCommand.ui32Data[1]); eError = SGXScheduleCCBCommandKM(psDeviceNode, SGXMKIF_CMD_CLEANUP, &sCommand, KERNEL_ID, 0, IMG_NULL, IMG_FALSE); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR,"SGXCleanupRequest: Failed to submit clean-up command")); SGXDumpDebugInfo(psDevInfo, IMG_FALSE); PVR_DBG_BREAK; return eError; } /* Wait for the uKernel process the cleanup request */ #if !defined(NO_HARDWARE) if(PollForValueKM(&psHostCtl->ui32CleanupStatus, PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE, PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE, 10 * MAX_HW_TIME_US, 1000, IMG_TRUE) != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR,"SGXCleanupRequest: Wait for uKernel to clean up (%u) failed", ui32CleanupType)); eError = PVRSRV_ERROR_TIMEOUT; SGXDumpDebugInfo(psDevInfo, IMG_FALSE); PVR_DBG_BREAK; } #endif #if defined(PDUMP) /* Pdump the poll as well. Note: We don't expect the cleanup to report busy as the client should have ensured the the resource has been finished with before requesting it's cleanup. This isn't true of the abnormal termination case but we don't expect to PDump that. Unless/until PDump has flow control there isn't anything else we can do. */ PDUMPCOMMENTWITHFLAGS(0, "Host Control - Poll for clean-up request to complete"); PDUMPMEMPOL(psHostCtlMemInfo, offsetof(SGXMKIF_HOST_CTL, ui32CleanupStatus), PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_DONE, PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_DONE, PDUMP_POLL_OPERATOR_EQUAL, 0, MAKEUNIQUETAG(psHostCtlMemInfo)); #endif /* PDUMP */ if (eError != PVRSRV_OK) { return eError; } } if (psHostCtl->ui32CleanupStatus & PVRSRV_USSE_EDM_CLEANUPCMD_BUSY) { /* Only one flag should be set */ PVR_ASSERT((psHostCtl->ui32CleanupStatus & PVRSRV_USSE_EDM_CLEANUPCMD_DONE) == 0); eError = PVRSRV_ERROR_RETRY; psHostCtl->ui32CleanupStatus &= ~(PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_BUSY); } else { eError = PVRSRV_OK; psHostCtl->ui32CleanupStatus &= ~(PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_DONE); } PDUMPMEM(IMG_NULL, psHostCtlMemInfo, offsetof(SGXMKIF_HOST_CTL, ui32CleanupStatus), sizeof(IMG_UINT32), 0, MAKEUNIQUETAG(psHostCtlMemInfo)); /* Request the cache invalidate */ #if defined(SGX_FEATURE_SYSTEM_CACHE) psDevInfo->ui32CacheControl |= (SGXMKIF_CC_INVAL_BIF_SL | SGXMKIF_CC_INVAL_DATA); #else psDevInfo->ui32CacheControl |= SGXMKIF_CC_INVAL_DATA; #endif return eError; } typedef struct _SGX_HW_RENDER_CONTEXT_CLEANUP_ { PVRSRV_DEVICE_NODE *psDeviceNode; PVRSRV_KERNEL_MEM_INFO *psHWRenderContextMemInfo; IMG_HANDLE hBlockAlloc; PRESMAN_ITEM psResItem; IMG_BOOL bCleanupTimerRunning; IMG_PVOID pvTimeData; } SGX_HW_RENDER_CONTEXT_CLEANUP; static PVRSRV_ERROR SGXCleanupHWRenderContextCallback(IMG_PVOID pvParam, IMG_UINT32 ui32Param, IMG_BOOL bForceCleanup) { PVRSRV_ERROR eError; SGX_HW_RENDER_CONTEXT_CLEANUP *psCleanup = pvParam; PVR_UNREFERENCED_PARAMETER(ui32Param); eError = SGXCleanupRequest(psCleanup->psDeviceNode, &psCleanup->psHWRenderContextMemInfo->sDevVAddr, PVRSRV_CLEANUPCMD_RC, bForceCleanup); if (eError == PVRSRV_ERROR_RETRY) { if (!psCleanup->bCleanupTimerRunning) { OSTimeCreateWithUSOffset(&psCleanup->pvTimeData, MAX_CLEANUP_TIME_US); psCleanup->bCleanupTimerRunning = IMG_TRUE; } else { if (OSTimeHasTimePassed(psCleanup->pvTimeData)) { eError = PVRSRV_ERROR_TIMEOUT_POLLING_FOR_VALUE; psCleanup->bCleanupTimerRunning = IMG_FALSE; OSTimeDestroy(psCleanup->pvTimeData); } } } else { if (psCleanup->bCleanupTimerRunning) { OSTimeDestroy(psCleanup->pvTimeData); } } if (eError != PVRSRV_ERROR_RETRY) { /* Free the Device Mem allocated */ PVRSRVFreeDeviceMemKM(psCleanup->psDeviceNode, psCleanup->psHWRenderContextMemInfo); /* Finally, free the cleanup structure itself */ OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_RENDER_CONTEXT_CLEANUP), psCleanup, psCleanup->hBlockAlloc); /*not nulling pointer, copy on stack*/ } return eError; } typedef struct _SGX_HW_TRANSFER_CONTEXT_CLEANUP_ { PVRSRV_DEVICE_NODE *psDeviceNode; PVRSRV_KERNEL_MEM_INFO *psHWTransferContextMemInfo; IMG_HANDLE hBlockAlloc; PRESMAN_ITEM psResItem; IMG_BOOL bCleanupTimerRunning; IMG_PVOID pvTimeData; } SGX_HW_TRANSFER_CONTEXT_CLEANUP; static PVRSRV_ERROR SGXCleanupHWTransferContextCallback(IMG_PVOID pvParam, IMG_UINT32 ui32Param, IMG_BOOL bForceCleanup) { PVRSRV_ERROR eError; SGX_HW_TRANSFER_CONTEXT_CLEANUP *psCleanup = (SGX_HW_TRANSFER_CONTEXT_CLEANUP *)pvParam; PVR_UNREFERENCED_PARAMETER(ui32Param); eError = SGXCleanupRequest(psCleanup->psDeviceNode, &psCleanup->psHWTransferContextMemInfo->sDevVAddr, PVRSRV_CLEANUPCMD_TC, bForceCleanup); if (eError == PVRSRV_ERROR_RETRY) { if (!psCleanup->bCleanupTimerRunning) { OSTimeCreateWithUSOffset(&psCleanup->pvTimeData, MAX_CLEANUP_TIME_US); psCleanup->bCleanupTimerRunning = IMG_TRUE; } else { if (OSTimeHasTimePassed(psCleanup->pvTimeData)) { eError = PVRSRV_ERROR_TIMEOUT_POLLING_FOR_VALUE; psCleanup->bCleanupTimerRunning = IMG_FALSE; OSTimeDestroy(psCleanup->pvTimeData); } } } else { if (psCleanup->bCleanupTimerRunning) { OSTimeDestroy(psCleanup->pvTimeData); } } if (eError != PVRSRV_ERROR_RETRY) { /* Free the Device Mem allocated */ PVRSRVFreeDeviceMemKM(psCleanup->psDeviceNode, psCleanup->psHWTransferContextMemInfo); /* Finally, free the cleanup structure itself */ OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_TRANSFER_CONTEXT_CLEANUP), psCleanup, psCleanup->hBlockAlloc); /*not nulling pointer, copy on stack*/ } return eError; } IMG_EXPORT IMG_HANDLE SGXRegisterHWRenderContextKM(IMG_HANDLE hDeviceNode, IMG_CPU_VIRTADDR *psHWRenderContextCpuVAddr, IMG_UINT32 ui32HWRenderContextSize, IMG_UINT32 ui32OffsetToPDDevPAddr, IMG_HANDLE hDevMemContext, IMG_DEV_VIRTADDR *psHWRenderContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc) { PVRSRV_ERROR eError; IMG_HANDLE hBlockAlloc; SGX_HW_RENDER_CONTEXT_CLEANUP *psCleanup; PVRSRV_DEVICE_NODE *psDeviceNode = (PVRSRV_DEVICE_NODE *)hDeviceNode; DEVICE_MEMORY_INFO *psDevMemoryInfo; DEVICE_MEMORY_HEAP_INFO *psHeapInfo; IMG_HANDLE hDevMemContextInt; MMU_CONTEXT *psMMUContext; IMG_DEV_PHYADDR sPDDevPAddr; int iPtrByte; IMG_UINT8 *pSrc; IMG_UINT8 *pDst; PRESMAN_ITEM psResItem; eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_RENDER_CONTEXT_CLEANUP), (IMG_VOID **)&psCleanup, &hBlockAlloc, "SGX Hardware Render Context Cleanup"); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Couldn't allocate memory for SGX_HW_RENDER_CONTEXT_CLEANUP structure")); goto exit0; } psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; psHeapInfo = &psDevMemoryInfo->psDeviceMemoryHeap[SGX_KERNEL_DATA_HEAP_ID]; eError = PVRSRVAllocDeviceMemKM(hDeviceNode, psPerProc, psHeapInfo->hDevMemHeap, PVRSRV_MEM_READ | PVRSRV_MEM_WRITE | PVRSRV_MEM_NO_SYNCOBJ | PVRSRV_MEM_EDM_PROTECT | PVRSRV_MEM_CACHE_CONSISTENT, ui32HWRenderContextSize, 32, IMG_NULL, 0, 0,0,0,IMG_NULL, /* No sparse mapping data */ &psCleanup->psHWRenderContextMemInfo, "HW Render Context"); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Couldn't allocate device memory for HW Render Context")); goto exit1; } eError = OSCopyFromUser(psPerProc, psCleanup->psHWRenderContextMemInfo->pvLinAddrKM, psHWRenderContextCpuVAddr, ui32HWRenderContextSize); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Couldn't copy user-mode copy of HWContext into device memory")); goto exit2; } /* Pass the DevVAddr of the new context back up through the bridge */ psHWRenderContextDevVAddr->uiAddr = psCleanup->psHWRenderContextMemInfo->sDevVAddr.uiAddr; /* Retrieve the PDDevPAddr */ eError = PVRSRVLookupHandle(psPerProc->psHandleBase, &hDevMemContextInt, hDevMemContext, PVRSRV_HANDLE_TYPE_DEV_MEM_CONTEXT); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Can't lookup DevMem Context")); goto exit2; } psMMUContext = BM_GetMMUContextFromMemContext(hDevMemContextInt); sPDDevPAddr = psDeviceNode->pfnMMUGetPDDevPAddr(psMMUContext); /* patch-in the Page-Directory Device-Physical address. Note that this is copied-in one byte at a time, as we have no guarantee that the usermode- provided ui32OffsetToPDDevPAddr is a validly-aligned address for the current CPU architecture. */ pSrc = (IMG_UINT8 *)&sPDDevPAddr; pDst = (IMG_UINT8 *)psCleanup->psHWRenderContextMemInfo->pvLinAddrKM; pDst += ui32OffsetToPDDevPAddr; for (iPtrByte = 0; iPtrByte < sizeof(IMG_DEV_PHYADDR); iPtrByte++) { pDst[iPtrByte] = pSrc[iPtrByte]; } #if defined(PDUMP) /* PDUMP the HW context */ PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "HW Render context struct"); PDUMPMEM( IMG_NULL, psCleanup->psHWRenderContextMemInfo, 0, ui32HWRenderContextSize, PDUMP_FLAGS_CONTINUOUS, MAKEUNIQUETAG(psCleanup->psHWRenderContextMemInfo)); /* PDUMP the PDDevPAddr */ PDUMPCOMMENT("Page directory address in HW render context"); PDUMPPDDEVPADDR( psCleanup->psHWRenderContextMemInfo, ui32OffsetToPDDevPAddr, sPDDevPAddr, MAKEUNIQUETAG(psCleanup->psHWRenderContextMemInfo), PDUMP_PD_UNIQUETAG); #endif psCleanup->hBlockAlloc = hBlockAlloc; psCleanup->psDeviceNode = psDeviceNode; psCleanup->bCleanupTimerRunning = IMG_FALSE; psResItem = ResManRegisterRes(psPerProc->hResManContext, RESMAN_TYPE_HW_RENDER_CONTEXT, (IMG_VOID *)psCleanup, 0, &SGXCleanupHWRenderContextCallback); if (psResItem == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: ResManRegisterRes failed")); goto exit2; } psCleanup->psResItem = psResItem; return (IMG_HANDLE)psCleanup; /* Error exit paths */ exit2: PVRSRVFreeDeviceMemKM(hDeviceNode, psCleanup->psHWRenderContextMemInfo); exit1: OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_RENDER_CONTEXT_CLEANUP), psCleanup, psCleanup->hBlockAlloc); /*not nulling pointer, out of scope*/ exit0: return IMG_NULL; } IMG_EXPORT PVRSRV_ERROR SGXUnregisterHWRenderContextKM(IMG_HANDLE hHWRenderContext, IMG_BOOL bForceCleanup) { PVRSRV_ERROR eError; SGX_HW_RENDER_CONTEXT_CLEANUP *psCleanup; PVR_ASSERT(hHWRenderContext != IMG_NULL); psCleanup = (SGX_HW_RENDER_CONTEXT_CLEANUP *)hHWRenderContext; if (psCleanup == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "SGXUnregisterHWRenderContextKM: invalid parameter")); return PVRSRV_ERROR_INVALID_PARAMS; } eError = ResManFreeResByPtr(psCleanup->psResItem, bForceCleanup); return eError; } IMG_EXPORT IMG_HANDLE SGXRegisterHWTransferContextKM(IMG_HANDLE hDeviceNode, IMG_CPU_VIRTADDR *psHWTransferContextCpuVAddr, IMG_UINT32 ui32HWTransferContextSize, IMG_UINT32 ui32OffsetToPDDevPAddr, IMG_HANDLE hDevMemContext, IMG_DEV_VIRTADDR *psHWTransferContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc) { PVRSRV_ERROR eError; IMG_HANDLE hBlockAlloc; SGX_HW_TRANSFER_CONTEXT_CLEANUP *psCleanup; PVRSRV_DEVICE_NODE *psDeviceNode = (PVRSRV_DEVICE_NODE *)hDeviceNode; DEVICE_MEMORY_INFO *psDevMemoryInfo; DEVICE_MEMORY_HEAP_INFO *psHeapInfo; IMG_HANDLE hDevMemContextInt; MMU_CONTEXT *psMMUContext; IMG_DEV_PHYADDR sPDDevPAddr; int iPtrByte; IMG_UINT8 *pSrc; IMG_UINT8 *pDst; PRESMAN_ITEM psResItem; eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_TRANSFER_CONTEXT_CLEANUP), (IMG_VOID **)&psCleanup, &hBlockAlloc, "SGX Hardware Transfer Context Cleanup"); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Couldn't allocate memory for SGX_HW_TRANSFER_CONTEXT_CLEANUP structure")); goto exit0; } psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; psHeapInfo = &psDevMemoryInfo->psDeviceMemoryHeap[SGX_KERNEL_DATA_HEAP_ID]; eError = PVRSRVAllocDeviceMemKM(hDeviceNode, psPerProc, psHeapInfo->hDevMemHeap, PVRSRV_MEM_READ | PVRSRV_MEM_WRITE | PVRSRV_MEM_NO_SYNCOBJ | PVRSRV_MEM_EDM_PROTECT | PVRSRV_MEM_CACHE_CONSISTENT, ui32HWTransferContextSize, 32, IMG_NULL, 0, 0,0,0,IMG_NULL, /* No sparse mapping data */ &psCleanup->psHWTransferContextMemInfo, "HW Render Context"); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Couldn't allocate device memory for HW Render Context")); goto exit1; } eError = OSCopyFromUser(psPerProc, psCleanup->psHWTransferContextMemInfo->pvLinAddrKM, psHWTransferContextCpuVAddr, ui32HWTransferContextSize); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Couldn't copy user-mode copy of HWContext into device memory")); goto exit2; } /* Pass the DevVAddr of the new context back up through the bridge */ psHWTransferContextDevVAddr->uiAddr = psCleanup->psHWTransferContextMemInfo->sDevVAddr.uiAddr; /* Retrieve the PDDevPAddr */ eError = PVRSRVLookupHandle(psPerProc->psHandleBase, &hDevMemContextInt, hDevMemContext, PVRSRV_HANDLE_TYPE_DEV_MEM_CONTEXT); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Can't lookup DevMem Context")); goto exit2; } psMMUContext = BM_GetMMUContextFromMemContext(hDevMemContextInt); sPDDevPAddr = psDeviceNode->pfnMMUGetPDDevPAddr(psMMUContext); /* patch-in the Page-Directory Device-Physical address. Note that this is copied-in one byte at a time, as we have no guarantee that the usermode- provided ui32OffsetToPDDevPAddr is a validly-aligned address for the current CPU architecture. */ pSrc = (IMG_UINT8 *)&sPDDevPAddr; pDst = (IMG_UINT8 *)psCleanup->psHWTransferContextMemInfo->pvLinAddrKM; pDst += ui32OffsetToPDDevPAddr; for (iPtrByte = 0; iPtrByte < sizeof(IMG_DEV_PHYADDR); iPtrByte++) { pDst[iPtrByte] = pSrc[iPtrByte]; } #if defined(PDUMP) /* PDUMP the HW Transfer Context */ PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "HW Transfer context struct"); PDUMPMEM( IMG_NULL, psCleanup->psHWTransferContextMemInfo, 0, ui32HWTransferContextSize, PDUMP_FLAGS_CONTINUOUS, MAKEUNIQUETAG(psCleanup->psHWTransferContextMemInfo)); /* PDUMP the PDDevPAddr */ PDUMPCOMMENT("Page directory address in HW transfer context"); PDUMPPDDEVPADDR( psCleanup->psHWTransferContextMemInfo, ui32OffsetToPDDevPAddr, sPDDevPAddr, MAKEUNIQUETAG(psCleanup->psHWTransferContextMemInfo), PDUMP_PD_UNIQUETAG); #endif psCleanup->hBlockAlloc = hBlockAlloc; psCleanup->psDeviceNode = psDeviceNode; psCleanup->bCleanupTimerRunning = IMG_FALSE; psResItem = ResManRegisterRes(psPerProc->hResManContext, RESMAN_TYPE_HW_TRANSFER_CONTEXT, psCleanup, 0, &SGXCleanupHWTransferContextCallback); if (psResItem == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: ResManRegisterRes failed")); goto exit2; } psCleanup->psResItem = psResItem; return (IMG_HANDLE)psCleanup; /* Error exit paths */ exit2: PVRSRVFreeDeviceMemKM(hDeviceNode, psCleanup->psHWTransferContextMemInfo); exit1: OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_TRANSFER_CONTEXT_CLEANUP), psCleanup, psCleanup->hBlockAlloc); /*not nulling pointer, out of scope*/ exit0: return IMG_NULL; } IMG_EXPORT PVRSRV_ERROR SGXUnregisterHWTransferContextKM(IMG_HANDLE hHWTransferContext, IMG_BOOL bForceCleanup) { PVRSRV_ERROR eError; SGX_HW_TRANSFER_CONTEXT_CLEANUP *psCleanup; PVR_ASSERT(hHWTransferContext != IMG_NULL); psCleanup = (SGX_HW_TRANSFER_CONTEXT_CLEANUP *)hHWTransferContext; if (psCleanup == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "SGXUnregisterHWTransferContextKM: invalid parameter")); return PVRSRV_ERROR_INVALID_PARAMS; } eError = ResManFreeResByPtr(psCleanup->psResItem, bForceCleanup); return eError; } IMG_EXPORT PVRSRV_ERROR SGXSetTransferContextPriorityKM( IMG_HANDLE hDeviceNode, IMG_HANDLE hHWTransferContext, IMG_UINT32 ui32Priority, IMG_UINT32 ui32OffsetOfPriorityField) { SGX_HW_TRANSFER_CONTEXT_CLEANUP *psCleanup; IMG_UINT8 *pSrc; IMG_UINT8 *pDst; int iPtrByte; PVR_UNREFERENCED_PARAMETER(hDeviceNode); if (hHWTransferContext != IMG_NULL) { psCleanup = (SGX_HW_TRANSFER_CONTEXT_CLEANUP *)hHWTransferContext; if ((ui32OffsetOfPriorityField + sizeof(ui32Priority)) >= psCleanup->psHWTransferContextMemInfo->uAllocSize) { PVR_DPF(( PVR_DBG_ERROR, "SGXSetTransferContextPriorityKM: invalid context prioirty offset")); return PVRSRV_ERROR_INVALID_PARAMS; } /* cannot be sure that offset (passed from user-land) is safe to deref as a word-ptr on current CPU arch: copy one byte at a time. */ pDst = (IMG_UINT8 *)psCleanup->psHWTransferContextMemInfo->pvLinAddrKM; pDst += ui32OffsetOfPriorityField; pSrc = (IMG_UINT8 *)&ui32Priority; for (iPtrByte = 0; iPtrByte < sizeof(ui32Priority); iPtrByte++) { pDst[iPtrByte] = pSrc[iPtrByte]; } } return PVRSRV_OK; } IMG_EXPORT PVRSRV_ERROR SGXSetRenderContextPriorityKM( IMG_HANDLE hDeviceNode, IMG_HANDLE hHWRenderContext, IMG_UINT32 ui32Priority, IMG_UINT32 ui32OffsetOfPriorityField) { SGX_HW_RENDER_CONTEXT_CLEANUP *psCleanup; IMG_UINT8 *pSrc; IMG_UINT8 *pDst; int iPtrByte; PVR_UNREFERENCED_PARAMETER(hDeviceNode); if (hHWRenderContext != IMG_NULL) { psCleanup = (SGX_HW_RENDER_CONTEXT_CLEANUP *)hHWRenderContext; if ((ui32OffsetOfPriorityField + sizeof(ui32Priority)) >= psCleanup->psHWRenderContextMemInfo->uAllocSize) { PVR_DPF(( PVR_DBG_ERROR, "SGXSetContextPriorityKM: invalid HWRenderContext prioirty offset")); return PVRSRV_ERROR_INVALID_PARAMS; } /* cannot be sure that offset (passed from user-land) is safe to deref as a word-ptr on current CPU arch: copy one byte at a time. */ pDst = (IMG_UINT8 *)psCleanup->psHWRenderContextMemInfo->pvLinAddrKM; pDst += ui32OffsetOfPriorityField; pSrc = (IMG_UINT8 *)&ui32Priority; for (iPtrByte = 0; iPtrByte < sizeof(ui32Priority); iPtrByte++) { pDst[iPtrByte] = pSrc[iPtrByte]; } } return PVRSRV_OK; } #if defined(SGX_FEATURE_2D_HARDWARE) typedef struct _SGX_HW_2D_CONTEXT_CLEANUP_ { PVRSRV_DEVICE_NODE *psDeviceNode; PVRSRV_KERNEL_MEM_INFO *psHW2DContextMemInfo; IMG_HANDLE hBlockAlloc; PRESMAN_ITEM psResItem; IMG_BOOL bCleanupTimerRunning; IMG_PVOID pvTimeData; } SGX_HW_2D_CONTEXT_CLEANUP; static PVRSRV_ERROR SGXCleanupHW2DContextCallback(IMG_PVOID pvParam, IMG_UINT32 ui32Param, IMG_BOOL bForceCleanup) { PVRSRV_ERROR eError; SGX_HW_2D_CONTEXT_CLEANUP *psCleanup = (SGX_HW_2D_CONTEXT_CLEANUP *)pvParam; PVR_UNREFERENCED_PARAMETER(ui32Param); /* First, ensure the context is no longer being utilised */ eError = SGXCleanupRequest(psCleanup->psDeviceNode, &psCleanup->psHW2DContextMemInfo->sDevVAddr, PVRSRV_CLEANUPCMD_2DC, bForceCleanup); if (eError == PVRSRV_ERROR_RETRY) { if (!psCleanup->bCleanupTimerRunning) { OSTimeCreateWithUSOffset(&psCleanup->pvTimeData, MAX_CLEANUP_TIME_US); psCleanup->bCleanupTimerRunning = IMG_TRUE; } else { if (OSTimeHasTimePassed(psCleanup->pvTimeData)) { eError = PVRSRV_ERROR_TIMEOUT_POLLING_FOR_VALUE; psCleanup->bCleanupTimerRunning = IMG_FALSE; OSTimeDestroy(psCleanup->pvTimeData); } } } else { if (psCleanup->bCleanupTimerRunning) { OSTimeDestroy(psCleanup->pvTimeData); } } if (eError != PVRSRV_ERROR_RETRY) { /* Free the Device Mem allocated */ PVRSRVFreeDeviceMemKM(psCleanup->psDeviceNode, psCleanup->psHW2DContextMemInfo); /* Finally, free the cleanup structure itself */ OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_2D_CONTEXT_CLEANUP), psCleanup, psCleanup->hBlockAlloc); /*not nulling pointer, copy on stack*/ } return eError; } IMG_HANDLE SGXRegisterHW2DContextKM(IMG_HANDLE hDeviceNode, IMG_CPU_VIRTADDR *psHW2DContextCpuVAddr, IMG_UINT32 ui32HW2DContextSize, IMG_UINT32 ui32OffsetToPDDevPAddr, IMG_HANDLE hDevMemContext, IMG_DEV_VIRTADDR *psHW2DContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc) { PVRSRV_ERROR eError; IMG_HANDLE hBlockAlloc; SGX_HW_2D_CONTEXT_CLEANUP *psCleanup; PVRSRV_DEVICE_NODE *psDeviceNode = (PVRSRV_DEVICE_NODE *)hDeviceNode; DEVICE_MEMORY_INFO *psDevMemoryInfo; DEVICE_MEMORY_HEAP_INFO *psHeapInfo; IMG_HANDLE hDevMemContextInt; MMU_CONTEXT *psMMUContext; IMG_DEV_PHYADDR sPDDevPAddr; int iPtrByte; IMG_UINT8 *pSrc; IMG_UINT8 *pDst; PRESMAN_ITEM psResItem; eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_2D_CONTEXT_CLEANUP), (IMG_VOID **)&psCleanup, &hBlockAlloc, "SGX Hardware 2D Context Cleanup"); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Couldn't allocate memory for SGX_HW_2D_CONTEXT_CLEANUP structure")); goto exit0; } psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; psHeapInfo = &psDevMemoryInfo->psDeviceMemoryHeap[SGX_KERNEL_DATA_HEAP_ID]; eError = PVRSRVAllocDeviceMemKM(hDeviceNode, psPerProc, psHeapInfo->hDevMemHeap, PVRSRV_MEM_READ | PVRSRV_MEM_WRITE | PVRSRV_MEM_NO_SYNCOBJ | PVRSRV_MEM_EDM_PROTECT | PVRSRV_MEM_CACHE_CONSISTENT, ui32HW2DContextSize, 32, IMG_NULL, 0, 0,0,0,IMG_NULL, /* No sparse mapping data */ &psCleanup->psHW2DContextMemInfo, "HW 2D Context"); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Couldn't allocate device memory for HW Render Context")); goto exit1; } eError = OSCopyFromUser(psPerProc, psCleanup->psHW2DContextMemInfo->pvLinAddrKM, psHW2DContextCpuVAddr, ui32HW2DContextSize); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Couldn't copy user-mode copy of HWContext into device memory")); goto exit2; } /* Pass the DevVAddr of the new context back up through the bridge */ psHW2DContextDevVAddr->uiAddr = psCleanup->psHW2DContextMemInfo->sDevVAddr.uiAddr; /* Retrieve the PDDevPAddr */ eError = PVRSRVLookupHandle(psPerProc->psHandleBase, &hDevMemContextInt, hDevMemContext, PVRSRV_HANDLE_TYPE_DEV_MEM_CONTEXT); if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Can't lookup DevMem Context")); goto exit2; } psMMUContext = BM_GetMMUContextFromMemContext(hDevMemContextInt); sPDDevPAddr = psDeviceNode->pfnMMUGetPDDevPAddr(psMMUContext); /* patch-in the Page-Directory Device-Physical address. Note that this is copied-in one byte at a time, as we have no guarantee that the usermode- provided ui32OffsetToPDDevPAddr is a validly-aligned address for the current CPU architecture. */ pSrc = (IMG_UINT8 *)&sPDDevPAddr; pDst = (IMG_UINT8 *)psCleanup->psHW2DContextMemInfo->pvLinAddrKM; pDst += ui32OffsetToPDDevPAddr; for (iPtrByte = 0; iPtrByte < sizeof(IMG_DEV_PHYADDR); iPtrByte++) { pDst[iPtrByte] = pSrc[iPtrByte]; } #if defined(PDUMP) /* PDUMP the HW 2D Context */ PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "HW 2D context struct"); PDUMPMEM( IMG_NULL, psCleanup->psHW2DContextMemInfo, 0, ui32HW2DContextSize, PDUMP_FLAGS_CONTINUOUS, MAKEUNIQUETAG(psCleanup->psHW2DContextMemInfo)); /* PDUMP the PDDevPAddr */ PDUMPCOMMENT("Page directory address in HW 2D transfer context"); PDUMPPDDEVPADDR( psCleanup->psHW2DContextMemInfo, ui32OffsetToPDDevPAddr, sPDDevPAddr, MAKEUNIQUETAG(psCleanup->psHW2DContextMemInfo), PDUMP_PD_UNIQUETAG); #endif psCleanup->hBlockAlloc = hBlockAlloc; psCleanup->psDeviceNode = psDeviceNode; psCleanup->bCleanupTimerRunning = IMG_FALSE; psResItem = ResManRegisterRes(psPerProc->hResManContext, RESMAN_TYPE_HW_2D_CONTEXT, psCleanup, 0, &SGXCleanupHW2DContextCallback); if (psResItem == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: ResManRegisterRes failed")); goto exit2; } psCleanup->psResItem = psResItem; return (IMG_HANDLE)psCleanup; /* Error exit paths */ exit2: PVRSRVFreeDeviceMemKM(hDeviceNode, psCleanup->psHW2DContextMemInfo); exit1: OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(SGX_HW_2D_CONTEXT_CLEANUP), psCleanup, psCleanup->hBlockAlloc); /*not nulling pointer, out of scope*/ exit0: return IMG_NULL; } IMG_EXPORT PVRSRV_ERROR SGXUnregisterHW2DContextKM(IMG_HANDLE hHW2DContext, IMG_BOOL bForceCleanup) { PVRSRV_ERROR eError; SGX_HW_2D_CONTEXT_CLEANUP *psCleanup; PVR_ASSERT(hHW2DContext != IMG_NULL); if (hHW2DContext == IMG_NULL) { return (PVRSRV_ERROR_INVALID_PARAMS); } psCleanup = (SGX_HW_2D_CONTEXT_CLEANUP *)hHW2DContext; eError = ResManFreeResByPtr(psCleanup->psResItem, bForceCleanup); return eError; } #endif /* #if defined(SGX_FEATURE_2D_HARDWARE)*/ /*!**************************************************************************** @Function SGX2DQuerySyncOpsCompleteKM @Input psSyncInfo : Sync object to be queried @Return IMG_TRUE - ops complete, IMG_FALSE - ops pending ******************************************************************************/ #ifdef INLINE_IS_PRAGMA #pragma inline(SGX2DQuerySyncOpsComplete) #endif static INLINE IMG_BOOL SGX2DQuerySyncOpsComplete(PVRSRV_KERNEL_SYNC_INFO *psSyncInfo, IMG_UINT32 ui32ReadOpsPending, IMG_UINT32 ui32WriteOpsPending) { PVRSRV_SYNC_DATA *psSyncData = psSyncInfo->psSyncData; return (IMG_BOOL)( (psSyncData->ui32ReadOpsComplete >= ui32ReadOpsPending) && (psSyncData->ui32WriteOpsComplete >= ui32WriteOpsPending) ); } /*!**************************************************************************** @Function SGX2DQueryBlitsCompleteKM @Input psDevInfo : SGX device info structure @Input psSyncInfo : Sync object to be queried @Return PVRSRV_ERROR ******************************************************************************/ IMG_EXPORT PVRSRV_ERROR SGX2DQueryBlitsCompleteKM(PVRSRV_SGXDEV_INFO *psDevInfo, PVRSRV_KERNEL_SYNC_INFO *psSyncInfo, IMG_BOOL bWaitForComplete) { IMG_UINT32 ui32ReadOpsPending, ui32WriteOpsPending; PVR_UNREFERENCED_PARAMETER(psDevInfo); PVR_DPF((PVR_DBG_CALLTRACE, "SGX2DQueryBlitsCompleteKM: Start")); ui32ReadOpsPending = psSyncInfo->psSyncData->ui32ReadOpsPending; ui32WriteOpsPending = psSyncInfo->psSyncData->ui32WriteOpsPending; if(SGX2DQuerySyncOpsComplete(psSyncInfo, ui32ReadOpsPending, ui32WriteOpsPending)) { /* Instant success */ PVR_DPF((PVR_DBG_CALLTRACE, "SGX2DQueryBlitsCompleteKM: No wait. Blits complete.")); return PVRSRV_OK; } /* Not complete yet */ if (!bWaitForComplete) { /* Just report not complete */ PVR_DPF((PVR_DBG_CALLTRACE, "SGX2DQueryBlitsCompleteKM: No wait. Ops pending.")); return PVRSRV_ERROR_CMD_NOT_PROCESSED; } /* Start polling */ PVR_DPF((PVR_DBG_MESSAGE, "SGX2DQueryBlitsCompleteKM: Ops pending. Start polling.")); LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US) { OSSleepms(1); if(SGX2DQuerySyncOpsComplete(psSyncInfo, ui32ReadOpsPending, ui32WriteOpsPending)) { /* Success */ PVR_DPF((PVR_DBG_CALLTRACE, "SGX2DQueryBlitsCompleteKM: Wait over. Blits complete.")); return PVRSRV_OK; } OSSleepms(1); } END_LOOP_UNTIL_TIMEOUT(); /* Timed out */ PVR_DPF((PVR_DBG_ERROR,"SGX2DQueryBlitsCompleteKM: Timed out. Ops pending.")); #if defined(DEBUG) { PVRSRV_SYNC_DATA *psSyncData = psSyncInfo->psSyncData; PVR_TRACE(("SGX2DQueryBlitsCompleteKM: Syncinfo: 0x%x, Syncdata: 0x%x", (IMG_UINTPTR_T)psSyncInfo, (IMG_UINTPTR_T)psSyncData)); PVR_TRACE(("SGX2DQueryBlitsCompleteKM: Read ops complete: %d, Read ops pending: %d", psSyncData->ui32ReadOpsComplete, psSyncData->ui32ReadOpsPending)); PVR_TRACE(("SGX2DQueryBlitsCompleteKM: Write ops complete: %d, Write ops pending: %d", psSyncData->ui32WriteOpsComplete, psSyncData->ui32WriteOpsPending)); } #endif return PVRSRV_ERROR_TIMEOUT; } IMG_EXPORT PVRSRV_ERROR SGXFlushHWRenderTargetKM(IMG_HANDLE psDeviceNode, IMG_DEV_VIRTADDR sHWRTDataSetDevVAddr, IMG_BOOL bForceCleanup) { PVR_ASSERT(sHWRTDataSetDevVAddr.uiAddr != IMG_NULL); return SGXCleanupRequest(psDeviceNode, &sHWRTDataSetDevVAddr, PVRSRV_CLEANUPCMD_RT, bForceCleanup); } IMG_UINT32 SGXConvertTimeStamp(PVRSRV_SGXDEV_INFO *psDevInfo, IMG_UINT32 ui32TimeWraps, IMG_UINT32 ui32Time) { #if defined(EUR_CR_TIMER) PVR_UNREFERENCED_PARAMETER(psDevInfo); PVR_UNREFERENCED_PARAMETER(ui32TimeWraps); return ui32Time; #else IMG_UINT64 ui64Clocks; IMG_UINT32 ui32Clocksx16; ui64Clocks = ((IMG_UINT64)ui32TimeWraps * psDevInfo->ui32uKernelTimerClock) + (psDevInfo->ui32uKernelTimerClock - (ui32Time & EUR_CR_EVENT_TIMER_VALUE_MASK)); ui32Clocksx16 = (IMG_UINT32)(ui64Clocks / 16); return ui32Clocksx16; #endif /* EUR_CR_TIMER */ } IMG_VOID SGXWaitClocks(PVRSRV_SGXDEV_INFO *psDevInfo, IMG_UINT32 ui32SGXClocks) { /* Round up to the next microsecond. */ OSWaitus(1 + (ui32SGXClocks * 1000000 / psDevInfo->ui32CoreClockSpeed)); } /****************************************************************************** End of file (sgxutils.c) ******************************************************************************/