/************************************************************************* * * Copyright(c) 2008 Imagination Technologies Ltd. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful but, except * as otherwise stated in writing, 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * * Contact Information: * Imagination Technologies Ltd. * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK * *************************************************************************/ #include #include #include #include #include #include #if defined(LDM_PLATFORM) #include #if defined(SGX_EARLYSUSPEND) #include #endif #endif #include "img_defs.h" #include "servicesext.h" #include "kerneldisplay.h" #include "omap_sgx_displayclass.h" #include "omap_display.h" /* XXX: Expect 2 framebuffers for virtual display */ #if (CONFIG_FB_OMAP2_NUM_FBS < 2) #error "Virtual display is supported only with 2 or more framebuffers, \ CONFIG_FB_OMAP2_NUM_FBS must be equal or greater than 2 \ see CONFIG_FB_OMAP2_NUM_FBS for details in the kernel config" #endif #define OMAP_DC_CMD_COUNT 1 #define MAX_BUFFERS_FLIPPING 4 /* Pointer Display->Services */ static PFN_DC_GET_PVRJTABLE pfnGetPVRJTable = NULL; /* Pointer to the display devices */ static struct OMAP_DISP_DEVINFO *pDisplayDevices = NULL; static int display_devices_count = 0; static void display_sync_handler(struct work_struct *work); static enum OMAP_ERROR get_pvr_dc_jtable (char *szFunctionName, PFN_DC_GET_PVRJTABLE *ppfnFuncTable); /* * Swap to display buffer. This buffer refers to one inside the * framebuffer memory. * in: hDevice, hBuffer, ui32SwapInterval, hPrivateTag, ui32ClipRectCount, * psClipRect */ static PVRSRV_ERROR SwapToDCBuffer(IMG_HANDLE hDevice, IMG_HANDLE hBuffer, IMG_UINT32 ui32SwapInterval, IMG_HANDLE hPrivateTag, IMG_UINT32 ui32ClipRectCount, IMG_RECT *psClipRect) { /* Nothing to do */ return PVRSRV_OK; } /* * Set display destination rectangle. * in: hDevice, hSwapChain, psRect */ static PVRSRV_ERROR SetDCDstRect(IMG_HANDLE hDevice, IMG_HANDLE hSwapChain, IMG_RECT *psRect) { /* Nothing to do */ return PVRSRV_ERROR_NOT_SUPPORTED; } /* * Set display source rectangle. * in: hDevice, hSwapChain, psRect */ static PVRSRV_ERROR SetDCSrcRect(IMG_HANDLE hDevice, IMG_HANDLE hSwapChain, IMG_RECT *psRect) { /* Nothing to do */ return PVRSRV_ERROR_NOT_SUPPORTED; } /* * Set display destination colour key. * in: hDevice, hSwapChain, ui32CKColour */ static PVRSRV_ERROR SetDCDstColourKey(IMG_HANDLE hDevice, IMG_HANDLE hSwapChain, IMG_UINT32 ui32CKColour) { /* Nothing to do */ return PVRSRV_ERROR_NOT_SUPPORTED; } /* * Set display source colour key. * in: hDevice, hSwapChain, ui32CKColour */ static PVRSRV_ERROR SetDCSrcColourKey(IMG_HANDLE hDevice, IMG_HANDLE hSwapChain, IMG_UINT32 ui32CKColour) { /* Nothing to do */ return PVRSRV_ERROR_NOT_SUPPORTED; } /* * Closes the display. * in: hDevice */ static PVRSRV_ERROR CloseDCDevice(IMG_HANDLE hDevice) { struct OMAP_DISP_DEVINFO *psDevInfo = (struct OMAP_DISP_DEVINFO*) hDevice; struct omap_display_device *display = psDevInfo->display; if(display->close(display)) WARNING_PRINTK("Unable to close properly display '%s'", display->name); return PVRSRV_OK; } /* * Flushes the sync queue present in the specified swap chain. * in: psSwapChain */ static void FlushInternalSyncQueue(struct OMAP_DISP_SWAPCHAIN *psSwapChain) { struct OMAP_DISP_DEVINFO *psDevInfo = (struct OMAP_DISP_DEVINFO*) psSwapChain->pvDevInfo; struct OMAP_DISP_FLIP_ITEM *psFlipItem; struct omap_display_device *display = psDevInfo->display; unsigned long ulMaxIndex; unsigned long i; psFlipItem = &psSwapChain->psFlipItems[psSwapChain->ulRemoveIndex]; ulMaxIndex = psSwapChain->ulBufferCount - 1; DEBUG_PRINTK("Flushing sync queue on display %lu", psDevInfo->ulDeviceID); for(i = 0; i < psSwapChain->ulBufferCount; i++) { if (psFlipItem->bValid == OMAP_FALSE) continue; DEBUG_PRINTK("Flushing swap buffer index %lu", psSwapChain->ulRemoveIndex); /* Flip the buffer if it hasn't been flipped */ if(psFlipItem->bFlipped == OMAP_FALSE) { display->present_buffer(psFlipItem->display_buffer); } /* If the command didn't complete, assume it did */ if(psFlipItem->bCmdCompleted == OMAP_FALSE) { DEBUG_PRINTK("Calling command complete for swap " "buffer index %lu", psSwapChain->ulRemoveIndex); psSwapChain->psPVRJTable->pfnPVRSRVCmdComplete( (IMG_HANDLE)psFlipItem->hCmdComplete, IMG_TRUE); } psSwapChain->ulRemoveIndex++; if(psSwapChain->ulRemoveIndex > ulMaxIndex) psSwapChain->ulRemoveIndex = 0; /* Put the state of the buffer to be used again later */ psFlipItem->bFlipped = OMAP_FALSE; psFlipItem->bCmdCompleted = OMAP_FALSE; psFlipItem->bValid = OMAP_FALSE; psFlipItem = &psSwapChain->psFlipItems[psSwapChain->ulRemoveIndex]; } psSwapChain->ulInsertIndex = 0; psSwapChain->ulRemoveIndex = 0; } /* * Sets the flush state of the specified display device * at the swap chain level without blocking the call. * in: psDevInfo, bFlushState */ static void SetFlushStateInternalNoLock(struct OMAP_DISP_DEVINFO* psDevInfo, enum OMAP_BOOL bFlushState) { struct OMAP_DISP_SWAPCHAIN *psSwapChain = psDevInfo->psSwapChain; /* Nothing to do if there is no swap chain */ if (psSwapChain == NULL){ DEBUG_PRINTK("Swap chain is null, nothing to do for" " display %lu", psDevInfo->ulDeviceID); return; } if (bFlushState) { DEBUG_PRINTK("Desired flushState is true for display %lu", psDevInfo->ulDeviceID); if (psSwapChain->ulSetFlushStateRefCount == 0) { psSwapChain->bFlushCommands = OMAP_TRUE; FlushInternalSyncQueue(psSwapChain); } psSwapChain->ulSetFlushStateRefCount++; } else { DEBUG_PRINTK("Desired flushState is false for display %lu", psDevInfo->ulDeviceID); if (psSwapChain->ulSetFlushStateRefCount != 0) { psSwapChain->ulSetFlushStateRefCount--; if (psSwapChain->ulSetFlushStateRefCount == 0) { psSwapChain->bFlushCommands = OMAP_FALSE; } } } } /* * Sets the flush state of the specified display device * at device level blocking the call if needed. * in: psDevInfo, bFlushState */ static void SetFlushStateExternal(struct OMAP_DISP_DEVINFO* psDevInfo, enum OMAP_BOOL bFlushState) { DEBUG_PRINTK("Executing for display %lu", psDevInfo->ulDeviceID); mutex_lock(&psDevInfo->sSwapChainLockMutex); if (psDevInfo->bFlushCommands != bFlushState) { psDevInfo->bFlushCommands = bFlushState; SetFlushStateInternalNoLock(psDevInfo, bFlushState); } mutex_unlock(&psDevInfo->sSwapChainLockMutex); } /* * Opens the display. * in: ui32DeviceID, phDevice * out: psSystemBufferSyncData */ static PVRSRV_ERROR OpenDCDevice(IMG_UINT32 ui32DeviceID, IMG_HANDLE *phDevice, PVRSRV_SYNC_DATA* psSystemBufferSyncData) { struct OMAP_DISP_DEVINFO *psDevInfo; struct omap_display_device *display; int i; psDevInfo = 0; for(i = 0; i < display_devices_count; i++) { if(ui32DeviceID == (&pDisplayDevices[i])->ulDeviceID) { psDevInfo = &pDisplayDevices[i]; break; } } if(!psDevInfo) { WARNING_PRINTK("Unable to identify display device with id %i", (int)ui32DeviceID); return OMAP_ERROR_INVALID_DEVICE; } psDevInfo->sSystemBuffer.psSyncData = psSystemBufferSyncData; display = psDevInfo->display; DEBUG_PRINTK("Opening display %lu '%s'",psDevInfo->ulDeviceID, display->name); /* TODO: Explain here why ORIENTATION_VERTICAL is used*/ if(display->open(display, ORIENTATION_VERTICAL | ORIENTATION_INVERT)) ERROR_PRINTK("Unable to open properly display '%s'", psDevInfo->display->name); display->present_buffer(display->main_buffer); /* TODO: Turn on display here? */ *phDevice = (IMG_HANDLE)psDevInfo; return PVRSRV_OK; } /* * Gets the available formats for the display. * in: hDevice * out: pui32NumFormats, psFormat */ static PVRSRV_ERROR EnumDCFormats(IMG_HANDLE hDevice, IMG_UINT32 *pui32NumFormats, DISPLAY_FORMAT *psFormat) { struct OMAP_DISP_DEVINFO *psDevInfo; if(!hDevice || !pui32NumFormats) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; *pui32NumFormats = 1; if(psFormat) psFormat[0] = psDevInfo->sDisplayFormat; else WARNING_PRINTK("Display format is null for" " display %lu", psDevInfo->ulDeviceID); return PVRSRV_OK; } /* * Gets the available dimensions for the display. * in: hDevice, psFormat * out: pui32NumDims, psDim */ static PVRSRV_ERROR EnumDCDims(IMG_HANDLE hDevice, DISPLAY_FORMAT *psFormat, IMG_UINT32 *pui32NumDims, DISPLAY_DIMS *psDim) { struct OMAP_DISP_DEVINFO *psDevInfo; if(!hDevice || !psFormat || !pui32NumDims) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; *pui32NumDims = 1; if(psDim) psDim[0] = psDevInfo->sDisplayDim; else WARNING_PRINTK("Display dimensions are null for" " display %lu", psDevInfo->ulDeviceID); return PVRSRV_OK; } /* * Gets the display framebuffer physical address. * in: hDevice * out: phBuffer */ static PVRSRV_ERROR GetDCSystemBuffer(IMG_HANDLE hDevice, IMG_HANDLE *phBuffer) { struct OMAP_DISP_DEVINFO *psDevInfo; if(!hDevice || !phBuffer) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; *phBuffer = (IMG_HANDLE)&psDevInfo->sSystemBuffer; return PVRSRV_OK; } /* * Gets the display general information. * in: hDevice * out: psDCInfo */ static PVRSRV_ERROR GetDCInfo(IMG_HANDLE hDevice, DISPLAY_INFO *psDCInfo) { struct OMAP_DISP_DEVINFO *psDevInfo; if(!hDevice || !psDCInfo) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; *psDCInfo = psDevInfo->sDisplayInfo; return PVRSRV_OK; } /* * Gets the display framebuffer virtual address. * in: hDevice * out: ppsSysAddr, pui32ByteSize, ppvCpuVAddr, phOSMapInfo, pbIsContiguous */ static PVRSRV_ERROR GetDCBufferAddr(IMG_HANDLE hDevice, IMG_HANDLE hBuffer, IMG_SYS_PHYADDR **ppsSysAddr, IMG_UINT32 *pui32ByteSize, IMG_VOID **ppvCpuVAddr, IMG_HANDLE *phOSMapInfo, IMG_BOOL *pbIsContiguous, IMG_UINT32 *pui32TilingStride) { struct OMAP_DISP_DEVINFO *psDevInfo; struct OMAP_DISP_BUFFER *psSystemBuffer; if(!hDevice || !hBuffer || !ppsSysAddr || !pui32ByteSize ) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; psSystemBuffer = (struct OMAP_DISP_BUFFER *)hBuffer; *ppsSysAddr = &psSystemBuffer->sSysAddr; *pui32ByteSize = (IMG_UINT32)psDevInfo->sSystemBuffer.ulBufferSize; if (ppvCpuVAddr) *ppvCpuVAddr = psSystemBuffer->sCPUVAddr; if (phOSMapInfo) *phOSMapInfo = (IMG_HANDLE)0; if (pbIsContiguous) *pbIsContiguous = IMG_TRUE; return PVRSRV_OK; } /* * Creates a swap chain. Called when a 3D application begins. * in: hDevice, ui32Flags, ui32BufferCount, psDstSurfAttrib, psSrcSurfAttrib * ui32OEMFlags * out: phSwapChain, ppsSyncData, pui32SwapChainID */ static PVRSRV_ERROR CreateDCSwapChain(IMG_HANDLE hDevice, IMG_UINT32 ui32Flags, DISPLAY_SURF_ATTRIBUTES *psDstSurfAttrib, DISPLAY_SURF_ATTRIBUTES *psSrcSurfAttrib, IMG_UINT32 ui32BufferCount, PVRSRV_SYNC_DATA **ppsSyncData, IMG_UINT32 ui32OEMFlags, IMG_HANDLE *phSwapChain, IMG_UINT32 *pui32SwapChainID) { struct OMAP_DISP_DEVINFO *psDevInfo; struct OMAP_DISP_SWAPCHAIN *psSwapChain; struct OMAP_DISP_BUFFER *psBuffer; struct OMAP_DISP_FLIP_ITEM *psFlipItems; IMG_UINT32 i; PVRSRV_ERROR eError; IMG_UINT32 ui32BuffersToSkip; struct omap_display_device *display; int err; if(!hDevice || !psDstSurfAttrib || !psSrcSurfAttrib || !ppsSyncData || !phSwapChain) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; if (psDevInfo->sDisplayInfo.ui32MaxSwapChains == 0) { ERROR_PRINTK("Unable to operate with 0 MaxSwapChains for" " display %lu", psDevInfo->ulDeviceID); return PVRSRV_ERROR_NOT_SUPPORTED; } if(psDevInfo->psSwapChain != NULL) { ERROR_PRINTK("Swap chain already exists for" " display %lu", psDevInfo->ulDeviceID); return PVRSRV_ERROR_FLIP_CHAIN_EXISTS; } if(ui32BufferCount > psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers) { ERROR_PRINTK("Too many buffers. Trying to use %u buffers while" " there is only %u available for display %lu", (unsigned int)ui32BufferCount, (unsigned int)psDevInfo-> sDisplayInfo.ui32MaxSwapChainBuffers, psDevInfo->ulDeviceID); return PVRSRV_ERROR_TOOMANYBUFFERS; } ui32BuffersToSkip = psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers - ui32BufferCount; if((psDstSurfAttrib->pixelformat != psDevInfo->sDisplayFormat.pixelformat) || (psDstSurfAttrib->sDims.ui32ByteStride != psDevInfo->sDisplayDim.ui32ByteStride) || (psDstSurfAttrib->sDims.ui32Width != psDevInfo->sDisplayDim.ui32Width) || (psDstSurfAttrib->sDims.ui32Height != psDevInfo->sDisplayDim.ui32Height)) { ERROR_PRINTK("Destination surface attributes differ from the" " current framebuffer for display %lu", psDevInfo->ulDeviceID); return PVRSRV_ERROR_INVALID_PARAMS; } if((psDstSurfAttrib->pixelformat != psSrcSurfAttrib->pixelformat) || (psDstSurfAttrib->sDims.ui32ByteStride != psSrcSurfAttrib->sDims.ui32ByteStride) || (psDstSurfAttrib->sDims.ui32Width != psSrcSurfAttrib->sDims.ui32Width) || (psDstSurfAttrib->sDims.ui32Height != psSrcSurfAttrib->sDims.ui32Height)) { ERROR_PRINTK("Destination surface attributes differ from the" " target destination surface for display %lu", psDevInfo->ulDeviceID); return PVRSRV_ERROR_INVALID_PARAMS; } /* Create the flip chain in display side */ display = psDevInfo->display; /* TODO: What about TILER buffers? */ /* * Creating the flip chain with the maximum number of buffers * we will decide which ones will be used later */ err = display->create_flip_chain( display, psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers); if(err) { ERROR_PRINTK("Unable to create the flip chain for '%s' display" " id %lu", display->name, psDevInfo->ulDeviceID); return PVRSRV_ERROR_INVALID_PARAMS; } /* Allocate memory needed for the swap chain */ psSwapChain = (struct OMAP_DISP_SWAPCHAIN*) kmalloc( sizeof(struct OMAP_DISP_SWAPCHAIN), GFP_KERNEL); if(!psSwapChain) { ERROR_PRINTK("Out of memory to allocate swap chain for" " display %lu", psDevInfo->ulDeviceID); return PVRSRV_ERROR_OUT_OF_MEMORY; } DEBUG_PRINTK("Creating swap chain for display %lu", psDevInfo->ulDeviceID ); /* Allocate memory for the buffer abstraction structures */ psBuffer = (struct OMAP_DISP_BUFFER*) kmalloc( sizeof(struct OMAP_DISP_BUFFER) * ui32BufferCount, GFP_KERNEL); if(!psBuffer) { ERROR_PRINTK("Out of memory to allocate the buffer" " abstraction structures for display %lu", psDevInfo->ulDeviceID); eError = PVRSRV_ERROR_OUT_OF_MEMORY; goto ErrorFreeSwapChain; } /* Allocate memory for the flip item abstraction structures */ psFlipItems = (struct OMAP_DISP_FLIP_ITEM *) kmalloc ( sizeof(struct OMAP_DISP_FLIP_ITEM) * ui32BufferCount, GFP_KERNEL); if (!psFlipItems) { ERROR_PRINTK("Out of memory to allocate the flip item" " abstraction structures for display %lu", psDevInfo->ulDeviceID); eError = PVRSRV_ERROR_OUT_OF_MEMORY; goto ErrorFreeBuffers; } /* Assign to the swap chain structure the initial data */ psSwapChain->ulBufferCount = (unsigned long)ui32BufferCount; psSwapChain->psBuffer = psBuffer; psSwapChain->psFlipItems = psFlipItems; psSwapChain->ulInsertIndex = 0; psSwapChain->ulRemoveIndex = 0; psSwapChain->psPVRJTable = &psDevInfo->sPVRJTable; psSwapChain->pvDevInfo = (void*)psDevInfo; /* * Init the workqueue (single thread, freezable and real time) * and its own work for this display */ INIT_WORK(&psDevInfo->sync_display_work, display_sync_handler); psDevInfo->sync_display_wq = __create_workqueue("pvr_display_sync_wq", 1, 1, 1); DEBUG_PRINTK("Swap chain will have %u buffers for display %lu", (unsigned int)ui32BufferCount, psDevInfo->ulDeviceID); /* Link the buffers available like a circular list */ for(i=0; iflip_chain->buffers[ui32SwapBuffer]; psBuffer[i].display_buffer = flip_buffer; psBuffer[i].psSyncData = ppsSyncData[i]; psBuffer[i].sSysAddr.uiAddr = flip_buffer->physical_addr; psBuffer[i].sCPUVAddr = (IMG_CPU_VIRTADDR) flip_buffer->virtual_addr; DEBUG_PRINTK("Display %lu buffer index %u has physical " "address 0x%x", psDevInfo->ulDeviceID, (unsigned int)i, (unsigned int)psBuffer[i].sSysAddr.uiAddr); } /* Initialize each flip item abstraction structure */ for(i=0; isSwapChainLockMutex); psDevInfo->psSwapChain = psSwapChain; psSwapChain->bFlushCommands = psDevInfo->bFlushCommands; if (psSwapChain->bFlushCommands) psSwapChain->ulSetFlushStateRefCount = 1; else psSwapChain->ulSetFlushStateRefCount = 0; mutex_unlock(&psDevInfo->sSwapChainLockMutex); *phSwapChain = (IMG_HANDLE)psSwapChain; return PVRSRV_OK; ErrorFreeBuffers: kfree(psBuffer); ErrorFreeSwapChain: kfree(psSwapChain); return eError; } /* * Destroy a swap chain. Called when a 3D application ends. * in: hDevice, hSwapChain */ static PVRSRV_ERROR DestroyDCSwapChain(IMG_HANDLE hDevice, IMG_HANDLE hSwapChain) { struct OMAP_DISP_DEVINFO *psDevInfo; struct OMAP_DISP_SWAPCHAIN *psSwapChain; struct omap_display_device *display; int err; if(!hDevice || !hSwapChain) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; psSwapChain = (struct OMAP_DISP_SWAPCHAIN*)hSwapChain; display = psDevInfo->display; if (psSwapChain != psDevInfo->psSwapChain) { ERROR_PRINTK("Swap chain handler differs from the one " "present in the display device pointer"); return PVRSRV_ERROR_INVALID_PARAMS; } DEBUG_PRINTK("Destroying swap chain for display %lu", psDevInfo->ulDeviceID); mutex_lock(&psDevInfo->sSwapChainLockMutex); FlushInternalSyncQueue(psSwapChain); psDevInfo->psSwapChain = NULL; /* * Present the buffer which is at the base of address of * the framebuffer */ display->present_buffer(display->main_buffer); /* Destroy the flip chain in display side */ err = display->destroy_flip_chain(display); if(err) { ERROR_PRINTK("Unable to destroy the flip chain for '%s' " "display id %lu", display->name, psDevInfo->ulDeviceID); } mutex_unlock(&psDevInfo->sSwapChainLockMutex); /* Destroy the workqueue */ flush_workqueue(psDevInfo->sync_display_wq); destroy_workqueue(psDevInfo->sync_display_wq); kfree(psSwapChain->psFlipItems); kfree(psSwapChain->psBuffer); kfree(psSwapChain); return PVRSRV_OK; } /* * Get display buffers. These are the buffers that can be allocated * inside the framebuffer memory. * in: hDevice, hSwapChain * out: pui32BufferCount, phBuffer */ static PVRSRV_ERROR GetDCBuffers(IMG_HANDLE hDevice, IMG_HANDLE hSwapChain, IMG_UINT32 *pui32BufferCount, IMG_HANDLE *phBuffer) { struct OMAP_DISP_DEVINFO *psDevInfo; struct OMAP_DISP_SWAPCHAIN *psSwapChain; unsigned long i; if(!hDevice || !hSwapChain || !pui32BufferCount || !phBuffer) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; psSwapChain = (struct OMAP_DISP_SWAPCHAIN*)hSwapChain; if (psSwapChain != psDevInfo->psSwapChain) { ERROR_PRINTK("Swap chain handler differs from the one " "present in the display device %lu pointer", psDevInfo->ulDeviceID); return PVRSRV_ERROR_INVALID_PARAMS; } *pui32BufferCount = (IMG_UINT32)psSwapChain->ulBufferCount; for(i=0; iulBufferCount; i++) phBuffer[i] = (IMG_HANDLE)&psSwapChain->psBuffer[i]; return PVRSRV_OK; } /* * Sets the display state. * in: ui32State, hDevice */ static IMG_VOID SetDCState(IMG_HANDLE hDevice, IMG_UINT32 ui32State) { struct OMAP_DISP_DEVINFO *psDevInfo = (struct OMAP_DISP_DEVINFO*) hDevice; switch (ui32State) { case DC_STATE_FLUSH_COMMANDS: DEBUG_PRINTK("Setting state to flush commands for" " display %lu", psDevInfo->ulDeviceID); SetFlushStateExternal(psDevInfo, OMAP_TRUE); break; case DC_STATE_NO_FLUSH_COMMANDS: DEBUG_PRINTK("Setting state to not flush commands for" " display %lu", psDevInfo->ulDeviceID); SetFlushStateExternal(psDevInfo, OMAP_FALSE); break; default: WARNING_PRINTK("Unknown command state %u for display" " %lu", (unsigned int)ui32State, psDevInfo->ulDeviceID); break; } } /* * Swap to display system buffer. This buffer refers to the one which * is that fits in the framebuffer memory. * in: hDevice, hSwapChain */ static PVRSRV_ERROR SwapToDCSystem(IMG_HANDLE hDevice, IMG_HANDLE hSwapChain) { struct OMAP_DISP_DEVINFO *psDevInfo; struct OMAP_DISP_SWAPCHAIN *psSwapChain; struct omap_display_device *display; if(!hDevice || !hSwapChain) { ERROR_PRINTK("Invalid parameters"); return PVRSRV_ERROR_INVALID_PARAMS; } psDevInfo = (struct OMAP_DISP_DEVINFO*)hDevice; psSwapChain = (struct OMAP_DISP_SWAPCHAIN*)hSwapChain; display = psDevInfo->display; DEBUG_PRINTK("Executing for display %lu", psDevInfo->ulDeviceID); if (psSwapChain != psDevInfo->psSwapChain) { ERROR_PRINTK("Swap chain handler differs from the one " "present in the display device %lu pointer", psDevInfo->ulDeviceID); return PVRSRV_ERROR_INVALID_PARAMS; } mutex_lock(&psDevInfo->sSwapChainLockMutex); FlushInternalSyncQueue(psSwapChain); display->present_buffer(display->main_buffer); mutex_unlock(&psDevInfo->sSwapChainLockMutex); return PVRSRV_OK; } /* * Handles the synchronization with the display * in: work */ static void display_sync_handler(struct work_struct *work) { /* * TODO: Since present_buffer_sync waits and then present, this * algorithm can be simplified further */ struct OMAP_DISP_DEVINFO *psDevInfo = container_of(work, struct OMAP_DISP_DEVINFO, sync_display_work); struct omap_display_device *display = psDevInfo->display; struct OMAP_DISP_FLIP_ITEM *psFlipItem; struct OMAP_DISP_SWAPCHAIN *psSwapChain; unsigned long ulMaxIndex; mutex_lock(&psDevInfo->sSwapChainLockMutex); psSwapChain = psDevInfo->psSwapChain; if (!psSwapChain || psSwapChain->bFlushCommands) goto ExitUnlock; psFlipItem = &psSwapChain->psFlipItems[psSwapChain->ulRemoveIndex]; ulMaxIndex = psSwapChain->ulBufferCount - 1; /* Iterate through the flip items and flip them if necessary */ while(psFlipItem->bValid) { if(psFlipItem->bFlipped) { if(!psFlipItem->bCmdCompleted) { psSwapChain->psPVRJTable->pfnPVRSRVCmdComplete( (IMG_HANDLE)psFlipItem->hCmdComplete, IMG_TRUE); psFlipItem->bCmdCompleted = OMAP_TRUE; } if(psFlipItem->ulSwapInterval == 0) { psSwapChain->ulRemoveIndex++; if(psSwapChain->ulRemoveIndex > ulMaxIndex) psSwapChain->ulRemoveIndex = 0; psFlipItem->bCmdCompleted = OMAP_FALSE; psFlipItem->bFlipped = OMAP_FALSE; psFlipItem->bValid = OMAP_FALSE; } else { /* * Here the swap interval is not zero yet * we need to schedule another work until * it reaches zero */ display->sync(display); psFlipItem->ulSwapInterval--; queue_work(psDevInfo->sync_display_wq, &psDevInfo->sync_display_work); goto ExitUnlock; } } else { display->present_buffer_sync( psFlipItem->display_buffer); /* * present_buffer_sync waits and then present, then * swap interval decreases here too. */ psFlipItem->ulSwapInterval--; psFlipItem->bFlipped = OMAP_TRUE; /* * If the flip has been presented here then we need * in the next sync execute the command complete, * schedule another work */ queue_work(psDevInfo->sync_display_wq, &psDevInfo->sync_display_work); goto ExitUnlock; } psFlipItem = &psSwapChain->psFlipItems[psSwapChain->ulRemoveIndex]; } ExitUnlock: mutex_unlock(&psDevInfo->sSwapChainLockMutex); } /* * Performs a flip. This function takes the necessary steps to present * the buffer to be flipped in the display. * in: hCmdCookie, ui32DataSize, pvData */ static IMG_BOOL ProcessFlip(IMG_HANDLE hCmdCookie, IMG_UINT32 ui32DataSize, IMG_VOID *pvData) { DISPLAYCLASS_FLIP_COMMAND *psFlipCmd; struct OMAP_DISP_DEVINFO *psDevInfo; struct OMAP_DISP_BUFFER *psBuffer; struct OMAP_DISP_SWAPCHAIN *psSwapChain; struct omap_display_device *display; #if defined(SYS_USING_INTERRUPTS) struct OMAP_DISP_FLIP_ITEM* psFlipItem; #endif if(!hCmdCookie || !pvData) { WARNING_PRINTK("Ignoring call with NULL parameters"); return IMG_FALSE; } psFlipCmd = (DISPLAYCLASS_FLIP_COMMAND*)pvData; if (psFlipCmd == IMG_NULL || sizeof(DISPLAYCLASS_FLIP_COMMAND) != ui32DataSize) { WARNING_PRINTK("NULL command or command data size is wrong"); return IMG_FALSE; } psDevInfo = (struct OMAP_DISP_DEVINFO*)psFlipCmd->hExtDevice; psBuffer = (struct OMAP_DISP_BUFFER*)psFlipCmd->hExtBuffer; psSwapChain = (struct OMAP_DISP_SWAPCHAIN*) psFlipCmd->hExtSwapChain; display = psDevInfo->display; mutex_lock(&psDevInfo->sSwapChainLockMutex); if (psDevInfo->bDeviceSuspended) { /* If is suspended then assume the commands are completed */ psSwapChain->psPVRJTable->pfnPVRSRVCmdComplete( hCmdCookie, IMG_TRUE); goto ExitTrueUnlock; } #if defined(SYS_USING_INTERRUPTS) if( psFlipCmd->ui32SwapInterval == 0 || psSwapChain->bFlushCommands == OMAP_TRUE) { #endif display->present_buffer(psBuffer->display_buffer); psSwapChain->psPVRJTable->pfnPVRSRVCmdComplete( hCmdCookie, IMG_TRUE); #if defined(SYS_USING_INTERRUPTS) goto ExitTrueUnlock; } psFlipItem = &psSwapChain->psFlipItems[psSwapChain->ulInsertIndex]; if(psFlipItem->bValid == OMAP_FALSE) { unsigned long ulMaxIndex = psSwapChain->ulBufferCount - 1; psFlipItem->bFlipped = OMAP_FALSE; /* * The buffer is queued here, must be consumed by the workqueue */ psFlipItem->hCmdComplete = (OMAP_HANDLE)hCmdCookie; psFlipItem->ulSwapInterval = (unsigned long)psFlipCmd->ui32SwapInterval; psFlipItem->sSysAddr = &psBuffer->sSysAddr; psFlipItem->bValid = OMAP_TRUE; psFlipItem->display_buffer = psBuffer->display_buffer; psSwapChain->ulInsertIndex++; if(psSwapChain->ulInsertIndex > ulMaxIndex) psSwapChain->ulInsertIndex = 0; /* Give work to the workqueue to sync with the display */ queue_work(psDevInfo->sync_display_wq, &psDevInfo->sync_display_work); goto ExitTrueUnlock; } mutex_unlock(&psDevInfo->sSwapChainLockMutex); return IMG_FALSE; #endif ExitTrueUnlock: mutex_unlock(&psDevInfo->sSwapChainLockMutex); return IMG_TRUE; } #if defined(LDM_PLATFORM) /* * Function called when the driver must suspend */ static void DriverSuspend(void) { struct OMAP_DISP_DEVINFO *psDevInfo; int i; if(!pDisplayDevices) return; for(i = 0; i < display_devices_count; i++) { psDevInfo = &pDisplayDevices[i]; mutex_lock(&psDevInfo->sSwapChainLockMutex); if (psDevInfo->bDeviceSuspended) { mutex_unlock(&psDevInfo->sSwapChainLockMutex); continue; } psDevInfo->bDeviceSuspended = OMAP_TRUE; SetFlushStateInternalNoLock(psDevInfo, OMAP_TRUE); mutex_unlock(&psDevInfo->sSwapChainLockMutex); } } /* * Function called when the driver must resume */ static void DriverResume(void) { struct OMAP_DISP_DEVINFO *psDevInfo; int i; if(!pDisplayDevices) return; for(i = 0; i < display_devices_count; i++) { psDevInfo = &pDisplayDevices[i]; mutex_lock(&psDevInfo->sSwapChainLockMutex); if (!psDevInfo->bDeviceSuspended) { mutex_unlock(&psDevInfo->sSwapChainLockMutex); continue; } SetFlushStateInternalNoLock(psDevInfo, OMAP_FALSE); psDevInfo->bDeviceSuspended = OMAP_FALSE; mutex_unlock(&psDevInfo->sSwapChainLockMutex); } } #endif /* defined(LDM_PLATFORM) */ /* * Frees the kernel framebuffer * in: psDevInfo */ static void deinit_display_device(struct OMAP_DISP_DEVINFO *psDevInfo) { /* TODO: Are we sure there is nothing to do here? */ } /* * Deinitialization routine for the 3rd party display driver */ static enum OMAP_ERROR destroy_display_devices(void) { struct OMAP_DISP_DEVINFO *psDevInfo; PVRSRV_DC_DISP2SRV_KMJTABLE *psJTable; int i; DEBUG_PRINTK("Deinitializing 3rd party display driver"); if(!pDisplayDevices) return OMAP_OK; for(i = 0; i < display_devices_count; i++) { psDevInfo = &pDisplayDevices[i]; if(!psDevInfo->display) continue; /* Remove the ProcessFlip command callback */ psJTable = &psDevInfo->sPVRJTable; if(!psJTable) continue; if (psDevInfo->sPVRJTable.pfnPVRSRVRemoveCmdProcList( psDevInfo->ulDeviceID, OMAP_DC_CMD_COUNT) != PVRSRV_OK) { ERROR_PRINTK("Unable to remove callback for " "ProcessFlip command for display %lu", psDevInfo->ulDeviceID); return OMAP_ERROR_GENERIC; } /* Remove the display device from services */ if (psJTable->pfnPVRSRVRemoveDCDevice( psDevInfo->ulDeviceID) != PVRSRV_OK) { ERROR_PRINTK("Unable to remove the display %lu " "from services", psDevInfo->ulDeviceID); return OMAP_ERROR_GENERIC; } deinit_display_device(psDevInfo); } kfree(pDisplayDevices); return OMAP_OK; } /* * Extracts the framebuffer data from the kernel driver * in: psDevInfo */ static enum OMAP_ERROR init_display_device(struct OMAP_DISP_DEVINFO *psDevInfo, struct omap_display_device *display) { int buffers_available = display->buffers_available; /* Extract the needed data from the display struct */ DEBUG_PRINTK("Display '%s' id %i information:", display->name, display->id); DEBUG_PRINTK("*Width, height: %u,%u", display->width, display->height); DEBUG_PRINTK("*Rotation: %u", display->rotation); DEBUG_PRINTK("*Stride: %u bytes", display->byte_stride); DEBUG_PRINTK("*Buffers available: %u", buffers_available); DEBUG_PRINTK("*Bytes per pixel: %u (%u bpp)", display->bytes_per_pixel, display->bits_per_pixel); if(display->bits_per_pixel == 16) { if(display->pixel_format == RGB_565) { DEBUG_PRINTK("*Format: RGB565"); psDevInfo->sDisplayFormat.pixelformat = PVRSRV_PIXEL_FORMAT_RGB565; } else WARNING_PRINTK("*Format: Unknown framebuffer" "format"); } else if(display->bits_per_pixel == 24 || display->bits_per_pixel == 32) { if(display->pixel_format == ARGB_8888) { DEBUG_PRINTK("*Format: ARGB8888"); psDevInfo->sDisplayFormat.pixelformat = PVRSRV_PIXEL_FORMAT_ARGB8888; } else WARNING_PRINTK("*Format: Unknown framebuffer" "format"); } else WARNING_PRINTK("*Format: Unknown framebuffer format"); if(display->main_buffer) { DEBUG_PRINTK("*Bytes per buffer: %lu", display->main_buffer->size); DEBUG_PRINTK("*Main buffer physical address: 0x%lx", display->main_buffer->physical_addr); DEBUG_PRINTK("*Main buffer virtual address: 0x%lx", display->main_buffer->virtual_addr); DEBUG_PRINTK("*Main buffer size: %lu bytes", display->main_buffer->size); } else { psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers = 0; ERROR_PRINTK("*No main buffer found for display '%s'", display->name); return OMAP_ERROR_INIT_FAILURE; } psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers = buffers_available; mutex_init(&psDevInfo->sSwapChainLockMutex); psDevInfo->psSwapChain = 0; psDevInfo->bFlushCommands = OMAP_FALSE; psDevInfo->bDeviceSuspended = OMAP_FALSE; if(psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers > 1) { if(MAX_BUFFERS_FLIPPING == 1) { DEBUG_PRINTK("Flipping support is possible" " but you decided not to use it"); } DEBUG_PRINTK("*Flipping support"); if(psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers > MAX_BUFFERS_FLIPPING) psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers = MAX_BUFFERS_FLIPPING; } else { DEBUG_PRINTK("*Flipping not supported"); } if (psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers == 0) { psDevInfo->sDisplayInfo.ui32MaxSwapChains = 0; psDevInfo->sDisplayInfo.ui32MaxSwapInterval = 0; } else { psDevInfo->sDisplayInfo.ui32MaxSwapChains = 1; psDevInfo->sDisplayInfo.ui32MaxSwapInterval = 3; } psDevInfo->sDisplayInfo.ui32MinSwapInterval = 0; /* Get the display and framebuffer needed info */ strncpy(psDevInfo->sDisplayInfo.szDisplayName, DISPLAY_DEVICE_NAME, MAX_DISPLAY_NAME_SIZE); psDevInfo->sDisplayDim.ui32Width = display->width; psDevInfo->sDisplayDim.ui32Height = display->height; psDevInfo->sDisplayDim.ui32ByteStride = display->byte_stride; psDevInfo->sSystemBuffer.sSysAddr.uiAddr = display->main_buffer->physical_addr; psDevInfo->sSystemBuffer.sCPUVAddr = (IMG_CPU_VIRTADDR) display->main_buffer->virtual_addr; psDevInfo->sSystemBuffer.ulBufferSize = display->main_buffer->size; psDevInfo->display = display; return OMAP_OK; } /* * Initialization routine for the 3rd party display driver */ static enum OMAP_ERROR create_display_devices(void) { PFN_CMD_PROC pfnCmdProcList[OMAP_DC_CMD_COUNT]; IMG_UINT32 aui32SyncCountList[OMAP_DC_CMD_COUNT][2]; int i; unsigned int bytes_to_alloc; DEBUG_PRINTK("Initializing 3rd party display driver"); /* Ask for the number of displays available */ omap_display_init(); /* TODO: allow more displays */ display_devices_count = 1; // omap_display_count(); DEBUG_PRINTK("Found %i displays", display_devices_count); /* * Obtain the function pointer for the jump table from kernel * services to fill it with the function pointers that we want */ if(get_pvr_dc_jtable ("PVRGetDisplayClassJTable", &pfnGetPVRJTable) != OMAP_OK) { ERROR_PRINTK("Unable to get the function to get the" " jump table display->services"); return OMAP_ERROR_INIT_FAILURE; } /* * Allocate the display device structures, one per display available */ bytes_to_alloc = sizeof(struct OMAP_DISP_DEVINFO) * display_devices_count; pDisplayDevices = (struct OMAP_DISP_DEVINFO *) kmalloc( bytes_to_alloc, GFP_KERNEL); if(!pDisplayDevices) { pDisplayDevices = NULL; ERROR_PRINTK("Out of memory"); return OMAP_ERROR_OUT_OF_MEMORY; } memset(pDisplayDevices, 0, bytes_to_alloc); /* * Initialize each display device */ for(i = 0; i < display_devices_count; i++) { struct omap_display_device *display; struct OMAP_DISP_DEVINFO * psDevInfo; enum omap_display_id id; psDevInfo = &pDisplayDevices[i]; psDevInfo->display = 0; id = OMAP_DISPID_VIRTUAL; /* * TODO: Modify this to allow primary, secondary, * not only virtual */ #if 0 switch(i) { case 0: id = OMAP_DISPID_PRIMARY; break; case 1: id = OMAP_DISPID_SECONDARY; break; case 2: id = OMAP_DISPID_TERTIARY; break; case 3: id = OMAP_DISPID_VIRTUAL; break; default: ERROR_PRINTK("Invalid display type %i", i); BUG(); } #endif display = omap_display_get(id); if(!display) continue; if(init_display_device(psDevInfo, display) != OMAP_OK) { ERROR_PRINTK("Unable to initialize display '%s' type" " %u", display->name, display->id); continue; #if 0 kfree(pDisplayDevices); pDisplayDevices = NULL; return OMAP_ERROR_INIT_FAILURE; #endif } /* * Populate each display device structure */ if(!(*pfnGetPVRJTable)(&psDevInfo->sPVRJTable)) { ERROR_PRINTK("Unable to get the jump table" " display->services for display '%s'", display->name); return OMAP_ERROR_INIT_FAILURE; } /* Populate the function table that services will use */ psDevInfo->sDCJTable.ui32TableSize = sizeof(PVRSRV_DC_SRV2DISP_KMJTABLE); psDevInfo->sDCJTable.pfnOpenDCDevice = OpenDCDevice; psDevInfo->sDCJTable.pfnCloseDCDevice = CloseDCDevice; psDevInfo->sDCJTable.pfnEnumDCFormats = EnumDCFormats; psDevInfo->sDCJTable.pfnEnumDCDims = EnumDCDims; psDevInfo->sDCJTable.pfnGetDCSystemBuffer = GetDCSystemBuffer; psDevInfo->sDCJTable.pfnGetDCInfo = GetDCInfo; psDevInfo->sDCJTable.pfnGetBufferAddr = GetDCBufferAddr; psDevInfo->sDCJTable.pfnCreateDCSwapChain = CreateDCSwapChain; psDevInfo->sDCJTable.pfnDestroyDCSwapChain = DestroyDCSwapChain; psDevInfo->sDCJTable.pfnSetDCDstRect = SetDCDstRect; psDevInfo->sDCJTable.pfnSetDCSrcRect = SetDCSrcRect; psDevInfo->sDCJTable.pfnSetDCDstColourKey = SetDCDstColourKey; psDevInfo->sDCJTable.pfnSetDCSrcColourKey = SetDCSrcColourKey; psDevInfo->sDCJTable.pfnGetDCBuffers = GetDCBuffers; psDevInfo->sDCJTable.pfnSwapToDCBuffer = SwapToDCBuffer; psDevInfo->sDCJTable.pfnSwapToDCSystem = SwapToDCSystem; psDevInfo->sDCJTable.pfnSetDCState = SetDCState; /* Register the display device */ if(psDevInfo->sPVRJTable.pfnPVRSRVRegisterDCDevice( &psDevInfo->sDCJTable, (IMG_UINT32*) &psDevInfo->ulDeviceID) != PVRSRV_OK) { ERROR_PRINTK("Unable to register the jump table" " services->display"); return OMAP_ERROR_DEVICE_REGISTER_FAILED; } DEBUG_PRINTK("Display '%s' registered with the GPU with" " id %lu", display->name, psDevInfo->ulDeviceID); /* * Register the ProcessFlip function to notify when a frame is * ready to be flipped */ pfnCmdProcList[DC_FLIP_COMMAND] = ProcessFlip; aui32SyncCountList[DC_FLIP_COMMAND][0] = 0; aui32SyncCountList[DC_FLIP_COMMAND][1] = 2; if (psDevInfo->sPVRJTable.pfnPVRSRVRegisterCmdProcList( psDevInfo->ulDeviceID, &pfnCmdProcList[0], aui32SyncCountList, OMAP_DC_CMD_COUNT) != PVRSRV_OK) { ERROR_PRINTK("Unable to register callback for " "ProcessFlip command"); return OMAP_ERROR_CANT_REGISTER_CALLBACK; } } return OMAP_OK; } /* * Here we get the function pointer to get jump table from * services using an external function. * in: szFunctionName * out: ppfnFuncTable */ static enum OMAP_ERROR get_pvr_dc_jtable (char *szFunctionName, PFN_DC_GET_PVRJTABLE *ppfnFuncTable) { if(strcmp("PVRGetDisplayClassJTable", szFunctionName) != 0) { ERROR_PRINTK("Unable to get function pointer for %s" " from services", szFunctionName); return OMAP_ERROR_INVALID_PARAMS; } *ppfnFuncTable = PVRGetDisplayClassJTable; return OMAP_OK; } #if defined(LDM_PLATFORM) static volatile enum OMAP_BOOL bDeviceSuspended; /* * Common suspend driver function * in: psSwapChain, aPhyAddr */ static void CommonSuspend(void) { if (bDeviceSuspended) { DEBUG_PRINTK("Driver is already suspended"); return; } DriverSuspend(); bDeviceSuspended = OMAP_TRUE; } #if defined(SGX_EARLYSUSPEND) static struct early_suspend driver_early_suspend; /* * Android specific, driver is requested to be suspended * in: ea_event */ static void DriverSuspend_Entry(struct early_suspend *ea_event) { DEBUG_PRINTK("Requested driver suspend"); CommonSuspend(); } /* * Android specific, driver is requested to be suspended * in: ea_event */ static void DriverResume_Entry(struct early_suspend *ea_event) { DEBUG_PRINTK("Requested driver resume"); DriverResume(); bDeviceSuspended = OMAP_FALSE; } static struct platform_driver omap_sgx_dc_driver = { .driver = { .name = DRVNAME, } }; #else /* defined(SGX_EARLYSUSPEND) */ /* * Function called when the driver is requested to be suspended * in: pDevice, state */ static int DriverSuspend_Entry(struct platform_device unref__ *pDevice, pm_message_t unref__ state) { DEBUG_PRINTK("Requested driver suspend"); CommonSuspend(); return 0; } /* * Function called when the driver is requested to resume * in: pDevice */ static int DriverResume_Entry(struct platform_device unref__ *pDevice) { DEBUG_PRINTK("Requested driver resume"); DriverResume(); bDeviceSuspended = OMAP_FALSE; return 0; } /* * Function called when the driver is requested to shutdown * in: pDevice */ static IMG_VOID DriverShutdown_Entry( struct platform_device unref__ *pDevice) { DEBUG_PRINTK("Requested driver shutdown"); CommonSuspend(); } static struct platform_driver omap_sgx_dc_driver = { .driver = { .name = DRVNAME, }, .suspend = DriverSuspend_Entry, .resume = DriverResume_Entry, .shutdown = DriverShutdown_Entry, }; #endif /* defined(SGX_EARLYSUSPEND) */ #endif /* defined(LDM_PLATFORM) */ /* * Driver init function */ static int __init omap_sgx_dc_init(void) { if(create_display_devices() != OMAP_OK) { WARNING_PRINTK("Driver init failed"); return -ENODEV; } #if defined(LDM_PLATFORM) DEBUG_PRINTK("Registering platform driver"); if (platform_driver_register(&omap_sgx_dc_driver)) { WARNING_PRINTK("Unable to register platform driver"); if(destroy_display_devices() != OMAP_OK) WARNING_PRINTK("Driver cleanup failed\n"); return -ENODEV; } #if defined(SGX_EARLYSUSPEND) driver_early_suspend.suspend = DriverSuspend_Entry; driver_early_suspend.resume = DriverResume_Entry; driver_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; register_early_suspend(&driver_early_suspend); DEBUG_PRINTK("Registered early suspend support"); #endif #endif return 0; } /* * Driver exit function */ static IMG_VOID __exit omap_sgx_dc_deinit(IMG_VOID) { #if defined(LDM_PLATFORM) DEBUG_PRINTK("Removing platform driver"); platform_driver_unregister(&omap_sgx_dc_driver); #if defined(SGX_EARLYSUSPEND) unregister_early_suspend(&driver_early_suspend); #endif #endif if(destroy_display_devices() != OMAP_OK) WARNING_PRINTK("Driver cleanup failed"); } MODULE_SUPPORTED_DEVICE(DEVNAME); late_initcall(omap_sgx_dc_init); module_exit(omap_sgx_dc_deinit);