| /*************************************************************************/ /*! |
| @File |
| @Title RGX HW Performance implementation |
| @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved |
| @Description RGX HW Performance implementation |
| @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. |
| */ /**************************************************************************/ |
| |
| //#define PVR_DPF_FUNCTION_TRACE_ON 1 |
| #undef PVR_DPF_FUNCTION_TRACE_ON |
| |
| #include "pvr_debug.h" |
| #include "pvr_hwperf.h" |
| #include "pvr_notifier.h" |
| #include "osfunc.h" |
| #include "allocmem.h" |
| |
| #include "pvrsrv.h" |
| #include "pvrsrv_tlstreams.h" |
| #include "pvrsrv_tlcommon.h" |
| #include "tlclient.h" |
| #include "tlstream.h" |
| |
| #include "rgx_hwperf_km.h" |
| #include "rgxhwperf.h" |
| #include "rgxapi_km.h" |
| #include "rgxfwutils.h" |
| #include "rgxtimecorr.h" |
| #include "devicemem.h" |
| #include "devicemem_pdump.h" |
| #include "pdump_km.h" |
| #include "pvrsrv_apphint.h" |
| |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| #include "pvr_gputrace.h" |
| #endif |
| |
| /* Defined to ensure HWPerf packets are not delayed */ |
| #define SUPPORT_TL_PROODUCER_CALLBACK 1 |
| |
| /* Defines size of buffers returned from acquire/release calls */ |
| #define FW_STREAM_BUFFER_SIZE (0x80000) |
| #define HOST_STREAM_BUFFER_SIZE (0x20000) |
| |
| /* Must be at least as large as two tl packets of maximum size */ |
| static_assert(HOST_STREAM_BUFFER_SIZE >= (PVRSRVTL_MAX_PACKET_SIZE<<1), |
| "HOST_STREAM_BUFFER_SIZE is less than (PVRSRVTL_MAX_PACKET_SIZE<<1)"); |
| static_assert(FW_STREAM_BUFFER_SIZE >= (PVRSRVTL_MAX_PACKET_SIZE<<1), |
| "FW_STREAM_BUFFER_SIZE is less than (PVRSRVTL_MAX_PACKET_SIZE<<1)"); |
| |
| |
| /* |
| RGXHWPerfCopyDataL1toL2 |
| */ |
| static IMG_UINT32 RGXHWPerfCopyDataL1toL2(IMG_HANDLE hHWPerfStream, |
| IMG_BYTE *pbFwBuffer, |
| IMG_UINT32 ui32BytesExp) |
| { |
| IMG_BYTE *pbL2Buffer; |
| IMG_UINT32 ui32L2BufFree; |
| IMG_UINT32 ui32BytesCopied = 0; |
| IMG_UINT32 ui32BytesExpMin = RGX_HWPERF_GET_SIZE(RGX_HWPERF_GET_PACKET(pbFwBuffer)); |
| PVRSRV_ERROR eError; |
| |
| /* HWPERF_MISR_FUNC_DEBUG enables debug code for investigating HWPerf issues */ |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| static IMG_UINT32 gui32Ordinal = IMG_UINT32_MAX; |
| #endif |
| |
| PVR_DPF_ENTERED; |
| |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| PVR_DPF((PVR_DBG_VERBOSE, "EVENTS to copy from 0x%p length:%05d", |
| pbFwBuffer, ui32BytesExp)); |
| #endif |
| |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| { |
| /* Check the incoming buffer of data has not lost any packets */ |
| IMG_BYTE *pbFwBufferIter = pbFwBuffer; |
| IMG_BYTE *pbFwBufferEnd = pbFwBuffer+ui32BytesExp; |
| do |
| { |
| RGX_HWPERF_V2_PACKET_HDR *asCurPos = RGX_HWPERF_GET_PACKET(pbFwBufferIter); |
| IMG_UINT32 ui32CurOrdinal = asCurPos->ui32Ordinal; |
| if (gui32Ordinal != IMG_UINT32_MAX) |
| { |
| if ((gui32Ordinal+1) != ui32CurOrdinal) |
| { |
| if (gui32Ordinal < ui32CurOrdinal) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "HWPerf [%p] packets lost (%u packets) between ordinal %u...%u", |
| pbFwBufferIter, |
| ui32CurOrdinal - gui32Ordinal - 1, |
| gui32Ordinal, |
| ui32CurOrdinal)); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "HWPerf [%p] packet ordinal out of sequence last: %u, current: %u", |
| pbFwBufferIter, |
| gui32Ordinal, |
| ui32CurOrdinal)); |
| } |
| } |
| } |
| gui32Ordinal = asCurPos->ui32Ordinal; |
| pbFwBufferIter += RGX_HWPERF_GET_SIZE(asCurPos); |
| } while( pbFwBufferIter < pbFwBufferEnd ); |
| } |
| #endif |
| |
| /* Try submitting all data in one TL packet. */ |
| eError = TLStreamReserve2( hHWPerfStream, |
| &pbL2Buffer, |
| (size_t)ui32BytesExp, ui32BytesExpMin, |
| &ui32L2BufFree); |
| if ( eError == PVRSRV_OK ) |
| { |
| OSDeviceMemCopy( pbL2Buffer, pbFwBuffer, (size_t)ui32BytesExp ); |
| eError = TLStreamCommit(hHWPerfStream, (size_t)ui32BytesExp); |
| if ( eError != PVRSRV_OK ) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "TLStreamCommit() failed (%d) in %s(), unable to copy packet from L1 to L2 buffer", |
| eError, __func__)); |
| goto e0; |
| } |
| /* Data were successfully written */ |
| ui32BytesCopied = ui32BytesExp; |
| } |
| else if (eError == PVRSRV_ERROR_STREAM_RESERVE_TOO_BIG) |
| { |
| /* There was not enough space for all data, copy as much as possible */ |
| IMG_UINT32 sizeSum = 0; |
| RGX_PHWPERF_V2_PACKET_HDR psCurPkt = RGX_HWPERF_GET_PACKET(pbFwBuffer); |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "Unable to reserve space (%d) in host buffer on first attempt, remaining free space: %d", ui32BytesExp, ui32L2BufFree)); |
| |
| /* Traverse the array to find how many packets will fit in the available space. */ |
| while ( sizeSum < ui32BytesExp && |
| sizeSum + RGX_HWPERF_GET_SIZE(psCurPkt) < ui32L2BufFree ) |
| { |
| sizeSum += RGX_HWPERF_GET_SIZE(psCurPkt); |
| psCurPkt = RGX_HWPERF_GET_NEXT_PACKET(psCurPkt); |
| } |
| |
| if ( 0 != sizeSum ) |
| { |
| eError = TLStreamReserve( hHWPerfStream, &pbL2Buffer, (size_t)sizeSum); |
| |
| if ( eError == PVRSRV_OK ) |
| { |
| OSDeviceMemCopy( pbL2Buffer, pbFwBuffer, (size_t)sizeSum ); |
| eError = TLStreamCommit(hHWPerfStream, (size_t)sizeSum); |
| if ( eError != PVRSRV_OK ) |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "TLStreamCommit() failed (%d) in %s(), unable to copy packet from L1 to L2 buffer", |
| eError, __func__)); |
| goto e0; |
| } |
| /* sizeSum bytes of hwperf packets have been successfully written */ |
| ui32BytesCopied = sizeSum; |
| } |
| else if ( PVRSRV_ERROR_STREAM_RESERVE_TOO_BIG == eError ) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "Can not write HWPerf packet into host buffer, check data in case of packet loss, remaining free space: %d", ui32L2BufFree)); |
| } |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "Can not find space in host buffer, check data in case of packet loss, remaining free space: %d", ui32L2BufFree)); |
| } |
| } |
| if ( PVRSRV_OK != eError && /* Some other error occurred */ |
| PVRSRV_ERROR_STREAM_RESERVE_TOO_BIG != eError ) /* Full error handled by caller, we returning the copied bytes count to caller*/ |
| { |
| PVR_DPF((PVR_DBG_ERROR, |
| "HWPerf enabled: Unexpected Error ( %d ) while copying FW buffer to TL buffer.", |
| eError)); |
| } |
| |
| e0: |
| /* Return the remaining packets left to be transported. */ |
| PVR_DPF_RETURN_VAL(ui32BytesCopied); |
| } |
| |
| |
| static INLINE IMG_UINT32 RGXHWPerfAdvanceRIdx( |
| const IMG_UINT32 ui32BufSize, |
| const IMG_UINT32 ui32Pos, |
| const IMG_UINT32 ui32Size) |
| { |
| return ( ui32Pos + ui32Size < ui32BufSize ? ui32Pos + ui32Size : 0 ); |
| } |
| |
| |
| /* |
| RGXHWPerfDataStore |
| */ |
| static IMG_UINT32 RGXHWPerfDataStore(PVRSRV_RGXDEV_INFO *psDevInfo) |
| { |
| RGXFWIF_TRACEBUF *psRGXFWIfTraceBufCtl = psDevInfo->psRGXFWIfTraceBuf; |
| IMG_BYTE* psHwPerfInfo = psDevInfo->psRGXFWIfHWPerfBuf; |
| IMG_UINT32 ui32SrcRIdx, ui32SrcWIdx, ui32SrcWrapCount; |
| IMG_UINT32 ui32BytesExp = 0, ui32BytesCopied = 0, ui32BytesCopiedSum = 0; |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| IMG_UINT32 ui32BytesExpSum = 0; |
| #endif |
| |
| PVR_DPF_ENTERED; |
| |
| /* Caller should check this member is valid before calling */ |
| PVR_ASSERT(psDevInfo->hHWPerfStream); |
| |
| /* Get a copy of the current |
| * read (first packet to read) |
| * write (empty location for the next write to be inserted) |
| * WrapCount (size in bytes of the buffer at or past end) |
| * indexes of the FW buffer */ |
| ui32SrcRIdx = psRGXFWIfTraceBufCtl->ui32HWPerfRIdx; |
| ui32SrcWIdx = psRGXFWIfTraceBufCtl->ui32HWPerfWIdx; |
| OSMemoryBarrier(); |
| ui32SrcWrapCount = psRGXFWIfTraceBufCtl->ui32HWPerfWrapCount; |
| |
| /* Is there any data in the buffer not yet retrieved? */ |
| if ( ui32SrcRIdx != ui32SrcWIdx ) |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "RGXHWPerfDataStore EVENTS found srcRIdx:%d srcWIdx: %d ", ui32SrcRIdx, ui32SrcWIdx)); |
| |
| /* Is the write position higher than the read position? */ |
| if ( ui32SrcWIdx > ui32SrcRIdx ) |
| { |
| /* Yes, buffer has not wrapped */ |
| ui32BytesExp = ui32SrcWIdx - ui32SrcRIdx; |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| ui32BytesExpSum += ui32BytesExp; |
| #endif |
| ui32BytesCopied = RGXHWPerfCopyDataL1toL2(psDevInfo->hHWPerfStream, |
| psHwPerfInfo + ui32SrcRIdx, |
| ui32BytesExp); |
| |
| /* Advance the read index and the free bytes counter by the number |
| * of bytes transported. Items will be left in buffer if not all data |
| * could be transported. Exit to allow buffer to drain. */ |
| psRGXFWIfTraceBufCtl->ui32HWPerfRIdx = RGXHWPerfAdvanceRIdx( |
| psDevInfo->ui32RGXFWIfHWPerfBufSize, ui32SrcRIdx, |
| ui32BytesCopied); |
| |
| ui32BytesCopiedSum += ui32BytesCopied; |
| } |
| /* No, buffer has wrapped and write position is behind read position */ |
| else |
| { |
| /* Byte count equal to |
| * number of bytes from read position to the end of the buffer, |
| * + data in the extra space in the end of the buffer. */ |
| ui32BytesExp = ui32SrcWrapCount - ui32SrcRIdx; |
| |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| ui32BytesExpSum += ui32BytesExp; |
| #endif |
| /* Attempt to transfer the packets to the TL stream buffer */ |
| ui32BytesCopied = RGXHWPerfCopyDataL1toL2(psDevInfo->hHWPerfStream, |
| psHwPerfInfo + ui32SrcRIdx, |
| ui32BytesExp); |
| |
| /* Advance read index as before and Update the local copy of the |
| * read index as it might be used in the last if branch*/ |
| ui32SrcRIdx = RGXHWPerfAdvanceRIdx( |
| psDevInfo->ui32RGXFWIfHWPerfBufSize, ui32SrcRIdx, |
| ui32BytesCopied); |
| |
| /* Update Wrap Count */ |
| if ( ui32SrcRIdx == 0) |
| { |
| psRGXFWIfTraceBufCtl->ui32HWPerfWrapCount = psDevInfo->ui32RGXFWIfHWPerfBufSize; |
| } |
| psRGXFWIfTraceBufCtl->ui32HWPerfRIdx = ui32SrcRIdx; |
| |
| ui32BytesCopiedSum += ui32BytesCopied; |
| |
| /* If all the data in the end of the array was copied, try copying |
| * wrapped data in the beginning of the array, assuming there is |
| * any and the RIdx was wrapped. */ |
| if ( (ui32BytesCopied == ui32BytesExp) |
| && (ui32SrcWIdx > 0) |
| && (ui32SrcRIdx == 0) ) |
| { |
| ui32BytesExp = ui32SrcWIdx; |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| ui32BytesExpSum += ui32BytesExp; |
| #endif |
| ui32BytesCopied = RGXHWPerfCopyDataL1toL2(psDevInfo->hHWPerfStream, |
| psHwPerfInfo, |
| ui32BytesExp); |
| /* Advance the FW buffer read position. */ |
| psRGXFWIfTraceBufCtl->ui32HWPerfRIdx = RGXHWPerfAdvanceRIdx( |
| psDevInfo->ui32RGXFWIfHWPerfBufSize, ui32SrcRIdx, |
| ui32BytesCopied); |
| |
| ui32BytesCopiedSum += ui32BytesCopied; |
| } |
| } |
| #ifdef HWPERF_MISR_FUNC_DEBUG |
| if (ui32BytesCopiedSum != ui32BytesExpSum) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "RGXHWPerfDataStore: FW L1 RIdx:%u. Not all bytes copied to L2: %u bytes out of %u expected", psRGXFWIfTraceBufCtl->ui32HWPerfRIdx, ui32BytesCopiedSum, ui32BytesExpSum)); |
| } |
| #endif |
| |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_VERBOSE, "RGXHWPerfDataStore NO EVENTS to transport")); |
| } |
| |
| PVR_DPF_RETURN_VAL(ui32BytesCopiedSum); |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfDataStoreCB(PVRSRV_DEVICE_NODE *psDevInfo) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_RGXDEV_INFO* psRgxDevInfo; |
| IMG_UINT32 ui32BytesCopied; |
| |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(psDevInfo); |
| psRgxDevInfo = psDevInfo->pvDevice; |
| |
| /* Keep HWPerf resource init check and use of |
| * resources atomic, they may not be freed during use |
| */ |
| OSLockAcquire(psRgxDevInfo->hHWPerfLock); |
| |
| if (psRgxDevInfo->hHWPerfStream != 0) |
| { |
| ui32BytesCopied = RGXHWPerfDataStore(psRgxDevInfo); |
| if ( ui32BytesCopied ) |
| { /* Signal consumers that packets may be available to read when |
| * running from a HW kick, not when called by client APP thread |
| * via the transport layer CB as this can lead to stream |
| * corruption.*/ |
| eError = TLStreamSync(psRgxDevInfo->hHWPerfStream); |
| PVR_ASSERT(eError == PVRSRV_OK); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "RGXHWPerfDataStoreCB: Zero bytes copied")); |
| RGXDEBUG_PRINT_IRQ_COUNT(psRgxDevInfo); |
| } |
| } |
| |
| OSLockRelease(psRgxDevInfo->hHWPerfLock); |
| |
| PVR_DPF_RETURN_OK; |
| } |
| |
| |
| /* Not currently supported by default */ |
| #if defined(SUPPORT_TL_PROODUCER_CALLBACK) |
| static PVRSRV_ERROR RGXHWPerfTLCB(IMG_HANDLE hStream, |
| IMG_UINT32 ui32ReqOp, IMG_UINT32* ui32Resp, void* pvUser) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_RGXDEV_INFO* psRgxDevInfo = (PVRSRV_RGXDEV_INFO*)pvUser; |
| |
| PVR_UNREFERENCED_PARAMETER(hStream); |
| PVR_UNREFERENCED_PARAMETER(ui32Resp); |
| |
| PVR_ASSERT(psRgxDevInfo); |
| |
| switch (ui32ReqOp) |
| { |
| case TL_SOURCECB_OP_CLIENT_EOS: |
| /* Keep HWPerf resource init check and use of |
| * resources atomic, they may not be freed during use |
| */ |
| OSLockAcquire(psRgxDevInfo->hHWPerfLock); |
| if (psRgxDevInfo->hHWPerfStream != 0) |
| { |
| (void) RGXHWPerfDataStore(psRgxDevInfo); |
| } |
| OSLockRelease(psRgxDevInfo->hHWPerfLock); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return eError; |
| } |
| #endif |
| |
| |
| /* References to key objects to allow kernel-side behaviour to function |
| * e.g. FTrace and KM interface to HWPerf. |
| */ |
| static PVRSRV_DEVICE_NODE* gpsRgxDevNode = NULL; |
| static PVRSRV_RGXDEV_INFO* gpsRgxDevInfo = NULL; |
| |
| static void RGXHWPerfL1BufferDeinit(void) |
| { |
| if (gpsRgxDevInfo && gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc) |
| { |
| if (gpsRgxDevInfo->psRGXFWIfHWPerfBuf != NULL) |
| { |
| DevmemReleaseCpuVirtAddr(gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc); |
| gpsRgxDevInfo->psRGXFWIfHWPerfBuf = NULL; |
| } |
| DevmemFwFree(gpsRgxDevInfo, gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc); |
| gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc = NULL; |
| } |
| } |
| |
| /*************************************************************************/ /*! |
| @Function RGXHWPerfInit |
| |
| @Description Called during driver init for initialization of HWPerf module |
| in the Rogue device driver. This function keeps allocated |
| only the minimal necessary resources, which are required for |
| functioning of HWPerf server module. |
| |
| @Input psRgxDevInfo RGX Device Node |
| |
| @Return PVRSRV_ERROR |
| */ /**************************************************************************/ |
| PVRSRV_ERROR RGXHWPerfInit(PVRSRV_DEVICE_NODE *psRgxDevNode) |
| { |
| PVRSRV_ERROR eError; |
| |
| PVR_DPF_ENTERED; |
| |
| /* expecting a valid device node */ |
| PVR_ASSERT(psRgxDevNode); |
| |
| /* Keep RGX device's reference for later use as this parameter is |
| * optional on later calls to HWPerf server module */ |
| gpsRgxDevNode = psRgxDevNode; |
| gpsRgxDevInfo = psRgxDevNode->pvDevice; |
| |
| /* Create a lock for HWPerf server module used for serializing, L1 to L2 |
| * copy calls (e.g. in case of TL producer callback) and L1, L2 resource |
| * allocation */ |
| eError = OSLockCreate(&gpsRgxDevInfo->hHWPerfLock, LOCK_TYPE_PASSIVE); |
| PVR_LOGR_IF_ERROR(eError, "OSLockCreate"); |
| |
| /* avoid uninitialised data */ |
| gpsRgxDevInfo->hHWPerfStream = 0; |
| gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc = NULL; |
| |
| PVR_DPF_RETURN_OK; |
| } |
| |
| /*************************************************************************/ /*! |
| @Function RGXHWPerfIsInitRequired |
| |
| @Description Returns true if the HWperf firmware buffer (L1 buffer) and host |
| driver TL buffer (L2 buffer) are not already allocated. Caller |
| must possess hHWPerfLock lock before calling this |
| function so the state tested is not inconsistent. |
| |
| @Return IMG_BOOL Whether initialization (allocation) is required |
| */ /**************************************************************************/ |
| static INLINE IMG_BOOL RGXHWPerfIsInitRequired(void) |
| { |
| PVR_ASSERT(OSLockIsLocked(gpsRgxDevInfo->hHWPerfLock)); |
| |
| #if !defined (NO_HARDWARE) |
| /* Both L1 and L2 buffers are required (for HWPerf functioning) on driver |
| * built for actual hardware (TC, EMU, etc.) |
| */ |
| if (gpsRgxDevInfo->hHWPerfStream == 0) |
| { |
| /* The allocation API (RGXHWPerfInitOnDemandResources) allocates |
| * device memory for both L1 and L2 without any checks. Hence, |
| * either both should be allocated or both be NULL. |
| * |
| * In-case this changes in future (for e.g. a situation where one |
| * of the 2 buffers is already allocated and other is required), |
| * add required checks before allocation calls to avoid memory leaks. |
| */ |
| PVR_ASSERT(gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc == NULL); |
| return IMG_TRUE; |
| } |
| PVR_ASSERT(gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc != NULL); |
| #else |
| /* On a NO-HW driver L2 is not allocated. So, no point in checking its |
| * allocation */ |
| if (gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc == NULL) |
| { |
| return IMG_TRUE; |
| } |
| #endif |
| return IMG_FALSE; |
| } |
| |
| /*************************************************************************/ /*! |
| @Function RGXHWPerfInitOnDemandResources |
| |
| @Description This function allocates the HWperf firmware buffer (L1 buffer) |
| and host driver TL buffer (L2 buffer) if HWPerf is enabled at |
| driver load time. Otherwise, these buffers are allocated |
| on-demand as and when required. Caller |
| must possess hHWPerfLock lock before calling this |
| function so the state tested is not inconsistent if called |
| outside of driver initialisation. |
| |
| @Return PVRSRV_ERROR |
| */ /**************************************************************************/ |
| PVRSRV_ERROR RGXHWPerfInitOnDemandResources(void) |
| { |
| PVRSRV_ERROR eError; |
| IMG_UINT32 ui32L2BufferSize; |
| DEVMEM_FLAGS_T uiMemAllocFlags; |
| |
| PVR_DPF_ENTERED; |
| |
| /* Create the L1 HWPerf buffer on demand */ |
| uiMemAllocFlags = PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) |
| | PVRSRV_MEMALLOCFLAG_GPU_READABLE |
| | PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE |
| | PVRSRV_MEMALLOCFLAG_CPU_READABLE |
| | PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE |
| | PVRSRV_MEMALLOCFLAG_UNCACHED |
| #if defined(PDUMP) |
| | PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC |
| #endif |
| ; |
| |
| /* Allocate HWPerf FW L1 buffer */ |
| eError = DevmemFwAllocate(gpsRgxDevInfo, |
| gpsRgxDevInfo->ui32RGXFWIfHWPerfBufSize+RGXFW_HWPERF_L1_PADDING_DEFAULT, |
| uiMemAllocFlags, |
| "FwHWPerfBuffer", |
| &gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate kernel fw hwperf buffer (%u)", |
| __FUNCTION__, eError)); |
| goto e0; |
| } |
| |
| /* Expecting the RuntimeCfg structure is mapped into CPU virtual memory. |
| * Also, make sure the FW address is not already set */ |
| PVR_ASSERT(gpsRgxDevInfo->psRGXFWIfRuntimeCfg && gpsRgxDevInfo->psRGXFWIfRuntimeCfg->sHWPerfBuf.ui32Addr == 0x0); |
| |
| /* Meta cached flag removed from this allocation as it was found |
| * FW performance was better without it. */ |
| RGXSetFirmwareAddress(&gpsRgxDevInfo->psRGXFWIfRuntimeCfg->sHWPerfBuf, |
| gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc, |
| 0, RFW_FWADDR_NOREF_FLAG); |
| |
| eError = DevmemAcquireCpuVirtAddr(gpsRgxDevInfo->psRGXFWIfHWPerfBufMemDesc, |
| (void**)&gpsRgxDevInfo->psRGXFWIfHWPerfBuf); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to acquire kernel hwperf buffer (%u)", |
| __FUNCTION__, eError)); |
| goto e0; |
| } |
| |
| /* On NO-HW driver, there is no MISR installed to copy data from L1 to L2. Hence, |
| * L2 buffer is not allocated */ |
| #if !defined(NO_HARDWARE) |
| /* Host L2 HWPERF buffer size in bytes must be bigger than the L1 buffer |
| * accessed by the FW. The MISR may try to write one packet the size of the L1 |
| * buffer in some scenarios. When logging is enabled in the MISR, it can be seen |
| * if the L2 buffer hits a full condition. The closer in size the L2 and L1 buffers |
| * are the more chance of this happening. |
| * Size chosen to allow MISR to write an L1 sized packet and for the client |
| * application/daemon to drain a L1 sized packet e.g. ~ 1.5*L1. |
| */ |
| ui32L2BufferSize = gpsRgxDevInfo->ui32RGXFWIfHWPerfBufSize + |
| (gpsRgxDevInfo->ui32RGXFWIfHWPerfBufSize>>1); |
| eError = TLStreamCreate(&gpsRgxDevInfo->hHWPerfStream, PVRSRV_TL_HWPERF_RGX_FW_STREAM, |
| ui32L2BufferSize, |
| TL_FLAG_RESERVE_DROP_NEWER | TL_FLAG_NO_SIGNAL_ON_COMMIT, |
| NULL, NULL, |
| #if !defined(SUPPORT_TL_PROODUCER_CALLBACK) |
| NULL, NULL |
| #else |
| /* Not enabled by default */ |
| RGXHWPerfTLCB, gpsRgxDevInfo |
| #endif |
| ); |
| PVR_LOGG_IF_ERROR(eError, "TLStreamCreate", e1); |
| #else /* defined (NO_HARDWARE) */ |
| PVR_UNREFERENCED_PARAMETER(ui32L2BufferSize); |
| PVR_UNREFERENCED_PARAMETER(RGXHWPerfTLCB); |
| ui32L2BufferSize = 0; |
| #endif |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "HWPerf buffer size in bytes: L1: %d L2: %d", |
| gpsRgxDevInfo->ui32RGXFWIfHWPerfBufSize, ui32L2BufferSize)); |
| |
| PVR_DPF_RETURN_OK; |
| |
| #if !defined(NO_HARDWARE) |
| e1: /* L2 buffer initialisation failures */ |
| gpsRgxDevInfo->hHWPerfStream = NULL; |
| #endif |
| e0: /* L1 buffer initialisation failures */ |
| RGXHWPerfL1BufferDeinit(); |
| |
| PVR_DPF_RETURN_RC(eError); |
| } |
| |
| |
| void RGXHWPerfDeinit(void) |
| { |
| PVR_DPF_ENTERED; |
| |
| /* Clean up the L2 buffer stream object if allocated */ |
| if (gpsRgxDevInfo && gpsRgxDevInfo->hHWPerfStream) |
| { |
| TLStreamClose(gpsRgxDevInfo->hHWPerfStream); |
| gpsRgxDevInfo->hHWPerfStream = NULL; |
| } |
| |
| /* Cleanup L1 buffer resources */ |
| RGXHWPerfL1BufferDeinit(); |
| |
| /* Cleanup the HWPerf server module lock resource */ |
| if (gpsRgxDevInfo && gpsRgxDevInfo->hHWPerfLock) |
| { |
| OSLockDestroy(gpsRgxDevInfo->hHWPerfLock); |
| gpsRgxDevInfo->hHWPerfLock = NULL; |
| } |
| |
| PVR_DPF_RETURN; |
| } |
| |
| |
| /****************************************************************************** |
| * RGX HW Performance Profiling Server API(s) |
| *****************************************************************************/ |
| |
| static PVRSRV_ERROR RGXHWPerfCtrlFwBuffer(const PVRSRV_DEVICE_NODE *psDeviceNode, |
| IMG_BOOL bToggle, |
| IMG_UINT64 ui64Mask) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_RGXDEV_INFO* psDevice = psDeviceNode->pvDevice; |
| RGXFWIF_KCCB_CMD sKccbCmd; |
| |
| /* If this method is being used whether to enable or disable |
| * then the hwperf buffers (host and FW) are likely to be needed |
| * eventually so create them, also helps unit testing. Buffers |
| * allocated on demand to reduce RAM foot print on systems not |
| * needing HWPerf resources. |
| * Obtain lock first, test and init if required. */ |
| OSLockAcquire(psDevice->hHWPerfLock); |
| |
| if (!psDevice->bFirmwareInitialised) |
| { |
| gpsRgxDevInfo->ui64HWPerfFilter = ui64Mask; // at least set filter |
| eError = PVRSRV_ERROR_NOT_INITIALISED; |
| |
| PVR_DPF((PVR_DBG_ERROR, "HWPerf has NOT been initialised yet." |
| " Mask has been SET to (%llx)", (long long) ui64Mask)); |
| |
| goto unlock_and_return; |
| } |
| |
| if (RGXHWPerfIsInitRequired()) |
| { |
| eError = RGXHWPerfInitOnDemandResources(); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Initialisation of on-demand HWPerfFW " |
| "resources failed", __func__)); |
| goto unlock_and_return; |
| } |
| } |
| |
| /* Unlock here as no further HWPerf resources are used below that would be |
| * affected if freed by another thread */ |
| OSLockRelease(psDevice->hHWPerfLock); |
| |
| /* Return if the filter is the same */ |
| if (!bToggle && gpsRgxDevInfo->ui64HWPerfFilter == ui64Mask) |
| goto return_; |
| |
| /* Prepare command parameters ... */ |
| sKccbCmd.eCmdType = RGXFWIF_KCCB_CMD_HWPERF_UPDATE_CONFIG; |
| sKccbCmd.uCmdData.sHWPerfCtrl.bToggle = bToggle; |
| sKccbCmd.uCmdData.sHWPerfCtrl.ui64Mask = ui64Mask; |
| |
| /* Ask the FW to carry out the HWPerf configuration command */ |
| eError = RGXScheduleCommand(psDeviceNode->pvDevice, RGXFWIF_DM_GP, |
| &sKccbCmd, sizeof(sKccbCmd), 0, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to set new HWPerfFW filter in " |
| "firmware (error = %d)", __func__, eError)); |
| goto return_; |
| } |
| |
| gpsRgxDevInfo->ui64HWPerfFilter = bToggle ? |
| gpsRgxDevInfo->ui64HWPerfFilter ^ ui64Mask : ui64Mask; |
| |
| /* Wait for FW to complete */ |
| eError = RGXWaitForFWOp(psDeviceNode->pvDevice, RGXFWIF_DM_GP, |
| psDeviceNode->psSyncPrim, PDUMP_FLAGS_CONTINUOUS); |
| PVR_LOGG_IF_ERROR(eError, "RGXWaitForFWOp", return_); |
| |
| #if defined(DEBUG) |
| if (bToggle) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "HWPerfFW events (%llx) have been TOGGLED", |
| ui64Mask)); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_WARNING, "HWPerfFW mask has been SET to (%llx)", |
| ui64Mask)); |
| } |
| #endif |
| |
| return PVRSRV_OK; |
| |
| unlock_and_return: |
| OSLockRelease(psDevice->hHWPerfLock); |
| |
| return_: |
| return eError; |
| } |
| |
| static PVRSRV_ERROR RGXHWPerfCtrlHostBuffer(const PVRSRV_DEVICE_NODE *psDeviceNode, |
| IMG_BOOL bToggle, |
| IMG_UINT32 ui32Mask) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| PVRSRV_RGXDEV_INFO* psDevice = psDeviceNode->pvDevice; |
| |
| OSLockAcquire(psDevice->hLockHWPerfHostStream); |
| if (psDevice->hHWPerfHostStream == NULL) |
| { |
| eError = RGXHWPerfHostInitOnDemandResources(); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Initialization of on-demand HWPerfHost" |
| " resources failed", __FUNCTION__)); |
| OSLockRelease(psDevice->hLockHWPerfHostStream); |
| return eError; |
| } |
| } |
| |
| psDevice->ui32HWPerfHostFilter = bToggle ? |
| psDevice->ui32HWPerfHostFilter ^ ui32Mask : ui32Mask; |
| OSLockRelease(psDevice->hLockHWPerfHostStream); |
| |
| #if defined(DEBUG) |
| if (bToggle) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "HWPerfHost events (%x) have been TOGGLED", |
| ui32Mask)); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_WARNING, "HWPerfHost mask has been SET to (%x)", |
| ui32Mask)); |
| } |
| #endif |
| |
| return PVRSRV_OK; |
| } |
| |
| /* |
| PVRSRVRGXCtrlHWPerfKM |
| */ |
| PVRSRV_ERROR PVRSRVRGXCtrlHWPerfKM( |
| CONNECTION_DATA *psConnection, |
| PVRSRV_DEVICE_NODE *psDeviceNode, |
| RGX_HWPERF_STREAM_ID eStreamId, |
| IMG_BOOL bToggle, |
| IMG_UINT64 ui64Mask) |
| { |
| PVR_UNREFERENCED_PARAMETER(psConnection); |
| |
| PVR_DPF_ENTERED; |
| PVR_ASSERT(psDeviceNode); |
| |
| if (eStreamId == RGX_HWPERF_STREAM_ID0_FW) |
| { |
| return RGXHWPerfCtrlFwBuffer(psDeviceNode, bToggle, ui64Mask); |
| } |
| else if (eStreamId == RGX_HWPERF_STREAM_ID1_HOST) |
| { |
| return RGXHWPerfCtrlHostBuffer(psDeviceNode, bToggle, (IMG_UINT32) ui64Mask); |
| } |
| else |
| { |
| PVR_DPF((PVR_DBG_ERROR, "PVRSRVRGXCtrlHWPerfKM: Unknown stream id.")); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| PVR_DPF_RETURN_OK; |
| } |
| |
| /* |
| AppHint interfaces |
| */ |
| static |
| PVRSRV_ERROR RGXHWPerfSetFwFilter(const PVRSRV_DEVICE_NODE *psDeviceNode, |
| const void *psPrivate, |
| IMG_UINT64 ui64Value) |
| { |
| PVR_UNREFERENCED_PARAMETER(psPrivate); |
| return RGXHWPerfCtrlFwBuffer(psDeviceNode, IMG_FALSE, ui64Value); |
| } |
| |
| static |
| PVRSRV_ERROR RGXHWPerfReadFwFilter(const PVRSRV_DEVICE_NODE *psDeviceNode, |
| const void *psPrivate, |
| IMG_UINT64 *pui64Value) |
| { |
| PVRSRV_RGXDEV_INFO *psDevice; |
| |
| PVR_UNREFERENCED_PARAMETER(psPrivate); |
| |
| if (!psDeviceNode || !psDeviceNode->pvDevice) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| psDevice = psDeviceNode->pvDevice; |
| *pui64Value = psDevice->ui64HWPerfFilter; |
| return PVRSRV_OK; |
| } |
| |
| static |
| PVRSRV_ERROR RGXHWPerfSetHostFilter(const PVRSRV_DEVICE_NODE *psDeviceNode, |
| const void *psPrivate, |
| IMG_UINT32 ui32Value) |
| { |
| PVR_UNREFERENCED_PARAMETER(psPrivate); |
| return RGXHWPerfCtrlHostBuffer(psDeviceNode, IMG_FALSE, ui32Value); |
| } |
| |
| static |
| PVRSRV_ERROR RGXHWPerfReadHostFilter(const PVRSRV_DEVICE_NODE *psDeviceNode, |
| const void *psPrivate, |
| IMG_UINT32 *pui32Value) |
| { |
| PVRSRV_RGXDEV_INFO *psDevice; |
| |
| PVR_UNREFERENCED_PARAMETER(psPrivate); |
| |
| if (!psDeviceNode || !psDeviceNode->pvDevice) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| psDevice = psDeviceNode->pvDevice; |
| *pui32Value = psDevice->ui32HWPerfHostFilter; |
| return PVRSRV_OK; |
| } |
| |
| void RGXHWPerfInitAppHintCallbacks(const PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| PVRSRVAppHintRegisterHandlersUINT64(APPHINT_ID_HWPerfFWFilter, |
| RGXHWPerfReadFwFilter, |
| RGXHWPerfSetFwFilter, |
| psDeviceNode, |
| NULL); |
| PVRSRVAppHintRegisterHandlersUINT32(APPHINT_ID_HWPerfHostFilter, |
| RGXHWPerfReadHostFilter, |
| RGXHWPerfSetHostFilter, |
| psDeviceNode, |
| NULL); |
| } |
| |
| /* |
| PVRSRVRGXEnableHWPerfCountersKM |
| */ |
| PVRSRV_ERROR PVRSRVRGXConfigEnableHWPerfCountersKM( |
| CONNECTION_DATA * psConnection, |
| PVRSRV_DEVICE_NODE * psDeviceNode, |
| IMG_UINT32 ui32ArrayLen, |
| RGX_HWPERF_CONFIG_CNTBLK * psBlockConfigs) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGXFWIF_KCCB_CMD sKccbCmd; |
| DEVMEM_MEMDESC* psFwBlkConfigsMemDesc; |
| RGX_HWPERF_CONFIG_CNTBLK* psFwArray; |
| |
| PVR_UNREFERENCED_PARAMETER(psConnection); |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(psDeviceNode); |
| PVR_ASSERT(ui32ArrayLen>0); |
| PVR_ASSERT(psBlockConfigs); |
| |
| /* Fill in the command structure with the parameters needed |
| */ |
| sKccbCmd.eCmdType = RGXFWIF_KCCB_CMD_HWPERF_CONFIG_ENABLE_BLKS; |
| sKccbCmd.uCmdData.sHWPerfCfgEnableBlks.ui32NumBlocks = ui32ArrayLen; |
| |
| eError = DevmemFwAllocate(psDeviceNode->pvDevice, |
| sizeof(RGX_HWPERF_CONFIG_CNTBLK)*ui32ArrayLen, |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_CPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE | |
| PVRSRV_MEMALLOCFLAG_UNCACHED | |
| PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC, |
| "FwHWPerfCountersConfigBlock", |
| &psFwBlkConfigsMemDesc); |
| if (eError != PVRSRV_OK) |
| PVR_LOGR_IF_ERROR(eError, "DevmemFwAllocate"); |
| |
| RGXSetFirmwareAddress(&sKccbCmd.uCmdData.sHWPerfCfgEnableBlks.sBlockConfigs, |
| psFwBlkConfigsMemDesc, 0, 0); |
| |
| eError = DevmemAcquireCpuVirtAddr(psFwBlkConfigsMemDesc, (void **)&psFwArray); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_LOGG_IF_ERROR(eError, "DevmemAcquireCpuVirtAddr", fail1); |
| } |
| |
| OSDeviceMemCopy(psFwArray, psBlockConfigs, sizeof(RGX_HWPERF_CONFIG_CNTBLK)*ui32ArrayLen); |
| DevmemPDumpLoadMem(psFwBlkConfigsMemDesc, |
| 0, |
| sizeof(RGX_HWPERF_CONFIG_CNTBLK)*ui32ArrayLen, |
| 0); |
| |
| /* PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXConfigEnableHWPerfCountersKM parameters set, calling FW")); */ |
| |
| /* Ask the FW to carry out the HWPerf configuration command |
| */ |
| eError = RGXScheduleCommand(psDeviceNode->pvDevice, |
| RGXFWIF_DM_GP, &sKccbCmd, sizeof(sKccbCmd), 0, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_LOGG_IF_ERROR(eError, "RGXScheduleCommand", fail2); |
| } |
| |
| /* PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXConfigEnableHWPerfCountersKM command scheduled for FW")); */ |
| |
| /* Wait for FW to complete */ |
| eError = RGXWaitForFWOp(psDeviceNode->pvDevice, RGXFWIF_DM_GP, psDeviceNode->psSyncPrim, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_LOGG_IF_ERROR(eError, "RGXWaitForFWOp", fail2); |
| } |
| |
| /* Release temporary memory used for block configuration |
| */ |
| RGXUnsetFirmwareAddress(psFwBlkConfigsMemDesc); |
| DevmemReleaseCpuVirtAddr(psFwBlkConfigsMemDesc); |
| DevmemFwFree(psDeviceNode->pvDevice, psFwBlkConfigsMemDesc); |
| |
| /* PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXConfigEnableHWPerfCountersKM firmware completed")); */ |
| |
| PVR_DPF((PVR_DBG_WARNING, "HWPerf %d counter blocks configured and ENABLED", ui32ArrayLen)); |
| |
| PVR_DPF_RETURN_OK; |
| |
| fail2: |
| DevmemReleaseCpuVirtAddr(psFwBlkConfigsMemDesc); |
| fail1: |
| RGXUnsetFirmwareAddress(psFwBlkConfigsMemDesc); |
| DevmemFwFree(psDeviceNode->pvDevice, psFwBlkConfigsMemDesc); |
| |
| PVR_DPF_RETURN_RC(eError); |
| } |
| |
| |
| /* |
| PVRSRVRGXConfigCustomCountersReadingHWPerfKM |
| */ |
| PVRSRV_ERROR PVRSRVRGXConfigCustomCountersKM( |
| CONNECTION_DATA * psConnection, |
| PVRSRV_DEVICE_NODE * psDeviceNode, |
| IMG_UINT16 ui16CustomBlockID, |
| IMG_UINT16 ui16NumCustomCounters, |
| IMG_UINT32 * pui32CustomCounterIDs) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGXFWIF_KCCB_CMD sKccbCmd; |
| DEVMEM_MEMDESC* psFwSelectCntrsMemDesc = NULL; |
| IMG_UINT32* psFwArray; |
| |
| PVR_UNREFERENCED_PARAMETER(psConnection); |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(psDeviceNode); |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVRGXSelectCustomCountersKM: configure block %u to read %u counters", ui16CustomBlockID, ui16NumCustomCounters)); |
| |
| /* Fill in the command structure with the parameters needed */ |
| sKccbCmd.eCmdType = RGXFWIF_KCCB_CMD_HWPERF_SELECT_CUSTOM_CNTRS; |
| sKccbCmd.uCmdData.sHWPerfSelectCstmCntrs.ui16NumCounters = ui16NumCustomCounters; |
| sKccbCmd.uCmdData.sHWPerfSelectCstmCntrs.ui16CustomBlock = ui16CustomBlockID; |
| |
| if (ui16NumCustomCounters > 0) |
| { |
| PVR_ASSERT(pui32CustomCounterIDs); |
| |
| eError = DevmemFwAllocate(psDeviceNode->pvDevice, |
| sizeof(IMG_UINT32) * ui16NumCustomCounters, |
| PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) | |
| PVRSRV_MEMALLOCFLAG_GPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE | |
| PVRSRV_MEMALLOCFLAG_CPU_READABLE | |
| PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE | |
| PVRSRV_MEMALLOCFLAG_UNCACHED | |
| PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC, |
| "FwHWPerfConfigCustomCounters", |
| &psFwSelectCntrsMemDesc); |
| if (eError != PVRSRV_OK) |
| PVR_LOGR_IF_ERROR(eError, "DevmemFwAllocate"); |
| |
| RGXSetFirmwareAddress(&sKccbCmd.uCmdData.sHWPerfSelectCstmCntrs.sCustomCounterIDs, |
| psFwSelectCntrsMemDesc, 0, 0); |
| |
| eError = DevmemAcquireCpuVirtAddr(psFwSelectCntrsMemDesc, (void **)&psFwArray); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_LOGG_IF_ERROR(eError, "DevmemAcquireCpuVirtAddr", fail1); |
| } |
| |
| OSDeviceMemCopy(psFwArray, pui32CustomCounterIDs, sizeof(IMG_UINT32) * ui16NumCustomCounters); |
| DevmemPDumpLoadMem(psFwSelectCntrsMemDesc, |
| 0, |
| sizeof(IMG_UINT32) * ui16NumCustomCounters, |
| 0); |
| } |
| |
| /* Push in the KCCB the command to configure the custom counters block */ |
| eError = RGXScheduleCommand(psDeviceNode->pvDevice, |
| RGXFWIF_DM_GP, &sKccbCmd, sizeof(sKccbCmd), 0, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_LOGG_IF_ERROR(eError, "RGXScheduleCommand", fail2); |
| } |
| PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXSelectCustomCountersKM: Command scheduled")); |
| |
| /* Wait for FW to complete */ |
| eError = RGXWaitForFWOp(psDeviceNode->pvDevice, RGXFWIF_DM_GP, psDeviceNode->psSyncPrim, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_LOGG_IF_ERROR(eError, "RGXWaitForFWOp", fail2); |
| } |
| PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXSelectCustomCountersKM: FW operation completed")); |
| |
| if (ui16NumCustomCounters > 0) |
| { |
| /* Release temporary memory used for block configuration */ |
| RGXUnsetFirmwareAddress(psFwSelectCntrsMemDesc); |
| DevmemReleaseCpuVirtAddr(psFwSelectCntrsMemDesc); |
| DevmemFwFree(psDeviceNode->pvDevice, psFwSelectCntrsMemDesc); |
| } |
| |
| PVR_DPF((PVR_DBG_MESSAGE, "HWPerf custom counters %u reading will be sent with the next HW events", ui16NumCustomCounters)); |
| |
| PVR_DPF_RETURN_OK; |
| |
| fail2: |
| if (psFwSelectCntrsMemDesc) DevmemReleaseCpuVirtAddr(psFwSelectCntrsMemDesc); |
| |
| fail1: |
| if (psFwSelectCntrsMemDesc) |
| { |
| RGXUnsetFirmwareAddress(psFwSelectCntrsMemDesc); |
| DevmemFwFree(psDeviceNode->pvDevice, psFwSelectCntrsMemDesc); |
| } |
| |
| PVR_DPF_RETURN_RC(eError); |
| } |
| /* |
| PVRSRVRGXDisableHWPerfcountersKM |
| */ |
| PVRSRV_ERROR PVRSRVRGXCtrlHWPerfCountersKM( |
| CONNECTION_DATA * psConnection, |
| PVRSRV_DEVICE_NODE * psDeviceNode, |
| IMG_BOOL bEnable, |
| IMG_UINT32 ui32ArrayLen, |
| IMG_UINT16 * psBlockIDs) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGXFWIF_KCCB_CMD sKccbCmd; |
| |
| PVR_UNREFERENCED_PARAMETER(psConnection); |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(psDeviceNode); |
| PVR_ASSERT(ui32ArrayLen>0); |
| PVR_ASSERT(ui32ArrayLen<=RGXFWIF_HWPERF_CTRL_BLKS_MAX); |
| PVR_ASSERT(psBlockIDs); |
| |
| /* Fill in the command structure with the parameters needed |
| */ |
| sKccbCmd.eCmdType = RGXFWIF_KCCB_CMD_HWPERF_CTRL_BLKS; |
| sKccbCmd.uCmdData.sHWPerfCtrlBlks.bEnable = bEnable; |
| sKccbCmd.uCmdData.sHWPerfCtrlBlks.ui32NumBlocks = ui32ArrayLen; |
| OSDeviceMemCopy(sKccbCmd.uCmdData.sHWPerfCtrlBlks.aeBlockIDs, psBlockIDs, sizeof(IMG_UINT16)*ui32ArrayLen); |
| |
| /* PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXCtrlHWPerfCountersKM parameters set, calling FW")); */ |
| |
| /* Ask the FW to carry out the HWPerf configuration command |
| */ |
| eError = RGXScheduleCommand(psDeviceNode->pvDevice, |
| RGXFWIF_DM_GP, &sKccbCmd, sizeof(sKccbCmd), 0, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| PVR_LOGR_IF_ERROR(eError, "RGXScheduleCommand"); |
| |
| /* PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXCtrlHWPerfCountersKM command scheduled for FW")); */ |
| |
| /* Wait for FW to complete */ |
| eError = RGXWaitForFWOp(psDeviceNode->pvDevice, RGXFWIF_DM_GP, psDeviceNode->psSyncPrim, PDUMP_FLAGS_CONTINUOUS); |
| if (eError != PVRSRV_OK) |
| PVR_LOGR_IF_ERROR(eError, "RGXWaitForFWOp"); |
| |
| /* PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXCtrlHWPerfCountersKM firmware completed")); */ |
| |
| #if defined(DEBUG) |
| if (bEnable) |
| PVR_DPF((PVR_DBG_WARNING, "HWPerf %d counter blocks have been ENABLED", ui32ArrayLen)); |
| else |
| PVR_DPF((PVR_DBG_WARNING, "HWPerf %d counter blocks have been DISABLED", ui32ArrayLen)); |
| #endif |
| |
| PVR_DPF_RETURN_OK; |
| } |
| |
| static INLINE IMG_UINT32 _RGXHWPerfFixBufferSize(IMG_UINT32 ui32BufSizeKB) |
| { |
| if (ui32BufSizeKB > HWPERF_HOST_TL_STREAM_SIZE_MAX) |
| { |
| /* Size specified as a AppHint but it is too big */ |
| PVR_DPF((PVR_DBG_WARNING,"RGXHWPerfHostInit: HWPerf Host buffer size " |
| "value (%u) too big, using maximum (%u)", ui32BufSizeKB, |
| HWPERF_HOST_TL_STREAM_SIZE_MAX)); |
| return HWPERF_HOST_TL_STREAM_SIZE_MAX<<10; |
| } |
| else if (ui32BufSizeKB >= HWPERF_HOST_TL_STREAM_SIZE_MIN) |
| { |
| return ui32BufSizeKB<<10; |
| } |
| else if (ui32BufSizeKB > 0) |
| { |
| /* Size specified as a AppHint but it is too small */ |
| PVR_DPF((PVR_DBG_WARNING,"RGXHWPerfHostInit: HWPerf Host buffer size " |
| "value (%u) too small, using minimum (%u)", ui32BufSizeKB, |
| HWPERF_HOST_TL_STREAM_SIZE_MIN)); |
| return HWPERF_HOST_TL_STREAM_SIZE_MIN<<10; |
| } |
| else |
| { |
| /* 0 size implies AppHint not set or is set to zero, |
| * use default size from driver constant. */ |
| return HWPERF_HOST_TL_STREAM_SIZE_DEFAULT<<10; |
| } |
| } |
| |
| /****************************************************************************** |
| * RGX HW Performance Host Stream API |
| *****************************************************************************/ |
| |
| /*************************************************************************/ /*! |
| @Function RGXHWPerfHostInit |
| |
| @Description Called during driver init for initialisation of HWPerfHost |
| stream in the Rogue device driver. This function keeps allocated |
| only the minimal necessary resources, which are required for |
| functioning of HWPerf server module. |
| |
| @Return PVRSRV_ERROR |
| */ /**************************************************************************/ |
| PVRSRV_ERROR RGXHWPerfHostInit(IMG_UINT32 ui32BufSizeKB) |
| { |
| PVRSRV_ERROR eError; |
| PVR_ASSERT(gpsRgxDevInfo != NULL); |
| |
| eError = OSLockCreate(&gpsRgxDevInfo->hLockHWPerfHostStream, LOCK_TYPE_PASSIVE); |
| PVR_LOGG_IF_ERROR(eError, "OSLockCreate", error); |
| |
| gpsRgxDevInfo->hHWPerfHostStream = NULL; |
| gpsRgxDevInfo->ui32HWPerfHostFilter = 0; /* disable all events */ |
| gpsRgxDevInfo->ui32HWPerfHostNextOrdinal = 0; |
| gpsRgxDevInfo->ui32HWPerfHostBufSize = _RGXHWPerfFixBufferSize(ui32BufSizeKB); |
| |
| error: |
| return eError; |
| } |
| |
| static void _HWPerfHostOnConnectCB(void *pvArg) |
| { |
| (void) pvArg; |
| |
| RGX_HWPERF_HOST_CLK_SYNC(); |
| } |
| |
| /*************************************************************************/ /*! |
| @Function RGXHWPerfHostInitOnDemandResources |
| |
| @Description This function allocates the HWPerfHost buffer if HWPerf is |
| enabled at driver load time. Otherwise, these buffers are |
| allocated on-demand as and when required. |
| |
| @Return PVRSRV_ERROR |
| */ /**************************************************************************/ |
| PVRSRV_ERROR RGXHWPerfHostInitOnDemandResources(void) |
| { |
| PVRSRV_ERROR eError; |
| |
| eError = TLStreamCreate(&gpsRgxDevInfo->hHWPerfHostStream, |
| PVRSRV_TL_HWPERF_HOST_SERVER_STREAM, gpsRgxDevInfo->ui32HWPerfHostBufSize, |
| TL_FLAG_RESERVE_DROP_NEWER, _HWPerfHostOnConnectCB, NULL, NULL, |
| NULL); |
| PVR_LOGG_IF_ERROR(eError, "TLStreamCreate", error_stream_create); |
| |
| PVR_DPF((DBGPRIV_MESSAGE, "HWPerf Host buffer size is %uKB", |
| gpsRgxDevInfo->ui32HWPerfHostBufSize)); |
| |
| return PVRSRV_OK; |
| |
| error_stream_create: |
| OSLockDestroy(gpsRgxDevInfo->hLockHWPerfHostStream); |
| gpsRgxDevInfo->hLockHWPerfHostStream = NULL; |
| |
| return eError; |
| } |
| |
| void RGXHWPerfHostDeInit(void) |
| { |
| if (gpsRgxDevInfo && gpsRgxDevInfo->hHWPerfHostStream) |
| { |
| TLStreamClose(gpsRgxDevInfo->hHWPerfHostStream); |
| gpsRgxDevInfo->hHWPerfHostStream = NULL; |
| } |
| |
| if (gpsRgxDevInfo && gpsRgxDevInfo->hLockHWPerfHostStream) |
| { |
| OSLockDestroy(gpsRgxDevInfo->hLockHWPerfHostStream); |
| gpsRgxDevInfo->hLockHWPerfHostStream = IMG_FALSE; |
| } |
| |
| /* Clear global RGX device reference */ |
| gpsRgxDevInfo = NULL; |
| gpsRgxDevNode = NULL; |
| } |
| |
| void RGXHWPerfHostSetEventFilter(IMG_UINT32 ui32Filter) |
| { |
| gpsRgxDevInfo->ui32HWPerfHostFilter = ui32Filter; |
| } |
| |
| IMG_BOOL RGXHWPerfHostIsEventEnabled(RGX_HWPERF_HOST_EVENT_TYPE eEvent) |
| { |
| return gpsRgxDevInfo != NULL && |
| (gpsRgxDevInfo->ui32HWPerfHostFilter & RGX_HWPERF_EVENT_MASK_VALUE(eEvent)); |
| } |
| |
| static inline void _PostFunctionPrologue(void) |
| { |
| PVR_ASSERT(gpsRgxDevInfo->hLockHWPerfHostStream != NULL); |
| PVR_ASSERT(gpsRgxDevInfo->hHWPerfHostStream != NULL); |
| |
| OSLockAcquire(gpsRgxDevInfo->hLockHWPerfHostStream); |
| |
| /* In case we drop packet we increment ordinal beforehand. */ |
| gpsRgxDevInfo->ui32HWPerfHostNextOrdinal++; |
| } |
| |
| static inline void _PostFunctionEpilogue(void) |
| { |
| OSLockRelease(gpsRgxDevInfo->hLockHWPerfHostStream); |
| } |
| |
| static inline IMG_UINT8 *_ReserveHWPerfStream(IMG_UINT32 ui32Size) |
| { |
| IMG_UINT8 *pui8Dest; |
| |
| PVRSRV_ERROR eError = TLStreamReserve(gpsRgxDevInfo->hHWPerfHostStream, |
| &pui8Dest, ui32Size); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: Could not reserve space in %s buffer" |
| " (%d). Dropping packet.", |
| __func__, PVRSRV_TL_HWPERF_HOST_SERVER_STREAM, eError)); |
| return NULL; |
| } |
| PVR_ASSERT(pui8Dest != NULL); |
| |
| return pui8Dest; |
| } |
| |
| static inline void _CommitHWPerfStream(IMG_UINT32 ui32Size) |
| { |
| PVRSRV_ERROR eError = TLStreamCommit(gpsRgxDevInfo->hHWPerfHostStream, |
| ui32Size); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "%s: Could not commit data to %s" |
| " (%d)", __func__, PVRSRV_TL_HWPERF_HOST_SERVER_STREAM, eError)); |
| } |
| } |
| |
| static inline void _SetupHostPacketHeader(IMG_UINT8 *pui8Dest, |
| RGX_HWPERF_HOST_EVENT_TYPE eEvType, |
| IMG_UINT32 ui32Size) |
| { |
| RGX_HWPERF_V2_PACKET_HDR *psHeader = (RGX_HWPERF_V2_PACKET_HDR *) pui8Dest; |
| |
| PVR_ASSERT(ui32Size<=RGX_HWPERF_MAX_PACKET_SIZE); |
| |
| psHeader->ui32Ordinal = gpsRgxDevInfo->ui32HWPerfHostNextOrdinal; |
| psHeader->ui64Timestamp = RGXGPUFreqCalibrateClockus64(); |
| psHeader->ui32Sig = HWPERF_PACKET_V2B_SIG; |
| psHeader->eTypeId = RGX_HWPERF_MAKE_TYPEID(RGX_HWPERF_STREAM_ID1_HOST, |
| eEvType, 0, 0); |
| psHeader->ui32Size = ui32Size; |
| } |
| |
| static inline IMG_UINT32 _CalculateHostCtrlPacketSize( |
| RGX_HWPERF_HOST_CTRL_TYPE eCtrlType) |
| { |
| RGX_HWPERF_HOST_CTRL_DATA *psData; |
| IMG_UINT32 ui32Size = sizeof(psData->eEvType); |
| |
| switch (eCtrlType) |
| { |
| case RGX_HWPERF_CTRL_TYPE_CLIENT_STREAM_OPEN: |
| case RGX_HWPERF_CTRL_TYPE_CLIENT_STREAM_CLOSE: |
| ui32Size += sizeof(psData->uData.ui32Pid); |
| break; |
| default: |
| // unknown type - this should never happen |
| PVR_DPF((PVR_DBG_ERROR, "RGXHWPerfHostPostCtrlEvent: Invalid alloc" |
| " event type")); |
| PVR_ASSERT(IMG_FALSE); |
| break; |
| } |
| |
| return RGX_HWPERF_MAKE_SIZE_VARIABLE(ui32Size); |
| } |
| |
| static inline void _SetupHostCtrlPacketData(IMG_UINT8 *pui8Dest, |
| RGX_HWPERF_HOST_CTRL_TYPE eEvType, |
| IMG_UINT32 ui32Pid) |
| { |
| RGX_HWPERF_HOST_CTRL_DATA *psData = (RGX_HWPERF_HOST_CTRL_DATA *) |
| (pui8Dest + sizeof(RGX_HWPERF_V2_PACKET_HDR)); |
| psData->eEvType = eEvType; |
| psData->uData.ui32Pid = ui32Pid; |
| } |
| |
| void RGXHWPerfHostPostCtrlEvent(RGX_HWPERF_HOST_CTRL_TYPE eEvType, |
| IMG_UINT32 ui32Pid) |
| { |
| IMG_UINT8 *pui8Dest; |
| IMG_UINT32 ui32Size = _CalculateHostCtrlPacketSize(eEvType); |
| |
| _PostFunctionPrologue(); |
| |
| if ((pui8Dest = _ReserveHWPerfStream(ui32Size)) == NULL) |
| { |
| goto cleanup; |
| } |
| |
| _SetupHostPacketHeader(pui8Dest, RGX_HWPERF_HOST_CTRL, ui32Size); |
| _SetupHostCtrlPacketData(pui8Dest, eEvType, ui32Pid); |
| |
| _CommitHWPerfStream(ui32Size); |
| |
| cleanup: |
| _PostFunctionEpilogue(); |
| } |
| |
| static inline void _SetupHostEnqPacketData(IMG_UINT8 *pui8Dest, |
| RGX_HWPERF_KICK_TYPE eEnqType, |
| IMG_UINT32 ui32Pid, |
| IMG_UINT32 ui32FWDMContext, |
| IMG_UINT32 ui32ExtJobRef, |
| IMG_UINT32 ui32IntJobRef) |
| { |
| RGX_HWPERF_HOST_ENQ_DATA *psData = (RGX_HWPERF_HOST_ENQ_DATA *) |
| (pui8Dest + sizeof(RGX_HWPERF_V2_PACKET_HDR)); |
| psData->ui32EnqType = eEnqType; |
| psData->ui32PID = ui32Pid; |
| psData->ui32ExtJobRef = ui32ExtJobRef; |
| psData->ui32IntJobRef = ui32IntJobRef; |
| psData->ui32DMContext = ui32FWDMContext; |
| psData->ui32Padding = 0; /* Set to zero for future compatibility */ |
| } |
| |
| void RGXHWPerfHostPostEnqEvent(RGX_HWPERF_KICK_TYPE eEnqType, |
| IMG_UINT32 ui32Pid, |
| IMG_UINT32 ui32FWDMContext, |
| IMG_UINT32 ui32ExtJobRef, |
| IMG_UINT32 ui32IntJobRef) |
| { |
| IMG_UINT8 *pui8Dest; |
| IMG_UINT32 ui32Size = RGX_HWPERF_MAKE_SIZE_FIXED(RGX_HWPERF_HOST_ENQ_DATA); |
| |
| _PostFunctionPrologue(); |
| |
| if ((pui8Dest = _ReserveHWPerfStream(ui32Size)) == NULL) |
| { |
| goto cleanup; |
| } |
| |
| _SetupHostPacketHeader(pui8Dest, RGX_HWPERF_HOST_ENQ, ui32Size); |
| _SetupHostEnqPacketData(pui8Dest, eEnqType, ui32Pid, ui32FWDMContext, |
| ui32ExtJobRef, ui32IntJobRef); |
| |
| _CommitHWPerfStream(ui32Size); |
| |
| cleanup: |
| _PostFunctionEpilogue(); |
| } |
| |
| static inline IMG_UINT32 _CalculateHostUfoPacketSize(RGX_HWPERF_UFO_EV eUfoType, |
| IMG_UINT uiNoOfUFOs) |
| { |
| IMG_UINT32 ui32Size = |
| (IMG_UINT32) offsetof(RGX_HWPERF_UFO_DATA, aui32StreamData); |
| RGX_HWPERF_UFO_DATA_ELEMENT *puData; |
| |
| switch (eUfoType) |
| { |
| case RGX_HWPERF_UFO_EV_CHECK_SUCCESS: |
| case RGX_HWPERF_UFO_EV_PRCHECK_SUCCESS: |
| ui32Size += uiNoOfUFOs * sizeof(puData->sCheckSuccess); |
| break; |
| case RGX_HWPERF_UFO_EV_CHECK_FAIL: |
| case RGX_HWPERF_UFO_EV_PRCHECK_FAIL: |
| ui32Size += uiNoOfUFOs * sizeof(puData->sCheckFail); |
| break; |
| case RGX_HWPERF_UFO_EV_UPDATE: |
| ui32Size += uiNoOfUFOs * sizeof(puData->sUpdate); |
| break; |
| default: |
| // unknown type - this should never happen |
| PVR_DPF((PVR_DBG_ERROR, "RGXHWPerfHostPostUfoEvent: Invalid UFO" |
| " event type")); |
| PVR_ASSERT(IMG_FALSE); |
| break; |
| } |
| |
| return RGX_HWPERF_MAKE_SIZE_VARIABLE(ui32Size); |
| } |
| |
| static inline void _SetupHostUfoPacketData(IMG_UINT8 *pui8Dest, |
| RGX_HWPERF_UFO_EV eUfoType, |
| RGX_HWPERF_UFO_DATA_ELEMENT psUFOData[], |
| IMG_UINT uiNoOfUFOs) |
| { |
| IMG_UINT uiUFOIdx; |
| RGX_HWPERF_HOST_UFO_DATA *psData = (RGX_HWPERF_HOST_UFO_DATA *) |
| (pui8Dest + sizeof(RGX_HWPERF_V2_PACKET_HDR)); |
| RGX_HWPERF_UFO_DATA_ELEMENT *puData = (RGX_HWPERF_UFO_DATA_ELEMENT *) |
| psData->aui32StreamData; |
| |
| psData->eEvType = eUfoType; |
| psData->ui32StreamInfo = RGX_HWPERF_MAKE_UFOPKTINFO(uiNoOfUFOs, |
| offsetof(RGX_HWPERF_HOST_UFO_DATA, aui32StreamData)); |
| |
| switch (eUfoType) |
| { |
| case RGX_HWPERF_UFO_EV_CHECK_SUCCESS: |
| case RGX_HWPERF_UFO_EV_PRCHECK_SUCCESS: |
| for (uiUFOIdx = 0; uiUFOIdx < uiNoOfUFOs; uiUFOIdx++) |
| { |
| puData->sCheckSuccess.ui32FWAddr = |
| psUFOData[uiUFOIdx].sCheckSuccess.ui32FWAddr; |
| puData->sCheckSuccess.ui32Value = |
| psUFOData[uiUFOIdx].sCheckSuccess.ui32Value; |
| |
| puData = (RGX_HWPERF_UFO_DATA_ELEMENT *) |
| (((IMG_BYTE *) puData) + sizeof(puData->sCheckSuccess)); |
| } |
| break; |
| case RGX_HWPERF_UFO_EV_CHECK_FAIL: |
| case RGX_HWPERF_UFO_EV_PRCHECK_FAIL: |
| for (uiUFOIdx = 0; uiUFOIdx < uiNoOfUFOs; uiUFOIdx++) |
| { |
| puData->sCheckFail.ui32FWAddr = |
| psUFOData[uiUFOIdx].sCheckFail.ui32FWAddr; |
| puData->sCheckFail.ui32Value = |
| psUFOData[uiUFOIdx].sCheckFail.ui32Value; |
| puData->sCheckFail.ui32Required = |
| psUFOData[uiUFOIdx].sCheckFail.ui32Required; |
| |
| puData = (RGX_HWPERF_UFO_DATA_ELEMENT *) |
| (((IMG_BYTE *) puData) + sizeof(puData->sCheckFail)); |
| } |
| break; |
| case RGX_HWPERF_UFO_EV_UPDATE: |
| for (uiUFOIdx = 0; uiUFOIdx < uiNoOfUFOs; uiUFOIdx++) |
| { |
| puData->sUpdate.ui32FWAddr = |
| psUFOData[uiUFOIdx].sUpdate.ui32FWAddr; |
| puData->sUpdate.ui32OldValue = |
| psUFOData[uiUFOIdx].sUpdate.ui32OldValue; |
| puData->sUpdate.ui32NewValue = |
| psUFOData[uiUFOIdx].sUpdate.ui32NewValue; |
| |
| puData = (RGX_HWPERF_UFO_DATA_ELEMENT *) |
| (((IMG_BYTE *) puData) + sizeof(puData->sUpdate)); |
| } |
| break; |
| default: |
| // unknown type - this should never happen |
| PVR_DPF((PVR_DBG_ERROR, "RGXHWPerfHostPostUfoEvent: Invalid UFO" |
| " event type")); |
| PVR_ASSERT(IMG_FALSE); |
| break; |
| } |
| } |
| |
| void RGXHWPerfHostPostUfoEvent(RGX_HWPERF_UFO_EV eUfoType, |
| RGX_HWPERF_UFO_DATA_ELEMENT psUFOData[], |
| IMG_UINT uiNoOfUFOs) |
| { |
| IMG_UINT8 *pui8Dest; |
| IMG_UINT32 ui32Size = _CalculateHostUfoPacketSize(eUfoType, uiNoOfUFOs); |
| |
| _PostFunctionPrologue(); |
| |
| if ((pui8Dest = _ReserveHWPerfStream(ui32Size)) == NULL) |
| { |
| goto cleanup; |
| } |
| |
| _SetupHostPacketHeader(pui8Dest, RGX_HWPERF_HOST_UFO, ui32Size); |
| _SetupHostUfoPacketData(pui8Dest, eUfoType, psUFOData, uiNoOfUFOs); |
| |
| _CommitHWPerfStream(ui32Size); |
| |
| cleanup: |
| _PostFunctionEpilogue(); |
| } |
| |
| static inline IMG_UINT32 _FixNameSizeAndCalculateHostAllocPacketSize( |
| RGX_HWPERF_HOST_RESOURCE_TYPE eAllocType, |
| const IMG_CHAR *psName, |
| IMG_UINT32 *ui32NameSize) |
| { |
| RGX_HWPERF_HOST_ALLOC_DATA *psData; |
| RGX_HWPERF_HOST_ALLOC_DETAIL *puData; |
| IMG_UINT32 ui32Size = sizeof(psData->ui32AllocType); |
| |
| /* first strip the terminator */ |
| if (psName[*ui32NameSize - 1] == '\0') |
| *ui32NameSize -= 1; |
| /* if string longer than maximum cut it (leave space for '\0') */ |
| if (*ui32NameSize >= SYNC_MAX_CLASS_NAME_LEN) |
| *ui32NameSize = SYNC_MAX_CLASS_NAME_LEN - 1; |
| |
| switch (eAllocType) |
| { |
| case RGX_HWPERF_HOST_RESOURCE_TYPE_SYNC: |
| ui32Size += sizeof(puData->sSyncAlloc) - SYNC_MAX_CLASS_NAME_LEN + |
| *ui32NameSize + 1; /* +1 for '\0' */ |
| break; |
| default: |
| // unknown type - this should never happen |
| PVR_DPF((PVR_DBG_ERROR, "RGXHWPerfHostPostAllocEvent: Invalid alloc" |
| " event type")); |
| PVR_ASSERT(IMG_FALSE); |
| break; |
| } |
| |
| return RGX_HWPERF_MAKE_SIZE_VARIABLE(ui32Size); |
| } |
| |
| static inline void _SetupHostAllocPacketData(IMG_UINT8 *pui8Dest, |
| RGX_HWPERF_HOST_RESOURCE_TYPE eAllocType, |
| IMG_UINT32 ui32FWAddr, |
| const IMG_CHAR *psName, |
| IMG_UINT32 ui32NameSize) |
| { |
| RGX_HWPERF_HOST_ALLOC_DATA *psData = (RGX_HWPERF_HOST_ALLOC_DATA *) |
| (pui8Dest + sizeof(RGX_HWPERF_V2_PACKET_HDR)); |
| psData->ui32AllocType = eAllocType; |
| psData->uAllocDetail.sSyncAlloc.ui32FWAddr = ui32FWAddr; |
| OSStringNCopy(psData->uAllocDetail.sSyncAlloc.acName, psName, |
| ui32NameSize); |
| /* we know here that string is not null terminated and that we have enough |
| * space for the terminator */ |
| psData->uAllocDetail.sSyncAlloc.acName[ui32NameSize] = '\0'; |
| } |
| |
| void RGXHWPerfHostPostAllocEvent(RGX_HWPERF_HOST_RESOURCE_TYPE eAllocType, |
| IMG_UINT32 ui32FWAddr, |
| const IMG_CHAR *psName, |
| IMG_UINT32 ui32NameSize) |
| { |
| IMG_UINT8 *pui8Dest; |
| IMG_UINT32 ui32Size = |
| _FixNameSizeAndCalculateHostAllocPacketSize(eAllocType, psName, |
| &ui32NameSize); |
| |
| _PostFunctionPrologue(); |
| |
| if ((pui8Dest = _ReserveHWPerfStream(ui32Size)) == NULL) |
| { |
| goto cleanup; |
| } |
| |
| _SetupHostPacketHeader(pui8Dest, RGX_HWPERF_HOST_ALLOC, ui32Size); |
| _SetupHostAllocPacketData(pui8Dest, eAllocType, ui32FWAddr, psName, |
| ui32NameSize); |
| |
| _CommitHWPerfStream(ui32Size); |
| |
| cleanup: |
| _PostFunctionEpilogue(); |
| } |
| |
| static inline void _SetupHostFreePacketData(IMG_UINT8 *pui8Dest, |
| RGX_HWPERF_HOST_RESOURCE_TYPE eFreeType, |
| IMG_UINT32 ui32FWAddr) |
| { |
| RGX_HWPERF_HOST_FREE_DATA *psData = (RGX_HWPERF_HOST_FREE_DATA *) |
| (pui8Dest + sizeof(RGX_HWPERF_V2_PACKET_HDR)); |
| psData->ui32FreeType = eFreeType; |
| psData->uFreeDetail.sSyncFree.ui32FWAddr = ui32FWAddr; |
| } |
| |
| void RGXHWPerfHostPostFreeEvent(RGX_HWPERF_HOST_RESOURCE_TYPE eFreeType, |
| IMG_UINT32 ui32FWAddr) |
| { |
| IMG_UINT8 *pui8Dest; |
| IMG_UINT32 ui32Size = RGX_HWPERF_MAKE_SIZE_FIXED(RGX_HWPERF_HOST_FREE_DATA); |
| |
| _PostFunctionPrologue(); |
| |
| if ((pui8Dest = _ReserveHWPerfStream(ui32Size)) == NULL) |
| { |
| goto cleanup; |
| } |
| |
| _SetupHostPacketHeader(pui8Dest, RGX_HWPERF_HOST_FREE, ui32Size); |
| _SetupHostFreePacketData(pui8Dest, eFreeType, ui32FWAddr); |
| |
| _CommitHWPerfStream(ui32Size); |
| |
| cleanup: |
| _PostFunctionEpilogue(); |
| } |
| |
| static inline void _SetupHostClkSyncPacketData(IMG_UINT8 *pui8Dest) |
| { |
| RGX_HWPERF_HOST_CLK_SYNC_DATA *psData = (RGX_HWPERF_HOST_CLK_SYNC_DATA *) |
| (pui8Dest + sizeof(RGX_HWPERF_V2_PACKET_HDR)); |
| RGXFWIF_GPU_UTIL_FWCB *psGpuUtilFWCB = gpsRgxDevInfo->psRGXFWIfGpuUtilFWCb; |
| IMG_UINT32 ui32CurrIdx = |
| RGXFWIF_TIME_CORR_CURR_INDEX(psGpuUtilFWCB->ui32TimeCorrSeqCount); |
| RGXFWIF_TIME_CORR *psTimeCorr = &psGpuUtilFWCB->sTimeCorr[ui32CurrIdx]; |
| |
| psData->ui64CRTimestamp = psTimeCorr->ui64CRTimeStamp; |
| psData->ui64OSTimestamp = psTimeCorr->ui64OSTimeStamp; |
| psData->ui32ClockSpeed = psTimeCorr->ui32CoreClockSpeed; |
| } |
| |
| void RGXHWPerfHostPostClkSyncEvent(void) |
| { |
| IMG_UINT8 *pui8Dest; |
| IMG_UINT32 ui32Size = |
| RGX_HWPERF_MAKE_SIZE_FIXED(RGX_HWPERF_HOST_CLK_SYNC_DATA); |
| |
| _PostFunctionPrologue(); |
| |
| if ((pui8Dest = _ReserveHWPerfStream(ui32Size)) == NULL) |
| { |
| goto cleanup; |
| } |
| |
| _SetupHostPacketHeader(pui8Dest, RGX_HWPERF_HOST_CLK_SYNC, ui32Size); |
| _SetupHostClkSyncPacketData(pui8Dest); |
| |
| _CommitHWPerfStream(ui32Size); |
| |
| cleanup: |
| _PostFunctionEpilogue(); |
| } |
| |
| /****************************************************************************** |
| * SUPPORT_GPUTRACE_EVENTS |
| * |
| * Currently only implemented on Linux and Android. Feature can be enabled on |
| * Android builds but can also be enabled on Linux builds for testing |
| * but requires the gpu.h FTrace event header file to be present. |
| *****************************************************************************/ |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| |
| static void RGXHWPerfFTraceCmdCompleteNotify(PVRSRV_CMDCOMP_HANDLE); |
| |
| typedef struct RGX_HWPERF_FTRACE_DATA { |
| /* This lock ensures the HWPerf TL stream reading resources are not destroyed |
| * by one thread disabling it while another is reading from it in. Keeps the |
| * state and resource create/destroy atomic and consistent. */ |
| POS_LOCK hFTraceLock; |
| IMG_HANDLE hGPUTraceCmdCompleteHandle; |
| IMG_HANDLE hGPUTraceTLStream; |
| IMG_UINT64 ui64LastSampledTimeCorrOSTimeStamp; |
| IMG_UINT32 ui32FTraceLastOrdinal; |
| /* This lock ensures that the reference counting operation on the FTrace UFO |
| * events and enable/disable operation on firmware event are performed as |
| * one atomic operation. This should ensure that there are no race conditions |
| * between reference counting and firmware event state change. |
| * See below comment for g_uiUfoEventRef. |
| */ |
| POS_LOCK hLockFTraceEventLock; |
| /* Multiple FTrace UFO events are reflected in the firmware as only one event. When |
| * we enable FTrace UFO event we want to also at the same time enable it in |
| * the firmware. Since there is a multiple-to-one relation between those events |
| * we count how many FTrace UFO events is enabled. If at least one event is |
| * enabled we enabled the firmware event. When all FTrace UFO events are disabled |
| * we disable firmware event. */ |
| IMG_UINT uiUfoEventRef; |
| /* Saved value of the clock source before the trace was enabled. We're keeping |
| * it here so that know we which clock should be selected after we disable the |
| * gpu ftrace. */ |
| IMG_UINT64 ui64LastTimeCorrClock; |
| } RGX_HWPERF_FTRACE_DATA; |
| |
| /* Caller must now hold hFTraceLock before calling this method. |
| */ |
| static PVRSRV_ERROR RGXHWPerfFTraceGPUEnable(void) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGX_HWPERF_FTRACE_DATA *psFtraceData; |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(gpsRgxDevNode && gpsRgxDevInfo); |
| |
| psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| |
| PVR_ASSERT(OSLockIsLocked(psFtraceData->hFTraceLock)); |
| |
| /* In the case where the AppHint has not been set we need to |
| * initialise the host driver HWPerf resources here. Allocated on |
| * demand to reduce RAM foot print on systems not needing HWPerf. |
| * Signal FW to enable event generation. |
| */ |
| if (gpsRgxDevInfo->bFirmwareInitialised) |
| { |
| IMG_UINT64 ui64UFOFilter = RGX_HWPERF_EVENT_MASK_VALUE(RGX_HWPERF_UFO) & |
| gpsRgxDevInfo->ui64HWPerfFilter; |
| |
| eError = PVRSRVRGXCtrlHWPerfKM(NULL, gpsRgxDevNode, IMG_FALSE, |
| RGX_HWPERF_STREAM_ID0_FW, |
| RGX_HWPERF_EVENT_MASK_HW_KICKFINISH | |
| ui64UFOFilter); |
| PVR_LOGG_IF_ERROR(eError, "PVRSRVRGXCtrlHWPerfKM", err_out); |
| } |
| else |
| { |
| /* only set filter and exit */ |
| gpsRgxDevInfo->ui64HWPerfFilter = RGX_HWPERF_EVENT_MASK_HW_KICKFINISH | |
| (RGX_HWPERF_EVENT_MASK_VALUE(RGX_HWPERF_UFO) & |
| gpsRgxDevInfo->ui64HWPerfFilter); |
| PVRGpuTraceSetPreEnabled(IMG_TRUE); |
| |
| PVR_DPF((PVR_DBG_WARNING, "HWPerfFW mask has been SET to (%llx)", |
| (long long) gpsRgxDevInfo->ui64HWPerfFilter)); |
| |
| return PVRSRV_OK; |
| } |
| |
| /* Open the TL Stream for HWPerf data consumption */ |
| eError = TLClientOpenStream(DIRECT_BRIDGE_HANDLE, |
| PVRSRV_TL_HWPERF_RGX_FW_STREAM, |
| PVRSRV_STREAM_FLAG_ACQUIRE_NONBLOCKING, |
| &psFtraceData->hGPUTraceTLStream); |
| PVR_LOGG_IF_ERROR(eError, "TLClientOpenStream", err_out); |
| |
| /* Set clock source for timer correlation data to sched_clock */ |
| psFtraceData->ui64LastTimeCorrClock = RGXGPUFreqCalibrateGetClockSource(); |
| RGXGPUFreqCalibrateSetClockSource(gpsRgxDevNode, RGXTIMECORR_CLOCK_SCHED); |
| |
| /* Reset the OS timestamp coming from the timer correlation data |
| * associated with the latest HWPerf event we processed. |
| */ |
| psFtraceData->ui64LastSampledTimeCorrOSTimeStamp = 0; |
| |
| PVRGpuTraceSetEnabled(IMG_TRUE); |
| |
| /* Register a notifier to collect HWPerf data whenever the HW completes |
| * an operation. |
| */ |
| eError = PVRSRVRegisterCmdCompleteNotify( |
| &psFtraceData->hGPUTraceCmdCompleteHandle, |
| &RGXHWPerfFTraceCmdCompleteNotify, |
| gpsRgxDevInfo); |
| PVR_LOGG_IF_ERROR(eError, "PVRSRVRegisterCmdCompleteNotify", err_close_stream); |
| |
| err_out: |
| PVR_DPF_RETURN_RC(eError); |
| |
| err_close_stream: |
| PVRGpuTraceSetEnabled(IMG_FALSE); |
| |
| TLClientCloseStream(DIRECT_BRIDGE_HANDLE, |
| psFtraceData->hGPUTraceTLStream); |
| psFtraceData->hGPUTraceTLStream = 0; |
| goto err_out; |
| } |
| |
| /* Caller must now hold hFTraceLock before calling this method. |
| */ |
| static PVRSRV_ERROR RGXHWPerfFTraceGPUDisable(IMG_BOOL bDeInit) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGX_HWPERF_FTRACE_DATA *psFtraceData; |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(gpsRgxDevNode && gpsRgxDevInfo); |
| |
| psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| |
| PVRGpuTraceSetEnabled(IMG_FALSE); |
| PVRGpuTraceSetPreEnabled(IMG_FALSE); |
| |
| if (!bDeInit) |
| { |
| PVR_ASSERT(OSLockIsLocked(psFtraceData->hFTraceLock)); |
| |
| eError = PVRSRVRGXCtrlHWPerfKM(NULL, gpsRgxDevNode, RGX_HWPERF_STREAM_ID0_FW, IMG_FALSE, (RGX_HWPERF_EVENT_MASK_NONE)); |
| PVR_LOG_IF_ERROR(eError, "PVRSRVRGXCtrlHWPerfKM"); |
| } |
| |
| if (psFtraceData->hGPUTraceCmdCompleteHandle) |
| { |
| /* Tracing is being turned off. Unregister the notifier. */ |
| eError = PVRSRVUnregisterCmdCompleteNotify( |
| psFtraceData->hGPUTraceCmdCompleteHandle); |
| PVR_LOG_IF_ERROR(eError, "PVRSRVUnregisterCmdCompleteNotify"); |
| psFtraceData->hGPUTraceCmdCompleteHandle = NULL; |
| } |
| |
| if (psFtraceData->hGPUTraceTLStream) |
| { |
| IMG_PBYTE pbTmp = NULL; |
| IMG_UINT32 ui32Tmp = 0; |
| |
| /* We have to flush both the L1 (FW) and L2 (Host) buffers in case there |
| * are some events left unprocessed in this FTrace/systrace "session" |
| * (note that even if we have just disabled HWPerf on the FW some packets |
| * could have been generated and already copied to L2 by the MISR handler). |
| * |
| * With the following calls we will both copy new data to the Host buffer |
| * (done by the producer callback in TLClientAcquireData) and advance |
| * the read offset in the buffer to catch up with the latest events. |
| */ |
| eError = TLClientAcquireData(DIRECT_BRIDGE_HANDLE, |
| psFtraceData->hGPUTraceTLStream, |
| &pbTmp, &ui32Tmp); |
| PVR_LOG_IF_ERROR(eError, "TLClientCloseStream"); |
| |
| /* Let close stream perform the release data on the outstanding acquired data */ |
| eError = TLClientCloseStream(DIRECT_BRIDGE_HANDLE, |
| psFtraceData->hGPUTraceTLStream); |
| PVR_LOG_IF_ERROR(eError, "TLClientCloseStream"); |
| |
| psFtraceData->hGPUTraceTLStream = NULL; |
| } |
| |
| if (psFtraceData->ui64LastTimeCorrClock != RGXTIMECORR_CLOCK_SCHED) |
| { |
| RGXGPUFreqCalibrateSetClockSource(gpsRgxDevNode, |
| psFtraceData->ui64LastTimeCorrClock); |
| } |
| |
| PVR_DPF_RETURN_RC(eError); |
| } |
| |
| PVRSRV_ERROR RGXHWPerfFTraceGPUEventsEnabledSet(IMG_BOOL bNewValue) |
| { |
| IMG_BOOL bOldValue; |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGX_HWPERF_FTRACE_DATA *psFtraceData; |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(gpsRgxDevNode && gpsRgxDevInfo); |
| |
| psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| |
| /* About to create/destroy FTrace resources, lock critical section |
| * to avoid HWPerf MISR thread contention. |
| */ |
| OSLockAcquire(psFtraceData->hFTraceLock); |
| |
| bOldValue = PVRGpuTraceEnabled(); |
| |
| if (bOldValue != bNewValue) |
| { |
| if (bNewValue) |
| { |
| eError = RGXHWPerfFTraceGPUEnable(); |
| } |
| else |
| { |
| eError = RGXHWPerfFTraceGPUDisable(IMG_FALSE); |
| } |
| } |
| |
| OSLockRelease(psFtraceData->hFTraceLock); |
| |
| PVR_DPF_RETURN_RC(eError); |
| } |
| |
| PVRSRV_ERROR PVRGpuTraceEnabledSet(IMG_BOOL bNewValue) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| |
| /* This entry point from DebugFS must take the global |
| * bridge lock at this outer level of the stack before calling |
| * into the RGX part of the driver which can lead to RGX |
| * device data changes and communication with the FW which |
| * all requires the bridge lock. |
| */ |
| OSAcquireBridgeLock(); |
| eError = RGXHWPerfFTraceGPUEventsEnabledSet(bNewValue); |
| OSReleaseBridgeLock(); |
| |
| PVR_DPF_RETURN_RC(eError); |
| } |
| |
| /* Calculate the OS timestamp given an RGX timestamp in the HWPerf event. */ |
| static uint64_t |
| CalculateEventTimestamp(PVRSRV_RGXDEV_INFO *psDevInfo, |
| uint32_t ui32TimeCorrIndex, |
| uint64_t ui64EventTimestamp) |
| { |
| RGXFWIF_GPU_UTIL_FWCB *psGpuUtilFWCB = psDevInfo->psRGXFWIfGpuUtilFWCb; |
| RGX_HWPERF_FTRACE_DATA *psFtraceData = psDevInfo->pvGpuFtraceData; |
| RGXFWIF_TIME_CORR *psTimeCorr = &psGpuUtilFWCB->sTimeCorr[ui32TimeCorrIndex]; |
| uint64_t ui64CRTimeStamp = psTimeCorr->ui64CRTimeStamp; |
| uint64_t ui64OSTimeStamp = psTimeCorr->ui64OSTimeStamp; |
| uint32_t ui32CRDeltaToOSDeltaKNs = psTimeCorr->ui32CRDeltaToOSDeltaKNs; |
| uint64_t ui64EventOSTimestamp, deltaRgxTimer, delta_ns; |
| |
| if (psFtraceData->ui64LastSampledTimeCorrOSTimeStamp > ui64OSTimeStamp) |
| { |
| /* The previous packet had a time reference (time correlation data) more |
| * recent than the one in the current packet, it means the timer |
| * correlation array wrapped too quickly (buffer too small) and in the |
| * previous call to RGXHWPerfFTraceGPUUfoEvent we read one of the |
| * newest timer correlations rather than one of the oldest ones. |
| */ |
| PVR_DPF((PVR_DBG_ERROR, "%s: The timestamps computed so far could be " |
| "wrong! The time correlation array size should be increased " |
| "to avoid this.", __func__)); |
| } |
| |
| psFtraceData->ui64LastSampledTimeCorrOSTimeStamp = ui64OSTimeStamp; |
| |
| /* RGX CR timer ticks delta */ |
| deltaRgxTimer = ui64EventTimestamp - ui64CRTimeStamp; |
| /* RGX time delta in nanoseconds */ |
| delta_ns = RGXFWIF_GET_DELTA_OSTIME_NS(deltaRgxTimer, ui32CRDeltaToOSDeltaKNs); |
| /* Calculate OS time of HWPerf event */ |
| ui64EventOSTimestamp = ui64OSTimeStamp + delta_ns; |
| |
| PVR_DPF((PVR_DBG_VERBOSE, "%s: psCurrentDvfs RGX %llu, OS %llu, DVFSCLK %u", |
| __func__, ui64CRTimeStamp, ui64OSTimeStamp, |
| psTimeCorr->ui32CoreClockSpeed)); |
| |
| return ui64EventOSTimestamp; |
| } |
| |
| void RGXHWPerfFTraceGPUEnqueueEvent(PVRSRV_RGXDEV_INFO *psDevInfo, |
| IMG_UINT32 ui32CtxId, IMG_UINT32 ui32JobId, |
| RGX_HWPERF_KICK_TYPE eKickType) |
| { |
| PVR_DPF_ENTERED; |
| |
| PVR_DPF((PVR_DBG_VERBOSE, "RGXHWPerfFTraceGPUEnqueueEvent: ui32CtxId %u, " |
| "ui32JobId %u", ui32CtxId, ui32JobId)); |
| |
| PVRGpuTraceClientWork(ui32CtxId, ui32JobId, |
| RGXHWPerfKickTypeToStr(eKickType)); |
| |
| PVR_DPF_RETURN; |
| } |
| |
| |
| static void RGXHWPerfFTraceGPUSwitchEvent(PVRSRV_RGXDEV_INFO *psDevInfo, |
| RGX_HWPERF_V2_PACKET_HDR* psHWPerfPkt, const IMG_CHAR* pszWorkName, |
| PVR_GPUTRACE_SWITCH_TYPE eSwType) |
| { |
| IMG_UINT64 ui64Timestamp; |
| RGX_HWPERF_HW_DATA_FIELDS* psHWPerfPktData; |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(psHWPerfPkt); |
| PVR_ASSERT(pszWorkName); |
| |
| psHWPerfPktData = (RGX_HWPERF_HW_DATA_FIELDS*) RGX_HWPERF_GET_PACKET_DATA_BYTES(psHWPerfPkt); |
| |
| ui64Timestamp = CalculateEventTimestamp(psDevInfo, psHWPerfPktData->ui32TimeCorrIndex, |
| psHWPerfPkt->ui64Timestamp); |
| |
| PVR_DPF((PVR_DBG_VERBOSE, "RGXHWPerfFTraceGPUSwitchEvent: %s ui32ExtJobRef=%d, ui32IntJobRef=%d, eSwType=%d", |
| pszWorkName, psHWPerfPktData->ui32DMContext, psHWPerfPktData->ui32IntJobRef, eSwType)); |
| |
| PVRGpuTraceWorkSwitch(ui64Timestamp, psHWPerfPktData->ui32DMContext, psHWPerfPktData->ui32CtxPriority, |
| psHWPerfPktData->ui32IntJobRef, pszWorkName, eSwType); |
| |
| PVR_DPF_RETURN; |
| } |
| |
| static void RGXHWPerfFTraceGPUUfoEvent(PVRSRV_RGXDEV_INFO *psDevInfo, |
| RGX_HWPERF_V2_PACKET_HDR* psHWPerfPkt) |
| { |
| IMG_UINT64 ui64Timestamp; |
| RGX_HWPERF_UFO_DATA *psHWPerfPktData; |
| IMG_UINT32 ui32UFOCount; |
| RGX_HWPERF_UFO_DATA_ELEMENT *puData; |
| |
| psHWPerfPktData = (RGX_HWPERF_UFO_DATA *) |
| RGX_HWPERF_GET_PACKET_DATA_BYTES(psHWPerfPkt); |
| |
| ui32UFOCount = RGX_HWPERF_GET_UFO_STREAMSIZE(psHWPerfPktData->ui32StreamInfo); |
| puData = (RGX_HWPERF_UFO_DATA_ELEMENT *) (((IMG_BYTE *) psHWPerfPktData) |
| + RGX_HWPERF_GET_UFO_STREAMOFFSET(psHWPerfPktData->ui32StreamInfo)); |
| |
| ui64Timestamp = CalculateEventTimestamp(psDevInfo, psHWPerfPktData->ui32TimeCorrIndex, |
| psHWPerfPkt->ui64Timestamp); |
| |
| PVR_DPF((PVR_DBG_VERBOSE, "RGXHWPerfFTraceGPUUfoEvent: ui32ExtJobRef=%d, " |
| "ui32IntJobRef=%d", psHWPerfPktData->ui32ExtJobRef, |
| psHWPerfPktData->ui32IntJobRef)); |
| |
| PVRGpuTraceUfo(ui64Timestamp, psHWPerfPktData->eEvType, |
| psHWPerfPktData->ui32ExtJobRef, psHWPerfPktData->ui32DMContext, |
| psHWPerfPktData->ui32IntJobRef, ui32UFOCount, puData); |
| } |
| |
| static void RGXHWPerfFTraceGPUFirmwareEvent(PVRSRV_RGXDEV_INFO *psDevInfo, |
| RGX_HWPERF_V2_PACKET_HDR* psHWPerfPkt, const IMG_CHAR* pszWorkName, |
| PVR_GPUTRACE_SWITCH_TYPE eSwType) |
| |
| { |
| uint64_t ui64Timestamp; |
| RGX_HWPERF_FW_DATA *psHWPerfPktData = (RGX_HWPERF_FW_DATA *) |
| RGX_HWPERF_GET_PACKET_DATA_BYTES(psHWPerfPkt); |
| |
| ui64Timestamp = CalculateEventTimestamp(psDevInfo, psHWPerfPktData->ui32TimeCorrIndex, |
| psHWPerfPkt->ui64Timestamp); |
| |
| PVRGpuTraceFirmware(ui64Timestamp, pszWorkName, eSwType); |
| } |
| |
| static IMG_BOOL ValidAndEmitFTraceEvent(PVRSRV_RGXDEV_INFO *psDevInfo, |
| RGX_HWPERF_V2_PACKET_HDR* psHWPerfPkt) |
| { |
| RGX_HWPERF_EVENT_TYPE eType; |
| RGX_HWPERF_FTRACE_DATA *psFtraceData = psDevInfo->pvGpuFtraceData; |
| IMG_UINT32 ui32HwEventTypeIndex; |
| static const struct { |
| IMG_CHAR* pszName; |
| PVR_GPUTRACE_SWITCH_TYPE eSwType; |
| } aszHwEventTypeMap[] = { |
| { /* RGX_HWPERF_FW_BGSTART */ "BG", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_FW_BGEND */ "BG", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_FW_IRQSTART */ "IRQ", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_FW_IRQEND */ "IRQ", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_FW_DBGSTART */ "DBG", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_FW_DBGEND */ "DBG", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_PMOOM_TAPAUSE */ "PMOOM_TAPAUSE", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_TAKICK */ "TA", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_TAFINISHED */ "TA", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_3DTQKICK */ "TQ3D", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_3DKICK */ "3D", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_3DFINISHED */ "3D", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_CDMKICK */ "CDM", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_CDMFINISHED */ "CDM", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_TLAKICK */ "TQ2D", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_TLAFINISHED */ "TQ2D", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_3DSPMKICK */ "3DSPM", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_PERIODIC */ NULL, 0 }, /* PERIODIC not supported */ |
| { /* RGX_HWPERF_HW_RTUKICK */ "RTU", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_RTUFINISHED */ "RTU", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_SHGKICK */ "SHG", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_SHGFINISHED */ "SHG", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_3DTQFINISHED */ "TQ3D", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_3DSPMFINISHED */ "3DSPM", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| { /* RGX_HWPERF_HW_PMOOM_TARESUME */ "PMOOM_TARESUME", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_TDMKICK */ "TDM", PVR_GPUTRACE_SWITCH_TYPE_BEGIN }, |
| { /* RGX_HWPERF_HW_TDMFINISHED */ "TDM", PVR_GPUTRACE_SWITCH_TYPE_END }, |
| }; |
| static_assert(RGX_HWPERF_HW_EVENT_RANGE0_FIRST_TYPE == RGX_HWPERF_FW_EVENT_RANGE_LAST_TYPE + 1, |
| "FW and HW events are not contiguous in RGX_HWPERF_EVENT_TYPE"); |
| |
| PVR_ASSERT(psHWPerfPkt); |
| eType = RGX_HWPERF_GET_TYPE(psHWPerfPkt); |
| |
| if (psFtraceData->ui32FTraceLastOrdinal != psHWPerfPkt->ui32Ordinal - 1) |
| { |
| RGX_HWPERF_STREAM_ID eStreamId = RGX_HWPERF_GET_STREAM_ID(psHWPerfPkt); |
| PVRGpuTraceEventsLost(eStreamId, |
| psFtraceData->ui32FTraceLastOrdinal, |
| psHWPerfPkt->ui32Ordinal); |
| PVR_DPF((PVR_DBG_ERROR, "FTrace events lost (stream_id = %u, ordinal: last = %u, current = %u)", |
| eStreamId, psFtraceData->ui32FTraceLastOrdinal, psHWPerfPkt->ui32Ordinal)); |
| } |
| |
| psFtraceData->ui32FTraceLastOrdinal = psHWPerfPkt->ui32Ordinal; |
| |
| /* Process UFO packets */ |
| if (eType == RGX_HWPERF_UFO) |
| { |
| RGXHWPerfFTraceGPUUfoEvent(psDevInfo, psHWPerfPkt); |
| return IMG_TRUE; |
| } |
| |
| if (eType <= RGX_HWPERF_HW_EVENT_RANGE0_LAST_TYPE) |
| { |
| /* this ID belongs to range 0, so index directly in range 0 */ |
| ui32HwEventTypeIndex = eType - RGX_HWPERF_FW_EVENT_RANGE_FIRST_TYPE; |
| } |
| else |
| { |
| /* this ID belongs to range 1, so first index in range 1 and skip number of slots used up for range 0 */ |
| ui32HwEventTypeIndex = (eType - RGX_HWPERF_HW_EVENT_RANGE1_FIRST_TYPE) + |
| (RGX_HWPERF_HW_EVENT_RANGE0_LAST_TYPE - RGX_HWPERF_FW_EVENT_RANGE_FIRST_TYPE + 1); |
| } |
| |
| if (ui32HwEventTypeIndex >= IMG_ARR_NUM_ELEMS(aszHwEventTypeMap)) |
| goto err_unsupported; |
| |
| if (aszHwEventTypeMap[ui32HwEventTypeIndex].pszName == NULL) |
| { |
| /* Not supported map entry, ignore event */ |
| goto err_unsupported; |
| } |
| |
| if (HWPERF_PACKET_IS_HW_TYPE(eType)) |
| { |
| RGXHWPerfFTraceGPUSwitchEvent(psDevInfo, psHWPerfPkt, |
| aszHwEventTypeMap[ui32HwEventTypeIndex].pszName, |
| aszHwEventTypeMap[ui32HwEventTypeIndex].eSwType); |
| } |
| else if (HWPERF_PACKET_IS_FW_TYPE(eType)) |
| { |
| RGXHWPerfFTraceGPUFirmwareEvent(psDevInfo, psHWPerfPkt, |
| aszHwEventTypeMap[ui32HwEventTypeIndex].pszName, |
| aszHwEventTypeMap[ui32HwEventTypeIndex].eSwType); |
| } |
| else |
| { |
| goto err_unsupported; |
| } |
| |
| return IMG_TRUE; |
| |
| err_unsupported: |
| PVR_DPF((PVR_DBG_VERBOSE, "%s: Unsupported event type %d", __func__, eType)); |
| return IMG_FALSE; |
| } |
| |
| |
| static void RGXHWPerfFTraceGPUProcessPackets(PVRSRV_RGXDEV_INFO *psDevInfo, |
| IMG_PBYTE pBuffer, IMG_UINT32 ui32ReadLen) |
| { |
| IMG_UINT32 ui32TlPackets = 0; |
| IMG_UINT32 ui32HWPerfPackets = 0; |
| IMG_UINT32 ui32HWPerfPacketsSent = 0; |
| IMG_PBYTE pBufferEnd; |
| PVRSRVTL_PPACKETHDR psHDRptr; |
| PVRSRVTL_PACKETTYPE ui16TlType; |
| |
| PVR_DPF_ENTERED; |
| |
| PVR_ASSERT(psDevInfo); |
| PVR_ASSERT(pBuffer); |
| PVR_ASSERT(ui32ReadLen); |
| |
| /* Process the TL Packets |
| */ |
| pBufferEnd = pBuffer+ui32ReadLen; |
| psHDRptr = GET_PACKET_HDR(pBuffer); |
| while ( psHDRptr < (PVRSRVTL_PPACKETHDR)pBufferEnd ) |
| { |
| ui16TlType = GET_PACKET_TYPE(psHDRptr); |
| if (ui16TlType == PVRSRVTL_PACKETTYPE_DATA) |
| { |
| IMG_UINT16 ui16DataLen = GET_PACKET_DATA_LEN(psHDRptr); |
| if (0 == ui16DataLen) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXHWPerfFTraceGPUProcessPackets: ZERO Data in TL data packet: %p", psHDRptr)); |
| } |
| else |
| { |
| RGX_HWPERF_V2_PACKET_HDR* psHWPerfPkt; |
| RGX_HWPERF_V2_PACKET_HDR* psHWPerfEnd; |
| |
| /* Check for lost hwperf data packets */ |
| psHWPerfEnd = RGX_HWPERF_GET_PACKET(GET_PACKET_DATA_PTR(psHDRptr)+ui16DataLen); |
| psHWPerfPkt = RGX_HWPERF_GET_PACKET(GET_PACKET_DATA_PTR(psHDRptr)); |
| do |
| { |
| if (ValidAndEmitFTraceEvent(psDevInfo, psHWPerfPkt)) |
| { |
| ui32HWPerfPacketsSent++; |
| } |
| ui32HWPerfPackets++; |
| psHWPerfPkt = RGX_HWPERF_GET_NEXT_PACKET(psHWPerfPkt); |
| } |
| while (psHWPerfPkt < psHWPerfEnd); |
| } |
| } |
| else if (ui16TlType == PVRSRVTL_PACKETTYPE_MOST_RECENT_WRITE_FAILED) |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "RGXHWPerfFTraceGPUProcessPackets: Indication that the transport buffer was full")); |
| } |
| else |
| { |
| /* else Ignore padding packet type and others */ |
| PVR_DPF((PVR_DBG_MESSAGE, "RGXHWPerfFTraceGPUProcessPackets: Ignoring TL packet, type %d", ui16TlType )); |
| } |
| |
| psHDRptr = GET_NEXT_PACKET_ADDR(psHDRptr); |
| ui32TlPackets++; |
| } |
| |
| PVR_DPF((PVR_DBG_VERBOSE, "RGXHWPerfFTraceGPUProcessPackets: TL " |
| "Packets processed %03d, HWPerf packets %03d, sent %03d", |
| ui32TlPackets, ui32HWPerfPackets, ui32HWPerfPacketsSent)); |
| |
| PVR_DPF_RETURN; |
| } |
| |
| |
| static |
| void RGXHWPerfFTraceCmdCompleteNotify(PVRSRV_CMDCOMP_HANDLE hCmdCompHandle) |
| { |
| PVRSRV_DATA* psPVRSRVData = PVRSRVGetPVRSRVData(); |
| PVRSRV_RGXDEV_INFO* psDeviceInfo = hCmdCompHandle; |
| RGX_HWPERF_FTRACE_DATA* psFtraceData; |
| PVRSRV_ERROR eError; |
| IMG_PBYTE pBuffer; |
| IMG_UINT32 ui32ReadLen; |
| |
| PVR_DPF_ENTERED; |
| |
| /* Exit if no HWPerf enabled device exits */ |
| PVR_ASSERT(psDeviceInfo != NULL && |
| psPVRSRVData != NULL && |
| gpsRgxDevInfo != NULL); |
| |
| psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| |
| /* Command-complete notifiers can run concurrently. If this is |
| * happening, just bail out and let the previous call finish. |
| * This is ok because we can process the queued packets on the next call. |
| */ |
| if (!PVRGpuTraceEnabled() || !(OSTryLockAcquire(psFtraceData->hFTraceLock))) |
| { |
| PVR_DPF_RETURN; |
| } |
| |
| /* PVRGpuTraceSetEnabled() and hGPUTraceTLStream are now |
| * called / atomically set inside the hFTraceLock so just assert here. |
| */ |
| PVR_ASSERT(psFtraceData->hGPUTraceTLStream); |
| |
| /* If we have a valid stream attempt to acquire some data */ |
| eError = TLClientAcquireData(DIRECT_BRIDGE_HANDLE, psFtraceData->hGPUTraceTLStream, &pBuffer, &ui32ReadLen); |
| if (eError == PVRSRV_OK) |
| { |
| /* Process the HWPerf packets and release the data */ |
| if (ui32ReadLen > 0) |
| { |
| PVR_DPF((PVR_DBG_VERBOSE, "RGXHWPerfFTraceGPUThread: DATA AVAILABLE offset=%p, length=%d", pBuffer, ui32ReadLen)); |
| |
| /* Process the transport layer data for HWPerf packets... */ |
| RGXHWPerfFTraceGPUProcessPackets(psDeviceInfo, pBuffer, ui32ReadLen); |
| |
| eError = TLClientReleaseData(DIRECT_BRIDGE_HANDLE, psFtraceData->hGPUTraceTLStream); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_LOG_ERROR(eError, "TLClientReleaseData"); |
| |
| /* Serious error, disable FTrace GPU events */ |
| |
| /* Release TraceLock so we always have the locking |
| * order BridgeLock->TraceLock to prevent AB-BA deadlocks*/ |
| OSLockRelease(psFtraceData->hFTraceLock); |
| OSAcquireBridgeLock(); |
| OSLockAcquire(psFtraceData->hFTraceLock); |
| RGXHWPerfFTraceGPUDisable(IMG_FALSE); |
| OSLockRelease(psFtraceData->hFTraceLock); |
| OSReleaseBridgeLock(); |
| goto out; |
| |
| } |
| } /* else no data, ignore */ |
| } |
| else if (eError != PVRSRV_ERROR_TIMEOUT) |
| { |
| PVR_LOG_ERROR(eError, "TLClientAcquireData"); |
| } |
| |
| OSLockRelease(psFtraceData->hFTraceLock); |
| out: |
| PVR_DPF_RETURN; |
| } |
| |
| PVRSRV_ERROR RGXHWPerfFTraceGPUInit(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| PVRSRV_ERROR eError; |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGX_HWPERF_FTRACE_DATA *psData = OSAllocMem(sizeof(RGX_HWPERF_FTRACE_DATA)); |
| if (psData == NULL) |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| |
| /* We initialise it only once because we want to track if any |
| * packets were dropped. */ |
| psData->ui32FTraceLastOrdinal = IMG_UINT32_MAX - 1; |
| |
| eError = OSLockCreate(&psData->hFTraceLock, LOCK_TYPE_DISPATCH); |
| PVR_LOGG_IF_ERROR(eError, "OSLockCreate", e0); |
| |
| eError = OSLockCreate(&psData->hLockFTraceEventLock, LOCK_TYPE_PASSIVE); |
| PVR_LOGG_IF_ERROR(eError, "OSLockCreate", e1); |
| |
| psData->uiUfoEventRef = 0; |
| |
| psDevInfo->pvGpuFtraceData = psData; |
| |
| return PVRSRV_OK; |
| |
| e1: |
| OSLockDestroy(psData->hFTraceLock); |
| e0: |
| OSFreeMem(psData); |
| |
| return eError; |
| } |
| |
| void RGXHWPerfFTraceGPUDeInit(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice; |
| RGX_HWPERF_FTRACE_DATA *psData = psDevInfo->pvGpuFtraceData; |
| |
| OSLockDestroy(psData->hFTraceLock); |
| OSLockDestroy(psData->hLockFTraceEventLock); |
| OSFreeMem(psDevInfo->pvGpuFtraceData); |
| } |
| |
| void PVRGpuTraceEnableUfoCallback(void) |
| { |
| PVRSRV_ERROR eError; |
| RGX_HWPERF_FTRACE_DATA *psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| |
| OSLockAcquire(psFtraceData->hLockFTraceEventLock); |
| |
| if (psFtraceData->uiUfoEventRef++ == 0) |
| { |
| IMG_UINT64 ui64Filter = RGX_HWPERF_EVENT_MASK_VALUE(RGX_HWPERF_UFO) | |
| gpsRgxDevInfo->ui64HWPerfFilter; |
| /* Small chance exists that ui64HWPerfFilter can be changed here and |
| * the newest filter value will be changed to the old one + UFO event. |
| * This is not a critical problem. */ |
| eError = PVRSRVRGXCtrlHWPerfKM(NULL, gpsRgxDevNode, |
| RGX_HWPERF_STREAM_ID0_FW, |
| IMG_FALSE, ui64Filter); |
| if (eError == PVRSRV_ERROR_NOT_INITIALISED) |
| { |
| /* If we land here that means that the FW is not initialised yet. |
| * We stored the filter and it will be passed to the firmware |
| * during it's initialisation phase. So ignore. */ |
| } |
| else if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "Could not enable UFO HWPerf event.")); |
| } |
| } |
| |
| OSLockRelease(psFtraceData->hLockFTraceEventLock); |
| } |
| |
| void PVRGpuTraceDisableUfoCallback(void) |
| { |
| PVRSRV_ERROR eError; |
| RGX_HWPERF_FTRACE_DATA *psFtraceData; |
| |
| /* We have to check if lock is valid because on driver unload |
| * RGXHWPerfFTraceGPUDeInit is called before kernel disables the ftrace |
| * events. This means that the lock will be destroyed before this callback |
| * is called. |
| * We can safely return if that situation happens because driver will be |
| * unloaded so we don't care about HWPerf state anymore. */ |
| if (gpsRgxDevInfo == NULL || gpsRgxDevInfo->pvGpuFtraceData == NULL) |
| return; |
| |
| psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| |
| OSLockAcquire(psFtraceData->hLockFTraceEventLock); |
| |
| if (--psFtraceData->uiUfoEventRef == 0) |
| { |
| IMG_UINT64 ui64Filter = ~(RGX_HWPERF_EVENT_MASK_VALUE(RGX_HWPERF_UFO)) & |
| gpsRgxDevInfo->ui64HWPerfFilter; |
| /* Small chance exists that ui64HWPerfFilter can be changed here and |
| * the newest filter value will be changed to the old one + UFO event. |
| * This is not a critical problem. */ |
| eError = PVRSRVRGXCtrlHWPerfKM(NULL, gpsRgxDevNode, |
| RGX_HWPERF_STREAM_ID0_FW, |
| IMG_FALSE, ui64Filter); |
| if (eError == PVRSRV_ERROR_NOT_INITIALISED) |
| { |
| /* If we land here that means that the FW is not initialised yet. |
| * We stored the filter and it will be passed to the firmware |
| * during it's initialisation phase. So ignore. */ |
| } |
| else if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "Could not disable UFO HWPerf event.")); |
| } |
| } |
| |
| OSLockRelease(psFtraceData->hLockFTraceEventLock); |
| } |
| |
| void PVRGpuTraceEnableFirmwareActivityCallback(void) |
| { |
| RGX_HWPERF_FTRACE_DATA *psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| uint64_t ui64Filter; |
| int i; |
| |
| OSLockAcquire(psFtraceData->hLockFTraceEventLock); |
| |
| ui64Filter = gpsRgxDevInfo->ui64HWPerfFilter; |
| |
| /* Enable all FW events. */ |
| for (i = RGX_HWPERF_FW_EVENT_RANGE_FIRST_TYPE; |
| i <= RGX_HWPERF_FW_EVENT_RANGE_LAST_TYPE; |
| i++) |
| { |
| ui64Filter |= RGX_HWPERF_EVENT_MASK_VALUE(i); |
| } |
| |
| if (PVRSRVRGXCtrlHWPerfKM(NULL, gpsRgxDevNode, RGX_HWPERF_STREAM_ID0_FW, |
| IMG_FALSE, ui64Filter) != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "Could not enable HWPerf event for firmware task timings.")); |
| } |
| |
| OSLockRelease(psFtraceData->hLockFTraceEventLock); |
| } |
| |
| void PVRGpuTraceDisableFirmwareActivityCallback(void) |
| { |
| RGX_HWPERF_FTRACE_DATA *psFtraceData = gpsRgxDevInfo->pvGpuFtraceData; |
| uint64_t ui64Filter; |
| int i; |
| |
| if (!psFtraceData) |
| return; |
| |
| OSLockAcquire(psFtraceData->hLockFTraceEventLock); |
| |
| ui64Filter = gpsRgxDevInfo->ui64HWPerfFilter; |
| |
| /* Disable all FW events. */ |
| for (i = RGX_HWPERF_FW_EVENT_RANGE_FIRST_TYPE; |
| i <= RGX_HWPERF_FW_EVENT_RANGE_LAST_TYPE; |
| i++) |
| { |
| ui64Filter &= ~RGX_HWPERF_EVENT_MASK_VALUE(i); |
| } |
| |
| if (PVRSRVRGXCtrlHWPerfKM(NULL, gpsRgxDevNode, RGX_HWPERF_STREAM_ID0_FW, |
| IMG_FALSE, ui64Filter) != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "Could not disable HWPerf event for firmware task timings.")); |
| } |
| |
| OSLockRelease(psFtraceData->hLockFTraceEventLock); |
| } |
| |
| #endif /* SUPPORT_GPUTRACE_EVENTS */ |
| |
| /****************************************************************************** |
| * Currently only implemented on Linux. Feature can be enabled to provide |
| * an interface to 3rd-party kernel modules that wish to access the |
| * HWPerf data. The API is documented in the rgxapi_km.h header and |
| * the rgx_hwperf* headers. |
| *****************************************************************************/ |
| |
| /* Internal HWPerf kernel connection/device data object to track the state |
| * of a client session. |
| */ |
| typedef struct |
| { |
| PVRSRV_DEVICE_NODE* psRgxDevNode; |
| PVRSRV_RGXDEV_INFO* psRgxDevInfo; |
| |
| /* TL Open/close state */ |
| IMG_HANDLE hSD[RGX_HWPERF_STREAM_ID_LAST]; |
| |
| /* TL Acquire/release state */ |
| IMG_PBYTE pHwpBuf[RGX_HWPERF_STREAM_ID_LAST]; /*!< buffer returned to user in acquire call */ |
| IMG_PBYTE pHwpBufEnd[RGX_HWPERF_STREAM_ID_LAST]; /*!< pointer to end of HwpBuf */ |
| IMG_PBYTE pTlBuf[RGX_HWPERF_STREAM_ID_LAST]; /*!< buffer obtained via TlAcquireData */ |
| IMG_PBYTE pTlBufPos[RGX_HWPERF_STREAM_ID_LAST]; /*!< initial position in TlBuf to acquire packets */ |
| IMG_PBYTE pTlBufRead[RGX_HWPERF_STREAM_ID_LAST]; /*!< pointer to the last packet read */ |
| IMG_UINT32 ui32AcqDataLen[RGX_HWPERF_STREAM_ID_LAST]; /*!< length of acquired TlBuf */ |
| IMG_BOOL bRelease[RGX_HWPERF_STREAM_ID_LAST]; /*!< used to determine whether or not to release currently held TlBuf */ |
| |
| |
| } RGX_KM_HWPERF_DEVDATA; |
| |
| |
| PVRSRV_ERROR RGXHWPerfLazyConnect( |
| IMG_HANDLE* phDevData) |
| { |
| RGX_KM_HWPERF_DEVDATA* psDevData; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!phDevData) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* Clear the handle to aid error checking by caller */ |
| *phDevData = NULL; |
| |
| /* Check the HWPerf module is initialised before we allow a connection */ |
| if (!gpsRgxDevNode || !gpsRgxDevInfo) |
| { |
| return PVRSRV_ERROR_INVALID_DEVICE; |
| } |
| |
| /* Allocation the session object for this connection */ |
| psDevData = OSAllocZMem(sizeof(*psDevData)); |
| if (psDevData == NULL) |
| { |
| return PVRSRV_ERROR_OUT_OF_MEMORY; |
| } |
| psDevData->psRgxDevNode = gpsRgxDevNode; |
| psDevData->psRgxDevInfo = gpsRgxDevInfo; |
| |
| *phDevData = psDevData; |
| |
| return PVRSRV_OK; |
| } |
| |
| PVRSRV_ERROR RGXHWPerfOpen( |
| IMG_HANDLE hDevData) |
| { |
| PVRSRV_ERROR eError; |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*) hDevData; |
| IMG_UINT32 ui32BufSize; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!psDevData) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* Check the HWPerf module is initialised before we allow a connection */ |
| if (!psDevData->psRgxDevNode || !psDevData->psRgxDevInfo) |
| { |
| return PVRSRV_ERROR_INVALID_DEVICE; |
| } |
| |
| /* In the case where the AppHint has not been set we need to |
| * initialise the HWPerf resources here. Allocated on-demand |
| * to reduce RAM foot print on systems not needing HWPerf. |
| */ |
| OSLockAcquire(gpsRgxDevInfo->hHWPerfLock); |
| if (RGXHWPerfIsInitRequired()) |
| { |
| eError = RGXHWPerfInitOnDemandResources(); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Initialization of on-demand HWPerfFW" |
| " resources failed", __FUNCTION__)); |
| OSLockRelease(gpsRgxDevInfo->hHWPerfLock); |
| goto e0; |
| } |
| } |
| OSLockRelease(gpsRgxDevInfo->hHWPerfLock); |
| |
| OSLockAcquire(gpsRgxDevInfo->hLockHWPerfHostStream); |
| if (gpsRgxDevInfo->hHWPerfHostStream == NULL) |
| { |
| eError = RGXHWPerfHostInitOnDemandResources(); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Initialization of on-demand HWPerfHost" |
| " resources failed", __FUNCTION__)); |
| OSLockRelease(gpsRgxDevInfo->hLockHWPerfHostStream); |
| goto e0; |
| } |
| } |
| OSLockRelease(gpsRgxDevInfo->hLockHWPerfHostStream); |
| |
| /* Open the RGX TL stream for reading in this session */ |
| eError = TLClientOpenStream(DIRECT_BRIDGE_HANDLE, |
| PVRSRV_TL_HWPERF_RGX_FW_STREAM, |
| PVRSRV_STREAM_FLAG_ACQUIRE_NONBLOCKING, |
| &psDevData->hSD[RGX_HWPERF_STREAM_ID0_FW]); |
| if (eError != PVRSRV_OK) |
| { |
| goto e1; |
| } |
| |
| /* Open the host TL stream for reading in this session */ |
| eError = TLClientOpenStream(DIRECT_BRIDGE_HANDLE, |
| PVRSRV_TL_HWPERF_HOST_SERVER_STREAM, |
| PVRSRV_STREAM_FLAG_ACQUIRE_NONBLOCKING, |
| &psDevData->hSD[RGX_HWPERF_STREAM_ID1_HOST]); |
| if (eError != PVRSRV_OK) |
| { |
| goto e1; |
| } |
| |
| /* Allocate a large enough buffer for use during the entire session to |
| * avoid the need to resize in the Acquire call as this might be in an ISR |
| * Choose size that can contain at least one packet. |
| */ |
| /* Allocate buffer for FW Stream */ |
| ui32BufSize = FW_STREAM_BUFFER_SIZE; |
| psDevData->pHwpBuf[RGX_HWPERF_STREAM_ID0_FW] = OSAllocMem(ui32BufSize); |
| if (psDevData->pHwpBuf[RGX_HWPERF_STREAM_ID0_FW] == NULL) |
| { |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto e1; |
| } |
| psDevData->pHwpBufEnd[RGX_HWPERF_STREAM_ID0_FW] = psDevData->pHwpBuf[RGX_HWPERF_STREAM_ID0_FW]+ui32BufSize; |
| |
| /* Allocate buffer for Host Stream */ |
| ui32BufSize = HOST_STREAM_BUFFER_SIZE; |
| psDevData->pHwpBuf[RGX_HWPERF_STREAM_ID1_HOST] = OSAllocMem(ui32BufSize); |
| if (psDevData->pHwpBuf[RGX_HWPERF_STREAM_ID1_HOST] == NULL) |
| { |
| eError = PVRSRV_ERROR_OUT_OF_MEMORY; |
| goto e2; |
| } |
| psDevData->pHwpBufEnd[RGX_HWPERF_STREAM_ID1_HOST] = psDevData->pHwpBuf[RGX_HWPERF_STREAM_ID1_HOST]+ui32BufSize; |
| |
| return PVRSRV_OK; |
| |
| e2: |
| OSFreeMem(psDevData->pHwpBuf[RGX_HWPERF_STREAM_ID0_FW]); |
| e1: |
| RGXHWPerfHostDeInit(); |
| e0: |
| RGXHWPerfDeinit(); |
| |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfConnect( |
| IMG_HANDLE* phDevData) |
| { |
| PVRSRV_ERROR eError; |
| |
| eError = RGXHWPerfLazyConnect(phDevData); |
| PVR_LOGG_IF_ERROR(eError, "RGXHWPerfLazyConnect", e0); |
| |
| eError = RGXHWPerfOpen(*phDevData); |
| PVR_LOGG_IF_ERROR(eError, "RGXHWPerfOpen", e1); |
| |
| return PVRSRV_OK; |
| |
| e1: |
| RGXHWPerfFreeConnection(*phDevData); |
| e0: |
| *phDevData = NULL; |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfControl( |
| IMG_HANDLE hDevData, |
| RGX_HWPERF_STREAM_ID eStreamId, |
| IMG_BOOL bToggle, |
| IMG_UINT64 ui64Mask) |
| { |
| PVRSRV_ERROR eError; |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*)hDevData; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!psDevData) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* Ensure we are initialised and have a valid device node */ |
| if (!psDevData->psRgxDevNode) |
| { |
| return PVRSRV_ERROR_INVALID_DEVICE; |
| } |
| |
| /* Call the internal server API */ |
| eError = PVRSRVRGXCtrlHWPerfKM(NULL, psDevData->psRgxDevNode, eStreamId, bToggle, ui64Mask); |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfConfigureAndEnableCounters( |
| IMG_HANDLE hDevData, |
| IMG_UINT32 ui32NumBlocks, |
| RGX_HWPERF_CONFIG_CNTBLK* asBlockConfigs) |
| { |
| PVRSRV_ERROR eError; |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*)hDevData; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!psDevData || ui32NumBlocks==0 || !asBlockConfigs) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (ui32NumBlocks > RGXFWIF_HWPERF_CTRL_BLKS_MAX) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* Ensure we are initialised and have a valid device node */ |
| if (!psDevData->psRgxDevNode) |
| { |
| return PVRSRV_ERROR_INVALID_DEVICE; |
| } |
| |
| /* Call the internal server API */ |
| eError = PVRSRVRGXConfigEnableHWPerfCountersKM(NULL, |
| psDevData->psRgxDevNode, ui32NumBlocks, asBlockConfigs); |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfDisableCounters( |
| IMG_HANDLE hDevData, |
| IMG_UINT32 ui32NumBlocks, |
| IMG_UINT16* aeBlockIDs) |
| { |
| PVRSRV_ERROR eError; |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*)hDevData; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!psDevData || ui32NumBlocks==0 || !aeBlockIDs) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (ui32NumBlocks > RGXFWIF_HWPERF_CTRL_BLKS_MAX) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* Ensure we are initialised and have a valid device node */ |
| if (!psDevData->psRgxDevNode) |
| { |
| return PVRSRV_ERROR_INVALID_DEVICE; |
| } |
| |
| /* Call the internal server API */ |
| eError = PVRSRVRGXCtrlHWPerfCountersKM(NULL, |
| psDevData->psRgxDevNode, IMG_FALSE, ui32NumBlocks, aeBlockIDs); |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfAcquireEvents( |
| IMG_HANDLE hDevData, |
| RGX_HWPERF_STREAM_ID eStreamId, |
| IMG_PBYTE* ppBuf, |
| IMG_UINT32* pui32BufLen) |
| { |
| PVRSRV_ERROR eError; |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*)hDevData; |
| IMG_PBYTE pDataDest; |
| IMG_UINT32 ui32TlPackets = 0; |
| IMG_PBYTE pBufferEnd; |
| PVRSRVTL_PPACKETHDR psHDRptr; |
| PVRSRVTL_PACKETTYPE ui16TlType; |
| |
| /* Reset the output arguments in case we discover an error */ |
| *ppBuf = NULL; |
| *pui32BufLen = 0; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!psDevData || eStreamId >= RGX_HWPERF_STREAM_ID_LAST) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (psDevData->pTlBuf[eStreamId] == NULL) |
| { |
| /* Acquire some data to read from the HWPerf TL stream */ |
| eError = TLClientAcquireData(DIRECT_BRIDGE_HANDLE, |
| psDevData->hSD[eStreamId], |
| &psDevData->pTlBuf[eStreamId], |
| &psDevData->ui32AcqDataLen[eStreamId]); |
| PVR_LOGR_IF_ERROR(eError, "TLClientAcquireData"); |
| |
| psDevData->pTlBufPos[eStreamId] = psDevData->pTlBuf[eStreamId]; |
| } |
| |
| /* TL indicates no data exists so return OK and zero. */ |
| if ((psDevData->pTlBufPos[eStreamId] == NULL) || (psDevData->ui32AcqDataLen[eStreamId] == 0)) |
| { |
| return PVRSRV_OK; |
| } |
| |
| /* Process each TL packet in the data buffer we have acquired */ |
| pBufferEnd = psDevData->pTlBuf[eStreamId]+psDevData->ui32AcqDataLen[eStreamId]; |
| pDataDest = psDevData->pHwpBuf[eStreamId]; |
| psHDRptr = GET_PACKET_HDR(psDevData->pTlBufPos[eStreamId]); |
| psDevData->pTlBufRead[eStreamId] = psDevData->pTlBufPos[eStreamId]; |
| while ( psHDRptr < (PVRSRVTL_PPACKETHDR)pBufferEnd ) |
| { |
| ui16TlType = GET_PACKET_TYPE(psHDRptr); |
| if (ui16TlType == PVRSRVTL_PACKETTYPE_DATA) |
| { |
| IMG_UINT16 ui16DataLen = GET_PACKET_DATA_LEN(psHDRptr); |
| if (0 == ui16DataLen) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "RGXHWPerfAcquireEvents: ZERO Data in TL data packet: %p", psHDRptr)); |
| } |
| else |
| { |
| /* Check next packet does not fill buffer */ |
| if (pDataDest + ui16DataLen > psDevData->pHwpBufEnd[eStreamId]) |
| { |
| break; |
| } |
| |
| /* For valid data copy it into the client buffer and move |
| * the write position on */ |
| OSDeviceMemCopy(pDataDest, GET_PACKET_DATA_PTR(psHDRptr), ui16DataLen); |
| pDataDest += ui16DataLen; |
| } |
| } |
| else if (ui16TlType == PVRSRVTL_PACKETTYPE_MOST_RECENT_WRITE_FAILED) |
| { |
| PVR_DPF((PVR_DBG_MESSAGE, "RGXHWPerfAcquireEvents: Indication that the transport buffer was full")); |
| } |
| else |
| { |
| /* else Ignore padding packet type and others */ |
| PVR_DPF((PVR_DBG_MESSAGE, "RGXHWPerfAcquireEvents: Ignoring TL packet, type %d", ui16TlType )); |
| } |
| |
| /* Update loop variable to the next packet and increment counts */ |
| psHDRptr = GET_NEXT_PACKET_ADDR(psHDRptr); |
| /* Updated to keep track of the next packet to be read. */ |
| psDevData->pTlBufRead[eStreamId] = (IMG_PBYTE) psHDRptr; |
| ui32TlPackets++; |
| } |
| |
| PVR_DPF((PVR_DBG_VERBOSE, "RGXHWPerfAcquireEvents: TL Packets processed %03d", ui32TlPackets)); |
| |
| psDevData->bRelease[eStreamId] = IMG_FALSE; |
| if (psHDRptr >= (PVRSRVTL_PPACKETHDR) pBufferEnd) |
| { |
| psDevData->bRelease[eStreamId] = IMG_TRUE; |
| } |
| |
| /* Update output arguments with client buffer details and true length */ |
| *ppBuf = psDevData->pHwpBuf[eStreamId]; |
| *pui32BufLen = pDataDest - psDevData->pHwpBuf[eStreamId]; |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfReleaseEvents( |
| IMG_HANDLE hDevData, |
| RGX_HWPERF_STREAM_ID eStreamId) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*)hDevData; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!psDevData || eStreamId >= RGX_HWPERF_STREAM_ID_LAST) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| if (psDevData->bRelease[eStreamId]) |
| { |
| /* Inform the TL that we are done with reading the data. */ |
| eError = TLClientReleaseData(DIRECT_BRIDGE_HANDLE, psDevData->hSD[eStreamId]); |
| psDevData->ui32AcqDataLen[eStreamId] = 0; |
| psDevData->pTlBuf[eStreamId] = NULL; |
| } |
| else |
| { |
| psDevData->pTlBufPos[eStreamId] = psDevData->pTlBufRead[eStreamId]; |
| } |
| return eError; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfGetFilter( |
| IMG_HANDLE hDevData, |
| RGX_HWPERF_STREAM_ID eStreamId, |
| IMG_UINT64 *ui64Filter) |
| { |
| PVRSRV_RGXDEV_INFO* psRgxDevInfo = |
| hDevData ? ((RGX_KM_HWPERF_DEVDATA*) hDevData)->psRgxDevInfo : NULL; |
| |
| /* Valid input argument values supplied by the caller */ |
| if (!psRgxDevInfo) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Invalid pointer to the RGX device", |
| __func__)); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* No need to take hHWPerfLock here since we are only reading data |
| * from always existing integers to return to debugfs which is an |
| * atomic operation. |
| */ |
| switch (eStreamId) { |
| case RGX_HWPERF_STREAM_ID0_FW: |
| *ui64Filter = psRgxDevInfo->ui64HWPerfFilter; |
| break; |
| case RGX_HWPERF_STREAM_ID1_HOST: |
| *ui64Filter = psRgxDevInfo->ui32HWPerfHostFilter; |
| break; |
| default: |
| PVR_DPF((PVR_DBG_ERROR, "%s: Invalid stream ID", |
| __func__)); |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfFreeConnection( |
| IMG_HANDLE hDevData) |
| { |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*) hDevData; |
| |
| /* Check session handle is not zero */ |
| if (!psDevData) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| /* Free the session memory */ |
| psDevData->psRgxDevNode = NULL; |
| psDevData->psRgxDevInfo = NULL; |
| OSFreeMem(psDevData); |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfClose( |
| IMG_HANDLE hDevData) |
| { |
| RGX_KM_HWPERF_DEVDATA* psDevData = (RGX_KM_HWPERF_DEVDATA*) hDevData; |
| IMG_UINT uiStreamId; |
| PVRSRV_ERROR eError; |
| |
| /* Check session handle is not zero */ |
| if (!psDevData) |
| { |
| return PVRSRV_ERROR_INVALID_PARAMS; |
| } |
| |
| for (uiStreamId = 0; uiStreamId < RGX_HWPERF_STREAM_ID_LAST; uiStreamId++) |
| { |
| /* If the TL buffer exists they have not called ReleaseData |
| * before disconnecting so clean it up */ |
| if (psDevData->pTlBuf[uiStreamId]) |
| { |
| /* TLClientReleaseData call and null out the buffer fields |
| * and length */ |
| eError = TLClientReleaseData(DIRECT_BRIDGE_HANDLE, psDevData->hSD[uiStreamId]); |
| psDevData->ui32AcqDataLen[uiStreamId] = 0; |
| psDevData->pTlBuf[uiStreamId] = NULL; |
| PVR_LOG_IF_ERROR(eError, "TLClientReleaseData"); |
| /* Packets may be lost if release was not required */ |
| if (!psDevData->bRelease[uiStreamId]) |
| { |
| PVR_DPF((PVR_DBG_WARNING, "RGXHWPerfClose: Events in buffer waiting to be read, remaining events may be lost.")); |
| } |
| } |
| |
| /* Close the TL stream, ignore the error if it occurs as we |
| * are disconnecting */ |
| if (psDevData->hSD[uiStreamId]) |
| { |
| eError = TLClientCloseStream(DIRECT_BRIDGE_HANDLE, |
| psDevData->hSD[uiStreamId]); |
| PVR_LOG_IF_ERROR(eError, "TLClientCloseStream"); |
| psDevData->hSD[uiStreamId] = NULL; |
| } |
| |
| /* Free the client buffers used in session */ |
| OSFreeMem(psDevData->pHwpBuf[uiStreamId]); |
| } |
| |
| return PVRSRV_OK; |
| } |
| |
| |
| PVRSRV_ERROR RGXHWPerfDisconnect( |
| IMG_HANDLE hDevData) |
| { |
| PVRSRV_ERROR eError = PVRSRV_OK; |
| |
| eError = RGXHWPerfClose(hDevData); |
| PVR_LOG_IF_ERROR(eError, "RGXHWPerfClose"); |
| |
| eError = RGXHWPerfFreeConnection(hDevData); |
| PVR_LOG_IF_ERROR(eError, "RGXHWPerfFreeConnection"); |
| |
| return eError; |
| } |
| |
| |
| const IMG_CHAR *RGXHWPerfKickTypeToStr(RGX_HWPERF_KICK_TYPE eKickType) |
| { |
| static const IMG_CHAR *aszKickType[RGX_HWPERF_KICK_TYPE_LAST+1] = { |
| "TA3D", "TQ2D", "TQ3D", "CDM", "RS", "VRDM", "TQTDM", "SYNC", "LAST" |
| }; |
| |
| /* cast in case of negative value */ |
| if (((IMG_UINT32) eKickType) >= RGX_HWPERF_KICK_TYPE_LAST) |
| { |
| return "<UNKNOWN>"; |
| } |
| |
| return aszKickType[eKickType]; |
| } |
| |
| /****************************************************************************** |
| End of file (rgxhwperf.c) |
| ******************************************************************************/ |