//
// Copyright  2009 Samsung Electronics Co; Ltd. All Rights Reserved.
//
//
//
//
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:    tvout_drv.c

Abstract:       Implementation of tvout driver DLL
                This module implements Low Level HW control

Functions:


Notes:


--*/

#include <windows.h>
#include <bsp_cfg.h>    // for reference HCLK, ECLK
#include <register_map.h>
#include "tvout_global.h"
#include "stda_common.h"
#include "stda_resource.h"
#include "stda_tvout_if.h"
#include "stda_video_layer.h"
#include "stda_grp.h"
#include "stda_context.h"
#include "stda_hdmi.h"
#include "tvout_drv.h"

//Internal Only
BOOL SetPowerDown();
BOOL SetPowerUp();


DBGPARAM dpCurSettings = 				\
{										\
	TEXT(__MODULE__),					\
	{									\
		TEXT("Errors"),			/* 0  */	\
		TEXT("Warnings"),			/* 1  */	\
		TEXT("Performance"),		/* 2  */	\
		TEXT("Temporary tests"),	/* 3  */	\
		TEXT("HDCP"),			/* 4  */	\
		TEXT("Context"),			/* 5  */	\
		TEXT("Video Prodcessor"),	/* 6  */	\
		TEXT("Video Mixer"),		/* 7  */	\
		TEXT("SDout"),			/* 8  */	\
		TEXT("HDMI"),			/* 9  */	\
		TEXT("TVout I/F"),			/* 10 */	\
		TEXT("Power"),			/* 11 */	\
		TEXT("Clock"),			/* 12 */	\
		TEXT("Interrupt"),			/* 13 */	\
		TEXT("TVout Driver"),		/* 14 */	\
		TEXT("Resource"),			/* 15 */	\
	},									\
	(TVOUTDRV_RETAILZONES)				\
};


BOOL
WINAPI
DllEntry(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		//DEBUGREGISTER(hinstDll);
		DBGMSG(TV_FUNC,(_T("[TVDRV] DllEntry() : Process Attach\r\n")));
	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
		DBGMSG(TV_FUNC,(_T("[TVDRV] DllEntry() : Process Detach\r\n")));
	}

	return TRUE;
}

DWORD
TVD_Init(
LPCTSTR pContext
)
{
	DBGMSG(TV_FUNC,(_T("[TVDRV] ++TVD_Init(0x%08x)\r\n"), pContext));

	if (STDA_initialize_tvout_engine() == FALSE)
	{
		DBGMSG(TV_USR1,(_T("[TVDRV:ERR] STDA_Init() : STDA_initialize_tvout_engine() Failed \n\r")));
		goto CleanUp;
	}

	DBGMSG(TV_FUNC,(_T("[TVDRV] --TVD_Init()\r\n")));

	return STDA_get_driver_signature();    // hDeviceContext

CleanUp:

	DBGMSG(TV_USR1,(_T("[TVDRV:ERR] --TVD_Init() : Failed\r\n")));

	return 0;
}

BOOL
TVD_Deinit(
DWORD hDeviceContext
)
{
	DBGMSG(TV_FUNC,(_T("[TVDRV] ++TVD_Deinit(0x%08x)\r\n"), hDeviceContext));

	if (hDeviceContext != STDA_get_driver_signature())
	{
		DBGMSG(TV_USR1,(_T("[TVDRV:ERR] TVD_Deinit() : Invalid Driver Handle[0x%08x]\r\n"), hDeviceContext));
		return FALSE;
	}

	STDA_deinitialize_tvout_engine();
	DBGMSG(TV_FUNC,(_T("[TVDRV] --TVD_Deinit()\r\n")));

	return TRUE;
}

DWORD
TVD_Open(
DWORD hDeviceContext,
DWORD AccessCode,
DWORD ShareMode
)
{
	DWORD dwOpenContext;

	DBGMSG(TV_FUNC,(_T("[TVDRV] ++TVD_Open(0x%08x, 0x%08x, 0x%08x)\r\n"), hDeviceContext, AccessCode, ShareMode));

	if (hDeviceContext != STDA_get_driver_signature())
	{
		DBGMSG(TV_USR1,(_T("[TVDRV:ERR] TVD_Open() : Invalid Driver Handle[0x%08x]\r\n"), hDeviceContext));
		goto CleanUp;
	}

	dwOpenContext = STDA_add_open_context();
	if (dwOpenContext == 0)
	{
		DBGMSG(TV_USR1,(_T("[TVDRV:ERR] TVD_Open() : Allocating Open Context Failed\r\n")));
		goto CleanUp;
	}

	DBGMSG(TV_FUNC,(_T("[TVDRV] --TVD_Open()\r\n")));

	return dwOpenContext;

CleanUp:

	DBGMSG(TV_USR1,(_T("[TVDRV:ERR] --TVD_Open() : Failed\r\n")));

	return 0;
}

