]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
gpu: vivante: Update driver from Freescale 3.10.53-1.1-ga BSP
[karo-tx-linux.git] / drivers / mxc / gpu-viv / hal / os / linux / kernel / gc_hal_kernel_device.c
index bff85a0e9bf4baee008135ff7d041cfbf27d0e67..ccec82f282bc27e7494f4bc0b894aa476bc0227a 100644 (file)
@@ -1,6 +1,6 @@
 /****************************************************************************
 *
-*    Copyright (C) 2005 - 2013 by Vivante Corp.
+*    Copyright (C) 2005 - 2014 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
 #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>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
-#include <mach/hardware.h>
-#endif
-#include <linux/pm_runtime.h>
 
 #define _GC_OBJ_ZONE    gcvZONE_DEVICE
 
-#define DEBUG_FILE                     "galcore_trace"
-#define PARENT_FILE            "gpu"
+#define DEBUG_FILE          "galcore_trace"
+#define PARENT_FILE         "gpu"
 
 
 #ifdef FLAREON
     static struct dove_gpio_irq_handler gc500_handle;
 #endif
 
-#define gcmIS_CORE_PRESENT(Device, Core) (Device->irqLines[Core] > 0)
+gckKERNEL
+_GetValidKernel(
+    gckGALDEVICE Device
+    )
+{
+    if (Device->kernels[gcvCORE_MAJOR])
+    {
+        return Device->kernels[gcvCORE_MAJOR];
+    }
+    else
+    if (Device->kernels[gcvCORE_2D])
+    {
+        return Device->kernels[gcvCORE_2D];
+    }
+    else
+    if (Device->kernels[gcvCORE_VG])
+    {
+        return Device->kernels[gcvCORE_VG];
+    }
+    else
+    {
+        return gcvNULL;
+    }
+}
+
+/******************************************************************************\
+******************************** Debugfs Support *******************************
+\******************************************************************************/
+
+/******************************************************************************\
+***************************** DEBUG SHOW FUNCTIONS *****************************
+\******************************************************************************/
+
+int gc_info_show(struct seq_file* m, void* data)
+{
+    gcsINFO_NODE *node = m->private;
+    gckGALDEVICE device = node->device;
+    int i = 0;
+    gceCHIPMODEL chipModel;
+    gctUINT32 chipRevision;
+
+    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+    {
+        if (device->irqLines[i] != -1)
+        {
+#if gcdENABLE_VG
+            if (i == gcvCORE_VG)
+            {
+                chipModel = device->kernels[i]->vg->hardware->chipModel;
+                chipRevision = device->kernels[i]->vg->hardware->chipRevision;
+            }
+            else
+#endif
+            {
+                chipModel = device->kernels[i]->hardware->identity.chipModel;
+                chipRevision = device->kernels[i]->hardware->identity.chipRevision;
+            }
+
+            seq_printf(m, "gpu      : %d\n", i);
+            seq_printf(m, "model    : %4x\n", chipModel);
+            seq_printf(m, "revision : %4x\n", chipRevision);
+            seq_printf(m, "\n");
+        }
+    }
+
+    return 0;
+}
+
+int gc_clients_show(struct seq_file* m, void* data)
+{
+    gcsINFO_NODE *node = m->private;
+    gckGALDEVICE device = node->device;
+
+    gckKERNEL kernel = _GetValidKernel(device);
+
+    gcsDATABASE_PTR database;
+    gctINT i, pid;
+    gctUINT8 name[24];
+
+    seq_printf(m, "%-8s%s\n", "PID", "NAME");
+    seq_printf(m, "------------------------\n");
+
+    /* Acquire the database mutex. */
+    gcmkVERIFY_OK(
+        gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
+
+    /* Walk the databases. */
+    for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i)
+    {
+        for (database = kernel->db->db[i];
+             database != gcvNULL;
+             database = database->next)
+        {
+            pid = database->processID;
+
+            gcmkVERIFY_OK(gckOS_ZeroMemory(name, gcmSIZEOF(name)));
+
+            gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name));
+
+            seq_printf(m, "%-8d%s\n", pid, name);
+        }
+    }
+
+    /* Release the database mutex. */
+    gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
+
+    /* Success. */
+    return 0;
+}
+
+static void
+_CounterAdd(
+    gcsDATABASE_COUNTERS * Dest,
+    gcsDATABASE_COUNTERS * Src
+    )
+{
+    Dest->bytes += Src->bytes;
+    Dest->maxBytes += Src->maxBytes;
+    Dest->totalBytes += Src->totalBytes;
+}
+
+static void
+_CounterPrint(
+    gcsDATABASE_COUNTERS * Counter,
+    gctCONST_STRING Name,
+    struct seq_file* m
+    )
+{
+    seq_printf(m, "    %s:\n", Name);
+    seq_printf(m, "        Used  : %10llu B\n", Counter->bytes);
+}
+
+int gc_meminfo_show(struct seq_file* m, void* data)
+{
+    gcsINFO_NODE *node = m->private;
+    gckGALDEVICE device = node->device;
+    gckKERNEL kernel = _GetValidKernel(device);
+    gckVIDMEM memory;
+    gceSTATUS status;
+    gcsDATABASE_PTR database;
+    gctUINT32 i;
+
+    gctUINT32 free = 0, used = 0, total = 0;
+
+    gcsDATABASE_COUNTERS contiguousCounter = {0, 0, 0};
+    gcsDATABASE_COUNTERS virtualCounter = {0, 0, 0};
+    gcsDATABASE_COUNTERS nonPagedCounter = {0, 0, 0};
+
+    status = gckKERNEL_GetVideoMemoryPool(kernel, gcvPOOL_SYSTEM, &memory);
+
+    if (gcmIS_SUCCESS(status))
+    {
+        gcmkVERIFY_OK(
+            gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE));
+
+        free  = memory->freeBytes;
+        used  = memory->bytes - memory->freeBytes;
+        total = memory->bytes;
+
+        gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex));
+    }
+
+    seq_printf(m, "VIDEO MEMORY:\n");
+    seq_printf(m, "    gcvPOOL_SYSTEM:\n");
+    seq_printf(m, "        Free  : %10u B\n", free);
+    seq_printf(m, "        Used  : %10u B\n", used);
+    seq_printf(m, "        Total : %10u B\n", total);
+
+    /* Acquire the database mutex. */
+    gcmkVERIFY_OK(
+        gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
+
+    /* Walk the databases. */
+    for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i)
+    {
+        for (database = kernel->db->db[i];
+             database != gcvNULL;
+             database = database->next)
+        {
+            gcsDATABASE_COUNTERS * counter = &database->vidMemPool[gcvPOOL_CONTIGUOUS];
+            _CounterAdd(&contiguousCounter, counter);
+
+            counter = &database->vidMemPool[gcvPOOL_VIRTUAL];
+            _CounterAdd(&virtualCounter, counter);
+
+
+            counter = &database->nonPaged;
+            _CounterAdd(&nonPagedCounter, counter);
+        }
+    }
+
+    /* Release the database mutex. */
+    gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
+
+    _CounterPrint(&contiguousCounter, "gcvPOOL_CONTIGUOUS", m);
+    _CounterPrint(&virtualCounter, "gcvPOOL_VIRTUAL", m);
+
+    seq_printf(m, "\n");
+
+    seq_printf(m, "NON PAGED MEMORY:\n");
+    seq_printf(m, "    Used  : %10llu B\n", nonPagedCounter.bytes);
+
+    return 0;
+}
+
+static int
+_ShowRecord(
+    IN struct seq_file *file,
+    IN gcsDATABASE_RECORD_PTR record
+    )
+{
+    seq_printf(file, "%4d%8d%16p%16p%16zu\n",
+        record->type,
+        record->kernel->core,
+        record->data,
+        record->physical,
+        record->bytes
+        );
+
+    return 0;
+}
+
+static int
+_ShowRecords(
+    IN struct seq_file *File,
+    IN gcsDATABASE_PTR Database
+    )
+{
+    gctUINT i;
+
+    seq_printf(File, "Records:\n");
+
+    seq_printf(File, "%s%8s%16s%16s%16s\n",
+               "Type", "GPU", "Data", "Physical", "Bytes");
+
+    for (i = 0; i < gcmCOUNTOF(Database->list); i++)
+    {
+        gcsDATABASE_RECORD_PTR record = Database->list[i];
+
+        while (record != NULL)
+        {
+            _ShowRecord(File, record);
+            record = record->next;
+        }
+    }
+
+    return 0;
+}
+
+void
+_ShowCounters(
+    struct seq_file *File,
+    gcsDATABASE_PTR Database
+    );
+
+static void
+_ShowProcess(
+    IN struct seq_file *File,
+    IN gcsDATABASE_PTR Database
+    )
+{
+    gctINT pid;
+    gctUINT8 name[24];
+
+    /* Process ID and name */
+    pid = Database->processID;
+    gcmkVERIFY_OK(gckOS_ZeroMemory(name, gcmSIZEOF(name)));
+    gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name));
+
+    seq_printf(File, "--------------------------------------------------------------------------------\n");
+    seq_printf(File, "Process: %-8d %s\n", pid, name);
+
+    /* Detailed records */
+    _ShowRecords(File, Database);
+
+    seq_printf(File, "Counters:\n");
+
+    _ShowCounters(File, Database);
+}
+
+static void
+_ShowProcesses(
+    IN struct seq_file * file,
+    IN gckKERNEL Kernel
+    )
+{
+    gcsDATABASE_PTR database;
+    gctINT i;
+
+    /* Acquire the database mutex. */
+    gcmkVERIFY_OK(
+        gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+
+    /* Idle time since last call */
+    seq_printf(file, "GPU Idle: %llu ns\n",  Kernel->db->idleTime);
+    Kernel->db->idleTime = 0;
+
+    /* Walk the databases. */
+    for (i = 0; i < gcmCOUNTOF(Kernel->db->db); ++i)
+    {
+        for (database = Kernel->db->db[i];
+             database != gcvNULL;
+             database = database->next)
+        {
+            _ShowProcess(file, database);
+        }
+    }
+
+    /* Release the database mutex. */
+    gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex));
+}
+
+static int
+gc_db_show(struct seq_file *m, void *data)
+{
+    gcsINFO_NODE *node = m->private;
+    gckGALDEVICE device = node->device;
+    gckKERNEL kernel = _GetValidKernel(device);
+    _ShowProcesses(m, kernel);
+    return 0 ;
+}
+
+static int
+gc_version_show(struct seq_file *m, void *data)
+{
+    seq_printf(m, "%s\n",  gcvVERSION_STRING);
+
+    return 0 ;
+}
+
+int gc_idle_show(struct seq_file* m, void* data)
+{
+    gcsINFO_NODE *node = m->private;
+    gckGALDEVICE device = node->device;
+    gckKERNEL kernel = _GetValidKernel(device);
+    gcuDATABASE_INFO info;
+
+    gckKERNEL_QueryProcessDB(kernel, 0, gcvFALSE, gcvDB_IDLE, &info);
+
+    seq_printf(m, "GPU idle time since last query: %llu ns\n", info.time);
+
+    return 0;
+}
+
+static gcsINFO InfoList[] =
+{
+    {"info", gc_info_show},
+    {"clients", gc_clients_show},
+    {"meminfo", gc_meminfo_show},
+    {"idle", gc_idle_show},
+    {"database", gc_db_show},
+    {"version", gc_version_show},
+};
+
+static gceSTATUS
+_DebugfsInit(
+    IN gckGALDEVICE Device
+    )
+{
+    gceSTATUS status;
+
+    gckDEBUGFS_DIR dir = &Device->debugfsDir;
+
+    gcmkONERROR(gckDEBUGFS_DIR_Init(dir, gcvNULL, "gc"));
+
+    gcmkONERROR(gckDEBUGFS_DIR_CreateFiles(dir, InfoList, gcmCOUNTOF(InfoList), Device));
+
+    return gcvSTATUS_OK;
+
+OnError:
+    return status;
+}
+
+static void
+_DebugfsCleanup(
+    IN gckGALDEVICE Device
+    )
+{
+    gckDEBUGFS_DIR dir = &Device->debugfsDir;
+
+    if (Device->debugfsDir.root)
+    {
+        gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles(dir, InfoList, gcmCOUNTOF(InfoList)));
+
+        gckDEBUGFS_DIR_Deinit(dir);
+    }
+}
+
 
 /******************************************************************************\
 *************************** Memory Allocation Wrappers *************************
@@ -68,7 +450,7 @@ _AllocateMemory(
         Device->os, gcvFALSE, &Bytes, Physical, Logical
         ));
 
-    *PhysAddr = ((PLINUX_MDL)*Physical)->dmaHandle - Device->baseAddress;
+    *PhysAddr = ((PLINUX_MDL)*Physical)->dmaHandle;
 
     /* Success. */
     gcmkFOOTER_ARG(
@@ -78,38 +460,261 @@ _AllocateMemory(
 
     return gcvSTATUS_OK;
 
-OnError:
-    gcmkFOOTER();
-    return status;
+OnError:
+    gcmkFOOTER();
+    return status;
+}
+
+static gceSTATUS
+_FreeMemory(
+    IN gckGALDEVICE Device,
+    IN gctPOINTER 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 ******************************
+\******************************************************************************/
+#if gcdMULTI_GPU
+static irqreturn_t isrRoutine3D0(int irq, void *ctxt)
+{
+    gceSTATUS status;
+    gckGALDEVICE device;
+
+    device = (gckGALDEVICE) ctxt;
+
+    /* Call kernel interrupt notification. */
+    status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR],
+                              gcvCORE_3D_0_ID,
+                              gcvNOTIFY_INTERRUPT,
+                              gcvTRUE);
+
+    if (gcmIS_SUCCESS(status))
+    {
+        /* Wake up the threadRoutine to process events. */
+        device->dataReady3D[gcvCORE_3D_0_ID] = gcvTRUE;
+        wake_up_interruptible(&device->intrWaitQueue3D[gcvCORE_3D_0_ID]);
+
+        return IRQ_HANDLED;
+    }
+
+    return IRQ_NONE;
+}
+
+static int threadRoutine3D0(void *ctxt)
+{
+    gckGALDEVICE device = (gckGALDEVICE) ctxt;
+
+    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+                   "Starting isr Thread with extension=%p",
+                   device);
+
+    for (;;)
+    {
+        /* Sleep until being awaken by the interrupt handler. */
+        wait_event_interruptible(device->intrWaitQueue3D[gcvCORE_3D_0_ID],
+                                 device->dataReady3D[gcvCORE_3D_0_ID] == gcvTRUE);
+        device->dataReady3D[gcvCORE_3D_0_ID] = 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],
+                         gcvCORE_3D_0_ID,
+                         gcvNOTIFY_INTERRUPT,
+                         gcvFALSE);
+    }
+}
+
+#if gcdMULTI_GPU > 1
+static irqreturn_t isrRoutine3D1(int irq, void *ctxt)
+{
+    gceSTATUS status;
+    gckGALDEVICE device;
+
+    device = (gckGALDEVICE) ctxt;
+
+    /* Call kernel interrupt notification. */
+    status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR],
+                              gcvCORE_3D_1_ID,
+                              gcvNOTIFY_INTERRUPT,
+                              gcvTRUE);
+
+    if (gcmIS_SUCCESS(status))
+    {
+        /* Wake up the worker thread to process events. */
+        device->dataReady3D[gcvCORE_3D_1_ID] = gcvTRUE;
+        wake_up_interruptible(&device->intrWaitQueue3D[gcvCORE_3D_1_ID]);
+
+        return IRQ_HANDLED;
+    }
+
+    return IRQ_NONE;
+}
+
+static int threadRoutine3D1(void *ctxt)
+{
+    gckGALDEVICE device = (gckGALDEVICE) ctxt;
+
+    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+                   "Starting isr Thread with extension=%p",
+                   device);
+
+    for (;;)
+    {
+        /* Sleep until being awaken by the interrupt handler. */
+        wait_event_interruptible(device->intrWaitQueue3D[gcvCORE_3D_1_ID],
+                                 device->dataReady3D[gcvCORE_3D_1_ID] == gcvTRUE);
+        device->dataReady3D[gcvCORE_3D_1_ID] = 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],
+                         gcvCORE_3D_1_ID,
+                         gcvNOTIFY_INTERRUPT,
+                         gcvFALSE);
+    }
+}
+#endif
+#elif gcdMULTI_GPU_AFFINITY
+static irqreturn_t isrRoutine3D0(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))
+    {
+        up(&device->semas[gcvCORE_MAJOR]);
+
+        return IRQ_HANDLED;
+    }
+
+    return IRQ_NONE;
+}
+
+static int threadRoutine3D0(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 gcc 4.6 happye*/
+
+        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 isrRoutine3D1(int irq, void *ctxt)
+{
+    gceSTATUS status;
+    gckGALDEVICE device;
+
+    device = (gckGALDEVICE) ctxt;
+
+    /* Call kernel interrupt notification. */
+    status = gckKERNEL_Notify(device->kernels[gcvCORE_OCL], gcvNOTIFY_INTERRUPT, gcvTRUE);
+
+    if (gcmIS_SUCCESS(status))
+    {
+        up(&device->semas[gcvCORE_OCL]);
+
+        return IRQ_HANDLED;
+    }
+
+    return IRQ_NONE;
 }
 
-static gceSTATUS
-_FreeMemory(
-    IN gckGALDEVICE Device,
-    IN gctPOINTER Logical,
-    IN gctPHYS_ADDR Physical)
+static int threadRoutine3D1(void *ctxt)
 {
-    gceSTATUS status;
-
-    gcmkHEADER_ARG("Device=0x%x Logical=0x%x Physical=0x%x",
-                   Device, Logical, Physical);
+    gckGALDEVICE device = (gckGALDEVICE) ctxt;
 
-    gcmkVERIFY_ARGUMENT(Device != NULL);
+    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+                   "Starting isr Thread with extension=%p",
+                   device);
 
-    status = gckOS_FreeContiguous(
-        Device->os, Physical, Logical,
-        ((PLINUX_MDL) Physical)->numPages * PAGE_SIZE
-        );
+    for (;;)
+    {
+        static int down;
 
-    gcmkFOOTER();
-    return status;
-}
+        down = down_interruptible(&device->semas[gcvCORE_OCL]);
+        if (down); /*To make gcc 4.6 happye*/
 
+        if (device->killThread == gcvTRUE)
+        {
+            /* The daemon exits. */
+            while (!kthread_should_stop())
+            {
+                gckOS_Delay(device->os, 1);
+            }
 
+            return 0;
+        }
 
-/******************************************************************************\
-******************************* Interrupt Handler ******************************
-\******************************************************************************/
+        gckKERNEL_Notify(device->kernels[gcvCORE_OCL],
+                         gcvNOTIFY_INTERRUPT,
+                         gcvFALSE);
+    }
+}
+#else
 static irqreturn_t isrRoutine(int irq, void *ctxt)
 {
     gceSTATUS status;
@@ -122,8 +727,6 @@ static irqreturn_t isrRoutine(int irq, void *ctxt)
 
     if (gcmIS_SUCCESS(status))
     {
-        device->dataReadys[gcvCORE_MAJOR] = gcvTRUE;
-
         up(&device->semas[gcvCORE_MAJOR]);
 
         return IRQ_HANDLED;
@@ -146,7 +749,6 @@ static int threadRoutine(void *ctxt)
 
         down = down_interruptible(&device->semas[gcvCORE_MAJOR]);
         if (down); /*To make gcc 4.6 happye*/
-        device->dataReadys[gcvCORE_MAJOR] = gcvFALSE;
 
         if (device->killThread == gcvTRUE)
         {
@@ -159,9 +761,12 @@ static int threadRoutine(void *ctxt)
             return 0;
         }
 
-        gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvFALSE);
+        gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR],
+                         gcvNOTIFY_INTERRUPT,
+                         gcvFALSE);
     }
 }
