RK3368 GPU: Rogue N Init.
[firefly-linux-kernel-4.4.55.git] / drivers / staging / imgtec / rogue / rgxworkest.c
1 /*************************************************************************/ /*!
2 @File           rgxworkest.c
3 @Title          RGX Workload Estimation Functionality
4 @Codingstyle    IMG
5 @Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
6 @Description    Kernel mode workload estimation functionality.
7 @License        Dual MIT/GPLv2
8
9 The contents of this file are subject to the MIT license as set out below.
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in
19 all copies or substantial portions of the Software.
20
21 Alternatively, the contents of this file may be used under the terms of
22 the GNU General Public License Version 2 ("GPL") in which case the provisions
23 of GPL are applicable instead of those above.
24
25 If you wish to allow use of your version of this file only under the terms of
26 GPL, and not to allow others to use your version of this file under the terms
27 of the MIT license, indicate your decision by deleting the provisions above
28 and replace them with the notice and other provisions required by GPL as set
29 out in the file called "GPL-COPYING" included in this distribution. If you do
30 not delete the provisions above, a recipient may use your version of this file
31 under the terms of either the MIT license or GPL.
32
33 This License is also included in this distribution in the file called
34 "MIT-COPYING".
35
36 EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
37 PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
38 BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
39 PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
40 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
41 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
42 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 */ /**************************************************************************/
44
45 #include "rgxworkest.h"
46 #include "rgxfwutils.h"
47 #include "rgxdevice.h"
48 #include "rgxpdvfs.h"
49 #include "device.h"
50 #include "pvr_debug.h"
51
52 #define ROUND_DOWN_TO_NEAREST_1024(number) (((number) >> 10) << 10)
53
54 void WorkEstRCInit(WORKEST_HOST_DATA *psWorkEstData)
55 {
56         /* Create hash tables for workload matching */
57         psWorkEstData->sWorkloadMatchingDataTA.psWorkloadDataHash =
58                 HASH_Create_Extended(WORKLOAD_HASH_SIZE,
59                                                          sizeof(RGX_WORKLOAD_TA3D *),
60                                                          &WorkEstHashFuncTA3D,
61                                                          (HASH_KEY_COMP *)&WorkEstHashCompareTA3D);
62
63         /* Create a lock to protect the hash table */
64         WorkEstHashLockCreate(&(psWorkEstData->sWorkloadMatchingDataTA.psWorkEstHashLock));
65
66         psWorkEstData->sWorkloadMatchingData3D.psWorkloadDataHash =
67                 HASH_Create_Extended(WORKLOAD_HASH_SIZE,
68                                                          sizeof(RGX_WORKLOAD_TA3D *),
69                                                          &WorkEstHashFuncTA3D,
70                                                          (HASH_KEY_COMP *)&WorkEstHashCompareTA3D);
71
72         /* Create a lock to protect the hash tables */
73         WorkEstHashLockCreate(&(psWorkEstData->sWorkloadMatchingData3D.psWorkEstHashLock));
74 }
75
76 void WorkEstRCDeInit(WORKEST_HOST_DATA *psWorkEstData,
77                      PVRSRV_RGXDEV_INFO *psDevInfo)
78 {
79         HASH_TABLE        *psWorkloadDataHash;
80         RGX_WORKLOAD_TA3D *pasWorkloadHashKeys;
81         RGX_WORKLOAD_TA3D *psWorkloadHashKey;
82         IMG_UINT32        ui32i;
83         IMG_UINT64        *paui64WorkloadCycleData;
84
85         pasWorkloadHashKeys = psWorkEstData->sWorkloadMatchingDataTA.asWorkloadHashKeys;
86         paui64WorkloadCycleData = psWorkEstData->sWorkloadMatchingDataTA.aui64HashCycleData;
87         psWorkloadDataHash = psWorkEstData->sWorkloadMatchingDataTA.psWorkloadDataHash;
88
89         if(psWorkloadDataHash)
90         {
91                 for(ui32i = 0; ui32i < WORKLOAD_HASH_SIZE; ui32i++)
92                 {
93                         if(paui64WorkloadCycleData[ui32i] > 0)
94                         {
95                                 psWorkloadHashKey = &pasWorkloadHashKeys[ui32i];
96                                 HASH_Remove_Extended(psWorkloadDataHash,
97                                                                          (uintptr_t*)&psWorkloadHashKey);
98                         }
99                 }
100
101                 HASH_Delete(psWorkloadDataHash);
102         }
103
104         /* Remove the hash lock */
105         WorkEstHashLockDestroy(psWorkEstData->sWorkloadMatchingDataTA.psWorkEstHashLock);
106
107         pasWorkloadHashKeys = psWorkEstData->sWorkloadMatchingData3D.asWorkloadHashKeys;
108         paui64WorkloadCycleData = psWorkEstData->sWorkloadMatchingData3D.aui64HashCycleData;
109         psWorkloadDataHash = psWorkEstData->sWorkloadMatchingData3D.psWorkloadDataHash;
110
111         if(psWorkloadDataHash)
112         {
113                 for(ui32i = 0; ui32i < WORKLOAD_HASH_SIZE; ui32i++)
114                 {
115                         if(paui64WorkloadCycleData[ui32i] > 0)
116                         {
117                                 psWorkloadHashKey = &pasWorkloadHashKeys[ui32i];
118                                 HASH_Remove_Extended(psWorkloadDataHash,
119                                                                          (uintptr_t*)&psWorkloadHashKey);
120                         }
121                 }
122
123                 HASH_Delete(psWorkloadDataHash);
124         }
125
126         /* Remove the hash lock */
127         WorkEstHashLockDestroy(psWorkEstData->sWorkloadMatchingData3D.psWorkEstHashLock);
128
129         return;
130 }
131
132 IMG_BOOL WorkEstHashCompareTA3D(size_t uKeySize,
133                                                                 void *pKey1,
134                                                                 void *pKey2)
135 {
136         RGX_WORKLOAD_TA3D *psWorkload1;
137         RGX_WORKLOAD_TA3D *psWorkload2;
138
139         if(pKey1 && pKey2)
140         {
141                 psWorkload1 = *((RGX_WORKLOAD_TA3D **)pKey1);
142                 psWorkload2 = *((RGX_WORKLOAD_TA3D **)pKey2);
143
144                 PVR_ASSERT(psWorkload1);
145                 PVR_ASSERT(psWorkload2);
146
147                 if(psWorkload1->ui32RenderTargetSize == psWorkload2->ui32RenderTargetSize
148                    && psWorkload1->ui32NumberOfDrawCalls == psWorkload2->ui32NumberOfDrawCalls
149                    && psWorkload1->ui32NumberOfIndices == psWorkload2->ui32NumberOfIndices
150                    && psWorkload1->ui32NumberOfMRTs == psWorkload2->ui32NumberOfMRTs)
151                 {
152                         /* This is added to allow this memory to be freed */
153                         *(uintptr_t*)pKey2 = *(uintptr_t*)pKey1;
154                         return IMG_TRUE;
155                 }
156         }
157         return IMG_FALSE;
158 }
159
160 static inline IMG_UINT32 WorkEstDoHash(IMG_UINT32 ui32Input)
161 {
162         IMG_UINT32 ui32HashPart;
163
164         ui32HashPart = ui32Input;
165         ui32HashPart += (ui32HashPart << 12);
166         ui32HashPart ^= (ui32HashPart >> 22);
167         ui32HashPart += (ui32HashPart << 4);
168         ui32HashPart ^= (ui32HashPart >> 9);
169         ui32HashPart += (ui32HashPart << 10);
170         ui32HashPart ^= (ui32HashPart >> 2);
171         ui32HashPart += (ui32HashPart << 7);
172         ui32HashPart ^= (ui32HashPart >> 12);
173
174         return ui32HashPart;
175 }
176
177 IMG_UINT32 WorkEstHashFuncTA3D(size_t uKeySize, void *pKey, IMG_UINT32 uHashTabLen)
178 {
179         RGX_WORKLOAD_TA3D *psWorkload = *((RGX_WORKLOAD_TA3D**)pKey);
180         IMG_UINT32 ui32HashKey = 0;
181         PVR_UNREFERENCED_PARAMETER(uHashTabLen);
182         PVR_UNREFERENCED_PARAMETER(uKeySize);
183
184         ui32HashKey += WorkEstDoHash(psWorkload->ui32RenderTargetSize);
185         ui32HashKey += WorkEstDoHash(psWorkload->ui32NumberOfDrawCalls);
186         ui32HashKey += WorkEstDoHash(psWorkload->ui32NumberOfIndices);
187         ui32HashKey += WorkEstDoHash(psWorkload->ui32NumberOfMRTs);
188
189         return ui32HashKey;
190 }
191
192 PVRSRV_ERROR WorkEstPrepare(PVRSRV_RGXDEV_INFO        *psDevInfo,
193                             WORKEST_HOST_DATA         *psWorkEstHostData,
194                             WORKLOAD_MATCHING_DATA    *psWorkloadMatchingData,
195                             IMG_UINT32                ui32RenderTargetSize,
196                             IMG_UINT32                ui32NumberOfDrawCalls,
197                             IMG_UINT32                ui32NumberOfIndices,
198                             IMG_UINT32                ui32NumberOfMRTs,
199                             IMG_UINT64                ui64DeadlineInus,
200                             RGXFWIF_WORKEST_KICK_DATA *psWorkEstKickData)
201 {
202         PVRSRV_ERROR          eError;
203         RGX_WORKLOAD_TA3D     *psWorkloadCharacteristics;
204         IMG_UINT64            *pui64CyclePrediction;
205         POS_LOCK              psWorkEstHashLock;
206         IMG_UINT64            ui64WorkloadDeadlineInus = ui64DeadlineInus;
207         IMG_UINT64            ui64CurrentTime;
208         HASH_TABLE            *psWorkloadDataHash;
209         WORKEST_RETURN_DATA   *psReturnData;
210
211         if(psDevInfo == NULL)
212         {
213                 PVR_DPF((PVR_DBG_ERROR,"WorkEstPrepare: Device Info not available"));
214                 return PVRSRV_ERROR_INVALID_PARAMS;
215         }
216
217         if(psDevInfo->bWorkEstEnabled != IMG_TRUE)
218         {
219                 /* No error message to avoid excessive messages */
220                 return PVRSRV_OK;
221         }
222
223         if(psWorkEstHostData == NULL)
224         {
225                 PVR_DPF((PVR_DBG_ERROR,
226                          "WorkEstPrepare: Host data not available"));
227                 return PVRSRV_ERROR_INVALID_PARAMS;
228         }
229
230         if(psWorkloadMatchingData == NULL)
231         {
232                 PVR_DPF((PVR_DBG_ERROR,
233                          "WorkEstPrepare: Workload Matching Data not available"));
234                 return PVRSRV_ERROR_INVALID_PARAMS;
235         }
236
237         psWorkloadDataHash = psWorkloadMatchingData->psWorkloadDataHash;
238         if(psWorkloadDataHash == NULL)
239         {
240                 PVR_DPF((PVR_DBG_ERROR,"WorkEstPrepare: Hash Table not available"));
241                 return PVRSRV_ERROR_INVALID_PARAMS;
242         }
243
244         psWorkEstHashLock = psWorkloadMatchingData->psWorkEstHashLock;
245         if(psWorkEstHashLock == NULL)
246         {
247                 PVR_DPF((PVR_DBG_ERROR,
248                         "WorkEstPrepare: Hash lock not available"
249                         ));
250                 eError = PVRSRV_ERROR_UNABLE_TO_RETRIEVE_HASH_VALUE;
251                 return eError;
252         }
253
254         eError = OSClockMonotonicus64(&ui64CurrentTime);
255         if(eError != PVRSRV_OK)
256         {
257                 PVR_DPF((PVR_DBG_ERROR,
258                          "WorkEstPrepare: Unable to access System Monotonic clock"));
259                 PVR_ASSERT(eError == PVRSRV_OK);
260                 return eError;
261         }
262
263 #if defined(SUPPORT_PDVFS)
264         psDevInfo->psDeviceNode->psDevConfig->sDVFS.sPDVFSData.bWorkInFrame = IMG_TRUE;
265 #endif
266
267         /* Set up data for the return path to process the workload */
268
269         /* Any host side data needed for the return path is stored in an array and
270          * only the array's index is passed to and from the firmware. This is a
271          * similar abstraction to using handles but is optimised for this case.
272          */
273         psReturnData =
274                 &psDevInfo->asReturnData[psDevInfo->ui32ReturnDataWO];
275
276         /* The index for the specific data is passed to the FW */
277         psWorkEstKickData->ui64ReturnDataIndex = psDevInfo->ui32ReturnDataWO;
278
279         psDevInfo->ui32ReturnDataWO =
280                 (psDevInfo->ui32ReturnDataWO + 1) & RETURN_DATA_ARRAY_WRAP_MASK;
281
282         /* The workload characteristics are needed in the return data for the
283          * matching of future workloads via the hash.
284          */
285         psWorkloadCharacteristics = &psReturnData->sWorkloadCharacteristics;
286         psWorkloadCharacteristics->ui32RenderTargetSize = ui32RenderTargetSize;
287         psWorkloadCharacteristics->ui32NumberOfDrawCalls = ui32NumberOfDrawCalls;
288         psWorkloadCharacteristics->ui32NumberOfIndices = ui32NumberOfIndices;
289         psWorkloadCharacteristics->ui32NumberOfMRTs = ui32NumberOfMRTs;
290
291         /* The matching data is needed as it holds the hash data. */
292         psReturnData->psWorkloadMatchingData = psWorkloadMatchingData;
293
294         /* The host data for the completion updates */
295         psReturnData->psWorkEstHostData = psWorkEstHostData;
296         if(ui64WorkloadDeadlineInus > ui64CurrentTime)
297         {
298                 /* This is rounded to reduce multiple deadlines with a minor spread
299                  * flooding the fw workload array.
300                  */
301                 psWorkEstKickData->ui64DeadlineInus =
302                         ROUND_DOWN_TO_NEAREST_1024(ui64WorkloadDeadlineInus);
303         }
304         else
305         {
306                 /* If the deadline has already passed assign as zero to suggest full
307                  * frequency
308                  */
309                 psWorkEstKickData->ui64DeadlineInus = 0;
310         }
311
312         /* Acquire the lock to access hash */
313         OSLockAcquire(psWorkEstHashLock);
314
315         /* Check if there is a prediction for this workload */
316         pui64CyclePrediction =
317                 (IMG_UINT64*) HASH_Retrieve(psWorkloadDataHash,
318                                             (uintptr_t)psWorkloadCharacteristics);
319
320         /* Release lock */
321         OSLockRelease(psWorkEstHashLock);
322
323         if(pui64CyclePrediction != NULL)
324         {
325                 /* Cycle prediction is available, store this prediction */
326                 psWorkEstKickData->ui64CyclesPrediction = *pui64CyclePrediction;
327         }
328         else
329         {
330                 /* There is no prediction */
331                 psWorkEstKickData->ui64CyclesPrediction = 0;
332         }
333
334         return PVRSRV_OK;
335 }
336
337 PVRSRV_ERROR WorkEstWorkloadFinished(PVRSRV_RGXDEV_INFO        *psDevInfo,
338                                      RGXFWIF_WORKEST_FWCCB_CMD *psReturnCmd)
339 {
340         RGX_WORKLOAD_TA3D           *psWorkloadCharacteristics;
341         RGX_WORKLOAD_TA3D           *pasWorkloadHashKeys;
342         IMG_UINT64                  *paui64HashCycleData;
343         IMG_UINT32                  *pui32HashArrayWO;
344         RGX_WORKLOAD_TA3D           *psWorkloadHashKey;
345         IMG_UINT64                  *pui64CyclesTaken;
346         HASH_TABLE                  *psWorkloadHash;
347         WORKLOAD_MATCHING_DATA      *psWorkloadMatchingData;
348         POS_LOCK                    psWorkEstHashLock;
349         IMG_BOOL                    bHashSucess;
350         WORKEST_RETURN_DATA         *psReturnData;
351         WORKEST_HOST_DATA           *psWorkEstHostData;
352         PVRSRV_ERROR                eError = PVRSRV_OK;
353
354         if(psDevInfo->bWorkEstEnabled != IMG_TRUE)
355         {
356                 /* No error message to avoid excessive messages */
357                 return PVRSRV_OK;
358         }
359
360         if(psReturnCmd == NULL)
361         {
362                 PVR_DPF((PVR_DBG_ERROR,
363                         "WorkEstFinished: Missing Return Command"));
364                 return PVRSRV_ERROR_INVALID_PARAMS;
365         }
366
367         if(psReturnCmd->ui64ReturnDataIndex >= RETURN_DATA_ARRAY_SIZE)
368         {
369                 PVR_DPF((PVR_DBG_ERROR,
370                         "WorkEstFinished: Handle Reference Out of Bounds"));
371                 return PVRSRV_ERROR_INVALID_PARAMS;
372         }
373
374         /* Retrieve the return data for this workload */
375         psReturnData = &psDevInfo->asReturnData[psReturnCmd->ui64ReturnDataIndex];
376
377         psWorkEstHostData = psReturnData->psWorkEstHostData;
378
379         if(psWorkEstHostData == NULL)
380         {
381                 PVR_DPF((PVR_DBG_ERROR,
382                         "WorkEstFinished: Missing host data"));
383                 eError = PVRSRV_ERROR_INVALID_PARAMS;
384                 return eError;
385         }
386
387         psWorkloadCharacteristics = &psReturnData->sWorkloadCharacteristics;
388
389         if(psWorkloadCharacteristics == NULL)
390         {
391                 PVR_DPF((PVR_DBG_ERROR,
392                         "WorkEstFinished: Missing workload characteristics"));
393                 eError = PVRSRV_ERROR_INVALID_PARAMS;
394                 goto hasherror;
395         }
396
397         psWorkloadMatchingData = psReturnData->psWorkloadMatchingData;
398
399         psWorkloadHash = psWorkloadMatchingData->psWorkloadDataHash;
400         if(psWorkloadHash == NULL)
401         {
402                 PVR_DPF((PVR_DBG_ERROR,
403                         "WorkEstFinished: Missing hash"));
404                 eError = PVRSRV_ERROR_INVALID_PARAMS;
405                 goto hasherror;
406         }
407
408         psWorkEstHashLock = psWorkloadMatchingData->psWorkEstHashLock;
409         if(psWorkEstHashLock == NULL)
410         {
411                 PVR_DPF((PVR_DBG_ERROR,
412                         "WorkEstFinished: Missing hash lock"));
413                 eError = PVRSRV_ERROR_INVALID_PARAMS;
414                 goto hasherror;
415         }
416
417         OSLockAcquire(psWorkEstHashLock);
418
419         pui64CyclesTaken =
420                 (IMG_UINT64*) HASH_Remove_Extended(psWorkloadHash,
421                                                    (uintptr_t*)&psWorkloadCharacteristics);
422
423         pui32HashArrayWO = &(psWorkloadMatchingData->ui32HashArrayWO);
424         paui64HashCycleData = psWorkloadMatchingData->aui64HashCycleData;
425         pasWorkloadHashKeys = psWorkloadMatchingData->asWorkloadHashKeys;
426
427         /* Remove the oldest Hash data before it becomes overwritten */
428         if(paui64HashCycleData[*pui32HashArrayWO] > 0)
429         {
430                 psWorkloadHashKey = &pasWorkloadHashKeys[*pui32HashArrayWO];
431                 HASH_Remove_Extended(psWorkloadHash,
432                                      (uintptr_t*)&psWorkloadHashKey);
433         }
434
435         if(pui64CyclesTaken == NULL)
436         {
437                 /* There is no existing entry for these characteristics. */
438                 pasWorkloadHashKeys[*pui32HashArrayWO] = *psWorkloadCharacteristics;
439
440                 paui64HashCycleData[*pui32HashArrayWO] = psReturnCmd->ui64CyclesTaken;
441         }
442         else
443         {
444                 *pui64CyclesTaken =
445                         (*pui64CyclesTaken + psReturnCmd->ui64CyclesTaken)/2;
446
447                 pasWorkloadHashKeys[*pui32HashArrayWO] = *psWorkloadCharacteristics;
448
449                 paui64HashCycleData[*pui32HashArrayWO] = *pui64CyclesTaken;
450
451                 /* Set the old value to 0 so it is known to be invalid */
452                 *pui64CyclesTaken = 0;
453         }
454
455
456         bHashSucess = HASH_Insert((HASH_TABLE*)(psWorkloadHash),
457                         (uintptr_t)&pasWorkloadHashKeys[*pui32HashArrayWO],
458                         (uintptr_t)&paui64HashCycleData[*pui32HashArrayWO]);
459         PVR_ASSERT(bHashSucess);
460
461         if(*pui32HashArrayWO == WORKLOAD_HASH_SIZE-1)
462         {
463                 *pui32HashArrayWO = 0;
464         }
465         else
466         {
467                 (*pui32HashArrayWO)++;
468         }
469
470         OSLockRelease(psWorkEstHashLock);
471
472 hasherror:
473
474         /* Update the received counter so that the FW is able to check as to whether
475          * all the workloads connected to a render context are finished.
476          */
477         psWorkEstHostData->ui32WorkEstCCBReceived++;
478         return eError;
479 }
480
481 void WorkEstHashLockCreate(POS_LOCK *psWorkEstHashLock)
482 {
483         if(*psWorkEstHashLock == NULL)
484         {
485                 OSLockCreate(psWorkEstHashLock, LOCK_TYPE_DISPATCH);
486         }
487         return;
488 }
489
490 void WorkEstHashLockDestroy(POS_LOCK sWorkEstHashLock)
491 {
492         if(sWorkEstHashLock != NULL)
493         {
494                 OSLockDestroy(sWorkEstHashLock);
495                 sWorkEstHashLock = NULL;
496         }
497         return;
498 }
499
500 void WorkEstCheckFirmwareCCB(PVRSRV_RGXDEV_INFO *psDevInfo)
501 {
502         RGXFWIF_WORKEST_FWCCB_CMD *psFwCCBCmd;
503
504         RGXFWIF_CCB_CTL *psFWCCBCtl = psDevInfo->psWorkEstFirmwareCCBCtl;
505         IMG_UINT8 *psFWCCB = psDevInfo->psWorkEstFirmwareCCB;
506
507         while (psFWCCBCtl->ui32ReadOffset != psFWCCBCtl->ui32WriteOffset)
508         {
509                 /* Point to the next command */
510                 psFwCCBCmd = ((RGXFWIF_WORKEST_FWCCB_CMD *)psFWCCB) + psFWCCBCtl->ui32ReadOffset;
511
512                 WorkEstWorkloadFinished(psDevInfo, psFwCCBCmd);
513
514                 /* Update read offset */
515                 psFWCCBCtl->ui32ReadOffset = (psFWCCBCtl->ui32ReadOffset + 1) & psFWCCBCtl->ui32WrapMask;
516         }
517 }