| /**************************************************************************** |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2014 - 2020 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 - 2020 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_vxworks.h" |
| #include "gc_hal_kernel_allocator.h" |
| |
| #define _GC_OBJ_ZONE gcvZONE_DEVICE |
| |
| static gckGALDEVICE galDevice; |
| |
| extern gcTA globalTA[16]; |
| |
| /******************************************************************************\ |
| *************************** 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_AllocateNonPagedMemory( |
| Device->os, gcvFALSE, gcvALLOC_FLAG_CONTIGUOUS, &Bytes, Physical, Logical |
| )); |
| |
| gcmkONERROR(gckOS_GetPhysicalFromHandle( |
| Device->os, *Physical, 0, &physAddr |
| )); |
| |
| *PhysAddr = physAddr; |
| |
| OnError: |
| gcmkFOOTER_ARG( |
| "*Logical=%p *Physical=%p *PhysAddr=0x%llx", |
| gcmOPT_POINTER(Logical), gcmOPT_POINTER(Physical), gcmOPT_VALUE(PhysAddr) |
| ); |
| |
| 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_FreeNonPagedMemory( |
| Device->os, Physical, Logical, |
| ((PVX_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)) |
| { |
| gckALLOCATOR allocator = ((PVX_MDL)device->contiguousPhysical)->allocator; |
| device->contiguousVidMem->capability = allocator->capability | gcvALLOC_FLAG_MEMLIMIT; |
| 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 |
| { |
| gckALLOCATOR allocator; |
| |
| gcmkONERROR(gckOS_RequestReservedMemory( |
| device->os, ContiguousBase, ContiguousSize, |
| "galcore contiguous memory", |
| Args->contiguousRequested, |
| &device->contiguousPhysical |
| )); |
| |
| allocator = ((PVX_MDL)device->contiguousPhysical)->allocator; |
| device->contiguousVidMem->capability = allocator->capability | gcvALLOC_FLAG_MEMLIMIT; |
| device->contiguousVidMem->physical = device->contiguousPhysical; |
| device->requestedContiguousBase = ContiguousBase; |
| device->requestedContiguousSize = ContiguousSize; |
| |
| device->contiguousPhysName = 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 void isrRoutine(void *ctxt) |
| { |
| gceSTATUS status; |
| gckGALDEVICE device; |
| gceCORE core = (gceCORE)gcmPTR2INT32(ctxt); |
| |
| device = galDevice; |
| |
| /* Call kernel interrupt notification. */ |
| status = gckHARDWARE_Interrupt(device->kernels[core]->hardware); |
| |
| if (gcmIS_SUCCESS(status)) |
| { |
| semGive(device->semas[core]); |
| } |
| } |
| |
| 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 (;;) |
| { |
| semTake(device->semas[core], -1); |
| |
| if (device->killThread == gcvTRUE) |
| { |
| gckOS_Delay(device->os, 1); |
| return 0; |
| } |
| |
| gckKERNEL_Notify(device->kernels[core], gcvNOTIFY_INTERRUPT); |
| } |
| } |
| |
| static void isrRoutineVG(int irq, void *ctxt) |
| { |
| } |
| |
| /******************************************************************************\ |
| ******************************* gckGALDEVICE Code ****************************** |
| \******************************************************************************/ |
| |
| static gceSTATUS |
| _StartThread( |
| IN int (*ThreadRoutine)(void *data), |
| IN gceCORE Core |
| ) |
| { |
| gckGALDEVICE device = galDevice; |
| int taskId = 0; |
| |
| if (device->kernels[Core] != gcvNULL) |
| { |
| |
| taskId = taskSpawn("galcore", 102, 8, 0x200000, |
| (FUNCPTR )ThreadRoutine, (void *)Core, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0); |
| |
| if(taskId != 0) |
| { |
| printf("galcoreid=0x%x\n",taskId); |
| } |
| else |
| { |
| return gcvSTATUS_FALSE; |
| } |
| |
| device->threadCtxts[Core] = taskId; |
| device->threadInitializeds[Core] = gcvTRUE; |
| |
| } |
| else |
| { |
| device->threadInitializeds[Core] = gcvFALSE; |
| return gcvSTATUS_FALSE; |
| } |
| |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckGALDEVICE_Construct |
| ** |
| ** Constructor. |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| ** gckGALDEVICE * Device |
| ** Pointer to a variable receiving the gckGALDEVICE object pointer on |
| ** success. |
| */ |
| gceSTATUS |
| gckGALDEVICE_Construct( |
| IN 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 |
| |
| IrqLine2D = -1; |
| /* Allocate device structure. */ |
| device = (gckGALDEVICE) malloc(sizeof(struct _gckGALDEVICE)); |
| if (!device) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| memset(device, 0, sizeof(struct _gckGALDEVICE)); |
| |
| device->platform = Args->platform; |
| |
| device->args = *Args; |
| |
| /* set up the contiguous memory */ |
| device->contiguousSize = ContiguousSize; |
| |
| /* Clear irq lines. */ |
| for (i = 0; i < gcvCORE_COUNT; i++) |
| { |
| device->irqLines[i] = -1; |
| } |
| |
| _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 < gcdMAX_GPU_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->irqLines[i] != -1) |
| { |
| device->registerBases[i] = Args->registerMemAddress; |
| device->requestedRegisterMemBases[i] = 0; |
| } |
| else |
| { |
| /* Should map register region */ |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| 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 gckOS object. */ |
| /* Construct the gckDEVICE object for os independent core management. */ |
| gcmkONERROR(gckDEVICE_Construct(device->os, &device->device)); |
| |
| /* Construct the gckOS object. */ |
| |
| if (device->irqLines[gcvCORE_MAJOR] != -1) |
| { |
| gcmkONERROR(gctaOS_ConstructOS(device->os, &device->taos)); |
| } |
| |
| /* Construct the gckOS object. */ |
| 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_EnablePowerManagement( |
| 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_EnablePowerManagement( |
| 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) |
| { |
| } |
| 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_EnablePowerManagement( |
| 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) |
| { |
| device->semas[i] = semCCreate(SEM_Q_FIFO, 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); |
| } |
| |
| { |
| /* 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) PHYS_TO_KM(physical); |
| |
| 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->internalPhysName = gcmPTR_TO_NAME(device->internalPhysical); |
| } |
| |
| if (device->externalPhysical) |
| { |
| device->externalPhysName = gcmPTR_TO_NAME(device->externalPhysical); |
| } |
| |
| if (device->contiguousPhysical) |
| { |
| device->contiguousPhysName = 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; |
| } |
| } |
| |
| for (i = 0; i < gcdMAX_GPU_COUNT; i++) |
| { |
| if (Device->irqLines[i] != -1) |
| { |
| semDelete(Device->semas[i]); |
| } |
| } |
| |
| if (Device->internalPhysName != 0) |
| { |
| gcmRELEASE_NAME(Device->internalPhysName); |
| Device->internalPhysName = 0; |
| } |
| if (Device->externalPhysName != 0) |
| { |
| gcmRELEASE_NAME(Device->externalPhysName); |
| Device->externalPhysName = 0; |
| } |
| if (Device->contiguousPhysName != 0) |
| { |
| gcmRELEASE_NAME(Device->contiguousPhysName); |
| Device->contiguousPhysName = 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. */ |
| 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 |
| ); |
| Device->externalPhysical = gcvNULL; |
| } |
| |
| 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->contiguousPhysical = gcvNULL; |
| 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]) |
| { |
| 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; |
| } |
| |
| /* Free the device. */ |
| free(Device); |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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= intConnect((void *)(Device->irqLines[Core]), isrRoutine, (gctPOINTER)Core); |
| |
| (*((volatile unsigned int *)0xbfd0005c) ) = (*((volatile unsigned int *)0xbfd0005c) ) | (1 << 6); |
| |
| 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= intConnect((void *)(Device->irqLines[gcvCORE_VG]), isrRoutine, (gctPOINTER)gcvCORE_VG); |
| |
| 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]) |
| { |
| intDisconnect((void *)(Device->irqLines[Core]), isrRoutine, (gctPOINTER)Core); |
| |
| 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]) |
| { |
| intDisconnect((void *)(Device->irqLines[gcvCORE_VG]), isrRoutineVG, (gctPOINTER)gcvCORE_VG); |
| |
| 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; |
| semFlush(Device->semas[i]); |
| |
| taskSuspend(Device->threadCtxts[i]); |
| Device->threadCtxts[i] = 0; |
| Device->threadInitializeds[i] = gcvFALSE; |
| } |
| } |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckGALDEVICE_QueryFrequency( |
| IN gckGALDEVICE Device |
| ) |
| { |
| gctUINT64 mcStart[gcvCORE_COUNT], shStart[gcvCORE_COUNT]; |
| gctUINT32 mcClk[gcvCORE_COUNT], shClk[gcvCORE_COUNT]; |
| gckHARDWARE hardware = gcvNULL; |
| gceSTATUS status; |
| gctUINT i; |
| |
| gcmkHEADER_ARG("Device=0x%p", Device); |
| |
| for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++) |
| { |
| |
| if (Device->kernels[i]) |
| { |
| hardware = Device->kernels[i]->hardware; |
| |
| mcStart[i] = shStart[i] = 0; |
| |
| gckHARDWARE_EnterQueryClock(hardware, |
| &mcStart[i], &shStart[i]); |
| } |
| } |
| |
| gcmkONERROR(gckOS_Delay(Device->os, 50)); |
| |
| for (i = gcvCORE_MAJOR; i < gcvCORE_COUNT; i++) |
| { |
| mcClk[i] = shClk[i] = 0; |
| |
| |
| if (Device->kernels[i] && mcStart[i]) |
| { |
| hardware = Device->kernels[i]->hardware; |
| |
| gckHARDWARE_ExitQueryClock(hardware, |
| mcStart[i], shStart[i], |
| &mcClk[i], &shClk[i]); |
| |
| hardware->mcClk = mcClk[i]; |
| hardware->shClk = shClk[i]; |
| } |
| } |
| |
| OnError: |
| gcmkFOOTER_NO(); |
| |
| return status; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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)); |
| |
| gcmkONERROR(gckGALDEVICE_QueryFrequency(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_SetPowerState( |
| Device->kernels[i]->hardware, gcvPOWER_OFF_BROADCAST |
| )); |
| } |
| } |
| |
| if (Device->kernels[gcvCORE_VG] != gcvNULL) |
| { |
| /* Setup the ISR routine. */ |
| gcmkONERROR(gckGALDEVICE_Setup_ISR_VG(Device)); |
| |
| } |
| |
| 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_EnablePowerManagement( |
| Device->kernels[i]->hardware, gcvTRUE |
| )); |
| |
| /* Switch to OFF power state. */ |
| gcmkONERROR(gckHARDWARE_SetPowerState( |
| 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)); |
| |
| } |
| |
| /* 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_SetPowerState( |
| Device->kernels[core]->hardware, gcvPOWER_OFF_BROADCAST |
| )); |
| |
| gcmkFOOTER_NO(); |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkFOOTER(); |
| return gcvSTATUS_OK; |
| } |
| |
| |