+#endif
 
 static irqreturn_t isrRoutine2D(int irq, void *ctxt)
 {
@@ -171,12 +776,14 @@ static irqreturn_t isrRoutine2D(int irq, void *ctxt)
     device = (gckGALDEVICE) ctxt;
 
     /* Call kernel interrupt notification. */
-    status = gckKERNEL_Notify(device->kernels[gcvCORE_2D], gcvNOTIFY_INTERRUPT, gcvTRUE);
-
+    status = gckKERNEL_Notify(device->kernels[gcvCORE_2D],
+#if gcdMULTI_GPU
+                              0,
+#endif
+                              gcvNOTIFY_INTERRUPT,
+                              gcvTRUE);
     if (gcmIS_SUCCESS(status))
     {
-        device->dataReadys[gcvCORE_2D] = gcvTRUE;
-
         up(&device->semas[gcvCORE_2D]);
 
         return IRQ_HANDLED;
@@ -199,7 +806,6 @@ static int threadRoutine2D(void *ctxt)
 
         down = down_interruptible(&device->semas[gcvCORE_2D]);
         if (down); /*To make gcc 4.6 happye*/
-        device->dataReadys[gcvCORE_2D] = gcvFALSE;
 
         if (device->killThread == gcvTRUE)
         {
@@ -211,8 +817,12 @@ static int threadRoutine2D(void *ctxt)
 
             return 0;
         }
-
-        gckKERNEL_Notify(device->kernels[gcvCORE_2D], gcvNOTIFY_INTERRUPT, gcvFALSE);
+        gckKERNEL_Notify(device->kernels[gcvCORE_2D],
+#if gcdMULTI_GPU
+                         0,
+#endif
+                         gcvNOTIFY_INTERRUPT,
+                         gcvFALSE);
     }
 }
 
