summaryrefslogtreecommitdiffstats
path: root/pvr-source/services4/srvkm/env/linux/ion.c
diff options
context:
space:
mode:
Diffstat (limited to 'pvr-source/services4/srvkm/env/linux/ion.c')
-rw-r--r--pvr-source/services4/srvkm/env/linux/ion.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/pvr-source/services4/srvkm/env/linux/ion.c b/pvr-source/services4/srvkm/env/linux/ion.c
new file mode 100644
index 0000000..3e772bc
--- /dev/null
+++ b/pvr-source/services4/srvkm/env/linux/ion.c
@@ -0,0 +1,363 @@
+/*************************************************************************/ /*!
+@Title Ion driver inter-operability code.
+@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 "ion.h"
+
+#include "services.h"
+#include "servicesint.h"
+#include "mutex.h"
+#include "lock.h"
+#include "mm.h"
+#include "handle.h"
+#include "perproc.h"
+#include "env_perproc.h"
+#include "private_data.h"
+#include "pvr_debug.h"
+
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+
+#if defined (CONFIG_ION_OMAP)
+#define MAX_HANDLES_PER_FD 2
+extern struct ion_client *gpsIONClient;
+
+int PVRSRVExportFDToIONHandles(int fd, struct ion_client **client,
+ struct ion_handle **handles,
+ unsigned int *num_handles)
+{
+ PVRSRV_FILE_PRIVATE_DATA *psPrivateData;
+ PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo;
+ LinuxMemArea *psLinuxMemArea;
+ PVRSRV_ERROR eError;
+ struct file *psFile;
+ int i;
+ unsigned int ui32NumHandles = *num_handles;
+ int ret = -EINVAL;
+
+ /* Take the bridge mutex so the handle won't be freed underneath us */
+ LinuxLockMutex(&gPVRSRVLock);
+
+ psFile = fget(fd);
+ if(!psFile)
+ goto err_unlock;
+
+ psPrivateData = psFile->private_data;
+ if(!psPrivateData)
+ {
+ PVR_DPF((PVR_DBG_ERROR, "%s: struct file* has no private_data; "
+ "invalid export handle", __func__));
+ goto err_fput;
+ }
+
+ eError = PVRSRVLookupHandle(KERNEL_HANDLE_BASE,
+ (IMG_PVOID *)&psKernelMemInfo,
+ psPrivateData->hKernelMemInfo,
+ PVRSRV_HANDLE_TYPE_MEM_INFO);
+ if(eError != PVRSRV_OK)
+ {
+ PVR_DPF((PVR_DBG_ERROR, "%s: Failed to look up MEM_INFO handle",
+ __func__));
+ goto err_fput;
+ }
+
+ psLinuxMemArea = (LinuxMemArea *)psKernelMemInfo->sMemBlk.hOSMemHandle;
+ BUG_ON(psLinuxMemArea == IMG_NULL);
+
+ if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_ION)
+ {
+ PVR_DPF((PVR_DBG_ERROR, "%s: Valid handle, but not an ION buffer",
+ __func__));
+ goto err_fput;
+ }
+
+ /* Client is requesting fewer handles then we have */
+ if(ui32NumHandles < psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes) {
+
+ PVR_DPF((PVR_DBG_ERROR, "%s: Client requested %u handles, but we have %u",
+ __func__,
+ ui32NumHandles,
+ psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes));
+
+ /* Clear client handles */
+ for (i = 0; i < ui32NumHandles; i++)
+ handles[i] = NULL;
+
+ /* Return number of handles to client */
+ *num_handles = psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes;
+ goto err_fput;
+ }
+
+ for (i = 0; (i < psLinuxMemArea->uData.sIONTilerAlloc.ui32NumValidPlanes) && (i < MAX_HANDLES_PER_FD); i++)
+ handles[i] = psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i];
+
+ *num_handles = i;
+
+ if(client)
+ *client = gpsIONClient;
+
+ ret = 0;
+
+err_fput:
+ fput(psFile);
+err_unlock:
+ /* Allow PVRSRV clients to communicate with srvkm again */
+ LinuxUnLockMutex(&gPVRSRVLock);
+
+ return ret;
+}
+
+struct ion_handle *
+PVRSRVExportFDToIONHandle(int fd, struct ion_client **client)
+{
+ unsigned int num_handles = 1;
+ struct ion_handle *psHandle = IMG_NULL;
+ PVRSRVExportFDToIONHandles(fd, client, &psHandle, &num_handles);
+ return psHandle;
+}
+
+EXPORT_SYMBOL(PVRSRVExportFDToIONHandles);
+EXPORT_SYMBOL(PVRSRVExportFDToIONHandle);
+#endif
+
+#if defined (SUPPORT_ION)
+#include "syscommon.h"
+#include "env_data.h"
+#include "../drivers/gpu/ion/ion_priv.h"
+#include "linux/kernel.h"
+
+struct ion_heap **apsIonHeaps;
+struct ion_device *psIonDev;
+
+static struct ion_platform_data generic_config = {
+ .nr = 2,
+ .heaps = {
+ {
+ .type = ION_HEAP_TYPE_SYSTEM_CONTIG,
+ .name = "System contig",
+ .id = ION_HEAP_TYPE_SYSTEM_CONTIG,
+ },
+ {
+ .type = ION_HEAP_TYPE_SYSTEM,
+ .name = "System",
+ .id = ION_HEAP_TYPE_SYSTEM,
+ }
+ }
+};
+
+PVRSRV_ERROR IonInit(IMG_VOID)
+{
+ int uiHeapCount = generic_config.nr;
+ int uiError;
+ int i;
+
+ apsIonHeaps = kzalloc(sizeof(struct ion_heap *) * uiHeapCount, GFP_KERNEL);
+ /* Create the ion devicenode */
+ psIonDev = ion_device_create(NULL);
+ if (IS_ERR_OR_NULL(psIonDev)) {
+ kfree(apsIonHeaps);
+ return PVRSRV_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* Register all the heaps */
+ for (i = 0; i < generic_config.nr; i++)
+ {
+ struct ion_platform_heap *psPlatHeapData = &generic_config.heaps[i];
+
+ apsIonHeaps[i] = ion_heap_create(psPlatHeapData);
+ if (IS_ERR_OR_NULL(apsIonHeaps[i]))
+ {
+ uiError = PTR_ERR(apsIonHeaps[i]);
+ goto failHeapCreate;
+ }
+ ion_device_add_heap(psIonDev, apsIonHeaps[i]);
+ }
+
+ return PVRSRV_OK;
+failHeapCreate:
+ for (i = 0; i < uiHeapCount; i++) {
+ if (apsIonHeaps[i])
+ {
+ ion_heap_destroy(apsIonHeaps[i]);
+ }
+ }
+ kfree(apsIonHeaps);
+ return PVRSRV_ERROR_OUT_OF_MEMORY;
+}
+
+IMG_VOID IonDeinit(IMG_VOID)
+{
+ int uiHeapCount = generic_config.nr;
+ int i;
+
+ for (i = 0; i < uiHeapCount; i++) {
+ if (apsIonHeaps[i])
+ {
+ ion_heap_destroy(apsIonHeaps[i]);
+ }
+ }
+ kfree(apsIonHeaps);
+ ion_device_destroy(psIonDev);
+}
+
+typedef struct _ION_IMPORT_DATA_
+{
+ struct ion_client *psIonClient;
+ struct ion_handle *psIonHandle;
+ IMG_PVOID pvKernAddr;
+} ION_IMPORT_DATA;
+
+PVRSRV_ERROR IonImportBufferAndAquirePhysAddr(IMG_HANDLE hIonDev,
+ IMG_HANDLE hIonFD,
+ IMG_UINT32 *pui32PageCount,
+ IMG_SYS_PHYADDR **ppasSysPhysAddr,
+ IMG_PVOID *ppvKernAddr,
+ IMG_HANDLE *phPriv)
+{
+ struct ion_client *psIonClient = hIonDev;
+ struct ion_handle *psIonHandle;
+ struct scatterlist *psScatterList;
+ struct scatterlist *psTemp;
+ IMG_SYS_PHYADDR *pasSysPhysAddr = NULL;
+ ION_IMPORT_DATA *psImportData;
+ PVRSRV_ERROR eError;
+ IMG_UINT32 ui32PageCount = 0;
+ IMG_UINT32 i;
+ IMG_PVOID pvKernAddr;
+ int fd = (int) hIonFD;
+
+ psImportData = kmalloc(sizeof(ION_IMPORT_DATA), GFP_KERNEL);
+ if (psImportData == NULL)
+ {
+ return PVRSRV_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* Get the buffer handle */
+ psIonHandle = ion_import_fd(psIonClient, fd);
+ if (psIonHandle == IMG_NULL)
+ {
+ eError = PVRSRV_ERROR_BAD_MAPPING;
+ goto exitFailImport;
+ }
+
+ /* Create data for free callback */
+ psImportData->psIonClient = psIonClient;
+ psImportData->psIonHandle = psIonHandle;
+
+ psScatterList = ion_map_dma(psIonClient, psIonHandle);
+ if (psScatterList == NULL)
+ {
+ eError = PVRSRV_ERROR_INVALID_PARAMS;
+ goto exitFailMap;
+ }
+
+ /*
+ We do a two pass process, 1st workout how many pages there
+ are, 2nd fill in the data.
+ */
+ for (i=0;i<2;i++)
+ {
+ psTemp = psScatterList;
+ if (i == 1)
+ {
+ pasSysPhysAddr = kmalloc(sizeof(IMG_SYS_PHYADDR) * ui32PageCount, GFP_KERNEL);
+ if (pasSysPhysAddr == NULL)
+ {
+ eError = PVRSRV_ERROR_OUT_OF_MEMORY;
+ goto exitFailAlloc;
+ }
+ ui32PageCount = 0; /* Reset the page count a we use if for the index */
+ }
+
+ while(psTemp)
+ {
+ IMG_UINT32 j;
+
+ for (j=0;j<psTemp->length;j+=PAGE_SIZE)
+ {
+ if (i == 1)
+ {
+ /* Pass 2: Get the page data */
+ pasSysPhysAddr[ui32PageCount].uiAddr = sg_phys(psTemp);
+ }
+ ui32PageCount++;
+ }
+ psTemp = sg_next(psTemp);
+ }
+ }
+
+ pvKernAddr = ion_map_kernel(psIonClient, psIonHandle);
+ if (IS_ERR(pvKernAddr))
+ {
+ pvKernAddr = IMG_NULL;
+ }
+
+ psImportData->pvKernAddr = pvKernAddr;
+
+ *ppvKernAddr = pvKernAddr;
+ *pui32PageCount = ui32PageCount;
+ *ppasSysPhysAddr = pasSysPhysAddr;
+ *phPriv = psImportData;
+ return PVRSRV_OK;
+
+exitFailAlloc:
+ ion_unmap_dma(psIonClient, psIonHandle);
+exitFailMap:
+ ion_free(psIonClient, psIonHandle);
+exitFailImport:
+ kfree(psImportData);
+ return eError;
+}
+
+
+IMG_VOID IonUnimportBufferAndReleasePhysAddr(IMG_HANDLE hPriv)
+{
+ ION_IMPORT_DATA *psImportData = hPriv;
+
+ ion_unmap_dma(psImportData->psIonClient, psImportData->psIonHandle);
+ if (psImportData->pvKernAddr)
+ {
+ ion_unmap_kernel(psImportData->psIonClient, psImportData->psIonHandle);
+ }
+ ion_free(psImportData->psIonClient, psImportData->psIonHandle);
+ kfree(psImportData);
+}
+#endif