| /*************************************************************************/ /*! |
| @File |
| @Title Common linux module setup |
| @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved |
| @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. |
| */ /**************************************************************************/ |
| |
| #include <linux/module.h> |
| |
| #include "pvr_debugfs.h" |
| #include "private_data.h" |
| #include "linkage.h" |
| #include "lists.h" |
| #include "power.h" |
| #include "env_connection.h" |
| #include "process_stats.h" |
| #include "module_common.h" |
| #include "pvrsrv.h" |
| #include "pvr_hwperf.h" |
| #include "pvr_drv.h" |
| #include <linux/moduleparam.h> |
| |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| #include "pvr_sync.h" |
| #endif |
| |
| #if defined(SUPPORT_BUFFER_SYNC) |
| #include "pvr_buffer_sync.h" |
| #endif |
| |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| #include "pvr_gputrace.h" |
| #endif |
| |
| #if defined(SUPPORT_KERNEL_SRVINIT) |
| #include "km_apphint.h" |
| #include "srvinit.h" |
| #endif |
| |
| #if defined(SUPPORT_PVRSRV_GPUVIRT) |
| #if !defined(PVRSRV_GPUVIRT_GUESTDRV) |
| #include "vmm_pvz_server.h" |
| #if defined(PVRSRV_GPUVIRT_MULTIDRV_MODEL) |
| #include "vmm_pvz_mdm.h" |
| #endif |
| #endif |
| #endif |
| |
| #if defined(PVRSRV_NEED_PVR_DPF) |
| extern IMG_UINT32 gPVRDebugLevel; |
| module_param(gPVRDebugLevel, uint, 0644); |
| MODULE_PARM_DESC(gPVRDebugLevel, |
| "Sets the level of debug output (default 0x7)"); |
| #endif /* defined(PVRSRV_NEED_PVR_DPF) */ |
| |
| #if defined(DEBUG) |
| extern IMG_UINT32 gPMRAllocFail; |
| module_param(gPMRAllocFail, uint, 0644); |
| MODULE_PARM_DESC(gPMRAllocFail, "When number of PMR allocs reaches" |
| " this value, it will fail (default value is 0 which" |
| "means that alloc function will behave normally)."); |
| #endif /* defined(DEBUG) */ |
| |
| #if !defined(SUPPORT_KERNEL_SRVINIT) |
| extern unsigned int gui32RGXLoadTimeDevCount; |
| |
| extern char *gazRGXBVNCList[PVRSRV_MAX_DEVICES]; |
| module_param_array_named(RGXBVNC, gazRGXBVNCList, charp, &gui32RGXLoadTimeDevCount, S_IRUGO); |
| MODULE_PARM_DESC(RGXBVNC, "Array of comma separated strings that define BVNC info of the devices. " |
| "module parameter usage is RGXBVNC=x.x.x.x,y.y.y.y etc"); |
| #endif |
| |
| #if defined(SUPPORT_DISPLAY_CLASS) |
| /* Display class interface */ |
| #include "kerneldisplay.h" |
| EXPORT_SYMBOL(DCRegisterDevice); |
| EXPORT_SYMBOL(DCUnregisterDevice); |
| EXPORT_SYMBOL(DCDisplayConfigurationRetired); |
| EXPORT_SYMBOL(DCDisplayHasPendingCommand); |
| EXPORT_SYMBOL(DCImportBufferAcquire); |
| EXPORT_SYMBOL(DCImportBufferRelease); |
| |
| /* Physmem interface (required by LMA DC drivers) */ |
| #include "physheap.h" |
| EXPORT_SYMBOL(PhysHeapAcquire); |
| EXPORT_SYMBOL(PhysHeapRelease); |
| EXPORT_SYMBOL(PhysHeapGetType); |
| EXPORT_SYMBOL(PhysHeapRegionGetCpuPAddr); |
| EXPORT_SYMBOL(PhysHeapRegionGetSize); |
| EXPORT_SYMBOL(PhysHeapCpuPAddrToDevPAddr); |
| |
| EXPORT_SYMBOL(PVRSRVSystemInstallDeviceLISR); |
| EXPORT_SYMBOL(PVRSRVSystemUninstallDeviceLISR); |
| #endif |
| |
| /* Host para-virtz call handlers (required by guest drivers) */ |
| #if defined(PVRSRV_GPUVIRT_MULTIDRV_MODEL) |
| #if !defined(PVRSRV_GPUVIRT_GUESTDRV) |
| EXPORT_SYMBOL(PvzServerCreateDevConfig); |
| EXPORT_SYMBOL(PvzServerDestroyDevConfig); |
| EXPORT_SYMBOL(PvzServerCreateDevConfig2); |
| EXPORT_SYMBOL(PvzServerDestroyDevConfig2); |
| EXPORT_SYMBOL(PvzServerCreateDevPhysHeaps); |
| EXPORT_SYMBOL(PvzServerDestroyDevPhysHeaps); |
| EXPORT_SYMBOL(PvzServerMapDevPhysHeap); |
| EXPORT_SYMBOL(PvzServerUnmapDevPhysHeap); |
| EXPORT_SYMBOL(PvzServerCreateDevPhysHeaps2); |
| EXPORT_SYMBOL(PvzServerDestroyDevPhysHeaps2); |
| #endif |
| #endif |
| |
| #if !(defined(PVRSRV_GPUVIRT_GUESTDRV) && defined(PVRSRV_GPUVIRT_MULTIDRV_MODEL)) |
| #include "pvr_notifier.h" |
| |
| /* |
| * Export some symbols that may be needed by other drivers |
| * |
| * When support for GPU virtualization is present and the multi-driver |
| * model (multiple drivers in same OS kernel) is being used, then only |
| * the host driver is a true device drivers (i.e. is registered with |
| * the kernel to manage the physical device), the other guest drivers |
| * are all modules. |
| */ |
| EXPORT_SYMBOL(PVRSRVCheckStatus); |
| EXPORT_SYMBOL(PVRSRVGetDriverStatus); |
| EXPORT_SYMBOL(PVRSRVGetErrorStringKM); |
| |
| #include "rgxapi_km.h" |
| #if defined(SUPPORT_SHARED_SLC) && !defined(PVRSRV_GPUVIRT_GUESTDRV) |
| /* Guest drivers do not perform device management so RGXInitSLC is absent */ |
| EXPORT_SYMBOL(RGXInitSLC); |
| #endif |
| |
| EXPORT_SYMBOL(RGXHWPerfConnect); |
| EXPORT_SYMBOL(RGXHWPerfDisconnect); |
| EXPORT_SYMBOL(RGXHWPerfControl); |
| EXPORT_SYMBOL(RGXHWPerfConfigureAndEnableCounters); |
| EXPORT_SYMBOL(RGXHWPerfDisableCounters); |
| EXPORT_SYMBOL(RGXHWPerfAcquireEvents); |
| EXPORT_SYMBOL(RGXHWPerfReleaseEvents); |
| #endif |
| |
| CONNECTION_DATA *LinuxConnectionFromFile(struct file *pFile) |
| { |
| if (pFile) |
| { |
| struct drm_file *psDRMFile = pFile->private_data; |
| |
| return psDRMFile->driver_priv; |
| } |
| |
| return NULL; |
| } |
| |
| struct file *LinuxFileFromConnection(CONNECTION_DATA *psConnection) |
| { |
| ENV_CONNECTION_DATA *psEnvConnection; |
| |
| psEnvConnection = PVRSRVConnectionPrivateData(psConnection); |
| PVR_ASSERT(psEnvConnection != NULL); |
| |
| return psEnvConnection->psFile; |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDriverInit |
| @Description Common one time driver initialisation |
| @Return int 0 on success and a Linux error code otherwise |
| */ /***************************************************************************/ |
| int PVRSRVCommonDriverInit(void) |
| { |
| PVRSRV_ERROR pvrerr; |
| int error = 0; |
| |
| #if defined(PDUMP) |
| error = dbgdrv_init(); |
| if (error != 0) |
| { |
| return error; |
| } |
| #endif |
| |
| error = PVRDebugFSInit(); |
| if (error != 0) |
| { |
| return error; |
| } |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| if (PVRSRVStatsInitialise() != PVRSRV_OK) |
| { |
| return -ENOMEM; |
| } |
| #endif |
| |
| if (PVROSFuncInit() != PVRSRV_OK) |
| { |
| return -ENOMEM; |
| } |
| |
| LinuxBridgeInit(); |
| |
| #if defined(SUPPORT_KERNEL_SRVINIT) |
| error = pvr_apphint_init(); |
| if (error != 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "%s: failed AppHint setup(%d)", |
| __func__, error)); |
| } |
| #endif |
| |
| pvrerr = PVRSRVDriverInit(); |
| if (pvrerr != PVRSRV_OK) |
| { |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDriverDeinit |
| @Description Common one time driver de-initialisation |
| @Return void |
| */ /***************************************************************************/ |
| void PVRSRVCommonDriverDeinit(void) |
| { |
| PVRSRVDriverDeInit(); |
| |
| #if defined(SUPPORT_KERNEL_SRVINIT) |
| pvr_apphint_deinit(); |
| #endif |
| |
| LinuxBridgeDeInit(); |
| |
| PVROSFuncDeInit(); |
| |
| #if defined(PVRSRV_ENABLE_PROCESS_STATS) |
| PVRSRVStatsDestroy(); |
| #endif |
| PVRDebugFSDeInit(); |
| |
| #if defined(PDUMP) |
| dbgdrv_cleanup(); |
| #endif |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDeviceInit |
| @Description Common device related initialisation. |
| @Input psDeviceNode The device node for which initialisation should be |
| performed |
| @Return int 0 on success and a Linux error code otherwise |
| */ /***************************************************************************/ |
| int PVRSRVCommonDeviceInit(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| int error = 0; |
| PVRSRV_RGXDEV_INFO *psDevInfo = (PVRSRV_RGXDEV_INFO *) psDeviceNode->pvDevice; |
| |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| { |
| PVRSRV_ERROR eError = pvr_sync_init(psDeviceNode); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: unable to create sync (%d)", |
| __func__, eError)); |
| return -EBUSY; |
| } |
| } |
| #endif |
| |
| #if defined(SUPPORT_BUFFER_SYNC) |
| psDeviceNode->psBufferSyncContext = |
| pvr_buffer_sync_context_create(psDeviceNode); |
| if (IS_ERR(psDeviceNode->psBufferSyncContext)) |
| { |
| error = PTR_ERR(psDeviceNode->psBufferSyncContext); |
| psDeviceNode->psBufferSyncContext = NULL; |
| |
| PVR_DPF((PVR_DBG_ERROR, |
| "%s: unable to initialise buffer_sync support (%d)", |
| __func__, error)); |
| return error; |
| } |
| #endif |
| |
| error = PVRDebugCreateDebugFSEntries(); |
| if (error != 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "%s: failed to create default debugfs entries (%d)", |
| __func__, error)); |
| } |
| |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| error = PVRGpuTraceInit(psDeviceNode); |
| if (error != 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "%s: failed to initialise PVR GPU Tracing (%d)", |
| __func__, error)); |
| } |
| #endif |
| |
| #if defined(SUPPORT_KERNEL_SRVINIT) |
| /* register the AppHint device control before device initialisation |
| * so individual AppHints can be configured during the init phase |
| */ |
| error = pvr_apphint_device_register(psDeviceNode); |
| if (error != 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "%s: failed to initialise device AppHints (%d)", |
| __func__, error)); |
| } |
| #else |
| error = PVRSRVHWperfCreateDebugFs(); |
| if (error != 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "%s: failed to initialise HWPerf debugfs (%d)", |
| __func__, error)); |
| } |
| #endif |
| |
| /*Initialize the device dependent bridges */ |
| |
| error = DeviceDepBridgeInit(psDevInfo->sDevFeatureCfg.ui64Features); |
| if (error != 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "%s: Device dependent bridge initialization failed (%d)", |
| __func__, error)); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDeviceDeinit |
| @Description Common device related de-initialisation. |
| @Input psDeviceNode The device node for which de-initialisation should |
| be performed |
| @Return void |
| */ /***************************************************************************/ |
| void PVRSRVCommonDeviceDeinit(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| int error = 0; |
| PVRSRV_RGXDEV_INFO *psDevInfo = (PVRSRV_RGXDEV_INFO *) psDeviceNode->pvDevice; |
| |
| #if defined(SUPPORT_KERNEL_SRVINIT) |
| pvr_apphint_device_unregister(psDeviceNode); |
| #else |
| PVRSRVHWperfDestroyDebugFs(); |
| #endif |
| |
| #if defined(SUPPORT_GPUTRACE_EVENTS) |
| PVRGpuTraceDeInit(psDeviceNode); |
| #endif |
| |
| PVRDebugRemoveDebugFSEntries(); |
| |
| #if defined(SUPPORT_BUFFER_SYNC) |
| pvr_buffer_sync_context_destroy(psDeviceNode->psBufferSyncContext); |
| #endif |
| |
| #if defined(SUPPORT_NATIVE_FENCE_SYNC) |
| pvr_sync_deinit(); |
| #endif |
| |
| error = DeviceDepBridgeDeInit(psDevInfo->sDevFeatureCfg.ui64Features); |
| if (error != 0) |
| { |
| PVR_DPF((PVR_DBG_WARNING, |
| "%s: Device dependent bridge deinitialization failed (%d)", |
| __func__, error)); |
| } |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDeviceShutdown |
| @Description Common device shutdown. |
| @Input psDeviceNode The device node representing the device that should |
| be shutdown |
| @Return void |
| */ /***************************************************************************/ |
| |
| void PVRSRVCommonDeviceShutdown(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| /* |
| * Take the bridge mutex, and never release it, to stop processes trying to |
| * use the driver after it has been shutdown. |
| */ |
| OSAcquireBridgeLock(); |
| |
| (void) PVRSRVSetDeviceSystemPowerState(psDeviceNode, |
| PVRSRV_SYS_POWER_STATE_OFF); |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDeviceSuspend |
| @Description Common device suspend. |
| @Input psDeviceNode The device node representing the device that should |
| be suspended |
| @Return int 0 on success and a Linux error code otherwise |
| */ /***************************************************************************/ |
| int PVRSRVCommonDeviceSuspend(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| /* |
| * OSSetDriverSuspended prevents processes from using the driver while it's |
| * suspended (this is needed for Android). Acquire the bridge lock first to |
| * ensure the driver isn't currently in use. |
| */ |
| OSAcquireBridgeLock(); |
| OSSetDriverSuspended(); |
| OSReleaseBridgeLock(); |
| |
| if (PVRSRVSetDeviceSystemPowerState(psDeviceNode, |
| PVRSRV_SYS_POWER_STATE_OFF) != PVRSRV_OK) |
| { |
| OSClearDriverSuspended(); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDeviceResume |
| @Description Common device resume. |
| @Input psDeviceNode The device node representing the device that should |
| be resumed |
| @Return int 0 on success and a Linux error code otherwise |
| */ /***************************************************************************/ |
| int PVRSRVCommonDeviceResume(PVRSRV_DEVICE_NODE *psDeviceNode) |
| { |
| if (PVRSRVSetDeviceSystemPowerState(psDeviceNode, |
| PVRSRV_SYS_POWER_STATE_ON) != PVRSRV_OK) |
| { |
| return -EINVAL; |
| } |
| |
| OSClearDriverSuspended(); |
| |
| /* |
| * Reprocess the device queues in case commands were blocked during |
| * suspend. |
| */ |
| if (psDeviceNode->eDevState == PVRSRV_DEVICE_STATE_ACTIVE) |
| { |
| PVRSRVCheckStatus(NULL); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDeviceOpen |
| @Description Common device open. |
| @Input psDeviceNode The device node representing the device being |
| opened by a user mode process |
| @Input psDRMFile The DRM file data that backs the file handle |
| returned to the user mode process |
| @Return int 0 on success and a Linux error code otherwise |
| */ /***************************************************************************/ |
| int PVRSRVCommonDeviceOpen(PVRSRV_DEVICE_NODE *psDeviceNode, |
| struct drm_file *psDRMFile) |
| { |
| PVRSRV_DATA *psPVRSRVData = PVRSRVGetPVRSRVData(); |
| ENV_CONNECTION_PRIVATE_DATA sPrivData; |
| void *pvConnectionData; |
| PVRSRV_ERROR eError; |
| int iErr = 0; |
| |
| OSAcquireBridgeLock(); |
| |
| if (!psPVRSRVData) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: No device data", __func__)); |
| iErr = -ENODEV; |
| goto e1; |
| } |
| |
| #if defined(SUPPORT_KERNEL_SRVINIT) |
| if (psDeviceNode->eDevState == PVRSRV_DEVICE_STATE_INIT) |
| { |
| eError = PVRSRVDeviceInitialise(psDeviceNode); |
| if (eError != PVRSRV_OK) |
| { |
| PVR_DPF((PVR_DBG_ERROR, "%s: Failed to initialise device (%s)", |
| __func__, PVRSRVGetErrorStringKM(eError))); |
| iErr = -ENODEV; |
| goto e1; |
| } |
| } |
| #endif |
| |
| sPrivData.psDevNode = psDeviceNode; |
| sPrivData.psFile = psDRMFile->filp; |
| |
| /* |
| * Here we pass the file pointer which will passed through to our |
| * OSConnectionPrivateDataInit function where we can save it so |
| * we can back reference the file structure from it's connection |
| */ |
| eError = PVRSRVConnectionConnect(&pvConnectionData, (void *) &sPrivData); |
| if (eError != PVRSRV_OK) |
| { |
| iErr = -ENOMEM; |
| goto e1; |
| } |
| |
| psDRMFile->driver_priv = pvConnectionData; |
| OSReleaseBridgeLock(); |
| |
| out: |
| return iErr; |
| e1: |
| OSReleaseBridgeLock(); |
| goto out; |
| } |
| |
| /**************************************************************************/ /*! |
| @Function PVRSRVCommonDeviceRelease |
| @Description Common device release. |
| @Input psDeviceNode The device node for the device that the given file |
| represents |
| @Input psDRMFile The DRM file data that's being released |
| @Return void |
| */ /***************************************************************************/ |
| void PVRSRVCommonDeviceRelease(PVRSRV_DEVICE_NODE *psDeviceNode, |
| struct drm_file *psDRMFile) |
| { |
| void *pvConnectionData; |
| |
| PVR_UNREFERENCED_PARAMETER(psDeviceNode); |
| |
| OSAcquireBridgeLock(); |
| |
| pvConnectionData = psDRMFile->driver_priv; |
| if (pvConnectionData) |
| { |
| PVRSRVConnectionDisconnect(pvConnectionData); |
| psDRMFile->driver_priv = NULL; |
| } |
| |
| OSReleaseBridgeLock(); |
| } |