@@ -224,13 +834,13 @@ static irqreturn_t isrRoutineVG(int irq, void *ctxt)
 
     device = (gckGALDEVICE) ctxt;
 
-       /* Serve the interrupt. */
-       status = gckVGINTERRUPT_Enque(device->kernels[gcvCORE_VG]->vg->interrupt);
+    /* Serve the interrupt. */
+    status = gckVGINTERRUPT_Enque(device->kernels[gcvCORE_VG]->vg->interrupt);
 
-       /* Determine the return value. */
-       return (status == gcvSTATUS_NOT_OUR_INTERRUPT)
-               ? IRQ_RETVAL(0)
-               : IRQ_RETVAL(1);
+    /* Determine the return value. */
+    return (status == gcvSTATUS_NOT_OUR_INTERRUPT)
+        ? IRQ_RETVAL(0)
+        : IRQ_RETVAL(1);
 #else
     return IRQ_NONE;
 #endif
@@ -250,7 +860,6 @@ static int threadRoutineVG(void *ctxt)
 
         down = down_interruptible(&device->semas[gcvCORE_VG]);
         if (down); /*To make gcc 4.6 happye*/
-        device->dataReadys[gcvCORE_VG] = gcvFALSE;
 
         if (device->killThread == gcvTRUE)
         {
@@ -262,8 +871,12 @@ static int threadRoutineVG(void *ctxt)
 
             return 0;
         }
-
-        gckKERNEL_Notify(device->kernels[gcvCORE_VG], gcvNOTIFY_INTERRUPT, gcvFALSE);
+        gckKERNEL_Notify(device->kernels[gcvCORE_VG],
+#if gcdMULTI_GPU
+                         0,
+#endif
+                         gcvNOTIFY_INTERRUPT,
+                         gcvFALSE);
     }
 }
 
