| /**************************************************************************** |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2014 - 2016 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 - 2016 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 <linux/device.h> |
| #include <linux/slab.h> |
| |
| #include "gc_hal_kernel_linux.h" |
| #include "gc_hal_driver.h" |
| |
| #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; |
| |
| static gcsPLATFORM platform; |
| |
| static gckGALDEVICE galDevice; |
| |
| static uint major = 199; |
| module_param(major, uint, 0644); |
| |
| #if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY |
| static int irqLine3D0 = -1; |
| module_param(irqLine3D0, int, 0644); |
| |
| static ulong registerMemBase3D0 = 0; |
| 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 |
| static int irqLine = -1; |
| module_param(irqLine, int, 0644); |
| |
| static ulong registerMemBase = 0x80000000; |
| module_param(registerMemBase, ulong, 0644); |
| |
| static ulong registerMemSize = 2 << 10; |
| module_param(registerMemSize, ulong, 0644); |
| #endif |
| |
| 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); |
| |
| #if gcdENABLE_DEC_COMPRESSION |
| static ulong registerMemBaseDEC300 = 0x00000000; |
| module_param(registerMemBaseDEC300, ulong, 0644); |
| |
| static ulong registerMemSizeDEC300 = 2 << 10; |
| module_param(registerMemSizeDEC300, ulong, 0644); |
| #endif |
| |
| #ifndef gcdDEFAULT_CONTIGUOUS_SIZE |
| #define gcdDEFAULT_CONTIGUOUS_SIZE (4 << 20) |
| #endif |
| static ulong contiguousSize = gcdDEFAULT_CONTIGUOUS_SIZE; |
| 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); |
| |
| static ulong physSize = 0; |
| module_param(physSize, ulong, 0644); |
| |
| static uint logFileSize = 0; |
| module_param(logFileSize,uint, 0644); |
| |
| static uint recovery = 1; |
| 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 = 0; |
| module_param(stuckDump, uint, 0644); |
| MODULE_PARM_DESC(stuckDump, "Level of stuck dump content (1: Minimal, 2: Middle, 3: Maximal)"); |
| |
| static int showArgs = 0; |
| module_param(showArgs, int, 0644); |
| |
| static int mmu = 1; |
| module_param(mmu, int, 0644); |
| |
| static int gpu3DMinClock = 1; |
| |
| static int contiguousRequested = 0; |
| |
| |
| static gctBOOL registerMemMapped = gcvFALSE; |
| static gctPOINTER registerMemAddress = gcvNULL; |
| |
| 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 |
| _UpdateModuleParam( |
| gcsMODULE_PARAMETERS *Param |
| ) |
| { |
| #if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY |
| #else |
| irqLine = Param->irqLine ; |
| registerMemBase = Param->registerMemBase; |
| registerMemSize = Param->registerMemSize; |
| #endif |
| irqLine2D = Param->irqLine2D ; |
| registerMemBase2D = Param->registerMemBase2D; |
| registerMemSize2D = Param->registerMemSize2D; |
| #if gcdENABLE_VG |
| irqLineVG = Param->irqLineVG; |
| registerMemBaseVG = Param->registerMemBaseVG; |
| registerMemSizeVG = Param->registerMemSizeVG; |
| #endif |
| contiguousSize = Param->contiguousSize; |
| contiguousBase = Param->contiguousBase; |
| bankSize = Param->bankSize; |
| fastClear = Param->fastClear; |
| compression = Param->compression; |
| powerManagement = Param->powerManagement; |
| gpuProfiler = Param->gpuProfiler; |
| signal = Param->signal; |
| baseAddress = Param->baseAddress; |
| physSize = Param->physSize; |
| logFileSize = Param->logFileSize; |
| recovery = Param->recovery; |
| stuckDump = Param->stuckDump; |
| showArgs = Param->showArgs; |
| contiguousRequested = Param->contiguousRequested; |
| gpu3DMinClock = Param->gpu3DMinClock; |
| registerMemMapped = Param->registerMemMapped; |
| registerMemAddress = Param->registerMemAddress; |
| } |
| |
| void |
| gckOS_DumpParam( |
| void |
| ) |
| { |
| printk("Galcore 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); |
| } |
| |
| #if gcdENABLE_DEC_COMPRESSION |
| printk(" registerMemBaseDEC300 = 0x%08lX\n", registerMemBaseDEC300); |
| printk(" registerMemSizeDEC300 = 0x%08lX\n", registerMemSizeDEC300); |
| #endif |
| |
| 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); |
| 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; |
| |
| if (size > device->contiguousSize) |
| { |
| gcmkTRACE_ZONE( |
| gcvLEVEL_ERROR, gcvZONE_DRIVER, |
| "%s(%d): Invalid mapping size.\n", |
| __FUNCTION__, __LINE__ |
| ); |
| |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| 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, |
| .gpu3DMinClock = gpu3DMinClock, |
| .contiguousRequested = contiguousRequested, |
| .platform = &platform, |
| .mmu = mmu, |
| .registerMemMapped = registerMemMapped, |
| .registerMemAddress = registerMemAddress, |
| #if gcdENABLE_DEC_COMPRESSION |
| .registerMemBaseDEC300 = registerMemBaseDEC300, |
| .registerMemSizeDEC300 = registerMemSizeDEC300, |
| #endif |
| }; |
| |
| gcmkHEADER(); |
| |
| printk(KERN_INFO "Galcore version %d.%d.%d.%d\n", |
| gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, gcvVERSION_BUILD); |
| |
| #if !VIVANTE_PROFILER_PM |
| /* when enable gpu profiler, we need to turn off gpu powerMangement */ |
| if (gpuProfiler) |
| { |
| powerManagement = 0; |
| } |
| #endif |
| |
| 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)) |
| { |
| /* 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. */ |
| device_class = class_create(THIS_MODULE, CLASS_NAME); |
| |
| 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(); |
| } |
| |
| gcmkFOOTER_NO(); |
| } |
| |
| #if !USE_PLATFORM_DRIVER |
| module_init(drv_init); |
| module_exit(drv_exit); |
| #else |
| |
| #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; |
| gcsMODULE_PARAMETERS moduleParam = { |
| #if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY |
| #else |
| .irqLine = irqLine, |
| .registerMemBase = registerMemBase, |
| .registerMemSize = registerMemSize, |
| #endif |
| .irqLine2D = irqLine2D, |
| .registerMemBase2D = registerMemBase2D, |
| .registerMemSize2D = registerMemSize2D, |
| .irqLineVG = irqLineVG, |
| .registerMemBaseVG = registerMemBaseVG, |
| .registerMemSizeVG = registerMemSizeVG, |
| .contiguousSize = contiguousSize, |
| .contiguousBase = contiguousBase, |
| .bankSize = bankSize, |
| .fastClear = fastClear, |
| .compression = compression, |
| .powerManagement = powerManagement, |
| .gpuProfiler = gpuProfiler, |
| .signal = signal, |
| .baseAddress = baseAddress, |
| .physSize = physSize, |
| .logFileSize = logFileSize, |
| .recovery = recovery, |
| .stuckDump = stuckDump, |
| .showArgs = showArgs, |
| .gpu3DMinClock = gpu3DMinClock, |
| .registerMemMapped = registerMemMapped, |
| }; |
| |
| gcmkHEADER(); |
| |
| platform.device = pdev; |
| |
| if (platform.ops->getPower) |
| { |
| if (gcmIS_ERROR(platform.ops->getPower(&platform))) |
| { |
| gcmkFOOTER_NO(); |
| return ret; |
| } |
| } |
| |
| if (platform.ops->adjustParam) |
| { |
| /* Override default module param. */ |
| platform.ops->adjustParam(&platform, &moduleParam); |
| |
| /* Update module param because drv_init() uses them directly. */ |
| _UpdateModuleParam(&moduleParam); |
| } |
| |
| ret = drv_init(); |
| |
| if (!ret) |
| { |
| platform_set_drvdata(pdev, galDevice); |
| |
| gcmkFOOTER_NO(); |
| return ret; |
| } |
| |
| 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(); |
| |
| if (platform.ops->putPower) |
| { |
| platform.ops->putPower(&platform); |
| } |
| |
| 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; |
| } |
| |
| #if defined(CONFIG_PM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) |
| #ifdef CONFIG_PM_SLEEP |
| static int gpu_system_suspend(struct device *dev) |
| { |
| pm_message_t state={0}; |
| return gpu_suspend(to_platform_device(dev), state); |
| } |
| |
| static int gpu_system_resume(struct device *dev) |
| { |
| return gpu_resume(to_platform_device(dev)); |
| } |
| #endif |
| |
| static const struct dev_pm_ops gpu_pm_ops = { |
| SET_SYSTEM_SLEEP_PM_OPS(gpu_system_suspend, gpu_system_resume) |
| }; |
| #endif |
| |
| 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 = { |
| .owner = THIS_MODULE, |
| .name = DEVICE_NAME, |
| #if defined(CONFIG_PM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) |
| .pm = &gpu_pm_ops, |
| #endif |
| } |
| }; |
| |
| static int __init gpu_init(void) |
| { |
| int ret = 0; |
| |
| memset(&platform, 0, sizeof(gcsPLATFORM)); |
| |
| gckPLATFORM_QueryOperations(&platform.ops); |
| |
| if (platform.ops == gcvNULL) |
| { |
| printk(KERN_ERR "galcore: No platform specific operations.\n"); |
| ret = -ENODEV; |
| goto out; |
| } |
| |
| if (platform.ops->allocPriv) |
| { |
| /* Allocate platform private data. */ |
| if (gcmIS_ERROR(platform.ops->allocPriv(&platform))) |
| { |
| ret = -ENOMEM; |
| goto out; |
| } |
| } |
| |
| if (platform.ops->needAddDevice |
| && platform.ops->needAddDevice(&platform)) |
| { |
| /* Allocate device */ |
| platform.device = platform_device_alloc(DEVICE_NAME, -1); |
| if (!platform.device) |
| { |
| printk(KERN_ERR "galcore: platform_device_alloc failed.\n"); |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| /* Add device */ |
| ret = platform_device_add(platform.device); |
| if (ret) |
| { |
| printk(KERN_ERR "galcore: platform_device_add failed.\n"); |
| goto put_dev; |
| } |
| } |
| |
| platform.driver = &gpu_driver; |
| |
| if (platform.ops->adjustDriver) |
| { |
| /* Override default platform_driver struct. */ |
| platform.ops->adjustDriver(&platform); |
| } |
| |
| ret = platform_driver_register(&gpu_driver); |
| if (!ret) |
| { |
| goto out; |
| } |
| |
| platform_device_del(platform.device); |
| put_dev: |
| platform_device_put(platform.device); |
| |
| out: |
| return ret; |
| } |
| |
| static void __exit gpu_exit(void) |
| { |
| platform_driver_unregister(&gpu_driver); |
| |
| if (platform.ops->needAddDevice |
| && platform.ops->needAddDevice(&platform)) |
| { |
| platform_device_unregister(platform.device); |
| } |
| |
| if (platform.priv) |
| { |
| /* Free platform private data. */ |
| platform.ops->freePriv(&platform); |
| } |
| } |
| |
| module_init(gpu_init); |
| module_exit(gpu_exit); |
| |
| #endif |