Files
thead-kernel/drivers/gpu/drm/img-rogue/rgxhwperf.c
2023-06-25 23:04:18 +08:00

665 lines
25 KiB
C

/*************************************************************************/ /*!
@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 "img_defs.h"
#include "pvr_debug.h"
#include "rgxdevice.h"
#include "pvrsrv_error.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 "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"
#include "process_stats.h"
#include "rgx_hwperf_table.h"
#include "rgxinit.h"
#include "info_page_defs.h"
/* This is defined by default to enable producer callbacks.
* Clients of the TL interface can disable the use of the callback
* with PVRSRV_STREAM_FLAG_DISABLE_PRODUCER_CALLBACK. */
#define SUPPORT_TL_PRODUCER_CALLBACK 1
/* Maximum enum value to prevent access to RGX_HWPERF_STREAM_ID2_CLIENT stream */
#define RGX_HWPERF_MAX_STREAM_ID (RGX_HWPERF_STREAM_ID2_CLIENT)
/* 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)");
static inline IMG_UINT32
RGXHWPerfGetPackets(IMG_UINT32 ui32BytesExp,
IMG_UINT32 ui32AllowedSize,
RGX_PHWPERF_V2_PACKET_HDR psCurPkt )
{
IMG_UINT32 sizeSum = 0;
/* Traverse the array to find how many packets will fit in the available space. */
while ( sizeSum < ui32BytesExp &&
sizeSum + RGX_HWPERF_GET_SIZE(psCurPkt) < ui32AllowedSize )
{
sizeSum += RGX_HWPERF_GET_SIZE(psCurPkt);
psCurPkt = RGX_HWPERF_GET_NEXT_PACKET(psCurPkt);
}
return sizeSum;
}
static inline void
RGXSuspendHWPerfL2DataCopy(PVRSRV_RGXDEV_INFO* psDeviceInfo,
IMG_BOOL bIsReaderConnected)
{
if (!bIsReaderConnected)
{
PVR_DPF((PVR_DBG_WARNING, "%s : HWPerf FW events enabled but host buffer for FW events is full "
"and no reader is currently connected, suspending event collection. "
"Connect a reader or restart driver to avoid event loss.", __func__));
psDeviceInfo->bSuspendHWPerfL2DataCopy = IMG_TRUE;
}
}
/*************************************************************************/ /*!
@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.
@Input psRgxDevInfo RGX Device Info, on which init requirement is
checked.
@Return IMG_BOOL Whether initialization (allocation) is required
*/ /**************************************************************************/
static INLINE IMG_BOOL RGXHWPerfIsInitRequired(PVRSRV_RGXDEV_INFO *psRgxDevInfo)
{
PVR_ASSERT(OSLockIsLocked(psRgxDevInfo->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 (psRgxDevInfo->hHWPerfStream == (IMG_HANDLE) NULL)
{
/* 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(psRgxDevInfo->psRGXFWIfHWPerfBufMemDesc == NULL);
return IMG_TRUE;
}
PVR_ASSERT(psRgxDevInfo->psRGXFWIfHWPerfBufMemDesc != NULL);
#else
/* On a NO-HW driver L2 is not allocated. So, no point in checking its
* allocation */
if (psRgxDevInfo->psRGXFWIfHWPerfBufMemDesc == NULL)
{
return IMG_TRUE;
}
#endif
return IMG_FALSE;
}
/******************************************************************************
* RGX HW Performance Profiling Server API(s)
*****************************************************************************/
static IMG_BOOL RGXServerFeatureFlagsToHWPerfFlagsAddBlock(
RGX_HWPERF_BVNC_BLOCK * const psBlocks,
IMG_UINT16 * const pui16Count,
const IMG_UINT16 ui16BlockID, /* see RGX_HWPERF_CNTBLK_ID */
const IMG_UINT16 ui16NumCounters,
const IMG_UINT16 ui16NumBlocks)
{
const IMG_UINT16 ui16Count = *pui16Count;
if (ui16Count < RGX_HWPERF_MAX_BVNC_BLOCK_LEN)
{
RGX_HWPERF_BVNC_BLOCK * const psBlock = &psBlocks[ui16Count];
/* If the GROUP is non-zero, convert from e.g. RGX_CNTBLK_ID_USC0 to RGX_CNTBLK_ID_USC_ALL. The table stores the former (plus the
number of blocks and counters) but PVRScopeServices expects the latter (plus the number of blocks and counters). The conversion
could always be moved to PVRScopeServices, but it's less code this way. */
psBlock->ui16BlockID = (ui16BlockID & RGX_CNTBLK_ID_GROUP_MASK) ? (ui16BlockID | RGX_CNTBLK_ID_UNIT_ALL_MASK) : ui16BlockID;
psBlock->ui16NumCounters = ui16NumCounters;
psBlock->ui16NumBlocks = ui16NumBlocks;
*pui16Count = ui16Count + 1;
return IMG_TRUE;
}
return IMG_FALSE;
}
PVRSRV_ERROR RGXServerFeatureFlagsToHWPerfFlags(PVRSRV_RGXDEV_INFO *psDevInfo, RGX_HWPERF_BVNC *psBVNC)
{
IMG_PCHAR pszBVNC;
PVR_LOG_RETURN_IF_FALSE((NULL != psDevInfo), "psDevInfo invalid", PVRSRV_ERROR_INVALID_PARAMS);
if ((pszBVNC = RGXDevBVNCString(psDevInfo)))
{
size_t uiStringLength = OSStringNLength(pszBVNC, RGX_HWPERF_MAX_BVNC_LEN - 1);
OSStringLCopy(psBVNC->aszBvncString, pszBVNC, uiStringLength + 1);
memset(&psBVNC->aszBvncString[uiStringLength], 0, RGX_HWPERF_MAX_BVNC_LEN - uiStringLength);
}
else
{
*psBVNC->aszBvncString = 0;
}
psBVNC->ui32BvncKmFeatureFlags = 0x0;
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, PERFBUS))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_PERFBUS_FLAG;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, S7_TOP_INFRASTRUCTURE))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_S7_TOP_INFRASTRUCTURE_FLAG;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, XT_TOP_INFRASTRUCTURE))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_XT_TOP_INFRASTRUCTURE_FLAG;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, PERF_COUNTER_BATCH))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_PERF_COUNTER_BATCH_FLAG;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, ROGUEXE))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_ROGUEXE_FLAG;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, DUST_POWER_ISLAND_S7))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_DUST_POWER_ISLAND_S7_FLAG;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, PBE2_IN_XE))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_PBE2_IN_XE_FLAG;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, GPU_MULTICORE_SUPPORT))
{
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_MULTICORE_FLAG;
}
#ifdef SUPPORT_WORKLOAD_ESTIMATION
/* Not a part of BVNC feature line and so doesn't need the feature supported check */
psBVNC->ui32BvncKmFeatureFlags |= RGX_HWPERF_FEATURE_WORKLOAD_ESTIMATION;
#endif
/* Define the HW counter block counts. */
{
RGX_HWPERF_BVNC_BLOCK * const psBlocks = psBVNC->aBvncBlocks;
IMG_UINT16 * const pui16Count = &psBVNC->ui16BvncBlocks;
const RGXFW_HWPERF_CNTBLK_TYPE_MODEL *asCntBlkTypeModel;
const IMG_UINT32 ui32CntBlkModelLen = RGXGetHWPerfBlockConfig(&asCntBlkTypeModel);
IMG_UINT32 ui32BlkCfgIdx;
size_t uiCount;
IMG_BOOL bOk = IMG_TRUE;
// Initialise to zero blocks
*pui16Count = 0;
// Add all the blocks
for (ui32BlkCfgIdx = 0; ui32BlkCfgIdx < ui32CntBlkModelLen; ui32BlkCfgIdx++)
{
const RGXFW_HWPERF_CNTBLK_TYPE_MODEL * const psCntBlkInfo = &asCntBlkTypeModel[ui32BlkCfgIdx];
RGX_HWPERF_CNTBLK_RT_INFO sCntBlkRtInfo;
/* psCntBlkInfo->ui8NumUnits gives compile-time info. For BVNC agnosticism, we use this: */
if (psCntBlkInfo->pfnIsBlkPresent(psCntBlkInfo, psDevInfo, &sCntBlkRtInfo))
{
bOk &= RGXServerFeatureFlagsToHWPerfFlagsAddBlock(psBlocks, pui16Count, psCntBlkInfo->ui32CntBlkIdBase, psCntBlkInfo->ui8NumCounters, sCntBlkRtInfo.ui32NumUnits);
}
}
/* If this fails, consider why the static_assert didn't fail, and consider increasing RGX_HWPERF_MAX_BVNC_BLOCK_LEN */
PVR_ASSERT(bOk);
// Zero the remaining entries
uiCount = *pui16Count;
OSDeviceMemSet(&psBlocks[uiCount], 0, (RGX_HWPERF_MAX_BVNC_BLOCK_LEN - uiCount) * sizeof(*psBlocks));
}
return PVRSRV_OK;
}
/*
PVRSRVRGXConfigMuxHWPerfCountersKM
*/
PVRSRV_ERROR PVRSRVRGXConfigMuxHWPerfCountersKM(
CONNECTION_DATA *psConnection,
PVRSRV_DEVICE_NODE *psDeviceNode,
IMG_UINT32 ui32ArrayLen,
RGX_HWPERF_CONFIG_MUX_CNTBLK *psBlockConfigs)
{
PVRSRV_ERROR eError = PVRSRV_OK;
RGXFWIF_KCCB_CMD sKccbCmd;
DEVMEM_MEMDESC* psFwBlkConfigsMemDesc;
RGX_HWPERF_CONFIG_MUX_CNTBLK* psFwArray;
IMG_UINT32 ui32kCCBCommandSlot;
PVRSRV_RGXDEV_INFO *psDevice;
PVR_LOG_RETURN_IF_FALSE(psDeviceNode != NULL, "psDeviceNode is NULL",
PVRSRV_ERROR_INVALID_PARAMS);
psDevice = psDeviceNode->pvDevice;
PVR_UNREFERENCED_PARAMETER(psConnection);
PVRSRV_VZ_RET_IF_MODE(GUEST, PVRSRV_ERROR_NOT_SUPPORTED);
PVR_LOG_RETURN_IF_FALSE(ui32ArrayLen > 0, "ui32ArrayLen is 0",
PVRSRV_ERROR_INVALID_PARAMS);
PVR_LOG_RETURN_IF_FALSE(psBlockConfigs != NULL, "psBlockConfigs is NULL",
PVRSRV_ERROR_INVALID_PARAMS);
PVR_DPF_ENTERED;
/* Fill in the command structure with the parameters needed
*/
sKccbCmd.eCmdType = RGXFWIF_KCCB_CMD_HWPERF_CONFIG_ENABLE_BLKS;
sKccbCmd.uCmdData.sHWPerfCfgEnableBlks.ui32NumBlocks = ui32ArrayLen;
/* used for passing counters config to the Firmware, write-only for the CPU */
eError = DevmemFwAllocate(psDevice,
sizeof(RGX_HWPERF_CONFIG_MUX_CNTBLK)*ui32ArrayLen,
PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) |
PVRSRV_MEMALLOCFLAG_GPU_READABLE |
PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE |
PVRSRV_MEMALLOCFLAG_GPU_UNCACHED |
PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE |
PVRSRV_MEMALLOCFLAG_CPU_UNCACHED_WC |
PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE |
PVRSRV_MEMALLOCFLAG_PHYS_HEAP_HINT(FW_MAIN),
"FwHWPerfCountersConfigBlock",
&psFwBlkConfigsMemDesc);
PVR_LOG_RETURN_IF_ERROR(eError, "DevmemFwAllocate");
eError = RGXSetFirmwareAddress(&sKccbCmd.uCmdData.sHWPerfCfgEnableBlks.sBlockConfigs,
psFwBlkConfigsMemDesc, 0, RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress", fail1);
eError = DevmemAcquireCpuVirtAddr(psFwBlkConfigsMemDesc, (void **)&psFwArray);
PVR_LOG_GOTO_IF_ERROR(eError, "DevmemAcquireCpuVirtAddr", fail2);
OSCachedMemCopyWMB(psFwArray, psBlockConfigs, sizeof(RGX_HWPERF_CONFIG_MUX_CNTBLK)*ui32ArrayLen);
DevmemPDumpLoadMem(psFwBlkConfigsMemDesc,
0,
sizeof(RGX_HWPERF_CONFIG_MUX_CNTBLK)*ui32ArrayLen,
PDUMP_FLAGS_CONTINUOUS);
/*PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXConfigMuxHWPerfCountersKM parameters set, calling FW"));*/
/* Ask the FW to carry out the HWPerf configuration command
*/
eError = RGXScheduleCommandAndGetKCCBSlot(psDevice,
RGXFWIF_DM_GP,
&sKccbCmd,
0,
PDUMP_FLAGS_CONTINUOUS,
&ui32kCCBCommandSlot);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXScheduleCommandAndGetKCCBSlot", fail2);
/*PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXConfigMuxHWPerfCountersKM command scheduled for FW"));*/
/* Wait for FW to complete */
eError = RGXWaitForKCCBSlotUpdate(psDevice, ui32kCCBCommandSlot, PDUMP_FLAGS_CONTINUOUS);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXWaitForKCCBSlotUpdate", fail3);
/* Release temporary memory used for block configuration
*/
RGXUnsetFirmwareAddress(psFwBlkConfigsMemDesc);
DevmemReleaseCpuVirtAddr(psFwBlkConfigsMemDesc);
DevmemFwUnmapAndFree(psDevice, psFwBlkConfigsMemDesc);
/*PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXConfigMuxHWPerfCountersKM firmware completed"));*/
PVR_DPF((PVR_DBG_WARNING, "HWPerf %d counter blocks configured and ENABLED", ui32ArrayLen));
PVR_DPF_RETURN_OK;
fail3:
DevmemReleaseCpuVirtAddr(psFwBlkConfigsMemDesc);
fail2:
RGXUnsetFirmwareAddress(psFwBlkConfigsMemDesc);
fail1:
DevmemFwUnmapAndFree(psDevice, 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;
IMG_UINT32 ui32kCCBCommandSlot;
PVRSRV_RGXDEV_INFO *psDevice = psDeviceNode->pvDevice;
PVR_UNREFERENCED_PARAMETER(psConnection);
PVRSRV_VZ_RET_IF_MODE(GUEST, PVRSRV_ERROR_NOT_SUPPORTED);
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);
/* used for passing counters config to the Firmware, write-only for the CPU */
eError = DevmemFwAllocate(psDevice,
sizeof(IMG_UINT32) * ui16NumCustomCounters,
PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(PMMETA_PROTECT) |
PVRSRV_MEMALLOCFLAG_GPU_READABLE |
PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE |
PVRSRV_MEMALLOCFLAG_GPU_UNCACHED |
PVRSRV_MEMALLOCFLAG_CPU_WRITEABLE |
PVRSRV_MEMALLOCFLAG_CPU_UNCACHED_WC |
PVRSRV_MEMALLOCFLAG_KERNEL_CPU_MAPPABLE |
PVRSRV_MEMALLOCFLAG_PHYS_HEAP_HINT(FW_MAIN),
"FwHWPerfConfigCustomCounters",
&psFwSelectCntrsMemDesc);
PVR_LOG_RETURN_IF_ERROR(eError, "DevmemFwAllocate");
eError = RGXSetFirmwareAddress(&sKccbCmd.uCmdData.sHWPerfSelectCstmCntrs.sCustomCounterIDs,
psFwSelectCntrsMemDesc, 0, RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress", fail1);
eError = DevmemAcquireCpuVirtAddr(psFwSelectCntrsMemDesc, (void **)&psFwArray);
PVR_LOG_GOTO_IF_ERROR(eError, "DevmemAcquireCpuVirtAddr", fail2);
OSCachedMemCopyWMB(psFwArray, pui32CustomCounterIDs, sizeof(IMG_UINT32) * ui16NumCustomCounters);
DevmemPDumpLoadMem(psFwSelectCntrsMemDesc,
0,
sizeof(IMG_UINT32) * ui16NumCustomCounters,
PDUMP_FLAGS_CONTINUOUS);
}
/* Push in the KCCB the command to configure the custom counters block */
eError = RGXScheduleCommandAndGetKCCBSlot(psDevice,
RGXFWIF_DM_GP,
&sKccbCmd,
0,
PDUMP_FLAGS_CONTINUOUS,
&ui32kCCBCommandSlot);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXScheduleCommandAndGetKCCBSlot", fail3);
PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXSelectCustomCountersKM: Command scheduled"));
/* Wait for FW to complete */
eError = RGXWaitForKCCBSlotUpdate(psDevice, ui32kCCBCommandSlot, PDUMP_FLAGS_CONTINUOUS);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXWaitForKCCBSlotUpdate", fail3);
PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVRGXSelectCustomCountersKM: FW operation completed"));
if (ui16NumCustomCounters > 0)
{
/* Release temporary memory used for block configuration */
RGXUnsetFirmwareAddress(psFwSelectCntrsMemDesc);
DevmemReleaseCpuVirtAddr(psFwSelectCntrsMemDesc);
DevmemFwUnmapAndFree(psDevice, psFwSelectCntrsMemDesc);
}
PVR_DPF((PVR_DBG_MESSAGE, "HWPerf custom counters %u reading will be sent with the next HW events", ui16NumCustomCounters));
PVR_DPF_RETURN_OK;
fail3:
if (psFwSelectCntrsMemDesc)
{
DevmemReleaseCpuVirtAddr(psFwSelectCntrsMemDesc);
}
fail2:
if (psFwSelectCntrsMemDesc)
{
RGXUnsetFirmwareAddress(psFwSelectCntrsMemDesc);
}
fail1:
if (psFwSelectCntrsMemDesc)
{
DevmemFwUnmapAndFree(psDevice, psFwSelectCntrsMemDesc);
}
PVR_DPF_RETURN_RC(eError);
}
/*
PVRSRVRGXConfigureHWPerfBlocksKM
*/
PVRSRV_ERROR PVRSRVRGXConfigureHWPerfBlocksKM(
CONNECTION_DATA * psConnection,
PVRSRV_DEVICE_NODE * psDeviceNode,
IMG_UINT32 ui32CtrlWord,
IMG_UINT32 ui32ArrayLen,
RGX_HWPERF_CONFIG_CNTBLK * psBlockConfigs)
{
IMG_INT i;
PVR_UNREFERENCED_PARAMETER(psConnection);
PVR_UNREFERENCED_PARAMETER(psDeviceNode);
PVR_DPF((PVR_DBG_WARNING, "%s: ui32CtrlWord = %u", __func__, ui32CtrlWord));
PVR_DPF((PVR_DBG_WARNING, "%s: ui32ArrayLen = %u", __func__, ui32ArrayLen));
for (i = 0; i < ui32ArrayLen; i++)
{
IMG_INT j;
PVR_DPF((PVR_DBG_WARNING, "%s: psBlockConfigs[%d].ui16BlockID = 0x%x",
__func__, i, psBlockConfigs[i].ui16BlockID));
PVR_DPF((PVR_DBG_WARNING, "%s: psBlockConfigs[%d].ui16NumCounters = %u",
__func__, i, psBlockConfigs[i].ui16NumCounters));
for (j = 0; j < psBlockConfigs[i].ui16NumCounters; j++)
{
PVR_DPF((PVR_DBG_WARNING, "%s: psBlockConfigs[%d].ui16Counters[%d] = 0x%x",
__func__, i, j, psBlockConfigs[i].ui16Counters[j]));
}
}
return PVRSRV_ERROR_NOT_IMPLEMENTED;
}
/******************************************************************************
* 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_MAX_STREAM_ID];
/* TL Acquire/release state */
IMG_PBYTE pHwpBuf[RGX_HWPERF_MAX_STREAM_ID]; /*!< buffer returned to user in acquire call */
IMG_PBYTE pHwpBufEnd[RGX_HWPERF_MAX_STREAM_ID]; /*!< pointer to end of HwpBuf */
IMG_PBYTE pTlBuf[RGX_HWPERF_MAX_STREAM_ID]; /*!< buffer obtained via TlAcquireData */
IMG_PBYTE pTlBufPos[RGX_HWPERF_MAX_STREAM_ID]; /*!< initial position in TlBuf to acquire packets */
IMG_PBYTE pTlBufRead[RGX_HWPERF_MAX_STREAM_ID]; /*!< pointer to the last packet read */
IMG_UINT32 ui32AcqDataLen[RGX_HWPERF_MAX_STREAM_ID]; /*!< length of acquired TlBuf */
IMG_BOOL bRelease[RGX_HWPERF_MAX_STREAM_ID]; /*!< used to determine whether or not to release currently held TlBuf */
} RGX_KM_HWPERF_DEVDATA;
PVRSRV_ERROR RGXHWPerfConfigMuxCounters(
RGX_HWPERF_CONNECTION *psHWPerfConnection,
IMG_UINT32 ui32NumBlocks,
RGX_HWPERF_CONFIG_MUX_CNTBLK *asBlockConfigs)
{
PVRSRV_ERROR eError = PVRSRV_OK;
RGX_KM_HWPERF_DEVDATA* psDevData;
RGX_HWPERF_DEVICE *psHWPerfDev;
PVRSRV_VZ_RET_IF_MODE(GUEST, PVRSRV_ERROR_NOT_IMPLEMENTED);
/* Validate input argument values supplied by the caller */
if (!psHWPerfConnection || ui32NumBlocks==0 || !asBlockConfigs)
{
return PVRSRV_ERROR_INVALID_PARAMS;
}
if (ui32NumBlocks > RGXFWIF_HWPERF_CTRL_BLKS_MAX)
{
return PVRSRV_ERROR_INVALID_PARAMS;
}
psHWPerfDev = psHWPerfConnection->psHWPerfDevList;
while (psHWPerfDev)
{
psDevData = (RGX_KM_HWPERF_DEVDATA *) psHWPerfDev->hDevData;
/* Call the internal server API */
eError = PVRSRVRGXConfigMuxHWPerfCountersKM(NULL,
psDevData->psRgxDevNode,
ui32NumBlocks,
asBlockConfigs);
PVR_LOG_RETURN_IF_ERROR(eError, "PVRSRVRGXCtrlHWPerfKM");
psHWPerfDev = psHWPerfDev->psNext;
}
return eError;
}
PVRSRV_ERROR RGXHWPerfConfigureAndEnableCustomCounters(
RGX_HWPERF_CONNECTION *psHWPerfConnection,
IMG_UINT16 ui16CustomBlockID,
IMG_UINT16 ui16NumCustomCounters,
IMG_UINT32 *pui32CustomCounterIDs)
{
PVRSRV_ERROR eError;
RGX_HWPERF_DEVICE *psHWPerfDev;
PVRSRV_VZ_RET_IF_MODE(GUEST, PVRSRV_ERROR_NOT_IMPLEMENTED);
/* Validate input arguments supplied by the caller */
PVR_LOG_RETURN_IF_FALSE((NULL != psHWPerfConnection), "psHWPerfConnection invalid",
PVRSRV_ERROR_INVALID_PARAMS);
PVR_LOG_RETURN_IF_FALSE((0 != ui16NumCustomCounters), "uiNumBlocks invalid",
PVRSRV_ERROR_INVALID_PARAMS);
PVR_LOG_RETURN_IF_FALSE((NULL != pui32CustomCounterIDs),"asBlockConfigs invalid",
PVRSRV_ERROR_INVALID_PARAMS);
/* Check # of blocks */
PVR_LOG_RETURN_IF_FALSE((!(ui16CustomBlockID > RGX_HWPERF_MAX_CUSTOM_BLKS)),"ui16CustomBlockID invalid",
PVRSRV_ERROR_INVALID_PARAMS);
/* Check # of counters */
PVR_LOG_RETURN_IF_FALSE((!(ui16NumCustomCounters > RGX_HWPERF_MAX_CUSTOM_CNTRS)),"ui16NumCustomCounters invalid",
PVRSRV_ERROR_INVALID_PARAMS);
psHWPerfDev = psHWPerfConnection->psHWPerfDevList;
while (psHWPerfDev)
{
RGX_KM_HWPERF_DEVDATA *psDevData = (RGX_KM_HWPERF_DEVDATA *) psHWPerfDev->hDevData;
eError = PVRSRVRGXConfigCustomCountersKM(NULL,
psDevData->psRgxDevNode,
ui16CustomBlockID, ui16NumCustomCounters, pui32CustomCounterIDs);
PVR_LOG_RETURN_IF_ERROR(eError, "PVRSRVRGXCtrlCustHWPerfKM");
psHWPerfDev = psHWPerfDev->psNext;
}
return PVRSRV_OK;
}
/******************************************************************************
End of file (rgxhwperf.c)
******************************************************************************/