@@ -287,9 +900,18 @@ static int threadRoutineVG(void *ctxt)
 */
 gceSTATUS
 gckGALDEVICE_Construct(
+#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY
+    IN gctINT IrqLine3D0,
+    IN gctUINT32 RegisterMemBase3D0,
+    IN gctSIZE_T RegisterMemSize3D0,
+    IN gctINT IrqLine3D1,
+    IN gctUINT32 RegisterMemBase3D1,
+    IN gctSIZE_T RegisterMemSize3D1,
+#else
     IN gctINT IrqLine,
     IN gctUINT32 RegisterMemBase,
     IN gctSIZE_T RegisterMemSize,
+#endif
     IN gctINT IrqLine2D,
     IN gctUINT32 RegisterMemBase2D,
     IN gctSIZE_T RegisterMemSize2D,
@@ -305,9 +927,9 @@ gckGALDEVICE_Construct(
     IN gctUINT32 PhysSize,
     IN gctINT Signal,
     IN gctUINT LogFileSize,
-    IN struct device *pdev,
     IN gctINT PowerManagement,
     IN gctINT GpuProfiler,
+    IN gcsDEVICE_CONSTRUCT_ARGS * Args,
     OUT gckGALDEVICE *Device
     )
 {
@@ -320,10 +942,25 @@ gckGALDEVICE_Construct(
     gckGALDEVICE device;
     gceSTATUS status;
     gctINT32 i;
+#if gcdMULTI_GPU
+    gctINT32 j;
+#endif
     gceHARDWARE_TYPE type;
     gckDB sharedDB = gcvNULL;
     gckKERNEL kernel = gcvNULL;
 
+#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY
+    gcmkHEADER_ARG("IrqLine3D0=%d RegisterMemBase3D0=0x%08x RegisterMemSize3D0=%u "
+                   "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u "
+                   "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u "
+                   "ContiguousBase=0x%08x ContiguousSize=%lu BankSize=%lu "
+                   "FastClear=%d Compression=%d PhysBaseAddr=0x%x PhysSize=%d Signal=%d",
+                   IrqLine3D0, RegisterMemBase3D0, RegisterMemSize3D0,
+                   IrqLine2D, RegisterMemBase2D, RegisterMemSize2D,
+                   IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG,
+                   ContiguousBase, ContiguousSize, BankSize, FastClear, Compression,
+                   PhysBaseAddr, PhysSize, Signal);
+#else
     gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%u "
                    "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u "
                    "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u "
@@ -334,9 +971,15 @@ gckGALDEVICE_Construct(
                    IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG,
                    ContiguousBase, ContiguousSize, BankSize, FastClear, Compression,
                    PhysBaseAddr, PhysSize, Signal);
+#endif
+
+#if gcdDISABLE_CORES_2D3D
+    IrqLine = -1;
+    IrqLine2D = -1;
+#endif
 
     /* Allocate device structure. */
-    device = kmalloc(sizeof(struct _gckGALDEVICE), GFP_KERNEL);
+    device = kmalloc(sizeof(struct _gckGALDEVICE), GFP_KERNEL | __GFP_NOWARN);
 
     if (!device)
     {
@@ -345,179 +988,182 @@ gckGALDEVICE_Construct(
 
     memset(device, 0, sizeof(struct _gckGALDEVICE));
 
-   device->dbgnode = gcvNULL;
-   if(LogFileSize != 0)
-   {
-       if(gckDebugFileSystemCreateNode(LogFileSize,PARENT_FILE,DEBUG_FILE,&(device->dbgnode)) != 0)
-       {
-               gcmkTRACE_ZONE(
-               gcvLEVEL_ERROR, gcvZONE_DRIVER,
-               "%s(%d): Failed to create  the debug file system  %s/%s \n",
-               __FUNCTION__, __LINE__,
-               PARENT_FILE, DEBUG_FILE
-               );
-       }
-       else
-       {
-               /*Everything is OK*/
-               gckDebugFileSystemSetCurrentNode(device->dbgnode);
-       }
-    }
-#ifdef CONFIG_PM
-    /*Init runtime pm for gpu*/
-    pm_runtime_enable(pdev);
-    device->pmdev = pdev;
-#endif
+    device->dbgNode = gcvNULL;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
-    /*get gpu regulator*/
-    device->gpu_regulator = regulator_get(pdev, "cpu_vddgpu");
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
-    device->gpu_regulator = devm_regulator_get(pdev, "pu");
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) || LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
-    if (IS_ERR(device->gpu_regulator)) {
-       gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DRIVER,
-               "%s(%d): Failed to get gpu regulator  %s/%s \n",
-               __FUNCTION__, __LINE__,
-               PARENT_FILE, DEBUG_FILE);
-       gcmkONERROR(gcvSTATUS_NOT_FOUND);
+    device->platform = Args->platform;
+
+    gcmkONERROR(_DebugfsInit(device));
+
+    if (gckDEBUGFS_CreateNode(
+            device, LogFileSize, device->debugfsDir.root ,DEBUG_FILE, &(device->dbgNode)))
+    {
+        gcmkTRACE_ZONE(
+            gcvLEVEL_ERROR, gcvZONE_DRIVER,
+            "%s(%d): Failed to create  the debug file system  %s/%s \n",
+            __FUNCTION__, __LINE__,
+            PARENT_FILE, DEBUG_FILE
+        );
     }
-#endif
-    /*Initialize the clock structure*/
-    if (IrqLine != -1) {
-        device->clk_3d_core = clk_get(pdev, "gpu3d_clk");
-        if (!IS_ERR(device->clk_3d_core)) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
-            if (cpu_is_mx6q()) {
-                   device->clk_3d_shader = clk_get(pdev, "gpu3d_shader_clk");
-                   if (IS_ERR(device->clk_3d_shader)) {
-                       IrqLine = -1;
-                       clk_put(device->clk_3d_core);
-                       device->clk_3d_core = NULL;
-                       device->clk_3d_shader = NULL;
-                       gckOS_Print("galcore: clk_get gpu3d_shader_clk failed, disable 3d!\n");
-                   }
-                 }
-#else
-                   device->clk_3d_axi = clk_get(pdev, "gpu3d_axi_clk");
-                   device->clk_3d_shader = clk_get(pdev, "gpu3d_shader_clk");
-                   if (IS_ERR(device->clk_3d_shader)) {
-                       IrqLine = -1;
-                       clk_put(device->clk_3d_core);
-                       device->clk_3d_core = NULL;
-                       device->clk_3d_shader = NULL;
-                       gckOS_Print("galcore: clk_get gpu3d_shader_clk failed, disable 3d!\n");
-                   }
-#endif
-        } else {
-            IrqLine = -1;
-            device->clk_3d_core = NULL;
-            gckOS_Print("galcore: clk_get gpu3d_clk failed, disable 3d!\n");
-        }
-    }
-    if ((IrqLine2D != -1) || (IrqLineVG != -1)) {
-        device->clk_2d_core = clk_get(pdev, "gpu2d_clk");
-        if (IS_ERR(device->clk_2d_core)) {
-            IrqLine2D = -1;
-            IrqLineVG = -1;
-            device->clk_2d_core = NULL;
-            gckOS_Print("galcore: clk_get 2d core clock failed, disable 2d/vg!\n");
-        } else {
-           if (IrqLine2D != -1) {
-                device->clk_2d_axi = clk_get(pdev, "gpu2d_axi_clk");
-                if (IS_ERR(device->clk_2d_axi)) {
-                    device->clk_2d_axi = NULL;
-                    IrqLine2D = -1;
-                    gckOS_Print("galcore: clk_get 2d axi clock failed, disable 2d\n");
-                }
-            }
-            if (IrqLineVG != -1) {
-                device->clk_vg_axi = clk_get(pdev, "openvg_axi_clk");
-                if (IS_ERR(device->clk_vg_axi)) {
-                    IrqLineVG = -1;
-                       device->clk_vg_axi = NULL;
-                       gckOS_Print("galcore: clk_get vg clock failed, disable vg!\n");
-                }
-            }
-        }
+    else if (LogFileSize)
+    {
+        gckDEBUGFS_SetCurrentNode(device->dbgNode);
+    }
+
+#if gcdMULTI_GPU
+    if (IrqLine3D0 != -1)
+    {
+        device->requestedRegisterMemBase3D[gcvCORE_3D_0_ID] = RegisterMemBase3D0;
+        device->requestedRegisterMemSize3D[gcvCORE_3D_0_ID] = RegisterMemSize3D0;
+    }
+
+    if (IrqLine3D1 != -1)
+    {
+        device->requestedRegisterMemBase3D[gcvCORE_3D_1_ID] = RegisterMemBase3D1;
+        device->requestedRegisterMemSize3D[gcvCORE_3D_1_ID] = RegisterMemSize3D1;
+    }
+#elif gcdMULTI_GPU_AFFINITY
+    if (IrqLine3D0 != -1)
+    {
+        device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase3D0;
+        device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize3D0;
     }
 
+    if (IrqLine3D1 != -1)
+    {
+        device->requestedRegisterMemBases[gcvCORE_OCL] = RegisterMemBase3D1;
+        device->requestedRegisterMemSizes[gcvCORE_OCL] = RegisterMemSize3D1;
+    }
+#else
     if (IrqLine != -1)
     {
-        device->requestedRegisterMemBases[gcvCORE_MAJOR]    = RegisterMemBase;
-        device->requestedRegisterMemSizes[gcvCORE_MAJOR]    = RegisterMemSize;
+        device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase;
+        device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize;
     }
+#endif
 
     if (IrqLine2D != -1)
     {
-        device->requestedRegisterMemBases[gcvCORE_2D]       = RegisterMemBase2D;
-        device->requestedRegisterMemSizes[gcvCORE_2D]       = RegisterMemSize2D;
+        device->requestedRegisterMemBases[gcvCORE_2D] = RegisterMemBase2D;
+        device->requestedRegisterMemSizes[gcvCORE_2D] = RegisterMemSize2D;
     }
 
     if (IrqLineVG != -1)
     {
-        device->requestedRegisterMemBases[gcvCORE_VG]       = RegisterMemBaseVG;
-        device->requestedRegisterMemSizes[gcvCORE_VG]       = RegisterMemSizeVG;
+        device->requestedRegisterMemBases[gcvCORE_VG] = RegisterMemBaseVG;
+        device->requestedRegisterMemSizes[gcvCORE_VG] = RegisterMemSizeVG;
     }
 
     device->requestedContiguousBase  = 0;
     device->requestedContiguousSize  = 0;
 
-
     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
     {
-        physical = device->requestedRegisterMemBases[i];
+#if gcdMULTI_GPU
+        if (i == gcvCORE_MAJOR)
+        {
+            for (j = 0; j < gcdMULTI_GPU; j++)
+            {
+                physical = device->requestedRegisterMemBase3D[j];
+
+                /* Set up register memory region. */
+                if (physical != 0)
+                {
+                    mem_region = request_mem_region(physical,
+                            device->requestedRegisterMemSize3D[j],
+                            "galcore register region");
+
+                    if (mem_region == gcvNULL)
+                    {
+                        gcmkTRACE_ZONE(
+                                gcvLEVEL_ERROR, gcvZONE_DRIVER,
+                                "%s(%d): Failed to claim %lu bytes @ 0x%08X\n",
+                                __FUNCTION__, __LINE__,
+                                physical, device->requestedRegisterMemSize3D[j]
+                        );
+
+                        gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+                    }
+
+                    device->registerBase3D[j] = (gctPOINTER) ioremap_nocache(
+                            physical, device->requestedRegisterMemSize3D[j]);
+
+                    if (device->registerBase3D[j] == gcvNULL)
+                    {
+                        gcmkTRACE_ZONE(
+                                gcvLEVEL_ERROR, gcvZONE_DRIVER,
+                                "%s(%d): Unable to map %ld bytes @ 0x%08X\n",
+                                __FUNCTION__, __LINE__,
+                                physical, device->requestedRegisterMemSize3D[j]
+                        );
+
+                        gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+                    }
 
-        /* Set up register memory region. */
-        if (physical != 0)
+                    physical += device->requestedRegisterMemSize3D[j];
+                }
+                else
+                {
+                    device->registerBase3D[j] = gcvNULL;
+                }
+            }
+        }
+        else
+#endif
         {
-            mem_region = request_mem_region(
-                physical, device->requestedRegisterMemSizes[i], "galcore register region"
-                );
+            physical = device->requestedRegisterMemBases[i];
 
-            if (mem_region == gcvNULL)
+            /* Set up register memory region. */
+            if (physical != 0)
             {
-                gcmkTRACE_ZONE(
-                    gcvLEVEL_ERROR, gcvZONE_DRIVER,
-                    "%s(%d): Failed to claim %lu bytes @ 0x%08X\n",
-                    __FUNCTION__, __LINE__,
-                    physical, device->requestedRegisterMemSizes[i]
+                mem_region = request_mem_region(physical,
+                        device->requestedRegisterMemSizes[i],
+                        "galcore register region");
+
+                if (mem_region == gcvNULL)
+                {
+                    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);
-            }
+                    gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+                }
 
-            device->registerBases[i] = (gctPOINTER) ioremap_nocache(
-                physical, device->requestedRegisterMemSizes[i]);
+                device->registerBases[i] = (gctPOINTER) ioremap_nocache(
+                        physical, device->requestedRegisterMemSizes[i]);
 
-            if (device->registerBases[i] == gcvNULL)
-            {
-                gcmkTRACE_ZONE(
-                    gcvLEVEL_ERROR, gcvZONE_DRIVER,
-                    "%s(%d): Unable to map %ld bytes @ 0x%08X\n",
-                    __FUNCTION__, __LINE__,
-                    physical, device->requestedRegisterMemSizes[i]
+                if (device->registerBases[i] == gcvNULL)
+                {
+                    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);
-            }
+                    gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+                }
 
-            physical += device->requestedRegisterMemSizes[i];
-        }
-        else
-        {
-            device->registerBases[i] = gcvNULL;
+                physical += device->requestedRegisterMemSizes[i];
+            }
         }
     }
 
     /* Set the base address */
-    device->baseAddress = PhysBaseAddr;
+    device->baseAddress = device->physBase = PhysBaseAddr;
+    device->physSize = PhysSize;
+    device->mmu      = Args->mmu;
 
     /* Construct the gckOS object. */
     gcmkONERROR(gckOS_Construct(device, &device->os));
 
+#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY
+    if (IrqLine3D0 != -1)
+#else
     if (IrqLine != -1)
+#endif
     {
         /* Construct the gckKERNEL object. */
         gcmkONERROR(gckKERNEL_Construct(
@@ -544,14 +1190,42 @@ gckGALDEVICE_Construct(
             device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression
             ));
 
-        gcmkONERROR(gckHARDWARE_SetPowerManagement(
-            device->kernels[gcvCORE_MAJOR]->hardware, PowerManagement
+        if(PowerManagement != -1)
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_MAJOR]->hardware, PowerManagement
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE
+                ));
+        }
+        else
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE
+                ));
+        }
+
+#if gcdENABLE_FSCALE_VAL_ADJUST
+        gcmkONERROR(gckHARDWARE_SetMinFscaleValue(
+            device->kernels[gcvCORE_MAJOR]->hardware, Args->gpu3DMinClock
             ));