BOOL
TVD_Close(
DWORD hOpenContext
)
{
	BOOL bRet;

	DBGMSG(TV_FUNC,(_T("[TVDRV] ++TVD_Close(0x%08x)\r\n"), hOpenContext));

	bRet = STDA_remove_open_context(hOpenContext);
	if (!bRet)
	{
		DBGMSG(TV_USR1,(_T("[TVDRV:ERR] TVD_Close() : Invalid Open Context !!!\r\n")));
	}

	DBGMSG(TV_FUNC,(_T("[TVDRV] --TVD_Close()\r\n")));

	return bRet;
}

DWORD
TVD_Read(
DWORD hOpenContext,
LPVOID pBuffer,
DWORD Count
)
{
	DBGMSG(TV_FUNC,(_T("[TVDRV] TVD_Read(0x%08x, 0x%08x, 0x%08x)\r\n"), hOpenContext, pBuffer, Count));

	return (0);    // End of File
}

DWORD
TVD_Write(
DWORD hOpenContext,
LPCVOID pBuffer,
DWORD Count
)
{
	DBGMSG(TV_FUNC,(_T("[TVDRV] TVD_Write(0x%08x, 0x%08x, 0x%08x)\r\n"), hOpenContext, pBuffer, Count));

	return (0);    // Number of Byte
}

DWORD
TVD_Seek(
DWORD hOpenContext,
long Amount,
WORD Type
)
{
	DBGMSG(TV_FUNC,(_T("[TVDRV] TVD_Seek(0x%08x, 0x%08x, 0x%08x)\r\n"), hOpenContext, Amount, Type));

	return (DWORD)-1;    // Failure
}

