1 /*************************************************************************/ /*!
3 @Title Device specific time correlation and calibration routines
4 @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
5 @Description Device specific time correlation and calibration routines
6 @License Dual MIT/GPLv2
8 The contents of this file are subject to the MIT license as set out below.
10 Permission is hereby granted, free of charge, to any person obtaining a copy
11 of this software and associated documentation files (the "Software"), to deal
12 in the Software without restriction, including without limitation the rights
13 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions:
17 The above copyright notice and this permission notice shall be included in
18 all copies or substantial portions of the Software.
20 Alternatively, the contents of this file may be used under the terms of
21 the GNU General Public License Version 2 ("GPL") in which case the provisions
22 of GPL are applicable instead of those above.
24 If you wish to allow use of your version of this file only under the terms of
25 GPL, and not to allow others to use your version of this file under the terms
26 of the MIT license, indicate your decision by deleting the provisions above
27 and replace them with the notice and other provisions required by GPL as set
28 out in the file called "GPL-COPYING" included in this distribution. If you do
29 not delete the provisions above, a recipient may use your version of this file
30 under the terms of either the MIT license or GPL.
32 This License is also included in this distribution in the file called
35 EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
36 PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
37 BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
38 PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
39 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
40 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
41 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 */ /**************************************************************************/
44 #include "rgxtimecorr.h"
45 #include "rgxfwutils.h"
47 /******************************************************************************
49 * - A calibration period is started on power-on and after a DVFS transition,
50 * and it's closed before a power-off and before a DVFS transition
51 * (so power-on -> dfvs -> dvfs -> power-off , power on -> dvfs -> dvfs...,
52 * where each arrow is a calibration period)
54 * - The timers on the Host and on the FW are correlated at the beginning of
55 * each period together with the (possibly calibrated) current GPU frequency
57 * - If the frequency has not changed since the last power-off/on sequence or
58 * before/after a DVFS transition (-> the transition didn't really happen)
59 * then multiple consecutive periods are merged (the higher the numbers the
60 * better the accuracy in the computed clock speed)
62 * - Correlation and calibration are also done more or less periodically
63 * (using a best effort approach)
65 *****************************************************************************/
67 static IMG_VOID _RGXMakeTimeCorrData(PVRSRV_DEVICE_NODE *psDeviceNode)
69 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
70 RGXFWIF_GPU_UTIL_FWCB *psGpuUtilFWCB = psDevInfo->psRGXFWIfGpuUtilFWCb;
71 RGX_GPU_DVFS_TABLE *psGpuDVFSTable = psDevInfo->psGpuDVFSTable;
72 RGXFWIF_TIME_CORR *psTimeCorr;
73 IMG_UINT32 ui32NewSeqCount;
74 IMG_UINT32 ui32CoreClockSpeed;
75 IMG_UINT32 ui32Remainder;
77 ui32CoreClockSpeed = psGpuDVFSTable->aui32DVFSClock[psGpuDVFSTable->ui32CurrentDVFSId];
79 ui32NewSeqCount = psGpuUtilFWCB->ui32TimeCorrSeqCount + 1;
80 psTimeCorr = &psGpuUtilFWCB->sTimeCorr[RGXFWIF_TIME_CORR_CURR_INDEX(ui32NewSeqCount)];
82 psTimeCorr->ui64CRTimeStamp = RGXReadHWTimerReg(psDevInfo);
83 psTimeCorr->ui64OSTimeStamp = OSClockns64();
84 psTimeCorr->ui32CoreClockSpeed = ui32CoreClockSpeed;
85 psTimeCorr->ui32CRDeltaToOSDeltaKNs =
86 RGXFWIF_GET_CRDELTA_TO_OSDELTA_K_NS(ui32CoreClockSpeed, ui32Remainder);
88 /* Make sure the values are written to memory before updating the index of the current entry */
89 OSWriteMemoryBarrier();
91 /* Update the index of the current entry in the timer correlation array */
92 psGpuUtilFWCB->ui32TimeCorrSeqCount = ui32NewSeqCount;
94 PVR_DPF((PVR_DBG_MESSAGE,"RGXMakeTimeCorrData: Correlated OS timestamp %llu (ns) with CR timestamp %llu, GPU clock speed %uHz",
95 psTimeCorr->ui64OSTimeStamp, psTimeCorr->ui64CRTimeStamp, psTimeCorr->ui32CoreClockSpeed));
99 static IMG_VOID _RGXGPUFreqCalibrationPeriodStart(PVRSRV_DEVICE_NODE *psDeviceNode, RGX_GPU_DVFS_TABLE *psGpuDVFSTable)
101 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
102 RGX_DATA *psRGXData = (RGX_DATA*)psDeviceNode->psDevConfig->hDevData;
103 IMG_UINT32 ui32CoreClockSpeed = psRGXData->psRGXTimingInfo->ui32CoreClockSpeed;
104 IMG_UINT32 ui32Index = RGX_GPU_DVFS_GET_INDEX(ui32CoreClockSpeed);
106 IMG_UINT64 ui64CRTimestamp = RGXReadHWTimerReg(psDevInfo);
107 IMG_UINT64 ui64OSTimestamp = OSClockus64();
109 psGpuDVFSTable->ui64CalibrationCRTimestamp = ui64CRTimestamp;
110 psGpuDVFSTable->ui64CalibrationOSTimestamp = ui64OSTimestamp;
112 /* Set the time needed to (re)calibrate the GPU frequency */
113 if((psGpuDVFSTable->aui32DVFSClock[ui32Index] == 0) || /* We never met this frequency */
114 (psGpuDVFSTable->aui32DVFSClock[ui32Index] == ui32CoreClockSpeed)) /* We weren't able to calibrate this frequency previously */
116 psGpuDVFSTable->aui32DVFSClock[ui32Index] = ui32CoreClockSpeed;
117 psGpuDVFSTable->ui32CalibrationPeriod = RGX_GPU_DVFS_FIRST_CALIBRATION_TIME_US;
119 PVR_DPF((PVR_DBG_MESSAGE, "RGXGPUFreqCalibrationStart: using uncalibrated GPU frequency %u", ui32CoreClockSpeed));
121 else if(psGpuDVFSTable->ui32CalibrationPeriod == RGX_GPU_DVFS_FIRST_CALIBRATION_TIME_US)
123 psGpuDVFSTable->ui32CalibrationPeriod = RGX_GPU_DVFS_TRANSITION_CALIBRATION_TIME_US;
127 psGpuDVFSTable->ui32CalibrationPeriod = RGX_GPU_DVFS_PERIODIC_CALIBRATION_TIME_US;
130 /* Update the index to the DVFS table */
131 psGpuDVFSTable->ui32CurrentDVFSId = ui32Index;
135 static IMG_VOID _RGXGPUFreqCalibrationPeriodStop(PVRSRV_DEVICE_NODE *psDeviceNode, RGX_GPU_DVFS_TABLE *psGpuDVFSTable)
137 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
139 IMG_UINT64 ui64CRTimestamp = RGXReadHWTimerReg(psDevInfo);
140 IMG_UINT64 ui64OSTimestamp = OSClockus64();
142 if(!psGpuDVFSTable->bAccumulatePeriod)
144 psGpuDVFSTable->ui64CalibrationCRTimediff = 0;
145 psGpuDVFSTable->ui64CalibrationOSTimediff = 0;
148 psGpuDVFSTable->ui64CalibrationCRTimediff += ui64CRTimestamp - psGpuDVFSTable->ui64CalibrationCRTimestamp;
149 psGpuDVFSTable->ui64CalibrationOSTimediff += ui64OSTimestamp - psGpuDVFSTable->ui64CalibrationOSTimestamp;
153 static IMG_UINT32 _RGXGPUFreqCalibrationCalculate(PVRSRV_DEVICE_NODE *psDeviceNode, RGX_GPU_DVFS_TABLE *psGpuDVFSTable)
155 IMG_UINT32 ui32CalibratedClockSpeed;
156 IMG_UINT32 ui32Remainder;
158 ui32CalibratedClockSpeed =
159 RGXFWIF_GET_GPU_CLOCK_FREQUENCY_HZ(psGpuDVFSTable->ui64CalibrationCRTimediff, psGpuDVFSTable->ui64CalibrationOSTimediff, ui32Remainder);
161 PVR_DPF((PVR_DBG_MESSAGE, "GPU frequency calibration: %u -> %u done over %llu us",
162 psGpuDVFSTable->aui32DVFSClock[psGpuDVFSTable->ui32CurrentDVFSId],
163 ui32CalibratedClockSpeed,
164 psGpuDVFSTable->ui64CalibrationOSTimediff));
166 psGpuDVFSTable->aui32DVFSClock[psGpuDVFSTable->ui32CurrentDVFSId] = ui32CalibratedClockSpeed;
168 return ui32CalibratedClockSpeed;
173 RGXGPUFreqCalibratePrePowerState
175 IMG_VOID RGXGPUFreqCalibratePrePowerState(IMG_HANDLE hDevHandle)
177 PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
178 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
179 RGX_GPU_DVFS_TABLE *psGpuDVFSTable = psDevInfo->psGpuDVFSTable;
181 _RGXGPUFreqCalibrationPeriodStop(psDeviceNode, psGpuDVFSTable);
183 if(psGpuDVFSTable->ui64CalibrationOSTimediff >= psGpuDVFSTable->ui32CalibrationPeriod)
185 _RGXGPUFreqCalibrationCalculate(psDeviceNode, psGpuDVFSTable);
191 RGXGPUFreqCalibratePostPowerState
193 IMG_VOID RGXGPUFreqCalibratePostPowerState(IMG_HANDLE hDevHandle)
195 PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
196 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
197 RGX_GPU_DVFS_TABLE *psGpuDVFSTable = psDevInfo->psGpuDVFSTable;
198 RGX_DATA *psRGXData = (RGX_DATA*)psDeviceNode->psDevConfig->hDevData;
199 IMG_UINT32 ui32CoreClockSpeed = psRGXData->psRGXTimingInfo->ui32CoreClockSpeed;
201 /* If the frequency hasn't changed then accumulate the time diffs to get a better result */
202 psGpuDVFSTable->bAccumulatePeriod = (RGX_GPU_DVFS_GET_INDEX(ui32CoreClockSpeed) == psGpuDVFSTable->ui32CurrentDVFSId);
204 _RGXGPUFreqCalibrationPeriodStart(psDeviceNode, psGpuDVFSTable);
206 /* Update the timer correlation data */
207 _RGXMakeTimeCorrData(psDeviceNode);
212 RGXGPUFreqCalibratePreClockSpeedChange
214 IMG_VOID RGXGPUFreqCalibratePreClockSpeedChange(IMG_HANDLE hDevHandle)
216 PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
217 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
218 RGX_GPU_DVFS_TABLE *psGpuDVFSTable = psDevInfo->psGpuDVFSTable;
220 _RGXGPUFreqCalibrationPeriodStop(psDeviceNode, psGpuDVFSTable);
222 /* Wait until RGXPostClockSpeedChange() to do anything as the GPU frequency may be left
223 * unchanged (in that case we delay calibration/correlation to get a better result later) */
228 RGXGPUFreqCalibratePostClockSpeedChange
230 IMG_UINT32 RGXGPUFreqCalibratePostClockSpeedChange(IMG_HANDLE hDevHandle, IMG_UINT32 ui32NewClockSpeed)
232 PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
233 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
234 RGX_GPU_DVFS_TABLE *psGpuDVFSTable = psDevInfo->psGpuDVFSTable;
235 IMG_UINT32 ui32ReturnedClockSpeed = ui32NewClockSpeed;
237 if(RGX_GPU_DVFS_GET_INDEX(ui32NewClockSpeed) != psGpuDVFSTable->ui32CurrentDVFSId)
239 /* Only calibrate if the last period was long enough */
240 if(psGpuDVFSTable->ui64CalibrationOSTimediff >= RGX_GPU_DVFS_TRANSITION_CALIBRATION_TIME_US)
242 ui32ReturnedClockSpeed = _RGXGPUFreqCalibrationCalculate(psDeviceNode, psGpuDVFSTable);
245 _RGXGPUFreqCalibrationPeriodStart(psDeviceNode, psGpuDVFSTable);
247 /* Update the timer correlation data */
248 _RGXMakeTimeCorrData(psDeviceNode);
250 psGpuDVFSTable->bAccumulatePeriod = IMG_FALSE;
254 psGpuDVFSTable->bAccumulatePeriod = IMG_TRUE;
257 return ui32ReturnedClockSpeed;
262 RGXGPUFreqCalibrateCorrelatePeriodic
264 IMG_VOID RGXGPUFreqCalibrateCorrelatePeriodic(IMG_HANDLE hDevHandle)
266 PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
267 PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
268 RGX_GPU_DVFS_TABLE *psGpuDVFSTable = psDevInfo->psGpuDVFSTable;
269 PVRSRV_DATA *psPVRSRVData;
270 IMG_UINT64 ui64TimeNow = OSClockus64();
271 PVRSRV_DEV_POWER_STATE ePowerState;
273 /* Check if it's the right time to recalibrate the GPU clock frequency */
274 if((ui64TimeNow - psGpuDVFSTable->ui64CalibrationOSTimestamp) < psGpuDVFSTable->ui32CalibrationPeriod) return;
276 /* Try to acquire the powerlock, if not possible then don't wait */
277 psPVRSRVData = PVRSRVGetPVRSRVData();
278 if (OSLockIsLocked(psPVRSRVData->hPowerLock)) return; /* Better to not wait here if possible */
279 /* There's a chance that the powerlock could be taken here, it's not that bad even if not desirable
280 (TODO use OSTryLockAcquire, currently implemented under Linux only) */
281 if (PVRSRVPowerLock() != PVRSRV_OK) return;
283 /* If the GPU is off then we can't do anything */
284 PVRSRVGetDevicePowerState(psDeviceNode->sDevId.ui32DeviceIndex, &ePowerState);
285 if (ePowerState != PVRSRV_DEV_POWER_STATE_ON)
291 /* All checks passed, we can calibrate and correlate */
292 _RGXGPUFreqCalibrationPeriodStop(psDeviceNode, psGpuDVFSTable);
293 _RGXGPUFreqCalibrationCalculate(psDeviceNode, psGpuDVFSTable);
294 _RGXGPUFreqCalibrationPeriodStart(psDeviceNode, psGpuDVFSTable);
295 _RGXMakeTimeCorrData(psDeviceNode);
297 /* Force Accumulate Period to false to not trigger a periodic calibration over and over again */
298 psGpuDVFSTable->bAccumulatePeriod = IMG_FALSE;
304 /******************************************************************************
305 End of file (rgxtimecorr.c)
306 ******************************************************************************/