+#endif
 
         gcmkONERROR(gckHARDWARE_SetGpuProfiler(
             device->kernels[gcvCORE_MAJOR]->hardware, GpuProfiler
             ));
 
+        gcmkVERIFY_OK(gckKERNEL_SetRecovery(
+            device->kernels[gcvCORE_MAJOR], Args->recovery, Args->stuckDump
+            ));
+
 #if COMMAND_PROCESSOR_VERSION == 1
         /* Start the command queue. */
         gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command));
@@ -562,6 +1236,79 @@ gckGALDEVICE_Construct(
         device->kernels[gcvCORE_MAJOR] = gcvNULL;
     }
 
+#if gcdMULTI_GPU_AFFINITY
+    if (IrqLine3D1 != -1)
+    {
+        /* Construct the gckKERNEL object. */
+        gcmkONERROR(gckKERNEL_Construct(
+            device->os, gcvCORE_OCL, device,
+            gcvNULL, &device->kernels[gcvCORE_OCL]));
+
+        if (sharedDB == gcvNULL) sharedDB = device->kernels[gcvCORE_OCL]->db;
+
+        /* Initialize core mapping */
+        if (device->kernels[gcvCORE_MAJOR] == gcvNULL)
+        {
+            for (i = 0; i < 8; i++)
+            {
+                device->coreMapping[i] = gcvCORE_OCL;
+            }
+        }
+        else
+        {
+            device->coreMapping[gcvHARDWARE_OCL] = gcvCORE_OCL;
+        }
+
+        /* Setup the ISR manager. */
+        gcmkONERROR(gckHARDWARE_SetIsrManager(
+            device->kernels[gcvCORE_OCL]->hardware,
+            (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR,
+            (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR,
+            device
+            ));
+
+        gcmkONERROR(gckHARDWARE_SetFastClear(
+            device->kernels[gcvCORE_OCL]->hardware, FastClear, Compression
+            ));
+
+#if gcdENABLE_FSCALE_VAL_ADJUST
+        gcmkONERROR(gckHARDWARE_SetMinFscaleValue(
+            device->kernels[gcvCORE_OCL]->hardware, Args->gpu3DMinClock
+            ));
+#endif
+        if(PowerManagement != -1)
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_OCL]->hardware, gcvFALSE
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_OCL]->hardware, PowerManagement
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_OCL]->hardware, gcvTRUE
+                ));
+        }
+        else
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_OCL]->hardware, gcvFALSE
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_OCL]->hardware, gcvTRUE
+                ));
+        }
+
+#if COMMAND_PROCESSOR_VERSION == 1
+        /* Start the command queue. */
+        gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_OCL]->command));
+#endif
+    }
+    else
+    {
+        device->kernels[gcvCORE_OCL] = gcvNULL;
+    }
+#endif
+
     if (IrqLine2D != -1)
     {
         gcmkONERROR(gckKERNEL_Construct(
@@ -586,7 +1333,11 @@ gckGALDEVICE_Construct(
         }
 
         /* Initialize core mapping */
-        if (device->kernels[gcvCORE_MAJOR] == gcvNULL)
+        if (device->kernels[gcvCORE_MAJOR] == gcvNULL
+#if gcdMULTI_GPU_AFFINITY
+            && device->kernels[gcvCORE_OCL] == gcvNULL
+#endif
+            )
         {
             for (i = 0; i < 8; i++)
             {
@@ -606,10 +1357,37 @@ gckGALDEVICE_Construct(
             device
             ));
 
-        gcmkONERROR(gckHARDWARE_SetPowerManagement(
-            device->kernels[gcvCORE_2D]->hardware, PowerManagement
+        if(PowerManagement != -1)
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_2D]->hardware, gcvFALSE
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_2D]->hardware, PowerManagement
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_2D]->hardware, gcvTRUE
+                ));
+        }
+        else
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerManagementLock(
+                device->kernels[gcvCORE_2D]->hardware, gcvFALSE
+                ));
+            gcmkONERROR(gckHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_2D]->hardware, gcvTRUE
+                ));
+        }
+
+#if gcdENABLE_FSCALE_VAL_ADJUST
+        gcmkONERROR(gckHARDWARE_SetMinFscaleValue(
+            device->kernels[gcvCORE_2D]->hardware, 1
             ));
