Maemo patchset 20101501+0m5
[h-e-n] / drivers / gpu / pvr / resman.c
diff --git a/drivers/gpu/pvr/resman.c b/drivers/gpu/pvr/resman.c
new file mode 100644 (file)
index 0000000..4159ea6
--- /dev/null
@@ -0,0 +1,533 @@
+/**********************************************************************
+ *
+ * Copyright(c) 2008 Imagination Technologies Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful but, except
+ * as otherwise stated in writing, without any warranty; without even the
+ * implied warranty of merchantability or fitness for a particular purpose.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Imagination Technologies Ltd. <gpl-support@imgtec.com>
+ * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK
+ *
+ ******************************************************************************/
+
+#include "services_headers.h"
+#include "resman.h"
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/hardirq.h>
+
+#include <linux/semaphore.h>
+
+static DECLARE_MUTEX(lock);
+
+#define ACQUIRE_SYNC_OBJ  do {                                            \
+               if (in_interrupt()) {                                      \
+                       printk(KERN_ERR "ISR cannot take RESMAN mutex\n"); \
+                       BUG();                                             \
+               } else {                                                   \
+                       down(&lock);                                       \
+               }                                                          \
+} while (0)
+#define RELEASE_SYNC_OBJ up(&lock)
+
+
+#define RESMAN_SIGNATURE 0x12345678
+
+struct RESMAN_ITEM {
+#ifdef DEBUG
+       u32 ui32Signature;
+#endif
+       struct RESMAN_ITEM **ppsThis;
+       struct RESMAN_ITEM *psNext;
+
+       u32 ui32Flags;
+       u32 ui32ResType;
+
+       void *pvParam;
+       u32 ui32Param;
+
+       enum PVRSRV_ERROR (*pfnFreeResource)(void *pvParam, u32 ui32Param);
+};
+
+struct RESMAN_CONTEXT {
+#ifdef DEBUG
+       u32 ui32Signature;
+#endif
+       struct RESMAN_CONTEXT **ppsThis;
+       struct RESMAN_CONTEXT *psNext;
+       struct PVRSRV_PER_PROCESS_DATA *psPerProc;
+       struct RESMAN_ITEM *psResItemList;
+
+};
+
+struct RESMAN_LIST {
+       struct RESMAN_CONTEXT *psContextList;
+};
+
+static struct RESMAN_LIST *gpsResList;
+
+#define PRINT_RESLIST(x, y, z)
+
+static void FreeResourceByPtr(struct RESMAN_ITEM *psItem,
+                                          IMG_BOOL bExecuteCallback);
+
+static int FreeResourceByCriteria(struct RESMAN_CONTEXT *psContext,
+               u32 ui32SearchCriteria,
+               u32 ui32ResType, void *pvParam,
+               u32 ui32Param,
+               IMG_BOOL bExecuteCallback);
+
+#ifdef DEBUG
+static void ValidateResList(struct RESMAN_LIST *psResList);
+#define VALIDATERESLIST() ValidateResList(gpsResList)
+#else
+#define VALIDATERESLIST()
+#endif
+
+enum PVRSRV_ERROR ResManInit(void)
+{
+       if (gpsResList == NULL) {
+
+               if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
+                              sizeof(*gpsResList),
+                              (void **) &gpsResList,
+                              NULL) != PVRSRV_OK)
+                       return PVRSRV_ERROR_OUT_OF_MEMORY;
+
+               gpsResList->psContextList = NULL;
+
+               VALIDATERESLIST();
+       }
+
+       return PVRSRV_OK;
+}
+
+void ResManDeInit(void)
+{
+       if (gpsResList != NULL)
+
+               OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*gpsResList),
+                         gpsResList, NULL);
+}
+
+enum PVRSRV_ERROR PVRSRVResManConnect(void *hPerProc,
+                                struct RESMAN_CONTEXT **phResManContext)
+{
+       enum PVRSRV_ERROR eError;
+       struct RESMAN_CONTEXT *psResManContext;
+
+       ACQUIRE_SYNC_OBJ;
+
+       VALIDATERESLIST();
+
+       eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(*psResManContext),
+                           (void **) &psResManContext, NULL);
+       if (eError != PVRSRV_OK) {
+               PVR_DPF(PVR_DBG_ERROR, "PVRSRVResManConnect: "
+                        "ERROR allocating new RESMAN context struct");
+               VALIDATERESLIST();
+               RELEASE_SYNC_OBJ;
+
+               return eError;
+       }
+#ifdef DEBUG
+       psResManContext->ui32Signature = RESMAN_SIGNATURE;
+#endif
+       psResManContext->psResItemList = NULL;
+       psResManContext->psPerProc = hPerProc;
+
+       psResManContext->psNext = gpsResList->psContextList;
+       psResManContext->ppsThis = &gpsResList->psContextList;
+       gpsResList->psContextList = psResManContext;
+       if (psResManContext->psNext)
+               psResManContext->psNext->ppsThis = &(psResManContext->psNext);
+
+       VALIDATERESLIST();
+
+       RELEASE_SYNC_OBJ;
+
+       *phResManContext = psResManContext;
+
+       return PVRSRV_OK;
+}
+
+static inline bool warn_unfreed_res(void)
+{
+       return !(current->flags & PF_SIGNALED);
+}
+
+static int free_one_res(struct RESMAN_CONTEXT *ctx, u32 restype)
+{
+       int freed;
+
+       freed = FreeResourceByCriteria(ctx, RESMAN_CRITERIA_RESTYPE, restype,
+                       NULL, 0, IMG_TRUE);
+       if (freed && warn_unfreed_res())
+               PVR_DPF(PVR_DBG_WARNING, "pvr: %s: cleaning up %d "
+                               "unfreed resource of type %d\n",
+                               current->comm, freed, restype);
+
+       return freed;
+}
+
+
+void PVRSRVResManDisconnect(struct RESMAN_CONTEXT *ctx, IMG_BOOL bKernelContext)
+{
+
+       ACQUIRE_SYNC_OBJ;
+
+       VALIDATERESLIST();
+
+       PRINT_RESLIST(gpsResList, ctx, IMG_TRUE);
+
+       if (!bKernelContext) {
+               int i = 0;
+               i += free_one_res(ctx, RESMAN_TYPE_OS_USERMODE_MAPPING);
+               i += free_one_res(ctx, RESMAN_TYPE_EVENT_OBJECT);
+               i += free_one_res(ctx, RESMAN_TYPE_HW_RENDER_CONTEXT);
+               i += free_one_res(ctx, RESMAN_TYPE_HW_TRANSFER_CONTEXT);
+               i += free_one_res(ctx, RESMAN_TYPE_HW_2D_CONTEXT);
+               i += free_one_res(ctx, RESMAN_TYPE_TRANSFER_CONTEXT);
+               i += free_one_res(ctx, RESMAN_TYPE_SHARED_PB_DESC_CREATE_LOCK);
+               i += free_one_res(ctx, RESMAN_TYPE_SHARED_PB_DESC);
+               i += free_one_res(ctx, RESMAN_TYPE_DISPLAYCLASS_SWAPCHAIN);
+               i += free_one_res(ctx, RESMAN_TYPE_DISPLAYCLASS_DEVICE);
+               i += free_one_res(ctx, RESMAN_TYPE_BUFFERCLASS_DEVICE);
+               i += free_one_res(ctx, RESMAN_TYPE_DEVICECLASSMEM_MAPPING);
+               i += free_one_res(ctx, RESMAN_TYPE_DEVICEMEM_WRAP);
+               i += free_one_res(ctx, RESMAN_TYPE_DEVICEMEM_MAPPING);
+               i += free_one_res(ctx, RESMAN_TYPE_KERNEL_DEVICEMEM_ALLOCATION);
+               i += free_one_res(ctx, RESMAN_TYPE_DEVICEMEM_ALLOCATION);
+               i += free_one_res(ctx, RESMAN_TYPE_DEVICEMEM_CONTEXT);
+
+               if (i && warn_unfreed_res())
+                       printk(KERN_DEBUG "pvr: %s: cleaning up %d "
+                                       "unfreed resources\n",
+                                       current->comm, i);
+
+       }
+
+       PVR_ASSERT(ctx->psResItemList == NULL);
+
+       *(ctx->ppsThis) = ctx->psNext;
+       if (ctx->psNext)
+               ctx->psNext->ppsThis = ctx->ppsThis;
+
+       OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct RESMAN_CONTEXT),
+                 ctx, NULL);
+
+       VALIDATERESLIST();
+
+       PRINT_RESLIST(gpsResList, ctx, IMG_FALSE);
+
+       RELEASE_SYNC_OBJ;
+}
+
+struct RESMAN_ITEM *ResManRegisterRes(struct RESMAN_CONTEXT *psResManContext,
+                          u32 ui32ResType, void *pvParam, u32 ui32Param,
+                          enum PVRSRV_ERROR (*pfnFreeResource)(void *pvParam,
+                                                               u32 ui32Param))
+{
+       struct RESMAN_ITEM *psNewResItem;
+
+       PVR_ASSERT(psResManContext != NULL);
+       PVR_ASSERT(ui32ResType != 0);
+
+       ACQUIRE_SYNC_OBJ;
+
+       VALIDATERESLIST();
+
+       PVR_DPF(PVR_DBG_MESSAGE, "ResManRegisterRes: register resource "
+                "Context 0x%x, ResType 0x%x, pvParam 0x%x, ui32Param 0x%x, "
+                "FreeFunc %08X",
+                psResManContext, ui32ResType, (u32) pvParam,
+                ui32Param, pfnFreeResource);
+
+       if (OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP,
+                      sizeof(struct RESMAN_ITEM), (void **) &psNewResItem,
+                      NULL) != PVRSRV_OK) {
+               PVR_DPF(PVR_DBG_ERROR, "ResManRegisterRes: "
+                        "ERROR allocating new resource item");
+
+               RELEASE_SYNC_OBJ;
+
+               return (struct RESMAN_ITEM *)NULL;
+       }
+
+#ifdef DEBUG
+       psNewResItem->ui32Signature = RESMAN_SIGNATURE;
+#endif
+       psNewResItem->ui32ResType = ui32ResType;
+       psNewResItem->pvParam = pvParam;
+       psNewResItem->ui32Param = ui32Param;
+       psNewResItem->pfnFreeResource = pfnFreeResource;
+       psNewResItem->ui32Flags = 0;
+
+       psNewResItem->ppsThis = &psResManContext->psResItemList;
+       psNewResItem->psNext = psResManContext->psResItemList;
+       psResManContext->psResItemList = psNewResItem;
+       if (psNewResItem->psNext)
+               psNewResItem->psNext->ppsThis = &psNewResItem->psNext;
+
+       VALIDATERESLIST();
+
+       RELEASE_SYNC_OBJ;
+
+       return psNewResItem;
+}
+
+void ResManFreeResByPtr(struct RESMAN_ITEM *psResItem)
+{
+       BUG_ON(!psResItem);
+
+       PVR_DPF(PVR_DBG_MESSAGE,
+                "ResManFreeResByPtr: freeing resource at %08X", psResItem);
+
+       ACQUIRE_SYNC_OBJ;
+
+       VALIDATERESLIST();
+
+       FreeResourceByPtr(psResItem, IMG_TRUE);
+
+       VALIDATERESLIST();
+
+       RELEASE_SYNC_OBJ;
+}
+
+void ResManFreeResByCriteria(struct RESMAN_CONTEXT *psResManContext,
+               u32 ui32SearchCriteria,
+               u32 ui32ResType,
+               void *pvParam, u32 ui32Param)
+{
+       PVR_ASSERT(psResManContext != NULL);
+
+       ACQUIRE_SYNC_OBJ;
+
+       VALIDATERESLIST();
+
+       PVR_DPF(PVR_DBG_MESSAGE, "ResManFreeResByCriteria: "
+               "Context 0x%x, Criteria 0x%x, Type 0x%x, Addr 0x%x, Param 0x%x",
+                psResManContext, ui32SearchCriteria, ui32ResType,
+                (u32) pvParam, ui32Param);
+
+       FreeResourceByCriteria(psResManContext, ui32SearchCriteria,
+                                       ui32ResType, pvParam, ui32Param,
+                                       IMG_TRUE);
+
+       VALIDATERESLIST();
+
+       RELEASE_SYNC_OBJ;
+}
+
+void ResManDissociateRes(struct RESMAN_ITEM *psResItem,
+                            struct RESMAN_CONTEXT *psNewResManContext)
+{
+       PVR_ASSERT(psResItem != NULL);
+       PVR_ASSERT(psResItem->ui32Signature == RESMAN_SIGNATURE);
+
+       if (psNewResManContext != NULL) {
+
+               if (psResItem->psNext)
+                       psResItem->psNext->ppsThis = psResItem->ppsThis;
+               *psResItem->ppsThis = psResItem->psNext;
+
+               psResItem->ppsThis = &psNewResManContext->psResItemList;
+               psResItem->psNext = psNewResManContext->psResItemList;
+               psNewResManContext->psResItemList = psResItem;
+               if (psResItem->psNext)
+                       psResItem->psNext->ppsThis = &psResItem->psNext;
+       } else {
+               FreeResourceByPtr(psResItem, IMG_FALSE);
+       }
+}
+
+IMG_INTERNAL enum PVRSRV_ERROR ResManFindResourceByPtr(
+                                       struct RESMAN_CONTEXT *psResManContext,
+                                       struct RESMAN_ITEM *psItem)
+{
+       struct RESMAN_ITEM *psCurItem;
+
+       PVR_ASSERT(psResManContext != NULL);
+       PVR_ASSERT(psItem != NULL);
+       PVR_ASSERT(psItem->ui32Signature == RESMAN_SIGNATURE);
+
+       ACQUIRE_SYNC_OBJ;
+
+       PVR_DPF(PVR_DBG_MESSAGE,
+                "FindResourceByPtr: psItem=%08X, psItem->psNext=%08X",
+                psItem, psItem->psNext);
+
+       PVR_DPF(PVR_DBG_MESSAGE,
+                "FindResourceByPtr: Resource Ctx 0x%x, Type 0x%x, Addr 0x%x, "
+                "Param 0x%x, FnCall %08X, Flags 0x%x",
+                psResManContext,
+                psItem->ui32ResType, (u32) psItem->pvParam,
+                psItem->ui32Param, psItem->pfnFreeResource,
+                psItem->ui32Flags);
+
+       psCurItem = psResManContext->psResItemList;
+
+       while (psCurItem != NULL) {
+
+               if (psCurItem != psItem) {
+
+                       psCurItem = psCurItem->psNext;
+               } else {
+
+                       RELEASE_SYNC_OBJ;
+                       return PVRSRV_OK;
+               }
+       }
+
+       RELEASE_SYNC_OBJ;
+
+       return PVRSRV_ERROR_NOT_OWNER;
+}
+
+static void FreeResourceByPtr(struct RESMAN_ITEM *psItem,
+                                     IMG_BOOL bExecuteCallback)
+{
+       PVR_ASSERT(psItem != NULL);
+       PVR_ASSERT(psItem->ui32Signature == RESMAN_SIGNATURE);
+
+       PVR_DPF(PVR_DBG_MESSAGE,
+                "FreeResourceByPtr: psItem=%08X, psItem->psNext=%08X",
+                psItem, psItem->psNext);
+
+       PVR_DPF(PVR_DBG_MESSAGE,
+                "FreeResourceByPtr: Type 0x%x, Addr 0x%x, "
+                "Param 0x%x, FnCall %08X, Flags 0x%x",
+                psItem->ui32ResType, (u32) psItem->pvParam,
+                psItem->ui32Param, psItem->pfnFreeResource,
+                psItem->ui32Flags);
+
+       if (psItem->psNext)
+               psItem->psNext->ppsThis = psItem->ppsThis;
+       *psItem->ppsThis = psItem->psNext;
+
+       RELEASE_SYNC_OBJ;
+
+       if (bExecuteCallback &&
+               psItem->pfnFreeResource(psItem->pvParam, psItem->ui32Param) !=
+                       PVRSRV_OK)
+               PVR_DPF(PVR_DBG_ERROR, "FreeResourceByPtr: "
+                               "ERROR calling FreeResource function");
+
+       ACQUIRE_SYNC_OBJ;
+
+       OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(struct RESMAN_ITEM),
+                       psItem, NULL);
+}
+
+static int FreeResourceByCriteria(struct RESMAN_CONTEXT *psResManContext,
+                               u32 ui32SearchCriteria, u32 ui32ResType,
+                               void *pvParam, u32 ui32Param,
+                               IMG_BOOL bExecuteCallback)
+{
+       struct RESMAN_ITEM *psCurItem;
+       bool bMatch;
+       int freed = 0;
+
+       psCurItem = psResManContext->psResItemList;
+
+       while (psCurItem != NULL) {
+
+               bMatch = IMG_TRUE;
+
+               if ((ui32SearchCriteria & RESMAN_CRITERIA_RESTYPE) &&
+                   psCurItem->ui32ResType != ui32ResType)
+                       bMatch = IMG_FALSE;
+               else if ((ui32SearchCriteria & RESMAN_CRITERIA_PVOID_PARAM) &&
+                        psCurItem->pvParam != pvParam)
+                       bMatch = IMG_FALSE;
+
+               else
+               if ((ui32SearchCriteria & RESMAN_CRITERIA_UI32_PARAM) &&
+                        psCurItem->ui32Param != ui32Param)
+                       bMatch = IMG_FALSE;
+
+               if (!bMatch) {
+
+                       psCurItem = psCurItem->psNext;
+               } else {
+
+                       FreeResourceByPtr(psCurItem, bExecuteCallback);
+                       psCurItem = psResManContext->psResItemList;
+                       freed++;
+               }
+       }
+
+       return freed;
+}
+
+#ifdef DEBUG
+static void ValidateResList(struct RESMAN_LIST *psResList)
+{
+       struct RESMAN_ITEM *psCurItem, **ppsThisItem;
+       struct RESMAN_CONTEXT *psCurContext, **ppsThisContext;
+
+       if (psResList == NULL) {
+               PVR_DPF(PVR_DBG_MESSAGE,
+                        "ValidateResList: resman not initialised yet");
+               return;
+       }
+
+       psCurContext = psResList->psContextList;
+       ppsThisContext = &psResList->psContextList;
+
+       while (psCurContext != NULL) {
+
+               PVR_ASSERT(psCurContext->ui32Signature == RESMAN_SIGNATURE);
+               if (psCurContext->ppsThis != ppsThisContext) {
+                       PVR_DPF(PVR_DBG_WARNING,
+                        "psCC=%08X psCC->ppsThis=%08X "
+                        "psCC->psNext=%08X ppsTC=%08X",
+                                psCurContext, psCurContext->ppsThis,
+                                psCurContext->psNext, ppsThisContext);
+                       PVR_ASSERT(psCurContext->ppsThis == ppsThisContext);
+               }
+
+               psCurItem = psCurContext->psResItemList;
+               ppsThisItem = &psCurContext->psResItemList;
+               while (psCurItem != NULL) {
+                       PVR_ASSERT(psCurItem->ui32Signature ==
+                                  RESMAN_SIGNATURE);
+                       if (psCurItem->ppsThis != ppsThisItem) {
+                               PVR_DPF(PVR_DBG_WARNING,
+                                        "psCurItem=%08X "
+                                        "psCurItem->ppsThis=%08X "
+                                        "psCurItem->psNext=%08X "
+                                        "ppsThisItem=%08X",
+                                        psCurItem, psCurItem->ppsThis,
+                                        psCurItem->psNext, ppsThisItem);
+                               PVR_ASSERT(psCurItem->ppsThis == ppsThisItem);
+                       }
+
+                       ppsThisItem = &psCurItem->psNext;
+                       psCurItem = psCurItem->psNext;
+               }
+
+               ppsThisContext = &psCurContext->psNext;
+               psCurContext = psCurContext->psNext;
+       }
+}
+#endif