blob: 93a914c9d79a785aa20cd85a81ba581f798c4971 [file] [log] [blame]
/****************************************************************************
*
* 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"
#if gcdENABLE_VG
#define _GC_OBJ_ZONE gcvZONE_VG
/******************************************************************************\
******************************* gckKERNEL API Code ******************************
\******************************************************************************/
/*******************************************************************************
**
** gckKERNEL_Construct
**
** Construct a new gckKERNEL object.
**
** INPUT:
**
** gckOS Os
** Pointer to an gckOS object.
**
** IN gctPOINTER Context
** Pointer to a driver defined context.
**
** OUTPUT:
**
** gckKERNEL * Kernel
** Pointer to a variable that will hold the pointer to the gckKERNEL
** object.
*/
gceSTATUS gckVGKERNEL_Construct(
IN gckOS Os,
IN gctPOINTER Context,
IN gckKERNEL inKernel,
OUT gckVGKERNEL * Kernel
)
{
gceSTATUS status;
gckVGKERNEL kernel = gcvNULL;
gcmkHEADER_ARG("Os=0x%x Context=0x%x", Os, Context);
/* Verify the arguments. */
gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
gcmkVERIFY_ARGUMENT(Kernel != gcvNULL);
do
{
/* Allocate the gckKERNEL object. */
gcmkERR_BREAK(gckOS_Allocate(
Os,
sizeof(struct _gckVGKERNEL),
(gctPOINTER *) &kernel
));
/* Initialize the gckKERNEL object. */
kernel->object.type = gcvOBJ_KERNEL;
kernel->os = Os;
kernel->context = Context;
kernel->hardware = gcvNULL;
kernel->interrupt = gcvNULL;
kernel->command = gcvNULL;
kernel->mmu = gcvNULL;
kernel->kernel = inKernel;
/* Construct the gckVGHARDWARE object. */
gcmkERR_BREAK(gckVGHARDWARE_Construct(
Os, &kernel->hardware
));
/* Set pointer to gckKERNEL object in gckVGHARDWARE object. */
kernel->hardware->kernel = kernel;
/* Construct the gckVGINTERRUPT object. */
gcmkERR_BREAK(gckVGINTERRUPT_Construct(
kernel, &kernel->interrupt
));
/* Construct the gckVGCOMMAND object. */
gcmkERR_BREAK(gckVGCOMMAND_Construct(
kernel, gcmKB2BYTES(8), gcmKB2BYTES(2), &kernel->command
));
/* Construct the gckVGMMU object. */
gcmkERR_BREAK(gckVGMMU_Construct(
kernel, gcmKB2BYTES(gcdGC355_VGMMU_MEMORY_SIZE_KB), &kernel->mmu
));
/* Return pointer to the gckKERNEL object. */
*Kernel = kernel;
gcmkFOOTER_ARG("*Kernel=0x%x", *Kernel);
/* Success. */
return gcvSTATUS_OK;
}
while (gcvFALSE);
/* Roll back. */
if (kernel != gcvNULL)
{
if (kernel->mmu != gcvNULL)
{
gcmkVERIFY_OK(gckVGMMU_Destroy(kernel->mmu));
}
if (kernel->command != gcvNULL)
{
gcmkVERIFY_OK(gckVGCOMMAND_Destroy(kernel->command));
}
if (kernel->interrupt != gcvNULL)
{
gcmkVERIFY_OK(gckVGINTERRUPT_Destroy(kernel->interrupt));
}
if (kernel->hardware != gcvNULL)
{
gcmkVERIFY_OK(gckVGHARDWARE_Destroy(kernel->hardware));
}
gcmkVERIFY_OK(gckOS_Free(Os, kernel));
}
gcmkFOOTER();
/* Return status. */
return status;
}
/*******************************************************************************
**
** gckKERNEL_Destroy
**
** Destroy an gckKERNEL object.
**
** INPUT:
**
** gckKERNEL Kernel
** Pointer to an gckKERNEL object to destroy.
**
** OUTPUT:
**
** Nothing.
*/
gceSTATUS gckVGKERNEL_Destroy(
IN gckVGKERNEL Kernel
)
{
gceSTATUS status;
gcmkHEADER_ARG("Kernel=0x%x", Kernel);
/* Verify the arguments. */
gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
do
{
/* Destroy the gckVGMMU object. */
if (Kernel->mmu != gcvNULL)
{
gcmkERR_BREAK(gckVGMMU_Destroy(Kernel->mmu));
Kernel->mmu = gcvNULL;
}
/* Destroy the gckVGCOMMAND object. */
if (Kernel->command != gcvNULL)
{
gcmkERR_BREAK(gckVGCOMMAND_Destroy(Kernel->command));
Kernel->command = gcvNULL;
}
/* Destroy the gckVGINTERRUPT object. */
if (Kernel->interrupt != gcvNULL)
{
gcmkERR_BREAK(gckVGINTERRUPT_Destroy(Kernel->interrupt));
Kernel->interrupt = gcvNULL;
}
/* Destroy the gckVGHARDWARE object. */
if (Kernel->hardware != gcvNULL)
{
gcmkERR_BREAK(gckVGHARDWARE_Destroy(Kernel->hardware));
Kernel->hardware = gcvNULL;
}
/* Mark the gckKERNEL object as unknown. */
Kernel->object.type = gcvOBJ_UNKNOWN;
/* Free the gckKERNEL object. */
gcmkERR_BREAK(gckOS_Free(Kernel->os, Kernel));
}
while (gcvFALSE);
gcmkFOOTER();
/* Return status. */
return status;
}
/*******************************************************************************
**
** gckKERNEL_Dispatch
**
** Dispatch a command received from the user HAL layer.
**
** INPUT:
**
** gckKERNEL Kernel
** Pointer to an gckKERNEL object.
**
** gcsHAL_INTERFACE * Interface
** Pointer to a gcsHAL_INTERFACE structure that defines the command to
** be dispatched.
**
** OUTPUT:
**
** gcsHAL_INTERFACE * Interface
** Pointer to a gcsHAL_INTERFACE structure that receives any data to be
** returned.
*/
gceSTATUS gckVGKERNEL_Dispatch(
IN gckKERNEL Kernel,
IN gctBOOL FromUser,
IN OUT gcsHAL_INTERFACE * Interface
)
{
gceSTATUS status;
gcsHAL_INTERFACE * kernelInterface = Interface;
gctUINT32 processID;
gckKERNEL kernel = Kernel;
gctPHYS_ADDR physical = gcvNULL;
gctPOINTER logical = gcvNULL;
gctSIZE_T bytes = 0;
gctBOOL powerMutexAcquired = gcvFALSE;
gcmkHEADER_ARG("Kernel=0x%x Interface=0x%x ", Kernel, Interface);
/* Verify the arguments. */
gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
gcmkVERIFY_ARGUMENT(Interface != gcvNULL);
gcmkONERROR(gckOS_GetProcessID(&processID));
/* Dispatch on command. */
switch (Interface->command)
{
case gcvHAL_QUERY_CHIP_IDENTITY:
/* Query chip identity. */
gcmkERR_BREAK(gckVGHARDWARE_QueryChipIdentity(
Kernel->vg->hardware,
&kernelInterface->u.QueryChipIdentity.chipModel,
&kernelInterface->u.QueryChipIdentity.chipRevision,
&kernelInterface->u.QueryChipIdentity.productID,
&kernelInterface->u.QueryChipIdentity.ecoID,
&kernelInterface->u.QueryChipIdentity.customerID,
&kernelInterface->u.QueryChipIdentity.chipFeatures,
&kernelInterface->u.QueryChipIdentity.chipMinorFeatures,
&kernelInterface->u.QueryChipIdentity.chipMinorFeatures2
));
break;
case gcvHAL_QUERY_COMMAND_BUFFER:
/* Query command buffer information. */
gcmkERR_BREAK(gckKERNEL_QueryCommandBuffer(
Kernel,
&kernelInterface->u.QueryCommandBuffer.information
));
break;
case gcvHAL_ALLOCATE_NON_PAGED_MEMORY:
bytes = (gctSIZE_T) kernelInterface->u.AllocateNonPagedMemory.bytes;
/* Allocate non-paged memory. */
gcmkERR_BREAK(gckOS_AllocateNonPagedMemory(
Kernel->os,
gcvTRUE,
&bytes,
&physical,
&logical
));
kernelInterface->u.AllocateNonPagedMemory.bytes = bytes;
kernelInterface->u.AllocateNonPagedMemory.logical = gcmPTR_TO_UINT64(logical);
kernelInterface->u.AllocateNonPagedMemory.physical = gcmPTR_TO_NAME(physical);
break;
case gcvHAL_FREE_NON_PAGED_MEMORY:
physical = gcmNAME_TO_PTR(kernelInterface->u.FreeNonPagedMemory.physical);
/* Unmap user logical out of physical memory first. */
gcmkERR_BREAK(gckOS_UnmapUserLogical(
Kernel->os,
physical,
(gctSIZE_T) kernelInterface->u.FreeNonPagedMemory.bytes,
gcmUINT64_TO_PTR(kernelInterface->u.FreeNonPagedMemory.logical)));
/* Free non-paged memory. */
gcmkERR_BREAK(gckOS_FreeNonPagedMemory(
Kernel->os,
(gctSIZE_T) kernelInterface->u.FreeNonPagedMemory.bytes,
physical,
gcmUINT64_TO_PTR(kernelInterface->u.FreeNonPagedMemory.logical)
));
gcmRELEASE_NAME(kernelInterface->u.FreeNonPagedMemory.physical);
break;
case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY:
bytes = (gctSIZE_T) kernelInterface->u.AllocateContiguousMemory.bytes;
/* Allocate contiguous memory. */
gcmkERR_BREAK(gckOS_AllocateContiguous(
Kernel->os,
gcvTRUE,
&bytes,
&physical,
&logical
));
kernelInterface->u.AllocateContiguousMemory.bytes = bytes;
kernelInterface->u.AllocateContiguousMemory.logical = gcmPTR_TO_UINT64(logical);
kernelInterface->u.AllocateContiguousMemory.physical = gcmPTR_TO_NAME(physical);
break;
case gcvHAL_FREE_CONTIGUOUS_MEMORY:
physical = gcmNAME_TO_PTR(kernelInterface->u.FreeContiguousMemory.physical);
/* Unmap user logical out of physical memory first. */
gcmkERR_BREAK(gckOS_UnmapUserLogical(
Kernel->os,
physical,
(gctSIZE_T) kernelInterface->u.FreeContiguousMemory.bytes,
gcmUINT64_TO_PTR(kernelInterface->u.FreeContiguousMemory.logical)
));
/* Free contiguous memory. */
gcmkERR_BREAK(gckOS_FreeContiguous(
Kernel->os,
physical,
gcmUINT64_TO_PTR(kernelInterface->u.FreeContiguousMemory.logical),
(gctSIZE_T) kernelInterface->u.FreeContiguousMemory.bytes
));
gcmRELEASE_NAME(kernelInterface->u.FreeContiguousMemory.physical);
break;
case gcvHAL_ALLOCATE_VIDEO_MEMORY:
gcmkERR_BREAK(gcvSTATUS_NOT_SUPPORTED);
break;
case gcvHAL_MAP_MEMORY:
/* Map memory. */
gcmkERR_BREAK(gckKERNEL_MapMemory(
Kernel,
gcmINT2PTR(kernelInterface->u.MapMemory.physical),
(gctSIZE_T) kernelInterface->u.MapMemory.bytes,
&logical
));
kernelInterface->u.MapMemory.logical = gcmPTR_TO_UINT64(logical);
break;
case gcvHAL_UNMAP_MEMORY:
/* Unmap memory. */
gcmkERR_BREAK(gckKERNEL_UnmapMemory(
Kernel,
gcmINT2PTR(kernelInterface->u.MapMemory.physical),
(gctSIZE_T) kernelInterface->u.MapMemory.bytes,
gcmUINT64_TO_PTR(kernelInterface->u.MapMemory.logical),
processID
));
break;
case gcvHAL_MAP_USER_MEMORY:
gcmkONERROR(gcvSTATUS_NOT_SUPPORTED);
break;
case gcvHAL_UNMAP_USER_MEMORY:
gcmkONERROR(gcvSTATUS_NOT_SUPPORTED);
break;
case gcvHAL_LOCK_VIDEO_MEMORY:
gcmkONERROR(gckKERNEL_LockVideoMemory(Kernel, gcvCORE_VG, processID, FromUser, Interface));
break;
case gcvHAL_BOTTOM_HALF_UNLOCK_VIDEO_MEMORY:
gcmkERR_BREAK(gckKERNEL_BottomHalfUnlockVideoMemory(Kernel, processID,
kernelInterface->u.BottomHalfUnlockVideoMemory.node));
break;
case gcvHAL_USER_SIGNAL:
#if !USE_NEW_LINUX_SIGNAL
/* Dispatch depends on the user signal subcommands. */
switch(Interface->u.UserSignal.command)
{
case gcvUSER_SIGNAL_CREATE:
/* Create a signal used in the user space. */
gcmkERR_BREAK(
gckOS_CreateUserSignal(Kernel->os,
Interface->u.UserSignal.manualReset,
&Interface->u.UserSignal.id));
gcmkVERIFY_OK(
gckKERNEL_AddProcessDB(Kernel,
processID, gcvDB_SIGNAL,
gcmINT2PTR(Interface->u.UserSignal.id),
gcvNULL,
0));
break;
case gcvUSER_SIGNAL_DESTROY:
gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(
Kernel,
processID, gcvDB_SIGNAL,
gcmINT2PTR(Interface->u.UserSignal.id)));
/* Destroy the signal. */
gcmkERR_BREAK(
gckOS_DestroyUserSignal(Kernel->os,
Interface->u.UserSignal.id));
break;
case gcvUSER_SIGNAL_SIGNAL:
/* Signal the signal. */
gcmkERR_BREAK(
gckOS_SignalUserSignal(Kernel->os,
Interface->u.UserSignal.id,
Interface->u.UserSignal.state));
break;
case gcvUSER_SIGNAL_WAIT:
/* Wait on the signal. */
status = gckOS_WaitUserSignal(Kernel->os,
Interface->u.UserSignal.id,
Interface->u.UserSignal.wait);
break;
default:
/* Invalid user signal command. */
gcmkERR_BREAK(gcvSTATUS_INVALID_ARGUMENT);
}
#endif
break;
case gcvHAL_COMMIT:
/* Commit a command and context buffer. */
gcmkERR_BREAK(gckVGCOMMAND_Commit(
Kernel->vg->command,
gcmUINT64_TO_PTR(kernelInterface->u.VGCommit.context),
gcmUINT64_TO_PTR(kernelInterface->u.VGCommit.queue),
kernelInterface->u.VGCommit.entryCount,
gcmUINT64_TO_PTR(kernelInterface->u.VGCommit.taskTable)
));
break;
case gcvHAL_GET_BASE_ADDRESS:
/* Get base address. */
gcmkONERROR(
gckOS_GetBaseAddress(Kernel->os,
&Interface->u.GetBaseAddress.baseAddress));
break;
case gcvHAL_EVENT_COMMIT:
gcmkERR_BREAK(gcvSTATUS_NOT_SUPPORTED);
break;
case gcvHAL_READ_REGISTER:
#if gcdREGISTER_ACCESS_FROM_USER
{
gceCHIPPOWERSTATE power;
gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->vg->hardware->powerMutex, gcvINFINITE));
powerMutexAcquired = gcvTRUE;
gcmkONERROR(gckVGHARDWARE_QueryPowerManagementState(Kernel->vg->hardware,
&power));
if (power == gcvPOWER_ON)
{
/* Read a register. */
gcmkONERROR(gckOS_ReadRegisterEx(
Kernel->os,
Kernel->core,
Interface->u.ReadRegisterData.address,
&Interface->u.ReadRegisterData.data));
}
else
{
/* Chip is in power-state. */
Interface->u.ReadRegisterData.data = 0;
status = gcvSTATUS_CHIP_NOT_READY;
}
gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->vg->hardware->powerMutex));
powerMutexAcquired = gcvFALSE;
}
#else
/* No access from user land to read registers. */
Interface->u.ReadRegisterData.data = 0;
status = gcvSTATUS_NOT_SUPPORTED;
#endif
break;
case gcvHAL_WRITE_REGISTER:
#if gcdREGISTER_ACCESS_FROM_USER
{
gceCHIPPOWERSTATE power;
gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->vg->hardware->powerMutex, gcvINFINITE));
powerMutexAcquired = gcvTRUE;
gcmkONERROR(gckVGHARDWARE_QueryPowerManagementState(Kernel->vg->hardware,
&power));
if (power == gcvPOWER_ON)
{
/* Write a register. */
gcmkONERROR(
gckOS_WriteRegisterEx(Kernel->os,
Kernel->core,
Interface->u.WriteRegisterData.address,
Interface->u.WriteRegisterData.data));
}
else
{
/* Chip is in power-state. */
Interface->u.WriteRegisterData.data = 0;
status = gcvSTATUS_CHIP_NOT_READY;
}
gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->vg->hardware->powerMutex));
powerMutexAcquired = gcvFALSE;
}
#else
/* No access from user land to write registers. */
status = gcvSTATUS_NOT_SUPPORTED;
#endif
break;
default:
/* Invalid command, try gckKERNEL_Dispatch */
status = gckKERNEL_Dispatch(Kernel, gcvNULL, gcvTRUE, Interface);
}
OnError:
/* Save status. */
kernelInterface->status = status;
if (powerMutexAcquired == gcvTRUE)
{
gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->vg->hardware->powerMutex));
}
gcmkFOOTER();
/* Return the status. */
return status;
}
/*******************************************************************************
**
** gckKERNEL_QueryCommandBuffer
**
** Query command buffer attributes.
**
** INPUT:
**
** gckKERNEL Kernel
** Pointer to an gckVGHARDWARE object.
**
** OUTPUT:
**
** gcsCOMMAND_BUFFER_INFO_PTR Information
** Pointer to the information structure to receive buffer attributes.
*/
gceSTATUS
gckKERNEL_QueryCommandBuffer(
IN gckKERNEL Kernel,
OUT gcsCOMMAND_BUFFER_INFO_PTR Information
)
{
gceSTATUS status;
gcmkHEADER_ARG("Kernel=0x%x *Pool=0x%x",
Kernel, Information);
/* Verify the arguments. */
gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
/* Get the information. */
status = gckVGCOMMAND_QueryCommandBuffer(Kernel->vg->command, Information);
gcmkFOOTER();
/* Return status. */
return status;
}
#endif /* gcdENABLE_VG */