+#endif
 
+        gcmkVERIFY_OK(gckKERNEL_SetRecovery(
+            device->kernels[gcvCORE_2D], Args->recovery, Args->stuckDump
+            ));
 
 #if COMMAND_PROCESSOR_VERSION == 1
         /* Start the command queue. */
@@ -630,6 +1408,9 @@ gckGALDEVICE_Construct(
         /* Initialize core mapping */
         if (device->kernels[gcvCORE_MAJOR] == gcvNULL
             && device->kernels[gcvCORE_2D] == gcvNULL
+#if gcdMULTI_GPU_AFFINITY
+            && device->kernels[gcvCORE_OCL] == gcvNULL
+#endif
             )
         {
             for (i = 0; i < 8; i++)
@@ -642,11 +1423,21 @@ gckGALDEVICE_Construct(
             device->coreMapping[gcvHARDWARE_VG] = gcvCORE_VG;
         }
 
+        if(PowerManagement != -1)
+        {
+            gcmkONERROR(gckVGHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_VG]->vg->hardware,
+                PowerManagement
+                ));
+        }
+        else
+        {
+            gcmkONERROR(gckVGHARDWARE_SetPowerManagement(
+                device->kernels[gcvCORE_VG]->vg->hardware,
+                gcvTRUE
+                ));
+        }
 
-        gcmkONERROR(gckVGHARDWARE_SetPowerManagement(
-            device->kernels[gcvCORE_VG]->vg->hardware,
-            PowerManagement
-            ));
 
 #endif
     }
@@ -656,14 +1447,36 @@ gckGALDEVICE_Construct(
     }
 
     /* Initialize the ISR. */
+#if gcdMULTI_GPU
+    device->irqLine3D[gcvCORE_3D_0_ID] = IrqLine3D0;
+#if gcdMULTI_GPU > 1
+    device->irqLine3D[gcvCORE_3D_1_ID] = IrqLine3D1;
+#endif
+#elif gcdMULTI_GPU_AFFINITY
+    device->irqLines[gcvCORE_MAJOR] = IrqLine3D0;
+    device->irqLines[gcvCORE_OCL]   = IrqLine3D1;
+#else
     device->irqLines[gcvCORE_MAJOR] = IrqLine;
-    device->irqLines[gcvCORE_2D]    = IrqLine2D;
-    device->irqLines[gcvCORE_VG]    = IrqLineVG;
+#endif
+    device->irqLines[gcvCORE_2D] = IrqLine2D;
+    device->irqLines[gcvCORE_VG] = IrqLineVG;
 
     /* Initialize the kernel thread semaphores. */
     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
     {
-        if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0);
+#if gcdMULTI_GPU
+        if (i == gcvCORE_MAJOR)
+        {
+            for (j = 0; j < gcdMULTI_GPU; j++)
+            {
+                if (device->irqLine3D[j] != -1) init_waitqueue_head(&device->intrWaitQueue3D[j]);
+            }
+        }
+        else
+#endif
+        {
+            if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0);
+        }
     }
 
     device->signal = Signal;
@@ -674,9 +1487,9 @@ gckGALDEVICE_Construct(
     }
 
     if (i == gcdMAX_GPU_COUNT)
-       {
-               gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
-       }
+    {
+        gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+    }
 
 #if gcdENABLE_VG
     if (i == gcvCORE_VG)
@@ -718,10 +1531,26 @@ gckGALDEVICE_Construct(
     /* Grab the first availiable kernel */
     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
     {
-        if (device->irqLines[i] != -1)
+#if gcdMULTI_GPU
+        if (i == gcvCORE_MAJOR)
+        {
+            for (j = 0; j < gcdMULTI_GPU; j++)
+            {
+                if (device->irqLine3D[j] != -1)
+                {
+                    kernel = device->kernels[i];
+                    break;
+                }
+            }
+        }
+        else
+#endif
         {
-            kernel = device->kernels[i];
-            break;
+            if (device->irqLines[i] != -1)
+            {
+                kernel = device->kernels[i];
+                break;
+            }
         }
     }
 
@@ -862,45 +1691,28 @@ gckGALDEVICE_Construct(
             }
             else
             {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
-                mem_region = request_mem_region(
-                    ContiguousBase, ContiguousSize, "galcore managed memory"
-                    );
-
-                if (mem_region == gcvNULL)
+                if (Args->contiguousRequested == gcvFALSE)
                 {
-                    gcmkTRACE_ZONE(
-                        gcvLEVEL_ERROR, gcvZONE_DRIVER,
-                        "%s(%d): Failed to claim %ld bytes @ 0x%08X\n",
-                        __FUNCTION__, __LINE__,
-                        ContiguousSize, ContiguousBase
+                    mem_region = request_mem_region(
+                        ContiguousBase, ContiguousSize, "galcore managed memory"
                         );
 
-                    gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
-                }
-#endif
-
-                device->requestedContiguousBase  = ContiguousBase;
-                device->requestedContiguousSize  = ContiguousSize;
-
-#if !gcdDYNAMIC_MAP_RESERVED_MEMORY && gcdENABLE_VG
-                if (gcmIS_CORE_PRESENT(device, gcvCORE_VG))
-                {
-                    device->contiguousBase
-#if gcdPAGED_MEMORY_CACHEABLE
-                        = (gctPOINTER) ioremap_cached(ContiguousBase, ContiguousSize);
-#else
-                        = (gctPOINTER) ioremap_nocache(ContiguousBase, ContiguousSize);
-#endif
-                    if (device->contiguousBase == gcvNULL)
+                    if (mem_region == gcvNULL)
                     {
-                        device->contiguousVidMem = gcvNULL;
-                        device->contiguousSize = 0;
+                        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->contiguousRequested      = Args->contiguousRequested;
 
                 device->contiguousPhysical = gcvNULL;
                 device->contiguousPhysicalName = 0;
@@ -911,7 +1723,7 @@ gckGALDEVICE_Construct(
     }
 
     /* Return pointer to the device. */
-    * Device = device;
+    *Device = device;
 
     gcmkFOOTER_ARG("*Device=0x%x", * Device);
     return gcvSTATUS_OK;
@@ -947,7 +1759,9 @@ gckGALDEVICE_Destroy(
     gckGALDEVICE Device)
 {
     gctINT i;
-    gceSTATUS status = gcvSTATUS_OK;
+#if gcdMULTI_GPU
+    gctINT j;
+#endif
     gckKERNEL kernel = gcvNULL;
 
     gcmkHEADER_ARG("Device=0x%x", Device);
@@ -957,12 +1771,29 @@ gckGALDEVICE_Destroy(
         /* Grab the first availiable kernel */
         for (i = 0; i < gcdMAX_GPU_COUNT; i++)
         {
-            if (Device->irqLines[i] != -1)
+#if gcdMULTI_GPU
+            if (i == gcvCORE_MAJOR)
             {
-                kernel = Device->kernels[i];
-                break;
+                for (j = 0; j < gcdMULTI_GPU; j++)
+                {
+                    if (Device->irqLine3D[j] != -1)
+                    {
+                        kernel = Device->kernels[i];
+                        break;
+                    }
+                }
+            }
+            else
+#endif
+            {
+                if (Device->irqLines[i] != -1)
+                {
+                    kernel = Device->kernels[i];
+                    break;
+                }
             }
         }
+
         if (Device->internalPhysicalName != 0)
         {
             gcmRELEASE_NAME(Device->internalPhysicalName);
@@ -990,144 +1821,118 @@ gckGALDEVICE_Destroy(
             }
         }
 
+        if (Device->internalLogical != gcvNULL)
         {
-            if (Device->internalLogical != gcvNULL)
-            {
-                /* Unmap the internal memory. */
-                iounmap(Device->internalLogical);
-                Device->internalLogical = gcvNULL;
-            }
+            /* Unmap the internal memory. */
+            iounmap(Device->internalLogical);
+            Device->internalLogical = gcvNULL;
+        }
 
-            if (Device->internalVidMem != gcvNULL)
-            {
-                /* Destroy the internal heap. */
-                gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->internalVidMem));
-                Device->internalVidMem = gcvNULL;
-            }
+        if (Device->internalVidMem != gcvNULL)
+        {
+            /* Destroy the internal heap. */
+            gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->internalVidMem));
+            Device->internalVidMem = gcvNULL;
         }
 
+        if (Device->externalLogical != gcvNULL)
         {
-            if (Device->externalLogical != gcvNULL)
-            {
-                /* Unmap the external memory. */
-                iounmap(Device->externalLogical);
-                Device->externalLogical = gcvNULL;
-            }
+            /* Unmap the external memory. */
+            iounmap(Device->externalLogical);
+            Device->externalLogical = gcvNULL;
+        }
 
-            if (Device->externalVidMem != gcvNULL)
+        if (Device->externalVidMem != gcvNULL)
+        {
+            /* destroy the external heap */
+            gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem));
+            Device->externalVidMem = gcvNULL;
+        }
+
+        if (Device->contiguousBase != gcvNULL)
+        {
+            if (Device->contiguousMapped == gcvFALSE)
             {
-                /* destroy the external heap */
-                gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem));
-                Device->externalVidMem = gcvNULL;
+                gcmkVERIFY_OK(_FreeMemory(
+                    Device,
+                    Device->contiguousBase,
+                    Device->contiguousPhysical
+                    ));
             }