BOOL
TVD_IOControl(
DWORD hOpenContext,
DWORD dwCode,
PBYTE pBufIn,
DWORD dwLenIn,
PBYTE pBufOut,
DWORD dwLenOut,
PDWORD pdwActualOut
)
{
	STDAContext *pCtxt;
	STDAPowerContext *pPMCtxt;
	BOOL bRet = TRUE;
	DWORD dwErr = ERROR_SUCCESS;

	pCtxt = STDA_get_context();
	pPMCtxt = STDA_get_power_context();

	RETAILMSG(FALSE,(_T("[TVDRV] TVD_IOControl(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\r\n"),
	hOpenContext, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActualOut));

	EnterCriticalSection(&pCtxt->csProc);

	switch(dwCode)
	{
		case IOCTL_POWER_CAPABILITIES:

			#ifdef SUPPORT_APM_TVD
	              DevicePowerNotify(L"TVD0:", D3, POWER_NAME);
			#endif
			// tell the power manager about ourselves
			if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(POWER_CAPABILITIES)) )
			{
				bRet = FALSE;
				dwErr = ERROR_INVALID_PARAMETER;
				DBGMSG(TV_USR1,(TEXT("[TVDRV:ERR] POWER_CAPABILITIES : Invalid Parameter\n\r")));
			}
			else
			{
				PPOWER_CAPABILITIES ppc = (PPOWER_CAPABILITIES) pBufOut;
				memset(ppc, 0, sizeof(POWER_CAPABILITIES));

				// support D0, D3
				ppc->DeviceDx = DX_MASK(D0) | DX_MASK(D3);

				DBGMSG(TV_FUNC,(_T("[TVDRV] IOCTL_POWER_CAPABILITIES: 0x%x \r\n"), ppc->DeviceDx));
				*pdwActualOut = sizeof(POWER_CAPABILITIES);
			}
			break;

		case IOCTL_POWER_GET:
		case IOCTL_STDA_PM_GET_POWER_STATUS:
			if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)) )
			{
				bRet = FALSE;
				dwErr = ERROR_INVALID_PARAMETER;
				DBGMSG(TV_FUNC,(TEXT("[TVDRV:ERR] CEDEVICE_POWER_STATE : Invalid Parameter\n\r")));
			}
			else
			{
				*(PCEDEVICE_POWER_STATE)pBufOut = pPMCtxt->powerState;
				DBGMSG(TV_FUNC,(_T("[TVDRV] IOCTL_POWER_GET: D%u \r\n"), pPMCtxt->powerState));
				*pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
			}
			break;

		case IOCTL_POWER_SET:
		{
			CEDEVICE_POWER_STATE NewDx;


#ifdef SUPPORT_APM_TVD
			//When I call DevicePowerNotify
			if(pPMCtxt->bChangePowerState)
			{

				DBGMSG(TV_FUNC,(_T("[TVDRV] Drv Change Drv Power Status\r\n")));
				DBGMSG(TV_FUNC,(_T("[TVDRV] IOCTL_POWER_SET: [D%u] ->[D%u] \r\n") ,pPMCtxt->powerState,pPMCtxt->powerStateChange));

				//Change Power status
				pPMCtxt->powerState = pPMCtxt->powerStateChange;
				pPMCtxt->bChangePowerState = FALSE;
				TVout_power_set_APM(pPMCtxt->powerState);
			}
			else
#endif
			{
				if ( !pdwActualOut || !pBufOut || (dwLenOut < sizeof(CEDEVICE_POWER_STATE)) )
				{
					bRet = FALSE;
					dwErr = ERROR_INVALID_PARAMETER;
					DBGMSG(TV_USR1,(TEXT("[TVDRV:ERR] CEDEVICE_POWER_STATE : Invalid Parameter\n\r")));
					break;
				}

				NewDx = *(PCEDEVICE_POWER_STATE)pBufOut;

				if ( VALID_DX(NewDx) )
				{
					DBGMSG(TV_FUNC,(_T("[TVDRV] PM Change Drv Power Status\r\n")));
					DBGMSG(TV_FUNC,(_T("[TVDRV] IOCTL_POWER_SET: [D%u] ->[D%u] \r\n") ,pPMCtxt->powerState,NewDx));

					// grab the CS since the normal Xxx_PowerXxx can not.
					switch ( NewDx )
					{
						case D0:
							SetPowerUp();

							break;
						default:
							SetPowerDown();

							break;
					}

				
				// return our state
				*(PCEDEVICE_POWER_STATE)pBufOut = pPMCtxt->powerState;

				//DBGMSG(TV_FUNC,(_T("[TVDRV] IOCTL_POWER_SET: [D%u] ->[D%u] \r\n") ,pPMCtxt->powerState,NewDx));


				*pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
				}
				else
				{
					bRet = FALSE;
					dwErr = ERROR_INVALID_PARAMETER;
					RETAILMSG(TV_PLAY,(TEXT("[WAV:ERR] CEDEVICE_POWER_STATE : Invalid Parameter Dx\n\r")));
				}
			}
		}
			break;

		case IOCTL_STDA_PM_SET_WAKEUP:
			SetPowerUp();

			break;

		case IOCTL_STDA_PM_SET_SLEEP:
			SetPowerDown();

			break;

		case IOCTL_STDA_RSC_REQUEST_TVOUT_INTERFACE:
		case IOCTL_STDA_RSC_RELEASE_TVOUT_INTERFACE:
		case IOCTL_STDA_RSC_REQUEST_VIDEO_PROCESSOR:
		case IOCTL_STDA_RSC_RELEASE_VIDEO_PROCESSOR:
		case IOCTL_STDA_RSC_REQUEST_GRAPHIC_0:
		case IOCTL_STDA_RSC_RELEASE_GRAPHIC_0:
		case IOCTL_STDA_RSC_REQUEST_GRAPHIC_1:
		case IOCTL_STDA_RSC_RELEASE_GRAPHIC_1:
			bRet = STDA_Resource_API_Proc(hOpenContext,
									STDA_get_api_function_code(dwCode),
									pBufIn, dwLenIn, pBufOut, dwLenOut,
									pdwActualOut);
			break;

		case IOCTL_STDA_TVOUT_INIT_INTERFACE_PARAM:
		case IOCTL_STDA_TVOUT_START:
		case IOCTL_STDA_TVOUT_STOP:
		case IOCTL_STDA_TVOUT_GET_INFO:
			bRet = STDA_TVoutIF_API_Proc(hOpenContext,
									STDA_get_api_function_code(dwCode), pBufIn,
									dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			break;

		case IOCTL_STDA_VL_INIT_PROCESSING_PARAM:
		case IOCTL_STDA_VL_INIT_BRIGHTNESS_OFFSET:
		case IOCTL_STDA_VL_INIT_CONTRAST_BRIGHTNESS:
		case IOCTL_STDA_VL_START:
		case IOCTL_STDA_VL_STOP:
		case IOCTL_STDA_VL_SET_PRIORITY:
		case IOCTL_STDA_VL_SET_TOP_ADDRESS:
		case IOCTL_STDA_VL_SET_BOTTOM_ADDRESS:
		case IOCTL_STDA_VL_SET_IMG_SIZE:
		case IOCTL_STDA_VL_SET_SRC_POSITION:
		case IOCTL_STDA_VL_SET_DEST_POSITION:
		case IOCTL_STDA_VL_SET_SRC_SIZE:
		case IOCTL_STDA_VL_SET_DEST_SIZE:
		case IOCTL_STDA_VL_SET_BRIGHTNESS_OFFSET:
		case IOCTL_STDA_VL_SET_CONTRAST_BRIGHTNESS:
		case IOCTL_STDA_VL_GET_PRIORITY:
			bRet = STDA_VideoLayer_API_Proc(hOpenContext,
									STDA_get_api_function_code(dwCode),
									pBufIn, dwLenIn, pBufOut, dwLenOut,
									pdwActualOut);
			break;


		case IOCTL_STDA_GRP0_INIT_PROCESSING_PARAM:
		case IOCTL_STDA_GRP0_START:
		case IOCTL_STDA_GRP0_STOP:
		case IOCTL_STDA_GRP0_SET_PRIORITY:
		case IOCTL_STDA_GRP0_SET_BASE_ADDRESS:
		case IOCTL_STDA_GRP0_SET_DEST_POSITION:
		case IOCTL_STDA_GRP0_GET_PRIORITY:
		case IOCTL_STDA_GRP1_INIT_PROCESSING_PARAM:
		case IOCTL_STDA_GRP1_START:
		case IOCTL_STDA_GRP1_STOP:
		case IOCTL_STDA_GRP1_SET_PRIORITY:
		case IOCTL_STDA_GRP1_SET_BASE_ADDRESS:
		case IOCTL_STDA_GRP1_SET_DEST_POSITION:
		case IOCTL_STDA_GRP1_GET_PRIORITY:
			bRet = STDA_Grp_API_Proc(hOpenContext,
									STDA_get_api_function_code(dwCode), pBufIn,
									dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			break;

		case IOCTL_STDA_BG_INIT_COLOR:
			bRet = STDA_BG_API_Proc(hOpenContext, 
									STDA_get_api_function_code(dwCode),pBufIn, 
									dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			break;

		case IOCTL_STDA_HDMI_INIT_SPD_INFOFRAME:
		case IOCTL_STDA_HDMI_INIT_HDCP_EN:
		case IOCTL_STDA_HDMI_INIT_AUDIO:
		case IOCTL_STDA_HDMI_GET_HPD_STATUS:
		case IOCTL_STDA_HDMI_AVAILABLE_MODE:
			bRet = STDA_HDMI_API_Proc(hOpenContext,
									STDA_get_api_function_code(dwCode), pBufIn,
									dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			break;

		case IOCTL_STDA_HDMI_WAIT_HPD_STATUS_CHANGE:
			break;

		case IOCTL_POWER_QUERY:
		case IOCTL_REGISTER_POWER_RELATIONSHIP:
		default:
			DBGMSG(TV_USR1,(_T("[TVDRV:ERR] TVD_IOControl() : Unknown IOCTL [0x%08x]\r\n"), dwCode));
									SetLastError (ERROR_INVALID_ACCESS);
			bRet = FALSE;
			break;
	}

	LeaveCriticalSection(&pCtxt->csProc);

	if(dwCode == IOCTL_STDA_HDMI_WAIT_HPD_STATUS_CHANGE)
	{
		if(STDA_resource_compare_TVout_interface(hOpenContext))
		{
			EnterCriticalSection(&pCtxt->csProcWaitHPD);
			bRet = STDA_HDMI_API_Proc(hOpenContext,
			STDA_get_api_function_code(dwCode), pBufIn,
			dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			LeaveCriticalSection(&pCtxt->csProcWaitHPD);
		}

		else if(STDA_resource_compare_VP(hOpenContext))
		{
			EnterCriticalSection(&pCtxt->csProcWaitHPDVP);
			bRet = STDA_VideoLayer_API_Proc(hOpenContext,
			STDA_get_api_function_code(dwCode), pBufIn,
			dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			LeaveCriticalSection(&pCtxt->csProcWaitHPDVP);
		}
	
		else if(STDA_resource_compare_GRP0(hOpenContext))
		{
			EnterCriticalSection(&pCtxt->csProcWaitHPDGRP0);
			bRet = STDA_Grp_API_Proc(hOpenContext,
			STDA_get_api_function_code(dwCode), pBufIn,
			dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			LeaveCriticalSection(&pCtxt->csProcWaitHPDGRP0);
		}
		else if(STDA_resource_compare_GRP1(hOpenContext))
		{
			EnterCriticalSection(&pCtxt->csProcWaitHPDGRP1);
			bRet = STDA_Grp_API_Proc(hOpenContext,
			STDA_get_api_function_code(dwCode), pBufIn,
			dwLenIn, pBufOut, dwLenOut, pdwActualOut);
			LeaveCriticalSection(&pCtxt->csProcWaitHPDGRP1);
		}
		else
		{
			RETAILMSG(TV_PLAY,(_T("[TVDRV:ERR] TVD_IOControl() : IOCTL_STDA_HDMI_WAIT_HPD_STATUS_CHANGE failed\r\n")));
			bRet = FALSE;
		}
	}

	RETAILMSG(FALSE, (_T("[TVDRV] --TVD_IOControl()\r\n")));

	return bRet;
}

BOOL
SetPowerUp()
{

	STDAPowerContext *pPMCtxt;

	DBGMSG(TV_FUNC,(_T("[TVDRV] ++SetPowerUp()\r\n")));

	pPMCtxt = STDA_get_power_context();

	if(pPMCtxt->pinStatus == HDMI_HPD_MODE)
	{
		STDA_HDMI_HpdSetMode(HDMI_HPD_MODE, FALSE);
	}
	else if(pPMCtxt->pinStatus == HDMI_EINT_MODE)
	{
		STDA_HDMI_HpdSetMode(HDMI_EINT_MODE, FALSE);
	}


	if (pPMCtxt->powerState != D0)
	{
		if(!STDA_tvout_wakeup())
		{
			pPMCtxt->powerState = D3;
		}
		else
		{
			pPMCtxt->powerState = D0;
		}
	}
	
	DBGMSG(TV_FUNC,(_T("[TVDRV] PowerStatus=[D%u]\r\n"),pPMCtxt->powerState));	
	DBGMSG(TV_FUNC,(_T("[TVDRV] --SetPowerUp()\r\n")));


	return TRUE;
}


BOOL
SetPowerDown()
{

	STDAPowerContext *pPMCtxt;

	DBGMSG(TV_FUNC,(_T("[TVDRV] ++SetPowerDown()\r\n")));

	pPMCtxt = STDA_get_power_context();
	
	STDA_HDMI_HpdSetMode(HDMI_EINT_MODE, FALSE);
	
	if (pPMCtxt->powerState != D3)
	{
		if(STDA_tvout_sleep())
		{
			pPMCtxt->powerState = D3;
		}
	}

	DBGMSG(TV_FUNC,(_T("[TVDRV] PowerStatus=[D%u]\r\n"),pPMCtxt->powerState));	
	DBGMSG(TV_FUNC,(_T("[TVDRV] --SetPowerDown()\r\n")));

	return TRUE;
}


void
TVD_PowerDown
(DWORD hDeviceContext)
{
	DBGMSG(TV_FUNC,(_T("[TVDRV] ++TVD_PowerDown(0x%08x)\r\n"), hDeviceContext));

//	SetPowerDown();

	DBGMSG(TV_FUNC,(_T("[TVDRV] --TVD_PowerDown()\r\n")));
}

void
TVD_PowerUp
(DWORD hDeviceContext)
{
	DBGMSG(TV_FUNC,(_T("[TVDRV] ++TVD_PowerUp(0x%08x)\r\n"), hDeviceContext));

//	SetPowerUp();
	
	DBGMSG(TV_FUNC,(_T("[TVDRV] --TVD_PowerUp()\r\n")));
}

