diff options
Diffstat (limited to 'pvr-source/services4/3rdparty/dc_omapfb3_linux')
5 files changed, 3303 insertions, 0 deletions
diff --git a/pvr-source/services4/3rdparty/dc_omapfb3_linux/Kbuild.mk b/pvr-source/services4/3rdparty/dc_omapfb3_linux/Kbuild.mk new file mode 100644 index 0000000..7f4fd99 --- /dev/null +++ b/pvr-source/services4/3rdparty/dc_omapfb3_linux/Kbuild.mk @@ -0,0 +1,48 @@ +########################################################################### ### +#@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +#@License Dual MIT/GPLv2 +# +# The contents of this file are subject to the MIT license as set out below. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# Alternatively, the contents of this file may be used under the terms of +# the GNU General Public License Version 2 ("GPL") in which case the provisions +# of GPL are applicable instead of those above. +# +# If you wish to allow use of your version of this file only under the terms of +# GPL, and not to allow others to use your version of this file under the terms +# of the MIT license, indicate your decision by deleting the provisions above +# and replace them with the notice and other provisions required by GPL as set +# out in the file called "GPL-COPYING" included in this distribution. If you do +# not delete the provisions above, a recipient may use your version of this file +# under the terms of either the MIT license or GPL. +# +# This License is also included in this distribution in the file called +# "MIT-COPYING". +# +# EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +# PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +### ########################################################################### + +ccflags-y += \ + -I$(TOP)/services4/3rdparty/dc_omapfb3_linux \ + -I$(KERNELDIR)/drivers/video/omap2 \ + -I$(KERNELDIR)/arch/arm/plat-omap/include + +omaplfb-y += \ + services4/3rdparty/dc_omapfb3_linux/omaplfb_displayclass.o \ + services4/3rdparty/dc_omapfb3_linux/omaplfb_linux.o diff --git a/pvr-source/services4/3rdparty/dc_omapfb3_linux/Linux.mk b/pvr-source/services4/3rdparty/dc_omapfb3_linux/Linux.mk new file mode 100644 index 0000000..75d11a9 --- /dev/null +++ b/pvr-source/services4/3rdparty/dc_omapfb3_linux/Linux.mk @@ -0,0 +1,45 @@ +########################################################################### ### +#@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +#@License Dual MIT/GPLv2 +# +# The contents of this file are subject to the MIT license as set out below. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# Alternatively, the contents of this file may be used under the terms of +# the GNU General Public License Version 2 ("GPL") in which case the provisions +# of GPL are applicable instead of those above. +# +# If you wish to allow use of your version of this file only under the terms of +# GPL, and not to allow others to use your version of this file under the terms +# of the MIT license, indicate your decision by deleting the provisions above +# and replace them with the notice and other provisions required by GPL as set +# out in the file called "GPL-COPYING" included in this distribution. If you do +# not delete the provisions above, a recipient may use your version of this file +# under the terms of either the MIT license or GPL. +# +# This License is also included in this distribution in the file called +# "MIT-COPYING". +# +# EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +# PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +### ########################################################################### + +modules := dc_omapfb3_linux + +dc_omapfb3_linux_type := kernel_module +dc_omapfb3_linux_target := omaplfb.ko +dc_omapfb3_linux_makefile := $(THIS_DIR)/Kbuild.mk diff --git a/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb.h b/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb.h new file mode 100644 index 0000000..804695b --- /dev/null +++ b/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb.h @@ -0,0 +1,323 @@ +/*************************************************************************/ /*! +@Title OMAP Linux display driver structures and prototypes +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ +#ifndef __OMAPLFB_H__ +#define __OMAPLFB_H__ + +#include <linux/version.h> + +#include <asm/atomic.h> + +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/fb.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/notifier.h> +#include <linux/mutex.h> + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)) +#define OMAPLFB_CONSOLE_LOCK() console_lock() +#define OMAPLFB_CONSOLE_UNLOCK() console_unlock() +#else +#define OMAPLFB_CONSOLE_LOCK() acquire_console_sem() +#define OMAPLFB_CONSOLE_UNLOCK() release_console_sem() +#endif + +#define unref__ __attribute__ ((unused)) + +typedef void * OMAPLFB_HANDLE; + +typedef bool OMAPLFB_BOOL, *OMAPLFB_PBOOL; +#define OMAPLFB_FALSE false +#define OMAPLFB_TRUE true + +typedef atomic_t OMAPLFB_ATOMIC_BOOL; + +typedef atomic_t OMAPLFB_ATOMIC_INT; + +/* OMAPLFB buffer structure */ +typedef struct OMAPLFB_BUFFER_TAG +{ + struct OMAPLFB_BUFFER_TAG *psNext; + struct OMAPLFB_DEVINFO_TAG *psDevInfo; + + struct work_struct sWork; + + /* Position of this buffer in the virtual framebuffer */ + unsigned long ulYOffset; + + /* IMG structures used, to minimise API function code */ + /* replace with own structures where necessary */ + IMG_SYS_PHYADDR sSysAddr; + IMG_CPU_VIRTADDR sCPUVAddr; + PVRSRV_SYNC_DATA *psSyncData; + + OMAPLFB_HANDLE hCmdComplete; + unsigned long ulSwapInterval; +} OMAPLFB_BUFFER; + +/* OMAPLFB swapchain structure */ +typedef struct OMAPLFB_SWAPCHAIN_TAG +{ + /* Swap chain ID */ + unsigned int uiSwapChainID; + + /* number of buffers in swapchain */ + unsigned long ulBufferCount; + + /* list of buffers in the swapchain */ + OMAPLFB_BUFFER *psBuffer; + + /* Swap chain work queue */ + struct workqueue_struct *psWorkQueue; + + /* + * Set if we didn't manage to wait for VSync on last swap, + * or if we think we need to wait for VSync on the next flip. + * The flag helps to avoid jitter when the screen is + * unblanked, by forcing an extended wait for VSync before + * attempting the next flip. + */ + OMAPLFB_BOOL bNotVSynced; + + /* Previous number of blank events */ + int iBlankEvents; + + /* Framebuffer Device ID for messages (e.g. printk) */ + unsigned int uiFBDevID; +} OMAPLFB_SWAPCHAIN; + +typedef struct OMAPLFB_FBINFO_TAG +{ + unsigned long ulFBSize; + unsigned long ulBufferSize; + unsigned long ulRoundedBufferSize; + unsigned long ulWidth; + unsigned long ulHeight; + unsigned long ulByteStride; + unsigned long ulPhysicalWidthmm; + unsigned long ulPhysicalHeightmm; + + /* IMG structures used, to minimise API function code */ + /* replace with own structures where necessary */ + IMG_SYS_PHYADDR sSysAddr;//system physical address + IMG_CPU_VIRTADDR sCPUVAddr; + + /* pixelformat of system/primary surface */ + PVRSRV_PIXEL_FORMAT ePixelFormat; + +#if defined(CONFIG_DSSCOMP) + OMAPLFB_BOOL bIs2D; + IMG_SYS_PHYADDR *psPageList; + struct ion_handle *psIONHandle; + IMG_UINT32 uiBytesPerPixel; +#endif +} OMAPLFB_FBINFO; + +/* kernel device information structure */ +typedef struct OMAPLFB_DEVINFO_TAG +{ + /* Framebuffer Device ID */ + unsigned int uiFBDevID; + + /* PVR Device ID */ + unsigned int uiPVRDevID; + + /* Swapchain create/destroy mutex */ + struct mutex sCreateSwapChainMutex; + + /* system surface info */ + OMAPLFB_BUFFER sSystemBuffer; + + /* jump table into PVR services */ + PVRSRV_DC_DISP2SRV_KMJTABLE sPVRJTable; + + /* jump table into DC */ + PVRSRV_DC_SRV2DISP_KMJTABLE sDCJTable; + + /* fb info structure */ + OMAPLFB_FBINFO sFBInfo; + + /* Only one swapchain supported by this device so hang it here */ + OMAPLFB_SWAPCHAIN *psSwapChain; + + /* Swap chain ID */ + unsigned int uiSwapChainID; + + /* True if PVR Services is flushing its command queues */ + OMAPLFB_ATOMIC_BOOL sFlushCommands; + + /* pointer to linux frame buffer information structure */ + struct fb_info *psLINFBInfo; + + /* Linux Framebuffer event notification block */ + struct notifier_block sLINNotifBlock; + + /* IMG structures used, to minimise API function code */ + /* replace with own structures where necessary */ + + /* Address of the surface being displayed */ + IMG_DEV_VIRTADDR sDisplayDevVAddr; + + DISPLAY_INFO sDisplayInfo; + + /* Display format */ + DISPLAY_FORMAT sDisplayFormat; + + /* Display dimensions */ + DISPLAY_DIMS sDisplayDim; + + /* True if screen is blanked */ + OMAPLFB_ATOMIC_BOOL sBlanked; + + /* Number of blank/unblank events */ + OMAPLFB_ATOMIC_INT sBlankEvents; + +#ifdef CONFIG_HAS_EARLYSUSPEND + /* Set by early suspend */ + OMAPLFB_ATOMIC_BOOL sEarlySuspendFlag; + + struct early_suspend sEarlySuspend; +#endif + +#if defined(SUPPORT_DRI_DRM) + OMAPLFB_ATOMIC_BOOL sLeaveVT; +#endif + +} OMAPLFB_DEVINFO; + +#define OMAPLFB_PAGE_SIZE 4096 + +/* DEBUG only printk */ +#ifdef DEBUG +#define DEBUG_PRINTK(x) printk x +#else +#define DEBUG_PRINTK(x) +#endif + +#define DISPLAY_DEVICE_NAME "PowerVR OMAP Linux Display Driver" +#define DRVNAME "omaplfb" +#define DEVNAME DRVNAME +#define DRIVER_PREFIX DRVNAME + +/*! + ***************************************************************************** + * Error values + *****************************************************************************/ +typedef enum _OMAPLFB_ERROR_ +{ + OMAPLFB_OK = 0, + OMAPLFB_ERROR_GENERIC = 1, + OMAPLFB_ERROR_OUT_OF_MEMORY = 2, + OMAPLFB_ERROR_TOO_FEW_BUFFERS = 3, + OMAPLFB_ERROR_INVALID_PARAMS = 4, + OMAPLFB_ERROR_INIT_FAILURE = 5, + OMAPLFB_ERROR_CANT_REGISTER_CALLBACK = 6, + OMAPLFB_ERROR_INVALID_DEVICE = 7, + OMAPLFB_ERROR_DEVICE_REGISTER_FAILED = 8, + OMAPLFB_ERROR_SET_UPDATE_MODE_FAILED = 9 +} OMAPLFB_ERROR; + +typedef enum _OMAPLFB_UPDATE_MODE_ +{ + OMAPLFB_UPDATE_MODE_UNDEFINED = 0, + OMAPLFB_UPDATE_MODE_MANUAL = 1, + OMAPLFB_UPDATE_MODE_AUTO = 2, + OMAPLFB_UPDATE_MODE_DISABLED = 3 +} OMAPLFB_UPDATE_MODE; + +#ifndef UNREFERENCED_PARAMETER +#define UNREFERENCED_PARAMETER(param) (param) = (param) +#endif + +OMAPLFB_ERROR OMAPLFBInit(void); +OMAPLFB_ERROR OMAPLFBDeInit(void); + +/* OS Specific APIs */ +OMAPLFB_DEVINFO *OMAPLFBGetDevInfoPtr(unsigned uiFBDevID); +unsigned OMAPLFBMaxFBDevIDPlusOne(void); +void *OMAPLFBAllocKernelMem(unsigned long ulSize); +void OMAPLFBFreeKernelMem(void *pvMem); +OMAPLFB_ERROR OMAPLFBGetLibFuncAddr(char *szFunctionName, PFN_DC_GET_PVRJTABLE *ppfnFuncTable); +OMAPLFB_ERROR OMAPLFBCreateSwapQueue (OMAPLFB_SWAPCHAIN *psSwapChain); +void OMAPLFBDestroySwapQueue(OMAPLFB_SWAPCHAIN *psSwapChain); +void OMAPLFBInitBufferForSwap(OMAPLFB_BUFFER *psBuffer); +void OMAPLFBSwapHandler(OMAPLFB_BUFFER *psBuffer); +void OMAPLFBQueueBufferForSwap(OMAPLFB_SWAPCHAIN *psSwapChain, OMAPLFB_BUFFER *psBuffer); +void OMAPLFBFlip(OMAPLFB_DEVINFO *psDevInfo, OMAPLFB_BUFFER *psBuffer); +OMAPLFB_UPDATE_MODE OMAPLFBGetUpdateMode(OMAPLFB_DEVINFO *psDevInfo); +OMAPLFB_BOOL OMAPLFBSetUpdateMode(OMAPLFB_DEVINFO *psDevInfo, OMAPLFB_UPDATE_MODE eMode); +OMAPLFB_BOOL OMAPLFBWaitForVSync(OMAPLFB_DEVINFO *psDevInfo); +OMAPLFB_BOOL OMAPLFBManualSync(OMAPLFB_DEVINFO *psDevInfo); +OMAPLFB_BOOL OMAPLFBCheckModeAndSync(OMAPLFB_DEVINFO *psDevInfo); +OMAPLFB_ERROR OMAPLFBUnblankDisplay(OMAPLFB_DEVINFO *psDevInfo); +OMAPLFB_ERROR OMAPLFBEnableLFBEventNotification(OMAPLFB_DEVINFO *psDevInfo); +OMAPLFB_ERROR OMAPLFBDisableLFBEventNotification(OMAPLFB_DEVINFO *psDevInfo); +void OMAPLFBCreateSwapChainLockInit(OMAPLFB_DEVINFO *psDevInfo); +void OMAPLFBCreateSwapChainLockDeInit(OMAPLFB_DEVINFO *psDevInfo); +void OMAPLFBCreateSwapChainLock(OMAPLFB_DEVINFO *psDevInfo); +void OMAPLFBCreateSwapChainUnLock(OMAPLFB_DEVINFO *psDevInfo); +void OMAPLFBAtomicBoolInit(OMAPLFB_ATOMIC_BOOL *psAtomic, OMAPLFB_BOOL bVal); +void OMAPLFBAtomicBoolDeInit(OMAPLFB_ATOMIC_BOOL *psAtomic); +void OMAPLFBAtomicBoolSet(OMAPLFB_ATOMIC_BOOL *psAtomic, OMAPLFB_BOOL bVal); +OMAPLFB_BOOL OMAPLFBAtomicBoolRead(OMAPLFB_ATOMIC_BOOL *psAtomic); +void OMAPLFBAtomicIntInit(OMAPLFB_ATOMIC_INT *psAtomic, int iVal); +void OMAPLFBAtomicIntDeInit(OMAPLFB_ATOMIC_INT *psAtomic); +void OMAPLFBAtomicIntSet(OMAPLFB_ATOMIC_INT *psAtomic, int iVal); +int OMAPLFBAtomicIntRead(OMAPLFB_ATOMIC_INT *psAtomic); +void OMAPLFBAtomicIntInc(OMAPLFB_ATOMIC_INT *psAtomic); + +#if defined(DEBUG) +void OMAPLFBPrintInfo(OMAPLFB_DEVINFO *psDevInfo); +#else +#define OMAPLFBPrintInfo(psDevInfo) +#endif + +#endif /* __OMAPLFB_H__ */ + +/****************************************************************************** + End of file (omaplfb.h) +******************************************************************************/ + diff --git a/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb_displayclass.c b/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb_displayclass.c new file mode 100644 index 0000000..ebca814 --- /dev/null +++ b/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb_displayclass.c @@ -0,0 +1,1722 @@ +/*************************************************************************/ /*! +@Title OMAP common display driver components +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +/************************************************************************** + The 3rd party driver is a specification of an API to integrate the IMG POWERVR + Services driver with 3rd Party display hardware. It is NOT a specification for + a display controller driver, rather a specification to extend the API for a + pre-existing driver for the display hardware. + + The 3rd party driver interface provides IMG POWERVR client drivers (e.g. PVR2D) + with an API abstraction of the system's underlying display hardware, allowing + the client drivers to indirectly control the display hardware and access its + associated memory. + + Functions of the API include + - query primary surface attributes (width, height, stride, pixel format, CPU + physical and virtual address) + - swap/flip chain creation and subsequent query of surface attributes + - asynchronous display surface flipping, taking account of asynchronous read + (flip) and write (render) operations to the display surface + + Note: having queried surface attributes the client drivers are able to map the + display memory to any IMG POWERVR Services device by calling + PVRSRVMapDeviceClassMemory with the display surface handle. + + This code is intended to be an example of how a pre-existing display driver may + be extended to support the 3rd Party Display interface to POWERVR Services + - IMG is not providing a display driver implementation. + **************************************************************************/ + +/* + * OMAP Linux 3rd party display driver. + * This is based on the Generic PVR Linux Framebuffer 3rd party display + * driver, with OMAP specific extensions to support flipping. + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/fb.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/notifier.h> + +/* IMG services headers */ +#include "img_defs.h" +#include "servicesext.h" +#include "kerneldisplay.h" +#include "omaplfb.h" + +#if defined(CONFIG_DSSCOMP) + +#if !defined(CONFIG_ION_OMAP) +#error CONFIG_DSSCOMP support requires CONFIG_ION_OMAP +#endif + +#include <linux/ion.h> +#include <linux/omap_ion.h> + +extern struct ion_client *gpsIONClient; + +#include <mach/tiler.h> +#include <video/dsscomp.h> +#include <plat/dsscomp.h> + +#endif /* defined(CONFIG_DSSCOMP) */ + +#define OMAPLFB_COMMAND_COUNT 1 + +#define OMAPLFB_VSYNC_SETTLE_COUNT 5 + +#define OMAPLFB_MAX_NUM_DEVICES FB_MAX +#if (OMAPLFB_MAX_NUM_DEVICES > FB_MAX) +#error "OMAPLFB_MAX_NUM_DEVICES must not be greater than FB_MAX" +#endif + +static OMAPLFB_DEVINFO *gapsDevInfo[OMAPLFB_MAX_NUM_DEVICES]; + +/* Top level 'hook ptr' */ +static PFN_DC_GET_PVRJTABLE gpfnGetPVRJTable = NULL; + +/* Round x up to a multiple of y */ +static inline unsigned long RoundUpToMultiple(unsigned long x, unsigned long y) +{ + unsigned long div = x / y; + unsigned long rem = x % y; + + return (div + ((rem == 0) ? 0 : 1)) * y; +} + +/* Greatest common divisor of x and y */ +static unsigned long GCD(unsigned long x, unsigned long y) +{ + while (y != 0) + { + unsigned long r = x % y; + x = y; + y = r; + } + + return x; +} + +/* Least common multiple of x and y */ +static unsigned long LCM(unsigned long x, unsigned long y) +{ + unsigned long gcd = GCD(x, y); + + return (gcd == 0) ? 0 : ((x / gcd) * y); +} + +unsigned OMAPLFBMaxFBDevIDPlusOne(void) +{ + return OMAPLFB_MAX_NUM_DEVICES; +} + +/* Returns DevInfo pointer for a given device */ +OMAPLFB_DEVINFO *OMAPLFBGetDevInfoPtr(unsigned uiFBDevID) +{ + WARN_ON(uiFBDevID >= OMAPLFBMaxFBDevIDPlusOne()); + + if (uiFBDevID >= OMAPLFB_MAX_NUM_DEVICES) + { + return NULL; + } + + return gapsDevInfo[uiFBDevID]; +} + +/* Sets the DevInfo pointer for a given device */ +static inline void OMAPLFBSetDevInfoPtr(unsigned uiFBDevID, OMAPLFB_DEVINFO *psDevInfo) +{ + WARN_ON(uiFBDevID >= OMAPLFB_MAX_NUM_DEVICES); + + if (uiFBDevID < OMAPLFB_MAX_NUM_DEVICES) + { + gapsDevInfo[uiFBDevID] = psDevInfo; + } +} + +static inline OMAPLFB_BOOL SwapChainHasChanged(OMAPLFB_DEVINFO *psDevInfo, OMAPLFB_SWAPCHAIN *psSwapChain) +{ + return (psDevInfo->psSwapChain != psSwapChain) || + (psDevInfo->uiSwapChainID != psSwapChain->uiSwapChainID); +} + +/* Don't wait for vertical sync */ +static inline OMAPLFB_BOOL DontWaitForVSync(OMAPLFB_DEVINFO *psDevInfo) +{ + OMAPLFB_BOOL bDontWait; + + bDontWait = OMAPLFBAtomicBoolRead(&psDevInfo->sBlanked) || + OMAPLFBAtomicBoolRead(&psDevInfo->sFlushCommands); + +#if defined(CONFIG_HAS_EARLYSUSPEND) + bDontWait = bDontWait || OMAPLFBAtomicBoolRead(&psDevInfo->sEarlySuspendFlag); +#endif +#if defined(SUPPORT_DRI_DRM) + bDontWait = bDontWait || OMAPLFBAtomicBoolRead(&psDevInfo->sLeaveVT); +#endif + return bDontWait; +} + +/* + * SetDCState + * Called from services. + */ +static IMG_VOID SetDCState(IMG_HANDLE hDevice, IMG_UINT32 ui32State) +{ + OMAPLFB_DEVINFO *psDevInfo = (OMAPLFB_DEVINFO *)hDevice; + + switch (ui32State) + { + case DC_STATE_FLUSH_COMMANDS: + OMAPLFBAtomicBoolSet(&psDevInfo->sFlushCommands, OMAPLFB_TRUE); + break; + case DC_STATE_NO_FLUSH_COMMANDS: + OMAPLFBAtomicBoolSet(&psDevInfo->sFlushCommands, OMAPLFB_FALSE); + break; + case DC_STATE_FORCE_SWAP_TO_SYSTEM: + OMAPLFBFlip(psDevInfo, &psDevInfo->sSystemBuffer); + break; + default: + break; + } +} + +/* + * OpenDCDevice + * Called from services. + */ +static PVRSRV_ERROR OpenDCDevice(IMG_UINT32 uiPVRDevID, + IMG_HANDLE *phDevice, + PVRSRV_SYNC_DATA* psSystemBufferSyncData) +{ + OMAPLFB_DEVINFO *psDevInfo; + OMAPLFB_ERROR eError; + unsigned uiMaxFBDevIDPlusOne = OMAPLFBMaxFBDevIDPlusOne(); + unsigned i; + + for (i = 0; i < uiMaxFBDevIDPlusOne; i++) + { + psDevInfo = OMAPLFBGetDevInfoPtr(i); + if (psDevInfo != NULL && psDevInfo->uiPVRDevID == uiPVRDevID) + { + break; + } + } + if (i == uiMaxFBDevIDPlusOne) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX + ": %s: PVR Device %u not found\n", __FUNCTION__, uiPVRDevID)); + return PVRSRV_ERROR_INVALID_DEVICE; + } + + /* store the system surface sync data */ + psDevInfo->sSystemBuffer.psSyncData = psSystemBufferSyncData; + + eError = OMAPLFBUnblankDisplay(psDevInfo); + if (eError != OMAPLFB_OK) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX + ": %s: Device %u: OMAPLFBUnblankDisplay failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, eError)); + return PVRSRV_ERROR_UNBLANK_DISPLAY_FAILED; + } + + /* return handle to the devinfo */ + *phDevice = (IMG_HANDLE)psDevInfo; + + return PVRSRV_OK; +} + +/* + * CloseDCDevice + * Called from services. + */ +static PVRSRV_ERROR CloseDCDevice(IMG_HANDLE hDevice) +{ +#if defined(SUPPORT_DRI_DRM) + OMAPLFB_DEVINFO *psDevInfo = (OMAPLFB_DEVINFO *)hDevice; + + OMAPLFBAtomicBoolSet(&psDevInfo->sLeaveVT, OMAPLFB_FALSE); + (void) OMAPLFBUnblankDisplay(psDevInfo); +#else + UNREFERENCED_PARAMETER(hDevice); +#endif + return PVRSRV_OK; +} + +/* + * EnumDCFormats + * Called from services. + */ +static PVRSRV_ERROR EnumDCFormats(IMG_HANDLE hDevice, + IMG_UINT32 *pui32NumFormats, + DISPLAY_FORMAT *psFormat) +{ + OMAPLFB_DEVINFO *psDevInfo; + + if(!hDevice || !pui32NumFormats) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + + *pui32NumFormats = 1; + + if(psFormat) + { + psFormat[0] = psDevInfo->sDisplayFormat; + } + + return PVRSRV_OK; +} + +/* + * EnumDCDims + * Called from services. + */ +static PVRSRV_ERROR EnumDCDims(IMG_HANDLE hDevice, + DISPLAY_FORMAT *psFormat, + IMG_UINT32 *pui32NumDims, + DISPLAY_DIMS *psDim) +{ + OMAPLFB_DEVINFO *psDevInfo; + + if(!hDevice || !psFormat || !pui32NumDims) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + + *pui32NumDims = 1; + + /* No need to look at psFormat; there is only one */ + if(psDim) + { + psDim[0] = psDevInfo->sDisplayDim; + } + + return PVRSRV_OK; +} + + +/* + * GetDCSystemBuffer + * Called from services. + */ +static PVRSRV_ERROR GetDCSystemBuffer(IMG_HANDLE hDevice, IMG_HANDLE *phBuffer) +{ + OMAPLFB_DEVINFO *psDevInfo; + + if(!hDevice || !phBuffer) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + + *phBuffer = (IMG_HANDLE)&psDevInfo->sSystemBuffer; + + return PVRSRV_OK; +} + + +/* + * GetDCInfo + * Called from services. + */ +static PVRSRV_ERROR GetDCInfo(IMG_HANDLE hDevice, DISPLAY_INFO *psDCInfo) +{ + OMAPLFB_DEVINFO *psDevInfo; + + if(!hDevice || !psDCInfo) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + + *psDCInfo = psDevInfo->sDisplayInfo; + + return PVRSRV_OK; +} + +/* + * GetDCBufferAddr + * Called from services. + */ +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) +{ + OMAPLFB_DEVINFO *psDevInfo; + OMAPLFB_BUFFER *psSystemBuffer; + + UNREFERENCED_PARAMETER(pui32TilingStride); + + if(!hDevice) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if(!hBuffer) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (!ppsSysAddr) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + if (!pui32ByteSize) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + + psSystemBuffer = (OMAPLFB_BUFFER *)hBuffer; + + *ppsSysAddr = &psSystemBuffer->sSysAddr; + + *pui32ByteSize = (IMG_UINT32)psDevInfo->sFBInfo.ulBufferSize; + + if (ppvCpuVAddr) + { +#if defined(CONFIG_DSSCOMP) + *ppvCpuVAddr = psDevInfo->sFBInfo.bIs2D ? NULL : psSystemBuffer->sCPUVAddr; +#else + *ppvCpuVAddr = psSystemBuffer->sCPUVAddr; +#endif + } + + if (phOSMapInfo) + { + *phOSMapInfo = (IMG_HANDLE)0; + } + + if (pbIsContiguous) + { +#if defined(CONFIG_DSSCOMP) + *pbIsContiguous = !psDevInfo->sFBInfo.bIs2D; +#else + *pbIsContiguous = IMG_TRUE; +#endif + } + +#if defined(CONFIG_DSSCOMP) + if (psDevInfo->sFBInfo.bIs2D) + { + int i = (psSystemBuffer->sSysAddr.uiAddr - psDevInfo->sFBInfo.psPageList->uiAddr) >> PAGE_SHIFT; + *ppsSysAddr = psDevInfo->sFBInfo.psPageList + psDevInfo->sFBInfo.ulHeight * i; + } +#endif + + return PVRSRV_OK; +} + +/* + * CreateDCSwapChain + * Called from services. + */ +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) +{ + OMAPLFB_DEVINFO *psDevInfo; + OMAPLFB_SWAPCHAIN *psSwapChain; + OMAPLFB_BUFFER *psBuffer; + IMG_UINT32 i; + PVRSRV_ERROR eError; + IMG_UINT32 ui32BuffersToSkip; + + UNREFERENCED_PARAMETER(ui32OEMFlags); + + /* Check parameters */ + if(!hDevice + || !psDstSurfAttrib + || !psSrcSurfAttrib + || !ppsSyncData + || !phSwapChain) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + + /* Do we support swap chains? */ + if (psDevInfo->sDisplayInfo.ui32MaxSwapChains == 0) + { + return PVRSRV_ERROR_NOT_SUPPORTED; + } + + OMAPLFBCreateSwapChainLock(psDevInfo); + + /* The driver only supports a single swapchain */ + if(psDevInfo->psSwapChain != NULL) + { + eError = PVRSRV_ERROR_FLIP_CHAIN_EXISTS; + goto ExitUnLock; + } + + /* Check the buffer count */ + if(ui32BufferCount > psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers) + { + eError = PVRSRV_ERROR_TOOMANYBUFFERS; + goto ExitUnLock; + } + + if ((psDevInfo->sFBInfo.ulRoundedBufferSize * (unsigned long)ui32BufferCount) > psDevInfo->sFBInfo.ulFBSize) + { + eError = PVRSRV_ERROR_TOOMANYBUFFERS; + goto ExitUnLock; + } + + /* + * We will allocate the swap chain buffers at the back of the frame + * buffer area. This preserves the front portion, which may be being + * used by other Linux Framebuffer based applications. + */ + ui32BuffersToSkip = psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers - ui32BufferCount; + + /* + * Verify the DST/SRC attributes, + * SRC/DST must match the current display mode config + */ + if(psDstSurfAttrib->pixelformat != psDevInfo->sDisplayFormat.pixelformat + || psDstSurfAttrib->sDims.ui32ByteStride != psDevInfo->sDisplayDim.ui32ByteStride + || psDstSurfAttrib->sDims.ui32Width != psDevInfo->sDisplayDim.ui32Width + || psDstSurfAttrib->sDims.ui32Height != psDevInfo->sDisplayDim.ui32Height) + { + /* DST doesn't match the current mode */ + eError = PVRSRV_ERROR_INVALID_PARAMS; + goto ExitUnLock; + } + + if(psDstSurfAttrib->pixelformat != psSrcSurfAttrib->pixelformat + || psDstSurfAttrib->sDims.ui32ByteStride != psSrcSurfAttrib->sDims.ui32ByteStride + || psDstSurfAttrib->sDims.ui32Width != psSrcSurfAttrib->sDims.ui32Width + || psDstSurfAttrib->sDims.ui32Height != psSrcSurfAttrib->sDims.ui32Height) + { + /* DST doesn't match the SRC */ + eError = PVRSRV_ERROR_INVALID_PARAMS; + goto ExitUnLock; + } + + /* check flags if implementation requires them */ + UNREFERENCED_PARAMETER(ui32Flags); + +#if defined(PVR_OMAPFB3_UPDATE_MODE) + if (!OMAPLFBSetUpdateMode(psDevInfo, PVR_OMAPFB3_UPDATE_MODE)) + { + printk(KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Couldn't set frame buffer update mode %d\n", __FUNCTION__, psDevInfo->uiFBDevID, PVR_OMAPFB3_UPDATE_MODE); + } +#endif + /* create a swapchain structure */ + psSwapChain = (OMAPLFB_SWAPCHAIN*)OMAPLFBAllocKernelMem(sizeof(OMAPLFB_SWAPCHAIN)); + if(!psSwapChain) + { + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto ExitUnLock; + } + + psBuffer = (OMAPLFB_BUFFER*)OMAPLFBAllocKernelMem(sizeof(OMAPLFB_BUFFER) * ui32BufferCount); + if(!psBuffer) + { + eError = PVRSRV_ERROR_OUT_OF_MEMORY; + goto ErrorFreeSwapChain; + } + + psSwapChain->ulBufferCount = (unsigned long)ui32BufferCount; + psSwapChain->psBuffer = psBuffer; + psSwapChain->bNotVSynced = OMAPLFB_TRUE; + psSwapChain->uiFBDevID = psDevInfo->uiFBDevID; + + /* Link the buffers */ + for(i=0; i<ui32BufferCount-1; i++) + { + psBuffer[i].psNext = &psBuffer[i+1]; + } + /* and link last to first */ + psBuffer[i].psNext = &psBuffer[0]; + + /* Configure the swapchain buffers */ + for(i=0; i<ui32BufferCount; i++) + { + IMG_UINT32 ui32SwapBuffer = i + ui32BuffersToSkip; + IMG_UINT32 ui32BufferOffset = ui32SwapBuffer * (IMG_UINT32)psDevInfo->sFBInfo.ulRoundedBufferSize; + +#if defined(CONFIG_DSSCOMP) + if (psDevInfo->sFBInfo.bIs2D) + { + ui32BufferOffset = 0; + } +#endif /* defined(CONFIG_DSSCOMP) */ + + psBuffer[i].psSyncData = ppsSyncData[i]; + + psBuffer[i].sSysAddr.uiAddr = psDevInfo->sFBInfo.sSysAddr.uiAddr + ui32BufferOffset; + psBuffer[i].sCPUVAddr = psDevInfo->sFBInfo.sCPUVAddr + ui32BufferOffset; + psBuffer[i].ulYOffset = ui32BufferOffset / psDevInfo->sFBInfo.ulByteStride; + psBuffer[i].psDevInfo = psDevInfo; + +#if defined(CONFIG_DSSCOMP) + if (psDevInfo->sFBInfo.bIs2D) + { + psBuffer[i].sSysAddr.uiAddr += ui32SwapBuffer * + ALIGN((IMG_UINT32)psDevInfo->sFBInfo.ulWidth * psDevInfo->sFBInfo.uiBytesPerPixel, PAGE_SIZE); + } +#endif /* defined(CONFIG_DSSCOMP) */ + + OMAPLFBInitBufferForSwap(&psBuffer[i]); + } + + if (OMAPLFBCreateSwapQueue(psSwapChain) != OMAPLFB_OK) + { + printk(KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Failed to create workqueue\n", __FUNCTION__, psDevInfo->uiFBDevID); + eError = PVRSRV_ERROR_UNABLE_TO_INSTALL_ISR; + goto ErrorFreeBuffers; + } + + if (OMAPLFBEnableLFBEventNotification(psDevInfo)!= OMAPLFB_OK) + { + eError = PVRSRV_ERROR_UNABLE_TO_ENABLE_EVENT; + printk(KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Couldn't enable framebuffer event notification\n", __FUNCTION__, psDevInfo->uiFBDevID); + goto ErrorDestroySwapQueue; + } + + psDevInfo->uiSwapChainID++; + if (psDevInfo->uiSwapChainID == 0) + { + psDevInfo->uiSwapChainID++; + } + + psSwapChain->uiSwapChainID = psDevInfo->uiSwapChainID; + + psDevInfo->psSwapChain = psSwapChain; + + *pui32SwapChainID = psDevInfo->uiSwapChainID; + + *phSwapChain = (IMG_HANDLE)psSwapChain; + + eError = PVRSRV_OK; + goto ExitUnLock; + +ErrorDestroySwapQueue: + OMAPLFBDestroySwapQueue(psSwapChain); +ErrorFreeBuffers: + OMAPLFBFreeKernelMem(psBuffer); +ErrorFreeSwapChain: + OMAPLFBFreeKernelMem(psSwapChain); +ExitUnLock: + OMAPLFBCreateSwapChainUnLock(psDevInfo); + return eError; +} + +/* + * DestroyDCSwapChain + * Called from services. + */ +static PVRSRV_ERROR DestroyDCSwapChain(IMG_HANDLE hDevice, + IMG_HANDLE hSwapChain) +{ + OMAPLFB_DEVINFO *psDevInfo; + OMAPLFB_SWAPCHAIN *psSwapChain; + OMAPLFB_ERROR eError; + + /* Check parameters */ + if(!hDevice || !hSwapChain) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + psSwapChain = (OMAPLFB_SWAPCHAIN*)hSwapChain; + + OMAPLFBCreateSwapChainLock(psDevInfo); + + if (SwapChainHasChanged(psDevInfo, psSwapChain)) + { + printk(KERN_WARNING DRIVER_PREFIX + ": %s: Device %u: Swap chain mismatch\n", __FUNCTION__, psDevInfo->uiFBDevID); + + eError = PVRSRV_ERROR_INVALID_PARAMS; + goto ExitUnLock; + } + + /* The swap queue is flushed before being destroyed */ + OMAPLFBDestroySwapQueue(psSwapChain); + + eError = OMAPLFBDisableLFBEventNotification(psDevInfo); + if (eError != OMAPLFB_OK) + { + printk(KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Couldn't disable framebuffer event notification\n", __FUNCTION__, psDevInfo->uiFBDevID); + } + + /* Free resources */ + OMAPLFBFreeKernelMem(psSwapChain->psBuffer); + OMAPLFBFreeKernelMem(psSwapChain); + + psDevInfo->psSwapChain = NULL; + + OMAPLFBFlip(psDevInfo, &psDevInfo->sSystemBuffer); + (void) OMAPLFBCheckModeAndSync(psDevInfo); + + eError = PVRSRV_OK; + +ExitUnLock: + OMAPLFBCreateSwapChainUnLock(psDevInfo); + + return eError; +} + +/* + * SetDCDstRect + * Called from services. + */ +static PVRSRV_ERROR SetDCDstRect(IMG_HANDLE hDevice, + IMG_HANDLE hSwapChain, + IMG_RECT *psRect) +{ + UNREFERENCED_PARAMETER(hDevice); + UNREFERENCED_PARAMETER(hSwapChain); + UNREFERENCED_PARAMETER(psRect); + + /* Only full display swapchains on this device */ + + return PVRSRV_ERROR_NOT_SUPPORTED; +} + +/* + * SetDCSrcRect + * Called from services. + */ +static PVRSRV_ERROR SetDCSrcRect(IMG_HANDLE hDevice, + IMG_HANDLE hSwapChain, + IMG_RECT *psRect) +{ + UNREFERENCED_PARAMETER(hDevice); + UNREFERENCED_PARAMETER(hSwapChain); + UNREFERENCED_PARAMETER(psRect); + + /* Only full display swapchains on this device */ + + return PVRSRV_ERROR_NOT_SUPPORTED; +} + +/* + * SetDCDstColourKey + * Called from services. + */ +static PVRSRV_ERROR SetDCDstColourKey(IMG_HANDLE hDevice, + IMG_HANDLE hSwapChain, + IMG_UINT32 ui32CKColour) +{ + UNREFERENCED_PARAMETER(hDevice); + UNREFERENCED_PARAMETER(hSwapChain); + UNREFERENCED_PARAMETER(ui32CKColour); + + /* Don't support DST CK on this device */ + + return PVRSRV_ERROR_NOT_SUPPORTED; +} + +/* + * SetDCSrcColourKey + * Called from services. + */ +static PVRSRV_ERROR SetDCSrcColourKey(IMG_HANDLE hDevice, + IMG_HANDLE hSwapChain, + IMG_UINT32 ui32CKColour) +{ + UNREFERENCED_PARAMETER(hDevice); + UNREFERENCED_PARAMETER(hSwapChain); + UNREFERENCED_PARAMETER(ui32CKColour); + + /* Don't support SRC CK on this device */ + + return PVRSRV_ERROR_NOT_SUPPORTED; +} + +/* + * GetDCBuffers + * Called from services. + */ +static PVRSRV_ERROR GetDCBuffers(IMG_HANDLE hDevice, + IMG_HANDLE hSwapChain, + IMG_UINT32 *pui32BufferCount, + IMG_HANDLE *phBuffer) +{ + OMAPLFB_DEVINFO *psDevInfo; + OMAPLFB_SWAPCHAIN *psSwapChain; + PVRSRV_ERROR eError; + unsigned i; + + /* Check parameters */ + if(!hDevice + || !hSwapChain + || !pui32BufferCount + || !phBuffer) + { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + psDevInfo = (OMAPLFB_DEVINFO*)hDevice; + psSwapChain = (OMAPLFB_SWAPCHAIN*)hSwapChain; + + OMAPLFBCreateSwapChainLock(psDevInfo); + + if (SwapChainHasChanged(psDevInfo, psSwapChain)) + { + printk(KERN_WARNING DRIVER_PREFIX + ": %s: Device %u: Swap chain mismatch\n", __FUNCTION__, psDevInfo->uiFBDevID); + + eError = PVRSRV_ERROR_INVALID_PARAMS; + goto Exit; + } + + /* Return the buffer count */ + *pui32BufferCount = (IMG_UINT32)psSwapChain->ulBufferCount; + + /* Return the buffers */ + for(i=0; i<psSwapChain->ulBufferCount; i++) + { + phBuffer[i] = (IMG_HANDLE)&psSwapChain->psBuffer[i]; + } + + eError = PVRSRV_OK; + +Exit: + OMAPLFBCreateSwapChainUnLock(psDevInfo); + + return eError; +} + +/* + * SwapToDCBuffer + * Called from services. + */ +static PVRSRV_ERROR SwapToDCBuffer(IMG_HANDLE hDevice, + IMG_HANDLE hBuffer, + IMG_UINT32 ui32SwapInterval, + IMG_HANDLE hPrivateTag, + IMG_UINT32 ui32ClipRectCount, + IMG_RECT *psClipRect) +{ + UNREFERENCED_PARAMETER(hDevice); + UNREFERENCED_PARAMETER(hBuffer); + UNREFERENCED_PARAMETER(ui32SwapInterval); + UNREFERENCED_PARAMETER(hPrivateTag); + UNREFERENCED_PARAMETER(ui32ClipRectCount); + UNREFERENCED_PARAMETER(psClipRect); + + /* * Nothing to do since Services common code does the work */ + + return PVRSRV_OK; +} + +/* + * Called after the screen has unblanked, or after any other occasion + * when we didn't wait for vsync, but now need to. Not doing this after + * unblank leads to screen jitter on some screens. + * Returns true if the screen has been deemed to have settled. + */ +static OMAPLFB_BOOL WaitForVSyncSettle(OMAPLFB_DEVINFO *psDevInfo) +{ + unsigned i; + for(i = 0; i < OMAPLFB_VSYNC_SETTLE_COUNT; i++) + { + if (DontWaitForVSync(psDevInfo) || !OMAPLFBWaitForVSync(psDevInfo)) + { + return OMAPLFB_FALSE; + } + } + + return OMAPLFB_TRUE; +} + +/* + * Swap handler. + * Called from the swap chain work queue handler. + * There is no need to take the swap chain creation lock in here, or use + * some other method of stopping the swap chain from being destroyed. + * This is because the swap chain creation lock is taken when queueing work, + * and the work queue is flushed before the swap chain is destroyed. + */ +void OMAPLFBSwapHandler(OMAPLFB_BUFFER *psBuffer) +{ + OMAPLFB_DEVINFO *psDevInfo = psBuffer->psDevInfo; + OMAPLFB_SWAPCHAIN *psSwapChain = psDevInfo->psSwapChain; + OMAPLFB_BOOL bPreviouslyNotVSynced; + +#if defined(SUPPORT_DRI_DRM) + if (!OMAPLFBAtomicBoolRead(&psDevInfo->sLeaveVT)) +#endif + { + OMAPLFBFlip(psDevInfo, psBuffer); + } + + bPreviouslyNotVSynced = psSwapChain->bNotVSynced; + psSwapChain->bNotVSynced = OMAPLFB_TRUE; + + + if (!DontWaitForVSync(psDevInfo)) + { + OMAPLFB_UPDATE_MODE eMode = OMAPLFBGetUpdateMode(psDevInfo); + int iBlankEvents = OMAPLFBAtomicIntRead(&psDevInfo->sBlankEvents); + + switch(eMode) + { + case OMAPLFB_UPDATE_MODE_AUTO: + psSwapChain->bNotVSynced = OMAPLFB_FALSE; + + if (bPreviouslyNotVSynced || psSwapChain->iBlankEvents != iBlankEvents) + { + psSwapChain->iBlankEvents = iBlankEvents; + psSwapChain->bNotVSynced = !WaitForVSyncSettle(psDevInfo); + } else if (psBuffer->ulSwapInterval != 0) + { + psSwapChain->bNotVSynced = !OMAPLFBWaitForVSync(psDevInfo); + } + break; +#if defined(PVR_OMAPFB3_MANUAL_UPDATE_SYNC_IN_SWAP) + case OMAPLFB_UPDATE_MODE_MANUAL: + if (psBuffer->ulSwapInterval != 0) + { + (void) OMAPLFBManualSync(psDevInfo); + } + break; +#endif + default: + break; + } + } + + psDevInfo->sPVRJTable.pfnPVRSRVCmdComplete((IMG_HANDLE)psBuffer->hCmdComplete, IMG_TRUE); +} + +/* Triggered by PVRSRVSwapToDCBuffer */ +static IMG_BOOL ProcessFlipV1(IMG_HANDLE hCmdCookie, + OMAPLFB_DEVINFO *psDevInfo, + OMAPLFB_SWAPCHAIN *psSwapChain, + OMAPLFB_BUFFER *psBuffer, + unsigned long ulSwapInterval) +{ + OMAPLFBCreateSwapChainLock(psDevInfo); + + /* The swap chain has been destroyed */ + if (SwapChainHasChanged(psDevInfo, psSwapChain)) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX + ": %s: Device %u (PVR Device ID %u): The swap chain has been destroyed\n", + __FUNCTION__, psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID)); + } + else + { + psBuffer->hCmdComplete = (OMAPLFB_HANDLE)hCmdCookie; + psBuffer->ulSwapInterval = ulSwapInterval; +#if defined(CONFIG_DSSCOMP) + if (is_tiler_addr(psBuffer->sSysAddr.uiAddr)) + { + IMG_UINT32 w = psBuffer->psDevInfo->sDisplayDim.ui32Width; + IMG_UINT32 h = psBuffer->psDevInfo->sDisplayDim.ui32Height; + struct dsscomp_setup_dispc_data comp = { + .num_mgrs = 1, + .mgrs[0].alpha_blending = 1, + .num_ovls = 1, + .ovls[0].cfg = + { + .width = w, + .win.w = w, + .crop.w = w, + .height = h, + .win.h = h, + .crop.h = h, + .stride = psBuffer->psDevInfo->sDisplayDim.ui32ByteStride, + .color_mode = OMAP_DSS_COLOR_ARGB32, + .enabled = 1, + .global_alpha = 255, + }, + .mode = DSSCOMP_SETUP_DISPLAY, + }; + struct tiler_pa_info *pas[1] = { NULL }; + comp.ovls[0].ba = (u32) psBuffer->sSysAddr.uiAddr; + dsscomp_gralloc_queue(&comp, pas, true, + (void *) psDevInfo->sPVRJTable.pfnPVRSRVCmdComplete, + (void *) psBuffer->hCmdComplete); + } + else +#endif /* defined(CONFIG_DSSCOMP) */ + { + OMAPLFBQueueBufferForSwap(psSwapChain, psBuffer); + } + } + + OMAPLFBCreateSwapChainUnLock(psDevInfo); + + return IMG_TRUE; +} + +#if defined(CONFIG_DSSCOMP) + +/* Triggered by PVRSRVSwapToDCBuffer2 */ +static IMG_BOOL ProcessFlipV2(IMG_HANDLE hCmdCookie, + OMAPLFB_DEVINFO *psDevInfo, + PDC_MEM_INFO *ppsMemInfos, + IMG_UINT32 ui32NumMemInfos, + struct dsscomp_setup_dispc_data *psDssData, + IMG_UINT32 ui32DssDataLength) +{ + struct tiler_pa_info *apsTilerPAs[5]; + IMG_UINT32 i, k; + + if(ui32DssDataLength != sizeof(*psDssData)) + { + WARN(1, "invalid size of private data (%d vs %d)", + ui32DssDataLength, sizeof(*psDssData)); + return IMG_FALSE; + } + + if(psDssData->num_ovls == 0 || ui32NumMemInfos == 0) + { + WARN(1, "must have at least one layer"); + return IMG_FALSE; + } + + for(i = k = 0; i < ui32NumMemInfos && k < ARRAY_SIZE(apsTilerPAs); i++, k++) + { + struct tiler_pa_info *psTilerInfo; + IMG_CPU_VIRTADDR virtAddr; + IMG_CPU_PHYADDR phyAddr; + IMG_UINT32 ui32NumPages; + IMG_SIZE_T uByteSize; + int j; + + psDevInfo->sPVRJTable.pfnPVRSRVDCMemInfoGetByteSize(ppsMemInfos[i], &uByteSize); + ui32NumPages = (uByteSize + PAGE_SIZE - 1) >> PAGE_SHIFT; + + apsTilerPAs[k] = NULL; + + psDevInfo->sPVRJTable.pfnPVRSRVDCMemInfoGetCpuPAddr(ppsMemInfos[i], 0, &phyAddr); + + /* NV12 buffers are already mapped to tiler */ + if(psDssData->ovls[k].cfg.color_mode == OMAP_DSS_COLOR_NV12) + { + psDssData->ovls[k].ba = (u32)phyAddr.uiAddr; + + psDevInfo->sPVRJTable.pfnPVRSRVDCMemInfoGetCpuPAddr(ppsMemInfos[i], (uByteSize * 2) / 3, &phyAddr); + psDssData->ovls[k].uv = (u32)phyAddr.uiAddr; + continue; + } + + /* Other kinds of buffer may also already be mapped to tiler */ + if(is_tiler_addr((u32)phyAddr.uiAddr)) + { + psDssData->ovls[k].ba = (u32)phyAddr.uiAddr; + continue; + } + + psTilerInfo = kzalloc(sizeof(*psTilerInfo), GFP_KERNEL); + if(!psTilerInfo) + { + continue; + } + + psTilerInfo->mem = kzalloc(sizeof(*psTilerInfo->mem) * ui32NumPages, GFP_KERNEL); + if(!psTilerInfo->mem) + { + kfree(psTilerInfo); + continue; + } + + psTilerInfo->num_pg = ui32NumPages; + psTilerInfo->memtype = TILER_MEM_USING; + + for(j = 0; j < ui32NumPages; j++) + { + psDevInfo->sPVRJTable.pfnPVRSRVDCMemInfoGetCpuPAddr(ppsMemInfos[i], j << PAGE_SHIFT, &phyAddr); + psTilerInfo->mem[j] = (u32)phyAddr.uiAddr; + } + + /* Need base address for in-page offset */ + psDevInfo->sPVRJTable.pfnPVRSRVDCMemInfoGetCpuVAddr(ppsMemInfos[i], &virtAddr); + psDssData->ovls[k].ba = (u32)virtAddr; + apsTilerPAs[k] = psTilerInfo; + } + + /* Set up cloned layer addresses (but don't duplicate tiler_pas) */ + for(i = k; i < psDssData->num_ovls && i < ARRAY_SIZE(apsTilerPAs); i++) + { + unsigned int ix = psDssData->ovls[i].ba; + if(ix >= ARRAY_SIZE(apsTilerPAs)) + { + WARN(1, "Invalid clone layer (%u); skipping all cloned layers", ix); + psDssData->num_ovls = k; + break; + } + apsTilerPAs[i] = apsTilerPAs[ix]; + psDssData->ovls[i].ba = psDssData->ovls[ix].ba; + psDssData->ovls[i].uv = psDssData->ovls[ix].uv; + } + + dsscomp_gralloc_queue(psDssData, apsTilerPAs, false, + (void *)psDevInfo->sPVRJTable.pfnPVRSRVCmdComplete, + (void *)hCmdCookie); + + for(i = 0; i < k; i++) + { + tiler_pa_free(apsTilerPAs[i]); + } + + return IMG_TRUE; +} + +#endif /* defined(CONFIG_DSSCOMP) */ + +/* Command processing flip handler function. Called from services. */ +static IMG_BOOL ProcessFlip(IMG_HANDLE hCmdCookie, + IMG_UINT32 ui32DataSize, + IMG_VOID *pvData) +{ + DISPLAYCLASS_FLIP_COMMAND *psFlipCmd; + OMAPLFB_DEVINFO *psDevInfo; + + /* Check parameters */ + if(!hCmdCookie || !pvData) + { + return IMG_FALSE; + } + + /* Validate data packet */ + psFlipCmd = (DISPLAYCLASS_FLIP_COMMAND*)pvData; + + if (psFlipCmd == IMG_NULL) + { + return IMG_FALSE; + } + + psDevInfo = (OMAPLFB_DEVINFO*)psFlipCmd->hExtDevice; + + if(psFlipCmd->hExtBuffer) + { + return ProcessFlipV1(hCmdCookie, + psDevInfo, + psFlipCmd->hExtSwapChain, + psFlipCmd->hExtBuffer, + psFlipCmd->ui32SwapInterval); + } + else + { +#if defined(CONFIG_DSSCOMP) + DISPLAYCLASS_FLIP_COMMAND2 *psFlipCmd2; + psFlipCmd2 = (DISPLAYCLASS_FLIP_COMMAND2 *)pvData; + return ProcessFlipV2(hCmdCookie, + psDevInfo, + psFlipCmd2->ppsMemInfos, + psFlipCmd2->ui32NumMemInfos, + psFlipCmd2->pvPrivData, + psFlipCmd2->ui32PrivDataLength); +#else + BUG(); +#endif + } +} + +/*! +****************************************************************************** + + @Function OMAPLFBInitFBDev + + @Description specifies devices in the systems memory map + + @Input psSysData - sys data + + @Return OMAPLFB_ERROR : + +******************************************************************************/ +static OMAPLFB_ERROR OMAPLFBInitFBDev(OMAPLFB_DEVINFO *psDevInfo) +{ + struct fb_info *psLINFBInfo; + struct module *psLINFBOwner; + OMAPLFB_FBINFO *psPVRFBInfo = &psDevInfo->sFBInfo; + OMAPLFB_ERROR eError = OMAPLFB_ERROR_GENERIC; + unsigned long FBSize; + unsigned long ulLCM; + unsigned uiFBDevID = psDevInfo->uiFBDevID; + + OMAPLFB_CONSOLE_LOCK(); + + psLINFBInfo = registered_fb[uiFBDevID]; + if (psLINFBInfo == NULL) + { + eError = OMAPLFB_ERROR_INVALID_DEVICE; + goto ErrorRelSem; + } + + FBSize = (psLINFBInfo->screen_size) != 0 ? + psLINFBInfo->screen_size : + psLINFBInfo->fix.smem_len; + + /* + * Try and filter out invalid FB info structures (a problem + * seen on some OMAP3 systems). + */ + if (FBSize == 0 || psLINFBInfo->fix.line_length == 0) + { + eError = OMAPLFB_ERROR_INVALID_DEVICE; + goto ErrorRelSem; + } + + psLINFBOwner = psLINFBInfo->fbops->owner; + if (!try_module_get(psLINFBOwner)) + { + printk(KERN_INFO DRIVER_PREFIX + ": %s: Device %u: Couldn't get framebuffer module\n", __FUNCTION__, uiFBDevID); + + goto ErrorRelSem; + } + + if (psLINFBInfo->fbops->fb_open != NULL) + { + int res; + + res = psLINFBInfo->fbops->fb_open(psLINFBInfo, 0); + if (res != 0) + { + printk(KERN_INFO DRIVER_PREFIX + " %s: Device %u: Couldn't open framebuffer(%d)\n", __FUNCTION__, uiFBDevID, res); + + goto ErrorModPut; + } + } + + psDevInfo->psLINFBInfo = psLINFBInfo; + + ulLCM = LCM(psLINFBInfo->fix.line_length, OMAPLFB_PAGE_SIZE); + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer physical address: 0x%lx\n", + psDevInfo->uiFBDevID, psLINFBInfo->fix.smem_start)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer virtual address: 0x%lx\n", + psDevInfo->uiFBDevID, (unsigned long)psLINFBInfo->screen_base)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer size: %lu\n", + psDevInfo->uiFBDevID, FBSize)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer virtual width: %u\n", + psDevInfo->uiFBDevID, psLINFBInfo->var.xres_virtual)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer virtual height: %u\n", + psDevInfo->uiFBDevID, psLINFBInfo->var.yres_virtual)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer width: %u\n", + psDevInfo->uiFBDevID, psLINFBInfo->var.xres)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer height: %u\n", + psDevInfo->uiFBDevID, psLINFBInfo->var.yres)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Framebuffer stride: %u\n", + psDevInfo->uiFBDevID, psLINFBInfo->fix.line_length)); + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: LCM of stride and page size: %lu\n", + psDevInfo->uiFBDevID, ulLCM)); + + /* Additional implementation specific information */ + OMAPLFBPrintInfo(psDevInfo); + +#if defined(CONFIG_DSSCOMP) + { + /* for some reason we need at least 3 buffers in the swap chain */ + int n = FBSize / RoundUpToMultiple(psLINFBInfo->fix.line_length * psLINFBInfo->var.yres, ulLCM); + int res; + int i, x, y, w; + ion_phys_addr_t phys; + size_t size; + struct tiler_view_t view; + + struct omap_ion_tiler_alloc_data sAllocData = + { + /* TILER will align width to 128-bytes */ + /* however, SGX must have full page width */ + .w = ALIGN(psLINFBInfo->var.xres, PAGE_SIZE / (psLINFBInfo->var.bits_per_pixel / 8)), + .h = psLINFBInfo->var.yres, + .fmt = psLINFBInfo->var.bits_per_pixel == 16 ? TILER_PIXEL_FMT_16BIT : TILER_PIXEL_FMT_32BIT, + .flags = 0, + }; + + printk(KERN_DEBUG DRIVER_PREFIX + " %s: Device %u: Requesting %d TILER 2D framebuffers\n", + __FUNCTION__, uiFBDevID, n); + + /* INTEGRATION_POINT: limit to MAX 3 FBs to save TILER container space */ + if (n != 3) + n = 3; + + sAllocData.w *= n; + + psPVRFBInfo->uiBytesPerPixel = psLINFBInfo->var.bits_per_pixel >> 3; + psPVRFBInfo->bIs2D = OMAPLFB_TRUE; + + res = omap_ion_tiler_alloc(gpsIONClient, &sAllocData); + psPVRFBInfo->psIONHandle = sAllocData.handle; + if (res < 0) + { + printk(KERN_ERR DRIVER_PREFIX + " %s: Device %u: Could not allocate 2D framebuffer(%d)\n", + __FUNCTION__, uiFBDevID, res); + goto ErrorModPut; + } + + psLINFBInfo->fix.smem_start = ion_phys(gpsIONClient, sAllocData.handle, &phys, &size); + + psPVRFBInfo->sSysAddr.uiAddr = phys; + psPVRFBInfo->sCPUVAddr = 0; + psPVRFBInfo->ulWidth = psLINFBInfo->var.xres; + psPVRFBInfo->ulHeight = psLINFBInfo->var.yres; + + psPVRFBInfo->ulByteStride = PAGE_ALIGN(psPVRFBInfo->ulWidth * psPVRFBInfo->uiBytesPerPixel); + w = psPVRFBInfo->ulByteStride >> PAGE_SHIFT; + + /* this is an "effective" FB size to get correct number of buffers */ + psPVRFBInfo->ulFBSize = sAllocData.h * n * psPVRFBInfo->ulByteStride; + psPVRFBInfo->psPageList = kzalloc(w * n * psPVRFBInfo->ulHeight * sizeof(*psPVRFBInfo->psPageList), GFP_KERNEL); + if (!psPVRFBInfo->psPageList) + { + printk(KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Could not allocate page list\n", __FUNCTION__, psDevInfo->uiFBDevID); + ion_free(gpsIONClient, sAllocData.handle); + goto ErrorModPut; + } + + tilview_create(&view, phys, psDevInfo->sFBInfo.ulWidth, psDevInfo->sFBInfo.ulHeight); + for(i = 0; i < n; i++) + { + for(y = 0; y < psDevInfo->sFBInfo.ulHeight; y++) + { + for(x = 0; x < w; x++) + { + psPVRFBInfo->psPageList[i * psDevInfo->sFBInfo.ulHeight * w + y * w + x].uiAddr = + phys + view.v_inc * y + ((x + i * w) << PAGE_SHIFT); + } + } + } + } +#else /* defined(CONFIG_DSSCOMP) */ + /* System Surface */ + psPVRFBInfo->sSysAddr.uiAddr = psLINFBInfo->fix.smem_start; + psPVRFBInfo->sCPUVAddr = psLINFBInfo->screen_base; + + psPVRFBInfo->ulWidth = psLINFBInfo->var.xres; + psPVRFBInfo->ulHeight = psLINFBInfo->var.yres; + psPVRFBInfo->ulByteStride = psLINFBInfo->fix.line_length; + psPVRFBInfo->ulFBSize = FBSize; +#endif /* defined(CONFIG_DSSCOMP) */ + + psPVRFBInfo->ulBufferSize = psPVRFBInfo->ulHeight * psPVRFBInfo->ulByteStride; + + /* Round the buffer size up to a multiple of the number of pages + * and the byte stride. + * This is used internally, to ensure buffers start on page + * boundaries, for the benefit of PVR Services. + */ + psPVRFBInfo->ulRoundedBufferSize = RoundUpToMultiple(psPVRFBInfo->ulBufferSize, ulLCM); + + if(psLINFBInfo->var.bits_per_pixel == 16) + { + if((psLINFBInfo->var.red.length == 5) && + (psLINFBInfo->var.green.length == 6) && + (psLINFBInfo->var.blue.length == 5) && + (psLINFBInfo->var.red.offset == 11) && + (psLINFBInfo->var.green.offset == 5) && + (psLINFBInfo->var.blue.offset == 0) && + (psLINFBInfo->var.red.msb_right == 0)) + { + psPVRFBInfo->ePixelFormat = PVRSRV_PIXEL_FORMAT_RGB565; + } + else + { + printk(KERN_INFO DRIVER_PREFIX ": %s: Device %u: Unknown FB format\n", __FUNCTION__, uiFBDevID); + } + } + else if(psLINFBInfo->var.bits_per_pixel == 32) + { + if((psLINFBInfo->var.red.length == 8) && + (psLINFBInfo->var.green.length == 8) && + (psLINFBInfo->var.blue.length == 8) && + (psLINFBInfo->var.red.offset == 16) && + (psLINFBInfo->var.green.offset == 8) && + (psLINFBInfo->var.blue.offset == 0) && + (psLINFBInfo->var.red.msb_right == 0)) + { + psPVRFBInfo->ePixelFormat = PVRSRV_PIXEL_FORMAT_ARGB8888; + } + else + { + printk(KERN_INFO DRIVER_PREFIX ": %s: Device %u: Unknown FB format\n", __FUNCTION__, uiFBDevID); + } + } + else + { + printk(KERN_INFO DRIVER_PREFIX ": %s: Device %u: Unknown FB format\n", __FUNCTION__, uiFBDevID); + } + + psDevInfo->sFBInfo.ulPhysicalWidthmm = + ((int)psLINFBInfo->var.width > 0) ? psLINFBInfo->var.width : 90; + + psDevInfo->sFBInfo.ulPhysicalHeightmm = + ((int)psLINFBInfo->var.height > 0) ? psLINFBInfo->var.height : 54; + + /* System Surface */ + psDevInfo->sFBInfo.sSysAddr.uiAddr = psPVRFBInfo->sSysAddr.uiAddr; + psDevInfo->sFBInfo.sCPUVAddr = psPVRFBInfo->sCPUVAddr; + + eError = OMAPLFB_OK; + goto ErrorRelSem; + +ErrorModPut: + module_put(psLINFBOwner); +ErrorRelSem: + OMAPLFB_CONSOLE_UNLOCK(); + + return eError; +} + +static void OMAPLFBDeInitFBDev(OMAPLFB_DEVINFO *psDevInfo) +{ + struct fb_info *psLINFBInfo = psDevInfo->psLINFBInfo; + struct module *psLINFBOwner; + + OMAPLFB_CONSOLE_LOCK(); + +#if defined(CONFIG_DSSCOMP) + { + OMAPLFB_FBINFO *psPVRFBInfo = &psDevInfo->sFBInfo; + kfree(psPVRFBInfo->psPageList); + if (psPVRFBInfo->psIONHandle) + { + ion_free(gpsIONClient, psPVRFBInfo->psIONHandle); + } + } +#endif /* defined(CONFIG_DSSCOMP) */ + + psLINFBOwner = psLINFBInfo->fbops->owner; + + if (psLINFBInfo->fbops->fb_release != NULL) + { + (void) psLINFBInfo->fbops->fb_release(psLINFBInfo, 0); + } + + module_put(psLINFBOwner); + + OMAPLFB_CONSOLE_UNLOCK(); +} + +static OMAPLFB_DEVINFO *OMAPLFBInitDev(unsigned uiFBDevID) +{ + PFN_CMD_PROC pfnCmdProcList[OMAPLFB_COMMAND_COUNT]; + IMG_UINT32 aui32SyncCountList[OMAPLFB_COMMAND_COUNT][2]; + OMAPLFB_DEVINFO *psDevInfo = NULL; + + /* Allocate device info. structure */ + psDevInfo = (OMAPLFB_DEVINFO *)OMAPLFBAllocKernelMem(sizeof(OMAPLFB_DEVINFO)); + + if(psDevInfo == NULL) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: Couldn't allocate device information structure\n", __FUNCTION__, uiFBDevID); + + goto ErrorExit; + } + + /* Any fields not set will be zero */ + memset(psDevInfo, 0, sizeof(OMAPLFB_DEVINFO)); + + psDevInfo->uiFBDevID = uiFBDevID; + + /* Get the kernel services function table */ + if(!(*gpfnGetPVRJTable)(&psDevInfo->sPVRJTable)) + { + goto ErrorFreeDevInfo; + } + + /* Save private fbdev information structure in the dev. info. */ + if(OMAPLFBInitFBDev(psDevInfo) != OMAPLFB_OK) + { + /* + * Leave it to OMAPLFBInitFBDev to print an error message, if + * required. The function may have failed because + * there is no Linux framebuffer device corresponding + * to the device ID. + */ + goto ErrorFreeDevInfo; + } + + psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers = (IMG_UINT32)(psDevInfo->sFBInfo.ulFBSize / psDevInfo->sFBInfo.ulRoundedBufferSize); + if (psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers != 0) + { + psDevInfo->sDisplayInfo.ui32MaxSwapChains = 1; + psDevInfo->sDisplayInfo.ui32MaxSwapInterval = 1; + } + + psDevInfo->sDisplayInfo.ui32PhysicalWidthmm = psDevInfo->sFBInfo.ulPhysicalWidthmm; + psDevInfo->sDisplayInfo.ui32PhysicalHeightmm = psDevInfo->sFBInfo.ulPhysicalHeightmm; + + strncpy(psDevInfo->sDisplayInfo.szDisplayName, DISPLAY_DEVICE_NAME, MAX_DISPLAY_NAME_SIZE); + + psDevInfo->sDisplayFormat.pixelformat = psDevInfo->sFBInfo.ePixelFormat; + psDevInfo->sDisplayDim.ui32Width = (IMG_UINT32)psDevInfo->sFBInfo.ulWidth; + psDevInfo->sDisplayDim.ui32Height = (IMG_UINT32)psDevInfo->sFBInfo.ulHeight; + psDevInfo->sDisplayDim.ui32ByteStride = (IMG_UINT32)psDevInfo->sFBInfo.ulByteStride; + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: Maximum number of swap chain buffers: %u\n", + psDevInfo->uiFBDevID, psDevInfo->sDisplayInfo.ui32MaxSwapChainBuffers)); + + /* Setup system buffer */ + psDevInfo->sSystemBuffer.sSysAddr = psDevInfo->sFBInfo.sSysAddr; + psDevInfo->sSystemBuffer.sCPUVAddr = psDevInfo->sFBInfo.sCPUVAddr; + psDevInfo->sSystemBuffer.psDevInfo = psDevInfo; + + OMAPLFBInitBufferForSwap(&psDevInfo->sSystemBuffer); + + /* + Setup the DC Jtable so SRVKM can call into this driver + */ + 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.pfnSetDCState = SetDCState; + + /* Register device with services and retrieve device index */ + if(psDevInfo->sPVRJTable.pfnPVRSRVRegisterDCDevice( + &psDevInfo->sDCJTable, + &psDevInfo->uiPVRDevID) != PVRSRV_OK) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: PVR Services device registration failed\n", __FUNCTION__, uiFBDevID); + + goto ErrorDeInitFBDev; + } + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX + ": Device %u: PVR Device ID: %u\n", + psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID)); + + /* Setup private command processing function table ... */ + pfnCmdProcList[DC_FLIP_COMMAND] = ProcessFlip; + + /* ... and associated sync count(s) */ + aui32SyncCountList[DC_FLIP_COMMAND][0] = 0; /* writes */ + aui32SyncCountList[DC_FLIP_COMMAND][1] = 10; /* reads */ + + /* + Register private command processing functions with + the Command Queue Manager and setup the general + command complete function in the devinfo. + */ + if (psDevInfo->sPVRJTable.pfnPVRSRVRegisterCmdProcList(psDevInfo->uiPVRDevID, + &pfnCmdProcList[0], + aui32SyncCountList, + OMAPLFB_COMMAND_COUNT) != PVRSRV_OK) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: Couldn't register command processing functions with PVR Services\n", __FUNCTION__, uiFBDevID); + goto ErrorUnregisterDevice; + } + + OMAPLFBCreateSwapChainLockInit(psDevInfo); + + OMAPLFBAtomicBoolInit(&psDevInfo->sBlanked, OMAPLFB_FALSE); + OMAPLFBAtomicIntInit(&psDevInfo->sBlankEvents, 0); + OMAPLFBAtomicBoolInit(&psDevInfo->sFlushCommands, OMAPLFB_FALSE); +#if defined(CONFIG_HAS_EARLYSUSPEND) + OMAPLFBAtomicBoolInit(&psDevInfo->sEarlySuspendFlag, OMAPLFB_FALSE); +#endif +#if defined(SUPPORT_DRI_DRM) + OMAPLFBAtomicBoolInit(&psDevInfo->sLeaveVT, OMAPLFB_FALSE); +#endif + return psDevInfo; + +ErrorUnregisterDevice: + (void)psDevInfo->sPVRJTable.pfnPVRSRVRemoveDCDevice(psDevInfo->uiPVRDevID); +ErrorDeInitFBDev: + OMAPLFBDeInitFBDev(psDevInfo); +ErrorFreeDevInfo: + OMAPLFBFreeKernelMem(psDevInfo); +ErrorExit: + return NULL; +} + +OMAPLFB_ERROR OMAPLFBInit(void) +{ + unsigned uiMaxFBDevIDPlusOne = OMAPLFBMaxFBDevIDPlusOne(); + unsigned i; + unsigned uiDevicesFound = 0; + + if(OMAPLFBGetLibFuncAddr ("PVRGetDisplayClassJTable", &gpfnGetPVRJTable) != OMAPLFB_OK) + { + return OMAPLFB_ERROR_INIT_FAILURE; + } + + /* + * We search for frame buffer devices backwards, as the last device + * registered with PVR Services will be the first device enumerated + * by PVR Services. + */ + for(i = uiMaxFBDevIDPlusOne; i-- != 0;) + { + OMAPLFB_DEVINFO *psDevInfo = OMAPLFBInitDev(i); + + if (psDevInfo != NULL) + { + /* Set the top-level anchor */ + OMAPLFBSetDevInfoPtr(psDevInfo->uiFBDevID, psDevInfo); + uiDevicesFound++; + } + } + + return (uiDevicesFound != 0) ? OMAPLFB_OK : OMAPLFB_ERROR_INIT_FAILURE; +} + +/* + * OMAPLFBDeInitDev + * DeInitialises one device + */ +static OMAPLFB_BOOL OMAPLFBDeInitDev(OMAPLFB_DEVINFO *psDevInfo) +{ + PVRSRV_DC_DISP2SRV_KMJTABLE *psPVRJTable = &psDevInfo->sPVRJTable; + + OMAPLFBCreateSwapChainLockDeInit(psDevInfo); + + OMAPLFBAtomicBoolDeInit(&psDevInfo->sBlanked); + OMAPLFBAtomicIntDeInit(&psDevInfo->sBlankEvents); + OMAPLFBAtomicBoolDeInit(&psDevInfo->sFlushCommands); +#if defined(CONFIG_HAS_EARLYSUSPEND) + OMAPLFBAtomicBoolDeInit(&psDevInfo->sEarlySuspendFlag); +#endif +#if defined(SUPPORT_DRI_DRM) + OMAPLFBAtomicBoolDeInit(&psDevInfo->sLeaveVT); +#endif + psPVRJTable = &psDevInfo->sPVRJTable; + + if (psPVRJTable->pfnPVRSRVRemoveCmdProcList (psDevInfo->uiPVRDevID, OMAPLFB_COMMAND_COUNT) != PVRSRV_OK) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: PVR Device %u: Couldn't unregister command processing functions\n", __FUNCTION__, psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID); + return OMAPLFB_FALSE; + } + + /* + * Remove display class device from kernel services device + * register. + */ + if (psPVRJTable->pfnPVRSRVRemoveDCDevice(psDevInfo->uiPVRDevID) != PVRSRV_OK) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: PVR Device %u: Couldn't remove device from PVR Services\n", __FUNCTION__, psDevInfo->uiFBDevID, psDevInfo->uiPVRDevID); + return OMAPLFB_FALSE; + } + + OMAPLFBDeInitFBDev(psDevInfo); + + OMAPLFBSetDevInfoPtr(psDevInfo->uiFBDevID, NULL); + + /* De-allocate data structure */ + OMAPLFBFreeKernelMem(psDevInfo); + + return OMAPLFB_TRUE; +} + +/* + * OMAPLFBDeInit + * Deinitialises the display class device component of the FBDev + */ +OMAPLFB_ERROR OMAPLFBDeInit(void) +{ + unsigned uiMaxFBDevIDPlusOne = OMAPLFBMaxFBDevIDPlusOne(); + unsigned i; + OMAPLFB_BOOL bError = OMAPLFB_FALSE; + + for(i = 0; i < uiMaxFBDevIDPlusOne; i++) + { + OMAPLFB_DEVINFO *psDevInfo = OMAPLFBGetDevInfoPtr(i); + + if (psDevInfo != NULL) + { + bError |= !OMAPLFBDeInitDev(psDevInfo); + } + } + + return (bError) ? OMAPLFB_ERROR_INIT_FAILURE : OMAPLFB_OK; +} + +/****************************************************************************** + End of file (omaplfb_displayclass.c) +******************************************************************************/ + diff --git a/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb_linux.c b/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb_linux.c new file mode 100644 index 0000000..82c7fce --- /dev/null +++ b/pvr-source/services4/3rdparty/dc_omapfb3_linux/omaplfb_linux.c @@ -0,0 +1,1165 @@ +/*************************************************************************/ /*! +@Title OMAP linux display driver components +@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved +@License Dual MIT/GPLv2 + +The contents of this file are subject to the MIT license as set out below. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Alternatively, the contents of this file may be used under the terms of +the GNU General Public License Version 2 ("GPL") in which case the provisions +of GPL are applicable instead of those above. + +If you wish to allow use of your version of this file only under the terms of +GPL, and not to allow others to use your version of this file under the terms +of the MIT license, indicate your decision by deleting the provisions above +and replace them with the notice and other provisions required by GPL as set +out in the file called "GPL-COPYING" included in this distribution. If you do +not delete the provisions above, a recipient may use your version of this file +under the terms of either the MIT license or GPL. + +This License is also included in this distribution in the file called +"MIT-COPYING". + +EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /**************************************************************************/ + +/************************************************************************** + The 3rd party driver is a specification of an API to integrate the IMG POWERVR + Services driver with 3rd Party display hardware. It is NOT a specification for + a display controller driver, rather a specification to extend the API for a + pre-existing driver for the display hardware. + + The 3rd party driver interface provides IMG POWERVR client drivers (e.g. PVR2D) + with an API abstraction of the system's underlying display hardware, allowing + the client drivers to indirectly control the display hardware and access its + associated memory. + + Functions of the API include + - query primary surface attributes (width, height, stride, pixel format, CPU + physical and virtual address) + - swap/flip chain creation and subsequent query of surface attributes + - asynchronous display surface flipping, taking account of asynchronous read + (flip) and write (render) operations to the display surface + + Note: having queried surface attributes the client drivers are able to map the + display memory to any IMG POWERVR Services device by calling + PVRSRVMapDeviceClassMemory with the display surface handle. + + This code is intended to be an example of how a pre-existing display driver may + be extended to support the 3rd Party Display interface to POWERVR Services + - IMG is not providing a display driver implementation. + **************************************************************************/ + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#endif + +#include <asm/atomic.h> + +#if defined(SUPPORT_DRI_DRM) +#include <drm/drmP.h> +#else +#include <linux/module.h> +#endif + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/hardirq.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/fb.h> +#include <linux/console.h> +#include <linux/omapfb.h> +#include <linux/mutex.h> + +#if defined(PVR_OMAPLFB_DRM_FB) +#include <plat/display.h> +#include <linux/omap_gpu.h> +#else /* defined(PVR_OMAPLFB_DRM_FB) */ +/* OmapZoom.org OMAP3 2.6.29 kernel tree - Needs mach/vrfb.h + * OmapZoom.org OMAP3 2.6.32 kernel tree - No additional header required + * OmapZoom.org OMAP4 2.6.33 kernel tree - No additional header required + * OmapZoom.org OMAP4 2.6.34 kernel tree - Needs plat/vrfb.h + * Sholes 2.6.32 kernel tree - Needs plat/vrfb.h + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) +#define PVR_OMAPFB3_NEEDS_PLAT_VRFB_H +#endif + +#if defined(PVR_OMAPFB3_NEEDS_PLAT_VRFB_H) +#include <plat/vrfb.h> +#else +#if defined(PVR_OMAPFB3_NEEDS_MACH_VRFB_H) +#include <mach/vrfb.h> +#endif +#endif + +#if defined(DEBUG) +#define PVR_DEBUG DEBUG +#undef DEBUG +#endif +#include <omapfb/omapfb.h> +#if defined(DEBUG) +#undef DEBUG +#endif +#if defined(PVR_DEBUG) +#define DEBUG PVR_DEBUG +#undef PVR_DEBUG +#endif +#endif /* defined(PVR_OMAPLFB_DRM_FB) */ + +#if defined(CONFIG_DSSCOMP) +#include <mach/tiler.h> +#include <video/dsscomp.h> +#include <plat/dsscomp.h> +#endif /* defined(CONFIG_DSSCOMP) */ + +#include "img_defs.h" +#include "servicesext.h" +#include "kerneldisplay.h" +#include "omaplfb.h" +#include "pvrmodule.h" +#if defined(SUPPORT_DRI_DRM) +#include "pvr_drm.h" +#include "3rdparty_dc_drm_shared.h" +#endif + +#if !defined(PVR_LINUX_USING_WORKQUEUES) +#error "PVR_LINUX_USING_WORKQUEUES must be defined" +#endif + +MODULE_SUPPORTED_DEVICE(DEVNAME); + +#if !defined(PVR_OMAPLFB_DRM_FB) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) +#define OMAP_DSS_DRIVER(drv, dev) struct omap_dss_driver *drv = (dev) != NULL ? (dev)->driver : NULL +#define OMAP_DSS_MANAGER(man, dev) struct omap_overlay_manager *man = (dev) != NULL ? (dev)->manager : NULL +#define WAIT_FOR_VSYNC(man) ((man)->wait_for_vsync) +#else +#define OMAP_DSS_DRIVER(drv, dev) struct omap_dss_device *drv = (dev) +#define OMAP_DSS_MANAGER(man, dev) struct omap_dss_device *man = (dev) +#define WAIT_FOR_VSYNC(man) ((man)->wait_vsync) +#endif +#endif /* !defined(PVR_OMAPLFB_DRM_FB) */ + +void *OMAPLFBAllocKernelMem(unsigned long ulSize) +{ + return kmalloc(ulSize, GFP_KERNEL); +} + +void OMAPLFBFreeKernelMem(void *pvMem) +{ + kfree(pvMem); +} + +void OMAPLFBCreateSwapChainLockInit(OMAPLFB_DEVINFO *psDevInfo) +{ + mutex_init(&psDevInfo->sCreateSwapChainMutex); +} + +void OMAPLFBCreateSwapChainLockDeInit(OMAPLFB_DEVINFO *psDevInfo) +{ + mutex_destroy(&psDevInfo->sCreateSwapChainMutex); +} + +void OMAPLFBCreateSwapChainLock(OMAPLFB_DEVINFO *psDevInfo) +{ + mutex_lock(&psDevInfo->sCreateSwapChainMutex); +} + +void OMAPLFBCreateSwapChainUnLock(OMAPLFB_DEVINFO *psDevInfo) +{ + mutex_unlock(&psDevInfo->sCreateSwapChainMutex); +} + +void OMAPLFBAtomicBoolInit(OMAPLFB_ATOMIC_BOOL *psAtomic, OMAPLFB_BOOL bVal) +{ + atomic_set(psAtomic, (int)bVal); +} + +void OMAPLFBAtomicBoolDeInit(OMAPLFB_ATOMIC_BOOL *psAtomic) +{ +} + +void OMAPLFBAtomicBoolSet(OMAPLFB_ATOMIC_BOOL *psAtomic, OMAPLFB_BOOL bVal) +{ + atomic_set(psAtomic, (int)bVal); +} + +OMAPLFB_BOOL OMAPLFBAtomicBoolRead(OMAPLFB_ATOMIC_BOOL *psAtomic) +{ + return (OMAPLFB_BOOL)atomic_read(psAtomic); +} + +void OMAPLFBAtomicIntInit(OMAPLFB_ATOMIC_INT *psAtomic, int iVal) +{ + atomic_set(psAtomic, iVal); +} + +void OMAPLFBAtomicIntDeInit(OMAPLFB_ATOMIC_INT *psAtomic) +{ +} + +void OMAPLFBAtomicIntSet(OMAPLFB_ATOMIC_INT *psAtomic, int iVal) +{ + atomic_set(psAtomic, iVal); +} + +int OMAPLFBAtomicIntRead(OMAPLFB_ATOMIC_INT *psAtomic) +{ + return atomic_read(psAtomic); +} + +void OMAPLFBAtomicIntInc(OMAPLFB_ATOMIC_INT *psAtomic) +{ + atomic_inc(psAtomic); +} + +OMAPLFB_ERROR OMAPLFBGetLibFuncAddr (char *szFunctionName, PFN_DC_GET_PVRJTABLE *ppfnFuncTable) +{ + if(strcmp("PVRGetDisplayClassJTable", szFunctionName) != 0) + { + return (OMAPLFB_ERROR_INVALID_PARAMS); + } + + /* Nothing to do - should be exported from pvrsrv.ko */ + *ppfnFuncTable = PVRGetDisplayClassJTable; + + return (OMAPLFB_OK); +} + +/* Inset a swap buffer into the swap chain work queue */ +void OMAPLFBQueueBufferForSwap(OMAPLFB_SWAPCHAIN *psSwapChain, OMAPLFB_BUFFER *psBuffer) +{ + int res = queue_work(psSwapChain->psWorkQueue, &psBuffer->sWork); + + if (res == 0) + { + printk(KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Buffer already on work queue\n", __FUNCTION__, psSwapChain->uiFBDevID); + } +} + +/* Process an item on a swap chain work queue */ +static void WorkQueueHandler(struct work_struct *psWork) +{ + OMAPLFB_BUFFER *psBuffer = container_of(psWork, OMAPLFB_BUFFER, sWork); + + OMAPLFBSwapHandler(psBuffer); +} + +/* Create a swap chain work queue */ +OMAPLFB_ERROR OMAPLFBCreateSwapQueue(OMAPLFB_SWAPCHAIN *psSwapChain) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,37)) +#define WQ_FREEZABLE WQ_FREEZEABLE +#endif + /* + * Calling alloc_ordered_workqueue with the WQ_FREEZABLE and + * WQ_MEM_RECLAIM flags set, (currently) has the same effect as + * calling create_freezable_workqueue. None of the other WQ + * flags are valid. Setting WQ_MEM_RECLAIM should allow the + * workqueue to continue to service the swap chain in low memory + * conditions, preventing the driver from holding on to + * resources longer than it needs to. + */ + psSwapChain->psWorkQueue = alloc_ordered_workqueue(DEVNAME, WQ_FREEZABLE | WQ_MEM_RECLAIM); +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)) + psSwapChain->psWorkQueue = create_freezable_workqueue(DEVNAME); +#else + /* + * Create a single-threaded, freezable, rt-prio workqueue. + * Such workqueues are frozen with user threads when a system + * suspends, before driver suspend entry points are called. + * This ensures this driver will not call into the Linux + * framebuffer driver after the latter is suspended. + */ + psSwapChain->psWorkQueue = __create_workqueue(DEVNAME, 1, 1, 1); +#endif +#endif + if (psSwapChain->psWorkQueue == NULL) + { + printk(KERN_ERR DRIVER_PREFIX ": %s: Device %u: Couldn't create workqueue\n", __FUNCTION__, psSwapChain->uiFBDevID); + + return (OMAPLFB_ERROR_INIT_FAILURE); + } + + return (OMAPLFB_OK); +} + +/* Prepare buffer for insertion into a swap chain work queue */ +void OMAPLFBInitBufferForSwap(OMAPLFB_BUFFER *psBuffer) +{ + INIT_WORK(&psBuffer->sWork, WorkQueueHandler); +} + +/* Destroy a swap chain work queue */ +void OMAPLFBDestroySwapQueue(OMAPLFB_SWAPCHAIN *psSwapChain) +{ + destroy_workqueue(psSwapChain->psWorkQueue); +} + +/* Flip display to given buffer */ +void OMAPLFBFlip(OMAPLFB_DEVINFO *psDevInfo, OMAPLFB_BUFFER *psBuffer) +{ + struct fb_var_screeninfo sFBVar; + int res; + unsigned long ulYResVirtual; + + OMAPLFB_CONSOLE_LOCK(); + + sFBVar = psDevInfo->psLINFBInfo->var; + + sFBVar.xoffset = 0; + sFBVar.yoffset = psBuffer->ulYOffset; + + ulYResVirtual = psBuffer->ulYOffset + sFBVar.yres; + +#if defined(CONFIG_DSSCOMP) + { + /* + * If using DSSCOMP, we need to use dsscomp queuing for normal + * framebuffer updates, so that previously used overlays get + * automatically disabled, and manager gets dirtied. We can + * do that because DSSCOMP takes ownership of all pipelines on + * a manager. + */ + struct fb_fix_screeninfo sFBFix = psDevInfo->psLINFBInfo->fix; + struct dsscomp_setup_dispc_data d = + { + .num_ovls = 1, + .num_mgrs = 1, + .mgrs[0].alpha_blending = 1, + .ovls[0] = + { + .cfg = + { + .win.w = sFBVar.xres, + .win.h = sFBVar.yres, + .crop.x = sFBVar.xoffset, + .crop.y = sFBVar.yoffset, + .crop.w = sFBVar.xres, + .crop.h = sFBVar.yres, + .width = sFBVar.xres_virtual, + .height = sFBVar.yres_virtual, + .stride = sFBFix.line_length, + .enabled = 1, + .global_alpha = 255, + }, + }, + }; + + /* do not map buffer into TILER1D as it is contiguous */ + struct tiler_pa_info *pas[] = { NULL }; + + d.ovls[0].ba = sFBFix.smem_start; + omapfb_mode_to_dss_mode(&sFBVar, &d.ovls[0].cfg.color_mode); + + res = dsscomp_gralloc_queue(&d, pas, true, NULL, NULL); + } +#else /* defined(CONFIG_DSSCOMP) */ + /* + * PVR_OMAPLFB_DONT_USE_FB_PAN_DISPLAY should be defined to work + * around flipping problems seen with the Taal LCDs on Blaze. + * The work around is safe to use with other types of screen on Blaze + * (e.g. HDMI) and on other platforms (e.g. Panda board). + */ +#if !defined(PVR_OMAPLFB_DONT_USE_FB_PAN_DISPLAY) + /* + * Attempt to change the virtual screen resolution if it is too + * small. Note that fb_set_var also pans the display. + */ + if (sFBVar.xres_virtual != sFBVar.xres || sFBVar.yres_virtual < ulYResVirtual) +#endif /* !defined(PVR_OMAPLFB_DONT_USE_FB_PAN_DISPLAY) */ + { + sFBVar.xres_virtual = sFBVar.xres; + sFBVar.yres_virtual = ulYResVirtual; + + sFBVar.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; + + res = fb_set_var(psDevInfo->psLINFBInfo, &sFBVar); + if (res != 0) + { + printk(KERN_ERR DRIVER_PREFIX ": %s: Device %u: fb_set_var failed (Y Offset: %lu, Error: %d)\n", __FUNCTION__, psDevInfo->uiFBDevID, psBuffer->ulYOffset, res); + } + } +#if !defined(PVR_OMAPLFB_DONT_USE_FB_PAN_DISPLAY) + else + { + res = fb_pan_display(psDevInfo->psLINFBInfo, &sFBVar); + if (res != 0) + { + printk(KERN_ERR DRIVER_PREFIX ": %s: Device %u: fb_pan_display failed (Y Offset: %lu, Error: %d)\n", __FUNCTION__, psDevInfo->uiFBDevID, psBuffer->ulYOffset, res); + } + } +#endif /* !defined(PVR_OMAPLFB_DONT_USE_FB_PAN_DISPLAY) */ +#endif /* defined(CONFIG_DSSCOMP) */ + + OMAPLFB_CONSOLE_UNLOCK(); +} + +#if !defined(PVR_OMAPLFB_DRM_FB) || defined(DEBUG) +static OMAPLFB_BOOL OMAPLFBValidateDSSUpdateMode(enum omap_dss_update_mode eMode) +{ + switch (eMode) + { + case OMAP_DSS_UPDATE_AUTO: + case OMAP_DSS_UPDATE_MANUAL: + case OMAP_DSS_UPDATE_DISABLED: + return OMAPLFB_TRUE; + default: + break; + } + + return OMAPLFB_FALSE; +} + +static OMAPLFB_UPDATE_MODE OMAPLFBFromDSSUpdateMode(enum omap_dss_update_mode eMode) +{ + switch (eMode) + { + case OMAP_DSS_UPDATE_AUTO: + return OMAPLFB_UPDATE_MODE_AUTO; + case OMAP_DSS_UPDATE_MANUAL: + return OMAPLFB_UPDATE_MODE_MANUAL; + case OMAP_DSS_UPDATE_DISABLED: + return OMAPLFB_UPDATE_MODE_DISABLED; + default: + break; + } + + return OMAPLFB_UPDATE_MODE_UNDEFINED; +} +#endif + +static OMAPLFB_BOOL OMAPLFBValidateUpdateMode(OMAPLFB_UPDATE_MODE eMode) +{ + switch(eMode) + { + case OMAPLFB_UPDATE_MODE_AUTO: + case OMAPLFB_UPDATE_MODE_MANUAL: + case OMAPLFB_UPDATE_MODE_DISABLED: + return OMAPLFB_TRUE; + default: + break; + } + + return OMAPLFB_FALSE; +} + +static enum omap_dss_update_mode OMAPLFBToDSSUpdateMode(OMAPLFB_UPDATE_MODE eMode) +{ + switch(eMode) + { + case OMAPLFB_UPDATE_MODE_AUTO: + return OMAP_DSS_UPDATE_AUTO; + case OMAPLFB_UPDATE_MODE_MANUAL: + return OMAP_DSS_UPDATE_MANUAL; + case OMAPLFB_UPDATE_MODE_DISABLED: + return OMAP_DSS_UPDATE_DISABLED; + default: + break; + } + + return -1; +} + +#if defined(DEBUG) +static const char *OMAPLFBUpdateModeToString(OMAPLFB_UPDATE_MODE eMode) +{ + switch(eMode) + { + case OMAPLFB_UPDATE_MODE_AUTO: + return "Auto Update Mode"; + case OMAPLFB_UPDATE_MODE_MANUAL: + return "Manual Update Mode"; + case OMAPLFB_UPDATE_MODE_DISABLED: + return "Update Mode Disabled"; + case OMAPLFB_UPDATE_MODE_UNDEFINED: + return "Update Mode Undefined"; + default: + break; + } + + return "Unknown Update Mode"; +} + +static const char *OMAPLFBDSSUpdateModeToString(enum omap_dss_update_mode eMode) +{ + if (!OMAPLFBValidateDSSUpdateMode(eMode)) + { + return "Unknown Update Mode"; + } + + return OMAPLFBUpdateModeToString(OMAPLFBFromDSSUpdateMode(eMode)); +} + +void OMAPLFBPrintInfo(OMAPLFB_DEVINFO *psDevInfo) +{ +#if defined(PVR_OMAPLFB_DRM_FB) + struct drm_connector *psConnector; + unsigned uConnectors; + unsigned uConnector; + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": Device %u: DRM framebuffer\n", psDevInfo->uiFBDevID)); + + for (psConnector = NULL, uConnectors = 0; + (psConnector = omap_fbdev_get_next_connector(psDevInfo->psLINFBInfo, psConnector)) != NULL;) + { + uConnectors++; + } + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": Device %u: Number of screens (DRM connectors): %u\n", psDevInfo->uiFBDevID, uConnectors)); + + if (uConnectors == 0) + { + return; + } + + for (psConnector = NULL, uConnector = 0; + (psConnector = omap_fbdev_get_next_connector(psDevInfo->psLINFBInfo, psConnector)) != NULL; uConnector++) + { + enum omap_dss_update_mode eMode = omap_connector_get_update_mode(psConnector); + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": Device %u: Screen %u: %s (%d)\n", psDevInfo->uiFBDevID, uConnector, OMAPLFBDSSUpdateModeToString(eMode), (int)eMode)); + + } +#else /* defined(PVR_OMAPLFB_DRM_FB) */ + OMAPLFB_UPDATE_MODE eMode = OMAPLFBGetUpdateMode(psDevInfo); + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": Device %u: non-DRM framebuffer\n", psDevInfo->uiFBDevID)); + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": Device %u: %s\n", psDevInfo->uiFBDevID, OMAPLFBUpdateModeToString(eMode))); +#endif /* defined(PVR_OMAPLFB_DRM_FB) */ +} +#endif /* defined(DEBUG) */ + +/* + * Get display update mode. + * If the mode is AUTO, we can wait for VSync, if desired. + */ +OMAPLFB_UPDATE_MODE OMAPLFBGetUpdateMode(OMAPLFB_DEVINFO *psDevInfo) +{ +#if defined(PVR_OMAPLFB_DRM_FB) + struct drm_connector *psConnector; + OMAPLFB_UPDATE_MODE eMode = OMAPLFB_UPDATE_MODE_UNDEFINED; + + /* + * There may be multiple displays connected. If at least one + * display is manual update mode, report all screens as being + * in that mode. + */ + for (psConnector = NULL; + (psConnector = omap_fbdev_get_next_connector(psDevInfo->psLINFBInfo, psConnector)) != NULL;) + { + switch(omap_connector_get_update_mode(psConnector)) + { + case OMAP_DSS_UPDATE_MANUAL: + eMode = OMAPLFB_UPDATE_MODE_MANUAL; + break; + case OMAP_DSS_UPDATE_DISABLED: + if (eMode == OMAPLFB_UPDATE_MODE_UNDEFINED) + { + eMode = OMAPLFB_UPDATE_MODE_DISABLED; + } + break; + case OMAP_DSS_UPDATE_AUTO: + /* Fall through to default case */ + default: + /* Asssume auto update is possible */ + if (eMode != OMAPLFB_UPDATE_MODE_MANUAL) + { + eMode = OMAPLFB_UPDATE_MODE_AUTO; + } + break; + } + } + + return eMode; +#else /* defined(PVR_OMAPLFB_DRM_FB) */ + struct omap_dss_device *psDSSDev = fb2display(psDevInfo->psLINFBInfo); + OMAP_DSS_DRIVER(psDSSDrv, psDSSDev); + + enum omap_dss_update_mode eMode; + + if (psDSSDrv == NULL) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: No DSS device\n", __FUNCTION__, psDevInfo->uiFBDevID)); + return OMAPLFB_UPDATE_MODE_UNDEFINED; + } + + if (psDSSDrv->get_update_mode == NULL) + { + if (strcmp(psDSSDev->name, "hdmi") == 0) + { + return OMAPLFB_UPDATE_MODE_AUTO; + } + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: No get_update_mode function\n", __FUNCTION__, psDevInfo->uiFBDevID)); + return OMAPLFB_UPDATE_MODE_UNDEFINED; + } + + eMode = psDSSDrv->get_update_mode(psDSSDev); + if (!OMAPLFBValidateDSSUpdateMode(eMode)) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Unknown update mode (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, (int)eMode)); + } + + return OMAPLFBFromDSSUpdateMode(eMode); +#endif /* defined(PVR_OMAPLFB_DRM_FB) */ +} + +/* Set display update mode */ +OMAPLFB_BOOL OMAPLFBSetUpdateMode(OMAPLFB_DEVINFO *psDevInfo, OMAPLFB_UPDATE_MODE eMode) +{ +#if defined(PVR_OMAPLFB_DRM_FB) + struct drm_connector *psConnector; + enum omap_dss_update_mode eDSSMode; + OMAPLFB_BOOL bSuccess = OMAPLFB_FALSE; + OMAPLFB_BOOL bFailure = OMAPLFB_FALSE; + + if (!OMAPLFBValidateUpdateMode(eMode)) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Unknown update mode (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, (int)eMode)); + return OMAPLFB_FALSE; + } + eDSSMode = OMAPLFBToDSSUpdateMode(eMode); + + for (psConnector = NULL; + (psConnector = omap_fbdev_get_next_connector(psDevInfo->psLINFBInfo, psConnector)) != NULL;) + { + int iRes = omap_connector_set_update_mode(psConnector, eDSSMode); + OMAPLFB_BOOL bRes = (iRes == 0); + + + bSuccess |= bRes; + bFailure |= !bRes; + } + + if (!bFailure) + { + if (!bSuccess) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: No screens\n", __FUNCTION__, psDevInfo->uiFBDevID)); + } + + return OMAPLFB_TRUE; + } + + if (!bSuccess) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Couldn't set %s for any screen\n", __FUNCTION__, psDevInfo->uiFBDevID, OMAPLFBUpdateModeToString(eMode))); + return OMAPLFB_FALSE; + } + + if (eMode == OMAPLFB_UPDATE_MODE_AUTO) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Couldn't set %s for all screens\n", __FUNCTION__, psDevInfo->uiFBDevID, OMAPLFBUpdateModeToString(eMode))); + return OMAPLFB_FALSE; + } + + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": %s: Device %u: %s set for some screens\n", __FUNCTION__, psDevInfo->uiFBDevID, OMAPLFBUpdateModeToString(eMode))); + + return OMAPLFB_TRUE; +#else /* defined(PVR_OMAPLFB_DRM_FB) */ + struct omap_dss_device *psDSSDev = fb2display(psDevInfo->psLINFBInfo); + OMAP_DSS_DRIVER(psDSSDrv, psDSSDev); + enum omap_dss_update_mode eDSSMode; + int res; + + if (psDSSDrv == NULL || psDSSDrv->set_update_mode == NULL) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Can't set update mode\n", __FUNCTION__, psDevInfo->uiFBDevID)); + return OMAPLFB_FALSE; + } + + if (!OMAPLFBValidateUpdateMode(eMode)) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Unknown update mode (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, (int)eMode)); + return OMAPLFB_FALSE; + } + eDSSMode = OMAPLFBToDSSUpdateMode(eMode); + + res = psDSSDrv->set_update_mode(psDSSDev, eDSSMode); + if (res != 0) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: set_update_mode (%s) failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, OMAPLFBDSSUpdateModeToString(eDSSMode), res)); + } + + return (res == 0); +#endif /* defined(PVR_OMAPLFB_DRM_FB) */ +} + +/* Wait for VSync */ +OMAPLFB_BOOL OMAPLFBWaitForVSync(OMAPLFB_DEVINFO *psDevInfo) +{ +#if defined(PVR_OMAPLFB_DRM_FB) + struct drm_connector *psConnector; + + for (psConnector = NULL; + (psConnector = omap_fbdev_get_next_connector(psDevInfo->psLINFBInfo, psConnector)) != NULL;) + { + (void) omap_encoder_wait_for_vsync(psConnector->encoder); + } + + return OMAPLFB_TRUE; +#else /* defined(PVR_OMAPLFB_DRM_FB) */ + struct omap_dss_device *psDSSDev = fb2display(psDevInfo->psLINFBInfo); + OMAP_DSS_MANAGER(psDSSMan, psDSSDev); + + if (psDSSMan != NULL && WAIT_FOR_VSYNC(psDSSMan) != NULL) + { + int res = WAIT_FOR_VSYNC(psDSSMan)(psDSSMan); + if (res != 0) + { + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: Device %u: Wait for vsync failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, res)); + return OMAPLFB_FALSE; + } + } + + return OMAPLFB_TRUE; +#endif /* defined(PVR_OMAPLFB_DRM_FB) */ +} + +/* + * Wait for screen to update. If the screen is in manual or auto update + * mode, we can call this function to wait for the screen to update. + */ +OMAPLFB_BOOL OMAPLFBManualSync(OMAPLFB_DEVINFO *psDevInfo) +{ +#if defined(PVR_OMAPLFB_DRM_FB) + struct drm_connector *psConnector; + + for (psConnector = NULL; + (psConnector = omap_fbdev_get_next_connector(psDevInfo->psLINFBInfo, psConnector)) != NULL; ) + { + /* Try manual sync first, then try wait for vsync */ + if (omap_connector_sync(psConnector) != 0) + { + (void) omap_encoder_wait_for_vsync(psConnector->encoder); + } + } + + return OMAPLFB_TRUE; +#else /* defined(PVR_OMAPLFB_DRM_FB) */ + struct omap_dss_device *psDSSDev = fb2display(psDevInfo->psLINFBInfo); + OMAP_DSS_DRIVER(psDSSDrv, psDSSDev); + + if (psDSSDrv != NULL && psDSSDrv->sync != NULL) + { + int res = psDSSDrv->sync(psDSSDev); + if (res != 0) + { + printk(KERN_ERR DRIVER_PREFIX ": %s: Device %u: Sync failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, res); + return OMAPLFB_FALSE; + } + } + + return OMAPLFB_TRUE; +#endif /* defined(PVR_OMAPLFB_DRM_FB) */ +} + +/* + * If the screen is manual or auto update mode, wait for the screen to + * update. + */ +OMAPLFB_BOOL OMAPLFBCheckModeAndSync(OMAPLFB_DEVINFO *psDevInfo) +{ + OMAPLFB_UPDATE_MODE eMode = OMAPLFBGetUpdateMode(psDevInfo); + + switch(eMode) + { + case OMAPLFB_UPDATE_MODE_AUTO: + case OMAPLFB_UPDATE_MODE_MANUAL: + return OMAPLFBManualSync(psDevInfo); + default: + break; + } + + return OMAPLFB_TRUE; +} + +/* Linux Framebuffer event notification handler */ +static int OMAPLFBFrameBufferEvents(struct notifier_block *psNotif, + unsigned long event, void *data) +{ + OMAPLFB_DEVINFO *psDevInfo; + struct fb_event *psFBEvent = (struct fb_event *)data; + struct fb_info *psFBInfo = psFBEvent->info; + OMAPLFB_BOOL bBlanked; + + /* Only interested in blanking events */ + if (event != FB_EVENT_BLANK) + { + return 0; + } + + bBlanked = (*(IMG_INT *)psFBEvent->data != 0) ? OMAPLFB_TRUE: OMAPLFB_FALSE; + + psDevInfo = OMAPLFBGetDevInfoPtr(psFBInfo->node); + +#if 0 + if (psDevInfo != NULL) + { + if (bBlanked) + { + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": %s: Device %u: Blank event received\n", __FUNCTION__, psDevInfo->uiFBDevID)); + } + else + { + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": %s: Device %u: Unblank event received\n", __FUNCTION__, psDevInfo->uiFBDevID)); + } + } + else + { + DEBUG_PRINTK((KERN_INFO DRIVER_PREFIX ": %s: Device %u: Blank/Unblank event for unknown framebuffer\n", __FUNCTION__, psFBInfo->node)); + } +#endif + + if (psDevInfo != NULL) + { + OMAPLFBAtomicBoolSet(&psDevInfo->sBlanked, bBlanked); + OMAPLFBAtomicIntInc(&psDevInfo->sBlankEvents); + } + + return 0; +} + +/* Unblank the screen */ +OMAPLFB_ERROR OMAPLFBUnblankDisplay(OMAPLFB_DEVINFO *psDevInfo) +{ + int res; + + OMAPLFB_CONSOLE_LOCK(); + res = fb_blank(psDevInfo->psLINFBInfo, 0); + OMAPLFB_CONSOLE_UNLOCK(); + if (res != 0 && res != -EINVAL) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: fb_blank failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, res); + return (OMAPLFB_ERROR_GENERIC); + } + + return (OMAPLFB_OK); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND + +/* Blank the screen */ +static void OMAPLFBBlankDisplay(OMAPLFB_DEVINFO *psDevInfo) +{ + OMAPLFB_CONSOLE_LOCK(); + fb_blank(psDevInfo->psLINFBInfo, 1); + OMAPLFB_CONSOLE_UNLOCK(); +} + +static void OMAPLFBEarlySuspendHandler(struct early_suspend *h) +{ + unsigned uiMaxFBDevIDPlusOne = OMAPLFBMaxFBDevIDPlusOne(); + unsigned i; + + for (i=0; i < uiMaxFBDevIDPlusOne; i++) + { + OMAPLFB_DEVINFO *psDevInfo = OMAPLFBGetDevInfoPtr(i); + + if (psDevInfo != NULL) + { + OMAPLFBAtomicBoolSet(&psDevInfo->sEarlySuspendFlag, OMAPLFB_TRUE); + OMAPLFBBlankDisplay(psDevInfo); + } + } +} + +static void OMAPLFBEarlyResumeHandler(struct early_suspend *h) +{ + unsigned uiMaxFBDevIDPlusOne = OMAPLFBMaxFBDevIDPlusOne(); + unsigned i; + + for (i=0; i < uiMaxFBDevIDPlusOne; i++) + { + OMAPLFB_DEVINFO *psDevInfo = OMAPLFBGetDevInfoPtr(i); + + if (psDevInfo != NULL) + { + OMAPLFBUnblankDisplay(psDevInfo); + OMAPLFBAtomicBoolSet(&psDevInfo->sEarlySuspendFlag, OMAPLFB_FALSE); + } + } +} + +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +/* Set up Linux Framebuffer event notification */ +OMAPLFB_ERROR OMAPLFBEnableLFBEventNotification(OMAPLFB_DEVINFO *psDevInfo) +{ + int res; + OMAPLFB_ERROR eError; + + /* Set up Linux Framebuffer event notification */ + memset(&psDevInfo->sLINNotifBlock, 0, sizeof(psDevInfo->sLINNotifBlock)); + + psDevInfo->sLINNotifBlock.notifier_call = OMAPLFBFrameBufferEvents; + + OMAPLFBAtomicBoolSet(&psDevInfo->sBlanked, OMAPLFB_FALSE); + OMAPLFBAtomicIntSet(&psDevInfo->sBlankEvents, 0); + + res = fb_register_client(&psDevInfo->sLINNotifBlock); + if (res != 0) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: fb_register_client failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, res); + + return (OMAPLFB_ERROR_GENERIC); + } + + eError = OMAPLFBUnblankDisplay(psDevInfo); + if (eError != OMAPLFB_OK) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: UnblankDisplay failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, eError); + return eError; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + psDevInfo->sEarlySuspend.suspend = OMAPLFBEarlySuspendHandler; + psDevInfo->sEarlySuspend.resume = OMAPLFBEarlyResumeHandler; + psDevInfo->sEarlySuspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1; + register_early_suspend(&psDevInfo->sEarlySuspend); +#endif + + return (OMAPLFB_OK); +} + +/* Disable Linux Framebuffer event notification */ +OMAPLFB_ERROR OMAPLFBDisableLFBEventNotification(OMAPLFB_DEVINFO *psDevInfo) +{ + int res; + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&psDevInfo->sEarlySuspend); +#endif + + /* Unregister for Framebuffer events */ + res = fb_unregister_client(&psDevInfo->sLINNotifBlock); + if (res != 0) + { + printk(KERN_ERR DRIVER_PREFIX + ": %s: Device %u: fb_unregister_client failed (%d)\n", __FUNCTION__, psDevInfo->uiFBDevID, res); + return (OMAPLFB_ERROR_GENERIC); + } + + OMAPLFBAtomicBoolSet(&psDevInfo->sBlanked, OMAPLFB_FALSE); + + return (OMAPLFB_OK); +} + +#if defined(SUPPORT_DRI_DRM) && defined(PVR_DISPLAY_CONTROLLER_DRM_IOCTL) +static OMAPLFB_DEVINFO *OMAPLFBPVRDevIDToDevInfo(unsigned uiPVRDevID) +{ + unsigned uiMaxFBDevIDPlusOne = OMAPLFBMaxFBDevIDPlusOne(); + unsigned i; + + for (i=0; i < uiMaxFBDevIDPlusOne; i++) + { + OMAPLFB_DEVINFO *psDevInfo = OMAPLFBGetDevInfoPtr(i); + + if (psDevInfo->uiPVRDevID == uiPVRDevID) + { + return psDevInfo; + } + } + + printk(KERN_ERR DRIVER_PREFIX + ": %s: PVR Device %u: Couldn't find device\n", __FUNCTION__, uiPVRDevID); + + return NULL; +} + +int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Ioctl)(struct drm_device unref__ *dev, void *arg, struct drm_file unref__ *pFile) +{ + uint32_t *puiArgs; + uint32_t uiCmd; + unsigned uiPVRDevID; + int ret = 0; + OMAPLFB_DEVINFO *psDevInfo; + + if (arg == NULL) + { + return -EFAULT; + } + + puiArgs = (uint32_t *)arg; + uiCmd = puiArgs[PVR_DRM_DISP_ARG_CMD]; + uiPVRDevID = puiArgs[PVR_DRM_DISP_ARG_DEV]; + + psDevInfo = OMAPLFBPVRDevIDToDevInfo(uiPVRDevID); + if (psDevInfo == NULL) + { + return -EINVAL; + } + + + switch (uiCmd) + { + case PVR_DRM_DISP_CMD_LEAVE_VT: + case PVR_DRM_DISP_CMD_ENTER_VT: + { + OMAPLFB_BOOL bLeaveVT = (uiCmd == PVR_DRM_DISP_CMD_LEAVE_VT); + DEBUG_PRINTK((KERN_WARNING DRIVER_PREFIX ": %s: PVR Device %u: %s\n", + __FUNCTION__, uiPVRDevID, + bLeaveVT ? "Leave VT" : "Enter VT")); + + OMAPLFBCreateSwapChainLock(psDevInfo); + + OMAPLFBAtomicBoolSet(&psDevInfo->sLeaveVT, bLeaveVT); + if (psDevInfo->psSwapChain != NULL) + { + flush_workqueue(psDevInfo->psSwapChain->psWorkQueue); + + if (bLeaveVT) + { + OMAPLFBFlip(psDevInfo, &psDevInfo->sSystemBuffer); + (void) OMAPLFBCheckModeAndSync(psDevInfo); + } + } + + OMAPLFBCreateSwapChainUnLock(psDevInfo); + (void) OMAPLFBUnblankDisplay(psDevInfo); + break; + } + case PVR_DRM_DISP_CMD_ON: + case PVR_DRM_DISP_CMD_STANDBY: + case PVR_DRM_DISP_CMD_SUSPEND: + case PVR_DRM_DISP_CMD_OFF: + { + int iFBMode; +#if defined(DEBUG) + { + const char *pszMode; + switch(uiCmd) + { + case PVR_DRM_DISP_CMD_ON: + pszMode = "On"; + break; + case PVR_DRM_DISP_CMD_STANDBY: + pszMode = "Standby"; + break; + case PVR_DRM_DISP_CMD_SUSPEND: + pszMode = "Suspend"; + break; + case PVR_DRM_DISP_CMD_OFF: + pszMode = "Off"; + break; + default: + pszMode = "(Unknown Mode)"; + break; + } + printk(KERN_WARNING DRIVER_PREFIX ": %s: PVR Device %u: Display %s\n", + __FUNCTION__, uiPVRDevID, pszMode); + } +#endif + switch(uiCmd) + { + case PVR_DRM_DISP_CMD_ON: + iFBMode = FB_BLANK_UNBLANK; + break; + case PVR_DRM_DISP_CMD_STANDBY: + iFBMode = FB_BLANK_HSYNC_SUSPEND; + break; + case PVR_DRM_DISP_CMD_SUSPEND: + iFBMode = FB_BLANK_VSYNC_SUSPEND; + break; + case PVR_DRM_DISP_CMD_OFF: + iFBMode = FB_BLANK_POWERDOWN; + break; + default: + return -EINVAL; + } + + OMAPLFBCreateSwapChainLock(psDevInfo); + + if (psDevInfo->psSwapChain != NULL) + { + flush_workqueue(psDevInfo->psSwapChain->psWorkQueue); + } + + OMAPLFB_CONSOLE_LOCK(); + ret = fb_blank(psDevInfo->psLINFBInfo, iFBMode); + OMAPLFB_CONSOLE_UNLOCK(); + + OMAPLFBCreateSwapChainUnLock(psDevInfo); + + break; + } + default: + { + ret = -EINVAL; + break; + } + } + + return ret; +} +#endif + +/* Insert the driver into the kernel */ +#if defined(SUPPORT_DRI_DRM) +int PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Init)(struct drm_device unref__ *dev) +#else +static int __init OMAPLFB_Init(void) +#endif +{ + + if(OMAPLFBInit() != OMAPLFB_OK) + { + printk(KERN_ERR DRIVER_PREFIX ": %s: OMAPLFBInit failed\n", __FUNCTION__); + return -ENODEV; + } + + return 0; + +} + +/* Remove the driver from the kernel */ +#if defined(SUPPORT_DRI_DRM) +void PVR_DRM_MAKENAME(DISPLAY_CONTROLLER, _Cleanup)(struct drm_device unref__ *dev) +#else +static void __exit OMAPLFB_Cleanup(void) +#endif +{ + if(OMAPLFBDeInit() != OMAPLFB_OK) + { + printk(KERN_ERR DRIVER_PREFIX ": %s: OMAPLFBDeInit failed\n", __FUNCTION__); + } +} + +#if !defined(SUPPORT_DRI_DRM) +/* + These macro calls define the initialisation and removal functions of the + driver. Although they are prefixed `module_', they apply when compiling + statically as well; in both cases they define the function the kernel will + run to start/stop the driver. +*/ +late_initcall(OMAPLFB_Init); +module_exit(OMAPLFB_Cleanup); +#endif |