summaryrefslogtreecommitdiff
path: root/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c')
-rw-r--r--drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c2288
1 files changed, 2288 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
new file mode 100644
index 000000000000..c69dd5c9f18b
--- /dev/null
+++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
@@ -0,0 +1,2288 @@
+/****************************************************************************
+*
+* The MIT License (MIT)
+*
+* Copyright (c) 2014 - 2018 Vivante Corporation
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*
+*****************************************************************************
+*
+* The GPL License (GPL)
+*
+* Copyright (C) 2014 - 2018 Vivante Corporation
+*
+* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*****************************************************************************
+*
+* Note: This software is released under dual MIT and GPL licenses. A
+* recipient may use this file under the terms of either the MIT license or
+* GPL License. If you wish to use only one license not the other, you can
+* indicate your decision by deleting one of the above license notices in your
+* version of this file.
+*
+*****************************************************************************/
+
+
+#include "gc_hal_kernel_linux.h"
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mman.h>
+#include <linux/slab.h>
+
+#define _GC_OBJ_ZONE gcvZONE_DEVICE
+
+#define DEBUG_FILE "galcore_trace"
+#define PARENT_FILE "gpu"
+
+#define gcdDEBUG_FS_WARN "Experimental debug entry, may be removed in future release, do NOT rely on it!\n"
+
+static gckGALDEVICE galDevice;
+
+extern gcTA globalTA[16];
+
+/******************************************************************************\
+******************************** 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;
+ gctUINT32 productID = 0;
+ gctUINT32 ecoID = 0;
+
+ 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;
+ productID = device->kernels[i]->hardware->identity.productID;
+ ecoID = device->kernels[i]->hardware->identity.ecoID;
+ }
+
+ seq_printf(m, "gpu : %d\n", i);
+ seq_printf(m, "model : %4x\n", chipModel);
+ seq_printf(m, "revision : %4x\n", chipRevision);
+ seq_printf(m, "product : %4x\n", productID);
+ seq_printf(m, "eco : %4x\n", ecoID);
+ 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;
+ char 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_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, minFree = 0, maxUsed = 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;
+ minFree = memory->minFreeBytes;
+ used = memory->bytes - memory->freeBytes;
+ maxUsed = memory->bytes - memory->minFreeBytes;
+ 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, " MinFree : %10u B\n", minFree);
+ seq_printf(m, " MaxUsed : %10u B\n", maxUsed);
+ 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
+ )
+{
+ static const char * recordTypes[gcvDB_NUM_TYPES] = {
+ "Unknown",
+ "VideoMemory",
+ "CommandBuffer",
+ "NonPaged",
+ "Contiguous",
+ "Signal",
+ "VidMemLock",
+ "Context",
+ "Idel",
+ "MapMemory",
+ "MapUserMemory",
+ "ShBuf",
+ };
+
+ seq_printf(File, "%-14s %3d %16p %16zu %16zu\n",
+ recordTypes[Record->type],
+ Record->kernel->core,
+ Record->data,
+ (size_t) 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, "%14s %3s %16s %16s %16s\n",
+ "Type", "GPU", "Data/Node", "Physical/Node", "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;
+}
+
+static void
+_ShowCounters(
+ struct seq_file *File,
+ gcsDATABASE_PTR Database
+ )
+{
+ gctUINT i = 0;
+
+ static const char * surfaceTypes[gcvSURF_NUM_TYPES] = {
+ "Unknown",
+ "Index",
+ "Vertex",
+ "Texture",
+ "RenderTarget",
+ "Depth",
+ "Bitmap",
+ "TileStatus",
+ "Image",
+ "Mask",
+ "Scissor",
+ "HZ",
+ "ICache",
+ "TxDesc",
+ "Fence",
+ "TFBHeader",
+ };
+
+ static const char * poolTypes[gcvPOOL_NUMBER_OF_POOLS] = {
+ "Unknown",
+ "Default",
+ "Local",
+ "Internal",
+ "External",
+ "Unified",
+ "System",
+ "Virtual",
+ "User",
+ "Contiguous",
+ };
+
+ static const char * otherCounterNames[] = {
+ "AllocNonPaged",
+ "AllocContiguous",
+ "MapUserMemory",
+ "MapMemory",
+ };
+
+ gcsDATABASE_COUNTERS * otherCounters[] = {
+ &Database->nonPaged,
+ &Database->contiguous,
+ &Database->mapUserMemory,
+ &Database->mapMemory,
+ };
+
+ seq_printf(File, "%-16s %16s %16s %16s\n", "", "Current", "Maximum", "Total");
+
+ /* Print surface type counters. */
+ seq_printf(File, "%-16s %16lld %16lld %16lld\n",
+ "All-Types",
+ Database->vidMem.bytes,
+ Database->vidMem.maxBytes,
+ Database->vidMem.totalBytes);
+
+ for (i = 1; i < gcvSURF_NUM_TYPES; i++)
+ {
+ seq_printf(File, "%-16s %16lld %16lld %16lld\n",
+ surfaceTypes[i],
+ Database->vidMemType[i].bytes,
+ Database->vidMemType[i].maxBytes,
+ Database->vidMemType[i].totalBytes);
+ }
+ seq_puts(File, "\n");
+
+ /* Print surface pool counters. */
+ seq_printf(File, "%-16s %16lld %16lld %16lld\n",
+ "All-Pools",
+ Database->vidMem.bytes,
+ Database->vidMem.maxBytes,
+ Database->vidMem.totalBytes);
+
+ for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++)
+ {
+ seq_printf(File, "%-16s %16lld %16lld %16lld\n",
+ poolTypes[i],
+ Database->vidMemPool[i].bytes,
+ Database->vidMemPool[i].maxBytes,
+ Database->vidMemPool[i].totalBytes);
+ }
+ seq_puts(File, "\n");
+
+ /* Print other counters. */
+ for (i = 0; i < gcmCOUNTOF(otherCounterNames); i++)
+ {
+ seq_printf(File, "%-16s %16lld %16lld %16lld\n",
+ otherCounterNames[i],
+ otherCounters[i]->bytes,
+ otherCounters[i]->maxBytes,
+ otherCounters[i]->totalBytes);
+ }
+ seq_puts(File, "\n");
+}
+
+static void
+_ShowProcess(
+ IN struct seq_file *File,
+ IN gcsDATABASE_PTR Database
+ )
+{
+ gctINT pid;
+ char name[24];
+
+ /* Process ID and name */
+ pid = Database->processID;
+ 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;
+ static gctUINT64 idleTime = 0;
+
+ /* Acquire the database mutex. */
+ gcmkVERIFY_OK(
+ gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE));
+
+ if (Kernel->db->idleTime)
+ {
+ /* Record idle time if DB upated. */
+ idleTime = Kernel->db->idleTime;
+ Kernel->db->idleTime = 0;
+ }
+
+ /* Idle time since last call */
+ seq_printf(File, "GPU Idle: %llu ns\n", idleTime);
+
+ /* 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)
+{
+ gcsINFO_NODE *node = m->private;
+ gckGALDEVICE device = node->device;
+ gcsPLATFORM * platform = device->platform;
+
+ seq_printf(m, "%s built at %s\n", gcvVERSION_STRING, HOST);
+
+ if (platform->name)
+ {
+ seq_printf(m, "Platform path: %s\n", platform->name);
+ }
+ else
+ {
+ seq_printf(m, "Code path: %s\n", __FILE__);
+ }
+
+ return 0 ;
+}
+
+/*******************************************************************************
+**
+** Show PM state timer.
+**
+** Entry is called as 'idle' for compatible reason, it shows more information
+** than idle actually.
+**
+** Start: Start time of this counting period.
+** End: End time of this counting peroid.
+** On: Time GPU stays in gcvPOWER_0N.
+** Off: Time GPU stays in gcvPOWER_0FF.
+** Idle: Time GPU stays in gcvPOWER_IDLE.
+** Suspend: Time GPU stays in gcvPOWER_SUSPEND.
+*/
+static int
+gc_idle_show(struct seq_file *m, void *data)
+{
+ gcsINFO_NODE *node = m->private;
+ gckGALDEVICE device = node->device;
+ gckKERNEL kernel = _GetValidKernel(device);
+
+ gctUINT64 start;
+ gctUINT64 end;
+ gctUINT64 on;
+ gctUINT64 off;
+ gctUINT64 idle;
+ gctUINT64 suspend;
+
+ gckHARDWARE_QueryStateTimer(kernel->hardware, &start, &end, &on, &off, &idle, &suspend);
+
+ /* Idle time since last call */
+ seq_printf(m, "Start: %llu ns\n", start);
+ seq_printf(m, "End: %llu ns\n", end);
+ seq_printf(m, "On: %llu ns\n", on);
+ seq_printf(m, "Off: %llu ns\n", off);
+ seq_printf(m, "Idle: %llu ns\n", idle);
+ seq_printf(m, "Suspend: %llu ns\n", suspend);
+
+ return 0 ;
+}
+
+extern void
+_DumpState(
+ IN gckKERNEL Kernel
+ );
+
+/*******************************************************************************
+**
+** Show PM state timer.
+**
+** Entry is called as 'idle' for compatible reason, it shows more information
+** than idle actually.
+**
+** Start: Start time of this counting period.
+** End: End time of this counting peroid.
+** On: Time GPU stays in gcvPOWER_0N.
+** Off: Time GPU stays in gcvPOWER_0FF.
+** Idle: Time GPU stays in gcvPOWER_IDLE.
+** Suspend: Time GPU stays in gcvPOWER_SUSPEND.
+*/
+
+static int dumpCore = 0;
+
+static int
+gc_dump_trigger_show(struct seq_file *m, void *data)
+{
+#if gcdENABLE_3D || gcdENABLE_2D
+ gcsINFO_NODE *node = m->private;
+ gckGALDEVICE device = node->device;
+ gckKERNEL kernel = gcvNULL;
+
+ if (dumpCore >= gcvCORE_MAJOR && dumpCore < gcvCORE_COUNT)
+ {
+ kernel = device->kernels[dumpCore];
+ }
+#endif
+
+ seq_printf(m, gcdDEBUG_FS_WARN);
+
+#if gcdENABLE_3D || gcdENABLE_2D
+ seq_printf(m, "Get dump from /proc/kmsg or /sys/kernel/debug/gc/galcore_trace\n");
+
+ if (kernel && kernel->hardware->options.powerManagement == gcvFALSE)
+ {
+ _DumpState(kernel);
+ }
+#endif
+
+ return 0;
+}
+
+static int dumpProcess = 0;
+
+
+static int gc_vidmem_show(struct seq_file *m, void *unused)
+{
+ gceSTATUS status;
+ gcsDATABASE_PTR database;
+ gcsINFO_NODE *node = m->private;
+ gckGALDEVICE device = node->device;
+ char name[64];
+ int i;
+
+ gckKERNEL kernel = _GetValidKernel(device);
+
+ if (dumpProcess == 0)
+ {
+ /* Acquire the database mutex. */
+ gcmkVERIFY_OK(
+ gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE));
+
+ for (i = 0; i < gcmCOUNTOF(kernel->db->db); i++)
+ {
+ for (database = kernel->db->db[i];
+ database != gcvNULL;
+ database = database->next)
+ {
+ gckOS_GetProcessNameByPid(database->processID, gcmSIZEOF(name), name);
+ seq_printf(m, "VidMem Usage (Process %d: %s):\n", database->processID, name);
+ _ShowCounters(m, database);
+ seq_puts(m, "\n");
+ }
+ }
+
+ /* Release the database mutex. */
+ gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex));
+ }
+ else
+ {
+ /* Find the database. */
+ status = gckKERNEL_FindDatabase(kernel, dumpProcess, gcvFALSE, &database);
+
+ if (gcmIS_ERROR(status))
+ {
+ seq_printf(m, "ERROR: process %d not found\n", dumpProcess);
+ return 0;
+ }
+
+ gckOS_GetProcessNameByPid(dumpProcess, gcmSIZEOF(name), name);
+ seq_printf(m, "VidMem Usage (Process %d: %s):\n", dumpProcess, name);
+ _ShowCounters(m, database);
+ }
+
+ return 0;
+}
+
+static inline int strtoint_from_user(const char __user *s,
+ size_t count, int *res)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
+ int ret = kstrtoint_from_user(s, count, 10, res);
+
+ return ret < 0 ? ret : count;
+#else
+ /* sign, base 2 representation, newline, terminator */
+ char buf[1 + sizeof(long) * 8 + 1 + 1];
+
+ size_t len = min(count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, s, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+ *res = (int) simple_strtol(buf, NULL, 0);
+
+ return count;
+#endif
+}
+
+static int gc_vidmem_write(const char __user *buf, size_t count, void* data)
+{
+ return strtoint_from_user(buf, count, &dumpProcess);
+}
+
+static int gc_dump_trigger_write(const char __user *buf, size_t count, void* data)
+{
+ return strtoint_from_user(buf, count, &dumpCore);
+}
+
+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},
+ {"vidmem", gc_vidmem_show, gc_vidmem_write},
+ {"dump_trigger", gc_dump_trigger_show, gc_dump_trigger_write},
+};
+
+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 *************************
+\******************************************************************************/
+
+static gceSTATUS
+_AllocateMemory(
+ IN gckGALDEVICE Device,
+ IN gctSIZE_T Bytes,
+ OUT gctPOINTER *Logical,
+ OUT gctPHYS_ADDR *Physical,
+ OUT gctUINT32 *PhysAddr
+ )
+{
+ gceSTATUS status;
+ gctPHYS_ADDR_T physAddr;
+
+ 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
+ ));
+
+ gcmkONERROR(gckOS_GetPhysicalAddress(
+ Device->os, *Logical, &physAddr
+ ));
+
+ gcmkSAFECASTPHYSADDRT(*PhysAddr, physAddr);
+
+ /* 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 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;
+}
+
+static gceSTATUS
+_SetupVidMem(
+ IN gckGALDEVICE Device,
+ IN gctUINT32 ContiguousBase,
+ IN gctSIZE_T ContiguousSize,
+ IN gctSIZE_T BankSize,
+ IN gcsDEVICE_CONSTRUCT_ARGS * Args
+ )
+{
+ gceSTATUS status;
+ gctUINT32 physAddr = ~0U;
+ gckGALDEVICE device = Device;
+
+ /* set up the contiguous memory */
+ device->contiguousBase = ContiguousBase;
+ device->contiguousSize = ContiguousSize;
+
+ if (ContiguousSize > 0)
+ {
+ if (ContiguousBase == 0)
+ {
+ while (device->contiguousSize > 0)
+ {
+ /* Allocate contiguous memory. */
+ status = _AllocateMemory(
+ device,
+ device->contiguousSize,
+ &device->contiguousLogical,
+ &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))
+ {
+ device->contiguousVidMem->physical = device->contiguousPhysical;
+ device->contiguousBase = physAddr;
+ break;
+ }
+
+ gcmkONERROR(_FreeMemory(
+ device,
+ device->contiguousLogical,
+ device->contiguousPhysical
+ ));
+
+ device->contiguousLogical = gcvNULL;
+ device->contiguousPhysical = gcvNULL;
+ }
+
+ 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->systemMemoryBaseAddress,
+ ContiguousSize,
+ 64, BankSize,
+ &device->contiguousVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable contiguous memory pool. */
+ device->contiguousVidMem = gcvNULL;
+ device->contiguousSize = 0;
+ }
+ else
+ {
+ gcmkONERROR(gckOS_RequestReservedMemory(
+ device->os, ContiguousBase, ContiguousSize,
+ "galcore contiguous memory",
+ Args->contiguousRequested,
+ &device->contiguousPhysical
+ ));
+
+ device->contiguousVidMem->physical = device->contiguousPhysical;
+ device->requestedContiguousBase = ContiguousBase;
+ device->requestedContiguousSize = ContiguousSize;
+
+ device->contiguousPhysicalName = 0;
+ device->contiguousSize = ContiguousSize;
+ }
+ }
+ }
+
+ return gcvSTATUS_OK;
+OnError:
+ return status;
+}
+
+void
+_SetupRegisterPhysical(
+ IN gckGALDEVICE Device,
+ IN gcsDEVICE_CONSTRUCT_ARGS * Args
+ )
+{
+ gctINT *irqs = Args->irqs;
+ gctUINT *registerBases = Args->registerBases;
+ gctUINT *registerSizes = Args->registerSizes;
+
+ gctINT i = 0;
+
+ for (i = 0; i < gcvCORE_COUNT; i++)
+ {
+ if (irqs[i] != -1)
+ {
+ Device->requestedRegisterMemBases[i] = registerBases[i];
+ Device->requestedRegisterMemSizes[i] = registerSizes[i];
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, _GC_OBJ_ZONE,
+ "Get register base %llx of core %d",
+ registerBases[i], i);
+ }
+ }
+}
+
+/******************************************************************************\
+******************************* Interrupt Handler ******************************
+\******************************************************************************/
+static irqreturn_t isrRoutine(int irq, void *ctxt)
+{
+ gceSTATUS status;
+ gckGALDEVICE device;
+ gceCORE core = (gceCORE)gcmPTR2INT32(ctxt) - 1;
+
+ device = galDevice;
+
+ /* Call kernel interrupt notification. */
+ status = gckKERNEL_Notify(device->kernels[core], gcvNOTIFY_INTERRUPT, gcvTRUE);
+
+ if (gcmIS_SUCCESS(status))
+ {
+ up(&device->semas[core]);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int threadRoutine(void *ctxt)
+{
+ gckGALDEVICE device = galDevice;
+ gceCORE core = (gceCORE) gcmPTR2INT32(ctxt);
+ gctUINT i;
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
+ "Starting isr Thread with extension=%p",
+ device);
+
+ if (core != gcvCORE_VG)
+ {
+ /* Make kernel update page table of this thread to include entry related to command buffer.*/
+ for (i = 0; i < gcdCOMMAND_QUEUES; i++)
+ {
+ gctUINT32 data = *(gctUINT32_PTR)device->kernels[core]->command->queues[i].logical;
+
+ data = 0;
+ }
+ }
+
+ for (;;)
+ {
+ static int down;
+
+ down = down_interruptible(&device->semas[core]);
+ 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[core],
+ gcvNOTIFY_INTERRUPT,
+ gcvFALSE);
+ }
+}
+
+static irqreturn_t isrRoutineVG(int irq, void *ctxt)
+{
+#if gcdENABLE_VG
+ gceSTATUS status;
+ gckGALDEVICE device;
+
+ device = (gckGALDEVICE) ctxt;
+
+ /* 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);
+#else
+ return IRQ_NONE;
+#endif
+}
+
+/******************************************************************************\
+******************************* gckGALDEVICE Code ******************************
+\******************************************************************************/
+
+static gceSTATUS
+_StartThread(
+ IN int (*ThreadRoutine)(void *data),
+ IN gceCORE Core
+ )
+{
+ gceSTATUS status;
+ gckGALDEVICE device = galDevice;
+ struct task_struct * task;
+
+ if (device->kernels[Core] != gcvNULL)
+ {
+ /* Start the kernel thread. */
+ task = kthread_run(ThreadRoutine, (void *)Core, "galcore deamon thread for core[%d]", Core);
+
+ 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[Core] = task;
+ device->threadInitializeds[Core] = gcvTRUE;
+ }
+ else
+ {
+ device->threadInitializeds[Core] = gcvFALSE;
+ }
+
+ return gcvSTATUS_OK;
+
+OnError:
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_Construct
+**
+** Constructor.
+**
+** INPUT:
+**
+** OUTPUT:
+**
+** gckGALDEVICE * Device
+** Pointer to a variable receiving the gckGALDEVICE object pointer on
+** success.
+*/
+gceSTATUS
+gckGALDEVICE_Construct(
+ IN gctINT IrqLine,
+ IN gctUINT32 RegisterMemBase,
+ IN gctSIZE_T RegisterMemSize,
+ IN gctINT IrqLine2D,
+ IN gctUINT32 RegisterMemBase2D,
+ IN gctSIZE_T RegisterMemSize2D,
+ IN gctINT IrqLineVG,
+ IN gctUINT32 RegisterMemBaseVG,
+ IN gctSIZE_T RegisterMemSizeVG,
+ IN gctUINT32 ContiguousBase,
+ IN gctSIZE_T ContiguousSize,
+ IN gctUINT32 ExternalBase,
+ IN gctSIZE_T ExternalSize,
+ IN gctSIZE_T BankSize,
+ IN gctINT FastClear,
+ IN gctINT Compression,
+ IN gctUINT32 PhysBaseAddr,
+ IN gctUINT32 PhysSize,
+ IN gctINT Signal,
+ IN gctUINT LogFileSize,
+ IN gctINT PowerManagement,
+ IN gctINT GpuProfiler,
+ IN gcsDEVICE_CONSTRUCT_ARGS * Args,
+ OUT gckGALDEVICE *Device
+ )
+{
+ gctUINT32 internalBaseAddress = 0, internalAlignment = 0;
+ gctUINT32 externalAlignment = 0;
+ gctUINT32 physical;
+ gckGALDEVICE device;
+ gceSTATUS status;
+ gctINT32 i;
+ gceHARDWARE_TYPE type;
+ gckKERNEL kernel = gcvNULL;
+
+ gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%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",
+ IrqLine, RegisterMemBase, RegisterMemSize,
+ IrqLine2D, RegisterMemBase2D, RegisterMemSize2D,
+ IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG,
+ ContiguousBase, ContiguousSize, BankSize, FastClear, Compression,
+ PhysBaseAddr, PhysSize, Signal);
+
+#if !gcdENABLE_3D
+ IrqLine = -1;
+#endif
+
+#if !gcdENABLE_2D
+ IrqLine2D = -1;
+#endif
+ /* 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));
+
+ device->dbgNode = gcvNULL;
+
+ device->platform = Args->platform;
+
+ device->args = *Args;
+
+ /* set up the contiguous memory */
+ device->contiguousSize = ContiguousSize;
+
+ /* Clear irq lines. */
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ device->irqLines[i] = -1;
+ }
+
+ 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
+ );
+ }
+ else if (LogFileSize)
+ {
+ gckDEBUGFS_SetCurrentNode(device->dbgNode);
+ }
+
+ _SetupRegisterPhysical(device, Args);
+
+ 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;
+ }
+
+ if (IrqLineVG != -1)
+ {
+ device->requestedRegisterMemBases[gcvCORE_VG] = RegisterMemBaseVG;
+ device->requestedRegisterMemSizes[gcvCORE_VG] = RegisterMemSizeVG;
+ }
+#if gcdDEC_ENABLE_AHB
+ {
+ device->requestedRegisterMemBases[gcvCORE_DEC] = Args->registerMemBaseDEC300;
+ device->requestedRegisterMemSizes[gcvCORE_DEC] = Args->registerMemSizeDEC300;
+ }
+#endif
+
+
+ for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++)
+ {
+ if (Args->irqs[i] != -1)
+ {
+ device->requestedRegisterMemBases[i] = Args->registerBases[i];
+ device->requestedRegisterMemSizes[i] = Args->registerSizes[i];
+
+ gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DEVICE,
+ "%s(%d): Core = %d, RegiseterBase = %x",
+ __FUNCTION__, __LINE__,
+ i, Args->registerBases[i]
+ );
+ }
+ }
+
+ /* Initialize the ISR. */
+ device->irqLines[gcvCORE_MAJOR] = IrqLine;
+ device->irqLines[gcvCORE_2D] = IrqLine2D;
+ device->irqLines[gcvCORE_VG] = IrqLineVG;
+
+ for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++)
+ {
+ if (Args->irqs[i] != -1)
+ {
+ device->irqLines[i] = Args->irqs[i];
+ }
+ }
+
+ device->requestedContiguousBase = 0;
+ device->requestedContiguousSize = 0;
+
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ physical = device->requestedRegisterMemBases[i];
+
+ /* Set up register memory region. */
+ if (physical != 0)
+ {
+ if (Args->registerMemMapped)
+ {
+ device->registerBases[i] = Args->registerMemAddress;
+ device->requestedRegisterMemBases[i] = 0;
+
+ }
+ else
+ {
+#if USE_LINUX_PCIE
+ device->registerBases[i] = (gctPOINTER) pci_iomap(device->platform->device, 1,
+ device->requestedRegisterMemSizes[i]);
+#else
+ if (!request_mem_region(physical, device->requestedRegisterMemSizes[i], "galcore register region"))
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Failed to claim %lu bytes @ 0x%zx\n",
+ __FUNCTION__, __LINE__,
+ physical, device->requestedRegisterMemSizes[i]
+ );
+
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+ device->registerBases[i] = (gctPOINTER)ioremap_nocache(
+ physical, device->requestedRegisterMemSizes[i]);
+#endif
+
+ 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);
+ }
+ }
+
+ physical += device->requestedRegisterMemSizes[i];
+ }
+ }
+
+ /* Set the base address */
+ device->baseAddress = device->physBase = PhysBaseAddr;
+ device->physSize = PhysSize;
+
+ /* Construct the gckOS object. */
+ gcmkONERROR(gckOS_Construct(device, &device->os));
+
+ /* Construct the gckDEVICE object for os independent core management. */
+ gcmkONERROR(gckDEVICE_Construct(device->os, &device->device));
+
+ if (device->irqLines[gcvCORE_MAJOR] != -1)
+ {
+ gcmkONERROR(gctaOS_ConstructOS(device->os, &device->taos));
+ }
+
+ gcmkONERROR(_SetupVidMem(device, ContiguousBase, ContiguousSize, BankSize, Args));
+
+ /* Set external base and size */
+ device->externalBase = ExternalBase;
+ device->externalSize = ExternalSize;
+
+ if (device->irqLines[gcvCORE_MAJOR] != -1)
+ {
+ gcmkONERROR(gcTA_Construct(device->taos, gcvCORE_MAJOR, &globalTA[gcvCORE_MAJOR]));
+
+ gcmkONERROR(gckDEVICE_AddCore(device->device, gcvCORE_MAJOR, Args->chipIDs[gcvCORE_MAJOR], device, &device->kernels[gcvCORE_MAJOR]));
+
+ gcmkONERROR(gckHARDWARE_SetFastClear(
+ device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression
+ ));
+
+ gcmkONERROR(gckHARDWARE_SetPowerManagement(
+ device->kernels[gcvCORE_MAJOR]->hardware, PowerManagement
+ ));
+
+#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
+ ));
+ }
+ else
+ {
+ device->kernels[gcvCORE_MAJOR] = gcvNULL;
+ }
+
+ if (device->irqLines[gcvCORE_2D] != -1)
+ {
+ gcmkONERROR(gckDEVICE_AddCore(device->device, gcvCORE_2D, gcvCHIP_ID_DEFAULT, device, &device->kernels[gcvCORE_2D]));
+
+ /* 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);
+ }
+
+ gcmkONERROR(gckHARDWARE_SetPowerManagement(
+ device->kernels[gcvCORE_2D]->hardware, PowerManagement
+ ));
+
+#if gcdENABLE_FSCALE_VAL_ADJUST
+ gcmkONERROR(gckHARDWARE_SetMinFscaleValue(
+ device->kernels[gcvCORE_2D]->hardware, 1
+ ));
+#endif
+ }
+ else
+ {
+ device->kernels[gcvCORE_2D] = gcvNULL;
+ }
+
+ if (device->irqLines[gcvCORE_VG] != -1)
+ {
+#if gcdENABLE_VG
+ gcmkONERROR(gckDEVICE_AddCore(device->device, gcvCORE_VG, gcvCHIP_ID_DEFAULT, device, &device->kernels[gcvCORE_VG]));
+
+ gcmkONERROR(gckVGHARDWARE_SetPowerManagement(
+ device->kernels[gcvCORE_VG]->vg->hardware,
+ PowerManagement
+ ));
+#endif
+ }
+ else
+ {
+ device->kernels[gcvCORE_VG] = gcvNULL;
+ }
+
+ /* Add core for multiple core. */
+ for (i = gcvCORE_3D1; i <= gcvCORE_3D_MAX; i++)
+ {
+ if (Args->irqs[i] != -1)
+ {
+ gcmkONERROR(gcTA_Construct(device->taos, (gceCORE)i, &globalTA[i]));
+ gckDEVICE_AddCore(device->device, i, Args->chipIDs[i], device, &device->kernels[i]);
+
+ gcmkONERROR(
+ gckHARDWARE_SetFastClear(device->kernels[i]->hardware,
+ FastClear,
+ Compression));
+
+ gcmkONERROR(gckHARDWARE_SetPowerManagement(
+ device->kernels[i]->hardware, PowerManagement
+ ));
+
+ gcmkONERROR(gckHARDWARE_SetGpuProfiler(
+ device->kernels[i]->hardware, GpuProfiler
+ ));
+ }
+ }
+
+ /* Initialize the kernel thread semaphores. */
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0);
+ }
+
+ device->signal = Signal;
+
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ if (device->kernels[i] != gcvNULL) break;
+ }
+
+ if (i == gcdMAX_GPU_COUNT)
+ {
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+#if gcdENABLE_VG
+ if (i == gcvCORE_VG)
+ {
+ /* Query the ceiling of the system memory. */
+ gcmkONERROR(gckVGHARDWARE_QuerySystemMemory(
+ device->kernels[i]->vg->hardware,
+ &device->systemMemorySize,
+ &device->systemMemoryBaseAddress
+ ));
+ }
+ else
+#endif
+ {
+ /* Query the ceiling of the system memory. */
+ gcmkONERROR(gckHARDWARE_QuerySystemMemory(
+ device->kernels[i]->hardware,
+ &device->systemMemorySize,
+ &device->systemMemoryBaseAddress
+ ));
+ }
+
+ /* Grab the first availiable kernel */
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ if (device->irqLines[i] != -1)
+ {
+ kernel = device->kernels[i];
+ break;
+ }
+ }
+
+ /* 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
+ = (gctPOINTER) ioremap_nocache(physical, device->internalSize);
+
+ if (device->internalLogical == gcvNULL)
+ {
+ gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
+ }
+
+ device->internalPhysical = (gctPHYS_ADDR)(gctUINTPTR_T) physical;
+ physical += device->internalSize;
+ }
+ }
+
+ if (device->externalSize > 0)
+ {
+ /* create the external memory heap */
+ status = gckVIDMEM_Construct(
+ device->os,
+ device->externalBase, device->externalSize, externalAlignment,
+ 0, &device->externalVidMem
+ );
+
+ if (gcmIS_ERROR(status))
+ {
+ /* Error, disable external heap. */
+ device->externalSize = 0;
+ }
+ else
+ {
+ /* Map external memory. */
+ gcmkONERROR(gckOS_RequestReservedMemory(
+ device->os,
+ device->externalBase, device->externalSize,
+ "galcore external memory",
+ gcvTRUE,
+ &device->externalPhysical
+ ));
+ device->externalVidMem->physical = device->externalPhysical;
+ }
+ }
+
+ if (device->internalPhysical)
+ {
+ device->internalPhysicalName = gcmPTR_TO_NAME(device->internalPhysical);
+ }
+
+ if (device->externalPhysical)
+ {
+ device->externalPhysicalName = gcmPTR_TO_NAME(device->externalPhysical);
+ }
+
+ if (device->contiguousPhysical)
+ {
+ device->contiguousPhysicalName = gcmPTR_TO_NAME(device->contiguousPhysical);
+ }
+
+ /* Return pointer to the device. */
+ *Device = galDevice = 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)
+{
+ gctINT i;
+ gckKERNEL kernel = gcvNULL;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ if (Device != gcvNULL)
+ {
+ /* Grab the first availiable kernel */
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ if (Device->irqLines[i] != -1)
+ {
+ kernel = Device->kernels[i];
+ break;
+ }
+ }
+
+ if (Device->internalPhysicalName != 0)
+ {
+ gcmRELEASE_NAME(Device->internalPhysicalName);
+ Device->internalPhysicalName = 0;
+ }
+ if (Device->externalPhysicalName != 0)
+ {
+ gcmRELEASE_NAME(Device->externalPhysicalName);
+ Device->externalPhysicalName = 0;
+ }
+ if (Device->contiguousPhysicalName != 0)
+ {
+ gcmRELEASE_NAME(Device->contiguousPhysicalName);
+ Device->contiguousPhysicalName = 0;
+ }
+
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ if (Device->kernels[i] != gcvNULL)
+ {
+ Device->kernels[i] = gcvNULL;
+ }
+ }
+
+ if (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->externalPhysical != gcvNULL)
+ {
+ gckOS_ReleaseReservedMemory(
+ Device->os,
+ Device->externalPhysical
+ );
+ }
+
+ if (Device->externalLogical != gcvNULL)
+ {
+ Device->externalLogical = gcvNULL;
+ }
+
+ if (Device->externalVidMem != gcvNULL)
+ {
+ /* destroy the external heap */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem));
+ Device->externalVidMem = gcvNULL;
+ }
+
+ if (Device->contiguousPhysical != gcvNULL)
+ {
+ if (Device->requestedContiguousBase == 0)
+ {
+ gcmkVERIFY_OK(_FreeMemory(
+ Device,
+ Device->contiguousLogical,
+ Device->contiguousPhysical
+ ));
+ }
+ else
+ {
+ gckOS_ReleaseReservedMemory(
+ Device->os,
+ Device->contiguousPhysical
+ );
+
+ Device->requestedContiguousBase = 0;
+ Device->requestedContiguousSize = 0;
+ }
+
+ Device->contiguousLogical = gcvNULL;
+ Device->contiguousPhysical = gcvNULL;
+ }
+
+ if (Device->contiguousVidMem != gcvNULL)
+ {
+ /* Destroy the contiguous heap. */
+ gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem));
+ Device->contiguousVidMem = gcvNULL;
+ }
+
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ if (Device->registerBases[i])
+ {
+ /* Unmap register memory. */
+ if (Device->requestedRegisterMemBases[i] != 0)
+ {
+#if USE_LINUX_PCIE
+ pci_iounmap(Device->platform->device, Device->registerBases[i]);
+#else
+
+ iounmap(Device->registerBases[i]);
+ release_mem_region(Device->requestedRegisterMemBases[i],
+ Device->requestedRegisterMemSizes[i]);
+#endif
+ }
+
+ Device->registerBases[i] = gcvNULL;
+ Device->requestedRegisterMemBases[i] = 0;
+ Device->requestedRegisterMemSizes[i] = 0;
+ }
+ }
+
+ if (Device->device)
+ {
+ gcmkVERIFY_OK(gckDEVICE_Destroy(Device->os, Device->device));
+
+ for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+ {
+ if (globalTA[i])
+ {
+ gcTA_Destroy(globalTA[i]);
+ globalTA[i] = gcvNULL;
+ }
+ }
+
+ Device->device = gcvNULL;
+ }
+
+ if (Device->taos)
+ {
+ gcmkVERIFY_OK(gctaOS_DestroyOS(Device->taos));
+ Device->taos = gcvNULL;
+ }
+
+ /* Destroy the gckOS object. */
+ if (Device->os != gcvNULL)
+ {
+ gcmkVERIFY_OK(gckOS_Destroy(Device->os));
+ Device->os = gcvNULL;
+ }
+
+ if (Device->dbgNode)
+ {
+ gckDEBUGFS_FreeNode(Device->dbgNode);
+
+ if(Device->dbgNode != gcvNULL)
+ {
+ kfree(Device->dbgNode);
+ Device->dbgNode = gcvNULL;
+ }
+ }
+
+ _DebugfsCleanup(Device);
+
+ /* Free the device. */
+ kfree(Device);
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+static const char *isrNames[] =
+{
+ "galcore:0",
+ "galcore:3d-1",
+ "galcore:3d-2",
+ "galcore:3d-3",
+ "galcore:3d-4",
+ "galcore:3d-5",
+ "galcore:3d-6",
+ "galcore:3d-7",
+ "galcore:2d",
+ "galcore:vg",
+#if gcdDEC_ENABLE_AHB
+ "galcore:dec"
+#endif
+};
+
+/*******************************************************************************
+**
+** 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.
+*/
+gceSTATUS
+gckGALDEVICE_Setup_ISR(
+ IN gceCORE Core
+ )
+{
+ gceSTATUS status;
+ gctINT ret = 0;
+ gckGALDEVICE Device = galDevice;
+
+ gcmkHEADER_ARG("Device=0x%x Core=%d", Device, Core);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->irqLines[Core] < 0)
+ {
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4))
+ {
+ _Static_assert(gcvCORE_COUNT == gcmCOUNTOF(isrNames),
+ "Core count is lager than isrNames size");
+ }
+#endif
+
+ /* Hook up the isr based on the irq line. */
+ ret = request_irq(
+ Device->irqLines[Core], isrRoutine, gcdIRQF_FLAG,
+ isrNames[Core], (void *)(uintptr_t)(Core + 1)
+ );
+
+ if (ret != 0)
+ {
+ gcmkTRACE_ZONE(
+ gcvLEVEL_ERROR, gcvZONE_DRIVER,
+ "%s(%d): Could not register irq line %d (error=%d)\n",
+ __FUNCTION__, __LINE__,
+ Device->irqLines[Core], ret
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Mark ISR as initialized. */
+ Device->isrInitializeds[Core] = gcvTRUE;
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+gceSTATUS
+gckGALDEVICE_Setup_ISR_VG(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ gctINT ret;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ if (Device->irqLines[gcvCORE_VG] < 0)
+ {
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Hook up the isr based on the irq line. */
+ ret = request_irq(
+ Device->irqLines[gcvCORE_VG], isrRoutineVG, gcdIRQF_FLAG,
+ isrNames[gcvCORE_VG], 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_VG], ret
+ );
+
+ gcmkONERROR(gcvSTATUS_GENERIC_IO);
+ }
+
+ /* Mark ISR as initialized. */
+ Device->isrInitializeds[gcvCORE_VG] = 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.
+*/
+gceSTATUS
+gckGALDEVICE_Release_ISR(
+ IN gceCORE Core
+ )
+{
+ gckGALDEVICE Device = galDevice;
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ /* release the irq */
+ if (Device->isrInitializeds[Core])
+ {
+ free_irq(Device->irqLines[Core], (void *)(uintptr_t)(Core + 1));
+ Device->isrInitializeds[Core] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+gceSTATUS
+gckGALDEVICE_Release_ISR_VG(
+ IN gckGALDEVICE Device
+ )
+{
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ /* release the irq */
+ if (Device->isrInitializeds[gcvCORE_VG])
+ {
+ free_irq(Device->irqLines[gcvCORE_VG], Device);
+ Device->isrInitializeds[gcvCORE_VG] = gcvFALSE;
+ }
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+}
+
+/*******************************************************************************
+**
+** 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.
+*/
+gceSTATUS
+gckGALDEVICE_Start_Threads(
+ IN gckGALDEVICE Device
+ )
+{
+ gceSTATUS status;
+ gctUINT i;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ gcmkONERROR(_StartThread(threadRoutine, gcvCORE_MAJOR));
+ gcmkONERROR(_StartThread(threadRoutine, gcvCORE_2D));
+
+ gcmkONERROR(_StartThread(threadRoutine, gcvCORE_VG));
+
+ for (i = gcvCORE_3D1; i <= gcvCORE_3D_MAX; i++)
+ {
+ gcmkONERROR(_StartThread(threadRoutine, i));
+ }
+
+ 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.
+*/
+gceSTATUS
+gckGALDEVICE_Stop_Threads(
+ gckGALDEVICE Device
+ )
+{
+ gctINT i;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ for (i = 0; i < gcdMAX_GPU_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] = gcvNULL;
+ 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;
+ gctUINT i;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ /* Start the kernel thread. */
+ gcmkONERROR(gckGALDEVICE_Start_Threads(Device));
+
+ for (i = 0; i < gcvCORE_COUNT; i++)
+ {
+ if (i == gcvCORE_VG)
+ {
+ continue;
+ }
+
+ if (Device->kernels[i] != gcvNULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Setup_ISR(i));
+
+ /* Switch to SUSPEND power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[i]->hardware, gcvPOWER_OFF_BROADCAST
+ ));
+ }
+ }
+
+ if (Device->kernels[gcvCORE_VG] != gcvNULL)
+ {
+ /* 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();
+ 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;
+ gctUINT i;
+
+ gcmkHEADER_ARG("Device=0x%x", Device);
+
+ gcmkVERIFY_ARGUMENT(Device != NULL);
+
+ for (i = 0; i < gcvCORE_COUNT; i++)
+ {
+ if (i == gcvCORE_VG)
+ {
+ continue;
+ }
+
+ if (Device->kernels[i] != gcvNULL)
+ {
+ gcmkONERROR(gckHARDWARE_SetPowerManagement(
+ Device->kernels[i]->hardware, gcvTRUE
+ ));
+
+ /* Switch to OFF power state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[i]->hardware, gcvPOWER_OFF
+ ));
+
+ /* Remove the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Release_ISR(i));
+ }
+ }
+
+ if (Device->kernels[gcvCORE_VG] != gcvNULL)
+ {
+ /* Setup the ISR routine. */
+ gcmkONERROR(gckGALDEVICE_Release_ISR_VG(Device));
+
+#if gcdENABLE_VG
+ /* Switch to OFF power state. */
+ gcmkONERROR(gckVGHARDWARE_SetPowerManagementState(
+ Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF
+ ));
+#endif
+ }
+
+ /* Stop the kernel thread. */
+ gcmkONERROR(gckGALDEVICE_Stop_Threads(Device));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return status;
+}
+
+/*******************************************************************************
+**
+** gckGALDEVICE_AddCore
+**
+** Add a core after gckGALDevice is constructed.
+**
+** INPUT:
+**
+** OUTPUT:
+**
+*/
+gceSTATUS
+gckGALDEVICE_AddCore(
+ IN gckGALDEVICE Device,
+ IN gcsDEVICE_CONSTRUCT_ARGS * Args
+ )
+{
+ gceSTATUS status;
+ gceCORE core = gcvCORE_COUNT;
+ gctUINT i = 0;
+
+ gcmkHEADER();
+ gcmkVERIFY_ARGUMENT(Device != gcvNULL);
+
+ /* Find which core is added. */
+ for (i = 0; i < gcvCORE_COUNT; i++)
+ {
+ if (Args->irqs[i] != -1)
+ {
+ core = i;
+ break;
+ }
+ }
+
+ if (i == gcvCORE_COUNT)
+ {
+ gcmkPRINT("[galcore]: No valid core information found");
+ gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
+ }
+
+
+ gcmkPRINT("[galcore]: add core[%d]", core);
+
+ /* Record irq, registerBase, registerSize. */
+ Device->irqLines[core] = Args->irqs[core];
+ _SetupRegisterPhysical(Device, Args);
+
+ /* Map register memory.*/
+
+ /* Add a platform indepedent framework. */
+ gcmkONERROR(gckDEVICE_AddCore(
+ Device->device,
+ core,
+ Args->chipIDs[core],
+ Device,
+ &Device->kernels[core]
+ ));
+
+ /* Start thread routine. */
+ _StartThread(threadRoutine, core);
+
+ /* Register ISR. */
+ gckGALDEVICE_Setup_ISR(core);
+
+ /* Set default power management state. */
+ gcmkONERROR(gckHARDWARE_SetPowerManagementState(
+ Device->kernels[core]->hardware, gcvPOWER_OFF_BROADCAST
+ ));
+
+ gcmkFOOTER_NO();
+ return gcvSTATUS_OK;
+
+OnError:
+ gcmkFOOTER();
+ return gcvSTATUS_OK;
+}
+