/**************************************************************************** * * 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_precomp.h" #include "gc_hal_kernel_context.h" #define _GC_OBJ_ZONE gcvZONE_ASYNC_COMMAND static gceSTATUS _HandlePatchList( IN gckASYNC_COMMAND Command, IN gcoCMDBUF CommandBuffer, IN gctBOOL NeedCopy ) { gceSTATUS status; gcsPATCH_LIST * uList; gcsPATCH_LIST * previous; gcsPATCH_LIST * kList; gcmkHEADER_ARG( "Command=0x%x CommandBuffer=0x%x NeedCopy=%d", Command, CommandBuffer, NeedCopy ); uList = gcmUINT64_TO_PTR(CommandBuffer->patchHead); while (uList) { gctUINT i; kList = gcvNULL; previous = uList; gcmkONERROR(gckKERNEL_OpenUserData( Command->kernel, NeedCopy, Command->kList, uList, gcmSIZEOF(gcsPATCH_LIST), (gctPOINTER *)&kList )); for (i = 0; i < kList->count; i++) { gcsPATCH * patch = &kList->patch[i]; /* Touch video memory node. */ gcmkVERIFY_OK(gckVIDMEM_SetCommitStamp(Command->kernel, gcvENGINE_BLT, patch->handle, Command->commitStamp)); } uList = kList->next; gcmkVERIFY_OK(gckKERNEL_CloseUserData( Command->kernel, NeedCopy, gcvFALSE, previous, gcmSIZEOF(gcsPATCH_LIST), (gctPOINTER *)&kList )); } gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: if (kList) { gcmkVERIFY_OK(gckKERNEL_CloseUserData( Command->kernel, NeedCopy, gcvFALSE, previous, gcmSIZEOF(gcsPATCH_LIST), (gctPOINTER *)&kList )); } gcmkFOOTER(); return status; } gceSTATUS gckASYNC_COMMAND_Construct( IN gckKERNEL Kernel, OUT gckASYNC_COMMAND * Command ) { gceSTATUS status; gckASYNC_COMMAND command; gckOS os = Kernel->os; gcmkHEADER(); /* Allocate gckASYNC_COMMAND object. */ gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsASYNC_COMMAND), (gctPOINTER *)&command)); gckOS_ZeroMemory(command, gcmSIZEOF(gcsASYNC_COMMAND)); /* Mutex to protect gckFE. */ gcmkONERROR(gckOS_CreateMutex(os, &command->mutex)); /* Initialize gckFE. */ gckFE_Initialize(Kernel->hardware, &command->fe); /* Initialize gckASYNC_COMMAND object. */ command->os = os; command->kernel = Kernel; command->hardware = Kernel->hardware; gcmkVERIFY_OK(gckHARDWARE_QueryCommandBuffer( Kernel->hardware, gcvENGINE_BLT, gcvNULL, gcvNULL, &command->reservedTail )); gcmkONERROR(gckFENCE_Create( os, Kernel, &command->fence )); gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsPATCH_LIST), &command->kList)); /* Commit stamp start from 1. */ command->commitStamp = 1; *Command = command; gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: /* Rollback. */ gckASYNC_COMMAND_Destroy(command); gcmkFOOTER(); return status; } gceSTATUS gckASYNC_COMMAND_Destroy( IN gckASYNC_COMMAND Command ) { gcmkHEADER(); if (Command) { if (Command->mutex) { gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutex)); } if (Command->fence) { gcmkVERIFY_OK(gckFENCE_Destory(Command->os, Command->fence)); } if (Command->kList) { gcmkOS_SAFE_FREE(Command->os, Command->kList); } if (Command->fe.freeDscriptors) { gcmkOS_SAFE_FREE(Command->os, Command->fe.freeDscriptors); } gcmkOS_SAFE_FREE(Command->os, Command); } gcmkFOOTER_NO(); return gcvSTATUS_OK; } gceSTATUS gckASYNC_COMMAND_Commit( IN gckASYNC_COMMAND Command, IN gcoCMDBUF CommandBuffer, IN gcsQUEUE_PTR EventQueue ) { gceSTATUS status; gctBOOL available = gcvFALSE; gctBOOL acquired = gcvFALSE; gcoCMDBUF commandBufferObject = gcvNULL; struct _gcoCMDBUF _commandBufferObject; gctUINT8_PTR commandBufferLogical; gctUINT8_PTR commandBufferTail; gctUINT commandBufferSize; gctUINT32 commandBufferAddress; gcsFEDescriptor descriptor; gctUINT32 skipFlushBytes; gctUINT32 fenceBytes; gctBOOL needCopy; gctUINT32 oldValue; gctUINT32 flushBytes; gcmkHEADER(); gckOS_QueryNeedCopy(Command->os, 0, &needCopy); gcmkVERIFY_OK(_HandlePatchList(Command, CommandBuffer, needCopy)); /* Open user passed gcoCMDBUF object. */ gcmkONERROR(gckKERNEL_OpenUserData( Command->kernel, needCopy, &_commandBufferObject, CommandBuffer, gcmSIZEOF(struct _gcoCMDBUF), (gctPOINTER *)&commandBufferObject )); gcmkVERIFY_OBJECT(commandBufferObject, gcvOBJ_COMMANDBUFFER); gckHARDWARE_FlushAsyncMMU(Command->hardware, gcvNULL, &flushBytes); gcmkONERROR(gckOS_AtomicExchange(Command->os, Command->hardware->pageTableDirty[gcvENGINE_BLT], 0, &oldValue)); if (oldValue) { commandBufferLogical = (gctUINT8_PTR) gcmUINT64_TO_PTR(commandBufferObject->logical) + commandBufferObject->startOffset; gckHARDWARE_FlushAsyncMMU(Command->hardware, commandBufferLogical, &flushBytes); skipFlushBytes = 0; } else { skipFlushBytes = flushBytes; } /* Compute the command buffer entry and the size. */ commandBufferLogical = (gctUINT8_PTR) gcmUINT64_TO_PTR(commandBufferObject->logical) + commandBufferObject->startOffset + skipFlushBytes; commandBufferSize = commandBufferObject->offset + Command->reservedTail - commandBufferObject->startOffset - skipFlushBytes; commandBufferTail = commandBufferLogical + commandBufferSize - Command->reservedTail; /* Get the hardware address. */ if (Command->kernel && Command->kernel->virtualCommandBuffer) { gckKERNEL kernel = Command->kernel; gckVIRTUAL_COMMAND_BUFFER_PTR virtualCommandBuffer = gcmNAME_TO_PTR(commandBufferObject->physical); if (virtualCommandBuffer == gcvNULL) { gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); } gcmkONERROR(gckKERNEL_GetGPUAddress( Command->kernel, commandBufferLogical, gcvTRUE, virtualCommandBuffer, &commandBufferAddress )); } else { gcmkONERROR(gckHARDWARE_ConvertLogical( Command->hardware, commandBufferLogical, gcvTRUE, &commandBufferAddress )); } gcmkONERROR(gckHARDWARE_Fence( Command->hardware, gcvENGINE_BLT, commandBufferTail, Command->fence->address, Command->commitStamp, &fenceBytes )); descriptor.start = commandBufferAddress; descriptor.end = commandBufferAddress + commandBufferSize; gcmkDUMPCOMMAND( Command->os, commandBufferLogical, commandBufferSize, gcvDUMP_BUFFER_USER, gcvFALSE ); gckOS_AcquireMutex(Command->os, Command->mutex, gcvINFINITE); acquired = gcvTRUE; /* Acquire a slot. */ for(;;) { gcmkONERROR(gckFE_ReserveSlot(Command->hardware, &Command->fe, &available)); if (available) { break; } else { gcmkTRACE_ZONE(gcvLEVEL_INFO, _GC_OBJ_ZONE, "No available slot, have to wait"); gckOS_Delay(Command->os, 1); } } /* Send descriptor. */ gckFE_Execute(Command->hardware, &Command->fe, &descriptor); Command->commitStamp++; gckOS_ReleaseMutex(Command->os, Command->mutex); acquired = gcvFALSE; gcmkVERIFY_OK(gckKERNEL_CloseUserData( Command->kernel, needCopy, gcvFALSE, CommandBuffer, gcmSIZEOF(struct _gcoCMDBUF), (gctPOINTER *)&commandBufferObject )); gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: if (acquired) { gckOS_ReleaseMutex(Command->os, Command->mutex); } if (commandBufferObject) { gcmkVERIFY_OK(gckKERNEL_CloseUserData( Command->kernel, needCopy, gcvFALSE, CommandBuffer, gcmSIZEOF(struct _gcoCMDBUF), (gctPOINTER *)&commandBufferObject )); } gcmkFOOTER(); return status; } gceSTATUS gckASYNC_COMMAND_EnterCommit( IN gckASYNC_COMMAND Command ) { return gckOS_AcquireMutex(Command->os, Command->mutex, gcvINFINITE); } gceSTATUS gckASYNC_COMMAND_ExitCommit( IN gckASYNC_COMMAND Command ) { return gckOS_ReleaseMutex(Command->os, Command->mutex); } gceSTATUS gckASYNC_COMMAND_Execute( IN gckASYNC_COMMAND Command, IN gctUINT32 Start, IN gctUINT32 End ) { gceSTATUS status; gcsFEDescriptor descriptor; gctBOOL available; descriptor.start = Start; descriptor.end = End; /* Acquire a slot. */ for(;;) { gcmkONERROR(gckFE_ReserveSlot(Command->hardware, &Command->fe, &available)); if (available) { break; } else { gckOS_Delay(Command->os, 1); } } /* Send descriptor. */ gckFE_Execute(Command->hardware, &Command->fe, &descriptor); return gcvSTATUS_OK; OnError: return status; }