| /*************************************************************************/ /*! |
| @File |
| @Title Device specific power 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 <stddef.h> |
| #include "rgxpower.h" |
| #include "rgxinit.h" |
| #include "rgx_fwif_km.h" |
| #include "rgxfwutils.h" |
| #include "pdump_km.h" |
| #include "pvr_debug.h" |
| #include "osfunc.h" |
| #include "rgxdebug.h" |
| #include "devicemem.h" |
| #include "devicemem_pdump.h" |
| #include "rgxtimecorr.h" |
| #include "devicemem_utils.h" |
| #include "htbserver.h" |
| #include "rgxstartstop.h" |
| #include "sync.h" |
| #include "lists.h" |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| #include "process_stats.h" |
| #endif |
| #if defined(PVR_DVFS) |
| #include "pvr_dvfs_device.h" |
| #endif |
| |
| static PVRSRV_ERROR RGXFWNotifyHostTimeout(PVRSRV_RGXDEV_INFO *psDevInfo) |
| { |
| PVRSRV_ERROR eError; |
| RGXFWIF_KCCB_CMD sCmd; |
| |
| /* Send the Timeout notification to the FW */ |
| sCmd.eCmdType = RGXFWIF_KCCB_CMD_POW; |
| sCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_FORCED_IDLE_REQ; |
| sCmd.uCmdData.sPowData.uPoweReqData.ePowRequestType = RGXFWIF_POWER_HOST_TIMEOUT; |
| |
| eError = RGXSendCommand(psDevInfo, |
| RGXFWIF_DM_GP, |
| &sCmd, |
| sizeof(sCmd), |
| PDUMP_FLAGS_NONE); |
| |
| return eError; |
| } |
| |
| static void _RGXUpdateGPUUtilStats(PVRSRV_RGXDEV_INFO *psDevInfo) |
| { |
| RGXFWIF_GPU_UTIL_FWCB *psUtilFWCb; |
| IMG_UINT64 *paui64StatsCounters; |
| IMG_UINT64 ui64LastPeriod; |
| IMG_UINT64 ui64LastState; |
| IMG_UINT64 ui64LastTime; |
| IMG_UINT64 ui64TimeNow; |
| |
| psUtilFWCb = psDevInfo->psRGXFWIfGpuUtilFWCb; |
| paui64StatsCounters = &psUtilFWCb->aui64StatsCounters[0]; |
| |
| OSLockAcquire(psDevInfo->hGPUUtilLock); |
| |
| ui64TimeNow = RGXFWIF_GPU_UTIL_GET_TIME(OSClockns64()); |
| |
| /* Update counters to account for the time since the last update */ |
| ui64LastState = RGXFWIF_GPU_UTIL_GET_STATE(psUtilFWCb->ui64LastWord); |
| ui64LastTime = RGXFWIF_GPU_UTIL_GET_TIME(psUtilFWCb->ui64LastWord); |
| ui64LastPeriod = RGXFWIF_GPU_UTIL_GET_PERIOD(ui64TimeNow, ui64LastTime); |
| paui64StatsCounters[ui64LastState] += ui64LastPeriod; |
| |
| /* Update state and time of the latest update */ |
| psUtilFWCb->ui64LastWord = RGXFWIF_GPU_UTIL_MAKE_WORD(ui64TimeNow, ui64LastState); |
| |
| OSLockRelease(psDevInfo->hGPUUtilLock); |
| } |
| |
| static INLINE PVRSRV_ERROR RGXDoStop(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| PVRSRV_ERROR eError; |
| |
| #if defined(SUPPORT_TRUSTED_DEVICE) && !defined(NO_HARDWARE) |
| PVRSRV_DEVICE_CONFIG *psDevConfig = psDeviceNode->psDevConfig; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| if (psDevConfig->pfnTDRGXStop == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXPrePowerState: TDRGXStop not implemented!")); |
| return PVRSRV_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| eError = psDevConfig->pfnTDRGXStop(psDevConfig->hSysData); |
| #else |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| eError = RGXStop(&psDevInfo->sLayerParams); |
| #endif |
| |
| return eError; |
| } |
| |
| /* |
| RGXPrePowerState |
| */ |
| PVRSRV_ERROR RGXPrePowerState (IMG_HANDLE hDevHandle, |
| PVRSRV_DEV_POWER_STATE eNewPowerState, |
| PVRSRV_DEV_POWER_STATE eCurrentPowerState, |
| IMG_BOOL bForced) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| if ((eNewPowerState != eCurrentPowerState) && |
| (eNewPowerState != PVRSRV_DEV_POWER_STATE_ON)) |
| { |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_KCCB_CMD sPowCmd; |
| RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf; |
| |
| /* Send the Power off request to the FW */ |
| sPowCmd.eCmdType = RGXFWIF_KCCB_CMD_POW; |
| sPowCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_OFF_REQ; |
| sPowCmd.uCmdData.sPowData.uPoweReqData.bForced = bForced; |
| |
| eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim", |
| __FUNCTION__)); |
| return eError; |
| } |
| |
| eError = RGXSendCommand(psDevInfo, |
| RGXFWIF_DM_GP, |
| &sPowCmd, |
| sizeof(sPowCmd), |
| PDUMP_FLAGS_NONE); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Failed to send Power off request")); |
| return eError; |
| } |
| |
| /* Wait for the firmware to complete processing. It cannot use PVRSRVWaitForValueKM as it relies |
| on the EventObject which is signalled in this MISR */ |
| eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 0x1, 0xFFFFFFFF); |
| |
| /* Check the Power state after the answer */ |
| if (eError == PVRSRV_OK) |
| { |
| /* Finally, de-initialise some registers. */ |
| if (psFWTraceBuf->ePowState == RGXFWIF_POW_OFF) |
| { |
| #if !defined(NO_HARDWARE) |
| IMG_UINT32 ui32TID; |
| for (ui32TID = 0; ui32TID < RGXFW_THREAD_NUM; ui32TID++) |
| { |
| /* Wait for the pending META/MIPS to host interrupts to come back. */ |
| eError = PVRSRVPollForValueKM(&psDevInfo->aui32SampleIRQCount[ui32TID], |
| psFWTraceBuf->aui32InterruptCount[ui32TID], |
| 0xffffffff); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, \ |
| "RGXPrePowerState: Wait for pending interrupts failed. Thread %u: Host:%u, FW: %u", \ |
| ui32TID, \ |
| psDevInfo->aui32SampleIRQCount[ui32TID], \ |
| psFWTraceBuf->aui32InterruptCount[ui32TID])); |
| |
| RGX_WaitForInterruptsTimeout(psDevInfo); |
| break; |
| } |
| } |
| #endif /* NO_HARDWARE */ |
| |
| /* Update GPU frequency and timer correlation related data */ |
| RGXGPUFreqCalibratePrePowerOff(psDeviceNode); |
| |
| /* Update GPU state counters */ |
| _RGXUpdateGPUUtilStats(psDevInfo); |
| |
| #if defined(PVR_DVFS) |
| eError = SuspendDVFS(); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Failed to suspend DVFS")); |
| return eError; |
| } |
| #endif |
| |
| psDevInfo->bRGXPowered = IMG_FALSE; |
| |
| eError = RGXDoStop(psDeviceNode); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXPrePowerState: RGXDoStop failed (%s)", |
| PVRSRVGetErrorStringKM(eError))); |
| eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_FAILURE; |
| } |
| } |
| else |
| { |
| /* the sync was updated but the pow state isn't off -> the FW denied the transition */ |
| eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_DENIED; |
| |
| if (bForced) |
| { /* It is an error for a forced request to be denied */ |
| PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Failure to power off during a forced power off. FW: %d", psFWTraceBuf->ePowState)); |
| } |
| } |
| } |
| else if (eError == PVRSRV_ERROR_TIMEOUT) |
| { |
| /* timeout waiting for the FW to ack the request: return timeout */ |
| PVR_DPF((PVR_DBG_WARNING,"RGXPrePowerState: Timeout waiting for powoff ack from the FW")); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Error waiting for powoff ack from the FW (%s)", PVRSRVGetErrorStringKM(eError))); |
| eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_FAILURE; |
| } |
| |
| } |
| |
| return eError; |
| } |
| |
| static INLINE PVRSRV_ERROR RGXDoStart(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| PVRSRV_ERROR eError; |
| |
| #if defined(SUPPORT_TRUSTED_DEVICE) && !defined(NO_HARDWARE) |
| PVRSRV_DEVICE_CONFIG *psDevConfig = psDeviceNode->psDevConfig; |
| |
| if (psDevConfig->pfnTDRGXStart == NULL) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXPostPowerState: TDRGXStart not implemented!")); |
| return PVRSRV_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| eError = psDevConfig->pfnTDRGXStart(psDevConfig->hSysData); |
| #else |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| |
| eError = RGXStart(&psDevInfo->sLayerParams); |
| #endif |
| |
| return eError; |
| } |
| |
| /* |
| RGXPostPowerState |
| */ |
| PVRSRV_ERROR RGXPostPowerState (IMG_HANDLE hDevHandle, |
| PVRSRV_DEV_POWER_STATE eNewPowerState, |
| PVRSRV_DEV_POWER_STATE eCurrentPowerState, |
| IMG_BOOL bForced) |
| { |
| if ((eNewPowerState != eCurrentPowerState) && |
| (eCurrentPowerState != PVRSRV_DEV_POWER_STATE_ON)) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_INIT *psRGXFWInit; |
| |
| if (PVRSRV_VZ_MODE_IS(DRIVER_MODE_GUEST)) |
| { |
| psDevInfo->bRGXPowered = IMG_TRUE; |
| return PVRSRV_OK; |
| } |
| |
| if (eCurrentPowerState == PVRSRV_DEV_POWER_STATE_OFF) |
| { |
| /* Update GPU frequency and timer correlation related data */ |
| RGXGPUFreqCalibratePostPowerOn(psDeviceNode); |
| |
| /* Update GPU state counters */ |
| _RGXUpdateGPUUtilStats(psDevInfo); |
| |
| eError = RGXDoStart(psDeviceNode); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXPostPowerState: RGXDoStart failed")); |
| return eError; |
| } |
| |
| OSMemoryBarrier(); |
| |
| #if defined(SUPPORT_EXTRA_METASP_DEBUG) |
| eError = ValidateFWImageWithSP(psDevInfo); |
| if (eError != PVRSRV_OK) return eError; |
| #endif |
| |
| eError = DevmemAcquireCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc, |
| (void **)&psRGXFWInit); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXPostPowerState: Failed to acquire kernel fw if ctl (%u)", |
| eError)); |
| return eError; |
| } |
| |
| /* |
| * Check whether the FW has started by polling on bFirmwareStarted flag |
| */ |
| if (PVRSRVPollForValueKM((IMG_UINT32 *)&psRGXFWInit->bFirmwareStarted, |
| IMG_TRUE, |
| 0xFFFFFFFF) != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXPostPowerState: Polling for 'FW started' flag failed.")); |
| eError = PVRSRV_ERROR_TIMEOUT; |
| |
| /* |
| * When bFirmwareStarted fails some info may be gained by doing the following |
| * debug dump but unfortunately it could lockup some cores or cause other power |
| * lock issues. The code is placed here to provide a possible example approach |
| * when all other ideas have been tried. |
| */ |
| /*{ |
| PVRSRV_POWER_DEV *psPowerDev = psDeviceNode->psPowerDev; |
| |
| if (psPowerDev) |
| { |
| PVRSRV_DEV_POWER_STATE eOldPowerState = psPowerDev->eCurrentPowerState; |
| |
| PVRSRVPowerUnlock(psDeviceNode); |
| psPowerDev->eCurrentPowerState = PVRSRV_DEV_POWER_STATE_ON; |
| RGXDumpDebugInfo(NULL, psDeviceNode->pvDevice); |
| psPowerDev->eCurrentPowerState = eOldPowerState; |
| PVRSRVPowerLock(psDeviceNode); |
| } |
| }*/ |
| |
| DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc); |
| return eError; |
| } |
| |
| #if defined(PDUMP) |
| PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "Wait for the Firmware to start."); |
| eError = DevmemPDumpDevmemPol32(psDevInfo->psRGXFWIfInitMemDesc, |
| offsetof(RGXFWIF_INIT, bFirmwareStarted), |
| IMG_TRUE, |
| 0xFFFFFFFFU, |
| PDUMP_POLL_OPERATOR_EQUAL, |
| PDUMP_FLAGS_CONTINUOUS); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXPostPowerState: problem pdumping POL for psRGXFWIfInitMemDesc (%d)", |
| eError)); |
| DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc); |
| return eError; |
| } |
| #endif |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| SetFirmwareStartTime(psRGXFWInit->ui32FirmwareStartedTimeStamp); |
| #endif |
| |
| HTBSyncPartitionMarker(psRGXFWInit->ui32MarkerVal); |
| |
| DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc); |
| |
| psDevInfo->bRGXPowered = IMG_TRUE; |
| |
| #if defined(PVR_DVFS) |
| eError = ResumeDVFS(); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXPostPowerState: Failed to resume DVFS")); |
| return eError; |
| } |
| #endif |
| } |
| } |
| |
| PDUMPCOMMENT("RGXPostPowerState: Current state: %d, New state: %d", eCurrentPowerState, eNewPowerState); |
| |
| return PVRSRV_OK; |
| } |
| |
| /* |
| RGXPreClockSpeedChange |
| */ |
| PVRSRV_ERROR RGXPreClockSpeedChange (IMG_HANDLE hDevHandle, |
| PVRSRV_DEV_POWER_STATE eCurrentPowerState) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGX_DATA *psRGXData = (RGX_DATA*)psDeviceNode->psDevConfig->hDevData; |
| RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| PVR_UNREFERENCED_PARAMETER(psRGXData); |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"RGXPreClockSpeedChange: RGX clock speed was %uHz", |
| psRGXData->psRGXTimingInfo->ui32CoreClockSpeed)); |
| |
| if ((eCurrentPowerState != PVRSRV_DEV_POWER_STATE_OFF) |
| && (psFWTraceBuf->ePowState != RGXFWIF_POW_OFF)) |
| { |
| /* Update GPU frequency and timer correlation related data */ |
| RGXGPUFreqCalibratePreClockSpeedChange(psDeviceNode); |
| } |
| |
| return eError; |
| } |
| |
| /* |
| RGXPostClockSpeedChange |
| */ |
| PVRSRV_ERROR RGXPostClockSpeedChange (IMG_HANDLE hDevHandle, |
| PVRSRV_DEV_POWER_STATE eCurrentPowerState) |
| { |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGX_DATA *psRGXData = (RGX_DATA*)psDeviceNode->psDevConfig->hDevData; |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf; |
| IMG_UINT32 ui32NewClockSpeed = psRGXData->psRGXTimingInfo->ui32CoreClockSpeed; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| /* Update runtime configuration with the new value */ |
| psDevInfo->psRGXFWIfRuntimeCfg->ui32CoreClockSpeed = ui32NewClockSpeed; |
| |
| if ((eCurrentPowerState != PVRSRV_DEV_POWER_STATE_OFF) |
| && (psFWTraceBuf->ePowState != RGXFWIF_POW_OFF)) |
| { |
| RGXFWIF_KCCB_CMD sCOREClkSpeedChangeCmd; |
| |
| RGXGPUFreqCalibratePostClockSpeedChange(psDeviceNode, ui32NewClockSpeed); |
| |
| sCOREClkSpeedChangeCmd.eCmdType = RGXFWIF_KCCB_CMD_CORECLKSPEEDCHANGE; |
| sCOREClkSpeedChangeCmd.uCmdData.sCORECLKSPEEDCHANGEData.ui32NewClockSpeed = ui32NewClockSpeed; |
| |
| /* Ensure the new clock speed is written to memory before requesting the FW to read it */ |
| OSMemoryBarrier(); |
| |
| PDUMPCOMMENT("Scheduling CORE clock speed change command"); |
| |
| PDUMPPOWCMDSTART(); |
| eError = RGXSendCommand(psDeviceNode->pvDevice, |
| RGXFWIF_DM_GP, |
| &sCOREClkSpeedChangeCmd, |
| sizeof(sCOREClkSpeedChangeCmd), |
| PDUMP_FLAGS_NONE); |
| PDUMPPOWCMDEND(); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PDUMPCOMMENT("Scheduling CORE clock speed change command failed"); |
| PVR_DPF((PVR_DBG_ERROR, "RGXPostClockSpeedChange: Scheduling KCCB command failed. Error:%u", eError)); |
| return eError; |
| } |
| |
| PVR_DPF((PVR_DBG_MESSAGE,"RGXPostClockSpeedChange: RGX clock speed changed to %uHz", |
| psRGXData->psRGXTimingInfo->ui32CoreClockSpeed)); |
| } |
| |
| return eError; |
| } |
| |
| /*! |
| ****************************************************************************** |
| |
| @Function RGXDustCountChange |
| |
| @Description |
| |
| Does change of number of DUSTs |
| |
| @Input hDevHandle : RGX Device Node |
| @Input ui32NumberOfDusts : Number of DUSTs to make transition to |
| |
| @Return PVRSRV_ERROR : |
| |
| ******************************************************************************/ |
| PVRSRV_ERROR RGXDustCountChange(IMG_HANDLE hDevHandle, |
| IMG_UINT32 ui32NumberOfDusts) |
| { |
| |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| PVRSRV_ERROR eError; |
| RGXFWIF_KCCB_CMD sDustCountChange; |
| IMG_UINT32 ui32MaxAvailableDusts = MAX(1, (psDevInfo->sDevFeatureCfg.ui32NumClusters/2)); |
| RGXFWIF_RUNTIME_CFG *psRuntimeCfg = psDevInfo->psRGXFWIfRuntimeCfg; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| if (ui32NumberOfDusts > ui32MaxAvailableDusts) |
| { |
| eError = PVRSRV_ERROR_INVALID_PARAMS; |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXDustCountChange: Invalid number of DUSTs (%u) while expecting value within <0,%u>. Error:%u", |
| ui32NumberOfDusts, |
| ui32MaxAvailableDusts, |
| eError)); |
| return eError; |
| } |
| |
| #if defined(FIX_HW_BRN_59042) |
| if (ui32NumberOfDusts < ui32MaxAvailableDusts && (ui32NumberOfDusts & 0x1)) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "RGXDustCountChange: Invalid number of DUSTs (%u) due to HW restriction. Allowed values are :-", |
| ui32NumberOfDusts)); |
| switch (ui32MaxAvailableDusts) |
| { |
| case 2: PVR_DPF((PVR_DBG_ERROR, "0, 2")); break; |
| case 3: PVR_DPF((PVR_DBG_ERROR, "0, 2, 3")); break; |
| case 4: PVR_DPF((PVR_DBG_ERROR, "0, 2, 4")); break; |
| case 5: PVR_DPF((PVR_DBG_ERROR, "0, 2, 4, 5")); break; |
| case 6: PVR_DPF((PVR_DBG_ERROR, "0, 2, 4, 6")); break; |
| default: break; |
| } |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| #endif |
| |
| psRuntimeCfg->ui32DefaultDustsNumInit = ui32NumberOfDusts; |
| |
| #if !defined(NO_HARDWARE) |
| { |
| RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf; |
| |
| if (psFWTraceBuf->ePowState == RGXFWIF_POW_OFF) |
| { |
| return PVRSRV_OK; |
| } |
| |
| if (psFWTraceBuf->ePowState != RGXFWIF_POW_FORCED_IDLE) |
| { |
| eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_DENIED; |
| PVR_DPF((PVR_DBG_ERROR,"RGXDustCountChange: Attempt to change dust count when not IDLE")); |
| return eError; |
| } |
| } |
| #endif |
| |
| eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim", |
| __FUNCTION__)); |
| return eError; |
| } |
| |
| sDustCountChange.eCmdType = RGXFWIF_KCCB_CMD_POW; |
| sDustCountChange.uCmdData.sPowData.ePowType = RGXFWIF_POW_NUMDUST_CHANGE; |
| sDustCountChange.uCmdData.sPowData.uPoweReqData.ui32NumOfDusts = ui32NumberOfDusts; |
| |
| PDUMPCOMMENT("Scheduling command to change Dust Count to %u", ui32NumberOfDusts); |
| eError = RGXSendCommand(psDeviceNode->pvDevice, |
| RGXFWIF_DM_GP, |
| &sDustCountChange, |
| sizeof(sDustCountChange), |
| PDUMP_FLAGS_NONE); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PDUMPCOMMENT("Scheduling command to change Dust Count failed. Error:%u", eError); |
| PVR_DPF((PVR_DBG_ERROR, "RGXDustCountChange: Scheduling KCCB to change Dust Count failed. Error:%u", eError)); |
| return eError; |
| } |
| |
| /* Wait for the firmware to answer. */ |
| eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 0x1, 0xFFFFFFFF); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXDustCountChange: Timeout waiting for idle request")); |
| return eError; |
| } |
| |
| #if defined(PDUMP) |
| PDUMPCOMMENT("RGXDustCountChange: Poll for Kernel SyncPrim [0x%p] on DM %d ", psDevInfo->psPowSyncPrim->pui32LinAddr, RGXFWIF_DM_GP); |
| |
| SyncPrimPDumpPol(psDevInfo->psPowSyncPrim, |
| 1, |
| 0xffffffff, |
| PDUMP_POLL_OPERATOR_EQUAL, |
| 0); |
| #endif |
| |
| return PVRSRV_OK; |
| } |
| |
| /* |
| @Function RGXAPMLatencyChange |
| */ |
| PVRSRV_ERROR RGXAPMLatencyChange(IMG_HANDLE hDevHandle, |
| IMG_UINT32 ui32ActivePMLatencyms, |
| IMG_BOOL bActivePMLatencyPersistant) |
| { |
| |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| PVRSRV_ERROR eError; |
| RGXFWIF_RUNTIME_CFG *psRuntimeCfg = psDevInfo->psRGXFWIfRuntimeCfg; |
| PVRSRV_DEV_POWER_STATE ePowerState; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| eError = PVRSRVPowerLock(psDeviceNode); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXAPMLatencyChange: Failed to acquire power lock")); |
| return eError; |
| } |
| |
| /* Update runtime configuration with the new values and ensure the |
| * new APM latency is written to memory before requesting the FW to |
| * read it |
| */ |
| psRuntimeCfg->ui32ActivePMLatencyms = ui32ActivePMLatencyms; |
| psRuntimeCfg->bActivePMLatencyPersistant = bActivePMLatencyPersistant; |
| OSMemoryBarrier(); |
| |
| eError = PVRSRVGetDevicePowerState(psDeviceNode, &ePowerState); |
| |
| if ((eError == PVRSRV_OK) && (ePowerState != PVRSRV_DEV_POWER_STATE_OFF)) |
| { |
| RGXFWIF_KCCB_CMD sActivePMLatencyChange; |
| sActivePMLatencyChange.eCmdType = RGXFWIF_KCCB_CMD_POW; |
| sActivePMLatencyChange.uCmdData.sPowData.ePowType = RGXFWIF_POW_APM_LATENCY_CHANGE; |
| sActivePMLatencyChange.uCmdData.sPowData.uPoweReqData.ui32ActivePMLatencyms = ui32ActivePMLatencyms; |
| |
| PDUMPCOMMENT("Scheduling command to change APM latency to %u", ui32ActivePMLatencyms); |
| eError = RGXSendCommand(psDeviceNode->pvDevice, |
| RGXFWIF_DM_GP, |
| &sActivePMLatencyChange, |
| sizeof(sActivePMLatencyChange), |
| PDUMP_FLAGS_NONE); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PDUMPCOMMENT("Scheduling command to change APM latency failed. Error:%u", eError); |
| PVR_DPF((PVR_DBG_ERROR, "RGXAPMLatencyChange: Scheduling KCCB to change APM latency failed. Error:%u", eError)); |
| goto ErrorExit; |
| } |
| } |
| |
| ErrorExit: |
| PVRSRVPowerUnlock(psDeviceNode); |
| |
| return eError; |
| } |
| |
| /* |
| RGXActivePowerRequest |
| */ |
| PVRSRV_ERROR RGXActivePowerRequest(IMG_HANDLE hDevHandle) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| #if defined(PVRSRV_USE_BRIDGE_LOCK) |
| OSAcquireBridgeLock(); |
| #endif |
| /* NOTE: If this function were to wait for event object attempt should be |
| made to prevent releasing bridge lock during sleep. Bridge lock should |
| be held during sleep. */ |
| |
| /* Powerlock to avoid further requests from racing with the FW hand-shake from now on |
| (previous kicks to this point are detected by the FW) */ |
| eError = PVRSRVPowerLock(psDeviceNode); |
| if(eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "%s: Failed to acquire PowerLock (device: %p, error: %s)", |
| __func__, psDeviceNode, PVRSRVGetErrorStringKM(eError))); |
| goto _RGXActivePowerRequest_PowerLock_failed; |
| } |
| |
| /* Check again for IDLE once we have the power lock */ |
| if (psFWTraceBuf->ePowState == RGXFWIF_POW_IDLE) |
| { |
| |
| psDevInfo->ui32ActivePMReqTotal++; |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| SetFirmwareHandshakeIdleTime(RGXReadHWTimerReg(psDevInfo)-psFWTraceBuf->ui64StartIdleTime); |
| #endif |
| |
| PDUMPPOWCMDSTART(); |
| eError = PVRSRVSetDevicePowerStateKM(psDeviceNode, |
| PVRSRV_DEV_POWER_STATE_OFF, |
| IMG_FALSE); /* forced */ |
| PDUMPPOWCMDEND(); |
| |
| if (eError == PVRSRV_OK) |
| { |
| psDevInfo->ui32ActivePMReqOk++; |
| } |
| else if (eError == PVRSRV_ERROR_DEVICE_POWER_CHANGE_DENIED) |
| { |
| psDevInfo->ui32ActivePMReqDenied++; |
| } |
| |
| } |
| |
| PVRSRVPowerUnlock(psDeviceNode); |
| |
| _RGXActivePowerRequest_PowerLock_failed: |
| #if defined(PVRSRV_USE_BRIDGE_LOCK) |
| OSReleaseBridgeLock(); |
| #endif |
| |
| return eError; |
| |
| } |
| /* |
| RGXForcedIdleRequest |
| */ |
| |
| #define RGX_FORCED_IDLE_RETRY_COUNT 10 |
| |
| PVRSRV_ERROR RGXForcedIdleRequest(IMG_HANDLE hDevHandle, IMG_BOOL bDeviceOffPermitted) |
| { |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_KCCB_CMD sPowCmd; |
| PVRSRV_ERROR eError; |
| IMG_UINT32 ui32RetryCount = 0; |
| #if !defined(NO_HARDWARE) |
| RGXFWIF_TRACEBUF *psFWTraceBuf; |
| #endif |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| #if !defined(NO_HARDWARE) |
| psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf; |
| |
| /* Firmware already forced idle */ |
| if (psFWTraceBuf->ePowState == RGXFWIF_POW_FORCED_IDLE) |
| { |
| return PVRSRV_OK; |
| } |
| |
| /* Firmware is not powered. Sometimes this is permitted, for instance we were forcing idle to power down. */ |
| if (psFWTraceBuf->ePowState == RGXFWIF_POW_OFF) |
| { |
| return (bDeviceOffPermitted) ? PVRSRV_OK : PVRSRV_ERROR_DEVICE_IDLE_REQUEST_DENIED; |
| } |
| #endif |
| |
| eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim", |
| __FUNCTION__)); |
| return eError; |
| } |
| sPowCmd.eCmdType = RGXFWIF_KCCB_CMD_POW; |
| sPowCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_FORCED_IDLE_REQ; |
| sPowCmd.uCmdData.sPowData.uPoweReqData.ePowRequestType = RGXFWIF_POWER_FORCE_IDLE; |
| |
| PDUMPCOMMENT("RGXForcedIdleRequest: Sending forced idle command"); |
| |
| /* Send one forced IDLE command to GP */ |
| eError = RGXSendCommand(psDevInfo, |
| RGXFWIF_DM_GP, |
| &sPowCmd, |
| sizeof(sPowCmd), |
| PDUMP_FLAGS_NONE); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXForcedIdleRequest: Failed to send idle request")); |
| return eError; |
| } |
| |
| /* Wait for GPU to finish current workload */ |
| do { |
| eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 0x1, 0xFFFFFFFF); |
| if ((eError == PVRSRV_OK) || (ui32RetryCount == RGX_FORCED_IDLE_RETRY_COUNT)) |
| { |
| break; |
| } |
| ui32RetryCount++; |
| PVR_DPF((PVR_DBG_WARNING,"RGXForcedIdleRequest: Request timeout. Retry %d of %d", ui32RetryCount, RGX_FORCED_IDLE_RETRY_COUNT)); |
| } while (IMG_TRUE); |
| |
| if (eError != PVRSRV_OK) |
| { |
| RGXFWNotifyHostTimeout(psDevInfo); |
| PVR_DPF((PVR_DBG_ERROR,"RGXForcedIdleRequest: Idle request failed. Firmware potentially left in forced idle state")); |
| return eError; |
| } |
| |
| #if defined(PDUMP) |
| PDUMPCOMMENT("RGXForcedIdleRequest: Poll for Kernel SyncPrim [0x%p] on DM %d ", psDevInfo->psPowSyncPrim->pui32LinAddr, RGXFWIF_DM_GP); |
| |
| SyncPrimPDumpPol(psDevInfo->psPowSyncPrim, |
| 1, |
| 0xffffffff, |
| PDUMP_POLL_OPERATOR_EQUAL, |
| 0); |
| #endif |
| |
| #if !defined(NO_HARDWARE) |
| /* Check the firmware state for idleness */ |
| if (psFWTraceBuf->ePowState != RGXFWIF_POW_FORCED_IDLE) |
| { |
| return PVRSRV_ERROR_DEVICE_IDLE_REQUEST_DENIED; |
| } |
| #endif |
| |
| return PVRSRV_OK; |
| } |
| |
| /* |
| RGXCancelForcedIdleRequest |
| */ |
| PVRSRV_ERROR RGXCancelForcedIdleRequest(IMG_HANDLE hDevHandle) |
| { |
| PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGXFWIF_KCCB_CMD sPowCmd; |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_VZ_RET_IF_MODE(DRIVER_MODE_GUEST, PVRSRV_OK); |
| |
| eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim", |
| __FUNCTION__)); |
| goto ErrorExit; |
| } |
| |
| /* Send the IDLE request to the FW */ |
| sPowCmd.eCmdType = RGXFWIF_KCCB_CMD_POW; |
| sPowCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_FORCED_IDLE_REQ; |
| sPowCmd.uCmdData.sPowData.uPoweReqData.ePowRequestType = RGXFWIF_POWER_CANCEL_FORCED_IDLE; |
| |
| PDUMPCOMMENT("RGXForcedIdleRequest: Sending cancel forced idle command"); |
| |
| /* Send cancel forced IDLE command to GP */ |
| eError = RGXSendCommand(psDevInfo, |
| RGXFWIF_DM_GP, |
| &sPowCmd, |
| sizeof(sPowCmd), |
| PDUMP_FLAGS_NONE); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PDUMPCOMMENT("RGXCancelForcedIdleRequest: Failed to send cancel IDLE request for DM%d", RGXFWIF_DM_GP); |
| goto ErrorExit; |
| } |
| |
| /* Wait for the firmware to answer. */ |
| eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 1, 0xFFFFFFFF); |
| |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR,"RGXCancelForcedIdleRequest: Timeout waiting for cancel idle request")); |
| goto ErrorExit; |
| } |
| |
| #if defined(PDUMP) |
| PDUMPCOMMENT("RGXCancelForcedIdleRequest: Poll for Kernel SyncPrim [0x%p] on DM %d ", psDevInfo->psPowSyncPrim->pui32LinAddr, RGXFWIF_DM_GP); |
| |
| SyncPrimPDumpPol(psDevInfo->psPowSyncPrim, |
| 1, |
| 0xffffffff, |
| PDUMP_POLL_OPERATOR_EQUAL, |
| 0); |
| #endif |
| |
| return eError; |
| |
| ErrorExit: |
| PVR_DPF((PVR_DBG_ERROR,"RGXCancelForcedIdleRequest: Firmware potentially left in forced idle state")); |
| return eError; |
| } |
| |
| /*! |
| ****************************************************************************** |
| |
| @Function PVRSRVGetNextDustCount |
| |
| @Description |
| |
| Calculate a sequence of dust counts to achieve full transition coverage. |
| We increment two counts of dusts and switch up and down between them. |
| It does contain a few redundant transitions. If two dust exist, the |
| output transitions should be as follows. |
| |
| 0->1, 0<-1, 0->2, 0<-2, (0->1) |
| 1->1, 1->2, 1<-2, (1->2) |
| 2->2, (2->0), |
| 0->0. Repeat. |
| |
| Redundant transitions in brackets. |
| |
| @Input psDustReqState : Counter state used to calculate next dust count |
| @Input ui32DustCount : Number of dusts in the core |
| |
| @Return PVRSRV_ERROR |
| |
| ******************************************************************************/ |
| IMG_UINT32 RGXGetNextDustCount(RGX_DUST_STATE *psDustReqState, IMG_UINT32 ui32DustCount) |
| { |
| if (psDustReqState->bToggle) |
| { |
| psDustReqState->ui32DustCount2++; |
| } |
| |
| if (psDustReqState->ui32DustCount2 > ui32DustCount) |
| { |
| psDustReqState->ui32DustCount1++; |
| psDustReqState->ui32DustCount2 = psDustReqState->ui32DustCount1; |
| } |
| |
| if (psDustReqState->ui32DustCount1 > ui32DustCount) |
| { |
| psDustReqState->ui32DustCount1 = 0; |
| psDustReqState->ui32DustCount2 = 0; |
| } |
| |
| psDustReqState->bToggle = !psDustReqState->bToggle; |
| |
| return (psDustReqState->bToggle) ? psDustReqState->ui32DustCount1 : psDustReqState->ui32DustCount2; |
| } |
| |
| /****************************************************************************** |
| End of file (rgxpower.c) |
| ******************************************************************************/ |