summaryrefslogtreecommitdiff
path: root/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu_vg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu_vg.c')
-rw-r--r--drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu_vg.c503
1 files changed, 503 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu_vg.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu_vg.c
new file mode 100644
index 000000000000..e3206277d891
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_mmu_vg.c
@@ -0,0 +1,503 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2011 by Vivante Corp.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the license, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+*****************************************************************************/
+
+
+
+
+#include "gc_hal_kernel_precomp.h"
+
+#if gcdENABLE_VG
+
+#define _GC_OBJ_ZONE gcvZONE_MMU
+
+/*******************************************************************************
+**
+** gckVGMMU_Construct
+**
+** Construct a new gckVGMMU object.
+**
+** INPUT:
+**
+** gckVGKERNEL Kernel
+** Pointer to an gckVGKERNEL object.
+**
+** gctSIZE_T MmuSize
+** Number of bytes for the page table.
+**
+** OUTPUT:
+**
+** gckVGMMU * Mmu
+** Pointer to a variable that receives the gckVGMMU object pointer.
+*/
+gceSTATUS gckVGMMU_Construct(
+ IN gckVGKERNEL Kernel,
+ IN gctSIZE_T MmuSize,
+ OUT gckVGMMU * Mmu
+ )
+{
+ gckOS os;
+ gckVGHARDWARE hardware;
+ gceSTATUS status;
+ gckVGMMU mmu;
+ gctUINT32 * pageTable;
+ gctUINT32 i;
+
+ gcmkHEADER_ARG("Kernel=0x%x MmuSize=0x%x Mmu=0x%x", Kernel, MmuSize, Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
+ gcmkVERIFY_ARGUMENT(MmuSize > 0);
+ gcmkVERIFY_ARGUMENT(Mmu != gcvNULL);
+
+ /* Extract the gckOS object pointer. */
+ os = Kernel->os;
+ gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
+
+ /* Extract the gckVGHARDWARE object pointer. */
+ hardware = Kernel->hardware;
+ gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
+
+ /* Allocate memory for the gckVGMMU object. */
+ status = gckOS_Allocate(os, sizeof(struct _gckVGMMU), (gctPOINTER *) &mmu);
+
+ if (status < 0)
+ {
+ /* Error. */
+ gcmkFATAL(
+ "%s(%d): could not allocate gckVGMMU object.",
+ __FUNCTION__, __LINE__
+ );
+
+ return status;
+ }
+
+ /* Initialize the gckVGMMU object. */
+ mmu->object.type = gcvOBJ_MMU;
+ mmu->os = os;
+ mmu->hardware = hardware;
+
+ /* Create the mutex. */
+ status = gckOS_CreateMutex(os, &mmu->mutex);
+
+ if (status < 0)
+ {
+ /* Roll back. */
+ mmu->object.type = gcvOBJ_UNKNOWN;
+ gcmkVERIFY_OK(gckOS_Free(os, mmu));
+
+ /* Error. */
+ return status;
+ }
+
+ /* Allocate the page table. */
+ mmu->pageTableSize = MmuSize;
+ status = gckOS_AllocateContiguous(os,
+ gcvFALSE,
+ &mmu->pageTableSize,
+ &mmu->pageTablePhysical,
+ &mmu->pageTableLogical);
+
+ if (status < 0)
+ {
+ /* Roll back. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, mmu->mutex));
+
+ mmu->object.type = gcvOBJ_UNKNOWN;
+ gcmkVERIFY_OK(gckOS_Free(os, mmu));
+
+ /* Error. */
+ gcmkFATAL(
+ "%s(%d): could not allocate page table.",
+ __FUNCTION__, __LINE__
+ );
+
+ return status;
+ }
+
+ /* Compute number of entries in page table. */
+ mmu->entryCount = mmu->pageTableSize / sizeof(gctUINT32);
+ mmu->entry = 0;
+
+ /* Mark the entire page table as available. */
+ pageTable = (gctUINT32 *) mmu->pageTableLogical;
+ for (i = 0; i < mmu->entryCount; i++)
+ {
+ pageTable[i] = (gctUINT32)~0;
+ }
+
+ /* Set page table address. */
+ status = gckVGHARDWARE_SetMMU(hardware, mmu->pageTableLogical);
+
+ if (status < 0)
+ {
+ /* Free the page table. */
+ gcmkVERIFY_OK(gckOS_FreeContiguous(mmu->os,
+ mmu->pageTablePhysical,
+ mmu->pageTableLogical,
+ mmu->pageTableSize));
+
+ /* Roll back. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(os, mmu->mutex));
+
+ mmu->object.type = gcvOBJ_UNKNOWN;
+ gcmkVERIFY_OK(gckOS_Free(os, mmu));
+
+ /* Error. */
+ gcmkFATAL(
+ "%s(%d): could not program page table.",
+ __FUNCTION__, __LINE__
+ );
+
+ return status;
+ }
+
+ /* Return the gckVGMMU object pointer. */
+ *Mmu = mmu;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_MMU,
+ "%s(%d): %u entries at %p.(0x%08X)\n",
+ __FUNCTION__, __LINE__,
+ mmu->entryCount,
+ mmu->pageTableLogical,
+ mmu->pageTablePhysical
+ );
+
+ gcmkFOOTER_NO();
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckVGMMU_Destroy
+**
+** Destroy a nAQMMU object.
+**
+** INPUT:
+**
+** gckVGMMU Mmu
+** Pointer to an gckVGMMU object.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS gckVGMMU_Destroy(
+ IN gckVGMMU Mmu
+ )
+{
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+
+ /* Free the page table. */
+ gcmkVERIFY_OK(gckOS_FreeContiguous(Mmu->os,
+ Mmu->pageTablePhysical,
+ Mmu->pageTableLogical,
+ Mmu->pageTableSize));
+
+ /* Roll back. */
+ gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->mutex));
+
+ /* Mark the gckVGMMU object as unknown. */
+ Mmu->object.type = gcvOBJ_UNKNOWN;
+
+ /* Free the gckVGMMU object. */
+ gcmkVERIFY_OK(gckOS_Free(Mmu->os, Mmu));
+
+ gcmkFOOTER_NO();
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckVGMMU_AllocatePages
+**
+** Allocate pages inside the page table.
+**
+** INPUT:
+**
+** gckVGMMU Mmu
+** Pointer to an gckVGMMU object.
+**
+** gctSIZE_T PageCount
+** Number of pages to allocate.
+**
+** OUTPUT:
+**
+** gctPOINTER * PageTable
+** Pointer to a variable that receives the base address of the page
+** table.
+**
+** gctUINT32 * Address
+** Pointer to a variable that receives the hardware specific address.
+*/
+gceSTATUS gckVGMMU_AllocatePages(
+ IN gckVGMMU Mmu,
+ IN gctSIZE_T PageCount,
+ OUT gctPOINTER * PageTable,
+ OUT gctUINT32 * Address
+ )
+{
+ gceSTATUS status;
+ gctUINT32 tail, index, i;
+ gctUINT32 * table;
+ gctBOOL allocated = gcvFALSE;
+
+ gcmkHEADER_ARG("Mmu=0x%x PageCount=0x%x PageTable=0x%x Address=0x%x",
+ Mmu, PageCount, PageTable, Address);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+ gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
+ gcmkVERIFY_ARGUMENT(Address != gcvNULL);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_MMU,
+ "%s(%d): %u pages.\n",
+ __FUNCTION__, __LINE__,
+ PageCount
+ );
+
+ if (PageCount > Mmu->entryCount)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_MMU,
+ "%s(%d): page table too small for %u pages.\n",
+ __FUNCTION__, __LINE__,
+ PageCount
+ );
+
+ /* Not enough pages avaiable. */
+ return gcvSTATUS_OUT_OF_RESOURCES;
+ }
+
+ /* Grab the mutex. */
+ status = gckOS_AcquireMutex(Mmu->os, Mmu->mutex, gcvINFINITE);
+
+ if (status < 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_MMU,
+ "%s(%d): could not acquire mutex.\n"
+ ,__FUNCTION__, __LINE__
+ );
+
+ /* Error. */
+ return status;
+ }
+
+ /* Compute the tail for this allocation. */
+ tail = Mmu->entryCount - PageCount;
+
+ /* Walk all entries until we find enough slots. */
+ for (index = Mmu->entry; index <= tail;)
+ {
+ /* Access page table. */
+ table = (gctUINT32 *) Mmu->pageTableLogical + index;
+
+ /* See if all slots are available. */
+ for (i = 0; i < PageCount; i++, table++)
+ {
+ if (*table != ~0)
+ {
+ /* Start from next slot. */
+ index += i + 1;
+ break;
+ }
+ }
+
+ if (i == PageCount)
+ {
+ /* Bail out if we have enough page entries. */
+ allocated = gcvTRUE;
+ break;
+ }
+ }
+
+ if (!allocated)
+ {
+ /* Flush the MMU. */
+ status = gckVGHARDWARE_FlushMMU(Mmu->hardware);
+
+ if (status >= 0)
+ {
+ /* Walk all entries until we find enough slots. */
+ for (index = 0; index <= tail;)
+ {
+ /* Access page table. */
+ table = (gctUINT32 *) Mmu->pageTableLogical + index;
+
+ /* See if all slots are available. */
+ for (i = 0; i < PageCount; i++, table++)
+ {
+ if (*table != ~0)
+ {
+ /* Start from next slot. */
+ index += i + 1;
+ break;
+ }
+ }
+
+ if (i == PageCount)
+ {
+ /* Bail out if we have enough page entries. */
+ allocated = gcvTRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!allocated && (status >= 0))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_MMU,
+ "%s(%d): not enough free pages for %u pages.\n",
+ __FUNCTION__, __LINE__,
+ PageCount
+ );
+
+ /* Not enough empty slots available. */
+ status = gcvSTATUS_OUT_OF_RESOURCES;
+ }
+
+ if (status >= 0)
+ {
+ /* Build virtual address. */
+ status = gckVGHARDWARE_BuildVirtualAddress(Mmu->hardware,
+ index,
+ 0,
+ Address);
+
+ if (status >= 0)
+ {
+ /* Update current entry into page table. */
+ Mmu->entry = index + PageCount;
+
+ /* Return pointer to page table. */
+ *PageTable = (gctUINT32 *) Mmu->pageTableLogical + index;
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_MMU,
+ "%s(%d): allocated %u pages at index %u (0x%08X) @ %p.\n",
+ __FUNCTION__, __LINE__,
+ PageCount,
+ index,
+ *Address,
+ *PageTable
+ );
+ }
+ }
+
+ /* Release the mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->mutex));
+ gcmkFOOTER();
+
+ /* Return status. */
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckVGMMU_FreePages
+**
+** Free pages inside the page table.
+**
+** INPUT:
+**
+** gckVGMMU Mmu
+** Pointer to an gckVGMMU object.
+**
+** gctPOINTER PageTable
+** Base address of the page table to free.
+**
+** gctSIZE_T PageCount
+** Number of pages to free.
+**
+** OUTPUT:
+**
+** Nothing.
+*/
+gceSTATUS gckVGMMU_FreePages(
+ IN gckVGMMU Mmu,
+ IN gctPOINTER PageTable,
+ IN gctSIZE_T PageCount
+ )
+{
+ gctUINT32 * table;
+
+ gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=0x%x",
+ Mmu, PageTable, PageCount);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageTable != gcvNULL);
+ gcmkVERIFY_ARGUMENT(PageCount > 0);
+
+ gcmkTRACE_ZONE(
+ gcvLEVEL_INFO, gcvZONE_MMU,
+ "%s(%d): freeing %u pages at index %u @ %p.\n",
+ __FUNCTION__, __LINE__,
+ PageCount,
+ ((gctUINT32 *) PageTable - (gctUINT32 *) Mmu->pageTableLogical),
+ PageTable
+ );
+
+ /* Convert pointer. */
+ table = (gctUINT32 *) PageTable;
+
+ /* Mark the page table entries as available. */
+ while (PageCount-- > 0)
+ {
+ *table++ = (gctUINT32)~0;
+ }
+
+ gcmkFOOTER_NO();
+ /* Success. */
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckVGMMU_SetPage(
+ IN gckVGMMU Mmu,
+ IN gctUINT32 PageAddress,
+ IN gctUINT32 *PageEntry
+ )
+{
+ gcmkHEADER_ARG("Mmu=0x%x", Mmu);
+
+ /* Verify the arguments. */
+ gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU);
+ gcmkVERIFY_ARGUMENT(PageEntry != gcvNULL);
+ gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF));
+
+ *PageEntry = PageAddress;
+
+ /* Success. */
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+#endif /* gcdENABLE_VG */