blob: 4c736f2304b748e6d417639a7787079938114cba [file] [log] [blame]
/*************************************************************************/ /*!
@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)
******************************************************************************/