summaryrefslogtreecommitdiff
path: root/kernel_drivers/v4_cleaned/gc_hal_kernel_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel_drivers/v4_cleaned/gc_hal_kernel_device.c')
-rw-r--r--kernel_drivers/v4_cleaned/gc_hal_kernel_device.c1214
1 files changed, 1214 insertions, 0 deletions
diff --git a/kernel_drivers/v4_cleaned/gc_hal_kernel_device.c b/kernel_drivers/v4_cleaned/gc_hal_kernel_device.c
new file mode 100644
index 0000000..43f4863
--- /dev/null
+++ b/kernel_drivers/v4_cleaned/gc_hal_kernel_device.c
@@ -0,0 +1,1214 @@
+/****************************************************************************
+*
+* Copyright (C) 2005 - 2012 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_linux.h"
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/slab.h>
+
+#define _GC_OBJ_ZONE gcvZONE_DEVICE
+
+/******************************************************************************\
+*************************** Memory Allocation Wrappers *************************
+\******************************************************************************/
+
+static gceSTATUS
+_AllocateMemory(
+ IN gckGALDEVICE Device,
+ IN size_t Bytes,
+ OUT void **Logical,
+ OUT gctPHYS_ADDR *Physical,
+ OUT u32 *PhysAddr
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x Bytes=%lu", Device, Bytes);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+ gcmkVERIFY_ARGUMENT(Logical != NULL);
+ gcmkVERIFY_ARGUMENT(Physical != NULL);
+ gcmkVERIFY_ARGUMENT(PhysAddr != NULL);
+
+ gcmkONERROR(gckOS_AllocateContiguous(
+ Device->os, gcvFALSE, &Bytes, Physical, Logical
+ ));
+
+ *PhysAddr = ((PLINUX_MDL)*Physical)->dmaHandle - Device->baseAddress;
+
+ /* Success. */
+ gcmkFOOTER_ARG(
+ "*Logical=0x%x *Physical=0x%x *PhysAddr=0x%08x",
+ *Logical, *Physical, *PhysAddr
+ );
+
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+_FreeMemory(
+ IN gckGALDEVICE Device,
+ IN void *Logical,
+ IN gctPHYS_ADDR Physical)
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x Logical=0x%x Physical=0x%x",
+ Device, Logical, Physical);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ status = gckOS_FreeContiguous(
+ Device->os, Physical, Logical,
+ ((PLINUX_MDL) Physical)->numPages * PAGE_SIZE
+ );
+
+ gcmkFOOTER();
+ return status;
+}
+
+
+
+/******************************************************************************\
+******************************* Interrupt Handler ******************************
+\******************************************************************************/
+static irqreturn_t isrRoutine(int irq, void *ctxt)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+
+ device = (gckGALDEVICE) ctxt;
+
+ /* Call kernel interrupt notification. */
+ status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvTRUE);
+
+ if (gcmIS_SUCCESS(status))
+ {
+ device->dataReadys[gcvCORE_MAJOR] = gcvTRUE;
+
+ up(&device->semas[gcvCORE_MAJOR]);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int threadRoutine(void *ctxt)
+{
+ gckGALDEVICE device = (gckGALDEVICE) ctxt;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Starting isr Thread with extension=%p",
+ device);
+
+ for (;;)
+ {
+ static int down;
+
+ down = down_interruptible(&device->semas[gcvCORE_MAJOR]);
+ if (down); /*To make gcc4.6 happy*/
+ device->dataReadys[gcvCORE_MAJOR] = gcvFALSE;
+
+ if (device->killThread == gcvTRUE)
+ {
+ /* The daemon exits. */
+ while (!kthread_should_stop())
+ {
+ gckOS_Delay(device->os, 1);
+ }
+
+ return 0;
+ }
+
+ gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvFALSE);
+ }
+}
+
+static irqreturn_t isrRoutine2D(int irq, void *ctxt)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+
+ device = (gckGALDEVICE) ctxt;
+
+ /* Call kernel interrupt notification. */
+ status = gckKERNEL_Notify(device->kernels[gcvCORE_2D], gcvNOTIFY_INTERRUPT, gcvTRUE);
+
+ if (gcmIS_SUCCESS(status))
+ {
+ device->dataReadys[gcvCORE_2D] = gcvTRUE;
+
+ up(&device->semas[gcvCORE_2D]);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int threadRoutine2D(void *ctxt)
+{
+ gckGALDEVICE device = (gckGALDEVICE) ctxt;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Starting isr Thread with extension=%p",
+ device);
+
+ for (;;)
+ {
+ static int down;
+
+ down = down_interruptible(&device->semas[gcvCORE_2D]);
+ if (down); /*To make gcc4.6 happy*/
+ device->dataReadys[gcvCORE_2D] = gcvFALSE;
+
+ if (device->killThread == gcvTRUE)
+ {
+ /* The daemon exits. */
+ while (!kthread_should_stop())
+ {
+ gckOS_Delay(device->os, 1);
+ }
+
+ return 0;
+ }
+
+ gckKERNEL_Notify(device->kernels[gcvCORE_2D], gcvNOTIFY_INTERRUPT, gcvFALSE);
+ }
+}
+
+/******************************************************************************\
+******************************* gckGALDEVICE Code ******************************
+\******************************************************************************/
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Setup_ISR
+**
+** Start the ISR routine.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** gcvSTATUS_OK
+** Setup successfully.
+** gcvSTATUS_GENERIC_IO
+** Setup failed.
+*/
+static gceSTATUS
+gckGALDEVICE_Setup_ISR(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ int ret;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->irqLines[gcvCORE_MAJOR] < 0)
+ {
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Hook up the isr based on the irq line. */
+ ret = request_irq(
+ Device->irqLines[gcvCORE_MAJOR], isrRoutine, IRQF_DISABLED,
+ "galcore interrupt service", Device
+ );
+
+ if (ret != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not register irq line %d (error=%d)\n",
+ __FUNCTION__, __LINE__,
+ Device->irqLines[gcvCORE_MAJOR], ret
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Mark ISR as initialized. */
+ Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+static gceSTATUS
+gckGALDEVICE_Setup_ISR_2D(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ int ret;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->irqLines[gcvCORE_2D] < 0)
+ {
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Hook up the isr based on the irq line. */
+ ret = request_irq(
+ Device->irqLines[gcvCORE_2D], isrRoutine2D, IRQF_DISABLED,
+ "galcore interrupt service for 2D", Device
+ );
+
+ if (ret != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not register irq line %d (error=%d)\n",
+ __FUNCTION__, __LINE__,
+ Device->irqLines[gcvCORE_2D], ret
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Mark ISR as initialized. */
+ Device->isrInitializeds[gcvCORE_2D] = gcvTRUE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Release_ISR
+**
+** Release the irq line.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+static gceSTATUS
+gckGALDEVICE_Release_ISR(
+ IN gckGALDEVICE Device
+ )
+{
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ /* release the irq */
+ if (Device->isrInitializeds[gcvCORE_MAJOR])
+ {
+ free_irq(Device->irqLines[gcvCORE_MAJOR], Device);
+
+ Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+static gceSTATUS
+gckGALDEVICE_Release_ISR_2D(
+ IN gckGALDEVICE Device
+ )
+{
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ /* release the irq */
+ if (Device->isrInitializeds[gcvCORE_2D])
+ {
+ free_irq(Device->irqLines[gcvCORE_2D], Device);
+
+ Device->isrInitializeds[gcvCORE_2D] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Construct
+**
+** Constructor.
+**
+** INPUT:
+**
+** OUTPUT:
+**
+** gckGALDEVICE * Device
+** Pointer to a variable receiving the gckGALDEVICE object pointer on
+** success.
+*/
+gceSTATUS
+gckGALDEVICE_Construct(
+ IN int IrqLine,
+ IN u32 RegisterMemBase,
+ IN size_t RegisterMemSize,
+ IN int IrqLine2D,
+ IN u32 RegisterMemBase2D,
+ IN size_t RegisterMemSize2D,
+ IN u32 ContiguousBase,
+ IN size_t ContiguousSize,
+ IN size_t BankSize,
+ IN int FastClear,
+ IN int Compression,
+ IN u32 PhysBaseAddr,
+ IN u32 PhysSize,
+ IN int Signal,
+ OUT gckGALDEVICE *Device
+ )
+{
+ u32 internalBaseAddress = 0, internalAlignment = 0;
+ u32 externalBaseAddress = 0, externalAlignment = 0;
+ u32 horizontalTileSize, verticalTileSize;
+ struct resource* mem_region;
+ u32 physAddr;
+ u32 physical;
+ gckGALDEVICE device;
+ gceSTATUS status;
+ s32 i;
+ gceHARDWARE_TYPE type;
+ gckDB sharedDB = NULL;
+
+ gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%u "
+ "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u "
+ "ContiguousBase=0x%08x ContiguousSize=%lu BankSize=%lu "
+ "FastClear=%d Compression=%d PhysBaseAddr=0x%x PhysSize=%d Signal=%d",
+ IrqLine, RegisterMemBase, RegisterMemSize,
+ IrqLine2D, RegisterMemBase2D, RegisterMemSize2D,
+ ContiguousBase, ContiguousSize, BankSize, FastClear, Compression,
+ PhysBaseAddr, PhysSize, Signal);
+
+ /* Allocate device structure. */
+ device = kmalloc(sizeof(struct _gckGALDEVICE), GFP_KERNEL | __GFP_NOWARN);
+
+ if (!device)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
+ }
+
+ memset(device, 0, sizeof(struct _gckGALDEVICE));
+
+ if (IrqLine != -1)
+ {
+ device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase;
+ device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize;
+ }
+
+ if (IrqLine2D != -1)
+ {
+ device->requestedRegisterMemBases[gcvCORE_2D] = RegisterMemBase2D;
+ device->requestedRegisterMemSizes[gcvCORE_2D] = RegisterMemSize2D;
+ }
+
+ device->requestedContiguousBase = 0;
+ device->requestedContiguousSize = 0;
+
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ physical = device->requestedRegisterMemBases[i];
+
+ /* Set up register memory region. */
+ if (physical != 0)
+ {
+ mem_region = request_mem_region(
+ physical, device->requestedRegisterMemSizes[i], "galcore register region"
+ );
+
+#if 0
+ if (mem_region == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Failed to claim %lu bytes @ 0x%08X\n",
+ __FUNCTION__, __LINE__,
+ physical, device->requestedRegisterMemSizes[i]
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+#endif
+
+ device->registerBases[i] = (void *) ioremap_nocache(
+ physical, device->requestedRegisterMemSizes[i]);
+
+ if (device->registerBases[i] == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Unable to map %ld bytes @ 0x%08X\n",
+ __FUNCTION__, __LINE__,
+ physical, device->requestedRegisterMemSizes[i]
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ physical += device->requestedRegisterMemSizes[i];
+ }
+ else
+ {
+ device->registerBases[i] = NULL;
+ }
+ }
+
+ /* Set the base address */
+ device->baseAddress = PhysBaseAddr;
+
+ /* Construct the gckOS object. */
+ gcmkONERROR(gckOS_Construct(device, &device->os));
+
+ if (IrqLine != -1)
+ {
+ /* Construct the gckKERNEL object. */
+ gcmkONERROR(gckKERNEL_Construct(
+ device->os, gcvCORE_MAJOR, device,
+ NULL, &device->kernels[gcvCORE_MAJOR]));
+
+ sharedDB = device->kernels[gcvCORE_MAJOR]->db;
+
+ /* Initialize core mapping */
+ for (i = 0; i < 8; i++)
+ {
+ device->coreMapping[i] = gcvCORE_MAJOR;
+ }
+
+ /* Setup the ISR manager. */
+ gcmkONERROR(gckHARDWARE_SetIsrManager(
+ device->kernels[gcvCORE_MAJOR]->hardware,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR,
+ device
+ ));
+
+ gcmkONERROR(gckHARDWARE_SetFastClear(
+ device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression
+ ));
+
+ /* Start the command queue. */
+ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command));
+ }
+ else
+ {
+ device->kernels[gcvCORE_MAJOR] = NULL;
+ }
+
+ if (IrqLine2D != -1)
+ {
+ gcmkONERROR(gckKERNEL_Construct(
+ device->os, gcvCORE_2D, device,
+ sharedDB, &device->kernels[gcvCORE_2D]));
+
+ if (sharedDB == NULL) sharedDB = device->kernels[gcvCORE_2D]->db;
+
+ /* Verify the hardware type */
+ gcmkONERROR(gckHARDWARE_GetType(device->kernels[gcvCORE_2D]->hardware, &type));
+
+ if (type != gcvHARDWARE_2D)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Unexpected hardware type: %d\n",
+ __FUNCTION__, __LINE__,
+ type
+ );
+
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+ /* Initialize core mapping */
+ if (device->kernels[gcvCORE_MAJOR] == NULL)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ device->coreMapping[i] = gcvCORE_2D;
+ }
+ }
+ else
+ {
+ device->coreMapping[gcvHARDWARE_2D] = gcvCORE_2D;
+ }
+
+ /* Setup the ISR manager. */
+ gcmkONERROR(gckHARDWARE_SetIsrManager(
+ device->kernels[gcvCORE_2D]->hardware,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR_2D,
+ (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR_2D,
+ device
+ ));
+
+ /* Start the command queue. */
+ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_2D]->command));
+ }
+ else
+ {
+ device->kernels[gcvCORE_2D] = NULL;
+ }
+
+ /* Initialize the ISR. */
+ device->irqLines[gcvCORE_MAJOR] = IrqLine;
+ device->irqLines[gcvCORE_2D] = IrqLine2D;
+
+ /* Initialize the kernel thread semaphores. */
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0);
+ }
+
+ device->signal = Signal;
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (device->kernels[i] != NULL) break;
+ }
+
+ if (i == gcdCORE_COUNT) gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+
+ /* Query the ceiling of the system memory. */
+ gcmkONERROR(gckHARDWARE_QuerySystemMemory(
+ device->kernels[i]->hardware,
+ &device->systemMemorySize,
+ &device->systemMemoryBaseAddress
+ ));
+
+ /* Query the amount of video memory. */
+ gcmkONERROR(gckHARDWARE_QueryMemory(
+ device->kernels[i]->hardware,
+ &device->internalSize, &internalBaseAddress, &internalAlignment,
+ &device->externalSize, &externalBaseAddress, &externalAlignment,
+ &horizontalTileSize, &verticalTileSize
+ ));
+
+ /* Set up the internal memory region. */
+ if (device->internalSize > 0)
+ {
+ status = gckVIDMEM_Construct(
+ device->os,
+ internalBaseAddress, device->internalSize, internalAlignment,
+ 0, &device->internalVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable internal heap. */
+ device->internalSize = 0;
+ }
+ else
+ {
+ /* Map internal memory. */
+ device->internalLogical
+ = (void *) ioremap_nocache(physical, device->internalSize);
+
+ if (device->internalLogical == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->internalPhysical = (gctPHYS_ADDR) physical;
+ physical += device->internalSize;
+ }
+ }
+
+ if (device->externalSize > 0)
+ {
+ /* create the external memory heap */
+ status = gckVIDMEM_Construct(
+ device->os,
+ externalBaseAddress, device->externalSize, externalAlignment,
+ 0, &device->externalVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable internal heap. */
+ device->externalSize = 0;
+ }
+ else
+ {
+ /* Map external memory. */
+ device->externalLogical
+ = (void *) ioremap_nocache(physical, device->externalSize);
+
+ if (device->externalLogical == NULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->externalPhysical = (gctPHYS_ADDR) physical;
+ physical += device->externalSize;
+ }
+ }
+
+ /* set up the contiguous memory */
+ device->contiguousSize = ContiguousSize;
+
+ if (ContiguousSize > 0)
+ {
+ if (ContiguousBase == 0)
+ {
+ while (device->contiguousSize > 0)
+ {
+ /* Allocate contiguous memory. */
+ status = _AllocateMemory(
+ device,
+ device->contiguousSize,
+ &device->contiguousBase,
+ &device->contiguousPhysical,
+ &physAddr
+ );
+
+ if (gcmIS_SUCCESS(status))
+ {
+ status = gckVIDMEM_Construct(
+ device->os,
+ physAddr | device->systemMemoryBaseAddress,
+ device->contiguousSize,
+ 64,
+ BankSize,
+ &device->contiguousVidMem
+ );
+
+ if (gcmIS_SUCCESS(status))
+ {
+ break;
+ }
+
+ gcmkONERROR(_FreeMemory(
+ device,
+ device->contiguousBase,
+ device->contiguousPhysical
+ ));
+
+ device->contiguousBase = NULL;
+ device->contiguousPhysical = NULL;
+ }
+
+ if (device->contiguousSize <= (4 << 20))
+ {
+ device->contiguousSize = 0;
+ }
+ else
+ {
+ device->contiguousSize -= (4 << 20);
+ }
+ }
+ }
+ else
+ {
+ /* Create the contiguous memory heap. */
+ status = gckVIDMEM_Construct(
+ device->os,
+ (ContiguousBase - device->baseAddress) | device->systemMemoryBaseAddress,
+ ContiguousSize,
+ 64, BankSize,
+ &device->contiguousVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable contiguous memory pool. */
+ device->contiguousVidMem = NULL;
+ device->contiguousSize = 0;
+ }
+ else
+ {
+ mem_region = request_mem_region(
+ ContiguousBase, ContiguousSize, "galcore managed memory"
+ );
+
+#if 0
+ if (mem_region == NULL)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Failed to claim %ld bytes @ 0x%08X\n",
+ __FUNCTION__, __LINE__,
+ ContiguousSize, ContiguousBase
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+#endif
+
+ device->requestedContiguousBase = ContiguousBase;
+ device->requestedContiguousSize = ContiguousSize;
+
+ device->contiguousPhysical = (gctPHYS_ADDR) ContiguousBase;
+ device->contiguousSize = ContiguousSize;
+ device->contiguousMapped = gcvTRUE;
+ }
+ }
+ }
+
+ /* Return pointer to the device. */
+ * Device = device;
+
+ gcmkFOOTER_ARG("*Device=0x%x", * Device);
+ return gcvSTATUS_OK;
+
+OnError:
+ /* Roll back. */
+ gcmkVERIFY_OK(gckGALDEVICE_Destroy(device));
+
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Destroy
+**
+** Class destructor.
+**
+** INPUT:
+**
+** Nothing.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+gceSTATUS
+gckGALDEVICE_Destroy(
+ gckGALDEVICE Device)
+{
+ int i;
+ gceSTATUS status = gcvSTATUS_OK;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ if (Device != NULL)
+ {
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (Device->kernels[i] != NULL)
+ {
+ /* Destroy the gckKERNEL object. */
+ gcmkVERIFY_OK(gckKERNEL_Destroy(Device->kernels[i]));
+ Device->kernels[i] = NULL;
+ }
+ }
+
+ {
+ if (Device->internalLogical != NULL)
+ {
+ /* Unmap the internal memory. */
+ iounmap(Device->internalLogical);
+ Device->internalLogical = NULL;
+ }
+
+ if (Device->internalVidMem != NULL)
+ {
+ /* Destroy the internal heap. */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->internalVidMem));
+ Device->internalVidMem = NULL;
+ }
+ }
+
+ {
+ if (Device->externalLogical != NULL)
+ {
+ /* Unmap the external memory. */
+ iounmap(Device->externalLogical);
+ Device->externalLogical = NULL;
+ }
+
+ if (Device->externalVidMem != NULL)
+ {
+ /* destroy the external heap */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem));
+ Device->externalVidMem = NULL;
+ }
+ }
+
+ {
+ if (Device->contiguousBase != NULL)
+ {
+ if (!Device->contiguousMapped)
+ {
+ gcmkONERROR(_FreeMemory(
+ Device,
+ Device->contiguousBase,
+ Device->contiguousPhysical
+ ));
+ }
+
+ Device->contiguousBase = NULL;
+ Device->contiguousPhysical = NULL;
+ }
+
+ if (Device->requestedContiguousBase != 0)
+ {
+ release_mem_region(Device->requestedContiguousBase, Device->requestedContiguousSize);
+ Device->requestedContiguousBase = 0;
+ Device->requestedContiguousSize = 0;
+ }
+
+ if (Device->contiguousVidMem != NULL)
+ {
+ /* Destroy the contiguous heap. */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem));
+ Device->contiguousVidMem = NULL;
+ }
+ }
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ if (Device->registerBases[i] != NULL)
+ {
+ /* Unmap register memory. */
+ iounmap(Device->registerBases[i]);
+ if (Device->requestedRegisterMemBases[i] != 0)
+ {
+ release_mem_region(Device->requestedRegisterMemBases[i], Device->requestedRegisterMemSizes[i]);
+ }
+
+ Device->registerBases[i] = NULL;
+ Device->requestedRegisterMemBases[i] = 0;
+ Device->requestedRegisterMemSizes[i] = 0;
+ }
+ }
+
+ /* Destroy the gckOS object. */
+ if (Device->os != NULL)
+ {
+ gcmkVERIFY_OK(gckOS_Destroy(Device->os));
+ Device->os = NULL;
+ }
+
+ /* Free the device. */
+ kfree(Device);
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Start_Threads
+**
+** Start the daemon threads.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** gcvSTATUS_OK
+** Start successfully.
+** gcvSTATUS_GENERIC_IO
+** Start failed.
+*/
+static gceSTATUS
+gckGALDEVICE_Start_Threads(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ struct task_struct * task;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->kernels[gcvCORE_MAJOR] != NULL)
+ {
+ /* Start the kernel thread. */
+ task = kthread_run(threadRoutine, Device, "galcore daemon thread");
+
+ if (IS_ERR(task))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not start the kernel thread.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ Device->threadCtxts[gcvCORE_MAJOR] = task;
+ Device->threadInitializeds[gcvCORE_MAJOR] = gcvTRUE;
+ }
+
+ if (Device->kernels[gcvCORE_2D] != NULL)
+ {
+ /* Start the kernel thread. */
+ task = kthread_run(threadRoutine2D, Device, "galcore daemon thread for 2D");
+
+ if (IS_ERR(task))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not start the kernel thread.\n",
+ __FUNCTION__, __LINE__
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ Device->threadCtxts[gcvCORE_2D] = task;
+ Device->threadInitializeds[gcvCORE_2D] = gcvTRUE;
+ }
+ else
+ {
+ Device->threadInitializeds[gcvCORE_2D] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Stop_Threads
+**
+** Stop the gal device, including the following actions: stop the daemon
+** thread, release the irq.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+static gceSTATUS
+gckGALDEVICE_Stop_Threads(
+ gckGALDEVICE Device
+ )
+{
+ int i;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ for (i = 0; i < gcdCORE_COUNT; i++)
+ {
+ /* Stop the kernel threads. */
+ if (Device->threadInitializeds[i])
+ {
+ Device->killThread = gcvTRUE;
+ up(&Device->semas[i]);
+
+ kthread_stop(Device->threadCtxts[i]);
+ Device->threadCtxts[i] = NULL;
+ Device->threadInitializeds[i] = gcvFALSE;
+ }
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Start
+**
+** Start the gal device, including the following actions: setup the isr routine
+** and start the daemoni thread.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** gcvSTATUS_OK
+** Start successfully.
+*/
+gceSTATUS
+gckGALDEVICE_Start(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ /* Start the kernel thread. */
+ gcmkONERROR(gckGALDEVICE_Start_Threads(Device));
+
+ if (Device->kernels[gcvCORE_MAJOR] != NULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR(Device));
+
+ /* Switch to SUSPEND power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF_BROADCAST
+ ));
+ }
+
+ if (Device->kernels[gcvCORE_2D] != NULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR_2D(Device));
+
+ /* Switch to SUSPEND power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF_BROADCAST
+ ));
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Stop
+**
+** Stop the gal device, including the following actions: stop the daemon
+** thread, release the irq.
+**
+** INPUT:
+**
+** gckGALDEVICE Device
+** Pointer to an gckGALDEVICE object.
+**
+** OUTPUT:
+**
+** Nothing.
+**
+** RETURNS:
+**
+** Nothing.
+*/
+gceSTATUS
+gckGALDEVICE_Stop(
+ gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->kernels[gcvCORE_MAJOR] != NULL)
+ {
+ /* Switch to OFF power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF
+ ));
+
+ /* Remove the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Release_ISR(Device));
+ }
+
+ if (Device->kernels[gcvCORE_2D] != NULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Release_ISR_2D(Device));
+
+ /* Switch to OFF power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF
+ ));
+ }
+
+ /* Stop the kernel thread. */
+ gcmkONERROR(gckGALDEVICE_Stop_Threads(Device));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}