| /************************************************************************* |
| * |
| * 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. <gpl-support@imgtec.com> |
| * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK |
| * |
| *************************************************************************/ |
| |
| #include <linux/slab.h> |
| #include <linux/version.h> |
| #include <linux/kernel.h> |
| #include <linux/console.h> |
| |
| #include <linux/module.h> |
| #include <linux/string.h> |
| #include <linux/notifier.h> |
| |
| #if defined(LDM_PLATFORM) |
| #include <linux/platform_device.h> |
| #if defined(SGX_EARLYSUSPEND) |
| #include <linux/earlysuspend.h> |
| #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; i<ui32BufferCount-1; i++) |
| { |
| psBuffer[i].psNext = &psBuffer[i+1]; |
| } |
| psBuffer[i].psNext = &psBuffer[0]; |
| |
| /* Initialize each buffer abstraction structure */ |
| for(i=0; i<ui32BufferCount; i++) |
| { |
| /* Get the needed buffers from the display flip chain */ |
| IMG_UINT32 ui32SwapBuffer = i + ui32BuffersToSkip; |
| struct omap_display_buffer * flip_buffer = |
| display->flip_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; i<ui32BufferCount; i++) |
| { |
| psFlipItems[i].bValid = OMAP_FALSE; |
| psFlipItems[i].bFlipped = OMAP_FALSE; |
| psFlipItems[i].bCmdCompleted = OMAP_FALSE; |
| psFlipItems[i].display_buffer = 0; |
| } |
| |
| mutex_lock(&psDevInfo->sSwapChainLockMutex); |
| |
| 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; i<psSwapChain->ulBufferCount; 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) { |
| /* Update display */ |
| display->present_buffer_sync(psFlipItem->display_buffer); |
| |
| psFlipItem->ulSwapInterval--; |
| psFlipItem->bFlipped = OMAP_TRUE; |
| |
| if (psFlipItem->ulSwapInterval == 0) { |
| |
| /* Mark the flip item as completed to reuse it */ |
| psSwapChain->ulRemoveIndex++; |
| if (psSwapChain->ulRemoveIndex > ulMaxIndex) |
| psSwapChain->ulRemoveIndex = 0; |
| psFlipItem->bCmdCompleted = OMAP_FALSE; |
| psFlipItem->bFlipped = OMAP_FALSE; |
| psFlipItem->bValid = OMAP_FALSE; |
| |
| psSwapChain->psPVRJTable->pfnPVRSRVCmdComplete( |
| (IMG_HANDLE)psFlipItem->hCmdComplete, |
| IMG_TRUE); |
| psFlipItem->bCmdCompleted = OMAP_TRUE; |
| } else { |
| /* |
| * Here the swap interval is not zero yet |
| * we need to schedule another work until |
| * it reaches zero |
| */ |
| queue_work(psDevInfo->sync_display_wq, |
| &psDevInfo->sync_display_work); |
| break; |
| } |
| |
| 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->sSystemBuffer.display_buffer = display->main_buffer; |
| 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"); |
| |
| /* Init display abstraction layer */ |
| omap_display_initialize(); |
| |
| /* Ask for the number of displays available */ |
| /* 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); |