/**************************************************************************** * * Copyright (C) 2005 - 2011 by Vivante Corp. * Copyright (C) 2011 Freescale Semiconductor, Inc. * * 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 #include #include "gc_hal_kernel_linux.h" #include "gc_hal_driver.h" #if USE_PLATFORM_DRIVER # include #endif #ifdef CONFIG_PXA_DVFM # include # include #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 gckGALDEVICE galDevice; static int major = 199; module_param(major, int, 0644); static int irqLine = -1; module_param(irqLine, int, 0644); static long registerMemBase = 0x80000000; module_param(registerMemBase, long, 0644); static ulong registerMemSize = 256 << 10; module_param(registerMemSize, ulong, 0644); static int irqLine2D = -1; module_param(irqLine2D, int, 0644); static long registerMemBase2D = 0x00000000; module_param(registerMemBase2D, long, 0644); static ulong registerMemSize2D = 256 << 10; module_param(registerMemSize2D, ulong, 0644); static int irqLineVG = -1; module_param(irqLineVG, int, 0644); static long registerMemBaseVG = 0x00000000; module_param(registerMemBaseVG, long, 0644); static ulong registerMemSizeVG = 256 << 10; module_param(registerMemSizeVG, ulong, 0644); static long contiguousSize = 4 << 20; module_param(contiguousSize, long, 0644); static ulong contiguousBase = 0; module_param(contiguousBase, ulong, 0644); static long bankSize = 32 << 20; module_param(bankSize, long, 0644); static int fastClear = -1; module_param(fastClear, int, 0644); static int compression = -1; module_param(compression, 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 int showArgs = 0; module_param(showArgs, int, 0644); #if ENABLE_GPU_CLOCK_BY_DRIVER unsigned long coreClock = 156000000; module_param(coreClock, ulong, 0644); #endif static struct clk * clk_3d_core; static struct clk * clk_3d_shader; static struct clk * clk_2d_core; 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 = { .open = drv_open, .release = drv_release, .unlocked_ioctl = drv_ioctl, .mmap = drv_mmap, }; 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); 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 < gcdCORE_COUNT; i++) { if (galDevice->kernels[i] != gcvNULL) { gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE)); } } attached = gcvTRUE; if (!galDevice->contiguousMapped) { 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 < gcdCORE_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) { gctUINT32 processID; gcmkVERIFY_OK(gckOS_GetProcessID(&processID)); gcmkONERROR(gckOS_UnmapMemoryEx( galDevice->os, galDevice->contiguousPhysical, galDevice->contiguousSize, data->contiguousLogical, data->pidOpen )); for (i = 0; i < gcdCORE_COUNT; i++) { if (galDevice->kernels[i] != gcvNULL) { gcmkVERIFY_OK( gckKERNEL_RemoveProcessDB(galDevice->kernels[i], processID, gcvDB_MAP_MEMORY, data->contiguousLogical)); } } data->contiguousLogical = gcvNULL; } } /* A process gets detached. */ for (i = 0; i < gcdCORE_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; 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, 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 < gcdCORE_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; status = gcvSTATUS_OK; } else { if (iface.hardwareType < 0 || 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); } } if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY)) { /* Special case for mapped memory. */ if ((data->mappedMemory != gcvNULL) && (iface.u.LockVideoMemory.node->VidMem.memory->object.type == gcvOBJ_VIDMEM) ) { /* Compute offset into mapped memory. */ gctUINT32 offset = (gctUINT8 *) iface.u.LockVideoMemory.memory - (gctUINT8 *) device->contiguousBase; /* Compute offset into user-mapped region. */ iface.u.LockVideoMemory.memory = (gctUINT8 *) data->mappedMemory + offset; } } /* Copy data back to the user. */ copyLen = copy_to_user( 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; 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_noncached(vma->vm_page_prot); vma->vm_flags |= VM_IO | VM_DONTCOPY | VM_DONTEXPAND; #endif vma->vm_pgoff = 0; if (device->contiguousMapped) { unsigned long size = vma->vm_end - vma->vm_start; int ret = io_remap_pfn_range( vma, vma->vm_start, (gctUINT32) device->contiguousPhysical >> 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; gcmkHEADER(); #if ENABLE_GPU_CLOCK_BY_DRIVER && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)) { # if 0 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 # else if (irqLine != -1) { clk_3d_core = clk_get(NULL, "gpu3d_clk"); if (!IS_ERR(clk_3d_core)) { clk_3d_shader = clk_get(NULL, "gpu3d_shader_clk"); if (!IS_ERR(clk_3d_shader)) { clk_enable(clk_3d_core); clk_enable(clk_3d_shader); } else { irqLine = -1; clk_put(clk_3d_core); clk_3d_core = NULL; clk_3d_shader = NULL; printk(KERN_ERR "galcore: clk_get gpu3d_shader_clk failed, disable 3d!\n"); } } else { irqLine = -1; clk_3d_core = NULL; printk(KERN_ERR "galcore: clk_get gpu3d_clk failed, disable 3d!\n"); } } if ((irqLine2D != -1) || (irqLineVG != -1)) { clk_2d_core = clk_get(NULL, "gpu2d_clk"); if (IS_ERR(clk_2d_core)) { irqLine2D = -1; irqLineVG = -1; clk_2d_core = NULL; printk(KERN_ERR "galcore: clk_get 2d clock failed, disable 2d/vg!\n"); } else { clk_enable(clk_2d_core); } } # endif } #endif if (showArgs) { printk("galcore options:\n"); printk(" irqLine = %d\n", irqLine); printk(" registerMemBase = 0x%08lX\n", registerMemBase); printk(" registerMemSize = 0x%08lX\n", registerMemSize); 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(" baseAddress = 0x%08lX\n", baseAddress); printk(" physSize = 0x%08lX\n", physSize); #if ENABLE_GPU_CLOCK_BY_DRIVER printk(" coreClock = %lu\n", coreClock); #endif } /* Create the GAL device. */ gcmkONERROR(gckGALDEVICE_Construct( irqLine, registerMemBase, registerMemSize, irqLine2D, registerMemBase2D, registerMemSize2D, irqLineVG, registerMemBaseVG, registerMemSizeVG, contiguousBase, contiguousSize, bankSize, fastClear, compression, baseAddress, physSize, signal, &device )); /* Start the GAL device. */ gcmkONERROR(gckGALDEVICE_Start(device)); if ((physSize != 0) && (device->kernels[gcvCORE_MAJOR] != gcvNULL) && (device->kernels[gcvCORE_MAJOR]->hardware->mmuVersion != 0)) { status = gckMMU_Enable(device->kernels[gcvCORE_MAJOR]->mmu, baseAddress, physSize); gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, "Enable new MMU: status=%d\n", status); 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); } /* Reset the base address */ device->baseAddress = 0; } /* Register the character device. */ ret = register_chrdev(major, DRV_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, "graphics_class"); 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, "galcore"); #else device_create(device_class, NULL, MKDEV(major, 0), "galcore"); #endif galDevice = device; gpuClass = device_class; gcmkTRACE_ZONE( gcvLEVEL_INFO, gcvZONE_DRIVER, "%s(%d): irqLine=%d, contiguousSize=%lu, memBase=0x%lX\n", __FUNCTION__, __LINE__, irqLine, contiguousSize, registerMemBase ); /* 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, DRV_NAME); gcmkVERIFY_OK(gckGALDEVICE_Stop(galDevice)); gcmkVERIFY_OK(gckGALDEVICE_Destroy(galDevice)); #if ENABLE_GPU_CLOCK_BY_DRIVER && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) { # if 0 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); # else if (clk_3d_core) { clk_disable(clk_3d_core); clk_put(clk_3d_core); clk_3d_core = NULL; } if (clk_3d_shader) { clk_disable(clk_3d_shader); clk_put(clk_3d_shader); clk_3d_shader = NULL; } if (clk_2d_core) { clk_disable(clk_2d_core); clk_put(clk_2d_core); clk_2d_core = NULL; } # endif } #endif gcmkFOOTER_NO(); } #if !USE_PLATFORM_DRIVER module_init(drv_init); module_exit(drv_exit); #else #ifdef CONFIG_DOVE_GPU # define DEVICE_NAME "dove_gpu" #else # define DEVICE_NAME "galcore" #endif static int __devinit gpu_probe(struct platform_device *pdev) { int ret = -ENODEV; struct resource* res; gcmkHEADER(); ret = platform_get_irq(pdev, 0); if (ret >= 0) irqLine = ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) { registerMemBase = res->start; registerMemSize = res->end - res->start + 1; } ret = platform_get_irq(pdev, 1); if (ret >= 0) irqLine2D = ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res) { registerMemBase2D = res->start; registerMemSize2D = res->end - res->start + 1; } ret = platform_get_irq(pdev, 2); if (ret >= 0) irqLineVG = ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 2); if (res) { registerMemBaseVG = res->start; registerMemSizeVG = res->end - res->start + 1; } #if 0 pdata = pdev->dev.platform_data; if (pdata) { contiguousBase = pdata->reserved_mem_base; contiguousSize = pdata->reserved_mem_size; } #endif 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; } static int __devinit gpu_remove(struct platform_device *pdev) { gcmkHEADER(); drv_exit(); gcmkFOOTER_NO(); return 0; } static int __devinit gpu_suspend(struct platform_device *dev, pm_message_t state) { gceSTATUS status; gckGALDEVICE device; gctINT i; device = platform_get_drvdata(dev); for (i = 0; i < gcdCORE_COUNT; i++) { if (device->kernels[i] != gcvNULL) { #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 __devinit gpu_resume(struct platform_device *dev) { gceSTATUS status; gckGALDEVICE device; gctINT i; device = platform_get_drvdata(dev); for (i = 0; i < gcdCORE_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; } } } return 0; } static const struct of_device_id gpu_viv_dt_ids[] = { { .compatible = "viv,galcore", }, { /* sentinel */ } }; static struct platform_driver gpu_driver = { .probe = gpu_probe, .remove = gpu_remove, .suspend = gpu_suspend, .resume = gpu_resume, .driver = { .of_match_table = gpu_viv_dt_ids, .name = DEVICE_NAME, } }; #if 0 /*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; #if 0 /*ndef 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; /* Allocate device */ gpu_device = platform_device_alloc(DEVICE_NAME, -1); 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; } #if 0 /*ndef 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); #if 0 /*ndef CONFIG_DOVE_GPU*/ platform_device_unregister(gpu_device); #endif } module_init(gpu_init); module_exit(gpu_exit); #endif