+
+            Device->contiguousBase     = gcvNULL;
+            Device->contiguousPhysical = gcvNULL;
         }
 
+        if (Device->requestedContiguousBase != 0
+         && Device->contiguousRequested == gcvFALSE
+        )
         {
-            if (Device->contiguousBase != gcvNULL)
-            {
-                if (Device->contiguousMapped)
-                {
-#if !gcdDYNAMIC_MAP_RESERVED_MEMORY && gcdENABLE_VG
-                    if (Device->contiguousBase)
-                    {
-                        /* Unmap the contiguous memory. */
-                        iounmap(Device->contiguousBase);
-                    }
-#endif
-                }
-                else
-                {
-                    gcmkONERROR(_FreeMemory(
-                        Device,
-                        Device->contiguousBase,
-                        Device->contiguousPhysical
-                        ));
-                }
+            release_mem_region(Device->requestedContiguousBase, Device->requestedContiguousSize);
+            Device->requestedContiguousBase = 0;
+            Device->requestedContiguousSize = 0;
+        }
 
-                Device->contiguousBase     = gcvNULL;
-                Device->contiguousPhysical = gcvNULL;
-            }
+        if (Device->contiguousVidMem != gcvNULL)
+        {
+            /* Destroy the contiguous heap. */
+            gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem));
+            Device->contiguousVidMem = gcvNULL;
+        }
 
-            if (Device->requestedContiguousBase != 0)
-            {
-                release_mem_region(Device->requestedContiguousBase, Device->requestedContiguousSize);
-                Device->requestedContiguousBase = 0;
-                Device->requestedContiguousSize = 0;
-            }
+        if (Device->dbgNode)
+        {
+            gckDEBUGFS_FreeNode(Device->dbgNode);
 
-            if (Device->contiguousVidMem != gcvNULL)
+            if(Device->dbgNode != gcvNULL)
             {
-                /* Destroy the contiguous heap. */
-                gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem));
-                Device->contiguousVidMem = gcvNULL;
+                kfree(Device->dbgNode);
+                Device->dbgNode = gcvNULL;
             }
         }
 
-       {
-           if(gckDebugFileSystemIsEnabled())
-           {
-                gckDebugFileSystemFreeNode(Device->dbgnode);
-                kfree(Device->dbgnode);
-                Device->dbgnode = gcvNULL;
-           }
-       }
-
         for (i = 0; i < gcdMAX_GPU_COUNT; i++)
         {
-            if (Device->registerBases[i] != gcvNULL)
+#if gcdMULTI_GPU
+            if (i == gcvCORE_MAJOR)
             {
-                /* Unmap register memory. */
-                iounmap(Device->registerBases[i]);
-                           if (Device->requestedRegisterMemBases[i] != 0)
-                           {
-                                   release_mem_region(Device->requestedRegisterMemBases[i], Device->requestedRegisterMemSizes[i]);
-                           }
-
-                Device->registerBases[i] = gcvNULL;
-                Device->requestedRegisterMemBases[i] = 0;
-                Device->requestedRegisterMemSizes[i] = 0;
+                for (j = 0; j < gcdMULTI_GPU; j++)
+                {
+                    if (Device->registerBase3D[j] != gcvNULL)
+                    {
+                        /* Unmap register memory. */
+                        iounmap(Device->registerBase3D[j]);
+                        if (Device->requestedRegisterMemBase3D[j] != 0)
+                        {
+                            release_mem_region(Device->requestedRegisterMemBase3D[j],
+                                    Device->requestedRegisterMemSize3D[j]);
+                        }
+
+                        Device->registerBase3D[j] = gcvNULL;
+                        Device->requestedRegisterMemBase3D[j] = 0;
+                        Device->requestedRegisterMemSize3D[j] = 0;
+                    }
+                }
             }
-        }
-
-        /*Disable clock*/
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
-        if (Device->clk_3d_axi) {
-           clk_put(Device->clk_3d_axi);
-           Device->clk_3d_axi = NULL;
-        }
-#endif
-        if (Device->clk_3d_core) {
-           clk_put(Device->clk_3d_core);
-           Device->clk_3d_core = NULL;
-        }
-        if (Device->clk_3d_shader) {
-           clk_put(Device->clk_3d_shader);
-           Device->clk_3d_shader = NULL;
-        }
-        if (Device->clk_2d_core) {
-           clk_put(Device->clk_2d_core);
-           Device->clk_2d_core = NULL;
-        }
-        if (Device->clk_2d_axi) {
-           clk_put(Device->clk_2d_axi);
-           Device->clk_2d_axi = NULL;
-        }
-        if (Device->clk_vg_axi) {
-           clk_put(Device->clk_vg_axi);
-           Device->clk_vg_axi = NULL;
-        }
-
-#ifdef CONFIG_PM
-        if(Device->pmdev)
-            pm_runtime_disable(Device->pmdev);
+            else
 #endif
+            {
+                if (Device->registerBases[i] != gcvNULL)
+                {
+                    /* Unmap register memory. */
+                    iounmap(Device->registerBases[i]);
+                    if (Device->requestedRegisterMemBases[i] != 0)
+                    {
+                        release_mem_region(Device->requestedRegisterMemBases[i],
+                                Device->requestedRegisterMemSizes[i]);
+                    }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
-        if (Device->gpu_regulator) {
-           regulator_put(Device->gpu_regulator);
-           Device->gpu_regulator = NULL;
+                    Device->registerBases[i] = gcvNULL;
+                    Device->requestedRegisterMemBases[i] = 0;
+                    Device->requestedRegisterMemSizes[i] = 0;
+                }
+            }
         }
-#endif
 
         /* Destroy the gckOS object. */
         if (Device->os != gcvNULL)
@@ -1136,16 +1941,14 @@ gckGALDEVICE_Destroy(
             Device->os = gcvNULL;
         }
 
+        _DebugfsCleanup(Device);
+
         /* Free the device. */
         kfree(Device);
     }
 
     gcmkFOOTER_NO();
     return gcvSTATUS_OK;
