blob: efd6363f5a1e060c6ef89d1334e0a8c58d28a8aa [file] [log] [blame]
/****************************************************************************
*
* Copyright (C) 2005 - 2014 by Vivante Corp.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*****************************************************************************/
#include <linux/device.h>
#include <linux/slab.h>
#include "gc_hal_kernel_linux.h"
#include "gc_hal_driver.h"
/*####modified for marvell-bg2*/
#if USE_GALOIS_SHM
#include "shm_api.h"
#endif
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
/*####end for marvell-bg2*/
#if USE_PLATFORM_DRIVER
# include <linux/platform_device.h>
#endif
#ifdef CONFIG_PXA_DVFM
# include <mach/dvfm.h>
# include <mach/pxa3xx_dvfm.h>
#endif
/* Zone used for header/footer. */
#define _GC_OBJ_ZONE gcvZONE_DRIVER
MODULE_DESCRIPTION("Vivante Graphics Driver");
MODULE_LICENSE("GPL");
static struct class* gpuClass;
gckGALDEVICE galDevice;
static uint major = 199;
module_param(major, uint, 0644);
/*####modified for marvell-bg2*/
#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY
#if SOC_BERLIN2CDP
static int irqLine3D0 = 52;
#else
static int irqLine = 37;
#endif
module_param(irqLine3D0, int, 0644);
static ulong registerMemBase3D0 = 0xf7bc0000;
module_param(registerMemBase3D0, ulong, 0644);
static ulong registerMemSize3D0 = 2 << 10;
module_param(registerMemSize3D0, ulong, 0644);
static int irqLine3D1 = -1;
module_param(irqLine3D1, int, 0644);
static ulong registerMemBase3D1 = 0;
module_param(registerMemBase3D1, ulong, 0644);
static ulong registerMemSize3D1 = 2 << 10;
module_param(registerMemSize3D1, ulong, 0644);
#else
#if SOC_BERLIN2CDP
static int irqLine = 52;
#else
static int irqLine = 37;
#endif
module_param(irqLine, int, 0644);
static ulong registerMemBase = 0xf7bc0000;
module_param(registerMemBase, ulong, 0644);
static ulong registerMemSize = 2 << 10;
module_param(registerMemSize, ulong, 0644);
#endif
/*####end for marvell-bg2*/
static int irqLine2D = -1;
module_param(irqLine2D, int, 0644);
static ulong registerMemBase2D = 0x00000000;
module_param(registerMemBase2D, ulong, 0644);
static ulong registerMemSize2D = 2 << 10;
module_param(registerMemSize2D, ulong, 0644);
static int irqLineVG = -1;
module_param(irqLineVG, int, 0644);
static ulong registerMemBaseVG = 0x00000000;
module_param(registerMemBaseVG, ulong, 0644);
static ulong registerMemSizeVG = 2 << 10;
module_param(registerMemSizeVG, ulong, 0644);
/*####modified for marvell-bg2*/
static ulong contiguousSize = 512;
/*####end for marvell-bg2*/
module_param(contiguousSize, ulong, 0644);
static ulong contiguousBase = 0;
module_param(contiguousBase, ulong, 0644);
static ulong bankSize = 0;
module_param(bankSize, ulong, 0644);
static int fastClear = -1;
module_param(fastClear, int, 0644);
static int compression = -1;
module_param(compression, int, 0644);
static int powerManagement = 1;
module_param(powerManagement, int, 0644);
static int gpuProfiler = 0;
module_param(gpuProfiler, int, 0644);
static int signal = 48;
module_param(signal, int, 0644);
static ulong baseAddress = 0;
module_param(baseAddress, ulong, 0644);
/*####modified for marvell-bg2*/
static ulong physSize = 0xfff00000;
module_param(physSize, ulong, 0644);
/*####end for marvell-bg2*/
static uint logFileSize = 0;
module_param(logFileSize,uint, 0644);
static uint recovery = 0;
module_param(recovery, uint, 0644);
MODULE_PARM_DESC(recovery, "Recover GPU from stuck (1: Enable, 0: Disable)");
/* Middle needs about 40KB buffer, Maximal may need more than 200KB buffer. */
static uint stuckDump = 2;
module_param(stuckDump, uint, 0644);
MODULE_PARM_DESC(stuckDump, "Level of stuck dump content (1: Minimal, 2: Middle, 3: Maximal)");
/*####modified for marvell-bg2*/
static int showArgs = 1;
module_param(showArgs, int, 0644);
int debugSHM = 0;
module_param(debugSHM, int, 0644);
int maxSHMSize = 536870912; /* set to 512MB */
module_param(maxSHMSize, int, 0644);
ulong coreClkRegister2D = 0;
module_param(coreClkRegister2D, ulong, 0644);
ulong coreClkRegister3D = 0;
module_param(coreClkRegister3D, ulong, 0644);
ulong sysClkRegister3D = 0;
module_param(sysClkRegister3D, ulong, 0644);
ulong coreClkBitfield2D = 0;
module_param(coreClkBitfield2D, ulong, 0644);
ulong coreClkBitfield3D = 0;
module_param(coreClkBitfield3D, ulong, 0644);
ulong sysClkBitfield3D = 0;
module_param(sysClkBitfield3D, ulong, 0644);
/*####end for marvell-bg2*/
#if ENABLE_GPU_CLOCK_BY_DRIVER
unsigned long coreClock = 156000000;
module_param(coreClock, ulong, 0644);
#endif
static int drv_open(
struct inode* inode,
struct file* filp
);
static int drv_release(
struct inode* inode,
struct file* filp
);
static long drv_ioctl(
struct file* filp,
unsigned int ioctlCode,
unsigned long arg
);
static int drv_mmap(
struct file* filp,
struct vm_area_struct* vma
);
static struct file_operations driver_fops =
{
.owner = THIS_MODULE,
.open = drv_open,
.release = drv_release,
.unlocked_ioctl = drv_ioctl,
#ifdef HAVE_COMPAT_IOCTL
.compat_ioctl = drv_ioctl,
#endif
.mmap = drv_mmap,
};
void
gckOS_DumpParam(
void
)
{
printk("gal3d options:\n");
#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY
printk(" irqLine3D0 = %d\n", irqLine3D0);
printk(" registerMemBase3D0 = 0x%08lX\n", registerMemBase3D0);
printk(" registerMemSize3D0 = 0x%08lX\n", registerMemSize3D0);
if (irqLine3D1 != -1)
{
printk(" irqLine3D1 = %d\n", irqLine3D1);
printk(" registerMemBase3D1 = 0x%08lX\n", registerMemBase3D1);
printk(" registerMemSize3D1 = 0x%08lX\n", registerMemSize3D1);
}
#else
printk(" irqLine = %d\n", irqLine);
printk(" registerMemBase = 0x%08lX\n", registerMemBase);
printk(" registerMemSize = 0x%08lX\n", registerMemSize);
#endif
if (irqLine2D != -1)
{
printk(" irqLine2D = %d\n", irqLine2D);
printk(" registerMemBase2D = 0x%08lX\n", registerMemBase2D);
printk(" registerMemSize2D = 0x%08lX\n", registerMemSize2D);
}
if (irqLineVG != -1)
{
printk(" irqLineVG = %d\n", irqLineVG);
printk(" registerMemBaseVG = 0x%08lX\n", registerMemBaseVG);
printk(" registerMemSizeVG = 0x%08lX\n", registerMemSizeVG);
}
printk(" contiguousSize = %ld\n", contiguousSize);
printk(" contiguousBase = 0x%08lX\n", contiguousBase);
printk(" bankSize = 0x%08lX\n", bankSize);
printk(" fastClear = %d\n", fastClear);
printk(" compression = %d\n", compression);
printk(" signal = %d\n", signal);
printk(" powerManagement = %d\n", powerManagement);
printk(" baseAddress = 0x%08lX\n", baseAddress);
printk(" physSize = 0x%08lX\n", physSize);
printk(" logFileSize = %d KB \n", logFileSize);
printk(" recovery = %d\n", recovery);
printk(" stuckDump = %d\n", stuckDump);
#if ENABLE_GPU_CLOCK_BY_DRIVER
printk(" coreClock = %lu\n", coreClock);
#endif
printk(" gpuProfiler = %d\n", gpuProfiler);
}
int drv_open(
struct inode* inode,
struct file* filp
)
{
gceSTATUS status;
gctBOOL attached = gcvFALSE;
gcsHAL_PRIVATE_DATA_PTR data = gcvNULL;
gctINT i;
gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);
if (filp == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): filp is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
data = kmalloc(sizeof(gcsHAL_PRIVATE_DATA), GFP_KERNEL | __GFP_NOWARN);
if (data == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): private_data is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
}
data->device = galDevice;
data->mappedMemory = gcvNULL;
data->contiguousLogical = gcvNULL;
gcmkONERROR(gckOS_GetProcessID(&data->pidOpen));
/* Attached the process. */
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (galDevice->kernels[i] != gcvNULL)
{
gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE));
}
}
attached = gcvTRUE;
if (!galDevice->contiguousMapped)
{
if (galDevice->contiguousPhysical != gcvNULL)
{
gcmkONERROR(gckOS_MapMemory(
galDevice->os,
galDevice->contiguousPhysical,
galDevice->contiguousSize,
&data->contiguousLogical
));
}
}
filp->private_data = data;
/* Success. */
gcmkFOOTER_NO();
return 0;
OnError:
if (data != gcvNULL)
{
if (data->contiguousLogical != gcvNULL)
{
gcmkVERIFY_OK(gckOS_UnmapMemory(
galDevice->os,
galDevice->contiguousPhysical,
galDevice->contiguousSize,
data->contiguousLogical
));
}
kfree(data);
}
if (attached)
{
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (galDevice->kernels[i] != gcvNULL)
{
gcmkVERIFY_OK(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvFALSE));
}
}
}
gcmkFOOTER();
return -ENOTTY;
}
int drv_release(
struct inode* inode,
struct file* filp
)
{
gceSTATUS status;
gcsHAL_PRIVATE_DATA_PTR data;
gckGALDEVICE device;
gctINT i;
gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp);
if (filp == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): filp is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
data = filp->private_data;
if (data == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): private_data is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
device = data->device;
if (device == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): device is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
if (!device->contiguousMapped)
{
if (data->contiguousLogical != gcvNULL)
{
gcmkONERROR(gckOS_UnmapMemoryEx(
galDevice->os,
galDevice->contiguousPhysical,
galDevice->contiguousSize,
data->contiguousLogical,
data->pidOpen
));
data->contiguousLogical = gcvNULL;
}
}
/* A process gets detached. */
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (galDevice->kernels[i] != gcvNULL)
{
gcmkONERROR(gckKERNEL_AttachProcessEx(galDevice->kernels[i], gcvFALSE, data->pidOpen));
}
}
kfree(data);
filp->private_data = NULL;
/* Success. */
gcmkFOOTER_NO();
return 0;
OnError:
gcmkFOOTER();
return -ENOTTY;
}
long drv_ioctl(
struct file* filp,
unsigned int ioctlCode,
unsigned long arg
)
{
gceSTATUS status;
gcsHAL_INTERFACE iface;
gctUINT32 copyLen;
DRIVER_ARGS drvArgs;
gckGALDEVICE device;
gcsHAL_PRIVATE_DATA_PTR data;
gctINT32 i, count;
gckVIDMEM_NODE nodeObject;
gcmkHEADER_ARG(
"filp=0x%08X ioctlCode=0x%08X arg=0x%08X",
filp, ioctlCode, arg
);
if (filp == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): filp is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
data = filp->private_data;
if (data == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): private_data is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
device = data->device;
if (device == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): device is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
if ((ioctlCode != IOCTL_GCHAL_INTERFACE)
&& (ioctlCode != IOCTL_GCHAL_KERNEL_INTERFACE)
)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): unknown command %d\n",
__FUNCTION__, __LINE__,
ioctlCode
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
/* Get the drvArgs. */
copyLen = copy_from_user(
&drvArgs, (void *) arg, sizeof(DRIVER_ARGS)
);
if (copyLen != 0)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): error copying of the input arguments.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
/* Now bring in the gcsHAL_INTERFACE structure. */
if ((drvArgs.InputBufferSize != sizeof(gcsHAL_INTERFACE))
|| (drvArgs.OutputBufferSize != sizeof(gcsHAL_INTERFACE))
)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): input or/and output structures are invalid.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
copyLen = copy_from_user(
&iface, gcmUINT64_TO_PTR(drvArgs.InputBuffer), sizeof(gcsHAL_INTERFACE)
);
if (copyLen != 0)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): error copying of input HAL interface.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
if (iface.command == gcvHAL_CHIP_INFO)
{
count = 0;
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL)
{
#if gcdENABLE_VG
if (i == gcvCORE_VG)
{
iface.u.ChipInfo.types[count] = gcvHARDWARE_VG;
}
else
#endif
{
gcmkVERIFY_OK(gckHARDWARE_GetType(device->kernels[i]->hardware,
&iface.u.ChipInfo.types[count]));
}
count++;
}
}
iface.u.ChipInfo.count = count;
iface.status = status = gcvSTATUS_OK;
}
else
{
if (iface.hardwareType > 7)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): unknown hardwareType %d\n",
__FUNCTION__, __LINE__,
iface.hardwareType
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
#if gcdENABLE_VG
if (device->coreMapping[iface.hardwareType] == gcvCORE_VG)
{
status = gckVGKERNEL_Dispatch(device->kernels[gcvCORE_VG],
(ioctlCode == IOCTL_GCHAL_INTERFACE),
&iface);
}
else
#endif
{
status = gckKERNEL_Dispatch(device->kernels[device->coreMapping[iface.hardwareType]],
(ioctlCode == IOCTL_GCHAL_INTERFACE),
&iface);
}
}
/* Redo system call after pending signal is handled. */
if (status == gcvSTATUS_INTERRUPTED)
{
gcmkFOOTER();
return -ERESTARTSYS;
}
if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY))
{
gcuVIDMEM_NODE_PTR node;
gctUINT32 processID;
gckOS_GetProcessID(&processID);
gcmkONERROR(gckVIDMEM_HANDLE_Lookup(device->kernels[device->coreMapping[iface.hardwareType]],
processID,
(gctUINT32)iface.u.LockVideoMemory.node,
&nodeObject));
node = nodeObject->node;
/* Special case for mapped memory. */
if ((data->mappedMemory != gcvNULL)
&& (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
)
{
/* Compute offset into mapped memory. */
gctUINT32 offset
= (gctUINT8 *) gcmUINT64_TO_PTR(iface.u.LockVideoMemory.memory)
- (gctUINT8 *) device->contiguousBase;
/* Compute offset into user-mapped region. */
iface.u.LockVideoMemory.memory =
gcmPTR_TO_UINT64((gctUINT8 *) data->mappedMemory + offset);
}
}
/* Copy data back to the user. */
copyLen = copy_to_user(
gcmUINT64_TO_PTR(drvArgs.OutputBuffer), &iface, sizeof(gcsHAL_INTERFACE)
);
if (copyLen != 0)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): error copying of output HAL interface.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
/* Success. */
gcmkFOOTER_NO();
return 0;
OnError:
gcmkFOOTER();
return -ENOTTY;
}
static int drv_mmap(
struct file* filp,
struct vm_area_struct* vma
)
{
gceSTATUS status = gcvSTATUS_OK;
gcsHAL_PRIVATE_DATA_PTR data;
gckGALDEVICE device;
gcmkHEADER_ARG("filp=0x%08X vma=0x%08X", filp, vma);
if (filp == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): filp is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
data = filp->private_data;
if (data == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): private_data is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
device = data->device;
if (device == gcvNULL)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): device is NULL\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
#if !gcdPAGED_MEMORY_CACHEABLE
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_flags |= gcdVM_FLAGS;
#endif
vma->vm_pgoff = 0;
if (device->contiguousMapped)
{
unsigned long size = vma->vm_end - vma->vm_start;
int ret = 0;
/*####modified for marvell-bg2*/
if (size > device->contiguousSize)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Invalid mapping size. size (%d) too large >= %d\n",
__FUNCTION__, __LINE__,
size, device->contiguousSize
);
gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT);
}
/*####end for marvell-bg2*/
ret = io_remap_pfn_range(
vma,
vma->vm_start,
device->requestedContiguousBase >> PAGE_SHIFT,
size,
vma->vm_page_prot
);
if (ret != 0)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): io_remap_pfn_range failed %d\n",
__FUNCTION__, __LINE__,
ret
);
data->mappedMemory = gcvNULL;
gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
}
data->mappedMemory = (gctPOINTER) vma->vm_start;
/* Success. */
gcmkFOOTER_NO();
return 0;
}
OnError:
gcmkFOOTER();
return -ENOTTY;
}
#if !USE_PLATFORM_DRIVER
static int __init drv_init(void)
#else
static int drv_init(void)
#endif
{
int ret;
int result = -EINVAL;
gceSTATUS status;
gckGALDEVICE device = gcvNULL;
struct class* device_class = gcvNULL;
gcsDEVICE_CONSTRUCT_ARGS args = {
.recovery = recovery,
.stuckDump = stuckDump,
};
gcmkHEADER();
#if ENABLE_GPU_CLOCK_BY_DRIVER && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
{
struct clk * clk;
clk = clk_get(NULL, "GCCLK");
if (IS_ERR(clk))
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): clk get error: %d\n",
__FUNCTION__, __LINE__,
PTR_ERR(clk)
);
result = -ENODEV;
gcmkONERROR(gcvSTATUS_GENERIC_IO);
}
/*
* APMU_GC_156M, APMU_GC_312M, APMU_GC_PLL2, APMU_GC_PLL2_DIV2 currently.
* Use the 2X clock.
*/
if (clk_set_rate(clk, coreClock * 2))
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Failed to set core clock.\n",
__FUNCTION__, __LINE__
);
result = -EAGAIN;
gcmkONERROR(gcvSTATUS_GENERIC_IO);
}
clk_enable(clk);
#if defined(CONFIG_PXA_DVFM) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
gc_pwr(1);
# endif
}
#endif
printk(KERN_INFO "Galcore version %d.%d.%d.%d\n",
gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, gcvVERSION_BUILD);
/* when enable gpu profiler, we need to turn off gpu powerMangement */
if(gpuProfiler)
powerManagement = 0;
if (showArgs)
{
gckOS_DumpParam();
}
if(logFileSize != 0)
{
gckDEBUGFS_Initialize();
}
/* Create the GAL device. */
status = gckGALDEVICE_Construct(
#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY
irqLine3D0,
registerMemBase3D0, registerMemSize3D0,
irqLine3D1,
registerMemBase3D1, registerMemSize3D1,
#else
irqLine,
registerMemBase, registerMemSize,
#endif
irqLine2D,
registerMemBase2D, registerMemSize2D,
irqLineVG,
registerMemBaseVG, registerMemSizeVG,
contiguousBase, contiguousSize,
bankSize, fastClear, compression, baseAddress, physSize, signal,
logFileSize,
powerManagement,
gpuProfiler,
&args,
&device
);
if (gcmIS_ERROR(status))
{
gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Failed to create the GAL device: status=%d\n",
__FUNCTION__, __LINE__, status);
goto OnError;
}
/* Start the GAL device. */
gcmkONERROR(gckGALDEVICE_Start(device));
if ((physSize != 0)
&& (device->kernels[gcvCORE_MAJOR] != gcvNULL)
&& (device->kernels[gcvCORE_MAJOR]->hardware->mmuVersion != 0))
{
#if !gcdSECURITY
status = gckMMU_Enable(device->kernels[gcvCORE_MAJOR]->mmu, baseAddress, physSize);
gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
"Enable new MMU: status=%d\n", status);
#if gcdMULTI_GPU_AFFINITY
status = gckMMU_Enable(device->kernels[gcvCORE_OCL]->mmu, baseAddress, physSize);
gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
"Enable new MMU: status=%d\n", status);
#endif
if ((device->kernels[gcvCORE_2D] != gcvNULL)
&& (device->kernels[gcvCORE_2D]->hardware->mmuVersion != 0))
{
status = gckMMU_Enable(device->kernels[gcvCORE_2D]->mmu, baseAddress, physSize);
gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER,
"Enable new MMU for 2D: status=%d\n", status);
}
#endif
/* Reset the base address */
device->baseAddress = 0;
}
/* Register the character device. */
ret = register_chrdev(major, DEVICE_NAME, &driver_fops);
if (ret < 0)
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Could not allocate major number for mmap.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
}
if (major == 0)
{
major = ret;
}
/* Create the device class. */
/*####modified for marvell-bg2*/
device_class = class_create(THIS_MODULE, "graphics_3d_class");
/*####end for marvell-bg2*/
if (IS_ERR(device_class))
{
gcmkTRACE_ZONE(
gcvLEVEL_ERROR, gcvZONE_DRIVER,
"%s(%d): Failed to create the class.\n",
__FUNCTION__, __LINE__
);
gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
device_create(device_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
#else
device_create(device_class, NULL, MKDEV(major, 0), DEVICE_NAME);
#endif
galDevice = device;
gpuClass = device_class;
#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY
gcmkTRACE_ZONE(
gcvLEVEL_INFO, gcvZONE_DRIVER,
"%s(%d): irqLine3D0=%d, contiguousSize=%lu, memBase3D0=0x%lX\n",
__FUNCTION__, __LINE__,
irqLine3D0, contiguousSize, registerMemBase3D0
);
#else
gcmkTRACE_ZONE(
gcvLEVEL_INFO, gcvZONE_DRIVER,
"%s(%d): irqLine=%d, contiguousSize=%lu, memBase=0x%lX\n",
__FUNCTION__, __LINE__,
irqLine, contiguousSize, registerMemBase
);
#endif
/* Success. */
gcmkFOOTER_NO();
return 0;
OnError:
/* Roll back. */
if (device_class != gcvNULL)
{
device_destroy(device_class, MKDEV(major, 0));
class_destroy(device_class);
}
if (device != gcvNULL)
{
gcmkVERIFY_OK(gckGALDEVICE_Stop(device));
gcmkVERIFY_OK(gckGALDEVICE_Destroy(device));
}
gcmkFOOTER();
return result;
}
#if !USE_PLATFORM_DRIVER
static void __exit drv_exit(void)
#else
static void drv_exit(void)
#endif
{
gcmkHEADER();
gcmkASSERT(gpuClass != gcvNULL);
device_destroy(gpuClass, MKDEV(major, 0));
class_destroy(gpuClass);
unregister_chrdev(major, DEVICE_NAME);
gcmkVERIFY_OK(gckGALDEVICE_Stop(galDevice));
gcmkVERIFY_OK(gckGALDEVICE_Destroy(galDevice));
if(gckDEBUGFS_IsEnabled())
{
gckDEBUGFS_Terminate();
}
#if ENABLE_GPU_CLOCK_BY_DRIVER && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
{
struct clk * clk = NULL;
#if defined(CONFIG_PXA_DVFM) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
gc_pwr(0);
#endif
clk = clk_get(NULL, "GCCLK");
clk_disable(clk);
}
#endif
gcmkFOOTER_NO();
}
#if !USE_PLATFORM_DRIVER
module_init(drv_init);
module_exit(drv_exit);
#else
/*####modified for marvell-bg2*/
static int drv_get_dts_for_gpu(void)
{
struct device_node *np;
struct resource res;
int rc = 0;
u32 value;
/* get the gpu3d information */
np = of_find_compatible_node(NULL, NULL, "marvell,berlin-gpu3d");
if (!np)
{
printk("gpu error: of_find_compatible_node for berlin-gpu3d failed!\n");
goto err_exit;
}
/* get the reg section */
rc = of_address_to_resource(np, 0, &res);
if (rc)
{
printk("gpu warning: of_address_to_resource for berlin-gpu3d failed!\n");
}
else
{
registerMemBase = res.start;
registerMemSize = resource_size(&res);
}
/* get the interrupt section */
rc = of_irq_to_resource(np, 0, &res);
if (!rc)
{
printk("gpu warning: of_irq_to_resource for berlin-gpu3d failed, rc=%d!\n", rc);
}
else
{
irqLine = rc;
}
/* get the nonsecure-mem-base section */
rc = of_property_read_u32(np, "marvell,nonsecure-mem-base", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for nonsecure-mem-base failed!\n");
}
else
{
contiguousBase = value;
}
/* get the nonsecure-mem-size section */
rc = of_property_read_u32(np, "marvell,nonsecure-mem-size", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for nonsecure-mem-size failed!\n");
}
else
{
contiguousSize = value;
}
/* get the phy-mem-size section */
rc = of_property_read_u32(np, "marvell,phy-mem-size", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for phy-mem-size failed!\n");
}
else
{
physSize = value;
}
/* get the 3D clock registers and corresponding bitfields */
rc = of_property_read_u32(np, "marvell,core-clock-register", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for 3D core-clock-register failed!\n");
coreClkRegister3D = 0;
}
else
{
coreClkRegister3D = value;
}
rc = of_property_read_u32(np, "marvell,sys-clock-register", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for 3D sys-clock-register failed!\n");
sysClkRegister3D = 0;
}
else
{
sysClkRegister3D = value;
}
rc = of_property_read_u32(np, "marvell,core-clock-bitfield", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for 3D core-clock-bitfield failed!\n");
coreClkBitfield3D = 0;
}
else
{
coreClkBitfield3D = value;
}
rc = of_property_read_u32(np, "marvell,sys-clock-bitfield", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for 3D sys-clock-bitfield failed!\n");
sysClkBitfield3D = 0;
}
else
{
sysClkBitfield3D = value;
}
of_node_put(np);
/* get the gpu2d information */
np = of_find_compatible_node(NULL, NULL, "marvell,berlin-gpu2d");
if (!np)
{
/* no 2d gpu, just goto exit */
goto err_exit;
}
/* get the reg section */
rc = of_address_to_resource(np, 0, &res);
if (rc)
{
printk("gpu warning: of_address_to_resource for berlin-gpu2d failed!\n");
}
else
{
registerMemBase2D = res.start;
registerMemSize2D = resource_size(&res);
}
/* get the interrupt section */
rc = of_irq_to_resource(np, 0, &res);
if (!rc)
{
printk("gpu warning: of_irq_to_resource for berlin-gpu2d failed!\n");
}
else
{
irqLine2D = rc;
}
/* get 2D core clock register and corresponding bitfield */
rc = of_property_read_u32(np, "marvell,core-clock-register", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for 2D core-clock-register failed!\n");
coreClkRegister2D = 0;
}
else
{
coreClkRegister2D = value;
}
rc = of_property_read_u32(np, "marvell,core-clock-bitfield", &value);
if (rc)
{
printk("gpu warning: of_property_read_u32 for 2D core-clock-bitfield failed!\n");
coreClkBitfield2D = 0;
}
else
{
coreClkBitfield2D = value;
}
of_node_put(np);
return 0;
err_exit:
return -1;
}
/*####end for marvell-bg2*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int gpu_probe(struct platform_device *pdev)
#else
static int __devinit gpu_probe(struct platform_device *pdev)
#endif
{
int ret = -ENODEV;
struct resource* res;
gcmkHEADER();
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "gpu_irq");
if (!res)
{
printk(KERN_ERR "%s: No irq line supplied.\n",__FUNCTION__);
goto gpu_probe_fail;
}
irqLine = res->start;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpu_base");
if (!res)
{
printk(KERN_ERR "%s: No register base supplied.\n",__FUNCTION__);
goto gpu_probe_fail;
}
registerMemBase = res->start;
registerMemSize = res->end - res->start + 1;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpu_mem");
if (!res)
{
printk(KERN_ERR "%s: No memory base supplied.\n",__FUNCTION__);
goto gpu_probe_fail;
}
contiguousBase = res->start;
contiguousSize = res->end - res->start + 1;
/*####modified for marvell-bg2*/
ret = drv_get_dts_for_gpu();
/*if there is no gpu2d, it will also return error*/
/*####end for marvell-bg2*/
ret = drv_init();
if (!ret)
{
platform_set_drvdata(pdev, galDevice);
gcmkFOOTER_NO();
return ret;
}
gpu_probe_fail:
gcmkFOOTER_ARG(KERN_INFO "Failed to register gpu driver: %d\n", ret);
return ret;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int gpu_remove(struct platform_device *pdev)
#else
static int __devexit gpu_remove(struct platform_device *pdev)
#endif
{
gcmkHEADER();
drv_exit();
gcmkFOOTER_NO();
return 0;
}
static int gpu_suspend(struct platform_device *dev, pm_message_t state)
{
gceSTATUS status;
gckGALDEVICE device;
gctINT i;
device = platform_get_drvdata(dev);
if (!device)
{
return -1;
}
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL)
{
/* Store states. */
#if gcdENABLE_VG
if (i == gcvCORE_VG)
{
status = gckVGHARDWARE_QueryPowerManagementState(device->kernels[i]->vg->hardware, &device->statesStored[i]);
}
else
#endif
{
status = gckHARDWARE_QueryPowerManagementState(device->kernels[i]->hardware, &device->statesStored[i]);
}
if (gcmIS_ERROR(status))
{
return -1;
}
#if gcdENABLE_VG
if (i == gcvCORE_VG)
{
status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_OFF);
}
else
#endif
{
status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_OFF);
}
if (gcmIS_ERROR(status))
{
return -1;
}
}
}
return 0;
}
static int gpu_resume(struct platform_device *dev)
{
gceSTATUS status;
gckGALDEVICE device;
gctINT i;
gceCHIPPOWERSTATE statesStored;
device = platform_get_drvdata(dev);
if (!device)
{
return -1;
}
for (i = 0; i < gcdMAX_GPU_COUNT; i++)
{
if (device->kernels[i] != gcvNULL)
{
#if gcdENABLE_VG
if (i == gcvCORE_VG)
{
status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_ON);
}
else
#endif
{
status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_ON);
}
if (gcmIS_ERROR(status))
{
return -1;
}
/* Convert global state to crossponding internal state. */
switch(device->statesStored[i])
{
case gcvPOWER_OFF:
statesStored = gcvPOWER_OFF_BROADCAST;
break;
case gcvPOWER_IDLE:
statesStored = gcvPOWER_IDLE_BROADCAST;
break;
case gcvPOWER_SUSPEND:
statesStored = gcvPOWER_SUSPEND_BROADCAST;
break;
case gcvPOWER_ON:
statesStored = gcvPOWER_ON_AUTO;
break;
default:
statesStored = device->statesStored[i];
break;
}
/* Restore states. */
#if gcdENABLE_VG
if (i == gcvCORE_VG)
{
status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, statesStored);
}
else
#endif
{
status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, statesStored);
}
if (gcmIS_ERROR(status))
{
return -1;
}
}
}
return 0;
}
static struct platform_driver gpu_driver = {
.probe = gpu_probe,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
.remove = gpu_remove,
#else
.remove = __devexit_p(gpu_remove),
#endif
.suspend = gpu_suspend,
.resume = gpu_resume,
.driver = {
/*####modified for marvell-bg2*/
.name = DRV_NAME,
/*####end for marvell-bg2*/
}
};
#ifndef CONFIG_DOVE_GPU
static struct resource gpu_resources[] = {
{
.name = "gpu_irq",
.flags = IORESOURCE_IRQ,
},
{
.name = "gpu_base",
.flags = IORESOURCE_MEM,
},
{
.name = "gpu_mem",
.flags = IORESOURCE_MEM,
},
};
static struct platform_device * gpu_device;
#endif
static int __init gpu_init(void)
{
int ret = 0;
#ifndef CONFIG_DOVE_GPU
gpu_resources[0].start = gpu_resources[0].end = irqLine;
gpu_resources[1].start = registerMemBase;
gpu_resources[1].end = registerMemBase + registerMemSize - 1;
gpu_resources[2].start = contiguousBase;
gpu_resources[2].end = contiguousBase + contiguousSize - 1;
/*####modified for marvell-bg2*/
/* Allocate device */
gpu_device = platform_device_alloc(DRV_NAME, -1);
/*####end for marvell-bg2*/
if (!gpu_device)
{
printk(KERN_ERR "galcore: platform_device_alloc failed.\n");
ret = -ENOMEM;
goto out;
}
/* Insert resource */
ret = platform_device_add_resources(gpu_device, gpu_resources, 3);
if (ret)
{
printk(KERN_ERR "galcore: platform_device_add_resources failed.\n");
goto put_dev;
}
/* Add device */
ret = platform_device_add(gpu_device);
if (ret)
{
printk(KERN_ERR "galcore: platform_device_add failed.\n");
goto put_dev;
}
#endif
ret = platform_driver_register(&gpu_driver);
if (!ret)
{
goto out;
}
#ifndef CONFIG_DOVE_GPU
platform_device_del(gpu_device);
put_dev:
platform_device_put(gpu_device);
#endif
out:
return ret;
}
static void __exit gpu_exit(void)
{
platform_driver_unregister(&gpu_driver);
#ifndef CONFIG_DOVE_GPU
platform_device_unregister(gpu_device);
#endif
}
/*####modified for marvell-bg2*/
#if MRVL_VIDEO_MEMORY_USE_ION
int gpu_alloc_secure_memory(unsigned int Bytes, void **handle, unsigned int *Physical)
{
if (galDevice == gcvNULL || galDevice->os == gcvNULL)
{
printk(KERN_ERR "gfx driver: gal device not initialized!\n");
return -1;
}
if (gckOS_AllocateIonMemory(galDevice->os,
gcvTRUE,
(gctSIZE_T *)&Bytes,
(gctPHYS_ADDR *)handle,
Physical) != gcvSTATUS_OK)
{
return -1;
}
return 0;
}
void gpu_free_memory(void *handle)
{
if (handle == NULL)
{
return;
}
if (galDevice == gcvNULL || galDevice->os == gcvNULL)
{
printk(KERN_ERR "gfx driver: gal device not initialized!\n");
return;
}
gckOS_FreeIonMemory(galDevice->os, (gctPHYS_ADDR)handle);
}
#endif
/*####end for marvell-bg2*/
module_init(gpu_init);
module_exit(gpu_exit);
#endif