2393 lines
64 KiB
C
2393 lines
64 KiB
C
/****************************************************************************
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2014 - 2019 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 - 2019 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_buffer.h"
|
|
|
|
#ifdef __QNXNTO__
|
|
#include "gc_hal_kernel_qnx.h"
|
|
#endif
|
|
|
|
#define _GC_OBJ_ZONE gcvZONE_EVENT
|
|
|
|
#define gcdEVENT_ALLOCATION_COUNT (4096 / gcmSIZEOF(gcsHAL_INTERFACE))
|
|
#define gcdEVENT_MIN_THRESHOLD 4
|
|
|
|
/******************************************************************************\
|
|
********************************* Support Code *********************************
|
|
\******************************************************************************/
|
|
|
|
static gcmINLINE gceSTATUS
|
|
gckEVENT_AllocateQueue(
|
|
IN gckEVENT Event,
|
|
OUT gcsEVENT_QUEUE_PTR * Queue
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x", Event);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(Queue != gcvNULL);
|
|
|
|
/* Do we have free queues? */
|
|
if (Event->freeList == gcvNULL)
|
|
{
|
|
gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
/* Move one free queue from the free list. */
|
|
* Queue = Event->freeList;
|
|
Event->freeList = Event->freeList->next;
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_ARG("*Queue=0x%x", gcmOPT_POINTER(Queue));
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
static gceSTATUS
|
|
gckEVENT_FreeQueue(
|
|
IN gckEVENT Event,
|
|
OUT gcsEVENT_QUEUE_PTR Queue
|
|
)
|
|
{
|
|
gceSTATUS status = gcvSTATUS_OK;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x", Event);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(Queue != gcvNULL);
|
|
|
|
/* Move one free queue from the free list. */
|
|
Queue->next = Event->freeList;
|
|
Event->freeList = Queue;
|
|
|
|
/* Success. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
static gceSTATUS
|
|
gckEVENT_FreeRecord(
|
|
IN gckEVENT Event,
|
|
IN gcsEVENT_PTR Record
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gctBOOL acquired = gcvFALSE;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(Record != gcvNULL);
|
|
|
|
/* Acquire the mutex. */
|
|
gcmkONERROR(gckOS_AcquireMutex(Event->os,
|
|
Event->freeEventMutex,
|
|
gcvINFINITE));
|
|
acquired = gcvTRUE;
|
|
|
|
/* Push the record on the free list. */
|
|
Record->next = Event->freeEventList;
|
|
Event->freeEventList = Record;
|
|
Event->freeEventCount += 1;
|
|
|
|
/* Release the mutex. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Roll back. */
|
|
if (acquired)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return gcvSTATUS_OK;
|
|
}
|
|
|
|
static gceSTATUS
|
|
gckEVENT_IsEmpty(
|
|
IN gckEVENT Event,
|
|
OUT gctBOOL_PTR IsEmpty
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gctSIZE_T i;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x", Event);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(IsEmpty != gcvNULL);
|
|
|
|
/* Assume the event queue is empty. */
|
|
*IsEmpty = gcvTRUE;
|
|
|
|
/* Walk the event queue. */
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
|
|
{
|
|
/* Check whether this event is in use. */
|
|
if (Event->queues[i].head != gcvNULL)
|
|
{
|
|
/* The event is in use, hence the queue is not empty. */
|
|
*IsEmpty = gcvFALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Try acquiring the mutex. */
|
|
status = gckOS_AcquireMutex(Event->os, Event->eventQueueMutex, 0);
|
|
if (status == gcvSTATUS_TIMEOUT)
|
|
{
|
|
/* Timeout - queue is no longer empty. */
|
|
*IsEmpty = gcvFALSE;
|
|
}
|
|
else
|
|
{
|
|
/* Bail out on error. */
|
|
gcmkONERROR(status);
|
|
|
|
/* Release the mutex. */
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
}
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_ARG("*IsEmpty=%d", gcmOPT_VALUE(IsEmpty));
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
static gceSTATUS
|
|
_TryToIdleGPU(
|
|
IN gckEVENT Event
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gctBOOL empty = gcvFALSE, idle = gcvFALSE;
|
|
gctBOOL powerLocked = gcvFALSE;
|
|
gckHARDWARE hardware;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x", Event);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
|
|
/* Grab gckHARDWARE object. */
|
|
hardware = Event->kernel->hardware;
|
|
gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
|
|
|
|
/* Check whether the event queue is empty. */
|
|
gcmkONERROR(gckEVENT_IsEmpty(Event, &empty));
|
|
|
|
if (empty)
|
|
{
|
|
status = gckOS_AcquireMutex(hardware->os, hardware->powerMutex, 0);
|
|
if (status == gcvSTATUS_TIMEOUT)
|
|
{
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
}
|
|
|
|
powerLocked = gcvTRUE;
|
|
|
|
/* Query whether the hardware is idle. */
|
|
gcmkONERROR(gckHARDWARE_QueryIdle(Event->kernel->hardware, &idle));
|
|
|
|
gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex));
|
|
powerLocked = gcvFALSE;
|
|
|
|
if (idle)
|
|
{
|
|
/* Inform the system of idle GPU. */
|
|
gcmkONERROR(gckOS_Broadcast(Event->os,
|
|
Event->kernel->hardware,
|
|
gcvBROADCAST_GPU_IDLE));
|
|
}
|
|
}
|
|
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
if (powerLocked)
|
|
{
|
|
gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex));
|
|
}
|
|
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
static gceSTATUS
|
|
__RemoveRecordFromProcessDB(
|
|
IN gckEVENT Event,
|
|
IN gcsEVENT_PTR Record
|
|
)
|
|
{
|
|
gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record);
|
|
gcmkVERIFY_ARGUMENT(Record != gcvNULL);
|
|
|
|
switch (Record->info.command)
|
|
{
|
|
case gcvHAL_UNLOCK_VIDEO_MEMORY:
|
|
gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
|
|
Event->kernel,
|
|
Record->processID,
|
|
gcvDB_VIDEO_MEMORY_LOCKED,
|
|
gcmUINT64_TO_PTR(Record->info.u.UnlockVideoMemory.node)));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
}
|
|
|
|
static gceSTATUS
|
|
_ReleaseVideoMemoryHandle(
|
|
IN gckKERNEL Kernel,
|
|
IN OUT gcsEVENT_PTR Record,
|
|
IN OUT gcsHAL_INTERFACE * Interface
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gckVIDMEM_NODE nodeObject;
|
|
gctUINT32 handle;
|
|
|
|
switch(Interface->command)
|
|
{
|
|
case gcvHAL_UNLOCK_VIDEO_MEMORY:
|
|
handle = (gctUINT32)Interface->u.UnlockVideoMemory.node;
|
|
|
|
gcmkONERROR(gckVIDMEM_HANDLE_Lookup(
|
|
Kernel, Record->processID, handle, &nodeObject));
|
|
|
|
Record->info.u.UnlockVideoMemory.node = gcmPTR_TO_UINT64(nodeObject);
|
|
|
|
gckVIDMEM_HANDLE_Dereference(Kernel, Record->processID, handle);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return gcvSTATUS_OK;
|
|
OnError:
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** _QueryFlush
|
|
**
|
|
** Check the type of surfaces which will be released by current event and
|
|
** determine the cache needed to flush.
|
|
**
|
|
*/
|
|
static gceSTATUS
|
|
_QueryFlush(
|
|
IN gckEVENT Event,
|
|
IN gcsEVENT_PTR Record,
|
|
OUT gceKERNEL_FLUSH *Flush
|
|
)
|
|
{
|
|
gceKERNEL_FLUSH flush = 0;
|
|
gckVIDMEM_NODE nodeObject;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record);
|
|
gcmkVERIFY_ARGUMENT(Record != gcvNULL);
|
|
|
|
while (Record != gcvNULL)
|
|
{
|
|
switch (Record->info.command)
|
|
{
|
|
case gcvHAL_UNLOCK_VIDEO_MEMORY:
|
|
nodeObject = gcmUINT64_TO_PTR(Record->info.u.UnlockVideoMemory.node);
|
|
|
|
switch (nodeObject->type)
|
|
{
|
|
case gcvVIDMEM_TYPE_TILE_STATUS:
|
|
flush |= gcvFLUSH_TILE_STATUS;
|
|
break;
|
|
case gcvVIDMEM_TYPE_COLOR_BUFFER:
|
|
flush |= gcvFLUSH_COLOR;
|
|
break;
|
|
case gcvVIDMEM_TYPE_DEPTH_BUFFER:
|
|
flush |= gcvFLUSH_DEPTH;
|
|
break;
|
|
case gcvVIDMEM_TYPE_TEXTURE:
|
|
flush |= gcvFLUSH_TEXTURE;
|
|
break;
|
|
case gcvVIDMEM_TYPE_ICACHE:
|
|
flush |= gcvFLUSH_ICACHE;
|
|
break;
|
|
case gcvVIDMEM_TYPE_TXDESC:
|
|
flush |= gcvFLUSH_TXDESC;
|
|
break;
|
|
case gcvVIDMEM_TYPE_FENCE:
|
|
flush |= gcvFLUSH_FENCE;
|
|
break;
|
|
case gcvVIDMEM_TYPE_VERTEX_BUFFER:
|
|
flush |= gcvFLUSH_VERTEX;
|
|
break;
|
|
case gcvVIDMEM_TYPE_TFBHEADER:
|
|
flush |= gcvFLUSH_TFBHEADER;
|
|
break;
|
|
case gcvVIDMEM_TYPE_GENERIC:
|
|
flush = gcvFLUSH_ALL;
|
|
goto Out;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Record = Record->next;
|
|
}
|
|
|
|
Out:
|
|
*Flush = flush;
|
|
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
}
|
|
|
|
void
|
|
_SubmitTimerFunction(
|
|
gctPOINTER Data
|
|
)
|
|
{
|
|
gckEVENT event = (gckEVENT)Data;
|
|
gcmkVERIFY_OK(gckEVENT_Submit(event, gcvTRUE, gcvFALSE));
|
|
}
|
|
|
|
/******************************************************************************\
|
|
******************************* gckEVENT API Code *******************************
|
|
\******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Construct
|
|
**
|
|
** Construct a new gckEVENT object.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckKERNEL Kernel
|
|
** Pointer to an gckKERNEL object.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** gckEVENT * Event
|
|
** Pointer to a variable that receives the gckEVENT object pointer.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Construct(
|
|
IN gckKERNEL Kernel,
|
|
IN gckCOMMAND Command,
|
|
OUT gckEVENT * Event
|
|
)
|
|
{
|
|
gckOS os;
|
|
gceSTATUS status;
|
|
gckEVENT eventObj = gcvNULL;
|
|
int i;
|
|
gcsEVENT_PTR record;
|
|
gctPOINTER pointer = gcvNULL;
|
|
|
|
gcmkHEADER_ARG("Kernel=0x%x", Kernel);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
|
|
gcmkVERIFY_ARGUMENT(Event != gcvNULL);
|
|
|
|
/* Extract the pointer to the gckOS object. */
|
|
os = Kernel->os;
|
|
gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
|
|
|
|
/* Allocate the gckEVENT object. */
|
|
gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckEVENT), &pointer));
|
|
|
|
eventObj = pointer;
|
|
|
|
/* Reset the object. */
|
|
gcmkVERIFY_OK(gckOS_ZeroMemory(eventObj, gcmSIZEOF(struct _gckEVENT)));
|
|
|
|
/* Initialize the gckEVENT object. */
|
|
eventObj->object.type = gcvOBJ_EVENT;
|
|
eventObj->kernel = Kernel;
|
|
eventObj->os = os;
|
|
eventObj->command = Command;
|
|
|
|
/* Create the mutexes. */
|
|
gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventQueueMutex));
|
|
gcmkONERROR(gckOS_CreateMutex(os, &eventObj->freeEventMutex));
|
|
gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventListMutex));
|
|
|
|
/* Create a bunch of event reccords. */
|
|
for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1)
|
|
{
|
|
/* Allocate an event record. */
|
|
gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsEVENT), &pointer));
|
|
|
|
record = pointer;
|
|
|
|
/* Push it on the free list. */
|
|
record->next = eventObj->freeEventList;
|
|
eventObj->freeEventList = record;
|
|
eventObj->freeEventCount += 1;
|
|
}
|
|
|
|
/* Initialize the free list of event queues. */
|
|
for (i = 0; i < gcdREPO_LIST_COUNT; i += 1)
|
|
{
|
|
eventObj->repoList[i].next = eventObj->freeList;
|
|
eventObj->freeList = &eventObj->repoList[i];
|
|
}
|
|
|
|
eventObj->freeQueueCount = gcmCOUNTOF(eventObj->queues);
|
|
|
|
gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending));
|
|
|
|
gcmkVERIFY_OK(gckOS_CreateTimer(os,
|
|
_SubmitTimerFunction,
|
|
(gctPOINTER)eventObj,
|
|
&eventObj->submitTimer));
|
|
|
|
#if gcdINTERRUPT_STATISTIC
|
|
gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->interruptCount));
|
|
gcmkONERROR(gckOS_AtomSet(os,eventObj->interruptCount, 0));
|
|
#endif
|
|
|
|
eventObj->notifyState = -1;
|
|
|
|
/* Return pointer to the gckEVENT object. */
|
|
*Event = eventObj;
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_ARG("*Event=0x%x", *Event);
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Roll back. */
|
|
if (eventObj != gcvNULL)
|
|
{
|
|
if (eventObj->eventQueueMutex != gcvNULL)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventQueueMutex));
|
|
}
|
|
|
|
if (eventObj->freeEventMutex != gcvNULL)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->freeEventMutex));
|
|
}
|
|
|
|
if (eventObj->eventListMutex != gcvNULL)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventListMutex));
|
|
}
|
|
|
|
while (eventObj->freeEventList != gcvNULL)
|
|
{
|
|
record = eventObj->freeEventList;
|
|
eventObj->freeEventList = record->next;
|
|
|
|
gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, record));
|
|
}
|
|
|
|
if (eventObj->pending != gcvNULL)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending));
|
|
}
|
|
|
|
#if gcdINTERRUPT_STATISTIC
|
|
if (eventObj->interruptCount)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->interruptCount));
|
|
}
|
|
#endif
|
|
gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, eventObj));
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Destroy
|
|
**
|
|
** Destroy an gckEVENT object.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Destroy(
|
|
IN gckEVENT Event
|
|
)
|
|
{
|
|
gcsEVENT_PTR record;
|
|
gcsEVENT_QUEUE_PTR queue;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x", Event);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
|
|
if (Event->submitTimer != gcvNULL)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_StopTimer(Event->os, Event->submitTimer));
|
|
gcmkVERIFY_OK(gckOS_DestroyTimer(Event->os, Event->submitTimer));
|
|
}
|
|
|
|
/* Delete the queue mutex. */
|
|
gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventQueueMutex));
|
|
|
|
/* Free all free events. */
|
|
while (Event->freeEventList != gcvNULL)
|
|
{
|
|
record = Event->freeEventList;
|
|
Event->freeEventList = record->next;
|
|
|
|
gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record));
|
|
}
|
|
|
|
/* Delete the free mutex. */
|
|
gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->freeEventMutex));
|
|
|
|
/* Free all pending queues. */
|
|
while (Event->queueHead != gcvNULL)
|
|
{
|
|
/* Get the current queue. */
|
|
queue = Event->queueHead;
|
|
|
|
/* Free all pending events. */
|
|
while (queue->head != gcvNULL)
|
|
{
|
|
record = queue->head;
|
|
queue->head = record->next;
|
|
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_WARNING, gcvZONE_EVENT,
|
|
gcmSIZEOF(record) + gcmSIZEOF(queue->source),
|
|
"Event record 0x%x is still pending for %d.",
|
|
record, queue->source
|
|
);
|
|
|
|
gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record));
|
|
}
|
|
|
|
/* Remove the top queue from the list. */
|
|
if (Event->queueHead == Event->queueTail)
|
|
{
|
|
Event->queueHead =
|
|
Event->queueTail = gcvNULL;
|
|
}
|
|
else
|
|
{
|
|
Event->queueHead = Event->queueHead->next;
|
|
}
|
|
|
|
/* Free the queue. */
|
|
gcmkVERIFY_OK(gckEVENT_FreeQueue(Event, queue));
|
|
}
|
|
|
|
/* Delete the list mutex. */
|
|
gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventListMutex));
|
|
|
|
gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending));
|
|
|
|
#if gcdINTERRUPT_STATISTIC
|
|
gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->interruptCount));
|
|
#endif
|
|
|
|
/* Mark the gckEVENT object as unknown. */
|
|
Event->object.type = gcvOBJ_UNKNOWN;
|
|
|
|
/* Free the gckEVENT object. */
|
|
gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, Event));
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_GetEvent
|
|
**
|
|
** Reserve the next available hardware event.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gctBOOL Wait
|
|
** Set to gcvTRUE to force the function to wait if no events are
|
|
** immediately available.
|
|
**
|
|
** gceKERNEL_WHERE Source
|
|
** Source of the event.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** gctUINT8 * EventID
|
|
** Reserved event ID.
|
|
*/
|
|
#define gcdINVALID_EVENT_PTR ((gcsEVENT_PTR)gcvMAXUINTPTR_T)
|
|
|
|
gceSTATUS
|
|
gckEVENT_GetEvent(
|
|
IN gckEVENT Event,
|
|
IN gctBOOL Wait,
|
|
OUT gctUINT8 * EventID,
|
|
IN gceKERNEL_WHERE Source
|
|
)
|
|
{
|
|
gctINT i, id;
|
|
gceSTATUS status;
|
|
gctBOOL acquired = gcvFALSE;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x Source=%d", Event, Source);
|
|
|
|
while (gcvTRUE)
|
|
{
|
|
/* Grab the queue mutex. */
|
|
gcmkONERROR(gckOS_AcquireMutex(Event->os,
|
|
Event->eventQueueMutex,
|
|
gcvINFINITE));
|
|
acquired = gcvTRUE;
|
|
|
|
/* Walk through all events. */
|
|
id = Event->lastID;
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
|
|
{
|
|
gctINT nextID = id + 1;
|
|
|
|
if (nextID == gcmCOUNTOF(Event->queues))
|
|
{
|
|
nextID = 0;
|
|
}
|
|
|
|
if (Event->queues[id].head == gcvNULL)
|
|
{
|
|
*EventID = (gctUINT8) id;
|
|
|
|
Event->lastID = (gctUINT8) nextID;
|
|
|
|
/* Save time stamp of event. */
|
|
Event->queues[id].head = gcdINVALID_EVENT_PTR;
|
|
Event->queues[id].stamp = ++(Event->stamp);
|
|
Event->queues[id].source = Source;
|
|
|
|
/* Decrease the number of free events. */
|
|
--Event->freeQueueCount;
|
|
|
|
#if gcdDYNAMIC_SPEED
|
|
if (Event->freeQueueCount <= gcdDYNAMIC_EVENT_THRESHOLD)
|
|
{
|
|
gcmkONERROR(gckOS_BroadcastHurry(
|
|
Event->os,
|
|
Event->kernel->hardware,
|
|
gcdDYNAMIC_EVENT_THRESHOLD - Event->freeQueueCount));
|
|
}
|
|
#endif
|
|
|
|
/* Release the queue mutex. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os,
|
|
Event->eventQueueMutex));
|
|
|
|
/* Success. */
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_INFO, gcvZONE_EVENT,
|
|
gcmSIZEOF(id),
|
|
"Using id=%d",
|
|
id
|
|
);
|
|
|
|
gcmkFOOTER_ARG("*EventID=%u", *EventID);
|
|
return gcvSTATUS_OK;
|
|
}
|
|
|
|
id = nextID;
|
|
}
|
|
|
|
#if gcdDYNAMIC_SPEED
|
|
/* No free events, speed up the GPU right now! */
|
|
gcmkONERROR(gckOS_BroadcastHurry(Event->os,
|
|
Event->kernel->hardware,
|
|
gcdDYNAMIC_EVENT_THRESHOLD));
|
|
#endif
|
|
|
|
/* Release the queue mutex. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
acquired = gcvFALSE;
|
|
|
|
/* Fail if wait is not requested. */
|
|
if (!Wait)
|
|
{
|
|
/* Out of resources. */
|
|
gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
/* Delay a while. */
|
|
gcmkONERROR(gckOS_Delay(Event->os, 1));
|
|
}
|
|
|
|
OnError:
|
|
if (acquired)
|
|
{
|
|
/* Release the queue mutex. */
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_AllocateRecord
|
|
**
|
|
** Allocate a record for the new event.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gctBOOL AllocateAllowed
|
|
** State for allocation if out of free events.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** gcsEVENT_PTR * Record
|
|
** Allocated event record.
|
|
*/
|
|
static gcmINLINE gceSTATUS
|
|
gckEVENT_AllocateRecord(
|
|
IN gckEVENT Event,
|
|
IN gctBOOL AllocateAllowed,
|
|
OUT gcsEVENT_PTR * Record
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gctBOOL acquired = gcvFALSE;
|
|
gctINT i;
|
|
gcsEVENT_PTR record;
|
|
gctPOINTER pointer = gcvNULL;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x AllocateAllowed=%d", Event, AllocateAllowed);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(Record != gcvNULL);
|
|
|
|
/* Acquire the mutex. */
|
|
gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->freeEventMutex, gcvINFINITE));
|
|
acquired = gcvTRUE;
|
|
|
|
/* Test if we are below the allocation threshold. */
|
|
if ( (AllocateAllowed && (Event->freeEventCount < gcdEVENT_MIN_THRESHOLD)) ||
|
|
(Event->freeEventCount == 0) )
|
|
{
|
|
/* Allocate a bunch of records. */
|
|
for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1)
|
|
{
|
|
/* Allocate an event record. */
|
|
gcmkONERROR(gckOS_Allocate(Event->os,
|
|
gcmSIZEOF(gcsEVENT),
|
|
&pointer));
|
|
|
|
record = pointer;
|
|
|
|
/* Push it on the free list. */
|
|
record->next = Event->freeEventList;
|
|
Event->freeEventList = record;
|
|
Event->freeEventCount += 1;
|
|
}
|
|
}
|
|
|
|
*Record = Event->freeEventList;
|
|
Event->freeEventList = Event->freeEventList->next;
|
|
Event->freeEventCount -= 1;
|
|
|
|
/* Release the mutex. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_ARG("*Record=0x%x", gcmOPT_POINTER(Record));
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Roll back. */
|
|
if (acquired)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex));
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_AddList
|
|
**
|
|
** Add a new event to the list of events.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gcsHAL_INTERFACE_PTR Interface
|
|
** Pointer to the interface for the event to be added.
|
|
**
|
|
** gceKERNEL_WHERE FromWhere
|
|
** Place in the pipe where the event needs to be generated.
|
|
**
|
|
** gctBOOL AllocateAllowed
|
|
** State for allocation if out of free events.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_AddList(
|
|
IN gckEVENT Event,
|
|
IN gcsHAL_INTERFACE_PTR Interface,
|
|
IN gceKERNEL_WHERE FromWhere,
|
|
IN gctBOOL AllocateAllowed,
|
|
IN gctBOOL FromKernel
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gctBOOL acquired = gcvFALSE;
|
|
gcsEVENT_PTR record = gcvNULL;
|
|
gcsEVENT_QUEUE_PTR queue;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x Interface=0x%x",
|
|
Event, Interface);
|
|
|
|
gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, _GC_OBJ_ZONE,
|
|
"FromWhere=%d AllocateAllowed=%d",
|
|
FromWhere, AllocateAllowed);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(Interface != gcvNULL);
|
|
|
|
/* Verify the event command. */
|
|
gcmkASSERT
|
|
( (Interface->command == gcvHAL_WRITE_DATA)
|
|
|| (Interface->command == gcvHAL_UNLOCK_VIDEO_MEMORY)
|
|
|| (Interface->command == gcvHAL_SIGNAL)
|
|
|| (Interface->command == gcvHAL_TIMESTAMP)
|
|
|| (Interface->command == gcvHAL_COMMIT_DONE)
|
|
|| (Interface->command == gcvHAL_DESTROY_MMU)
|
|
);
|
|
|
|
/* Validate the source. */
|
|
if ((FromWhere != gcvKERNEL_COMMAND) && (FromWhere != gcvKERNEL_PIXEL))
|
|
{
|
|
/* Invalid argument. */
|
|
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
|
|
}
|
|
|
|
/* Allocate a free record. */
|
|
gcmkONERROR(gckEVENT_AllocateRecord(Event, AllocateAllowed, &record));
|
|
|
|
/* Termninate the record. */
|
|
record->next = gcvNULL;
|
|
|
|
/* Record the committer. */
|
|
record->fromKernel = FromKernel;
|
|
|
|
/* Copy the event interface into the record. */
|
|
gckOS_MemCopy(&record->info, Interface, gcmSIZEOF(record->info));
|
|
|
|
/* Get process ID. */
|
|
gcmkONERROR(gckOS_GetProcessID(&record->processID));
|
|
|
|
if (FromKernel == gcvFALSE)
|
|
{
|
|
gcmkONERROR(__RemoveRecordFromProcessDB(Event, record));
|
|
|
|
/* Handle is belonged to current process, it must be released now. */
|
|
status = _ReleaseVideoMemoryHandle(Event->kernel, record, Interface);
|
|
|
|
if (gcmIS_ERROR(status))
|
|
{
|
|
/* Ingore error because there are other events in the queue. */
|
|
status = gcvSTATUS_OK;
|
|
goto OnError;
|
|
}
|
|
}
|
|
|
|
#ifdef __QNXNTO__
|
|
record->kernel = Event->kernel;
|
|
#endif
|
|
|
|
/* Acquire the mutex. */
|
|
gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->eventListMutex, gcvINFINITE));
|
|
acquired = gcvTRUE;
|
|
|
|
/* Do we need to allocate a new queue? */
|
|
if ((Event->queueTail == gcvNULL) || (Event->queueTail->source < FromWhere))
|
|
{
|
|
/* Allocate a new queue. */
|
|
gcmkONERROR(gckEVENT_AllocateQueue(Event, &queue));
|
|
|
|
/* Initialize the queue. */
|
|
queue->source = FromWhere;
|
|
queue->head = gcvNULL;
|
|
queue->next = gcvNULL;
|
|
|
|
/* Attach it to the list of allocated queues. */
|
|
if (Event->queueTail == gcvNULL)
|
|
{
|
|
Event->queueHead =
|
|
Event->queueTail = queue;
|
|
}
|
|
else
|
|
{
|
|
Event->queueTail->next = queue;
|
|
Event->queueTail = queue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
queue = Event->queueTail;
|
|
}
|
|
|
|
/* Attach the record to the queue. */
|
|
if (queue->head == gcvNULL)
|
|
{
|
|
queue->head = record;
|
|
queue->tail = record;
|
|
}
|
|
else
|
|
{
|
|
queue->tail->next = record;
|
|
queue->tail = record;
|
|
}
|
|
|
|
/* Release the mutex. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Roll back. */
|
|
if (acquired)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
|
|
}
|
|
|
|
if (record != gcvNULL)
|
|
{
|
|
gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record));
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Unlock
|
|
**
|
|
** Schedule an event to unlock virtual memory.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gceKERNEL_WHERE FromWhere
|
|
** Place in the pipe where the event needs to be generated.
|
|
**
|
|
** gcuVIDMEM_NODE_PTR Node
|
|
** Pointer to a gcuVIDMEM_NODE union that specifies the virtual memory
|
|
** to unlock.
|
|
**
|
|
** gceVIDMEM_TYPE Type
|
|
** Video memory allocation type to unlock.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Unlock(
|
|
IN gckEVENT Event,
|
|
IN gceKERNEL_WHERE FromWhere,
|
|
IN gctPOINTER Node
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gcsHAL_INTERFACE iface;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x FromWhere=%d Node=0x%x",
|
|
Event, FromWhere, Node);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(Node != gcvNULL);
|
|
|
|
/* Mark the event as an unlock. */
|
|
iface.command = gcvHAL_UNLOCK_VIDEO_MEMORY;
|
|
iface.u.UnlockVideoMemory.node = gcmPTR_TO_UINT64(Node);
|
|
iface.u.UnlockVideoMemory.asynchroneous = 0;
|
|
|
|
/* Append it to the queue. */
|
|
gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE));
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Signal
|
|
**
|
|
** Schedule an event to trigger a signal.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gctSIGNAL Signal
|
|
** Pointer to the signal to trigger.
|
|
**
|
|
** gceKERNEL_WHERE FromWhere
|
|
** Place in the pipe where the event needs to be generated.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Signal(
|
|
IN gckEVENT Event,
|
|
IN gctSIGNAL Signal,
|
|
IN gceKERNEL_WHERE FromWhere
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gcsHAL_INTERFACE iface;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x Signal=0x%x FromWhere=%d",
|
|
Event, Signal, FromWhere);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
gcmkVERIFY_ARGUMENT(Signal != gcvNULL);
|
|
|
|
/* Mark the event as a signal. */
|
|
iface.command = gcvHAL_SIGNAL;
|
|
iface.u.Signal.signal = gcmPTR_TO_UINT64(Signal);
|
|
iface.u.Signal.auxSignal = 0;
|
|
iface.u.Signal.process = 0;
|
|
|
|
#ifdef __QNXNTO__
|
|
iface.u.Signal.coid = 0;
|
|
iface.u.Signal.rcvid = 0;
|
|
|
|
gcmkONERROR(gckOS_SignalPending(Event->os, Signal));
|
|
#endif
|
|
|
|
/* Append it to the queue. */
|
|
gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE));
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Submit
|
|
**
|
|
** Submit the current event queue to the GPU.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gctBOOL Wait
|
|
** Submit requires one vacant event; if Wait is set to not zero,
|
|
** and there are no vacant events at this time, the function will
|
|
** wait until an event becomes vacant so that submission of the
|
|
** queue is successful.
|
|
**
|
|
** gctBOOL FromPower
|
|
** Determines whether the call originates from inside the power
|
|
** management or not.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Submit(
|
|
IN gckEVENT Event,
|
|
IN gctBOOL Wait,
|
|
IN gctBOOL FromPower
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gctUINT8 id = 0xFF;
|
|
gcsEVENT_QUEUE_PTR queue;
|
|
gctBOOL acquired = gcvFALSE;
|
|
gckCOMMAND command = gcvNULL;
|
|
gctBOOL commitEntered = gcvFALSE;
|
|
gctUINT32 bytes;
|
|
gctPOINTER buffer;
|
|
gctUINT32 executeBytes;
|
|
gctUINT32 flushBytes;
|
|
|
|
#if gcdINTERRUPT_STATISTIC
|
|
gctINT32 oldValue;
|
|
#endif
|
|
|
|
#if gcdSECURITY
|
|
gctPOINTER reservedBuffer;
|
|
#endif
|
|
|
|
gckHARDWARE hardware;
|
|
|
|
gceKERNEL_FLUSH flush = gcvFALSE;
|
|
gctUINT64 commitStamp;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x Wait=%d", Event, Wait);
|
|
|
|
/* Get gckCOMMAND object. */
|
|
command = Event->command;
|
|
hardware = Event->kernel->hardware;
|
|
|
|
gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
|
|
|
|
gckOS_GetTicks(&Event->lastCommitStamp);
|
|
|
|
/* Are there event queues? */
|
|
if (Event->queueHead != gcvNULL)
|
|
{
|
|
/* Acquire the command queue. */
|
|
gcmkONERROR(gckCOMMAND_EnterCommit(command, FromPower));
|
|
commitEntered = gcvTRUE;
|
|
|
|
/* Get current commit stamp. */
|
|
commitStamp = command->commitStamp;
|
|
|
|
if (commitStamp)
|
|
{
|
|
commitStamp -= 1;
|
|
}
|
|
|
|
/* Process all queues. */
|
|
while (Event->queueHead != gcvNULL)
|
|
{
|
|
/* Acquire the list mutex. */
|
|
gcmkONERROR(gckOS_AcquireMutex(Event->os,
|
|
Event->eventListMutex,
|
|
gcvINFINITE));
|
|
acquired = gcvTRUE;
|
|
|
|
/* Get the current queue. */
|
|
queue = Event->queueHead;
|
|
|
|
/* Allocate an event ID. */
|
|
gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source));
|
|
|
|
/* Copy event list to event ID queue. */
|
|
Event->queues[id].head = queue->head;
|
|
|
|
/* Update current commit stamp. */
|
|
Event->queues[id].commitStamp = commitStamp;
|
|
|
|
/* Remove the top queue from the list. */
|
|
if (Event->queueHead == Event->queueTail)
|
|
{
|
|
Event->queueHead = gcvNULL;
|
|
Event->queueTail = gcvNULL;
|
|
}
|
|
else
|
|
{
|
|
Event->queueHead = Event->queueHead->next;
|
|
}
|
|
|
|
/* Free the queue. */
|
|
gcmkONERROR(gckEVENT_FreeQueue(Event, queue));
|
|
|
|
/* Release the list mutex. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
|
|
acquired = gcvFALSE;
|
|
|
|
|
|
if (command->feType == gcvHW_FE_WAIT_LINK)
|
|
{
|
|
/* Determine cache needed to flush. */
|
|
gcmkVERIFY_OK(_QueryFlush(Event, Event->queues[id].head, &flush));
|
|
|
|
/* Get the size of the hardware event. */
|
|
gcmkONERROR(gckWLFE_Event(
|
|
hardware,
|
|
gcvNULL,
|
|
id,
|
|
Event->queues[id].source,
|
|
&bytes
|
|
));
|
|
|
|
/* Get the size of flush command. */
|
|
gcmkONERROR(gckHARDWARE_Flush(
|
|
hardware,
|
|
flush,
|
|
gcvNULL,
|
|
&flushBytes
|
|
));
|
|
|
|
bytes += flushBytes;
|
|
}
|
|
else if (command->feType == gcvHW_FE_ASYNC)
|
|
{
|
|
/* Get the size of the hardware event. */
|
|
gcmkONERROR(gckASYNC_FE_Event(
|
|
hardware,
|
|
gcvNULL,
|
|
id,
|
|
Event->queues[id].source,
|
|
&bytes
|
|
));
|
|
}
|
|
else
|
|
{
|
|
/* Get the size of the hardware event. */
|
|
gcmkONERROR(gckMCFE_Event(
|
|
hardware,
|
|
gcvNULL,
|
|
id,
|
|
Event->queues[id].source,
|
|
&bytes
|
|
));
|
|
}
|
|
|
|
/* Total bytes need to execute. */
|
|
executeBytes = bytes;
|
|
|
|
/* Reserve space in the command queue. */
|
|
gcmkONERROR(gckCOMMAND_Reserve(command, bytes, &buffer, &bytes));
|
|
#if gcdSECURITY
|
|
reservedBuffer = buffer;
|
|
#endif
|
|
|
|
#if gcdINTERRUPT_STATISTIC
|
|
gcmkVERIFY_OK(gckOS_AtomIncrement(
|
|
Event->os,
|
|
Event->interruptCount,
|
|
&oldValue
|
|
));
|
|
#endif
|
|
|
|
if (command->feType == gcvHW_FE_WAIT_LINK)
|
|
{
|
|
/* Set the flush in the command queue. */
|
|
gcmkONERROR(gckHARDWARE_Flush(
|
|
hardware,
|
|
flush,
|
|
buffer,
|
|
&flushBytes
|
|
));
|
|
|
|
/* Advance to next command. */
|
|
buffer = (gctUINT8_PTR)buffer + flushBytes;
|
|
|
|
/* Set the hardware event in the command queue. */
|
|
gcmkONERROR(gckWLFE_Event(
|
|
hardware,
|
|
buffer,
|
|
id,
|
|
Event->queues[id].source,
|
|
&bytes
|
|
));
|
|
|
|
#if gcdSECURITY
|
|
gckKERNEL_SecurityExecute(
|
|
Event->kernel,
|
|
reservedBuffer,
|
|
executeBytes
|
|
);
|
|
#else
|
|
/* Execute the hardware event. */
|
|
gcmkONERROR(gckCOMMAND_Execute(command, executeBytes));
|
|
#endif
|
|
}
|
|
else if (command->feType == gcvHW_FE_ASYNC)
|
|
{
|
|
/* Set the hardware event in the command queue. */
|
|
gcmkONERROR(gckASYNC_FE_Event(
|
|
hardware,
|
|
buffer,
|
|
id,
|
|
Event->queues[id].source,
|
|
&bytes
|
|
));
|
|
|
|
/* Execute the hardware event. */
|
|
gcmkONERROR(gckCOMMAND_ExecuteAsync(command, executeBytes));
|
|
}
|
|
else
|
|
{
|
|
/* Set the hardware event in the command queue. */
|
|
gcmkONERROR(gckMCFE_Event(
|
|
hardware,
|
|
buffer,
|
|
id,
|
|
Event->queues[id].source,
|
|
&bytes
|
|
));
|
|
|
|
/* Execute the hardware event. */
|
|
gcmkONERROR(gckCOMMAND_ExecuteMultiChannel(command, 0, 0, executeBytes));
|
|
}
|
|
|
|
#if gcdNULL_DRIVER
|
|
/* Notify immediately on infinite hardware. */
|
|
gcmkONERROR(gckEVENT_Interrupt(Event, 1 << id));
|
|
|
|
gcmkONERROR(gckEVENT_Notify(Event, 0));
|
|
#endif
|
|
}
|
|
|
|
/* Release the command queue. */
|
|
gcmkONERROR(gckCOMMAND_ExitCommit(command, FromPower));
|
|
|
|
#if !gcdNULL_DRIVER
|
|
if (!FromPower)
|
|
{
|
|
gcmkVERIFY_OK(_TryToIdleGPU(Event));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
if (acquired)
|
|
{
|
|
/* Need to unroll the mutex acquire. */
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex));
|
|
}
|
|
|
|
if (commitEntered)
|
|
{
|
|
/* Release the command queue mutex. */
|
|
gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, FromPower));
|
|
}
|
|
|
|
if (id != 0xFF)
|
|
{
|
|
/* Need to unroll the event allocation. */
|
|
Event->queues[id].head = gcvNULL;
|
|
}
|
|
|
|
if (status == gcvSTATUS_GPU_NOT_RESPONDING)
|
|
{
|
|
/* Broadcast GPU stuck. */
|
|
status = gckOS_Broadcast(Event->os,
|
|
Event->kernel->hardware,
|
|
gcvBROADCAST_GPU_STUCK);
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Commit
|
|
**
|
|
** Commit an event queue from the user.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gcsQUEUE_PTR Queue
|
|
** User event queue.
|
|
**
|
|
** gctBOOL Forced
|
|
** Force fire a event. There won't be interrupt if there's no events
|
|
queued. Force a event by append a dummy one if this parameter is on.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Commit(
|
|
IN gckEVENT Event,
|
|
IN gcsQUEUE_PTR Queue,
|
|
IN gctBOOL Forced
|
|
)
|
|
{
|
|
gceSTATUS status;
|
|
gcsQUEUE_PTR record = gcvNULL, next;
|
|
gctUINT32 processID;
|
|
gctBOOL needCopy = gcvFALSE;
|
|
gctPOINTER pointer = gcvNULL;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x Queue=0x%x", Event, Queue);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
|
|
/* Get the current process ID. */
|
|
gcmkONERROR(gckOS_GetProcessID(&processID));
|
|
|
|
/* Query if we need to copy the client data. */
|
|
gcmkONERROR(gckOS_QueryNeedCopy(Event->os, processID, &needCopy));
|
|
|
|
/* Loop while there are records in the queue. */
|
|
while (Queue != gcvNULL)
|
|
{
|
|
gcsQUEUE queue;
|
|
|
|
if (needCopy)
|
|
{
|
|
/* Point to stack record. */
|
|
record = &queue;
|
|
|
|
/* Copy the data from the client. */
|
|
gcmkONERROR(gckOS_CopyFromUserData(Event->os,
|
|
record,
|
|
Queue,
|
|
gcmSIZEOF(gcsQUEUE)));
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Map record into kernel memory. */
|
|
gcmkONERROR(gckOS_MapUserPointer(Event->os,
|
|
Queue,
|
|
gcmSIZEOF(gcsQUEUE),
|
|
&pointer));
|
|
|
|
record = pointer;
|
|
}
|
|
|
|
/* Append event record to event queue. */
|
|
gcmkONERROR(
|
|
gckEVENT_AddList(Event, &record->iface, gcvKERNEL_PIXEL, gcvTRUE, gcvFALSE));
|
|
|
|
/* Next record in the queue. */
|
|
next = gcmUINT64_TO_PTR(record->next);
|
|
|
|
if (!needCopy)
|
|
{
|
|
/* Unmap record from kernel memory. */
|
|
gcmkONERROR(
|
|
gckOS_UnmapUserPointer(Event->os,
|
|
Queue,
|
|
gcmSIZEOF(gcsQUEUE),
|
|
(gctPOINTER *) record));
|
|
record = gcvNULL;
|
|
}
|
|
|
|
Queue = next;
|
|
}
|
|
|
|
if (Forced && Event->queueHead == gcvNULL)
|
|
{
|
|
gcsHAL_INTERFACE iface;
|
|
iface.command = gcvHAL_COMMIT_DONE;
|
|
|
|
gcmkONERROR(gckEVENT_AddList(Event, &iface, gcvKERNEL_PIXEL, gcvFALSE, gcvTRUE));
|
|
}
|
|
|
|
/* Submit the event list. */
|
|
gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE));
|
|
|
|
/* Success */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
if (pointer)
|
|
{
|
|
/* Roll back. */
|
|
gcmkVERIFY_OK(gckOS_UnmapUserPointer(Event->os,
|
|
Queue,
|
|
gcmSIZEOF(gcsQUEUE),
|
|
(gctPOINTER*)pointer));
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Interrupt
|
|
**
|
|
** Called by the interrupt service routine to store the triggered interrupt
|
|
** mask to be later processed by gckEVENT_Notify.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gctUINT32 Data
|
|
** Mask for the 32 interrupts.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Interrupt(
|
|
IN gckEVENT Event,
|
|
IN gctUINT32 Data
|
|
)
|
|
{
|
|
/* Combine current interrupt status with pending flags. */
|
|
gckOS_AtomSetMask(Event->pending, Data);
|
|
|
|
#if gcdINTERRUPT_STATISTIC
|
|
{
|
|
gctINT j = 0;
|
|
gctINT32 oldValue;
|
|
|
|
for (j = 0; j < gcmCOUNTOF(Event->queues); j++)
|
|
{
|
|
if ((Data & (1 << j)))
|
|
{
|
|
gckOS_AtomDecrement(Event->os,
|
|
Event->interruptCount,
|
|
&oldValue);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Success. */
|
|
return gcvSTATUS_OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** gckEVENT_Notify
|
|
**
|
|
** Process all triggered interrupts.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_Notify(
|
|
IN gckEVENT Event,
|
|
IN gctUINT32 IDs
|
|
)
|
|
{
|
|
gceSTATUS status = gcvSTATUS_OK;
|
|
gctINT i;
|
|
gcsEVENT_QUEUE * queue;
|
|
gctUINT mask = 0;
|
|
gctBOOL acquired = gcvFALSE;
|
|
gctSIGNAL signal;
|
|
gctUINT pending = 0;
|
|
|
|
#if gcmIS_DEBUG(gcdDEBUG_TRACE)
|
|
gctINT eventNumber = 0;
|
|
#endif
|
|
gckVIDMEM_NODE nodeObject;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x IDs=0x%x", Event, IDs);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
|
|
gcmDEBUG_ONLY(
|
|
if (IDs != 0)
|
|
{
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
|
|
{
|
|
if (Event->queues[i].head != gcvNULL)
|
|
{
|
|
gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
|
|
"Queue(%d): stamp=%llu source=%d",
|
|
i,
|
|
Event->queues[i].stamp,
|
|
Event->queues[i].source);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
/* Begin of event handling. */
|
|
Event->notifyState = 0;
|
|
|
|
for (;;)
|
|
{
|
|
gcsEVENT_PTR record;
|
|
|
|
/* Grab the mutex queue. */
|
|
gcmkONERROR(gckOS_AcquireMutex(Event->os,
|
|
Event->eventQueueMutex,
|
|
gcvINFINITE));
|
|
acquired = gcvTRUE;
|
|
|
|
gckOS_AtomGet(Event->os, Event->pending, (gctINT32_PTR)&pending);
|
|
|
|
if (pending == 0)
|
|
{
|
|
/* Release the mutex queue. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
acquired = gcvFALSE;
|
|
|
|
/* No more pending interrupts - done. */
|
|
break;
|
|
}
|
|
|
|
if (pending & 0x80000000)
|
|
{
|
|
gcmkPRINT("AXI BUS ERROR");
|
|
gckHARDWARE_DumpGPUState(Event->kernel->hardware);
|
|
pending &= 0x7FFFFFFF;
|
|
}
|
|
|
|
if ((pending & 0x40000000) && Event->kernel->hardware->mmuVersion)
|
|
{
|
|
#if gcdUSE_MMU_EXCEPTION
|
|
#if gcdALLOC_ON_FAULT
|
|
status = gckHARDWARE_HandleFault(Event->kernel->hardware);
|
|
#endif
|
|
if (gcmIS_ERROR(status))
|
|
{
|
|
/* Dump error is fault can't be handle. */
|
|
gckHARDWARE_DumpMMUException(Event->kernel->hardware);
|
|
|
|
gckHARDWARE_DumpGPUState(Event->kernel->hardware);
|
|
}
|
|
#endif
|
|
|
|
pending &= 0xBFFFFFFF;
|
|
}
|
|
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_INFO, gcvZONE_EVENT,
|
|
gcmSIZEOF(pending),
|
|
"Pending interrupts 0x%x",
|
|
pending
|
|
);
|
|
|
|
queue = gcvNULL;
|
|
|
|
gcmDEBUG_ONLY(
|
|
if (IDs == 0)
|
|
{
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
|
|
{
|
|
if (Event->queues[i].head != gcvNULL)
|
|
{
|
|
gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
|
|
"Queue(%d): stamp=%llu source=%d",
|
|
i,
|
|
Event->queues[i].stamp,
|
|
Event->queues[i].source);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
/* Find the oldest pending interrupt. */
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
|
|
{
|
|
if ((Event->queues[i].head != gcvNULL)
|
|
&& (pending & (1 << i))
|
|
)
|
|
{
|
|
if ((queue == gcvNULL)
|
|
|| (Event->queues[i].stamp < queue->stamp)
|
|
)
|
|
{
|
|
queue = &Event->queues[i];
|
|
mask = 1 << i;
|
|
#if gcmIS_DEBUG(gcdDEBUG_TRACE)
|
|
eventNumber = i;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
if (queue == gcvNULL)
|
|
{
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_ERROR, gcvZONE_EVENT,
|
|
gcmSIZEOF(pending),
|
|
"Interrupts 0x%x are not pending.",
|
|
pending
|
|
);
|
|
|
|
gckOS_AtomClearMask(Event->pending, pending);
|
|
|
|
/* Release the mutex queue. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
acquired = gcvFALSE;
|
|
break;
|
|
}
|
|
|
|
/* Check whether there is a missed interrupt. */
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
|
|
{
|
|
if ((Event->queues[i].head != gcvNULL)
|
|
&& (Event->queues[i].stamp < queue->stamp)
|
|
&& (Event->queues[i].source <= queue->source)
|
|
)
|
|
{
|
|
gcmkTRACE_N(
|
|
gcvLEVEL_ERROR,
|
|
gcmSIZEOF(i) + gcmSIZEOF(Event->queues[i].stamp),
|
|
"Event %d lost (stamp %llu)",
|
|
i, Event->queues[i].stamp
|
|
);
|
|
|
|
/* Use this event instead. */
|
|
queue = &Event->queues[i];
|
|
mask = 0;
|
|
}
|
|
}
|
|
|
|
if (mask != 0)
|
|
{
|
|
#if gcmIS_DEBUG(gcdDEBUG_TRACE)
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_INFO, gcvZONE_EVENT,
|
|
gcmSIZEOF(eventNumber),
|
|
"Processing interrupt %d",
|
|
eventNumber
|
|
);
|
|
#endif
|
|
}
|
|
|
|
gckOS_AtomClearMask(Event->pending, mask);
|
|
|
|
if (!gckHARDWARE_IsFeatureAvailable(Event->kernel->hardware, gcvFEATURE_FENCE_64BIT))
|
|
{
|
|
/* Write out commit stamp.*/
|
|
*(gctUINT64 *)(Event->kernel->command->fence->logical) = queue->commitStamp;
|
|
}
|
|
|
|
/* Signal clients waiting for fence. */
|
|
gcmkVERIFY_OK(gckFENCE_Signal(
|
|
Event->os,
|
|
Event->kernel->command->fence
|
|
));
|
|
|
|
/* Grab the event head. */
|
|
record = queue->head;
|
|
|
|
/* Now quickly clear its event list. */
|
|
queue->head = gcvNULL;
|
|
|
|
/* Increase the number of free events. */
|
|
Event->freeQueueCount++;
|
|
|
|
/* Release the mutex queue. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
acquired = gcvFALSE;
|
|
|
|
/* Walk all events for this interrupt. */
|
|
while (record != gcvNULL)
|
|
{
|
|
gcsEVENT_PTR recordNext;
|
|
#ifndef __QNXNTO__
|
|
gctPOINTER logical;
|
|
#endif
|
|
/* Grab next record. */
|
|
recordNext = record->next;
|
|
|
|
#ifdef __QNXNTO__
|
|
/*
|
|
* Assign record->processID as the pid for this galcore thread.
|
|
* Used in the OS calls which do not take a pid.
|
|
*/
|
|
drv_thread_specific_key_assign(record->processID, 0);
|
|
#endif
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_INFO, gcvZONE_EVENT,
|
|
gcmSIZEOF(record->info.command),
|
|
"Processing event type: %d",
|
|
record->info.command
|
|
);
|
|
|
|
switch (record->info.command)
|
|
{
|
|
case gcvHAL_WRITE_DATA:
|
|
#ifndef __QNXNTO__
|
|
/* Convert physical into logical address. */
|
|
gcmkERR_BREAK(
|
|
gckOS_MapPhysical(Event->os,
|
|
record->info.u.WriteData.address,
|
|
gcmSIZEOF(gctUINT32),
|
|
&logical));
|
|
|
|
/* Write data. */
|
|
gcmkERR_BREAK(
|
|
gckOS_WriteMemory(Event->os,
|
|
logical,
|
|
record->info.u.WriteData.data));
|
|
|
|
/* Unmap the physical memory. */
|
|
gcmkERR_BREAK(
|
|
gckOS_UnmapPhysical(Event->os,
|
|
logical,
|
|
gcmSIZEOF(gctUINT32)));
|
|
#else
|
|
/* Write data. */
|
|
gcmkERR_BREAK(
|
|
gckOS_WriteMemory(Event->os,
|
|
gcmUINT64_TO_PTR(record->info.u.WriteData.address),
|
|
record->info.u.WriteData.data));
|
|
#endif
|
|
break;
|
|
|
|
case gcvHAL_UNLOCK_VIDEO_MEMORY:
|
|
gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
|
|
"gcvHAL_UNLOCK_VIDEO_MEMORY: 0x%x",
|
|
record->info.u.UnlockVideoMemory.node);
|
|
|
|
nodeObject = gcmUINT64_TO_PTR(record->info.u.UnlockVideoMemory.node);
|
|
|
|
/* Unlock, sync'ed. */
|
|
status = gckVIDMEM_NODE_Unlock(
|
|
Event->kernel,
|
|
nodeObject,
|
|
record->processID,
|
|
gcvNULL
|
|
);
|
|
|
|
/* Deref node. */
|
|
status = gckVIDMEM_NODE_Dereference(Event->kernel, nodeObject);
|
|
break;
|
|
|
|
case gcvHAL_SIGNAL:
|
|
signal = gcmUINT64_TO_PTR(record->info.u.Signal.signal);
|
|
gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
|
|
"gcvHAL_SIGNAL: 0x%x",
|
|
signal);
|
|
|
|
#ifdef __QNXNTO__
|
|
if ((record->info.u.Signal.coid == 0)
|
|
&& (record->info.u.Signal.rcvid == 0)
|
|
)
|
|
{
|
|
/* Kernel signal. */
|
|
gcmkERR_BREAK(
|
|
gckOS_SignalPulse(Event->os,
|
|
signal));
|
|
}
|
|
else
|
|
{
|
|
/* User signal. */
|
|
gcmkERR_BREAK(
|
|
gckOS_UserSignal(Event->os,
|
|
signal,
|
|
record->info.u.Signal.rcvid,
|
|
record->info.u.Signal.coid));
|
|
}
|
|
#else
|
|
/* Set signal. */
|
|
if (gcmUINT64_TO_PTR(record->info.u.Signal.process) == gcvNULL)
|
|
{
|
|
/* Kernel signal. */
|
|
gcmkERR_BREAK(
|
|
gckOS_Signal(Event->os,
|
|
signal,
|
|
gcvTRUE));
|
|
}
|
|
else
|
|
{
|
|
/* User signal. */
|
|
gcmkERR_BREAK(
|
|
gckOS_UserSignal(Event->os,
|
|
signal,
|
|
gcmUINT64_TO_PTR(record->info.u.Signal.process)));
|
|
}
|
|
|
|
gcmkASSERT(record->info.u.Signal.auxSignal == 0);
|
|
#endif
|
|
break;
|
|
|
|
case gcvHAL_TIMESTAMP:
|
|
gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
|
|
"gcvHAL_TIMESTAMP: %d %d",
|
|
record->info.u.TimeStamp.timer,
|
|
record->info.u.TimeStamp.request);
|
|
|
|
/* Process the timestamp. */
|
|
switch (record->info.u.TimeStamp.request)
|
|
{
|
|
case 0:
|
|
status = gckOS_GetTime(&Event->kernel->timers[
|
|
record->info.u.TimeStamp.timer].
|
|
stopTime);
|
|
break;
|
|
|
|
case 1:
|
|
status = gckOS_GetTime(&Event->kernel->timers[
|
|
record->info.u.TimeStamp.timer].
|
|
startTime);
|
|
break;
|
|
|
|
default:
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_ERROR, gcvZONE_EVENT,
|
|
gcmSIZEOF(record->info.u.TimeStamp.request),
|
|
"Invalid timestamp request: %d",
|
|
record->info.u.TimeStamp.request
|
|
);
|
|
|
|
status = gcvSTATUS_INVALID_ARGUMENT;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case gcvHAL_COMMIT_DONE:
|
|
break;
|
|
|
|
default:
|
|
/* Invalid argument. */
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_ERROR, gcvZONE_EVENT,
|
|
gcmSIZEOF(record->info.command),
|
|
"Unknown event type: %d",
|
|
record->info.command
|
|
);
|
|
|
|
status = gcvSTATUS_INVALID_ARGUMENT;
|
|
break;
|
|
}
|
|
|
|
/* Make sure there are no errors generated. */
|
|
if (gcmIS_ERROR(status))
|
|
{
|
|
gcmkTRACE_ZONE_N(
|
|
gcvLEVEL_WARNING, gcvZONE_EVENT,
|
|
gcmSIZEOF(status),
|
|
"Event produced status: %d(%s)",
|
|
status, gckOS_DebugStatus2Name(status));
|
|
}
|
|
|
|
/* Free the event. */
|
|
gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record));
|
|
|
|
/* Advance to next record. */
|
|
record = recordNext;
|
|
}
|
|
|
|
gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT,
|
|
"Handled interrupt 0x%x", mask);
|
|
}
|
|
|
|
if (IDs == 0)
|
|
{
|
|
gcmkONERROR(_TryToIdleGPU(Event));
|
|
}
|
|
|
|
/* End of event handling. */
|
|
Event->notifyState = -1;
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
if (acquired)
|
|
{
|
|
/* Release mutex. */
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
}
|
|
|
|
/* End of event handling. */
|
|
Event->notifyState = -1;
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
** gckEVENT_FreeProcess
|
|
**
|
|
** Free all events owned by a particular process ID.
|
|
**
|
|
** INPUT:
|
|
**
|
|
** gckEVENT Event
|
|
** Pointer to an gckEVENT object.
|
|
**
|
|
** gctUINT32 ProcessID
|
|
** Process ID of the process to be freed up.
|
|
**
|
|
** OUTPUT:
|
|
**
|
|
** Nothing.
|
|
*/
|
|
gceSTATUS
|
|
gckEVENT_FreeProcess(
|
|
IN gckEVENT Event,
|
|
IN gctUINT32 ProcessID
|
|
)
|
|
{
|
|
gctSIZE_T i;
|
|
gctBOOL acquired = gcvFALSE;
|
|
gcsEVENT_PTR record, next;
|
|
gceSTATUS status;
|
|
gcsEVENT_PTR deleteHead, deleteTail;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x ProcessID=%d", Event, ProcessID);
|
|
|
|
/* Verify the arguments. */
|
|
gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT);
|
|
|
|
/* Walk through all queues. */
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); ++i)
|
|
{
|
|
if (Event->queues[i].head != gcvNULL)
|
|
{
|
|
/* Grab the event queue mutex. */
|
|
gcmkONERROR(gckOS_AcquireMutex(Event->os,
|
|
Event->eventQueueMutex,
|
|
gcvINFINITE));
|
|
acquired = gcvTRUE;
|
|
|
|
/* Grab the mutex head. */
|
|
record = Event->queues[i].head;
|
|
Event->queues[i].head = gcvNULL;
|
|
Event->queues[i].tail = gcvNULL;
|
|
deleteHead = gcvNULL;
|
|
deleteTail = gcvNULL;
|
|
|
|
while (record != gcvNULL)
|
|
{
|
|
next = record->next;
|
|
if (record->processID == ProcessID)
|
|
{
|
|
if (deleteHead == gcvNULL)
|
|
{
|
|
deleteHead = record;
|
|
}
|
|
else
|
|
{
|
|
deleteTail->next = record;
|
|
}
|
|
|
|
deleteTail = record;
|
|
}
|
|
else
|
|
{
|
|
if (Event->queues[i].head == gcvNULL)
|
|
{
|
|
Event->queues[i].head = record;
|
|
}
|
|
else
|
|
{
|
|
Event->queues[i].tail->next = record;
|
|
}
|
|
|
|
Event->queues[i].tail = record;
|
|
}
|
|
|
|
record->next = gcvNULL;
|
|
record = next;
|
|
}
|
|
|
|
/* Release the mutex queue. */
|
|
gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
acquired = gcvFALSE;
|
|
|
|
/* Loop through the entire list of events. */
|
|
for (record = deleteHead; record != gcvNULL; record = next)
|
|
{
|
|
/* Get the next event record. */
|
|
next = record->next;
|
|
|
|
/* Free the event record. */
|
|
gcmkONERROR(gckEVENT_FreeRecord(Event, record));
|
|
}
|
|
}
|
|
}
|
|
|
|
gcmkONERROR(_TryToIdleGPU(Event));
|
|
|
|
/* Success. */
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
|
|
OnError:
|
|
/* Release the event queue mutex. */
|
|
if (acquired)
|
|
{
|
|
gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex));
|
|
}
|
|
|
|
/* Return the status. */
|
|
gcmkFOOTER();
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
_PrintRecord(
|
|
gcsEVENT_PTR record
|
|
)
|
|
{
|
|
switch (record->info.command)
|
|
{
|
|
case gcvHAL_WRITE_DATA:
|
|
gcmkPRINT(" gcvHAL_WRITE_DATA");
|
|
break;
|
|
|
|
case gcvHAL_UNLOCK_VIDEO_MEMORY:
|
|
gcmkPRINT(" gcvHAL_UNLOCK_VIDEO_MEMORY");
|
|
break;
|
|
|
|
case gcvHAL_SIGNAL:
|
|
gcmkPRINT(" gcvHAL_SIGNAL process=%lld signal=0x%llx",
|
|
record->info.u.Signal.process,
|
|
record->info.u.Signal.signal);
|
|
break;
|
|
|
|
case gcvHAL_TIMESTAMP:
|
|
gcmkPRINT(" gcvHAL_TIMESTAMP");
|
|
break;
|
|
|
|
case gcvHAL_COMMIT_DONE:
|
|
gcmkPRINT(" gcvHAL_COMMIT_DONE");
|
|
break;
|
|
|
|
case gcvHAL_DESTROY_MMU:
|
|
gcmkPRINT(" gcvHAL_DESTORY_MMU mmu=%p",
|
|
gcmUINT64_TO_PTR(record->info.u.DestroyMmu.mmu));
|
|
|
|
break;
|
|
default:
|
|
gcmkPRINT(" Illegal Event %d", record->info.command);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
** gckEVENT_Dump
|
|
**
|
|
** Dump record in event queue when stuck happens.
|
|
** No protection for the event queue.
|
|
**/
|
|
gceSTATUS
|
|
gckEVENT_Dump(
|
|
IN gckEVENT Event
|
|
)
|
|
{
|
|
gcsEVENT_QUEUE_PTR queueHead = Event->queueHead;
|
|
gcsEVENT_QUEUE_PTR queue;
|
|
gcsEVENT_PTR record = gcvNULL;
|
|
gctINT i;
|
|
#if gcdINTERRUPT_STATISTIC
|
|
gctINT32 pendingInterrupt;
|
|
gctUINT32 intrAcknowledge;
|
|
#endif
|
|
gctINT32 pending;
|
|
|
|
gcmkHEADER_ARG("Event=0x%x", Event);
|
|
|
|
gcmkPRINT("**************************\n");
|
|
gcmkPRINT("*** EVENT STATE DUMP ***\n");
|
|
gcmkPRINT("**************************\n");
|
|
|
|
gcmkPRINT(" Unsumbitted Event:");
|
|
while(queueHead)
|
|
{
|
|
queue = queueHead;
|
|
record = queueHead->head;
|
|
|
|
gcmkPRINT(" [%p]:", queue);
|
|
while(record)
|
|
{
|
|
_PrintRecord(record);
|
|
record = record->next;
|
|
}
|
|
|
|
if (queueHead == Event->queueTail)
|
|
{
|
|
queueHead = gcvNULL;
|
|
}
|
|
else
|
|
{
|
|
queueHead = queueHead->next;
|
|
}
|
|
}
|
|
|
|
gcmkPRINT(" Untriggered Event:");
|
|
for (i = 0; i < gcmCOUNTOF(Event->queues); i++)
|
|
{
|
|
queue = &Event->queues[i];
|
|
record = queue->head;
|
|
|
|
gcmkPRINT(" [%d]:", i);
|
|
while(record)
|
|
{
|
|
_PrintRecord(record);
|
|
record = record->next;
|
|
}
|
|
}
|
|
|
|
#if gcdINTERRUPT_STATISTIC
|
|
gckOS_AtomGet(Event->os, Event->interruptCount, &pendingInterrupt);
|
|
gcmkPRINT(" Number of Pending Interrupt: %d", pendingInterrupt);
|
|
|
|
if (Event->kernel->recovery == 0)
|
|
{
|
|
gceSTATUS status;
|
|
|
|
status = gckOS_ReadRegisterEx(
|
|
Event->os,
|
|
Event->kernel->core,
|
|
0x10,
|
|
&intrAcknowledge
|
|
);
|
|
if (gcmIS_ERROR(status))
|
|
{
|
|
gcmkPRINT(" READ INTR_ACKNOWLEDGE ERROR!");
|
|
}
|
|
else
|
|
{
|
|
gcmkPRINT(" INTR_ACKNOWLEDGE=0x%x", intrAcknowledge);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
gcmkPRINT(" Notify State=%d", Event->notifyState);
|
|
|
|
gckOS_AtomGet(Event->os, Event->pending, &pending);
|
|
|
|
gcmkPRINT(" Pending=0x%x", pending);
|
|
|
|
gcmkFOOTER_NO();
|
|
return gcvSTATUS_OK;
|
|
}
|