-
-OnError:
-    gcmkFOOTER();
-    return status;
 }
 
 /*******************************************************************************
@@ -1176,7 +1979,7 @@ gckGALDEVICE_Setup_ISR(
     )
 {
     gceSTATUS status;
-    gctINT ret;
+    gctINT ret = 0;
 
     gcmkHEADER_ARG("Device=0x%x", Device);
 
@@ -1198,12 +2001,94 @@ gckGALDEVICE_Setup_ISR(
     ret = dove_gpio_request(
         DOVE_GPIO0_7, &gc500_handle
         );
+#else
+#if gcdMULTI_GPU
+    ret = request_irq(
+        Device->irqLine3D[gcvCORE_3D_0_ID], isrRoutine3D0, IRQF_DISABLED,
+        "galcore_3d_0", Device
+        );
+
+    if (ret != 0)
+    {
+        gcmkTRACE_ZONE(
+            gcvLEVEL_ERROR, gcvZONE_DRIVER,
+            "%s(%d): Could not register irq line %d (error=%d)\n",
+            __FUNCTION__, __LINE__,
+            Device->irqLine3D[gcvCORE_3D_0_ID], ret
+            );
+
+        gcmkONERROR(gcvSTATUS_GENERIC_IO);
+    }
+
+    /* Mark ISR as initialized. */
+    Device->isrInitialized3D[gcvCORE_3D_0_ID] = gcvTRUE;
+
+#if gcdMULTI_GPU > 1
+    ret = request_irq(
+        Device->irqLine3D[gcvCORE_3D_1_ID], isrRoutine3D1, IRQF_DISABLED,
+        "galcore_3d_1", Device
+        );
+
+    if (ret != 0)
+    {
+        gcmkTRACE_ZONE(
+            gcvLEVEL_ERROR, gcvZONE_DRIVER,
+            "%s(%d): Could not register irq line %d (error=%d)\n",
+            __FUNCTION__, __LINE__,
+            Device->irqLine3D[gcvCORE_3D_1_ID], ret
+            );
+
+        gcmkONERROR(gcvSTATUS_GENERIC_IO);
+    }
+
+    /* Mark ISR as initialized. */
+    Device->isrInitialized3D[gcvCORE_3D_1_ID] = gcvTRUE;
+#endif
+#elif gcdMULTI_GPU_AFFINITY
+    ret = request_irq(
+        Device->irqLines[gcvCORE_MAJOR], isrRoutine3D0, IRQF_DISABLED,
+        "galcore_3d_0", 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;
+
+    ret = request_irq(
+        Device->irqLines[gcvCORE_OCL], isrRoutine3D1, IRQF_DISABLED,
+        "galcore_3d_1", 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_OCL], ret
+            );
+
+        gcmkONERROR(gcvSTATUS_GENERIC_IO);
+    }
+
+    /* Mark ISR as initialized. */
+    Device->isrInitializeds[gcvCORE_OCL] = gcvTRUE;
 #else
     ret = request_irq(
         Device->irqLines[gcvCORE_MAJOR], isrRoutine, IRQF_DISABLED,
         "galcore interrupt service", Device
         );
-#endif
 
     if (ret != 0)
     {
@@ -1219,6 +2104,8 @@ gckGALDEVICE_Setup_ISR(
 
     /* Mark ISR as initialized. */
     Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE;
+#endif
+#endif
 
     gcmkFOOTER_NO();
     return gcvSTATUS_OK;
@@ -1372,6 +2259,22 @@ gckGALDEVICE_Release_ISR(
 
     gcmkVERIFY_ARGUMENT(Device != NULL);
 
+#if gcdMULTI_GPU
+    /* release the irq */
+    if (Device->isrInitialized3D[gcvCORE_3D_0_ID])
+    {
+        free_irq(Device->irqLine3D[gcvCORE_3D_0_ID], Device);
+        Device->isrInitialized3D[gcvCORE_3D_0_ID] = gcvFALSE;
+    }
+#if gcdMULTI_GPU > 1
+    /* release the irq */
+    if (Device->isrInitialized3D[gcvCORE_3D_1_ID])
+    {
+        free_irq(Device->irqLine3D[gcvCORE_3D_1_ID], Device);
+        Device->isrInitialized3D[gcvCORE_3D_1_ID] = gcvFALSE;
+    }
+#endif
+#else
     /* release the irq */
     if (Device->isrInitializeds[gcvCORE_MAJOR])
     {
@@ -1380,9 +2283,9 @@ gckGALDEVICE_Release_ISR(
 #else
         free_irq(Device->irqLines[gcvCORE_MAJOR], Device);
 #endif
-
-           Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE;
+        Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE;
     }
+#endif
 
     gcmkFOOTER_NO();
     return gcvSTATUS_OK;
@@ -1406,7 +2309,7 @@ gckGALDEVICE_Release_ISR_2D(
         free_irq(Device->irqLines[gcvCORE_2D], Device);
 #endif
 
-           Device->isrInitializeds[gcvCORE_2D] = gcvFALSE;
+        Device->isrInitializeds[gcvCORE_2D] = gcvFALSE;
     }
 
     gcmkFOOTER_NO();
@@ -1431,7 +2334,7 @@ gckGALDEVICE_Release_ISR_VG(
         free_irq(Device->irqLines[gcvCORE_VG], Device);
 #endif
 
-           Device->isrInitializeds[gcvCORE_VG] = gcvFALSE;
+        Device->isrInitializeds[gcvCORE_VG] = gcvFALSE;
     }
 
     gcmkFOOTER_NO();
@@ -1472,6 +2375,86 @@ gckGALDEVICE_Start_Threads(
 
     gcmkVERIFY_ARGUMENT(Device != NULL);
 
+#if gcdMULTI_GPU
+    if (Device->kernels[gcvCORE_MAJOR] != gcvNULL)
+    {
+        /* Start the kernel thread. */
+        task = kthread_run(threadRoutine3D0, Device, "galcore_3d_0");
+
+        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->threadCtxt3D[gcvCORE_3D_0_ID]          = task;
+        Device->threadInitialized3D[gcvCORE_3D_0_ID]   = gcvTRUE;
+
+#if gcdMULTI_GPU > 1
+        /* Start the kernel thread. */
+        task = kthread_run(threadRoutine3D1, Device, "galcore_3d_1");
+
+        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->threadCtxt3D[gcvCORE_3D_1_ID]          = task;
+        Device->threadInitialized3D[gcvCORE_3D_1_ID]   = gcvTRUE;
+#endif
+    }
+#elif gcdMULTI_GPU_AFFINITY
+    if (Device->kernels[gcvCORE_MAJOR] != gcvNULL)
+    {
+        /* Start the kernel thread. */
+        task = kthread_run(threadRoutine3D0, Device, "galcore_3d_0");
+
+        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_OCL] != gcvNULL)
+    {
+        /* Start the kernel thread. */
+        task = kthread_run(threadRoutine3D1, Device, "galcore_3d_1");
+
+        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_OCL]          = task;
+        Device->threadInitializeds[gcvCORE_OCL]   = gcvTRUE;
+    }
+#else
     if (Device->kernels[gcvCORE_MAJOR] != gcvNULL)
     {
         /* Start the kernel thread. */
@@ -1491,6 +2474,7 @@ gckGALDEVICE_Start_Threads(
         Device->threadCtxts[gcvCORE_MAJOR]          = task;
         Device->threadInitializeds[gcvCORE_MAJOR]   = gcvTRUE;
     }
+#endif
 
     if (Device->kernels[gcvCORE_2D] != gcvNULL)
     {
@@ -1574,6 +2558,9 @@ gckGALDEVICE_Stop_Threads(
     )
 {
     gctINT i;
+#if gcdMULTI_GPU
+    gctINT j;
+#endif
 
     gcmkHEADER_ARG("Device=0x%x", Device);
 
@@ -1581,15 +2568,37 @@ gckGALDEVICE_Stop_Threads(
 
     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
     {
-        /* Stop the kernel threads. */
-        if (Device->threadInitializeds[i])
+#if gcdMULTI_GPU
+        if (i == gcvCORE_MAJOR)
+        {
+            for (j = 0; j < gcdMULTI_GPU; j++)
+            {
+                /* Stop the kernel threads. */
+                if (Device->threadInitialized3D[j])
+                {
+                    Device->killThread = gcvTRUE;
+                    Device->dataReady3D[j] = gcvTRUE;
+                    wake_up_interruptible(&Device->intrWaitQueue3D[j]);
+
+                    kthread_stop(Device->threadCtxt3D[j]);
+                    Device->threadCtxt3D[j]        = gcvNULL;
+                    Device->threadInitialized3D[j] = gcvFALSE;
+                }
+            }
+        }
+        else
+#endif
         {
-            Device->killThread = gcvTRUE;
-            up(&Device->semas[i]);
+            /* Stop the kernel threads. */
+            if (Device->threadInitializeds[i])
+            {
+                Device->killThread = gcvTRUE;
+                up(&Device->semas[i]);
 
-            kthread_stop(Device->threadCtxts[i]);
-            Device->threadCtxts[i]        = gcvNULL;
-            Device->threadInitializeds[i] = gcvFALSE;
+                kthread_stop(Device->threadCtxts[i]);
+                Device->threadCtxts[i]        = gcvNULL;
+                Device->threadInitializeds[i] = gcvFALSE;
+            }
         }
     }
 
@@ -1657,10 +2666,12 @@ gckGALDEVICE_Start(
         /* Setup the ISR routine. */
         gcmkONERROR(gckGALDEVICE_Setup_ISR_VG(Device));
 
+#if gcdENABLE_VG
         /* Switch to SUSPEND power state. */
         gcmkONERROR(gckVGHARDWARE_SetPowerManagementState(
             Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF_BROADCAST
             ));
+#endif
     }
 
     gcmkFOOTER_NO();